| @@ -1530,6 +1530,7 @@ name = "space-crush-common" | |||
| version = "0.1.0" | |||
| dependencies = [ | |||
| "glc", | |||
| "hibitset", | |||
| "lazy_static", | |||
| "log", | |||
| "log4rs", | |||
| @@ -1737,7 +1738,7 @@ version = "0.2.1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "d68a369614cd12ca384ca6e75ab0a5ac3a5acbfe8003622e20808a322b9bb652" | |||
| dependencies = [ | |||
| "flate2 0.2.20", | |||
| "flate2 1.0.14", | |||
| "vfs", | |||
| "zip", | |||
| ] | |||
| @@ -20,5 +20,5 @@ shred = { version = "0.10", features = [ "shred-derive" ] } | |||
| shrev = "1.1" | |||
| smallvec = { version = "1.5", features = [ "serde" ] } | |||
| space-crush-common = "0.1" | |||
| specs = "0.16" | |||
| specs = "*" | |||
| 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() { | |||
| 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), | |||
| ); | |||
| 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), | |||
| ); | |||
| 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), | |||
| ); | |||
| } | |||
| @@ -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; | |||
| use debug::{Fleets as DebugFleets, Ships as DebugShips, Summary as DebugSummary}; | |||
| use misc::{Events, TextManager, Window}; | |||
| 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> { | |||
| is_running: bool, | |||
| @@ -24,7 +25,7 @@ pub struct 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 events = Events::new(world)?; | |||
| let window = Window::new(events.handle(), &config)?; | |||
| @@ -33,17 +34,20 @@ impl<'a, 'b> App<'a, 'b> { | |||
| let camera = Camera::new()?; | |||
| let uniform = Uniform::new()?; | |||
| let geometry = Geometry::new(world)?; | |||
| let player_state = PlayerState::new(player_id); | |||
| world.insert(state); | |||
| world.insert(config); | |||
| world.insert(camera); | |||
| world.insert(uniform); | |||
| world.insert(geometry); | |||
| world.insert(player_state); | |||
| let text_manager = TextManager::new(world)?; | |||
| let mut dispatcher = DispatcherBuilder::new() | |||
| .with(StateUpdate::new(world)?, "state_update", &[]) | |||
| .with(FleetInfoUpdate::new(world)?, "fleet_info_update", &[]) | |||
| .with_thread_local(Init::new(world)?) | |||
| .with_thread_local(Planets::new(world)?) | |||
| .with_thread_local(Asteroids::new(world)?) | |||
| @@ -5,7 +5,7 @@ use space_crush_common::{ | |||
| misc::{init_logger, Vfs}, | |||
| Dispatcher, | |||
| }; | |||
| use specs::{World, WorldExt}; | |||
| use specs::{Builder, Entity, World, WorldExt}; | |||
| fn main() -> Result<(), Error> { | |||
| let vfs = Vfs::new(&["space-crush-app"])?; | |||
| @@ -33,12 +33,12 @@ fn run(vfs: Vfs) -> Result<(), Error> { | |||
| let mut world = World::new(); | |||
| world.insert(vfs); | |||
| let player1 = world.create_entity().build(); | |||
| 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"); | |||
| @@ -49,31 +49,35 @@ fn run(vfs: Vfs) -> Result<(), Error> { | |||
| app.process(&world)?; | |||
| } | |||
| save_world(&mut world, WORLD_FILEPATH)?; | |||
| Ok(()) | |||
| } | |||
| fn create_world(world: &mut World) { | |||
| fn create_world(world: &mut World, player_id: Entity) { | |||
| use glc::{ | |||
| matrix::Angle, | |||
| vector::{Vector2f, Vector4f}, | |||
| }; | |||
| use space_crush_app::components::FleetInfo; | |||
| use space_crush_common::{ | |||
| 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}, | |||
| }; | |||
| 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 | |||
| .create_entity() | |||
| @@ -86,6 +90,7 @@ fn create_world(world: &mut World) { | |||
| orbit_min: 125.0, | |||
| orbit_max: 175.0, | |||
| }) | |||
| .with(FleetInfo::default()) | |||
| .with(Asteroid { | |||
| type_: AsteroidType::Metal, | |||
| }) | |||
| @@ -102,6 +107,7 @@ fn create_world(world: &mut World) { | |||
| orbit_min: 125.0, | |||
| orbit_max: 175.0, | |||
| }) | |||
| .with(FleetInfo::default()) | |||
| .with(Asteroid { | |||
| type_: AsteroidType::Crystal, | |||
| }) | |||
| @@ -110,7 +116,7 @@ fn create_world(world: &mut World) { | |||
| let planet = world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .with(Owned { owner: player1 }) | |||
| .with(PlayerOwned { owner: player_id }) | |||
| .with(Position { | |||
| pos: Vector2f::default(), | |||
| size: 250.0, | |||
| @@ -119,6 +125,7 @@ fn create_world(world: &mut World) { | |||
| orbit_min: 325.0, | |||
| orbit_max: 425.0, | |||
| }) | |||
| .with(FleetInfo::default()) | |||
| .with(Planet {}) | |||
| .build(); | |||
| @@ -126,7 +133,7 @@ fn create_world(world: &mut World) { | |||
| world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .with(Owned { owner: player1 }) | |||
| .with(PlayerOwned { owner: player_id }) | |||
| .with(Position { | |||
| pos: Vector2f::new( | |||
| 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(), | |||
| speed: 100.0, | |||
| }) | |||
| .with(FleetOwned { owner: planet }) | |||
| .with(Ship { | |||
| type_: match i % 3 { | |||
| 0 => ShipType::Fighter, | |||
| @@ -145,7 +153,6 @@ fn create_world(world: &mut World) { | |||
| 2 => ShipType::Transporter, | |||
| _ => unreachable!(), | |||
| }, | |||
| fleet: planet, | |||
| agility: Angle::Deg(360.0), | |||
| target_pos: Default::default(), | |||
| target_dir: Default::default(), | |||
| @@ -153,37 +160,3 @@ fn create_world(world: &mut World) { | |||
| .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.height - window_size.height) / 2, | |||
| )); | |||
| window.set_cursor_grab(true).unwrap(); | |||
| let context = unsafe { context.make_current().unwrap() }; | |||
| gl::load_with(|s| context.get_proc_address(s)); | |||
| @@ -6,7 +6,7 @@ use glc::{ | |||
| vector::Vector4f, | |||
| }; | |||
| use space_crush_common::{ | |||
| components::{Asteroid, AsteroidType, Owned, Player, Position}, | |||
| components::{Asteroid, AsteroidType, Player, PlayerOwned, Position}, | |||
| misc::LogResult, | |||
| }; | |||
| use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | |||
| @@ -61,7 +61,7 @@ pub struct AsteroidsData<'a> { | |||
| geometry: ReadExpect<'a, Geometry>, | |||
| position: ReadStorage<'a, Position>, | |||
| asteroid: ReadStorage<'a, Asteroid>, | |||
| owned: ReadStorage<'a, Owned>, | |||
| owned: ReadStorage<'a, PlayerOwned>, | |||
| player: ReadStorage<'a, Player>, | |||
| } | |||
| @@ -6,7 +6,7 @@ use glc::{ | |||
| vector::Vector4f, | |||
| }; | |||
| use space_crush_common::{ | |||
| components::{Owned, Planet, Player, Position}, | |||
| components::{Planet, Player, PlayerOwned, Position}, | |||
| misc::LogResult, | |||
| }; | |||
| use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | |||
| @@ -58,7 +58,7 @@ pub struct PlanetsData<'a> { | |||
| geometry: ReadExpect<'a, Geometry>, | |||
| position: ReadStorage<'a, Position>, | |||
| planet: ReadStorage<'a, Planet>, | |||
| owned: ReadStorage<'a, Owned>, | |||
| owned: ReadStorage<'a, PlayerOwned>, | |||
| player: ReadStorage<'a, Player>, | |||
| } | |||
| @@ -6,7 +6,7 @@ use glc::{ | |||
| vector::Vector4f, | |||
| }; | |||
| use space_crush_common::{ | |||
| components::{Owned, Player, Position, Ship, ShipType, Velocity}, | |||
| components::{Player, PlayerOwned, Position, Ship, ShipType, Velocity}, | |||
| misc::LogResult, | |||
| }; | |||
| use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | |||
| @@ -65,7 +65,7 @@ pub struct ShipsData<'a> { | |||
| position: ReadStorage<'a, Position>, | |||
| velocity: ReadStorage<'a, Velocity>, | |||
| ship: ReadStorage<'a, Ship>, | |||
| owned: ReadStorage<'a, Owned>, | |||
| owned: ReadStorage<'a, PlayerOwned>, | |||
| player: ReadStorage<'a, Player>, | |||
| } | |||
| @@ -1,11 +1,13 @@ | |||
| mod camera; | |||
| mod config; | |||
| mod geometry; | |||
| mod player_state; | |||
| mod state; | |||
| mod uniform; | |||
| pub use camera::Camera; | |||
| pub use config::Config; | |||
| pub use geometry::Geometry; | |||
| pub use player_state::PlayerState; | |||
| pub use state::State; | |||
| 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; | |||
| pub use fleet_info_update::FleetInfoUpdate; | |||
| pub use state_update::StateUpdate; | |||
| @@ -6,6 +6,7 @@ edition = "2018" | |||
| [dependencies] | |||
| glc = { version = "0.1", features = [ "serde" ] } | |||
| hibitset = "0.6" | |||
| log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn" ] } | |||
| lazy_static = "1.4" | |||
| log4rs = "0.13" | |||
| @@ -14,7 +15,7 @@ serde = "1.0" | |||
| serde_yaml = "0.8" | |||
| shred = { version = "0.10", features = [ "shred-derive" ] } | |||
| shrev = "1.1" | |||
| specs = { version = "0.16", features = [ "serde" ] } | |||
| specs = { version = "*", features = [ "serde" ] } | |||
| thiserror = "1.0" | |||
| vfs = "0.4" | |||
| vfs-zip = "0.2" | |||
| @@ -1,5 +1,11 @@ | |||
| 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)] | |||
| pub struct Fleet { | |||
| @@ -7,6 +13,46 @@ pub struct Fleet { | |||
| 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 { | |||
| 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 fleet; | |||
| mod owned; | |||
| mod planet; | |||
| mod player; | |||
| mod position; | |||
| @@ -8,10 +7,9 @@ mod ship; | |||
| mod velocity; | |||
| 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 player::Player; | |||
| pub use player::{Owned as PlayerOwned, Player}; | |||
| 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; | |||
| @@ -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 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)] | |||
| pub struct Player { | |||
| 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 { | |||
| 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 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 type_: Type, | |||
| pub fleet: Entity, | |||
| pub agility: Angle<f32>, | |||
| pub target_pos: 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 { | |||
| Fighter, | |||
| Bomber, | |||
| 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 { | |||
| 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_result; | |||
| mod persistence; | |||
| @@ -6,6 +7,7 @@ mod world; | |||
| pub use self::log::init as init_logger; | |||
| pub use self::vfs::{Vfs, VfsError}; | |||
| pub use flagged_storage::{ComponentEvent, FlaggedStorage}; | |||
| pub use log_result::LogResult; | |||
| pub use persistence::{PersistWorld, Persistence}; | |||
| pub use world::WorldHelper; | |||
| @@ -5,7 +5,7 @@ use specs::{ | |||
| 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 */ | |||
| @@ -14,7 +14,16 @@ pub struct PersistWorldMarker; | |||
| impl Persistence for PersistWorld { | |||
| type Marker = SimpleMarker<PersistWorldMarker>; | |||
| type Components = (Position, Velocity, Planet, Ship, Owned, Player, Fleet); | |||
| type Components = ( | |||
| Position, | |||
| Velocity, | |||
| Planet, | |||
| Ship, | |||
| Player, | |||
| PlayerOwned, | |||
| Fleet, | |||
| FleetOwned, | |||
| ); | |||
| } | |||
| /* Persistence */ | |||
| @@ -7,7 +7,7 @@ use rand::random; | |||
| use specs::{prelude::*, ParJoin, Read, ReadStorage, System, WriteStorage}; | |||
| use crate::{ | |||
| components::{Fleet, Position, Ship, Velocity}, | |||
| components::{Fleet, FleetOwned, Position, Ship, Velocity}, | |||
| constants::{ | |||
| SHIP_ORBIT_AGILITY, SHIP_ORBIT_ANGLE_DELTA_MIN, SHIP_ORBIT_ANGLE_DELTA_RND, | |||
| SHIP_ORBIT_DISTANCE_MAX, VECTOR_2F_POS_X, | |||
| @@ -22,6 +22,7 @@ pub struct Fleets; | |||
| pub struct FleetsData<'a> { | |||
| ship: WriteStorage<'a, Ship>, | |||
| velocity: WriteStorage<'a, Velocity>, | |||
| fleet_owned: ReadStorage<'a, FleetOwned>, | |||
| position: ReadStorage<'a, Position>, | |||
| fleet: ReadStorage<'a, Fleet>, | |||
| global: Read<'a, Global>, | |||
| @@ -34,15 +35,24 @@ impl<'a> System<'a> for Fleets { | |||
| let FleetsData { | |||
| mut ship, | |||
| mut velocity, | |||
| fleet_owned, | |||
| position, | |||
| fleet, | |||
| global, | |||
| } = data; | |||
| (&mut ship, &mut velocity, &position) | |||
| (&mut ship, &mut velocity, &fleet_owned, &position) | |||
| .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>, | |||
| ship: &mut Ship, | |||
| velocity: &mut Velocity, | |||
| fleet_id: Entity, | |||
| position: &Position, | |||
| delta: 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), | |||
| (_, _) => return, | |||
| }; | |||