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
wip/yesman
Katharina Fey 3 years ago
parent 5dab336049
commit effbdeed66
Signed by: kookie
GPG Key ID: 90734A9E619C8A6C
  1. 215
      games/rstnode/Cargo.lock
  2. 3
      games/rstnode/Cargo.toml
  3. 61
      games/rstnode/log.rs
  4. 10
      games/rstnode/rst-client/Cargo.toml
  5. 35
      games/rstnode/rst-client/src/graphics/entities/mod.rs
  6. 2
      games/rstnode/rst-client/src/graphics/mod.rs
  7. 2
      games/rstnode/rst-client/src/input.rs
  8. 6
      games/rstnode/rst-client/src/main.rs
  9. 1
      games/rstnode/rst-client/src/net.rs
  10. 1
      games/rstnode/rst-client/src/settings.rs
  11. 0
      games/rstnode/rst-client/src/state/_match.rs
  12. 70
      games/rstnode/rst-client/src/state/if_impl.rs
  13. 10
      games/rstnode/rst-client/src/state/map.rs
  14. 51
      games/rstnode/rst-client/src/state/mod.rs
  15. 1
      games/rstnode/rst-core/Cargo.toml
  16. 4
      games/rstnode/rst-core/src/_if.rs
  17. 12
      games/rstnode/rst-core/src/_match.rs
  18. 49
      games/rstnode/rst-core/src/data.rs
  19. 51
      games/rstnode/rst-core/src/error.rs
  20. 9
      games/rstnode/rst-core/src/lib.rs
  21. 3
      games/rstnode/rst-core/src/lobby.rs
  22. 78
      games/rstnode/rst-core/src/mailbox.rs
  23. 20
      games/rstnode/rst-core/src/map.rs
  24. 49
      games/rstnode/rst-core/src/net/endpoint.rs
  25. 52
      games/rstnode/rst-core/src/net/gen.rs
  26. 17
      games/rstnode/rst-core/src/net/handler.rs
  27. 20
      games/rstnode/rst-core/src/net/mod.rs
  28. 17
      games/rstnode/rst-core/src/net/parser.rs
  29. 62
      games/rstnode/rst-core/src/server.rs
  30. 0
      games/rstnode/rst-core/src/wire/game/action.rs
  31. 5
      games/rstnode/rst-core/src/wire/game/mod.rs
  32. 19
      games/rstnode/rst-core/src/wire/game/update.rs
  33. 11
      games/rstnode/rst-core/src/wire/mod.rs
  34. 25
      games/rstnode/rst-core/src/wire/proto/mod.rs
  35. 41
      games/rstnode/rst-core/src/wire/proto/request.rs
  36. 0
      games/rstnode/rst-core/src/wire/proto/response.rs
  37. 2
      games/rstnode/rst-core/src/wire/req.rs
  38. 10
      games/rstnode/rst-core/src/wire/resp.rs
  39. 12
      games/rstnode/rst-server/Cargo.toml
  40. 5
      games/rstnode/rst-server/src/constants.rs
  41. 24
      games/rstnode/rst-server/src/log.rs
  42. 19
      games/rstnode/rst-server/src/main.rs
  43. 43
      games/rstnode/rst-server/src/net/mod.rs
  44. 9
      games/rstnode/rst-server/src/net/parser.rs
  45. 153
      games/rstnode/rst-server/src/server.rs

@ -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"

@ -5,6 +5,9 @@ members = [
"rst-launcher",
"rst-client",
"rst-server",
# Utility libraries
"async-quic"
]
# [profile.release.overrides."*"]

@ -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)
})
}

@ -8,14 +8,18 @@ authors = ["Bread Machine", "Katharina Fey <kookie@spacekookie.de>"]
[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 = "*"
tracing-subscriber = "0.2"

@ -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<Link>,
}
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(())
}
}

@ -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<()>;
}

