1.12.2 protocol support (340) (#40)
* Add new 1.12.2 packets and shift IDs CraftRecipeResponse AdvancementTab SelectAdvancementTab Advancements CraftingRecipeRequest UnlockRecipes CraftingBookData * Fix unlock recipes packet, add length-prefixed arrays https://wiki.vg/index.php?title=Protocol&oldid=14204#Unlock_Recipes * Update resources to 1.12.2 * Handle NBTTag metadata (value 13), parsed as nbt::NamedTag https://wiki.vg/index.php?title=Entity_metadata&oldid=14048#Entity_Metadata_Format https://github.com/iceiix/steven/pull/40#issuecomment-443454757 * Fix entity packet IDs, 0x25 now is Entity https://wiki.vg/index.php?title=Protocol&oldid=14204#Entity * Add NBT long array (type 12) support https://wiki.vg/NBT#Specification * Entity metadata type is now a VarInt, not u8: https://wiki.vg/index.php?title=Entity_metadata&oldid=14048#Entity_Metadata_Format * Keep alives changed to longs, no longer VarInts https://wiki.vg/index.php?title=Protocol&oldid=14204#Keep_Alive_.28serverbound.29 * Parse CraftRecipeResponse (0x2b) * Add structs for advancements data * Implement Serializable trait for Advancement and AdvancementDisplay * Implement advancement progress parsing; advancement packet works * Particle packet adds fallingdust (46) with length 1 https://wiki.vg/index.php?title=Protocol&oldid=14204#Particle_2
This commit is contained in:
parent
400cf2c18b
commit
b554dbb98b
|
@ -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;
|
||||
|
|
|
@ -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<VarInt, VarInt> =,
|
||||
field recipe_ids2: LenPrefixed<VarInt, VarInt> = when(|p: &UnlockRecipes| p.action.0 == 0),
|
||||
}
|
||||
/// EntityDestroy destroys the entities with the ids in the provided slice.
|
||||
packet EntityDestroy {
|
||||
field entity_ids: LenPrefixed<VarInt, VarInt> =,
|
||||
|
@ -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<VarInt, packet::Advancement> =,
|
||||
field identifiers: LenPrefixed<VarInt, String> =,
|
||||
field progress: LenPrefixed<VarInt, packet::AdvancementProgress> =,
|
||||
}
|
||||
/// 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<String>,
|
||||
pub display_data: Option<AdvancementDisplay>,
|
||||
pub criteria: LenPrefixed<VarInt, String>,
|
||||
pub requirements: LenPrefixed<VarInt, LenPrefixed<VarInt, String>>,
|
||||
}
|
||||
|
||||
impl Serializable for Advancement {
|
||||
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
|
||||
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<VarInt, String> = Serializable::read_from(buf)?;
|
||||
let requirements: LenPrefixed<VarInt, LenPrefixed<VarInt, String>> = Serializable::read_from(buf)?;
|
||||
Ok(Advancement {
|
||||
id,
|
||||
parent_id,
|
||||
display_data,
|
||||
criteria,
|
||||
requirements,
|
||||
})
|
||||
}
|
||||
|
||||
fn write_to<W: io::Write>(&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<crate::item::Stack>,
|
||||
pub frame_type: VarInt,
|
||||
pub flags: i32,
|
||||
pub background_texture: Option<String>,
|
||||
pub x_coord: f32,
|
||||
pub y_coord: f32,
|
||||
}
|
||||
|
||||
impl Serializable for AdvancementDisplay {
|
||||
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
|
||||
let title: String = Serializable::read_from(buf)?;
|
||||
let description: String = Serializable::read_from(buf)?;
|
||||
let icon: Option<crate::item::Stack> = Serializable::read_from(buf)?;
|
||||
let frame_type: VarInt = Serializable::read_from(buf)?;
|
||||
let flags: i32 = Serializable::read_from(buf)?;
|
||||
let background_texture: Option<String> = 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<W: io::Write>(&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<VarInt, CriterionProgress>,
|
||||
}
|
||||
|
||||
impl Serializable for AdvancementProgress {
|
||||
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
|
||||
Ok(AdvancementProgress {
|
||||
id: Serializable::read_from(buf)?,
|
||||
criteria: Serializable::read_from(buf)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn write_to<W: io::Write>(&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<i64>,
|
||||
}
|
||||
|
||||
impl Serializable for CriterionProgress {
|
||||
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
|
||||
let id = Serializable::read_from(buf)?;
|
||||
let achieved: u8 = Serializable::read_from(buf)?;
|
||||
let date_of_achieving: Option<i64> = if achieved != 0 {
|
||||
Serializable::read_from(buf)?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(CriterionProgress {
|
||||
id,
|
||||
date_of_achieving,
|
||||
})
|
||||
}
|
||||
|
||||
fn write_to<W: io::Write>(&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,
|
||||
|
|
Loading…
Reference in New Issue