use std::{ io::Cursor, sync::Arc, thread, time::{Duration, SystemTime, UNIX_EPOCH}, }; use rust_mc_proto::{DataReader, DataWriter, Packet, read_packet}; use crate::server::{ ServerError, data::{ReadWriteNBT, text_component::TextComponent}, player::context::ClientContext, }; use super::id::*; pub fn send_update_tags(client: Arc) -> Result<(), ServerError> { // TODO: rewrite this hardcode bullshit client.write_packet(&Packet::from_bytes( clientbound::configuration::UPDATE_TAGS, include_bytes!("update-tags.bin"), )) } pub fn send_registry_data(client: Arc) -> Result<(), ServerError> { // TODO: rewrite this hardcode bullshit let mut registry_data = Cursor::new(include_bytes!("registry-data.bin")); while let Ok(mut packet) = read_packet(&mut registry_data, None) { packet.set_id(clientbound::configuration::REGISTRY_DATA); client.write_packet(&packet)?; } Ok(()) } // Добавки в Configuration стейт чтобы все работало pub fn handle_configuration_state( client: Arc, // Контекст клиента ) -> Result<(), ServerError> { let mut packet = Packet::empty(clientbound::configuration::FEATURE_FLAGS); packet.write_varint(1)?; packet.write_string("minecraft:vanilla")?; client.write_packet(&packet)?; let mut packet = Packet::empty(clientbound::configuration::KNOWN_PACKS); packet.write_varint(1)?; packet.write_string("minecraft")?; packet.write_string("core")?; packet.write_string("1.21.5")?; client.write_packet(&packet)?; client.read_packet(serverbound::configuration::KNOWN_PACKS)?; send_registry_data(client.clone())?; send_update_tags(client.clone()) } pub fn send_login(client: Arc) -> Result<(), ServerError> { // Отправка пакета Login let mut packet = Packet::empty(clientbound::play::LOGIN); packet.write_int(0)?; // Entity ID packet.write_boolean(false)?; // Is hardcore packet.write_varint(4)?; // Dimension Names packet.write_string("minecraft:overworld")?; packet.write_string("minecraft:nether")?; packet.write_string("minecraft:the_end")?; packet.write_string("minecraft:overworld_caves")?; packet.write_varint(0)?; // Max Players packet.write_varint(8)?; // View Distance packet.write_varint(5)?; // Simulation Distance packet.write_boolean(false)?; // Reduced Debug Info packet.write_boolean(true)?; // Enable respawn screen packet.write_boolean(false)?; // Do limited crafting packet.write_varint(0)?; // Dimension Type packet.write_string("minecraft:overworld")?; // Dimension Name packet.write_long(0x0f38f26ad09c3e20)?; // Hashed seed packet.write_byte(0)?; // Game mode packet.write_signed_byte(-1)?; // Previous Game mode packet.write_boolean(false)?; // Is Debug packet.write_boolean(true)?; // Is Flat packet.write_boolean(false)?; // Has death location packet.write_varint(20)?; // Portal cooldown packet.write_varint(60)?; // Sea level packet.write_boolean(false)?; // Enforces Secure Chat client.write_packet(&packet) } pub fn send_game_event( client: Arc, event: u8, value: f32, ) -> Result<(), ServerError> { let mut packet = Packet::empty(clientbound::play::GAME_EVENT); packet.write_byte(event)?; packet.write_float(value)?; client.write_packet(&packet) } pub fn sync_player_pos( client: Arc, x: f64, y: f64, z: f64, vel_x: f64, vel_y: f64, vel_z: f64, yaw: f32, pitch: f32, flags: i32, ) -> Result<(), ServerError> { let timestamp = (SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_millis() & 0xFFFFFFFF) as i32; let mut packet = Packet::empty(clientbound::play::SYNCHRONIZE_PLAYER_POSITION); packet.write_varint(timestamp)?; packet.write_double(x)?; packet.write_double(y)?; packet.write_double(z)?; packet.write_double(vel_x)?; packet.write_double(vel_y)?; packet.write_double(vel_z)?; packet.write_float(yaw)?; packet.write_float(pitch)?; packet.write_int(flags)?; client.write_packet(&packet)?; Ok(()) } pub fn set_center_chunk(client: Arc, x: i32, z: i32) -> Result<(), ServerError> { let mut packet = Packet::empty(clientbound::play::SET_CENTER_CHUNK); packet.write_varint(x)?; packet.write_varint(z)?; client.write_packet(&packet) } pub fn send_example_chunk(client: Arc, x: i32, z: i32) -> Result<(), ServerError> { let mut packet = Packet::empty(clientbound::play::CHUNK_DATA_AND_UPDATE_LIGHT); packet.write_int(x)?; packet.write_int(z)?; // heightmap packet.write_varint(1)?; // heightmaps count packet.write_varint(0)?; // MOTION_BLOCKING packet.write_varint(256)?; // Length of the following long array (16 * 16 = 256) for _ in 0..256 { packet.write_long(0)?; // height - 0 } // sending chunk data let mut chunk_data = Vec::new(); // we want to fill the area from -64 to 0, so it will be 4 chunk sections for _ in 0..4 { chunk_data.write_short(4096)?; // non-air blocks count, 16 * 16 * 16 = 4096 stone blocks // blocks paletted container chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format chunk_data.write_varint(1)?; // block state id in the registry (1 for stone) // biomes palleted container chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format chunk_data.write_varint(27)?; // biome id in the registry } // air chunk sections for _ in 0..20 { chunk_data.write_short(0)?; // non-air blocks count, 0 // blocks paletted container chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format chunk_data.write_varint(0)?; // block state id in the registry (0 for air) // biomes palleted container chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format chunk_data.write_varint(27)?; // biome id in the registry } packet.write_usize_varint(chunk_data.len())?; packet.write_bytes(&chunk_data)?; packet.write_byte(0)?; // light data packet.write_byte(0)?; packet.write_byte(0)?; packet.write_byte(0)?; packet.write_byte(0)?; packet.write_byte(0)?; packet.write_byte(0)?; client.write_packet(&packet)?; Ok(()) } pub fn send_keep_alive(client: Arc) -> Result<(), ServerError> { let timestamp = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs() as i64; let mut packet = Packet::empty(clientbound::play::KEEP_ALIVE); packet.write_long(timestamp)?; client.write_packet(&packet)?; let mut packet = client.read_packet(serverbound::play::KEEP_ALIVE)?; let timestamp2 = packet.read_long()?; if timestamp2 != timestamp { // Послать клиента нахуй println!("KeepAlive Err") } else { println!("KeepAlive Ok") } Ok(()) } pub fn send_system_message( client: Arc, message: TextComponent, is_action_bar: bool, ) -> Result<(), ServerError> { let mut packet = Packet::empty(clientbound::play::SYSTEM_CHAT_MESSAGE); packet.write_nbt(&message)?; packet.write_boolean(is_action_bar)?; client.write_packet(&packet) } // Отдельная функция для работы с самой игрой pub fn handle_play_state( client: Arc, // Контекст клиента ) -> Result<(), ServerError> { thread::spawn({ let client = client.clone(); move || { let _ = client.run_read_loop(); client.close(); } }); send_login(client.clone())?; sync_player_pos(client.clone(), 8.0, 0.0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0)?; send_game_event(client.clone(), 13, 0.0)?; // 13 - Start waiting for level chunks set_center_chunk(client.clone(), 0, 0)?; send_example_chunk(client.clone(), 0, 0)?; thread::spawn({ let client = client.clone(); move || -> Result<(), ServerError> { while client.is_alive() { let mut packet = client.read_any_packet()?; match packet.id() { serverbound::play::SET_PLAYER_POSITION => { let x = packet.read_double()?; let y = packet.read_double()?; let z = packet.read_double()?; let _ = packet.read_byte()?; // flags client.set_position((x, y, z)); } serverbound::play::SET_PLAYER_POSITION_AND_ROTATION => { let x = packet.read_double()?; let y = packet.read_double()?; let z = packet.read_double()?; let yaw = packet.read_float()?; let pitch = packet.read_float()?; let _ = packet.read_byte()?; // flags client.set_position((x, y, z)); client.set_rotation((yaw, pitch)); } serverbound::play::SET_PLAYER_ROTATION => { let yaw = packet.read_float()?; let pitch = packet.read_float()?; let _ = packet.read_byte()?; // flags client.set_rotation((yaw, pitch)); } _ => { client.push_back(packet); } } } Ok(()) } }); let mut ticks_alive = 0u64; while client.is_alive() { println!("{ticks_alive}"); if ticks_alive % 200 == 0 { // 10 secs timer send_keep_alive(client.clone())?; } if ticks_alive % 20 == 0 { // 1 sec timer let (x, y, z) = client.position(); send_system_message( client.clone(), TextComponent::rainbow(format!("Pos: {} {} {}", x as i64, y as i64, z as i64)), false, )?; } send_system_message( client.clone(), TextComponent::rainbow(format!("Ticks alive: {}", ticks_alive)), true, )?; thread::sleep(Duration::from_millis(50)); // 1 tick ticks_alive += 1; } println!("Client die"); Ok(()) }