Browse Source

Add visible name property tom piles

main
ThePerkinrex 5 years ago
parent
commit
58997c0369
No known key found for this signature in database GPG Key ID: FD81DE6D75E20917
  1. 4
      README.md
  2. 1
      protobuf/game.proto
  3. 10
      server/build.rs
  4. 6
      server/schema/game-config.json
  5. 4
      server/schema/server-properties.json
  6. 3
      server/src/command.rs
  7. 2
      server/src/db.rs
  8. 243
      server/src/games/config.rs
  9. 2
      server/src/games/mod.rs
  10. 24
      server/src/games/run.rs
  11. 24
      server/src/games/run/engine.rs
  12. 101
      server/src/games/run/types.rs
  13. 8
      server/src/logger.rs
  14. 4
      server/src/main.rs
  15. 6
      server/src/server/game.rs
  16. 8
      server/src/server/lobby.rs
  17. 2
      server/src/server/protos/game.rs
  18. 51
      server/src/server/socket_manager.rs
  19. 7
      server/src/server_properties.rs
  20. 2
      unity/Assets/Scripts/GameLoader.cs
  21. 50
      unity/Assets/Scripts/grpc/Game.cs

4
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` 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"],

1
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<string, Pile> piles = 1;

10
server/build.rs

@ -11,7 +11,15 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
}
}
if let Err(e) = prost_build::Config::new().out_dir("src/server/protos").compile_protos(&files, &[<std::path::PathBuf as From<&str>>::from("../protobuf"), "../protobuf/include".into()]) {
if let Err(e) = prost_build::Config::new()
.out_dir("src/server/protos")
.compile_protos(
&files,
&[
<std::path::PathBuf as From<&str>>::from("../protobuf"),
"../protobuf/include".into(),
],
) {
eprintln!("{}", e);
return Err(e.into());
}

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

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

3
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
}
}

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

243
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<String>,
pub script: String,
pub available_cards: HashMap<String, Card>,
pub default_back: Option<PathBuf>,
pub piles: HashMap<String, Pile>,
pub player_piles: HashMap<String, Pile>,
pub name: String,
#[serde(default = "default_version")]
pub version: String,
#[serde(default)]
pub authors: Vec<String>,
pub script: String,
pub available_cards: HashMap<String, Card>,
pub default_back: Option<PathBuf>,
pub piles: HashMap<String, Pile>,
pub player_piles: HashMap<String, Pile>,
}
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<PathBuf>,
#[serde(flatten)]
pub other: HashMap<String, serde_json::Value>,
pub image: PathBuf,
pub back_image: Option<PathBuf>,
#[serde(flatten)]
pub other: HashMap<String, serde_json::Value>,
}
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
pub struct Pile {
#[serde(default)]
pub cards: Vec<String>,
#[serde(default)]
pub face_down: bool,
#[serde(default = "default_visible")]
pub visible: bool,
#[serde(flatten)]
pub other: HashMap<String, serde_json::Value>,
pub name: String,
#[serde(default)]
pub cards: Vec<String>,
#[serde(default)]
pub face_down: bool,
#[serde(default = "default_visible")]
pub visible: bool,
#[serde(flatten)]
pub other: HashMap<String, serde_json::Value>,
}
fn default_visible() -> bool {true}
fn default_visible() -> bool {
true
}
impl Config {
pub fn load<P: AsRef<std::path::Path> + 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<P: AsRef<std::path::Path> + 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<P1: AsRef<Path>, P2: AsRef<Path>>(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::<std::path::Path>::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::<std::path::Path>::as_ref(&e)
.join("schema")
.join("game-config.json"),
serde_json::to_string_pretty(&schemars::schema_for!(Config)).unwrap(),
)
.unwrap()
}
}
}

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

24
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<String> {
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<String>)> {
@ -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,

24
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::<Card>()
.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
}

101
server/src/games/run/types.rs

@ -143,7 +143,7 @@ impl Data {
pub struct Card {
pub kind: String,
pub uuid: Uuid,
pub other: HashMap<String, serde_json::Value>
pub other: HashMap<String, serde_json::Value>,
}
impl Card {
@ -170,49 +170,78 @@ impl Debug for Card {
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RunningPile {
pub cards: Vec<Card>,
pub name: String,
pub cards: Vec<Card>,
pub face_down: bool,
pub visible: bool,
#[serde(flatten)]
pub other: HashMap<String, serde_json::Value>,
pub other: HashMap<String, serde_json::Value>,
}
impl RunningPile {
pub fn from_pile(p: Pile, available_cards: &HashMap<String, super::super::config::Card>) -> 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<String, super::super::config::Card>,
) -> 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<Self, Box<rhai::EvalAltResult>> {
// println!("{}", map.get("cards")
// .ok_or("Pile doesn't have property cards")?.type_name());
let cards: Vec<Card> =
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<Result<(String, serde_json::Value), Box<rhai::EvalAltResult>>> =
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::<serde_json::Value>(&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<Self, Box<rhai::EvalAltResult>> {
// println!("{}", map.get("cards")
// .ok_or("Pile doesn't have property cards")?.type_name());
let cards: Vec<Card> =
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<Result<(String, serde_json::Value), Box<rhai::EvalAltResult>>> =
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::<serde_json::Value>(&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,
})
}
}

8
server/src/logger.rs

@ -31,10 +31,7 @@ pub fn setup_with_handler<F: Fn(String) -> String + Send + 'static>(
properties: &crate::server_properties::ServerProperties,
handler: F,
) -> Result<(Close, Stdin<String>), 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;
},
}
},
}
}

4
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);

6
server/src/server/game.rs

@ -54,7 +54,8 @@ pub(super) async fn get_status(data: &mut ServiceData) -> Result<GameStatus> {
})
.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<GameStatus> {
})
.collect(),
face_down: v.face_down,
visible: v.visible
visible: v.visible,
name: v.name,
},
)
})

8
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(())
}

2
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 {

51
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<OwnedWriteHalf>,
}
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<MessageWriter> {
@ -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(())
}
}
}
}

7
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") {

2
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>().text = key;
tab.GetComponentInChildren<Text>().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 });

50
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 {
}
}
/// <summary>Field number for the "name" field.</summary>
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;
}
}
}
}

Loading…
Cancel
Save