1.8.9 (47) multiprotocol support (#57)
Protocol 47 (1.8.9-1.8) is the biggest multiprotocol (https://github.com/iceiix/steven/issues/18) change yet: * New chunk format (load_chunk18) * New metadata format (Metadata18) * New packets and changes to 13 packets References: http://wiki.vg/index.php?title=Protocol&oldid=7368 https://wiki.vg/Protocol_version_numbers#Versions_after_the_Netty_rewrite https://wiki.vg/Protocol_History#1.8 https://github.com/PrismarineJS/minecraft-data/blob/master/data/pc/1.8/protocol.json 1.8 chunk format: https://wiki.vg/index.php?title=Chunk_Format&oldid=6124 1.9 chunk format: https://wiki.vg/index.php?title=Chunk_Format&oldid=7411 1.8 vs 1.9: https://wiki.vg/index.php?title=Chunk_Format&diff=7411&oldid=6124 https://github.com/PrismarineJS/prismarine-chunk/blob/master/src/pc/1.8/section.js https://github.com/PrismarineJS/prismarine-chunk/blob/master/src/pc/1.8/chunk.js Details: * Add 1.8.9 packet IDs from https://github.com/iceiix/steven/pull/37 * Add ChunkDataBulk, parse the ChunkMeta and save data in Vec<u8> * Add EntityEquipment u16 variant, EntityStatus, ChunkData u16 variants * SpawnPlayer with added held item https://wiki.vg/Protocol_History#15w31a Removed Current Item short from Spawn Player (0x0C) * SpawnObject no UUID and optional velocity https://wiki.vg/index.php?title=Protocol&oldid=7368#Spawn_Object https://wiki.vg/Protocol_History#15w31a Added Entity UUID after entity ID to Spawn Object (0x0E) Spawn Object always sends velocity, even if data is 0 * SpawnMob no UUID variant https://wiki.vg/Protocol_History#15w31a Added Entity UUID after entity ID to Spawn Mob (0x0F) * Maps packet without tracking position boolean https://wiki.vg/index.php?title=Protocol&oldid=7368#Map https://wiki.vg/Protocol_History#15w34a Added tracking position boolean to Map (0x34) * Update Entity NBT was removed and Bossbar added (both 0x49) >1.8 https://wiki.vg/index.php?title=Protocol&oldid=7368#Update_Entity_NBT https://wiki.vg/Protocol_History#15w31a Removed Update Entity NBT Packet (0x49) Added Boss Bar packet (0x4 * Use entity without hands https://wiki.vg/index.php?title=Protocol&oldid=7368#Use_Entity https://wiki.vg/Protocol_History#15w31a Added VarInt enum for selected hand in Use Entity (0x02); only sent if type is interact or interact at * Player block placement, held item stack and face byte variant https://wiki.vg/index.php?title=Protocol&oldid=7368#Player_Block_Placement https://wiki.vg/Protocol_History#15w31a Face for Player Block Placement is now a VarInt enum instead of a byte Replaced held item (slot) with VarInt enum selected hand in Player Block Placement * Arm swing without hands, a packet with no fields, uses a ZST https://wiki.vg/index.php?title=Protocol&oldid=7368#Animation_2 https://github.com/iceiix/steven/pull/57#issuecomment-444289008 https://doc.rust-lang.org/nomicon/exotic-sizes.html * ClickWindow uses u8 mode, same as in 15w39c * ClientSettings without hands * SpectateTeleport is added before ResourcePackStatus * Copy load_chunk to load_chunk19 and load_chunk18 * 1.8 chunk reading implementation, load_chunk18 * Support both metadata formats, Metadata18/Metadata19 * Remove fmt::Debug * Implement formatting in MetadataBase and bounce through fmt::Debug
This commit is contained in:
parent
bd4a521072
commit
1cb671093d
|
@ -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; 7] = [340, 316, 315, 210, 109, 107, 74];
|
pub const SUPPORTED_PROTOCOLS: [i32; 8] = [340, 316, 315, 210, 109, 107, 74, 47];
|
||||||
|
|
||||||
|
|
||||||
/// Helper macro for defining packets
|
/// Helper macro for defining packets
|
||||||
|
|
|
@ -92,6 +92,13 @@ state_packets!(
|
||||||
field displayed_skin_parts: u8 =,
|
field displayed_skin_parts: u8 =,
|
||||||
field main_hand: VarInt =,
|
field main_hand: VarInt =,
|
||||||
}
|
}
|
||||||
|
packet ClientSettings_u8_Handsfree {
|
||||||
|
field locale: String =,
|
||||||
|
field view_distance: u8 =,
|
||||||
|
field chat_mode: u8 =,
|
||||||
|
field chat_colors: bool =,
|
||||||
|
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 =,
|
||||||
|
@ -141,6 +148,13 @@ state_packets!(
|
||||||
field target_z: f32 = when(|p: &UseEntity| p.ty.0 == 2),
|
field target_z: f32 = when(|p: &UseEntity| p.ty.0 == 2),
|
||||||
field hand: VarInt = when(|p: &UseEntity| p.ty.0 == 0 || p.ty.0 == 2),
|
field hand: VarInt = when(|p: &UseEntity| p.ty.0 == 0 || p.ty.0 == 2),
|
||||||
}
|
}
|
||||||
|
packet UseEntity_Handsfree {
|
||||||
|
field target_id: VarInt =,
|
||||||
|
field ty: VarInt =,
|
||||||
|
field target_x: 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),
|
||||||
|
}
|
||||||
/// 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.
|
||||||
|
@ -273,6 +287,9 @@ state_packets!(
|
||||||
packet ArmSwing {
|
packet ArmSwing {
|
||||||
field hand: VarInt =,
|
field hand: VarInt =,
|
||||||
}
|
}
|
||||||
|
packet ArmSwing_Handsfree {
|
||||||
|
field empty: () =,
|
||||||
|
}
|
||||||
/// 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 =,
|
||||||
|
@ -294,6 +311,15 @@ state_packets!(
|
||||||
field cursor_y: u8 =,
|
field cursor_y: u8 =,
|
||||||
field cursor_z: u8 =,
|
field cursor_z: u8 =,
|
||||||
}
|
}
|
||||||
|
packet PlayerBlockPlacement_u8_Item {
|
||||||
|
field location: Position =,
|
||||||
|
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 {
|
||||||
field hand: VarInt =,
|
field hand: VarInt =,
|
||||||
|
@ -330,6 +356,19 @@ state_packets!(
|
||||||
field velocity_y: i16 =,
|
field velocity_y: i16 =,
|
||||||
field velocity_z: i16 =,
|
field velocity_z: i16 =,
|
||||||
}
|
}
|
||||||
|
packet SpawnObject_i32_NoUUID {
|
||||||
|
field entity_id: VarInt =,
|
||||||
|
field ty: u8 =,
|
||||||
|
field x: i32 =,
|
||||||
|
field y: i32 =,
|
||||||
|
field z: i32 =,
|
||||||
|
field pitch: i8 =,
|
||||||
|
field yaw: i8 =,
|
||||||
|
field data: i32 =,
|
||||||
|
field velocity_x: i16 = when(|p: &SpawnObject_i32_NoUUID| p.data != 0),
|
||||||
|
field velocity_y: i16 = when(|p: &SpawnObject_i32_NoUUID| p.data != 0),
|
||||||
|
field velocity_z: i16 = when(|p: &SpawnObject_i32_NoUUID| p.data != 0),
|
||||||
|
}
|
||||||
/// SpawnExperienceOrb spawns a single experience orb into the world when
|
/// SpawnExperienceOrb spawns a single experience orb into the world when
|
||||||
/// it is in range of the client. The count controls the amount of experience
|
/// it is in range of the client. The count controls the amount of experience
|
||||||
/// gained when collected.
|
/// gained when collected.
|
||||||
|
@ -378,7 +417,7 @@ state_packets!(
|
||||||
field velocity_x: i16 =,
|
field velocity_x: i16 =,
|
||||||
field velocity_y: i16 =,
|
field velocity_y: i16 =,
|
||||||
field velocity_z: i16 =,
|
field velocity_z: i16 =,
|
||||||
field metadata: types::Metadata =,
|
field metadata: types::Metadata19 =,
|
||||||
}
|
}
|
||||||
packet SpawnMob_u8 {
|
packet SpawnMob_u8 {
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
|
@ -393,7 +432,7 @@ state_packets!(
|
||||||
field velocity_x: i16 =,
|
field velocity_x: i16 =,
|
||||||
field velocity_y: i16 =,
|
field velocity_y: i16 =,
|
||||||
field velocity_z: i16 =,
|
field velocity_z: i16 =,
|
||||||
field metadata: types::Metadata =,
|
field metadata: types::Metadata19 =,
|
||||||
}
|
}
|
||||||
packet SpawnMob_u8_i32 {
|
packet SpawnMob_u8_i32 {
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
|
@ -408,7 +447,21 @@ state_packets!(
|
||||||
field velocity_x: i16 =,
|
field velocity_x: i16 =,
|
||||||
field velocity_y: i16 =,
|
field velocity_y: i16 =,
|
||||||
field velocity_z: i16 =,
|
field velocity_z: i16 =,
|
||||||
field metadata: types::Metadata =,
|
field metadata: types::Metadata19 =,
|
||||||
|
}
|
||||||
|
packet SpawnMob_u8_i32_NoUUID_18 {
|
||||||
|
field entity_id: VarInt =,
|
||||||
|
field ty: u8 =,
|
||||||
|
field x: i32 =,
|
||||||
|
field y: i32 =,
|
||||||
|
field z: i32 =,
|
||||||
|
field yaw: i8 =,
|
||||||
|
field pitch: i8 =,
|
||||||
|
field head_pitch: i8 =,
|
||||||
|
field velocity_x: i16 =,
|
||||||
|
field velocity_y: i16 =,
|
||||||
|
field velocity_z: i16 =,
|
||||||
|
field metadata: types::Metadata18 =,
|
||||||
}
|
}
|
||||||
/// SpawnPainting spawns a painting into the world when it is in range of
|
/// SpawnPainting spawns a painting into the world when it is in range of
|
||||||
/// the client. The title effects the size and the texture of the painting.
|
/// the client. The title effects the size and the texture of the painting.
|
||||||
|
@ -436,7 +489,7 @@ state_packets!(
|
||||||
field z: f64 =,
|
field z: f64 =,
|
||||||
field yaw: i8 =,
|
field yaw: i8 =,
|
||||||
field pitch: i8 =,
|
field pitch: i8 =,
|
||||||
field metadata: types::Metadata =,
|
field metadata: types::Metadata19 =,
|
||||||
}
|
}
|
||||||
packet SpawnPlayer_i32 {
|
packet SpawnPlayer_i32 {
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
|
@ -446,7 +499,18 @@ state_packets!(
|
||||||
field z: i32 =,
|
field z: i32 =,
|
||||||
field yaw: i8 =,
|
field yaw: i8 =,
|
||||||
field pitch: i8 =,
|
field pitch: i8 =,
|
||||||
field metadata: types::Metadata =,
|
field metadata: types::Metadata19 =,
|
||||||
|
}
|
||||||
|
packet SpawnPlayer_i32_HeldItem_18 {
|
||||||
|
field entity_id: VarInt =,
|
||||||
|
field uuid: UUID =,
|
||||||
|
field x: i32 =,
|
||||||
|
field y: i32 =,
|
||||||
|
field z: i32 =,
|
||||||
|
field yaw: i8 =,
|
||||||
|
field pitch: i8 =,
|
||||||
|
field current_item: u16 =,
|
||||||
|
field metadata: types::Metadata18 =,
|
||||||
}
|
}
|
||||||
/// 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 {
|
||||||
|
@ -664,6 +728,18 @@ state_packets!(
|
||||||
field bitmask: VarInt =,
|
field bitmask: VarInt =,
|
||||||
field data: LenPrefixedBytes<VarInt> =,
|
field data: LenPrefixedBytes<VarInt> =,
|
||||||
}
|
}
|
||||||
|
packet ChunkData_NoEntities_u16 {
|
||||||
|
field chunk_x: i32 =,
|
||||||
|
field chunk_z: i32 =,
|
||||||
|
field new: bool =,
|
||||||
|
field bitmask: u16 =,
|
||||||
|
field data: LenPrefixedBytes<VarInt> =,
|
||||||
|
}
|
||||||
|
packet ChunkDataBulk {
|
||||||
|
field skylight: bool =,
|
||||||
|
field chunk_meta: LenPrefixed<VarInt, packet::ChunkMeta> =,
|
||||||
|
field chunk_data: 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.
|
||||||
|
@ -737,6 +813,16 @@ state_packets!(
|
||||||
field z: Option<u8> = when(|p: &Maps| p.columns > 0),
|
field z: Option<u8> = when(|p: &Maps| p.columns > 0),
|
||||||
field data: Option<LenPrefixedBytes<VarInt>> = when(|p: &Maps| p.columns > 0),
|
field data: Option<LenPrefixedBytes<VarInt>> = when(|p: &Maps| p.columns > 0),
|
||||||
}
|
}
|
||||||
|
packet Maps_NoTracking {
|
||||||
|
field item_damage: VarInt =,
|
||||||
|
field scale: i8 =,
|
||||||
|
field icons: LenPrefixed<VarInt, packet::MapIcon> =,
|
||||||
|
field columns: u8 =,
|
||||||
|
field rows: Option<u8> = when(|p: &Maps_NoTracking| p.columns > 0),
|
||||||
|
field x: 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),
|
||||||
|
}
|
||||||
/// 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 =,
|
||||||
|
@ -782,6 +868,11 @@ state_packets!(
|
||||||
packet Entity {
|
packet Entity {
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
}
|
}
|
||||||
|
/// EntityUpdateNBT updates the entity named binary tag.
|
||||||
|
packet EntityUpdateNBT {
|
||||||
|
field entity_id: VarInt =,
|
||||||
|
field nbt: Option<nbt::NamedTag> =,
|
||||||
|
}
|
||||||
/// Teleports the player's vehicle
|
/// Teleports the player's vehicle
|
||||||
packet VehicleTeleport {
|
packet VehicleTeleport {
|
||||||
field x: f64 =,
|
field x: f64 =,
|
||||||
|
@ -881,6 +972,10 @@ state_packets!(
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
field head_yaw: i8 =,
|
field head_yaw: i8 =,
|
||||||
}
|
}
|
||||||
|
packet EntityStatus {
|
||||||
|
field entity_id: i32 =,
|
||||||
|
field entity_status: i8 =,
|
||||||
|
}
|
||||||
/// SelectAdvancementTab indicates the client should switch the advancement tab.
|
/// SelectAdvancementTab indicates the client should switch the advancement tab.
|
||||||
packet SelectAdvancementTab {
|
packet SelectAdvancementTab {
|
||||||
field has_id: bool =,
|
field has_id: bool =,
|
||||||
|
@ -915,7 +1010,11 @@ state_packets!(
|
||||||
/// EntityMetadata updates the metadata for an entity.
|
/// EntityMetadata updates the metadata for an entity.
|
||||||
packet EntityMetadata {
|
packet EntityMetadata {
|
||||||
field entity_id: VarInt =,
|
field entity_id: VarInt =,
|
||||||
field metadata: types::Metadata =,
|
field metadata: types::Metadata19 =,
|
||||||
|
}
|
||||||
|
packet EntityMetadata_18 {
|
||||||
|
field entity_id: VarInt =,
|
||||||
|
field metadata: types::Metadata18 =,
|
||||||
}
|
}
|
||||||
/// 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.
|
||||||
|
@ -944,6 +1043,11 @@ state_packets!(
|
||||||
field slot: VarInt =,
|
field slot: VarInt =,
|
||||||
field item: Option<item::Stack> =,
|
field item: Option<item::Stack> =,
|
||||||
}
|
}
|
||||||
|
packet EntityEquipment_u16 {
|
||||||
|
field entity_id: VarInt =,
|
||||||
|
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 =,
|
||||||
|
@ -1266,6 +1370,29 @@ impl Serializable for BlockChangeRecord {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct ChunkMeta {
|
||||||
|
pub x: i32,
|
||||||
|
pub z: i32,
|
||||||
|
pub bitmask: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for ChunkMeta {
|
||||||
|
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
|
||||||
|
Ok(ChunkMeta {
|
||||||
|
x: Serializable::read_from(buf)?,
|
||||||
|
z: Serializable::read_from(buf)?,
|
||||||
|
bitmask: Serializable::read_from(buf)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), Error> {
|
||||||
|
self.x.write_to(buf)?;
|
||||||
|
self.z.write_to(buf)?;
|
||||||
|
self.bitmask.write_to(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct ExplosionRecord {
|
pub struct ExplosionRecord {
|
||||||
pub x: i8,
|
pub x: i8,
|
||||||
|
|
|
@ -6,6 +6,7 @@ mod v1_10_2;
|
||||||
mod v1_9_2;
|
mod v1_9_2;
|
||||||
mod v1_9;
|
mod v1_9;
|
||||||
mod v15w39c;
|
mod v15w39c;
|
||||||
|
mod v1_8_9;
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -32,6 +33,9 @@ pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir:
|
||||||
// 15w39a/b/c
|
// 15w39a/b/c
|
||||||
74 => v15w39c::translate_internal_packet_id(state, dir, id, to_internal),
|
74 => v15w39c::translate_internal_packet_id(state, dir, id, to_internal),
|
||||||
|
|
||||||
|
// 1.8.9 - 1.8
|
||||||
|
47 => v1_8_9::translate_internal_packet_id(state, dir, id, to_internal),
|
||||||
|
|
||||||
_ => panic!("unsupported protocol version"),
|
_ => panic!("unsupported protocol version"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
protocol_packet_ids!(
|
||||||
|
handshake Handshaking {
|
||||||
|
serverbound Serverbound {
|
||||||
|
0x00 => Handshake
|
||||||
|
}
|
||||||
|
clientbound Clientbound {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
play Play {
|
||||||
|
serverbound Serverbound {
|
||||||
|
0x00 => KeepAliveServerbound_VarInt
|
||||||
|
0x01 => ChatMessage
|
||||||
|
0x02 => UseEntity_Handsfree
|
||||||
|
0x03 => Player
|
||||||
|
0x04 => PlayerPosition
|
||||||
|
0x05 => PlayerLook
|
||||||
|
0x06 => PlayerPositionLook
|
||||||
|
0x07 => PlayerDigging_u8
|
||||||
|
0x08 => PlayerBlockPlacement_u8_Item
|
||||||
|
0x09 => HeldItemChange
|
||||||
|
0x0a => ArmSwing_Handsfree
|
||||||
|
0x0b => PlayerAction
|
||||||
|
0x0c => SteerVehicle
|
||||||
|
0x0d => CloseWindow
|
||||||
|
0x0e => ClickWindow_u8
|
||||||
|
0x0f => ConfirmTransactionServerbound
|
||||||
|
0x10 => CreativeInventoryAction
|
||||||
|
0x11 => EnchantItem
|
||||||
|
0x12 => SetSign
|
||||||
|
0x13 => ClientAbilities
|
||||||
|
0x14 => TabComplete_NoAssume
|
||||||
|
0x15 => ClientSettings_u8_Handsfree
|
||||||
|
0x16 => ClientStatus
|
||||||
|
0x17 => PluginMessageServerbound
|
||||||
|
0x18 => SpectateTeleport
|
||||||
|
0x19 => ResourcePackStatus
|
||||||
|
}
|
||||||
|
clientbound Clientbound {
|
||||||
|
0x00 => KeepAliveClientbound_VarInt
|
||||||
|
0x01 => JoinGame_i8
|
||||||
|
0x02 => ServerMessage
|
||||||
|
0x03 => TimeUpdate
|
||||||
|
0x04 => EntityEquipment_u16
|
||||||
|
0x05 => SpawnPosition
|
||||||
|
0x06 => UpdateHealth
|
||||||
|
0x07 => Respawn
|
||||||
|
0x08 => TeleportPlayer_NoConfirm
|
||||||
|
0x09 => SetCurrentHotbarSlot
|
||||||
|
0x0a => EntityUsedBed
|
||||||
|
0x0b => Animation
|
||||||
|
0x0c => SpawnPlayer_i32_HeldItem_18
|
||||||
|
0x0d => CollectItem_nocount
|
||||||
|
0x0e => SpawnObject_i32_NoUUID
|
||||||
|
0x0f => SpawnMob_u8_i32_NoUUID_18
|
||||||
|
0x10 => SpawnPainting_NoUUID
|
||||||
|
0x11 => SpawnExperienceOrb_i32
|
||||||
|
0x12 => EntityVelocity
|
||||||
|
0x13 => EntityDestroy
|
||||||
|
0x14 => Entity
|
||||||
|
0x15 => EntityMove_i8
|
||||||
|
0x16 => EntityLook
|
||||||
|
0x17 => EntityLookAndMove_i8
|
||||||
|
0x18 => EntityTeleport_i32
|
||||||
|
0x19 => EntityHeadLook
|
||||||
|
0x1a => EntityStatus
|
||||||
|
0x1b => EntityAttach_leashed
|
||||||
|
0x1c => EntityMetadata_18
|
||||||
|
0x1d => EntityEffect
|
||||||
|
0x1e => EntityRemoveEffect
|
||||||
|
0x1f => SetExperience
|
||||||
|
0x20 => EntityProperties
|
||||||
|
0x21 => ChunkData_NoEntities_u16
|
||||||
|
0x22 => MultiBlockChange
|
||||||
|
0x23 => BlockChange
|
||||||
|
0x24 => BlockAction
|
||||||
|
0x25 => BlockBreakAnimation
|
||||||
|
0x26 => ChunkDataBulk
|
||||||
|
0x27 => Explosion
|
||||||
|
0x28 => Effect
|
||||||
|
0x29 => NamedSoundEffect_u8_NoCategory
|
||||||
|
0x2a => Particle
|
||||||
|
0x2b => ChangeGameState
|
||||||
|
0x2c => SpawnGlobalEntity_i32
|
||||||
|
0x2d => WindowOpen
|
||||||
|
0x2e => WindowClose
|
||||||
|
0x2f => WindowSetSlot
|
||||||
|
0x30 => WindowItems
|
||||||
|
0x31 => WindowProperty
|
||||||
|
0x32 => ConfirmTransaction
|
||||||
|
0x33 => UpdateSign
|
||||||
|
0x34 => Maps_NoTracking
|
||||||
|
0x35 => UpdateBlockEntity
|
||||||
|
0x36 => SignEditorOpen
|
||||||
|
0x37 => Statistics
|
||||||
|
0x38 => PlayerInfo
|
||||||
|
0x39 => PlayerAbilities
|
||||||
|
0x3a => TabCompleteReply
|
||||||
|
0x3b => ScoreboardObjective
|
||||||
|
0x3c => UpdateScore
|
||||||
|
0x3d => ScoreboardDisplay
|
||||||
|
0x3e => Teams
|
||||||
|
0x3f => PluginMessageClientbound
|
||||||
|
0x40 => Disconnect
|
||||||
|
0x41 => ServerDifficulty
|
||||||
|
0x42 => CombatEvent
|
||||||
|
0x43 => Camera
|
||||||
|
0x44 => WorldBorder
|
||||||
|
0x45 => Title_notext_component
|
||||||
|
0x46 => SetCompression
|
||||||
|
0x47 => PlayerListHeaderFooter
|
||||||
|
0x48 => ResourcePackSend
|
||||||
|
0x49 => EntityUpdateNBT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
login Login {
|
||||||
|
serverbound Serverbound {
|
||||||
|
0x00 => LoginStart
|
||||||
|
0x01 => EncryptionResponse
|
||||||
|
}
|
||||||
|
clientbound Clientbound {
|
||||||
|
0x00 => LoginDisconnect
|
||||||
|
0x01 => EncryptionRequest
|
||||||
|
0x02 => LoginSuccess
|
||||||
|
0x03 => SetInitialCompression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
status Status {
|
||||||
|
serverbound Serverbound {
|
||||||
|
0x00 => StatusRequest
|
||||||
|
0x01 => StatusPing
|
||||||
|
}
|
||||||
|
clientbound Clientbound {
|
||||||
|
0x00 => StatusResponse
|
||||||
|
0x01 => StatusPong
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
|
@ -382,6 +382,8 @@ impl Server {
|
||||||
KeepAliveClientbound_VarInt => on_keep_alive_varint,
|
KeepAliveClientbound_VarInt => on_keep_alive_varint,
|
||||||
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,
|
||||||
|
ChunkDataBulk => on_chunk_data_bulk,
|
||||||
ChunkUnload => on_chunk_unload,
|
ChunkUnload => on_chunk_unload,
|
||||||
BlockChange => on_block_change,
|
BlockChange => on_block_change,
|
||||||
MultiBlockChange => on_multi_block_change,
|
MultiBlockChange => on_multi_block_change,
|
||||||
|
@ -397,6 +399,7 @@ impl Server {
|
||||||
EntityDestroy => on_entity_destroy,
|
EntityDestroy => on_entity_destroy,
|
||||||
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_18 => on_player_spawn_i32_helditem_18,
|
||||||
EntityTeleport_f64 => on_entity_teleport_f64,
|
EntityTeleport_f64 => on_entity_teleport_f64,
|
||||||
EntityTeleport_i32 => on_entity_teleport_i32,
|
EntityTeleport_i32 => on_entity_teleport_i32,
|
||||||
EntityMove_i16 => on_entity_move_i16,
|
EntityMove_i16 => on_entity_move_i16,
|
||||||
|
@ -541,7 +544,7 @@ impl Server {
|
||||||
cursor_y: at.y as f32,
|
cursor_y: at.y as f32,
|
||||||
cursor_z: at.z as f32,
|
cursor_z: at.z as f32,
|
||||||
});
|
});
|
||||||
} else {
|
} else if self.protocol_version >= 49 {
|
||||||
self.write_packet(packet::play::serverbound::PlayerBlockPlacement_u8 {
|
self.write_packet(packet::play::serverbound::PlayerBlockPlacement_u8 {
|
||||||
location: pos,
|
location: pos,
|
||||||
face: protocol::VarInt(match face {
|
face: protocol::VarInt(match face {
|
||||||
|
@ -558,6 +561,23 @@ 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 {
|
||||||
|
location: pos,
|
||||||
|
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,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -732,6 +752,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_18(&mut self, spawn: packet::play::clientbound::SpawnPlayer_i32_HeldItem_18) {
|
||||||
|
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(&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) {
|
||||||
|
@ -930,7 +954,7 @@ impl Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_chunk_data(&mut self, chunk_data: packet::play::clientbound::ChunkData) {
|
fn on_chunk_data(&mut self, chunk_data: packet::play::clientbound::ChunkData) {
|
||||||
self.world.load_chunk(
|
self.world.load_chunk19(
|
||||||
chunk_data.chunk_x,
|
chunk_data.chunk_x,
|
||||||
chunk_data.chunk_z,
|
chunk_data.chunk_z,
|
||||||
chunk_data.new,
|
chunk_data.new,
|
||||||
|
@ -960,7 +984,7 @@ impl Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_chunk_data_no_entities(&mut self, chunk_data: packet::play::clientbound::ChunkData_NoEntities) {
|
fn on_chunk_data_no_entities(&mut self, chunk_data: packet::play::clientbound::ChunkData_NoEntities) {
|
||||||
self.world.load_chunk(
|
self.world.load_chunk19(
|
||||||
chunk_data.chunk_x,
|
chunk_data.chunk_x,
|
||||||
chunk_data.chunk_z,
|
chunk_data.chunk_z,
|
||||||
chunk_data.new,
|
chunk_data.new,
|
||||||
|
@ -969,6 +993,21 @@ impl Server {
|
||||||
).unwrap();
|
).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_chunk_data_no_entities_u16(&mut self, chunk_data: packet::play::clientbound::ChunkData_NoEntities_u16) {
|
||||||
|
let chunk_meta = vec![crate::protocol::packet::ChunkMeta {
|
||||||
|
x: chunk_data.chunk_x,
|
||||||
|
z: chunk_data.chunk_z,
|
||||||
|
bitmask: chunk_data.bitmask,
|
||||||
|
}];
|
||||||
|
let skylight = false;
|
||||||
|
self.world.load_chunks18(chunk_data.new, skylight, &chunk_meta, chunk_data.data.data).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_chunk_data_bulk(&mut self, bulk: packet::play::clientbound::ChunkDataBulk) {
|
||||||
|
let new = true;
|
||||||
|
self.world.load_chunks18(new, bulk.skylight, &bulk.chunk_meta.data, bulk.chunk_data.to_vec()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
fn on_chunk_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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,32 +38,161 @@ impl <T: MetaValue> MetadataKey<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Metadata {
|
pub struct Metadata18 {
|
||||||
map: HashMap<i32, Value>,
|
map: HashMap<i32, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Metadata {
|
pub struct Metadata19 {
|
||||||
pub fn new() -> Metadata {
|
map: HashMap<i32, Value>,
|
||||||
Metadata { map: HashMap::new() }
|
}
|
||||||
|
|
||||||
|
trait MetadataBase: fmt::Debug + Default {
|
||||||
|
fn map(&self) -> &HashMap<i32, Value>;
|
||||||
|
fn map_mut(&mut self) -> &mut HashMap<i32, Value>;
|
||||||
|
|
||||||
|
fn get<T: MetaValue>(&self, key: &MetadataKey<T>) -> Option<&T> {
|
||||||
|
self.map().get(&key.index).map(T::unwrap)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get<T: MetaValue>(&self, key: &MetadataKey<T>) -> Option<&T> {
|
fn put<T: MetaValue>(&mut self, key: &MetadataKey<T>, val: T) {
|
||||||
self.map.get(&key.index).map(T::unwrap)
|
self.map_mut().insert(key.index, val.wrap());
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put<T: MetaValue>(&mut self, key: &MetadataKey<T>, val: T) {
|
|
||||||
self.map.insert(key.index, val.wrap());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put_raw<T: MetaValue>(&mut self, index: i32, val: T) {
|
fn put_raw<T: MetaValue>(&mut self, index: i32, val: T) {
|
||||||
self.map.insert(index, val.wrap());
|
self.map_mut().insert(index, val.wrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt2(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Metadata[ ")?;
|
||||||
|
for (k, v) in self.map() {
|
||||||
|
write!(f, "{:?}={:?}, ", k, v)?;
|
||||||
|
}
|
||||||
|
write!(f, "]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serializable for Metadata {
|
impl MetadataBase for Metadata18 {
|
||||||
|
fn map(&self) -> &HashMap<i32, Value> { &self.map }
|
||||||
|
fn map_mut(&mut self) -> &mut HashMap<i32, Value> { &mut self.map }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MetadataBase for Metadata19 {
|
||||||
|
fn map(&self) -> &HashMap<i32, Value> { &self.map }
|
||||||
|
fn map_mut(&mut self) -> &mut HashMap<i32, Value> { &mut self.map }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Metadata18 {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { map: HashMap::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Metadata19 {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { map: HashMap::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
impl Serializable for Metadata18 {
|
||||||
|
|
||||||
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, protocol::Error> {
|
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, protocol::Error> {
|
||||||
let mut m = Metadata::new();
|
let mut m = Self::new();
|
||||||
|
loop {
|
||||||
|
let ty_index = u8::read_from(buf)? as i32;
|
||||||
|
if ty_index == 0x7f {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let index = ty_index & 0x1f;
|
||||||
|
let ty = ty_index >> 5;
|
||||||
|
|
||||||
|
match ty {
|
||||||
|
0 => m.put_raw(index, i8::read_from(buf)?),
|
||||||
|
1 => m.put_raw(index, i16::read_from(buf)?),
|
||||||
|
2 => m.put_raw(index, i32::read_from(buf)?),
|
||||||
|
3 => m.put_raw(index, f32::read_from(buf)?),
|
||||||
|
4 => m.put_raw(index, String::read_from(buf)?),
|
||||||
|
5 => m.put_raw(index, Option::<item::Stack>::read_from(buf)?),
|
||||||
|
6 => m.put_raw(index,
|
||||||
|
[i32::read_from(buf)?,
|
||||||
|
i32::read_from(buf)?,
|
||||||
|
i32::read_from(buf)?]),
|
||||||
|
7 => m.put_raw(index,
|
||||||
|
[f32::read_from(buf)?,
|
||||||
|
f32::read_from(buf)?,
|
||||||
|
f32::read_from(buf)?]),
|
||||||
|
_ => return Err(protocol::Error::Err("unknown metadata type".to_owned())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), protocol::Error> {
|
||||||
|
for (k, v) in &self.map {
|
||||||
|
if (*k as u8) > 0x1f {
|
||||||
|
panic!("write metadata index {:x} > 0x1f", *k as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ty_index: u8 = *k as u8;
|
||||||
|
const TYPE_SHIFT: usize = 5;
|
||||||
|
|
||||||
|
match *v
|
||||||
|
{
|
||||||
|
Value::Byte(ref val) => {
|
||||||
|
u8::write_to(&(ty_index | (0 << TYPE_SHIFT)), buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::Short(ref val) => {
|
||||||
|
u8::write_to(&(ty_index | (1 << TYPE_SHIFT)), buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::Int(ref val) => {
|
||||||
|
u8::write_to(&(ty_index | (2 << TYPE_SHIFT)), buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::Float(ref val) => {
|
||||||
|
u8::write_to(&(ty_index | (3 << TYPE_SHIFT)), buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::String(ref val) => {
|
||||||
|
u8::write_to(&(ty_index | (4 << TYPE_SHIFT)), buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::OptionalItemStack(ref val) => {
|
||||||
|
u8::write_to(&(ty_index | (5 << TYPE_SHIFT)), buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::Vector(ref val) => {
|
||||||
|
u8::write_to(&(ty_index | (6 << TYPE_SHIFT)), buf)?;
|
||||||
|
val[0].write_to(buf)?;
|
||||||
|
val[1].write_to(buf)?;
|
||||||
|
val[2].write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::Rotation(ref val) => {
|
||||||
|
u8::write_to(&(ty_index | (7 << TYPE_SHIFT)), buf)?;
|
||||||
|
val[0].write_to(buf)?;
|
||||||
|
val[1].write_to(buf)?;
|
||||||
|
val[2].write_to(buf)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::FormatComponent(_) | Value::Bool(_) | Value::Position(_) |
|
||||||
|
Value::OptionalPosition(_) | Value::Direction(_) | Value::OptionalUUID(_) |
|
||||||
|
Value::Block(_) | Value::NBTTag(_) => {
|
||||||
|
panic!("attempted to write 1.9+ metadata to 1.8");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u8::write_to(&0x7f, buf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for Metadata19 {
|
||||||
|
|
||||||
|
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, protocol::Error> {
|
||||||
|
let mut m = Self::new();
|
||||||
loop {
|
loop {
|
||||||
let index = u8::read_from(buf)? as i32;
|
let index = u8::read_from(buf)? as i32;
|
||||||
if index == 0xFF {
|
if index == 0xFF {
|
||||||
|
@ -179,6 +308,7 @@ impl Serializable for Metadata {
|
||||||
// TODO: write NBT tags metadata
|
// TODO: write NBT tags metadata
|
||||||
//nbt::Tag(*val).write_to(buf)?;
|
//nbt::Tag(*val).write_to(buf)?;
|
||||||
}
|
}
|
||||||
|
_ => panic!("unexpected metadata"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
u8::write_to(&0xFF, buf)?;
|
u8::write_to(&0xFF, buf)?;
|
||||||
|
@ -186,25 +316,25 @@ impl Serializable for Metadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Metadata {
|
// TODO: is it possible to implement these traits on MetadataBase instead?
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
impl fmt::Debug for Metadata19 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt2(f) } }
|
||||||
write!(f, "Metadata[ ")?;
|
impl fmt::Debug for Metadata18 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt2(f) } }
|
||||||
for (k, v) in &self.map {
|
|
||||||
write!(f, "{:?}={:?}, ", k, v)?;
|
impl Default for Metadata19 {
|
||||||
}
|
fn default() -> Self {
|
||||||
write!(f, "]")
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Default for Metadata18 {
|
||||||
impl Default for Metadata {
|
fn default() -> Self {
|
||||||
fn default() -> Metadata {
|
Self::new()
|
||||||
Metadata::new()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Byte(i8),
|
Byte(i8),
|
||||||
|
Short(i16),
|
||||||
Int(i32),
|
Int(i32),
|
||||||
Float(f32),
|
Float(f32),
|
||||||
String(String),
|
String(String),
|
||||||
|
@ -212,6 +342,7 @@ pub enum Value {
|
||||||
OptionalItemStack(Option<item::Stack>),
|
OptionalItemStack(Option<item::Stack>),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Vector([f32; 3]),
|
Vector([f32; 3]),
|
||||||
|
Rotation([i32; 3]),
|
||||||
Position(Position),
|
Position(Position),
|
||||||
OptionalPosition(Option<Position>),
|
OptionalPosition(Option<Position>),
|
||||||
Direction(protocol::VarInt), // TODO: Proper type
|
Direction(protocol::VarInt), // TODO: Proper type
|
||||||
|
@ -237,6 +368,18 @@ impl MetaValue for i8 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MetaValue for i16 {
|
||||||
|
fn unwrap(value: &Value) -> &Self {
|
||||||
|
match *value {
|
||||||
|
Value::Short(ref val) => val,
|
||||||
|
_ => panic!("incorrect key"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn wrap(self) -> Value {
|
||||||
|
Value::Short(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl MetaValue for i32 {
|
impl MetaValue for i32 {
|
||||||
fn unwrap(value: &Value) -> &Self {
|
fn unwrap(value: &Value) -> &Self {
|
||||||
match *value {
|
match *value {
|
||||||
|
@ -309,6 +452,18 @@ impl MetaValue for bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MetaValue for [i32; 3] {
|
||||||
|
fn unwrap(value: &Value) -> &Self {
|
||||||
|
match *value {
|
||||||
|
Value::Rotation(ref val) => val,
|
||||||
|
_ => panic!("incorrect key"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn wrap(self) -> Value {
|
||||||
|
Value::Rotation(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl MetaValue for [f32; 3] {
|
impl MetaValue for [f32; 3] {
|
||||||
fn unwrap(value: &Value) -> &Self {
|
fn unwrap(value: &Value) -> &Self {
|
||||||
match *value {
|
match *value {
|
||||||
|
@ -406,7 +561,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic() {
|
fn basic() {
|
||||||
let mut m = Metadata::new();
|
let mut m = Metadata19::new();
|
||||||
|
|
||||||
m.put(&TEST, "Hello world".to_owned());
|
m.put(&TEST, "Hello world".to_owned());
|
||||||
|
|
||||||
|
|
110
src/world/mod.rs
110
src/world/mod.rs
|
@ -545,7 +545,115 @@ impl World {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_chunk(&mut self, x: i32, z: i32, new: bool, mask: u16, data: Vec<u8>) -> Result<(), protocol::Error> {
|
pub fn load_chunks18(&mut self, new: bool, skylight: bool, chunk_metas: &[crate::protocol::packet::ChunkMeta], data: Vec<u8>) -> Result<(), protocol::Error> {
|
||||||
|
let mut data = std::io::Cursor::new(data);
|
||||||
|
|
||||||
|
for chunk_meta in chunk_metas {
|
||||||
|
let x = chunk_meta.x;
|
||||||
|
let z = chunk_meta.z;
|
||||||
|
let mask = chunk_meta.bitmask;
|
||||||
|
|
||||||
|
self.load_chunk18(x, z, new, skylight, mask, &mut data)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_chunk18(&mut self, x: i32, z: i32, new: bool, _skylight: bool, mask: u16, data: &mut std::io::Cursor<Vec<u8>>) -> Result<(), protocol::Error> {
|
||||||
|
use std::io::Read;
|
||||||
|
use byteorder::ReadBytesExt;
|
||||||
|
|
||||||
|
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()
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
for bi in 0 .. 4096 {
|
||||||
|
let id = data.read_u16::<byteorder::LittleEndian>()?;
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if new {
|
||||||
|
data.read_exact(&mut chunk.biomes)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.calculate_heightmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0 .. 16 {
|
||||||
|
if mask & (1 << i) == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for pos in [
|
||||||
|
(-1, 0, 0), (1, 0, 0),
|
||||||
|
(0, -1, 0), (0, 1, 0),
|
||||||
|
(0, 0, -1), (0, 0, 1)].into_iter() {
|
||||||
|
self.flag_section_dirty(x + pos.0, i as i32 + pos.1, z + pos.2);
|
||||||
|
}
|
||||||
|
self.update_range(
|
||||||
|
(x<<4) - 1, (i<<4) - 1, (z<<4) - 1,
|
||||||
|
(x<<4) + 17, (i<<4) + 17, (z<<4) + 17
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_chunk19(&mut self, x: i32, z: i32, new: bool, mask: u16, data: Vec<u8>) -> Result<(), protocol::Error> {
|
||||||
use std::io::{Cursor, Read};
|
use std::io::{Cursor, Read};
|
||||||
use byteorder::ReadBytesExt;
|
use byteorder::ReadBytesExt;
|
||||||
use crate::protocol::{VarInt, Serializable, LenPrefixed};
|
use crate::protocol::{VarInt, Serializable, LenPrefixed};
|
||||||
|
|
Loading…
Reference in New Issue