use std::io::prelude::*; use std::net::{SocketAddr, TcpListener, TcpStream}; use std::thread::sleep; use std::time::{Duration, UNIX_EPOCH}; use std::time::SystemTime; use std::thread; use std::sync::{Arc, Mutex}; use flate2::Compression; use flate2::write::GzEncoder; use rand::prelude::*; use colored::Colorize; // #[macro_use] // extern crate lazy_static; impl Default for Player { fn default() -> Self { Player { id: SpecialPlayers::SelfPlayer as u8, username: "".to_string(), verification_key: [0; 64], unused: 0x00, position_x: 0, position_y: 0, position_z: 0, yaw: 0, pitch: 0, operator: false, outgoing_data: Vec::new() } } } struct Player { // Struct `Player` is never constructed `#[warn(fuck_you)]` on by default pub id: u8, pub username: String, pub verification_key: [u8; 64], pub unused: u8, pub position_x: i16, pub position_y: i16, pub position_z: i16, pub yaw: u8, pub pitch: u8, pub operator: bool, pub outgoing_data: Vec } enum SpecialPlayers { SelfPlayer = 0xFF } #[derive(Copy, Clone, PartialEq)] enum PlayerStatus { Disconnected, ConnectedSelf, Connected } struct World { pub size_x: i16, pub size_y: i16, pub size_z: i16, pub data: Vec } fn build_world(size_x: i16, size_y: i16, size_z: i16) -> Vec { let mut rng = rand::thread_rng(); let mut world_dat: Vec = Vec::new(); for y in 0..size_y { for _z in 0..size_z { for _x in 0..size_x { if y == 0 { world_dat.push(rng.gen()); // Bookshelf } else { world_dat.push(0x00); // Air } } } } return world_dat; } const SIZE_X: i16 = 64; const SIZE_Y: i16 = 32; const SIZE_Z: i16 = 64; // lazy_static!{ // static ref WORLD: World = World { // size_x: SIZE_X, // size_y: SIZE_Y, // size_z: SIZE_Z, // data: build_world(SIZE_X, SIZE_Y, SIZE_Z) // }; // } fn handle_client(mut stream: TcpStream, client_number: u8, players_arc_clone: Arc>, world_arc_clone: Arc>) { thread::spawn(move || { let mut player_statuses = [PlayerStatus::Disconnected; 255]; let mut immediate_join = [false; 255]; { let mut players = players_arc_clone.lock().unwrap(); for i in 0..players.len() { let current_player = &mut players[i]; match current_player.id { 255=> { continue; } _=> { player_statuses[i] = PlayerStatus::Connected; immediate_join[i] = true; //println!("Player {} is immediate join!", i); } } } } player_statuses[client_number as usize] = PlayerStatus::ConnectedSelf; loop { let mut buffer = [0; 1]; let _ = stream.read(&mut buffer); match buffer[0] { 0x00=> { let mut payload_buffer = [0; 130]; // Byte + String + String + Byte let _ = stream.read(&mut payload_buffer); if payload_buffer[0] != 7 { // Shit pant let _ = &mut stream.write(&client_disconnect("Something went wrong (CODE: PACKET_SKIPPED)")); println!("this client is wiggidy wack yo!"); break; } //println!("Client Prot Ver: {}", payload_buffer[0]); let mut username = String::new(); for i in 0..64 { username.push(payload_buffer[i+1] as char); } //println!("Username: {}", username); let mut verif_key = [0; 64]; for i in 0..64 { verif_key[i] = payload_buffer[i+65]; } let mut verif_key_formatted = String::new(); use std::fmt::Write; for &byte in &verif_key { write!(&mut verif_key_formatted, "{:X}", byte).expect("Piss"); } //println!("Verification key: 0x{}", verif_key_formatted); //println!("\"Unused\" Byte: {}", payload_buffer[129]); { let mut players = players_arc_clone.lock().unwrap(); let current_player = &mut players[client_number as usize]; current_player.id = client_number; current_player.username = username.trim().to_string(); current_player.verification_key = verif_key; current_player.unused = payload_buffer[129]; current_player.position_x = 0; current_player.position_y = 128; current_player.position_z = 0; current_player.yaw = 0; current_player.pitch = 0; current_player.operator = false; bomb_server_details(&mut stream, ¤t_player, &world_arc_clone); for i in 0..immediate_join.len() { if immediate_join[i] { //println!("Immediately joining {}", i); let _ = &mut stream.write(&spawn_player( players[i].id, &players[i].username, players[i].position_x, players[i].position_y, players[i].position_z, players[i].yaw, players[i].pitch )); } } } }, 0x05=>{ let mut payload_buffer = [0; 8]; // Short (2) + Short (2) + Short (2) + Byte (1) + Byte (1) let _ = stream.read(&mut payload_buffer); let position_x = ((payload_buffer[0] as i16) << (8 as i16)) + payload_buffer[1] as i16; let position_y = ((payload_buffer[2] as i16) << (8 as i16)) + payload_buffer[3] as i16; let position_z = ((payload_buffer[4] as i16) << (8 as i16)) + payload_buffer[5] as i16; let mode = payload_buffer[6]; let mut block_type = payload_buffer[7]; // Sanity check (Stop losers from losing) if position_x > SIZE_X || position_y > SIZE_Y || position_z > SIZE_Z { // Fuck you! let _ = &mut stream.write(&client_disconnect("Block position was not within world bounds, naughty boy")); break; } if mode == 0x00 { block_type = 0x00; // Air } let world_offset = position_x + (position_z * SIZE_X) + (position_y * SIZE_X * SIZE_Z); { let mut world_dat = world_arc_clone.lock().unwrap(); world_dat.data[world_offset as usize] = block_type; } let mut update_block_bytes: Vec = Vec::new(); update_block_bytes.push(0x06); update_block_bytes.extend_from_slice(&stream_write_short(position_x)); update_block_bytes.extend_from_slice(&stream_write_short(position_y)); update_block_bytes.extend_from_slice(&stream_write_short(position_z)); update_block_bytes.push(block_type); let mut players = players_arc_clone.lock().unwrap(); for i in 0..players.len() { if players[i].id != 255 && players[i].id != client_number { players[i].outgoing_data.extend_from_slice(&update_block_bytes); } } }, 0x08=>{ let mut payload_buffer = [0; 9]; // SByte + FShort (2B) + FShort + FShort + // Byte + Byte let _ = stream.read(&mut payload_buffer); if payload_buffer[0] != SpecialPlayers::SelfPlayer as u8 { let _ = &mut stream.write(&client_disconnect("Evil bit level hacking")); break; } { let mut players = players_arc_clone.lock().unwrap(); let current_player = &mut players[client_number as usize]; current_player.position_x = ((payload_buffer[1] as i16) << (8 as i16)) + payload_buffer[2] as i16; current_player.position_y = ((payload_buffer[3] as i16) << (8 as i16)) + payload_buffer[4] as i16; current_player.position_z = ((payload_buffer[5] as i16) << (8 as i16)) + payload_buffer[6] as i16; current_player.yaw = payload_buffer[7]; current_player.pitch = payload_buffer[8]; } }, 0x0D=>{ let mut payload_buffer = [0; 65]; // Byte + String let _ = stream.read(&mut payload_buffer); if payload_buffer[0] != SpecialPlayers::SelfPlayer as u8 { let _ = &mut stream.write(&client_disconnect("Evil bit level hacking")); break; } let mut message = ['a'; 64]; for i in 0..64 { message[i] = payload_buffer[i+1] as char; } let mut players = players_arc_clone.lock().unwrap(); for i in 0..players.len() { if players[i].id != 255 && players[i].id != client_number { let sender: u8 = players[client_number as usize].id; players[i].outgoing_data.extend_from_slice(&send_chat_message(sender, String::from_iter(message))); } } let _ = &mut stream.write(&send_chat_message(SpecialPlayers::SelfPlayer as u8, String::from_iter(message))); println!("{}", String::from_iter(message)); }, _=>println!("Packet {} not implemented!", buffer[0]), } let is_kill = &mut stream.write(&ping()); // Ping that MF if is_kill.is_err() { break; } sleep(Duration::from_millis(1000/1000)); // 1000 TPS TODO: Delta time { let mut players = players_arc_clone.lock().unwrap(); if players[client_number as usize].outgoing_data.len() > 0 { let _ = stream.write(&players[client_number as usize].outgoing_data); players[client_number as usize].outgoing_data.clear(); } for i in 0..players.len() { if players[i].id != 255 { if player_statuses[i] == PlayerStatus::Disconnected { let _ = stream.write(&spawn_player( players[i].id, &players[i].username, players[i].position_x, players[i].position_y, players[i].position_z, players[i].yaw, players[i].pitch )); player_statuses[i] = PlayerStatus::Connected; let _ = stream.write(&send_chat_message(players[i].id, format!("{} has joined the game!", &players[i].username))); } } else { if player_statuses[i] == PlayerStatus::Connected { let _ = stream.write(&despawn_player(i.try_into().unwrap())); let _ = stream.write(&send_chat_message(i.try_into().unwrap(), format!("{} has left the game!", &players[i].username))); player_statuses[i] = PlayerStatus::Disconnected; } } if player_statuses[i] == PlayerStatus::Connected { let _ = stream.write(&set_position_and_orientation( players[i].id, players[i].position_x, players[i].position_y, players[i].position_z, players[i].yaw, players[i].pitch )); } } } } { let mut players = players_arc_clone.lock().unwrap(); let current_player = &mut players[client_number as usize]; current_player.id = SpecialPlayers::SelfPlayer as u8; } println!("Thread {} is kill!", client_number); }); } fn to_mc_string(text: &str) -> [u8; 64] { let text_vec: Vec = text.chars().take(64).collect(); let mut balls = [0; 64]; for i in 0..64 { balls[i] = 0x20; } for i in 0..text_vec.len() { balls[i] = text_vec[i] as u8; } return balls; } // fn stream.write(data: &[u8]) -> Vec { // // for i in 0..data.len() { // // stream.write(&[data[i]])?; // // } // //let _ = stream.write(data)?; // //Ok(()) // return data.to_vec(); // } // fn stream_write_short(data: i16) -> Vec { // stream.write(&[(data >> 0x08) as u8])?; // stream.write(&[(data & 0x00FF) as u8])?; //stream.write(&[(data >> 0x08) as u8, (data & 0x00FF) as u8])?; //Ok(()) // return [(data >> 0x08) as u8, (data & 0x00FF) as u8].to_vec(); } fn client_disconnect(text: &str) -> Vec { //stream.write(&[0x0E])?; // Disconnect //stream.write(&to_mc_string(text))?; let mut ret_val: Vec = vec![]; ret_val.push(0x0E); ret_val.append(&mut to_mc_string(text).to_vec()); return ret_val; } fn server_identification(is_op: bool) -> Vec { let mut ret_val: Vec = vec![]; ret_val.push(0x00); let start = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos(); ret_val.push(0x07); let end = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos(); println!("Single stream write: {}ns", end - start); let server_name = "Erm... what the sigma?"; ret_val.append(&mut to_mc_string(server_name).to_vec()); let server_motd = "Pragmatism not idealism"; ret_val.append(&mut to_mc_string(server_motd).to_vec()); if is_op { ret_val.push(0x64); } else { ret_val.push(0x00); } return ret_val; } fn ping() -> Vec { return vec![0x01]; } fn init_level() -> Vec { return vec![0x02]; } fn finalize_level(world_arc_clone: &Arc>) -> Vec { let mut ret_val: Vec = vec![]; ret_val.push(0x04); let world_dat = world_arc_clone.lock().unwrap(); ret_val.append(&mut stream_write_short(world_dat.size_x).to_vec()); ret_val.append(&mut stream_write_short(world_dat.size_y).to_vec()); ret_val.append(&mut stream_write_short(world_dat.size_z).to_vec()); return ret_val; } fn spawn_player(player_id: u8, name: &String, pos_x: i16, pos_y: i16, pos_z: i16, yaw: u8, pitch: u8) -> Vec { let mut ret_val: Vec = vec![]; ret_val.push(0x07); ret_val.push(player_id); ret_val.append(&mut to_mc_string(name).to_vec()); ret_val.append(&mut stream_write_short(pos_x).to_vec()); ret_val.append(&mut stream_write_short(pos_y).to_vec()); ret_val.append(&mut stream_write_short(pos_z).to_vec()); ret_val.push(yaw); ret_val.push(pitch); return ret_val; } fn despawn_player(player_id: u8) -> Vec { // stream.write(&[0x0c])?; // stream.write(&[player_id])?; return [0x0C, player_id].to_vec(); } fn send_chat_message(source_id: u8, message: String) -> Vec { let mut ret_val: Vec = vec![]; ret_val.push(0x0D); ret_val.push(source_id); ret_val.append(&mut to_mc_string(&message).to_vec()); return ret_val; } //fn orientation_update(stream: &mut TcpStream, player_id: u8, yaw: u8, pitch: u8) -> std::io::Result<()> { // stream.write(&[0x0B])?; // // stream.write(&[player_id])?; // stream.write(&[yaw])?; // stream.write(&[pitch])?; // // Ok(()) //} fn set_position_and_orientation(player_id: u8, pos_x: i16, pos_y: i16, pos_z: i16, yaw: u8, pitch: u8) -> Vec { let mut ret_val: Vec = vec![]; ret_val.push(0x08); ret_val.push(player_id); ret_val.append(&mut stream_write_short(pos_x).to_vec()); ret_val.append(&mut stream_write_short(pos_y).to_vec()); ret_val.append(&mut stream_write_short(pos_z).to_vec()); ret_val.push(yaw); ret_val.push(pitch); return ret_val; } fn send_level_data(world_arc_clone: &Arc>) -> Vec { let mut ret_val: Vec = vec![]; let mut world_dat = world_arc_clone.lock().unwrap().data.clone(); // Big endian fold lmao world_dat.insert(0, ((world_dat.len() & 0xFF) >> 0) as u8); world_dat.insert(0, ((world_dat.len() & 0xFF00) >> 8) as u8); world_dat.insert(0, ((world_dat.len() & 0xFF0000) >> 16) as u8); world_dat.insert(0, ((world_dat.len() & 0xFF000000) >> 24) as u8); // TODO: Stream GZIP straight onto the network let mut world_dat_compressor = GzEncoder::new(Vec::new(), Compression::fast()); for i in 0..world_dat.len() { let _ = world_dat_compressor.write(&[world_dat[i]]); } let world_dat_gzipped = world_dat_compressor.finish().unwrap(); let number_of_chunks = ((world_dat_gzipped.len() as f32)/1024.0_f32).ceil() as usize; let mut current_chunk = 0; if number_of_chunks != 1 { while current_chunk + 1 != number_of_chunks { ret_val.push(0x03); ret_val.append(&mut stream_write_short(0x400)); let mut chunk_data_buffer = [0u8; 1024]; for i in 0..1024 { chunk_data_buffer[i] = world_dat_gzipped[current_chunk*1024+i]; } ret_val.append(&mut chunk_data_buffer.to_vec()); let mut percentage = current_chunk/number_of_chunks*100; if percentage > 100 { percentage = 100; } ret_val.push(percentage.try_into().unwrap()); current_chunk += 1; } } let remaining_chunk_size = world_dat_gzipped.len() - (current_chunk * 1024); if remaining_chunk_size > 0 { ret_val.push(0x03); ret_val.append(&mut stream_write_short(remaining_chunk_size.try_into().unwrap())); let mut remaining_data_buffer = [0u8; 1024]; for i in 0..remaining_chunk_size { remaining_data_buffer[i] = world_dat_gzipped[current_chunk*1024+i]; } ret_val.append(&mut remaining_data_buffer.to_vec()); ret_val.push(100); } println!("World transmission size: {}KiB", ret_val.len() as f32 / 1024.0); return ret_val; } fn bomb_server_details(stream: &mut TcpStream, current_player: &Player, world_arc_clone: &Arc>) { let mut compound_data: Vec = vec![]; println!("Server IDENT"); compound_data.append(&mut server_identification(current_player.operator)); println!("Intialize level"); compound_data.append(&mut init_level()); println!("Send level data"); compound_data.append(&mut send_level_data(&world_arc_clone)); // Approaching Nirvana - Maw of the beast println!("Finalize level"); compound_data.append(&mut finalize_level(&world_arc_clone)); println!("Spawning player"); compound_data.append(&mut spawn_player(SpecialPlayers::SelfPlayer as u8, ¤t_player.username, 64, 2, 64, 0, 0)); let _ = stream.write(&compound_data); } fn _create_player_info_window(client_number: u8, players_arc_clone: Arc>) { thread::spawn(move || { let mut thread_alive = false; loop { { let mut players = players_arc_clone.lock().unwrap(); let current_player = &mut players[client_number as usize]; if current_player.id == 255 && thread_alive == true { println!("[{}{}] {} has disconnected!", "THREAD: ".cyan(), client_number, current_player.username); break; } else { thread_alive = true; } println!("[{}{}] id: {}", "THREAD: ".cyan(), client_number, current_player.id); println!("[{}{}] pos_x: {}", "THREAD: ".cyan(), client_number, current_player.position_x >> 5); println!("[{}{}] pos_y: {}", "THREAD: ".cyan(), client_number, current_player.position_y >> 5); println!("[{}{}] pos_z: {}", "THREAD: ".cyan(), client_number, current_player.position_z >> 5); } sleep(Duration::from_millis(200)); } }); } fn main() -> std::io::Result<()> { let players: [Player; 255] = core::array::from_fn(|_| Player::default()); let players_arc = Arc::new(Mutex::new(players)); let world_instance: World = World {size_x: SIZE_X, size_y: SIZE_Y, size_z: SIZE_Z, data: build_world(SIZE_X, SIZE_Y, SIZE_Z)}; let world_arc = Arc::new(Mutex::new(world_instance)); let addr = SocketAddr::from(([0, 0, 0, 0], 25565)); let listener = TcpListener::bind(addr)?; let mut thread_number : u8 = 0; for stream in listener.incoming() { let players_arc_clone = Arc::clone(&players_arc); let world_arc_clone = Arc::clone(&world_arc); //let _players_arc_clone_debug = Arc::clone(&players_arc); handle_client(stream?, thread_number, players_arc_clone, world_arc_clone); //_create_player_info_window(thread_number, _players_arc_clone_debug); if thread_number < 255 { thread_number += 1; } else { thread_number = 0; } } Ok(()) }