@ -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<()> {

@ -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();

@ -0,0 +1 @@
//! Client networking endpoint logic

@ -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,

@ -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<Self>, name: String, pw: String) -> Result<UserId, RegErr> {
todo!()
}
async fn login(self: Arc<Self>, name: String, pw: String) -> Result<User, AuthErr> {
todo!()
}
async fn logout(self: Arc<Self>, user: User) -> Result<(), AuthErr> {
todo!()
}
async fn anonymous(self: Arc<Self>, name: String) -> Result<User, AuthErr> {
todo!()
}
async fn join(self: Arc<Self>, user: User, lobby: LobbyId) -> Result<Lobby, LobbyErr> {
todo!()
}
async fn leave(self: Arc<Self>, user: User, lobby: LobbyId) -> Result<(), LobbyErr> {
todo!()
}
async fn ready(
self: Arc<Self>,
user: User,
lobby: LobbyId,
ready: bool,
) -> Result<LobbyUpdate, LobbyErr> {
todo!()
}
async fn start_req(
self: Arc<Self>,
user: UserId,
lobby: LobbyId,
) -> Result<DateTime<Utc>, LobbyErr> {
todo!()
}
async fn perform_action(
self: Arc<Self>,
user: User,
mtch: MatchId,
act: Action,
) -> UpdateState {
todo!()
}
async fn leave_match(self: Arc<Self>, user: User, mtch: MatchId) -> Result<(), MatchErr> {
todo!()
}
}

@ -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,
}

@ -1,5 +1,10 @@
//! Game client state handling
mod map;
pub use map::*;
mod if_impl;
use crate::{
assets::Assets,
graphics::{
@ -11,7 +16,10 @@ use crate::{
GameSettings,
};
use ggez::{event::EventHandler, graphics, Context, GameResult};
use rst_core::data::{Color, Level, Node, Owner, Player, Upgrade};
use rst_core::{
data::{Color, Level, Link, Node, Owner, Player, Upgrade},
io::Io,
};
use std::sync::Arc;
pub struct ClientState {
@ -21,17 +29,28 @@ pub struct ClientState {
vp: Viewport,
// Game state
node: NodeRndr,
node1: NodeRndr,
node2: NodeRndr,
link: Arc<Link>,
}
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(),
node: NodeRndr {
node1: NodeRndr {
loc: Coordinates(512.0, 512.0),
inner: Arc::new(Node {
id: 0,
@ -44,11 +63,27 @@ impl ClientState {
money: 1000.into(),
}),
type_: Upgrade::Relay(Level::One),
links: 0,
link_states: vec![],
buffer: vec![],
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,
}
}
@ -80,14 +115,14 @@ impl EventHandler for ClientState {
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();
self.node1.draw(&self, ctx).unwrap();
self.node2.draw(&self, ctx).unwrap();
graphics::present(ctx)
}

@ -27,3 +27,4 @@ chrono = { version = "0.4", features = ["serde"] }
const_env = "0.1"
quadtree_rs = "0.1"
rand = "0.7"
tracing = "0.1"

@ -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;

@ -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<VecDeque<Action>>,
inbox: Inbox,
/// Update outbox
outbox: Outbox,
/// The time the match was initialised
init_t: DateTime<Utc>,
/// The synced time the match was started
@ -46,7 +49,8 @@ impl From<MetaLobby> 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) {

@ -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<Arc<Link>>,
/// Router state
#[serde(skip)]
pub router: Option<Router>,
/// Input buffer
#[serde(skip)]
pub buffer: Vec<Packet>,
pub buffer: VecDeque<Packet>,
}
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 {

@ -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<T> = std::result::Result<T, Error>;
impl From<RegErr> for Error {
fn from(e: RegErr) -> Self {
Error::Register(e)
}
}
impl From<AuthErr> for Error {
fn from(e: AuthErr) -> Self {
Error::Auth(e)
}
}
impl From<LobbyErr> for Error {
fn from(e: LobbyErr) -> Self {
Error::Lobby(e)
}
}
impl From<MatchErr> for Error {
fn from(e: MatchErr) -> Self {
Error::Match(e)
}
}

@ -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;

@ -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<Color>,
pub inner: Lobby,
pub outbox: Outbox,
}
impl MetaLobby {
@ -80,6 +82,7 @@ impl MetaLobby {
players: vec![],
settings: vec![],
},
outbox: Outbox::new(),
}
}

@ -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<VecDeque<ClientUpdate>>,
}
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<ClientUpdate>) {
let mut q = self.queue.write().await;
q.push_back(update.into());
}
/// Run a closure for all queued items
pub async fn run_for<F: Fn(&ClientUpdate)>(&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<VecDeque<Action>>,
}
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<Action>) {
let mut q = self.queue.write().await;
q.push_back(update.into());
}
pub async fn pop(&self) -> Option<Action> {
self.queue.write().await.pop_front()
}
}

