diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/componentwrapper.rs | 6 | ||||
| -rw-r--r-- | src/encyclopedia.rs | 4 | ||||
| -rw-r--r-- | src/main.rs | 43 | ||||
| -rw-r--r-- | src/persistence.rs | 23 | ||||
| -rw-r--r-- | src/playerstate.rs | 50 | ||||
| -rw-r--r-- | src/resources.rs | 28 | ||||
| -rw-r--r-- | src/room.rs | 104 | ||||
| -rw-r--r-- | src/systems/controlinput.rs | 37 | ||||
| -rw-r--r-- | src/systems/create.rs | 15 | ||||
| -rw-r--r-- | src/systems/take.rs | 2 |
10 files changed, 223 insertions, 89 deletions
diff --git a/src/componentwrapper.rs b/src/componentwrapper.rs index 951745d..e5f9b9f 100644 --- a/src/componentwrapper.rs +++ b/src/componentwrapper.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use specs::{Builder, world::LazyBuilder}; +use specs::Builder; use crate::PlayerId; use crate::components::{Visible, Blocking, Player, Floor, Item, Inventory, Health, Serialise}; @@ -20,7 +20,7 @@ macro_rules! components { impl ComponentWrapper { - pub fn build<'a>(&self, builder: LazyBuilder<'a>) -> LazyBuilder<'a> { + pub fn build<A: Builder>(&self, builder: A ) -> A { match self.clone() { $( Self::$comp(c) => builder.with(c), @@ -101,7 +101,7 @@ components!( ); - +pub type PreEntity = Vec<ComponentWrapper>; diff --git a/src/encyclopedia.rs b/src/encyclopedia.rs index 920f15e..bdb12af 100644 --- a/src/encyclopedia.rs +++ b/src/encyclopedia.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use serde_json::Value; use crate::assemblage::Assemblage; -use crate::componentwrapper::ComponentWrapper; +use crate::componentwrapper::PreEntity; use crate::template::Template; #[derive(Default, Clone)] @@ -20,7 +20,7 @@ impl Encyclopedia { Ok(Encyclopedia{items}) } - pub fn construct(&self, template: &Template) -> Result<Vec<ComponentWrapper>, &'static str> { + pub fn construct(&self, template: &Template) -> Result<PreEntity, &'static str> { let assemblage = self.items.get(&template.name).ok_or("unknown assemblage name")?; assemblage.instantiate(template) } diff --git a/src/main.rs b/src/main.rs index 5b2bc8a..73c26ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ - +use std::collections::HashMap; use std::thread::sleep; use std::time::Duration; use std::path::Path; @@ -41,6 +41,8 @@ use self::util::ToJson; use self::roomtemplate::RoomTemplate; use self::defaultencyclopedia::default_encyclopedia; use self::persistence::{FileStorage, PersistentStorage}; +use crate::controls::Action; +use crate::playerstate::PlayerState; @@ -60,7 +62,7 @@ fn main() { let mut room = gen_room(); - let storage = FileStorage::new("~/.rustifarm/saves"); + let storage = FileStorage::new(FileStorage::savedir().expect("couldn't find any save directory")); if let Ok(state) = storage.load_room("room".to_string()) { room.load_saved(&state); println!("loaded saved state successfully"); @@ -74,12 +76,39 @@ fn main() { let mut count = 0; loop { let actions = gameserver.update(); - - room.set_input(actions); + let mut inputs = HashMap::new(); + for action in actions { + match action { + Action::Input(player, control) => {inputs.insert(player, control);} + Action::Join(player) => { + let state = match storage.load_player(player.clone()) { + Ok(state) => state, + Err(_) => PlayerState::new(player.name.clone()) + }; + room.add_player(player.clone(), &state); + } + Action::Leave(player) => { + if let Err(err) = storage.save_player(player.clone(), room.remove_player(player).unwrap()) { + println!("{:?}", err); + } + } + } + } + room.set_input(inputs); room.update(); if count % 50 == 0 { - storage.save_room("room".to_string(), room.save()); - println!("{}", room.save().to_json()); + if let Err(err) = storage.save_room(room.name.clone(), room.save()) { + println!("{:?}",err); + } else { + println!("{}", room.save().to_json()); + } + for (playerid, state) in room.save_players() { + if let Err(err) = storage.save_player(playerid.clone(), state.clone()) { + println!("{:?}",err); + } else { + println!("{:?} {}", playerid, state.to_json()); + } + } } let messages = room.view(); for (player, message) in messages { @@ -93,7 +122,7 @@ fn main() { fn gen_room<'a, 'b>() -> Room<'a, 'b> { let assemblages = default_encyclopedia(); - let mut room = Room::new(assemblages); + let mut room = Room::new("room", assemblages); let roomtemplate = RoomTemplate::from_json(&json!({ "width": 42, diff --git a/src/persistence.rs b/src/persistence.rs index 1808652..7c15ec0 100644 --- a/src/persistence.rs +++ b/src/persistence.rs @@ -1,6 +1,7 @@ use std::path::PathBuf; use std::fs; +use std::env; use serde_json; use serde_json::Value; use crate::{ @@ -29,9 +30,27 @@ pub struct FileStorage { } impl FileStorage { - pub fn new(path: &str) -> Self { + pub fn new(path: PathBuf) -> Self { Self { - directory: PathBuf::from(path) + directory: path + } + } + + pub fn savedir() -> Option<PathBuf> { + if let Some(pathname) = env::var_os("ASCIIFARM_SAVE_DIR") { + Some(PathBuf::from(pathname)) + } else if let Some(pathname) = env::var_os("XDG_DATA_HOME") { + let mut path = PathBuf::from(pathname); + path.push("asciifarm"); + path.push("saves"); + Some(path) + } else if let Some(pathname) = env::var_os("HOME") { + let mut path = PathBuf::from(pathname); + path.push(".asciifarm"); + path.push("saves"); + Some(path) + } else { + None } } } diff --git a/src/playerstate.rs b/src/playerstate.rs index dac463b..e68e890 100644 --- a/src/playerstate.rs +++ b/src/playerstate.rs @@ -2,27 +2,43 @@ use serde_json::{Value, json}; use crate::template::Template; +use crate::{ + componentwrapper::{ComponentWrapper, PreEntity}, + PlayerId, + components::{Visible, Player, Inventory, Health, Item} +}; - +#[derive(Debug, Clone)] pub struct PlayerState { - name: String, - room: String, - inventory_capacity: usize, - inventory: Vec<Template>, - health: i64, - maximum_health: i64 + pub name: String, + pub room: String, + pub inventory_capacity: usize, + pub inventory: Vec<Template>, + pub health: i64, + pub maximum_health: i64 } impl PlayerState { - pub fn new(name: String, room: String, inventory: Vec<Template>, health: i64) -> Self { + pub fn new(name: String) -> Self { + Self{ + name: name, + room: String::new(), + inventory: Vec::new(), + inventory_capacity: 10, + health: 9, + maximum_health: 10 + } + } + + pub fn create(name: String, room: String, inventory: Vec<Template>, inventory_capacity: usize, health: i64, maximum_health: i64) -> Self { Self { name, room, inventory, health, - inventory_capacity: 10, - maximum_health: 50 + inventory_capacity, + maximum_health } } @@ -58,4 +74,18 @@ impl PlayerState { maximum_health: val.get("maxhealth")?.as_i64()? }) } + + pub fn construct(&self, id: PlayerId) -> PreEntity { + vec![ + ComponentWrapper::Visible(Visible{sprite: "player".to_string(), height: 1.0}), + ComponentWrapper::Player(Player::new(id)), + ComponentWrapper::Inventory(Inventory{ + items: self.inventory.iter().map( + |template| Item{ent: template.clone(), name: template.name.clone()} + ).collect(), + capacity: self.inventory_capacity + }), + ComponentWrapper::Health(Health{health: self.health, maxhealth: self.maximum_health}) + ] + } } diff --git a/src/resources.rs b/src/resources.rs index 0e40a00..f50d2bb 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -3,16 +3,18 @@ use std::collections::{HashMap, HashSet}; use specs::Entity; use super::pos::Pos; -use super::controls::Action; +use super::controls::Control; use super::worldmessages::WorldMessage; -use super::template::Template; +use crate::componentwrapper::PreEntity; use crate::encyclopedia::Encyclopedia; use crate::PlayerId; +use crate::util::Result; +use crate::template::Template; #[derive(Default)] pub struct Input { - pub actions: Vec<Action> + pub actions: HashMap<PlayerId, Control> } #[derive(Default)] @@ -38,6 +40,24 @@ pub struct Ground { #[derive(Default)] pub struct NewEntities { - pub templates: Vec<(Pos, Template)>, + pub to_build: Vec<(Pos, PreEntity)>, pub encyclopedia: Encyclopedia } +impl NewEntities { + pub fn new(encyclopedia: Encyclopedia) -> Self { + Self{ + to_build: Vec::new(), + encyclopedia + } + } + pub fn create(&mut self, pos: Pos, template: Template) -> Result<()> { + let components = self.encyclopedia.construct(&template)?; + self.to_build.push((pos, components)); + Ok(()) + } +} + +#[derive(Default)] +pub struct Players { + pub entities: HashMap<PlayerId, Entity> +} diff --git a/src/room.rs b/src/room.rs index 28abfa7..83702fe 100644 --- a/src/room.rs +++ b/src/room.rs @@ -6,17 +6,20 @@ use specs::{ WorldExt, DispatcherBuilder, Dispatcher, - Join + Builder, + Join, + Entity }; -use super::controls::Action; +use super::controls::Control; use super::worldmessages::WorldMessage; use super::resources::{ Size, Output, Input, NewEntities, - Spawn + Spawn, + Players }; use super::systems::{ moving::Move, @@ -27,28 +30,38 @@ use super::systems::{ create::Create, take::Take }; -use crate::components::{Position, Serialise}; +use crate::components::{ + Position, + Serialise, + Player, + Inventory, + Health, + New, + Removed +}; use crate::encyclopedia::Encyclopedia; use crate::roomtemplate::RoomTemplate; use crate::savestate::SaveState; use crate::template::Template; -use crate::{Pos, PlayerId}; +use crate::playerstate::PlayerState; +use crate::{Pos, PlayerId, aerr}; +use crate::util::Result; pub struct Room<'a, 'b> { world: World, - dispatcher: Dispatcher<'a, 'b> + dispatcher: Dispatcher<'a, 'b>, + pub name: String } impl <'a, 'b>Room<'a, 'b> { - pub fn new(encyclopedia: Encyclopedia) -> Room<'a, 'b> { + pub fn new(name: &str, encyclopedia: Encyclopedia) -> Room<'a, 'b> { let mut world = World::new(); - world.insert(NewEntities{ - templates: Vec::new(), - encyclopedia - }); + world.insert(NewEntities::new(encyclopedia)); + world.insert(Players::default()); + world.insert(Spawn::default()); world.register::<Serialise>(); let mut dispatcher = DispatcherBuilder::new() @@ -66,7 +79,8 @@ impl <'a, 'b>Room<'a, 'b> { Room { world, - dispatcher + dispatcher, + name: name.to_string() } } @@ -83,7 +97,7 @@ impl <'a, 'b>Room<'a, 'b> { let y = (idx as i64) / width; for template in templates { - self.create_entity(template.clone().unsaved(), Pos{x, y}); + let _ = self.create_entity(template.clone().unsaved(), Pos{x, y}); } } } @@ -97,13 +111,31 @@ impl <'a, 'b>Room<'a, 'b> { self.world.maintain(); } - pub fn set_input(&mut self, actions: Vec<Action>){ + pub fn set_input(&mut self, actions: HashMap<PlayerId, Control>){ self.world.fetch_mut::<Input>().actions = actions; } + pub fn add_player(&mut self, id: PlayerId, state: &PlayerState){ + let pre_player = state.construct(id.clone()); + let spawn = self.world.fetch::<Spawn>().pos; + let mut builder = self.world.create_entity(); + let ent = builder.entity; + for comp in pre_player { + builder = comp.build(builder); + } + builder.with(Position::new(spawn)).with(New).build(); + self.world.fetch_mut::<Players>().entities.insert(id, ent); + } + + pub fn remove_player(&mut self, id: PlayerId) -> Result<PlayerState>{ + let ent = self.world.fetch_mut::<Players>().entities.remove(&id).ok_or(aerr!("failed to remove player"))?; + self.world.write_component::<Removed>().insert(ent, Removed)?; + self.save_player_ent(ent).ok_or(aerr!("failed to find player to remove")) + } + pub fn save(&self) -> SaveState { let positions = self.world.read_component::<Position>(); - let serialisers = self.world.write_component::<Serialise>(); + let serialisers = self.world.read_component::<Serialise>(); let mut state = SaveState::new(); for (pos, serialiser) in (&positions, &serialisers).join() { state.changes.entry(pos.pos).or_insert(Vec::new()).push(serialiser.template.clone()); @@ -114,13 +146,49 @@ impl <'a, 'b>Room<'a, 'b> { pub fn load_saved(&mut self, state: &SaveState) { for (pos, templates) in state.changes.iter() { for template in templates { - self.create_entity(template.clone(), *pos); + let _ = self.create_entity(template.clone(), *pos); } } } - fn create_entity(&mut self, template: Template, pos: Pos){ - self.world.fetch_mut::<NewEntities>().templates.push((pos, template)); + pub fn save_players(&self) -> HashMap<PlayerId, PlayerState> { + let players = self.world.read_component::<Player>(); + let inventories = self.world.read_component::<Inventory>(); + let healths = self.world.read_component::<Health>(); + let mut saved = HashMap::new(); + for (player, inventory, health) in (&players, &inventories, &healths).join() { + saved.insert(player.id.clone(), PlayerState::create( + player.id.name.clone(), + self.name.clone(), + inventory.items.iter().map(|item| item.ent.clone()).collect(), + inventory.capacity, + health.health, + health.maxhealth + )); + } + saved + } + + fn save_player_ent(&self, ent: Entity) -> Option<PlayerState> { + let players = self.world.read_component::<Player>(); + let player = players.get(ent)?; + let inventories = self.world.read_component::<Inventory>(); + let inventory = inventories.get(ent)?; + let healths = self.world.read_component::<Health>(); + let health = healths.get(ent)?; + Some(PlayerState::create( + player.id.name.clone(), + self.name.clone(), + inventory.items.iter().map(|item| item.ent.clone()).collect(), + inventory.capacity, + health.health, + health.maxhealth + )) + } + + fn create_entity(&mut self, template: Template, pos: Pos) -> Result<()>{ + self.world.fetch_mut::<NewEntities>().create(pos, template)?; + Ok(()) } } diff --git a/src/systems/controlinput.rs b/src/systems/controlinput.rs index e9b1103..a2de78d 100644 --- a/src/systems/controlinput.rs +++ b/src/systems/controlinput.rs @@ -1,22 +1,15 @@ -use std::collections::{HashMap, HashSet}; - use specs::{ ReadStorage, WriteStorage, Read, - Write, Entities, System, Join }; -use crate::{PlayerId, hashmap}; -use crate::components::{Controller, Player, Removed}; -use crate::controls::{Control, Action}; -use crate::resources::{Input, NewEntities, Spawn}; -use crate::template::Template; -use crate::parameter::Parameter; +use crate::components::{Controller, Player}; +use crate::resources::{Input}; pub struct ControlInput; @@ -25,12 +18,9 @@ impl <'a> System<'a> for ControlInput { Entities<'a>, Read<'a, Input>, WriteStorage<'a, Controller>, - ReadStorage<'a, Player>, - Write<'a, NewEntities>, - Read<'a, Spawn>, - WriteStorage<'a, Removed> + ReadStorage<'a, Player> ); - fn run(&mut self, (entities, input, mut controllers, players, mut new, spawn, mut removed): Self::SystemData) { + fn run(&mut self, (entities, input, mut controllers, players): Self::SystemData) { { let mut ents = Vec::new(); for (ent, _controller) in (&*entities, &controllers).join() { @@ -41,27 +31,10 @@ impl <'a> System<'a> for ControlInput { } } - let mut playercontrols: HashMap<&PlayerId, Control> = HashMap::new(); - let mut leaving = HashSet::new(); - for action in &input.actions { - match action { - Action::Join(player) => { - new.templates.push(( - spawn.pos, - Template::new("player", hashmap!("name".to_string() => Parameter::String(player.name.clone()))).unsaved() - )); - } - Action::Leave(player) => {leaving.insert(player);} - Action::Input(player, control) => {playercontrols.insert(player, control.clone());} - } - } for (player, entity) in (&players, &entities).join() { - if let Some(control) = playercontrols.get(&player.id){ + if let Some(control) = input.actions.get(&player.id){ let _ = controllers.insert(entity, Controller(control.clone())); } - if leaving.contains(&player.id) { - let _ = removed.insert(entity, Removed); - } } } } diff --git a/src/systems/create.rs b/src/systems/create.rs index 35ef747..8463f9e 100644 --- a/src/systems/create.rs +++ b/src/systems/create.rs @@ -34,18 +34,13 @@ impl <'a> System<'a> for Create { new.remove(ent); } } - for (pos, template) in &new_entities.templates { + for (pos, preentity) in &new_entities.to_build { let mut builder = updater.create_entity(&entities); - match new_entities.encyclopedia.construct(template) { - Ok(comps) => { - for comp in comps { - builder = comp.build(builder); - } - builder.with(Position::new(*pos)).with(New).build(); - }, - Err(msg) => {println!("{}", msg);} + for comp in preentity { + builder = comp.build(builder); } + builder.with(Position::new(*pos)).with(New).build(); } - new_entities.templates.clear(); + new_entities.to_build.clear(); } } diff --git a/src/systems/take.rs b/src/systems/take.rs index affa3d1..bf0f8e8 100644 --- a/src/systems/take.rs +++ b/src/systems/take.rs @@ -53,7 +53,7 @@ impl <'a> System<'a> for Take { } Control::Drop(_rank) => { if let Some(item) = inventory.items.pop() { - new.templates.push((position.pos, item.ent)); + let _ = new.create(position.pos, item.ent); } } _ => {} |
