Update source files

This commit is contained in:
2025-10-13 16:40:21 -05:00
commit 3b2598f24f
178 changed files with 30153 additions and 0 deletions

View 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"] }

View 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;
}
}