@ -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<NodeId, (i64, i64)>,
/// Link IDs mapped to link objects
links: BTreeMap<u16, Arc<Link>>,
_links: BTreeMap<u16, Arc<Link>>,
/// A coordinate map for the network
coord: Quadtree<i64, Arc<MapNode>>,
}
@ -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<F>(&mut self, cb: F) -> ServerResult<Response>
where
F: Fn(&mut Map) -> ServerResult<Response>,
{
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

@ -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<BTreeMap<Id, Client>>,
}
pub struct Client {}
impl Endpoint {
pub async fn new(bind: &str) -> Arc<Self> {
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>) {
self.running.store(false, Ordering::Relaxed);
}
pub async fn listen(self: &Arc<Self>, serv: Arc<Server>) {
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?");
}
}
}
}

@ -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<UserId, RegErr>) -> Response {
Response::Register(r)
}
pub fn login(r: Result<User, AuthErr>) -> 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<Lobby, LobbyErr>) -> 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<Utc>) -> 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
}

@ -0,0 +1,17 @@
use crate::{
net::Client,
server::Server,
wire::{Request, Response},
};
use async_std::sync::{Receiver, Sender};
pub struct Handler {
tx: Sender<Response>,
}
impl Handler {
/// Start a message handler with a handle to send a reply back to the clients
pub fn start(req: Request) {
}
}

@ -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
// }

@ -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<impl GameIf>) -> 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(),
}
}

