Merge pull request #2 from szymon-jozef/feat/settings

feat/settings
This commit is contained in:
Szymon
2026-08-18 13:23:59 -07:00
committed by GitHub
9 changed files with 252 additions and 33 deletions

118
Cargo.lock generated
View File

@@ -127,12 +127,14 @@ dependencies = [
name = "checkers"
version = "0.0.1"
dependencies = [
"dirs",
"env_logger",
"log",
"postcard",
"rand",
"serde",
"tokio",
"toml",
"uuid",
]
@@ -197,6 +199,27 @@ dependencies = [
"thiserror",
]
[[package]]
name = "dirs"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys",
]
[[package]]
name = "embedded-io"
version = "0.4.0"
@@ -278,6 +301,17 @@ dependencies = [
"slab",
]
[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "getrandom"
version = "0.4.2"
@@ -425,6 +459,15 @@ version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "libredox"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d0a00925a9f930d679b6789b721e3a7f9ed110f41b86d2497caa780c3a070a"
dependencies = [
"libc",
]
[[package]]
name = "lock_api"
version = "0.4.14"
@@ -469,6 +512,12 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "parking_lot"
version = "0.12.5"
@@ -567,7 +616,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
dependencies = [
"chacha20",
"getrandom",
"getrandom 0.4.2",
"rand_core",
]
@@ -586,6 +635,17 @@ dependencies = [
"bitflags 2.13.1",
]
[[package]]
name = "redox_users"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [
"getrandom 0.2.17",
"libredox",
"thiserror",
]
[[package]]
name = "regex"
version = "1.13.1"
@@ -685,6 +745,15 @@ dependencies = [
"zmij",
]
[[package]]
name = "serde_spanned"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
dependencies = [
"serde_core",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.8"
@@ -802,6 +871,45 @@ dependencies = [
"syn 3.0.3",
]
[[package]]
name = "toml"
version = "1.1.4+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5"
dependencies = [
"indexmap",
"serde_core",
"serde_spanned",
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
]
[[package]]
name = "toml_datetime"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.1.3+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
[[package]]
name = "unicode-ident"
version = "1.0.24"
@@ -826,7 +934,7 @@ version = "1.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239"
dependencies = [
"getrandom",
"getrandom 0.4.2",
"js-sys",
"serde_core",
"wasm-bindgen",
@@ -950,6 +1058,12 @@ dependencies = [
"windows-link",
]
[[package]]
name = "winnow"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
[[package]]
name = "wit-bindgen"
version = "0.51.0"

View File

@@ -10,12 +10,14 @@ authors = [ "szymon-jozef" ]
edition = "2024"
[dependencies]
dirs = "6.0.0"
env_logger = "0.11.11"
log = "0.4.33"
postcard = { version = "1.1.3", features = ["alloc"] }
rand = "0.10.2"
serde = { version = "1.0.229", features = ["derive"] }
tokio = { version = "1.53.1", features = ["full"] }
toml = "1.1.4"
[dependencies.uuid]
version = "1.24.0"

View File

@@ -1,2 +1,3 @@
pub mod logic;
pub mod network;
pub mod settings;

View File

@@ -1,4 +1,4 @@
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::net::SocketAddr;
use log::{debug, error, info, warn};
use tokio::sync::mpsc::{self, Receiver, Sender};
@@ -21,14 +21,9 @@ use crate::{
},
network_identity::NetworkIdentity,
},
settings::{client_settings::ClientSettings, general_settings::SettingsLike},
};
#[derive(Clone)]
pub struct ClientSettings {
server_url: SocketAddr,
name: String,
}
pub enum ClientCommands {
SendCapture(CapturePath),
SendMove { from: Position, to: Position },
@@ -64,10 +59,7 @@ pub struct Client {
impl Client {
pub async fn new() -> Option<Client> {
let settings = ClientSettings {
server_url: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 6767),
name: "Morbius".into(),
};
let settings = ClientSettings::new();
let (conn_sender, conn_reciever) =
mpsc::channel::<(SocketAddr, Message<ServerMessage>)>(1024);

View File

@@ -1,8 +1,5 @@
use log::{debug, error, info, warn};
use std::{
collections::HashMap,
net::{IpAddr, Ipv4Addr, SocketAddr},
};
use std::{collections::HashMap, net::SocketAddr};
use tokio::{
net::TcpListener,
sync::mpsc::{Receiver, Sender},
@@ -23,14 +20,15 @@ use crate::{
},
network_identity::NetworkServerIdentity,
},
settings::{general_settings::SettingsLike, server_settings::ServerSettings},
};
// TODO! More settings!
pub struct ServerSettings {
pub addr: SocketAddr,
pub max_connections: usize,
pub allow_spectators: bool,
}
// TODO! Maybe move this somewhere more global and share with `board` struct
const MAX_PLAYABLE_CONNECTIONS: usize = 2; // DON'T EVER CHANGE THIS AS GAME OF CHECKERS HAS ONLY TWO PLAYERS, U
// UNDERSTAND????
//
// There will be a spectator mode, hence the name __PLAYABLE__ connection. You will be able to have
// more connections, but they won't be able to play
#[derive(PartialEq)]
enum ServerStage {
@@ -71,11 +69,7 @@ pub struct Server {
impl Server {
pub async fn new() -> Server {
// TODO! Should be read from a file
let settings = ServerSettings {
addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 6767),
max_connections: 2,
allow_spectators: false,
};
let settings = ServerSettings::new();
let (sender, reciever) =
tokio::sync::mpsc::channel::<(SocketAddr, Message<ClientMessage>)>(1024); // random
@@ -153,7 +147,7 @@ impl Server {
tokio::select! {
new_client_opt = self.welcoming_reciever.as_mut().expect("Welcoming reciever was not set. Was the server started?").recv() => {
if let Some((addr, sender)) = new_client_opt {
if self.state.stage == ServerStage::Lobby && self.connections.keys().len() < self.settings.max_connections && !self.settings.allow_spectators {
if self.state.stage == ServerStage::Lobby && self.connections.keys().len() < MAX_PLAYABLE_CONNECTIONS && !self.settings.allow_spectators {
let identity = NetworkServerIdentity::new(sender);
self.connections.insert(addr, identity);
self.request_handshake(addr).await;
@@ -226,7 +220,7 @@ impl Server {
}
}
if names.len() != self.settings.max_connections {
if names.len() != MAX_PLAYABLE_CONNECTIONS {
error!("Tried starting the game, but could not gather enough ready player names");
return;
}
@@ -322,11 +316,11 @@ impl Server {
self.state.ready_count += 1;
info!(
"{} signaled readiness. Currently ready: {}/{}",
addr, self.state.ready_count, self.settings.max_connections
addr, self.state.ready_count, MAX_PLAYABLE_CONNECTIONS
);
self.connections.get_mut(&addr).expect("Couldn't get the identity of the ready player. I guess this should never happen so i'm panicking like a little bitch").identity.is_ready = true; // TODO! This sometimes happens for some reason (when there is a game going, both player leave and someone tries to join)
if self.state.ready_count == self.settings.max_connections {
if self.state.ready_count == MAX_PLAYABLE_CONNECTIONS {
info!("All players are ready! Changing state to ServerStage::Game");
self.state.stage = ServerStage::Game;
self.start_game().await;
@@ -482,7 +476,7 @@ impl Server {
self.state.ready_count -= 1;
info!(
"Currently {}/{} players are ready",
self.state.ready_count, self.settings.max_connections
self.state.ready_count, MAX_PLAYABLE_CONNECTIONS
);
}
self.connections.remove(&addr);

View File

@@ -0,0 +1,26 @@
use std::net::SocketAddr;
use serde::{Deserialize, Serialize};
use crate::settings::general_settings::{DEFAULT_URL, SettingsLike};
#[derive(Clone, Serialize, Deserialize)]
pub struct ClientSettings {
pub server_url: SocketAddr,
pub name: String,
}
impl Default for ClientSettings {
fn default() -> Self {
Self {
server_url: DEFAULT_URL,
name: "Player".to_string(),
}
}
}
impl SettingsLike for ClientSettings {
fn get_file_path() -> Result<std::path::PathBuf, Box<dyn core::error::Error>> {
Ok(Self::get_config_dir()?.join("client.toml"))
}
}

View File

@@ -0,0 +1,60 @@
use core::error;
use std::{
net::{IpAddr, Ipv4Addr, SocketAddr},
path::PathBuf,
};
use log::warn;
use serde::{Serialize, de::DeserializeOwned};
pub const DEFAULT_URL: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 6767);
// maybe consider some better name for this
pub trait SettingsLike: Serialize + DeserializeOwned + Default {
fn get_config_dir() -> Result<PathBuf, Box<dyn error::Error>> {
Ok(dirs::config_local_dir()
.ok_or("Couldn't get config dir!")?
.join("checkers"))
}
fn get_file_path() -> Result<PathBuf, Box<dyn error::Error>>;
fn save_to_file(&self) -> Result<(), Box<dyn error::Error>> {
let path: PathBuf = Self::get_file_path()?;
if let Some(parent_dir) = path.parent() {
std::fs::create_dir_all(parent_dir)?;
}
let toml = toml::to_string(&self)?;
std::fs::write(path, toml)?;
Ok(())
}
fn read_from_file() -> Result<Self, Box<dyn error::Error>> {
Ok(toml::from_slice(&std::fs::read(Self::get_file_path()?)?)?) // i love rust
}
/// Try to create new instance of `Self` with values from config file. If there is no content
/// defaults to Self::default. If config file is invalid overwrites it with default values
fn new() -> Self {
match Self::read_from_file() {
Ok(settings) => return settings,
Err(e) => warn!("Could not load settings from file, because: {}", e),
}
let settings = Self::default();
if let Err(e) = settings.save_to_file() {
// this kinda destroys original file if it has
// content that is invalid, i'm not sure if i
// like this, but i'll keep it for now.
//
// ADDING TODO! SO I DONT FORGET AGOUT THIS
warn!("Could not save server settings to the file, because: {}", e);
}
settings
}
}

3
src/settings/mod.rs Normal file
View File

@@ -0,0 +1,3 @@
pub mod client_settings;
pub mod general_settings;
pub mod server_settings;

View File

@@ -0,0 +1,27 @@
use std::{net::SocketAddr, path::PathBuf};
use serde::{Deserialize, Serialize};
use crate::settings::general_settings::{DEFAULT_URL, SettingsLike};
// TODO! More settings!
#[derive(Serialize, Deserialize)]
pub struct ServerSettings {
pub addr: SocketAddr,
pub allow_spectators: bool,
}
impl Default for ServerSettings {
fn default() -> Self {
Self {
addr: DEFAULT_URL,
allow_spectators: false,
}
}
}
impl SettingsLike for ServerSettings {
fn get_file_path() -> Result<PathBuf, Box<dyn std::error::Error>> {
Ok(Self::get_config_dir()?.join("server.toml"))
}
}