From 81cde9fcef7258ffbb76e5a44bd8ecbc12c2ace5 Mon Sep 17 00:00:00 2001 From: Martin Indra Date: Sun, 16 Jul 2023 21:25:15 +0200 Subject: [PATCH] Lobby: store DE Connector socket addr --- crates/lobby/src/games/db.rs | 11 +++++-- crates/lobby/src/games/endpoints.rs | 12 ++++---- crates/lobby/src/games/init.sql | 3 +- crates/lobby_client/src/endpoints.rs | 21 +++++++------ crates/lobby_model/src/games.rs | 46 +++++++++++++++++++++++----- crates/lobby_model/src/lib.rs | 4 +-- crates/menu/src/create.rs | 10 ++++-- docs/src/multiplayer/openapi.yaml | 12 +++++++- 8 files changed, 87 insertions(+), 32 deletions(-) diff --git a/crates/lobby/src/games/db.rs b/crates/lobby/src/games/db.rs index 9c68420f..72d1a8e9 100644 --- a/crates/lobby/src/games/db.rs +++ b/crates/lobby/src/games/db.rs @@ -16,6 +16,10 @@ use crate::{ db_error, }; +// This should correspond to the longest valid socket address. IPv6 hast up to +// 39 characters + colon + 5 characters for port number. +const SERVER_LEN: usize = 45; + #[derive(Clone)] pub(super) struct Games { pool: &'static Pool, @@ -33,6 +37,7 @@ impl Games { game_name_len = MAX_GAME_NAME_LEN, map_name_len = MAX_MAP_NAME_LEN, map_hash_len = MAP_HASH_LEN, + server_len = SERVER_LEN, ); info!("Initializing games..."); @@ -66,16 +71,18 @@ impl Games { /// This method creates a new game in the DB and places all users to it. pub(super) async fn create(&self, game: Game) -> Result<(), CreationError> { - let game_config = game.config(); + let game_setup = game.setup(); + let game_config = game_setup.config(); let mut transaction = self.pool.begin().await.map_err(CreationError::Database)?; let result = - query("INSERT INTO games (name, max_players, map_hash, map_name) VALUES(?, ?, ?, ?);") + query("INSERT INTO games (name, max_players, map_hash, map_name, server) VALUES(?, ?, ?, ?, ?);") .bind(game_config.name()) .bind(game_config.max_players()) .bind(game_config.map().hash()) .bind(game_config.map().name()) + .bind(game_setup.server().to_string()) .execute(&mut transaction) .await; db_error!( diff --git a/crates/lobby/src/games/endpoints.rs b/crates/lobby/src/games/endpoints.rs index c5612a79..d1dc3aea 100644 --- a/crates/lobby/src/games/endpoints.rs +++ b/crates/lobby/src/games/endpoints.rs @@ -1,5 +1,5 @@ use actix_web::{get, post, put, web, HttpResponse, Responder}; -use de_lobby_model::{Game, GameConfig, Validatable}; +use de_lobby_model::{Game, GameSetup, Validatable}; use log::{error, warn}; use super::db::{AdditionError, CreationError, Games, RemovalError}; @@ -20,15 +20,15 @@ pub(super) fn configure(cfg: &mut web::ServiceConfig) { async fn create( claims: web::ReqData, games: web::Data, - game_config: web::Json, + game_setup: web::Json, ) -> impl Responder { - let game_config = game_config.into_inner(); - if let Err(error) = game_config.validate() { - warn!("Invalid game configuration: {:?}", error); + let game_setup = game_setup.into_inner(); + if let Err(error) = game_setup.validate() { + warn!("Invalid game setup: {:?}", error); return HttpResponse::BadRequest().json(format!("{error}")); } - let game = Game::new(game_config, claims.username().to_owned()); + let game = Game::from_author(game_setup, claims.username().to_owned()); match games.create(game).await { Ok(_) => HttpResponse::Ok().json(()), Err(CreationError::NameTaken) => { diff --git a/crates/lobby/src/games/init.sql b/crates/lobby/src/games/init.sql index 7283027c..4616f847 100644 --- a/crates/lobby/src/games/init.sql +++ b/crates/lobby/src/games/init.sql @@ -2,7 +2,8 @@ CREATE TABLE IF NOT EXISTS games ( name CHARACTER({game_name_len}) NOT NULL PRIMARY KEY, max_players TINYINT NOT NULL, map_hash CHARACTER({map_hash_len}) NOT NULL, - map_name CHARACTER({map_name_len}) NOT NULL + map_name CHARACTER({map_name_len}) NOT NULL, + server CHARACTER({server_len}) NOT NULL ); CREATE TABLE IF NOT EXISTS players ( diff --git a/crates/lobby_client/src/endpoints.rs b/crates/lobby_client/src/endpoints.rs index e3cd0478..b9d0b630 100644 --- a/crates/lobby_client/src/endpoints.rs +++ b/crates/lobby_client/src/endpoints.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use de_lobby_model::{GameConfig, GameListing, Token, UserWithPassword, UsernameAndPassword}; +use de_lobby_model::{GameListing, GameSetup, Token, UserWithPassword, UsernameAndPassword}; use reqwest::{header::HeaderValue, Method, Request}; use serde::Serialize; use url::Url; @@ -55,11 +55,11 @@ impl LobbyRequestCreator for SignInRequest { } } -pub struct CreateGameRequest(GameConfig); +pub struct CreateGameRequest(GameSetup); impl CreateGameRequest { - pub fn new(config: GameConfig) -> Self { - Self(config) + pub fn new(setup: GameSetup) -> Self { + Self(setup) } } @@ -158,7 +158,7 @@ fn encode(parts: &[&str]) -> Cow<'static, str> { #[cfg(test)] mod tests { - use de_lobby_model::{GameMap, User}; + use de_lobby_model::{GameConfig, GameMap, User}; use super::*; @@ -204,14 +204,16 @@ mod tests { #[test] fn test_create() { - let request = CreateGameRequest::new(GameConfig::new( + let config = GameConfig::new( "Druhá Hra".to_owned(), 2, GameMap::new( "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".to_owned(), "custom".to_owned(), ), - )); + ); + let request = + CreateGameRequest::new(GameSetup::new("127.0.0.1:8082".parse().unwrap(), config)); assert_eq!(request.path().as_ref(), "/a/games"); let request = request.create(Url::parse("http://example.com/a/games").unwrap()); @@ -220,9 +222,10 @@ mod tests { let body = String::from_utf8(request.body().unwrap().as_bytes().unwrap().to_vec()).unwrap(); let expected_body = concat!( - r#"{"name":"Druhá Hra","maxPlayers":2,"map":{"hash":"#, + r#"{"server":"127.0.0.1:8082","config":{"name":"Druhá Hra","maxPlayers":2,"#, + r#""map":{"hash":"#, r#""0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef","#, - r#""name":"custom"}}"# + r#""name":"custom"}}}"# ); assert_eq!(body, expected_body); diff --git a/crates/lobby_model/src/games.rs b/crates/lobby_model/src/games.rs index b5ac08ab..c0f36380 100644 --- a/crates/lobby_model/src/games.rs +++ b/crates/lobby_model/src/games.rs @@ -1,3 +1,5 @@ +use std::net::SocketAddr; + use serde::{Deserialize, Serialize}; use crate::{ensure, validation}; @@ -10,21 +12,22 @@ const MAX_PLAYERS: u8 = 4; #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Game { - config: GameConfig, + setup: GameSetup, players: Vec, } impl Game { /// Creates a new game with the author being the only player. - pub fn new(config: GameConfig, author: String) -> Self { - Self { - config, - players: vec![author], - } + pub fn from_author(setup: GameSetup, author: String) -> Self { + Self::new(setup, vec![author]) } - pub fn config(&self) -> &GameConfig { - &self.config + pub fn new(setup: GameSetup, players: Vec) -> Self { + Self { setup, players } + } + + pub fn setup(&self) -> &GameSetup { + &self.setup } pub fn players(&self) -> &[String] { @@ -74,6 +77,33 @@ impl GamePartial { } } +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GameSetup { + server: SocketAddr, + config: GameConfig, +} + +impl GameSetup { + pub fn new(server: SocketAddr, config: GameConfig) -> Self { + Self { server, config } + } + + pub fn server(&self) -> SocketAddr { + self.server + } + + pub fn config(&self) -> &GameConfig { + &self.config + } +} + +impl validation::Validatable for GameSetup { + fn validate(&self) -> validation::Result { + self.config.validate() + } +} + #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GameConfig { diff --git a/crates/lobby_model/src/lib.rs b/crates/lobby_model/src/lib.rs index ca30f6b0..1201a39c 100644 --- a/crates/lobby_model/src/lib.rs +++ b/crates/lobby_model/src/lib.rs @@ -3,8 +3,8 @@ pub use auth::{ MIN_PASSWORD_LEN, }; pub use games::{ - Game, GameConfig, GameListing, GameMap, GamePartial, MAP_HASH_LEN, MAX_GAME_NAME_LEN, - MAX_MAP_NAME_LEN, + Game, GameConfig, GameListing, GameMap, GamePartial, GameSetup, MAP_HASH_LEN, + MAX_GAME_NAME_LEN, MAX_MAP_NAME_LEN, }; pub use validation::Validatable; diff --git a/crates/menu/src/create.rs b/crates/menu/src/create.rs index 618ac287..dccae1da 100644 --- a/crates/menu/src/create.rs +++ b/crates/menu/src/create.rs @@ -1,10 +1,12 @@ +use std::net::SocketAddr; + use bevy::prelude::*; use de_gui::{ ButtonCommands, ButtonOps, GuiCommands, LabelCommands, OuterStyle, TextBoxCommands, TextBoxQuery, ToastEvent, }; use de_lobby_client::CreateGameRequest; -use de_lobby_model::{GameConfig, GameMap, Validatable}; +use de_lobby_model::{GameConfig, GameMap, GameSetup, Validatable}; use de_map::hash::MapHash; use crate::{ @@ -255,13 +257,15 @@ fn create_game_system( } }; + let game_server: SocketAddr = "127.0.0.1:8082".parse().unwrap(); let game_config = GameConfig::new(name, max_players, selected_map.0.clone()); - if let Err(error) = game_config.validate() { + let game_setup = GameSetup::new(game_server, game_config); + if let Err(error) = game_setup.validate() { toasts.send(ToastEvent::new(format!("{error}"))); return; } - sender.send(CreateGameRequest::new(game_config)); + sender.send(CreateGameRequest::new(game_setup)); } fn response_system( diff --git a/docs/src/multiplayer/openapi.yaml b/docs/src/multiplayer/openapi.yaml index b64bd3b2..f9153c8f 100644 --- a/docs/src/multiplayer/openapi.yaml +++ b/docs/src/multiplayer/openapi.yaml @@ -95,7 +95,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/game-config" + $ref: "#/components/schemas/game-setup" responses: "200": description: The game was successfully crated and the user joined it. @@ -189,6 +189,16 @@ components: A unique user name. It is a non-empty Unicode string with maximum length of 32 bytes when encoded in UTF-8. It does not start or end with whitespace. + game-setup: + type: object + properties: + server: + type: string + description: >- + An IPv4 or IPv6 socket address. For example 127.0.0.1:8082. + config: + $ref: "#/components/schemas/game-config" + game-config: type: object properties: