1.16.4 (754) / 1.16.3 (753) / 1.16.2 (751) protocol (#390)

Adds support for 1.16.4 (754) / 1.16.3 (753) / 1.16.2 (751) protocols

* Update packet IDs and readme

* Add and handle ChunkData_Biomes3D_VarInt variant

* Support world chunk data padded bit map array

* Add and handle JoinGame_WorldNames_IsHard variant

* Add and handle MultiBlockChange_Packed variant

* Add UnlockRecipes_WithBlastSmoker variant

* Add SetDisplayedRecipe and SetRecipeBookState packets
This commit is contained in:
iceiix 2020-11-24 10:30:30 -08:00 committed by ice_iix
parent f4414e7814
commit 4a757656ab
8 changed files with 332 additions and 8 deletions

View File

@ -23,6 +23,9 @@ Join with your favorite IRC client.
| Game version | Protocol version | Supported? |
| ------ | --- | --- |
| 1.16.4 | 754 | ✓ |
| 1.16.3 | 753 | ✓ |
| 1.16.2 | 751 | ✓ |
| 1.16.1 | 736 | ✓ |
| 1.16 | 735 | ✓ |
| 1.15.2 | 578 | ✓ |

View File

@ -40,9 +40,9 @@ use std::net::TcpStream;
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
use std::time::{Duration, Instant};
pub const SUPPORTED_PROTOCOLS: [i32; 21] = [
736, 735, 578, 575, 498, 490, 485, 480, 477, 452, 451, 404, 340, 316, 315, 210, 109, 107, 74,
47, 5,
pub const SUPPORTED_PROTOCOLS: [i32; 24] = [
754, 753, 751, 736, 735, 578, 575, 498, 490, 485, 480, 477, 452, 451, 404, 340, 316, 315, 210,
109, 107, 74, 47, 5,
];
static CURRENT_PROTOCOL_VERSION: AtomicI32 = AtomicI32::new(SUPPORTED_PROTOCOLS[0]);
@ -1156,6 +1156,8 @@ impl Conn {
let pos = buf.position() as usize;
let ibuf = buf.into_inner();
if ibuf.len() != pos {
debug!("pos = {:?}", pos);
debug!("ibuf = {:?}", ibuf);
return Result::Err(Error::Err(format!(
"Failed to read all of packet 0x{:X}, \
had {} bytes left",

View File

@ -353,6 +353,16 @@ state_packets!(
field crafting_book_open: bool = when(|p: &CraftingBookData| p.action.0 == 1),
field crafting_filter: bool = when(|p: &CraftingBookData| p.action.0 == 1),
}
/// SetDisplayedRecipe replaces CraftingBookData, type 0.
packet SetDisplayedRecipe {
field recipe_id: String =,
}
/// SetRecipeBookState replaces CraftingBookData, type 1.
packet SetRecipeBookState {
field book_id: VarInt =, // TODO: enum, 0: crafting, 1: furnace, 2: blast furnace, 3: smoker
field book_open: bool =,
field filter_active: bool =,
}
packet NameItem {
field item_name: String =,
}
@ -881,6 +891,11 @@ state_packets!(
field message: format::Component =,
}
/// MultiBlockChange is used to update a batch of blocks in a single packet.
packet MultiBlockChange_Packed {
field chunk_section_pos: u64 =,
field no_trust_edges: bool =,
field records: LenPrefixed<VarInt, VarLong> =,
}
packet MultiBlockChange_VarInt {
field chunk_x: i32 =,
field chunk_z: i32 =,
@ -1047,6 +1062,16 @@ state_packets!(
}
/// ChunkData sends or updates a single chunk on the client. If New is set
/// then biome data should be sent too.
packet ChunkData_Biomes3D_VarInt {
field chunk_x: i32 =,
field chunk_z: i32 =,
field new: bool =,
field bitmask: VarInt =,
field heightmaps: Option<nbt::NamedTag> =,
field biomes: LenPrefixed<VarInt, VarInt> = when(|p: &ChunkData_Biomes3D_VarInt| p.new),
field data: LenPrefixedBytes<VarInt> =,
field block_entities: LenPrefixed<VarInt, Option<nbt::NamedTag>> =,
}
packet ChunkData_Biomes3D_bool {
field chunk_x: i32 =,
field chunk_z: i32 =,
@ -1200,6 +1225,39 @@ state_packets!(
}
/// JoinGame is sent after completing the login process. This
/// sets the initial state for the client.
packet JoinGame_WorldNames_IsHard {
/// The entity id the client will be referenced by
field entity_id: i32 =,
/// Whether hardcore mode is enabled
field is_hardcore: bool =,
/// The starting gamemode of the client
field gamemode: u8 =,
/// The previous gamemode of the client
field previous_gamemode: u8 =,
/// Identifiers for all worlds on the server
field world_names: LenPrefixed<VarInt, String> =,
/// Represents a dimension registry
field dimension_codec: Option<nbt::NamedTag> =,
/// The dimension the client is starting in
field dimension: Option<nbt::NamedTag> =,
/// The world being spawned into
field world_name: String =,
/// Truncated SHA-256 hash of world's seed
field hashed_seed: i64 =,
/// The max number of players on the server
field max_players: VarInt =,
/// The render distance (2-32)
field view_distance: VarInt =,
/// Whether the client should reduce the amount of debug
/// information it displays in F3 mode
field reduced_debug_info: bool =,
/// Whether to prompt or immediately respawn
field enable_respawn_screen: bool =,
/// Whether the world is in debug mode
field is_debug: bool =,
/// Whether the world is a superflat world
field is_flat: bool =,
}
packet JoinGame_WorldNames {
/// The entity id the client will be referenced by
field entity_id: i32 =,
@ -1231,7 +1289,6 @@ state_packets!(
/// Whether the world is a superflat world
field is_flat: bool =,
}
packet JoinGame_HashedSeed_Respawn {
/// The entity id the client will be referenced by
field entity_id: i32 =,
@ -1527,6 +1584,19 @@ state_packets!(
field recipe_ids: LenPrefixed<VarInt, String> =,
field recipe_ids2: LenPrefixed<VarInt, String> = when(|p: &UnlockRecipes_WithSmelting| p.action.0 == 0),
}
packet UnlockRecipes_WithBlastSmoker {
field action: VarInt =,
field crafting_book_open: bool =,
field filtering_craftable: bool =,
field smelting_book_open: bool =,
field filtering_smeltable: bool =,
field blast_furnace_open: bool =,
field filtering_blast_furnace: bool =,
field smoker_open: bool =,
field filtering_smoker: bool =,
field recipe_ids: LenPrefixed<VarInt, String> =,
field recipe_ids2: LenPrefixed<VarInt, String> = when(|p: &UnlockRecipes_WithBlastSmoker| p.action.0 == 0),
}
/// EntityDestroy destroys the entities with the ids in the provided slice.
packet EntityDestroy {
field entity_ids: LenPrefixed<VarInt, VarInt> =,

View File

@ -14,6 +14,7 @@ mod v1_14_3;
mod v1_14_4;
mod v1_15;
mod v1_16_1;
mod v1_16_4;
mod v1_7_10;
mod v1_8_9;
mod v1_9;
@ -25,6 +26,9 @@ mod v1_9_2;
pub fn protocol_name_to_protocol_version(s: String) -> i32 {
match s.as_ref() {
"" => SUPPORTED_PROTOCOLS[0],
"1.16.4" => 754,
"1.16.3" => 753,
"1.16.2" => 751,
"1.16.1" => 736,
"1.16" => 735,
"1.15.2" => 578,
@ -64,6 +68,7 @@ pub fn translate_internal_packet_id_for_version(
to_internal: bool,
) -> i32 {
match version {
754 | 753 | 751 => v1_16_4::translate_internal_packet_id(state, dir, id, to_internal),
736 => v1_16_1::translate_internal_packet_id(state, dir, id, to_internal),
735 => v1_16_1::translate_internal_packet_id(state, dir, id, to_internal),
578 => v1_15::translate_internal_packet_id(state, dir, id, to_internal),

View File

@ -0,0 +1,179 @@
protocol_packet_ids!(
handshake Handshaking {
serverbound Serverbound {
0x00 => Handshake
}
clientbound Clientbound {
}
}
play Play {
serverbound Serverbound {
0x00 => TeleportConfirm
0x01 => QueryBlockNBT
0x02 => SetDifficulty
0x03 => ChatMessage
0x04 => ClientStatus
0x05 => ClientSettings
0x06 => TabComplete
0x07 => ConfirmTransactionServerbound
0x08 => ClickWindowButton
0x09 => ClickWindow
0x0a => CloseWindow
0x0b => PluginMessageServerbound
0x0c => EditBook
0x0d => QueryEntityNBT
0x0e => UseEntity_Sneakflag
0x0f => GenerateStructure
0x10 => KeepAliveServerbound_i64
0x11 => LockDifficulty
0x12 => PlayerPosition
0x13 => PlayerPositionLook
0x14 => PlayerLook
0x15 => Player
0x16 => VehicleMove
0x17 => SteerBoat
0x18 => PickItem
0x19 => CraftRecipeRequest
0x1a => ClientAbilities_u8
0x1b => PlayerDigging
0x1c => PlayerAction
0x1d => SteerVehicle
0x1e => SetDisplayedRecipe
0x1f => SetRecipeBookState
0x20 => NameItem
0x21 => ResourcePackStatus
0x22 => AdvancementTab
0x23 => SelectTrade
0x24 => SetBeaconEffect
0x25 => HeldItemChange
0x26 => UpdateCommandBlock
0x27 => UpdateCommandBlockMinecart
0x28 => CreativeInventoryAction
0x29 => UpdateJigsawBlock_Joint
0x2a => UpdateStructureBlock
0x2b => SetSign
0x2c => ArmSwing
0x2d => SpectateTeleport
0x2e => PlayerBlockPlacement_insideblock
0x2f => UseItem
}
clientbound Clientbound {
0x00 => SpawnObject_VarInt
0x01 => SpawnExperienceOrb
0x02 => SpawnMob_NoMeta
0x03 => SpawnPainting_VarInt
0x04 => SpawnPlayer_f64_NoMeta
0x05 => Animation
0x06 => Statistics
0x07 => AcknowledgePlayerDigging
0x08 => BlockBreakAnimation
0x09 => UpdateBlockEntity
0x0a => BlockAction
0x0b => BlockChange_VarInt
0x0c => BossBar
0x0d => ServerDifficulty_Locked
0x0e => ServerMessage_Sender
0x0f => TabCompleteReply
0x10 => DeclareCommands
0x11 => ConfirmTransaction
0x12 => WindowClose
0x13 => WindowItems
0x14 => WindowProperty
0x15 => WindowSetSlot
0x16 => SetCooldown
0x17 => PluginMessageClientbound
0x18 => NamedSoundEffect
0x19 => Disconnect
0x1a => EntityAction
0x1b => Explosion
0x1c => ChunkUnload
0x1d => ChangeGameState
0x1e => WindowOpenHorse
0x1f => KeepAliveClientbound_i64
0x20 => ChunkData_Biomes3D_VarInt
0x21 => Effect
0x22 => Particle_f64
0x23 => UpdateLight_WithTrust
0x24 => JoinGame_WorldNames_IsHard
0x25 => Maps
0x26 => TradeList_WithRestock
0x27 => EntityMove_i16
0x28 => EntityLookAndMove_i16
0x29 => EntityLook_VarInt
0x2a => Entity
0x2b => VehicleTeleport
0x2c => OpenBook
0x2d => WindowOpen_VarInt
0x2e => SignEditorOpen
0x2f => CraftRecipeResponse
0x30 => PlayerAbilities
0x31 => CombatEvent
0x32 => PlayerInfo
0x33 => FacePlayer
0x34 => TeleportPlayer_WithConfirm
0x35 => UnlockRecipes_WithBlastSmoker
0x36 => EntityDestroy
0x37 => EntityRemoveEffect
0x38 => ResourcePackSend
0x39 => Respawn_WorldName
0x3a => EntityHeadLook
0x3b => MultiBlockChange_Packed
0x3c => SelectAdvancementTab
0x3d => WorldBorder
0x3e => Camera
0x3f => SetCurrentHotbarSlot
0x40 => UpdateViewPosition
0x41 => UpdateViewDistance
0x42 => SpawnPosition
0x43 => ScoreboardDisplay
0x44 => EntityMetadata
0x45 => EntityAttach
0x46 => EntityVelocity
0x47 => EntityEquipment_VarInt // TODO: changed to an array, but earlier than 1.16.1
0x48 => SetExperience
0x49 => UpdateHealth
0x4a => ScoreboardObjective
0x4b => SetPassengers
0x4c => Teams_VarInt
0x4d => UpdateScore
0x4e => TimeUpdate
0x4f => Title
0x50 => EntitySoundEffect
0x51 => SoundEffect
0x52 => StopSound
0x53 => PlayerListHeaderFooter
0x54 => NBTQueryResponse
0x55 => CollectItem
0x56 => EntityTeleport_f64
0x57 => Advancements
0x58 => EntityProperties
0x59 => EntityEffect
0x5a => DeclareRecipes
0x5b => TagsWithEntities
}
}
login Login {
serverbound Serverbound {
0x00 => LoginStart
0x01 => EncryptionResponse
0x02 => LoginPluginResponse
}
clientbound Clientbound {
0x00 => LoginDisconnect
0x01 => EncryptionRequest
0x02 => LoginSuccess_UUID
0x03 => SetInitialCompression
0x04 => LoginPluginRequest
}
}
status Status {
serverbound Serverbound {
0x00 => StatusRequest
0x01 => StatusPing
}
clientbound Clientbound {
0x00 => StatusResponse
0x01 => StatusPong
}
}
);

View File

@ -16,6 +16,7 @@ pub struct Map {
bits: Vec<u64>,
pub bit_size: usize,
length: usize,
padded: bool,
}
#[test]
@ -53,17 +54,20 @@ impl Map {
bit_size: size,
length: len,
bits: Vec::with_capacity((len * size) / 64),
padded: false,
};
for _ in 0..len {
map.bits.push(0)
}
map
}
pub fn from_raw(bits: Vec<u64>, size: usize) -> Map {
pub fn from_raw(bits: Vec<u64>, size: usize, padded: bool) -> Map {
Map {
length: (bits.len() * 64 + (size - 1)) / size,
bit_size: size,
bits,
padded,
}
}
@ -75,8 +79,17 @@ impl Map {
n
}
fn get_bit_offset(&self, i: usize) -> usize {
let padding = if self.padded {
i / (64 / self.bit_size) * (64 % self.bit_size)
} else {
0
};
i * self.bit_size + padding
}
pub fn set(&mut self, i: usize, val: usize) {
let i = i * self.bit_size;
let i = self.get_bit_offset(i);
let pos = i / 64;
let mask = (1u64 << self.bit_size) - 1;
let ii = i % 64;
@ -90,7 +103,7 @@ impl Map {
}
pub fn get(&self, i: usize) -> usize {
let i = i * self.bit_size;
let i = self.get_bit_offset(i);
let pos = i / 64;
let mask = (1 << self.bit_size) - 1;
let ii = i % 64;

View File

@ -509,6 +509,7 @@ impl Server {
self pck {
PluginMessageClientbound_i16 => on_plugin_message_clientbound_i16,
PluginMessageClientbound => on_plugin_message_clientbound_1,
JoinGame_WorldNames_IsHard => on_game_join_worldnames_ishard,
JoinGame_WorldNames => on_game_join_worldnames,
JoinGame_HashedSeed_Respawn => on_game_join_hashedseed_respawn,
JoinGame_i32_ViewDistance => on_game_join_i32_viewdistance,
@ -521,6 +522,7 @@ impl Server {
KeepAliveClientbound_i64 => on_keep_alive_i64,
KeepAliveClientbound_VarInt => on_keep_alive_varint,
KeepAliveClientbound_i32 => on_keep_alive_i32,
ChunkData_Biomes3D_VarInt => on_chunk_data_biomes3d_varint,
ChunkData_Biomes3D_bool => on_chunk_data_biomes3d_bool,
ChunkData => on_chunk_data,
ChunkData_Biomes3D => on_chunk_data_biomes3d,
@ -533,6 +535,7 @@ impl Server {
ChunkUnload => on_chunk_unload,
BlockChange_VarInt => on_block_change_varint,
BlockChange_u8 => on_block_change_u8,
MultiBlockChange_Packed => on_multi_block_change_packed,
MultiBlockChange_VarInt => on_multi_block_change_varint,
MultiBlockChange_u16 => on_multi_block_change_u16,
TeleportPlayer_WithConfirm => on_teleport_player_withconfirm,
@ -970,6 +973,13 @@ impl Server {
}
}
fn on_game_join_worldnames_ishard(
&mut self,
join: packet::play::clientbound::JoinGame_WorldNames_IsHard,
) {
self.on_game_join(join.gamemode, join.entity_id)
}
fn on_game_join_worldnames(&mut self, join: packet::play::clientbound::JoinGame_WorldNames) {
self.on_game_join(join.gamemode, join.entity_id)
}
@ -1777,6 +1787,22 @@ impl Server {
}
}
fn on_chunk_data_biomes3d_varint(
&mut self,
chunk_data: packet::play::clientbound::ChunkData_Biomes3D_VarInt,
) {
self.world
.load_chunk115(
chunk_data.chunk_x,
chunk_data.chunk_z,
chunk_data.new,
chunk_data.bitmask.0 as u16,
chunk_data.data.data,
)
.unwrap();
self.load_block_entities(chunk_data.block_entities.data);
}
fn on_chunk_data_biomes3d_bool(
&mut self,
chunk_data: packet::play::clientbound::ChunkData_Biomes3D_bool,
@ -1934,6 +1960,31 @@ impl Server {
);
}
fn on_multi_block_change_packed(
&mut self,
block_change: packet::play::clientbound::MultiBlockChange_Packed,
) {
let sx = (block_change.chunk_section_pos >> 42) as i32;
let sy = ((block_change.chunk_section_pos << 44) >> 44) as i32;
let sz = ((block_change.chunk_section_pos << 22) >> 42) as i32;
for record in block_change.records.data {
let block_raw_id = record.0 >> 12;
let lz = (record.0 & 0xf) as i32;
let ly = ((record.0 >> 4) & 0xf) as i32;
let lx = ((record.0 >> 8) & 0xf) as i32;
self.world.set_block(
Position::new(sx + lx as i32, sy + ly as i32, sz + lz as i32),
block::Block::by_vanilla_id(
block_raw_id as usize,
self.protocol_version,
&self.world.modded_block_ids,
),
);
}
}
fn on_multi_block_change_varint(
&mut self,
block_change: packet::play::clientbound::MultiBlockChange_VarInt,

View File

@ -1066,7 +1066,8 @@ impl World {
}
let bits = LenPrefixed::<VarInt, u64>::read_from(&mut data)?.data;
let m = bit::Map::from_raw(bits, bit_size as usize);
let padded = self.protocol_version >= 736;
let m = bit::Map::from_raw(bits, bit_size as usize, padded);
for bi in 0..4096 {
let id = m.get(bi);