use crate::{
world::entity::entity_converters::LocalEntityAndGlobalEntityConverter,
world::sync::{
host_entity_channel::HostEntityChannel, remote_entity_channel::RemoteEntityChannel,
},
BigMapKey, ComponentKind, EntityAuthStatus, EntityCommand, EntityMessage, GlobalEntity,
HostEntity, HostType, LocalEntityMap, OwnedLocalEntity,
};
#[allow(dead_code)]
struct Position {
x: f32,
y: f32,
}
#[allow(dead_code)]
struct Velocity {
x: f32,
y: f32,
}
#[allow(dead_code)]
struct Health {
value: u32,
}
fn component_kind<T: 'static>() -> ComponentKind {
ComponentKind::from(std::any::TypeId::of::<T>())
}
#[test]
fn server_side_migration_complete_flow() {
let mut entity_map = LocalEntityMap::new(HostType::Server);
let mut remote_engine = crate::world::sync::remote_engine::RemoteEngine::new(HostType::Server);
let mut host_engine = crate::world::sync::host_engine::HostEngine::new(HostType::Server);
let global_entity = GlobalEntity::from_u64(10001);
let remote_entity = crate::world::local::local_entity::RemoteEntity::new(42);
let host_entity = HostEntity::new(100);
entity_map.insert_with_remote_entity(global_entity, remote_entity);
let mut remote_channel = RemoteEntityChannel::new(HostType::Client);
let pos_kind = component_kind::<Position>();
let vel_kind = component_kind::<Velocity>();
let health_kind = component_kind::<Health>();
remote_channel.insert_component(pos_kind);
remote_channel.insert_component(vel_kind);
remote_channel.insert_component(health_kind);
remote_channel.insert_component_channel_as_inserted(pos_kind, 1);
remote_channel.insert_component_channel_as_inserted(vel_kind, 2);
remote_channel.insert_component_channel_as_inserted(health_kind, 3);
remote_engine.insert_entity_channel(remote_entity, remote_channel);
let component_kinds = {
let channel = remote_engine.get_world().get(&remote_entity).unwrap();
channel.extract_inserted_component_kinds()
};
assert!(component_kinds.contains(&pos_kind));
assert!(component_kinds.contains(&vel_kind));
assert!(component_kinds.contains(&health_kind));
assert_eq!(component_kinds.len(), 3);
let _old_remote_channel = remote_engine.remove_entity_channel(&remote_entity);
let new_host_channel =
HostEntityChannel::new_with_components(HostType::Server, component_kinds);
host_engine.insert_entity_channel(host_entity, new_host_channel);
entity_map.remove_by_global_entity(&global_entity);
entity_map.insert_with_host_entity(global_entity, host_entity);
let old_entity = OwnedLocalEntity::Remote { id: remote_entity.value(), is_static: false };
let new_entity = OwnedLocalEntity::Host { id: host_entity.value(), is_static: false };
entity_map.install_entity_redirect(old_entity, new_entity);
let redirected = entity_map.apply_entity_redirect(&old_entity);
assert_eq!(redirected, new_entity);
let other_entity = OwnedLocalEntity::Remote { id: 99, is_static: false };
let not_redirected = entity_map.apply_entity_redirect(&other_entity);
assert_eq!(not_redirected, other_entity);
let host_channel = host_engine.get_entity_channel(&host_entity).unwrap();
assert_eq!(host_channel.component_kinds().len(), 3);
assert!(host_channel.component_kinds().contains(&pos_kind));
assert!(host_channel.component_kinds().contains(&vel_kind));
assert!(host_channel.component_kinds().contains(&health_kind));
}
#[test]
fn client_side_migration_with_command_replay() {
let mut host_channel = HostEntityChannel::new(HostType::Client);
let global_entity = GlobalEntity::from_u64(10002);
let pos_kind = component_kind::<Position>();
let vel_kind = component_kind::<Velocity>();
host_channel.send_command(EntityCommand::InsertComponent(global_entity, pos_kind));
host_channel.send_command(EntityCommand::InsertComponent(global_entity, vel_kind));
host_channel.send_command(EntityCommand::Publish(Some(1), global_entity));
host_channel.send_command(EntityCommand::EnableDelegation(Some(2), global_entity));
let commands = host_channel.extract_outgoing_commands();
assert_eq!(commands.len(), 4);
let component_kinds = host_channel.component_kinds().clone();
assert_eq!(component_kinds.len(), 2);
let mut remote_channel = RemoteEntityChannel::new(HostType::Client);
for &comp_kind in &component_kinds {
remote_channel.insert_component(comp_kind);
}
let mut replayed_commands = 0;
for command in commands {
if command.is_valid_for_remote_entity() {
replayed_commands += 1;
}
}
assert!(replayed_commands > 0);
}
#[test]
fn migration_with_buffered_operations() {
let mut remote_channel = RemoteEntityChannel::new(HostType::Client);
let pos_kind = component_kind::<Position>();
let vel_kind = component_kind::<Velocity>();
remote_channel.receive_message(5, EntityMessage::<()>::InsertComponent((), vel_kind));
remote_channel.receive_message(3, EntityMessage::<()>::InsertComponent((), pos_kind));
remote_channel.receive_message(1, EntityMessage::<()>::Spawn(()));
remote_channel.receive_message(4, EntityMessage::<()>::RemoveComponent((), pos_kind));
remote_channel.force_drain_all_buffers();
}
#[test]
fn migration_error_handling() {
let entity_map = LocalEntityMap::new(HostType::Server);
let fake_entity = GlobalEntity::from_u64(999);
let result = entity_map.global_entity_to_remote_entity(&fake_entity);
assert!(result.is_err());
}
#[test]
fn high_frequency_migration_operations() {
let mut entity_map = LocalEntityMap::new(HostType::Server);
let mut redirects = Vec::new();
for i in 0..1000 {
let old_entity = OwnedLocalEntity::Remote { id: i, is_static: false };
let new_entity = OwnedLocalEntity::Host { id: i + 1000, is_static: false };
entity_map.install_entity_redirect(old_entity, new_entity);
redirects.push((old_entity, new_entity));
}
for (old_entity, expected_new_entity) in redirects {
let redirected = entity_map.apply_entity_redirect(&old_entity);
assert_eq!(redirected, expected_new_entity);
}
let non_existent = OwnedLocalEntity::Remote { id: 9999, is_static: false };
let result = entity_map.apply_entity_redirect(&non_existent);
assert_eq!(result, non_existent);
}
#[test]
fn migration_memory_efficiency() {
let mut remote_channel = RemoteEntityChannel::new(HostType::Client);
let pos_kind = component_kind::<Position>();
for i in 1..=100 {
remote_channel.receive_message(i, EntityMessage::<()>::InsertComponent((), pos_kind));
remote_channel.receive_message(i + 100, EntityMessage::<()>::RemoveComponent((), pos_kind));
}
remote_channel.force_drain_all_buffers();
let component_kinds = remote_channel.extract_inserted_component_kinds();
assert!(component_kinds.len() <= 1); }
#[test]
fn concurrent_migration_scenarios() {
let mut remote_channel = RemoteEntityChannel::new(HostType::Client);
let pos_kind = component_kind::<Position>();
let vel_kind = component_kind::<Velocity>();
remote_channel.receive_message(1, EntityMessage::<()>::Spawn(()));
remote_channel.receive_message(2, EntityMessage::<()>::InsertComponent((), pos_kind));
remote_channel.receive_message(3, EntityMessage::<()>::InsertComponent((), vel_kind));
remote_channel.receive_message(4, EntityMessage::<()>::RemoveComponent((), pos_kind));
remote_channel.force_drain_all_buffers();
let component_kinds = remote_channel.extract_inserted_component_kinds();
assert!(component_kinds.len() <= 2); }
#[test]
fn migration_with_authority_changes() {
let mut host_channel = HostEntityChannel::new(HostType::Client);
let global_entity = GlobalEntity::from_u64(10003);
host_channel.send_command(EntityCommand::Publish(Some(1), global_entity));
host_channel.send_command(EntityCommand::EnableDelegation(Some(2), global_entity));
host_channel.send_command(EntityCommand::SetAuthority(
Some(3),
global_entity,
EntityAuthStatus::Granted,
));
let commands = host_channel.extract_outgoing_commands();
assert_eq!(commands.len(), 3);
let mut has_publish = false;
let mut has_delegation = false;
let mut has_authority = false;
for command in commands {
match command {
EntityCommand::Publish(_, _) => has_publish = true,
EntityCommand::EnableDelegation(_, _) => has_delegation = true,
EntityCommand::SetAuthority(_, _, _) => has_authority = true,
_ => {}
}
}
assert!(has_publish);
assert!(has_delegation);
assert!(has_authority);
}