@ -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<Self> {
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<F>(self: Arc<Self>, id: MatchId, cb: F) -> ServerResult<Response>
where
F: Fn(&mut Map) -> ServerResult<Response>,
{
match self.matches.get(&id) {
Some(ref m) => m.lock().await.map.update(cb),
None => Err(ServerErr::NoSuchMatch),
}
}
// pub async fn update_map<F>(self: Arc<Self>, id: MatchId, cb: F) -> ServerResult<Response>
// where
// F: Fn(&mut Map) -> ServerResult<Response>,
// {
// match self.matches.get(&id) {
// Some(ref m) => m.lock().await.map.update(cb),
// None => Err(ServerErr::NoSuchMatch),
// }
// }
pub async fn update_players<F>(self: Arc<Self>, id: MatchId, cb: F) -> ServerResult<Response>
where
@ -91,31 +90,32 @@ impl Server {
#[async_trait]
impl GameIf for Server {
async fn register(self: Arc<Self>, name: String, pw: String) -> Result<UserId, RegErr> {
unimplemented!()
async fn register(self: Arc<Self>, _name: String, _pw: String) -> Result<UserId, RegErr> {
todo!()
}
async fn login(self: Arc<Self>, name: String, pw: String) -> Result<User, AuthErr> {
unimplemented!()
async fn login(self: Arc<Self>, _name: String, _pw: String) -> Result<User, AuthErr> {
todo!()
}
async fn logout(self: Arc<Self>, user: User) -> Result<(), AuthErr> {
unimplemented!()
async fn logout(self: Arc<Self>, _user: User) -> Result<(), AuthErr> {
todo!()
}
async fn anonymous(self: Arc<Self>, name: String) -> Result<User, AuthErr> {
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<Self>, user: User, lobby: LobbyId) -> Result<Lobby, LobbyErr> {
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<Self>, 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<LobbyUpdate, LobbyErr> {
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<DateTime<Utc>, 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<Self>, user: User, mtch: MatchId) -> Result<(), MatchErr> {
async fn leave_match(self: Arc<Self>, _user: User, _mtch: MatchId) -> Result<(), MatchErr> {
unimplemented!()
}
}

@ -0,0 +1,5 @@
mod action;
pub use action::*;
mod update;
pub use update::*;

@ -1,11 +1,13 @@
//! Update to the game state
use super::UserId;
use crate::data::{NodeId, PacketId, Player, Upgrade};
use crate::{
data::{NodeId, PacketId, Player, Upgrade},
wire::UserId,
};
use serde::{Deserialize, Serialize};
/// An update provided by the game server
#[derive(Serialize, Deserialize)]
#[derive(Clone, Serialize, Deserialize)]
pub enum Update {
/// Update made to a node
Node(NodeUpdate),
@ -20,7 +22,7 @@ pub enum Update {
}
/// Update made to a node
#[derive(Serialize, Deserialize)]
#[derive(Clone, Serialize, Deserialize)]
pub enum NodeUpdate {
/// The node owner changed
Owner(Player),
@ -35,7 +37,7 @@ pub enum NodeUpdate {
}
/// Update made to a link
#[derive(Serialize, Deserialize)]
#[derive(Clone, Serialize, Deserialize)]
pub enum LinkUpdate {
/// Take a packet from a node's buffer
TakePacket(PacketId),
@ -44,20 +46,21 @@ pub enum LinkUpdate {
}
/// Update made to a packet
#[derive(Serialize, Deserialize)]
#[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(Serialize, Deserialize)]
#[derive(Clone, Serialize, Deserialize)]
pub enum UserUpdate {
UserLeft(UserId),
UserPaused(UserId),
}
/// An error occured, can be non-fatal
#[derive(Serialize, Deserialize)]
#[derive(Clone, Serialize, Deserialize)]
pub enum UpdateError {
/// You are the last user in the match
LastUser,

@ -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};

@ -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<Response, NetErr> {
todo!()
}
/// Use this function to send a response to a client
pub fn response_to(r: Response, client: String) -> Result<(), NetErr> {
todo!()
}

@ -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)
}

@ -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

@ -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,

@ -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 <kookie@spacekookie.de>"]
[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"
tracing-subscriber = "0.2"
num_cpus = "1.0"
systemstat = "0.1"

@ -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;

@ -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 {
() => {

@ -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;
}

@ -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<BTreeMap<Id, Client>>,
}
impl ServerEndpoint {
pub async fn new(bind: &str) -> Arc<Self> {
let socket = UdpSocket::bind(bind).await.unwrap();
Arc::new(Self {
socket,
bind: bind.into(),
clients: Default::default(),
})
}
pub async fn listen(self: &Arc<Self>) {
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,
}

@ -1,9 +0,0 @@
use rst_core::wire::Request;
pub async fn request(req: Request) {
use Request::*;
match req {
Register(name, pw) => {},
_ => todo!(),
}
}

@ -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<T> = Result<T, ServerErr>;
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<MatchId, Mutex<Match>>,
users: UserStore,
lobbies: LobbyList,
}
impl Server {
/// Create a new game server
pub(crate) fn new() -> Arc<Self> {
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<Self>, 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<Self>) -> ServerResult<u64> {
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<Self>) -> ServerResult<u64> {
Ok(0)
}
// pub async fn update_map<F>(self: Arc<Self>, id: MatchId, cb: F) -> ServerResult<Response>
// where
// F: Fn(&mut Map) -> ServerResult<Response>,
// {
// match self.matches.get(&id) {
// Some(ref m) => m.lock().await.map.update(cb),
// None => Err(ServerErr::NoSuchMatch),
// }
// }
pub async fn update_players<F>(self: Arc<Self>, id: MatchId, cb: F) -> ServerResult<Response>
where
F: Fn(&mut Vec<Player>) -> ServerResult<Response>,
{
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<Self>, _name: String, _pw: String) -> Result<UserId, RegErr> {
todo!()
}
async fn login(self: Arc<Self>, _name: String, _pw: String) -> Result<User, AuthErr> {
todo!()
}
async fn logout(self: Arc<Self>, _user: User) -> Result<(), AuthErr> {
todo!()
}
async fn anonymous(self: Arc<Self>, name: String) -> Result<User, AuthErr> {
// let (_, auth) = self.users.add(name, None, true).await;
// Ok(auth)
todo!()
}
async fn join(self: Arc<Self>, user: User, lobby: LobbyId) -> Result<Lobby, LobbyErr> {
let mu = self.users.get(&user).await?;
self.lobbies.get_mut(lobby, |l| l.join(&mu)).await
}
async fn leave(self: Arc<Self>, 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<Self>,
user: User,
lobby: LobbyId,
ready: bool,
) -> Result<LobbyUpdate, LobbyErr> {
self.lobbies.get_mut(lobby, |l| l.ready(user, ready)).await
}
/// A start request was received
async fn start_req(
self: Arc<Self>,
user: UserId,
lobby: LobbyId,
) -> Result<DateTime<Utc>, 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<Self>,
user: User,
mtch: MatchId,
act: Action,
) -> UpdateState {
unimplemented!()
}
async fn leave_match(self: Arc<Self>, _user: User, _mtch: MatchId) -> Result<(), MatchErr> {
unimplemented!()
}
}
Loading…
Cancel
Save