diff --git a/Cargo.lock b/Cargo.lock index 6afa0ec08..28c943b26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,6 +211,19 @@ dependencies = [ "serde_bytes", ] +[[package]] +name = "fastsnbt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b48b5b742a5441eb6dac3dfe2c2bd048e91fd8e98abf266b4d9f89972aa8ec" +dependencies = [ + "byteorder", + "itoa", + "nom", + "ryu", + "serde", +] + [[package]] name = "fdeflate" version = "0.3.4" @@ -333,6 +346,12 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.4" @@ -356,6 +375,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -506,6 +535,7 @@ dependencies = [ "cfb8", "crossbeam-channel", "fastnbt", + "fastsnbt", "flate2", "image", "log", diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index e2cd8fbab..7979b44f4 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -30,4 +30,6 @@ crossbeam-channel = "0.5.13" uuid = "1.10" toml = "0.8.17" fastnbt = "2.5.0" +fastsnbt = "0.2" + diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index 747ec1587..42e3588eb 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -1,7 +1,7 @@ use crate::{ protocol::{ client::{ - config::{CFinishConfig, CKnownPacks, CPluginMessage, CRegistryData, Entry}, + config::{CFinishConfig, CKnownPacks, CRegistryData, Entry}, login::{CEncryptionRequest, CLoginSuccess}, status::{CPingResponse, CStatusResponse}, }, @@ -83,10 +83,8 @@ impl ClientPacketProcessor for Client { let verify_token: [u8; 4] = rand::random(); let public_key_der = &server.public_key_der; let packet = CEncryptionRequest::new( - "".into(), - public_key_der.len() as i32, + "", public_key_der, - verify_token.len() as i32, &verify_token, false, // TODO ); @@ -101,7 +99,7 @@ impl ClientPacketProcessor for Client { dbg!("encryption response"); // should be impossible if self.uuid.is_none() || self.name.is_none() { - self.kick("UUID or Name is none".into()); + self.kick("UUID or Name is none"); return; } self.enable_encryption(server, encryption_response.shared_secret) @@ -126,14 +124,11 @@ impl ClientPacketProcessor for Client { self.connection_state = ConnectionState::Config; Server::send_brand(self); // known data packs - self.send_packet(CKnownPacks::new( - 1, - &[KnownPack { - namespace: "minecraft".to_string(), - id: "core".to_string(), - version: "1.21".to_string(), - }], - )); + self.send_packet(CKnownPacks::new(&[KnownPack { + namespace: "minecraft", + id: "core", + version: "1.21", + }])); dbg!("login achnowlaged"); } fn handle_client_information( @@ -155,11 +150,10 @@ impl ClientPacketProcessor for Client { fn handle_known_packs(&mut self, server: &mut Server, config_acknowledged: SKnownPacks) { self.send_packet(CRegistryData::new( - "0".into(), - 1, - vec![ + "dimension_type", + &[ Entry { - entry_id: "minecraft:dimension_type".into(), + entry_id: "minecraft:dimension_type", has_data: true, }, /* Entry { diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 4e928093d..feb20806a 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -222,7 +222,7 @@ impl Client { Ok(self.closed) } - pub fn kick(&mut self, reason: String) { + pub fn kick(&mut self, reason: &str) { // Todo match self.connection_state { ConnectionState::Login => { diff --git a/pumpkin/src/client/packet_decoder.rs b/pumpkin/src/client/packet_decoder.rs index 4792d1495..efb6699b3 100644 --- a/pumpkin/src/client/packet_decoder.rs +++ b/pumpkin/src/client/packet_decoder.rs @@ -56,7 +56,7 @@ impl PacketDecoder { Ok(Some(RawPacket { len: packet_len, id: packet_id, - bytebuf: ByteBuffer::new(BytesMut::from(data)), + bytebuf: ByteBuffer::new(data), })) } diff --git a/pumpkin/src/protocol/bytebuf/mod.rs b/pumpkin/src/protocol/bytebuf/mod.rs index 8ff23a98b..0bbb3a1dc 100644 --- a/pumpkin/src/protocol/bytebuf/mod.rs +++ b/pumpkin/src/protocol/bytebuf/mod.rs @@ -1,5 +1,5 @@ use core::str; -use std::io::{self, Error, ErrorKind, Read, Write}; +use std::io::{self, Error, ErrorKind}; use bytes::{Buf, BufMut, BytesMut}; @@ -180,17 +180,17 @@ impl ByteBuffer { self.put_list(v, |p, &v| p.put_var_int(v)) } - /* pub fn get_nbt(&mut self) -> Option { - match crab_nbt::NbtTag::deserialize(self.buf()) { - Ok(v) => Some(v), - Err(err) => None, + /* pub fn get_nbt(&mut self) -> Option { + match crab_nbt::NbtTag::deserialize(self.buf()) { + Ok(v) => Some(v), + Err(err) => None, + } } - } - - pub fn put_nbt(&mut self, nbt: NbtTag) { - self.buffer.put_slice(&nbt.serialize()); - } */ + pub fn put_nbt(&mut self, nbt: N) { + self.buffer.put_slice(&nbt.serialize()); + } + */ pub fn buf(&mut self) -> &mut BytesMut { &mut self.buffer } diff --git a/pumpkin/src/protocol/client/config/mod.rs b/pumpkin/src/protocol/client/config/mod.rs index a4f6799b2..aa4e72099 100644 --- a/pumpkin/src/protocol/client/config/mod.rs +++ b/pumpkin/src/protocol/client/config/mod.rs @@ -26,25 +26,25 @@ impl<'a> ClientPacket for CPluginMessage<'a> { fn write(&self, bytebuf: &mut ByteBuffer) { bytebuf.put_string(&self.channel); - bytebuf.put_slice(&self.data); + bytebuf.put_slice(self.data); } } -pub struct CConfigDisconnect { - reason: String, +pub struct CConfigDisconnect<'a> { + reason: &'a str, } -impl CConfigDisconnect { - pub fn new(reason: String) -> Self { +impl<'a> CConfigDisconnect<'a> { + pub fn new(reason: &'a str) -> Self { Self { reason } } } -impl ClientPacket for CConfigDisconnect { +impl<'a> ClientPacket for CConfigDisconnect<'a> { const PACKET_ID: crate::protocol::VarInt = 0x02; fn write(&self, bytebuf: &mut ByteBuffer) { - bytebuf.put_string(&self.reason); + bytebuf.put_string(self.reason); } } @@ -69,13 +69,12 @@ impl ClientPacket for CFinishConfig { } pub struct CKnownPacks<'a> { - count: VarInt, - known_packs: &'a [KnownPack], + known_packs: &'a [KnownPack<'a>], } impl<'a> CKnownPacks<'a> { - pub fn new(count: VarInt, known_packs: &'a [KnownPack]) -> Self { - Self { count, known_packs } + pub fn new(known_packs: &'a [KnownPack]) -> Self { + Self { known_packs } } } @@ -83,47 +82,43 @@ impl<'a> ClientPacket for CKnownPacks<'a> { const PACKET_ID: VarInt = 0x0E; fn write(&self, bytebuf: &mut ByteBuffer) { - // bytebuf.write_var_int(self.count); - bytebuf.put_list::(&self.known_packs, |p, v| { - p.put_string(&v.namespace); - p.put_string(&v.id); - p.put_string(&v.version); + bytebuf.put_list::(self.known_packs, |p, v| { + p.put_string(v.namespace); + p.put_string(v.id); + p.put_string(v.version); }); } } -pub struct CRegistryData { - registry_id: String, - entry_count: VarInt, - entries: Vec, +pub struct CRegistryData<'a> { + registry_id: &'a str, + entries: &'a [Entry<'a>], } -impl CRegistryData { - pub fn new(registry_id: String, entry_count: VarInt, entries: Vec) -> Self { +impl<'a> CRegistryData<'a> { + pub fn new(registry_id: &'a str, entries: &'a [Entry]) -> Self { Self { registry_id, - entry_count, entries, } } } -pub struct Entry { - pub entry_id: String, +pub struct Entry<'a> { + pub entry_id: &'a str, pub has_data: bool, // data provided by registry::write_codec } -impl ClientPacket for CRegistryData { +impl<'a> ClientPacket for CRegistryData<'a> { const PACKET_ID: VarInt = 0x07; fn write(&self, bytebuf: &mut ByteBuffer) { - bytebuf.put_string(&self.registry_id); - bytebuf.put_var_int(self.entry_count); - bytebuf.put_list::(&self.entries, |p, v| { - p.put_string(&v.entry_id); + bytebuf.put_string(self.registry_id); + bytebuf.put_list::(self.entries, |p, v| { + p.put_string(v.entry_id); p.put_bool(v.has_data); - registry::write_single_dimension(p, -64, 320); + registry::write_codec(p, -64, 384); }); } } diff --git a/pumpkin/src/protocol/client/login/mod.rs b/pumpkin/src/protocol/client/login/mod.rs index 1a80fd819..cbccab05a 100644 --- a/pumpkin/src/protocol/client/login/mod.rs +++ b/pumpkin/src/protocol/client/login/mod.rs @@ -1,16 +1,16 @@ use crate::protocol::{bytebuf::ByteBuffer, ClientPacket, VarInt}; -pub struct CLoginDisconnect { - reason: String, +pub struct CLoginDisconnect<'a> { + reason: &'a str, } -impl CLoginDisconnect { - pub fn new(reason: String) -> Self { +impl<'a> CLoginDisconnect<'a> { + pub fn new(reason: &'a str) -> Self { Self { reason } } } -impl ClientPacket for CLoginDisconnect { +impl<'a> ClientPacket for CLoginDisconnect<'a> { const PACKET_ID: VarInt = 0x00; fn write(&self, bytebuf: &mut ByteBuffer) { @@ -19,28 +19,22 @@ impl ClientPacket for CLoginDisconnect { } pub struct CEncryptionRequest<'a> { - server_id: String, // 20 - public_key_length: VarInt, + server_id: &'a str, // 20 public_key: &'a [u8], - verify_token_length: VarInt, verify_token: &'a [u8], should_authenticate: bool, } impl<'a> CEncryptionRequest<'a> { pub fn new( - server_id: String, - public_key_length: VarInt, + server_id: &'a str, public_key: &'a [u8], - verify_token_length: VarInt, verify_token: &'a [u8], should_authenticate: bool, ) -> Self { Self { server_id, - public_key_length, public_key, - verify_token_length, verify_token, should_authenticate, } @@ -51,10 +45,10 @@ impl<'a> ClientPacket for CEncryptionRequest<'a> { const PACKET_ID: VarInt = 0x01; fn write(&self, bytebuf: &mut ByteBuffer) { - bytebuf.put_string(self.server_id.as_str()); - bytebuf.put_var_int(self.public_key_length); + bytebuf.put_string(self.server_id); + bytebuf.put_var_int(self.public_key.len() as VarInt); bytebuf.put_slice(self.public_key); - bytebuf.put_var_int(self.verify_token_length); + bytebuf.put_var_int(self.verify_token.len() as VarInt); bytebuf.put_slice(self.verify_token); bytebuf.put_bool(self.should_authenticate); } diff --git a/pumpkin/src/protocol/mod.rs b/pumpkin/src/protocol/mod.rs index 4cb711be2..f3c959dcb 100644 --- a/pumpkin/src/protocol/mod.rs +++ b/pumpkin/src/protocol/mod.rs @@ -124,7 +124,7 @@ pub trait ClientPacket { fn write(&self, bytebuf: &mut ByteBuffer); } -#[derive(Serialize, Deserialize)] +#[derive(Serialize)] pub struct StatusResponse { pub version: Version, pub players: Players, @@ -132,27 +132,27 @@ pub struct StatusResponse { pub favicon: String, // data:image/png;base64, // Players, favicon ... } -#[derive(Serialize, Deserialize)] +#[derive(Serialize)] pub struct Version { pub name: String, pub protocol: u32, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize)] pub struct Players { pub max: u32, pub online: u32, pub sample: Vec, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize)] pub struct Sample { pub name: String, pub id: String, // uuid } -pub struct KnownPack { - pub namespace: String, - pub id: String, - pub version: String, +pub struct KnownPack<'a> { + pub namespace: &'a str, + pub id: &'a str, + pub version: &'a str, } diff --git a/pumpkin/src/protocol/registry/biomes.rs b/pumpkin/src/protocol/registry/biomes.rs index fa1fbd79a..351da43e0 100644 --- a/pumpkin/src/protocol/registry/biomes.rs +++ b/pumpkin/src/protocol/registry/biomes.rs @@ -1,6 +1,6 @@ use crate::protocol::VarInt; -use super::CodecItem; +use super::RegistryValue; use serde::Serialize; #[derive(Debug, Clone, Serialize)] @@ -72,7 +72,7 @@ struct Music { } // 1.20.6 default https://gist.github.com/WinX64/ab8c7a8df797c273b32d3a3b66522906 -pub(super) fn all() -> Vec> { +pub(super) fn all() -> Vec> { let biome = Biome { has_precipitation: false, temperature: 1.0, @@ -99,7 +99,7 @@ pub(super) fn all() -> Vec> { }, }; - vec![CodecItem { + vec![RegistryValue { name: "minecraft:plains".into(), id: 0, element: biome, diff --git a/pumpkin/src/protocol/registry/chat_type.rs b/pumpkin/src/protocol/registry/chat_type.rs index 66bc22792..27da25a17 100644 --- a/pumpkin/src/protocol/registry/chat_type.rs +++ b/pumpkin/src/protocol/registry/chat_type.rs @@ -1,4 +1,4 @@ -use super::CodecItem; +use super::RegistryValue; use serde::Serialize; #[derive(Debug, Clone, Serialize)] @@ -13,8 +13,8 @@ struct Decoration { parameters: Vec, } -pub(super) fn all() -> Vec> { - vec![CodecItem { +pub(super) fn all() -> Vec> { + vec![RegistryValue { name: "minecraft:chat".into(), id: 0, element: ChatType { diff --git a/pumpkin/src/protocol/registry/damage_type.rs b/pumpkin/src/protocol/registry/damage_type.rs index 669bc41de..10e54c06a 100644 --- a/pumpkin/src/protocol/registry/damage_type.rs +++ b/pumpkin/src/protocol/registry/damage_type.rs @@ -1,6 +1,7 @@ -use super::CodecItem; use serde::Serialize; +use super::RegistryValue; + #[derive(Debug, Clone, Serialize)] pub struct DamageType { message_id: String, @@ -60,10 +61,10 @@ const NAMES: &[&str] = &[ "wither_skull", ]; -pub(super) fn all() -> Vec> { +pub(super) fn all() -> Vec> { let mut items: Vec<_> = NAMES .iter() - .map(|name| CodecItem { + .map(|name| RegistryValue { name: (*name).into(), id: 0, element: DamageType { diff --git a/pumpkin/src/protocol/registry/dimensions.rs b/pumpkin/src/protocol/registry/dimensions.rs index fd9782c04..9e539028f 100644 --- a/pumpkin/src/protocol/registry/dimensions.rs +++ b/pumpkin/src/protocol/registry/dimensions.rs @@ -1,48 +1,83 @@ -use serde::Serialize; +use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Dimension { - #[serde(skip_serializing_if = "Option::is_none")] - fixed_time: Option, - has_skylight: bool, - has_ceiling: bool, - ultrawarm: bool, - natural: bool, - coordinate_scale: f64, + pub ambient_light: f32, bed_works: bool, - respawn_anchor_works: bool, - min_y: i32, - height: i32, - logical_height: i32, - infiniburn: String, - effects: String, - ambient_light: f32, - piglin_safe: bool, + coordinate_scale: f64, + effects: DimensionEffects, + #[serde(skip_serializing_if = "Option::is_none")] + fixed_time: Option, + has_ceiling: bool, has_raids: bool, - monster_spawn_light_level: i32, + has_skylight: bool, + height: i32, + infiniburn: String, + logical_height: i32, + min_y: i32, monster_spawn_block_light_limit: i32, + monster_spawn_light_level: MonsterSpawnLightLevel, + natural: bool, + piglin_safe: bool, + respawn_anchor_works: bool, + ultrawarm: bool, } -pub fn overworld(world_min_y: i32, world_height: u32) -> Dimension { - Dimension { - piglin_safe: false, - natural: true, - ambient_light: 0.0, - fixed_time: Some(6000.0), - infiniburn: "#minecraft:infiniburn_overworld".into(), - respawn_anchor_works: false, - has_skylight: true, - bed_works: true, - effects: "minecraft:overworld".into(), - has_raids: false, - logical_height: 384, - coordinate_scale: 1.0, - ultrawarm: false, - has_ceiling: false, - min_y: world_min_y, - height: (world_height as i32 + 15) / 16 * 16, +#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, Debug)] +pub enum DimensionEffects { + #[serde(rename = "minecraft:overworld")] + #[default] + Overworld, + #[serde(rename = "minecraft:the_nether")] + TheNether, + #[serde(rename = "minecraft:the_end")] + TheEnd, +} - monster_spawn_light_level: 7, - monster_spawn_block_light_limit: 7, +impl Default for Dimension { + fn default() -> Self { + Self { + ambient_light: 0.0, + bed_works: true, + coordinate_scale: 1.0, + effects: DimensionEffects::default(), + fixed_time: None, + has_ceiling: false, + has_raids: true, + has_skylight: true, + height: 384, + infiniburn: "#minecraft:infiniburn_overworld".into(), + logical_height: 384, + min_y: -64, + monster_spawn_block_light_limit: 0, + monster_spawn_light_level: MonsterSpawnLightLevel::Int(7), + natural: true, + piglin_safe: false, + respawn_anchor_works: false, + ultrawarm: false, + } + } +} + +#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum MonsterSpawnLightLevel { + Int(i32), + Tagged(MonsterSpawnLightLevelTagged), +} + +#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)] +#[serde(tag = "type", content = "value")] +pub enum MonsterSpawnLightLevelTagged { + #[serde(rename = "minecraft:uniform")] + Uniform { + min_inclusive: i32, + max_inclusive: i32, + }, +} + +impl From for MonsterSpawnLightLevel { + fn from(value: i32) -> Self { + Self::Int(value) } } diff --git a/pumpkin/src/protocol/registry/mod.rs b/pumpkin/src/protocol/registry/mod.rs index d30ad43aa..a933dbb10 100644 --- a/pumpkin/src/protocol/registry/mod.rs +++ b/pumpkin/src/protocol/registry/mod.rs @@ -1,3 +1,4 @@ +use dimensions::Dimension; use serde::Serialize; use super::bytebuf::ByteBuffer; @@ -11,45 +12,55 @@ mod dimensions; struct LoginInfo { #[serde(rename = "minecraft:dimension_type")] dimensions: Codec, - #[serde(rename = "minecraft:worldgen/biome")] + /* #[serde(rename = "minecraft:worldgen/biome")] biomes: Codec, #[serde(rename = "minecraft:chat_type")] chat: Codec, #[serde(rename = "minecraft:damage_type")] - damage: Codec, + damage: Codec, */ } #[derive(Debug, Clone, Serialize)] struct Codec { #[serde(rename = "type")] ty: String, - value: Vec>, + value: Vec>, } #[derive(Debug, Clone, Serialize)] -struct CodecItem { +struct RegistryValue { name: String, id: i32, element: T, } pub fn write_single_dimension(out: &mut ByteBuffer, world_min_y: i32, world_height: u32) { - let dimension = dimensions::overworld(world_min_y, world_height); - // out.put_nbt(nbt!(dimension)) + let dimension = Dimension::default(); + let nbt = Codec { + ty: "minecraft:dimension_type".into(), + value: vec![RegistryValue { + name: "minecraft:overworld".into(), + id: 0, + element: dimension, + }], + }; + let val = &fastsnbt::to_string(&nbt).unwrap(); + dbg!(val); + out.put_string(val); } pub fn write_codec(out: &mut ByteBuffer, world_min_y: i32, world_height: u32) { - let dimension = dimensions::overworld(world_min_y, world_height); + let dimension = Dimension::default(); let info = LoginInfo { dimensions: Codec { ty: "minecraft:dimension_type".into(), - value: vec![CodecItem { + value: vec![RegistryValue { name: "minecraft:overworld".into(), id: 0, element: dimension, }], }, - biomes: Codec { + /* biomes: Codec { ty: "minecraft:worldgen/biome".into(), value: biomes::all(), }, @@ -60,11 +71,13 @@ pub fn write_codec(out: &mut ByteBuffer, world_min_y: i32, world_height: u32) { damage: Codec { ty: "minecraft:damage_type".into(), value: damage_type::all(), - }, + }, */ }; + let val = &fastsnbt::to_string(&info).unwrap(); + dbg!(val); // Dimension codec - // out.put_slice(&nbt::to_nbt("", &info).unwrap().serialize()); + out.put_string(val); // Current dimension type (key in dimension codec) out.put_string("minecraft:overworld"); // Current world diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs index 3ff33afb1..8765e055e 100644 --- a/pumpkin/src/server.rs +++ b/pumpkin/src/server.rs @@ -16,10 +16,7 @@ use crate::{ Entity, EntityId, }, protocol::{ - client::{ - config::{CFinishConfig, CKnownPacks, CPluginMessage, CRegistryData, Entry}, - play::CLogin, - }, + client::{config::CPluginMessage, play::CLogin}, Players, Sample, StatusResponse, VarInt, VarInt32, Version, }, world::World, diff --git a/pumpkin/src/world.rs b/pumpkin/src/world.rs index 2f8927440..2fabe1134 100644 --- a/pumpkin/src/world.rs +++ b/pumpkin/src/world.rs @@ -4,6 +4,12 @@ pub struct World { pub players: Vec, } +impl Default for World { + fn default() -> Self { + Self::new() + } +} + impl World { pub fn new() -> Self { Self {