2023-10-29 20:48:53 +01:00
|
|
|
use log::{info, trace, warn};
|
2023-10-31 14:32:21 +01:00
|
|
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
|
2023-10-29 20:48:53 +01:00
|
|
|
use std::thread;
|
2023-10-07 20:46:24 +02:00
|
|
|
use std::time::{Duration, Instant, SystemTime};
|
|
|
|
|
|
2023-10-27 21:14:34 +02:00
|
|
|
use renet::{
|
|
|
|
|
transport::{
|
|
|
|
|
NetcodeServerTransport, ServerAuthentication, ServerConfig, NETCODE_USER_DATA_BYTES,
|
|
|
|
|
},
|
2023-10-31 14:32:21 +01:00
|
|
|
ConnectionConfig, RenetServer, ServerEvent,
|
2023-10-27 21:14:34 +02:00
|
|
|
};
|
|
|
|
|
|
2023-10-07 20:46:24 +02:00
|
|
|
// Only clients that can provide the same PROTOCOL_ID that the server is using will be able to connect.
|
|
|
|
|
// This can be used to make sure players use the most recent version of the client for instance.
|
|
|
|
|
pub const PROTOCOL_ID: u64 = 2878;
|
|
|
|
|
|
2023-10-29 20:48:53 +01:00
|
|
|
/// Utility function for extracting a players name from renet user data
|
|
|
|
|
fn name_from_user_data(user_data: &[u8; NETCODE_USER_DATA_BYTES]) -> String {
|
|
|
|
|
let mut buffer = [0u8; 8];
|
|
|
|
|
buffer.copy_from_slice(&user_data[0..8]);
|
|
|
|
|
let mut len = u64::from_le_bytes(buffer) as usize;
|
|
|
|
|
len = len.min(NETCODE_USER_DATA_BYTES - 8);
|
|
|
|
|
let data = user_data[8..len + 8].to_vec();
|
|
|
|
|
String::from_utf8(data).unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-01 18:04:03 +01:00
|
|
|
fn main() {
|
2023-10-07 20:46:24 +02:00
|
|
|
env_logger::init();
|
|
|
|
|
|
2023-10-27 21:14:34 +02:00
|
|
|
let mut server = RenetServer::new(ConnectionConfig::default());
|
|
|
|
|
|
|
|
|
|
// Setup transport layer
|
|
|
|
|
const SERVER_ADDR: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 5000);
|
|
|
|
|
let socket: UdpSocket = UdpSocket::bind(SERVER_ADDR).unwrap();
|
|
|
|
|
let server_config = ServerConfig {
|
|
|
|
|
max_clients: 2,
|
|
|
|
|
protocol_id: PROTOCOL_ID,
|
|
|
|
|
public_addr: SERVER_ADDR,
|
|
|
|
|
authentication: ServerAuthentication::Unsecure,
|
|
|
|
|
};
|
2023-10-31 14:32:21 +01:00
|
|
|
let current_time = SystemTime::now()
|
|
|
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
|
|
|
.unwrap();
|
2023-10-27 21:14:34 +02:00
|
|
|
let mut transport = NetcodeServerTransport::new(current_time, server_config, socket).unwrap();
|
|
|
|
|
|
2025-08-17 15:59:53 +02:00
|
|
|
trace!("❂ TricTrac server listening on {SERVER_ADDR}");
|
2023-10-07 20:46:24 +02:00
|
|
|
|
2023-10-29 20:48:53 +01:00
|
|
|
let mut game_state = store::GameState::default();
|
2023-10-07 20:46:24 +02:00
|
|
|
let mut last_updated = Instant::now();
|
|
|
|
|
loop {
|
|
|
|
|
// Update server time
|
|
|
|
|
let now = Instant::now();
|
2023-10-27 21:14:34 +02:00
|
|
|
let delta_time = now - last_updated;
|
|
|
|
|
server.update(delta_time);
|
|
|
|
|
transport.update(delta_time, &mut server).unwrap();
|
2023-10-07 20:46:24 +02:00
|
|
|
last_updated = now;
|
|
|
|
|
|
|
|
|
|
// Receive connection events from clients
|
|
|
|
|
while let Some(event) = server.get_event() {
|
|
|
|
|
match event {
|
2023-10-27 21:14:34 +02:00
|
|
|
ServerEvent::ClientConnected { client_id } => {
|
2023-10-28 15:12:04 +02:00
|
|
|
let user_data = transport.user_data(client_id).unwrap();
|
|
|
|
|
|
|
|
|
|
// Tell the recently joined player about the other player
|
|
|
|
|
for (player_id, player) in game_state.players.iter() {
|
|
|
|
|
let event = store::GameEvent::PlayerJoined {
|
|
|
|
|
player_id: *player_id,
|
|
|
|
|
name: player.name.clone(),
|
|
|
|
|
};
|
|
|
|
|
server.send_message(client_id, 0, bincode::serialize(&event).unwrap());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add the new player to the game
|
|
|
|
|
let event = store::GameEvent::PlayerJoined {
|
|
|
|
|
player_id: client_id,
|
|
|
|
|
name: name_from_user_data(&user_data),
|
|
|
|
|
};
|
|
|
|
|
game_state.consume(&event);
|
|
|
|
|
|
|
|
|
|
// Tell all players that a new player has joined
|
|
|
|
|
server.broadcast_message(0, bincode::serialize(&event).unwrap());
|
|
|
|
|
|
2025-08-17 15:59:53 +02:00
|
|
|
info!("🎉 Client {client_id} connected.");
|
2023-10-28 15:12:04 +02:00
|
|
|
// In TicTacTussle the game can begin once two players has joined
|
|
|
|
|
if game_state.players.len() == 2 {
|
2023-10-31 14:32:21 +01:00
|
|
|
let event = store::GameEvent::BeginGame {
|
|
|
|
|
goes_first: client_id,
|
|
|
|
|
};
|
2023-10-28 15:12:04 +02:00
|
|
|
game_state.consume(&event);
|
|
|
|
|
server.broadcast_message(0, bincode::serialize(&event).unwrap());
|
|
|
|
|
trace!("The game gas begun");
|
|
|
|
|
}
|
2023-10-07 20:46:24 +02:00
|
|
|
}
|
2024-11-07 12:52:57 +01:00
|
|
|
ServerEvent::ClientDisconnected {
|
|
|
|
|
client_id,
|
|
|
|
|
reason: _,
|
|
|
|
|
} => {
|
2023-10-28 15:12:04 +02:00
|
|
|
// First consume a disconnect event
|
2023-10-31 14:32:21 +01:00
|
|
|
let event = store::GameEvent::PlayerDisconnected {
|
|
|
|
|
player_id: client_id,
|
|
|
|
|
};
|
2023-10-28 15:12:04 +02:00
|
|
|
game_state.consume(&event);
|
|
|
|
|
server.broadcast_message(0, bincode::serialize(&event).unwrap());
|
2025-08-17 15:59:53 +02:00
|
|
|
info!("Client {client_id} disconnected");
|
2023-10-28 15:12:04 +02:00
|
|
|
|
|
|
|
|
// Then end the game, since tic tac toe can't go on with a single player
|
|
|
|
|
let event = store::GameEvent::EndGame {
|
2023-10-31 14:32:21 +01:00
|
|
|
reason: store::EndGameReason::PlayerLeft {
|
|
|
|
|
player_id: client_id,
|
|
|
|
|
},
|
2023-10-28 15:12:04 +02:00
|
|
|
};
|
|
|
|
|
game_state.consume(&event);
|
|
|
|
|
server.broadcast_message(0, bincode::serialize(&event).unwrap());
|
|
|
|
|
|
|
|
|
|
// NOTE: Since we don't authenticate users we can't do any reconnection attempts.
|
|
|
|
|
// We simply have no way to know if the next user is the same as the one that disconnected.
|
2023-10-07 20:46:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-28 15:12:04 +02:00
|
|
|
|
|
|
|
|
// Receive GameEvents from clients. Broadcast valid events.
|
|
|
|
|
for client_id in server.clients_id().into_iter() {
|
|
|
|
|
while let Some(message) = server.receive_message(client_id, 0) {
|
|
|
|
|
if let Ok(event) = bincode::deserialize::<store::GameEvent>(&message) {
|
|
|
|
|
if game_state.validate(&event) {
|
|
|
|
|
game_state.consume(&event);
|
2025-08-17 15:59:53 +02:00
|
|
|
trace!("Player {client_id} sent:\n\t{event:#?}");
|
2023-10-28 15:12:04 +02:00
|
|
|
server.broadcast_message(0, bincode::serialize(&event).unwrap());
|
|
|
|
|
|
|
|
|
|
// Determine if a player has won the game
|
|
|
|
|
if let Some(winner) = game_state.determine_winner() {
|
|
|
|
|
let event = store::GameEvent::EndGame {
|
|
|
|
|
reason: store::EndGameReason::PlayerWon { winner },
|
|
|
|
|
};
|
|
|
|
|
server.broadcast_message(0, bincode::serialize(&event).unwrap());
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2025-08-17 15:59:53 +02:00
|
|
|
warn!("Player {client_id} sent invalid event:\n\t{event:#?}");
|
2023-10-28 15:12:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-29 20:48:53 +01:00
|
|
|
transport.send_packets(&mut server);
|
2023-10-31 14:32:21 +01:00
|
|
|
thread::sleep(Duration::from_millis(50));
|
|
|
|
|
}
|
2022-12-01 18:04:03 +01:00
|
|
|
}
|