diff --git a/Cargo.lock b/Cargo.lock index c44ddb2..9fe1370 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -550,6 +550,7 @@ version = "0.1.0" dependencies = [ "gl", "imagefmt", + "serde", "thiserror", ] @@ -1529,6 +1530,7 @@ dependencies = [ "glc", "log", "log4rs", + "serde", "serde_yaml", "shred", "shrev", @@ -1553,6 +1555,7 @@ dependencies = [ "hibitset", "log", "rayon", + "serde", "shred", "shrev", "tuple_utils", diff --git a/glc/Cargo.toml b/glc/Cargo.toml index 1957514..d5e74b1 100644 --- a/glc/Cargo.toml +++ b/glc/Cargo.toml @@ -4,7 +4,11 @@ version = "0.1.0" authors = ["Bergmann89 "] edition = "2018" +[features] +default = [ ] + [dependencies] gl = { version = "0.1", features = [ "generate_global" ] } imagefmt = "4.0" +serde = { version = "1.0", optional = true } thiserror = "1.0" diff --git a/glc/src/matrix.rs b/glc/src/matrix.rs index ef71d55..62e9249 100644 --- a/glc/src/matrix.rs +++ b/glc/src/matrix.rs @@ -5,10 +5,16 @@ use std::convert::{AsMut, AsRef}; use std::fmt::{Debug, Formatter, Result as FmtResult}; use std::ops::{Deref, DerefMut, Mul}; +#[cfg(feature = "serde")] +use serde::{ + de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Visitor}, + ser::{Serialize, SerializeStruct, Serializer}, +}; + use super::vector::{Element, Vector2, Vector3, Vector4}; macro_rules! first_ptr { - ($this:ident, $first:ident $(,$other:ident)*) => { + ($this:ident, $first:ident $(, $other:ident)*) => { unsafe { $this.$first.as_ptr() } }; } @@ -143,6 +149,82 @@ macro_rules! define_mat { } } } + + #[cfg(feature = "serde")] + impl Serialize for $Name + where + T: Serialize, + { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer + { + let mut s = serializer.serialize_struct(stringify!($Name), $size)?; + unsafe { $(s.serialize_field(stringify!($f), &self.$f)?;)+ } + s.end() + } + } + + #[cfg(feature = "serde")] + impl<'de, T> Deserialize<'de> for $Name + where + T: Deserialize<'de>, + { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de> + { + struct MatrixVisitor(std::marker::PhantomData); + + impl<'de, X> Visitor<'de> for MatrixVisitor + where X: Deserialize<'de>, + { + type Value = $Name; + + fn expecting(&self, formatter: &mut Formatter) -> FmtResult { + formatter.write_fmt(format_args!("struct {}", stringify!($Name))) + } + + fn visit_seq(self, mut seq: V) -> Result<$Name, V::Error> + where + V: SeqAccess<'de>, + { + Ok($Name { + $($f: seq.next_element()?.ok_or_else(|| Error::invalid_length($i, &self))?,)+ + }) + } + + fn visit_map(self, mut map: V) -> Result<$Name, V::Error> + where + V: MapAccess<'de>, + { + $(let mut $f = None;)+ + + while let Some(key) = map.next_key()? { + match key { + $( + stringify!($f) => { + if $f.is_some() { + return Err(Error::duplicate_field(stringify!($f))); + } + $f = Some(map.next_value()?); + } + )+ + value => return Err(Error::unknown_field(value, FIELDS)), + } + } + + Ok($Name { + $($f: $f.ok_or_else(|| Error::missing_field(stringify!($f)))?,)+ + }) + } + } + + const FIELDS: &'static [&'static str] = &[$(stringify!($f),)+]; + + deserializer.deserialize_struct(stringify!($Name), FIELDS, MatrixVisitor::(std::marker::PhantomData)) + } + } }; } diff --git a/glc/src/vector.rs b/glc/src/vector.rs index cfd2d75..e20c540 100644 --- a/glc/src/vector.rs +++ b/glc/src/vector.rs @@ -4,8 +4,14 @@ use std::convert::{AsMut, AsRef}; use std::fmt::{Debug, Formatter, Result as FmtResult}; use std::ops::{Add, Deref, DerefMut, Div, Mul, Neg, Sub}; +#[cfg(feature = "serde")] +use serde::{ + de::{Deserialize, Deserializer, Error}, + ser::{Serialize, Serializer}, +}; + macro_rules! first_ptr { - ($this:ident, $first:ident $(,$other:ident)*) => { + ($this:ident, $first:ident $(, $other:ident)*) => { unsafe { &$this.$first } }; } @@ -155,6 +161,36 @@ macro_rules! define_vec { } } } + + #[cfg(feature = "serde")] + impl Serialize for $Name + where + T: Serialize, + { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer + { + unsafe { vec![$(&self.$f,)+].serialize(serializer) } + } + } + + #[cfg(feature = "serde")] + impl<'de, T> Deserialize<'de> for $Name + where + T: Deserialize<'de>, + { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de> + { + let mut data = Vec::::deserialize(deserializer)?.into_iter(); + + Ok(Self { + $($f: data.next().ok_or_else(|| D::Error::custom("Vector is missing some elements!"))?,)+ + }) + } + } }; } diff --git a/space-crush-app/resources/world.json b/space-crush-app/resources/world.json new file mode 100644 index 0000000..30c2cfa --- /dev/null +++ b/space-crush-app/resources/world.json @@ -0,0 +1 @@ +[{"marker":[0],"components":[{"pos":[0.0,0.0],"size":500.0},{}]}] diff --git a/space-crush-app/src/main.rs b/space-crush-app/src/main.rs index efa92dd..ec3eed0 100644 --- a/space-crush-app/src/main.rs +++ b/space-crush-app/src/main.rs @@ -33,27 +33,45 @@ fn run(vfs: Vfs) -> Result<(), Error> { let mut common = Dispatcher::new(&mut world); let mut app = App::new(&mut world)?; - create_test_world(&mut world); + load_world(&mut world, WORLD_FILEPATH)?; while app.is_running() { + world.maintain(); + common.process(&world); app.process(&world)?; } + save_world(&mut world, WORLD_FILEPATH)?; + + Ok(()) +} + +fn load_world(world: &mut World, path: &str) -> Result<(), Error> { + use serde_json::de::{Deserializer, IoRead}; + use space_crush_common::misc::{PersistWorld, Persistence, WorldHelper}; + + PersistWorld::setup(world); + + let vfs = world.resource::()?; + let mut file = vfs.join(path)?.open_file()?; + let mut read = IoRead::new(&mut file); + let mut deserializer = Deserializer::new(&mut read); + world.deserialize(PersistWorld, &mut deserializer)?; + Ok(()) } -fn create_test_world(world: &mut World) { - use glc::vector::Vector2f; - use space_crush_common::components::{Planet, Position}; - use specs::Builder; - - world - .create_entity() - .with(Position { - pos: Vector2f::default(), - size: 500.0, - }) - .with(Planet) - .build(); +fn save_world(world: &mut World, path: &str) -> Result<(), Error> { + use serde_json::Serializer; + use space_crush_common::misc::{PersistWorld, WorldHelper}; + + let vfs = world.resource::()?; + let mut file = vfs.join(path)?.create_file()?; + let mut serializer = Serializer::new(&mut file); + world.serialize(PersistWorld, &mut serializer)?; + + Ok(()) } + +const WORLD_FILEPATH: &str = "resources/world.json"; diff --git a/space-crush-common/Cargo.toml b/space-crush-common/Cargo.toml index a911214..5b95348 100644 --- a/space-crush-common/Cargo.toml +++ b/space-crush-common/Cargo.toml @@ -5,13 +5,14 @@ authors = ["Bergmann89 "] edition = "2018" [dependencies] -glc = "0.1" +glc = { version = "0.1", features = [ "serde" ] } log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn" ] } log4rs = "0.13" +serde = "1.0" serde_yaml = "0.8" shred = { version = "0.10", features = [ "shred-derive" ] } shrev = "1.1" -specs = "0.16" +specs = { version = "0.16", features = [ "serde" ] } thiserror = "1.0" vfs = "0.4" vfs-zip = "0.2" diff --git a/space-crush-common/src/components/planet.rs b/space-crush-common/src/components/planet.rs index 8a9e4e7..1acbfd4 100644 --- a/space-crush-common/src/components/planet.rs +++ b/space-crush-common/src/components/planet.rs @@ -1,7 +1,8 @@ +use serde::{Deserialize, Serialize}; use specs::{Component, NullStorage}; -#[derive(Default)] -pub struct Planet; +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct Planet {} impl Component for Planet { type Storage = NullStorage; diff --git a/space-crush-common/src/components/position.rs b/space-crush-common/src/components/position.rs index a52ae7f..009ee6b 100644 --- a/space-crush-common/src/components/position.rs +++ b/space-crush-common/src/components/position.rs @@ -1,6 +1,8 @@ use glc::vector::Vector2f; +use serde::{Deserialize, Serialize}; use specs::{Component, VecStorage}; +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct Position { pub pos: Vector2f, pub size: f32, diff --git a/space-crush-common/src/misc/mod.rs b/space-crush-common/src/misc/mod.rs index c06fb18..c4b83c4 100644 --- a/space-crush-common/src/misc/mod.rs +++ b/space-crush-common/src/misc/mod.rs @@ -1,7 +1,9 @@ mod log; +mod persistence; mod vfs; mod world; pub use self::log::init as init_logger; pub use self::vfs::{Vfs, VfsError}; -pub use world::WorldHelper; +pub use persistence::PersistWorld; +pub use world::{Persistence, WorldHelper}; diff --git a/space-crush-common/src/misc/persistence.rs b/space-crush-common/src/misc/persistence.rs new file mode 100644 index 0000000..39b18e6 --- /dev/null +++ b/space-crush-common/src/misc/persistence.rs @@ -0,0 +1,13 @@ +use specs::saveload::SimpleMarker; + +use crate::components::{Planet, Position}; + +use super::Persistence; + +pub struct PersistWorld; +pub struct PersistWorldMarker; + +impl Persistence for PersistWorld { + type Marker = SimpleMarker; + type Components = (Position, Planet); +} diff --git a/space-crush-common/src/misc/world.rs b/space-crush-common/src/misc/world.rs index bc2db0c..d57b97b 100644 --- a/space-crush-common/src/misc/world.rs +++ b/space-crush-common/src/misc/world.rs @@ -1,11 +1,21 @@ use std::any::type_name; +use serde::{ + de::{DeserializeOwned, Deserializer}, + ser::{Serialize, Serializer}, +}; use shred::{Fetch, FetchMut, Resource}; use shrev::{Event, EventChannel, ReaderId}; -use specs::World; +use specs::{ + error::NoError, + saveload::{DeserializeComponents, Marker, SerializeComponents}, + Component, Entities, ReadStorage, World, WorldExt, Write, WriteStorage, +}; use crate::Error; +/* WorldHelper */ + pub trait WorldHelper { fn resource(&self) -> Result, Error> where @@ -18,6 +28,16 @@ pub trait WorldHelper { fn register_event_reader(&self) -> Result, Error> where E: Event; + + fn serialize(&self, persistence: T, serializer: S) -> Result<(), S::Error> + where + T: Persistence, + S: Serializer; + + fn deserialize<'de, T, D>(&self, persistence: T, deserializer: D) -> Result<(), D::Error> + where + T: Persistence, + D: Deserializer<'de>; } impl WorldHelper for World { @@ -43,4 +63,125 @@ impl WorldHelper for World { { Ok(self.resource_mut::>()?.register_reader()) } + + fn serialize(&self, persistence: T, serializer: S) -> Result<(), S::Error> + where + T: Persistence, + S: Serializer, + { + persistence.serialize(self, serializer) + } + + fn deserialize<'de, T, D>(&self, persistence: T, deserializer: D) -> Result<(), D::Error> + where + T: Persistence, + D: Deserializer<'de>, + { + persistence.deserialize(self, deserializer) + } +} + +/* Persistence */ + +pub trait Persistence { + type Marker: Marker; + type Components: PersistenceComponents; + + fn setup(world: &mut World) + where + ::Storage: Default, + ::Allocator: Default, + { + world.register::(); + world.insert(::Allocator::default()); + } + + fn serialize(&self, world: &World, serializer: S) -> Result<(), S::Error> + where + S: Serializer, + { + Self::Components::serialize(world, serializer) + } + + fn deserialize<'de, D>(&self, world: &World, deserializer: D) -> Result<(), D::Error> + where + D: Deserializer<'de>, + { + Self::Components::deserialize(world, deserializer) + } } + +/* PersistenceComponents */ + +pub trait PersistenceComponents +where + M: Marker, +{ + fn serialize(world: &World, serializer: S) -> Result<(), S::Error> + where + S: Serializer; + + fn deserialize<'de, D>(world: &World, deserializer: D) -> Result<(), D::Error> + where + D: Deserializer<'de>; +} + +macro_rules! define_persistence_components { + ($($T:ident),*) => { + #[allow(non_snake_case)] + impl PersistenceComponents for ($($T,)+) + where + M: Marker, + M::Allocator: Default, + $($T: Component + Serialize + DeserializeOwned + Clone,)+ + { + fn serialize(world: &World, serializer: S) -> Result<(), S::Error> + where + S: Serializer, + { + let (entities, marker, $($T,)+) = + world.system_data::<(Entities, ReadStorage, $(ReadStorage<$T>,)+)>(); + + SerializeComponents::::serialize(&($($T,)+), &entities, &marker, serializer)?; + + Ok(()) + } + + fn deserialize<'de, D>(world: &World, deserializer: D) -> Result<(), D::Error> + where + D: Deserializer<'de>, + { + let (entities, mut marker, mut allocator, $($T,)+) = world.system_data::<( + Entities, + WriteStorage, + Write, + $(WriteStorage<$T>,)+ + )>(); + + DeserializeComponents::::deserialize( + &mut ($($T,)+), + &entities, + &mut marker, + &mut allocator, + deserializer, + ) + } + } + } +} + +define_persistence_components!(T1); +define_persistence_components!(T1, T2); +define_persistence_components!(T1, T2, T3); +define_persistence_components!(T1, T2, T3, T4); +define_persistence_components!(T1, T2, T3, T4, T5); +define_persistence_components!(T1, T2, T3, T4, T5, T6); +define_persistence_components!(T1, T2, T3, T4, T5, T6, T7); +define_persistence_components!(T1, T2, T3, T4, T5, T6, T7, T8); +define_persistence_components!(T1, T2, T3, T4, T5, T6, T7, T8, T9); +define_persistence_components!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); +define_persistence_components!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11); +define_persistence_components!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); +define_persistence_components!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13); +define_persistence_components!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14); +define_persistence_components!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);