use flate2::write::GzEncoder; use flate2::Compression; use log::info; use std::io::prelude::*; use std::net::TcpStream; use std::sync::{Arc, Mutex}; use crate::error::AppError; use crate::Player; use crate::SpecialPlayers; use crate::World; pub 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; } balls } pub fn stream_write_short(data: i16) -> Vec { [(data >> 0x08) as u8, (data & 0x00FF) as u8].to_vec() } pub fn client_disconnect(text: &str) -> Vec { let mut ret_val: Vec = vec![]; ret_val.push(0x0E); ret_val.append(&mut to_mc_string(text).to_vec()); ret_val } pub fn server_identification(is_op: bool) -> Vec { let mut ret_val: Vec = vec![]; ret_val.push(0x00); ret_val.push(0x07); 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); } ret_val } pub fn ping() -> Vec { vec![0x01] } pub fn init_level() -> Vec { vec![0x02] } pub fn finalize_level(world_arc_clone: &Arc>) -> Result, AppError> { let mut ret_val: Vec = vec![]; ret_val.push(0x04); let world_dat = world_arc_clone.lock()?; 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()); Ok(ret_val) } pub 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 << 5).to_vec()); // FShort ret_val.append(&mut stream_write_short(pos_y << 5).to_vec()); ret_val.append(&mut stream_write_short(pos_z << 5).to_vec()); ret_val.push(yaw); ret_val.push(pitch); ret_val } pub fn despawn_player(player_id: u8) -> Vec { [0x0C, player_id].to_vec() } pub fn send_chat_message( source_id: u8, mut source_username: String, mut message: String, ) -> Vec { let mut ret_val: Vec = vec![]; ret_val.push(0x0D); if source_username.len() != 0 { source_username.push_str(": "); } message.insert_str(0, &source_username); ret_val.push(source_id); ret_val.append(&mut to_mc_string(&message).to_vec()); ret_val } pub fn write_chat_stream(stream: &mut TcpStream, message: String) { let _ = stream.write(&send_chat_message( SpecialPlayers::SelfPlayer as u8, "".to_string(), message, )); } pub 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); ret_val } pub fn send_level_data(world_arc_clone: &Arc>) -> Result, AppError> { let mut ret_val: Vec = vec![]; let mut world_dat = world_arc_clone.lock()?.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()); let _ = world_dat_compressor.write_all(&world_dat); let world_dat_gzipped = world_dat_compressor.finish()?; 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()?); 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()?)); 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); } info!( "World transmission size: {}KiB", ret_val.len() as f32 / 1024.0 ); Ok(ret_val) } pub fn bomb_server_details( stream: &mut TcpStream, current_player: &Player, world_arc_clone: &Arc>, ) -> Result<(), AppError> { let mut compound_data: Vec = vec![]; compound_data.append(&mut server_identification(current_player.operator)); compound_data.append(&mut init_level()); // info!("Send level data"); compound_data.append(&mut send_level_data(&world_arc_clone)?); // Approaching Nirvana - Maw of the beast compound_data.append(&mut finalize_level(&world_arc_clone)?); info!("Spawning player: {}", ¤t_player.username); compound_data.append(&mut spawn_player( SpecialPlayers::SelfPlayer as u8, ¤t_player.username, 32, 17, 32, 0, 0, )); let _ = stream.write(&compound_data); Ok(()) }