mirror of
https://github.com/Xevion/smart-rgb.git
synced 2025-12-12 18:13:06 -06:00
Update source files
This commit is contained in:
14
crates/borders-server/Cargo.toml
Normal file
14
crates/borders-server/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "borders-server"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
borders-core = { path = "../borders-core" }
|
||||
flume = "0.11"
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time"] }
|
||||
tracing = "0.1"
|
||||
tracing-log = "0.2"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
152
crates/borders-server/src/main.rs
Normal file
152
crates/borders-server/src/main.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
use borders_core::networking::{
|
||||
Intent,
|
||||
protocol::NetMessage,
|
||||
server::{ServerRegistry, start_server},
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::time::{Duration, Instant, interval};
|
||||
use tracing::{error, info};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
// Initialize tracing
|
||||
// Debug builds: debug level for our crates, info for dependencies
|
||||
// Release builds: warn level for our crates, error for dependencies
|
||||
#[cfg(debug_assertions)]
|
||||
let default_filter = "borders_core=debug,borders_server=debug,borders_protocol=debug,info";
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
let default_filter = "borders_core=warn,borders_server=warn,borders_protocol=warn,error";
|
||||
|
||||
tracing_subscriber::fmt().with_env_filter(tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| tracing_subscriber::EnvFilter::new(default_filter))).init();
|
||||
|
||||
// Initialize log-to-tracing bridge for dependencies using log crate
|
||||
tracing_log::LogTracer::init().expect("Failed to set logger");
|
||||
|
||||
// Parse command line arguments
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let bind_address = if args.len() > 1 { args[1].clone() } else { "127.0.0.1:4433".to_string() };
|
||||
|
||||
info!(bind_address = %bind_address, "Starting borders relay server");
|
||||
|
||||
// Create channels for communication
|
||||
let (intent_tx, intent_rx) = flume::unbounded::<Intent>();
|
||||
|
||||
// Create server registry
|
||||
let registry = Arc::new(RwLock::new(ServerRegistry::new()));
|
||||
|
||||
// Spawn network listener task
|
||||
let intent_tx_clone = intent_tx.clone();
|
||||
let registry_clone = registry.clone();
|
||||
let bind_addr = bind_address.clone();
|
||||
|
||||
info!("Spawning server listener task...");
|
||||
tokio::spawn(async move {
|
||||
info!("Server listener task started");
|
||||
if let Err(e) = start_server(&bind_addr, intent_tx_clone, registry_clone).await {
|
||||
error!(error = %e, "Server listener failed");
|
||||
}
|
||||
});
|
||||
|
||||
// Main server loop: collect intents and broadcast turns
|
||||
let mut turn_number = 0u64;
|
||||
let mut tick_interval = interval(Duration::from_millis(100)); // 100ms tick rate
|
||||
|
||||
// Spawn phase tracking
|
||||
let mut spawn_config: HashMap<u16, u32> = HashMap::new();
|
||||
let mut spawn_timeout_started: Option<Instant> = None;
|
||||
let mut game_started = false;
|
||||
const SPAWN_TIMEOUT_MS: u64 = 5000;
|
||||
|
||||
info!("Server running - spawn phase active");
|
||||
|
||||
loop {
|
||||
let _loop_span = tracing::debug_span!("server_loop").entered();
|
||||
tick_interval.tick().await;
|
||||
|
||||
// During spawn phase, handle SetSpawn intents
|
||||
if !game_started {
|
||||
let _spawn_span = tracing::trace_span!("spawn_phase_processing").entered();
|
||||
while let Ok(intent) = intent_rx.try_recv() {
|
||||
match intent {
|
||||
Intent::SetSpawn { player_id, tile_index } => {
|
||||
info!("Player {} set spawn at tile {}", player_id, tile_index);
|
||||
spawn_config.insert(player_id, tile_index);
|
||||
|
||||
// Start timeout on first spawn
|
||||
if spawn_timeout_started.is_none() {
|
||||
spawn_timeout_started = Some(Instant::now());
|
||||
info!("Spawn timeout started ({}ms)", SPAWN_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
// Broadcast spawn configuration to all clients
|
||||
let spawn_message = NetMessage::SpawnConfiguration { spawns: spawn_config.clone() };
|
||||
registry.write().await.broadcast(spawn_message);
|
||||
}
|
||||
Intent::Action(_) => {
|
||||
info!("Received Action intent during spawn phase - ignoring");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if spawn timeout has expired
|
||||
if let Some(timeout_start) = spawn_timeout_started {
|
||||
let elapsed = timeout_start.elapsed();
|
||||
if elapsed >= Duration::from_millis(SPAWN_TIMEOUT_MS) {
|
||||
info!("Spawn timeout expired - starting game!");
|
||||
|
||||
// Create Turn(0) with all spawn actions
|
||||
use borders_core::game::action::GameAction;
|
||||
let spawn_intents: Vec<Intent> = spawn_config.iter().map(|(&player_id, &tile_index)| Intent::Action(GameAction::Spawn { player_id, tile_index })).collect();
|
||||
|
||||
let start_turn = NetMessage::Turn { turn: 0, intents: spawn_intents.clone() };
|
||||
|
||||
info!("Broadcasting Turn(0) with {} spawns", spawn_intents.len());
|
||||
registry.write().await.broadcast(start_turn);
|
||||
|
||||
// Mark game as started and clear spawn phase
|
||||
game_started = true;
|
||||
spawn_config.clear();
|
||||
spawn_timeout_started = None;
|
||||
turn_number = 1;
|
||||
|
||||
info!("Spawn phase complete - game started");
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Normal turn generation (after game has started)
|
||||
let mut intents = Vec::new();
|
||||
while let Ok(intent) = intent_rx.try_recv() {
|
||||
match intent {
|
||||
Intent::Action(action) => {
|
||||
intents.push(Intent::Action(action));
|
||||
}
|
||||
Intent::SetSpawn { .. } => {
|
||||
info!("Received SetSpawn intent after game started - ignoring");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create and broadcast turn
|
||||
let turn_message = NetMessage::Turn { turn: turn_number, intents: intents.clone() };
|
||||
|
||||
let client_count = { registry.read().await.client_count() };
|
||||
|
||||
if !intents.is_empty() || turn_number.is_multiple_of(100) {
|
||||
let _broadcast_span = tracing::trace_span!("turn_broadcast", turn_number = turn_number, intent_count = intents.len(), client_count = client_count).entered();
|
||||
|
||||
info!(turn_number = turn_number, intent_count = intents.len(), client_count = client_count, "Broadcasting turn");
|
||||
|
||||
registry.write().await.broadcast(turn_message.clone());
|
||||
} else {
|
||||
registry.write().await.broadcast(turn_message);
|
||||
}
|
||||
|
||||
turn_number += 1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user