//! Contains the necessary ingredients to start a graf karto world server. use crate::net::server::ConnectionManager; use crate::net::Cargo; use crate::world::{Component, World}; use std::io; use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::thread::{self, JoinHandle}; /// The default port the dedicated graf karto server runs on. pub const DEFAULT_PORT: u16 = 44309; fn unspecified(port: u16, ipv4: bool) -> SocketAddr { if ipv4 { SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), port) } else { SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), port) } } /// Starts a thread for the server and tries to bind to the specified port. If the /// port cannot be bound to it fails returning an error, otherwise the join handle /// for the server thread is returned. pub fn start_with_port(port: u16, ipv4: bool) -> Result, io::Error> { let addr = unspecified(port, ipv4); let conn_man = ConnectionManager::start(addr)?; Ok(start(conn_man).0) } /// Starts a thread on any free system port. Returns an error in case that's not /// possible. pub fn start_any_port(ipv4: bool) -> Result<(JoinHandle<()>, Arc, u16), io::Error> { let addr = unspecified(0, ipv4); let conn_man = ConnectionManager::start(addr)?; let port = conn_man.port(); let (handle, running) = start(conn_man); Ok((handle, running, port)) } fn start(conn_man: ConnectionManager) -> (JoinHandle<()>, Arc) { info!("Server started on port {}", conn_man.port()); let running = Arc::new(AtomicBool::new(true)); let running_cl = running.clone(); let mut world = World::new(); let handle = thread::spawn(move || { while running_cl.load(Ordering::Relaxed) { if let Some((_user, cargo)) = conn_man.next_packet() { match cargo { Cargo::AddRoom(room) => { let room_id = world.push_room(room.clone()); conn_man.broadcast(Cargo::SetRoom((room_id, room))); } Cargo::AddIcon(icon) => { let icon_id = world.push_icon(icon.clone()); conn_man.broadcast(Cargo::SetIcon((icon_id, icon))); } Cargo::AddWall(wall) => { let wall_id = world.push_wall(wall.clone()); conn_man.broadcast(Cargo::SetWall((wall_id, wall))); } Cargo::SetRoom((id, new)) => { if let Some(old) = world.get_room_mut(id) { *old = new; } else { error!( "Unable to change room. Room with id `{}` does not exist.", id ); } } Cargo::SetIcon((id, new)) => { if let Some(old) = world.get_icon_mut(id) { *old = new; } else { error!( "Unable to change icon. Icon with id `{}` does not exist.", id ); } } Cargo::SetWall((id, new)) => { if let Some(old) = world.get_wall_mut(id) { *old = new; } else { error!( "Unable to change wall. Wall with id `{}` does not exist.", id ); } } Cargo::ClearAll => { world.clear(); conn_man.broadcast(Cargo::ClearAll); } Cargo::Remove(id) => { if world.remove(id) { conn_man.broadcast(Cargo::Remove(id)); } else { error!("Unable to remove item. No item with id `{}` found.", id); } } Cargo::ApplyMatrix((id, matrix)) => { if let Some(room) = world.get_room_mut(id) { room.as_non_rigid_mut().unwrap().apply_matrix(&matrix); conn_man.broadcast(Cargo::SetRoom((id, room.clone()))); } else if let Some(wall) = world.get_wall_mut(id) { wall.as_non_rigid_mut().unwrap().apply_matrix(&matrix); conn_man.broadcast(Cargo::SetWall((id, wall.clone()))); } } Cargo::AddMapData(_) | Cargo::UpdateMapData(_) => { error!("This packet is not allowed in the client -> server direction"); } } } } }); (handle, running) }