aboutsummaryrefslogtreecommitdiff
path: root/src/server/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/mod.rs')
-rw-r--r--src/server/mod.rs126
1 files changed, 126 insertions, 0 deletions
diff --git a/src/server/mod.rs b/src/server/mod.rs
new file mode 100644
index 0000000..d25b799
--- /dev/null
+++ b/src/server/mod.rs
@@ -0,0 +1,126 @@
+//! 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<JoinHandle<()>, 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<AtomicBool>, 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<Cargo>) -> (JoinHandle<()>, Arc<AtomicBool>) {
+ 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)
+}