From 131a297815e0b99dd9a87dc80da6f84be855ddef Mon Sep 17 00:00:00 2001 From: flafmg Date: Tue, 30 Jul 2024 20:56:16 -0300 Subject: [PATCH] now it works --- Cargo.toml | 4 +- server-config.yml | 2 +- src/main.rs | 3 +- src/server/game/dmf_map.rs | 22 +- src/server/game/player.rs | 91 +++- src/server/map_builder.rs | 179 +++++++ src/server/maps.rs | 54 +++ src/server/mod.rs | 2 + src/server/network/heartbeat.rs | 11 +- src/server/network/packet.rs | 2 +- src/server/network/packet_resolver.rs | 437 ++++++++++++++---- .../network/packet_stream/packet_reader.rs | 1 + .../network/packet_stream/packet_writer.rs | 3 + src/server/network/packets/clientbound.rs | 154 +----- src/server/network/packets/serverbound.rs | 44 +- src/server/server.rs | 91 ++-- 16 files changed, 773 insertions(+), 327 deletions(-) create mode 100644 src/server/map_builder.rs create mode 100644 src/server/maps.rs diff --git a/Cargo.toml b/Cargo.toml index d924833..4dedab5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,9 @@ edition = "2021" tokio = { version = "1.39", features = ["full"] } reqwest = { version = "0.12", features = ["json"] } serde = { version = "1.0", features = ["derive"] } -serde_yaml = { version = "0.9" } +serde_yaml = "0.9" async-trait = "0.1" flate2 = "1.0.30" rand = "0.8" +noise = "0.9" +dashmap = "6.0" diff --git a/server-config.yml b/server-config.yml index bdaebda..e0a6a4f 100644 --- a/server-config.yml +++ b/server-config.yml @@ -1,7 +1,7 @@ addr: 0.0.0.0 port: 25565 heartbeat_url: https://www.classicube.net/server/heartbeat -name: Dandelion dev server +name: dandelion dev [github.com/flafmg/dandelion-classic] motd: wtf are you doing here, get out public: true do_user_auth: true diff --git a/src/main.rs b/src/main.rs index 70116ba..f3675f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,8 @@ mod server; #[tokio::main] async fn main() -> Result<(), Box> { - let server = Arc::new(Server::new().await?); // i fucking hate arcs + let server = Arc::new(Server::new().await?); server.start().await?; + Ok(()) } diff --git a/src/server/game/dmf_map.rs b/src/server/game/dmf_map.rs index 0f9f7f0..5611bc9 100644 --- a/src/server/game/dmf_map.rs +++ b/src/server/game/dmf_map.rs @@ -6,6 +6,7 @@ use std::{ const HEADER_INDENTIFIER: &str = "DANDELION MAP FORMAT"; const HEADER_VERSION: u8 = 0x00; +#[derive(Debug, Clone)] pub struct DmfMap { pub x_spawn: i16, pub y_spawn: i16, @@ -40,7 +41,20 @@ impl DmfMap { blocks: vec![0x00; total_blocks], }; } - + pub fn get_block(&self, x: i16, y: i16, z: i16) -> u8 { + if x < self.x_size && y < self.y_size && z < self.z_size { + let index = (y as usize * self.z_size as usize * self.x_size as usize) + + (z as usize * self.x_size as usize) + + x as usize; + self.blocks[index] + } else { + println!( + "Error: attempted to get block outside world ({}, {}, {})", + x, y, z + ); + 0x00 + } + } pub fn set_block(&mut self, x: i16, y: i16, z: i16, block: u8) { if x < self.x_size && y < self.y_size && z < self.z_size { let index = (y as usize * self.z_size as usize * self.x_size as usize) @@ -54,7 +68,11 @@ impl DmfMap { ); } } - + pub fn set_spawn_point(&mut self, x: i16, y: i16, z: i16) { + self.x_spawn = x; + self.y_spawn = y; + self.z_spawn = z; + } pub fn save_file(&self, path: &str) -> io::Result<()> { let mut file = File::create(path)?; print!("saving file to {}", path); diff --git a/src/server/game/player.rs b/src/server/game/player.rs index eb6f233..aa60e5e 100644 --- a/src/server/game/player.rs +++ b/src/server/game/player.rs @@ -1,33 +1,42 @@ +use crate::server::game::dmf_map::DmfMap; +use crate::server::network::packets::clientbound::{ + SendMessagePacket, SetPositionAndOrientationPacket, +}; +use crate::server::network::{ + packet::PacketTrait, + packet_stream::packet_writer::PacketWriter, + packets::clientbound::{LevelDataChunkPacket, LevelFinalizePacket, LevelInitializePacket}, +}; +use flate2::write::GzEncoder; +use flate2::Compression; +use std::io::prelude::*; use std::sync::Arc; - -use tokio::{io::WriteHalf, net::TcpStream, sync::Mutex}; - -use crate::server::network::packet::PacketTrait; +use tokio::{io::WriteHalf, net::TcpStream, sync::RwLock}; #[derive(Clone, Debug)] pub struct Player { id: i8, name: String, - current_world: String, + pub current_map: String, pub x: i16, pub y: i16, pub z: i16, pub yaw: u8, pub pitch: u8, - pub socket: Arc>>, + pub socket: Arc>>, } impl Player { pub fn new( id: i8, name: String, - current_world: String, - socket: Arc>>, + current_map: String, + socket: Arc>>, ) -> Self { Self { id, name, - current_world, + current_map, x: 0, y: 0, z: 0, @@ -46,7 +55,7 @@ impl Player { } pub fn get_current_world(&self) -> &str { - &self.current_world + &self.current_map } pub fn set_pos(&mut self, x: i16, y: i16, z: i16, pitch: u8, yaw: u8) { @@ -56,4 +65,66 @@ impl Player { self.pitch = pitch; self.yaw = yaw; } + pub async fn teleport(&mut self, x: i16, y: i16, z: i16, pitch: u8, yaw: u8) { + self.set_pos(x, y, z, pitch, yaw); + let mut set_position = SetPositionAndOrientationPacket::new(-1, x, y, z, pitch, yaw); + set_position.write(&mut PacketWriter::new()); + let mut socket = self.socket.write().await; + set_position.resolve(&mut socket).await; + } + pub async fn send_message(&self, msg: &str) { + let mut message_packet = SendMessagePacket::new(-1, msg.to_string()); + message_packet.write(&mut PacketWriter::new()); + let mut socket = self.socket.write().await; + message_packet.resolve(&mut socket).await; + } + pub async fn send_to_level(&self, map: &DmfMap) { + let block_data = &map.blocks; + + let mut socket_guard = self.socket.write().await; + + let mut resolve_packet_response = LevelInitializePacket::new(); + let mut packet_writer = PacketWriter::new(); + resolve_packet_response.write(&mut packet_writer); + resolve_packet_response + .resolve(&mut *socket_guard) + .await + .unwrap(); + + let total_length = block_data.len(); + let mut prefixed_data = Vec::with_capacity(4 + total_length); + prefixed_data.extend_from_slice(&(total_length as u32).to_be_bytes()); + prefixed_data.extend_from_slice(&block_data); + + let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); + encoder.write_all(&prefixed_data).unwrap(); + let compressed_data = encoder.finish().unwrap(); + + let chunk_size = 1024; + for (i, chunk) in compressed_data.chunks(chunk_size).enumerate() { + let chunk_length = chunk.len() as i16; + let percent_complete = ((i * chunk_size + chunk_length as usize) as f32 + / compressed_data.len() as f32 + * 100.0) as u8; + + let mut chunk_data = chunk.to_vec(); + if chunk_data.len() < chunk_size { + chunk_data.resize(chunk_size, 0x00); + } + + let mut packet = LevelDataChunkPacket::new(chunk_length, chunk_data, percent_complete); + let mut packet_writer = PacketWriter::new(); + packet.write(&mut packet_writer); + + if let Err(e) = packet.resolve(&mut *socket_guard).await { + eprintln!("Error sending chunk {}: {}", i, e); + break; + } + } + + let mut level_finalize = LevelFinalizePacket::new(map.x_size, map.y_size, map.z_size); + let mut packet_writer = PacketWriter::new(); + level_finalize.write(&mut packet_writer); + level_finalize.resolve(&mut *socket_guard).await.unwrap(); + } } diff --git a/src/server/map_builder.rs b/src/server/map_builder.rs new file mode 100644 index 0000000..d0e8c34 --- /dev/null +++ b/src/server/map_builder.rs @@ -0,0 +1,179 @@ +// shitty map generation thingy slow as hell +// flat = flat duh +// noise = plain noise +// classic = water in 31 and sand on 33 and bellow + +use noise::{NoiseFn, Perlin}; + +use super::game::dmf_map::DmfMap; + +pub struct MapBuilder; + +impl MapBuilder { + pub async fn create_map(preset: &str, params: Option) -> DmfMap { + match preset { + "flat" => Self::create_flat_map( + params.clone().unwrap().flat_ground_level, + params.clone().unwrap().dimensions, + ), + "noise" => Self::create_noise_map( + params.clone().unwrap().noise_layers, + params.clone().unwrap().dimensions, + ), + "island" => Self::create_classic_map( + params.clone().unwrap().noise_layers, + params.unwrap().dimensions, + ), + _ => panic!("Unknown preset"), + } + } + + fn create_flat_map(ground_level: u32, dimensions: Dimensions) -> DmfMap { + let mut map = DmfMap::new( + 0, + ground_level as i16 + 4, + 0, + dimensions.x, + dimensions.y, + dimensions.z, + ); + + for x in 0..dimensions.x { + for z in 0..dimensions.z { + for y in 0..ground_level { + map.set_block(x as i16, y as i16, z as i16, 0x01); // stone + } + for y in ground_level..(ground_level + 3) { + map.set_block(x as i16, y as i16, z as i16, 0x03); // dirt + } + map.set_block(x as i16, ground_level as i16 + 3, z as i16, 0x02); + // grass + } + } + + map + } + + fn create_noise_map(layers: Vec, dimensions: Dimensions) -> DmfMap { + let mut map = DmfMap::new(0, 64, 0, dimensions.x, dimensions.y, dimensions.z); + + for x in 0..dimensions.x { + for z in 0..dimensions.z { + let mut height = 0.0; + for layer in &layers { + let perlin = Perlin::new(50); + height += + perlin.get([x as f64 / layer.scale, z as f64 / layer.scale]) * layer.weight; + } + let height = (height + dimensions.y as f64 / 2.0).min(dimensions.y as f64) as u32; + + for y in 0..height { + map.set_block(x as i16, y as i16, z as i16, 0x01); // stone + } + for y in height..(height + 3) { + map.set_block(x as i16, y as i16, z as i16, 0x03); // dirt + } + map.set_block(x as i16, height as i16 + 3, z as i16, 0x02); // grass + } + } + + map + } + + fn create_classic_map(layers: Vec, dimensions: Dimensions) -> DmfMap { + const SHORE_FACTOR: f64 = 0.25; + const SHORE_AFFECTED_DISTANCE: f64 = 25.0; // Distance in blocks that affects the shore + + let water_level = 31; + let mut map = DmfMap::new( + 0, + water_level + 1, + 0, + dimensions.x, + dimensions.y, + dimensions.z, + ); + + for x in 0..dimensions.x { + for z in 0..dimensions.z { + let mut height = 0.0; + for layer in &layers { + let perlin = Perlin::new(70); + height += + perlin.get([x as f64 / layer.scale, z as f64 / layer.scale]) * layer.weight; + } + + let distance_from_edge_x = (x as f64).min((dimensions.x - x) as f64); + let distance_from_edge_z = (z as f64).min((dimensions.z - z) as f64); + let distance_from_edge = distance_from_edge_x.min(distance_from_edge_z); + let shore_adjustment = if distance_from_edge <= SHORE_AFFECTED_DISTANCE { + (1.0 - distance_from_edge / SHORE_AFFECTED_DISTANCE) * SHORE_FACTOR + } else { + 0.0 + }; + + let height = ((height + dimensions.y as f64 / 2.0) * (1.0 - shore_adjustment)) + .min(dimensions.y as f64) as u32; + + for y in 0..height { + map.set_block(x as i16, y as i16, z as i16, 0x01); + } + } + } + + for x in 0..dimensions.x { + for z in 0..dimensions.z { + for y in 0..=water_level { + if map.get_block(x as i16, y as i16, z as i16) == 0x00 { + map.set_block(x as i16, y as i16, z as i16, 0x08); + } + } + } + } + + for x in 0..dimensions.x { + for z in 0..dimensions.z { + for y in (0..dimensions.y).rev() { + let block = map.get_block(x as i16, y as i16, z as i16); + + if block == 0x01 { + if map.get_block(x as i16, y as i16 + 1, z as i16) == 0x00 + || map.get_block(x as i16, y as i16 + 1, z as i16) == 0x08 + { + if y as u32 <= water_level as u32 + 2 { + map.set_block(x as i16, y as i16, z as i16, 0x0c); + } else { + map.set_block(x as i16, y as i16, z as i16, 0x02); + for i in 1..=3 { + map.set_block(x as i16, y as i16 - i, z as i16, 0x03); + } + } + } + } + } + } + } + + map + } +} + +#[derive(Clone)] +pub struct PresetParams { + pub flat_ground_level: u32, + pub noise_layers: Vec, + pub dimensions: Dimensions, +} + +#[derive(Clone)] +pub struct NoiseLayer { + pub scale: f64, + pub weight: f64, +} + +#[derive(Clone)] +pub struct Dimensions { + pub x: i16, + pub y: i16, + pub z: i16, +} diff --git a/src/server/maps.rs b/src/server/maps.rs new file mode 100644 index 0000000..84a3863 --- /dev/null +++ b/src/server/maps.rs @@ -0,0 +1,54 @@ +use crate::server::game::dmf_map::DmfMap; +use dashmap::DashMap; +use std::fs; +use std::path::Path; +use std::sync::Arc; +use tokio::io; +use tokio::time::{sleep, Duration}; + +pub async fn load_all_maps_in(path: &str, maps: Arc>) -> io::Result<()> { + let entries = fs::read_dir(path)?; + + for entry in entries { + let entry = entry?; + let path = entry.path(); + if path.is_file() { + let file_name = path.file_stem().unwrap().to_str().unwrap().to_string(); + load_map(&path, maps.clone()).await?; + println!("map: {} loaded!", file_name); + } + } + + Ok(()) +} + +pub async fn load_map(path: &Path, maps: Arc>) -> io::Result<()> { + let file_name = path.file_stem().unwrap().to_str().unwrap().to_string(); + let map = DmfMap::load_file(path.to_str().unwrap()).unwrap(); + maps.insert(file_name, map); + + Ok(()) +} + +pub async fn unload_map(name: &str, maps: Arc>) { + maps.remove(name); + println!("Unloaded map: {}", name); +} + +pub async fn save_all(maps: Arc>) { + for r in maps.iter() { + let (name, map) = r.pair(); + if let Err(e) = map.save_file(&format!("maps/{}.dmf", name)) { + eprintln!("Failed to save map {}: {}", name, e); + } else { + println!("Saved map: {}", name); + } + } +} + +pub async fn save_all_loop(maps: Arc>) { + loop { + save_all(Arc::clone(&maps)).await; + sleep(Duration::from_secs(90)).await; + } +} diff --git a/src/server/mod.rs b/src/server/mod.rs index cff9599..b130ef4 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,4 +1,6 @@ pub mod config; pub mod game; +pub mod map_builder; +pub mod maps; pub mod network; pub mod server; diff --git a/src/server/network/heartbeat.rs b/src/server/network/heartbeat.rs index d334c87..367078c 100644 --- a/src/server/network/heartbeat.rs +++ b/src/server/network/heartbeat.rs @@ -9,8 +9,6 @@ const HEARTBEAT_INTERVAL: u64 = 45; const SERVER_SOFTWARE: &str = "&eDANDELION &70.0.1"; pub async fn start_heartbeat_loop(server: Arc) { - println!("starting hearbeat loop"); - loop { let server_clone = server.clone(); if let Err(er) = send_heartbeats(server_clone).await { @@ -32,10 +30,7 @@ async fn send_heartbeats(server: Arc) -> Result<(), reqwest::Error> { ("public", config.public.to_string()), ("version", "7".to_string()), ("salt", server.salt.clone()), - ( - "users", - server.connected_players.lock().await.len().to_string(), - ), + ("users", server.connected_players.len().to_string()), ("software", SERVER_SOFTWARE.to_string()), ("web", "false".to_string()), ]) @@ -45,8 +40,8 @@ async fn send_heartbeats(server: Arc) -> Result<(), reqwest::Error> { if response.status().is_success() { let response_body = response.text().await?; } else { - println!("Hearbeat ERROR: {}", response.status()); + println!("Heartbeat ERROR: {}", response.status()); } - return Ok(()); + Ok(()) } diff --git a/src/server/network/packet.rs b/src/server/network/packet.rs index 759bbff..46ad08d 100644 --- a/src/server/network/packet.rs +++ b/src/server/network/packet.rs @@ -6,7 +6,7 @@ use tokio::{io::WriteHalf, net::TcpStream, sync::Mutex}; use super::packet_stream::{packet_reader::PacketReader, packet_writer::PacketWriter}; #[async_trait] -pub trait PacketTrait { +pub trait PacketTrait: Send + Sync { fn packet_id(&self) -> u8; fn write(&mut self, writer: &mut PacketWriter); fn read(&mut self, reader: &mut PacketReader); diff --git a/src/server/network/packet_resolver.rs b/src/server/network/packet_resolver.rs index 10b1b13..81295f1 100644 --- a/src/server/network/packet_resolver.rs +++ b/src/server/network/packet_resolver.rs @@ -2,36 +2,71 @@ use super::packet::PacketTrait; use super::packet_stream::packet_reader::PacketReader; use super::packet_stream::packet_writer::PacketWriter; use super::packets::clientbound::{ - LevelDataChunkPacket, LevelFinalizePacket, LevelInitializePacket, + DespawnPlayerPacket, LevelDataChunkPacket, LevelFinalizePacket, LevelInitializePacket, + PingPacket, UpdateSetBlockPacket, +}; +use super::packets::serverbound::{ + MessagePacket, PlayerIndentificationPacket, PositionAndOrientationUpdatePacket, SetBlockPacket, }; -use super::packets::serverbound::PlayerIndentificationPacket; use crate::server::game::dmf_map::DmfMap; use crate::server::game::player::Player; -use crate::server::network::packets::clientbound::PingPacket; +use crate::server::network::packets::clientbound::{ + SendMessagePacket, SetPositionAndOrientationPacket, SpawnPlayerPacket, +}; use crate::server::server::Server; use flate2::write::GzEncoder; use flate2::Compression; +use std::collections::VecDeque; +use std::fmt::format; use std::io::prelude::*; use std::sync::Arc; use std::time::Duration; -use tokio::io::WriteHalf; +use tokio::io::{self, AsyncWriteExt, WriteHalf}; use tokio::net::TcpStream; -use tokio::sync::Mutex; +use tokio::sync::RwLock; use tokio::time::sleep; -const PING_TIME: u64 = 5; +const PING_TIME_MILLIS: u64 = 100; +const PACKET_FLUSH_MILLIS: u64 = 20; + +pub struct PacketQueue { + queue: Arc, Box)>>>, +} + +impl PacketQueue { + pub fn new() -> Self { + Self { + queue: Arc::new(RwLock::new(VecDeque::new())), + } + } + + pub async fn enqueue(&self, owner_id: Option, packet: Box) { + let mut queue = self.queue.write().await; + queue.push_back((owner_id, packet)); + } + + pub async fn dequeue(&self) -> Option<(Option, Box)> { + let mut queue = self.queue.write().await; + queue.pop_front() + } +} pub struct PacketResolver { pub server: Arc, + pub packet_queue: PacketQueue, } impl PacketResolver { pub fn new(server: Arc) -> Self { - return Self { server }; + Self { + server, + packet_queue: PacketQueue::new(), + } } - pub async fn handle_packet(&self, data: &[u8], socket: Arc>>) { + + pub async fn handle_packet(&self, data: &[u8], socket: Arc>>) { if data.is_empty() { - println!("ERROR: empty data received"); + eprintln!("Empty data received"); return; } @@ -39,114 +74,338 @@ impl PacketResolver { let mut reader = PacketReader::new(data); match packet_id { - 0x00 => { - let mut packet = PlayerIndentificationPacket::new(); - packet.read(&mut reader); - - let player_id = self.get_last_id().await; - let player = Player::new( - player_id, - packet.username.clone(), - "default".to_string(), - Arc::clone(&socket), - ); - self.server - .connected_players - .lock() + 0x00 => self.player_connect(&mut reader, socket).await, + 0x05 => self.handle_set_block(&mut reader, socket).await, + 0x08 => { + self.handle_position_and_orientation(&mut reader, socket) .await - .insert(player_id, player.clone()); + } + 0x0d => self.handle_message(&mut reader, socket).await, + _ => println!("Unknown packet ID: {}", packet_id), + } + } + + async fn player_connect( + &self, + reader: &mut PacketReader<'_>, + socket: Arc>>, + ) { + let packet = self.read_player_identification_packet(reader); + let player_id = self.get_last_id().await; + let player = self + .create_player(player_id, packet.username.clone(), socket.clone()) + .await; + self.add_player_to_server(player_id, player.clone()).await; + self.send_server_identification(packet, player.socket.clone()) + .await; + self.load_default_map_and_send_to_player(player.clone()) + .await; + + let join_message = format!("welcome {}!", player.get_name()); + for player in self.server.connected_players.iter() { + player.send_message(&join_message).await; + } + + player.send_message("&fwelcome to this silly server!").await; + player + .send_message("&edandelion &fis a &3server software&f in rust!") + .await; + player.send_message("&fcheck the source code bellow!").await; + player + .send_message("&bhttps://github.com/flafmg/dandelion-classic") + .await; + player + .send_message("&cthis is indevelopment so it may be buggy") + .await; + } + + fn read_player_identification_packet( + &self, + reader: &mut PacketReader<'_>, + ) -> PlayerIndentificationPacket { + let mut packet = PlayerIndentificationPacket::new( + self.server.config.name.clone(), + self.server.config.motd.clone(), + ); + packet.read(reader); + packet + } + + async fn create_player( + &self, + player_id: i8, + username: String, + socket: Arc>>, + ) -> Player { + Player::new( + player_id, + username, + "default".to_string(), + Arc::clone(&socket), + ) + } + + async fn add_player_to_server(&self, player_id: i8, player: Player) { + self.server.connected_players.insert(player_id, player); + } + + async fn send_server_identification( + &self, + packet: PlayerIndentificationPacket, + socket: Arc>>, + ) { + let resolve_result = { + let mut socket = socket.write().await; + packet.resolve(&mut socket).await + }; + + if let Err(e) = resolve_result { + eprintln!("Error resolving server identification: {}", e); + } + } + + async fn load_default_map_and_send_to_player(&self, mut player: Player) { + let default_map_name = self.server.config.default_map.clone(); + let map = self + .server + .loaded_maps + .get(&default_map_name) + .map(|e| e.clone()); + + if let Some(mut map) = map { + player.send_to_level(&map).await; + map.set_spawn_point(4718, 1171, 4166); + self.spawn_player(&mut player, &map).await; + } else { + eprintln!("Error: Default map '{}' not found.", default_map_name); + } + } + + async fn spawn_player(&self, player: &mut Player, map: &DmfMap) { + player + .teleport(map.x_spawn, map.y_spawn, map.z_spawn, 0, 0) + .await; + + let spawn_player = SpawnPlayerPacket::new( + player.get_id(), + player.get_name().to_string(), + player.x, + player.y, + player.z, + 0, + 0, + ); + self.send_packet_to_all(Some(player), spawn_player).await; + self.spawn_online_players(player).await; + } + + async fn spawn_online_players(&self, new_player: &Player) { + let mut socket = new_player.socket.write().await; + for player in self.server.connected_players.iter() { + if player.get_id() != new_player.get_id() { + let mut spawn_player = SpawnPlayerPacket::new( + player.get_id(), + player.get_name().to_string(), + player.x, + player.y, + player.z, + player.yaw, + player.pitch, + ); + let mut packet_writer = PacketWriter::new(); + spawn_player.write(&mut packet_writer); + spawn_player.resolve(&mut socket).await; + } + } + } - let mut socket_guard = socket.lock().await; - packet.resolve(&mut *socket_guard).await.unwrap(); - println!("Player {} connected!", player.get_name()); + async fn handle_set_block( + &self, + reader: &mut PacketReader<'_>, + socket: Arc>>, + ) { + let player = self + .get_player_by_socket(Arc::clone(&socket)) + .await + .unwrap(); - self.send_level_data(&mut *socket_guard).await; + let mut set_block_packet = SetBlockPacket::new(); + set_block_packet.read(reader); + + let map_name = "default"; + let mut maps = self.server.loaded_maps.clone(); + let mut map = maps.get_mut(map_name).unwrap(); + let block = if set_block_packet.mode == 0x00 { + 0x00 + } else { + set_block_packet.block_type + }; + + map.set_block( + set_block_packet.x, + set_block_packet.y, + set_block_packet.z, + block, + ); + + let update_set_block = UpdateSetBlockPacket::new( + set_block_packet.x, + set_block_packet.y, + set_block_packet.z, + block, + ); + self.packet_queue + .enqueue(Some(player.get_id()), Box::new(update_set_block)) + .await; + } + + async fn handle_position_and_orientation( + &self, + reader: &mut PacketReader<'_>, + socket: Arc>>, + ) { + let mut player = self + .get_player_by_socket(Arc::clone(&socket)) + .await + .unwrap(); + + let mut position_packet = PositionAndOrientationUpdatePacket::new(); + position_packet.read(reader); + + player.set_pos( + position_packet.x, + position_packet.y, + position_packet.z, + position_packet.pitch, + position_packet.yaw, + ); + + let mut set_position_packet = SetPositionAndOrientationPacket::new( + player.get_id(), + player.x, + player.y, + player.z, + player.yaw, + player.pitch, + ); + self.send_packet_to_all(Some(&player), set_position_packet) + .await; + } + + async fn handle_message( + &self, + reader: &mut PacketReader<'_>, + socket: Arc>>, + ) { + let mut player = self + .get_player_by_socket(Arc::clone(&socket)) + .await + .unwrap(); + + let mut message_packet = MessagePacket::new(); + message_packet.read(reader); + + let message = format!("{}: {}", player.get_name(), message_packet.message); + + let mut send_message_packet = SendMessagePacket::new(player.get_id(), message); + + self.packet_queue + .enqueue(None, Box::new(send_message_packet)) + .await; + } + + async fn get_player_by_socket( + &self, + socket: Arc>>, + ) -> Option { + for player in self.server.connected_players.iter() { + if Arc::ptr_eq(&player.socket, &socket) { + return Some(player.clone()); + } + } + None + } + + pub async fn send_packet_to_all(&self, owner: Option<&Player>, mut packet: impl PacketTrait) { + let mut packet_writer = PacketWriter::new(); + packet.write(&mut packet_writer); + + for player in self.server.connected_players.iter() { + if let Some(owner) = owner { + if player.get_id() == owner.get_id() { + continue; + } } - 0x02 => {} - _ => { - println!("ERROR: Unknown packet ID: {}", packet_id); + + let mut socket = player.socket.write().await; + packet.resolve(&mut socket).await; + } + } + pub async fn despawn_player(&self, id: i8) { + let despawn_player = DespawnPlayerPacket::new(id); + self.packet_queue + .enqueue(None, Box::new(despawn_player)) + .await; + } + pub async fn send_to_all_queued(&self) { + loop { + while let Some((owner_id, mut packet)) = self.packet_queue.dequeue().await { + let mut packet_writer = PacketWriter::new(); + packet.write(&mut packet_writer); + + for player in self.server.connected_players.iter() { + if let Some(owner_id) = owner_id { + if player.get_id() == owner_id { + continue; + } + } + + let mut socket = player.socket.write().await; + packet.resolve(&mut socket).await; + } } + sleep(Duration::from_millis(PACKET_FLUSH_MILLIS)).await; } } pub async fn ping_players_loop(&self) { loop { - let mut players = self.server.connected_players.lock().await; let mut remove_ids = Vec::new(); - for (id, player) in players.iter() { - let mut socket = player.socket.lock().await; + for player in self.server.connected_players.iter() { + let mut socket = player.socket.write().await; let mut ping_packet = PingPacket::new(); let mut packet_writer = PacketWriter::new(); ping_packet.write(&mut packet_writer); - if let Err(er) = ping_packet.resolve(&mut socket).await { - println!("Player {} [{}] disconected", player.get_name(), id); - remove_ids.push(*id); + if let Err(er) = socket.write_all(&packet_writer.into_inner()).await { + println!("Player {} disconnected", player.get_name()); + remove_ids.push(player.get_id()); } } for id in remove_ids { - players.remove(&id); + let leave_message = format!( + "goodbye {}", + self.server.connected_players.get(&id).unwrap().get_name() + ); + self.server.connected_players.remove(&id); + self.despawn_player(id).await; + + for player in self.server.connected_players.iter() { + player.send_message(&leave_message).await; + } } - sleep(Duration::from_secs(PING_TIME)).await; + sleep(Duration::from_millis(PING_TIME_MILLIS)).await; } } async fn get_last_id(&self) -> i8 { - let players = self.server.connected_players.lock().await; for id in 0..=127 { let id = id as i8; - if !players.contains_key(&id) { + if !self.server.connected_players.contains_key(&id) { return id; } } - return -1; - } - - async fn send_level_data(&self, socket: &mut WriteHalf) { - let world = DmfMap::load_file("maps/default.dmf").unwrap(); - let block_data = world.blocks; - - let mut resolve_packet_response = LevelInitializePacket::new(); - let mut packet_writer = PacketWriter::new(); - resolve_packet_response.write(&mut packet_writer); - resolve_packet_response.resolve(socket).await; - - let total_length = block_data.len(); - let mut prefixed_data = Vec::with_capacity(4 + total_length); - prefixed_data.extend_from_slice(&(total_length as u32).to_be_bytes()); - prefixed_data.extend_from_slice(&block_data); - - let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); - encoder.write_all(&prefixed_data).unwrap(); - let compressed_data = encoder.finish().unwrap(); - - let chunk_size = 1024; - for (i, chunk) in compressed_data.chunks(chunk_size).enumerate() { - println!("sending chunk {}", i); - let chunk_length = chunk.len() as i16; - let percent_complete = ((i * chunk_size + chunk_length as usize) as f32 - / compressed_data.len() as f32 - * 100.0) as u8; - - let mut chunk_data = chunk.to_vec(); - if chunk_data.len() < chunk_size { - chunk_data.resize(chunk_size, 0x00); - } - - let mut packet = LevelDataChunkPacket::new(chunk_length, chunk_data, percent_complete); - let mut packet_writer = PacketWriter::new(); - packet.write(&mut packet_writer); - - if let Err(e) = packet.resolve(socket).await { - println!("Error sending chunk {}: {}", i, e); - break; - } - } - - let mut level_finalize = LevelFinalizePacket::new(world.x_size, world.y_size, world.z_size); - let mut packet_writer = PacketWriter::new(); - level_finalize.write(&mut packet_writer); - level_finalize.resolve(socket).await; + -1 } } diff --git a/src/server/network/packet_stream/packet_reader.rs b/src/server/network/packet_stream/packet_reader.rs index 03a59fa..d9e2da9 100644 --- a/src/server/network/packet_stream/packet_reader.rs +++ b/src/server/network/packet_stream/packet_reader.rs @@ -1,3 +1,4 @@ +#[derive(Debug, Clone)] pub struct PacketReader<'a> { data: &'a [u8], index: usize, diff --git a/src/server/network/packet_stream/packet_writer.rs b/src/server/network/packet_stream/packet_writer.rs index a50f6d2..4b6d502 100644 --- a/src/server/network/packet_stream/packet_writer.rs +++ b/src/server/network/packet_stream/packet_writer.rs @@ -34,4 +34,7 @@ impl PacketWriter { pub fn to_bytes(&self) -> &Vec { return &self.data; } + pub fn into_inner(self) -> Vec { + self.data + } } diff --git a/src/server/network/packets/clientbound.rs b/src/server/network/packets/clientbound.rs index 347294d..d821b0d 100644 --- a/src/server/network/packets/clientbound.rs +++ b/src/server/network/packets/clientbound.rs @@ -14,12 +14,12 @@ pub struct ServerIdentificationPacket { user_type: u8, } impl ServerIdentificationPacket { - pub fn new() -> Self { + pub fn new(server_name: String, server_motd: String) -> Self { Self { data: Vec::new(), protocol_version: 0x07, - server_name: "dandelion".to_string(), - server_motd: "hello mom!".to_string(), + server_name, + server_motd, user_type: 0x64, } } @@ -44,7 +44,6 @@ impl PacketTrait for ServerIdentificationPacket { socket: &mut WriteHalf, ) -> Result<(), Box> { socket.write_all(&self.data).await?; - Ok(()) } } @@ -187,14 +186,14 @@ impl PacketTrait for PingPacket { } } -pub struct SetBlockPacket { +pub struct UpdateSetBlockPacket { data: Vec, x: i16, y: i16, z: i16, block_type: u8, } -impl SetBlockPacket { +impl UpdateSetBlockPacket { pub fn new(x: i16, y: i16, z: i16, block_type: u8) -> Self { Self { data: Vec::new(), @@ -206,7 +205,7 @@ impl SetBlockPacket { } } #[async_trait] -impl PacketTrait for SetBlockPacket { +impl PacketTrait for UpdateSetBlockPacket { fn packet_id(&self) -> u8 { 0x06 } @@ -342,141 +341,6 @@ impl PacketTrait for SetPositionAndOrientationPacket { } } -pub struct PositionAndOrientationUpdatePacket { - data: Vec, - player_id: i8, - delta_x: i8, - delta_y: i8, - delta_z: i8, - yaw: u8, - pitch: u8, -} -impl PositionAndOrientationUpdatePacket { - pub fn new(player_id: i8, delta_x: i8, delta_y: i8, delta_z: i8, yaw: u8, pitch: u8) -> Self { - Self { - data: Vec::new(), - player_id, - delta_x, - delta_y, - delta_z, - yaw, - pitch, - } - } -} -#[async_trait] -impl PacketTrait for PositionAndOrientationUpdatePacket { - fn packet_id(&self) -> u8 { - 0x09 - } - - fn write(&mut self, writer: &mut PacketWriter) { - writer.write_byte(self.packet_id()); - writer.write_sbyte(self.player_id); - writer.write_byte(self.delta_x as u8); - writer.write_byte(self.delta_y as u8); - writer.write_byte(self.delta_z as u8); - writer.write_byte(self.yaw); - writer.write_byte(self.pitch); - self.data = writer.to_bytes().clone(); - } - - fn read(&mut self, _reader: &mut PacketReader) {} - - async fn resolve( - &self, - socket: &mut WriteHalf, - ) -> Result<(), Box> { - socket.write_all(&self.data).await?; - Ok(()) - } -} - -pub struct PositionUpdatePacket { - data: Vec, - player_id: i8, - delta_x: i8, - delta_y: i8, - delta_z: i8, -} -impl PositionUpdatePacket { - pub fn new(player_id: i8, delta_x: i8, delta_y: i8, delta_z: i8) -> Self { - Self { - data: Vec::new(), - player_id, - delta_x, - delta_y, - delta_z, - } - } -} -#[async_trait] -impl PacketTrait for PositionUpdatePacket { - fn packet_id(&self) -> u8 { - 0x0a - } - - fn write(&mut self, writer: &mut PacketWriter) { - writer.write_byte(self.packet_id()); - writer.write_sbyte(self.player_id); - writer.write_byte(self.delta_x as u8); - writer.write_byte(self.delta_y as u8); - writer.write_byte(self.delta_z as u8); - self.data = writer.to_bytes().clone(); - } - - fn read(&mut self, _reader: &mut PacketReader) {} - - async fn resolve( - &self, - socket: &mut WriteHalf, - ) -> Result<(), Box> { - socket.write_all(&self.data).await?; - Ok(()) - } -} - -pub struct OrientationUpdatePacket { - data: Vec, - player_id: i8, - yaw: u8, - pitch: u8, -} -impl OrientationUpdatePacket { - pub fn new(player_id: i8, yaw: u8, pitch: u8) -> Self { - Self { - data: Vec::new(), - player_id, - yaw, - pitch, - } - } -} -#[async_trait] -impl PacketTrait for OrientationUpdatePacket { - fn packet_id(&self) -> u8 { - 0x0b - } - - fn write(&mut self, writer: &mut PacketWriter) { - writer.write_byte(self.packet_id()); - writer.write_sbyte(self.player_id); - writer.write_byte(self.yaw); - writer.write_byte(self.pitch); - self.data = writer.to_bytes().clone(); - } - - fn read(&mut self, _reader: &mut PacketReader) {} - - async fn resolve( - &self, - socket: &mut WriteHalf, - ) -> Result<(), Box> { - socket.write_all(&self.data).await?; - Ok(()) - } -} - pub struct DespawnPlayerPacket { data: Vec, player_id: i8, @@ -512,12 +376,12 @@ impl PacketTrait for DespawnPlayerPacket { } } -pub struct MessagePacket { +pub struct SendMessagePacket { data: Vec, player_id: i8, message: String, } -impl MessagePacket { +impl SendMessagePacket { pub fn new(player_id: i8, message: String) -> Self { Self { data: Vec::new(), @@ -527,7 +391,7 @@ impl MessagePacket { } } #[async_trait] -impl PacketTrait for MessagePacket { +impl PacketTrait for SendMessagePacket { fn packet_id(&self) -> u8 { 0x0d } diff --git a/src/server/network/packets/serverbound.rs b/src/server/network/packets/serverbound.rs index 225721d..0810c02 100644 --- a/src/server/network/packets/serverbound.rs +++ b/src/server/network/packets/serverbound.rs @@ -11,13 +11,19 @@ pub struct PlayerIndentificationPacket { pub protocol_version: u8, pub username: String, pub verification_key: String, + + server_name: String, + server_motd: String, } impl PlayerIndentificationPacket { - pub fn new() -> Self { + pub fn new(server_name: String, server_motd: String) -> Self { return Self { protocol_version: 0, username: String::new(), verification_key: String::new(), + + server_name, + server_motd, }; } } @@ -38,7 +44,8 @@ impl PacketTrait for PlayerIndentificationPacket { &self, socket: &mut WriteHalf, ) -> Result<(), Box> { - let mut resolve_packet_response = ServerIdentificationPacket::new(); + let mut resolve_packet_response = + ServerIdentificationPacket::new(self.server_name.clone(), self.server_motd.clone()); let mut paclet_writer = PacketWriter::new(); resolve_packet_response.write(&mut paclet_writer); resolve_packet_response.resolve(socket).await?; @@ -48,11 +55,11 @@ impl PacketTrait for PlayerIndentificationPacket { } pub struct SetBlockPacket { - x: i16, - y: i16, - z: i16, - mode: u8, - block_type: u8, + pub x: i16, + pub y: i16, + pub z: i16, + pub mode: u8, + pub block_type: u8, } impl SetBlockPacket { @@ -76,6 +83,7 @@ impl PacketTrait for SetBlockPacket { fn write(&mut self, _writer: &mut PacketWriter) {} fn read(&mut self, reader: &mut PacketReader) { + reader.read_byte(); self.x = reader.read_short(); self.y = reader.read_short(); self.z = reader.read_short(); @@ -91,16 +99,16 @@ impl PacketTrait for SetBlockPacket { } } -pub struct PositionAndOrientationPacket { - player_id: i8, - x: i16, - y: i16, - z: i16, - yaw: u8, - pitch: u8, +pub struct PositionAndOrientationUpdatePacket { + pub player_id: i8, + pub x: i16, + pub y: i16, + pub z: i16, + pub yaw: u8, + pub pitch: u8, } -impl PositionAndOrientationPacket { +impl PositionAndOrientationUpdatePacket { pub fn new() -> Self { Self { player_id: -1, @@ -114,7 +122,7 @@ impl PositionAndOrientationPacket { } #[async_trait] -impl PacketTrait for PositionAndOrientationPacket { +impl PacketTrait for PositionAndOrientationUpdatePacket { fn packet_id(&self) -> u8 { 0x08 } @@ -122,6 +130,7 @@ impl PacketTrait for PositionAndOrientationPacket { fn write(&mut self, _writer: &mut PacketWriter) {} fn read(&mut self, reader: &mut PacketReader) { + reader.read_byte(); self.player_id = reader.read_sbyte(); self.x = reader.read_short(); self.y = reader.read_short(); @@ -140,7 +149,7 @@ impl PacketTrait for PositionAndOrientationPacket { pub struct MessagePacket { player_id: i8, - message: String, + pub message: String, } impl MessagePacket { @@ -161,6 +170,7 @@ impl PacketTrait for MessagePacket { fn write(&mut self, _writer: &mut PacketWriter) {} fn read(&mut self, reader: &mut PacketReader) { + reader.read_byte(); self.player_id = reader.read_sbyte(); self.message = reader.read_string(); } diff --git a/src/server/server.rs b/src/server/server.rs index c0115eb..2a8f1cd 100644 --- a/src/server/server.rs +++ b/src/server/server.rs @@ -1,24 +1,23 @@ use crate::server::network::{heartbeat::start_heartbeat_loop, packet_resolver::PacketResolver}; +use dashmap::DashMap; use rand::Rng; -use std::collections::HashMap; -use std::fs::write; use std::iter::repeat_with; use std::sync::Arc; use tokio::fs; -use tokio::io::{self, AsyncReadExt, AsyncWriteExt}; +use tokio::io::{self, AsyncReadExt, AsyncWriteExt, WriteHalf}; use tokio::net::{TcpListener, TcpStream}; -use tokio::sync::Mutex; +use tokio::sync::RwLock; use super::config::Config; use super::game::dmf_map::DmfMap; use super::game::player::Player; +use super::map_builder::{Dimensions, MapBuilder, NoiseLayer, PresetParams}; +use super::maps; pub struct Server { - pub connected_players: Arc>>, - - pub loaded_maps: Arc>>, + pub connected_players: Arc>, + pub loaded_maps: Arc>, pub config: Arc, - pub salt: String, } @@ -27,18 +26,25 @@ impl Server { let config = Arc::new(Config::load("server-config.yml")?); let salt = generate_salt(16); let server = Server { - connected_players: Arc::new(Mutex::new(HashMap::new())), - loaded_maps: Arc::new(Mutex::new(HashMap::new())), + connected_players: Arc::new(DashMap::new()), + loaded_maps: Arc::new(DashMap::new()), config, salt, }; - return Ok(server); + Ok(server) } pub async fn start(self: Arc) -> Result<(), Box> { - let resolver = Arc::new(PacketResolver::new(Arc::clone(&self))); - create_default_test_world().await?; + println!("Initializing server..."); + + maps::load_all_maps_in("maps", Arc::clone(&self.loaded_maps)).await?; + tokio::spawn({ + let maps = Arc::clone(&self.loaded_maps); + async move { + maps::save_all_loop(maps).await; + } + }); tokio::spawn({ let server = Arc::clone(&self); @@ -47,14 +53,23 @@ impl Server { } }); - let resolver_clone = Arc::clone(&resolver); - tokio::spawn(async move { - resolver_clone.ping_players_loop().await; + let resolver = Arc::new(PacketResolver::new(Arc::clone(&self))); + tokio::spawn({ + let resolver_clone = Arc::clone(&resolver); + async move { + resolver_clone.ping_players_loop().await; + } + }); + tokio::spawn({ + let resolver_clone = Arc::clone(&resolver); + async move { + resolver_clone.send_to_all_queued().await; + } }); let address = format!("{}:{}", self.config.addr, self.config.port); let listener = TcpListener::bind(&address).await?; - println!("Starting server on {}", &address); + println!("Server started on {}", &address); loop { let (socket, addr) = listener.accept().await?; @@ -71,61 +86,33 @@ async fn handle_client( resolver: Arc, ) -> Result<(), Box> { let (mut reader, mut writer) = tokio::io::split(socket); - let mut reader = Arc::new(Mutex::new(reader)); - let mut writer = Arc::new(Mutex::new(writer)); + let reader = Arc::new(RwLock::new(reader)); + let writer = Arc::new(RwLock::new(writer)); let mut buf = [0; 1024]; loop { - let n = match reader.lock().await.read(&mut buf).await { + let n = match reader.write().await.read(&mut buf).await { Ok(0) => { println!("Connection closed"); break; } Ok(n) => n, Err(e) => { - println!("Error reading incoming data: {}", e); + eprintln!("Error reading incoming data: {}", e); break; } }; - println!("Incoming data: "); - - let hex = buf[..n] - .iter() - .map(|b| format!("{:02x}", b)) - .collect::>() - .join(" "); - println!(" HEX: {}", hex); - - match String::from_utf8(buf[..n].to_vec()) { - Ok(s) => println!(" STR: {}", s), - Err(_) => println!(" STR: ?"), - } - resolver.handle_packet(&buf[..n], Arc::clone(&writer)).await; } - if let Err(e) = writer.lock().await.shutdown().await { - println!("Erro closing writer: {}", e); + if let Err(e) = writer.write().await.shutdown().await { + eprintln!("Error closing writer: {}", e); } + Ok(()) } -async fn create_default_test_world() -> io::Result<()> { - let mut world = DmfMap::new(128, 4, 128, 256, 64, 256); - - for x in 0..256 { - for z in 0..256 { - for y in 0..31 { - world.set_block(x, y, z, 0x01); - } - world.set_block(x, 31, z, 0x02); - } - } - - fs::create_dir_all("maps").await?; - world.save_file("maps/default.dmf") -} fn generate_salt(length: usize) -> String { const BASE62: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";