From effbdeed66e8de8e769b8ac069926ad1a9110e62 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 14 Feb 2021 00:06:14 +0100 Subject: rstnode: refactoring server and client components into rst-core * Add an inbox/ outbox system to server components * Define a data flow from Request -> computation -> Update * Create simple handlers to call server or client code for requests --- games/rstnode/Cargo.lock | 215 +++++++++++++++++++-- games/rstnode/Cargo.toml | 3 + games/rstnode/log.rs | 61 ++++++ games/rstnode/rst-client/Cargo.toml | 10 +- .../rst-client/src/graphics/entities/mod.rs | 35 +++- games/rstnode/rst-client/src/graphics/mod.rs | 2 +- games/rstnode/rst-client/src/input.rs | 2 +- games/rstnode/rst-client/src/main.rs | 6 +- games/rstnode/rst-client/src/net.rs | 1 + games/rstnode/rst-client/src/settings.rs | 1 - games/rstnode/rst-client/src/state.rs | 94 --------- games/rstnode/rst-client/src/state/_match.rs | 0 games/rstnode/rst-client/src/state/if_impl.rs | 70 +++++++ games/rstnode/rst-client/src/state/map.rs | 10 + games/rstnode/rst-client/src/state/mod.rs | 129 +++++++++++++ games/rstnode/rst-core/Cargo.toml | 1 + games/rstnode/rst-core/src/_if.rs | 4 +- games/rstnode/rst-core/src/_match.rs | 12 +- games/rstnode/rst-core/src/data.rs | 49 +++-- games/rstnode/rst-core/src/error.rs | 51 +++++ games/rstnode/rst-core/src/lib.rs | 9 + games/rstnode/rst-core/src/lobby.rs | 3 + games/rstnode/rst-core/src/mailbox.rs | 78 ++++++++ games/rstnode/rst-core/src/map.rs | 20 +- games/rstnode/rst-core/src/net/endpoint.rs | 49 +++++ games/rstnode/rst-core/src/net/gen.rs | 52 +++++ games/rstnode/rst-core/src/net/handler.rs | 17 ++ games/rstnode/rst-core/src/net/mod.rs | 20 ++ games/rstnode/rst-core/src/net/parser.rs | 17 ++ games/rstnode/rst-core/src/server.rs | 62 +++--- games/rstnode/rst-core/src/wire/action.rs | 33 ---- games/rstnode/rst-core/src/wire/game/action.rs | 33 ++++ games/rstnode/rst-core/src/wire/game/mod.rs | 5 + games/rstnode/rst-core/src/wire/game/update.rs | 75 +++++++ games/rstnode/rst-core/src/wire/mod.rs | 11 +- games/rstnode/rst-core/src/wire/proto/mod.rs | 25 +++ games/rstnode/rst-core/src/wire/proto/request.rs | 41 ++++ games/rstnode/rst-core/src/wire/proto/response.rs | 0 games/rstnode/rst-core/src/wire/req.rs | 2 +- games/rstnode/rst-core/src/wire/resp.rs | 10 +- games/rstnode/rst-core/src/wire/update.rs | 72 ------- games/rstnode/rst-server/Cargo.toml | 12 +- games/rstnode/rst-server/src/constants.rs | 5 +- games/rstnode/rst-server/src/log.rs | 24 ++- games/rstnode/rst-server/src/main.rs | 19 +- games/rstnode/rst-server/src/net/mod.rs | 43 ----- games/rstnode/rst-server/src/net/parser.rs | 9 - games/rstnode/rst-server/src/server.rs | 153 +++++++++++++++ 48 files changed, 1285 insertions(+), 370 deletions(-) create mode 100644 games/rstnode/log.rs create mode 100644 games/rstnode/rst-client/src/net.rs delete mode 100644 games/rstnode/rst-client/src/state.rs create mode 100644 games/rstnode/rst-client/src/state/_match.rs create mode 100644 games/rstnode/rst-client/src/state/if_impl.rs create mode 100644 games/rstnode/rst-client/src/state/map.rs create mode 100644 games/rstnode/rst-client/src/state/mod.rs create mode 100644 games/rstnode/rst-core/src/error.rs create mode 100644 games/rstnode/rst-core/src/mailbox.rs create mode 100644 games/rstnode/rst-core/src/net/endpoint.rs create mode 100644 games/rstnode/rst-core/src/net/gen.rs create mode 100644 games/rstnode/rst-core/src/net/handler.rs create mode 100644 games/rstnode/rst-core/src/net/mod.rs create mode 100644 games/rstnode/rst-core/src/net/parser.rs delete mode 100644 games/rstnode/rst-core/src/wire/action.rs create mode 100644 games/rstnode/rst-core/src/wire/game/action.rs create mode 100644 games/rstnode/rst-core/src/wire/game/mod.rs create mode 100644 games/rstnode/rst-core/src/wire/game/update.rs create mode 100644 games/rstnode/rst-core/src/wire/proto/mod.rs create mode 100644 games/rstnode/rst-core/src/wire/proto/request.rs create mode 100644 games/rstnode/rst-core/src/wire/proto/response.rs delete mode 100644 games/rstnode/rst-core/src/wire/update.rs delete mode 100644 games/rstnode/rst-server/src/net/mod.rs delete mode 100644 games/rstnode/rst-server/src/net/parser.rs create mode 100644 games/rstnode/rst-server/src/server.rs diff --git a/games/rstnode/Cargo.lock b/games/rstnode/Cargo.lock index a7f7a83b6123..0247eaf0006f 100644 --- a/games/rstnode/Cargo.lock +++ b/games/rstnode/Cargo.lock @@ -163,6 +163,14 @@ dependencies = [ "syn 1.0.60", ] +[[package]] +name = "async-quic" +version = "0.1.0" +dependencies = [ + "async-std", + "quinn-proto", +] + [[package]] name = "async-std" version = "1.5.0" @@ -248,6 +256,12 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + [[package]] name = "base64" version = "0.13.0" @@ -289,6 +303,18 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bitvec" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ba35e9565969edb811639dbebfe34edc0368e472c5018474c8eb2543397f81" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake2" version = "0.8.1" @@ -362,12 +388,24 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + [[package]] name = "bytes" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" +[[package]] +name = "bytesize" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81a18687293a1546b67c246452202bbbf143d239cb43494cc163da14979082da" + [[package]] name = "bzip2" version = "0.3.3" @@ -509,9 +547,9 @@ checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" [[package]] name = "clang-sys" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0659001ab56b791be01d4b729c44376edc6718cf389a502e579b77b758f3296c" +checksum = "5cb92721cb37482245ed88428f72253ce422b3b4ee169c70a0642521bb5db4cc" dependencies = [ "glob", "libc", @@ -629,7 +667,7 @@ version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc4369b5e4c0cddf64ad8981c0111e7df4f7078f4d6ba98fb31f2e17c4c57b7e" dependencies = [ - "bytes", + "bytes 1.0.1", "memchr", ] @@ -1279,6 +1317,20 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" +[[package]] +name = "err-derive" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22deed3a8124cff5fa835713fa105621e43bbdc46690c3a6b68328a012d350d4" +dependencies = [ + "proc-macro-error", + "proc-macro2 1.0.24", + "quote 1.0.8", + "rustversion", + "syn 1.0.60", + "synstructure", +] + [[package]] name = "error-chain" version = "0.12.4" @@ -1377,6 +1429,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + [[package]] name = "futf" version = "0.1.4" @@ -1619,9 +1677,9 @@ dependencies = [ [[package]] name = "ggez" -version = "0.6.0-rc0" +version = "0.6.0-rc1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abbd382ca53791d06ccdf41aa339716e7617862b354e64f9033b47d65b964908" +checksum = "74b418a47a9e1ee8dbd8f7a9378851e7af13dbc1b88132c3eaec39bd73e614b3" dependencies = [ "approx 0.4.0", "bitflags", @@ -2128,11 +2186,24 @@ dependencies = [ "tinyvec 1.1.1", ] +[[package]] +name = "lexical-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 0.1.10", + "ryu", + "static_assertions", +] + [[package]] name = "libc" -version = "0.2.85" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3" +checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" [[package]] name = "libflate" @@ -2673,6 +2744,8 @@ version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab6f70b46d6325aa300f1c7bb3d470127dfc27806d8ea6bf294ee0ce643ce2b1" dependencies = [ + "bitvec", + "lexical-core", "memchr", "version_check", ] @@ -3222,6 +3295,30 @@ dependencies = [ "toml", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2 1.0.24", + "quote 1.0.8", + "syn 1.0.60", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2 1.0.24", + "quote 1.0.8", + "version_check", +] + [[package]] name = "proc-macro-hack" version = "0.5.19" @@ -3293,6 +3390,22 @@ dependencies = [ "num", ] +[[package]] +name = "quinn-proto" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0ea0a358c179c6b7af34805c675d1664a9c6a234a7acd7efdbb32d2f39d3d2a" +dependencies = [ + "bytes 0.5.6", + "err-derive", + "rand 0.7.3", + "ring", + "rustls 0.17.0", + "slab", + "tracing", + "webpki", +] + [[package]] name = "quote" version = "0.6.13" @@ -3311,6 +3424,12 @@ dependencies = [ "proc-macro2 1.0.24", ] +[[package]] +name = "radium" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" + [[package]] name = "rand" version = "0.7.3" @@ -3650,6 +3769,7 @@ dependencies = [ "ratman-identity 0.4.0", "serde", "serde_yaml", + "tracing", ] [[package]] @@ -3660,7 +3780,10 @@ version = "0.1.0" name = "rst-node-client" version = "0.0.0" dependencies = [ + "async-std", + "async-trait", "cairo-rs", + "chrono", "clap", "ggez", "librsvg", @@ -3677,7 +3800,11 @@ name = "rst-node-server" version = "0.0.0" dependencies = [ "async-std", + "async-trait", + "chrono", + "num_cpus", "rst-core", + "systemstat", "tracing", "tracing-subscriber", ] @@ -3688,7 +3815,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" dependencies = [ - "base64", + "base64 0.13.0", "blake2b_simd", "constant_time_eq", "crossbeam-utils 0.8.1", @@ -3715,13 +3842,26 @@ dependencies = [ "semver", ] +[[package]] +name = "rustls" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1" +dependencies = [ + "base64 0.11.0", + "log", + "ring", + "sct", + "webpki", +] + [[package]] name = "rustls" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b" dependencies = [ - "base64", + "base64 0.13.0", "log", "ring", "sct", @@ -3738,6 +3878,12 @@ dependencies = [ "owned_ttf_parser 0.6.0", ] +[[package]] +name = "rustversion" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5d2a036dc6d2d8fd16fde3498b04306e29bd193bf306a57427019b823d5acd" + [[package]] name = "rusty-xinput" version = "1.2.0" @@ -3855,9 +4001,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.16" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd2af560da3c1fdc02cb80965289254fc35dff869810061e2d8290ee48848ae" +checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23" dependencies = [ "dtoa", "linked-hash-map", @@ -4162,6 +4308,39 @@ dependencies = [ "unicode-xid 0.2.1", ] +[[package]] +name = "synstructure" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" +dependencies = [ + "proc-macro2 1.0.24", + "quote 1.0.8", + "syn 1.0.60", + "unicode-xid 0.2.1", +] + +[[package]] +name = "systemstat" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31c241679f72241744c20d064a4db7feeb2caa214a8d6e2d4243b8c674a29a5" +dependencies = [ + "bytesize", + "chrono", + "lazy_static", + "libc", + "nom 6.1.0", + "time 0.1.43", + "winapi 0.3.9", +] + +[[package]] +name = "tap" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36474e732d1affd3a6ed582781b3683df3d0563714c59c39591e8ff707cf078e" + [[package]] name = "tar" version = "0.4.32" @@ -4462,9 +4641,9 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" dependencies = [ "tinyvec 1.1.1", ] @@ -4508,14 +4687,14 @@ version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "294b85ef5dbc3670a72e82a89971608a1fcc4ed5c7c5a2895230d31a95f0569b" dependencies = [ - "base64", + "base64 0.13.0", "chunked_transfer", "cookie", "cookie_store", "log", "once_cell", "qstring", - "rustls", + "rustls 0.19.0", "url", "webpki", "webpki-roots", @@ -4851,6 +5030,12 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + [[package]] name = "x11-dl" version = "2.18.5" diff --git a/games/rstnode/Cargo.toml b/games/rstnode/Cargo.toml index 4aa2532acf2d..5dc3bd29c5f5 100644 --- a/games/rstnode/Cargo.toml +++ b/games/rstnode/Cargo.toml @@ -5,6 +5,9 @@ members = [ "rst-launcher", "rst-client", "rst-server", + + # Utility libraries + "async-quic" ] # [profile.release.overrides."*"] diff --git a/games/rstnode/log.rs b/games/rstnode/log.rs new file mode 100644 index 000000000000..6462413952e1 --- /dev/null +++ b/games/rstnode/log.rs @@ -0,0 +1,61 @@ +//! Logging specifics + +const BANNER: &'static str = " +██████╗ ███████╗████████╗ ███╗ ██╗ ██████╗ ██████╗ ███████╗ +██╔══██╗██╔════╝╚══██╔══╝ ████╗ ██║██╔═══██╗██╔══██╗██╔════╝ +██████╔╝███████╗ ██║ ██╔██╗ ██║██║ ██║██║ ██║█████╗ +██╔══██╗╚════██║ ██║ ██║╚██╗██║██║ ██║██║ ██║██╔══╝ +██║ ██║███████║ ██║ ██║ ╚████║╚██████╔╝██████╔╝███████╗ +╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚══════╝"; + +use systemstat::{Platform, System}; +use tracing_subscriber::{filter::LevelFilter, fmt, EnvFilter}; + +#[cfg(not(target_os = "windows"))] +const PLATFORM_DISCLAIMER: &'static str = "Platform: Unspecified *nix-like"; +#[cfg(target_os = "windows")] +static PLATFORM_DISCLAIMER: &'static str = + "WARNING: Windows server hosts are not officially supported!"; + +pub(crate) fn initialise() { + let filter = EnvFilter::try_from_env("RST_LOG") + .unwrap_or_default() + .add_directive(LevelFilter::DEBUG.into()) + .add_directive("async_std=error".parse().unwrap()) + .add_directive("gfx_device_gl=error".parse().unwrap()) + .add_directive("ggez=error".parse().unwrap()) + .add_directive("selectors=error".parse().unwrap()) + .add_directive("gilrs=error".parse().unwrap()) + .add_directive("mio=error".parse().unwrap()); + + // Initialise the logger + fmt().with_env_filter(filter).init(); + info!("Initialising..."); + info!("{}", BANNER); + info!("{}", PLATFORM_DISCLAIMER); + info!("Available cores: {}", num_cpus::get()); + info!("Available RAM: {}", mem()); + info!("Version: {}", crate::constants::VERSION); +} + +fn mem() -> String { + let sys = System::new(); + let mem = sys.memory().unwrap(); + format!( + "{} / {}", + mem.free.to_string_as(true), + mem.total.to_string_as(true) + ) +} + +#[macro_export] +macro_rules! fatal { + () => { + error!("Unknown failure!"); + std::process::exit(2) + }; + ($($arg:tt)*) => ({ + error!($($arg)*); + std::process::exit(2) + }) +} diff --git a/games/rstnode/rst-client/Cargo.toml b/games/rstnode/rst-client/Cargo.toml index 7b5155f8108c..281b44376902 100644 --- a/games/rstnode/rst-client/Cargo.toml +++ b/games/rstnode/rst-client/Cargo.toml @@ -8,14 +8,18 @@ authors = ["Bread Machine", "Katharina Fey "] [dependencies] rst-core = { path = "../rst-core" } +# async-quic = { path = "../async-quic" } cairo-rs = { version="0.8.0", features=["v1_16", "png", "svg"] } librsvg = { git = "https://gitlab.gnome.org/GNOME/librsvg.git", rev = "d34f570f" } -ggez = "0.6.0-rc0" +ggez = "0.6.0-rc1" mint = "0.5" # Required because ggez is trash svg = "0.9" +async-trait = "0.1" +async-std = { version = "1.0", features = ["unstable", "attributes"] } +chrono = { version = "0.4", features = ["serde"] } clap = "2.0" +tempfile = "*" tracing = "0.1" -tracing-subscriber = "0.2" -tempfile = "*" \ No newline at end of file +tracing-subscriber = "0.2" \ No newline at end of file diff --git a/games/rstnode/rst-client/src/graphics/entities/mod.rs b/games/rstnode/rst-client/src/graphics/entities/mod.rs index a3289f6f412d..db675f816fbf 100644 --- a/games/rstnode/rst-client/src/graphics/entities/mod.rs +++ b/games/rstnode/rst-client/src/graphics/entities/mod.rs @@ -3,10 +3,10 @@ //! Generally the naming convention should be: `{type}Rndr` //! (`Renderer`, but shorter). -use crate::color; use super::prelude::*; +use crate::color; -use rst_core::data::{Owner, Node, Color}; +use rst_core::data::{Color, Link, Node, Owner}; use std::sync::Arc; /// A set of universal X/Y coordinates @@ -24,10 +24,6 @@ pub struct NodeRndr { } impl Renderer for NodeRndr { - fn update(&mut self, _: &mut ClientState, _: &mut Context) -> GameResult<()> { - Ok(()) - } - fn draw(&self, s: &ClientState, ctx: &mut Context) -> GameResult<()> { let frame = s.assets().find("frame/frame_s").unwrap(); let icon = s.assets().find("relay/relay1").unwrap(); @@ -39,10 +35,35 @@ impl Renderer for NodeRndr { Owner::Player(ref p) => color::to(p.color), Owner::Neutral => color::to(Color::white()), }; - + frame.draw(ctx, DrawParam::new().dest([x, y]).color(color))?; icon.draw(ctx, DrawParam::new().dest([x, y]).color(color))?; Ok(()) } } + +pub struct LinkRndr { + pub loc: Coordinates, + pub inner: Arc, +} + +impl Renderer for LinkRndr { + fn draw(&self, s: &ClientState, ctx: &mut Context) -> GameResult<()> { + // let frame = s.assets().find("frame/frame_s").unwrap(); + // let icon = s.assets().find("relay/relay1").unwrap(); + + // let x = self.loc.0 - frame.width() as f32; + // let y = self.loc.1 - frame.height() as f32; + + // let color = match self.inner.owner { + // Owner::Player(ref p) => color::to(p.color), + // Owner::Neutral => color::to(Color::white()), + // }; + + // frame.draw(ctx, DrawParam::new().dest([x, y]).color(color))?; + // icon.draw(ctx, DrawParam::new().dest([x, y]).color(color))?; + + Ok(()) + } +} diff --git a/games/rstnode/rst-client/src/graphics/mod.rs b/games/rstnode/rst-client/src/graphics/mod.rs index a7ba676ecc6b..757d44d312d4 100644 --- a/games/rstnode/rst-client/src/graphics/mod.rs +++ b/games/rstnode/rst-client/src/graphics/mod.rs @@ -31,6 +31,6 @@ pub(self) mod prelude { /// A rendering trait which is given graphics context, and game state pub trait Renderer { - fn update(&mut self, _state: &mut ClientState, _ctx: &mut Context) -> GameResult<()>; + // fn update(&mut self, _state: &mut ClientState, _ctx: &mut Context) -> GameResult<()>: fn draw(&self, _state: &ClientState, _ctx: &mut Context) -> GameResult<()>; } diff --git a/games/rstnode/rst-client/src/input.rs b/games/rstnode/rst-client/src/input.rs index e9d2516a94c3..83ae17286d61 100644 --- a/games/rstnode/rst-client/src/input.rs +++ b/games/rstnode/rst-client/src/input.rs @@ -60,7 +60,7 @@ impl EventHandler for InputHandle { } fn mouse_wheel_event(&mut self, _ctx: &mut Context, _: f32, y: f32) { - self.scroll_offset += y * 0.1; + self.scroll_offset -= y * 0.25; } fn draw(&mut self, _: &mut Context) -> GameResult<()> { diff --git a/games/rstnode/rst-client/src/main.rs b/games/rstnode/rst-client/src/main.rs index a6ba4a59cdc1..4159a37d1d4a 100644 --- a/games/rstnode/rst-client/src/main.rs +++ b/games/rstnode/rst-client/src/main.rs @@ -1,5 +1,8 @@ //! RST Node game client +// Remove the warning spam +#![allow(warnings)] + #[macro_use] extern crate tracing; @@ -21,7 +24,8 @@ mod window; pub(crate) use settings::{GameSettings, GraphicsSettings, WindowSettings}; pub(crate) use state::*; -fn main() { +#[async_std::main] +async fn main() { // Initialise logging mechanism log::initialise(); diff --git a/games/rstnode/rst-client/src/net.rs b/games/rstnode/rst-client/src/net.rs new file mode 100644 index 000000000000..ff3c47774ac6 --- /dev/null +++ b/games/rstnode/rst-client/src/net.rs @@ -0,0 +1 @@ +//! Client networking endpoint logic diff --git a/games/rstnode/rst-client/src/settings.rs b/games/rstnode/rst-client/src/settings.rs index cb44eacca3a3..7fd0ba6881c1 100644 --- a/games/rstnode/rst-client/src/settings.rs +++ b/games/rstnode/rst-client/src/settings.rs @@ -43,7 +43,6 @@ pub struct Samples(pub u8); impl<'s> From<&'s Samples> for NumSamples { fn from(s: &'s Samples) -> Self { match s.0 { - 0 => Self::Zero, 1 => Self::One, 2 => Self::Two, 4 => Self::Four, diff --git a/games/rstnode/rst-client/src/state.rs b/games/rstnode/rst-client/src/state.rs deleted file mode 100644 index 8099cf9add96..000000000000 --- a/games/rstnode/rst-client/src/state.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! Game client state handling - -use crate::{ - assets::Assets, - graphics::{ - entities::{Coordinates, NodeRndr}, - Renderer, - }, - input::InputHandle, - viewport::Viewport, - GameSettings, -}; -use ggez::{event::EventHandler, graphics, Context, GameResult}; -use rst_core::data::{Color, Level, Node, Owner, Player, Upgrade}; -use std::sync::Arc; - -pub struct ClientState { - assets: Assets, - settings: GameSettings, - input: InputHandle, - vp: Viewport, - - // Game state - node: NodeRndr, -} - -impl ClientState { - pub fn new(settings: GameSettings, assets: Assets) -> Self { - Self { - assets, - settings, - vp: Viewport::new(), - input: InputHandle::new(), - node: NodeRndr { - loc: Coordinates(512.0, 512.0), - inner: Arc::new(Node { - id: 0, - health: 100.into(), - max_health: 100.into(), - owner: Owner::Player(Player { - id: 0, - name: "kookie".into(), - color: Color::blue(), - money: 1000.into(), - }), - type_: Upgrade::Relay(Level::One), - links: 0, - link_states: vec![], - buffer: vec![], - }), - }, - } - } - - pub fn viewport(&mut self) -> &mut Viewport { - &mut self.vp - } - - pub fn assets(&self) -> &Assets { - &self.assets - } -} - -impl EventHandler for ClientState { - fn update(&mut self, ctx: &mut Context) -> GameResult<()> { - // TODO: get simulation updates - - // Get new input state - self.input.update(ctx)?; - - // Apply inputs to viewpoirt - self.vp.apply(ctx, &self.input)?; - - // Update viewport - self.vp.update(ctx)?; - Ok(()) - } - - fn mouse_wheel_event(&mut self, ctx: &mut Context, x: f32, y: f32) { - self.input.mouse_wheel_event(ctx, x, y); - let zoom = self.input.scroll_offset; - self.viewport().zoom(zoom); - - } - - fn draw(&mut self, ctx: &mut Context) -> GameResult<()> { - graphics::clear(ctx, graphics::Color::from_rgb(15, 15, 15)); - - // Render the node - self.node.draw(&self, ctx).unwrap(); - - graphics::present(ctx) - } -} diff --git a/games/rstnode/rst-client/src/state/_match.rs b/games/rstnode/rst-client/src/state/_match.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/games/rstnode/rst-client/src/state/if_impl.rs b/games/rstnode/rst-client/src/state/if_impl.rs new file mode 100644 index 000000000000..38bac369d3cc --- /dev/null +++ b/games/rstnode/rst-client/src/state/if_impl.rs @@ -0,0 +1,70 @@ +#![allow(unused)] + +use super::ClientState; +use async_trait::async_trait; +use chrono::{DateTime, Utc}; +use rst_core::{ + wire::{ + game::Action, AuthErr, Lobby, LobbyErr, LobbyId, LobbyUpdate, MatchErr, MatchId, RegErr, + UpdateState, User, UserId, + }, + GameIf, Match, +}; +use std::sync::Arc; + +#[async_trait] +impl GameIf for ClientState { + async fn register(self: Arc, name: String, pw: String) -> Result { + todo!() + } + + async fn login(self: Arc, name: String, pw: String) -> Result { + todo!() + } + + async fn logout(self: Arc, user: User) -> Result<(), AuthErr> { + todo!() + } + + async fn anonymous(self: Arc, name: String) -> Result { + todo!() + } + + async fn join(self: Arc, user: User, lobby: LobbyId) -> Result { + todo!() + } + + async fn leave(self: Arc, user: User, lobby: LobbyId) -> Result<(), LobbyErr> { + todo!() + } + + async fn ready( + self: Arc, + user: User, + lobby: LobbyId, + ready: bool, + ) -> Result { + todo!() + } + + async fn start_req( + self: Arc, + user: UserId, + lobby: LobbyId, + ) -> Result, LobbyErr> { + todo!() + } + + async fn perform_action( + self: Arc, + user: User, + mtch: MatchId, + act: Action, + ) -> UpdateState { + todo!() + } + + async fn leave_match(self: Arc, user: User, mtch: MatchId) -> Result<(), MatchErr> { + todo!() + } +} diff --git a/games/rstnode/rst-client/src/state/map.rs b/games/rstnode/rst-client/src/state/map.rs new file mode 100644 index 000000000000..1c4666da6b4f --- /dev/null +++ b/games/rstnode/rst-client/src/state/map.rs @@ -0,0 +1,10 @@ +use rst_core::map::{Map, MapNode}; + +/// Client map state wrapper +/// +/// The map state is calculated by the server and updates are streamed +/// to all clients in a [Match](rst_core::Match). +pub struct MapState { + inner: Map, +} + diff --git a/games/rstnode/rst-client/src/state/mod.rs b/games/rstnode/rst-client/src/state/mod.rs new file mode 100644 index 000000000000..45e69eee10bb --- /dev/null +++ b/games/rstnode/rst-client/src/state/mod.rs @@ -0,0 +1,129 @@ +//! Game client state handling + +mod map; +pub use map::*; + +mod if_impl; + +use crate::{ + assets::Assets, + graphics::{ + entities::{Coordinates, NodeRndr}, + Renderer, + }, + input::InputHandle, + viewport::Viewport, + GameSettings, +}; +use ggez::{event::EventHandler, graphics, Context, GameResult}; +use rst_core::{ + data::{Color, Level, Link, Node, Owner, Player, Upgrade}, + io::Io, +}; +use std::sync::Arc; + +pub struct ClientState { + assets: Assets, + settings: GameSettings, + input: InputHandle, + vp: Viewport, + + // Game state + node1: NodeRndr, + node2: NodeRndr, + link: Arc, +} + +impl ClientState { + pub fn new(settings: GameSettings, assets: Assets) -> Self { + let link = Arc::new(Link { + id: 0, + a: 0, + b: 1, + length: 12, + pp: vec![], + io: Io::default(), + }); + + Self { + assets, + settings, + vp: Viewport::new(), + input: InputHandle::new(), + node1: NodeRndr { + loc: Coordinates(512.0, 512.0), + inner: Arc::new(Node { + id: 0, + health: 100.into(), + max_health: 100.into(), + owner: Owner::Player(Player { + id: 0, + name: "kookie".into(), + color: Color::blue(), + money: 1000.into(), + }), + type_: Upgrade::Relay(Level::One), + links: 1, + router: None, + link_states: vec![Arc::clone(&link)], + buffer: vec![].into(), + }), + }, + node2: NodeRndr { + loc: Coordinates(128.0, 128.0), + inner: Arc::new(Node { + id: 0, + health: 100.into(), + max_health: 100.into(), + owner: Owner::Neutral, + type_: Upgrade::Relay(Level::One), + links: 1, + router: None, + link_states: vec![Arc::clone(&link)], + buffer: vec![].into(), + }), + }, + link, + } + } + + pub fn viewport(&mut self) -> &mut Viewport { + &mut self.vp + } + + pub fn assets(&self) -> &Assets { + &self.assets + } +} + +impl EventHandler for ClientState { + fn update(&mut self, ctx: &mut Context) -> GameResult<()> { + // TODO: get simulation updates + + // Get new input state + self.input.update(ctx)?; + + // Apply inputs to viewpoirt + self.vp.apply(ctx, &self.input)?; + + // Update viewport + self.vp.update(ctx)?; + Ok(()) + } + + fn mouse_wheel_event(&mut self, ctx: &mut Context, x: f32, y: f32) { + self.input.mouse_wheel_event(ctx, x, y); + let zoom = self.input.scroll_offset; + self.viewport().zoom(zoom); + } + + fn draw(&mut self, ctx: &mut Context) -> GameResult<()> { + graphics::clear(ctx, graphics::Color::from_rgb(15, 15, 15)); + + // Render the node + self.node1.draw(&self, ctx).unwrap(); + self.node2.draw(&self, ctx).unwrap(); + + graphics::present(ctx) + } +} diff --git a/games/rstnode/rst-core/Cargo.toml b/games/rstnode/rst-core/Cargo.toml index da0d3fa0ad55..08294475112e 100644 --- a/games/rstnode/rst-core/Cargo.toml +++ b/games/rstnode/rst-core/Cargo.toml @@ -27,3 +27,4 @@ chrono = { version = "0.4", features = ["serde"] } const_env = "0.1" quadtree_rs = "0.1" rand = "0.7" +tracing = "0.1" \ No newline at end of file diff --git a/games/rstnode/rst-core/src/_if.rs b/games/rstnode/rst-core/src/_if.rs index 1a842840708c..f3ed8ee5ffde 100644 --- a/games/rstnode/rst-core/src/_if.rs +++ b/games/rstnode/rst-core/src/_if.rs @@ -1,8 +1,8 @@ //! A common trait interface between the server and the client use crate::wire::{ - Action, AuthErr, Lobby, LobbyErr, LobbyId, LobbyUpdate, MatchErr, MatchId, RegErr, UpdateState, - User, UserId, + game::Action, AuthErr, Lobby, LobbyErr, LobbyId, LobbyUpdate, MatchErr, MatchId, RegErr, + UpdateState, User, UserId, }; use async_std::sync::Arc; use async_trait::async_trait; diff --git a/games/rstnode/rst-core/src/_match.rs b/games/rstnode/rst-core/src/_match.rs index ce75af5af393..e1cc45374ebc 100644 --- a/games/rstnode/rst-core/src/_match.rs +++ b/games/rstnode/rst-core/src/_match.rs @@ -1,8 +1,9 @@ use crate::{ data::Player, lobby::MetaLobby, + mailbox::{Inbox, Outbox}, map::Map, - wire::{Action, LobbyUser, MatchId, UserId}, + wire::{game::Action, LobbyUser, MatchId, UserId}, }; use async_std::sync::{Arc, RwLock}; use chrono::{DateTime, Utc}; @@ -23,7 +24,9 @@ pub struct Match { /// The active game map pub map: Map, /// Input inbox (handled in-order each game tick) - inbox: RwLock>, + inbox: Inbox, + /// Update outbox + outbox: Outbox, /// The time the match was initialised init_t: DateTime, /// The synced time the match was started @@ -46,7 +49,8 @@ impl From for Match { }) .collect(), map: Map::new(), - inbox: Default::default(), + inbox: Inbox::new(), + outbox: Outbox::new(), init_t: Utc::now(), start_t: None, } @@ -61,7 +65,7 @@ impl Match { /// Queue a new game action pub async fn queue(&self, cmd: Action) { - self.inbox.write().await.push_back(cmd); + self.inbox.queue(cmd).await; } pub async fn handle_inbox(&mut self) { diff --git a/games/rstnode/rst-core/src/data.rs b/games/rstnode/rst-core/src/data.rs index d0494fdef3e2..44d4e2a1a456 100644 --- a/games/rstnode/rst-core/src/data.rs +++ b/games/rstnode/rst-core/src/data.rs @@ -4,10 +4,13 @@ use crate::io::Io; use async_std::sync::Arc; -use rand::seq::SliceRandom; -use rand::thread_rng; +use rand::{seq::SliceRandom, thread_rng}; +use ratman::Router; use serde::{Deserialize, Serialize}; -use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32}; +use std::{ + collections::VecDeque, + sync::atomic::{AtomicBool, AtomicU16, AtomicU32, Ordering}, +}; pub type NodeId = usize; @@ -31,9 +34,12 @@ pub struct Node { pub links: u8, /// Active link states pub link_states: Vec>, + /// Router state + #[serde(skip)] + pub router: Option, /// Input buffer #[serde(skip)] - pub buffer: Vec, + pub buffer: VecDeque, } pub type LinkId = usize; @@ -42,19 +48,19 @@ pub type LinkId = usize; #[derive(Serialize, Deserialize)] pub struct Link { /// This link ID - id: LinkId, + pub id: LinkId, /// Node 1 - a: NodeId, + pub a: NodeId, /// Node 2 - b: NodeId, + pub b: NodeId, /// The step length - length: usize, + pub length: usize, /// Packets present on this link #[serde(skip)] - pp: Vec<(Packet, AtomicU32)>, + pub pp: Vec<(Packet, AtomicU32)>, /// Actual Rx, Tx pair #[serde(skip)] - io: Io, + pub io: Io, } pub type PacketId = usize; @@ -86,8 +92,27 @@ pub struct Player { pub money: AtomicU16, } +// This is required because atomics can't safely be cloned +impl Clone for Player { + fn clone(&self) -> Self { + let Self { + ref id, + ref name, + ref color, + ref money, + } = self; + + Self { + id: id.clone(), + name: name.clone(), + color: color.clone(), + money: money.load(Ordering::Relaxed).into(), + } + } +} + /// Optionally, players can create teams -#[derive(Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] pub struct Team { /// Name of the team name: String, @@ -189,8 +214,6 @@ pub enum Owner { Player(Player), } - - /// Encodes upgrade level without numbers #[derive(Copy, Clone, Serialize, Deserialize)] pub enum Level { diff --git a/games/rstnode/rst-core/src/error.rs b/games/rstnode/rst-core/src/error.rs new file mode 100644 index 000000000000..38c93bbe823f --- /dev/null +++ b/games/rstnode/rst-core/src/error.rs @@ -0,0 +1,51 @@ +pub use crate::wire::{AuthErr, LobbyErr, MatchErr, RegErr}; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; + +/// A wrapper around the different RST node errors that can occur +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum Error { + /// Failure in the registration process + Register(RegErr), + /// Failure in the authentication process + Auth(AuthErr), + /// Failure handling a lobby + Lobby(LobbyErr), + /// Failure in a match setting + Match(MatchErr), +} + +impl std::error::Error for Error {} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "An RST error") + } +} + +/// A RST Node specific error +pub type Result = std::result::Result; + +impl From for Error { + fn from(e: RegErr) -> Self { + Error::Register(e) + } +} + +impl From for Error { + fn from(e: AuthErr) -> Self { + Error::Auth(e) + } +} + +impl From for Error { + fn from(e: LobbyErr) -> Self { + Error::Lobby(e) + } +} + +impl From for Error { + fn from(e: MatchErr) -> Self { + Error::Match(e) + } +} diff --git a/games/rstnode/rst-core/src/lib.rs b/games/rstnode/rst-core/src/lib.rs index 5ee4e49750c9..dc1a84f63b58 100644 --- a/games/rstnode/rst-core/src/lib.rs +++ b/games/rstnode/rst-core/src/lib.rs @@ -13,9 +13,15 @@ //! which is them implemented by [Server](crate::server::Server), and //! [MatchClient](crate::client::MatchClient). +// Remove the warning spam +#![allow(warnings)] + #[macro_use] extern crate const_env; +#[macro_use] +extern crate tracing; + pub(crate) mod _loop; mod _if; @@ -26,11 +32,14 @@ pub use _match::Match; pub mod config; pub mod data; +pub mod error; pub mod gens; pub mod io; pub mod lobby; +pub mod mailbox; pub mod map; pub mod mapstore; +pub mod net; pub mod server; pub mod stats; pub mod users; diff --git a/games/rstnode/rst-core/src/lobby.rs b/games/rstnode/rst-core/src/lobby.rs index 496f98bd7b6f..d58ba7ceb8ee 100644 --- a/games/rstnode/rst-core/src/lobby.rs +++ b/games/rstnode/rst-core/src/lobby.rs @@ -2,6 +2,7 @@ use crate::{ data::{Color, ColorPalette}, + mailbox::Outbox, users::MetaUser, wire::{Lobby, LobbyErr, LobbyId, LobbyUpdate, LobbyUser, User, UserId}, }; @@ -68,6 +69,7 @@ impl LobbyList { pub struct MetaLobby { pub palette: Vec, pub inner: Lobby, + pub outbox: Outbox, } impl MetaLobby { @@ -80,6 +82,7 @@ impl MetaLobby { players: vec![], settings: vec![], }, + outbox: Outbox::new(), } } diff --git a/games/rstnode/rst-core/src/mailbox.rs b/games/rstnode/rst-core/src/mailbox.rs new file mode 100644 index 000000000000..c7e4512dbeae --- /dev/null +++ b/games/rstnode/rst-core/src/mailbox.rs @@ -0,0 +1,78 @@ +use crate::wire::{ + game::{Action, Update}, + Lobby, +}; + +use async_std::sync::RwLock; +use std::collections::VecDeque; + +pub enum ClientUpdate { + /// Change to the lobby state + Lobby(Lobby), + /// A game simulation update + GameUpdate(Update), +} + +impl<'l> From<&'l Lobby> for ClientUpdate { + fn from(l: &'l Lobby) -> Self { + ClientUpdate::Lobby(l.clone()) + } +} + +impl<'l> From<&'l Update> for ClientUpdate { + fn from(u: &'l Update) -> Self { + ClientUpdate::GameUpdate(u.clone()) + } +} + +/// A message out buffer that can be attached to any server entity +pub struct Outbox { + queue: RwLock>, +} + +impl Outbox { + pub fn new() -> Self { + Self { + queue: Default::default(), + } + } + + /// Queue a new item to send out + pub async fn queue(&self, update: impl Into) { + let mut q = self.queue.write().await; + q.push_back(update.into()); + } + + /// Run a closure for all queued items + pub async fn run_for(&self, handle: F) { + let q = self.queue.read().await; + q.iter().for_each(|item| handle(item)); + } + + /// Clear the outbox for the next update interval + pub async fn clear(&self) { + self.queue.write().await.clear(); + } +} + +pub struct Inbox { + queue: RwLock>, +} + +impl Inbox { + pub fn new() -> Self { + Self { + queue: Default::default(), + } + } + + /// Queue a new item to send out + pub async fn queue(&self, update: impl Into) { + let mut q = self.queue.write().await; + q.push_back(update.into()); + } + + pub async fn pop(&self) -> Option { + self.queue.write().await.pop_front() + } +} diff --git a/games/rstnode/rst-core/src/map.rs b/games/rstnode/rst-core/src/map.rs index 37f758b4a433..8c6578177e6c 100644 --- a/games/rstnode/rst-core/src/map.rs +++ b/games/rstnode/rst-core/src/map.rs @@ -1,17 +1,11 @@ //! Implements a map graph and world logic use crate::{ - config::{LinkCfg, MapCfg, NodeCfg}, data::{Link, Node, NodeId}, - server::{ServerErr, ServerResult}, wire::Response, }; use async_std::sync::Arc; -use quadtree_rs::{ - area::{Area, AreaBuilder}, - point::Point, - Quadtree, -}; +use quadtree_rs::{area::AreaBuilder, point::Point, Quadtree}; use std::collections::BTreeMap; pub struct MapNode { @@ -29,7 +23,7 @@ pub struct Map { /// Node IDs mapped to coordinates nodes: BTreeMap, /// Link IDs mapped to link objects - links: BTreeMap>, + _links: BTreeMap>, /// A coordinate map for the network coord: Quadtree>, } @@ -38,16 +32,14 @@ impl Map { pub fn new() -> Self { Self { nodes: BTreeMap::new(), - links: BTreeMap::new(), + _links: BTreeMap::new(), coord: Quadtree::new(2), } } - pub fn update(&mut self, cb: F) -> ServerResult - where - F: Fn(&mut Map) -> ServerResult, - { - unimplemented!() + /// Get the position of a node by its ID + pub fn node_position(&self, id: NodeId) -> Option<(i64, i64)> { + self.nodes.get(&id).cloned() } /// Get all objects that can be selected by a single point diff --git a/games/rstnode/rst-core/src/net/endpoint.rs b/games/rstnode/rst-core/src/net/endpoint.rs new file mode 100644 index 000000000000..0c8e2f912421 --- /dev/null +++ b/games/rstnode/rst-core/src/net/endpoint.rs @@ -0,0 +1,49 @@ +use crate::{server::Server, Id}; +use async_std::{ + net::UdpSocket, + sync::{Arc, RwLock}, + task, +}; +use std::{ + collections::BTreeMap, + net::SocketAddr, + sync::atomic::{AtomicBool, Ordering}, +}; + +pub struct Endpoint { + running: AtomicBool, + socket: UdpSocket, + bind: String, + clients: RwLock>, +} + +pub struct Client {} + +impl Endpoint { + pub async fn new(bind: &str) -> Arc { + let socket = UdpSocket::bind(bind).await.unwrap(); + Arc::new(Self { + running: true.into(), + socket, + bind: bind.into(), + clients: Default::default(), + }) + } + + /// Stop the endpoint + pub fn stop(self: &Arc) { + self.running.store(false, Ordering::Relaxed); + } + + pub async fn listen(self: &Arc, serv: Arc) { + let mut buf = vec![0; 1024]; + + info!("Listening for connections on {}", self.bind); + while self.running.load(Ordering::Relaxed) { + let (int, peer) = self.socket.recv_from(&mut buf).await.unwrap(); + if int > 1024 { + warn!("Read a larger chunk than buffer?"); + } + } + } +} diff --git a/games/rstnode/rst-core/src/net/gen.rs b/games/rstnode/rst-core/src/net/gen.rs new file mode 100644 index 000000000000..f0a1f58f1905 --- /dev/null +++ b/games/rstnode/rst-core/src/net/gen.rs @@ -0,0 +1,52 @@ +use crate::{ + error::Error, + wire::{ + AuthErr, Lobby, LobbyErr, LobbyId, LobbyUpdate, MatchErr, RegErr, Response, UpdateState, + User, UserId, + }, +}; +use chrono::{DateTime, Utc}; + +pub fn register(r: Result) -> Response { + Response::Register(r) +} + +pub fn login(r: Result) -> Response { + Response::Login(r) +} + +pub fn logout(r: Result<(), AuthErr>) -> Response { + Response::Logout(r) +} + +pub fn rooms(r: Vec<(String, LobbyId)>) -> Response { + Response::Rooms(r) +} + +pub fn join(r: Result) -> Response { + Response::Join(r) +} + +pub fn leave(r: Result<(), LobbyErr>) -> Response { + Response::Leave(r) +} + +pub fn ready(r: LobbyUpdate) -> Response { + Response::Ready(r) +} + +pub fn start_req(r: DateTime) -> Response { + Response::StartReq(r) +} + +pub fn game_update(r: UpdateState) -> Response { + Response::GameUpdate(r) +} + +pub fn leave_game(r: Result<(), MatchErr>) -> Response { + Response::LeaveGame(r) +} + +pub fn invalid() -> Response { + Response::Invalid +} diff --git a/games/rstnode/rst-core/src/net/handler.rs b/games/rstnode/rst-core/src/net/handler.rs new file mode 100644 index 000000000000..aa0ab6a281c5 --- /dev/null +++ b/games/rstnode/rst-core/src/net/handler.rs @@ -0,0 +1,17 @@ +use crate::{ + net::Client, + server::Server, + wire::{Request, Response}, +}; +use async_std::sync::{Receiver, Sender}; + +pub struct Handler { + tx: Sender, +} + +impl Handler { + /// Start a message handler with a handle to send a reply back to the clients + pub fn start(req: Request) { + + } +} diff --git a/games/rstnode/rst-core/src/net/mod.rs b/games/rstnode/rst-core/src/net/mod.rs new file mode 100644 index 000000000000..f60cb3b97668 --- /dev/null +++ b/games/rstnode/rst-core/src/net/mod.rs @@ -0,0 +1,20 @@ +mod endpoint; +pub use endpoint::*; + +mod gen; +pub use gen::*; + +mod handler; +pub use handler::*; + +mod parser; +pub use parser::*; + +// #[async_std::test] +// async fn user_connection() { +// let serv = Server::new(); +// let ep = Endpoint::new("localhost:9999").await; +// task::spawn(async move { ep.listen(serv).await }); + +// // Create a fake client here +// } diff --git a/games/rstnode/rst-core/src/net/parser.rs b/games/rstnode/rst-core/src/net/parser.rs new file mode 100644 index 000000000000..d7fdf3de964a --- /dev/null +++ b/games/rstnode/rst-core/src/net/parser.rs @@ -0,0 +1,17 @@ +use crate::{ + error::Error, + wire::{Request, Response}, + GameIf, +}; +use std::sync::Arc; + +/// Parse a request and call a game interface function for it +pub async fn request(req: Request, game: Arc) -> Response { + use Request::*; + match req { + Register(name, pw) => super::register(game.register(name, pw).await), + Login(name, pw_hash) => super::login(game.login(name, pw_hash).await), + Logout(user) => super::logout(game.logout(user).await), + _ => super::invalid(), + } +} diff --git a/games/rstnode/rst-core/src/server.rs b/games/rstnode/rst-core/src/server.rs index 3d95c3638c98..68fa2a074e34 100644 --- a/games/rstnode/rst-core/src/server.rs +++ b/games/rstnode/rst-core/src/server.rs @@ -4,18 +4,17 @@ //! connections according to a address given to the initialiser. use crate::{ - _if::GameIf, - _match::Match, data::Player, lobby::LobbyList, map::Map, users::UserStore, wire::{ - Action, AuthErr, Lobby, LobbyErr, LobbyId, LobbyUpdate, MatchErr, MatchId, RegErr, + game::Action, AuthErr, Lobby, LobbyErr, LobbyId, LobbyUpdate, MatchErr, MatchId, RegErr, Response, UpdateState, User, UserId, }, + GameIf, Match, }; -use async_std::sync::{Arc, Mutex, RwLock}; +use async_std::sync::{Arc, Mutex}; use async_trait::async_trait; use chrono::{DateTime, Utc}; use std::{collections::BTreeMap, path::Path}; @@ -40,12 +39,12 @@ pub struct Server { impl Server { /// Create a new game server - fn new() -> Self { - Self { + pub(crate) fn new() -> Arc { + Arc::new(Self { matches: Default::default(), users: UserStore::new(), lobbies: LobbyList::new(), - } + }) } /// Open the state dir of a game server @@ -68,15 +67,15 @@ impl Server { Ok(0) } - pub async fn update_map(self: Arc, id: MatchId, cb: F) -> ServerResult - where - F: Fn(&mut Map) -> ServerResult, - { - match self.matches.get(&id) { - Some(ref m) => m.lock().await.map.update(cb), - None => Err(ServerErr::NoSuchMatch), - } - } + // pub async fn update_map(self: Arc, id: MatchId, cb: F) -> ServerResult + // where + // F: Fn(&mut Map) -> ServerResult, + // { + // match self.matches.get(&id) { + // Some(ref m) => m.lock().await.map.update(cb), + // None => Err(ServerErr::NoSuchMatch), + // } + // } pub async fn update_players(self: Arc, id: MatchId, cb: F) -> ServerResult where @@ -91,31 +90,32 @@ impl Server { #[async_trait] impl GameIf for Server { - async fn register(self: Arc, name: String, pw: String) -> Result { - unimplemented!() + async fn register(self: Arc, _name: String, _pw: String) -> Result { + todo!() } - async fn login(self: Arc, name: String, pw: String) -> Result { - unimplemented!() + async fn login(self: Arc, _name: String, _pw: String) -> Result { + todo!() } - async fn logout(self: Arc, user: User) -> Result<(), AuthErr> { - unimplemented!() + async fn logout(self: Arc, _user: User) -> Result<(), AuthErr> { + todo!() } async fn anonymous(self: Arc, name: String) -> Result { - let (_, auth) = self.users.add(name, None, true).await; - Ok(auth) + // let (_, auth) = self.users.add(name, None, true).await; + // Ok(auth) + todo!() } async fn join(self: Arc, user: User, lobby: LobbyId) -> Result { let mu = self.users.get(&user).await?; - self.lobbies.get_mut(lobby, |mut l| l.join(&mu)).await + self.lobbies.get_mut(lobby, |l| l.join(&mu)).await } async fn leave(self: Arc, user: User, lobby: LobbyId) -> Result<(), LobbyErr> { let mu = self.users.get(&user).await?; - self.lobbies.get_mut(lobby, |mut l| l.leave(&mu)).await + self.lobbies.get_mut(lobby, |l| l.leave(&mu)).await } async fn ready( @@ -124,9 +124,7 @@ impl GameIf for Server { lobby: LobbyId, ready: bool, ) -> Result { - self.lobbies - .get_mut(lobby, |mut l| l.ready(user, ready)) - .await + self.lobbies.get_mut(lobby, |l| l.ready(user, ready)).await } /// A start request was received @@ -135,8 +133,8 @@ impl GameIf for Server { user: UserId, lobby: LobbyId, ) -> Result, LobbyErr> { - self.lobbies.get_mut(lobby, |mut l| l.start(user)).await?; - let lob = self.lobbies.consume(lobby).await?; + self.lobbies.get_mut(lobby, |l| l.start(user)).await??; + let _lob = self.lobbies.consume(lobby).await?; Ok(Utc::now()) } @@ -149,7 +147,7 @@ impl GameIf for Server { unimplemented!() } - async fn leave_match(self: Arc, user: User, mtch: MatchId) -> Result<(), MatchErr> { + async fn leave_match(self: Arc, _user: User, _mtch: MatchId) -> Result<(), MatchErr> { unimplemented!() } } diff --git a/games/rstnode/rst-core/src/wire/action.rs b/games/rstnode/rst-core/src/wire/action.rs deleted file mode 100644 index 22ab7ce6868e..000000000000 --- a/games/rstnode/rst-core/src/wire/action.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::data::{NodeId, Upgrade}; -use serde::{Deserialize, Serialize}; - -/// All actions that a user can trigger via the UI -#[derive(Serialize, Deserialize)] -pub enum Action { - /// Cancel the running action - Cancel(NodeId), - /// Start a capture action - Capture { from: NodeId, to: NodeId }, - /// Set the compute targets - Compute { from: NodeId, to: Vec }, - /// Set to payload analysis mode - Payload(NodeId), - /// Send an exploit across the network - Reset { - from: NodeId, - to: NodeId, - exp: Exploit, - }, - /// Try to upgrade the node to a level - Upgrade { node: NodeId, level: Upgrade }, -} - -/// A type of exploit a node can start running -#[derive(Serialize, Deserialize)] -pub enum Exploit { - Reset, - CNS, - Nitm, - Virus, - TakeOver, -} diff --git a/games/rstnode/rst-core/src/wire/game/action.rs b/games/rstnode/rst-core/src/wire/game/action.rs new file mode 100644 index 000000000000..22ab7ce6868e --- /dev/null +++ b/games/rstnode/rst-core/src/wire/game/action.rs @@ -0,0 +1,33 @@ +use crate::data::{NodeId, Upgrade}; +use serde::{Deserialize, Serialize}; + +/// All actions that a user can trigger via the UI +#[derive(Serialize, Deserialize)] +pub enum Action { + /// Cancel the running action + Cancel(NodeId), + /// Start a capture action + Capture { from: NodeId, to: NodeId }, + /// Set the compute targets + Compute { from: NodeId, to: Vec }, + /// Set to payload analysis mode + Payload(NodeId), + /// Send an exploit across the network + Reset { + from: NodeId, + to: NodeId, + exp: Exploit, + }, + /// Try to upgrade the node to a level + Upgrade { node: NodeId, level: Upgrade }, +} + +/// A type of exploit a node can start running +#[derive(Serialize, Deserialize)] +pub enum Exploit { + Reset, + CNS, + Nitm, + Virus, + TakeOver, +} diff --git a/games/rstnode/rst-core/src/wire/game/mod.rs b/games/rstnode/rst-core/src/wire/game/mod.rs new file mode 100644 index 000000000000..2cb32cbaa79c --- /dev/null +++ b/games/rstnode/rst-core/src/wire/game/mod.rs @@ -0,0 +1,5 @@ +mod action; +pub use action::*; + +mod update; +pub use update::*; diff --git a/games/rstnode/rst-core/src/wire/game/update.rs b/games/rstnode/rst-core/src/wire/game/update.rs new file mode 100644 index 000000000000..44a2e646bd34 --- /dev/null +++ b/games/rstnode/rst-core/src/wire/game/update.rs @@ -0,0 +1,75 @@ +//! Update to the game state + +use crate::{ + data::{NodeId, PacketId, Player, Upgrade}, + wire::UserId, +}; +use serde::{Deserialize, Serialize}; + +/// An update provided by the game server +#[derive(Clone, Serialize, Deserialize)] +pub enum Update { + /// Update made to a node + Node(NodeUpdate), + /// Update made to a link + Link(LinkUpdate), + /// Update made to a packet + Packet(PacketUpdate), + /// Update made to the user set + User(UserUpdate), + /// An error occured, can be non-fatal + Error(UpdateError), +} + +/// Update made to a node +#[derive(Clone, Serialize, Deserialize)] +pub enum NodeUpdate { + /// The node owner changed + Owner(Player), + /// Represent a new upgrade state + Level { node: NodeId, new: Upgrade }, + /// A new packet was consumed from a link + NewPacket(PacketId), + /// Remove a packet from the node buffer + SentPacket(PacketId), + /// Dropped a packet + DropPacket(PacketId), +} + +/// Update made to a link +#[derive(Clone, Serialize, Deserialize)] +pub enum LinkUpdate { + /// Take a packet from a node's buffer + TakePacket(PacketId), + /// Give a packet to a node's buffer + GivePacket(PacketId), +} + +/// Update made to a packet +#[derive(Clone, Serialize, Deserialize)] +pub enum PacketUpdate { + /// Advance a packet along one step along the link + Increment(PacketId), +} + +/// Update made to the user set +#[derive(Clone, Serialize, Deserialize)] +pub enum UserUpdate { + UserLeft(UserId), + UserPaused(UserId), +} + +/// An error occured, can be non-fatal +#[derive(Clone, Serialize, Deserialize)] +pub enum UpdateError { + /// You are the last user in the match + LastUser, + /// The game crashed, so kick all + GameCrashed, + /// The server's time was behind the client time + /// + /// This means that newer events will be dropped from the map + /// state. This should prompt the client to warn the user this + /// has happened, then resync the time of the game states. + TimeAheadServer, +} diff --git a/games/rstnode/rst-core/src/wire/mod.rs b/games/rstnode/rst-core/src/wire/mod.rs index 493f0bcb6885..1f15bca79028 100644 --- a/games/rstnode/rst-core/src/wire/mod.rs +++ b/games/rstnode/rst-core/src/wire/mod.rs @@ -1,20 +1,19 @@ //! Network formats and container messages -mod action; -pub use action::*; - mod env; pub use env::*; +pub mod game; + +mod proto; +pub use proto::*; + mod resp; pub use resp::*; mod req; pub use req::*; -mod update; -pub use update::*; - use crate::{data::Color, Id}; use serde::{Deserialize, Serialize}; diff --git a/games/rstnode/rst-core/src/wire/proto/mod.rs b/games/rstnode/rst-core/src/wire/proto/mod.rs new file mode 100644 index 000000000000..07598bc85540 --- /dev/null +++ b/games/rstnode/rst-core/src/wire/proto/mod.rs @@ -0,0 +1,25 @@ +//! This module implements the client-server game protocol + +use super::{Request, Response}; + +pub enum NetErr { + Refused, + Dropped, + Timeout, + BadData, +} + +/// Use this function to send a request to a partical remote +/// +/// The function makes sure that you get a valid response back, but +/// does not yet ensure that this response is correct for the request +/// in question. +pub fn request_to(r: Request, remote: String) -> Result { + todo!() +} + +/// Use this function to send a response to a client +pub fn response_to(r: Response, client: String) -> Result<(), NetErr> { + todo!() +} + diff --git a/games/rstnode/rst-core/src/wire/proto/request.rs b/games/rstnode/rst-core/src/wire/proto/request.rs new file mode 100644 index 000000000000..a95c240c4a14 --- /dev/null +++ b/games/rstnode/rst-core/src/wire/proto/request.rs @@ -0,0 +1,41 @@ +use crate::wire::{Action, LobbyId, MatchId, Request, User}; + +pub fn register(name: String, pw: String) -> Request { + Request::Register(name, pw) +} +c +pub fn login(name: String, pw: String) -> Request { + Request::Login(name, pw) +} + +pub fn logout(user: User) -> Request { + Request::Logout(user) +} + +pub fn anonymous(name: String) -> Request { + Request::Anonymous(name) +} + +pub fn join(user: User, lid: LobbyId) -> Request { + Request::Join(user, lid) +} + +pub fn leave(user: User, lid: LobbyId) -> Request { + Request::Leave(user, lid) +} + +pub fn ready(user: User, lid: LobbyId, ready: bool) -> Request { + Request::Ready(user, lid, ready) +} + +pub fn start_req(user: User, lid: LobbyId) -> Request { + Request::StartReq(user, lid) +} + +pub fn game_action(user: User, mid: MatchId, act: Action) -> Request { + Request::GameAction(user, mid, act) +} + +pub fn leave_game(user: User, mid: MatchId) -> Request { + Request::LeaveGame(user, mid) +} diff --git a/games/rstnode/rst-core/src/wire/proto/response.rs b/games/rstnode/rst-core/src/wire/proto/response.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/games/rstnode/rst-core/src/wire/req.rs b/games/rstnode/rst-core/src/wire/req.rs index ffefcbdb6ac7..be5c905795a9 100644 --- a/games/rstnode/rst-core/src/wire/req.rs +++ b/games/rstnode/rst-core/src/wire/req.rs @@ -1,4 +1,4 @@ -use super::{action::Action, LobbyId, MatchId, User}; +use super::{game::Action, LobbyId, MatchId, User}; use serde::{Deserialize, Serialize}; /// A message sent from the game client to the server diff --git a/games/rstnode/rst-core/src/wire/resp.rs b/games/rstnode/rst-core/src/wire/resp.rs index ef2e192a6044..99598cceb9e6 100644 --- a/games/rstnode/rst-core/src/wire/resp.rs +++ b/games/rstnode/rst-core/src/wire/resp.rs @@ -27,9 +27,11 @@ pub enum Response { GameUpdate(UpdateState), /// Leave the match (forfeit) LeaveGame(Result<(), MatchErr>), + /// The given request was entirely invalid + Invalid, } -#[derive(Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum RegErr { /// The password is way too bad BadPassword, @@ -39,7 +41,7 @@ pub enum RegErr { OtherError, } -#[derive(Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum AuthErr { /// Wrong password for the user WrongPassword, @@ -51,7 +53,7 @@ pub enum AuthErr { OtherError, } -#[derive(Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum LobbyErr { /// The requested room is already full RoomFull, @@ -85,7 +87,7 @@ pub enum UpdateState { } /// An error that can occur in a match -#[derive(Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum MatchErr { /// The provided player wasn't in the match (anymore?) NotInMatch, diff --git a/games/rstnode/rst-core/src/wire/update.rs b/games/rstnode/rst-core/src/wire/update.rs deleted file mode 100644 index a1b47ff07e50..000000000000 --- a/games/rstnode/rst-core/src/wire/update.rs +++ /dev/null @@ -1,72 +0,0 @@ -//! Update to the game state - -use super::UserId; -use crate::data::{NodeId, PacketId, Player, Upgrade}; -use serde::{Deserialize, Serialize}; - -/// An update provided by the game server -#[derive(Serialize, Deserialize)] -pub enum Update { - /// Update made to a node - Node(NodeUpdate), - /// Update made to a link - Link(LinkUpdate), - /// Update made to a packet - Packet(PacketUpdate), - /// Update made to the user set - User(UserUpdate), - /// An error occured, can be non-fatal - Error(UpdateError), -} - -/// Update made to a node -#[derive(Serialize, Deserialize)] -pub enum NodeUpdate { - /// The node owner changed - Owner(Player), - /// Represent a new upgrade state - Level { node: NodeId, new: Upgrade }, - /// A new packet was consumed from a link - NewPacket(PacketId), - /// Remove a packet from the node buffer - SentPacket(PacketId), - /// Dropped a packet - DropPacket(PacketId), -} - -/// Update made to a link -#[derive(Serialize, Deserialize)] -pub enum LinkUpdate { - /// Take a packet from a node's buffer - TakePacket(PacketId), - /// Give a packet to a node's buffer - GivePacket(PacketId), -} - -/// Update made to a packet -#[derive(Serialize, Deserialize)] -pub enum PacketUpdate { - /// Advance a packet along one step along the link - Increment(PacketId), -} - -/// Update made to the user set -#[derive(Serialize, Deserialize)] -pub enum UserUpdate { - UserLeft(UserId), -} - -/// An error occured, can be non-fatal -#[derive(Serialize, Deserialize)] -pub enum UpdateError { - /// You are the last user in the match - LastUser, - /// The game crashed, so kick all - GameCrashed, - /// The server's time was behind the client time - /// - /// This means that newer events will be dropped from the map - /// state. This should prompt the client to warn the user this - /// has happened, then resync the time of the game states. - TimeAheadServer, -} diff --git a/games/rstnode/rst-server/Cargo.toml b/games/rstnode/rst-server/Cargo.toml index bf175b81325d..8903e637fc3f 100644 --- a/games/rstnode/rst-server/Cargo.toml +++ b/games/rstnode/rst-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rst-node-server" -description = "Backend game server" +description = "RST Node game server" version = "0.0.0" edition = "2018" license = "AGPL-3.0-or-later" @@ -8,6 +8,12 @@ authors = ["Bread Machine", "Katharina Fey "] [dependencies] rst-core = { path = "../rst-core" } -async-std = { version = "1.0", features = ["attributes"] } +async-std = { version = "1.0", features = ["unstable", "attributes"] } +async-trait = "0.1" + +chrono = { version = "0.4", features = ["serde"] } tracing = "0.1" -tracing-subscriber = "0.2" \ No newline at end of file +tracing-subscriber = "0.2" +num_cpus = "1.0" +systemstat = "0.1" + diff --git a/games/rstnode/rst-server/src/constants.rs b/games/rstnode/rst-server/src/constants.rs index e40a5c35c5db..8dc23f7c68d8 100644 --- a/games/rstnode/rst-server/src/constants.rs +++ b/games/rstnode/rst-server/src/constants.rs @@ -1,5 +1,6 @@ - - pub const NAME: &'static str = "RST Node"; pub const VERSION: &'static str = env!("CARGO_PKG_VERSION"); pub const AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS"); + +pub const DEFAULT_BIND: &'static str = "0.0.0.0"; +pub const DEFAULT_PORT: u16 = 10022; diff --git a/games/rstnode/rst-server/src/log.rs b/games/rstnode/rst-server/src/log.rs index 62d03167bb7a..676395134a59 100644 --- a/games/rstnode/rst-server/src/log.rs +++ b/games/rstnode/rst-server/src/log.rs @@ -8,8 +8,15 @@ const BANNER: &'static str = " ██║ ██║███████║ ██║ ██║ ╚████║╚██████╔╝██████╔╝███████╗ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚══════╝"; +use systemstat::{Platform, System}; use tracing_subscriber::{filter::LevelFilter, fmt, EnvFilter}; +#[cfg(not(target_os = "windows"))] +const PLATFORM_DISCLAIMER: &'static str = "Platform: Unspecified *nix-like"; +#[cfg(target_os = "windows")] +static PLATFORM_DISCLAIMER: &'static str = + "WARNING: Windows server hosts are not officially supported!"; + pub(crate) fn initialise() { let filter = EnvFilter::try_from_env("RST_LOG") .unwrap_or_default() @@ -19,13 +26,24 @@ pub(crate) fn initialise() { // Initialise the logger fmt().with_env_filter(filter).init(); - info!("Initialising server..."); + info!("Initialising..."); info!("{}", BANNER); - info!("Available cores: unknown"); - info!("Available RAM: unknown"); + info!("{}", PLATFORM_DISCLAIMER); + info!("Available cores: {}", num_cpus::get()); + info!("Available RAM: {}", mem()); info!("Version: {}", crate::constants::VERSION); } +fn mem() -> String { + let sys = System::new(); + let mem = sys.memory().unwrap(); + format!( + "{} / {}", + mem.free.to_string_as(true), + mem.total.to_string_as(true) + ) +} + #[macro_export] macro_rules! fatal { () => { diff --git a/games/rstnode/rst-server/src/main.rs b/games/rstnode/rst-server/src/main.rs index d3335e941ca8..fbdd33bb864d 100644 --- a/games/rstnode/rst-server/src/main.rs +++ b/games/rstnode/rst-server/src/main.rs @@ -1,15 +1,22 @@ +//! RST Node game server + +// Remove the warning spam +#![allow(warnings)] + #[macro_use] extern crate tracing; -mod constants; -mod log; -mod net; -use net::ServerEndpoint; +pub(crate) mod constants; +pub(crate) mod log; + +use rst_core::net::Endpoint; #[async_std::main] async fn main() { log::initialise(); - let serv = ServerEndpoint::new("0.0.0.0:10022").await; - serv.listen().await; + let addr = format!("{}:{}", constants::DEFAULT_BIND, constants::DEFAULT_PORT); + let serv = Endpoint::new(addr.as_str()).await; + // serv.listen().await; + // net::run().await; } diff --git a/games/rstnode/rst-server/src/net/mod.rs b/games/rstnode/rst-server/src/net/mod.rs deleted file mode 100644 index deb35f7f89b9..000000000000 --- a/games/rstnode/rst-server/src/net/mod.rs +++ /dev/null @@ -1,43 +0,0 @@ -#![allow(unused)] - -mod parser; - -use async_std::{ - net::UdpSocket, - sync::{Arc, RwLock}, - task, -}; -use rst_core::Id; -use std::{collections::BTreeMap, net::SocketAddr}; - -pub struct ServerEndpoint { - socket: UdpSocket, - bind: String, - clients: RwLock>, -} - -impl ServerEndpoint { - pub async fn new(bind: &str) -> Arc { - let socket = UdpSocket::bind(bind).await.unwrap(); - Arc::new(Self { - socket, - bind: bind.into(), - clients: Default::default(), - }) - } - - pub async fn listen(self: &Arc) { - let mut buf = vec![0; 1024]; - - info!("Listening for connections on {}", self.bind); - - loop { - let (_, peer) = self.socket.recv_from(&mut buf).await.unwrap(); - } - } -} - -pub struct Client { - addr: SocketAddr, -} - diff --git a/games/rstnode/rst-server/src/net/parser.rs b/games/rstnode/rst-server/src/net/parser.rs deleted file mode 100644 index d320e13095bb..000000000000 --- a/games/rstnode/rst-server/src/net/parser.rs +++ /dev/null @@ -1,9 +0,0 @@ -use rst_core::wire::Request; - -pub async fn request(req: Request) { - use Request::*; - match req { - Register(name, pw) => {}, - _ => todo!(), - } -} diff --git a/games/rstnode/rst-server/src/server.rs b/games/rstnode/rst-server/src/server.rs new file mode 100644 index 000000000000..9badba85d1a6 --- /dev/null +++ b/games/rstnode/rst-server/src/server.rs @@ -0,0 +1,153 @@ +//! Game server state handler +//! +//! A server can host many lobbies at the same time. It listens for +//! connections according to a address given to the initialiser. + +use async_std::sync::{Arc, Mutex}; +use async_trait::async_trait; +use chrono::{DateTime, Utc}; +use rst_core::{ + data::Player, + lobby::LobbyList, + map::Map, + users::UserStore, + wire::{ + Action, AuthErr, Lobby, LobbyErr, LobbyId, LobbyUpdate, MatchErr, MatchId, RegErr, + Response, UpdateState, User, UserId, + }, + GameIf, Match, +}; +use std::{collections::BTreeMap, path::Path}; + +/// A convenience result wrapper for server actions +pub type ServerResult = Result; +pub enum ServerErr { + /// The requested directory is corrupted + NoSuchDir, + /// Corrupted game state + Corrupted, + /// No such match found + NoSuchMatch, +} + +/// The game's server backend +pub struct Server { + matches: BTreeMap>, + users: UserStore, + lobbies: LobbyList, +} + +impl Server { + /// Create a new game server + pub(crate) fn new() -> Arc { + Arc::new(Self { + matches: Default::default(), + users: UserStore::new(), + lobbies: LobbyList::new(), + }) + } + + /// Open the state dir of a game server + pub async fn open(self: Arc, path: &Path) -> ServerResult<()> { + Ok(()) + } + + /// Stop accepting new game connections and shutdown gracefully + /// + /// Returns the number of matches still going on. + pub async fn shutdown(self: Arc) -> ServerResult { + Ok(0) + } + + /// Save and close the statedir and kicking all players + /// + /// Returns the number of players that were kicked off the server + /// prematurely. + pub async fn kill(self: Arc) -> ServerResult { + Ok(0) + } + + // pub async fn update_map(self: Arc, id: MatchId, cb: F) -> ServerResult + // where + // F: Fn(&mut Map) -> ServerResult, + // { + // match self.matches.get(&id) { + // Some(ref m) => m.lock().await.map.update(cb), + // None => Err(ServerErr::NoSuchMatch), + // } + // } + + pub async fn update_players(self: Arc, id: MatchId, cb: F) -> ServerResult + where + F: Fn(&mut Vec) -> ServerResult, + { + match self.matches.get(&id) { + Some(ref mut m) => cb(&mut m.lock().await.players), + None => Err(ServerErr::NoSuchMatch), + } + } +} + +#[async_trait] +impl GameIf for Server { + async fn register(self: Arc, _name: String, _pw: String) -> Result { + todo!() + } + + async fn login(self: Arc, _name: String, _pw: String) -> Result { + todo!() + } + + async fn logout(self: Arc, _user: User) -> Result<(), AuthErr> { + todo!() + } + + async fn anonymous(self: Arc, name: String) -> Result { + // let (_, auth) = self.users.add(name, None, true).await; + // Ok(auth) + todo!() + } + + async fn join(self: Arc, user: User, lobby: LobbyId) -> Result { + let mu = self.users.get(&user).await?; + self.lobbies.get_mut(lobby, |l| l.join(&mu)).await + } + + async fn leave(self: Arc, user: User, lobby: LobbyId) -> Result<(), LobbyErr> { + let mu = self.users.get(&user).await?; + self.lobbies.get_mut(lobby, |l| l.leave(&mu)).await + } + + async fn ready( + self: Arc, + user: User, + lobby: LobbyId, + ready: bool, + ) -> Result { + self.lobbies.get_mut(lobby, |l| l.ready(user, ready)).await + } + + /// A start request was received + async fn start_req( + self: Arc, + user: UserId, + lobby: LobbyId, + ) -> Result, LobbyErr> { + self.lobbies.get_mut(lobby, |l| l.start(user)).await??; + let _lob = self.lobbies.consume(lobby).await?; + Ok(Utc::now()) + } + + async fn perform_action( + self: Arc, + user: User, + mtch: MatchId, + act: Action, + ) -> UpdateState { + unimplemented!() + } + + async fn leave_match(self: Arc, _user: User, _mtch: MatchId) -> Result<(), MatchErr> { + unimplemented!() + } +} -- cgit v1.2.3