1.7.10 (5) multiprotocol support (#64)
Adds 1.7.10 protocol version 5 support, a major update with significant changes. Expands https://github.com/iceiix/steven/issues/18 Enhance protocol support * Add v1_7_10 protocol packet structures and IDs * EncryptionRequest/Response i16 variant in login protocol * 1.7.10 slot NBT data parsing * Support both 1.7/1.8+ item::Stack in read_from, using if protocol_verson * 1.7.10 chunk format support, ChunkDataBulk_17 and ChunkData_17 * Extract dirty_chunks_by_bitmask from load_chunks17/18/19 * Implement keepalive i32 handler * Send PlayerPositionLook_HeadY * Send PlayerBlockPlacement_u8_Item_u8y * Handle JoinGame_i8_NoDebug * Handle SpawnPlayer_i32_HeldItem_String * BlockChange_u8, MultiBlockChange_i16, UpdateBlockEntity_Data, EntityMove_i8_i32_NoGround, EntityLook_i32_NoGround, EntityLookAndMove_i8_i32_NoGround * UpdateSign_u16, PlayerInfo_String, EntityDestroy_u8, EntityTeleport_i32_i32_NoGround * Send feet_y = head_y - 1.62, fixes Illegal stance https://wiki.vg/index.php?title=Protocol&oldid=6003#Player_Position > Updates the players XYZ position on the server. If HeadY - FeetY is less than 0.1 or greater than 1.65, the stance is illegal and the client will be kicked with the message “Illegal Stance”. > Absolute feet position, normally HeadY - 1.62. Used to modify the players bounding box when going up stairs, crouching, etc… * Set on_ground = true in entity teleport, fixes bouncing * Implement block change, fix metadata/id packing, bounce _u8 through on_block_change * Implement on_multi_block_change_u16, used with explosions
This commit is contained in:
parent
44dc7eedd5
commit
2b641c9af3
28
src/item.rs
28
src/item.rs
|
@ -43,11 +43,32 @@ impl Serializable for Option<Stack> {
|
|||
if id == -1 {
|
||||
return Ok(None);
|
||||
}
|
||||
let count = buf.read_u8()? as isize;
|
||||
let damage = buf.read_i16::<BigEndian>()? as isize;
|
||||
|
||||
let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION };
|
||||
|
||||
let tag: Option<nbt::NamedTag> = if protocol_version >= 47 {
|
||||
Serializable::read_from(buf)?
|
||||
} else {
|
||||
// 1.7 uses a different slot data format described on https://wiki.vg/index.php?title=Slot_Data&diff=6056&oldid=4753
|
||||
let tag_size = buf.read_i16::<BigEndian>()?;
|
||||
if tag_size != -1 {
|
||||
for _ in 0..tag_size {
|
||||
let _ = buf.read_u8()?;
|
||||
}
|
||||
// TODO: decompress zlib NBT for 1.7
|
||||
None
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Some(Stack {
|
||||
id: id as isize,
|
||||
count: buf.read_u8()? as isize,
|
||||
damage: buf.read_i16::<BigEndian>()? as isize,
|
||||
tag: Serializable::read_from(buf)?,
|
||||
count,
|
||||
damage,
|
||||
tag,
|
||||
}))
|
||||
}
|
||||
fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), protocol::Error> {
|
||||
|
@ -56,6 +77,7 @@ impl Serializable for Option<Stack> {
|
|||
buf.write_i16::<BigEndian>(val.id as i16)?;
|
||||
buf.write_u8(val.count as u8)?;
|
||||
buf.write_i16::<BigEndian>(val.damage as i16)?;
|
||||
// TODO: compress zlib NBT if 1.7
|
||||
val.tag.write_to(buf)?;
|
||||
}
|
||||
None => buf.write_i16::<BigEndian>(-1)?,
|
||||
|
|
|
@ -37,7 +37,7 @@ use flate2::Compression;
|
|||
use std::time::{Instant, Duration};
|
||||
use crate::shared::Position;
|
||||
|
||||
pub const SUPPORTED_PROTOCOLS: [i32; 8] = [340, 316, 315, 210, 109, 107, 74, 47];
|
||||
pub const SUPPORTED_PROTOCOLS: [i32; 9] = [340, 316, 315, 210, 109, 107, 74, 47, 5];
|
||||
|
||||
// TODO: switch to using thread_local storage?, see https://doc.rust-lang.org/std/macro.thread_local.html
|
||||
pub static mut CURRENT_PROTOCOL_VERSION: i32 = SUPPORTED_PROTOCOLS[0];
|
||||
|
@ -553,6 +553,16 @@ impl <L: Lengthable> fmt::Debug for LenPrefixedBytes<L> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Lengthable for u8 {
|
||||
fn into(self) -> usize {
|
||||
self as usize
|
||||
}
|
||||
|
||||
fn from(u: usize) -> u8 {
|
||||
u as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl Lengthable for i16 {
|
||||
fn into(self) -> usize {
|
||||
self as usize
|
||||
|
|
|
@ -66,6 +66,9 @@ state_packets!(
|
|||
field has_target: bool =,
|
||||
field target: Option<Position> = when(|p: &TabComplete_NoAssume| p.has_target),
|
||||
}
|
||||
packet TabComplete_NoAssume_NoTarget {
|
||||
field text: String =,
|
||||
}
|
||||
/// ChatMessage is sent by the client when it sends a chat message or
|
||||
/// executes a command (prefixed by '/').
|
||||
packet ChatMessage {
|
||||
|
@ -75,6 +78,9 @@ state_packets!(
|
|||
packet ClientStatus {
|
||||
field action_id: VarInt =,
|
||||
}
|
||||
packet ClientStatus_u8 {
|
||||
field action_id: u8=,
|
||||
}
|
||||
/// ClientSettings is sent by the client to update its current settings.
|
||||
packet ClientSettings {
|
||||
field locale: String =,
|
||||
|
@ -99,6 +105,14 @@ state_packets!(
|
|||
field chat_colors: bool =,
|
||||
field displayed_skin_parts: u8 =,
|
||||
}
|
||||
packet ClientSettings_u8_Handsfree_Difficulty {
|
||||
field locale: String =,
|
||||
field view_distance: u8 =,
|
||||
field chat_mode: u8 =,
|
||||
field chat_colors: bool =,
|
||||
field difficulty: u8 =,
|
||||
field displayed_skin_parts: u8 =,
|
||||
}
|
||||
/// ConfirmTransactionServerbound is a reply to ConfirmTransaction.
|
||||
packet ConfirmTransactionServerbound {
|
||||
field id: u8 =,
|
||||
|
@ -138,6 +152,10 @@ state_packets!(
|
|||
field channel: String =,
|
||||
field data: Vec<u8> =,
|
||||
}
|
||||
packet PluginMessageServerbound_i16 {
|
||||
field channel: String =,
|
||||
field data: LenPrefixedBytes<i16> =,
|
||||
}
|
||||
/// UseEntity is sent when the user interacts (right clicks) or attacks
|
||||
/// (left clicks) an entity.
|
||||
packet UseEntity {
|
||||
|
@ -155,6 +173,10 @@ state_packets!(
|
|||
field target_y: f32 = when(|p: &UseEntity_Handsfree| p.ty.0 == 2),
|
||||
field target_z: f32 = when(|p: &UseEntity_Handsfree| p.ty.0 == 2),
|
||||
}
|
||||
packet UseEntity_Handsfree_i32 {
|
||||
field target_id: i32 =,
|
||||
field ty: u8 =,
|
||||
}
|
||||
/// KeepAliveServerbound is sent by a client as a response to a
|
||||
/// KeepAliveClientbound. If the client doesn't reply the server
|
||||
/// may disconnect the client.
|
||||
|
@ -164,6 +186,9 @@ state_packets!(
|
|||
packet KeepAliveServerbound_VarInt {
|
||||
field id: VarInt =,
|
||||
}
|
||||
packet KeepAliveServerbound_i32 {
|
||||
field id: i32 =,
|
||||
}
|
||||
/// PlayerPosition is used to update the player's position.
|
||||
packet PlayerPosition {
|
||||
field x: f64 =,
|
||||
|
@ -171,6 +196,13 @@ state_packets!(
|
|||
field z: f64 =,
|
||||
field on_ground: bool =,
|
||||
}
|
||||
packet PlayerPosition_HeadY {
|
||||
field x: f64 =,
|
||||
field feet_y: f64 =,
|
||||
field head_y: f64 =,
|
||||
field z: f64 =,
|
||||
field on_ground: bool =,
|
||||
}
|
||||
/// PlayerPositionLook is a combination of PlayerPosition and
|
||||
/// PlayerLook.
|
||||
packet PlayerPositionLook {
|
||||
|
@ -181,6 +213,15 @@ state_packets!(
|
|||
field pitch: f32 =,
|
||||
field on_ground: bool =,
|
||||
}
|
||||
packet PlayerPositionLook_HeadY {
|
||||
field x: f64 =,
|
||||
field feet_y: f64 =,
|
||||
field head_y: f64 =,
|
||||
field z: f64 =,
|
||||
field yaw: f32 =,
|
||||
field pitch: f32 =,
|
||||
field on_ground: bool =,
|
||||
}
|
||||
/// PlayerLook is used to update the player's rotation.
|
||||
packet PlayerLook {
|
||||
field yaw: f32 =,
|
||||
|
@ -229,12 +270,24 @@ state_packets!(
|
|||
field location: Position =,
|
||||
field face: u8 =,
|
||||
}
|
||||
packet PlayerDigging_u8_u8y {
|
||||
field status: u8 =,
|
||||
field x: i32 =,
|
||||
field y: u8 =,
|
||||
field z: i32 =,
|
||||
field face: u8 =,
|
||||
}
|
||||
/// PlayerAction is sent when a player preforms various actions.
|
||||
packet PlayerAction {
|
||||
field entity_id: VarInt =,
|
||||
field action_id: VarInt =,
|
||||
field jump_boost: VarInt =,
|
||||
}
|
||||
packet PlayerAction_i32 {
|
||||
field entity_id: i32 =,
|
||||
field action_id: i8 =,
|
||||
field jump_boost: i32 =,
|
||||
}
|
||||
/// SteerVehicle is sent by the client when steers or preforms an action
|
||||
/// on a vehicle.
|
||||
packet SteerVehicle {
|
||||
|
@ -242,6 +295,12 @@ state_packets!(
|
|||
field forward: f32 =,
|
||||
field flags: u8 =,
|
||||
}
|
||||
packet SteerVehicle_jump_unmount {
|
||||
field sideways: f32 =,
|
||||
field forward: f32 =,
|
||||
field jump: bool =,
|
||||
field unmount: bool =,
|
||||
}
|
||||
/// CraftingBookData is sent when the player interacts with the crafting book.
|
||||
packet CraftingBookData {
|
||||
field action: VarInt =,
|
||||
|
@ -282,6 +341,15 @@ state_packets!(
|
|||
field line3: String =,
|
||||
field line4: String =,
|
||||
}
|
||||
packet SetSign_i16y {
|
||||
field x: i32 =,
|
||||
field y: i16 =,
|
||||
field z: i32 =,
|
||||
field line1: String =,
|
||||
field line2: String =,
|
||||
field line3: String =,
|
||||
field line4: String =,
|
||||
}
|
||||
/// ArmSwing is sent by the client when the player left clicks (to swing their
|
||||
/// arm).
|
||||
packet ArmSwing {
|
||||
|
@ -290,6 +358,10 @@ state_packets!(
|
|||
packet ArmSwing_Handsfree {
|
||||
field empty: () =,
|
||||
}
|
||||
packet ArmSwing_Handsfree_ID {
|
||||
field entity_id: i32 =,
|
||||
field animation: u8 =,
|
||||
}
|
||||
/// SpectateTeleport is sent by clients in spectator mode to teleport to a player.
|
||||
packet SpectateTeleport {
|
||||
field target: UUID =,
|
||||
|
@ -319,6 +391,16 @@ state_packets!(
|
|||
field cursor_y: u8 =,
|
||||
field cursor_z: u8 =,
|
||||
}
|
||||
packet PlayerBlockPlacement_u8_Item_u8y {
|
||||
field x: i32 =,
|
||||
field y: u8 =,
|
||||
field z: i32 =,
|
||||
field face: u8 =,
|
||||
field hand: Option<item::Stack> =,
|
||||
field cursor_x: u8 =,
|
||||
field cursor_y: u8 =,
|
||||
field cursor_z: u8 =,
|
||||
}
|
||||
|
||||
/// UseItem is sent when the client tries to use an item.
|
||||
packet UseItem {
|
||||
|
@ -478,6 +560,14 @@ state_packets!(
|
|||
field location: Position =,
|
||||
field direction: u8 =,
|
||||
}
|
||||
packet SpawnPainting_NoUUID_i32 {
|
||||
field entity_id: VarInt =,
|
||||
field title: String =,
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
field direction: i32 =,
|
||||
}
|
||||
/// SpawnPlayer is used to spawn a player when they are in range of the client.
|
||||
/// This packet alone isn't enough to display the player as the skin and username
|
||||
/// information is in the player information packet.
|
||||
|
@ -512,6 +602,20 @@ state_packets!(
|
|||
field current_item: u16 =,
|
||||
field metadata: types::Metadata =,
|
||||
}
|
||||
packet SpawnPlayer_i32_HeldItem_String {
|
||||
field entity_id: VarInt =,
|
||||
field uuid: String =,
|
||||
field name: String =,
|
||||
field properties: LenPrefixed<VarInt, packet::SpawnProperty> =,
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
field yaw: i8 =,
|
||||
field pitch: i8 =,
|
||||
field current_item: u16 =,
|
||||
field metadata: types::Metadata =,
|
||||
}
|
||||
|
||||
/// Animation is sent by the server to play an animation on a specific entity.
|
||||
packet Animation {
|
||||
field entity_id: VarInt =,
|
||||
|
@ -528,6 +632,13 @@ state_packets!(
|
|||
field location: Position =,
|
||||
field stage: i8 =,
|
||||
}
|
||||
packet BlockBreakAnimation_i32 {
|
||||
field entity_id: VarInt =,
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
field stage: i8 =,
|
||||
}
|
||||
/// UpdateBlockEntity updates the nbt tag of a block entity in the
|
||||
/// world.
|
||||
packet UpdateBlockEntity {
|
||||
|
@ -535,6 +646,14 @@ state_packets!(
|
|||
field action: u8 =,
|
||||
field nbt: Option<nbt::NamedTag> =,
|
||||
}
|
||||
packet UpdateBlockEntity_Data {
|
||||
field x: i32 =,
|
||||
field y: i16 =,
|
||||
field z: i32 =,
|
||||
field action: u8 =,
|
||||
field data_length: i16 =,
|
||||
field gzipped_nbt: Vec<u8> =,
|
||||
}
|
||||
/// BlockAction triggers different actions depending on the target block.
|
||||
packet BlockAction {
|
||||
field location: Position =,
|
||||
|
@ -542,11 +661,26 @@ state_packets!(
|
|||
field byte2: u8 =,
|
||||
field block_type: VarInt =,
|
||||
}
|
||||
packet BlockAction_u16 {
|
||||
field x: i32 =,
|
||||
field y: u16 =,
|
||||
field z: i32 =,
|
||||
field byte1: u8 =,
|
||||
field byte2: u8 =,
|
||||
field block_type: VarInt =,
|
||||
}
|
||||
/// BlockChange is used to update a single block on the client.
|
||||
packet BlockChange {
|
||||
packet BlockChange_VarInt {
|
||||
field location: Position =,
|
||||
field block_id: VarInt =,
|
||||
}
|
||||
packet BlockChange_u8 {
|
||||
field x: i32 =,
|
||||
field y: u8 =,
|
||||
field z: i32 =,
|
||||
field block_id: VarInt =,
|
||||
field block_metadata: u8 =,
|
||||
}
|
||||
/// BossBar displays and/or changes a boss bar that is displayed on the
|
||||
/// top of the client's screen. This is normally used for bosses such as
|
||||
/// the ender dragon or the wither.
|
||||
|
@ -578,12 +712,22 @@ state_packets!(
|
|||
/// 0 - Chat message, 1 - System message, 2 - Action bar message
|
||||
field position: u8 =,
|
||||
}
|
||||
packet ServerMessage_NoPosition {
|
||||
field message: format::Component =,
|
||||
}
|
||||
/// MultiBlockChange is used to update a batch of blocks in a single packet.
|
||||
packet MultiBlockChange {
|
||||
packet MultiBlockChange_VarInt {
|
||||
field chunk_x: i32 =,
|
||||
field chunk_z: i32 =,
|
||||
field records: LenPrefixed<VarInt, packet::BlockChangeRecord> =,
|
||||
}
|
||||
packet MultiBlockChange_u16 {
|
||||
field chunk_x: i32 =,
|
||||
field chunk_z: i32 =,
|
||||
field record_count: u16 =,
|
||||
field data_size: i32 =,
|
||||
field data: Vec<u8> =,
|
||||
}
|
||||
/// ConfirmTransaction notifies the client whether a transaction was successful
|
||||
/// or failed (e.g. due to lag).
|
||||
packet ConfirmTransaction {
|
||||
|
@ -606,6 +750,14 @@ state_packets!(
|
|||
field slot_count: u8 =,
|
||||
field entity_id: i32 = when(|p: &WindowOpen| p.ty == "EntityHorse"),
|
||||
}
|
||||
packet WindowOpen_u8 {
|
||||
field id: u8 =,
|
||||
field ty: u8 =,
|
||||
field title: format::Component =,
|
||||
field slot_count: u8 =,
|
||||
field use_provided_window_title: bool =,
|
||||
field entity_id: i32 = when(|p: &WindowOpen_u8| p.ty == 11),
|
||||
}
|
||||
/// WindowItems sets every item in a window.
|
||||
packet WindowItems {
|
||||
field id: u8 =,
|
||||
|
@ -636,6 +788,10 @@ state_packets!(
|
|||
field channel: String =,
|
||||
field data: Vec<u8> =,
|
||||
}
|
||||
packet PluginMessageClientbound_i16 {
|
||||
field channel: String =,
|
||||
field data: LenPrefixedBytes<i16> =,
|
||||
}
|
||||
/// Plays a sound by name on the client
|
||||
packet NamedSoundEffect {
|
||||
field name: String =,
|
||||
|
@ -711,6 +867,9 @@ state_packets!(
|
|||
packet KeepAliveClientbound_VarInt {
|
||||
field id: VarInt =,
|
||||
}
|
||||
packet KeepAliveClientbound_i32 {
|
||||
field id: i32 =,
|
||||
}
|
||||
/// ChunkData sends or updates a single chunk on the client. If New is set
|
||||
/// then biome data should be sent too.
|
||||
packet ChunkData {
|
||||
|
@ -735,11 +894,25 @@ state_packets!(
|
|||
field bitmask: u16 =,
|
||||
field data: LenPrefixedBytes<VarInt> =,
|
||||
}
|
||||
packet ChunkData_17 {
|
||||
field chunk_x: i32 =,
|
||||
field chunk_z: i32 =,
|
||||
field new: bool =,
|
||||
field bitmask: u16 =,
|
||||
field add_bitmask: u16 =,
|
||||
field compressed_data: LenPrefixedBytes<i32> =,
|
||||
}
|
||||
packet ChunkDataBulk {
|
||||
field skylight: bool =,
|
||||
field chunk_meta: LenPrefixed<VarInt, packet::ChunkMeta> =,
|
||||
field chunk_data: Vec<u8> =,
|
||||
}
|
||||
packet ChunkDataBulk_17 {
|
||||
field chunk_column_count: u16 =,
|
||||
field data_length: i32 =,
|
||||
field skylight: bool =,
|
||||
field chunk_data_and_meta: Vec<u8> =,
|
||||
}
|
||||
/// Effect plays a sound effect or particle at the target location with the
|
||||
/// volume (of sounds) being relative to the player's position unless
|
||||
/// DisableRelative is set to true.
|
||||
|
@ -749,6 +922,14 @@ state_packets!(
|
|||
field data: i32 =,
|
||||
field disable_relative: bool =,
|
||||
}
|
||||
packet Effect_u8y {
|
||||
field effect_id: i32 =,
|
||||
field x: i32 =,
|
||||
field y: u8 =,
|
||||
field z: i32 =,
|
||||
field data: i32 =,
|
||||
field disable_relative: bool =,
|
||||
}
|
||||
/// Particle spawns particles at the target location with the various
|
||||
/// modifiers.
|
||||
packet Particle {
|
||||
|
@ -765,6 +946,17 @@ state_packets!(
|
|||
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),
|
||||
}
|
||||
packet Particle_Named {
|
||||
field particle_id: String =,
|
||||
field x: f32 =,
|
||||
field y: f32 =,
|
||||
field z: f32 =,
|
||||
field offset_x: f32 =,
|
||||
field offset_y: f32 =,
|
||||
field offset_z: f32 =,
|
||||
field speed: f32 =,
|
||||
field count: i32 =,
|
||||
}
|
||||
/// JoinGame is sent after completing the login process. This
|
||||
/// sets the initial state for the client.
|
||||
packet JoinGame_i32 {
|
||||
|
@ -801,6 +993,14 @@ state_packets!(
|
|||
/// information it displays in F3 mode
|
||||
field reduced_debug_info: bool =,
|
||||
}
|
||||
packet JoinGame_i8_NoDebug {
|
||||
field entity_id: i32 =,
|
||||
field gamemode: u8 =,
|
||||
field dimension: i8 =,
|
||||
field difficulty: u8 =,
|
||||
field max_players: u8 =,
|
||||
field level_type: String =,
|
||||
}
|
||||
/// Maps updates a single map's contents
|
||||
packet Maps {
|
||||
field item_damage: VarInt =,
|
||||
|
@ -823,6 +1023,10 @@ state_packets!(
|
|||
field z: Option<u8> = when(|p: &Maps_NoTracking| p.columns > 0),
|
||||
field data: Option<LenPrefixedBytes<VarInt>> = when(|p: &Maps_NoTracking| p.columns > 0),
|
||||
}
|
||||
packet Maps_NoTracking_Data {
|
||||
field item_damage: VarInt =,
|
||||
field data: LenPrefixedBytes<i16> =,
|
||||
}
|
||||
/// EntityMove moves the entity with the id by the offsets provided.
|
||||
packet EntityMove_i16 {
|
||||
field entity_id: VarInt =,
|
||||
|
@ -838,6 +1042,12 @@ state_packets!(
|
|||
field delta_z: i8 =,
|
||||
field on_ground: bool =,
|
||||
}
|
||||
packet EntityMove_i8_i32_NoGround {
|
||||
field entity_id: i32 =,
|
||||
field delta_x: i8 =,
|
||||
field delta_y: i8 =,
|
||||
field delta_z: i8 =,
|
||||
}
|
||||
/// EntityLookAndMove is a combination of EntityMove and EntityLook.
|
||||
packet EntityLookAndMove_i16 {
|
||||
field entity_id: VarInt =,
|
||||
|
@ -857,17 +1067,33 @@ state_packets!(
|
|||
field pitch: i8 =,
|
||||
field on_ground: bool =,
|
||||
}
|
||||
packet EntityLookAndMove_i8_i32_NoGround {
|
||||
field entity_id: i32 =,
|
||||
field delta_x: i8 =,
|
||||
field delta_y: i8 =,
|
||||
field delta_z: i8 =,
|
||||
field yaw: i8 =,
|
||||
field pitch: i8 =,
|
||||
}
|
||||
/// EntityLook rotates the entity to the new angles provided.
|
||||
packet EntityLook {
|
||||
packet EntityLook_VarInt {
|
||||
field entity_id: VarInt =,
|
||||
field yaw: i8 =,
|
||||
field pitch: i8 =,
|
||||
field on_ground: bool =,
|
||||
}
|
||||
packet EntityLook_i32_NoGround {
|
||||
field entity_id: i32 =,
|
||||
field yaw: i8 =,
|
||||
field pitch: i8 =,
|
||||
}
|
||||
/// Entity does nothing. It is a result of subclassing used in Minecraft.
|
||||
packet Entity {
|
||||
field entity_id: VarInt =,
|
||||
}
|
||||
packet Entity_i32 {
|
||||
field entity_id: i32 =,
|
||||
}
|
||||
/// EntityUpdateNBT updates the entity named binary tag.
|
||||
packet EntityUpdateNBT {
|
||||
field entity_id: VarInt =,
|
||||
|
@ -886,6 +1112,11 @@ state_packets!(
|
|||
packet SignEditorOpen {
|
||||
field location: Position =,
|
||||
}
|
||||
packet SignEditorOpen_i32 {
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
}
|
||||
/// CraftRecipeResponse is a response to CraftRecipeRequest, notifies the UI.
|
||||
packet CraftRecipeResponse {
|
||||
field window_id: u8 =,
|
||||
|
@ -912,6 +1143,11 @@ state_packets!(
|
|||
packet PlayerInfo {
|
||||
field inner: packet::PlayerInfoData =,
|
||||
}
|
||||
packet PlayerInfo_String {
|
||||
field name: String =,
|
||||
field online: bool =,
|
||||
field ping: u16 =,
|
||||
}
|
||||
/// TeleportPlayer is sent to change the player's position. The client is expected
|
||||
/// to reply to the server with the same positions as contained in this packet
|
||||
/// otherwise will reject future packets.
|
||||
|
@ -937,6 +1173,12 @@ state_packets!(
|
|||
field entity_id: VarInt =,
|
||||
field location: Position =,
|
||||
}
|
||||
packet EntityUsedBed_i32 {
|
||||
field entity_id: i32 =,
|
||||
field x: i32 =,
|
||||
field y: u8 =,
|
||||
field z: i32 =,
|
||||
}
|
||||
packet UnlockRecipes {
|
||||
field action: VarInt =,
|
||||
field crafting_book_open: bool =,
|
||||
|
@ -948,11 +1190,18 @@ state_packets!(
|
|||
packet EntityDestroy {
|
||||
field entity_ids: LenPrefixed<VarInt, VarInt> =,
|
||||
}
|
||||
packet EntityDestroy_u8 {
|
||||
field entity_ids: LenPrefixed<u8, i32> =,
|
||||
}
|
||||
/// EntityRemoveEffect removes an effect from an entity.
|
||||
packet EntityRemoveEffect {
|
||||
field entity_id: VarInt =,
|
||||
field effect_id: i8 =,
|
||||
}
|
||||
packet EntityRemoveEffect_i32 {
|
||||
field entity_id: i32 =,
|
||||
field effect_id: i8 =,
|
||||
}
|
||||
/// ResourcePackSend causes the client to check its cache for the requested
|
||||
/// resource packet and download it if its missing. Once the resource pack
|
||||
/// is obtained the client will use it.
|
||||
|
@ -972,6 +1221,10 @@ state_packets!(
|
|||
field entity_id: VarInt =,
|
||||
field head_yaw: i8 =,
|
||||
}
|
||||
packet EntityHeadLook_i32 {
|
||||
field entity_id: i32 =,
|
||||
field head_yaw: i8 =,
|
||||
}
|
||||
packet EntityStatus {
|
||||
field entity_id: i32 =,
|
||||
field entity_status: i8 =,
|
||||
|
@ -1012,6 +1265,10 @@ state_packets!(
|
|||
field entity_id: VarInt =,
|
||||
field metadata: types::Metadata =,
|
||||
}
|
||||
packet EntityMetadata_i32 {
|
||||
field entity_id: i32 =,
|
||||
field metadata: types::Metadata =,
|
||||
}
|
||||
/// EntityAttach attaches to entities together, either by mounting or leashing.
|
||||
/// -1 can be used at the EntityID to deattach.
|
||||
packet EntityAttach {
|
||||
|
@ -1031,6 +1288,12 @@ state_packets!(
|
|||
field velocity_y: i16 =,
|
||||
field velocity_z: i16 =,
|
||||
}
|
||||
packet EntityVelocity_i32 {
|
||||
field entity_id: i32 =,
|
||||
field velocity_x: i16 =,
|
||||
field velocity_y: i16 =,
|
||||
field velocity_z: i16 =,
|
||||
}
|
||||
/// EntityEquipment is sent to display an item on an entity, like a sword
|
||||
/// or armor. Slot 0 is the held item and slots 1 to 4 are boots, leggings
|
||||
/// chestplate and helmet respectively.
|
||||
|
@ -1044,18 +1307,33 @@ state_packets!(
|
|||
field slot: u16 =,
|
||||
field item: Option<item::Stack> =,
|
||||
}
|
||||
packet EntityEquipment_u16_i32 {
|
||||
field entity_id: i32 =,
|
||||
field slot: u16 =,
|
||||
field item: Option<item::Stack> =,
|
||||
}
|
||||
/// SetExperience updates the experience bar on the client.
|
||||
packet SetExperience {
|
||||
field experience_bar: f32 =,
|
||||
field level: VarInt =,
|
||||
field total_experience: VarInt =,
|
||||
}
|
||||
packet SetExperience_i16 {
|
||||
field experience_bar: f32 =,
|
||||
field level: i16 =,
|
||||
field total_experience: i16 =,
|
||||
}
|
||||
/// UpdateHealth is sent by the server to update the player's health and food.
|
||||
packet UpdateHealth {
|
||||
field health: f32 =,
|
||||
field food: VarInt =,
|
||||
field food_saturation: f32 =,
|
||||
}
|
||||
packet UpdateHealth_u16 {
|
||||
field health: f32 =,
|
||||
field food: u16 =,
|
||||
field food_saturation: f32 =,
|
||||
}
|
||||
/// ScoreboardObjective creates/updates a scoreboard objective.
|
||||
packet ScoreboardObjective {
|
||||
field name: String =,
|
||||
|
@ -1063,6 +1341,11 @@ state_packets!(
|
|||
field value: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2),
|
||||
field ty: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2),
|
||||
}
|
||||
packet ScoreboardObjective_NoMode {
|
||||
field name: String =,
|
||||
field value: String =,
|
||||
field ty: u8 =,
|
||||
}
|
||||
/// SetPassengers mounts entities to an entity
|
||||
packet SetPassengers {
|
||||
field entity_id: VarInt =,
|
||||
|
@ -1081,6 +1364,15 @@ state_packets!(
|
|||
field color: Option<i8> = when(|p: &Teams| p.mode == 0 || p.mode == 2),
|
||||
field players: Option<LenPrefixed<VarInt, String>> = when(|p: &Teams| p.mode == 0 || p.mode == 3 || p.mode == 4),
|
||||
}
|
||||
packet Teams_NoVisColor {
|
||||
field name: String =,
|
||||
field mode: u8 =,
|
||||
field display_name: Option<String> = when(|p: &Teams_NoVisColor| p.mode == 0 || p.mode == 2),
|
||||
field prefix: Option<String> = when(|p: &Teams_NoVisColor| p.mode == 0 || p.mode == 2),
|
||||
field suffix: Option<String> = when(|p: &Teams_NoVisColor| p.mode == 0 || p.mode == 2),
|
||||
field flags: Option<u8> = when(|p: &Teams_NoVisColor| p.mode == 0 || p.mode == 2),
|
||||
field players: Option<LenPrefixed<VarInt, String>> = when(|p: &Teams_NoVisColor| p.mode == 0 || p.mode == 3 || p.mode == 4),
|
||||
}
|
||||
/// UpdateScore is used to update or remove an item from a scoreboard
|
||||
/// objective.
|
||||
packet UpdateScore {
|
||||
|
@ -1089,11 +1381,22 @@ state_packets!(
|
|||
field object_name: String =,
|
||||
field value: Option<VarInt> = when(|p: &UpdateScore| p.action != 1),
|
||||
}
|
||||
packet UpdateScore_i32 {
|
||||
field name: String =,
|
||||
field action: u8 =,
|
||||
field object_name: String =,
|
||||
field value: Option<i32 > = when(|p: &UpdateScore_i32| p.action != 1),
|
||||
}
|
||||
/// SpawnPosition is sent to change the player's current spawn point. Currently
|
||||
/// only used by the client for the compass.
|
||||
packet SpawnPosition {
|
||||
field location: Position =,
|
||||
}
|
||||
packet SpawnPosition_i32 {
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
}
|
||||
/// TimeUpdate is sent to sync the world's time to the client, the client
|
||||
/// will manually tick the time itself so this doesn't need to sent repeatedly
|
||||
/// but if the server or client has issues keeping up this can fall out of sync
|
||||
|
@ -1136,6 +1439,15 @@ state_packets!(
|
|||
field line3: format::Component =,
|
||||
field line4: format::Component =,
|
||||
}
|
||||
packet UpdateSign_u16 {
|
||||
field x: i32 =,
|
||||
field y: u16 =,
|
||||
field z: i32 =,
|
||||
field line1: format::Component =,
|
||||
field line2: format::Component =,
|
||||
field line3: format::Component =,
|
||||
field line4: format::Component =,
|
||||
}
|
||||
/// SoundEffect plays the named sound at the target location.
|
||||
packet SoundEffect {
|
||||
field name: VarInt =,
|
||||
|
@ -1171,6 +1483,10 @@ state_packets!(
|
|||
field collected_entity_id: VarInt =,
|
||||
field collector_entity_id: VarInt =,
|
||||
}
|
||||
packet CollectItem_nocount_i32 {
|
||||
field collected_entity_id: i32 =,
|
||||
field collector_entity_id: i32 =,
|
||||
}
|
||||
/// EntityTeleport teleports the entity to the target location. This is
|
||||
/// sent if the entity moves further than EntityMove allows.
|
||||
packet EntityTeleport_f64 {
|
||||
|
@ -1191,6 +1507,14 @@ state_packets!(
|
|||
field pitch: i8 =,
|
||||
field on_ground: bool =,
|
||||
}
|
||||
packet EntityTeleport_i32_i32_NoGround {
|
||||
field entity_id: i32 =,
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
field yaw: i8 =,
|
||||
field pitch: i8 =,
|
||||
}
|
||||
packet Advancements {
|
||||
field reset_clear: bool =,
|
||||
field mapping: LenPrefixed<VarInt, packet::Advancement> =,
|
||||
|
@ -1202,6 +1526,10 @@ state_packets!(
|
|||
field entity_id: VarInt =,
|
||||
field properties: LenPrefixed<i32, packet::EntityProperty> =,
|
||||
}
|
||||
packet EntityProperties_i32 {
|
||||
field entity_id: i32 =,
|
||||
field properties: LenPrefixed<i32, packet::EntityProperty_i16> =,
|
||||
}
|
||||
/// EntityEffect applies a status effect to an entity for a given duration.
|
||||
packet EntityEffect {
|
||||
field entity_id: VarInt =,
|
||||
|
@ -1210,6 +1538,12 @@ state_packets!(
|
|||
field duration: VarInt =,
|
||||
field hide_particles: bool =,
|
||||
}
|
||||
packet EntityEffect_i32 {
|
||||
field entity_id: i32 =,
|
||||
field effect_id: i8 =,
|
||||
field amplifier: i8 =,
|
||||
field duration: i16 =,
|
||||
}
|
||||
}
|
||||
}
|
||||
login Login {
|
||||
|
@ -1231,6 +1565,10 @@ state_packets!(
|
|||
/// public key
|
||||
field verify_token: LenPrefixedBytes<VarInt> =,
|
||||
}
|
||||
packet EncryptionResponse_i16 {
|
||||
field shared_secret: LenPrefixedBytes<i16> =,
|
||||
field verify_token: LenPrefixedBytes<i16> =,
|
||||
}
|
||||
}
|
||||
clientbound Clientbound {
|
||||
/// LoginDisconnect is sent by the server if there was any issues
|
||||
|
@ -1252,6 +1590,11 @@ state_packets!(
|
|||
/// correctly
|
||||
field verify_token: LenPrefixedBytes<VarInt> =,
|
||||
}
|
||||
packet EncryptionRequest_i16 {
|
||||
field server_id: String =,
|
||||
field public_key: LenPrefixedBytes<i16> =,
|
||||
field verify_token: LenPrefixedBytes<i16> =,
|
||||
}
|
||||
/// LoginSuccess is sent by the server if the player successfully
|
||||
/// authenicates with the session servers (online mode) or straight
|
||||
/// after LoginStart (offline mode).
|
||||
|
@ -1323,6 +1666,29 @@ state_packets!(
|
|||
}
|
||||
);
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SpawnProperty {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
pub signature: String,
|
||||
}
|
||||
|
||||
impl Serializable for SpawnProperty {
|
||||
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
|
||||
Ok(SpawnProperty {
|
||||
name: Serializable::read_from(buf)?,
|
||||
value: Serializable::read_from(buf)?,
|
||||
signature: Serializable::read_from(buf)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), Error> {
|
||||
self.name.write_to(buf)?;
|
||||
self.value.write_to(buf)?;
|
||||
self.signature.write_to(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Statistic {
|
||||
pub name: String,
|
||||
|
@ -1625,6 +1991,30 @@ impl Serializable for EntityProperty {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct EntityProperty_i16 {
|
||||
pub key: String,
|
||||
pub value: f64,
|
||||
pub modifiers: LenPrefixed<i16, PropertyModifier>,
|
||||
}
|
||||
|
||||
impl Serializable for EntityProperty_i16 {
|
||||
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
|
||||
Ok(EntityProperty_i16 {
|
||||
key: Serializable::read_from(buf)?,
|
||||
value: Serializable::read_from(buf)?,
|
||||
modifiers: Serializable::read_from(buf)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), Error> {
|
||||
self.key.write_to(buf)?;
|
||||
self.value.write_to(buf)?;
|
||||
self.modifiers.write_to(buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct PropertyModifier {
|
||||
pub uuid: UUID,
|
||||
|
|
|
@ -7,6 +7,7 @@ mod v1_9_2;
|
|||
mod v1_9;
|
||||
mod v15w39c;
|
||||
mod v1_8_9;
|
||||
mod v1_7_10;
|
||||
|
||||
pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 {
|
||||
match version {
|
||||
|
@ -36,6 +37,9 @@ pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir:
|
|||
// 1.8.9 - 1.8
|
||||
47 => v1_8_9::translate_internal_packet_id(state, dir, id, to_internal),
|
||||
|
||||
// 1.7.10 - 1.7.6
|
||||
5 => v1_7_10::translate_internal_packet_id(state, dir, id, to_internal),
|
||||
|
||||
_ => panic!("unsupported protocol version"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,12 +48,12 @@ protocol_packet_ids!(
|
|||
0x08 => BlockBreakAnimation
|
||||
0x09 => UpdateBlockEntity
|
||||
0x0a => BlockAction
|
||||
0x0b => BlockChange
|
||||
0x0b => BlockChange_VarInt
|
||||
0x0c => BossBar
|
||||
0x0d => ServerDifficulty
|
||||
0x0e => TabCompleteReply
|
||||
0x0f => ServerMessage
|
||||
0x10 => MultiBlockChange
|
||||
0x10 => MultiBlockChange_VarInt
|
||||
0x11 => ConfirmTransaction
|
||||
0x12 => WindowClose
|
||||
0x13 => WindowOpen
|
||||
|
@ -77,7 +77,7 @@ protocol_packet_ids!(
|
|||
0x25 => Maps
|
||||
0x26 => EntityMove_i8
|
||||
0x27 => EntityLookAndMove_i8
|
||||
0x28 => EntityLook
|
||||
0x28 => EntityLook_VarInt
|
||||
0x29 => Entity
|
||||
0x2a => SignEditorOpen
|
||||
0x2b => PlayerAbilities
|
||||
|
|
|
@ -51,12 +51,12 @@ protocol_packet_ids!(
|
|||
0x08 => BlockBreakAnimation
|
||||
0x09 => UpdateBlockEntity
|
||||
0x0a => BlockAction
|
||||
0x0b => BlockChange
|
||||
0x0b => BlockChange_VarInt
|
||||
0x0c => BossBar
|
||||
0x0d => ServerDifficulty
|
||||
0x0e => TabCompleteReply
|
||||
0x0f => ServerMessage
|
||||
0x10 => MultiBlockChange
|
||||
0x10 => MultiBlockChange_VarInt
|
||||
0x11 => ConfirmTransaction
|
||||
0x12 => WindowClose
|
||||
0x13 => WindowOpen
|
||||
|
@ -79,7 +79,7 @@ protocol_packet_ids!(
|
|||
0x24 => Maps
|
||||
0x25 => EntityMove_i16
|
||||
0x26 => EntityLookAndMove_i16
|
||||
0x27 => EntityLook
|
||||
0x27 => EntityLook_VarInt
|
||||
0x28 => Entity
|
||||
0x29 => VehicleTeleport
|
||||
0x2a => SignEditorOpen
|
||||
|
|
|
@ -51,12 +51,12 @@ protocol_packet_ids!(
|
|||
0x08 => BlockBreakAnimation
|
||||
0x09 => UpdateBlockEntity
|
||||
0x0a => BlockAction
|
||||
0x0b => BlockChange
|
||||
0x0b => BlockChange_VarInt
|
||||
0x0c => BossBar
|
||||
0x0d => ServerDifficulty
|
||||
0x0e => TabCompleteReply
|
||||
0x0f => ServerMessage
|
||||
0x10 => MultiBlockChange
|
||||
0x10 => MultiBlockChange_VarInt
|
||||
0x11 => ConfirmTransaction
|
||||
0x12 => WindowClose
|
||||
0x13 => WindowOpen
|
||||
|
@ -79,7 +79,7 @@ protocol_packet_ids!(
|
|||
0x24 => Maps
|
||||
0x25 => EntityMove_i16
|
||||
0x26 => EntityLookAndMove_i16
|
||||
0x27 => EntityLook
|
||||
0x27 => EntityLook_VarInt
|
||||
0x28 => Entity
|
||||
0x29 => VehicleTeleport
|
||||
0x2a => SignEditorOpen
|
||||
|
|
|
@ -54,12 +54,12 @@ protocol_packet_ids!(
|
|||
0x08 => BlockBreakAnimation
|
||||
0x09 => UpdateBlockEntity
|
||||
0x0a => BlockAction
|
||||
0x0b => BlockChange
|
||||
0x0b => BlockChange_VarInt
|
||||
0x0c => BossBar
|
||||
0x0d => ServerDifficulty
|
||||
0x0e => TabCompleteReply
|
||||
0x0f => ServerMessage
|
||||
0x10 => MultiBlockChange
|
||||
0x10 => MultiBlockChange_VarInt
|
||||
0x11 => ConfirmTransaction
|
||||
0x12 => WindowClose
|
||||
0x13 => WindowOpen
|
||||
|
@ -83,7 +83,7 @@ protocol_packet_ids!(
|
|||
0x25 => Entity
|
||||
0x26 => EntityMove_i16
|
||||
0x27 => EntityLookAndMove_i16
|
||||
0x28 => EntityLook
|
||||
0x28 => EntityLook_VarInt
|
||||
0x29 => VehicleTeleport
|
||||
0x2a => SignEditorOpen
|
||||
0x2b => CraftRecipeResponse
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
protocol_packet_ids!(
|
||||
handshake Handshaking {
|
||||
serverbound Serverbound {
|
||||
0x00 => Handshake
|
||||
}
|
||||
clientbound Clientbound {
|
||||
}
|
||||
}
|
||||
play Play {
|
||||
serverbound Serverbound {
|
||||
0x00 => KeepAliveServerbound_i32
|
||||
0x01 => ChatMessage
|
||||
0x02 => UseEntity_Handsfree_i32
|
||||
0x03 => Player
|
||||
0x04 => PlayerPosition_HeadY
|
||||
0x05 => PlayerLook
|
||||
0x06 => PlayerPositionLook_HeadY
|
||||
0x07 => PlayerDigging_u8_u8y
|
||||
0x08 => PlayerBlockPlacement_u8_Item_u8y
|
||||
0x09 => HeldItemChange
|
||||
0x0a => ArmSwing_Handsfree_ID
|
||||
0x0b => PlayerAction_i32
|
||||
0x0c => SteerVehicle_jump_unmount
|
||||
0x0d => CloseWindow
|
||||
0x0e => ClickWindow_u8
|
||||
0x0f => ConfirmTransactionServerbound
|
||||
0x10 => CreativeInventoryAction
|
||||
0x11 => EnchantItem
|
||||
0x12 => SetSign_i16y
|
||||
0x13 => ClientAbilities
|
||||
0x14 => TabComplete_NoAssume_NoTarget
|
||||
0x15 => ClientSettings_u8_Handsfree_Difficulty
|
||||
0x16 => ClientStatus_u8
|
||||
0x17 => PluginMessageServerbound_i16
|
||||
}
|
||||
clientbound Clientbound {
|
||||
0x00 => KeepAliveClientbound_i32
|
||||
0x01 => JoinGame_i8_NoDebug
|
||||
0x02 => ServerMessage_NoPosition
|
||||
0x03 => TimeUpdate
|
||||
0x04 => EntityEquipment_u16_i32
|
||||
0x05 => SpawnPosition_i32
|
||||
0x06 => UpdateHealth_u16
|
||||
0x07 => Respawn
|
||||
0x08 => TeleportPlayer_NoConfirm
|
||||
0x09 => SetCurrentHotbarSlot
|
||||
0x0a => EntityUsedBed_i32
|
||||
0x0b => Animation
|
||||
0x0c => SpawnPlayer_i32_HeldItem_String
|
||||
0x0d => CollectItem_nocount_i32
|
||||
0x0e => SpawnObject_i32_NoUUID
|
||||
0x0f => SpawnMob_u8_i32_NoUUID
|
||||
0x10 => SpawnPainting_NoUUID_i32
|
||||
0x11 => SpawnExperienceOrb_i32
|
||||
0x12 => EntityVelocity_i32
|
||||
0x13 => EntityDestroy_u8
|
||||
0x14 => Entity_i32
|
||||
0x15 => EntityMove_i8_i32_NoGround
|
||||
0x16 => EntityLook_i32_NoGround
|
||||
0x17 => EntityLookAndMove_i8_i32_NoGround
|
||||
0x18 => EntityTeleport_i32_i32_NoGround
|
||||
0x19 => EntityHeadLook_i32
|
||||
0x1a => EntityStatus
|
||||
0x1b => EntityAttach_leashed
|
||||
0x1c => EntityMetadata_i32
|
||||
0x1d => EntityEffect_i32
|
||||
0x1e => EntityRemoveEffect_i32
|
||||
0x1f => SetExperience_i16
|
||||
0x20 => EntityProperties_i32
|
||||
0x21 => ChunkData_17
|
||||
0x22 => MultiBlockChange_u16
|
||||
0x23 => BlockChange_u8
|
||||
0x24 => BlockAction_u16
|
||||
0x25 => BlockBreakAnimation_i32
|
||||
0x26 => ChunkDataBulk_17
|
||||
0x27 => Explosion
|
||||
0x28 => Effect_u8y
|
||||
0x29 => NamedSoundEffect_u8_NoCategory
|
||||
0x2a => Particle_Named
|
||||
0x2b => ChangeGameState
|
||||
0x2c => SpawnGlobalEntity_i32
|
||||
0x2d => WindowOpen_u8
|
||||
0x2e => WindowClose
|
||||
0x2f => WindowSetSlot
|
||||
0x30 => WindowItems
|
||||
0x31 => WindowProperty
|
||||
0x32 => ConfirmTransaction
|
||||
0x33 => UpdateSign_u16
|
||||
0x34 => Maps_NoTracking_Data
|
||||
0x35 => UpdateBlockEntity_Data
|
||||
0x36 => SignEditorOpen_i32
|
||||
0x37 => Statistics
|
||||
0x38 => PlayerInfo_String
|
||||
0x39 => PlayerAbilities
|
||||
0x3a => TabCompleteReply
|
||||
0x3b => ScoreboardObjective_NoMode
|
||||
0x3c => UpdateScore_i32
|
||||
0x3d => ScoreboardDisplay
|
||||
0x3e => Teams_NoVisColor
|
||||
0x3f => PluginMessageClientbound_i16
|
||||
0x40 => Disconnect
|
||||
}
|
||||
}
|
||||
login Login {
|
||||
serverbound Serverbound {
|
||||
0x00 => LoginStart
|
||||
0x01 => EncryptionResponse_i16
|
||||
}
|
||||
clientbound Clientbound {
|
||||
0x00 => LoginDisconnect
|
||||
0x01 => EncryptionRequest_i16
|
||||
0x02 => LoginSuccess
|
||||
}
|
||||
}
|
||||
status Status {
|
||||
serverbound Serverbound {
|
||||
0x00 => StatusRequest
|
||||
0x01 => StatusPing
|
||||
}
|
||||
clientbound Clientbound {
|
||||
0x00 => StatusResponse
|
||||
0x01 => StatusPong
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ protocol_packet_ids!(
|
|||
0x13 => EntityDestroy
|
||||
0x14 => Entity
|
||||
0x15 => EntityMove_i8
|
||||
0x16 => EntityLook
|
||||
0x16 => EntityLook_VarInt
|
||||
0x17 => EntityLookAndMove_i8
|
||||
0x18 => EntityTeleport_i32
|
||||
0x19 => EntityHeadLook
|
||||
|
@ -70,8 +70,8 @@ protocol_packet_ids!(
|
|||
0x1f => SetExperience
|
||||
0x20 => EntityProperties
|
||||
0x21 => ChunkData_NoEntities_u16
|
||||
0x22 => MultiBlockChange
|
||||
0x23 => BlockChange
|
||||
0x22 => MultiBlockChange_VarInt
|
||||
0x23 => BlockChange_VarInt
|
||||
0x24 => BlockAction
|
||||
0x25 => BlockBreakAnimation
|
||||
0x26 => ChunkDataBulk
|
||||
|
|
|
@ -51,12 +51,12 @@ protocol_packet_ids!(
|
|||
0x08 => BlockBreakAnimation
|
||||
0x09 => UpdateBlockEntity
|
||||
0x0a => BlockAction
|
||||
0x0b => BlockChange
|
||||
0x0b => BlockChange_VarInt
|
||||
0x0c => BossBar
|
||||
0x0d => ServerDifficulty
|
||||
0x0e => TabCompleteReply
|
||||
0x0f => ServerMessage
|
||||
0x10 => MultiBlockChange
|
||||
0x10 => MultiBlockChange_VarInt
|
||||
0x11 => ConfirmTransaction
|
||||
0x12 => WindowClose
|
||||
0x13 => WindowOpen
|
||||
|
@ -79,7 +79,7 @@ protocol_packet_ids!(
|
|||
0x24 => Maps
|
||||
0x25 => EntityMove_i16
|
||||
0x26 => EntityLookAndMove_i16
|
||||
0x27 => EntityLook
|
||||
0x27 => EntityLook_VarInt
|
||||
0x28 => Entity
|
||||
0x29 => VehicleTeleport
|
||||
0x2a => SignEditorOpen
|
||||
|
|
|
@ -51,12 +51,12 @@ protocol_packet_ids!(
|
|||
0x08 => BlockBreakAnimation
|
||||
0x09 => UpdateBlockEntity
|
||||
0x0a => BlockAction
|
||||
0x0b => BlockChange
|
||||
0x0b => BlockChange_VarInt
|
||||
0x0c => BossBar
|
||||
0x0d => ServerDifficulty
|
||||
0x0e => TabCompleteReply
|
||||
0x0f => ServerMessage
|
||||
0x10 => MultiBlockChange
|
||||
0x10 => MultiBlockChange_VarInt
|
||||
0x11 => ConfirmTransaction
|
||||
0x12 => WindowClose
|
||||
0x13 => WindowOpen
|
||||
|
@ -79,7 +79,7 @@ protocol_packet_ids!(
|
|||
0x24 => Maps
|
||||
0x25 => EntityMove_i16
|
||||
0x26 => EntityLookAndMove_i16
|
||||
0x27 => EntityLook
|
||||
0x27 => EntityLook_VarInt
|
||||
0x28 => Entity
|
||||
0x29 => VehicleTeleport
|
||||
0x2a => SignEditorOpen
|
||||
|
|
|
@ -120,14 +120,23 @@ impl Server {
|
|||
username: profile.username.clone(),
|
||||
})?;
|
||||
|
||||
let packet;
|
||||
use std::rc::Rc;
|
||||
let (server_id, public_key, verify_token);
|
||||
loop {
|
||||
match conn.read_packet()? {
|
||||
protocol::packet::Packet::SetInitialCompression(val) => {
|
||||
conn.set_compresssion(val.threshold.0);
|
||||
},
|
||||
protocol::packet::Packet::EncryptionRequest(val) => {
|
||||
packet = val;
|
||||
server_id = Rc::new(val.server_id);
|
||||
public_key = Rc::new(val.public_key.data);
|
||||
verify_token = Rc::new(val.verify_token.data);
|
||||
break;
|
||||
},
|
||||
protocol::packet::Packet::EncryptionRequest_i16(val) => {
|
||||
server_id = Rc::new(val.server_id);
|
||||
public_key = Rc::new(val.public_key.data);
|
||||
verify_token = Rc::new(val.verify_token.data);
|
||||
break;
|
||||
},
|
||||
protocol::packet::Packet::LoginSuccess(val) => {
|
||||
|
@ -145,25 +154,26 @@ impl Server {
|
|||
};
|
||||
}
|
||||
|
||||
println!("packet.public_key.data = {:?}", &packet.public_key.data);
|
||||
let mut shared = [0; 16];
|
||||
// TODO: is this cryptographically secure enough?
|
||||
rand::thread_rng().fill(&mut shared);
|
||||
|
||||
println!("shared ({:} bytes) = {:?}", shared.len(), &shared);
|
||||
println!("packet.verify_token.data = {:?}", &packet.verify_token.data);
|
||||
let shared_e = rsa_public_encrypt_pkcs1::encrypt(&public_key, &shared).unwrap();
|
||||
let token_e = rsa_public_encrypt_pkcs1::encrypt(&public_key, &verify_token).unwrap();
|
||||
|
||||
let shared_e = rsa_public_encrypt_pkcs1::encrypt(&packet.public_key.data, &shared).unwrap();
|
||||
let token_e = rsa_public_encrypt_pkcs1::encrypt(&packet.public_key.data, &packet.verify_token.data).unwrap();
|
||||
println!("new shared_e({:}) = {:?}", shared_e.len(), &shared_e);
|
||||
println!("new token_e({:}) = {:?}", token_e.len(), &token_e);
|
||||
profile.join_server(&server_id, &shared, &public_key)?;
|
||||
|
||||
profile.join_server(&packet.server_id, &shared, &packet.public_key.data)?;
|
||||
|
||||
conn.write_packet(protocol::packet::login::serverbound::EncryptionResponse {
|
||||
shared_secret: protocol::LenPrefixedBytes::new(shared_e),
|
||||
verify_token: protocol::LenPrefixedBytes::new(token_e),
|
||||
})?;
|
||||
if protocol_version >= 47 {
|
||||
conn.write_packet(protocol::packet::login::serverbound::EncryptionResponse {
|
||||
shared_secret: protocol::LenPrefixedBytes::new(shared_e),
|
||||
verify_token: protocol::LenPrefixedBytes::new(token_e),
|
||||
})?;
|
||||
} else {
|
||||
conn.write_packet(protocol::packet::login::serverbound::EncryptionResponse_i16 {
|
||||
shared_secret: protocol::LenPrefixedBytes::new(shared_e),
|
||||
verify_token: protocol::LenPrefixedBytes::new(token_e),
|
||||
})?;
|
||||
}
|
||||
|
||||
let mut read = conn.clone();
|
||||
let mut write = conn.clone();
|
||||
|
@ -377,36 +387,51 @@ impl Server {
|
|||
self pck {
|
||||
JoinGame_i32 => on_game_join_i32,
|
||||
JoinGame_i8 => on_game_join_i8,
|
||||
JoinGame_i8_NoDebug => on_game_join_i8_nodebug,
|
||||
Respawn => on_respawn,
|
||||
KeepAliveClientbound_i64 => on_keep_alive_i64,
|
||||
KeepAliveClientbound_VarInt => on_keep_alive_varint,
|
||||
KeepAliveClientbound_i32 => on_keep_alive_i32,
|
||||
ChunkData => on_chunk_data,
|
||||
ChunkData_NoEntities => on_chunk_data_no_entities,
|
||||
ChunkData_NoEntities_u16 => on_chunk_data_no_entities_u16,
|
||||
ChunkData_17 => on_chunk_data_17,
|
||||
ChunkDataBulk => on_chunk_data_bulk,
|
||||
ChunkDataBulk_17 => on_chunk_data_bulk_17,
|
||||
ChunkUnload => on_chunk_unload,
|
||||
BlockChange => on_block_change,
|
||||
MultiBlockChange => on_multi_block_change,
|
||||
BlockChange_VarInt => on_block_change_varint,
|
||||
BlockChange_u8 => on_block_change_u8,
|
||||
MultiBlockChange_VarInt => on_multi_block_change_varint,
|
||||
MultiBlockChange_u16 => on_multi_block_change_u16,
|
||||
TeleportPlayer_WithConfirm => on_teleport_player_withconfirm,
|
||||
TeleportPlayer_NoConfirm => on_teleport_player_noconfirm,
|
||||
TimeUpdate => on_time_update,
|
||||
ChangeGameState => on_game_state_change,
|
||||
UpdateBlockEntity => on_block_entity_update,
|
||||
UpdateBlockEntity_Data => on_block_entity_update_data,
|
||||
UpdateSign => on_sign_update,
|
||||
UpdateSign_u16 => on_sign_update_u16,
|
||||
PlayerInfo => on_player_info,
|
||||
PlayerInfo_String => on_player_info_string,
|
||||
Disconnect => on_disconnect,
|
||||
// Entities
|
||||
EntityDestroy => on_entity_destroy,
|
||||
EntityDestroy_u8 => on_entity_destroy_u8,
|
||||
SpawnPlayer_f64 => on_player_spawn_f64,
|
||||
SpawnPlayer_i32 => on_player_spawn_i32,
|
||||
SpawnPlayer_i32_HeldItem => on_player_spawn_i32_helditem,
|
||||
SpawnPlayer_i32_HeldItem_String => on_player_spawn_i32_helditem_string,
|
||||
EntityTeleport_f64 => on_entity_teleport_f64,
|
||||
EntityTeleport_i32 => on_entity_teleport_i32,
|
||||
EntityTeleport_i32_i32_NoGround => on_entity_teleport_i32_i32_noground,
|
||||
EntityMove_i16 => on_entity_move_i16,
|
||||
EntityMove_i8 => on_entity_move_i8,
|
||||
EntityLook => on_entity_look,
|
||||
EntityMove_i8_i32_NoGround => on_entity_move_i8_i32_noground,
|
||||
EntityLook_VarInt => on_entity_look_varint,
|
||||
EntityLook_i32_NoGround => on_entity_look_i32_noground,
|
||||
EntityLookAndMove_i16 => on_entity_look_and_move_i16,
|
||||
EntityLookAndMove_i8 => on_entity_look_and_move_i8,
|
||||
EntityLookAndMove_i8_i32_NoGround => on_entity_look_and_move_i8_i32_noground,
|
||||
}
|
||||
},
|
||||
Err(err) => panic!("Err: {:?}", err),
|
||||
|
@ -503,15 +528,28 @@ impl Server {
|
|||
|
||||
// Sync our position to the server
|
||||
// Use the smaller packets when possible
|
||||
let packet = packet::play::serverbound::PlayerPositionLook {
|
||||
x: position.position.x,
|
||||
y: position.position.y,
|
||||
z: position.position.z,
|
||||
yaw: -(rotation.yaw as f32) * (180.0 / PI),
|
||||
pitch: (-rotation.pitch as f32) * (180.0 / PI) + 180.0,
|
||||
on_ground,
|
||||
};
|
||||
self.write_packet(packet);
|
||||
if self.protocol_version >= 47 {
|
||||
let packet = packet::play::serverbound::PlayerPositionLook {
|
||||
x: position.position.x,
|
||||
y: position.position.y,
|
||||
z: position.position.z,
|
||||
yaw: -(rotation.yaw as f32) * (180.0 / PI),
|
||||
pitch: (-rotation.pitch as f32) * (180.0 / PI) + 180.0,
|
||||
on_ground,
|
||||
};
|
||||
self.write_packet(packet);
|
||||
} else {
|
||||
let packet = packet::play::serverbound::PlayerPositionLook_HeadY {
|
||||
x: position.position.x,
|
||||
feet_y: position.position.y - 1.62,
|
||||
head_y: position.position.y,
|
||||
z: position.position.z,
|
||||
yaw: -(rotation.yaw as f32) * (180.0 / PI),
|
||||
pitch: (-rotation.pitch as f32) * (180.0 / PI) + 180.0,
|
||||
on_ground,
|
||||
};
|
||||
self.write_packet(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -561,7 +599,7 @@ impl Server {
|
|||
cursor_y: (at.y * 16.0) as u8,
|
||||
cursor_z: (at.z * 16.0) as u8,
|
||||
});
|
||||
} else {
|
||||
} else if self.protocol_version >= 47 {
|
||||
self.write_packet(packet::play::serverbound::PlayerBlockPlacement_u8_Item {
|
||||
location: pos,
|
||||
face: match face {
|
||||
|
@ -578,6 +616,25 @@ impl Server {
|
|||
cursor_y: (at.y * 16.0) as u8,
|
||||
cursor_z: (at.z * 16.0) as u8,
|
||||
});
|
||||
} else {
|
||||
self.write_packet(packet::play::serverbound::PlayerBlockPlacement_u8_Item_u8y {
|
||||
x: pos.x,
|
||||
y: pos.y as u8,
|
||||
z: pos.x,
|
||||
face: match face {
|
||||
Direction::Down => 0,
|
||||
Direction::Up => 1,
|
||||
Direction::North => 2,
|
||||
Direction::South => 3,
|
||||
Direction::West => 4,
|
||||
Direction::East => 5,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
hand: None,
|
||||
cursor_x: (at.x * 16.0) as u8,
|
||||
cursor_y: (at.y * 16.0) as u8,
|
||||
cursor_z: (at.z * 16.0) as u8,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -599,6 +656,12 @@ impl Server {
|
|||
});
|
||||
}
|
||||
|
||||
fn on_keep_alive_i32(&mut self, keep_alive: packet::play::clientbound::KeepAliveClientbound_i32) {
|
||||
self.write_packet(packet::play::serverbound::KeepAliveServerbound_i32 {
|
||||
id: keep_alive.id,
|
||||
});
|
||||
}
|
||||
|
||||
fn on_game_join_i32(&mut self, join: packet::play::clientbound::JoinGame_i32) {
|
||||
self.on_game_join(join.gamemode, join.entity_id)
|
||||
}
|
||||
|
@ -607,6 +670,11 @@ impl Server {
|
|||
self.on_game_join(join.gamemode, join.entity_id)
|
||||
}
|
||||
|
||||
fn on_game_join_i8_nodebug(&mut self, join: packet::play::clientbound::JoinGame_i8_NoDebug) {
|
||||
self.on_game_join(join.gamemode, join.entity_id)
|
||||
}
|
||||
|
||||
|
||||
fn on_game_join(&mut self, gamemode: u8, entity_id: i32) {
|
||||
let gamemode = Gamemode::from_int((gamemode & 0x7) as i32);
|
||||
let player = entity::player::create_local(&mut self.entities);
|
||||
|
@ -622,9 +690,14 @@ impl Server {
|
|||
self.player = Some(player);
|
||||
|
||||
// Let the server know who we are
|
||||
self.write_packet(plugin_messages::Brand {
|
||||
brand: "Steven".into(),
|
||||
}.as_message());
|
||||
let brand = plugin_messages::Brand {
|
||||
brand: "Steven".into(),
|
||||
};
|
||||
if self.protocol_version >= 47 {
|
||||
self.write_packet(brand.as_message());
|
||||
} else {
|
||||
self.write_packet(brand.as_message17());
|
||||
}
|
||||
}
|
||||
|
||||
fn on_respawn(&mut self, respawn: packet::play::clientbound::Respawn) {
|
||||
|
@ -672,6 +745,14 @@ impl Server {
|
|||
}
|
||||
}
|
||||
|
||||
fn on_entity_destroy_u8(&mut self, entity_destroy: packet::play::clientbound::EntityDestroy_u8) {
|
||||
for id in entity_destroy.entity_ids.data {
|
||||
if let Some(entity) = self.entity_map.remove(&id) {
|
||||
self.entities.remove_entity(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_entity_teleport_f64(&mut self, entity_telport: packet::play::clientbound::EntityTeleport_f64) {
|
||||
self.on_entity_teleport(entity_telport.entity_id.0, entity_telport.x, entity_telport.y, entity_telport.z, entity_telport.yaw as f64, entity_telport.pitch as f64, entity_telport.on_ground)
|
||||
}
|
||||
|
@ -680,6 +761,12 @@ impl Server {
|
|||
self.on_entity_teleport(entity_telport.entity_id.0, entity_telport.x as f64, entity_telport.y as f64, entity_telport.z as f64, entity_telport.yaw as f64, entity_telport.pitch as f64, entity_telport.on_ground)
|
||||
}
|
||||
|
||||
fn on_entity_teleport_i32_i32_noground(&mut self, entity_telport: packet::play::clientbound::EntityTeleport_i32_i32_NoGround) {
|
||||
let on_ground = true; // TODO: how is this supposed to be set? (for 1.7)
|
||||
self.on_entity_teleport(entity_telport.entity_id, entity_telport.x as f64, entity_telport.y as f64, entity_telport.z as f64, entity_telport.yaw as f64, entity_telport.pitch as f64, on_ground)
|
||||
}
|
||||
|
||||
|
||||
fn on_entity_teleport(&mut self, entity_id: i32, x: f64, y: f64, z: f64, yaw: f64, pitch: f64, _on_ground: bool) {
|
||||
use std::f64::consts::PI;
|
||||
if let Some(entity) = self.entity_map.get(&entity_id) {
|
||||
|
@ -701,6 +788,11 @@ impl Server {
|
|||
self.on_entity_move(m.entity_id.0, m.delta_x as f64, m.delta_y as f64, m.delta_z as f64)
|
||||
}
|
||||
|
||||
fn on_entity_move_i8_i32_noground(&mut self, m: packet::play::clientbound::EntityMove_i8_i32_NoGround) {
|
||||
self.on_entity_move(m.entity_id, m.delta_x as f64, m.delta_y as f64, m.delta_z as f64)
|
||||
}
|
||||
|
||||
|
||||
fn on_entity_move(&mut self, entity_id: i32, delta_x: f64, delta_y: f64, delta_z: f64) {
|
||||
if let Some(entity) = self.entity_map.get(&entity_id) {
|
||||
let position = self.entities.get_component_mut(*entity, self.target_position).unwrap();
|
||||
|
@ -710,15 +802,23 @@ impl Server {
|
|||
}
|
||||
}
|
||||
|
||||
fn on_entity_look(&mut self, look: packet::play::clientbound::EntityLook) {
|
||||
fn on_entity_look(&mut self, entity_id: i32, yaw: f64, pitch: f64) {
|
||||
use std::f64::consts::PI;
|
||||
if let Some(entity) = self.entity_map.get(&look.entity_id.0) {
|
||||
if let Some(entity) = self.entity_map.get(&entity_id) {
|
||||
let rotation = self.entities.get_component_mut(*entity, self.target_rotation).unwrap();
|
||||
rotation.yaw = -((look.yaw as f64) / 256.0) * PI * 2.0;
|
||||
rotation.pitch = -((look.pitch as f64) / 256.0) * PI * 2.0;
|
||||
rotation.yaw = -(yaw / 256.0) * PI * 2.0;
|
||||
rotation.pitch = -(pitch / 256.0) * PI * 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
fn on_entity_look_varint(&mut self, look: packet::play::clientbound::EntityLook_VarInt) {
|
||||
self.on_entity_look(look.entity_id.0, look.yaw as f64, look.pitch as f64)
|
||||
}
|
||||
|
||||
fn on_entity_look_i32_noground(&mut self, look: packet::play::clientbound::EntityLook_i32_NoGround) {
|
||||
self.on_entity_look(look.entity_id, look.yaw as f64, look.pitch as f64)
|
||||
}
|
||||
|
||||
fn on_entity_look_and_move_i16(&mut self, lookmove: packet::play::clientbound::EntityLookAndMove_i16) {
|
||||
self.on_entity_look_and_move(lookmove.entity_id.0,
|
||||
lookmove.delta_x as f64, lookmove.delta_y as f64, lookmove.delta_z as f64,
|
||||
|
@ -731,6 +831,12 @@ impl Server {
|
|||
lookmove.yaw as f64, lookmove.pitch as f64)
|
||||
}
|
||||
|
||||
fn on_entity_look_and_move_i8_i32_noground(&mut self, lookmove: packet::play::clientbound::EntityLookAndMove_i8_i32_NoGround) {
|
||||
self.on_entity_look_and_move(lookmove.entity_id,
|
||||
lookmove.delta_x as f64, lookmove.delta_y as f64, lookmove.delta_z as f64,
|
||||
lookmove.yaw as f64, lookmove.pitch as f64)
|
||||
}
|
||||
|
||||
fn on_entity_look_and_move(&mut self, entity_id: i32, delta_x: f64, delta_y: f64, delta_z: f64, yaw: f64, pitch: f64) {
|
||||
use std::f64::consts::PI;
|
||||
if let Some(entity) = self.entity_map.get(&entity_id) {
|
||||
|
@ -756,6 +862,10 @@ impl Server {
|
|||
self.on_player_spawn(spawn.entity_id.0, spawn.uuid, spawn.x as f64, spawn.y as f64, spawn.z as f64, spawn.yaw as f64, spawn.pitch as f64)
|
||||
}
|
||||
|
||||
fn on_player_spawn_i32_helditem_string(&mut self, spawn: packet::play::clientbound::SpawnPlayer_i32_HeldItem_String) {
|
||||
self.on_player_spawn(spawn.entity_id.0, protocol::UUID::from_str(&spawn.uuid), spawn.x as f64, spawn.y as f64, spawn.z as f64, spawn.yaw as f64, spawn.pitch as f64)
|
||||
}
|
||||
|
||||
fn on_player_spawn(&mut self, entity_id: i32, uuid: protocol::UUID, x: f64, y: f64, z: f64, pitch: f64, yaw: f64) {
|
||||
use std::f64::consts::PI;
|
||||
if let Some(entity) = self.entity_map.remove(&entity_id) {
|
||||
|
@ -855,6 +965,10 @@ impl Server {
|
|||
}
|
||||
}
|
||||
|
||||
fn on_block_entity_update_data(&mut self, _block_update: packet::play::clientbound::UpdateBlockEntity_Data) {
|
||||
// TODO: handle UpdateBlockEntity_Data for 1.7, decompress gzipped_nbt
|
||||
}
|
||||
|
||||
fn on_sign_update(&mut self, mut update_sign: packet::play::clientbound::UpdateSign) {
|
||||
use crate::format;
|
||||
format::convert_legacy(&mut update_sign.line1);
|
||||
|
@ -870,6 +984,26 @@ impl Server {
|
|||
));
|
||||
}
|
||||
|
||||
fn on_sign_update_u16(&mut self, mut update_sign: packet::play::clientbound::UpdateSign_u16) {
|
||||
use crate::format;
|
||||
format::convert_legacy(&mut update_sign.line1);
|
||||
format::convert_legacy(&mut update_sign.line2);
|
||||
format::convert_legacy(&mut update_sign.line3);
|
||||
format::convert_legacy(&mut update_sign.line4);
|
||||
self.world.add_block_entity_action(world::BlockEntityAction::UpdateSignText(
|
||||
Position::new(update_sign.x, update_sign.y as i32, update_sign.z),
|
||||
update_sign.line1,
|
||||
update_sign.line2,
|
||||
update_sign.line3,
|
||||
update_sign.line4,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
fn on_player_info_string(&mut self, _player_info: packet::play::clientbound::PlayerInfo_String) {
|
||||
// TODO: support PlayerInfo_String for 1.7
|
||||
}
|
||||
|
||||
fn on_player_info(&mut self, player_info: packet::play::clientbound::PlayerInfo) {
|
||||
use crate::protocol::packet::PlayerDetail::*;
|
||||
use base64;
|
||||
|
@ -1003,23 +1137,39 @@ impl Server {
|
|||
self.world.load_chunks18(chunk_data.new, skylight, &chunk_meta, chunk_data.data.data).unwrap();
|
||||
}
|
||||
|
||||
fn on_chunk_data_17(&mut self, chunk_data: packet::play::clientbound::ChunkData_17) {
|
||||
self.world.load_chunk17(chunk_data.chunk_x, chunk_data.chunk_z, chunk_data.new, chunk_data.bitmask, chunk_data.add_bitmask, chunk_data.compressed_data.data).unwrap();
|
||||
}
|
||||
|
||||
fn on_chunk_data_bulk(&mut self, bulk: packet::play::clientbound::ChunkDataBulk) {
|
||||
let new = true;
|
||||
self.world.load_chunks18(new, bulk.skylight, &bulk.chunk_meta.data, bulk.chunk_data.to_vec()).unwrap();
|
||||
}
|
||||
|
||||
fn on_chunk_data_bulk_17(&mut self, bulk: packet::play::clientbound::ChunkDataBulk_17) {
|
||||
self.world.load_chunks17(bulk.chunk_column_count, bulk.data_length, bulk.skylight, &bulk.chunk_data_and_meta).unwrap();
|
||||
}
|
||||
|
||||
fn on_chunk_unload(&mut self, chunk_unload: packet::play::clientbound::ChunkUnload) {
|
||||
self.world.unload_chunk(chunk_unload.x, chunk_unload.z, &mut self.entities);
|
||||
}
|
||||
|
||||
fn on_block_change(&mut self, block_change: packet::play::clientbound::BlockChange) {
|
||||
self.world.set_block(
|
||||
block_change.location,
|
||||
block::Block::by_vanilla_id(block_change.block_id.0 as usize)
|
||||
fn on_block_change(&mut self, location: Position, id: i32) {
|
||||
self.world.set_block(location, block::Block::by_vanilla_id(id as usize))
|
||||
}
|
||||
|
||||
fn on_block_change_varint(&mut self, block_change: packet::play::clientbound::BlockChange_VarInt) {
|
||||
self.on_block_change(block_change.location, block_change.block_id.0)
|
||||
}
|
||||
|
||||
fn on_block_change_u8(&mut self, block_change: packet::play::clientbound::BlockChange_u8) {
|
||||
self.on_block_change(
|
||||
crate::shared::Position::new(block_change.x, block_change.y as i32, block_change.z),
|
||||
(block_change.block_id.0 << 4) | (block_change.block_metadata as i32)
|
||||
);
|
||||
}
|
||||
|
||||
fn on_multi_block_change(&mut self, block_change: packet::play::clientbound::MultiBlockChange) {
|
||||
fn on_multi_block_change_varint(&mut self, block_change: packet::play::clientbound::MultiBlockChange_VarInt) {
|
||||
let ox = block_change.chunk_x << 4;
|
||||
let oz = block_change.chunk_z << 4;
|
||||
for record in block_change.records.data {
|
||||
|
@ -1033,6 +1183,30 @@ impl Server {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_multi_block_change_u16(&mut self, block_change: packet::play::clientbound::MultiBlockChange_u16) {
|
||||
let ox = block_change.chunk_x << 4;
|
||||
let oz = block_change.chunk_z << 4;
|
||||
|
||||
let mut data = std::io::Cursor::new(block_change.data);
|
||||
|
||||
for _ in 0 .. block_change.record_count {
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
|
||||
let record = data.read_u32::<BigEndian>().unwrap();
|
||||
|
||||
let id = record & 0x0000_ffff;
|
||||
let y = ((record & 0x00ff_0000) >> 16) as i32;
|
||||
let z = oz + ((record & 0x0f00_0000) >> 24) as i32;
|
||||
let x = ox + ((record & 0xf000_0000) >> 28) as i32;
|
||||
|
||||
self.world.set_block(
|
||||
Position::new(x, y, z),
|
||||
block::Block::by_vanilla_id(id as usize)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
use crate::protocol::Serializable;
|
||||
use crate::protocol::packet::play::serverbound::PluginMessageServerbound;
|
||||
use crate::protocol::packet::play::serverbound::PluginMessageServerbound_i16;
|
||||
|
||||
pub struct Brand {
|
||||
pub brand: String,
|
||||
|
@ -15,4 +16,15 @@ impl Brand {
|
|||
data,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: cleanup this duplication for 1.7, return either message dynamically
|
||||
pub fn as_message17(self) -> PluginMessageServerbound_i16 {
|
||||
let mut data = vec![];
|
||||
Serializable::write_to(&self.brand, &mut data).unwrap();
|
||||
PluginMessageServerbound_i16 {
|
||||
channel: "MC|Brand".into(),
|
||||
data: crate::protocol::LenPrefixedBytes::<i16>::new(data),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
218
src/world/mod.rs
218
src/world/mod.rs
|
@ -28,6 +28,8 @@ use crate::chunk_builder;
|
|||
use crate::ecs;
|
||||
use crate::entity::block_entity;
|
||||
use crate::format;
|
||||
use flate2::read::ZlibDecoder;
|
||||
use std::io::Read;
|
||||
|
||||
pub mod biome;
|
||||
mod storage;
|
||||
|
@ -558,6 +560,24 @@ impl World {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn dirty_chunks_by_bitmask(&mut self, x: i32, z: i32, mask: u16) {
|
||||
for i in 0 .. 16 {
|
||||
if mask & (1 << i) == 0 {
|
||||
continue;
|
||||
}
|
||||
for pos in [
|
||||
(-1, 0, 0), (1, 0, 0),
|
||||
(0, -1, 0), (0, 1, 0),
|
||||
(0, 0, -1), (0, 0, 1)].into_iter() {
|
||||
self.flag_section_dirty(x + pos.0, i as i32 + pos.1, z + pos.2);
|
||||
}
|
||||
self.update_range(
|
||||
(x<<4) - 1, (i<<4) - 1, (z<<4) - 1,
|
||||
(x<<4) + 17, (i<<4) + 17, (z<<4) + 17
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_chunk18(&mut self, x: i32, z: i32, new: bool, _skylight: bool, mask: u16, data: &mut std::io::Cursor<Vec<u8>>) -> Result<(), protocol::Error> {
|
||||
use std::io::Read;
|
||||
use byteorder::ReadBytesExt;
|
||||
|
@ -635,21 +655,175 @@ impl World {
|
|||
chunk.calculate_heightmap();
|
||||
}
|
||||
|
||||
for i in 0 .. 16 {
|
||||
if mask & (1 << i) == 0 {
|
||||
continue;
|
||||
}
|
||||
for pos in [
|
||||
(-1, 0, 0), (1, 0, 0),
|
||||
(0, -1, 0), (0, 1, 0),
|
||||
(0, 0, -1), (0, 0, 1)].into_iter() {
|
||||
self.flag_section_dirty(x + pos.0, i as i32 + pos.1, z + pos.2);
|
||||
}
|
||||
self.update_range(
|
||||
(x<<4) - 1, (i<<4) - 1, (z<<4) - 1,
|
||||
(x<<4) + 17, (i<<4) + 17, (z<<4) + 17
|
||||
);
|
||||
self.dirty_chunks_by_bitmask(x, z, mask);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_chunks17(&mut self, chunk_column_count: u16, data_length: i32, skylight: bool, data: &[u8]) -> Result<(), protocol::Error> {
|
||||
let compressed_chunk_data = &data[0..data_length as usize];
|
||||
let metadata = &data[data_length as usize..];
|
||||
|
||||
let mut zlib = ZlibDecoder::new(std::io::Cursor::new(compressed_chunk_data.to_vec()));
|
||||
let mut chunk_data = Vec::new();
|
||||
zlib.read_to_end(&mut chunk_data)?;
|
||||
|
||||
let mut chunk_data = std::io::Cursor::new(chunk_data);
|
||||
|
||||
// Chunk metadata
|
||||
let mut metadata = std::io::Cursor::new(metadata);
|
||||
for _i in 0..chunk_column_count {
|
||||
use byteorder::ReadBytesExt;
|
||||
|
||||
let x = metadata.read_i32::<byteorder::BigEndian>()?;
|
||||
let z = metadata.read_i32::<byteorder::BigEndian>()?;
|
||||
let mask = metadata.read_u16::<byteorder::BigEndian>()?;
|
||||
let mask_add = metadata.read_u16::<byteorder::BigEndian>()?;
|
||||
|
||||
let new = true;
|
||||
|
||||
self.load_uncompressed_chunk17(x, z, new, skylight, mask, mask_add, &mut chunk_data)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_chunk17(&mut self, x: i32, z: i32, new: bool, mask: u16, mask_add: u16, compressed_data: Vec<u8>) -> Result<(), protocol::Error> {
|
||||
let mut zlib = ZlibDecoder::new(std::io::Cursor::new(compressed_data.to_vec()));
|
||||
let mut data = Vec::new();
|
||||
zlib.read_to_end(&mut data)?;
|
||||
|
||||
let skylight = true;
|
||||
self.load_uncompressed_chunk17(x, z, new, skylight, mask, mask_add, &mut std::io::Cursor::new(data))
|
||||
}
|
||||
|
||||
fn load_uncompressed_chunk17(&mut self, x: i32, z: i32, new: bool, skylight: bool, mask: u16, mask_add: u16, data: &mut std::io::Cursor<Vec<u8>>) -> Result<(), protocol::Error> {
|
||||
use std::io::Read;
|
||||
use crate::types::nibble;
|
||||
|
||||
let cpos = CPos(x, z);
|
||||
{
|
||||
let chunk = if new {
|
||||
self.chunks.insert(cpos, Chunk::new(cpos));
|
||||
self.chunks.get_mut(&cpos).unwrap()
|
||||
} else {
|
||||
if !self.chunks.contains_key(&cpos) {
|
||||
return Ok(());
|
||||
}
|
||||
self.chunks.get_mut(&cpos).unwrap()
|
||||
};
|
||||
|
||||
// Block type array - whole byte per block
|
||||
let mut block_types = [[0u8; 4096]; 16];
|
||||
for i in 0 .. 16 {
|
||||
if chunk.sections[i].is_none() {
|
||||
let mut fill_sky = chunk.sections.iter()
|
||||
.skip(i)
|
||||
.all(|v| v.is_none());
|
||||
fill_sky &= (mask & !((1 << i) | ((1 << i) - 1))) == 0;
|
||||
if !fill_sky || mask & (1 << i) != 0 {
|
||||
chunk.sections[i] = Some(Section::new(i as u8, fill_sky));
|
||||
}
|
||||
}
|
||||
if mask & (1 << i) == 0 {
|
||||
continue;
|
||||
}
|
||||
let section = chunk.sections[i as usize].as_mut().unwrap();
|
||||
section.dirty = true;
|
||||
|
||||
data.read_exact(&mut block_types[i])?;
|
||||
}
|
||||
|
||||
// Block metadata array - half byte per block
|
||||
let mut block_meta: [nibble::Array; 16] = [
|
||||
// TODO: cleanup this initialization
|
||||
nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16),
|
||||
nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16),
|
||||
nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16),
|
||||
nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16),
|
||||
];
|
||||
|
||||
for i in 0 .. 16 {
|
||||
if mask & (1 << i) == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
data.read_exact(&mut block_meta[i].data)?;
|
||||
}
|
||||
|
||||
// Block light array - half byte per block
|
||||
for i in 0 .. 16 {
|
||||
if mask & (1 << i) == 0 {
|
||||
continue;
|
||||
}
|
||||
let section = chunk.sections[i as usize].as_mut().unwrap();
|
||||
|
||||
data.read_exact(&mut section.block_light.data)?;
|
||||
}
|
||||
|
||||
// Sky light array - half byte per block - only if 'skylight' is true
|
||||
if skylight {
|
||||
for i in 0 .. 16 {
|
||||
if mask & (1 << i) == 0 {
|
||||
continue;
|
||||
}
|
||||
let section = chunk.sections[i as usize].as_mut().unwrap();
|
||||
|
||||
data.read_exact(&mut section.sky_light.data)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Add array - half byte per block - uses secondary bitmask
|
||||
let mut block_add: [nibble::Array; 16] = [
|
||||
// TODO: cleanup this initialization
|
||||
nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16),
|
||||
nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16),
|
||||
nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16),
|
||||
nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16), nibble::Array::new(16 * 16 * 16),
|
||||
];
|
||||
|
||||
for i in 0 .. 16 {
|
||||
if mask_add & (1 << i) == 0 {
|
||||
continue;
|
||||
}
|
||||
data.read_exact(&mut block_add[i].data)?;
|
||||
}
|
||||
|
||||
// Now that we have the block types, metadata, and add, combine to initialize the blocks
|
||||
for i in 0 .. 16 {
|
||||
if mask & (1 << i) == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let section = chunk.sections[i as usize].as_mut().unwrap();
|
||||
|
||||
for bi in 0 .. 4096 {
|
||||
let id = ((block_add[i].get(bi) as u16) << 12) | ((block_types[i][bi] as u16) << 4) | (block_meta[i].get(bi) as u16);
|
||||
section.blocks.set(bi, block::Block::by_vanilla_id(id as usize));
|
||||
|
||||
// Spawn block entities
|
||||
let b = section.blocks.get(bi);
|
||||
if block_entity::BlockEntityType::get_block_entity(b).is_some() {
|
||||
let pos = Position::new(
|
||||
(bi & 0xF) as i32,
|
||||
(bi >> 8) as i32,
|
||||
((bi >> 4) & 0xF) as i32
|
||||
) + (chunk.position.0 << 4, (i << 4) as i32, chunk.position.1 << 4);
|
||||
if chunk.block_entities.contains_key(&pos) {
|
||||
self.block_entity_actions.push_back(BlockEntityAction::Remove(pos))
|
||||
}
|
||||
self.block_entity_actions.push_back(BlockEntityAction::Create(pos))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if new {
|
||||
data.read_exact(&mut chunk.biomes)?;
|
||||
}
|
||||
|
||||
chunk.calculate_heightmap();
|
||||
}
|
||||
|
||||
self.dirty_chunks_by_bitmask(x, z, mask);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -733,21 +907,7 @@ impl World {
|
|||
chunk.calculate_heightmap();
|
||||
}
|
||||
|
||||
for i in 0 .. 16 {
|
||||
if mask & (1 << i) == 0 {
|
||||
continue;
|
||||
}
|
||||
for pos in [
|
||||
(-1, 0, 0), (1, 0, 0),
|
||||
(0, -1, 0), (0, 1, 0),
|
||||
(0, 0, -1), (0, 0, 1)].into_iter() {
|
||||
self.flag_section_dirty(x + pos.0, i as i32 + pos.1, z + pos.2);
|
||||
}
|
||||
self.update_range(
|
||||
(x<<4) - 1, (i<<4) - 1, (z<<4) - 1,
|
||||
(x<<4) + 17, (i<<4) + 17, (z<<4) + 17
|
||||
);
|
||||
}
|
||||
self.dirty_chunks_by_bitmask(x, z, mask);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue