aboutsummaryrefslogtreecommitdiff
path: root/src/world/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/world/mod.rs')
-rw-r--r--src/world/mod.rs196
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()
+ }
+}