use std::io::prelude::*; use std::net::{SocketAddr, TcpListener, TcpStream}; use std::thread::sleep; use std::time::Duration; 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 } } } 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 } 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>) { 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 _ = client_disconnect(&mut stream, "Something went wrong (CODE: PACKET_SKIPPED)"); println!("THIS CLIENT IS FUCKED!"); 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); for i in 0..immediate_join.len() { if immediate_join[i] { //println!("Immediately joining {}", i); let _ = spawn_player( &mut stream, players[i].id, &players[i].username, players[i].position_x, players[i].position_y, players[i].position_z, players[i].yaw, players[i].pitch ); } } } }, 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 _ = client_disconnect(&mut stream, "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 _ = client_disconnect(&mut stream, "Evil bit level hacking"); break; } let mut message = ['a'; 64]; for i in 0..64 { message[i] = payload_buffer[i+1] as char; } let _ = send_chat_message(&mut stream, SpecialPlayers::SelfPlayer as u8, String::from_iter(message)); println!("{}", String::from_iter(message)); }, _=>println!("Packet {} not implemented!", buffer[0]), } let is_kill = ping(&mut stream); // Ping that MF if is_kill.is_err() { break; } sleep(Duration::from_millis(50)); // 20 TPS TODO: Delta time { let players = players_arc_clone.lock().unwrap(); for i in 0..players.len() { if players[i].id != 255 { if player_statuses[i] == PlayerStatus::Disconnected { let _ = spawn_player( &mut stream, 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 _ = send_chat_message(&mut stream, players[i].id, format!("{} has joined the game!", &players[i].username)); } } else { if player_statuses[i] == PlayerStatus::Connected { let _ = despawn_player(&mut stream, i.try_into().unwrap()); let _ = send_chat_message(&mut stream, i.try_into().unwrap(), format!("{} has left the game!", &players[i].username)); player_statuses[i] = PlayerStatus::Disconnected; } } if player_statuses[i] == PlayerStatus::Connected { let _ = set_position_and_orientation( &mut stream, 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_array(data: &[u8], stream: &mut TcpStream) -> std::io::Result<()> { for i in 0..data.len() { stream.write(&[data[i]])?; } Ok(()) } fn stream_write_short(data: i16, stream: &mut TcpStream) -> std::io::Result<()> { stream.write(&[(data >> 0x08) as u8])?; stream.write(&[(data & 0x00FF) as u8])?; Ok(()) } fn client_disconnect(stream: &mut TcpStream, text: &str) -> std::io::Result<()> { stream.write(&[0x0E])?; // Disconnect stream_write_array(&to_mc_string(text), stream)?; Ok(()) } fn server_identification(stream: &mut TcpStream, is_op: bool) -> std::io::Result<()> { stream.write(&[0x00])?; stream.write(&[0x07])?; let server_name = "Erm... what the sigma?"; stream_write_array(&to_mc_string(server_name), stream)?; let server_motd = "Pragmatism not idealism"; stream_write_array(&to_mc_string(server_motd), stream)?; if is_op { stream.write(&[0x64])?; } else { stream.write(&[0x00])?; } Ok(()) } fn ping (stream: &mut TcpStream) -> std::io::Result<()> { stream.write(&[0x01])?; Ok(()) } fn init_level(stream: &mut TcpStream) -> std::io::Result<()> { stream.write(&[0x02])?; Ok(()) } fn finalize_level(stream: &mut TcpStream, size_x: i16, size_y: i16, size_z: i16) -> std::io::Result<()> { stream.write(&[0x04])?; stream_write_short(size_x, stream)?; stream_write_short(size_y, stream)?; stream_write_short(size_z, stream)?; Ok(()) } fn spawn_player(stream: &mut TcpStream, player_id: u8, name: &String, pos_x: i16, pos_y: i16, pos_z: i16, yaw: u8, pitch: u8) -> std::io::Result<()> { stream.write(&[0x07])?; stream.write(&[player_id])?; stream_write_array(&to_mc_string(name), stream)?; stream_write_short(pos_x, stream)?; stream_write_short(pos_y, stream)?; stream_write_short(pos_z, stream)?; stream.write(&[yaw])?; stream.write(&[pitch])?; Ok(()) } fn despawn_player(stream: &mut TcpStream, player_id: u8) -> std::io::Result<()> { stream.write(&[0x0c])?; stream.write(&[player_id])?; Ok(()) } fn send_chat_message(stream: &mut TcpStream, source_id: u8, message: String) -> std::io::Result<()> { stream.write(&[0x0D])?; stream.write(&[source_id])?; stream_write_array(&to_mc_string(&message), stream)?; Ok(()) } //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(stream: &mut TcpStream, player_id: u8, pos_x: i16, pos_y: i16, pos_z: i16, yaw: u8, pitch: u8) -> std::io::Result<()> { stream.write(&[0x08])?; stream.write(&[player_id])?; stream_write_short(pos_x, stream)?; stream_write_short(pos_y, stream)?; stream_write_short(pos_z, stream)?; stream.write(&[yaw])?; stream.write(&[pitch])?; Ok(()) } fn send_level_data(stream: &mut TcpStream) -> std::io::Result<()> { let mut world_dat = WORLD.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 { stream.write(&[0x03])?; stream_write_short(0x400, stream)?; let mut chunk_data_buffer = [0u8; 1024]; for i in 0..1024 { chunk_data_buffer[i] = world_dat_gzipped[current_chunk*1024+i]; } stream_write_array(&chunk_data_buffer, stream)?; let mut percentage = current_chunk/number_of_chunks*100; if percentage > 100 { percentage = 100; } stream.write(&[percentage.try_into().unwrap()])?; current_chunk += 1; } } let remaining_chunk_size = world_dat_gzipped.len() - (current_chunk * 1024); if remaining_chunk_size > 0 { stream.write(&[0x03])?; stream_write_short(remaining_chunk_size.try_into().unwrap(), stream)?; 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]; } stream_write_array(&remaining_data_buffer, stream)?; stream.write(&[100])?; } Ok(()) } fn bomb_server_details(stream: &mut TcpStream, current_player: &Player) { //println!("Server IDENT"); let _ = server_identification(stream, current_player.operator); //println!("Intialize level"); let _ = init_level(stream); //println!("Send level data"); let _ = send_level_data(stream); // Approaching Nirvana - Maw of the beast //println!("Finalize level"); let _ = finalize_level(stream, WORLD.size_x, WORLD.size_y, WORLD.size_z); //println!("Spawning player"); let _ = spawn_player(stream, SpecialPlayers::SelfPlayer as u8, ¤t_player.username, 64, 2, 64, 0, 0); } 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 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 _players_arc_clone_debug = Arc::clone(&players_arc); handle_client(stream?, thread_number, players_arc_clone); //_create_player_info_window(thread_number, _players_arc_clone_debug); if thread_number < 255 { thread_number += 1; } else { thread_number = 0; } } Ok(()) }