summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortroido <troido@protonmail.com>2020-02-18 01:11:49 +0100
committertroido <troido@protonmail.com>2020-02-18 01:11:49 +0100
commitc71ecb48fa4368035a852e2d06869a21382a6876 (patch)
tree8d5a598fedf4e42bd6d8d1b0d476301dede2f20c
parentc921686355c86afb5bf47e4b6c696057ede01b1f (diff)
Players are now saved/loaded too
-rw-r--r--src/componentwrapper.rs6
-rw-r--r--src/encyclopedia.rs4
-rw-r--r--src/main.rs43
-rw-r--r--src/persistence.rs23
-rw-r--r--src/playerstate.rs50
-rw-r--r--src/resources.rs28
-rw-r--r--src/room.rs104
-rw-r--r--src/systems/controlinput.rs37
-rw-r--r--src/systems/create.rs15
-rw-r--r--src/systems/take.rs2
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);
}
}
_ => {}