//! The world contains all items of a session and the ways to manipulate them. //! Generally speaking, manipulation will be done on the server side and then //! the changes are sent to the client. pub mod component; pub mod icon; pub mod room; pub mod wall; pub use component::*; pub use icon::*; pub use room::*; pub use wall::*; use crate::stable_vec::StableVec; use ron::de::from_reader; use ron::ser::{to_string_pretty, PrettyConfig}; use serde::{Deserialize, Serialize}; use std::fs::File; use std::io::{self, Write}; use std::path::Path; /// Main structure that contains all information about a world necessary to argue /// about structures in it. It can be persistified and loaded from disk, as well /// as sent over the network. Everything in it is assigned an id to make it easy /// to argue about a specific item. IDs may only be assigned by the server, not /// the client, since otherwise multiple clients may try to create the same item. #[derive(Debug, Serialize, Deserialize)] pub struct World { rooms: StableVec, walls: StableVec, icons: StableVec, used_ids: StableVec<()>, } impl World { /// Create a new, empty world. pub fn new() -> Self { Self { rooms: StableVec::new(), walls: StableVec::new(), icons: StableVec::new(), used_ids: StableVec::new(), } } /// Remove all items from the world, leaving it empty. pub fn clear(&mut self) { self.rooms.clear(); self.walls.clear(); self.icons.clear(); self.used_ids.clear(); } /// Remove the item with the given id. Returns `false` in case no item with /// such an id existed in the first place. pub fn remove(&mut self, id: usize) -> bool { if self.used_ids.remove(id).is_none() { return false; } if self.rooms.remove(id).is_some() { true } else if self.walls.remove(id).is_some() { true } else if self.icons.remove(id).is_some() { true } else { panic!("used_ids and the actual ids are out of sync") } } /// Create a world from the raw parts of the world. The components must fit, /// meaning no id must exist twice and the used ids must correspond to the /// actually used ids. Use with care. pub fn from_raw_unchecked( rooms: StableVec, walls: StableVec, icons: StableVec, used_ids: StableVec<()>, ) -> Self { Self { rooms, walls, icons, used_ids, } } /// Load the world data from a file. Fails if the file does not exist or /// cannot be correctly parsed. pub fn load_from_file>(path: P) -> io::Result { let file = File::open(&path)?; let data: Self = match from_reader(file) { Ok(data) => data, Err(err) => { return Err(io::Error::new(io::ErrorKind::InvalidData, err)); } }; Ok(data) } /// Write the world data to the file located at `path`. If the file already /// exists, it will be overwritten. If the write fails, an IO-Error is returned. pub fn write_to_file>(&self, path: P) -> io::Result<()> { let mut file = File::create(&path)?; let pretty_conf = PrettyConfig::new() .with_depth_limit(4) .with_decimal_floats(true) .with_separate_tuple_members(true) .with_indentor("\t".to_owned()); let string = match to_string_pretty(&self, pretty_conf) { Ok(string) => string, Err(err) => { return Err(io::Error::new(io::ErrorKind::InvalidInput, err)); } }; file.write_all(&string.as_bytes()) } /// Add a room to the world. Currently, holes are not supported in the polygon, but this might /// change later. pub fn push_room(&mut self, room: Room) -> usize { let id = self.used_ids.insert_anywhere(()); self.rooms .try_insert(id, room) .expect("Id vecs out of sync"); id } /// Add a wall to the world. pub fn push_wall(&mut self, wall: Wall) -> usize { let id = self.used_ids.insert_anywhere(()); self.walls .try_insert(id, wall) .expect("Id vecs out of sync"); id } /// Add an icon to the world. pub fn push_icon(&mut self, icon: Icon) -> usize { let id = self.used_ids.insert_anywhere(()); self.icons .try_insert(id, icon) .expect("Id vecs out of sync"); id } /// Get all icons of the world. pub fn icons(&self) -> &StableVec { &self.icons } /// Get an icon mutably. pub fn get_icon_mut(&mut self, id: usize) -> Option<&mut Icon> { self.icons.get_mut(id) } /// Get all rooms of the world. pub fn rooms(&self) -> &StableVec { &self.rooms } /// Get a room mutably. pub fn get_room_mut(&mut self, id: usize) -> Option<&mut Room> { self.rooms.get_mut(id) } /// Get all walls of the world. pub fn walls(&self) -> &StableVec { &self.walls } /// Get a wall mutably. pub fn get_wall_mut(&mut self, id: usize) -> Option<&mut Wall> { self.walls.get_mut(id) } pub fn get_mut(&mut self, id: usize) -> Option<&mut dyn Component> { if let Some(c) = self.rooms.get_mut(id) { Some(c as &mut dyn Component) } else if let Some(c) = self.walls.get_mut(id) { Some(c as &mut dyn Component) } else if let Some(c) = self.icons.get_mut(id) { Some(c as &mut dyn Component) } else { None } } } impl Default for World { fn default() -> Self { Self::new() } }