transform server event

This commit is contained in:
crabman 2024-02-11 12:11:04 +01:00
parent c1115dfe97
commit b9a3fa1edc
No known key found for this signature in database
7 changed files with 148 additions and 75 deletions

View File

@ -9,6 +9,7 @@ use crate::{
misc::PortalData,
DisconnectReason, LootOwner, Ori, Pos, UnresolvedChatMsg, Vel,
},
generation::EntityInfo,
lottery::LootSpec,
mounting::VolumePos,
outcome::Outcome,
@ -260,6 +261,8 @@ pub struct SetPetStayEvent(pub EcsEntity, pub EcsEntity, pub bool);
pub struct PossessEvent(pub Uid, pub Uid);
pub struct TransformEvent(pub Uid, pub EntityInfo);
pub struct InitializeCharacterEvent {
pub entity: EcsEntity,
pub character_id: CharacterId,
@ -531,6 +534,7 @@ pub fn register_event_busses(ecs: &mut World) {
ecs.insert(EventBus::<TeleportToPositionEvent>::default());
ecs.insert(EventBus::<StartTeleportingEvent>::default());
ecs.insert(EventBus::<ToggleSpriteLightEvent>::default());
ecs.insert(EventBus::<TransformEvent>::default());
}
/// Define ecs read data for event busses. And a way to convert them all to

View File

@ -35,8 +35,7 @@ use common::{
},
invite::InviteKind,
misc::PortalData,
AdminRole, ChatType, Content, Inventory, Item, LightEmitter, Presence, PresenceKind,
WaypointArea,
AdminRole, ChatType, Content, Inventory, Item, LightEmitter, WaypointArea,
},
depot,
effect::Effect,
@ -54,7 +53,7 @@ use common::{
rtsim::{Actor, Role},
terrain::{Block, BlockKind, CoordinateConversions, SpriteKind, TerrainChunkSize},
tether::Tethered,
uid::{IdMaps, Uid},
uid::Uid,
vol::ReadVol,
weather, Damage, DamageKind, DamageSource, Explosion, LoadoutBuilder, RadiusEffect,
};
@ -628,6 +627,8 @@ fn handle_into_npc(
args: Vec<String>,
action: &ServerChatCommand,
) -> CmdResult<()> {
use crate::events::shared::{transform_entity, TransformEntityError};
if client != target {
server.notify_client(
client,
@ -661,69 +662,18 @@ fn handle_into_npc(
None,
);
match NpcData::from_entity_info(entity_info) {
NpcData::Data {
inventory,
stats,
skill_set,
poise,
health,
body,
scale,
// changing alignments is cool idea, but needs more work
alignment: _,
// we aren't interested in these (yet?)
pos: _,
agent: _,
loot: _,
} => {
// Should do basically what StateExt::create_npc does
insert_or_replace_component(server, target, inventory, "player")?;
insert_or_replace_component(server, target, stats, "player")?;
insert_or_replace_component(server, target, skill_set, "player")?;
insert_or_replace_component(server, target, poise, "player")?;
if let Some(health) = health {
insert_or_replace_component(server, target, health, "player")?;
}
insert_or_replace_component(server, target, body, "player")?;
insert_or_replace_component(server, target, body.mass(), "player")?;
insert_or_replace_component(server, target, body.density(), "player")?;
insert_or_replace_component(server, target, body.collider(), "player")?;
insert_or_replace_component(server, target, scale, "player")?;
transform_entity(server, target, entity_info).map_err(|error| match error {
TransformEntityError::EntityDead => {
Content::localized_with_args("command-entity-dead", [("entity", "target")])
},
NpcData::Waypoint(_) => {
return Err(Content::localized("command-unimplemented-waypoint-spawn"));
TransformEntityError::UnexpectedNpcWaypoint => {
Content::localized("command-unimplemented-waypoint-spawn")
},
NpcData::Teleporter(_, _) => {
return Err(Content::localized("command-unimplemented-teleporter-spawn"));
TransformEntityError::UnexpectedNpcTeleporter => {
Content::localized("command-unimplemented-teleporter-spawn")
},
}
})?;
// Black magic
//
// Mainly needed to disable persistence
{
// TODO: let Imbris work out some edge-cases:
// - error on PresenseKind::LoadingCharacter
// - handle active inventory actions
let ecs = server.state.ecs();
let mut presences = ecs.write_storage::<Presence>();
let presence = presences.get_mut(target);
if let Some(presence) = presence
&& let PresenceKind::Character(id) = presence.kind
{
server.state.ecs().write_resource::<IdMaps>().remove_entity(
Some(target),
None,
Some(id),
None,
);
presence.kind = PresenceKind::Possessor;
}
}
// End of black magic
Ok(())
}

View File

@ -9,7 +9,7 @@ use crate::{
error,
rtsim::RtSim,
state_ext::StateExt,
sys::terrain::SAFE_ZONE_RADIUS,
sys::terrain::{NpcData, SAFE_ZONE_RADIUS},
Server, Settings, SpawnPoint,
};
use common::{
@ -31,9 +31,11 @@ use common::{
DestroyEvent, EmitExt, Emitter, EnergyChangeEvent, EntityAttackedHookEvent, EventBus,
ExplosionEvent, HealthChangeEvent, KnockbackEvent, LandOnGroundEvent, MakeAdminEvent,
ParryHookEvent, PoiseChangeEvent, RemoveLightEmitterEvent, RespawnEvent, SoundEvent,
StartTeleportingEvent, TeleportToEvent, TeleportToPositionEvent, UpdateMapMarkerEvent,
StartTeleportingEvent, TeleportToEvent, TeleportToPositionEvent, TransformEvent,
UpdateMapMarkerEvent,
},
event_emitters,
generation::EntityInfo,
link::Is,
lottery::distribute_many,
mounting::{Rider, VolumeRider},
@ -49,13 +51,13 @@ use common::{
vol::ReadVol,
CachedSpatialGrid, Damage, DamageKind, DamageSource, GroupTarget, RadiusEffect,
};
use common_net::msg::ServerGeneral;
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
use common_state::{AreasContainer, BlockChange, NoDurabilityArea};
use hashbrown::HashSet;
use rand::Rng;
use specs::{
shred, DispatcherBuilder, Entities, Entity as EcsEntity, Entity, Join, LendJoin, Read,
ReadExpect, ReadStorage, SystemData, Write, WriteExpect, WriteStorage,
ReadExpect, ReadStorage, SystemData, WorldExt, Write, WriteExpect, WriteStorage,
};
use std::{collections::HashMap, iter, sync::Arc, time::Duration};
use tracing::{debug, warn};
@ -2098,3 +2100,114 @@ impl ServerEvent for StartTeleportingEvent {
}
}
}
pub fn handle_transform(server: &mut Server, TransformEvent(uid, info): TransformEvent) {
let Some(entity) = server.state().ecs().entity_from_uid(uid) else {
return;
};
let _ = transform_entity(server, entity, info);
}
pub enum TransformEntityError {
EntityDead,
UnexpectedNpcWaypoint,
UnexpectedNpcTeleporter,
}
pub fn transform_entity(
server: &mut Server,
entity: Entity,
info: EntityInfo,
) -> Result<(), TransformEntityError> {
let is_player = server
.state()
.read_storage::<comp::Player>()
.contains(entity);
match NpcData::from_entity_info(info) {
NpcData::Data {
inventory,
stats,
skill_set,
poise,
health,
body,
scale,
agent,
loot,
alignment: _,
pos: _,
} => {
fn set_or_remove_component<C: specs::Component>(
server: &mut Server,
entity: EcsEntity,
component: Option<C>,
) -> Result<(), TransformEntityError> {
let mut storage = server.state.ecs_mut().write_storage::<C>();
if let Some(component) = component {
storage
.insert(entity, component)
.and(Ok(()))
.map_err(|_| TransformEntityError::EntityDead)
} else {
storage.remove(entity);
Ok(())
}
}
// Disable persistence
{
// Run persistence once before disabling it
super::player::persist_entity(server.state_mut(), entity);
// TODO: let Imbris work out some edge-cases:
// - error on PresenseKind::LoadingCharacter
// - handle active inventory actions
let ecs = server.state.ecs();
let mut presences = ecs.write_storage::<Presence>();
let presence = presences.get_mut(entity);
if let Some(presence) = presence
&& let PresenceKind::Character(id) = presence.kind
{
server.state.ecs().write_resource::<IdMaps>().remove_entity(
Some(entity),
None,
Some(id),
None,
);
presence.kind = PresenceKind::Possessor;
}
}
// Should do basically what StateExt::create_npc does
set_or_remove_component(server, entity, Some(inventory))?;
set_or_remove_component(server, entity, Some(stats))?;
set_or_remove_component(server, entity, Some(skill_set))?;
set_or_remove_component(server, entity, Some(poise))?;
set_or_remove_component(server, entity, health)?;
set_or_remove_component(server, entity, Some(body))?;
set_or_remove_component(server, entity, Some(body.mass()))?;
set_or_remove_component(server, entity, Some(body.density()))?;
set_or_remove_component(server, entity, Some(body.collider()))?;
set_or_remove_component(server, entity, Some(scale))?;
// Don't add Agent or ItemDrops to players
if !is_player {
set_or_remove_component(server, entity, agent)?;
set_or_remove_component(server, entity, loot.to_items().map(comp::ItemDrops))?;
}
},
NpcData::Waypoint(_) => {
return Err(TransformEntityError::UnexpectedNpcWaypoint);
},
NpcData::Teleporter(_, _) => {
return Err(TransformEntityError::UnexpectedNpcTeleporter);
},
}
Ok(())
}

View File

@ -11,16 +11,13 @@ use specs::{
WriteExpect,
};
pub use group_manip::update_map_markers;
pub(crate) use trade::cancel_trades_for;
use self::{
entity_creation::{
handle_create_item_drop, handle_create_npc, handle_create_object, handle_create_ship,
handle_create_teleporter, handle_create_waypoint, handle_initialize_character,
handle_initialize_spectator, handle_loaded_character_data, handle_shockwave, handle_shoot,
},
entity_manipulation::handle_delete,
entity_manipulation::{handle_delete, handle_transform},
interaction::handle_tame_pet,
mounting::{handle_mount, handle_mount_volume, handle_unmount},
player::{
@ -40,6 +37,14 @@ mod mounting;
mod player;
mod trade;
pub(crate) mod shared {
pub(crate) use super::{
entity_manipulation::{transform_entity, TransformEntityError},
group_manip::update_map_markers,
trade::cancel_trades_for,
};
}
pub trait ServerEvent: Send + Sync + 'static {
type SystemData<'a>: specs::SystemData<'a>;
@ -165,6 +170,7 @@ impl Server {
));
});
self.handle_serial_events(handle_possess);
self.handle_serial_events(handle_transform);
self.handle_serial_events(|this, ev: CommandEvent| {
this.process_command(ev.0, ev.1, ev.2);
});

View File

@ -65,7 +65,7 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity, skip_persisten
// Cancel trades here since we don't use `delete_entity_recorded` and we
// remove `Uid` below.
super::cancel_trades_for(state, entity);
super::trade::cancel_trades_for(state, entity);
let maybe_group = state.read_component_copied::<group::Group>(entity);
let maybe_admin = state.delete_component::<comp::Admin>(entity);
@ -248,7 +248,7 @@ pub fn handle_client_disconnect(
// temporarily unable to log in during this period to avoid
// the race condition of their login fetching their old data
// and overwriting the data saved here.
fn persist_entity(state: &mut State, entity: EcsEntity) -> EcsEntity {
pub(super) fn persist_entity(state: &mut State, entity: EcsEntity) -> EcsEntity {
if let (
Some(presence),
Some(skill_set),

View File

@ -1,4 +1,4 @@
use crate::{client::Client, events::update_map_markers};
use crate::{client::Client, events::shared::update_map_markers};
use common::{
comp::{
self, anchor::Anchor, group::GroupManager, Agent, Alignment, Behavior, BehaviorCapability,

View File

@ -2,7 +2,7 @@ use crate::{
automod::AutoMod,
chat::ChatExporter,
client::Client,
events::{self, update_map_markers},
events::{self, shared::update_map_markers},
persistence::PersistedComponents,
pet::restore_pet,
presence::RepositionOnChunkLoad,
@ -1212,7 +1212,7 @@ impl StateExt for State {
}
// Cancel extant trades
events::cancel_trades_for(self, entity);
events::shared::cancel_trades_for(self, entity);
// NOTE: We expect that these 3 components are never removed from an entity (nor
// mutated) (at least not without updating the relevant mappings)!