From 8b5084460e99e8a2c1e8706fb8d7eb210a658084 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Wed, 7 May 2025 17:55:32 +0300 Subject: [PATCH] send players --- Cargo.lock | 64 +++++++++++++++++++++++++++++ Cargo.toml | 2 +- src/context.rs | 19 ++++++++- src/play/mod.rs | 95 +++++++++++++++++++++++++++++++++++-------- src/player/context.rs | 84 ++++++++++++++++++++++++-------------- 5 files changed, 216 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e24daf..da16af8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,6 +109,15 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bumpalo" version = "3.17.0" @@ -214,6 +223,16 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "darling" version = "0.20.11" @@ -273,6 +292,16 @@ dependencies = [ "serde", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "either" version = "1.15.0" @@ -330,6 +359,16 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -495,6 +534,16 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "memchr" version = "2.7.4" @@ -967,6 +1016,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -984,6 +1039,15 @@ name = "uuid" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +dependencies = [ + "md-5", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasm-bindgen" diff --git a/Cargo.toml b/Cargo.toml index b3cedf4..a6e975e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ palette = "0.7.6" craftflow-nbt = "2.1.0" colog = "1.3.0" log = "0.4.27" -uuid = "1.16.0" +uuid = { version = "1.16.0", features = ["v3"] } dashmap = "6.1.0" paste = "1.0.15" ignore-result = "0.2.0" diff --git a/src/context.rs b/src/context.rs index 5d0a706..3c2aa57 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,4 +1,7 @@ -use std::{net::SocketAddr, sync::Arc}; +use std::{ + net::SocketAddr, + sync::{Arc, atomic::AtomicI32}, +}; use dashmap::DashMap; use itertools::Itertools; @@ -15,6 +18,7 @@ use super::{ pub struct ServerContext { pub config: Arc, pub clients: DashMap>, + pub world: WorldContext, listeners: Vec>, handlers: Vec>, } @@ -26,6 +30,7 @@ impl ServerContext { listeners: Vec::new(), handlers: Vec::new(), clients: DashMap::new(), + world: WorldContext::new(), } } @@ -92,3 +97,15 @@ impl ServerContext { self.listeners.iter().sorted_by_key(sort_by).collect_vec() } } + +pub struct WorldContext { + pub entity_id_counter: AtomicI32, +} + +impl WorldContext { + pub fn new() -> WorldContext { + WorldContext { + entity_id_counter: AtomicI32::new(0), + } + } +} diff --git a/src/play/mod.rs b/src/play/mod.rs index 6f65792..1f9fd97 100644 --- a/src/play/mod.rs +++ b/src/play/mod.rs @@ -1,3 +1,4 @@ +use std::sync::atomic::Ordering; use std::{sync::Arc, thread, time::Duration}; use config::handle_configuration_state; @@ -6,7 +7,9 @@ use helper::{ unload_chunk, }; use rust_mc_proto::{DataReader, DataWriter, Packet}; +use uuid::Uuid; +use crate::player::context::EntityInfo; use crate::{ ServerError, data::text_component::TextComponent, event::PacketHandler, player::context::ClientContext, @@ -57,7 +60,7 @@ 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_int(client.entity_info().entity_id)?; // Entity ID packet.write_boolean(false)?; // Is hardcore packet.write_varint(4)?; // Dimension Names packet.write_string("minecraft:overworld")?; @@ -96,10 +99,13 @@ pub fn send_example_chunk(client: Arc, x: i32, z: i32) -> Result< // 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 + packet.write_varint(0)?; // MOTION_BLOCKING - 0 + // bits per entry is ceil(log2(385)) = 9 where 385 is the world height + // so, the length of the following array is (9 * 16 * 16) / 8 = 37 + // ... idk how it came to that + packet.write_varint(37)?; // Length of the following long array + for _ in 0..37 { + packet.write_long(0)?; // THIS WORKS ONLY BECAUSE OUR HEIGHT IS 0 } // sending chunk data @@ -117,7 +123,7 @@ pub fn send_example_chunk(client: Arc, x: i32, z: i32) -> Result< // 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 + chunk_data.write_varint(1)?; // biome id in the registry } // air chunk sections @@ -181,10 +187,60 @@ pub fn send_example_chunks_in_distance( Ok(()) } +pub fn send_player( + receiver: Arc, + player: Arc, +) -> Result<(), ServerError> { + // Отправка пакета Login + let mut packet = Packet::empty(clientbound::play::SPAWN_ENTITY); + + let (x, y, z) = player.entity_info().position(); + let (yaw, pitch) = player.entity_info().rotation(); + let (vel_x, vel_y, vel_z) = player.entity_info().velocity(); + + packet.write_varint(player.entity_info().entity_id)?; // Entity ID + packet.write_uuid(&player.entity_info().uuid)?; // Entity UUID + packet.write_varint(148)?; // Entity type TODO: move to const + packet.write_double(x)?; + packet.write_double(y)?; + packet.write_double(z)?; + packet.write_byte((pitch / 360.0 * 256.0) as u8)?; + packet.write_byte((yaw / 360.0 * 256.0) as u8)?; + packet.write_byte((yaw / 360.0 * 256.0) as u8)?; // head yaw TODO: make player head yaw field + packet.write_varint(0)?; + packet.write_short(vel_x as i16)?; + packet.write_short(vel_y as i16)?; + packet.write_short(vel_z as i16)?; + + receiver.write_packet(&packet) +} + +pub fn get_offline_uuid(name: &str) -> Uuid { + let mut namespaces_bytes: [u8; 16] = [0; 16]; + for (i, byte) in format!("OfflinePlayer:{}", &name[..2]) + .as_bytes() + .iter() + .enumerate() + { + namespaces_bytes[i] = *byte; + } + let namespace = Uuid::from_bytes(namespaces_bytes); + Uuid::new_v3(&namespace, (&name[2..]).as_bytes()) +} + // Отдельная функция для работы с самой игрой pub fn handle_play_state( client: Arc, // Контекст клиента ) -> Result<(), ServerError> { + client.set_entity_info(EntityInfo::new( + client + .server + .world + .entity_id_counter + .fetch_add(1, Ordering::SeqCst), + get_offline_uuid(&client.player_info().unwrap().name), // TODO: authenticated uuid + )); + thread::spawn({ let client = client.clone(); @@ -195,7 +251,7 @@ pub fn handle_play_state( }); 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)?; // idk why, but now you need to set y to 3 here + 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)?; @@ -207,12 +263,21 @@ pub fn handle_play_state( // sync_player_pos(client.clone(), 8.0, 0.0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0)?; + for player in client.server.players() { + send_player(client.clone(), player.clone())?; + send_player(player.clone(), client.clone())?; + } + thread::spawn({ let client = client.clone(); move || -> Result<(), ServerError> { while client.is_alive() { - let mut packet = client.read_any_packet()?; + let mut packet = client.read_packet(&[ + serverbound::play::SET_PLAYER_POSITION, + serverbound::play::SET_PLAYER_POSITION_AND_ROTATION, + serverbound::play::SET_PLAYER_ROTATION, + ])?; match packet.id() { serverbound::play::SET_PLAYER_POSITION => { @@ -221,7 +286,7 @@ pub fn handle_play_state( let z = packet.read_double()?; let _ = packet.read_byte()?; // flags - client.set_position((x, y, z)); + client.entity_info().set_position((x, y, z)); } serverbound::play::SET_PLAYER_POSITION_AND_ROTATION => { let x = packet.read_double()?; @@ -231,19 +296,17 @@ pub fn handle_play_state( let pitch = packet.read_float()?; let _ = packet.read_byte()?; // flags - client.set_position((x, y, z)); - client.set_rotation((yaw, pitch)); + client.entity_info().set_position((x, y, z)); + client.entity_info().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_packet_back(packet); + client.entity_info().set_rotation((yaw, pitch)); } + _ => {} } } @@ -261,7 +324,7 @@ pub fn handle_play_state( if ticks_alive % 20 == 0 { // 1 sec timer - let (x, _, z) = client.position(); + let (x, _, z) = client.entity_info().position(); let (chunk_x, chunk_z) = ((x / 16.0) as i64, (z / 16.0) as i64); let (chunk_x, chunk_z) = (chunk_x as i32, chunk_z as i32); diff --git a/src/player/context.rs b/src/player/context.rs index 74f6bdb..a481be3 100644 --- a/src/player/context.rs +++ b/src/player/context.rs @@ -29,9 +29,7 @@ pub struct ClientContext { packet_buffer: Mutex>, read_loop: AtomicBool, is_alive: AtomicBool, - position: RwLock<(f64, f64, f64)>, - velocity: RwLock<(f64, f64, f64)>, - rotation: RwLock<(f32, f32)>, + entity_info: RwLock>>, } // Реализуем сравнение через адрес @@ -63,9 +61,7 @@ impl ClientContext { packet_buffer: Mutex::new(VecDeque::new()), read_loop: AtomicBool::new(false), is_alive: AtomicBool::new(true), - position: RwLock::new((0.0, 0.0, 0.0)), - velocity: RwLock::new((0.0, 0.0, 0.0)), - rotation: RwLock::new((0.0, 0.0)), + entity_info: RwLock::new(None), } } @@ -81,6 +77,10 @@ impl ClientContext { *self.player_info.write().unwrap() = Some(player_info); } + pub fn set_entity_info(self: &Arc, entity_info: EntityInfo) { + *self.entity_info.write().unwrap() = Some(Arc::new(entity_info)); + } + pub fn set_state(self: &Arc, state: ConnectionState) -> Result<(), ServerError> { *self.state.write().unwrap() = state.clone(); @@ -107,34 +107,14 @@ impl ClientContext { self.player_info.read().unwrap().clone() } + pub fn entity_info(self: &Arc) -> Arc { + self.entity_info.read().unwrap().clone().unwrap() + } + pub fn state(self: &Arc) -> ConnectionState { self.state.read().unwrap().clone() } - pub fn set_position(self: &Arc, position: (f64, f64, f64)) { - *self.position.write().unwrap() = position; - } - - pub fn set_velocity(self: &Arc, velocity: (f64, f64, f64)) { - *self.velocity.write().unwrap() = velocity; - } - - pub fn set_rotation(self: &Arc, rotation: (f32, f32)) { - *self.rotation.write().unwrap() = rotation; - } - - pub fn position(self: &Arc) -> (f64, f64, f64) { - self.position.read().unwrap().clone() - } - - pub fn velocity(self: &Arc) -> (f64, f64, f64) { - self.velocity.read().unwrap().clone() - } - - pub fn rotation(self: &Arc) -> (f32, f32) { - self.rotation.read().unwrap().clone() - } - pub fn write_packet(self: &Arc, packet: &Packet) -> Result<(), ServerError> { let state = self.state(); let mut packet = packet.clone(); @@ -304,3 +284,47 @@ pub struct PlayerInfo { pub name: String, pub uuid: Uuid, } + +pub struct EntityInfo { + pub entity_id: i32, + pub uuid: Uuid, + position: RwLock<(f64, f64, f64)>, + velocity: RwLock<(f64, f64, f64)>, + rotation: RwLock<(f32, f32)>, +} + +impl EntityInfo { + pub fn new(entity_id: i32, uuid: Uuid) -> EntityInfo { + EntityInfo { + entity_id, + uuid, + position: RwLock::new((0.0, 0.0, 0.0)), + velocity: RwLock::new((0.0, 0.0, 0.0)), + rotation: RwLock::new((0.0, 0.0)), + } + } + + pub fn set_position(self: &Arc, position: (f64, f64, f64)) { + *self.position.write().unwrap() = position; + } + + pub fn set_velocity(self: &Arc, velocity: (f64, f64, f64)) { + *self.velocity.write().unwrap() = velocity; + } + + pub fn set_rotation(self: &Arc, rotation: (f32, f32)) { + *self.rotation.write().unwrap() = rotation; + } + + pub fn position(self: &Arc) -> (f64, f64, f64) { + self.position.read().unwrap().clone() + } + + pub fn velocity(self: &Arc) -> (f64, f64, f64) { + self.velocity.read().unwrap().clone() + } + + pub fn rotation(self: &Arc) -> (f32, f32) { + self.rotation.read().unwrap().clone() + } +}