diff --git a/CHANGELOG.md b/CHANGELOG.md index 52af5b74..235d1392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- Removed `Mapper` and `MapNetworkEntities` in favor of `EntityMapper` and `MapEntities` introduced in Bevy 0.13.0 +- Changed conditional systems to follow the new pattern, avoid returning a closure. + ## [0.22.0] - 2024-02-17 ### Changed diff --git a/Cargo.toml b/Cargo.toml index fe4bef00..c4cca038 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,10 +19,8 @@ license = "MIT OR Apache-2.0" include = ["/benches", "/src", "/tests", "/LICENSE*"] [dependencies] -bevy_renet = "0.0.10" -bevy = { version = "0.12.1", default-features = false, features = [ - "bevy_scene", -] } +bevy_renet = "0.0.11" +bevy = { version = "0.13", default-features = false, features = ["bevy_scene"] } bincode = "1.3" serde = "1.0" varint-rs = "2.2" @@ -36,7 +34,7 @@ spin_sleep = "1.1" anyhow = "1.0" clap = { version = "4.1", features = ["derive"] } ron = "0.8" -bevy = { version = "0.12", default-features = false, features = [ +bevy = { version = "0.13", default-features = false, features = [ "bevy_asset", "bevy_core_pipeline", "bevy_render", diff --git a/deny.toml b/deny.toml index 3e75a331..d18c7e77 100644 --- a/deny.toml +++ b/deny.toml @@ -11,36 +11,27 @@ allow-osi-fsf-free = "either" multiple-versions = "deny" wildcards = "allow" skip = [ - { name = "async-channel", version = "1.9" }, - { name = "async-lock", version = "2.8" }, { name = "bitflags", version = "2.0" }, { name = "event-listener", version = "<5.0" }, { name = "event-listener-strategy", version = "0.5" }, - { name = "fastrand", version = "1.9" }, - { name = "foreign-types", version = "0.3" }, - { name = "foreign-types-shared", version = "0.1" }, - { name = "futures-lite", version = "1.13" }, - { name = "hashbrown", version = "0.12" }, - { name = "indexmap", version = "1.0" }, - { name = "libloading", version = "0.7" }, - { name = "num_enum", version = "0.6" }, - { name = "num_enum_derive", version = "0.5" }, + { name = "objc-sys", version = "0.2.0-beta.2" }, + { name = "objc2", version = "0.3.0-beta.3.patch-leaks.3" }, + { name = "objc2-encode", version = "2.0.0-pre.2" }, { name = "redox_syscall", version = "0.3" }, { name = "regex-automata", version = "0.1" }, { name = "regex-syntax", version = "0.6" }, - { name = "regex-syntax", version = "0.7" }, { name = "syn", version = "1.0" }, - { name = "toml_edit", version = "0.19" }, { name = "tracing-log", version = "0.1" }, - { name = "windows-sys", version = "0.45" }, - { name = "windows-targets", version = "0.42" }, - { name = "windows_aarch64_gnullvm", version = "0.42" }, - { name = "windows_aarch64_msvc", version = "0.42" }, - { name = "windows_i686_gnu", version = "0.42" }, - { name = "windows_i686_msvc", version = "0.42" }, - { name = "windows_x86_64_gnu", version = "0.42" }, - { name = "windows_x86_64_gnullvm", version = "0.42" }, - { name = "windows_x86_64_msvc", version = "0.42" }, + { name = "windows" }, + { name = "windows-sys" }, + { name = "windows-targets" }, + { name = "windows_aarch64_gnullvm" }, + { name = "windows_aarch64_msvc" }, + { name = "windows_i686_gnu" }, + { name = "windows_i686_msvc" }, + { name = "windows_x86_64_gnu" }, + { name = "windows_x86_64_gnullvm" }, + { name = "windows_x86_64_msvc" }, ] [sources] diff --git a/examples/simple_box.rs b/examples/simple_box.rs index 0ed48500..fcaab974 100644 --- a/examples/simple_box.rs +++ b/examples/simple_box.rs @@ -43,8 +43,8 @@ impl Plugin for SimpleBoxPlugin { .add_systems( Update, ( - Self::movement_system.run_if(has_authority()), // Runs only on the server or a single player. - Self::server_event_system.run_if(resource_exists::()), // Runs only on the server. + Self::movement_system.run_if(has_authority), // Runs only on the server or a single player. + Self::server_event_system.run_if(resource_exists::), // Runs only on the server. (Self::draw_boxes_system, Self::input_system), ), ); @@ -174,18 +174,18 @@ impl SimpleBoxPlugin { } /// Reads player inputs and sends [`MoveCommandEvents`] - fn input_system(mut move_events: EventWriter, input: Res>) { + fn input_system(mut move_events: EventWriter, input: Res>) { let mut direction = Vec2::ZERO; - if input.pressed(KeyCode::Right) { + if input.pressed(KeyCode::ArrowRight) { direction.x += 1.0; } - if input.pressed(KeyCode::Left) { + if input.pressed(KeyCode::ArrowLeft) { direction.x -= 1.0; } - if input.pressed(KeyCode::Up) { + if input.pressed(KeyCode::ArrowUp) { direction.y += 1.0; } - if input.pressed(KeyCode::Down) { + if input.pressed(KeyCode::ArrowDown) { direction.y -= 1.0; } if direction != Vec2::ZERO { diff --git a/examples/tic_tac_toe.rs b/examples/tic_tac_toe.rs index ecb3c70d..9798f1f0 100644 --- a/examples/tic_tac_toe.rs +++ b/examples/tic_tac_toe.rs @@ -45,7 +45,7 @@ struct TicTacToePlugin; impl Plugin for TicTacToePlugin { fn build(&self, app: &mut App) { - app.add_state::() + app.init_state::() .init_resource::() .init_resource::() .replicate::() @@ -70,18 +70,18 @@ impl Plugin for TicTacToePlugin { .add_systems( Update, ( - Self::connecting_text_system.run_if(resource_added::()), - Self::server_waiting_text_system.run_if(resource_added::()), - Self::server_event_system.run_if(resource_exists::()), + Self::connecting_text_system.run_if(resource_added::), + Self::server_waiting_text_system.run_if(resource_added::), + Self::server_event_system.run_if(resource_exists::), Self::start_game_system - .run_if(client_connected()) - .run_if(any_component_added::()), // Wait until client replicates players before starting the game. + .run_if(client_connected) + .run_if(any_component_added::), // Wait until client replicates players before starting the game. ( - Self::cell_interatction_system.run_if(local_player_turn()), - Self::picking_system.run_if(has_authority()), + Self::cell_interatction_system.run_if(local_player_turn), + Self::picking_system.run_if(has_authority), Self::symbol_init_system, - Self::turn_advance_system.run_if(any_component_added::()), - Self::symbol_turn_text_system.run_if(resource_changed::()), + Self::turn_advance_system.run_if(any_component_added::), + Self::symbol_turn_text_system.run_if(resource_changed::), ) .run_if(in_state(GameState::InGame)), ), @@ -514,22 +514,22 @@ impl TicTacToePlugin { /// Returns `true` if the local player can select cells. fn local_player_turn( -) -> impl FnMut(Res, Option>, Query<(&Player, &Symbol)>) -> bool -{ - |current_turn, client_transport, players| { - let client_id = client_transport - .map(|client| ClientId::from_raw(client.client_id())) - .unwrap_or(SERVER_ID); - - players - .iter() - .any(|(player, &symbol)| player.0 == client_id && symbol == current_turn.0) - } + current_turn: Res, + client_transport: Option>, + players: Query<(&Player, &Symbol)>, +) -> bool { + let client_id = client_transport + .map(|client| client.client_id()) + .unwrap_or(SERVER_ID); + + players + .iter() + .any(|(player, &symbol)| player.0 == client_id && symbol == current_turn.0) } /// A condition for systems to check if any component of type `T` was added to the world. -fn any_component_added() -> impl FnMut(Query<(), Added>) -> bool { - |components| !components.is_empty() +fn any_component_added(components: Query<(), Added>) -> bool { + !components.is_empty() } const PORT: u16 = 5000; diff --git a/src/client.rs b/src/client.rs index 6596534c..52a40036 100644 --- a/src/client.rs +++ b/src/client.rs @@ -3,12 +3,12 @@ pub mod diagnostics; use std::io::Cursor; -use bevy::{prelude::*, utils::EntityHashMap}; +use bevy::{ecs::entity::EntityHashMap, prelude::*}; use bevy_renet::{ client_connected, client_just_connected, client_just_disconnected, renet::{Bytes, RenetClient}, transport::NetcodeClientPlugin, - RenetClientPlugin, + RenetClientPlugin, RenetReceive, RenetSend, }; use bincode::{DefaultOptions, Options}; use varint_rs::VarintReader; @@ -32,30 +32,24 @@ impl Plugin for ClientPlugin { .configure_sets( PreUpdate, ClientSet::ResetEvents - .after(NetcodeClientPlugin::update_system) + .after(RenetReceive) .before(ClientSet::Receive) - .run_if(client_just_connected()), - ) - .configure_sets( - PreUpdate, - ClientSet::Receive.after(NetcodeClientPlugin::update_system), + .run_if(client_just_connected), ) + .configure_sets(PreUpdate, ClientSet::Receive.after(RenetReceive)) .configure_sets( PreUpdate, ClientSet::Reset - .after(NetcodeClientPlugin::update_system) - .run_if(client_just_disconnected()), - ) - .configure_sets( - PostUpdate, - ClientSet::Send.before(NetcodeClientPlugin::send_packets), + .after(RenetReceive) + .run_if(client_just_disconnected), ) + .configure_sets(PostUpdate, ClientSet::Send.before(RenetSend)) .add_systems( PreUpdate, Self::replication_receiving_system .map(Result::unwrap) .in_set(ClientSet::Receive) - .run_if(client_connected()), + .run_if(client_connected), ) .add_systems(PreUpdate, Self::reset_system.in_set(ClientSet::Reset)); } @@ -453,14 +447,15 @@ fn apply_update_components( /// Deserializes `entity` from compressed index and generation. /// -/// For details see [`ReplicationBuffer::write_entity`](crate::server::replication_message::replication_buffer::write_entity). +/// For details see +/// [`ReplicationBuffer::write_entity`](crate::server::replication_message::replication_buffer::write_entity). fn deserialize_entity(cursor: &mut Cursor<&[u8]>) -> bincode::Result { let flagged_index: u64 = cursor.read_u64_varint()?; let has_generation = (flagged_index & 1) > 0; let generation = if has_generation { - cursor.read_u32_varint()? + cursor.read_u32_varint()? + 1 } else { - 0u32 + 1u32 }; let bits = (generation as u64) << 32 | (flagged_index >> 1); @@ -514,7 +509,7 @@ pub enum ClientSet { /// /// If [`ClientSet::Reset`] is disabled, then this needs to be cleaned up manually. #[derive(Default, Deref, DerefMut, Resource)] -pub struct ServerEntityTicks(EntityHashMap); +pub struct ServerEntityTicks(EntityHashMap); /// All cached buffered updates, used by the replicon client to align replication updates with initialization /// messages. diff --git a/src/client/client_mapper.rs b/src/client/client_mapper.rs index 258a109a..2b7efdfb 100644 --- a/src/client/client_mapper.rs +++ b/src/client/client_mapper.rs @@ -1,17 +1,14 @@ -use bevy::{ - prelude::*, - utils::{hashbrown::hash_map::Entry, EntityHashMap}, -}; +use bevy::{ecs::entity::EntityHashMap, prelude::*, utils::hashbrown::hash_map::Entry}; -use crate::replicon_core::replication_rules::{Mapper, Replication}; +use crate::replicon_core::replication_rules::Replication; /// Maps server entities into client entities inside components. /// /// Spawns new client entity if a mapping doesn't exists. pub struct ClientMapper<'a> { world: &'a mut World, - server_to_client: &'a mut EntityHashMap, - client_to_server: &'a mut EntityHashMap, + server_to_client: &'a mut EntityHashMap, + client_to_server: &'a mut EntityHashMap, } impl<'a> ClientMapper<'a> { @@ -25,8 +22,8 @@ impl<'a> ClientMapper<'a> { } } -impl Mapper for ClientMapper<'_> { - fn map(&mut self, entity: Entity) -> Entity { +impl EntityMapper for ClientMapper<'_> { + fn map_entity(&mut self, entity: Entity) -> Entity { *self.server_to_client.entry(entity).or_insert_with(|| { let client_entity = self.world.spawn(Replication).id(); self.client_to_server.insert(client_entity, entity); @@ -41,8 +38,8 @@ impl Mapper for ClientMapper<'_> { /// via [`Self::remove_by_client`] or [`Self::clear`]. #[derive(Default, Resource)] pub struct ServerEntityMap { - server_to_client: EntityHashMap, - client_to_server: EntityHashMap, + server_to_client: EntityHashMap, + client_to_server: EntityHashMap, } impl ServerEntityMap { @@ -110,12 +107,12 @@ impl ServerEntityMap { } #[inline] - pub fn to_client(&self) -> &EntityHashMap { + pub fn to_client(&self) -> &EntityHashMap { &self.server_to_client } #[inline] - pub fn to_server(&self) -> &EntityHashMap { + pub fn to_server(&self) -> &EntityHashMap { &self.client_to_server } diff --git a/src/client/diagnostics.rs b/src/client/diagnostics.rs index 025d9ef0..68f16def 100644 --- a/src/client/diagnostics.rs +++ b/src/client/diagnostics.rs @@ -1,4 +1,4 @@ -use bevy::diagnostic::DiagnosticId; +use bevy::diagnostic::DiagnosticPath; use bevy::{ diagnostic::{Diagnostic, Diagnostics, RegisterDiagnostic}, prelude::*, @@ -37,95 +37,95 @@ impl Plugin for ClientDiagnosticsPlugin { Self::diagnostic_system.run_if(on_timer(Duration::from_secs(1))), ) .init_resource::() - .register_diagnostic(Diagnostic::new( - Self::ENTITY_CHANGES, - "entities changed per second", - Self::DIAGNOSTIC_HISTORY_LEN, - )) - .register_diagnostic(Diagnostic::new( - Self::COMPONENT_CHANGES, - "components changed per second", - Self::DIAGNOSTIC_HISTORY_LEN, - )) - .register_diagnostic(Diagnostic::new( - Self::MAPPINGS, - "mappings added per second", - Self::DIAGNOSTIC_HISTORY_LEN, - )) - .register_diagnostic(Diagnostic::new( - Self::DESPAWNS, - "despawns per second", - Self::DIAGNOSTIC_HISTORY_LEN, - )) - .register_diagnostic(Diagnostic::new( - Self::PACKETS, - "packets per second", - Self::DIAGNOSTIC_HISTORY_LEN, - )) - .register_diagnostic(Diagnostic::new( - Self::BYTES, - "bytes per second", - Self::DIAGNOSTIC_HISTORY_LEN, - )); + .register_diagnostic( + Diagnostic::new(Self::ENTITY_CHANGES) + .with_suffix("entities changed per second") + .with_max_history_length(Self::DIAGNOSTIC_HISTORY_LEN), + ) + .register_diagnostic( + Diagnostic::new(Self::COMPONENT_CHANGES) + .with_suffix("components changed per second") + .with_max_history_length(Self::DIAGNOSTIC_HISTORY_LEN), + ) + .register_diagnostic( + Diagnostic::new(Self::MAPPINGS) + .with_suffix("mappings added per second") + .with_max_history_length(Self::DIAGNOSTIC_HISTORY_LEN), + ) + .register_diagnostic( + Diagnostic::new(Self::DESPAWNS) + .with_suffix("despawns per second") + .with_max_history_length(Self::DIAGNOSTIC_HISTORY_LEN), + ) + .register_diagnostic( + Diagnostic::new(Self::PACKETS) + .with_suffix("packets per second") + .with_max_history_length(Self::DIAGNOSTIC_HISTORY_LEN), + ) + .register_diagnostic( + Diagnostic::new(Self::BYTES) + .with_suffix("bytes per second") + .with_max_history_length(Self::DIAGNOSTIC_HISTORY_LEN), + ); } } impl ClientDiagnosticsPlugin { /// How many entities modified per second by replication. - pub const ENTITY_CHANGES: DiagnosticId = - DiagnosticId::from_u128(87359945710349305342211647237348); + pub const ENTITY_CHANGES: DiagnosticPath = + DiagnosticPath::const_new("replication.client.entity_changes"); /// How many components modified per second by replication. - pub const COMPONENT_CHANGES: DiagnosticId = - DiagnosticId::from_u128(36575095059706152186806005753628); + pub const COMPONENT_CHANGES: DiagnosticPath = + DiagnosticPath::const_new("replication.client.component_changes"); /// How many client-mappings added per second by replication. - pub const MAPPINGS: DiagnosticId = DiagnosticId::from_u128(61564098061172206743744706749187); + pub const MAPPINGS: DiagnosticPath = DiagnosticPath::const_new("replication.client.mappings"); /// How many despawns per second from replication. - pub const DESPAWNS: DiagnosticId = DiagnosticId::from_u128(11043323327351349675112378115868); + pub const DESPAWNS: DiagnosticPath = DiagnosticPath::const_new("replication.client.despawns"); /// How many replication packets processed per second. - pub const PACKETS: DiagnosticId = DiagnosticId::from_u128(40094818756895929689855772983865); + pub const PACKETS: DiagnosticPath = DiagnosticPath::const_new("replication.client.packets"); /// How many bytes of replication packets payloads per second. - pub const BYTES: DiagnosticId = DiagnosticId::from_u128(87998088176776397493423835383418); + pub const BYTES: DiagnosticPath = DiagnosticPath::const_new("replication.client.bytes"); /// Max diagnostic history length. pub const DIAGNOSTIC_HISTORY_LEN: usize = 60; fn diagnostic_system(mut stats: ResMut, mut diagnostics: Diagnostics) { - diagnostics.add_measurement(Self::ENTITY_CHANGES, || { + diagnostics.add_measurement(&Self::ENTITY_CHANGES, || { if stats.packets == 0 { 0_f64 } else { stats.entities_changed as f64 / stats.packets as f64 } }); - diagnostics.add_measurement(Self::COMPONENT_CHANGES, || { + diagnostics.add_measurement(&Self::COMPONENT_CHANGES, || { if stats.packets == 0 { 0_f64 } else { stats.components_changed as f64 / stats.packets as f64 } }); - diagnostics.add_measurement(Self::MAPPINGS, || { + diagnostics.add_measurement(&Self::MAPPINGS, || { if stats.packets == 0 { 0_f64 } else { stats.mappings as f64 / stats.packets as f64 } }); - diagnostics.add_measurement(Self::DESPAWNS, || { + diagnostics.add_measurement(&Self::DESPAWNS, || { if stats.packets == 0 { 0_f64 } else { stats.despawns as f64 / stats.packets as f64 } }); - diagnostics.add_measurement(Self::BYTES, || { + diagnostics.add_measurement(&Self::BYTES, || { if stats.packets == 0 { 0_f64 } else { stats.bytes as f64 / stats.packets as f64 } }); - diagnostics.add_measurement(Self::PACKETS, || stats.packets as f64); + diagnostics.add_measurement(&Self::PACKETS, || stats.packets as f64); *stats = ClientStats::default(); } } diff --git a/src/lib.rs b/src/lib.rs index c7c9c350..35779176 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -109,18 +109,18 @@ struct DummyComponent; If your component contains an entity then it cannot be deserialized as is because entity IDs are different on server and client. The client should do the mapping. Therefore, to replicate such components properly, they need implement -[`MapNetworkEntities`] and registered using [`AppReplicationExt::replicate_mapped()`]: +Bevy's `MapEntities` trait and registered using [`AppReplicationExt::replicate_mapped()`]: ``` -# use bevy::prelude::*; +# use bevy::{prelude::*, ecs::entity::{EntityMapper, MapEntities}}; # use bevy_replicon::prelude::*; # use serde::{Deserialize, Serialize}; #[derive(Component, Deserialize, Serialize)] struct MappedComponent(Entity); -impl MapNetworkEntities for MappedComponent { - fn map_entities(&mut self, mapper: &mut T) { - self.0 = mapper.map(self.0); +impl MapEntities for MappedComponent { + fn map_entities(&mut self, mapper: &mut T) { + self.0 = mapper.map_entity(self.0); } } ``` @@ -224,8 +224,8 @@ fn player_init_system( commands.entity(entity).insert(( GlobalTransform::default(), VisibilityBundle::default(), - meshes.add(Mesh::from(shape::Capsule::default())), - materials.add(Color::AZURE.into()), + meshes.add(Mesh::from(Capsule3d::default())), + materials.add(Color::AZURE), )); } } @@ -284,7 +284,7 @@ app.add_client_event::(EventType::Ordered) .add_systems(Update, event_sending_system); fn event_sending_system(mut dummy_events: EventWriter) { - dummy_events.send_default() + dummy_events.send_default(); } fn event_receiving_system(mut dummy_events: EventReader>) { @@ -299,10 +299,10 @@ struct DummyEvent; Just like components, if an event contains an entity, then the client should map it before sending it to the server. -To do this, use [`ClientEventAppExt::add_mapped_client_event()`] and implement [`MapNetworkEntities`]: +To do this, use [`ClientEventAppExt::add_mapped_client_event()`] and implement Bevy's `MapEntities`: ``` -# use bevy::prelude::*; +# use bevy::{prelude::*, ecs::entity::MapEntities}; # use bevy_replicon::prelude::*; # use serde::{Deserialize, Serialize}; # let mut app = App::new(); @@ -312,9 +312,9 @@ app.add_mapped_client_event::(EventType::Ordered); #[derive(Debug, Deserialize, Event, Serialize, Clone)] struct MappedEvent(Entity); -impl MapNetworkEntities for MappedEvent { - fn map_entities(&mut self, mapper: &mut T) { - self.0 = mapper.map(self.0); +impl MapEntities for MappedEvent { + fn map_entities(&mut self, entity_mapper: &mut T) { + self.0 = entity_mapper.map_entity(self.0); } } ``` @@ -402,7 +402,7 @@ app.add_plugins(( )) .add_systems( Update, - visibility_system.run_if(resource_exists::()), + visibility_system.run_if(resource_exists::), ); /// Disables the visibility of other players' entities that are further away than the visible distance. @@ -481,9 +481,7 @@ pub mod prelude { renet::{RenetClient, RenetServer}, replicon_core::{ dont_replicate::{CommandDontReplicateExt, EntityDontReplicateExt}, - replication_rules::{ - AppReplicationExt, MapNetworkEntities, Mapper, Replication, ReplicationRules, - }, + replication_rules::{AppReplicationExt, Replication, ReplicationRules}, replicon_tick::RepliconTick, NetworkChannels, ReplicationChannel, RepliconCorePlugin, }, diff --git a/src/network_event.rs b/src/network_event.rs index b6ad24b9..e5e67a78 100644 --- a/src/network_event.rs +++ b/src/network_event.rs @@ -3,11 +3,9 @@ pub mod server_event; use std::{marker::PhantomData, time::Duration}; -use bevy::{prelude::*, utils::EntityHashMap}; +use bevy::{ecs::entity::EntityHashMap, prelude::*}; use bevy_renet::renet::SendType; -use crate::replicon_core::replication_rules::Mapper; - /// Holds a server's channel ID for `T`. #[derive(Resource)] pub struct ServerEventChannel { @@ -100,10 +98,10 @@ impl From for SendType { /// Maps server entities into client entities inside events. /// /// Panics if a mapping doesn't exists. -pub struct EventMapper<'a>(pub &'a EntityHashMap); +pub struct EventMapper<'a>(pub &'a EntityHashMap); -impl Mapper for EventMapper<'_> { - fn map(&mut self, entity: Entity) -> Entity { +impl EntityMapper for EventMapper<'_> { + fn map_entity(&mut self, entity: Entity) -> Entity { *self .0 .get(&entity) diff --git a/src/network_event/client_event.rs b/src/network_event/client_event.rs index 5ebf1057..0c747207 100644 --- a/src/network_event/client_event.rs +++ b/src/network_event/client_event.rs @@ -1,4 +1,7 @@ -use bevy::{ecs::event::Event, prelude::*}; +use bevy::{ + ecs::{entity::MapEntities, event::Event}, + prelude::*, +}; use bevy_renet::{ client_connected, renet::{ClientId, RenetClient, RenetServer, SendType}, @@ -10,7 +13,7 @@ use super::ClientEventChannel; use crate::{ client::{client_mapper::ServerEntityMap, ClientSet}, network_event::EventMapper, - replicon_core::{replication_rules::MapNetworkEntities, NetworkChannels}, + replicon_core::NetworkChannels, server::{has_authority, ServerSet, SERVER_ID}, }; @@ -22,10 +25,10 @@ pub trait ClientEventAppExt { send_type: impl Into, ) -> &mut Self; - /// Same as [`Self::add_client_event`], but additionally maps client entities to server before sending. - fn add_mapped_client_event< - T: Event + Serialize + DeserializeOwned + MapNetworkEntities + Clone, - >( + /// Same as [`Self::add_client_event`], but additionally maps client entities to server inside the event before sending. + /// + /// Always use it for events that contain entities. + fn add_mapped_client_event( &mut self, send_type: impl Into, ) -> &mut Self; @@ -116,9 +119,7 @@ impl ClientEventAppExt for App { self.add_client_event_with::(send_type, sending_system::, receiving_system::) } - fn add_mapped_client_event< - T: Event + Serialize + DeserializeOwned + MapNetworkEntities + Clone, - >( + fn add_mapped_client_event( &mut self, send_type: impl Into, ) -> &mut Self { @@ -149,14 +150,14 @@ impl ClientEventAppExt for App { reset_system::.in_set(ClientSet::ResetEvents), receiving_system .in_set(ServerSet::Receive) - .run_if(resource_exists::()), + .run_if(resource_exists::), ), ) .add_systems( PostUpdate, ( - sending_system.run_if(client_connected()), - local_resending_system::.run_if(has_authority()), + sending_system.run_if(client_connected), + local_resending_system::.run_if(has_authority), ) .chain() .in_set(ClientSet::Send), @@ -197,7 +198,7 @@ fn sending_system( } } -fn mapping_and_sending_system( +fn mapping_and_sending_system( mut events: EventReader, mut client: ResMut, entity_map: Res, @@ -223,7 +224,7 @@ fn local_resending_system( client_events.send(FromClient { client_id: SERVER_ID, event, - }) + }); } } diff --git a/src/network_event/server_event.rs b/src/network_event/server_event.rs index 1bcf5b61..0124e8f3 100644 --- a/src/network_event/server_event.rs +++ b/src/network_event/server_event.rs @@ -1,6 +1,9 @@ use std::io::Cursor; -use bevy::{ecs::event::Event, prelude::*}; +use bevy::{ + ecs::{entity::MapEntities, event::Event}, + prelude::*, +}; use bevy_renet::{ client_connected, renet::{Bytes, ClientId, RenetClient, RenetServer, SendType}, @@ -14,9 +17,7 @@ use crate::{ client::{client_mapper::ServerEntityMap, ClientSet}, network_event::EventMapper, prelude::{ClientPlugin, ServerPlugin}, - replicon_core::{ - replication_rules::MapNetworkEntities, replicon_tick::RepliconTick, NetworkChannels, - }, + replicon_core::{replicon_tick::RepliconTick, NetworkChannels}, server::{ client_cache::{ClientCache, ClientState}, has_authority, ServerSet, SERVER_ID, @@ -31,8 +32,10 @@ pub trait ServerEventAppExt { send_type: impl Into, ) -> &mut Self; - /// Same as [`Self::add_server_event`], but additionally maps server entities to client after receiving. - fn add_mapped_server_event( + /// Same as [`Self::add_server_event`], but additionally maps server entities to client inside the event after receiving. + /// + /// Always use it for events that contain entities. + fn add_mapped_server_event( &mut self, send_type: impl Into, ) -> &mut Self; @@ -132,7 +135,7 @@ impl ServerEventAppExt for App { self.add_server_event_with::(send_type, sending_system::, receiving_system::) } - fn add_mapped_server_event( + fn add_mapped_server_event( &mut self, send_type: impl Into, ) -> &mut Self { @@ -166,14 +169,14 @@ impl ServerEventAppExt for App { .chain() .after(ClientPlugin::replication_receiving_system) .in_set(ClientSet::Receive) - .run_if(client_connected()), + .run_if(client_connected), ), ) .add_systems( PostUpdate, ( - sending_system.run_if(resource_exists::()), - local_resending_system::.run_if(has_authority()), + sending_system.run_if(resource_exists::), + local_resending_system::.run_if(has_authority), ) .chain() .after(ServerPlugin::replication_sending_system) @@ -216,7 +219,7 @@ fn receiving_system( } } -fn receiving_and_mapping_system( +fn receiving_and_mapping_system( mut server_events: EventWriter, mut client: ResMut, mut event_queue: ResMut>, diff --git a/src/parent_sync.rs b/src/parent_sync.rs index 8e84a0c0..cdaa5558 100644 --- a/src/parent_sync.rs +++ b/src/parent_sync.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; use crate::{ client::ClientSet, - replicon_core::replication_rules::{AppReplicationExt, MapNetworkEntities, Mapper}, + replicon_core::replication_rules::AppReplicationExt, server::{has_authority, ServerSet}, }; @@ -27,7 +27,7 @@ impl Plugin for ParentSyncPlugin { .add_systems( PostUpdate, (Self::update_system, Self::removal_system) - .run_if(has_authority()) + .run_if(has_authority) .before(ServerSet::Send), ); } @@ -80,17 +80,9 @@ impl ParentSyncPlugin { pub struct ParentSync(Option); impl MapEntities for ParentSync { - fn map_entities(&mut self, entity_mapper: &mut EntityMapper) { + fn map_entities(&mut self, entity_mapper: &mut T) { if let Some(ref mut entity) = self.0 { - *entity = entity_mapper.get_or_reserve(*entity); - } - } -} - -impl MapNetworkEntities for ParentSync { - fn map_entities(&mut self, mapper: &mut T) { - if let Some(ref mut entity) = self.0 { - *entity = mapper.map(*entity); + *entity = entity_mapper.map_entity(*entity); } } } diff --git a/src/replicon_core/dont_replicate.rs b/src/replicon_core/dont_replicate.rs index a3ecb43b..807f14f6 100644 --- a/src/replicon_core/dont_replicate.rs +++ b/src/replicon_core/dont_replicate.rs @@ -29,7 +29,7 @@ pub trait CommandDontReplicateExt { fn dont_replicate(&mut self) -> &mut Self; } -impl CommandDontReplicateExt for EntityCommands<'_, '_, '_> { +impl CommandDontReplicateExt for EntityCommands<'_> { fn dont_replicate(&mut self) -> &mut Self { self.add(|mut entity: EntityWorldMut| { entity.dont_replicate::(); diff --git a/src/replicon_core/replication_rules.rs b/src/replicon_core/replication_rules.rs index 11ae467b..763d788c 100644 --- a/src/replicon_core/replication_rules.rs +++ b/src/replicon_core/replication_rules.rs @@ -1,6 +1,11 @@ use std::io::Cursor; -use bevy::{ecs::component::ComponentId, prelude::*, ptr::Ptr, utils::HashMap}; +use bevy::{ + ecs::{component::ComponentId, entity::MapEntities}, + prelude::*, + ptr::Ptr, + utils::HashMap, +}; use bincode::{DefaultOptions, Options}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -15,12 +20,12 @@ pub trait AppReplicationExt { where C: Component + Serialize + DeserializeOwned; - /// Same as [`Self::replicate`], but maps component entities using [`MapNetworkEntities`] trait. + /// Same as [`Self::replicate`], but additionally maps server entities to client inside the component after receiving. /// - /// Always use it for components that contains entities. + /// Always use it for components that contain entities. fn replicate_mapped(&mut self) -> &mut Self where - C: Component + Serialize + DeserializeOwned + MapNetworkEntities; + C: Component + Serialize + DeserializeOwned + MapEntities; /// Same as [`Self::replicate`], but uses the specified functions for serialization, deserialization, and removal. fn replicate_with( @@ -47,7 +52,7 @@ impl AppReplicationExt for App { fn replicate_mapped(&mut self) -> &mut Self where - C: Component + Serialize + DeserializeOwned + MapNetworkEntities, + C: Component + Serialize + DeserializeOwned + MapEntities, { self.replicate_with::( serialize_component::, @@ -193,18 +198,6 @@ pub struct Replication; #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub(crate) struct ReplicationId(usize); -/// Maps entities inside component. -/// -/// The same as [`bevy::ecs::entity::MapEntities`], but never creates new entities on mapping error. -pub trait MapNetworkEntities { - /// Maps stored entities using specified map. - fn map_entities(&mut self, mapper: &mut T); -} - -pub trait Mapper { - fn map(&mut self, entity: Entity) -> Entity; -} - /// Default serialization function. pub fn serialize_component( component: Ptr, @@ -229,7 +222,7 @@ pub fn deserialize_component( } /// Like [`deserialize_component`], but also maps entities before insertion. -pub fn deserialize_mapped_component( +pub fn deserialize_mapped_component( entity: &mut EntityWorldMut, entity_map: &mut ServerEntityMap, cursor: &mut Cursor<&[u8]>, diff --git a/src/scene.rs b/src/scene.rs index 9c14fd2b..6603030a 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -62,7 +62,7 @@ pub fn replicate_into(scene: &mut DynamicScene, world: &World) { let entities_offset = scene.entities.len(); for entity in archetype.entities() { scene.entities.push(DynamicEntity { - entity: entity.entity(), + entity: entity.id(), components: Vec::new(), }); } @@ -90,7 +90,7 @@ pub fn replicate_into(scene: &mut DynamicScene, world: &World) { for (index, archetype_entity) in archetype.entities().iter().enumerate() { let component = reflect_component - .reflect(world.entity(archetype_entity.entity())) + .reflect(world.entity(archetype_entity.id())) .unwrap_or_else(|| panic!("entity should have {type_name}")); scene.entities[entities_offset + index] diff --git a/src/server.rs b/src/server.rs index 962c8ca5..be9453ba 100644 --- a/src/server.rs +++ b/src/server.rs @@ -81,7 +81,7 @@ impl Plugin for ServerPlugin { ) .chain() .in_set(ServerSet::Receive) - .run_if(resource_exists::()), + .run_if(resource_exists::), ) .add_systems( PostUpdate, @@ -89,8 +89,8 @@ impl Plugin for ServerPlugin { Self::replication_sending_system .map(Result::unwrap) .in_set(ServerSet::Send) - .run_if(resource_exists::()) - .run_if(resource_changed::()), + .run_if(resource_exists::) + .run_if(resource_changed::), Self::reset_system.run_if(resource_removed::()), ), ); @@ -102,7 +102,7 @@ impl Plugin for ServerPlugin { PostUpdate, Self::increment_tick .before(Self::replication_sending_system) - .run_if(resource_exists::()) + .run_if(resource_exists::) .run_if(on_timer(tick_time)), ); } @@ -111,7 +111,7 @@ impl Plugin for ServerPlugin { PostUpdate, Self::increment_tick .before(Self::replication_sending_system) - .run_if(resource_exists::()), + .run_if(resource_exists::), ); } TickPolicy::Manual => (), @@ -296,11 +296,9 @@ fn collect_changes( for entity in archetype.entities() { for (init_message, update_message, client_state) in messages.iter_mut_with_state() { - init_message.start_entity_data(entity.entity()); - update_message.start_entity_data(entity.entity()); - client_state - .visibility_mut() - .cache_visibility(entity.entity()); + init_message.start_entity_data(entity.id()); + update_message.start_entity_data(entity.id()); + client_state.visibility_mut().cache_visibility(entity.id()); } // SAFETY: all replicated archetypes have marker component with table storage. @@ -349,7 +347,7 @@ fn collect_changes( )?; } else { let tick = client_state - .get_change_limit(entity.entity()) + .get_change_limit(entity.id()) .expect("entity should be present after adding component"); if ticks.is_changed(tick, change_tick.this_run()) { update_message.write_component( @@ -374,7 +372,7 @@ fn collect_changes( // If there is any insertion or we must initialize, include all updates into init message // and bump the last acknowledged tick to keep entity updates atomic. init_message.take_entity_data(update_message)?; - client_state.set_change_limit(entity.entity(), change_tick.this_run()); + client_state.set_change_limit(entity.id(), change_tick.this_run()); } else { update_message.end_entity_data()?; } @@ -413,8 +411,8 @@ unsafe fn get_component_unchecked<'w>( } StorageType::SparseSet => { let sparse_set = sparse_sets.get(component_id).unwrap_unchecked(); - let component = sparse_set.get(entity.entity()).unwrap_unchecked(); - let ticks = sparse_set.get_ticks(entity.entity()).unwrap_unchecked(); + let component = sparse_set.get(entity.id()).unwrap_unchecked(); + let ticks = sparse_set.get_ticks(entity.id()).unwrap_unchecked(); (component, ticks) } @@ -479,8 +477,8 @@ fn collect_removals( } /// Condition that returns `true` for server or in singleplayer and `false` for client. -pub fn has_authority() -> impl FnMut(Option>) -> bool + Clone { - move |client| client.is_none() +pub fn has_authority(client: Option>) -> bool { + client.is_none() } /// Set with replication and event systems related to server. diff --git a/src/server/client_cache.rs b/src/server/client_cache.rs index 03a813db..8ff1b83e 100644 --- a/src/server/client_cache.rs +++ b/src/server/client_cache.rs @@ -3,9 +3,9 @@ pub mod client_visibility; use std::mem; use bevy::{ - ecs::component::Tick, + ecs::{component::Tick, entity::EntityHashMap}, prelude::*, - utils::{Duration, EntityHashMap, HashMap}, + utils::{Duration, HashMap}, }; use bevy_renet::renet::ClientId; @@ -146,7 +146,7 @@ pub struct ClientState { id: ClientId, /// Lowest tick for use in change detection for each entity. - ticks: EntityHashMap, + ticks: EntityHashMap, /// Entity visibility settings. visibility: ClientVisibility, diff --git a/src/server/client_cache/client_visibility.rs b/src/server/client_cache/client_visibility.rs index ebd6bd5f..84f8c233 100644 --- a/src/server/client_cache/client_visibility.rs +++ b/src/server/client_cache/client_visibility.rs @@ -1,6 +1,7 @@ use bevy::{ + ecs::entity::{EntityHashMap, EntityHashSet}, prelude::*, - utils::{hashbrown::hash_map::Entry, EntityHashMap, EntityHashSet}, + utils::hashbrown::hash_map::Entry, }; use super::VisibilityPolicy; @@ -281,32 +282,32 @@ enum VisibilityFilter { Blacklist { /// All blacklisted entities and an indicator of whether they are in the queue for deletion /// at the end of this tick. - list: EntityHashMap, + list: EntityHashMap, /// All entities that were removed from the list in this tick. /// /// Visibility of these entities has been lost. - added: EntityHashSet, + added: EntityHashSet, /// All entities that were added to the list in this tick. /// /// Visibility of these entities has been gained. - removed: EntityHashSet, + removed: EntityHashSet, }, Whitelist { /// All whitelisted entities and an indicator of whether they were added to the list in /// this tick. - list: EntityHashMap, + list: EntityHashMap, /// All entities that were added to the list in this tick. /// /// Visibility of these entities has been gained. - added: EntityHashSet, + added: EntityHashSet, /// All entities that were removed from the list in this tick. /// /// Visibility of these entities has been lost. - removed: EntityHashSet, + removed: EntityHashSet, }, } diff --git a/src/server/despawn_buffer.rs b/src/server/despawn_buffer.rs index f9ab7a8f..4b48122b 100644 --- a/src/server/despawn_buffer.rs +++ b/src/server/despawn_buffer.rs @@ -16,7 +16,7 @@ impl Plugin for DespawnBufferPlugin { Self::detection_system .before(ServerPlugin::replication_sending_system) .in_set(ServerSet::Send) - .run_if(resource_exists::()), + .run_if(resource_exists::), ); } } diff --git a/src/server/removal_buffer.rs b/src/server/removal_buffer.rs index 40d80194..4f05a1dc 100644 --- a/src/server/removal_buffer.rs +++ b/src/server/removal_buffer.rs @@ -1,11 +1,12 @@ use bevy::{ ecs::{ component::ComponentId, + entity::EntityHashMap, event::ManualEventReader, removal_detection::{RemovedComponentEntity, RemovedComponentEvents}, }, prelude::*, - utils::{EntityHashMap, HashMap}, + utils::HashMap, }; use bevy_renet::renet::RenetServer; @@ -28,7 +29,7 @@ impl Plugin for RemovalBufferPlugin { .after(DespawnBufferPlugin::detection_system) .before(ServerPlugin::replication_sending_system) .in_set(ServerSet::Send) - .run_if(resource_exists::()), + .run_if(resource_exists::), ); } } @@ -61,7 +62,7 @@ impl RemovalBufferPlugin { #[derive(Default, Resource)] pub(crate) struct RemovalBuffer { /// Component removals grouped by entity. - removals: EntityHashMap>, + removals: EntityHashMap>, /// [`Vec`]'s from entity removals. /// diff --git a/src/server/replication_messages.rs b/src/server/replication_messages.rs index 5e8161a1..a79a34ec 100644 --- a/src/server/replication_messages.rs +++ b/src/server/replication_messages.rs @@ -675,15 +675,17 @@ fn can_pack(header_size: usize, base: usize, add: usize) -> bool { /// Serializes `entity` by writing its index and generation as separate varints. /// /// The index is first prepended with a bit flag to indicate if the generation -/// is serialized or not (it is not serialized if equal to zero). +/// is serialized or not. It is not serialized if <= 1; note that generations are `NonZeroU32` +/// and a value of zero is used in `Option` to signify `None`, so generation 1 is the first +/// generation. fn serialize_entity(cursor: &mut Cursor>, entity: Entity) -> bincode::Result<()> { let mut flagged_index = (entity.index() as u64) << 1; - let flag = entity.generation() > 0; + let flag = entity.generation() > 1; flagged_index |= flag as u64; cursor.write_u64_varint(flagged_index)?; if flag { - cursor.write_u32_varint(entity.generation())?; + cursor.write_u32_varint(entity.generation() - 1)?; } Ok(()) diff --git a/tests/client_event.rs b/tests/client_event.rs index cc53fdb6..d2b209f9 100644 --- a/tests/client_event.rs +++ b/tests/client_event.rs @@ -1,6 +1,10 @@ mod connect; -use bevy::{ecs::event::Events, prelude::*, time::TimePlugin}; +use bevy::{ + ecs::{entity::MapEntities, event::Events}, + prelude::*, + time::TimePlugin, +}; use bevy_replicon::prelude::*; use serde::{Deserialize, Serialize}; @@ -111,8 +115,8 @@ struct DummyEvent; #[derive(Deserialize, Event, Serialize, Clone)] struct MappedEvent(Entity); -impl MapNetworkEntities for MappedEvent { - fn map_entities(&mut self, mapper: &mut T) { - self.0 = mapper.map(self.0); +impl MapEntities for MappedEvent { + fn map_entities(&mut self, entity_mapper: &mut M) { + self.0 = entity_mapper.map_entity(self.0); } } diff --git a/tests/init_replication.rs b/tests/init_replication.rs index cea4f6b7..7d431caa 100644 --- a/tests/init_replication.rs +++ b/tests/init_replication.rs @@ -1,10 +1,7 @@ mod connect; -use bevy::prelude::*; -use bevy_replicon::{ - prelude::*, - renet::{transport::NetcodeClientTransport, ClientId}, -}; +use bevy::{ecs::entity::MapEntities, prelude::*}; +use bevy_replicon::{prelude::*, renet::transport::NetcodeClientTransport}; use serde::{Deserialize, Serialize}; #[test] @@ -164,7 +161,7 @@ fn client_spawn() { let server_entity = server_app.world.spawn((Replication, TableComponent)).id(); let client_transport = client_app.world.resource::(); - let client_id = ClientId::from_raw(client_transport.client_id()); + let client_id = client_transport.client_id(); let mut entity_map = server_app.world.resource_mut::(); entity_map.insert( @@ -683,9 +680,9 @@ fn removal_with_despawn() { #[derive(Component, Deserialize, Serialize)] struct MappedComponent(Entity); -impl MapNetworkEntities for MappedComponent { - fn map_entities(&mut self, mapper: &mut T) { - self.0 = mapper.map(self.0); +impl MapEntities for MappedComponent { + fn map_entities(&mut self, entity_mapper: &mut M) { + self.0 = entity_mapper.map_entity(self.0); } } diff --git a/tests/other.rs b/tests/other.rs index f0cf86a3..40858bf5 100644 --- a/tests/other.rs +++ b/tests/other.rs @@ -1,7 +1,7 @@ mod connect; use bevy::prelude::*; -use bevy_renet::renet::{transport::NetcodeClientTransport, ClientId}; +use bevy_renet::renet::transport::NetcodeClientTransport; use bevy_replicon::{prelude::*, scene}; use serde::{Deserialize, Serialize}; @@ -61,7 +61,7 @@ fn diagnostics() { let server_entity = server_app.world.spawn((Replication, DummyComponent)).id(); let client_transport = client_app.world.resource::(); - let client_id = ClientId::from_raw(client_transport.client_id()); + let client_id = client_transport.client_id(); let mut entity_map = server_app.world.resource_mut::(); entity_map.insert( client_id, diff --git a/tests/server_event.rs b/tests/server_event.rs index 4500d564..3813ae82 100644 --- a/tests/server_event.rs +++ b/tests/server_event.rs @@ -1,6 +1,10 @@ mod connect; -use bevy::{ecs::event::Events, prelude::*, time::TimePlugin}; +use bevy::{ + ecs::{entity::MapEntities, event::Events}, + prelude::*, + time::TimePlugin, +}; use bevy_replicon::{ prelude::*, renet::{transport::NetcodeClientTransport, ClientId}, @@ -47,7 +51,7 @@ fn sending_receiving() { connect::single_client(&mut server_app, &mut client_app); let client_transport = client_app.world.resource::(); - let client_id = ClientId::from_raw(client_transport.client_id()); + let client_id = client_transport.client_id(); for (mode, events_count) in [ (SendMode::Broadcast, 1), @@ -264,8 +268,8 @@ struct DummyEvent; #[derive(Deserialize, Event, Serialize)] struct MappedEvent(Entity); -impl MapNetworkEntities for MappedEvent { - fn map_entities(&mut self, mapper: &mut T) { - self.0 = mapper.map(self.0); +impl MapEntities for MappedEvent { + fn map_entities(&mut self, entity_mapper: &mut T) { + self.0 = entity_mapper.map_entity(self.0); } } diff --git a/tests/update_replication.rs b/tests/update_replication.rs index da853721..2ab924a4 100644 --- a/tests/update_replication.rs +++ b/tests/update_replication.rs @@ -3,10 +3,7 @@ mod connect; use bevy::{prelude::*, utils::Duration}; use bevy_replicon::{ prelude::*, - renet::{ - transport::{NetcodeClientTransport, NetcodeServerTransport}, - ClientId, - }, + renet::transport::{NetcodeClientTransport, NetcodeServerTransport}, }; use serde::{Deserialize, Serialize}; @@ -327,7 +324,7 @@ fn cleanup() { // Take and drop received message to make systems miss it. let client_transport = client_app.world.resource::(); - let client_id = ClientId::from_raw(client_transport.client_id()); + let client_id = client_transport.client_id(); let delta = server_app.world.resource::