From 58997c0369895eaaff53c420d228a13214725b5e Mon Sep 17 00:00:00 2001 From: ThePerkinrex Date: Tue, 20 Jul 2021 16:12:36 +0200 Subject: [PATCH] Add visible name property tom piles --- README.md | 4 +- protobuf/game.proto | 1 + server/build.rs | 10 +- server/schema/game-config.json | 6 + server/schema/server-properties.json | 4 + server/src/command.rs | 3 +- server/src/db.rs | 2 +- server/src/games/config.rs | 243 ++++++++++++++------------- server/src/games/mod.rs | 2 +- server/src/games/run.rs | 24 ++- server/src/games/run/engine.rs | 24 +-- server/src/games/run/types.rs | 101 +++++++---- server/src/logger.rs | 8 +- server/src/main.rs | 4 +- server/src/server/game.rs | 6 +- server/src/server/lobby.rs | 8 +- server/src/server/protos/game.rs | 2 + server/src/server/socket_manager.rs | 51 ++++-- server/src/server_properties.rs | 7 + unity/Assets/Scripts/GameLoader.cs | 2 +- unity/Assets/Scripts/grpc/Game.cs | 50 +++++- 21 files changed, 346 insertions(+), 216 deletions(-) diff --git a/README.md b/README.md index 66566cc..8530085 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,10 @@ When run it creates a `properties.json` with some configs, `output.log` with the ## Making a game The most important part of the game is the `game.json` file. It contains the metadata and many attributes for the game. -(`$json_schema` is where the file located in `server/schema/game-config.json`, in the future a public link will be offered) + ```json { - "$schema": "$json_schema", + "$schema": "https://theperkinrex.duckdns.org/CardsSimulator/cards-simulator/raw/branch/main/server/schema/game-config.json", "name": "UNO", "version": "0.0.0", "authors": ["ThePerkinrex"], diff --git a/protobuf/game.proto b/protobuf/game.proto index 5d9d811..96350f6 100644 --- a/protobuf/game.proto +++ b/protobuf/game.proto @@ -54,6 +54,7 @@ message GameStatus { repeated Card cards = 1; bool faceDown = 2; bool visible = 3; + string name = 4; } message Piles { map piles = 1; diff --git a/server/build.rs b/server/build.rs index 238829b..3d67586 100644 --- a/server/build.rs +++ b/server/build.rs @@ -11,7 +11,15 @@ fn main() -> Result<(), Box> { } } } - if let Err(e) = prost_build::Config::new().out_dir("src/server/protos").compile_protos(&files, &[>::from("../protobuf"), "../protobuf/include".into()]) { + if let Err(e) = prost_build::Config::new() + .out_dir("src/server/protos") + .compile_protos( + &files, + &[ + >::from("../protobuf"), + "../protobuf/include".into(), + ], + ) { eprintln!("{}", e); return Err(e.into()); } diff --git a/server/schema/game-config.json b/server/schema/game-config.json index 62e5d9a..7656a66 100644 --- a/server/schema/game-config.json +++ b/server/schema/game-config.json @@ -73,6 +73,9 @@ }, "Pile": { "type": "object", + "required": [ + "name" + ], "properties": { "cards": { "default": [], @@ -85,6 +88,9 @@ "default": false, "type": "boolean" }, + "name": { + "type": "string" + }, "visible": { "default": true, "type": "boolean" diff --git a/server/schema/server-properties.json b/server/schema/server-properties.json index 9a26295..76bc06e 100644 --- a/server/schema/server-properties.json +++ b/server/schema/server-properties.json @@ -11,6 +11,10 @@ "default": "Spah's sappin' mah sentreh", "type": "string" }, + "schema": { + "default": "https://theperkinrex.duckdns.org/CardsSimulator/cards-simulator/raw/branch/main/server/schema/server-properties.json", + "type": "string" + }, "use_colors": { "default": true, "type": "boolean" diff --git a/server/src/command.rs b/server/src/command.rs index 620ee6e..bf7db91 100644 --- a/server/src/command.rs +++ b/server/src/command.rs @@ -1,4 +1,3 @@ - use std::fs::read_dir; pub fn command_handler(command: String) -> String { @@ -20,4 +19,4 @@ pub fn command_handler(command: String) -> String { } } command -} \ No newline at end of file +} diff --git a/server/src/db.rs b/server/src/db.rs index 17b791c..2d40514 100644 --- a/server/src/db.rs +++ b/server/src/db.rs @@ -1,8 +1,8 @@ // use sqlx::{pool::PoolConnection, prelude::*, query, query_as, SqliteConnection, SqlitePool}; // use tokio::stream::StreamExt; -use fallible_iterator::FallibleIterator; use anyhow::Result; +use fallible_iterator::FallibleIterator; use rusqlite::{params, Connection, Error as SqliteError, ErrorCode}; use server_client::encapsulate; use tokio::task::spawn; diff --git a/server/src/games/config.rs b/server/src/games/config.rs index 1c1d298..5264592 100644 --- a/server/src/games/config.rs +++ b/server/src/games/config.rs @@ -6,149 +6,152 @@ use std::{collections::HashMap, fs::File, io::ErrorKind, path::Path}; #[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)] pub struct Config { - pub name: String, - #[serde(default = "default_version")] - pub version: String, - #[serde(default)] - pub authors: Vec, - pub script: String, - pub available_cards: HashMap, - pub default_back: Option, - pub piles: HashMap, - pub player_piles: HashMap, + pub name: String, + #[serde(default = "default_version")] + pub version: String, + #[serde(default)] + pub authors: Vec, + pub script: String, + pub available_cards: HashMap, + pub default_back: Option, + pub piles: HashMap, + pub player_piles: HashMap, } fn default_version() -> String { - "0.0.0".into() + "0.0.0".into() } #[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)] pub struct Card { - pub image: PathBuf, - pub back_image: Option, - #[serde(flatten)] - pub other: HashMap, + pub image: PathBuf, + pub back_image: Option, + #[serde(flatten)] + pub other: HashMap, } #[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)] pub struct Pile { - #[serde(default)] - pub cards: Vec, - #[serde(default)] - pub face_down: bool, - #[serde(default = "default_visible")] - pub visible: bool, - #[serde(flatten)] - pub other: HashMap, + pub name: String, + #[serde(default)] + pub cards: Vec, + #[serde(default)] + pub face_down: bool, + #[serde(default = "default_visible")] + pub visible: bool, + #[serde(flatten)] + pub other: HashMap, } -fn default_visible() -> bool {true} +fn default_visible() -> bool { + true +} impl Config { - pub fn load + std::fmt::Debug>(file: P) -> Self { - let s: Config = serde_json::from_reader(std::fs::File::open(&file).unwrap()) - .map_err(|e| { - log::error!( - "Malformed game defintion file @ {}", - file.as_ref().display() - ); - log::error!("JSON Error: {}", e); - panic!() - }) - .unwrap(); - if s.default_back.is_none() { - for (name, card) in &s.available_cards { - if card.back_image.is_none() { - panic!("Card {} from game {} can not have a default back if there's not default back", name, s.name) - } - } - } - s.start_image_caching(file.as_ref().parent().unwrap().to_path_buf()); - s - } + pub fn load + std::fmt::Debug>(file: P) -> Self { + let s: Config = serde_json::from_reader(std::fs::File::open(&file).unwrap()) + .map_err(|e| { + log::error!( + "Malformed game defintion file @ {}", + file.as_ref().display() + ); + log::error!("JSON Error: {}", e); + panic!() + }) + .unwrap(); + if s.default_back.is_none() { + for (name, card) in &s.available_cards { + if card.back_image.is_none() { + panic!("Card {} from game {} can not have a default back if there's not default back", name, s.name) + } + } + } + s.start_image_caching(file.as_ref().parent().unwrap().to_path_buf()); + s + } - fn start_image_caching(&self, folder: PathBuf) { + fn start_image_caching(&self, folder: PathBuf) { // TODO Add checks to say if it has finished - if let Some(p) = &self.default_back { - let p = p.clone(); - let folder = folder.clone(); - tokio::task::spawn_blocking(|| cache_image(p, folder)); - } - for ( - _, - Card { - image, - back_image, - other: _, - }, - ) in &self.available_cards - { - { - let folder = folder.clone(); - let p = image.clone(); - tokio::task::spawn_blocking(|| cache_image(p, folder)); - }; - if let Some(back_image) = back_image { - let p = back_image.clone(); - let folder = folder.clone(); - tokio::task::spawn_blocking(|| cache_image(p, folder)); - } - } - } + if let Some(p) = &self.default_back { + let p = p.clone(); + let folder = folder.clone(); + tokio::task::spawn_blocking(|| cache_image(p, folder)); + } + for ( + _, + Card { + image, + back_image, + other: _, + }, + ) in &self.available_cards + { + { + let folder = folder.clone(); + let p = image.clone(); + tokio::task::spawn_blocking(|| cache_image(p, folder)); + }; + if let Some(back_image) = back_image { + let p = back_image.clone(); + let folder = folder.clone(); + tokio::task::spawn_blocking(|| cache_image(p, folder)); + } + } + } - pub fn reload_cache(&self, folder: PathBuf) { + pub fn reload_cache(&self, folder: PathBuf) { std::fs::remove_dir_all(folder.join(".cache")).unwrap(); - self.start_image_caching(folder); - } + self.start_image_caching(folder); + } } fn cache_image, P2: AsRef>(p: P1, folder: P2) { - let original = folder.as_ref().join(p.as_ref()); - let cache_folder = folder.as_ref().join(".cache"); - let mut cached = cache_folder.join(p); - // log::info!("Caching {} on {}", original.display(), cached.display()); - // log::info!("Creating {}", cache_folder.display()); - match std::fs::create_dir(cache_folder) { - Err(e) if e.kind() == ErrorKind::AlreadyExists => Ok(()), // Ignore if folder already exists - x => x, - } - .unwrap(); - cached.set_extension("png"); - if cached.exists() - && cached.metadata().unwrap().modified().unwrap() - > original.metadata().unwrap().modified().unwrap() - { - // Cache is updated, do nothing - log::info!("cache for {} is up to date", original.display()); - } else { - // Update cache - // log::info!("Updating cache for: {}", original.display()); - let mut face_buf = Vec::new(); - image::open(&original) - .expect(&format!("Error loading the image in {:?}", original)) - .write_to(&mut face_buf, image::ImageOutputFormat::Png) - .unwrap(); - match std::fs::create_dir_all(cached.parent().unwrap()) { - Err(e) if e.kind() == ErrorKind::AlreadyExists => Ok(()), // Ignore if folder already exists - x => x, - } - .unwrap(); - File::create(cached).unwrap().write_all(&face_buf).unwrap(); - log::info!("Updated cache for: {}", original.display()); - } + let original = folder.as_ref().join(p.as_ref()); + let cache_folder = folder.as_ref().join(".cache"); + let mut cached = cache_folder.join(p); + // log::info!("Caching {} on {}", original.display(), cached.display()); + // log::info!("Creating {}", cache_folder.display()); + match std::fs::create_dir(cache_folder) { + Err(e) if e.kind() == ErrorKind::AlreadyExists => Ok(()), // Ignore if folder already exists + x => x, + } + .unwrap(); + cached.set_extension("png"); + if cached.exists() + && cached.metadata().unwrap().modified().unwrap() + > original.metadata().unwrap().modified().unwrap() + { + // Cache is updated, do nothing + log::info!("cache for {} is up to date", original.display()); + } else { + // Update cache + // log::info!("Updating cache for: {}", original.display()); + let mut face_buf = Vec::new(); + image::open(&original) + .expect(&format!("Error loading the image in {:?}", original)) + .write_to(&mut face_buf, image::ImageOutputFormat::Png) + .unwrap(); + match std::fs::create_dir_all(cached.parent().unwrap()) { + Err(e) if e.kind() == ErrorKind::AlreadyExists => Ok(()), // Ignore if folder already exists + x => x, + } + .unwrap(); + File::create(cached).unwrap().write_all(&face_buf).unwrap(); + log::info!("Updated cache for: {}", original.display()); + } } pub fn setup() { - #[cfg(debug_assertions)] - { - if let Ok(e) = std::env::var("CARGO_MANIFEST_DIR") { - std::fs::write( - AsRef::::as_ref(&e) - .join("schema") - .join("game-config.json"), - serde_json::to_string_pretty(&schemars::schema_for!(Config)).unwrap(), - ) - .unwrap() - } - } + #[cfg(debug_assertions)] + { + if let Ok(e) = std::env::var("CARGO_MANIFEST_DIR") { + std::fs::write( + AsRef::::as_ref(&e) + .join("schema") + .join("game-config.json"), + serde_json::to_string_pretty(&schemars::schema_for!(Config)).unwrap(), + ) + .unwrap() + } + } } diff --git a/server/src/games/mod.rs b/server/src/games/mod.rs index 1206234..7df6563 100644 --- a/server/src/games/mod.rs +++ b/server/src/games/mod.rs @@ -3,7 +3,7 @@ use std::fs::read_dir; mod config; mod run; -pub use config::{Pile, Config}; +pub use config::{Config, Pile}; pub use run::{CardId, CardIdx, PileKind, RunningGame, RunningPile}; use uuid::Uuid; diff --git a/server/src/games/run.rs b/server/src/games/run.rs index c21bba5..b52eca2 100644 --- a/server/src/games/run.rs +++ b/server/src/games/run.rs @@ -4,8 +4,8 @@ use rhai::{ }; use uuid::Uuid; -use std::collections::HashMap; use super::config::{Config, Pile}; +use std::collections::HashMap; mod engine; use engine::setup_engine; @@ -15,8 +15,6 @@ mod types; pub use types::{CardId, CardIdx, PileKind, RunningPile}; use types::{Data, Player, RhaiResult}; - - pub struct RunningGame { #[allow(unused)] // TODO Remove name: String, @@ -32,9 +30,7 @@ pub struct RunningGame { impl RunningGame { fn get_fns(ast: &AST) -> Vec { - ast.iter_functions() - .map(|m| m.name.to_string()) - .collect() + ast.iter_functions().map(|m| m.name.to_string()).collect() } pub fn compile(path: std::path::PathBuf, conf: &Config) -> RhaiResult<(AST, Vec)> { @@ -56,8 +52,20 @@ impl RunningGame { let engine = setup_engine(conf); let setup = Func::<(Dynamic,), Dynamic>::create_from_ast(engine, ast, "setup"); - let piles = conf.piles.clone().into_iter().map(|(k,v)| (k, RunningPile::from_pile(v, &conf.available_cards))).collect(); - let player_piles = vec![conf.player_piles.clone().into_iter().map(|(k,v)| (k, RunningPile::from_pile(v, &conf.available_cards))).collect(); current_players.len()]; + let piles = conf + .piles + .clone() + .into_iter() + .map(|(k, v)| (k, RunningPile::from_pile(v, &conf.available_cards))) + .collect(); + let player_piles = vec![ + conf.player_piles + .clone() + .into_iter() + .map(|(k, v)| (k, RunningPile::from_pile(v, &conf.available_cards))) + .collect(); + current_players.len() + ]; let Data { piles, player_piles, diff --git a/server/src/games/run/engine.rs b/server/src/games/run/engine.rs index 40bafbb..83697f4 100644 --- a/server/src/games/run/engine.rs +++ b/server/src/games/run/engine.rs @@ -6,11 +6,9 @@ use rhai::{ use std::fmt::{Debug, Display}; -use crate::games::{Config, run::types::Card}; +use crate::games::{run::types::Card, Config}; -use super::{ - types::{CardId, Data, Player, RhaiResult, RunningPile}, -}; +use super::types::{CardId, Data, Player, RhaiResult, RunningPile}; // TODO Write somekind of documentation on functions available & stuff pub fn setup_engine(conf: &Config) -> Engine { let mut engine = Engine::new(); @@ -35,7 +33,7 @@ pub fn setup_engine(conf: &Config) -> Engine { .register_fn("==", |a: Player, b: Player| a.num == b.num) .register_fn("==", |a: Player, b: i64| a.num == b as u32) .register_fn("==", |a: i64, b: Player| b.num == a as u32); - + engine .register_type::() .register_get("kind", Card::get_kind) @@ -65,12 +63,16 @@ pub fn setup_engine(conf: &Config) -> Engine { .register_fn("debug", |p: &mut CardId| p.to_string()) .register_fn("+", |s: &str, p: CardId| format!("{}{}", s, p)) .register_fn("+", |p: CardId, s: &str| p.to_string().push_str(s)); - - let available_cards = conf.available_cards.clone(); - engine.register_result_fn("new_card", move |kind: &str| { - let card = available_cards.get(kind).unwrap(); - to_dynamic(Card { kind: kind.to_string(), uuid: uuid::Uuid::new_v4(), other: card.other.clone() }) - }); + + let available_cards = conf.available_cards.clone(); + engine.register_result_fn("new_card", move |kind: &str| { + let card = available_cards.get(kind).unwrap(); + to_dynamic(Card { + kind: kind.to_string(), + uuid: uuid::Uuid::new_v4(), + other: card.other.clone(), + }) + }); engine } diff --git a/server/src/games/run/types.rs b/server/src/games/run/types.rs index 4d22b77..6411e11 100644 --- a/server/src/games/run/types.rs +++ b/server/src/games/run/types.rs @@ -143,7 +143,7 @@ impl Data { pub struct Card { pub kind: String, pub uuid: Uuid, - pub other: HashMap + pub other: HashMap, } impl Card { @@ -170,49 +170,78 @@ impl Debug for Card { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct RunningPile { - pub cards: Vec, + pub name: String, + pub cards: Vec, pub face_down: bool, pub visible: bool, #[serde(flatten)] - pub other: HashMap, + pub other: HashMap, } impl RunningPile { - pub fn from_pile(p: Pile, available_cards: &HashMap) -> Self { - - Self { - cards: p.cards.into_iter().map(|kind| {let other = available_cards.get(&kind).unwrap().other.clone(); Card {kind, uuid: Uuid::new_v4(), other}}).collect(), - other: p.other, + pub fn from_pile( + p: Pile, + available_cards: &HashMap, + ) -> Self { + Self { + cards: p + .cards + .into_iter() + .map(|kind| { + let other = available_cards.get(&kind).unwrap().other.clone(); + Card { + kind, + uuid: Uuid::new_v4(), + other, + } + }) + .collect(), + other: p.other, face_down: p.face_down, - visible: p.visible - } - } + visible: p.visible, + name: p.name, + } + } } impl RunningPile { - pub fn from_rhai_map(map: rhai::Map) -> Result> { - // println!("{}", map.get("cards") - // .ok_or("Pile doesn't have property cards")?.type_name()); - let cards: Vec = - rhai::serde::from_dynamic(map.get("cards").ok_or("Pile doesn't have property cards")?)?; - - let face_down: bool = rhai::serde::from_dynamic(map.get("face_down").ok_or("Pile doesn't have property face_down")?)?; - let visible: bool = rhai::serde::from_dynamic(map.get("visible").ok_or("Pile doesn't have property visible")?)?; - - let other_fallible: Vec>> = - map.into_iter() - .map(|(x, v)| (x.to_string(), v)) - .filter(|(s, _)| s != &"cards".to_string()) - .map(|(k, v)| Ok((k, rhai::serde::from_dynamic::(&v)?))) - .collect(); - - let mut other = HashMap::new(); - - for x in other_fallible { - let (k, v) = x?; - other.insert(k, v); - } - - Ok(Self { cards, other, face_down, visible }) - } + pub fn from_rhai_map(map: rhai::Map) -> Result> { + // println!("{}", map.get("cards") + // .ok_or("Pile doesn't have property cards")?.type_name()); + let cards: Vec = + rhai::serde::from_dynamic(map.get("cards").ok_or("Pile doesn't have property cards")?)?; + + let face_down: bool = rhai::serde::from_dynamic( + map.get("face_down") + .ok_or("Pile doesn't have property face_down")?, + )?; + let visible: bool = rhai::serde::from_dynamic( + map.get("visible") + .ok_or("Pile doesn't have property visible")?, + )?; + let name: String = + rhai::serde::from_dynamic(map.get("name").ok_or("Pile doesn't have property name")?)?; + + let other_fallible: Vec>> = + map.into_iter() + .map(|(x, v)| (x.to_string(), v)) + .filter(|(s, _)| s != &"cards".to_string()) + .map(|(k, v)| Ok((k, rhai::serde::from_dynamic::(&v)?))) + .collect(); + + let mut other = HashMap::new(); + + for x in other_fallible { + let (k, v) = x?; + other.insert(k, v); + } + + Ok(Self { + cards, + other, + face_down, + visible, + name, + }) + } } diff --git a/server/src/logger.rs b/server/src/logger.rs index b4b0818..6084dce 100644 --- a/server/src/logger.rs +++ b/server/src/logger.rs @@ -31,10 +31,7 @@ pub fn setup_with_handler String + Send + 'static>( properties: &crate::server_properties::ServerProperties, handler: F, ) -> Result<(Close, Stdin), fern::InitError> { - let (stdout, stdin, close, join_handle) = TerminalHandler::new( - handler, - properties.use_colors, - ); + let (stdout, stdin, close, join_handle) = TerminalHandler::new(handler, properties.use_colors); // let simple_formatter = |out: fern::FormatCallback, message: _, record: &log::Record| { // out.finish(format_args!( @@ -211,7 +208,6 @@ where _ => return true, } while is_event_available() { - updated = true; match crossterm::event::read().unwrap() { Event::Resize(_cols, rows) => self.lines.size = rows as usize - 1, @@ -244,7 +240,7 @@ where MouseEventKind::ScrollDown => self.lines.scroll_down(), _ => { updated = false; - }, + } }, } } diff --git a/server/src/main.rs b/server/src/main.rs index 7483d8b..133420d 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,10 +1,10 @@ mod allocator; +mod command; mod db; mod games; mod logger; mod server; mod server_properties; -mod command; use log::info; @@ -17,7 +17,7 @@ async fn main() { let (close, _stdin) = logger::setup(&p).unwrap(); // let packet = include_bytes!("a.bin"); // decode(&*packet); - + // info!(target: "setup", "Starting server in `{}`", std::env::current_dir().unwrap().display()); info!(target: "setup", "Server name: `{}`", p.name); info!(target: "setup", "Serving on address `{}`", p.addr); diff --git a/server/src/server/game.rs b/server/src/server/game.rs index 7753ec0..e369368 100644 --- a/server/src/server/game.rs +++ b/server/src/server/game.rs @@ -54,7 +54,8 @@ pub(super) async fn get_status(data: &mut ServiceData) -> Result { }) .collect(), face_down: v.face_down, - visible: v.visible + visible: v.visible, + name: v.name, }, ) }) @@ -81,7 +82,8 @@ pub(super) async fn get_status(data: &mut ServiceData) -> Result { }) .collect(), face_down: v.face_down, - visible: v.visible + visible: v.visible, + name: v.name, }, ) }) diff --git a/server/src/server/lobby.rs b/server/src/server/lobby.rs index 8884d59..cb2887b 100644 --- a/server/src/server/lobby.rs +++ b/server/src/server/lobby.rs @@ -68,7 +68,13 @@ pub(super) async fn ready(data: &mut ServiceData, socket_mgr: &SocketManager) -> log::info!("Player {} is ready", uuid); if is_starting { let game_status = super::game::get_status(data).await?; - socket_mgr.broadcast_to_lobby(&mut data.db, data.user_id.get()?, Data::GameStatus(game_status)).await?; + socket_mgr + .broadcast_to_lobby( + &mut data.db, + data.user_id.get()?, + Data::GameStatus(game_status), + ) + .await?; } Ok(()) } diff --git a/server/src/server/protos/game.rs b/server/src/server/protos/game.rs index 4232b3f..f45fc1c 100644 --- a/server/src/server/protos/game.rs +++ b/server/src/server/protos/game.rs @@ -93,6 +93,8 @@ pub mod game_status { pub face_down: bool, #[prost(bool, tag="3")] pub visible: bool, + #[prost(string, tag="4")] + pub name: ::prost::alloc::string::String, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct Piles { diff --git a/server/src/server/socket_manager.rs b/server/src/server/socket_manager.rs index b92b990..3bb4099 100644 --- a/server/src/server/socket_manager.rs +++ b/server/src/server/socket_manager.rs @@ -1,23 +1,26 @@ -use std::collections::HashMap; +use super::protos; +use crate::db; +use anyhow::anyhow; +use anyhow::Result; use log::debug; +use prost::Message; +use std::collections::HashMap; use tokio::{ io::{AsyncWriteExt, BufWriter}, - net::{tcp::OwnedWriteHalf}, + net::tcp::OwnedWriteHalf, sync::RwLock, }; use uuid::Uuid; -use anyhow::Result; -use prost::Message; -use super::protos; -use anyhow::anyhow; -use crate::db; pub struct MessageWriter { writer: BufWriter, } impl MessageWriter { - pub async fn write(&mut self, packet: protos::protocol::server_client_packet::Data) -> Result<()> { + pub async fn write( + &mut self, + packet: protos::protocol::server_client_packet::Data, + ) -> Result<()> { let length = packet.encoded_len(); if length > u32::MAX as usize { panic!("Can't send a message larger than {} bytes", u32::MAX); @@ -43,13 +46,18 @@ pub struct SocketManager { impl SocketManager { pub fn new() -> Self { Self { - writers: RwLock::new(HashMap::new()) + writers: RwLock::new(HashMap::new()), } } pub async fn connect(&self, uuid: Uuid, writer: OwnedWriteHalf) { let mut lock = self.writers.write().await; - lock.insert(uuid, RwLock::new(MessageWriter {writer: BufWriter::new(writer)})); + lock.insert( + uuid, + RwLock::new(MessageWriter { + writer: BufWriter::new(writer), + }), + ); } pub async fn disconnect(&self, uuid: &Uuid) -> Option { @@ -57,20 +65,33 @@ impl SocketManager { lock.remove(uuid).map(|x| x.into_inner()) } - pub async fn write(&self, uuid: &Uuid, message: protos::protocol::server_client_packet::Data) -> Result<()> { + pub async fn write( + &self, + uuid: &Uuid, + message: protos::protocol::server_client_packet::Data, + ) -> Result<()> { let lock = self.writers.read().await; { - let mut lock = lock.get(uuid).ok_or(anyhow!("Can't get socket with uuid {}", uuid))?.write().await; + let mut lock = lock + .get(uuid) + .ok_or(anyhow!("Can't get socket with uuid {}", uuid))? + .write() + .await; lock.write(message).await?; } Ok(()) } - pub async fn broadcast_to_lobby(&self, db: &mut db::DbClient, user_uuid: Uuid, message: protos::protocol::server_client_packet::Data) -> Result<()> { + pub async fn broadcast_to_lobby( + &self, + db: &mut db::DbClient, + user_uuid: Uuid, + message: protos::protocol::server_client_packet::Data, + ) -> Result<()> { let users = db.get_uuids_in_lobby_where_user_is(user_uuid).await; for user in users { self.write(&user, message.clone()).await?; } Ok(()) - } -} \ No newline at end of file + } +} diff --git a/server/src/server_properties.rs b/server/src/server_properties.rs index 6f34df7..165de94 100644 --- a/server/src/server_properties.rs +++ b/server/src/server_properties.rs @@ -11,6 +11,8 @@ pub struct ServerProperties { pub addr: SocketAddr, #[serde(default = "default_colors")] pub use_colors: bool, + #[serde(alias="$schema", default = "default_schema")] + schema: String } impl ServerProperties { @@ -32,6 +34,7 @@ impl Default for ServerProperties { name: default_name(), addr: default_addr(), use_colors: default_colors(), + schema: default_schema(), } } } @@ -48,6 +51,10 @@ fn default_name() -> String { "Spah's sappin' mah sentreh".into() } +fn default_schema() -> String { + "https://theperkinrex.duckdns.org/CardsSimulator/cards-simulator/raw/branch/main/server/schema/server-properties.json".into() +} + pub fn setup() { if cfg!(debug_assertions) { if let Ok(e) = std::env::var("CARGO_MANIFEST_DIR") { diff --git a/unity/Assets/Scripts/GameLoader.cs b/unity/Assets/Scripts/GameLoader.cs index 65fccd5..1d04afa 100644 --- a/unity/Assets/Scripts/GameLoader.cs +++ b/unity/Assets/Scripts/GameLoader.cs @@ -188,7 +188,7 @@ public class GameLoader : MonoBehaviour { var pile = Instantiate(instance.playerPilePrefab, instance.playerPiles.transform); var tab = Instantiate(instance.pileTabPrefab, instance.pileTabs.transform); - tab.GetComponentInChildren().text = key; + tab.GetComponentInChildren().text = value.Name; RegisterPile(key + mmc.currentUsername, new PileProperties { name = key, owner = mmc.currentUsername, cards = value.Cards.ToArray(), gameObject = pile, tab = tab, faceDown = value.FaceDown }); } else UpdatePile(key + mmc.currentUsername, new PileProperties { name = key, owner = mmc.currentUsername, cards = value.Cards.ToArray(), gameObject = pileEntry.gameObject, tab = pileEntry.tab, faceDown = value.FaceDown }); diff --git a/unity/Assets/Scripts/grpc/Game.cs b/unity/Assets/Scripts/grpc/Game.cs index 4e88932..bcb97d9 100644 --- a/unity/Assets/Scripts/grpc/Game.cs +++ b/unity/Assets/Scripts/grpc/Game.cs @@ -33,17 +33,17 @@ namespace Game { "CgVvd25lZBgBIAEoDUgAEigKBmNvbW1vbhgCIAEoCzIWLmdvb2dsZS5wcm90", "b2J1Zi5FbXB0eUgAQgYKBGtpbmQiYAoGQ2FyZElkEiAKCHBpbGVLaW5kGAEg", "ASgLMg4uZ2FtZS5QaWxlS2luZBIQCghwaWxlTmFtZRgCIAEoCRIiCgljYXJk", - "SW5kZXgYAyABKAsyDy5nYW1lLkNhcmRJbmRleCKuAwoKR2FtZVN0YXR1cxIr", + "SW5kZXgYAyABKAsyDy5nYW1lLkNhcmRJbmRleCK8AwoKR2FtZVN0YXR1cxIr", "Cgtjb21tb25QaWxlcxgBIAEoCzIWLmdhbWUuR2FtZVN0YXR1cy5QaWxlcxIr", "CgtwbGF5ZXJQaWxlcxgCIAMoCzIWLmdhbWUuR2FtZVN0YXR1cy5QaWxlcxIb", "CgVuYW1lcxgDIAMoCzIMLmNvbW1vbi5OYW1lEhMKC2N1cnJlbnRUdXJuGAQg", "ASgNGkMKBENhcmQSHAoEa2luZBgBIAEoCzIOLmdhbWUuQ2FyZEtpbmQSDwoH", - "dmlzaWJsZRgCIAEoCBIMCgR1dWlkGAMgASgJGk8KBFBpbGUSJAoFY2FyZHMY", + "dmlzaWJsZRgCIAEoCBIMCgR1dWlkGAMgASgJGl0KBFBpbGUSJAoFY2FyZHMY", "ASADKAsyFS5nYW1lLkdhbWVTdGF0dXMuQ2FyZBIQCghmYWNlRG93bhgCIAEo", - "CBIPCgd2aXNpYmxlGAMgASgIGn4KBVBpbGVzEjAKBXBpbGVzGAEgAygLMiEu", - "Z2FtZS5HYW1lU3RhdHVzLlBpbGVzLlBpbGVzRW50cnkaQwoKUGlsZXNFbnRy", - "eRILCgNrZXkYASABKAkSJAoFdmFsdWUYAiABKAsyFS5nYW1lLkdhbWVTdGF0", - "dXMuUGlsZToCOAFiBnByb3RvMw==")); + "CBIPCgd2aXNpYmxlGAMgASgIEgwKBG5hbWUYBCABKAkafgoFUGlsZXMSMAoF", + "cGlsZXMYASADKAsyIS5nYW1lLkdhbWVTdGF0dXMuUGlsZXMuUGlsZXNFbnRy", + "eRpDCgpQaWxlc0VudHJ5EgsKA2tleRgBIAEoCRIkCgV2YWx1ZRgCIAEoCzIV", + "LmdhbWUuR2FtZVN0YXR1cy5QaWxlOgI4AWIGcHJvdG8z")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.EmptyReflection.Descriptor, global::Common.CommonReflection.Descriptor, }, new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { @@ -53,7 +53,7 @@ namespace Game { new pbr::GeneratedClrTypeInfo(typeof(global::Game.PileKind), global::Game.PileKind.Parser, new[]{ "Owned", "Common" }, new[]{ "Kind" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Game.CardId), global::Game.CardId.Parser, new[]{ "PileKind", "PileName", "CardIndex" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Game.GameStatus), global::Game.GameStatus.Parser, new[]{ "CommonPiles", "PlayerPiles", "Names", "CurrentTurn" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Game.GameStatus.Types.Card), global::Game.GameStatus.Types.Card.Parser, new[]{ "Kind", "Visible", "Uuid" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::Game.GameStatus.Types.Pile), global::Game.GameStatus.Types.Pile.Parser, new[]{ "Cards", "FaceDown", "Visible" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Game.GameStatus.Types.Pile), global::Game.GameStatus.Types.Pile.Parser, new[]{ "Cards", "FaceDown", "Visible", "Name" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Game.GameStatus.Types.Piles), global::Game.GameStatus.Types.Piles.Parser, new[]{ "Piles_" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, })}) })); } @@ -1855,6 +1855,7 @@ namespace Game { cards_ = other.cards_.Clone(); faceDown_ = other.faceDown_; visible_ = other.visible_; + name_ = other.name_; _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); } @@ -1895,6 +1896,17 @@ namespace Game { } } + /// Field number for the "name" field. + public const int NameFieldNumber = 4; + private string name_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string Name { + get { return name_; } + set { + name_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public override bool Equals(object other) { return Equals(other as Pile); @@ -1911,6 +1923,7 @@ namespace Game { if(!cards_.Equals(other.cards_)) return false; if (FaceDown != other.FaceDown) return false; if (Visible != other.Visible) return false; + if (Name != other.Name) return false; return Equals(_unknownFields, other._unknownFields); } @@ -1920,6 +1933,7 @@ namespace Game { hash ^= cards_.GetHashCode(); if (FaceDown != false) hash ^= FaceDown.GetHashCode(); if (Visible != false) hash ^= Visible.GetHashCode(); + if (Name.Length != 0) hash ^= Name.GetHashCode(); if (_unknownFields != null) { hash ^= _unknownFields.GetHashCode(); } @@ -1945,6 +1959,10 @@ namespace Game { output.WriteRawTag(24); output.WriteBool(Visible); } + if (Name.Length != 0) { + output.WriteRawTag(34); + output.WriteString(Name); + } if (_unknownFields != null) { _unknownFields.WriteTo(output); } @@ -1963,6 +1981,10 @@ namespace Game { output.WriteRawTag(24); output.WriteBool(Visible); } + if (Name.Length != 0) { + output.WriteRawTag(34); + output.WriteString(Name); + } if (_unknownFields != null) { _unknownFields.WriteTo(ref output); } @@ -1979,6 +2001,9 @@ namespace Game { if (Visible != false) { size += 1 + 1; } + if (Name.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Name); + } if (_unknownFields != null) { size += _unknownFields.CalculateSize(); } @@ -1997,6 +2022,9 @@ namespace Game { if (other.Visible != false) { Visible = other.Visible; } + if (other.Name.Length != 0) { + Name = other.Name; + } _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); } @@ -2023,6 +2051,10 @@ namespace Game { Visible = input.ReadBool(); break; } + case 34: { + Name = input.ReadString(); + break; + } } } #endif @@ -2049,6 +2081,10 @@ namespace Game { Visible = input.ReadBool(); break; } + case 34: { + Name = input.ReadString(); + break; + } } } }