mod bookkeeping; mod clients; mod components; mod config; mod entities; mod events; mod generators; mod interests; mod messages; mod physics; mod profiler; mod registry; mod search; mod stats; mod systems; mod types; mod utils; mod voxels; use actix::Recipient; use hashbrown::HashMap; use log::{info, warn}; use nanoid::nanoid; use profiler::Profiler; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use specs::{ shred::{Fetch, FetchMut, Resource}, Builder, Component, DispatcherBuilder, Entity, EntityBuilder, Join, ReadStorage, SystemData, World as ECSWorld, WorldExt, WriteStorage, }; use std::path::PathBuf; use std::{env, sync::Arc}; use std::{ fs::{self, File}, time::Duration, }; use crate::{ encode_message, protocols::Peer, server::{Message, MessageType}, EncodedMessage, EntityOperation, EntityProtocol, PeerProtocol, Vec2, Vec3, }; use super::common::ClientFilter; pub use bookkeeping::*; pub use clients::*; pub use components::*; pub use config::*; pub use entities::*; pub use events::*; pub use generators::*; pub use interests::*; pub use messages::*; pub use physics::*; pub use registry::*; pub use search::*; pub use stats::*; pub use systems::*; pub use types::*; pub use utils::*; pub use voxels::*; pub type Transports = HashMap>; /// The default client metadata parser, parses PositionComp and DirectionComp, and updates RigidBodyComp. pub fn default_client_parser(world: &mut World, metadata: &str, client_ent: Entity) { let metadata: PeerUpdate = match serde_json::from_str(metadata) { Ok(metadata) => metadata, Err(e) => { warn!("Could not parse peer update: {}", metadata); return; } }; if let Some(position) = metadata.position { { let mut positions = world.write_component::(); if let Some(p) = positions.get_mut(client_ent) { p.0.set(position.0, position.1, position.2); } } { let mut bodies = world.write_component::(); if let Some(b) = bodies.get_mut(client_ent) { b.0.set_position(position.0, position.1, position.2); } } } if let Some(direction) = metadata.direction { let mut directions = world.write_component::(); if let Some(d) = directions.get_mut(client_ent) { d.0.set(direction.0, direction.1, direction.2); } } } #[derive(Debug, Serialize, Deserialize)] pub struct PeerUpdate { position: Option>, direction: Option>, } /// A voxelize world. pub struct World { /// ID of the world, generated from `nanoid!()`. pub id: String, /// Name of the world, used for connection. pub name: String, /// Whether if the world has started. pub started: bool, /// Whether if the world is preloading. pub preloading: bool, /// The progress of preloading. pub preload_progress: f32, /// Entity component system world. ecs: ECSWorld, /// The modifier of the ECS dispatcher. dispatcher: Arc DispatcherBuilder<'static, 'static>>, /// The modifier of any new client. client_modifier: Option>, /// The metadata parser for clients. client_parser: Arc, /// The handler for `Method`s. method_handles: HashMap>, /// The handlers for `Event`s. event_handles: HashMap>, /// The handler for `Transport`s. transport_handle: Option>, /// The handler for commands. command_handle: Option>, /// A map to spawn and create entities. entity_loaders: HashMap EntityBuilder>>, } fn dispatcher() -> DispatcherBuilder<'static, 'static> { DispatcherBuilder::new() .with(UpdateStatsSystem, "update-stats", &[]) .with(EntitiesMetaSystem, "entities-meta", &[]) .with(PeersMetaSystem, "peers-meta", &[]) .with(CurrentChunkSystem, "current-chunk", &[]) .with(ChunkUpdatingSystem, "chunk-updating", &["current-chunk"]) .with(ChunkRequestsSystem, "chunk-requests", &["current-chunk"]) .with( ChunkGeneratingSystem, "chunk-generation", &["chunk-requests"], ) .with(ChunkSendingSystem, "chunk-sending", &["chunk-generation"]) .with(ChunkSavingSystem, "chunk-saving", &["chunk-generation"]) .with(PhysicsSystem, "physics", &["current-chunk", "update-stats"]) .with(DataSavingSystem, "entities-saving", &["entities-meta"]) .with( EntitiesSendingSystem, "entities-sending", &["entities-meta"], ) .with(PeersSendingSystem, "peers-sending", &["peers-meta"]) .with( BroadcastSystem, "broadcast", &["chunk-sending", "entities-sending", "peers-sending"], ) .with( CleanupSystem, "cleanup", &["entities-sending", "peers-sending"], ) .with(EventsSystem, "events", &["broadcast"]) .with(EntityObserveSystem, "entity-observe", &[]) .with(PathFindingSystem, "path-finding", &["entity-observe"]) .with(TargetMetadataSystem, "target-meta", &[]) .with(PathMetadataSystem, "path-meta", &[]) .with(EntityTreeSystem, "entity-tree", &[]) .with(WalkTowardsSystem, "walk-towards", &["path-finding"]) } #[derive(Serialize, Deserialize)] struct OnLoadRequest { center: Vec2, direction: Vec2, chunks: Vec>, } #[derive(Serialize, Deserialize)] struct OnUnloadRequest { chunks: Vec>, } #[derive(Serialize, Deserialize)] struct OnEventRequest { name: String, payload: Value, } #[derive(Serialize, Deserialize)] struct BuiltInSetTimeMethodPayload { time: f32, } #[derive(Serialize, Deserialize)] struct BuiltInUpdateBlockEntityMethodPayload { id: String, json: String, } impl World { /// Create a new voxelize world. pub fn new(name: &str, config: &WorldConfig) -> Self { let id = nanoid!(); if config.saving { let folder = PathBuf::from(&config.save_dir); // If folder doesn't exist, create it. if !folder.exists() { if let Err(e) = fs::create_dir_all(&folder) { panic!("Could not create world folder: {}", e); } } } let mut ecs = ECSWorld::new(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.insert(name.to_owned()); ecs.insert(config.clone()); ecs.insert(Chunks::new(config)); ecs.insert(EntitiesSaver::new(config.saving, &config.save_dir)); ecs.insert(Stats::new( config.saving, &config.save_dir, config.default_time, )); ecs.insert(Search::new()); ecs.insert(Mesher::new()); ecs.insert(Pipeline::new()); ecs.insert(Clients::new()); ecs.insert(MessageQueue::new()); ecs.insert(Physics::new()); ecs.insert(Events::new()); ecs.insert(Transports::new()); ecs.insert(ChunkInterests::new()); ecs.insert(Bookkeeping::new()); ecs.insert(KdTree::new()); ecs.insert(EncodedMessageQueue::new()); ecs.insert(Profiler::new(Duration::from_secs_f64(0.001))); let mut world = Self { id, name: name.to_owned(), started: false, preloading: false, preload_progress: 0.0, ecs, dispatcher: Arc::new(dispatcher), method_handles: HashMap::default(), event_handles: HashMap::default(), entity_loaders: HashMap::default(), client_parser: Arc::new(default_client_parser), client_modifier: None, transport_handle: None, command_handle: None, }; world.set_method_handle("vox-builtin:get-stats", |world, client_id, _| { let stats_json = world.stats().get_stats(); world.write_resource::().push(( Message::new(&MessageType::Stats) .json(&serde_json::to_string(&stats_json).unwrap()) .build(), ClientFilter::Direct(client_id.to_owned()), )); }); world.set_method_handle("vox-builtin:set-time", |world, _, payload| { let payload: BuiltInSetTimeMethodPayload = serde_json::from_str(payload) .expect("Could not parse vox-builtin:set-time payload."); let time_per_day = world.config().time_per_day as f32; world.stats_mut().set_time(payload.time % time_per_day); }); world.set_method_handle("vox-builtin:update-block-entity", |world, _, payload| { let payload: BuiltInUpdateBlockEntityMethodPayload = serde_json::from_str(payload) .expect("Could not parse vox-builtin:update-block-entity payload."); let entities = world.ecs().entities(); let ids = world.ecs().read_storage::(); let mut to_update = vec![]; for (entity, id_comp) in (&entities, &ids).join() { if id_comp.0 == payload.id { to_update.push(entity); break; } } drop((entities, ids)); for entity in to_update { world .ecs_mut() .write_storage::() .insert(entity, JsonComp::new(&payload.json)) .expect("Failed to write JsonComp"); } }); world } /// Get a reference to the ECS world.. pub fn ecs(&self) -> &ECSWorld { &self.ecs } /// Get a mutable reference to the ECS world. pub fn ecs_mut(&mut self) -> &mut ECSWorld { &mut self.ecs } /// Insert a component into an entity. pub fn add(&mut self, e: Entity, c: T) { let mut storage: WriteStorage = SystemData::fetch(self.ecs()); storage.insert(e, c).unwrap(); } /// Remove a component type from an entity. pub fn remove(&mut self, e: Entity) { let mut storage: WriteStorage = SystemData::fetch(self.ecs()); storage.remove(e); } /// Read an ECS resource generically. pub fn read_resource(&self) -> Fetch { self.ecs.read_resource::() } /// Write an ECS resource generically. pub fn write_resource(&mut self) -> FetchMut { self.ecs.write_resource::() } /// Read an ECS component storage. pub fn read_component(&self) -> ReadStorage { self.ecs.read_component::() } /// Write an ECS component storage. pub fn write_component(&mut self) -> WriteStorage { self.ecs.write_component::() } /// Get an ID from IDComp from an entity pub fn get_id(&self, entity: Entity) -> String { if let Some(id) = self.read_component::().get(entity) { id.0.to_owned() } else { panic!("Something went wrong! An entity does not have an `IDComp` attached!"); } } /// Add a transport address to this world. pub(crate) fn add_transport(&mut self, id: &str, addr: &Recipient) { let init_message = self.generate_init_message(id); self.send(addr, &init_message); self.write_resource::() .insert(id.to_owned(), addr.to_owned()); } /// Remove a transport address from this world. pub(crate) fn remove_transport(&mut self, id: &str) { self.write_resource::().remove(id); } /// Add a client to the world by an ID and an Actix actor address. pub(crate) fn add_client( &mut self, id: &str, username: &str, addr: &Recipient, ) { let init_message = self.generate_init_message(id); let body = RigidBody::new(&AABB::new().scale_x(0.8).scale_y(1.8).scale_z(0.8).build()).build(); let interactor = self.physics_mut().register(&body); let ent = self .ecs .create_entity() .with(ClientFlag::default()) .with(IDComp::new(id)) .with(NameComp::new(username)) .with(AddrComp::new(addr)) .with(ChunkRequestsComp::default()) .with(CurrentChunkComp::default()) .with(MetadataComp::default()) .with(PositionComp::default()) .with(DirectionComp::default()) .with(RigidBodyComp::new(&body)) .with(InteractorComp::new(&interactor)) .with(CollisionsComp::new()) .build(); if let Some(modifier) = self.client_modifier.to_owned() { modifier(self, ent); } self.clients_mut().insert( id.to_owned(), Client { id: id.to_owned(), entity: ent, username: username.to_owned(), addr: addr.to_owned(), }, ); self.send(addr, &init_message); let join_message = Message::new(&MessageType::Join).text(id).build(); self.broadcast(join_message, ClientFilter::All); info!("Client at {} joined the server to world: {}", id, self.name); } /// Remove a client from the world by endpoint. pub(crate) fn remove_client(&mut self, id: &str) { let removed = self.clients_mut().remove(id); if let Some(client) = removed { { // Remove rapier physics body. let interactors = self.ecs.read_storage::(); let interactor = interactors .get(client.entity) .unwrap_or_else(|| { panic!( "Something went wrong with deleting this client: {}", client.id ) }) .to_owned(); let body_handle = interactor.body_handle().to_owned(); let collider_handle = interactor.collider_handle().to_owned(); drop(interactors); { let mut physics = self.physics_mut(); physics.unregister(&body_handle, &collider_handle); } { let mut interactors = self.ecs.write_storage::(); interactors.remove(client.entity); } { let mut collisions = self.ecs.write_storage::(); collisions.remove(client.entity); } { let mut rigid_bodies = self.ecs.write_storage::(); rigid_bodies.remove(client.entity); } { let mut clients = self.ecs.write_storage::(); clients.remove(client.entity); } let entities = self.ecs.entities(); entities.delete(client.entity).unwrap_or_else(|_| { panic!( "Something went wrong with deleting this client: {}", client.id ) }); } self.ecs.maintain(); let leave_message = Message::new(&MessageType::Leave).text(&client.id).build(); self.broadcast(leave_message, ClientFilter::All); info!("Client at {} left the world: {}", id, self.name); } } pub fn set_dispatcher DispatcherBuilder<'static, 'static> + 'static>( &mut self, dispatch: F, ) { self.dispatcher = Arc::new(dispatch); } pub fn set_client_modifier(&mut self, modifier: F) { self.client_modifier = Some(Arc::new(modifier)); } pub fn set_client_parser(&mut self, parser: F) { self.client_parser = Arc::new(parser); } pub fn set_method_handle( &mut self, method: &str, handle: F, ) { self.method_handles .insert(method.to_lowercase(), Arc::new(handle)); } pub fn set_event_handle( &mut self, event: &str, handle: F, ) { self.event_handles .insert(event.to_lowercase(), Arc::new(handle)); } pub fn set_transport_handle(&mut self, handle: F) { self.transport_handle = Some(Arc::new(handle)); } pub fn set_command_handle(&mut self, handle: F) { self.command_handle = Some(Arc::new(handle)); } pub fn set_entity_loader EntityBuilder + 'static>( &mut self, etype: &str, loader: F, ) { self.entity_loaders .insert(etype.to_lowercase(), Arc::new(loader)); } /// Handler for protobuf requests from clients. pub(crate) fn on_request(&mut self, client_id: &str, data: Message) { let msg_type = MessageType::from_i32(data.r#type).unwrap(); match msg_type { MessageType::Peer => self.on_peer(client_id, data), MessageType::Load => self.on_load(client_id, data), MessageType::Unload => self.on_unload(client_id, data), MessageType::Method => self.on_method(client_id, data), MessageType::Chat => self.on_chat(client_id, data), MessageType::Update => self.on_update(client_id, data), MessageType::Event => self.on_event(client_id, data), MessageType::Transport => { if self.transport_handle.is_none() { warn!("Transport calls are being called, but no transport handlers set!"); } else { let handle = self.transport_handle.as_ref().unwrap().to_owned(); handle( self, serde_json::from_str(&data.json) .expect("Something went wrong with the transport JSON value."), ); } } _ => { info!("Received message of unknown type: {:?}", msg_type); } } } /// Broadcast a protobuf message to a subset or all of the clients in the world. pub fn broadcast(&mut self, data: Message, filter: ClientFilter) { self.write_resource::().push((data, filter)); } /// Send a direct message to an endpoint pub fn send(&self, addr: &Recipient, data: &Message) { addr.do_send(EncodedMessage(encode_message(data))); } /// Access to the world's config. pub fn config(&self) -> Fetch { self.read_resource::() } /// Access all clients in the ECS world. pub fn clients(&self) -> Fetch { self.read_resource::() } /// Access a mutable clients map in the ECS world. pub fn clients_mut(&mut self) -> FetchMut { self.write_resource::() } /// Access the registry in the ECS world. pub fn registry(&self) -> Fetch { self.read_resource::() } /// Access chunks management in the ECS world. pub fn chunks(&self) -> Fetch { self.read_resource::() } /// Access a mutable chunk manager in the ECS world. pub fn chunks_mut(&mut self) -> FetchMut { self.write_resource::() } /// Access physics management in the ECS world. pub fn physics(&self) -> Fetch { self.read_resource::() } /// Access a mutable physics manager in the ECS world. pub fn physics_mut(&mut self) -> FetchMut { self.write_resource::() } /// Access the chunk interests manager in the ECS world. pub fn chunk_interest(&self) -> Fetch { self.read_resource::() } /// Access the mutable chunk interest manager in the ECS world. pub fn chunk_interest_mut(&mut self) -> FetchMut { self.write_resource::() } /// Access the event queue in the ECS world. pub fn events(&self) -> Fetch { self.read_resource::() } /// Access the mutable events queue in the ECS world. pub fn events_mut(&mut self) -> FetchMut { self.write_resource::() } /// Access the search tree in the ECS world. pub fn search(&self) -> Fetch { self.read_resource::() } /// Access the mutable search tree in the ECS world. pub fn search_mut(&mut self) -> FetchMut { self.write_resource::() } /// Access the stats manager in the ECS world. pub fn stats(&self) -> Fetch { self.read_resource::() } /// Access the mutable stats manager in the ECS world. pub fn stats_mut(&mut self) -> FetchMut { self.write_resource::() } /// Access pipeline management in the ECS world. pub fn pipeline(&self) -> Fetch { self.read_resource::() } /// Access a mutable pipeline management in the ECS world. pub fn pipeline_mut(&mut self) -> FetchMut { self.write_resource::() } /// Access the mesher in the ECS world. pub fn mesher(&self) -> Fetch { self.read_resource::() } /// Access a mutable mesher in the ECS world. pub fn mesher_mut(&mut self) -> FetchMut { self.write_resource::() } /// Create a basic entity ready to be added more. pub fn create_base_entity(&mut self, id: &str, etype: &str) -> EntityBuilder { self.ecs_mut() .create_entity() .with(IDComp::new(id)) .with(EntityFlag::default()) .with(CurrentChunkComp::default()) } /// Create a basic entity ready to be added more. pub fn create_entity(&mut self, id: &str, etype: &str) -> EntityBuilder { self.create_base_entity(id, etype) .with(ETypeComp::new(etype, false)) .with(MetadataComp::new()) .with(CollisionsComp::new()) } /// Create a basic entity ready to be added more. pub fn create_block_entity(&mut self, id: &str, etype: &str) -> EntityBuilder { self.create_base_entity(id, etype) .with(ETypeComp::new(etype, true)) } /// Spawn an entity of type at a location. pub fn spawn_entity_at(&mut self, etype: &str, position: &Vec3) -> Option { if !self.entity_loaders.contains_key(&etype.to_lowercase()) { warn!("Tried to spawn unknown entity type: {}", etype); return None; } let loader = self .entity_loaders .get(&etype.to_lowercase()) .unwrap() .to_owned(); let ent = loader(self, MetadataComp::default()).build(); self.populate_entity(ent, &nanoid!(), etype, MetadataComp::default()); set_position(self.ecs_mut(), ent, position.0, position.1, position.2); Some(ent) } pub fn revive_entity( &mut self, id: &str, etype: &str, metadata: MetadataComp, ) -> Option { if etype.starts_with("block::") { // info!("Reviving block entity with metadata: {:?}", metadata); let entity = self .create_block_entity(id, etype) .with( metadata .get::("json") .unwrap_or(JsonComp::new("{}")), ) .with(metadata.get::("voxel").unwrap_or_default()) .with(metadata) .build(); return Some(entity); } if !self.entity_loaders.contains_key(&etype.to_lowercase()) { warn!("Tried to revive unknown entity type: {}", etype); return None; } let loader = self .entity_loaders .get(&etype.to_lowercase()) .unwrap() .to_owned(); let ent = loader(self, metadata.to_owned()).build(); self.populate_entity(ent, id, etype, metadata); Some(ent) } pub fn populate_entity(&mut self, ent: Entity, id: &str, etype: &str, metadata: MetadataComp) { self.ecs_mut() .write_storage::() .insert(ent, IDComp::new(id)) .expect("Failed to insert ID component"); let (entity_type, is_block) = if etype.starts_with("block::") { (etype, true) } else { (etype, false) }; self.ecs_mut() .write_storage::() .insert(ent, ETypeComp::new(entity_type, is_block)) .expect("Failed to insert entity type component"); self.ecs_mut() .write_storage::() .insert(ent, EntityFlag::default()) .expect("Failed to insert entity flag"); self.ecs_mut() .write_storage::() .insert(ent, CurrentChunkComp::default()) .expect("Failed to insert current chunk component"); self.ecs_mut() .write_storage::() .insert(ent, CollisionsComp::new()) .expect("Failed to insert collisions component"); self.ecs_mut() .write_storage::() .insert(ent, metadata) .expect("Failed to insert metadata component"); } /// Check if this world is empty. pub fn is_empty(&self) -> bool { self.read_resource::().is_empty() } /// Prepare to start. pub(crate) fn prepare(&mut self) { // Merge consecutive chunk stages that don't require spaces together. self.pipeline_mut().merge_stages(); self.load_entities(); for (position, body) in ( &self.ecs.read_storage::(), &mut self.ecs.write_storage::(), ) .join() { body.0 .set_position(position.0 .0, position.0 .1, position.0 .2); } } /// Preload the chunks in the world. pub(crate) fn preload(&mut self) { let radius = self.config().preload_radius as i32; { for x in -radius..=radius { for z in -radius..=radius { let coords = Vec2(x, z); let neighbors = self.chunks().light_traversed_chunks(&coords); neighbors.into_iter().for_each(|coords| { let is_within = { let chunks = self.chunks(); chunks.is_within_world(&coords) }; let mut pipeline = self.pipeline_mut(); if is_within { pipeline.add_chunk(&coords, false); } }); } } } self.preloading = true; } /// Tick of the world, run every 16ms. pub(crate) fn tick(&mut self) { if !self.started { self.started = true; } if self.preloading { let light_padding = (self.config().max_light_level as f32 / self.config().chunk_size as f32) .ceil() as usize; let check_radius = (self.config().preload_radius - light_padding) as i32; let mut total = 0; let supposed = (check_radius * 2).pow(2); for x in -check_radius..=check_radius { for z in -check_radius..=check_radius { let chunks = self.chunks(); let coords = Vec2(x, z); if chunks.is_chunk_ready(&coords) { total += 1; } else { if let Some(chunk) = chunks.raw(&coords) { if chunk.status == ChunkStatus::Meshing && !self.mesher().map.contains(&coords) { // Add the chunk back to meshing queue. drop(chunks); self.mesher_mut().add_chunk(&coords, false); } } // drop(chunks); // let is_in_pipeline = self.pipeline().has_chunk(&coords); // let is_in_mesher = self.mesher().map.contains(&coords); // info!( // "Chunk {:?} is not ready. In pipeline: {}, in mesher: {}, status: {:?}", // coords, is_in_pipeline, is_in_mesher, status // ); } } } self.preload_progress = (total as f32 / supposed as f32).min(1.0); if total >= supposed { self.preloading = false; } } if !self.preloading && self.is_empty() { return; } let mut dispatcher = (self.dispatcher)().build(); dispatcher.dispatch(&self.ecs); self.write_resource::().summarize(); self.ecs.maintain(); } /// Handler for `Peer` type messages. fn on_peer(&mut self, client_id: &str, data: Message) { let client_ent = if let Some(client) = self.clients().get(client_id) { client.entity.to_owned() } else { return; }; data.peers.into_iter().for_each(|peer| { let Peer { metadata, username, .. } = peer; { let mut names = self.write_component::(); if let Some(n) = names.get_mut(client_ent) { n.0 = username.to_owned(); } } self.client_parser.clone()(self, &metadata, client_ent); if let Some(client) = self.clients_mut().get_mut(client_id) { client.username = username; } }) } /// Handler for `Load` type messages. fn on_load(&mut self, client_id: &str, data: Message) { let client_ent = if let Some(client) = self.clients().get(client_id) { client.entity.to_owned() } else { return; }; let json: OnLoadRequest = match serde_json::from_str(&data.json) { Ok(json) => json, Err(e) => { warn!("`on_load` error. Could not read JSON string: {}", data.json); return; } }; let chunks = json.chunks; if chunks.is_empty() { return; } { let mut storage = self.write_component::(); let requests = storage.get_mut(client_ent).unwrap(); chunks.iter().for_each(|coords| { requests.add(coords); }); requests.set_center(&json.center); requests.set_direction(&json.direction); requests.sort(); } } /// Handler for `Unload` type messages. fn on_unload(&mut self, client_id: &str, data: Message) { let client_ent = if let Some(client) = self.clients().get(client_id) { client.entity.to_owned() } else { return; }; let json: OnUnloadRequest = match serde_json::from_str(&data.json) { Ok(json) => json, Err(e) => { warn!( "`on_unload` error. Could not read JSON string: {}", data.json ); return; } }; let chunks = json.chunks; if chunks.is_empty() { return; } { let mut storage = self.write_component::(); if let Some(requests) = storage.get_mut(client_ent) { chunks.iter().for_each(|coords| { requests.remove(coords); }); } } { let mut interests = self.chunk_interest_mut(); let mut to_remove = Vec::new(); chunks.iter().for_each(|coords| { interests.remove(client_id, coords); if !interests.has_interests(coords) { to_remove.push(coords); } }); drop(interests); to_remove.into_iter().for_each(|coords| { self.pipeline_mut().remove_chunk(coords); self.mesher_mut().remove_chunk(coords); }) } } /// Handler for `Update` type messages. fn on_update(&mut self, _: &str, data: Message) { let chunk_size = self.config().chunk_size; let mut chunks = self.chunks_mut(); data.updates.into_iter().for_each(|update| { let coords = ChunkUtils::map_voxel_to_chunk(update.vx, update.vy, update.vz, chunk_size); if !chunks.is_within_world(&coords) { return; } chunks.update_voxel(&Vec3(update.vx, update.vy, update.vz), update.voxel); }); } /// Handler for `Method` type messages. fn on_method(&mut self, client_id: &str, data: Message) { if let Some(method) = data.method { if !self .method_handles .contains_key(&method.name.to_lowercase()) { warn!("`Method` type messages received, but no method handler set."); return; } let handle = self.method_handles.get(&method.name).unwrap().to_owned(); handle(self, client_id, &method.payload); } } /// Handler for `Event` type messages. fn on_event(&mut self, client_id: &str, data: Message) { let client_ent = if let Some(client) = self.clients().get(client_id) { client.entity.to_owned() } else { return; }; data.events.into_iter().for_each(|event| { if !self.event_handles.contains_key(&event.name.to_lowercase()) { let curr_chunk = self .read_component::() .get(client_ent) .unwrap() .coords .clone(); self.events_mut().dispatch( Event::new(&event.name) .payload(event.payload) .location(curr_chunk) .build(), ); return; } let handle = self.event_handles.get(&event.name).unwrap().to_owned(); handle(self, client_id, &event.payload); }); } /// Handler for `Chat` type messages. fn on_chat(&mut self, id: &str, data: Message) { if let Some(chat) = data.chat.clone() { let sender = chat.sender; let body = chat.body; info!("{}: {}", sender, body); let command_symbol = self.config().command_symbol.to_owned(); if body.starts_with(&command_symbol) { if let Some(handle) = self.command_handle.to_owned() { handle(self, id, body.strip_prefix(&command_symbol).unwrap()); } else { warn!("Clients are sending commands, but no command handler set."); } } else { self.broadcast(data, ClientFilter::All); } } } /// Load existing entities. fn load_entities(&mut self) { if self.config().saving { // TODO: THIS FEELS HACKY let paths = fs::read_dir(self.read_resource::().folder.clone()).unwrap(); let mut loaded_entities = HashMap::new(); for path in paths { let path = path.unwrap().path(); if let Ok(entity_data) = File::open(&path) { let id = path.file_stem().unwrap().to_str().unwrap().to_owned(); let mut data: HashMap = serde_json::from_reader(entity_data) .unwrap_or_else(|_| panic!("Could not load entity file: {:?}", path)); let etype: String = serde_json::from_value(data.remove("etype").unwrap()) .unwrap_or_else(|_| { panic!("EType filed does not exist on file: {:?}", path) }); let metadata: MetadataComp = serde_json::from_value(data.remove("metadata").unwrap()).unwrap_or_else( |_| panic!("Metadata field does not exist on file: {:?}", path), ); if let Some(ent) = self.revive_entity(&id, &etype, metadata.to_owned()) { loaded_entities.insert(id.to_owned(), (etype, ent, metadata)); } } } if !loaded_entities.is_empty() { let name = self.name.to_owned(); let mut bookkeeping = self.write_resource::(); info!( "World {:?} loaded {} entities from disk.", name, loaded_entities.len() ); bookkeeping.entities = loaded_entities; } } } fn generate_init_message(&self, id: &str) -> Message { let config = (*self.config()).to_owned(); let mut json = HashMap::new(); json.insert("id".to_owned(), json!(id)); json.insert("blocks".to_owned(), json!(self.registry().blocks_by_name)); json.insert("options".to_owned(), json!(config)); json.insert( "stats".to_owned(), json!(self.read_resource::().get_stats()), ); /* ------------------------ Loading other the clients ----------------------- */ let ids = self.read_component::(); let flags = self.read_component::(); let names = self.read_component::(); let metadatas = self.read_component::(); let mut peers = vec![]; for (pid, name, metadata, _) in (&ids, &names, &metadatas, &flags).join() { peers.push(PeerProtocol { id: pid.0.to_owned(), username: name.0.to_owned(), metadata: metadata.to_string(), }) } /* -------------------------- Loading all entities -------------------------- */ let etypes = self.read_component::(); let metadatas = self.read_component::(); let mut entities = vec![]; for (id, etype, metadata) in (&ids, &etypes, &metadatas).join() { if !etype.0.starts_with("block::") && metadata.is_empty() { continue; } let j_str = metadata.to_string(); entities.push(EntityProtocol { // Intentionally not using the `EntityOperation::Create` variant here // because the entity is already technically created. operation: EntityOperation::Update, id: id.0.to_owned(), r#type: etype.0.to_owned(), metadata: Some(j_str), }); } drop(ids); drop(etypes); drop(metadatas); Message::new(&MessageType::Init) .json(&serde_json::to_string(&json).unwrap()) .peers(&peers) .entities(&entities) .build() } }