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 {
|
if id == -1 {
|
||||||
return Ok(None);
|
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 {
|
Ok(Some(Stack {
|
||||||
id: id as isize,
|
id: id as isize,
|
||||||
count: buf.read_u8()? as isize,
|
count,
|
||||||
damage: buf.read_i16::<BigEndian>()? as isize,
|
damage,
|
||||||
tag: Serializable::read_from(buf)?,
|
tag,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), protocol::Error> {
|
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_i16::<BigEndian>(val.id as i16)?;
|
||||||
buf.write_u8(val.count as u8)?;
|
buf.write_u8(val.count as u8)?;
|
||||||
buf.write_i16::<BigEndian>(val.damage as i16)?;
|
buf.write_i16::<BigEndian>(val.damage as i16)?;
|
||||||
|
// TODO: compress zlib NBT if 1.7
|
||||||
val.tag.write_to(buf)?;
|
val.tag.write_to(buf)?;
|
||||||
}
|
}
|
||||||
None => buf.write_i16::<BigEndian>(-1)?,
|
None => buf.write_i16::<BigEndian>(-1)?,
|
||||||
|
|
|
@ -37,7 +37,7 @@ use flate2::Compression;
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
use crate::shared::Position;
|
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
|
// 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];
|
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 {
|
impl Lengthable for i16 {
|
||||||
fn into(self) -> usize {
|
fn into(self) -> usize {
|
||||||
self as usize
|
self as usize
|
||||||
|
|
|
@ -66,6 +66,9 @@ state_packets!(
|
||||||
field has_target: bool =,
|
field has_target: bool =,
|
||||||
field target: Option<Position> = when(|p: &TabComplete_NoAssume| p.has_target),
|
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
|
/// ChatMessage is sent by the client when it sends a chat message or
|
||||||
/// executes a command (prefixed by '/').
|
/// executes a command (prefixed by '/').
|
||||||
packet ChatMessage {
|
packet ChatMessage {
|
||||||
|
@ -75,6 +78,9 @@ state_packets!(
|
||||||
packet ClientStatus {
|
packet ClientStatus {
|
||||||
field action_id: VarInt =,
|
field action_id: VarInt =,
|
||||||
}
|
}
|
||||||
|
packet ClientStatus_u8 {
|
||||||
|
field action_id: u8=,
|
||||||
|
}
|
||||||
/// ClientSettings is sent by the client to update its current settings.
|
/// ClientSettings is sent by the client to update its current settings.
|
||||||
packet ClientSettings {
|
packet ClientSettings {
|
||||||
field locale: String =,
|
field locale: String =,
|
||||||
|
@ -99,6 +105,14 @@ state_packets!(
|
||||||
field chat_colors: bool =,
|
field chat_colors: bool =,
|
||||||
field displayed_skin_parts: u8 =,
|
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.
|
/// ConfirmTransactionServerbound is a reply to ConfirmTransaction.
|
||||||
packet ConfirmTransactionServerbound {
|
packet ConfirmTransactionServerbound {
|
||||||
field id: u8 =,
|
field id: u8 =,
|
||||||
|
@ -138,6 +152,10 @@ state_packets!(
|
||||||
field channel: String =,
|
field channel: String =,
|
||||||
field data: Vec<u8> =,
|
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
|
/// UseEntity is sent when the user interacts (right clicks) or attacks
|
||||||
/// (left clicks) an entity.
|
/// (left clicks) an entity.
|
||||||
packet UseEntity {
|
packet UseEntity {
|
||||||
|
@ -155,6 +173,10 @@ state_packets!(
|
||||||
field target_y: f32 = when(|p: &UseEntity_Handsfree| p.ty.0 == 2),
|
field target_y: f32 = when(|p: &UseEntity_Handsfree| p.ty.0 == 2),
|
||||||
field target_z: 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
|
/// KeepAliveServerbound is sent by a client as a response to a
|
||||||
/// KeepAliveClientbound. If the client doesn't reply the server
|
/// KeepAliveClientbound. If the client doesn't reply the server
|
||||||
/// may disconnect the client.
|
/// may disconnect the client.
|
||||||
|
@ -164,6 +186,9 @@ state_packets!(
|
||||||
packet KeepAliveServerbound_VarInt {
|
packet KeepAliveServerbound_VarInt {
|
||||||
field id: VarInt =,
|
field id: VarInt =,
|
||||||
}
|
}
|
||||||
|
packet KeepAliveServerbound_i32 {
|
||||||
|
field id: i32 =,
|
||||||
|
}
|
||||||
/// PlayerPosition is used to update the player's position.
|
/// PlayerPosition is used to update the player's position.
|
||||||
packet PlayerPosition {
|
packet PlayerPosition {
|
||||||
field x: f64 =,
|
field x: f64 =,
|
||||||
|
@ -171,6 +196,13 @@ state_packets!(
|
||||||
field z: f64 =,
|
field z: f64 =,
|
||||||
field on_ground: bool =,
|
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
|
/// PlayerPositionLook is a combination of PlayerPosition and
|
||||||
/// PlayerLook.
|
/// PlayerLook.
|
||||||
packet PlayerPositionLook {
|
packet PlayerPositionLook {
|
||||||
|
@ -181,6 +213,15 @@ state_packets!(
|
||||||
field pitch: f32 =,
|
field pitch: f32 =,
|
||||||
field on_ground: bool =,
|
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.
|
/// PlayerLook is used to update the player's rotation.
|
||||||
packet PlayerLook {
|
packet PlayerLook {
|
||||||
field yaw: f32 =,
|
field yaw: f32 =,
|
||||||
|
@ -229,12 +270,24 @@ state_packets!(
|
||||||
field location: Position =,
|
field location: Position =,
|
||||||
field face: u8 =,
|
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.
|
/// PlayerAction is sent when a player preforms various actions.
|
||||||
packet PlayerAction {
|
packet PlayerAction {
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
field action_id: VarInt =,
|
field action_id: VarInt =,
|
||||||
field jump_boost: 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
|
/// SteerVehicle is sent by the client when steers or preforms an action
|
||||||
/// on a vehicle.
|
/// on a vehicle.
|
||||||
packet SteerVehicle {
|
packet SteerVehicle {
|
||||||
|
@ -242,6 +295,12 @@ state_packets!(
|
||||||
field forward: f32 =,
|
field forward: f32 =,
|
||||||
field flags: u8 =,
|
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.
|
/// CraftingBookData is sent when the player interacts with the crafting book.
|
||||||
packet CraftingBookData {
|
packet CraftingBookData {
|
||||||
field action: VarInt =,
|
field action: VarInt =,
|
||||||
|
@ -282,6 +341,15 @@ state_packets!(
|
||||||
field line3: String =,
|
field line3: String =,
|
||||||
field line4: 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
|
/// ArmSwing is sent by the client when the player left clicks (to swing their
|
||||||
/// arm).
|
/// arm).
|
||||||
packet ArmSwing {
|
packet ArmSwing {
|
||||||
|
@ -290,6 +358,10 @@ state_packets!(
|
||||||
packet ArmSwing_Handsfree {
|
packet ArmSwing_Handsfree {
|
||||||
field empty: () =,
|
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.
|
/// SpectateTeleport is sent by clients in spectator mode to teleport to a player.
|
||||||
packet SpectateTeleport {
|
packet SpectateTeleport {
|
||||||
field target: UUID =,
|
field target: UUID =,
|
||||||
|
@ -319,6 +391,16 @@ state_packets!(
|
||||||
field cursor_y: u8 =,
|
field cursor_y: u8 =,
|
||||||
field cursor_z: 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.
|
/// UseItem is sent when the client tries to use an item.
|
||||||
packet UseItem {
|
packet UseItem {
|
||||||
|
@ -478,6 +560,14 @@ state_packets!(
|
||||||
field location: Position =,
|
field location: Position =,
|
||||||
field direction: u8 =,
|
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.
|
/// 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
|
/// This packet alone isn't enough to display the player as the skin and username
|
||||||
/// information is in the player information packet.
|
/// information is in the player information packet.
|
||||||
|
@ -512,6 +602,20 @@ state_packets!(
|
||||||
field current_item: u16 =,
|
field current_item: u16 =,
|
||||||
field metadata: types::Metadata =,
|
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.
|
/// Animation is sent by the server to play an animation on a specific entity.
|
||||||
packet Animation {
|
packet Animation {
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
|
@ -528,6 +632,13 @@ state_packets!(
|
||||||
field location: Position =,
|
field location: Position =,
|
||||||
field stage: i8 =,
|
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
|
/// UpdateBlockEntity updates the nbt tag of a block entity in the
|
||||||
/// world.
|
/// world.
|
||||||
packet UpdateBlockEntity {
|
packet UpdateBlockEntity {
|
||||||
|
@ -535,6 +646,14 @@ state_packets!(
|
||||||
field action: u8 =,
|
field action: u8 =,
|
||||||
field nbt: Option<nbt::NamedTag> =,
|
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.
|
/// BlockAction triggers different actions depending on the target block.
|
||||||
packet BlockAction {
|
packet BlockAction {
|
||||||
field location: Position =,
|
field location: Position =,
|
||||||
|
@ -542,11 +661,26 @@ state_packets!(
|
||||||
field byte2: u8 =,
|
field byte2: u8 =,
|
||||||
field block_type: VarInt =,
|
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.
|
/// BlockChange is used to update a single block on the client.
|
||||||
packet BlockChange {
|
packet BlockChange_VarInt {
|
||||||
field location: Position =,
|
field location: Position =,
|
||||||
field block_id: VarInt =,
|
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
|
/// 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
|
/// top of the client's screen. This is normally used for bosses such as
|
||||||
/// the ender dragon or the wither.
|
/// the ender dragon or the wither.
|
||||||
|
@ -578,12 +712,22 @@ state_packets!(
|
||||||
/// 0 - Chat message, 1 - System message, 2 - Action bar message
|
/// 0 - Chat message, 1 - System message, 2 - Action bar message
|
||||||
field position: u8 =,
|
field position: u8 =,
|
||||||
}
|
}
|
||||||
|
packet ServerMessage_NoPosition {
|
||||||
|
field message: format::Component =,
|
||||||
|
}
|
||||||
/// MultiBlockChange is used to update a batch of blocks in a single packet.
|
/// MultiBlockChange is used to update a batch of blocks in a single packet.
|
||||||
packet MultiBlockChange {
|
packet MultiBlockChange_VarInt {
|
||||||
field chunk_x: i32 =,
|
field chunk_x: i32 =,
|
||||||
field chunk_z: i32 =,
|
field chunk_z: i32 =,
|
||||||
field records: LenPrefixed<VarInt, packet::BlockChangeRecord> =,
|
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
|
/// ConfirmTransaction notifies the client whether a transaction was successful
|
||||||
/// or failed (e.g. due to lag).
|
/// or failed (e.g. due to lag).
|
||||||
packet ConfirmTransaction {
|
packet ConfirmTransaction {
|
||||||
|
@ -606,6 +750,14 @@ state_packets!(
|
||||||
field slot_count: u8 =,
|
field slot_count: u8 =,
|
||||||
field entity_id: i32 = when(|p: &WindowOpen| p.ty == "EntityHorse"),
|
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.
|
/// WindowItems sets every item in a window.
|
||||||
packet WindowItems {
|
packet WindowItems {
|
||||||
field id: u8 =,
|
field id: u8 =,
|
||||||
|
@ -636,6 +788,10 @@ state_packets!(
|
||||||
field channel: String =,
|
field channel: String =,
|
||||||
field data: Vec<u8> =,
|
field data: Vec<u8> =,
|
||||||
}
|
}
|
||||||
|
packet PluginMessageClientbound_i16 {
|
||||||
|
field channel: String =,
|
||||||
|
field data: LenPrefixedBytes<i16> =,
|
||||||
|
}
|
||||||
/// Plays a sound by name on the client
|
/// Plays a sound by name on the client
|
||||||
packet NamedSoundEffect {
|
packet NamedSoundEffect {
|
||||||
field name: String =,
|
field name: String =,
|
||||||
|
@ -711,6 +867,9 @@ state_packets!(
|
||||||
packet KeepAliveClientbound_VarInt {
|
packet KeepAliveClientbound_VarInt {
|
||||||
field id: VarInt =,
|
field id: VarInt =,
|
||||||
}
|
}
|
||||||
|
packet KeepAliveClientbound_i32 {
|
||||||
|
field id: i32 =,
|
||||||
|
}
|
||||||
/// ChunkData sends or updates a single chunk on the client. If New is set
|
/// ChunkData sends or updates a single chunk on the client. If New is set
|
||||||
/// then biome data should be sent too.
|
/// then biome data should be sent too.
|
||||||
packet ChunkData {
|
packet ChunkData {
|
||||||
|
@ -735,11 +894,25 @@ state_packets!(
|
||||||
field bitmask: u16 =,
|
field bitmask: u16 =,
|
||||||
field data: LenPrefixedBytes<VarInt> =,
|
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 {
|
packet ChunkDataBulk {
|
||||||
field skylight: bool =,
|
field skylight: bool =,
|
||||||
field chunk_meta: LenPrefixed<VarInt, packet::ChunkMeta> =,
|
field chunk_meta: LenPrefixed<VarInt, packet::ChunkMeta> =,
|
||||||
field chunk_data: Vec<u8> =,
|
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
|
/// Effect plays a sound effect or particle at the target location with the
|
||||||
/// volume (of sounds) being relative to the player's position unless
|
/// volume (of sounds) being relative to the player's position unless
|
||||||
/// DisableRelative is set to true.
|
/// DisableRelative is set to true.
|
||||||
|
@ -749,6 +922,14 @@ state_packets!(
|
||||||
field data: i32 =,
|
field data: i32 =,
|
||||||
field disable_relative: bool =,
|
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
|
/// Particle spawns particles at the target location with the various
|
||||||
/// modifiers.
|
/// modifiers.
|
||||||
packet Particle {
|
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 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),
|
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
|
/// JoinGame is sent after completing the login process. This
|
||||||
/// sets the initial state for the client.
|
/// sets the initial state for the client.
|
||||||
packet JoinGame_i32 {
|
packet JoinGame_i32 {
|
||||||
|
@ -801,6 +993,14 @@ state_packets!(
|
||||||
/// information it displays in F3 mode
|
/// information it displays in F3 mode
|
||||||
field reduced_debug_info: bool =,
|
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
|
/// Maps updates a single map's contents
|
||||||
packet Maps {
|
packet Maps {
|
||||||
field item_damage: VarInt =,
|
field item_damage: VarInt =,
|
||||||
|
@ -823,6 +1023,10 @@ state_packets!(
|
||||||
field z: Option<u8> = when(|p: &Maps_NoTracking| p.columns > 0),
|
field z: Option<u8> = when(|p: &Maps_NoTracking| p.columns > 0),
|
||||||
field data: Option<LenPrefixedBytes<VarInt>> = 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.
|
/// EntityMove moves the entity with the id by the offsets provided.
|
||||||
packet EntityMove_i16 {
|
packet EntityMove_i16 {
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
|
@ -838,6 +1042,12 @@ state_packets!(
|
||||||
field delta_z: i8 =,
|
field delta_z: i8 =,
|
||||||
field on_ground: bool =,
|
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.
|
/// EntityLookAndMove is a combination of EntityMove and EntityLook.
|
||||||
packet EntityLookAndMove_i16 {
|
packet EntityLookAndMove_i16 {
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
|
@ -857,17 +1067,33 @@ state_packets!(
|
||||||
field pitch: i8 =,
|
field pitch: i8 =,
|
||||||
field on_ground: bool =,
|
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.
|
/// EntityLook rotates the entity to the new angles provided.
|
||||||
packet EntityLook {
|
packet EntityLook_VarInt {
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
field yaw: i8 =,
|
field yaw: i8 =,
|
||||||
field pitch: i8 =,
|
field pitch: i8 =,
|
||||||
field on_ground: bool =,
|
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.
|
/// Entity does nothing. It is a result of subclassing used in Minecraft.
|
||||||
packet Entity {
|
packet Entity {
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
}
|
}
|
||||||
|
packet Entity_i32 {
|
||||||
|
field entity_id: i32 =,
|
||||||
|
}
|
||||||
/// EntityUpdateNBT updates the entity named binary tag.
|
/// EntityUpdateNBT updates the entity named binary tag.
|
||||||
packet EntityUpdateNBT {
|
packet EntityUpdateNBT {
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
|
@ -886,6 +1112,11 @@ state_packets!(
|
||||||
packet SignEditorOpen {
|
packet SignEditorOpen {
|
||||||
field location: Position =,
|
field location: Position =,
|
||||||
}
|
}
|
||||||
|
packet SignEditorOpen_i32 {
|
||||||
|
field x: i32 =,
|
||||||
|
field y: i32 =,
|
||||||
|
field z: i32 =,
|
||||||
|
}
|
||||||
/// CraftRecipeResponse is a response to CraftRecipeRequest, notifies the UI.
|
/// CraftRecipeResponse is a response to CraftRecipeRequest, notifies the UI.
|
||||||
packet CraftRecipeResponse {
|
packet CraftRecipeResponse {
|
||||||
field window_id: u8 =,
|
field window_id: u8 =,
|
||||||
|
@ -912,6 +1143,11 @@ state_packets!(
|
||||||
packet PlayerInfo {
|
packet PlayerInfo {
|
||||||
field inner: packet::PlayerInfoData =,
|
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
|
/// 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
|
/// to reply to the server with the same positions as contained in this packet
|
||||||
/// otherwise will reject future packets.
|
/// otherwise will reject future packets.
|
||||||
|
@ -937,6 +1173,12 @@ state_packets!(
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
field location: Position =,
|
field location: Position =,
|
||||||
}
|
}
|
||||||
|
packet EntityUsedBed_i32 {
|
||||||
|
field entity_id: i32 =,
|
||||||
|
field x: i32 =,
|
||||||
|
field y: u8 =,
|
||||||
|
field z: i32 =,
|
||||||
|
}
|
||||||
packet UnlockRecipes {
|
packet UnlockRecipes {
|
||||||
field action: VarInt =,
|
field action: VarInt =,
|
||||||
field crafting_book_open: bool =,
|
field crafting_book_open: bool =,
|
||||||
|
@ -948,11 +1190,18 @@ state_packets!(
|
||||||
packet EntityDestroy {
|
packet EntityDestroy {
|
||||||
field entity_ids: LenPrefixed<VarInt, VarInt> =,
|
field entity_ids: LenPrefixed<VarInt, VarInt> =,
|
||||||
}
|
}
|
||||||
|
packet EntityDestroy_u8 {
|
||||||
|
field entity_ids: LenPrefixed<u8, i32> =,
|
||||||
|
}
|
||||||
/// EntityRemoveEffect removes an effect from an entity.
|
/// EntityRemoveEffect removes an effect from an entity.
|
||||||
packet EntityRemoveEffect {
|
packet EntityRemoveEffect {
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
field effect_id: i8 =,
|
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
|
/// ResourcePackSend causes the client to check its cache for the requested
|
||||||
/// resource packet and download it if its missing. Once the resource pack
|
/// resource packet and download it if its missing. Once the resource pack
|
||||||
/// is obtained the client will use it.
|
/// is obtained the client will use it.
|
||||||
|
@ -972,6 +1221,10 @@ state_packets!(
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
field head_yaw: i8 =,
|
field head_yaw: i8 =,
|
||||||
}
|
}
|
||||||
|
packet EntityHeadLook_i32 {
|
||||||
|
field entity_id: i32 =,
|
||||||
|
field head_yaw: i8 =,
|
||||||
|
}
|
||||||
packet EntityStatus {
|
packet EntityStatus {
|
||||||
field entity_id: i32 =,
|
field entity_id: i32 =,
|
||||||
field entity_status: i8 =,
|
field entity_status: i8 =,
|
||||||
|
@ -1012,6 +1265,10 @@ state_packets!(
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
field metadata: types::Metadata =,
|
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.
|
/// EntityAttach attaches to entities together, either by mounting or leashing.
|
||||||
/// -1 can be used at the EntityID to deattach.
|
/// -1 can be used at the EntityID to deattach.
|
||||||
packet EntityAttach {
|
packet EntityAttach {
|
||||||
|
@ -1031,6 +1288,12 @@ state_packets!(
|
||||||
field velocity_y: i16 =,
|
field velocity_y: i16 =,
|
||||||
field velocity_z: 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
|
/// 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
|
/// or armor. Slot 0 is the held item and slots 1 to 4 are boots, leggings
|
||||||
/// chestplate and helmet respectively.
|
/// chestplate and helmet respectively.
|
||||||
|
@ -1044,18 +1307,33 @@ state_packets!(
|
||||||
field slot: u16 =,
|
field slot: u16 =,
|
||||||
field item: Option<item::Stack> =,
|
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.
|
/// SetExperience updates the experience bar on the client.
|
||||||
packet SetExperience {
|
packet SetExperience {
|
||||||
field experience_bar: f32 =,
|
field experience_bar: f32 =,
|
||||||
field level: VarInt =,
|
field level: VarInt =,
|
||||||
field total_experience: 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.
|
/// UpdateHealth is sent by the server to update the player's health and food.
|
||||||
packet UpdateHealth {
|
packet UpdateHealth {
|
||||||
field health: f32 =,
|
field health: f32 =,
|
||||||
field food: VarInt =,
|
field food: VarInt =,
|
||||||
field food_saturation: f32 =,
|
field food_saturation: f32 =,
|
||||||
}
|
}
|
||||||
|
packet UpdateHealth_u16 {
|
||||||
|
field health: f32 =,
|
||||||
|
field food: u16 =,
|
||||||
|
field food_saturation: f32 =,
|
||||||
|
}
|
||||||
/// ScoreboardObjective creates/updates a scoreboard objective.
|
/// ScoreboardObjective creates/updates a scoreboard objective.
|
||||||
packet ScoreboardObjective {
|
packet ScoreboardObjective {
|
||||||
field name: String =,
|
field name: String =,
|
||||||
|
@ -1063,6 +1341,11 @@ state_packets!(
|
||||||
field value: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2),
|
field value: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2),
|
||||||
field ty: 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
|
/// SetPassengers mounts entities to an entity
|
||||||
packet SetPassengers {
|
packet SetPassengers {
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
|
@ -1081,6 +1364,15 @@ state_packets!(
|
||||||
field color: Option<i8> = when(|p: &Teams| p.mode == 0 || p.mode == 2),
|
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),
|
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
|
/// UpdateScore is used to update or remove an item from a scoreboard
|
||||||
/// objective.
|
/// objective.
|
||||||
packet UpdateScore {
|
packet UpdateScore {
|
||||||
|
@ -1089,11 +1381,22 @@ state_packets!(
|
||||||
field object_name: String =,
|
field object_name: String =,
|
||||||
field value: Option<VarInt> = when(|p: &UpdateScore| p.action != 1),
|
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
|
/// SpawnPosition is sent to change the player's current spawn point. Currently
|
||||||
/// only used by the client for the compass.
|
/// only used by the client for the compass.
|
||||||
packet SpawnPosition {
|
packet SpawnPosition {
|
||||||
field location: Position =,
|
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
|
/// 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
|
/// 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
|
/// 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 line3: format::Component =,
|
||||||
field line4: 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.
|
/// SoundEffect plays the named sound at the target location.
|
||||||
packet SoundEffect {
|
packet SoundEffect {
|
||||||
field name: VarInt =,
|
field name: VarInt =,
|
||||||
|
@ -1171,6 +1483,10 @@ state_packets!(
|
||||||
field collected_entity_id: VarInt =,
|
field collected_entity_id: VarInt =,
|
||||||
field collector_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
|
/// EntityTeleport teleports the entity to the target location. This is
|
||||||
/// sent if the entity moves further than EntityMove allows.
|
/// sent if the entity moves further than EntityMove allows.
|
||||||
packet EntityTeleport_f64 {
|
packet EntityTeleport_f64 {
|
||||||
|
@ -1191,6 +1507,14 @@ state_packets!(
|
||||||
field pitch: i8 =,
|
field pitch: i8 =,
|
||||||
field on_ground: bool =,
|
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 {
|
packet Advancements {
|
||||||
field reset_clear: bool =,
|
field reset_clear: bool =,
|
||||||
field mapping: LenPrefixed<VarInt, packet::Advancement> =,
|
field mapping: LenPrefixed<VarInt, packet::Advancement> =,
|
||||||
|
@ -1202,6 +1526,10 @@ state_packets!(
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
field properties: LenPrefixed<i32, packet::EntityProperty> =,
|
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.
|
/// EntityEffect applies a status effect to an entity for a given duration.
|
||||||
packet EntityEffect {
|
packet EntityEffect {
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
|
@ -1210,6 +1538,12 @@ state_packets!(
|
||||||
field duration: VarInt =,
|
field duration: VarInt =,
|
||||||
field hide_particles: bool =,
|
field hide_particles: bool =,
|
||||||
}
|
}
|
||||||
|
packet EntityEffect_i32 {
|
||||||
|
field entity_id: i32 =,
|
||||||
|
field effect_id: i8 =,
|
||||||
|
field amplifier: i8 =,
|
||||||
|
field duration: i16 =,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
login Login {
|
login Login {
|
||||||
|
@ -1231,6 +1565,10 @@ state_packets!(
|
||||||
/// public key
|
/// public key
|
||||||
field verify_token: LenPrefixedBytes<VarInt> =,
|
field verify_token: LenPrefixedBytes<VarInt> =,
|
||||||
}
|
}
|
||||||
|
packet EncryptionResponse_i16 {
|
||||||
|
field shared_secret: LenPrefixedBytes<i16> =,
|
||||||
|
field verify_token: LenPrefixedBytes<i16> =,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
clientbound Clientbound {
|
clientbound Clientbound {
|
||||||
/// LoginDisconnect is sent by the server if there was any issues
|
/// LoginDisconnect is sent by the server if there was any issues
|
||||||
|
@ -1252,6 +1590,11 @@ state_packets!(
|
||||||
/// correctly
|
/// correctly
|
||||||
field verify_token: LenPrefixedBytes<VarInt> =,
|
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
|
/// LoginSuccess is sent by the server if the player successfully
|
||||||
/// authenicates with the session servers (online mode) or straight
|
/// authenicates with the session servers (online mode) or straight
|
||||||
/// after LoginStart (offline mode).
|
/// 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)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Statistic {
|
pub struct Statistic {
|
||||||
pub name: String,
|
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)]
|
#[derive(Debug, Default)]
|
||||||
pub struct PropertyModifier {
|
pub struct PropertyModifier {
|
||||||
pub uuid: UUID,
|
pub uuid: UUID,
|
||||||
|
|
|
@ -7,6 +7,7 @@ mod v1_9_2;
|
||||||
mod v1_9;
|
mod v1_9;
|
||||||
mod v15w39c;
|
mod v15w39c;
|
||||||
mod v1_8_9;
|
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 {
|
pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 {
|
||||||
match version {
|
match version {
|
||||||
|
@ -36,6 +37,9 @@ pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir:
|
||||||
// 1.8.9 - 1.8
|
// 1.8.9 - 1.8
|
||||||
47 => v1_8_9::translate_internal_packet_id(state, dir, id, to_internal),
|
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"),
|
_ => panic!("unsupported protocol version"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,12 +48,12 @@ protocol_packet_ids!(
|
||||||
0x08 => BlockBreakAnimation
|
0x08 => BlockBreakAnimation
|
||||||
0x09 => UpdateBlockEntity
|
0x09 => UpdateBlockEntity
|
||||||
0x0a => BlockAction
|
0x0a => BlockAction
|
||||||
0x0b => BlockChange
|
0x0b => BlockChange_VarInt
|
||||||
0x0c => BossBar
|
0x0c => BossBar
|
||||||
0x0d => ServerDifficulty
|
0x0d => ServerDifficulty
|
||||||
0x0e => TabCompleteReply
|
0x0e => TabCompleteReply
|
||||||
0x0f => ServerMessage
|
0x0f => ServerMessage
|
||||||
0x10 => MultiBlockChange
|
0x10 => MultiBlockChange_VarInt
|
||||||
0x11 => ConfirmTransaction
|
0x11 => ConfirmTransaction
|
||||||
0x12 => WindowClose
|
0x12 => WindowClose
|
||||||
0x13 => WindowOpen
|
0x13 => WindowOpen
|
||||||
|
@ -77,7 +77,7 @@ protocol_packet_ids!(
|
||||||
0x25 => Maps
|
0x25 => Maps
|
||||||
0x26 => EntityMove_i8
|
0x26 => EntityMove_i8
|
||||||
0x27 => EntityLookAndMove_i8
|
0x27 => EntityLookAndMove_i8
|
||||||
0x28 => EntityLook
|
0x28 => EntityLook_VarInt
|
||||||
0x29 => Entity
|
0x29 => Entity
|
||||||
0x2a => SignEditorOpen
|
0x2a => SignEditorOpen
|
||||||
0x2b => PlayerAbilities
|
0x2b => PlayerAbilities
|
||||||
|
|
|
@ -51,12 +51,12 @@ protocol_packet_ids!(
|
||||||
0x08 => BlockBreakAnimation
|
0x08 => BlockBreakAnimation
|
||||||
0x09 => UpdateBlockEntity
|
0x09 => UpdateBlockEntity
|
||||||
0x0a => BlockAction
|
0x0a => BlockAction
|
||||||
0x0b => BlockChange
|
0x0b => BlockChange_VarInt
|
||||||
0x0c => BossBar
|
0x0c => BossBar
|
||||||
0x0d => ServerDifficulty
|
0x0d => ServerDifficulty
|
||||||
0x0e => TabCompleteReply
|
0x0e => TabCompleteReply
|
||||||
0x0f => ServerMessage
|
0x0f => ServerMessage
|
||||||
0x10 => MultiBlockChange
|
0x10 => MultiBlockChange_VarInt
|
||||||
0x11 => ConfirmTransaction
|
0x11 => ConfirmTransaction
|
||||||
0x12 => WindowClose
|
0x12 => WindowClose
|
||||||
0x13 => WindowOpen
|
0x13 => WindowOpen
|
||||||
|
@ -79,7 +79,7 @@ protocol_packet_ids!(
|
||||||
0x24 => Maps
|
0x24 => Maps
|
||||||
0x25 => EntityMove_i16
|
0x25 => EntityMove_i16
|
||||||
0x26 => EntityLookAndMove_i16
|
0x26 => EntityLookAndMove_i16
|
||||||
0x27 => EntityLook
|
0x27 => EntityLook_VarInt
|
||||||
0x28 => Entity
|
0x28 => Entity
|
||||||
0x29 => VehicleTeleport
|
0x29 => VehicleTeleport
|
||||||
0x2a => SignEditorOpen
|
0x2a => SignEditorOpen
|
||||||
|
|
|
@ -51,12 +51,12 @@ protocol_packet_ids!(
|
||||||
0x08 => BlockBreakAnimation
|
0x08 => BlockBreakAnimation
|
||||||
0x09 => UpdateBlockEntity
|
0x09 => UpdateBlockEntity
|
||||||
0x0a => BlockAction
|
0x0a => BlockAction
|
||||||
0x0b => BlockChange
|
0x0b => BlockChange_VarInt
|
||||||
0x0c => BossBar
|
0x0c => BossBar
|
||||||
0x0d => ServerDifficulty
|
0x0d => ServerDifficulty
|
||||||
0x0e => TabCompleteReply
|
0x0e => TabCompleteReply
|
||||||
0x0f => ServerMessage
|
0x0f => ServerMessage
|
||||||
0x10 => MultiBlockChange
|
0x10 => MultiBlockChange_VarInt
|
||||||
0x11 => ConfirmTransaction
|
0x11 => ConfirmTransaction
|
||||||
0x12 => WindowClose
|
0x12 => WindowClose
|
||||||
0x13 => WindowOpen
|
0x13 => WindowOpen
|
||||||
|
@ -79,7 +79,7 @@ protocol_packet_ids!(
|
||||||
0x24 => Maps
|
0x24 => Maps
|
||||||
0x25 => EntityMove_i16
|
0x25 => EntityMove_i16
|
||||||
0x26 => EntityLookAndMove_i16
|
0x26 => EntityLookAndMove_i16
|
||||||
0x27 => EntityLook
|
0x27 => EntityLook_VarInt
|
||||||
0x28 => Entity
|
0x28 => Entity
|
||||||
0x29 => VehicleTeleport
|
0x29 => VehicleTeleport
|
||||||
0x2a => SignEditorOpen
|
0x2a => SignEditorOpen
|
||||||
|
|
|
@ -54,12 +54,12 @@ protocol_packet_ids!(
|
||||||
0x08 => BlockBreakAnimation
|
0x08 => BlockBreakAnimation
|
||||||
0x09 => UpdateBlockEntity
|
0x09 => UpdateBlockEntity
|
||||||
0x0a => BlockAction
|
0x0a => BlockAction
|
||||||
0x0b => BlockChange
|
0x0b => BlockChange_VarInt
|
||||||
0x0c => BossBar
|
0x0c => BossBar
|
||||||
0x0d => ServerDifficulty
|
0x0d => ServerDifficulty
|
||||||
0x0e => TabCompleteReply
|
0x0e => TabCompleteReply
|
||||||
0x0f => ServerMessage
|
0x0f => ServerMessage
|
||||||
0x10 => MultiBlockChange
|
0x10 => MultiBlockChange_VarInt
|
||||||
0x11 => ConfirmTransaction
|
0x11 => ConfirmTransaction
|
||||||
0x12 => WindowClose
|
0x12 => WindowClose
|
||||||
0x13 => WindowOpen
|
0x13 => WindowOpen
|
||||||
|
@ -83,7 +83,7 @@ protocol_packet_ids!(
|
||||||
0x25 => Entity
|
0x25 => Entity
|
||||||
0x26 => EntityMove_i16
|
0x26 => EntityMove_i16
|
||||||
0x27 => EntityLookAndMove_i16
|
0x27 => EntityLookAndMove_i16
|
||||||
0x28 => EntityLook
|
0x28 => EntityLook_VarInt
|
||||||
0x29 => VehicleTeleport
|
0x29 => VehicleTeleport
|
||||||
0x2a => SignEditorOpen
|
0x2a => SignEditorOpen
|
||||||
0x2b => CraftRecipeResponse
|
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
|
0x13 => EntityDestroy
|
||||||
0x14 => Entity
|
0x14 => Entity
|
||||||
0x15 => EntityMove_i8
|
0x15 => EntityMove_i8
|
||||||
0x16 => EntityLook
|
0x16 => EntityLook_VarInt
|
||||||
0x17 => EntityLookAndMove_i8
|
0x17 => EntityLookAndMove_i8
|
||||||
0x18 => EntityTeleport_i32
|
0x18 => EntityTeleport_i32
|
||||||
0x19 => EntityHeadLook
|
0x19 => EntityHeadLook
|
||||||
|
@ -70,8 +70,8 @@ protocol_packet_ids!(
|
||||||
0x1f => SetExperience
|
0x1f => SetExperience
|
||||||
0x20 => EntityProperties
|
0x20 => EntityProperties
|
||||||
0x21 => ChunkData_NoEntities_u16
|
0x21 => ChunkData_NoEntities_u16
|
||||||
0x22 => MultiBlockChange
|
0x22 => MultiBlockChange_VarInt
|
||||||
0x23 => BlockChange
|
0x23 => BlockChange_VarInt
|
||||||
0x24 => BlockAction
|
0x24 => BlockAction
|
||||||
0x25 => BlockBreakAnimation
|
0x25 => BlockBreakAnimation
|
||||||
0x26 => ChunkDataBulk
|
0x26 => ChunkDataBulk
|
||||||
|
|
|
@ -51,12 +51,12 @@ protocol_packet_ids!(
|
||||||
0x08 => BlockBreakAnimation
|
0x08 => BlockBreakAnimation
|
||||||
0x09 => UpdateBlockEntity
|
0x09 => UpdateBlockEntity
|
||||||
0x0a => BlockAction
|
0x0a => BlockAction
|
||||||
0x0b => BlockChange
|
0x0b => BlockChange_VarInt
|
||||||
0x0c => BossBar
|
0x0c => BossBar
|
||||||
0x0d => ServerDifficulty
|
0x0d => ServerDifficulty
|
||||||
0x0e => TabCompleteReply
|
0x0e => TabCompleteReply
|
||||||
0x0f => ServerMessage
|
0x0f => ServerMessage
|
||||||
0x10 => MultiBlockChange
|
0x10 => MultiBlockChange_VarInt
|
||||||
0x11 => ConfirmTransaction
|
0x11 => ConfirmTransaction
|
||||||
0x12 => WindowClose
|
0x12 => WindowClose
|
||||||
0x13 => WindowOpen
|
0x13 => WindowOpen
|
||||||
|
@ -79,7 +79,7 @@ protocol_packet_ids!(
|
||||||
0x24 => Maps
|
0x24 => Maps
|
||||||
0x25 => EntityMove_i16
|
0x25 => EntityMove_i16
|
||||||
0x26 => EntityLookAndMove_i16
|
0x26 => EntityLookAndMove_i16
|
||||||
0x27 => EntityLook
|
0x27 => EntityLook_VarInt
|
||||||
0x28 => Entity
|
0x28 => Entity
|
||||||
0x29 => VehicleTeleport
|
0x29 => VehicleTeleport
|
||||||
0x2a => SignEditorOpen
|
0x2a => SignEditorOpen
|
||||||
|
|
|
@ -51,12 +51,12 @@ protocol_packet_ids!(
|
||||||
0x08 => BlockBreakAnimation
|
0x08 => BlockBreakAnimation
|
||||||
0x09 => UpdateBlockEntity
|
0x09 => UpdateBlockEntity
|
||||||
0x0a => BlockAction
|
0x0a => BlockAction
|
||||||
0x0b => BlockChange
|
0x0b => BlockChange_VarInt
|
||||||
0x0c => BossBar
|
0x0c => BossBar
|
||||||
0x0d => ServerDifficulty
|
0x0d => ServerDifficulty
|
||||||
0x0e => TabCompleteReply
|
0x0e => TabCompleteReply
|
||||||
0x0f => ServerMessage
|
0x0f => ServerMessage
|
||||||
0x10 => MultiBlockChange
|
0x10 => MultiBlockChange_VarInt
|
||||||
0x11 => ConfirmTransaction
|
0x11 => ConfirmTransaction
|
||||||
0x12 => WindowClose
|
0x12 => WindowClose
|
||||||
0x13 => WindowOpen
|
0x13 => WindowOpen
|
||||||
|
@ -79,7 +79,7 @@ protocol_packet_ids!(
|
||||||
0x24 => Maps
|
0x24 => Maps
|
||||||
0x25 => EntityMove_i16
|
0x25 => EntityMove_i16
|
||||||
0x26 => EntityLookAndMove_i16
|
0x26 => EntityLookAndMove_i16
|
||||||
0x27 => EntityLook
|
0x27 => EntityLook_VarInt
|
||||||
0x28 => Entity
|
0x28 => Entity
|
||||||
0x29 => VehicleTeleport
|
0x29 => VehicleTeleport
|
||||||
0x2a => SignEditorOpen
|
0x2a => SignEditorOpen
|
||||||
|
|
|
@ -120,14 +120,23 @@ impl Server {
|
||||||
username: profile.username.clone(),
|
username: profile.username.clone(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let packet;
|
use std::rc::Rc;
|
||||||
|
let (server_id, public_key, verify_token);
|
||||||
loop {
|
loop {
|
||||||
match conn.read_packet()? {
|
match conn.read_packet()? {
|
||||||
protocol::packet::Packet::SetInitialCompression(val) => {
|
protocol::packet::Packet::SetInitialCompression(val) => {
|
||||||
conn.set_compresssion(val.threshold.0);
|
conn.set_compresssion(val.threshold.0);
|
||||||
},
|
},
|
||||||
protocol::packet::Packet::EncryptionRequest(val) => {
|
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;
|
break;
|
||||||
},
|
},
|
||||||
protocol::packet::Packet::LoginSuccess(val) => {
|
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];
|
let mut shared = [0; 16];
|
||||||
// TODO: is this cryptographically secure enough?
|
// TODO: is this cryptographically secure enough?
|
||||||
rand::thread_rng().fill(&mut shared);
|
rand::thread_rng().fill(&mut shared);
|
||||||
|
|
||||||
println!("shared ({:} bytes) = {:?}", shared.len(), &shared);
|
let shared_e = rsa_public_encrypt_pkcs1::encrypt(&public_key, &shared).unwrap();
|
||||||
println!("packet.verify_token.data = {:?}", &packet.verify_token.data);
|
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();
|
profile.join_server(&server_id, &shared, &public_key)?;
|
||||||
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(&packet.server_id, &shared, &packet.public_key.data)?;
|
if protocol_version >= 47 {
|
||||||
|
conn.write_packet(protocol::packet::login::serverbound::EncryptionResponse {
|
||||||
conn.write_packet(protocol::packet::login::serverbound::EncryptionResponse {
|
shared_secret: protocol::LenPrefixedBytes::new(shared_e),
|
||||||
shared_secret: protocol::LenPrefixedBytes::new(shared_e),
|
verify_token: protocol::LenPrefixedBytes::new(token_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 read = conn.clone();
|
||||||
let mut write = conn.clone();
|
let mut write = conn.clone();
|
||||||
|
@ -377,36 +387,51 @@ impl Server {
|
||||||
self pck {
|
self pck {
|
||||||
JoinGame_i32 => on_game_join_i32,
|
JoinGame_i32 => on_game_join_i32,
|
||||||
JoinGame_i8 => on_game_join_i8,
|
JoinGame_i8 => on_game_join_i8,
|
||||||
|
JoinGame_i8_NoDebug => on_game_join_i8_nodebug,
|
||||||
Respawn => on_respawn,
|
Respawn => on_respawn,
|
||||||
KeepAliveClientbound_i64 => on_keep_alive_i64,
|
KeepAliveClientbound_i64 => on_keep_alive_i64,
|
||||||
KeepAliveClientbound_VarInt => on_keep_alive_varint,
|
KeepAliveClientbound_VarInt => on_keep_alive_varint,
|
||||||
|
KeepAliveClientbound_i32 => on_keep_alive_i32,
|
||||||
ChunkData => on_chunk_data,
|
ChunkData => on_chunk_data,
|
||||||
ChunkData_NoEntities => on_chunk_data_no_entities,
|
ChunkData_NoEntities => on_chunk_data_no_entities,
|
||||||
ChunkData_NoEntities_u16 => on_chunk_data_no_entities_u16,
|
ChunkData_NoEntities_u16 => on_chunk_data_no_entities_u16,
|
||||||
|
ChunkData_17 => on_chunk_data_17,
|
||||||
ChunkDataBulk => on_chunk_data_bulk,
|
ChunkDataBulk => on_chunk_data_bulk,
|
||||||
|
ChunkDataBulk_17 => on_chunk_data_bulk_17,
|
||||||
ChunkUnload => on_chunk_unload,
|
ChunkUnload => on_chunk_unload,
|
||||||
BlockChange => on_block_change,
|
BlockChange_VarInt => on_block_change_varint,
|
||||||
MultiBlockChange => on_multi_block_change,
|
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_WithConfirm => on_teleport_player_withconfirm,
|
||||||
TeleportPlayer_NoConfirm => on_teleport_player_noconfirm,
|
TeleportPlayer_NoConfirm => on_teleport_player_noconfirm,
|
||||||
TimeUpdate => on_time_update,
|
TimeUpdate => on_time_update,
|
||||||
ChangeGameState => on_game_state_change,
|
ChangeGameState => on_game_state_change,
|
||||||
UpdateBlockEntity => on_block_entity_update,
|
UpdateBlockEntity => on_block_entity_update,
|
||||||
|
UpdateBlockEntity_Data => on_block_entity_update_data,
|
||||||
UpdateSign => on_sign_update,
|
UpdateSign => on_sign_update,
|
||||||
|
UpdateSign_u16 => on_sign_update_u16,
|
||||||
PlayerInfo => on_player_info,
|
PlayerInfo => on_player_info,
|
||||||
|
PlayerInfo_String => on_player_info_string,
|
||||||
Disconnect => on_disconnect,
|
Disconnect => on_disconnect,
|
||||||
// Entities
|
// Entities
|
||||||
EntityDestroy => on_entity_destroy,
|
EntityDestroy => on_entity_destroy,
|
||||||
|
EntityDestroy_u8 => on_entity_destroy_u8,
|
||||||
SpawnPlayer_f64 => on_player_spawn_f64,
|
SpawnPlayer_f64 => on_player_spawn_f64,
|
||||||
SpawnPlayer_i32 => on_player_spawn_i32,
|
SpawnPlayer_i32 => on_player_spawn_i32,
|
||||||
SpawnPlayer_i32_HeldItem => on_player_spawn_i32_helditem,
|
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_f64 => on_entity_teleport_f64,
|
||||||
EntityTeleport_i32 => on_entity_teleport_i32,
|
EntityTeleport_i32 => on_entity_teleport_i32,
|
||||||
|
EntityTeleport_i32_i32_NoGround => on_entity_teleport_i32_i32_noground,
|
||||||
EntityMove_i16 => on_entity_move_i16,
|
EntityMove_i16 => on_entity_move_i16,
|
||||||
EntityMove_i8 => on_entity_move_i8,
|
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_i16 => on_entity_look_and_move_i16,
|
||||||
EntityLookAndMove_i8 => on_entity_look_and_move_i8,
|
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),
|
Err(err) => panic!("Err: {:?}", err),
|
||||||
|
@ -503,15 +528,28 @@ impl Server {
|
||||||
|
|
||||||
// Sync our position to the server
|
// Sync our position to the server
|
||||||
// Use the smaller packets when possible
|
// Use the smaller packets when possible
|
||||||
let packet = packet::play::serverbound::PlayerPositionLook {
|
if self.protocol_version >= 47 {
|
||||||
x: position.position.x,
|
let packet = packet::play::serverbound::PlayerPositionLook {
|
||||||
y: position.position.y,
|
x: position.position.x,
|
||||||
z: position.position.z,
|
y: position.position.y,
|
||||||
yaw: -(rotation.yaw as f32) * (180.0 / PI),
|
z: position.position.z,
|
||||||
pitch: (-rotation.pitch as f32) * (180.0 / PI) + 180.0,
|
yaw: -(rotation.yaw as f32) * (180.0 / PI),
|
||||||
on_ground,
|
pitch: (-rotation.pitch as f32) * (180.0 / PI) + 180.0,
|
||||||
};
|
on_ground,
|
||||||
self.write_packet(packet);
|
};
|
||||||
|
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_y: (at.y * 16.0) as u8,
|
||||||
cursor_z: (at.z * 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 {
|
self.write_packet(packet::play::serverbound::PlayerBlockPlacement_u8_Item {
|
||||||
location: pos,
|
location: pos,
|
||||||
face: match face {
|
face: match face {
|
||||||
|
@ -578,6 +616,25 @@ impl Server {
|
||||||
cursor_y: (at.y * 16.0) as u8,
|
cursor_y: (at.y * 16.0) as u8,
|
||||||
cursor_z: (at.z * 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) {
|
fn on_game_join_i32(&mut self, join: packet::play::clientbound::JoinGame_i32) {
|
||||||
self.on_game_join(join.gamemode, join.entity_id)
|
self.on_game_join(join.gamemode, join.entity_id)
|
||||||
}
|
}
|
||||||
|
@ -607,6 +670,11 @@ impl Server {
|
||||||
self.on_game_join(join.gamemode, join.entity_id)
|
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) {
|
fn on_game_join(&mut self, gamemode: u8, entity_id: i32) {
|
||||||
let gamemode = Gamemode::from_int((gamemode & 0x7) as i32);
|
let gamemode = Gamemode::from_int((gamemode & 0x7) as i32);
|
||||||
let player = entity::player::create_local(&mut self.entities);
|
let player = entity::player::create_local(&mut self.entities);
|
||||||
|
@ -622,9 +690,14 @@ impl Server {
|
||||||
self.player = Some(player);
|
self.player = Some(player);
|
||||||
|
|
||||||
// Let the server know who we are
|
// Let the server know who we are
|
||||||
self.write_packet(plugin_messages::Brand {
|
let brand = plugin_messages::Brand {
|
||||||
brand: "Steven".into(),
|
brand: "Steven".into(),
|
||||||
}.as_message());
|
};
|
||||||
|
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) {
|
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) {
|
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)
|
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)
|
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) {
|
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;
|
use std::f64::consts::PI;
|
||||||
if let Some(entity) = self.entity_map.get(&entity_id) {
|
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)
|
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) {
|
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) {
|
if let Some(entity) = self.entity_map.get(&entity_id) {
|
||||||
let position = self.entities.get_component_mut(*entity, self.target_position).unwrap();
|
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;
|
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();
|
let rotation = self.entities.get_component_mut(*entity, self.target_rotation).unwrap();
|
||||||
rotation.yaw = -((look.yaw as f64) / 256.0) * PI * 2.0;
|
rotation.yaw = -(yaw / 256.0) * PI * 2.0;
|
||||||
rotation.pitch = -((look.pitch as f64) / 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) {
|
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,
|
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,
|
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)
|
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) {
|
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;
|
use std::f64::consts::PI;
|
||||||
if let Some(entity) = self.entity_map.get(&entity_id) {
|
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)
|
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) {
|
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;
|
use std::f64::consts::PI;
|
||||||
if let Some(entity) = self.entity_map.remove(&entity_id) {
|
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) {
|
fn on_sign_update(&mut self, mut update_sign: packet::play::clientbound::UpdateSign) {
|
||||||
use crate::format;
|
use crate::format;
|
||||||
format::convert_legacy(&mut update_sign.line1);
|
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) {
|
fn on_player_info(&mut self, player_info: packet::play::clientbound::PlayerInfo) {
|
||||||
use crate::protocol::packet::PlayerDetail::*;
|
use crate::protocol::packet::PlayerDetail::*;
|
||||||
use base64;
|
use base64;
|
||||||
|
@ -1003,23 +1137,39 @@ impl Server {
|
||||||
self.world.load_chunks18(chunk_data.new, skylight, &chunk_meta, chunk_data.data.data).unwrap();
|
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) {
|
fn on_chunk_data_bulk(&mut self, bulk: packet::play::clientbound::ChunkDataBulk) {
|
||||||
let new = true;
|
let new = true;
|
||||||
self.world.load_chunks18(new, bulk.skylight, &bulk.chunk_meta.data, bulk.chunk_data.to_vec()).unwrap();
|
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) {
|
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);
|
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) {
|
fn on_block_change(&mut self, location: Position, id: i32) {
|
||||||
self.world.set_block(
|
self.world.set_block(location, block::Block::by_vanilla_id(id as usize))
|
||||||
block_change.location,
|
}
|
||||||
block::Block::by_vanilla_id(block_change.block_id.0 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 ox = block_change.chunk_x << 4;
|
||||||
let oz = block_change.chunk_z << 4;
|
let oz = block_change.chunk_z << 4;
|
||||||
for record in block_change.records.data {
|
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)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
use crate::protocol::Serializable;
|
use crate::protocol::Serializable;
|
||||||
use crate::protocol::packet::play::serverbound::PluginMessageServerbound;
|
use crate::protocol::packet::play::serverbound::PluginMessageServerbound;
|
||||||
|
use crate::protocol::packet::play::serverbound::PluginMessageServerbound_i16;
|
||||||
|
|
||||||
pub struct Brand {
|
pub struct Brand {
|
||||||
pub brand: String,
|
pub brand: String,
|
||||||
|
@ -15,4 +16,15 @@ impl Brand {
|
||||||
data,
|
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::ecs;
|
||||||
use crate::entity::block_entity;
|
use crate::entity::block_entity;
|
||||||
use crate::format;
|
use crate::format;
|
||||||
|
use flate2::read::ZlibDecoder;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
pub mod biome;
|
pub mod biome;
|
||||||
mod storage;
|
mod storage;
|
||||||
|
@ -558,6 +560,24 @@ impl World {
|
||||||
Ok(())
|
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> {
|
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 std::io::Read;
|
||||||
use byteorder::ReadBytesExt;
|
use byteorder::ReadBytesExt;
|
||||||
|
@ -635,21 +655,175 @@ impl World {
|
||||||
chunk.calculate_heightmap();
|
chunk.calculate_heightmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0 .. 16 {
|
self.dirty_chunks_by_bitmask(x, z, mask);
|
||||||
if mask & (1 << i) == 0 {
|
Ok(())
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
for pos in [
|
pub fn load_chunks17(&mut self, chunk_column_count: u16, data_length: i32, skylight: bool, data: &[u8]) -> Result<(), protocol::Error> {
|
||||||
(-1, 0, 0), (1, 0, 0),
|
let compressed_chunk_data = &data[0..data_length as usize];
|
||||||
(0, -1, 0), (0, 1, 0),
|
let metadata = &data[data_length as usize..];
|
||||||
(0, 0, -1), (0, 0, 1)].into_iter() {
|
|
||||||
self.flag_section_dirty(x + pos.0, i as i32 + pos.1, z + pos.2);
|
let mut zlib = ZlibDecoder::new(std::io::Cursor::new(compressed_chunk_data.to_vec()));
|
||||||
}
|
let mut chunk_data = Vec::new();
|
||||||
self.update_range(
|
zlib.read_to_end(&mut chunk_data)?;
|
||||||
(x<<4) - 1, (i<<4) - 1, (z<<4) - 1,
|
|
||||||
(x<<4) + 17, (i<<4) + 17, (z<<4) + 17
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -733,21 +907,7 @@ impl World {
|
||||||
chunk.calculate_heightmap();
|
chunk.calculate_heightmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0 .. 16 {
|
self.dirty_chunks_by_bitmask(x, z, mask);
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue