diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 7a61b26..c0f7a6c 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -36,7 +36,7 @@ use flate2::Compression; use std::time::{Instant, Duration}; use crate::shared::Position; -pub const SUPPORTED_PROTOCOL: i32 = 316; +pub const SUPPORTED_PROTOCOL: i32 = 340; /// Helper macro for defining packets @@ -96,24 +96,27 @@ pub const CloseWindow: i32 = 0x08; pub const PluginMessageServerbound: i32 = 0x09; pub const UseEntity: i32 = 0x0a; pub const KeepAliveServerbound: i32 = 0x0b; -pub const PlayerPosition: i32 = 0x0c; -pub const PlayerPositionLook: i32 = 0x0d; -pub const PlayerLook: i32 = 0x0e; -pub const Player: i32 = 0x0f; +pub const Player: i32 = 0x0c; +pub const PlayerPosition: i32 = 0x0d; +pub const PlayerPositionLook: i32 = 0x0e; +pub const PlayerLook: i32 = 0x0f; pub const VehicleMove: i32 = 0x10; pub const SteerBoat: i32 = 0x11; -pub const ClientAbilities: i32 = 0x12; -pub const PlayerDigging: i32 = 0x13; -pub const PlayerAction: i32 = 0x14; -pub const SteerVehicle: i32 = 0x15; -pub const ResourcePackStatus: i32 = 0x16; -pub const HeldItemChange: i32 = 0x17; -pub const CreativeInventoryAction: i32 = 0x18; -pub const SetSign: i32 = 0x19; -pub const ArmSwing: i32 = 0x1a; -pub const SpectateTeleport: i32 = 0x1b; -pub const PlayerBlockPlacement: i32 = 0x1c; -pub const UseItem: i32 = 0x1d; +pub const CraftRecipeRequest: i32 = 0x12; +pub const ClientAbilities: i32 = 0x13; +pub const PlayerDigging: i32 = 0x14; +pub const PlayerAction: i32 = 0x15; +pub const SteerVehicle: i32 = 0x16; +pub const CraftingBookData: i32 = 0x17; +pub const ResourcePackStatus: i32 = 0x18; +pub const AdvancementTab: i32 = 0x19; +pub const HeldItemChange: i32 = 0x1a; +pub const CreativeInventoryAction: i32 = 0x1b; +pub const SetSign: i32 = 0x1c; +pub const ArmSwing: i32 = 0x1d; +pub const SpectateTeleport: i32 = 0x1e; +pub const PlayerBlockPlacement: i32 = 0x1f; +pub const UseItem: i32 = 0x20; pub const SpawnObject: i32 = 0x00; pub const SpawnExperienceOrb: i32 = 0x01; pub const SpawnGlobalEntity: i32 = 0x02; @@ -151,45 +154,49 @@ pub const Effect: i32 = 0x21; pub const Particle: i32 = 0x22; pub const JoinGame: i32 = 0x23; pub const Maps: i32 = 0x24; -pub const EntityMove: i32 = 0x25; -pub const EntityLookAndMove: i32 = 0x26; -pub const EntityLook: i32 = 0x27; -pub const Entity: i32 = 0x28; +pub const Entity: i32 = 0x25; +pub const EntityMove: i32 = 0x26; +pub const EntityLookAndMove: i32 = 0x27; +pub const EntityLook: i32 = 0x28; pub const VehicleTeleport: i32 = 0x29; pub const SignEditorOpen: i32 = 0x2a; -pub const PlayerAbilities: i32 = 0x2b; -pub const CombatEvent: i32 = 0x2c; -pub const PlayerInfo: i32 = 0x2d; -pub const TeleportPlayer: i32 = 0x2e; -pub const EntityUsedBed: i32 = 0x2f; -pub const EntityDestroy: i32 = 0x30; -pub const EntityRemoveEffect: i32 = 0x31; -pub const ResourcePackSend: i32 = 0x32; -pub const Respawn: i32 = 0x33; -pub const EntityHeadLook: i32 = 0x34; -pub const WorldBorder: i32 = 0x35; -pub const Camera: i32 = 0x36; -pub const SetCurrentHotbarSlot: i32 = 0x37; -pub const ScoreboardDisplay: i32 = 0x38; -pub const EntityMetadata: i32 = 0x39; -pub const EntityAttach: i32 = 0x3a; -pub const EntityVelocity: i32 = 0x3b; -pub const EntityEquipment: i32 = 0x3c; -pub const SetExperience: i32 = 0x3d; -pub const UpdateHealth: i32 = 0x3e; -pub const ScoreboardObjective: i32 = 0x3f; -pub const SetPassengers: i32 = 0x40; -pub const Teams: i32 = 0x41; -pub const UpdateScore: i32 = 0x42; -pub const SpawnPosition: i32 = 0x43; -pub const TimeUpdate: i32 = 0x44; -pub const Title: i32 = 0x45; -pub const SoundEffect: i32 = 0x46; -pub const PlayerListHeaderFooter: i32 = 0x47; -pub const CollectItem: i32 = 0x48; -pub const EntityTeleport: i32 = 0x49; -pub const EntityProperties: i32 = 0x4a; -pub const EntityEffect: i32 = 0x4b; +pub const CraftRecipeResponse: i32 = 0x2b; +pub const PlayerAbilities: i32 = 0x2c; +pub const CombatEvent: i32 = 0x2d; +pub const PlayerInfo: i32 = 0x2e; +pub const TeleportPlayer: i32 = 0x2f; +pub const EntityUsedBed: i32 = 0x30; +pub const UnlockRecipes: i32 = 0x31; +pub const EntityDestroy: i32 = 0x32; +pub const EntityRemoveEffect: i32 = 0x33; +pub const ResourcePackSend: i32 = 0x34; +pub const Respawn: i32 = 0x35; +pub const EntityHeadLook: i32 = 0x36; +pub const SelectAdvancementTab: i32 = 0x37; +pub const WorldBorder: i32 = 0x38; +pub const Camera: i32 = 0x39; +pub const SetCurrentHotbarSlot: i32 = 0x3a; +pub const ScoreboardDisplay: i32 = 0x3b; +pub const EntityMetadata: i32 = 0x3c; +pub const EntityAttach: i32 = 0x3d; +pub const EntityVelocity: i32 = 0x3e; +pub const EntityEquipment: i32 = 0x3f; +pub const SetExperience: i32 = 0x40; +pub const UpdateHealth: i32 = 0x41; +pub const ScoreboardObjective: i32 = 0x42; +pub const SetPassengers: i32 = 0x43; +pub const Teams: i32 = 0x44; +pub const UpdateScore: i32 = 0x45; +pub const SpawnPosition: i32 = 0x46; +pub const TimeUpdate: i32 = 0x47; +pub const Title: i32 = 0x48; +pub const SoundEffect: i32 = 0x49; +pub const PlayerListHeaderFooter: i32 = 0x4a; +pub const CollectItem: i32 = 0x4b; +pub const EntityTeleport: i32 = 0x4c; +pub const Advancements: i32 = 0x4d; +pub const EntityProperties: i32 = 0x4e; +pub const EntityEffect: i32 = 0x4f; pub const LoginStart: i32 = 0x00; pub const EncryptionResponse: i32 = 0x01; pub const LoginDisconnect: i32 = 0x00; diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index dc6dcca..f23e353 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -124,7 +124,7 @@ state_packets!( /// KeepAliveClientbound. If the client doesn't reply the server /// may disconnect the client. packet KeepAliveServerbound { - field id: VarInt =, + field id: i64 =, } /// PlayerPosition is used to update the player's position. packet PlayerPosition { @@ -166,6 +166,12 @@ state_packets!( field unknown: bool =, field unknown2: bool =, } + /// CraftRecipeRequest is sent when player clicks a recipe in the crafting book. + packet CraftRecipeRequest { + field window_id: u8 =, + field recipe: VarInt =, + field make_all: bool =, + } /// ClientAbilities is used to modify the players current abilities. /// Currently flying is the only one packet ClientAbilities { @@ -193,11 +199,23 @@ state_packets!( field forward: f32 =, field flags: u8 =, } + /// CraftingBookData is sent when the player interacts with the crafting book. + packet CraftingBookData { + field action: VarInt =, + field recipe_id: i32 = when(|p: &CraftingBookData| p.action.0 == 0), + field crafting_book_open: bool = when(|p: &CraftingBookData| p.action.0 == 1), + field crafting_filter: bool = when(|p: &CraftingBookData| p.action.0 == 1), + } /// ResourcePackStatus informs the server of the client's current progress /// in activating the requested resource pack packet ResourcePackStatus { field result: VarInt =, } + // TODO: Document + packet AdvancementTab { + field action: VarInt =, + field tab_id: String = when(|p: &AdvancementTab| p.action.0 == 0), + } /// HeldItemChange is sent when the player changes the currently active /// hotbar slot. packet HeldItemChange { @@ -488,7 +506,7 @@ state_packets!( /// The client should reply with the KeepAliveServerbound /// packet setting ID to the same as this one. packet KeepAliveClientbound { - field id: VarInt =, + field id: i64 =, } /// ChunkData sends or updates a single chunk on the client. If New is set /// then biome data should be sent too. @@ -522,7 +540,7 @@ state_packets!( field offset_z: f32 =, field speed: f32 =, field count: i32 =, - field data1: VarInt = when(|p: &Particle| p.particle_id == 36 || p.particle_id == 37 || p.particle_id == 38), + field data1: VarInt = when(|p: &Particle| p.particle_id == 36 || p.particle_id == 37 || p.particle_id == 38 || p.particle_id == 46), field data2: VarInt = when(|p: &Particle| p.particle_id == 36), } /// JoinGame is sent after completing the login process. This @@ -598,6 +616,11 @@ state_packets!( packet SignEditorOpen { field location: Position =, } + /// CraftRecipeResponse is a response to CraftRecipeRequest, notifies the UI. + packet CraftRecipeResponse { + field window_id: u8 =, + field recipe: VarInt =, + } /// PlayerAbilities is used to modify the players current abilities. Flying, /// creative, god mode etc. packet PlayerAbilities { @@ -636,6 +659,13 @@ state_packets!( field entity_id: VarInt =, field location: Position =, } + packet UnlockRecipes { + field action: VarInt =, + field crafting_book_open: bool =, + field filtering_craftable: bool =, + field recipe_ids: LenPrefixed =, + field recipe_ids2: LenPrefixed = when(|p: &UnlockRecipes| p.action.0 == 0), + } /// EntityDestroy destroys the entities with the ids in the provided slice. packet EntityDestroy { field entity_ids: LenPrefixed =, @@ -664,6 +694,11 @@ state_packets!( field entity_id: VarInt =, field head_yaw: i8 =, } + /// SelectAdvancementTab indicates the client should switch the advancement tab. + packet SelectAdvancementTab { + field has_id: bool =, + field tab_id: String = when(|p: &SelectAdvancementTab| p.has_id), + } /// WorldBorder configures the world's border. packet WorldBorder { field action: VarInt =, @@ -818,6 +853,12 @@ state_packets!( field pitch: i8 =, field on_ground: bool =, } + packet Advancements { + field reset_clear: bool =, + field mapping: LenPrefixed =, + field identifiers: LenPrefixed =, + field progress: LenPrefixed =, + } /// EntityProperties updates the properties for an entity. packet EntityProperties { field entity_id: VarInt =, @@ -1043,6 +1084,163 @@ impl Default for MapIcon { } } +#[derive(Debug, Default)] +pub struct Advancement { + pub id: String, + pub parent_id: Option, + pub display_data: Option, + pub criteria: LenPrefixed, + pub requirements: LenPrefixed>, +} + +impl Serializable for Advancement { + fn read_from(buf: &mut R) -> Result { + let id: String = Serializable::read_from(buf)?; + let parent_id = { + let has_parent: u8 = Serializable::read_from(buf)?; + if has_parent != 0 { + let parent_id: String = Serializable::read_from(buf)?; + Some(parent_id) + } else { + None + } + }; + + let has_display: u8 = Serializable::read_from(buf)?; + let display_data = { + if has_display != 0 { + let display_data: AdvancementDisplay = Serializable::read_from(buf)?; + Some(display_data) + } else { + None + } + }; + + let criteria: LenPrefixed = Serializable::read_from(buf)?; + let requirements: LenPrefixed> = Serializable::read_from(buf)?; + Ok(Advancement { + id, + parent_id, + display_data, + criteria, + requirements, + }) + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + self.id.write_to(buf)?; + self.parent_id.write_to(buf)?; + self.display_data.write_to(buf)?; + self.criteria.write_to(buf)?; + self.requirements.write_to(buf) + } + +} + +#[derive(Debug, Default)] +pub struct AdvancementDisplay { + pub title: String, + pub description: String, + pub icon: Option, + pub frame_type: VarInt, + pub flags: i32, + pub background_texture: Option, + pub x_coord: f32, + pub y_coord: f32, +} + +impl Serializable for AdvancementDisplay { + fn read_from(buf: &mut R) -> Result { + let title: String = Serializable::read_from(buf)?; + let description: String = Serializable::read_from(buf)?; + let icon: Option = Serializable::read_from(buf)?; + let frame_type: VarInt = Serializable::read_from(buf)?; + let flags: i32 = Serializable::read_from(buf)?; + let background_texture: Option = if flags & 1 != 0 { + Serializable::read_from(buf)? + } else { + None + }; + let x_coord: f32 = Serializable::read_from(buf)?; + let y_coord: f32 = Serializable::read_from(buf)?; + + Ok(AdvancementDisplay { + title, + description, + icon, + frame_type, + flags, + background_texture, + x_coord, + y_coord, + }) + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + self.title.write_to(buf)?; + self.description.write_to(buf)?; + self.icon.write_to(buf)?; + self.frame_type.write_to(buf)?; + self.flags.write_to(buf)?; + if self.flags & 1 != 0 { + self.background_texture.write_to(buf)?; + } + self.x_coord.write_to(buf)?; + self.y_coord.write_to(buf) + } + +} + +#[derive(Debug, Default)] +pub struct AdvancementProgress { + pub id: String, + pub criteria: LenPrefixed, +} + +impl Serializable for AdvancementProgress { + fn read_from(buf: &mut R) -> Result { + Ok(AdvancementProgress { + id: Serializable::read_from(buf)?, + criteria: Serializable::read_from(buf)?, + }) + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + self.id.write_to(buf)?; + self.criteria.write_to(buf) + } +} + +#[derive(Debug, Default)] +pub struct CriterionProgress { + pub id: String, + pub date_of_achieving: Option, +} + +impl Serializable for CriterionProgress { + fn read_from(buf: &mut R) -> Result { + let id = Serializable::read_from(buf)?; + let achieved: u8 = Serializable::read_from(buf)?; + let date_of_achieving: Option = if achieved != 0 { + Serializable::read_from(buf)? + } else { + None + }; + + Ok(CriterionProgress { + id, + date_of_achieving, + }) + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + self.id.write_to(buf)?; + self.date_of_achieving.write_to(buf) + } +} + + + #[derive(Debug, Default)] pub struct EntityProperty { pub key: String,