diff --git a/games/rstnode/Cargo.lock b/games/rstnode/Cargo.lock index 33713c4b097..a222eccc771 100644 --- a/games/rstnode/Cargo.lock +++ b/games/rstnode/Cargo.lock @@ -3878,6 +3878,7 @@ dependencies = [ "async-std", "async-trait", "chrono", + "clap", "num_cpus", "rst-core", "systemstat", diff --git a/games/rstnode/rst-client/src/cli.rs b/games/rstnode/rst-client/src/cli.rs index dcc40b76726..72d598962b8 100644 --- a/games/rstnode/rst-client/src/cli.rs +++ b/games/rstnode/rst-client/src/cli.rs @@ -21,8 +21,8 @@ pub fn parse(settings: &mut GameSettings) { ) .arg( Arg::with_name("assets") - .required(true) .takes_value(true) + .default_value("./assets/raw") .help("Specify the path to load assets from"), ) .arg( diff --git a/games/rstnode/rst-client/src/editor/mod.rs b/games/rstnode/rst-client/src/editor/mod.rs index 969ac27088a..6f5f0c67b3a 100644 --- a/games/rstnode/rst-client/src/editor/mod.rs +++ b/games/rstnode/rst-client/src/editor/mod.rs @@ -1,4 +1,6 @@ -use crate::{assets::Assets, input::InputArbiter, ui::Button, viewport::Viewport, GameSettings}; +use crate::{ + assets::Assets, input::InputArbiter, ui::Button, viewport::Viewport, EventId, GameSettings, +}; use ggez::{ event::{EventHandler, MouseButton}, graphics::{self, Color}, @@ -10,12 +12,14 @@ pub struct EditorState { settings: GameSettings, input: InputArbiter, vp: Viewport, - btn: Button, + btn: EventId, + btn2: EventId, } impl EditorState { pub fn new(settings: GameSettings, assets: Assets) -> Self { info!("Initialising map editor state"); + Self { assets, settings, @@ -26,7 +30,15 @@ impl EditorState { (250.0, 125.0).into(), Some("Create Node".into()), Color::from_rgb(50, 50, 50), - ), + ) + .register(), + btn2: Button::new( + (300.0, 25.0).into(), + (250.0, 125.0).into(), + Some("Destroy Node".into()), + Color::from_rgb(50, 50, 50), + ) + .register(), } } } @@ -36,15 +48,7 @@ impl EventHandler for EditorState { Ok(()) } - fn mouse_button_down_event(&mut self, ctx: &mut Context, btn: MouseButton, x: f32, y: f32) { - self.btn.mouse_button_down_event(ctx, btn, x, y) - } - fn draw(&mut self, ctx: &mut Context) -> GameResult<()> { - graphics::clear(ctx, graphics::Color::from_rgb(15, 15, 15)); - - self.btn.draw(ctx)?; - - graphics::present(ctx) + Ok(()) } } diff --git a/games/rstnode/rst-client/src/event.rs b/games/rstnode/rst-client/src/event.rs index 54388bb8c43..88a85ed0fce 100644 --- a/games/rstnode/rst-client/src/event.rs +++ b/games/rstnode/rst-client/src/event.rs @@ -1,17 +1,73 @@ use ggez::{ event::{EventHandler, KeyCode, KeyMods, MouseButton}, - Context, GameResult, + graphics, Context, GameResult, }; +use std::collections::BTreeMap; +use std::sync::{ + atomic::{AtomicUsize, Ordering}, + mpsc::{channel, Receiver, Sender}, +}; + +static ID_CTR: AtomicUsize = AtomicUsize::new(0); + +static mut tx: Option> = None; +static mut rx: Option> = None; + +#[inline(always)] +pub fn get_tx() -> Sender { + unsafe { tx.as_ref() }.expect("Call event::setup() first!").clone() +} + +/// Must absolutely call this function before using the event system! +pub fn setup() { + let (tx_, rx_) = channel(); + unsafe { tx = Some(tx_) }; + unsafe { rx = Some(rx_) }; +} -pub struct EventLayer { - layers: Vec>, +pub type EventId = usize; + +pub fn new_id() -> EventId { + ID_CTR.fetch_add(1, Ordering::Relaxed) +} + +pub type BoxedHandler = Box; + +pub enum HandlerDiff { + Add { id: usize, inner: BoxedHandler }, + Remove(usize), +} + +impl HandlerDiff { + pub fn add(inner: impl EventHandler + 'static) -> (usize, Self) { + let id = new_id(); + ( + id, + Self::Add { + id, + inner: Box::new(inner), + }, + ) + } + + pub fn remove(id: usize) -> Self { + Self::Remove(id) + } +} + +pub struct EventLayer { + layers: BTreeMap, + layer_rx: Receiver, render: L, } impl EventLayer { pub fn new(render: L) -> Self { + let layer_rx = unsafe { rx.take().expect("Call event::setup() first!") }; + Self { - layers: vec![], + layers: BTreeMap::new(), + layer_rx, render, } } @@ -19,38 +75,59 @@ impl EventLayer { impl EventHandler for EventLayer { fn update(&mut self, ctx: &mut Context) -> GameResult<()> { + // Add all new layers to the update stack + while let Ok(l) = self.layer_rx.try_recv() { + match l { + HandlerDiff::Add { id, inner } => { + self.layers.insert(id, inner); + } + HandlerDiff::Remove(ref id) => { + self.layers.remove(id); + } + } + } + + // Then update all the layers self.layers .iter_mut() - .map(|l| l.update(ctx)) + .map(|(_, l)| l.update(ctx)) .collect::>>()?; + // Update the renderer last self.render.update(ctx)?; Ok(()) } fn draw(&mut self, ctx: &mut Context) -> GameResult<()> { + graphics::clear(ctx, graphics::Color::from_rgb(15, 15, 15)); self.render.draw(ctx)?; - Ok(()) + + self.layers + .iter_mut() + .map(|(_, l)| l.draw(ctx)) + .collect::>(); + + graphics::present(ctx) } fn mouse_button_down_event(&mut self, ctx: &mut Context, button: MouseButton, x: f32, y: f32) { self.layers .iter_mut() - .map(|l| l.mouse_button_down_event(ctx, button, x, y)) + .map(|(_, l)| l.mouse_button_down_event(ctx, button, x, y)) .collect::>(); } fn mouse_button_up_event(&mut self, ctx: &mut Context, button: MouseButton, x: f32, y: f32) { self.layers .iter_mut() - .map(|l| l.mouse_button_up_event(ctx, button, x, y)) + .map(|(_, l)| l.mouse_button_up_event(ctx, button, x, y)) .collect::>(); } fn mouse_motion_event(&mut self, ctx: &mut Context, x: f32, y: f32, dx: f32, dy: f32) { self.layers .iter_mut() - .map(|l| l.mouse_motion_event(ctx, x, y, dx, dy)) + .map(|(_, l)| l.mouse_motion_event(ctx, x, y, dx, dy)) .collect::>(); } @@ -69,7 +146,7 @@ impl EventHandler for EventLayer { ) { self.layers .iter_mut() - .map(|l| l.key_down_event(ctx, keycode, keymods, repeat)) + .map(|(_, l)| l.key_down_event(ctx, keycode, keymods, repeat)) .collect::>(); } @@ -77,7 +154,7 @@ impl EventHandler for EventLayer { fn key_up_event(&mut self, ctx: &mut Context, keycode: KeyCode, keymods: KeyMods) { self.layers .iter_mut() - .map(|l| l.key_up_event(ctx, keycode, keymods)) + .map(|(_, l)| l.key_up_event(ctx, keycode, keymods)) .collect::>(); } @@ -86,7 +163,7 @@ impl EventHandler for EventLayer { fn text_input_event(&mut self, ctx: &mut Context, character: char) { self.layers .iter_mut() - .map(|l| l.text_input_event(ctx, character)) + .map(|(_, l)| l.text_input_event(ctx, character)) .collect::>(); } } diff --git a/games/rstnode/rst-client/src/main.rs b/games/rstnode/rst-client/src/main.rs index b0acb22e26d..3dd93012f47 100644 --- a/games/rstnode/rst-client/src/main.rs +++ b/games/rstnode/rst-client/src/main.rs @@ -23,9 +23,9 @@ mod ui; mod viewport; mod window; +use crate::{assets::Assets, window::Window}; pub(crate) use editor::*; pub(crate) use event::*; -#[allow(unused)] pub(crate) use settings::{GameSettings, GraphicsSettings, WindowSettings}; pub(crate) use state::*; @@ -41,28 +41,30 @@ async fn main() { log::initialise(); // Initialise window context - let mut window = window::create(&settings); + let mut win = window::create(&settings); // Load assets tree - let assets = - assets::load_tree(window.ctx(), &settings).unwrap_or_else(|e| fatal!("LoadError: {}!", e)); + let assets = assets::load_tree(win.ctx(), &settings) + .unwrap_or_else(|e| fatal!("Asset tree failed to load: {}!", e)); - // Either create client state or editor state - if settings.editor { - let state = EditorState::new(settings, assets); + // Event system setup + event::setup(); - // Initialise the viewport first! - // state.viewport().init(window.ctx()); + match settings.editor { + true => run_editor(win, settings, assets), + false => run_game(win, settings, assets), + } +} - // Window goes brrrr - window.run(state) - } else { - let mut state = ClientState::new(settings, assets); +fn run_editor(win: Window, settings: GameSettings, assets: Assets) { + let state = EventLayer::new(EditorState::new(settings, assets)); + win.run(state); +} - // Initialise the viewport first! - state.viewport().init(window.ctx()); +fn run_game(mut win: Window, settings: GameSettings, assets: Assets) { + let mut state = ClientState::new(settings, assets); + state.viewport().init(win.ctx()); - // Window goes brrrr - window.run(state) - } + let mut layer = EventLayer::new(state); + win.run(layer) } diff --git a/games/rstnode/rst-client/src/ui/button.rs b/games/rstnode/rst-client/src/ui/button.rs index 9d51b0271cb..c39f905360d 100644 --- a/games/rstnode/rst-client/src/ui/button.rs +++ b/games/rstnode/rst-client/src/ui/button.rs @@ -1,4 +1,4 @@ -use crate::color::ColorUtils; +use crate::{color::ColorUtils, get_tx, EventId, HandlerDiff}; use ggez::graphics::{self, Align, Color, DrawMode, DrawParam, Drawable, MeshBuilder, Rect, Text}; use ggez::{ event::{EventHandler, MouseButton}, @@ -38,13 +38,16 @@ impl Button { } /// Register an on-click listening closure - pub fn on_click () + 'static>(&mut self, btn: MouseButton, cb: C) { + pub fn on_click () + 'static>(mut self, btn: MouseButton, cb: C) -> Self { self.cbs.insert(btn, Box::new(cb)); + self } - /// Create a button with text which auto-infers its required size - pub fn auto(pos: Pos, text: String, color: impl Into) -> Self { - todo!() + /// Register the button with the event handler + pub fn register(self) -> EventId { + let (btn_id, diff) = HandlerDiff::add(self); + get_tx().send(diff).unwrap(); + btn_id } fn boundry_check(&self, x: f32, y: f32) -> bool { @@ -70,6 +73,13 @@ impl EventHandler for Button { } } + fn mouse_button_up_event(&mut self, _ctx: &mut Context, btn: MouseButton, x: f32, y: f32) { + if self.boundry_check(x, y) { + trace!("Releasing button state"); + std::mem::swap(&mut self.color_base, &mut self.color_trim); + } + } + fn draw(&mut self, ctx: &mut Context) -> GameResult<()> { let mut mb = MeshBuilder::new(); let bounds = Rect { diff --git a/games/rstnode/rst-core/src/lib.rs b/games/rstnode/rst-core/src/lib.rs index bbf0e8e5933..6e35d193786 100644 --- a/games/rstnode/rst-core/src/lib.rs +++ b/games/rstnode/rst-core/src/lib.rs @@ -30,16 +30,15 @@ pub use _if::GameIf; mod _match; pub use _match::Match; -pub mod loader; pub mod config; pub mod data; pub mod error; pub mod gens; pub mod io; +pub mod loader; pub mod lobby; pub mod mailbox; pub mod map; -pub mod mapstore; pub mod net; pub mod server; pub mod stats; diff --git a/games/rstnode/rst-core/src/loader.rs b/games/rstnode/rst-core/src/loader.rs index 13cb3773fbf..4bfbccfff65 100644 --- a/games/rstnode/rst-core/src/loader.rs +++ b/games/rstnode/rst-core/src/loader.rs @@ -36,31 +36,33 @@ pub struct MapLoader { impl MapLoader { pub fn load_path(path: PathBuf) -> Self { - Self { - root: path.clone(), - inner: fs::read_dir(&path) - .unwrap() - .filter_map(|map| { - let map = map.unwrap(); - let name = map.file_name().into_string().unwrap(); + info!("Loading map path: {}", path.to_str().unwrap()); + let root = path.clone(); + let inner = fs::read_dir(&path) + .unwrap() + .filter_map(|map| { + let map = map.unwrap(); + let name = map.file_name().into_string().unwrap(); - if map.path().is_dir() { - warn!("Directories in map path will be ignored: {}!", name); - None - } else { - let np = map.path().with_extension(""); - let name = np.file_name().unwrap().to_str().unwrap(); + if map.path().is_dir() { + warn!("Directories in map path will be ignored: {}!", name); + None + } else { + let np = map.path().with_extension(""); + let name = np.file_name().unwrap().to_str().unwrap(); - debug!("Loading map {}", name); - let mut c = String::new(); - let mut f = File::open(map.path()).unwrap(); - f.read_to_string(&mut c).unwrap(); + debug!("Loading map {}", name); + let mut c = String::new(); + let mut f = File::open(map.path()).unwrap(); + f.read_to_string(&mut c).unwrap(); - Some((name.into(), serde_yaml::from_str(&c).unwrap())) - } - }) - .collect(), - } + Some((name.into(), serde_yaml::from_str(&c).unwrap())) + } + }) + .collect(); + + info!("Loading complete!"); + Self { root, inner } } /// Save all maps that are present currently diff --git a/games/rstnode/rst-core/src/mapstore.rs b/games/rstnode/rst-core/src/mapstore.rs deleted file mode 100644 index eaacba34f27..00000000000 --- a/games/rstnode/rst-core/src/mapstore.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! Map store - -use crate::config::MapCfg; -use std::{collections::BTreeMap, fs, path::Path}; - -#[deprecated] -pub struct MapStore { - configs: BTreeMap, -} - -impl MapStore { - /// Load a set of map configs - pub fn load_path(&mut self, path: &Path) { - fs::read_dir(&path).unwrap().for_each(|d| { - let name = d.unwrap().file_name().into_string().unwrap(); - }); - } -} diff --git a/games/rstnode/rst-server/Cargo.toml b/games/rstnode/rst-server/Cargo.toml index 8903e637fc3..b6822fff019 100644 --- a/games/rstnode/rst-server/Cargo.toml +++ b/games/rstnode/rst-server/Cargo.toml @@ -11,6 +11,7 @@ rst-core = { path = "../rst-core" } async-std = { version = "1.0", features = ["unstable", "attributes"] } async-trait = "0.1" +clap = "2.0" chrono = { version = "0.4", features = ["serde"] } tracing = "0.1" tracing-subscriber = "0.2" diff --git a/games/rstnode/rst-server/src/main.rs b/games/rstnode/rst-server/src/main.rs index 2766f1f3c97..593c12dc7f7 100644 --- a/games/rstnode/rst-server/src/main.rs +++ b/games/rstnode/rst-server/src/main.rs @@ -6,16 +6,20 @@ #[macro_use] extern crate tracing; -pub(crate) mod loader; +pub(crate) mod cli; pub(crate) mod constants; +pub(crate) mod loader; pub(crate) mod log; -use rst_core::net::Endpoint; +use rst_core::{loader::MapLoader, net::Endpoint}; #[async_std::main] async fn main() { log::initialise(); + let map_path = cli::parse(); + let maps = MapLoader::load_path(map_path); + let addr = format!("{}:{}", constants::DEFAULT_BIND, constants::DEFAULT_PORT); let serv = Endpoint::new(addr.as_str()).await; // serv.listen().await;