diff options
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/assemblages.rs | 2 | ||||
| -rw-r--r-- | src/components.rs | 32 | ||||
| -rw-r--r-- | src/pos.rs | 20 | ||||
| -rw-r--r-- | src/room.rs | 7 | ||||
| -rw-r--r-- | src/systems/makefloor.rs | 6 | ||||
| -rw-r--r-- | src/systems/moving.rs | 15 | ||||
| -rw-r--r-- | src/systems/view.rs | 96 | ||||
| -rw-r--r-- | src/worldmessages.rs | 21 |
9 files changed, 136 insertions, 64 deletions
@@ -14,3 +14,4 @@ users = "0.8" specs = { version = "0.15", features = ["specs-derive"] } rand = "0.7" serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } diff --git a/src/assemblages.rs b/src/assemblages.rs index a80e5c9..b43548c 100644 --- a/src/assemblages.rs +++ b/src/assemblages.rs @@ -50,7 +50,7 @@ impl Grass { } -assemblage!(Player {name: String}; Visible{sprite: "player".to_string(), height: 1.0}, Played{name: name.to_string()}); +assemblage!(Player {name: String}; Visible{sprite: "player".to_string(), height: 1.0}, Played::new(name.to_string())); impl Player { pub fn new(name: &str) -> Player { diff --git a/src/components.rs b/src/components.rs index 3b17f0c..18de7f0 100644 --- a/src/components.rs +++ b/src/components.rs @@ -1,16 +1,38 @@ use specs::{ DenseVecStorage, + VecStorage, + FlaggedStorage, Component }; use super::controls::Control; +use super::pos::Pos; -#[derive(Component, Debug, Clone)] + +#[derive(Debug, Clone)] +pub struct Position{ + pub pos: Pos, + pub prev: Option<Pos> +} +impl Position { + pub fn new(pos: Pos) -> Position { + Position{pos, prev: None} + } +} + +impl Component for Position { + type Storage = FlaggedStorage<Self, VecStorage<Self>>; +} + +#[derive(Debug, Clone)] pub struct Visible { pub sprite: String, pub height: f32 } +impl Component for Visible { + type Storage = FlaggedStorage<Self, VecStorage<Self>>; +} #[derive(Component, Debug)] pub struct Controller(pub Control); @@ -20,5 +42,11 @@ pub struct Blocking; #[derive(Component, Debug)] pub struct Played { - pub name: String + pub name: String, + pub is_new: bool +} +impl Played { + pub fn new(name: String) -> Played { + Played{name, is_new: true} + } } @@ -1,18 +1,17 @@ - use std::ops::Add; use serde_json::{Value, json}; -use specs::{Component, VecStorage}; +use serde::{Serialize, Serializer, ser::SerializeTuple}; use super::util::{clamp, ToJson}; -#[derive(Component, Debug, Hash, PartialEq, Eq, Clone, Copy)] -#[storage(VecStorage)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] pub struct Pos { pub x: i32, pub y: i32 } + impl Pos { pub fn new(x: i32, y: i32) -> Pos { @@ -27,6 +26,19 @@ impl Pos { } } + +impl Serialize for Pos { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let mut tup = serializer.serialize_tuple(2)?; + tup.serialize_element(&self.x)?; + tup.serialize_element(&self.y)?; + tup.end() + } +} + impl Add<Pos> for Pos { type Output = Pos; diff --git a/src/room.rs b/src/room.rs index da92d69..8341eba 100644 --- a/src/room.rs +++ b/src/room.rs @@ -12,6 +12,7 @@ use specs::{ use super::controls::Action; use super::pos::Pos; +use super::components::Position; use super::assemblages::Assemblage; use super::worldmessages::WorldMessage; use super::resources::{ @@ -49,7 +50,7 @@ impl <'a, 'b>Room<'a, 'b> { .with(MakeFloor, "makefloor", &[]) .with(Move, "move", &["makefloor", "controlinput"]) .with(ClearControllers, "clearcontrollers", &["move"]) - .with(View, "view", &["move"]) + .with(View::default(), "view", &["move"]) .build(); dispatcher.setup(&mut world); @@ -69,7 +70,7 @@ impl <'a, 'b>Room<'a, 'b> { let assemblages = self.world.remove::<NewEntities>().unwrap_or(NewEntities{assemblages: Vec::new()}).assemblages; self.world.insert(NewEntities{assemblages: Vec::new()}); for (pos, assemblage) in assemblages{ - assemblage.build(self.world.create_entity()).with(pos).build(); + assemblage.build(self.world.create_entity()).with(Position::new(pos)).build(); } self.world.maintain(); } @@ -84,7 +85,7 @@ impl <'a, 'b>Room<'a, 'b> { } pub fn add_obj(&mut self, template: &dyn Assemblage, (x, y): (i32, i32)) -> Entity { - template.build(self.world.create_entity()).with(Pos{x, y}).build() + template.build(self.world.create_entity()).with(Position::new(Pos{x, y})).build() } } diff --git a/src/systems/makefloor.rs b/src/systems/makefloor.rs index b6fedd9..209d965 100644 --- a/src/systems/makefloor.rs +++ b/src/systems/makefloor.rs @@ -8,7 +8,7 @@ use specs::{ Join }; -use super::super::pos::Pos; +use super::super::components::Position; use super::super::resources::{ Floor @@ -17,11 +17,11 @@ use super::super::resources::{ pub struct MakeFloor; impl <'a> System<'a> for MakeFloor { - type SystemData = (Entities<'a>, Write<'a, Floor>, ReadStorage<'a, Pos>); + type SystemData = (Entities<'a>, Write<'a, Floor>, ReadStorage<'a, Position>); fn run(&mut self, (entities, mut floor, positions): Self::SystemData) { floor.cells.clear(); for (ent, pos) in (&entities, &positions).join() { - floor.cells.entry(*pos).or_insert(Vec::new()).push(ent); + floor.cells.entry(pos.pos).or_insert(Vec::new()).push(ent); } } } diff --git a/src/systems/moving.rs b/src/systems/moving.rs index 38588d5..2ea0650 100644 --- a/src/systems/moving.rs +++ b/src/systems/moving.rs @@ -11,7 +11,8 @@ use super::super::pos::Pos; use super::super::components::{ Controller, - Blocking + Blocking, + Position }; use super::super::controls::{ @@ -27,12 +28,12 @@ use super::super::resources::{ pub struct Move; impl <'a> System<'a> for Move { - type SystemData = (ReadStorage<'a, Controller>, WriteStorage<'a, Pos>, Read<'a, Size>, ReadStorage<'a, Blocking>, Read<'a, Floor>); - fn run(&mut self, (controller, mut pos, size, blocking, floor): Self::SystemData) { - for (controller, pos) in (&controller, &mut pos).join(){ + type SystemData = (ReadStorage<'a, Controller>, WriteStorage<'a, Position>, Read<'a, Size>, ReadStorage<'a, Blocking>, Read<'a, Floor>); + fn run(&mut self, (controllers, mut positions, size, blocking, floor): Self::SystemData) { + for (controller, mut pos) in (&controllers, &mut positions.restrict_mut()).join(){ match &controller.0 { Control::Move(direction) => { - let newpos = (*pos + direction.to_position()).clamp(Pos::new(0, 0), Pos::new(size.width - 1, size.height - 1)); + let newpos = (pos.get_unchecked().pos + direction.to_position()).clamp(Pos::new(0, 0), Pos::new(size.width - 1, size.height - 1)); let mut blocked = false; for ent in floor.cells.get(&newpos).unwrap_or(&Vec::new()) { if blocking.get(*ent).is_some(){ @@ -41,7 +42,9 @@ impl <'a> System<'a> for Move { } } if !blocked { - pos.clone_from(&newpos); + let mut pos_mut = pos.get_mut_unchecked(); + pos_mut.prev = Some(pos_mut.pos); + pos_mut.pos = newpos.clone(); } } _ => {} diff --git a/src/systems/view.rs b/src/systems/view.rs index 209b1a5..b854348 100644 --- a/src/systems/view.rs +++ b/src/systems/view.rs @@ -1,8 +1,14 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use specs::{ + BitSet, + storage::ComponentEvent, + ReaderId, + World, + SystemData, ReadStorage, + WriteStorage, Read, Write, System, @@ -10,39 +16,29 @@ use specs::{ }; use super::super::pos::Pos; - -use super::super::components::{ - Visible, - Played -}; - -use super::super::resources::{ - Size, - Output -}; - -use super::super::worldmessages::{ - WorldMessage, - WorldUpdate, - FieldMessage -}; +use super::super::components::{Visible, Played, Position}; +use super::super::resources::{Size, Output}; +use super::super::worldmessages::{WorldMessage, WorldUpdate, FieldMessage}; +#[derive(Default)] +pub struct View { + reader_id: Option<ReaderId<ComponentEvent>>, + dirty: BitSet +} -pub struct View; impl <'a> System<'a> for View { - type SystemData = (ReadStorage<'a, Pos>, ReadStorage<'a, Visible>, Read<'a, Size>, ReadStorage<'a, Played>, Write<'a, Output>); - fn run(&mut self, (positions, visible, size, players, mut output): Self::SystemData) { - + type SystemData = (ReadStorage<'a, Position>, ReadStorage<'a, Visible>, Read<'a, Size>, WriteStorage<'a, Played>, Write<'a, Output>); + fn run(&mut self, (positions, visible, size, mut players, mut output): Self::SystemData) { let mut cells: HashMap<Pos, Vec<Visible>> = HashMap::new(); for (pos, vis) in (&positions, &visible).join(){ - cells.entry(*pos).or_insert(Vec::new()).push(vis.clone()); - cells.get_mut(pos).unwrap().sort_by(|a, b| b.height.partial_cmp(&a.height).unwrap()); + cells.entry(pos.pos).or_insert(Vec::new()).push(vis.clone()); + cells.get_mut(&pos.pos).unwrap().sort_by(|a, b| b.height.partial_cmp(&a.height).unwrap()); } let width = size.width; let height = size.height; - let (values, mapping) = draw_room(cells, (width, height)); + let (values, mapping) = draw_room(cells.clone(), (width, height)); let field = WorldUpdate::Field(FieldMessage{ width, @@ -50,19 +46,57 @@ impl <'a> System<'a> for View { field: values, mapping }); + + + self.dirty.clear(); + { + let events = positions.channel().read(self.reader_id.as_mut().unwrap()); + for event in events { + match event { + ComponentEvent::Modified(id) | ComponentEvent::Inserted(id) | ComponentEvent::Removed(id) => { + self.dirty.add(*id); + } + }; + } + } + let mut changed: HashSet<Pos> = HashSet::new(); + for (pos, _) in (&positions, &self.dirty).join(){ + changed.insert(pos.pos); + if let Some(prev) = pos.prev{ + changed.insert(prev); + } + } + let has_changed: bool = changed.len() > 0; + let mut changes: Vec<(Pos, Vec<String>)> = Vec::new(); + for pos in changed { + changes.push((pos, cells.get(&pos).unwrap().iter().map(|v| v.sprite.clone()).collect())); + } + let changed_msg = WorldUpdate::Change(changes); + + output.output.clear(); - for (player, pos) in (&players, &positions).join() { - - let message = WorldMessage{updates: vec![ - field.clone(), - WorldUpdate::Pos(*pos) - ]}; + for (mut player, pos) in (&mut players, &positions).join() { + let mut updates: Vec<WorldUpdate> = Vec::new(); + if player.is_new { + updates.push(field.clone()); + } else if has_changed { + updates.push(changed_msg.clone()); + } + updates.push(WorldUpdate::Pos(pos.pos)); + let message = WorldMessage{updates}; output.output.insert(player.name.clone(), message); + player.is_new = false; } } + + fn setup(&mut self, world: &mut World) { + Self::SystemData::setup(world); + self.reader_id = Some( + WriteStorage::<Position>::fetch(&world).register_reader() + ); + } } - fn draw_room(cells: HashMap<Pos, Vec<Visible>>, (width, height): (i32, i32)) -> (Vec<usize>, Vec<Vec<String>>){ let size = width * height; diff --git a/src/worldmessages.rs b/src/worldmessages.rs index 8feb29f..8d9d8c8 100644 --- a/src/worldmessages.rs +++ b/src/worldmessages.rs @@ -1,5 +1,6 @@ use serde_json::{Value, json}; +use serde::Serialize; use super::util::ToJson; use super::pos::Pos; @@ -18,35 +19,27 @@ impl ToJson for WorldMessage { #[derive(Clone)] pub enum WorldUpdate { Field(FieldMessage), - Pos(Pos) + Pos(Pos), + Change(Vec<(Pos, Vec<String>)>) } impl ToJson for WorldUpdate { fn to_json(&self) -> Value { match self { - WorldUpdate::Field(msg) => Value::Array(vec![Value::String("field".to_string()), msg.to_json()]), - WorldUpdate::Pos(pos) => Value::Array(vec![Value::String("playerpos".to_string()), pos.to_json()]) + WorldUpdate::Field(msg) => json!(["field", msg]), + WorldUpdate::Pos(pos) => json!(["playerpos", pos]), + WorldUpdate::Change(changes) => json!(["changecells", changes]) } } } -#[derive(Clone)] +#[derive(Clone, Serialize)] pub struct FieldMessage { pub width: i32, pub height: i32, pub field: Vec<usize>, pub mapping: Vec<Vec<String>> } -impl ToJson for FieldMessage { - fn to_json(&self) -> Value { - json!({ - "width": self.width, - "height": self.height, - "field": self.field, - "mapping": self.mapping - }) - } -} |
