@@ -1530,6 +1530,7 @@ name = "space-crush-common" | |||||
version = "0.1.0" | version = "0.1.0" | ||||
dependencies = [ | dependencies = [ | ||||
"glc", | "glc", | ||||
"hibitset", | |||||
"lazy_static", | "lazy_static", | ||||
"log", | "log", | ||||
"log4rs", | "log4rs", | ||||
@@ -1737,7 +1738,7 @@ version = "0.2.1" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
checksum = "d68a369614cd12ca384ca6e75ab0a5ac3a5acbfe8003622e20808a322b9bb652" | checksum = "d68a369614cd12ca384ca6e75ab0a5ac3a5acbfe8003622e20808a322b9bb652" | ||||
dependencies = [ | dependencies = [ | ||||
"flate2 0.2.20", | |||||
"flate2 1.0.14", | |||||
"vfs", | "vfs", | ||||
"zip", | "zip", | ||||
] | ] | ||||
@@ -20,5 +20,5 @@ shred = { version = "0.10", features = [ "shred-derive" ] } | |||||
shrev = "1.1" | shrev = "1.1" | ||||
smallvec = { version = "1.5", features = [ "serde" ] } | smallvec = { version = "1.5", features = [ "serde" ] } | ||||
space-crush-common = "0.1" | space-crush-common = "0.1" | ||||
specs = "0.16" | |||||
specs = "*" | |||||
thiserror = "1.0" | thiserror = "1.0" |
@@ -0,0 +1,11 @@ | |||||
use space_crush_common::components::ShipCount; | |||||
use specs::{Component, HashMapStorage}; | |||||
#[derive(Default, Debug)] | |||||
pub struct FleetInfo { | |||||
pub count: ShipCount, | |||||
} | |||||
impl Component for FleetInfo { | |||||
type Storage = HashMapStorage<Self>; | |||||
} |
@@ -0,0 +1,3 @@ | |||||
mod fleet_info; | |||||
pub use fleet_info::FleetInfo; |
@@ -32,15 +32,15 @@ impl<'a> System<'a> for Fleets { | |||||
for (p, f) in (&position, &fleet).join() { | for (p, f) in (&position, &fleet).join() { | ||||
geometry.render_lines( | geometry.render_lines( | ||||
Vector4f::new(0.5, 0.5, 0.5, 0.2), | |||||
Vector4f::new(0.5, 0.5, 0.5, 0.05), | |||||
&create_circle(p.pos, f.orbit_min), | &create_circle(p.pos, f.orbit_min), | ||||
); | ); | ||||
geometry.render_lines( | geometry.render_lines( | ||||
Vector4f::new(0.5, 0.5, 0.5, 0.2), | |||||
Vector4f::new(0.5, 0.5, 0.5, 0.05), | |||||
&create_circle(p.pos, f.orbit_max), | &create_circle(p.pos, f.orbit_max), | ||||
); | ); | ||||
geometry.render_lines( | geometry.render_lines( | ||||
Vector4f::new(0.5, 0.5, 0.5, 0.1), | |||||
Vector4f::new(0.5, 0.5, 0.5, 0.05), | |||||
&create_circle(p.pos, SHIP_ORBIT_DISTANCE_MAX * f.orbit_max), | &create_circle(p.pos, SHIP_ORBIT_DISTANCE_MAX * f.orbit_max), | ||||
); | ); | ||||
} | } | ||||
@@ -1,20 +1,21 @@ | |||||
mod constants; | |||||
mod debug; | |||||
mod error; | |||||
mod misc; | |||||
mod render; | |||||
mod resources; | |||||
mod systems; | |||||
pub mod components; | |||||
pub mod constants; | |||||
pub mod debug; | |||||
pub mod error; | |||||
pub mod misc; | |||||
pub mod render; | |||||
pub mod resources; | |||||
pub mod systems; | |||||
use specs::{Dispatcher, DispatcherBuilder, World}; | |||||
use specs::{Dispatcher, DispatcherBuilder, Entity, World}; | |||||
pub use error::Error; | pub use error::Error; | ||||
use debug::{Fleets as DebugFleets, Ships as DebugShips, Summary as DebugSummary}; | use debug::{Fleets as DebugFleets, Ships as DebugShips, Summary as DebugSummary}; | ||||
use misc::{Events, TextManager, Window}; | use misc::{Events, TextManager, Window}; | ||||
use render::{Asteroids, Init, Planets, Ships}; | use render::{Asteroids, Init, Planets, Ships}; | ||||
use resources::{Camera, Config, Geometry, State, Uniform}; | |||||
use systems::StateUpdate; | |||||
use resources::{Camera, Config, Geometry, PlayerState, State, Uniform}; | |||||
use systems::{FleetInfoUpdate, StateUpdate}; | |||||
pub struct App<'a, 'b> { | pub struct App<'a, 'b> { | ||||
is_running: bool, | is_running: bool, | ||||
@@ -24,7 +25,7 @@ pub struct App<'a, 'b> { | |||||
} | } | ||||
impl<'a, 'b> App<'a, 'b> { | impl<'a, 'b> App<'a, 'b> { | ||||
pub fn new(world: &mut World) -> Result<Self, Error> { | |||||
pub fn new(world: &mut World, player_id: Entity) -> Result<Self, Error> { | |||||
let config = Config::new(world)?; | let config = Config::new(world)?; | ||||
let events = Events::new(world)?; | let events = Events::new(world)?; | ||||
let window = Window::new(events.handle(), &config)?; | let window = Window::new(events.handle(), &config)?; | ||||
@@ -33,17 +34,20 @@ impl<'a, 'b> App<'a, 'b> { | |||||
let camera = Camera::new()?; | let camera = Camera::new()?; | ||||
let uniform = Uniform::new()?; | let uniform = Uniform::new()?; | ||||
let geometry = Geometry::new(world)?; | let geometry = Geometry::new(world)?; | ||||
let player_state = PlayerState::new(player_id); | |||||
world.insert(state); | world.insert(state); | ||||
world.insert(config); | world.insert(config); | ||||
world.insert(camera); | world.insert(camera); | ||||
world.insert(uniform); | world.insert(uniform); | ||||
world.insert(geometry); | world.insert(geometry); | ||||
world.insert(player_state); | |||||
let text_manager = TextManager::new(world)?; | let text_manager = TextManager::new(world)?; | ||||
let mut dispatcher = DispatcherBuilder::new() | let mut dispatcher = DispatcherBuilder::new() | ||||
.with(StateUpdate::new(world)?, "state_update", &[]) | .with(StateUpdate::new(world)?, "state_update", &[]) | ||||
.with(FleetInfoUpdate::new(world)?, "fleet_info_update", &[]) | |||||
.with_thread_local(Init::new(world)?) | .with_thread_local(Init::new(world)?) | ||||
.with_thread_local(Planets::new(world)?) | .with_thread_local(Planets::new(world)?) | ||||
.with_thread_local(Asteroids::new(world)?) | .with_thread_local(Asteroids::new(world)?) | ||||
@@ -5,7 +5,7 @@ use space_crush_common::{ | |||||
misc::{init_logger, Vfs}, | misc::{init_logger, Vfs}, | ||||
Dispatcher, | Dispatcher, | ||||
}; | }; | ||||
use specs::{World, WorldExt}; | |||||
use specs::{Builder, Entity, World, WorldExt}; | |||||
fn main() -> Result<(), Error> { | fn main() -> Result<(), Error> { | ||||
let vfs = Vfs::new(&["space-crush-app"])?; | let vfs = Vfs::new(&["space-crush-app"])?; | ||||
@@ -33,12 +33,12 @@ fn run(vfs: Vfs) -> Result<(), Error> { | |||||
let mut world = World::new(); | let mut world = World::new(); | ||||
world.insert(vfs); | world.insert(vfs); | ||||
let player1 = world.create_entity().build(); | |||||
let mut common = Dispatcher::new(&mut world); | let mut common = Dispatcher::new(&mut world); | ||||
let mut app = App::new(&mut world)?; | |||||
let mut app = App::new(&mut world, player1)?; | |||||
if !load_world(&mut world, WORLD_FILEPATH)? { | |||||
create_world(&mut world); | |||||
} | |||||
create_world(&mut world, player1); | |||||
info!("Application initialized"); | info!("Application initialized"); | ||||
@@ -49,31 +49,35 @@ fn run(vfs: Vfs) -> Result<(), Error> { | |||||
app.process(&world)?; | app.process(&world)?; | ||||
} | } | ||||
save_world(&mut world, WORLD_FILEPATH)?; | |||||
Ok(()) | Ok(()) | ||||
} | } | ||||
fn create_world(world: &mut World) { | |||||
fn create_world(world: &mut World, player_id: Entity) { | |||||
use glc::{ | use glc::{ | ||||
matrix::Angle, | matrix::Angle, | ||||
vector::{Vector2f, Vector4f}, | vector::{Vector2f, Vector4f}, | ||||
}; | }; | ||||
use space_crush_app::components::FleetInfo; | |||||
use space_crush_common::{ | use space_crush_common::{ | ||||
components::{ | components::{ | ||||
Asteroid, AsteroidType, Fleet, Owned, Planet, Player, Position, Ship, ShipType, | |||||
Velocity, | |||||
Asteroid, AsteroidType, Fleet, FleetOwned, Planet, Player, PlayerOwned, Position, Ship, | |||||
ShipType, Velocity, | |||||
}, | }, | ||||
misc::{PersistWorld, Persistence}, | misc::{PersistWorld, Persistence}, | ||||
}; | }; | ||||
use specs::{saveload::MarkedBuilder, Builder}; | |||||
use specs::{saveload::MarkedBuilder, WriteStorage}; | |||||
let player1 = world | |||||
.create_entity() | |||||
.with(Player { | |||||
color: Vector4f::new(0.0, 0.5, 1.0, 0.1), | |||||
}) | |||||
.build(); | |||||
PersistWorld::setup(world); | |||||
world | |||||
.system_data::<WriteStorage<Player>>() | |||||
.insert( | |||||
player_id, | |||||
Player { | |||||
color: Vector4f::new(0.0, 0.5, 1.0, 0.1), | |||||
}, | |||||
) | |||||
.unwrap(); | |||||
world | world | ||||
.create_entity() | .create_entity() | ||||
@@ -86,6 +90,7 @@ fn create_world(world: &mut World) { | |||||
orbit_min: 125.0, | orbit_min: 125.0, | ||||
orbit_max: 175.0, | orbit_max: 175.0, | ||||
}) | }) | ||||
.with(FleetInfo::default()) | |||||
.with(Asteroid { | .with(Asteroid { | ||||
type_: AsteroidType::Metal, | type_: AsteroidType::Metal, | ||||
}) | }) | ||||
@@ -102,6 +107,7 @@ fn create_world(world: &mut World) { | |||||
orbit_min: 125.0, | orbit_min: 125.0, | ||||
orbit_max: 175.0, | orbit_max: 175.0, | ||||
}) | }) | ||||
.with(FleetInfo::default()) | |||||
.with(Asteroid { | .with(Asteroid { | ||||
type_: AsteroidType::Crystal, | type_: AsteroidType::Crystal, | ||||
}) | }) | ||||
@@ -110,7 +116,7 @@ fn create_world(world: &mut World) { | |||||
let planet = world | let planet = world | ||||
.create_entity() | .create_entity() | ||||
.marked::<<PersistWorld as Persistence>::Marker>() | .marked::<<PersistWorld as Persistence>::Marker>() | ||||
.with(Owned { owner: player1 }) | |||||
.with(PlayerOwned { owner: player_id }) | |||||
.with(Position { | .with(Position { | ||||
pos: Vector2f::default(), | pos: Vector2f::default(), | ||||
size: 250.0, | size: 250.0, | ||||
@@ -119,6 +125,7 @@ fn create_world(world: &mut World) { | |||||
orbit_min: 325.0, | orbit_min: 325.0, | ||||
orbit_max: 425.0, | orbit_max: 425.0, | ||||
}) | }) | ||||
.with(FleetInfo::default()) | |||||
.with(Planet {}) | .with(Planet {}) | ||||
.build(); | .build(); | ||||
@@ -126,7 +133,7 @@ fn create_world(world: &mut World) { | |||||
world | world | ||||
.create_entity() | .create_entity() | ||||
.marked::<<PersistWorld as Persistence>::Marker>() | .marked::<<PersistWorld as Persistence>::Marker>() | ||||
.with(Owned { owner: player1 }) | |||||
.with(PlayerOwned { owner: player_id }) | |||||
.with(Position { | .with(Position { | ||||
pos: Vector2f::new( | pos: Vector2f::new( | ||||
500.0 * random::<f32>() - 250.0, | 500.0 * random::<f32>() - 250.0, | ||||
@@ -138,6 +145,7 @@ fn create_world(world: &mut World) { | |||||
dir: Vector2f::new(random::<f32>() - 0.5, random::<f32>() - 0.5).normalize(), | dir: Vector2f::new(random::<f32>() - 0.5, random::<f32>() - 0.5).normalize(), | ||||
speed: 100.0, | speed: 100.0, | ||||
}) | }) | ||||
.with(FleetOwned { owner: planet }) | |||||
.with(Ship { | .with(Ship { | ||||
type_: match i % 3 { | type_: match i % 3 { | ||||
0 => ShipType::Fighter, | 0 => ShipType::Fighter, | ||||
@@ -145,7 +153,6 @@ fn create_world(world: &mut World) { | |||||
2 => ShipType::Transporter, | 2 => ShipType::Transporter, | ||||
_ => unreachable!(), | _ => unreachable!(), | ||||
}, | }, | ||||
fleet: planet, | |||||
agility: Angle::Deg(360.0), | agility: Angle::Deg(360.0), | ||||
target_pos: Default::default(), | target_pos: Default::default(), | ||||
target_dir: Default::default(), | target_dir: Default::default(), | ||||
@@ -153,37 +160,3 @@ fn create_world(world: &mut World) { | |||||
.build(); | .build(); | ||||
} | } | ||||
} | } | ||||
fn load_world(world: &mut World, path: &str) -> Result<bool, Error> { | |||||
use serde_json::de::{Deserializer, IoRead}; | |||||
use space_crush_common::misc::{PersistWorld, Persistence, WorldHelper}; | |||||
PersistWorld::setup(world); | |||||
let vfs = world.resource::<Vfs>()?; | |||||
let path = vfs.join(path)?; | |||||
if !path.exists() { | |||||
return Ok(false); | |||||
} | |||||
let mut file = path.open_file()?; | |||||
let mut read = IoRead::new(&mut file); | |||||
let mut deserializer = Deserializer::new(&mut read); | |||||
world.deserialize(PersistWorld, &mut deserializer)?; | |||||
Ok(true) | |||||
} | |||||
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::<Vfs>()?; | |||||
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"; |
@@ -99,7 +99,6 @@ impl Window { | |||||
(monitor_size.width - window_size.width) / 2, | (monitor_size.width - window_size.width) / 2, | ||||
(monitor_size.height - window_size.height) / 2, | (monitor_size.height - window_size.height) / 2, | ||||
)); | )); | ||||
window.set_cursor_grab(true).unwrap(); | |||||
let context = unsafe { context.make_current().unwrap() }; | let context = unsafe { context.make_current().unwrap() }; | ||||
gl::load_with(|s| context.get_proc_address(s)); | gl::load_with(|s| context.get_proc_address(s)); | ||||
@@ -6,7 +6,7 @@ use glc::{ | |||||
vector::Vector4f, | vector::Vector4f, | ||||
}; | }; | ||||
use space_crush_common::{ | use space_crush_common::{ | ||||
components::{Asteroid, AsteroidType, Owned, Player, Position}, | |||||
components::{Asteroid, AsteroidType, Player, PlayerOwned, Position}, | |||||
misc::LogResult, | misc::LogResult, | ||||
}; | }; | ||||
use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | ||||
@@ -61,7 +61,7 @@ pub struct AsteroidsData<'a> { | |||||
geometry: ReadExpect<'a, Geometry>, | geometry: ReadExpect<'a, Geometry>, | ||||
position: ReadStorage<'a, Position>, | position: ReadStorage<'a, Position>, | ||||
asteroid: ReadStorage<'a, Asteroid>, | asteroid: ReadStorage<'a, Asteroid>, | ||||
owned: ReadStorage<'a, Owned>, | |||||
owned: ReadStorage<'a, PlayerOwned>, | |||||
player: ReadStorage<'a, Player>, | player: ReadStorage<'a, Player>, | ||||
} | } | ||||
@@ -6,7 +6,7 @@ use glc::{ | |||||
vector::Vector4f, | vector::Vector4f, | ||||
}; | }; | ||||
use space_crush_common::{ | use space_crush_common::{ | ||||
components::{Owned, Planet, Player, Position}, | |||||
components::{Planet, Player, PlayerOwned, Position}, | |||||
misc::LogResult, | misc::LogResult, | ||||
}; | }; | ||||
use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | ||||
@@ -58,7 +58,7 @@ pub struct PlanetsData<'a> { | |||||
geometry: ReadExpect<'a, Geometry>, | geometry: ReadExpect<'a, Geometry>, | ||||
position: ReadStorage<'a, Position>, | position: ReadStorage<'a, Position>, | ||||
planet: ReadStorage<'a, Planet>, | planet: ReadStorage<'a, Planet>, | ||||
owned: ReadStorage<'a, Owned>, | |||||
owned: ReadStorage<'a, PlayerOwned>, | |||||
player: ReadStorage<'a, Player>, | player: ReadStorage<'a, Player>, | ||||
} | } | ||||
@@ -6,7 +6,7 @@ use glc::{ | |||||
vector::Vector4f, | vector::Vector4f, | ||||
}; | }; | ||||
use space_crush_common::{ | use space_crush_common::{ | ||||
components::{Owned, Player, Position, Ship, ShipType, Velocity}, | |||||
components::{Player, PlayerOwned, Position, Ship, ShipType, Velocity}, | |||||
misc::LogResult, | misc::LogResult, | ||||
}; | }; | ||||
use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | ||||
@@ -65,7 +65,7 @@ pub struct ShipsData<'a> { | |||||
position: ReadStorage<'a, Position>, | position: ReadStorage<'a, Position>, | ||||
velocity: ReadStorage<'a, Velocity>, | velocity: ReadStorage<'a, Velocity>, | ||||
ship: ReadStorage<'a, Ship>, | ship: ReadStorage<'a, Ship>, | ||||
owned: ReadStorage<'a, Owned>, | |||||
owned: ReadStorage<'a, PlayerOwned>, | |||||
player: ReadStorage<'a, Player>, | player: ReadStorage<'a, Player>, | ||||
} | } | ||||
@@ -1,11 +1,13 @@ | |||||
mod camera; | mod camera; | ||||
mod config; | mod config; | ||||
mod geometry; | mod geometry; | ||||
mod player_state; | |||||
mod state; | mod state; | ||||
mod uniform; | mod uniform; | ||||
pub use camera::Camera; | pub use camera::Camera; | ||||
pub use config::Config; | pub use config::Config; | ||||
pub use geometry::Geometry; | pub use geometry::Geometry; | ||||
pub use player_state::PlayerState; | |||||
pub use state::State; | pub use state::State; | ||||
pub use uniform::Uniform; | pub use uniform::Uniform; |
@@ -0,0 +1,13 @@ | |||||
#![allow(dead_code)] | |||||
use specs::Entity; | |||||
pub struct PlayerState { | |||||
pub player_id: Entity, | |||||
} | |||||
impl PlayerState { | |||||
pub fn new(player_id: Entity) -> Self { | |||||
Self { player_id } | |||||
} | |||||
} |
@@ -0,0 +1,165 @@ | |||||
use std::collections::HashMap; | |||||
use shrev::ReaderId; | |||||
use space_crush_common::{ | |||||
components::{FleetOwned, PlayerOwned, Ship}, | |||||
misc::ComponentEvent, | |||||
}; | |||||
use specs::{ | |||||
hibitset::BitSetLike, prelude::*, world::Index, Entities, ReadExpect, ReadStorage, System, | |||||
World, WriteStorage, | |||||
}; | |||||
use crate::{components::FleetInfo, resources::PlayerState, Error}; | |||||
pub struct FleetInfoUpdate { | |||||
modified: HashMap<Index, Modified>, | |||||
need_update: BitSet, | |||||
fleet_owned_id: ReaderId<ComponentEvent<FleetOwned>>, | |||||
player_owned_id: ReaderId<ComponentEvent<PlayerOwned>>, | |||||
} | |||||
#[derive(Default)] | |||||
struct Modified { | |||||
old_fleet_id: Option<Entity>, | |||||
old_player_id: Option<Entity>, | |||||
} | |||||
impl FleetInfoUpdate { | |||||
pub fn new(world: &mut World) -> Result<Self, Error> { | |||||
let modified = HashMap::new(); | |||||
let need_update = BitSet::new(); | |||||
let fleet_owned_id = unsafe { | |||||
WriteStorage::<FleetOwned>::setup(world); | |||||
let mut fleet_owned = world.system_data::<WriteStorage<FleetOwned>>(); | |||||
fleet_owned | |||||
.unprotected_storage_mut() | |||||
.channel_mut() | |||||
.register_reader() | |||||
}; | |||||
let player_owned_id = unsafe { | |||||
WriteStorage::<PlayerOwned>::setup(world); | |||||
let mut player_owned = world.system_data::<WriteStorage<PlayerOwned>>(); | |||||
player_owned | |||||
.unprotected_storage_mut() | |||||
.channel_mut() | |||||
.register_reader() | |||||
}; | |||||
Ok(Self { | |||||
modified, | |||||
need_update, | |||||
fleet_owned_id, | |||||
player_owned_id, | |||||
}) | |||||
} | |||||
} | |||||
#[derive(SystemData)] | |||||
pub struct FleetInfoUpdateData<'a> { | |||||
entities: Entities<'a>, | |||||
player_state: ReadExpect<'a, PlayerState>, | |||||
ships: ReadStorage<'a, Ship>, | |||||
fleet_infos: WriteStorage<'a, FleetInfo>, | |||||
fleet_owned: ReadStorage<'a, FleetOwned>, | |||||
player_owned: ReadStorage<'a, PlayerOwned>, | |||||
} | |||||
impl<'a> System<'a> for FleetInfoUpdate { | |||||
type SystemData = FleetInfoUpdateData<'a>; | |||||
fn run(&mut self, data: Self::SystemData) { | |||||
let FleetInfoUpdateData { | |||||
entities, | |||||
player_state, | |||||
ships, | |||||
mut fleet_infos, | |||||
fleet_owned, | |||||
player_owned, | |||||
} = data; | |||||
self.modified.clear(); | |||||
self.need_update.clear(); | |||||
/* player owned events */ | |||||
let events = player_owned | |||||
.unprotected_storage() | |||||
.channel() | |||||
.read(&mut self.player_owned_id); | |||||
for event in events { | |||||
match event { | |||||
ComponentEvent::Inserted(id) => { | |||||
self.need_update.add(*id); | |||||
} | |||||
ComponentEvent::Modified(id, player_owned) | |||||
| ComponentEvent::Removed(id, player_owned) => { | |||||
self.need_update.add(*id); | |||||
self.modified.entry(*id).or_default().old_player_id = Some(player_owned.owner); | |||||
} | |||||
} | |||||
} | |||||
/* fleet owned events */ | |||||
let events = fleet_owned | |||||
.unprotected_storage() | |||||
.channel() | |||||
.read(&mut self.fleet_owned_id); | |||||
for event in events { | |||||
match event { | |||||
ComponentEvent::Inserted(id) => { | |||||
self.need_update.add(*id); | |||||
} | |||||
ComponentEvent::Modified(id, fleet_owned) | |||||
| ComponentEvent::Removed(id, fleet_owned) => { | |||||
self.need_update.add(*id); | |||||
self.modified.entry(*id).or_default().old_fleet_id = Some(fleet_owned.owner); | |||||
} | |||||
} | |||||
} | |||||
if self.need_update.is_empty() { | |||||
return; | |||||
} | |||||
/* update fleets */ | |||||
let player_id = player_state.player_id; | |||||
for (fleet_id, fleet_info) in (&entities, &mut fleet_infos).join() { | |||||
for (ship_id, ship, fleet_owned, player_owned, _) in ( | |||||
&entities, | |||||
&ships, | |||||
&fleet_owned, | |||||
&player_owned, | |||||
&self.need_update, | |||||
) | |||||
.join() | |||||
{ | |||||
let new_match = fleet_owned.owner == fleet_id && player_owned.owner == player_id; | |||||
let old_match = match self.modified.get(&ship_id.id()) { | |||||
None => false, | |||||
Some(modified) => match (modified.old_fleet_id, modified.old_player_id) { | |||||
(Some(old_fleet_id), Some(old_player_id)) => { | |||||
old_fleet_id != fleet_id && old_player_id == player_id | |||||
} | |||||
(Some(old_fleet_id), None) => { | |||||
old_fleet_id != fleet_id && player_owned.owner == player_id | |||||
} | |||||
(None, Some(old_player_id)) => { | |||||
fleet_owned.owner != fleet_id && old_player_id == player_id | |||||
} | |||||
(None, None) => false, | |||||
}, | |||||
}; | |||||
if old_match && !new_match { | |||||
fleet_info.count[ship.type_] = fleet_info.count[ship.type_].saturating_sub(1); | |||||
} else if !old_match && new_match { | |||||
fleet_info.count[ship.type_] += 1; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,3 +1,5 @@ | |||||
mod fleet_info_update; | |||||
mod state_update; | mod state_update; | ||||
pub use fleet_info_update::FleetInfoUpdate; | |||||
pub use state_update::StateUpdate; | pub use state_update::StateUpdate; |
@@ -6,6 +6,7 @@ edition = "2018" | |||||
[dependencies] | [dependencies] | ||||
glc = { version = "0.1", features = [ "serde" ] } | glc = { version = "0.1", features = [ "serde" ] } | ||||
hibitset = "0.6" | |||||
log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn" ] } | log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn" ] } | ||||
lazy_static = "1.4" | lazy_static = "1.4" | ||||
log4rs = "0.13" | log4rs = "0.13" | ||||
@@ -14,7 +15,7 @@ serde = "1.0" | |||||
serde_yaml = "0.8" | serde_yaml = "0.8" | ||||
shred = { version = "0.10", features = [ "shred-derive" ] } | shred = { version = "0.10", features = [ "shred-derive" ] } | ||||
shrev = "1.1" | shrev = "1.1" | ||||
specs = { version = "0.16", features = [ "serde" ] } | |||||
specs = { version = "*", features = [ "serde" ] } | |||||
thiserror = "1.0" | thiserror = "1.0" | ||||
vfs = "0.4" | vfs = "0.4" | ||||
vfs-zip = "0.2" | vfs-zip = "0.2" |
@@ -1,5 +1,11 @@ | |||||
use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||
use specs::{Component, HashMapStorage}; | |||||
use specs::{ | |||||
error::NoError, | |||||
saveload::{ConvertSaveload, Marker}, | |||||
Component, Entity, HashMapStorage, VecStorage, | |||||
}; | |||||
use crate::misc::FlaggedStorage; | |||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)] | #[derive(Clone, Debug, Default, Serialize, Deserialize)] | ||||
pub struct Fleet { | pub struct Fleet { | ||||
@@ -7,6 +13,46 @@ pub struct Fleet { | |||||
pub orbit_max: f32, | pub orbit_max: f32, | ||||
} | } | ||||
#[derive(Copy, Clone, Debug)] | |||||
pub struct Owned { | |||||
pub owner: Entity, | |||||
} | |||||
#[derive(Serialize, Deserialize)] | |||||
pub struct OwnedData<M> { | |||||
pub owner: M, | |||||
} | |||||
impl Component for Fleet { | impl Component for Fleet { | ||||
type Storage = HashMapStorage<Self>; | type Storage = HashMapStorage<Self>; | ||||
} | } | ||||
impl Component for Owned { | |||||
type Storage = FlaggedStorage<Self, VecStorage<Self>>; | |||||
} | |||||
impl<M> ConvertSaveload<M> for Owned | |||||
where | |||||
for<'de> M: Marker + Serialize + Deserialize<'de>, | |||||
{ | |||||
type Data = OwnedData<M>; | |||||
type Error = NoError; | |||||
fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error> | |||||
where | |||||
F: FnMut(Entity) -> Option<M>, | |||||
{ | |||||
let owner = ids(self.owner).unwrap(); | |||||
Ok(OwnedData { owner }) | |||||
} | |||||
fn convert_from<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error> | |||||
where | |||||
F: FnMut(M) -> Option<Entity>, | |||||
{ | |||||
let owner = ids(data.owner).unwrap(); | |||||
Ok(Owned { owner }) | |||||
} | |||||
} |
@@ -1,6 +1,5 @@ | |||||
mod asteroid; | mod asteroid; | ||||
mod fleet; | mod fleet; | ||||
mod owned; | |||||
mod planet; | mod planet; | ||||
mod player; | mod player; | ||||
mod position; | mod position; | ||||
@@ -8,10 +7,9 @@ mod ship; | |||||
mod velocity; | mod velocity; | ||||
pub use asteroid::{Asteroid, Type as AsteroidType}; | pub use asteroid::{Asteroid, Type as AsteroidType}; | ||||
pub use fleet::Fleet; | |||||
pub use owned::Owned; | |||||
pub use fleet::{Fleet, Owned as FleetOwned}; | |||||
pub use planet::Planet; | pub use planet::Planet; | ||||
pub use player::Player; | |||||
pub use player::{Owned as PlayerOwned, Player}; | |||||
pub use position::Position; | pub use position::Position; | ||||
pub use ship::{Ship, Type as ShipType}; | |||||
pub use ship::{Count as ShipCount, Ship, Type as ShipType}; | |||||
pub use velocity::Velocity; | pub use velocity::Velocity; |
@@ -1,46 +0,0 @@ | |||||
use serde::{Deserialize, Serialize}; | |||||
use specs::{ | |||||
error::NoError, | |||||
saveload::{ConvertSaveload, Marker}, | |||||
Component, Entity, VecStorage, | |||||
}; | |||||
#[derive(Clone, Debug)] | |||||
pub struct Owned { | |||||
pub owner: Entity, | |||||
} | |||||
#[derive(Serialize, Deserialize)] | |||||
pub struct OwnedData<M> { | |||||
pub owner: M, | |||||
} | |||||
impl Component for Owned { | |||||
type Storage = VecStorage<Self>; | |||||
} | |||||
impl<M> ConvertSaveload<M> for Owned | |||||
where | |||||
for<'de> M: Marker + Serialize + Deserialize<'de>, | |||||
{ | |||||
type Data = OwnedData<M>; | |||||
type Error = NoError; | |||||
fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error> | |||||
where | |||||
F: FnMut(Entity) -> Option<M>, | |||||
{ | |||||
let owner = ids(self.owner).unwrap(); | |||||
Ok(OwnedData { owner }) | |||||
} | |||||
fn convert_from<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error> | |||||
where | |||||
F: FnMut(M) -> Option<Entity>, | |||||
{ | |||||
let owner = ids(data.owner).unwrap(); | |||||
Ok(Owned { owner }) | |||||
} | |||||
} |
@@ -1,12 +1,58 @@ | |||||
use glc::vector::Vector4f; | use glc::vector::Vector4f; | ||||
use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||
use specs::{Component, HashMapStorage}; | |||||
use specs::{ | |||||
error::NoError, | |||||
saveload::{ConvertSaveload, Marker}, | |||||
Component, Entity, HashMapStorage, VecStorage, | |||||
}; | |||||
use crate::misc::FlaggedStorage; | |||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)] | #[derive(Clone, Debug, Default, Serialize, Deserialize)] | ||||
pub struct Player { | pub struct Player { | ||||
pub color: Vector4f, | pub color: Vector4f, | ||||
} | } | ||||
#[derive(Copy, Clone, Debug)] | |||||
pub struct Owned { | |||||
pub owner: Entity, | |||||
} | |||||
#[derive(Serialize, Deserialize)] | |||||
pub struct OwnedData<M> { | |||||
pub owner: M, | |||||
} | |||||
impl Component for Player { | impl Component for Player { | ||||
type Storage = HashMapStorage<Self>; | type Storage = HashMapStorage<Self>; | ||||
} | } | ||||
impl Component for Owned { | |||||
type Storage = FlaggedStorage<Self, VecStorage<Self>>; | |||||
} | |||||
impl<M> ConvertSaveload<M> for Owned | |||||
where | |||||
for<'de> M: Marker + Serialize + Deserialize<'de>, | |||||
{ | |||||
type Data = OwnedData<M>; | |||||
type Error = NoError; | |||||
fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error> | |||||
where | |||||
F: FnMut(Entity) -> Option<M>, | |||||
{ | |||||
let owner = ids(self.owner).unwrap(); | |||||
Ok(OwnedData { owner }) | |||||
} | |||||
fn convert_from<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error> | |||||
where | |||||
F: FnMut(M) -> Option<Entity>, | |||||
{ | |||||
let owner = ids(data.owner).unwrap(); | |||||
Ok(Owned { owner }) | |||||
} | |||||
} |
@@ -1,88 +1,53 @@ | |||||
use std::ops::{Index, IndexMut}; | |||||
use glc::{matrix::Angle, vector::Vector2f}; | use glc::{matrix::Angle, vector::Vector2f}; | ||||
use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||
use specs::{ | |||||
error::NoError, | |||||
saveload::{ConvertSaveload, Marker}, | |||||
Component, Entity, VecStorage, | |||||
}; | |||||
use specs::{Component, VecStorage}; | |||||
#[derive(Clone, Debug)] | |||||
#[derive(Clone, Debug, Serialize, Deserialize)] | |||||
pub struct Ship { | pub struct Ship { | ||||
pub type_: Type, | pub type_: Type, | ||||
pub fleet: Entity, | |||||
pub agility: Angle<f32>, | pub agility: Angle<f32>, | ||||
pub target_pos: Vector2f, | pub target_pos: Vector2f, | ||||
pub target_dir: Vector2f, | pub target_dir: Vector2f, | ||||
} | } | ||||
#[derive(Clone, Debug, Serialize, Deserialize)] | |||||
#[derive(Clone, Debug, Default)] | |||||
pub struct Count { | |||||
pub fighter: usize, | |||||
pub bomber: usize, | |||||
pub transporter: usize, | |||||
} | |||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)] | |||||
pub enum Type { | pub enum Type { | ||||
Fighter, | Fighter, | ||||
Bomber, | Bomber, | ||||
Transporter, | Transporter, | ||||
} | } | ||||
#[derive(Serialize, Deserialize)] | |||||
pub struct ShipData<M> { | |||||
pub type_: Type, | |||||
pub fleet: M, | |||||
pub agility: Angle<f32>, | |||||
pub target_pos: Vector2f, | |||||
pub target_dir: Vector2f, | |||||
} | |||||
impl Component for Ship { | impl Component for Ship { | ||||
type Storage = VecStorage<Self>; | type Storage = VecStorage<Self>; | ||||
} | } | ||||
impl<M> ConvertSaveload<M> for Ship | |||||
where | |||||
for<'de> M: Marker + Serialize + Deserialize<'de>, | |||||
{ | |||||
type Data = ShipData<M>; | |||||
type Error = NoError; | |||||
impl Index<Type> for Count { | |||||
type Output = usize; | |||||
fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error> | |||||
where | |||||
F: FnMut(Entity) -> Option<M>, | |||||
{ | |||||
let Ship { | |||||
type_, | |||||
fleet, | |||||
agility, | |||||
target_pos, | |||||
target_dir, | |||||
} = self.clone(); | |||||
let fleet = ids(fleet).unwrap(); | |||||
Ok(ShipData { | |||||
type_, | |||||
fleet, | |||||
agility, | |||||
target_pos, | |||||
target_dir, | |||||
}) | |||||
fn index(&self, index: Type) -> &Self::Output { | |||||
match index { | |||||
Type::Fighter => &self.fighter, | |||||
Type::Bomber => &self.bomber, | |||||
Type::Transporter => &self.transporter, | |||||
} | |||||
} | } | ||||
} | |||||
fn convert_from<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error> | |||||
where | |||||
F: FnMut(M) -> Option<Entity>, | |||||
{ | |||||
let ShipData { | |||||
type_, | |||||
fleet, | |||||
agility, | |||||
target_pos, | |||||
target_dir, | |||||
} = data; | |||||
let fleet = ids(fleet).unwrap(); | |||||
Ok(Ship { | |||||
type_, | |||||
fleet, | |||||
agility, | |||||
target_pos, | |||||
target_dir, | |||||
}) | |||||
impl IndexMut<Type> for Count { | |||||
fn index_mut(&mut self, index: Type) -> &mut Self::Output { | |||||
match index { | |||||
Type::Fighter => &mut self.fighter, | |||||
Type::Bomber => &mut self.bomber, | |||||
Type::Transporter => &mut self.transporter, | |||||
} | |||||
} | } | ||||
} | } |
@@ -0,0 +1,92 @@ | |||||
#![allow(dead_code)] | |||||
use hibitset::BitSetLike; | |||||
use shrev::EventChannel; | |||||
use specs::{ | |||||
storage::{TryDefault, UnprotectedStorage}, | |||||
world::Index, | |||||
Component, DenseVecStorage, | |||||
}; | |||||
pub struct FlaggedStorage<C, T = DenseVecStorage<C>> | |||||
where | |||||
C: Send + Sync + 'static, | |||||
{ | |||||
channel: EventChannel<ComponentEvent<C>>, | |||||
storage: T, | |||||
} | |||||
pub enum ComponentEvent<C> | |||||
where | |||||
C: Send + Sync + 'static, | |||||
{ | |||||
Inserted(Index), | |||||
Modified(Index, C), | |||||
Removed(Index, C), | |||||
} | |||||
impl<C, T> FlaggedStorage<C, T> | |||||
where | |||||
C: Send + Sync + 'static, | |||||
{ | |||||
pub fn channel(&self) -> &EventChannel<ComponentEvent<C>> { | |||||
&self.channel | |||||
} | |||||
pub fn channel_mut(&mut self) -> &mut EventChannel<ComponentEvent<C>> { | |||||
&mut self.channel | |||||
} | |||||
} | |||||
impl<C, T> Default for FlaggedStorage<C, T> | |||||
where | |||||
C: Send + Sync + 'static, | |||||
T: TryDefault, | |||||
{ | |||||
fn default() -> Self { | |||||
FlaggedStorage { | |||||
channel: EventChannel::new(), | |||||
storage: T::unwrap_default(), | |||||
} | |||||
} | |||||
} | |||||
impl<C: Component + Clone, T: UnprotectedStorage<C>> UnprotectedStorage<C> for FlaggedStorage<C, T> | |||||
where | |||||
C: Send + Sync + 'static, | |||||
{ | |||||
unsafe fn clean<B>(&mut self, has: B) | |||||
where | |||||
B: BitSetLike, | |||||
{ | |||||
self.storage.clean(has); | |||||
} | |||||
unsafe fn get(&self, id: Index) -> &C { | |||||
self.storage.get(id) | |||||
} | |||||
unsafe fn get_mut(&mut self, id: Index) -> &mut C { | |||||
let ret = self.storage.get_mut(id); | |||||
self.channel | |||||
.single_write(ComponentEvent::Modified(id, ret.clone())); | |||||
ret | |||||
} | |||||
unsafe fn insert(&mut self, id: Index, comp: C) { | |||||
self.storage.insert(id, comp); | |||||
self.channel.single_write(ComponentEvent::Inserted(id)); | |||||
} | |||||
unsafe fn remove(&mut self, id: Index) -> C { | |||||
let c = self.storage.remove(id); | |||||
self.channel | |||||
.single_write(ComponentEvent::Removed(id, c.clone())); | |||||
c | |||||
} | |||||
} |
@@ -1,3 +1,4 @@ | |||||
mod flagged_storage; | |||||
mod log; | mod log; | ||||
mod log_result; | mod log_result; | ||||
mod persistence; | mod persistence; | ||||
@@ -6,6 +7,7 @@ mod world; | |||||
pub use self::log::init as init_logger; | pub use self::log::init as init_logger; | ||||
pub use self::vfs::{Vfs, VfsError}; | pub use self::vfs::{Vfs, VfsError}; | ||||
pub use flagged_storage::{ComponentEvent, FlaggedStorage}; | |||||
pub use log_result::LogResult; | pub use log_result::LogResult; | ||||
pub use persistence::{PersistWorld, Persistence}; | pub use persistence::{PersistWorld, Persistence}; | ||||
pub use world::WorldHelper; | pub use world::WorldHelper; |
@@ -5,7 +5,7 @@ use specs::{ | |||||
Component, Entities, ReadStorage, World, WorldExt, Write, WriteStorage, | Component, Entities, ReadStorage, World, WorldExt, Write, WriteStorage, | ||||
}; | }; | ||||
use crate::components::{Fleet, Owned, Planet, Player, Position, Ship, Velocity}; | |||||
use crate::components::{Fleet, FleetOwned, Planet, Player, PlayerOwned, Position, Ship, Velocity}; | |||||
/* PersistWorld */ | /* PersistWorld */ | ||||
@@ -14,7 +14,16 @@ pub struct PersistWorldMarker; | |||||
impl Persistence for PersistWorld { | impl Persistence for PersistWorld { | ||||
type Marker = SimpleMarker<PersistWorldMarker>; | type Marker = SimpleMarker<PersistWorldMarker>; | ||||
type Components = (Position, Velocity, Planet, Ship, Owned, Player, Fleet); | |||||
type Components = ( | |||||
Position, | |||||
Velocity, | |||||
Planet, | |||||
Ship, | |||||
Player, | |||||
PlayerOwned, | |||||
Fleet, | |||||
FleetOwned, | |||||
); | |||||
} | } | ||||
/* Persistence */ | /* Persistence */ | ||||
@@ -7,7 +7,7 @@ use rand::random; | |||||
use specs::{prelude::*, ParJoin, Read, ReadStorage, System, WriteStorage}; | use specs::{prelude::*, ParJoin, Read, ReadStorage, System, WriteStorage}; | ||||
use crate::{ | use crate::{ | ||||
components::{Fleet, Position, Ship, Velocity}, | |||||
components::{Fleet, FleetOwned, Position, Ship, Velocity}, | |||||
constants::{ | constants::{ | ||||
SHIP_ORBIT_AGILITY, SHIP_ORBIT_ANGLE_DELTA_MIN, SHIP_ORBIT_ANGLE_DELTA_RND, | SHIP_ORBIT_AGILITY, SHIP_ORBIT_ANGLE_DELTA_MIN, SHIP_ORBIT_ANGLE_DELTA_RND, | ||||
SHIP_ORBIT_DISTANCE_MAX, VECTOR_2F_POS_X, | SHIP_ORBIT_DISTANCE_MAX, VECTOR_2F_POS_X, | ||||
@@ -22,6 +22,7 @@ pub struct Fleets; | |||||
pub struct FleetsData<'a> { | pub struct FleetsData<'a> { | ||||
ship: WriteStorage<'a, Ship>, | ship: WriteStorage<'a, Ship>, | ||||
velocity: WriteStorage<'a, Velocity>, | velocity: WriteStorage<'a, Velocity>, | ||||
fleet_owned: ReadStorage<'a, FleetOwned>, | |||||
position: ReadStorage<'a, Position>, | position: ReadStorage<'a, Position>, | ||||
fleet: ReadStorage<'a, Fleet>, | fleet: ReadStorage<'a, Fleet>, | ||||
global: Read<'a, Global>, | global: Read<'a, Global>, | ||||
@@ -34,15 +35,24 @@ impl<'a> System<'a> for Fleets { | |||||
let FleetsData { | let FleetsData { | ||||
mut ship, | mut ship, | ||||
mut velocity, | mut velocity, | ||||
fleet_owned, | |||||
position, | position, | ||||
fleet, | fleet, | ||||
global, | global, | ||||
} = data; | } = data; | ||||
(&mut ship, &mut velocity, &position) | |||||
(&mut ship, &mut velocity, &fleet_owned, &position) | |||||
.par_join() | .par_join() | ||||
.for_each(|(ship, vel, pos)| { | |||||
progress_ship(&position, &fleet, ship, vel, pos, global.delta) | |||||
.for_each(|(ship, vel, fleet_owned, pos)| { | |||||
progress_ship( | |||||
&position, | |||||
&fleet, | |||||
ship, | |||||
vel, | |||||
fleet_owned.owner, | |||||
pos, | |||||
global.delta, | |||||
) | |||||
}); | }); | ||||
} | } | ||||
} | } | ||||
@@ -52,11 +62,12 @@ fn progress_ship<'a>( | |||||
fleets: &ReadStorage<'a, Fleet>, | fleets: &ReadStorage<'a, Fleet>, | ||||
ship: &mut Ship, | ship: &mut Ship, | ||||
velocity: &mut Velocity, | velocity: &mut Velocity, | ||||
fleet_id: Entity, | |||||
position: &Position, | position: &Position, | ||||
delta: f32, | delta: f32, | ||||
) { | ) { | ||||
let (orbit_pos, orbit_min, orbit_max): (Vector2f, f32, f32) = | let (orbit_pos, orbit_min, orbit_max): (Vector2f, f32, f32) = | ||||
match (positions.get(ship.fleet), fleets.get(ship.fleet)) { | |||||
match (positions.get(fleet_id), fleets.get(fleet_id)) { | |||||
(Some(position), Some(fleet)) => (position.pos, fleet.orbit_min, fleet.orbit_max), | (Some(position), Some(fleet)) => (position.pos, fleet.orbit_min, fleet.orbit_max), | ||||
(_, _) => return, | (_, _) => return, | ||||
}; | }; | ||||