From 22c18205bcb3702ddf60f41f77f6f06554202f35 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 22 Mar 2020 19:44:26 +0100 Subject: Updating gameplay starting mechanics, adding quadtree to map --- Cargo.lock | 110 +++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 8 ++-- src/_if.rs | 20 +++++--- src/_match.rs | 52 +++++++++++++++++++++ src/bin/game.rs | 5 ++ src/bin/server.rs | 6 +++ src/config.rs | 41 ++++++++++++++++ src/data.rs | 17 ++++--- src/lib.rs | 20 ++++++++ src/lobby.rs | 136 +++++++++++++++++++++++++++++++++++++++++++++++------- src/main.rs | 42 ----------------- src/map.rs | 29 ++++++++---- src/mapstore.rs | 17 +++++++ src/server.rs | 45 +++++++++++++----- src/users.rs | 10 +++- src/wire/mod.rs | 23 ++------- src/wire/resp.rs | 17 ++++--- 17 files changed, 476 insertions(+), 122 deletions(-) create mode 100644 src/_match.rs create mode 100644 src/bin/game.rs create mode 100644 src/bin/server.rs create mode 100644 src/config.rs create mode 100644 src/lib.rs delete mode 100644 src/main.rs create mode 100644 src/mapstore.rs diff --git a/Cargo.lock b/Cargo.lock index e770659226e5..13da0bddb9b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -535,6 +535,38 @@ dependencies = [ "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "darling" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "darling_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "darling_macro 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "darling_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "darling_macro" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "darling_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "deflate" version = "0.7.20" @@ -544,6 +576,29 @@ dependencies = [ "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "derive_builder" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "darling 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_builder_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "derive_builder_core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "darling 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "digest" version = "0.8.1" @@ -1089,6 +1144,11 @@ name = "hound" version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "image" version = "0.22.5" @@ -1475,6 +1535,29 @@ dependencies = [ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-complex 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-iter 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-complex" version = "0.2.4" @@ -1519,6 +1602,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1715,6 +1799,15 @@ dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quadtree_rs" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "derive_builder 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quote" version = "0.6.13" @@ -2010,10 +2103,12 @@ dependencies = [ "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "ggez 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "netmod-mem 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quadtree_rs 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "ratman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "ratman-identity 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2295,6 +2390,11 @@ name = "stdweb-internal-runtime" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "strsim" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "subtle" version = "1.0.0" @@ -2696,7 +2796,12 @@ dependencies = [ "checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" "checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" "checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" +"checksum darling 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fcfbcb0c5961907597a7d1148e3af036268f2b773886b8bb3eeb1e1281d3d3d6" +"checksum darling_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6afc018370c3bff3eb51f89256a6bdb18b4fdcda72d577982a14954a7a0b402c" +"checksum darling_macro 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c6d8dac1c6f1d29a41c4712b4400f878cb4fcc4c7628f298dd75038e024998d1" "checksum deflate 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)" = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4" +"checksum derive_builder 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ac53fa6a3cda160df823a9346442525dcaf1e171999a1cf23e67067e4fd64d4" +"checksum derive_builder_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0288a23da9333c246bb18c143426074a6ae96747995c5819d2947b64cd942b37" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum directories 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c" "checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" @@ -2757,6 +2862,7 @@ dependencies = [ "checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772" "checksum hex 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76cdda6bf525062a0c9e8f14ee2b37935c86b8efb6c8b69b3c83dfb518a914af" "checksum hound 3.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549" +"checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" "checksum image 0.22.5 (registry+https://github.com/rust-lang/crates.io-index)" = "08ed2ada878397b045454ac7cfb011d73132c59f31a955d230bd1f1c2e68eb4a" "checksum inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" "checksum io-kit-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f21dcc74995dd4cd090b147e79789f8d65959cbfb5f0b118002db869ea3bd0a0" @@ -2802,6 +2908,8 @@ dependencies = [ "checksum nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" "checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +"checksum num 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +"checksum num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" "checksum num-complex 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" "checksum num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eafd0b45c5537c3ba526f79d3e75120036502bebacbb3f3220914067ce39dbf2" "checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" @@ -2833,6 +2941,7 @@ dependencies = [ "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" "checksum pulldown-cmark 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eef52fac62d0ea7b9b4dc7da092aa64ea7ec3d90af6679422d3d7e0e14b6ee15" +"checksum quadtree_rs 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6ec1506903c0f5cff273bd34df6646c7040f2a7c5b0a92a9179d89551bed9ffd" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" @@ -2895,6 +3004,7 @@ dependencies = [ "checksum stdweb-derive 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" "checksum stdweb-internal-macros 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" "checksum stdweb-internal-runtime 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" +"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" "checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" diff --git a/Cargo.toml b/Cargo.toml index 8e9c96152b5a..c62fd1354e38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,14 @@ -# cargo-features = ["profile-overrides"] - [package] name = "rstnode" description = "A game about capturing computers on a network" version = "0.0.0" edition = "2018" -license = "GPL-3.0-or-later" -authors = ["Katharina Fey "] +license = "AGPL-3.0-or-later" +authors = ["Bread Machine", "Katharina Fey "] [dependencies] serde = { version = "1.0", features = ["derive", "rc"] } +serde_json = "1.0" async-std = { version = "1.0", features = ["unstable"] } async-trait = "0.1" @@ -21,6 +20,7 @@ ggez = "0.5" cgmath = { version = "*", features = ["mint"] } chrono = { version = "0.4", features = ["serde"] } rand = "0.7" +quadtree_rs = "0.1" # [profile.release.overrides."*"] # opt-level = 3 \ No newline at end of file diff --git a/src/_if.rs b/src/_if.rs index 91cb3a2daf18..59d80fb165af 100644 --- a/src/_if.rs +++ b/src/_if.rs @@ -1,11 +1,11 @@ //! A common trait interface between the server and the client use crate::wire::{ - Action, AuthErr, Lobby, LobbyId, MatchErr, MatchId, RegErr, RoomErr, RoomUpdate, UpdateState, + Action, AuthErr, Lobby, LobbyErr, LobbyId, LobbyUpdate, MatchErr, MatchId, RegErr, UpdateState, User, UserId, }; -use async_trait::async_trait; use async_std::sync::Arc; +use async_trait::async_trait; use chrono::{DateTime, Utc}; /// The main game interface implemented by the server and client @@ -24,19 +24,25 @@ pub trait GameIf { async fn anonymous(self: Arc, name: String) -> Result; /// Join a match-making lobby - async fn join(self: Arc, user: User, lobby: LobbyId) -> Result; + async fn join(self: Arc, user: User, lobby: LobbyId) -> Result; /// Leave a match-making lobby - async fn leave(self: Arc, user: User, lobby: LobbyId) -> Result<(), RoomErr>; + async fn leave(self: Arc, user: User, lobby: LobbyId) -> Result<(), LobbyErr>; /// Set the player's ready state - async fn ready(self: Arc, user: User, lobby: LobbyId, ready: bool) -> RoomUpdate; + async fn ready( + self: Arc, + user: User, + lobby: LobbyId, + ready: bool, + ) -> Result; /// Send a start request (as lobby admin) - async fn start_req(self: Arc, user: User, lobby: LobbyId) -> DateTime; + async fn start_req(self: Arc, user: UserId, lobby: LobbyId) -> Result, LobbyErr>; /// Perform a game action as a user - async fn perform_action(self: Arc, user: User, mtch: MatchId, act: Action) -> UpdateState; + async fn perform_action(self: Arc, user: User, mtch: MatchId, act: Action) + -> UpdateState; /// Leave a match async fn leave_match(self: Arc, user: User, mtch: MatchId) -> Result<(), MatchErr>; diff --git a/src/_match.rs b/src/_match.rs new file mode 100644 index 000000000000..23d3be8aeb0d --- /dev/null +++ b/src/_match.rs @@ -0,0 +1,52 @@ +use crate::{ + data::Player, + lobby::MetaLobby, + map::Map, + wire::{LobbyUser, MatchId, UserId}, +}; +use async_std::sync::Arc; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; + +/// Describes a match for the server +pub struct Match { + /// The match id + pub id: MatchId, + /// The list of active players + pub players: Vec, + /// The active game map + pub map: Map, + /// The time the match was initialised + pub init_t: DateTime, + /// The synced time the match was started + pub start_t: Option>, +} + +impl From for Match { + fn from(ml: MetaLobby) -> Self { + Self { + id: ml.inner.id, + players: ml + .inner + .players + .into_iter() + .map(|lu| Player { + id: lu.id, + name: lu.name, + color: lu.color, + money: 0.into(), + }) + .collect(), + map: Map::new(), + init_t: Utc::now(), + start_t: None, + } + } +} + +impl Match { + /// Set the start time of the match, which may be in the future + pub fn set_start(&mut self, t: DateTime) { + self.start_t = Some(t); + } +} diff --git a/src/bin/game.rs b/src/bin/game.rs new file mode 100644 index 000000000000..9bf9fc9f9631 --- /dev/null +++ b/src/bin/game.rs @@ -0,0 +1,5 @@ +//! The main game UI client + +fn main() { + +} diff --git a/src/bin/server.rs b/src/bin/server.rs new file mode 100644 index 000000000000..47db85926ac1 --- /dev/null +++ b/src/bin/server.rs @@ -0,0 +1,6 @@ +//! The dedicated server binary + + +fn main() { + +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 000000000000..8a1b0b4deb0a --- /dev/null +++ b/src/config.rs @@ -0,0 +1,41 @@ +//! The file formats backing maps and other configs + +use crate::data::{NodeId, LinkId}; +use serde::{Serialize, Deserialize}; +use std::path::Path; + +/// A config tree that describes a map +#[derive(Serialize, Deserialize)] +pub struct MapCfg { + /// The set of nodes + pub nodes: Vec, + /// Links connecting nodes + pub links: Vec, + /// Default spawn points (player count) + pub spawns: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct NodeCfg { + /// Node ID + pub id: NodeId, + /// Render/world position + pub x: f64, + pub y: f64, +} + +#[derive(Serialize, Deserialize)] +pub struct LinkCfg { + /// The link ID + id: LinkId, + /// List of connectioned nodes + con: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct SpawnCfg { + /// The node of the spawn point + pub n_id: NodeId, + /// At what number of players is this spawn available? + pub max_players: usize, +} diff --git a/src/data.rs b/src/data.rs index 4cfe0c09e463..940bda10e7e8 100644 --- a/src/data.rs +++ b/src/data.rs @@ -78,13 +78,13 @@ pub type PlayerId = usize; #[derive(Serialize, Deserialize)] pub struct Player { /// A unique player ID (per match) - id: PlayerId, + pub id: PlayerId, /// The player name - name: String, + pub name: String, /// Player color - color: Color, + pub color: Color, /// The player's money - money: AtomicU16, + pub money: AtomicU16, } /// Optionally, players can create teams @@ -144,7 +144,7 @@ pub trait ColorPalette { /// Create a new color palette fn palette() -> Self; /// Get a palette without a certain colour - fn without(b: &Color) -> Self; + fn without(&mut self, b: &Color); /// Mix a color back into the available palette fn remix(&mut self, new: Color); } @@ -167,8 +167,11 @@ impl ColorPalette for Vec { pal } - fn without(b: &Color) -> Self { - Self::palette().into_iter().filter(|a| a == b).collect() + /// Drop a colour from the palette + fn without(&mut self, b: &Color) { + if let Some(pos) = self.into_iter().rposition(|a| a == b) { + self.remove(pos); + } } fn remix(&mut self, new: Color) { diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000000..d6e3f246a08d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,20 @@ +#![allow(warnings)] + +mod _if; +pub use _if::GameIf; + +pub mod data; +pub mod gens; + +mod _match; +mod config; +mod io; +mod lobby; +mod map; +mod mapstore; +mod server; +mod stats; +mod users; +mod wire; + +pub use identity::Identity as Id; diff --git a/src/lobby.rs b/src/lobby.rs index ac6ab305414a..496f98bd7b6f 100644 --- a/src/lobby.rs +++ b/src/lobby.rs @@ -3,7 +3,7 @@ use crate::{ data::{Color, ColorPalette}, users::MetaUser, - wire::{Lobby, LobbyId, LobbyUser}, + wire::{Lobby, LobbyErr, LobbyId, LobbyUpdate, LobbyUser, User, UserId}, }; use async_std::sync::{Arc, RwLock}; use std::{ @@ -14,15 +14,60 @@ use std::{ /// A list of all the lobbies on the server pub struct LobbyList { max: AtomicUsize, - users: RwLock>>, + lobbies: RwLock>, +} + +impl LobbyList { + pub fn new() -> Self { + Self { + max: 0.into(), + lobbies: Default::default(), + } + } + + /// Create a new lobby + pub async fn create(&self, map: String) -> LobbyId { + let id = self.max.fetch_add(1, Ordering::Relaxed); + self.lobbies + .write() + .await + .insert(id, MetaLobby::create(id, map)); + id + } + + /// Remove a lobby by ID + pub async fn destroy(&self, id: LobbyId) -> Result<(), LobbyErr> { + self.consume(id).await.map(|_| ()) + } + + /// Remove and return the lobby + pub async fn consume(&self, id: LobbyId) -> Result { + self.lobbies + .write() + .await + .remove(&id) + .map_or(Err(LobbyErr::NoSuchRoom), |l| Ok(l)) + } + + /// Get mutable access to a lobby + pub async fn get_mut(&self, id: LobbyId, cb: F) -> Result + where + F: Fn(&mut MetaLobby) -> T, + { + self.lobbies + .write() + .await + .get_mut(&id) + .map_or(Err(LobbyErr::OtherError), |ref mut l| Ok(cb(l))) + } } /// Additional state held by the server /// /// The meta lobby will also sync updates to all connected users, when updates are made to the lobby pub struct MetaLobby { - palette: Vec, - inner: Lobby, + pub palette: Vec, + pub inner: Lobby, } impl MetaLobby { @@ -38,21 +83,19 @@ impl MetaLobby { } } - pub fn join(&mut self, user: MetaUser) { + pub fn join(&mut self, user: &MetaUser) -> Lobby { let color = if &user.name == "spacekookie" { let color = Color::blue(); + self.palette.without(&color); - let num = self.inner.players.len(); - self.palette = Vec::without(&color); - let in_use = self.palette.split_off(num); - - self.inner + if let Some(user) = self + .inner .players .iter_mut() - .zip(in_use.into_iter()) - .for_each(|(user, color)| { - user.color = color; - }); + .find(|u| u.color == Color::blue()) + { + user.color = self.palette.remove(0); + } color } else { @@ -60,14 +103,17 @@ impl MetaLobby { }; self.inner.players.push(LobbyUser { + admin: false, id: user.id, - name: user.name, + name: user.name.clone(), ready: false, color, }); + + self.inner.clone() } - pub fn leave(&mut self, user: MetaUser) { + pub fn leave(&mut self, user: &MetaUser) { let (pos, user) = self .inner .players @@ -84,4 +130,62 @@ impl MetaLobby { self.palette.remix(user.color); self.inner.players.remove(pos); } + + /// Check if a user is even present in a lobby + /// + /// Perform this prerequisite check before making other user-specific changes to the lobby + pub fn in_lobby(&self, user: UserId) -> bool { + self.inner + .players + .iter() + .find(|u| u.id == user) + .map(|_| true) + .unwrap_or(false) + } + + /// Set the ready state for a user + pub fn ready(&mut self, user: User, ready: bool) -> LobbyUpdate { + if let Some(user) = self + .inner + .players + .iter_mut() + .find(|u| u.id == user.id) + .as_mut() + { + user.ready = ready; + } + + LobbyUpdate::Ready( + self.inner + .players + .iter() + .filter_map(|u| if u.ready { Some(u.id) } else { None }) + .collect(), + ) + } + + /// Try to start a game, if the user can and everybody is ready + pub fn start(&mut self, user: UserId) -> Result<(), LobbyErr> { + if let Some(_) = self + .inner + .players + .iter() + .filter(|u| u.admin) + .find(|u| u.id == user) + { + return Err(LobbyErr::NotAuthorized); + }; + + match self + .inner + .players + .iter() + .filter(|u| !u.ready) + .collect::>() + .len() + { + 0 => Err(LobbyErr::NotAllReady), + _ => Ok(()), + } + } } diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 976a99af2895..000000000000 --- a/src/main.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![allow(warnings)] - -mod _if; -mod data; -mod gens; -mod io; -mod lobby; -mod map; -mod server; -mod stats; -mod users; -mod wire; - -pub(crate) use identity::Identity as Id; - -use ggez::{ - self, conf, - event::{self, EventHandler}, - Context, ContextBuilder, GameResult, -}; - -struct GameState; - -impl EventHandler for GameState { - fn update(&mut self, _: &mut Context) -> GameResult { - Ok(()) - } - - fn draw(&mut self, _: &mut Context) -> GameResult { - Ok(()) - } -} - -fn main() -> GameResult { - let cb = ContextBuilder::new("RstNode", "Katharina Fey") - .window_setup(conf::WindowSetup::default().title("RstNode").vsync(true)) - .window_mode(conf::WindowMode::default().dimensions(800.0, 600.0)); - - let (ctx, el) = &mut cb.build()?; - let mut game = GameState; // smash the state - event::run(ctx, el, &mut game) -} diff --git a/src/map.rs b/src/map.rs index f977bec93e1d..212ce75100ad 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,31 +1,42 @@ //! Implements a map graph and world logic use crate::{ - data::{Link, Node}, - server::{ServerResult, ServerErr}, - wire::Response + config::{LinkCfg, MapCfg, NodeCfg}, + data::{Link, Node, NodeId}, + server::{ServerErr, ServerResult}, + wire::Response, }; use async_std::sync::Arc; -use serde::{Deserialize, Serialize}; +use quadtree_rs::Quadtree; use std::collections::BTreeMap; +pub struct MapNode { + pos: (f64, f64), + inner: Node, +} + /// A map that people fight on /// /// A map is defined by it's graph relationships, but also where on /// the map nodes are placed, how much spacing there is, etc. All /// this information is encoded in the same structs because it's /// static, and just more convenient. -#[derive(Default, Serialize, Deserialize)] pub struct Map { - /// Node IDs mapped to node objects - nodes: BTreeMap>, + /// Node IDs mapped to coordinates + nodes: BTreeMap, /// Link IDs mapped to link objects links: BTreeMap>, + /// A coordinate map for the network + coord: Quadtree>, } impl Map { - pub fn new() -> Arc { - Arc::new(Self::default()) + pub fn new() -> Self { + Self { + nodes: BTreeMap::new(), + links: BTreeMap::new(), + coord: Quadtree::new(2), + } } pub fn update(&mut self, cb: F) -> ServerResult diff --git a/src/mapstore.rs b/src/mapstore.rs new file mode 100644 index 000000000000..85c5e36ef93b --- /dev/null +++ b/src/mapstore.rs @@ -0,0 +1,17 @@ +//! Map store + +use crate::config::MapCfg; +use std::{collections::BTreeMap, fs, path::Path}; + +pub struct MapStore { + configs: BTreeMap, +} + +impl MapStore { + /// Load a set of map configs + pub fn load_path(&mut self, path: &Path) { + fs::read_dir(&path).unwrap().for_each(|d| { + let name = d.unwrap().file_name().into_string().unwrap(); + }); + } +} diff --git a/src/server.rs b/src/server.rs index 6ddfb70a9d41..7a2fad27bb1f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,10 +1,13 @@ use crate::{ _if::GameIf, + _match::Match, + data::Player, + lobby::LobbyList, map::Map, users::UserStore, wire::{ - Action, AuthErr, Lobby, LobbyId, Match, MatchErr, MatchId, MatchUser, RegErr, Response, - RoomErr, RoomUpdate, UpdateState, User, UserId, + Action, AuthErr, Lobby, LobbyErr, LobbyId, LobbyUpdate, MatchErr, MatchId, RegErr, + Response, UpdateState, User, UserId, }, }; use async_std::sync::{Arc, Mutex, RwLock}; @@ -27,6 +30,7 @@ pub enum ServerErr { pub struct Server { matches: BTreeMap>, users: UserStore, + lobbies: LobbyList, } impl Server { @@ -35,6 +39,7 @@ impl Server { Self { matches: Default::default(), users: UserStore::new(), + lobbies: LobbyList::new(), } } @@ -63,14 +68,14 @@ impl Server { F: Fn(&mut Map) -> ServerResult, { match self.matches.get(&id) { - Some(ref mut m) => m.lock().await.map.update(cb), + 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, + F: Fn(&mut Vec) -> ServerResult, { match self.matches.get(&id) { Some(ref mut m) => cb(&mut m.lock().await.players), @@ -98,20 +103,36 @@ impl GameIf for Server { Ok(auth) } - async fn join(self: Arc, user: User, lobby: LobbyId) -> Result { - unimplemented!() + 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 } - async fn leave(self: Arc, user: User, lobby: LobbyId) -> Result<(), RoomErr> { - unimplemented!() + 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 } - async fn ready(self: Arc, user: User, lobby: LobbyId, ready: bool) -> RoomUpdate { - unimplemented!() + async fn ready( + self: Arc, + user: User, + lobby: LobbyId, + ready: bool, + ) -> Result { + self.lobbies + .get_mut(lobby, |mut l| l.ready(user, ready)) + .await } - async fn start_req(self: Arc, user: User, lobby: LobbyId) -> DateTime { - unimplemented!() + /// A start request was received + async fn start_req( + self: Arc, + user: UserId, + lobby: LobbyId, + ) -> Result, LobbyErr> { + self.lobbies.get_mut(lobby, |mut l| l.start(user)).await?; + let lob = self.lobbies.consume(lobby).await?; + Ok(Utc::now()) } async fn perform_action( diff --git a/src/users.rs b/src/users.rs index edc3c13fe3b1..0c93b83ec1da 100644 --- a/src/users.rs +++ b/src/users.rs @@ -1,7 +1,7 @@ //! A users abstraction module use crate::{ - wire::{User, UserId}, + wire::{LobbyErr, User, UserId}, Id, }; use async_std::sync::{Arc, RwLock}; @@ -31,6 +31,14 @@ impl UserStore { } } + /// Get the metadata user for a login user + pub async fn get(&self, user: &User) -> Result, LobbyErr> { + match self.users.read().await.get(&user.id) { + Some(ref u) => Ok(Arc::clone(u)), + None => Err(LobbyErr::OtherError), + } + } + pub async fn add>>( &self, name: String, diff --git a/src/wire/mod.rs b/src/wire/mod.rs index bf8dea0e8131..b1c231b44580 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -23,7 +23,7 @@ use serde::{Deserialize, Serialize}; pub type UserId = usize; /// Represents a user payload -#[derive(Clone, Serialize, Deserialize)] +#[derive(Copy, Clone, Serialize, Deserialize)] pub struct User { /// The internal user ID pub id: UserId, @@ -34,7 +34,7 @@ pub struct User { } /// A more lobby specific abstraction for a user -#[derive(Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] pub struct LobbyUser { /// The user ID pub id: UserId, @@ -42,6 +42,8 @@ pub struct LobbyUser { pub name: String, /// Are they ready? pub ready: bool, + /// Are they the lobby admin? + pub admin: bool, /// The colour they will be in the match pub color: Color, } @@ -50,7 +52,7 @@ pub struct LobbyUser { pub type LobbyId = usize; /// Represent a lobby -#[derive(Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] pub struct Lobby { /// The ID of the lobby pub id: LobbyId, @@ -65,19 +67,4 @@ pub struct Lobby { /// An alias for a match ID pub type MatchId = usize; -/// Mapping users to a player in game -#[derive(Serialize, Deserialize)] -pub struct MatchUser { - pub user: User, - pub player: Player, -} -#[derive(Serialize, Deserialize)] -pub struct Match { - /// The match id - pub id: MatchId, - /// The list of active players - pub players: Vec, - /// The active game map - pub map: Map, -} diff --git a/src/wire/resp.rs b/src/wire/resp.rs index b9f41fbe93e4..4812baf13e39 100644 --- a/src/wire/resp.rs +++ b/src/wire/resp.rs @@ -16,18 +16,17 @@ pub enum Response { /// Get a list of available pairs Rooms(Vec<(String, LobbyId)>), /// A user joins a game lobby - Join(Result), + Join(Result), /// A user leaves a game lobby - Leave(Result<(), RoomErr>), + Leave(Result<(), LobbyErr>), /// Get the new set of ready states - Ready(RoomUpdate), + Ready(LobbyUpdate), /// Receiving a start request time StartReq(DateTime), /// Response to the action with the state update GameUpdate(UpdateState), /// Leave the match (forfeit) LeaveGame(Result<(), MatchErr>), - } #[derive(Serialize, Deserialize)] @@ -53,17 +52,23 @@ pub enum AuthErr { } #[derive(Serialize, Deserialize)] -pub enum RoomErr { +pub enum LobbyErr { /// The requested room is already full RoomFull, /// The room id is unknown NoSuchRoom, /// Previously not in room NotInRoom, + /// Not everybody was ready + NotAllReady, + /// A request was sent by someone who isn't authorised + NotAuthorized, + /// Other internal error, try again? + OtherError, } #[derive(Serialize, Deserialize)] -pub enum RoomUpdate { +pub enum LobbyUpdate { /// The set of ready users Ready(Vec), } -- cgit v1.2.3