mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Remove original character from game world when possessing to make persistence more robust.
This commit is contained in:
parent
10803a9735
commit
e52159f638
@ -1843,26 +1843,13 @@ impl Client {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let uids = self.state.ecs().read_storage::<Uid>();
|
||||
if let Some((prev_uid, presence)) =
|
||||
uids.get(old_entity).copied().zip(self.presence)
|
||||
{
|
||||
self.presence = Some(match presence {
|
||||
PresenceKind::Character(char_id) => {
|
||||
PresenceKind::Possessor(char_id, prev_uid)
|
||||
},
|
||||
PresenceKind::Spectator => PresenceKind::Spectator,
|
||||
PresenceKind::Possessor(old_char_id, old_uid) => {
|
||||
if old_uid == uid {
|
||||
// Returning to original entity
|
||||
PresenceKind::Character(old_char_id)
|
||||
} else {
|
||||
PresenceKind::Possessor(old_char_id, old_uid)
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(presence) = self.presence {
|
||||
self.presence = Some(match presence {
|
||||
PresenceKind::Spectator => PresenceKind::Spectator,
|
||||
PresenceKind::Character(_) => PresenceKind::Possessor,
|
||||
PresenceKind::Possessor => PresenceKind::Possessor,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return Err(Error::Other("Failed to find entity from uid.".into()));
|
||||
|
@ -19,30 +19,21 @@ pub use self::{
|
||||
},
|
||||
world_msg::WorldMapMsg,
|
||||
};
|
||||
use common::{character::CharacterId, uid::Uid};
|
||||
use common::character::CharacterId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
pub enum PresenceKind {
|
||||
Spectator,
|
||||
Character(CharacterId),
|
||||
Possessor(
|
||||
/// The original character Id before possession began. Used to revert
|
||||
/// back to original `Character` presence if original entity is
|
||||
/// re-possessed.
|
||||
CharacterId,
|
||||
/// The original entity Uid.
|
||||
Uid,
|
||||
),
|
||||
Possessor,
|
||||
}
|
||||
|
||||
impl PresenceKind {
|
||||
/// Check if the presence represents a control of a character, and thus
|
||||
/// certain in-game messages from the client such as control inputs
|
||||
/// should be handled.
|
||||
pub fn controlling_char(&self) -> bool {
|
||||
matches!(self, Self::Character(_) | Self::Possessor(_, _))
|
||||
}
|
||||
pub fn controlling_char(&self) -> bool { matches!(self, Self::Character(_) | Self::Possessor) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
|
@ -271,7 +271,7 @@ fn persist_entity(state: &mut State, entity: EcsEntity) -> EcsEntity {
|
||||
);
|
||||
},
|
||||
PresenceKind::Spectator => { /* Do nothing, spectators do not need persisting */ },
|
||||
PresenceKind::Possessor(_, _) => { /* Do nothing, possessor's are not persisted */ },
|
||||
PresenceKind::Possessor => { /* Do nothing, possessor's are not persisted */ },
|
||||
};
|
||||
}
|
||||
|
||||
@ -289,15 +289,17 @@ pub fn handle_possess(server: &mut Server, possessor_uid: Uid, possesse_uid: Uid
|
||||
};
|
||||
use common_net::sync::WorldSyncExt;
|
||||
|
||||
let ecs = server.state.ecs();
|
||||
let state = server.state_mut();
|
||||
let mut delete_entity = None;
|
||||
|
||||
if let (Some(possessor), Some(possesse)) = (
|
||||
ecs.entity_from_uid(possessor_uid.into()),
|
||||
ecs.entity_from_uid(possesse_uid.into()),
|
||||
state.ecs().entity_from_uid(possessor_uid.into()),
|
||||
state.ecs().entity_from_uid(possesse_uid.into()),
|
||||
) {
|
||||
// In this section we check various invariants and can return early if any of
|
||||
// them are not met.
|
||||
{
|
||||
let ecs = state.ecs();
|
||||
// Check that entities still exist
|
||||
if !possessor.gen().is_alive()
|
||||
|| !ecs.is_alive(possessor)
|
||||
@ -344,13 +346,18 @@ pub fn handle_possess(server: &mut Server, possessor_uid: Uid, possesse_uid: Uid
|
||||
|
||||
// Sync the player's character data to the database. This must be done before
|
||||
// moving any components from the entity.
|
||||
drop(ecs);
|
||||
let state = server.state_mut();
|
||||
//
|
||||
// NOTE: Below we delete old entity (if PresenceKind::Character) as if logging out. This is
|
||||
// to prevent any potential for item duplication (although it would only be possible if the
|
||||
// player could repossess their entity, hand off some items, and then crash the server in a
|
||||
// particular time window, and only admins should have access to the item with this ability
|
||||
// in the first place (though that isn't foolproof)). We could potentially fix this but it
|
||||
// would require some tweaks to the CharacterUpdater code (to be able to deque the pending
|
||||
// persistence request issued here if repossesing the original character), and it seems
|
||||
// prudent to be more conservative with making changes there to support this feature.
|
||||
let possessor = persist_entity(state, possessor);
|
||||
drop(state);
|
||||
// TODO: delete old entity (if PresenceKind::Character) as if logging out.
|
||||
let ecs = state.ecs();
|
||||
|
||||
let ecs = server.state.ecs();
|
||||
let mut clients = ecs.write_storage::<Client>();
|
||||
|
||||
// Transfer client component. Note: we require this component for possession.
|
||||
@ -361,17 +368,6 @@ pub fn handle_possess(server: &mut Server, possessor_uid: Uid, possesse_uid: Uid
|
||||
clients.insert(possesse, client).expect("Checked entity was alive!");
|
||||
|
||||
// Other components to transfer if they exist.
|
||||
// TODO: don't transfer character id, TODO: consider how this could relate to
|
||||
// database duplications, might need to model this like the player
|
||||
// logging out. Note: logging back in is delayed because it would
|
||||
// re-load from the database before old information is saved, if you are
|
||||
// able to reposess the same entity, this should not be an issue,
|
||||
// although the logout save being duplicated with the batch save may need to be
|
||||
// considered, the logout save would be outdated. If you could cause
|
||||
// your old entity to drop items while possessing another entity that
|
||||
// would cause duplication in the database (on the other hand this ability
|
||||
// should be strictly limited to admins, and not intended to be a normal
|
||||
// gameplay ability).
|
||||
fn transfer_component<C: specs::Component>(
|
||||
storage: &mut specs::WriteStorage<'_, C>,
|
||||
possessor: EcsEntity,
|
||||
@ -397,16 +393,13 @@ pub fn handle_possess(server: &mut Server, possessor_uid: Uid, possesse_uid: Uid
|
||||
transfer_component(&mut presence, possessor, possesse, |mut presence| {
|
||||
presence.kind = match presence.kind {
|
||||
PresenceKind::Spectator => PresenceKind::Spectator,
|
||||
// TODO: also perform this transition on the client in response to entity switch.
|
||||
// Disable persistence by changing the presence.
|
||||
PresenceKind::Character(char_id) => PresenceKind::Possessor(char_id, possessor_uid),
|
||||
PresenceKind::Possessor(old_char_id, old_uid) => if old_uid == possesse_uid {
|
||||
// If moving back to the original entity, shift back to the character
|
||||
// presence which will re-enable persistence.
|
||||
PresenceKind::Character(old_char_id)
|
||||
} else {
|
||||
PresenceKind::Possessor(old_char_id, old_uid)
|
||||
// This prevents persistence from overwriting original character info with stuff
|
||||
// from the new character.
|
||||
PresenceKind::Character(_) => {
|
||||
delete_entity = Some(possessor);
|
||||
PresenceKind::Possessor
|
||||
},
|
||||
PresenceKind::Possessor => PresenceKind::Possessor,
|
||||
};
|
||||
|
||||
presence
|
||||
@ -439,8 +432,8 @@ pub fn handle_possess(server: &mut Server, possessor_uid: Uid, possesse_uid: Uid
|
||||
);
|
||||
|
||||
drop((clients, players)); // need to drop so we can use `notify_players` below
|
||||
server.state().notify_players(remove_player_msg);
|
||||
server.state().notify_players(add_player_msg);
|
||||
state.notify_players(remove_player_msg);
|
||||
state.notify_players(add_player_msg);
|
||||
}
|
||||
|
||||
// Put possess item into loadout
|
||||
@ -489,4 +482,18 @@ pub fn handle_possess(server: &mut Server, possessor_uid: Uid, possesse_uid: Uid
|
||||
client.send_fallible(ServerGeneral::CompSync(comp_sync_package));
|
||||
}
|
||||
}
|
||||
|
||||
// Outside block above to prevent borrow conflicts (i.e. convenient to let everything drop at
|
||||
// the end of the block rather than doing it manually for this).
|
||||
// See note on `persist_entity` call above for why we do this.
|
||||
if let Some(entity) = delete_entity {
|
||||
// Delete old entity
|
||||
if let Err(e) = state.delete_entity_recorded(entity) {
|
||||
error!(
|
||||
?e,
|
||||
?entity,
|
||||
"Failed to delete entity when removing character during possession."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ impl<'a> System<'a> for Sys {
|
||||
map_marker,
|
||||
))
|
||||
},
|
||||
PresenceKind::Spectator | PresenceKind::Possessor(_, _) => None,
|
||||
PresenceKind::Spectator | PresenceKind::Possessor => None,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
@ -1024,7 +1024,7 @@ impl Hud {
|
||||
let character_id = match client.presence().unwrap() {
|
||||
PresenceKind::Character(id) => Some(id),
|
||||
PresenceKind::Spectator => unreachable!("HUD creation in Spectator mode!"),
|
||||
PresenceKind::Possessor(_, _) => None,
|
||||
PresenceKind::Possessor => None,
|
||||
};
|
||||
|
||||
// Create a new HotbarState from the persisted slots.
|
||||
|
@ -1365,7 +1365,7 @@ impl PlayState for SessionState {
|
||||
PresenceKind::Spectator => {
|
||||
unreachable!("HUD adaption in Spectator mode!")
|
||||
},
|
||||
PresenceKind::Possessor(_, _) => None,
|
||||
PresenceKind::Possessor => None,
|
||||
};
|
||||
|
||||
// Get or update the ServerProfile.
|
||||
|
Loading…
Reference in New Issue
Block a user