diff options
Diffstat (limited to 'src/world/mod.rs')
| -rw-r--r-- | src/world/mod.rs | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/src/world/mod.rs b/src/world/mod.rs new file mode 100644 index 0000000..9ddd9e9 --- /dev/null +++ b/src/world/mod.rs @@ -0,0 +1,196 @@ +//! 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<Room>, + walls: StableVec<Wall>, + icons: StableVec<Icon>, + 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<Room>, + walls: StableVec<Wall>, + icons: StableVec<Icon>, + 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<P: AsRef<Path>>(path: P) -> io::Result<Self> { + 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<P: AsRef<Path>>(&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<Icon> { + &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<Room> { + &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<Wall> { + &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() + } +} |
