Skip to content

Commit c8acc43

Browse files
committed
1.14 protocol support (477) (#132). Closes #72
Adds 1.14 (477) protocol support, based on: https://wiki.vg/index.php?title=Pre-release_protocol&oldid=14723 * New packets: SetDifficulty, LockDifficulty, UpdateJigsawBlock, UpdateViewPosition, UpdateViewDistance * New metadata: Optional VarInt (17) and Pose (18) * Add new join game variant with view distance, without difficulty * Add new server difficulty variant, with locked boolean * Implement recipe parsing changes, add stonecutting recipe type
1 parent c1692e9 commit c8acc43

File tree

6 files changed

+167
-4
lines changed

6 files changed

+167
-4
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Join with your favorite IRC client or [Matrix](https://matrix.to/#/#_espernet_#s
2323

2424
| Game version | Protocol version | Supported? |
2525
| ------ | --- | --- |
26+
| 1.14 | 477 ||
2627
| 19w02a | 452 ||
2728
| 18w50a | 451 ||
2829
| 1.13.2 | 404 ||

src/protocol/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use flate2::Compression;
3838
use std::time::{Instant, Duration};
3939
use crate::shared::Position;
4040

41-
pub const SUPPORTED_PROTOCOLS: [i32; 12] = [404, 451, 452, 340, 316, 315, 210, 109, 107, 74, 47, 5];
41+
pub const SUPPORTED_PROTOCOLS: [i32; 13] = [477, 452, 451, 404, 340, 316, 315, 210, 109, 107, 74, 47, 5];
4242

4343
// TODO: switch to using thread_local storage?, see https://doc.rust-lang.org/std/macro.thread_local.html
4444
pub static mut CURRENT_PROTOCOL_VERSION: i32 = SUPPORTED_PROTOCOLS[0];

src/protocol/packet.rs

+82-3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ state_packets!(
5757
field transaction_id: VarInt =,
5858
field location: Position =,
5959
}
60+
packet SetDifficulty {
61+
field new_difficulty: u8 =,
62+
}
6063
/// TabComplete is sent by the client when the client presses tab in
6164
/// the chat box.
6265
packet TabComplete {
@@ -202,6 +205,9 @@ state_packets!(
202205
packet KeepAliveServerbound_i32 {
203206
field id: i32 =,
204207
}
208+
packet LockDifficulty {
209+
field locked: bool =,
210+
}
205211
/// PlayerPosition is used to update the player's position.
206212
packet PlayerPosition {
207213
field x: f64 =,
@@ -370,6 +376,12 @@ state_packets!(
370376
field slot: i16 =,
371377
field clicked_item: Option<item::Stack> =,
372378
}
379+
packet UpdateJigsawBlock {
380+
field location: Position =,
381+
field attachment_type: String =,
382+
field target_pool: String =,
383+
field final_state: String =,
384+
}
373385
packet UpdateStructureBlock {
374386
field location: Position =,
375387
field action: VarInt =,
@@ -753,6 +765,10 @@ state_packets!(
753765
packet ServerDifficulty {
754766
field difficulty: u8 =,
755767
}
768+
packet ServerDifficulty_Locked {
769+
field difficulty: u8 =,
770+
field locked: bool =,
771+
}
756772
/// TabCompleteReply is sent as a reply to a tab completion request.
757773
/// The matches should be possible completions for the command/chat the
758774
/// player sent.
@@ -1055,6 +1071,23 @@ state_packets!(
10551071
}
10561072
/// JoinGame is sent after completing the login process. This
10571073
/// sets the initial state for the client.
1074+
packet JoinGame_i32_ViewDistance {
1075+
/// The entity id the client will be referenced by
1076+
field entity_id: i32 =,
1077+
/// The starting gamemode of the client
1078+
field gamemode: u8 =,
1079+
/// The dimension the client is starting in
1080+
field dimension: i32 =,
1081+
/// The max number of players on the server
1082+
field max_players: u8 =,
1083+
/// The level type of the server
1084+
field level_type: String =,
1085+
/// The render distance (2-32)
1086+
field view_distance: VarInt =,
1087+
/// Whether the client should reduce the amount of debug
1088+
/// information it displays in F3 mode
1089+
field reduced_debug_info: bool =,
1090+
}
10581091
packet JoinGame_i32 {
10591092
/// The entity id the client will be referenced by
10601093
field entity_id: i32 =,
@@ -1377,6 +1410,15 @@ state_packets!(
13771410
packet SetCurrentHotbarSlot {
13781411
field slot: u8 =,
13791412
}
1413+
/// UpdateViewPosition is used to determine what chunks should be remain loaded.
1414+
packet UpdateViewPosition {
1415+
field chunk_x: VarInt =,
1416+
field chunk_z: VarInt =,
1417+
}
1418+
/// UpdateViewDistance is sent by the integrated server when changing render distance.
1419+
packet UpdateViewDistance {
1420+
field view_distance: VarInt =,
1421+
}
13801422
/// ScoreboardDisplay is used to set the display position of a scoreboard.
13811423
packet ScoreboardDisplay {
13821424
field position: u8 =,
@@ -2396,6 +2438,11 @@ pub enum RecipeData {
23962438
experience: f32,
23972439
cooking_time: VarInt,
23982440
},
2441+
Stonecutting {
2442+
group: String,
2443+
ingredient: RecipeIngredient,
2444+
result: Option<item::Stack>,
2445+
},
23992446
}
24002447

24012448
impl Default for RecipeData {
@@ -2413,8 +2460,35 @@ pub struct Recipe {
24132460

24142461
impl Serializable for Recipe {
24152462
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
2416-
let id = String::read_from(buf)?;
2417-
let ty = String::read_from(buf)?;
2463+
let (id, ty, namespace) = {
2464+
let a = String::read_from(buf)?;
2465+
let b = String::read_from(buf)?;
2466+
2467+
let protocol_version = unsafe { crate::protocol::CURRENT_PROTOCOL_VERSION };
2468+
2469+
// 1.14+ swaps recipe identifier and type, and adds namespace to type
2470+
if protocol_version >= 477 {
2471+
let ty = a;
2472+
let id = b;
2473+
2474+
if let Some(at) = ty.find(':') {
2475+
let (namespace, ty) = ty.split_at(at + 1);
2476+
let ty: String = ty.into();
2477+
let namespace: String = namespace.into();
2478+
(id, ty, namespace)
2479+
} else {
2480+
(id, ty, "minecraft:".to_string())
2481+
}
2482+
} else {
2483+
let ty = b;
2484+
let id = a;
2485+
(id, ty, "minecraft:".to_string())
2486+
}
2487+
};
2488+
2489+
if namespace != "minecraft:" {
2490+
panic!("unrecognized recipe type namespace: {}", namespace);
2491+
}
24182492

24192493
let data =
24202494
match ty.as_ref() {
@@ -2473,13 +2547,18 @@ impl Serializable for Recipe {
24732547
experience: Serializable::read_from(buf)?,
24742548
cooking_time: Serializable::read_from(buf)?,
24752549
},
2476-
"campfire" => RecipeData::Campfire {
2550+
"campfire" | "campfire_cooking" => RecipeData::Campfire {
24772551
group: Serializable::read_from(buf)?,
24782552
ingredient: Serializable::read_from(buf)?,
24792553
result: Serializable::read_from(buf)?,
24802554
experience: Serializable::read_from(buf)?,
24812555
cooking_time: Serializable::read_from(buf)?,
24822556
},
2557+
"stonecutting" => RecipeData::Stonecutting {
2558+
group: Serializable::read_from(buf)?,
2559+
ingredient: Serializable::read_from(buf)?,
2560+
result: Serializable::read_from(buf)?,
2561+
},
24832562
_ => panic!("unrecognized recipe type: {}", ty)
24842563
};
24852564

src/protocol/versions.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::protocol::*;
22

3+
mod v1_14;
34
mod v19w02a;
45
mod v18w50a;
56
mod v1_13_2;
@@ -16,6 +17,8 @@ pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir:
1617
match version {
1718
// https://wiki.vg/Protocol_History
1819
// https://wiki.vg/Protocol_version_numbers#Versions_after_the_Netty_rewrite
20+
21+
477 => v1_14::translate_internal_packet_id(state, dir, id, to_internal),
1922

2023
// 19w02a
2124
452 => v19w02a::translate_internal_packet_id(state, dir, id, to_internal),

src/server/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ impl Server {
388388
match pck {
389389
Ok(pck) => handle_packet!{
390390
self pck {
391+
JoinGame_i32_ViewDistance => on_game_join_i32_viewdistance,
391392
JoinGame_i32 => on_game_join_i32,
392393
JoinGame_i8 => on_game_join_i8,
393394
JoinGame_i8_NoDebug => on_game_join_i8_nodebug,
@@ -666,6 +667,10 @@ impl Server {
666667
});
667668
}
668669

670+
fn on_game_join_i32_viewdistance(&mut self, join: packet::play::clientbound::JoinGame_i32_ViewDistance) {
671+
self.on_game_join(join.gamemode, join.entity_id)
672+
}
673+
669674
fn on_game_join_i32(&mut self, join: packet::play::clientbound::JoinGame_i32) {
670675
self.on_game_join(join.gamemode, join.entity_id)
671676
}

src/types/metadata.rs

+75
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,14 @@ impl Metadata {
322322
}
323323
15 => panic!("TODO: particle"),
324324
16 => m.put_raw(index, VillagerData::read_from(buf)?),
325+
17 => {
326+
if bool::read_from(buf)? {
327+
m.put_raw(index, Option::<protocol::VarInt>::read_from(buf)?);
328+
} else {
329+
m.put_raw::<Option<protocol::VarInt>>(index, None);
330+
}
331+
},
332+
18 => m.put_raw(index, PoseData::read_from(buf)?),
325333
_ => return Err(protocol::Error::Err("unknown metadata type".to_owned())),
326334
}
327335
}
@@ -405,6 +413,14 @@ impl Metadata {
405413
u8::write_to(&16, buf)?;
406414
val.write_to(buf)?;
407415
}
416+
Value::OptionalVarInt(ref val) => {
417+
u8::write_to(&17, buf)?;
418+
val.write_to(buf)?;
419+
}
420+
Value::Pose(ref val) => {
421+
u8::write_to(&18, buf)?;
422+
val.write_to(buf)?;
423+
}
408424
_ => panic!("unexpected metadata"),
409425
}
410426
}
@@ -478,6 +494,8 @@ pub enum Value {
478494
NBTTag(nbt::NamedTag),
479495
Particle(ParticleData),
480496
Villager(VillagerData),
497+
OptionalVarInt(Option<protocol::VarInt>),
498+
Pose(PoseData),
481499
}
482500

483501
#[derive(Debug)]
@@ -639,6 +657,38 @@ impl Serializable for VillagerData {
639657
}
640658
}
641659

660+
#[derive(Debug)]
661+
pub enum PoseData {
662+
Standing,
663+
FallFlying,
664+
Sleeping,
665+
Swimming,
666+
SpinAttack,
667+
Sneaking,
668+
Dying,
669+
}
670+
671+
impl Serializable for PoseData {
672+
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, protocol::Error> {
673+
let n = protocol::VarInt::read_from(buf)?;
674+
Ok(match n.0 {
675+
0 => PoseData::Standing,
676+
1 => PoseData::FallFlying,
677+
2 => PoseData::Sleeping,
678+
3 => PoseData::Swimming,
679+
4 => PoseData::SpinAttack,
680+
5 => PoseData::Sneaking,
681+
6 => PoseData::Dying,
682+
_ => panic!("unknown pose data: {}", n.0),
683+
})
684+
}
685+
686+
fn write_to<W: io::Write>(&self, _buf: &mut W) -> Result<(), protocol::Error> {
687+
unimplemented!()
688+
}
689+
}
690+
691+
642692

643693
pub trait MetaValue {
644694
fn unwrap(_: &Value) -> &Self;
@@ -862,6 +912,31 @@ impl MetaValue for VillagerData {
862912
}
863913
}
864914

915+
impl MetaValue for Option<protocol::VarInt> {
916+
fn unwrap(value: &Value) -> &Self {
917+
match *value {
918+
Value::OptionalVarInt(ref val) => val,
919+
_ => panic!("incorrect key"),
920+
}
921+
}
922+
fn wrap(self) -> Value {
923+
Value::OptionalVarInt(self)
924+
}
925+
}
926+
927+
impl MetaValue for PoseData {
928+
fn unwrap(value: &Value) -> &Self {
929+
match *value {
930+
Value::Pose(ref val) => val,
931+
_ => panic!("incorrect key"),
932+
}
933+
}
934+
fn wrap(self) -> Value {
935+
Value::Pose(self)
936+
}
937+
}
938+
939+
865940
#[cfg(test)]
866941
mod test {
867942
use super::*;

0 commit comments

Comments
 (0)