1.13.2 (404) multiprotocol support (#67)
Adds support for 1.13.2 protocol (404) Expands https://github.com/iceiix/steven/issues/18 Enhance protocol support Metadata: * Support 1.13.2 slot data format, bool and varint item id, optional damage (moved to NBT) https://wiki.vg/index.php?title=Slot_Data&type=revision&diff=14363&oldid=7835 Packets: * Add 1.13.2 packets, and implement all the command data parsers https://wiki.vg/Command_Data#Parsers * Send new plugin channel minecraft:brand https://wiki.vg/Plugin_channels#minecraft:brand * Add 1.13.2 metadata format, with shifted IDs https://wiki.vg/Entity_metadata#Entity_Metadata_Format * Implement particle entity metadata * Add structures for 16 new packets Blocks: The Flattening: * Assign flattened IDs in correct order using new 'offset' macro token * Assign hierarchical (pre-flattening) block IDs sequentially by counting Some data * Split VANILLA_ID_MAP into flat/hier struct, to support before and after the flattening * Extend travis build time to 20 minutes because the blocks macro takes a long time * Support both flat/hier blocks by passing protocol_version to by_vanilla_id Add block states and offsets for all blocks, replacing metadata for 1.13+: * Add stripped logs and what was Log2 to Log * Add the Wood blocks, should be called bark, previously Axis::None Log * Add leaves distance and offset * Add jungle/acacia to Leaves moved from Leaves2 * Add dispenser offsets, direction * Add note block states * Add offset None to Missing253 and Missing254, no holes in block states of 1.13.2 * Add bed colors * Add seagrass, tall seagrass, remove redundant deadgrass, and piston offset * Add torch, TNT, fire offsets, remove slabs * Add furnance offset, merges lit into a property * Add pressure plate offsets, new pressure plates, redstone ore/lit merged * Add lever offsets, new directions from ceiling/floor, rename LeverDirections * Add redstone torch offsets, new blocks since lit/unlit is now merged, and standing/wall is split * Change lever to split face/facing, rm LeverDirection, add AttachedFace * Add stone button offsets, face/facing similar to lever * Move face/facing data and variant to AttachedFace, reuse for lever/stonebutton * Add data_with_facing_and_powered() to AttachedFace, for lever/stonebutton * Add wooden button offsets each wood * Add pumpkin without a face * Add carved pumpkin, portal offsets * Add lit pumpkin (as jack-o-lantern) offsets after carved pumpkin * Add repeater offsets, merged into Repeater * Change brown mushroom block to booleans instead of MushroomVariant * Add mushroom block offsets, red/brown mushroom blocks, and a new mushroom stem block * Add command block, cobblestone walls, and flower pot offsets Empty flower pot, and potted plants including saplings. Rename variant DarkOak to DarkOakSaplings because it is a sapling, and remove the duplicate Dandelion variant which causes duplicate blocks. * Increase recursion limit in steven_blocks * Add colored banner offsets * Add wooden slab including double slab, in a different position for pre-1.13 and 1.13 * StoneSlabVariant::Wood -> StoneSlabVariant::PetrifiedWood * Add fence_gate_offset() for wooden fence gates * Add frosted ice age, offset * Add new blocks: kelp, turtle egg, coral, coral fans, sea pickle, blue ice, smooth stone * Add new blocks: conduit, void air, cave aid, bubble column, last of the 1.13 blocks
This commit is contained in:
parent
e360fbd3d4
commit
11f2d2ae13
|
@ -17,7 +17,7 @@ before_install:
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y gcc libegl1-mesa-dev libgles2-mesa-dev ; fi
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y gcc libegl1-mesa-dev libgles2-mesa-dev ; fi
|
||||||
script:
|
script:
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export LIBRARY_PATH="$LIBRARY_PATH:/usr/local/lib" ; fi
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export LIBRARY_PATH="$LIBRARY_PATH:/usr/local/lib" ; fi
|
||||||
- cargo build --verbose
|
- travis_wait 20 cargo build --verbose
|
||||||
- cargo test --verbose
|
- cargo test --verbose
|
||||||
os:
|
os:
|
||||||
- linux
|
- linux
|
||||||
|
|
2046
blocks/src/lib.rs
2046
blocks/src/lib.rs
File diff suppressed because it is too large
Load Diff
|
@ -105,6 +105,19 @@ impl Direction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn offset(&self) -> usize {
|
||||||
|
match *self {
|
||||||
|
Direction::North => 0,
|
||||||
|
Direction::East => 1,
|
||||||
|
Direction::South => 2,
|
||||||
|
Direction::West => 3,
|
||||||
|
Direction::Up => 4,
|
||||||
|
Direction::Down => 5,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn horizontal_index(&self) -> usize {
|
pub fn horizontal_index(&self) -> usize {
|
||||||
match *self {
|
match *self {
|
||||||
Direction::North => 2,
|
Direction::North => 2,
|
||||||
|
@ -115,6 +128,16 @@ impl Direction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn horizontal_offset(&self) -> usize {
|
||||||
|
match *self {
|
||||||
|
Direction::North => 0,
|
||||||
|
Direction::South => 1,
|
||||||
|
Direction::West => 2,
|
||||||
|
Direction::East => 3,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn axis(&self) -> Axis {
|
pub fn axis(&self) -> Axis {
|
||||||
match *self {
|
match *self {
|
||||||
Direction::Down | Direction::Up => Axis::Y,
|
Direction::Down | Direction::Up => Axis::Y,
|
||||||
|
|
|
@ -92,7 +92,7 @@ impl ecs::System for SignRenderer {
|
||||||
let info = m.get_component_mut(e, self.sign_info).unwrap();
|
let info = m.get_component_mut(e, self.sign_info).unwrap();
|
||||||
info.dirty = false;
|
info.dirty = false;
|
||||||
match world.get_block(position) {
|
match world.get_block(position) {
|
||||||
Block::WallSign{facing} => {
|
Block::WallSign{facing, ..} => {
|
||||||
info.offset_z = 7.5 / 16.0;
|
info.offset_z = 7.5 / 16.0;
|
||||||
match facing {
|
match facing {
|
||||||
Direction::North => {},
|
Direction::North => {},
|
||||||
|
@ -102,7 +102,7 @@ impl ecs::System for SignRenderer {
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Block::StandingSign{rotation} => {
|
Block::StandingSign{rotation, ..} => {
|
||||||
info.offset_y = 5.0 / 16.0;
|
info.offset_y = 5.0 / 16.0;
|
||||||
info.has_stand = true;
|
info.has_stand = true;
|
||||||
info.rotation = -(rotation.data() as f64 / 16.0) * PI * 2.0 + PI;
|
info.rotation = -(rotation.data() as f64 / 16.0) * PI * 2.0 + PI;
|
||||||
|
|
32
src/item.rs
32
src/item.rs
|
@ -21,7 +21,7 @@ use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
|
||||||
pub struct Stack {
|
pub struct Stack {
|
||||||
id: isize,
|
id: isize,
|
||||||
count: isize,
|
count: isize,
|
||||||
damage: isize,
|
damage: Option<isize>,
|
||||||
tag: Option<nbt::NamedTag>,
|
tag: Option<nbt::NamedTag>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ impl Default for Stack {
|
||||||
Stack {
|
Stack {
|
||||||
id: -1,
|
id: -1,
|
||||||
count: 0,
|
count: 0,
|
||||||
damage: 0,
|
damage: None,
|
||||||
tag: None,
|
tag: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,14 +39,31 @@ impl Default for Stack {
|
||||||
|
|
||||||
impl Serializable for Option<Stack> {
|
impl Serializable for Option<Stack> {
|
||||||
fn read_from<R: io::Read>(buf: &mut R) -> Result<Option<Stack>, protocol::Error> {
|
fn read_from<R: io::Read>(buf: &mut R) -> Result<Option<Stack>, protocol::Error> {
|
||||||
let id = buf.read_i16::<BigEndian>()?;
|
let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION };
|
||||||
|
|
||||||
|
if protocol_version >= 404 {
|
||||||
|
let present = buf.read_u8()? != 0;
|
||||||
|
if !present {
|
||||||
|
return Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = if protocol_version >= 404 {
|
||||||
|
protocol::VarInt::read_from(buf)?.0 as isize
|
||||||
|
} else {
|
||||||
|
buf.read_i16::<BigEndian>()? as isize
|
||||||
|
};
|
||||||
|
|
||||||
if id == -1 {
|
if id == -1 {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let count = buf.read_u8()? as isize;
|
let count = buf.read_u8()? as isize;
|
||||||
let damage = buf.read_i16::<BigEndian>()? as isize;
|
let damage = if protocol_version >= 404 {
|
||||||
|
// 1.13.2+ stores damage in the NBT
|
||||||
let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION };
|
None
|
||||||
|
} else {
|
||||||
|
Some(buf.read_i16::<BigEndian>()? as isize)
|
||||||
|
};
|
||||||
|
|
||||||
let tag: Option<nbt::NamedTag> = if protocol_version >= 47 {
|
let tag: Option<nbt::NamedTag> = if protocol_version >= 47 {
|
||||||
Serializable::read_from(buf)?
|
Serializable::read_from(buf)?
|
||||||
|
@ -74,9 +91,10 @@ impl Serializable for Option<Stack> {
|
||||||
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> {
|
||||||
match *self {
|
match *self {
|
||||||
Some(ref val) => {
|
Some(ref val) => {
|
||||||
|
// TODO: if protocol_version >= 404, send present and id varint, no damage, for 1.13.2
|
||||||
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.unwrap_or(0) as i16)?;
|
||||||
// TODO: compress zlib NBT if 1.7
|
// TODO: compress zlib NBT if 1.7
|
||||||
val.tag.write_to(buf)?;
|
val.tag.write_to(buf)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -828,14 +828,14 @@ struct BlockFace {
|
||||||
tint_index: i32,
|
tint_index: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
struct Model {
|
struct Model {
|
||||||
faces: Vec<Face>,
|
faces: Vec<Face>,
|
||||||
ambient_occlusion: bool,
|
ambient_occlusion: bool,
|
||||||
weight: f64,
|
weight: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
struct Face {
|
struct Face {
|
||||||
cull_face: Direction,
|
cull_face: Direction,
|
||||||
facing: Direction,
|
facing: Direction,
|
||||||
|
@ -1028,7 +1028,7 @@ pub const PRECOMPUTED_VERTS: [&[BlockVertex; 4]; 6] = [
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct BlockVertex {
|
pub struct BlockVertex {
|
||||||
pub x: f32,
|
pub x: f32,
|
||||||
pub y: f32,
|
pub y: f32,
|
||||||
|
|
|
@ -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; 9] = [340, 316, 315, 210, 109, 107, 74, 47, 5];
|
pub const SUPPORTED_PROTOCOLS: [i32; 10] = [404, 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,17 @@ impl <L: Lengthable> fmt::Debug for LenPrefixedBytes<L> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Lengthable for bool {
|
||||||
|
fn into(self) -> usize {
|
||||||
|
if self { 1 } else { 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from(u: usize) -> bool {
|
||||||
|
u != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Lengthable for u8 {
|
impl Lengthable for u8 {
|
||||||
fn into(self) -> usize {
|
fn into(self) -> usize {
|
||||||
self as usize
|
self as usize
|
||||||
|
|
|
@ -53,6 +53,10 @@ state_packets!(
|
||||||
packet TeleportConfirm {
|
packet TeleportConfirm {
|
||||||
field teleport_id: VarInt =,
|
field teleport_id: VarInt =,
|
||||||
}
|
}
|
||||||
|
packet QueryBlockNBT {
|
||||||
|
field transaction_id: VarInt =,
|
||||||
|
field location: Position =,
|
||||||
|
}
|
||||||
/// TabComplete is sent by the client when the client presses tab in
|
/// TabComplete is sent by the client when the client presses tab in
|
||||||
/// the chat box.
|
/// the chat box.
|
||||||
packet TabComplete {
|
packet TabComplete {
|
||||||
|
@ -156,6 +160,15 @@ state_packets!(
|
||||||
field channel: String =,
|
field channel: String =,
|
||||||
field data: LenPrefixedBytes<i16> =,
|
field data: LenPrefixedBytes<i16> =,
|
||||||
}
|
}
|
||||||
|
packet EditBook {
|
||||||
|
field new_book: Option<item::Stack> =,
|
||||||
|
field is_signing: bool =,
|
||||||
|
field hand: VarInt =,
|
||||||
|
}
|
||||||
|
packet QueryEntityNBT {
|
||||||
|
field transaction_id: VarInt =,
|
||||||
|
field entity_id: VarInt =,
|
||||||
|
}
|
||||||
/// 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 {
|
||||||
|
@ -240,10 +253,13 @@ state_packets!(
|
||||||
field yaw: f32 =,
|
field yaw: f32 =,
|
||||||
field pitch: f32 =,
|
field pitch: f32 =,
|
||||||
}
|
}
|
||||||
/// TODO: Document
|
/// SteerBoat is used to visually update the boat paddles.
|
||||||
packet SteerBoat {
|
packet SteerBoat {
|
||||||
field unknown: bool =,
|
field left_paddle_turning: bool =,
|
||||||
field unknown2: bool =,
|
field right_paddle_turning: bool =,
|
||||||
|
}
|
||||||
|
packet PickItem {
|
||||||
|
field slot_to_use: VarInt =,
|
||||||
}
|
}
|
||||||
/// CraftRecipeRequest is sent when player clicks a recipe in the crafting book.
|
/// CraftRecipeRequest is sent when player clicks a recipe in the crafting book.
|
||||||
packet CraftRecipeRequest {
|
packet CraftRecipeRequest {
|
||||||
|
@ -308,6 +324,9 @@ state_packets!(
|
||||||
field crafting_book_open: bool = when(|p: &CraftingBookData| p.action.0 == 1),
|
field crafting_book_open: bool = when(|p: &CraftingBookData| p.action.0 == 1),
|
||||||
field crafting_filter: bool = when(|p: &CraftingBookData| p.action.0 == 1),
|
field crafting_filter: bool = when(|p: &CraftingBookData| p.action.0 == 1),
|
||||||
}
|
}
|
||||||
|
packet NameItem {
|
||||||
|
field item_name: String =,
|
||||||
|
}
|
||||||
/// ResourcePackStatus informs the server of the client's current progress
|
/// ResourcePackStatus informs the server of the client's current progress
|
||||||
/// in activating the requested resource pack
|
/// in activating the requested resource pack
|
||||||
packet ResourcePackStatus {
|
packet ResourcePackStatus {
|
||||||
|
@ -322,17 +341,53 @@ state_packets!(
|
||||||
field action: VarInt =,
|
field action: VarInt =,
|
||||||
field tab_id: String = when(|p: &AdvancementTab| p.action.0 == 0),
|
field tab_id: String = when(|p: &AdvancementTab| p.action.0 == 0),
|
||||||
}
|
}
|
||||||
|
packet SelectTrade {
|
||||||
|
field selected_slot: VarInt =,
|
||||||
|
}
|
||||||
|
packet SetBeaconEffect {
|
||||||
|
field primary_effect: VarInt =,
|
||||||
|
field secondary_effect: VarInt =,
|
||||||
|
}
|
||||||
/// HeldItemChange is sent when the player changes the currently active
|
/// HeldItemChange is sent when the player changes the currently active
|
||||||
/// hotbar slot.
|
/// hotbar slot.
|
||||||
packet HeldItemChange {
|
packet HeldItemChange {
|
||||||
field slot: i16 =,
|
field slot: i16 =,
|
||||||
}
|
}
|
||||||
|
packet UpdateCommandBlock {
|
||||||
|
field location: Position =,
|
||||||
|
field command: String =,
|
||||||
|
field mode: VarInt =,
|
||||||
|
field flags: u8 =,
|
||||||
|
}
|
||||||
|
packet UpdateCommandBlockMinecart {
|
||||||
|
field entity_id: VarInt =,
|
||||||
|
field command: String =,
|
||||||
|
field track_output: bool =,
|
||||||
|
}
|
||||||
/// CreativeInventoryAction is sent when the client clicks in the creative
|
/// CreativeInventoryAction is sent when the client clicks in the creative
|
||||||
/// inventory. This is used to spawn items in creative.
|
/// inventory. This is used to spawn items in creative.
|
||||||
packet CreativeInventoryAction {
|
packet CreativeInventoryAction {
|
||||||
field slot: i16 =,
|
field slot: i16 =,
|
||||||
field clicked_item: Option<item::Stack> =,
|
field clicked_item: Option<item::Stack> =,
|
||||||
}
|
}
|
||||||
|
packet UpdateStructureBlock {
|
||||||
|
field location: Position =,
|
||||||
|
field action: VarInt =,
|
||||||
|
field mode: VarInt =,
|
||||||
|
field name: String =,
|
||||||
|
field offset_x: i8 =,
|
||||||
|
field offset_y: i8 =,
|
||||||
|
field offset_z: i8 =,
|
||||||
|
field size_x: i8 =,
|
||||||
|
field size_y: i8 =,
|
||||||
|
field size_z: i8 =,
|
||||||
|
field mirror: VarInt =,
|
||||||
|
field rotation: VarInt =,
|
||||||
|
field metadata: String =,
|
||||||
|
field integrity: f32 =,
|
||||||
|
field seed: VarLong =,
|
||||||
|
field flags: i8 =,
|
||||||
|
}
|
||||||
/// SetSign sets the text on a sign after placing it.
|
/// SetSign sets the text on a sign after placing it.
|
||||||
packet SetSign {
|
packet SetSign {
|
||||||
field location: Position =,
|
field location: Position =,
|
||||||
|
@ -704,6 +759,10 @@ state_packets!(
|
||||||
packet TabCompleteReply {
|
packet TabCompleteReply {
|
||||||
field matches: LenPrefixed<VarInt, String> =,
|
field matches: LenPrefixed<VarInt, String> =,
|
||||||
}
|
}
|
||||||
|
packet DeclareCommands {
|
||||||
|
field nodes: LenPrefixed<VarInt, packet::CommandNode> =,
|
||||||
|
field root_index: VarInt =,
|
||||||
|
}
|
||||||
/// ServerMessage is a message sent by the server. It could be from a player
|
/// ServerMessage is a message sent by the server. It could be from a player
|
||||||
/// or just a system message. The Type field controls the location the
|
/// or just a system message. The Type field controls the location the
|
||||||
/// message is displayed at and when the message is displayed.
|
/// message is displayed at and when the message is displayed.
|
||||||
|
@ -1148,6 +1207,15 @@ state_packets!(
|
||||||
field online: bool =,
|
field online: bool =,
|
||||||
field ping: u16 =,
|
field ping: u16 =,
|
||||||
}
|
}
|
||||||
|
packet FacePlayer {
|
||||||
|
field feet_eyes: VarInt =,
|
||||||
|
field target_x: f64 =,
|
||||||
|
field target_y: f64 =,
|
||||||
|
field target_z: f64 =,
|
||||||
|
field is_entity: bool =,
|
||||||
|
field entity_id: Option<VarInt> = when(|p: &FacePlayer| p.is_entity),
|
||||||
|
field entity_feet_eyes: Option<VarInt> = when(|p: &FacePlayer| p.is_entity),
|
||||||
|
}
|
||||||
/// 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.
|
||||||
|
@ -1179,12 +1247,21 @@ state_packets!(
|
||||||
field y: u8 =,
|
field y: u8 =,
|
||||||
field z: i32 =,
|
field z: i32 =,
|
||||||
}
|
}
|
||||||
packet UnlockRecipes {
|
packet UnlockRecipes_NoSmelting {
|
||||||
field action: VarInt =,
|
field action: VarInt =,
|
||||||
field crafting_book_open: bool =,
|
field crafting_book_open: bool =,
|
||||||
field filtering_craftable: bool =,
|
field filtering_craftable: bool =,
|
||||||
field recipe_ids: LenPrefixed<VarInt, VarInt> =,
|
field recipe_ids: LenPrefixed<VarInt, VarInt> =,
|
||||||
field recipe_ids2: LenPrefixed<VarInt, VarInt> = when(|p: &UnlockRecipes| p.action.0 == 0),
|
field recipe_ids2: LenPrefixed<VarInt, VarInt> = when(|p: &UnlockRecipes_NoSmelting| p.action.0 == 0),
|
||||||
|
}
|
||||||
|
packet UnlockRecipes_WithSmelting {
|
||||||
|
field action: VarInt =,
|
||||||
|
field crafting_book_open: bool =,
|
||||||
|
field filtering_craftable: bool =,
|
||||||
|
field smelting_book_open: bool =,
|
||||||
|
field filtering_smeltable: bool =,
|
||||||
|
field recipe_ids: LenPrefixed<VarInt, String> =,
|
||||||
|
field recipe_ids2: LenPrefixed<VarInt, String> = when(|p: &UnlockRecipes_WithSmelting| p.action.0 == 0),
|
||||||
}
|
}
|
||||||
/// EntityDestroy destroys the entities with the ids in the provided slice.
|
/// EntityDestroy destroys the entities with the ids in the provided slice.
|
||||||
packet EntityDestroy {
|
packet EntityDestroy {
|
||||||
|
@ -1229,6 +1306,10 @@ state_packets!(
|
||||||
field entity_id: i32 =,
|
field entity_id: i32 =,
|
||||||
field entity_status: i8 =,
|
field entity_status: i8 =,
|
||||||
}
|
}
|
||||||
|
packet NBTQueryResponse {
|
||||||
|
field transaction_id: VarInt =,
|
||||||
|
field nbt: Option<nbt::NamedTag> =,
|
||||||
|
}
|
||||||
/// 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 =,
|
||||||
|
@ -1405,6 +1486,11 @@ state_packets!(
|
||||||
field world_age: i64 =,
|
field world_age: i64 =,
|
||||||
field time_of_day: i64 =,
|
field time_of_day: i64 =,
|
||||||
}
|
}
|
||||||
|
packet StopSound {
|
||||||
|
field flags: u8 =,
|
||||||
|
field source: Option<VarInt> = when(|p: &StopSound| p.flags & 0x01 != 0),
|
||||||
|
field sound: Option<String> = when(|p: &StopSound| p.flags & 0x02 != 0),
|
||||||
|
}
|
||||||
/// Title configures an on-screen title.
|
/// Title configures an on-screen title.
|
||||||
packet Title {
|
packet Title {
|
||||||
field action: VarInt =,
|
field action: VarInt =,
|
||||||
|
@ -1544,6 +1630,14 @@ state_packets!(
|
||||||
field amplifier: i8 =,
|
field amplifier: i8 =,
|
||||||
field duration: i16 =,
|
field duration: i16 =,
|
||||||
}
|
}
|
||||||
|
packet DeclareRecipes {
|
||||||
|
field recipes: LenPrefixed<VarInt, packet::Recipe> =,
|
||||||
|
}
|
||||||
|
packet Tags {
|
||||||
|
field block_tags: LenPrefixed<VarInt, packet::Tags> =,
|
||||||
|
field item_tags: LenPrefixed<VarInt, packet::Tags> =,
|
||||||
|
field fluid_tags: LenPrefixed<VarInt, packet::Tags> =,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
login Login {
|
login Login {
|
||||||
|
@ -1569,6 +1663,11 @@ state_packets!(
|
||||||
field shared_secret: LenPrefixedBytes<i16> =,
|
field shared_secret: LenPrefixedBytes<i16> =,
|
||||||
field verify_token: LenPrefixedBytes<i16> =,
|
field verify_token: LenPrefixedBytes<i16> =,
|
||||||
}
|
}
|
||||||
|
packet LoginPluginResponse {
|
||||||
|
field message_id: VarInt =,
|
||||||
|
field successful: bool =,
|
||||||
|
field data: Vec<u8> =,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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
|
||||||
|
@ -1609,6 +1708,11 @@ state_packets!(
|
||||||
/// Threshold where a packet should be sent compressed
|
/// Threshold where a packet should be sent compressed
|
||||||
field threshold: VarInt =,
|
field threshold: VarInt =,
|
||||||
}
|
}
|
||||||
|
packet LoginPluginRequest {
|
||||||
|
field message_id: VarInt =,
|
||||||
|
field channel: String =,
|
||||||
|
field data: Vec<u8> =,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
status Status {
|
status Status {
|
||||||
|
@ -2165,3 +2269,324 @@ pub struct PlayerProperty {
|
||||||
pub value: String,
|
pub value: String,
|
||||||
pub signature: Option<String>,
|
pub signature: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use crate::item;
|
||||||
|
type RecipeIngredient = LenPrefixed<VarInt, Option<item::Stack>>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RecipeData {
|
||||||
|
Shapeless {
|
||||||
|
group: String,
|
||||||
|
ingredients: LenPrefixed<VarInt, RecipeIngredient>,
|
||||||
|
result: Option<item::Stack>,
|
||||||
|
},
|
||||||
|
Shaped {
|
||||||
|
width: VarInt,
|
||||||
|
height: VarInt,
|
||||||
|
group: String,
|
||||||
|
ingredients: Vec<RecipeIngredient>,
|
||||||
|
result: Option<item::Stack>,
|
||||||
|
},
|
||||||
|
ArmorDye,
|
||||||
|
BookCloning,
|
||||||
|
MapCloning,
|
||||||
|
MapExtending,
|
||||||
|
FireworkRocket,
|
||||||
|
FireworkStar,
|
||||||
|
FireworkStarFade,
|
||||||
|
RepairItem,
|
||||||
|
TippedArrow,
|
||||||
|
BannerDuplicate,
|
||||||
|
BannerAddPattern,
|
||||||
|
ShieldDecoration,
|
||||||
|
ShulkerBoxColoring,
|
||||||
|
Smelting {
|
||||||
|
group: String,
|
||||||
|
ingredient: RecipeIngredient,
|
||||||
|
result: Option<item::Stack>,
|
||||||
|
experience: f32,
|
||||||
|
cooking_time: VarInt,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RecipeData {
|
||||||
|
fn default() -> Self {
|
||||||
|
RecipeData::ArmorDye
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Recipe {
|
||||||
|
pub id: String,
|
||||||
|
pub ty: String,
|
||||||
|
pub data: RecipeData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for Recipe {
|
||||||
|
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
|
||||||
|
let id = String::read_from(buf)?;
|
||||||
|
let ty = String::read_from(buf)?;
|
||||||
|
|
||||||
|
let data =
|
||||||
|
match ty.as_ref() {
|
||||||
|
"crafting_shapeless" => RecipeData::Shapeless {
|
||||||
|
group: Serializable::read_from(buf)?,
|
||||||
|
ingredients: Serializable::read_from(buf)?,
|
||||||
|
result: Serializable::read_from(buf)?,
|
||||||
|
},
|
||||||
|
"crafting_shaped" => {
|
||||||
|
let width: VarInt = Serializable::read_from(buf)?;
|
||||||
|
let height: VarInt = Serializable::read_from(buf)?;
|
||||||
|
let group: String = Serializable::read_from(buf)?;
|
||||||
|
|
||||||
|
let capacity = width.0 as usize * height.0 as usize;
|
||||||
|
|
||||||
|
let mut ingredients = Vec::with_capacity(capacity);
|
||||||
|
for _ in 0 .. capacity {
|
||||||
|
ingredients.push(Serializable::read_from(buf)?);
|
||||||
|
}
|
||||||
|
let result: Option<item::Stack> = Serializable::read_from(buf)?;
|
||||||
|
|
||||||
|
RecipeData::Shaped { width, height, group, ingredients, result }
|
||||||
|
}
|
||||||
|
"crafting_special_armordye" => RecipeData::ArmorDye,
|
||||||
|
"crafting_special_bookcloning" => RecipeData::BookCloning,
|
||||||
|
"crafting_special_mapcloning" => RecipeData::MapCloning,
|
||||||
|
"crafting_special_mapextending" => RecipeData::MapExtending,
|
||||||
|
"crafting_special_firework_rocket" => RecipeData::FireworkRocket,
|
||||||
|
"crafting_special_firework_star" => RecipeData::FireworkStar,
|
||||||
|
"crafting_special_firework_star_fade" => RecipeData::FireworkStarFade,
|
||||||
|
"crafting_special_repairitem" => RecipeData::RepairItem,
|
||||||
|
"crafting_special_tippedarrow" => RecipeData::TippedArrow,
|
||||||
|
"crafting_special_bannerduplicate" => RecipeData::BannerDuplicate,
|
||||||
|
"crafting_special_banneraddpattern" => RecipeData::BannerAddPattern,
|
||||||
|
"crafting_special_shielddecoration" => RecipeData::ShieldDecoration,
|
||||||
|
"crafting_special_shulkerboxcoloring" => RecipeData::ShulkerBoxColoring,
|
||||||
|
"smelting" => RecipeData::Smelting {
|
||||||
|
group: Serializable::read_from(buf)?,
|
||||||
|
ingredient: Serializable::read_from(buf)?,
|
||||||
|
result: Serializable::read_from(buf)?,
|
||||||
|
experience: Serializable::read_from(buf)?,
|
||||||
|
cooking_time: Serializable::read_from(buf)?,
|
||||||
|
},
|
||||||
|
_ => panic!("unrecognized recipe type: {}", ty)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Recipe { id, ty, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_to<W: io::Write>(&self, _: &mut W) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Tags {
|
||||||
|
pub tag_name: String,
|
||||||
|
pub entries: LenPrefixed<VarInt, VarInt>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for Tags {
|
||||||
|
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
|
||||||
|
Ok(Tags {
|
||||||
|
tag_name: Serializable::read_from(buf)?,
|
||||||
|
entries: Serializable::read_from(buf)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_to<W: io::Write>(&self, _: &mut W) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct CommandNode {
|
||||||
|
pub flags: u8,
|
||||||
|
pub children: LenPrefixed<VarInt, VarInt>,
|
||||||
|
pub redirect_node: Option<VarInt>,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub parser: Option<String>,
|
||||||
|
pub properties: Option<CommandProperty>,
|
||||||
|
pub suggestions_type: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
enum CommandNodeType {
|
||||||
|
Root,
|
||||||
|
Literal,
|
||||||
|
Argument,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum CommandProperty {
|
||||||
|
Bool,
|
||||||
|
Double {
|
||||||
|
flags: u8,
|
||||||
|
min: Option<f64>,
|
||||||
|
max: Option<f64>,
|
||||||
|
},
|
||||||
|
Float {
|
||||||
|
flags: u8,
|
||||||
|
min: Option<f32>,
|
||||||
|
max: Option<f32>,
|
||||||
|
},
|
||||||
|
Integer {
|
||||||
|
flags: u8,
|
||||||
|
min: Option<i32>,
|
||||||
|
max: Option<i32>,
|
||||||
|
},
|
||||||
|
String {
|
||||||
|
token_type: VarInt,
|
||||||
|
},
|
||||||
|
Entity {
|
||||||
|
flags: u8,
|
||||||
|
},
|
||||||
|
GameProfile,
|
||||||
|
BlockPos,
|
||||||
|
Vec3,
|
||||||
|
Vec2,
|
||||||
|
BlockState,
|
||||||
|
BlockPredicate,
|
||||||
|
ItemStack,
|
||||||
|
ItemPredicate,
|
||||||
|
Color,
|
||||||
|
Component,
|
||||||
|
Message,
|
||||||
|
Nbt,
|
||||||
|
NbtPath,
|
||||||
|
Objective,
|
||||||
|
ObjectiveCriteria,
|
||||||
|
Operation,
|
||||||
|
Particle,
|
||||||
|
Rotation,
|
||||||
|
ScoreboardSlot,
|
||||||
|
ScoreHolder {
|
||||||
|
flags: u8,
|
||||||
|
},
|
||||||
|
Swizzle,
|
||||||
|
Team,
|
||||||
|
ItemSlot,
|
||||||
|
ResourceLocation,
|
||||||
|
MobEffect,
|
||||||
|
Function,
|
||||||
|
EntityAnchor,
|
||||||
|
Range {
|
||||||
|
decimals: bool,
|
||||||
|
},
|
||||||
|
ItemEnchantment,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Serializable for CommandNode {
|
||||||
|
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
|
||||||
|
let flags: u8 = Serializable::read_from(buf)?;
|
||||||
|
let children: LenPrefixed<VarInt, VarInt> = Serializable::read_from(buf)?;
|
||||||
|
|
||||||
|
let node_type = match flags & 0x03 {
|
||||||
|
0 => CommandNodeType::Root,
|
||||||
|
1 => CommandNodeType::Literal,
|
||||||
|
2 => CommandNodeType::Argument,
|
||||||
|
_ => panic!("unrecognized command node type {}", flags & 0x03),
|
||||||
|
};
|
||||||
|
let _is_executable = flags & 0x04 != 0;
|
||||||
|
let has_redirect = flags & 0x08 != 0;
|
||||||
|
let has_suggestions_type = flags & 0x10 != 0;
|
||||||
|
|
||||||
|
let redirect_node: Option<VarInt> = if has_redirect {
|
||||||
|
Some(Serializable::read_from(buf)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let name: Option<String> = if node_type == CommandNodeType::Argument || node_type == CommandNodeType::Literal {
|
||||||
|
Serializable::read_from(buf)?
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let parser: Option<String> = if node_type == CommandNodeType::Argument {
|
||||||
|
Serializable::read_from(buf)?
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let properties: Option<CommandProperty> = if let Some(ref parse) = parser {
|
||||||
|
Some(match parse.as_ref() {
|
||||||
|
"brigadier:bool" => CommandProperty::Bool,
|
||||||
|
"brigadier:double" => {
|
||||||
|
let flags = Serializable::read_from(buf)?;
|
||||||
|
let min = if flags & 0x01 != 0 { Some(Serializable::read_from(buf)?) } else { None };
|
||||||
|
let max = if flags & 0x02 != 0 { Some(Serializable::read_from(buf)?) } else { None };
|
||||||
|
CommandProperty::Double { flags, min, max }
|
||||||
|
},
|
||||||
|
"brigadier:float" => {
|
||||||
|
let flags = Serializable::read_from(buf)?;
|
||||||
|
let min = if flags & 0x01 != 0 { Some(Serializable::read_from(buf)?) } else { None };
|
||||||
|
let max = if flags & 0x02 != 0 { Some(Serializable::read_from(buf)?) } else { None };
|
||||||
|
CommandProperty::Float { flags, min, max }
|
||||||
|
},
|
||||||
|
"brigadier:integer" => {
|
||||||
|
let flags = Serializable::read_from(buf)?;
|
||||||
|
let min = if flags & 0x01 != 0 { Some(Serializable::read_from(buf)?) } else { None };
|
||||||
|
let max = if flags & 0x02 != 0 { Some(Serializable::read_from(buf)?) } else { None };
|
||||||
|
CommandProperty::Integer { flags, min, max }
|
||||||
|
},
|
||||||
|
"brigadier:string" => {
|
||||||
|
CommandProperty::String { token_type: Serializable::read_from(buf)? }
|
||||||
|
},
|
||||||
|
"minecraft:entity" => {
|
||||||
|
CommandProperty::Entity { flags: Serializable::read_from(buf)? }
|
||||||
|
},
|
||||||
|
"minecraft:game_profile" => CommandProperty::GameProfile,
|
||||||
|
"minecraft:block_pos" => CommandProperty::BlockPos,
|
||||||
|
"minecraft:vec3" => CommandProperty::Vec3,
|
||||||
|
"minecraft:vec2" => CommandProperty::Vec2,
|
||||||
|
"minecraft:block_state" => CommandProperty::BlockState,
|
||||||
|
"minecraft:block_predicate" => CommandProperty::BlockPredicate,
|
||||||
|
"minecraft:item_stack" => CommandProperty::ItemStack,
|
||||||
|
"minecraft:item_predicate" => CommandProperty::ItemPredicate,
|
||||||
|
"minecraft:color" => CommandProperty::Color,
|
||||||
|
"minecraft:component" => CommandProperty::Component,
|
||||||
|
"minecraft:message" => CommandProperty::Message,
|
||||||
|
"minecraft:nbt" => CommandProperty::Nbt,
|
||||||
|
"minecraft:nbt_path" => CommandProperty::NbtPath,
|
||||||
|
"minecraft:objective" => CommandProperty::Objective,
|
||||||
|
"minecraft:objective_criteria" => CommandProperty::ObjectiveCriteria,
|
||||||
|
"minecraft:operation" => CommandProperty::Operation,
|
||||||
|
"minecraft:particle" => CommandProperty::Particle,
|
||||||
|
"minecraft:rotation" => CommandProperty::Rotation,
|
||||||
|
"minecraft:scoreboard_slot" => CommandProperty::ScoreboardSlot,
|
||||||
|
"minecraft:score_holder" => {
|
||||||
|
CommandProperty::ScoreHolder { flags: Serializable::read_from(buf)? }
|
||||||
|
},
|
||||||
|
"minecraft:swizzle" => CommandProperty::Swizzle,
|
||||||
|
"minecraft:team" => CommandProperty::Team,
|
||||||
|
"minecraft:item_slot" => CommandProperty::ItemSlot,
|
||||||
|
"minecraft:resource_location" => CommandProperty::ResourceLocation,
|
||||||
|
"minecraft:mob_effect" => CommandProperty::MobEffect,
|
||||||
|
"minecraft:function" => CommandProperty::Function,
|
||||||
|
"minecraft:entity_anchor" => CommandProperty::EntityAnchor,
|
||||||
|
"minecraft:range" => {
|
||||||
|
CommandProperty::Range { decimals: Serializable::read_from(buf)? }
|
||||||
|
},
|
||||||
|
"minecraft:item_enchantment" => CommandProperty::ItemEnchantment,
|
||||||
|
_ => panic!("unsupported command node parser {}", parse),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let suggestions_type: Option<String> = if has_suggestions_type {
|
||||||
|
Serializable::read_from(buf)?
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(CommandNode { flags, children, redirect_node, name, parser, properties, suggestions_type })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_to<W: io::Write>(&self, _: &mut W) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::protocol::*;
|
use crate::protocol::*;
|
||||||
|
|
||||||
|
mod v1_13_2;
|
||||||
mod v1_12_2;
|
mod v1_12_2;
|
||||||
mod v1_11_2;
|
mod v1_11_2;
|
||||||
mod v1_10_2;
|
mod v1_10_2;
|
||||||
|
@ -13,6 +14,9 @@ pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir:
|
||||||
match version {
|
match version {
|
||||||
// https://wiki.vg/Protocol_History
|
// https://wiki.vg/Protocol_History
|
||||||
// https://wiki.vg/Protocol_version_numbers#Versions_after_the_Netty_rewrite
|
// https://wiki.vg/Protocol_version_numbers#Versions_after_the_Netty_rewrite
|
||||||
|
// 1.13.2
|
||||||
|
404 => v1_13_2::translate_internal_packet_id(state, dir, id, to_internal),
|
||||||
|
|
||||||
// 1.12.2
|
// 1.12.2
|
||||||
340 => v1_12_2::translate_internal_packet_id(state, dir, id, to_internal),
|
340 => v1_12_2::translate_internal_packet_id(state, dir, id, to_internal),
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@ protocol_packet_ids!(
|
||||||
0x2e => PlayerInfo
|
0x2e => PlayerInfo
|
||||||
0x2f => TeleportPlayer_WithConfirm
|
0x2f => TeleportPlayer_WithConfirm
|
||||||
0x30 => EntityUsedBed
|
0x30 => EntityUsedBed
|
||||||
0x31 => UnlockRecipes
|
0x31 => UnlockRecipes_NoSmelting
|
||||||
0x32 => EntityDestroy
|
0x32 => EntityDestroy
|
||||||
0x33 => EntityRemoveEffect
|
0x33 => EntityRemoveEffect
|
||||||
0x34 => ResourcePackSend
|
0x34 => ResourcePackSend
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
protocol_packet_ids!(
|
||||||
|
handshake Handshaking {
|
||||||
|
serverbound Serverbound {
|
||||||
|
0x00 => Handshake
|
||||||
|
}
|
||||||
|
clientbound Clientbound {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
play Play {
|
||||||
|
serverbound Serverbound {
|
||||||
|
0x00 => TeleportConfirm
|
||||||
|
0x01 => QueryBlockNBT
|
||||||
|
0x02 => ChatMessage
|
||||||
|
0x03 => ClientStatus
|
||||||
|
0x04 => ClientSettings
|
||||||
|
0x05 => TabComplete
|
||||||
|
0x06 => ConfirmTransactionServerbound
|
||||||
|
0x07 => EnchantItem
|
||||||
|
0x08 => ClickWindow
|
||||||
|
0x09 => CloseWindow
|
||||||
|
0x0a => PluginMessageServerbound
|
||||||
|
0x0b => EditBook
|
||||||
|
0x0c => QueryEntityNBT
|
||||||
|
0x0d => UseEntity
|
||||||
|
0x0e => KeepAliveServerbound_i64
|
||||||
|
0x0f => Player
|
||||||
|
0x10 => PlayerPosition
|
||||||
|
0x11 => PlayerPositionLook
|
||||||
|
0x12 => PlayerLook
|
||||||
|
0x13 => VehicleMove
|
||||||
|
0x14 => SteerBoat
|
||||||
|
0x15 => PickItem
|
||||||
|
0x16 => CraftRecipeRequest
|
||||||
|
0x17 => ClientAbilities
|
||||||
|
0x18 => PlayerDigging
|
||||||
|
0x19 => PlayerAction
|
||||||
|
0x1a => SteerVehicle
|
||||||
|
0x1b => CraftingBookData
|
||||||
|
0x1c => NameItem
|
||||||
|
0x1d => ResourcePackStatus
|
||||||
|
0x1e => AdvancementTab
|
||||||
|
0x1f => SelectTrade
|
||||||
|
0x20 => SetBeaconEffect
|
||||||
|
0x21 => HeldItemChange
|
||||||
|
0x22 => UpdateCommandBlock
|
||||||
|
0x23 => UpdateCommandBlockMinecart
|
||||||
|
0x24 => CreativeInventoryAction
|
||||||
|
0x25 => UpdateStructureBlock
|
||||||
|
0x26 => SetSign
|
||||||
|
0x27 => ArmSwing
|
||||||
|
0x28 => SpectateTeleport
|
||||||
|
0x29 => PlayerBlockPlacement_f32
|
||||||
|
0x2a => UseItem
|
||||||
|
}
|
||||||
|
clientbound Clientbound {
|
||||||
|
0x00 => SpawnObject
|
||||||
|
0x01 => SpawnExperienceOrb
|
||||||
|
0x02 => SpawnGlobalEntity
|
||||||
|
0x03 => SpawnMob
|
||||||
|
0x04 => SpawnPainting
|
||||||
|
0x05 => SpawnPlayer_f64
|
||||||
|
0x06 => Animation
|
||||||
|
0x07 => Statistics
|
||||||
|
0x08 => BlockBreakAnimation
|
||||||
|
0x09 => UpdateBlockEntity
|
||||||
|
0x0a => BlockAction
|
||||||
|
0x0b => BlockChange_VarInt
|
||||||
|
0x0c => BossBar
|
||||||
|
0x0d => ServerDifficulty
|
||||||
|
0x0e => ServerMessage
|
||||||
|
0x0f => MultiBlockChange_VarInt
|
||||||
|
0x10 => TabCompleteReply
|
||||||
|
0x11 => DeclareCommands
|
||||||
|
0x12 => ConfirmTransaction
|
||||||
|
0x13 => WindowClose
|
||||||
|
0x14 => WindowOpen
|
||||||
|
0x15 => WindowItems
|
||||||
|
0x16 => WindowProperty
|
||||||
|
0x17 => WindowSetSlot
|
||||||
|
0x18 => SetCooldown
|
||||||
|
0x19 => PluginMessageClientbound
|
||||||
|
0x1a => NamedSoundEffect
|
||||||
|
0x1b => Disconnect
|
||||||
|
0x1c => EntityAction
|
||||||
|
0x1d => NBTQueryResponse
|
||||||
|
0x1e => Explosion
|
||||||
|
0x1f => ChunkUnload
|
||||||
|
0x20 => ChangeGameState
|
||||||
|
0x21 => KeepAliveClientbound_i64
|
||||||
|
0x22 => ChunkData
|
||||||
|
0x23 => Effect
|
||||||
|
0x24 => Particle
|
||||||
|
0x25 => JoinGame_i32
|
||||||
|
0x26 => Maps
|
||||||
|
0x27 => Entity
|
||||||
|
0x28 => EntityMove_i16
|
||||||
|
0x29 => EntityLookAndMove_i16
|
||||||
|
0x2a => EntityLook_VarInt
|
||||||
|
0x2b => VehicleTeleport
|
||||||
|
0x2c => SignEditorOpen
|
||||||
|
0x2d => CraftRecipeResponse
|
||||||
|
0x2e => PlayerAbilities
|
||||||
|
0x2f => CombatEvent
|
||||||
|
0x30 => PlayerInfo
|
||||||
|
0x31 => FacePlayer
|
||||||
|
0x32 => TeleportPlayer_WithConfirm
|
||||||
|
0x33 => EntityUsedBed
|
||||||
|
0x34 => UnlockRecipes_WithSmelting
|
||||||
|
0x35 => EntityDestroy
|
||||||
|
0x36 => EntityRemoveEffect
|
||||||
|
0x37 => ResourcePackSend
|
||||||
|
0x38 => Respawn
|
||||||
|
0x39 => EntityHeadLook
|
||||||
|
0x3a => SelectAdvancementTab
|
||||||
|
0x3b => WorldBorder
|
||||||
|
0x3c => Camera
|
||||||
|
0x3d => SetCurrentHotbarSlot
|
||||||
|
0x3e => ScoreboardDisplay
|
||||||
|
0x3f => EntityMetadata
|
||||||
|
0x40 => EntityAttach
|
||||||
|
0x41 => EntityVelocity
|
||||||
|
0x42 => EntityEquipment
|
||||||
|
0x43 => SetExperience
|
||||||
|
0x44 => UpdateHealth
|
||||||
|
0x45 => ScoreboardObjective
|
||||||
|
0x46 => SetPassengers
|
||||||
|
0x47 => Teams
|
||||||
|
0x48 => UpdateScore
|
||||||
|
0x49 => SpawnPosition
|
||||||
|
0x4a => TimeUpdate
|
||||||
|
0x4c => StopSound
|
||||||
|
0x4d => SoundEffect
|
||||||
|
0x4e => PlayerListHeaderFooter
|
||||||
|
0x4f => CollectItem
|
||||||
|
0x50 => EntityTeleport_f64
|
||||||
|
0x51 => Advancements
|
||||||
|
0x52 => EntityProperties
|
||||||
|
0x53 => EntityEffect
|
||||||
|
0x54 => DeclareRecipes
|
||||||
|
0x55 => Tags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
login Login {
|
||||||
|
serverbound Serverbound {
|
||||||
|
0x00 => LoginStart
|
||||||
|
0x01 => EncryptionResponse
|
||||||
|
0x02 => LoginPluginResponse
|
||||||
|
}
|
||||||
|
clientbound Clientbound {
|
||||||
|
0x00 => LoginDisconnect
|
||||||
|
0x01 => EncryptionRequest
|
||||||
|
0x02 => LoginSuccess
|
||||||
|
0x03 => SetInitialCompression
|
||||||
|
0x04 => LoginPluginRequest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
status Status {
|
||||||
|
serverbound Serverbound {
|
||||||
|
0x00 => StatusRequest
|
||||||
|
0x01 => StatusPing
|
||||||
|
}
|
||||||
|
clientbound Clientbound {
|
||||||
|
0x00 => StatusResponse
|
||||||
|
0x01 => StatusPong
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
|
@ -242,13 +242,13 @@ impl Server {
|
||||||
if xx == 0 && z == 0 {
|
if xx == 0 && z == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
server.world.set_block(Position::new(x + xx, h + 3, z + zz), block::Leaves{ variant: block::TreeVariant::Oak, check_decay: false, decayable: false });
|
server.world.set_block(Position::new(x + xx, h + 3, z + zz), block::Leaves{ variant: block::TreeVariant::Oak, check_decay: false, decayable: false, distance: 1 });
|
||||||
server.world.set_block(Position::new(x + xx, h + 4, z + zz), block::Leaves{ variant: block::TreeVariant::Oak, check_decay: false, decayable: false });
|
server.world.set_block(Position::new(x + xx, h + 4, z + zz), block::Leaves{ variant: block::TreeVariant::Oak, check_decay: false, decayable: false, distance: 1 });
|
||||||
if xx.abs() <= 1 && zz.abs() <= 1 {
|
if xx.abs() <= 1 && zz.abs() <= 1 {
|
||||||
server.world.set_block(Position::new(x + xx, h + 5, z + zz), block::Leaves{ variant: block::TreeVariant::Oak, check_decay: false, decayable: false });
|
server.world.set_block(Position::new(x + xx, h + 5, z + zz), block::Leaves{ variant: block::TreeVariant::Oak, check_decay: false, decayable: false, distance: 1 });
|
||||||
}
|
}
|
||||||
if xx * xx + zz * zz <= 1 {
|
if xx * xx + zz * zz <= 1 {
|
||||||
server.world.set_block(Position::new(x + xx, h + 6, z + zz), block::Leaves{ variant: block::TreeVariant::Oak, check_decay: false, decayable: false });
|
server.world.set_block(Position::new(x + xx, h + 6, z + zz), block::Leaves{ variant: block::TreeVariant::Oak, check_decay: false, decayable: false, distance: 1 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -280,7 +280,7 @@ impl Server {
|
||||||
disconnect_reason: None,
|
disconnect_reason: None,
|
||||||
just_disconnected: false,
|
just_disconnected: false,
|
||||||
|
|
||||||
world: world::World::new(),
|
world: world::World::new(protocol_version),
|
||||||
world_age: 0,
|
world_age: 0,
|
||||||
world_time: 0.0,
|
world_time: 0.0,
|
||||||
world_time_target: 0.0,
|
world_time_target: 0.0,
|
||||||
|
@ -701,7 +701,7 @@ impl Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_respawn(&mut self, respawn: packet::play::clientbound::Respawn) {
|
fn on_respawn(&mut self, respawn: packet::play::clientbound::Respawn) {
|
||||||
self.world = world::World::new();
|
self.world = world::World::new(self.protocol_version);
|
||||||
let gamemode = Gamemode::from_int((respawn.gamemode & 0x7) as i32);
|
let gamemode = Gamemode::from_int((respawn.gamemode & 0x7) as i32);
|
||||||
|
|
||||||
if let Some(player) = self.player {
|
if let Some(player) = self.player {
|
||||||
|
@ -1155,7 +1155,7 @@ impl Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_block_change(&mut self, location: Position, id: i32) {
|
fn on_block_change(&mut self, location: Position, id: i32) {
|
||||||
self.world.set_block(location, block::Block::by_vanilla_id(id as usize))
|
self.world.set_block(location, block::Block::by_vanilla_id(id as usize, self.protocol_version))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_block_change_varint(&mut self, block_change: packet::play::clientbound::BlockChange_VarInt) {
|
fn on_block_change_varint(&mut self, block_change: packet::play::clientbound::BlockChange_VarInt) {
|
||||||
|
@ -1179,7 +1179,7 @@ impl Server {
|
||||||
record.y as i32,
|
record.y as i32,
|
||||||
oz + (record.xz & 0xF) as i32
|
oz + (record.xz & 0xF) as i32
|
||||||
),
|
),
|
||||||
block::Block::by_vanilla_id(record.block_id.0 as usize)
|
block::Block::by_vanilla_id(record.block_id.0 as usize, self.protocol_version)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1202,7 +1202,7 @@ impl Server {
|
||||||
|
|
||||||
self.world.set_block(
|
self.world.set_block(
|
||||||
Position::new(x, y, z),
|
Position::new(x, y, z),
|
||||||
block::Block::by_vanilla_id(id as usize)
|
block::Block::by_vanilla_id(id as usize, self.protocol_version)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,18 @@ pub struct Brand {
|
||||||
|
|
||||||
impl Brand {
|
impl Brand {
|
||||||
pub fn as_message(self) -> PluginMessageServerbound {
|
pub fn as_message(self) -> PluginMessageServerbound {
|
||||||
|
let protocol_version = unsafe { crate::protocol::CURRENT_PROTOCOL_VERSION };
|
||||||
|
|
||||||
|
let channel_name = if protocol_version >= 404 {
|
||||||
|
"minecraft:brand"
|
||||||
|
} else {
|
||||||
|
"MC|Brand"
|
||||||
|
};
|
||||||
|
|
||||||
let mut data = vec![];
|
let mut data = vec![];
|
||||||
Serializable::write_to(&self.brand, &mut data).unwrap();
|
Serializable::write_to(&self.brand, &mut data).unwrap();
|
||||||
PluginMessageServerbound {
|
PluginMessageServerbound {
|
||||||
channel: "MC|Brand".into(),
|
channel: channel_name.into(),
|
||||||
data,
|
data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ use std::io;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use crate::protocol;
|
use crate::protocol;
|
||||||
use crate::protocol::Serializable;
|
use crate::protocol::Serializable;
|
||||||
|
use crate::protocol::LenPrefixed;
|
||||||
use crate::format;
|
use crate::format;
|
||||||
use crate::item;
|
use crate::item;
|
||||||
use crate::shared::Position;
|
use crate::shared::Position;
|
||||||
|
@ -271,13 +272,151 @@ impl Metadata {
|
||||||
u8::write_to(&0xFF, buf)?;
|
u8::write_to(&0xFF, buf)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_from113<R: io::Read>(buf: &mut R) -> Result<Self, protocol::Error> {
|
||||||
|
let mut m = Self::new();
|
||||||
|
loop {
|
||||||
|
let index = u8::read_from(buf)? as i32;
|
||||||
|
if index == 0xFF {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let ty = protocol::VarInt::read_from(buf)?.0;
|
||||||
|
match ty {
|
||||||
|
0 => m.put_raw(index, i8::read_from(buf)?),
|
||||||
|
1 => m.put_raw(index, protocol::VarInt::read_from(buf)?.0),
|
||||||
|
2 => m.put_raw(index, f32::read_from(buf)?),
|
||||||
|
3 => m.put_raw(index, String::read_from(buf)?),
|
||||||
|
4 => m.put_raw(index, format::Component::read_from(buf)?),
|
||||||
|
5 => m.put_raw(index, LenPrefixed::<bool, format::Component>::read_from(buf)?),
|
||||||
|
6 => m.put_raw(index, Option::<item::Stack>::read_from(buf)?),
|
||||||
|
7 => m.put_raw(index, bool::read_from(buf)?),
|
||||||
|
8 => m.put_raw(index,
|
||||||
|
[f32::read_from(buf)?,
|
||||||
|
f32::read_from(buf)?,
|
||||||
|
f32::read_from(buf)?]),
|
||||||
|
9 => m.put_raw(index, Position::read_from(buf)?),
|
||||||
|
10 => {
|
||||||
|
if bool::read_from(buf)? {
|
||||||
|
m.put_raw(index, Option::<Position>::read_from(buf)?);
|
||||||
|
} else {
|
||||||
|
m.put_raw::<Option<Position>>(index, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
11 => m.put_raw(index, protocol::VarInt::read_from(buf)?),
|
||||||
|
12 => {
|
||||||
|
if bool::read_from(buf)? {
|
||||||
|
m.put_raw(index, Option::<protocol::UUID>::read_from(buf)?);
|
||||||
|
} else {
|
||||||
|
m.put_raw::<Option<protocol::UUID>>(index, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
13 => m.put_raw(index, protocol::VarInt::read_from(buf)?.0 as u16),
|
||||||
|
14 => {
|
||||||
|
let ty = u8::read_from(buf)?;
|
||||||
|
if ty != 0 {
|
||||||
|
let name = nbt::read_string(buf)?;
|
||||||
|
let tag = nbt::Tag::read_from(buf)?;
|
||||||
|
|
||||||
|
m.put_raw(index, nbt::NamedTag(name, tag));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
15 => panic!("TODO: particle"),
|
||||||
|
_ => return Err(protocol::Error::Err("unknown metadata type".to_owned())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_to113<W: io::Write>(&self, buf: &mut W) -> Result<(), protocol::Error> {
|
||||||
|
for (k, v) in &self.map {
|
||||||
|
(*k as u8).write_to(buf)?;
|
||||||
|
match *v {
|
||||||
|
Value::Byte(ref val) => {
|
||||||
|
u8::write_to(&0, buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::Int(ref val) => {
|
||||||
|
u8::write_to(&1, buf)?;
|
||||||
|
protocol::VarInt(*val).write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::Float(ref val) => {
|
||||||
|
u8::write_to(&2, buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::String(ref val) => {
|
||||||
|
u8::write_to(&3, buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::FormatComponent(ref val) => {
|
||||||
|
u8::write_to(&4, buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::OptionalFormatComponent(ref val) => {
|
||||||
|
u8::write_to(&5, buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::OptionalItemStack(ref val) => {
|
||||||
|
u8::write_to(&6, buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::Bool(ref val) => {
|
||||||
|
u8::write_to(&7, buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::Vector(ref val) => {
|
||||||
|
u8::write_to(&8, buf)?;
|
||||||
|
val[0].write_to(buf)?;
|
||||||
|
val[1].write_to(buf)?;
|
||||||
|
val[2].write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::Position(ref val) => {
|
||||||
|
u8::write_to(&9, buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::OptionalPosition(ref val) => {
|
||||||
|
u8::write_to(&10, buf)?;
|
||||||
|
val.is_some().write_to(buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::Direction(ref val) => {
|
||||||
|
u8::write_to(&11, buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::OptionalUUID(ref val) => {
|
||||||
|
u8::write_to(&12, buf)?;
|
||||||
|
val.is_some().write_to(buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::Block(ref val) => {
|
||||||
|
u8::write_to(&13, buf)?;
|
||||||
|
protocol::VarInt(*val as i32).write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::NBTTag(ref _val) => {
|
||||||
|
u8::write_to(&14, buf)?;
|
||||||
|
// TODO: write NBT tags metadata
|
||||||
|
//nbt::Tag(*val).write_to(buf)?;
|
||||||
|
}
|
||||||
|
Value::Particle(ref val) => {
|
||||||
|
u8::write_to(&15, buf)?;
|
||||||
|
val.write_to(buf)?;
|
||||||
|
}
|
||||||
|
_ => panic!("unexpected metadata"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u8::write_to(&0xFF, buf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serializable for Metadata {
|
impl Serializable for Metadata {
|
||||||
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 protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION };
|
let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION };
|
||||||
|
|
||||||
if protocol_version >= 74 {
|
if protocol_version >= 404 {
|
||||||
|
Metadata::read_from113(buf)
|
||||||
|
} else if protocol_version >= 74 {
|
||||||
Metadata::read_from19(buf)
|
Metadata::read_from19(buf)
|
||||||
} else {
|
} else {
|
||||||
Metadata::read_from18(buf)
|
Metadata::read_from18(buf)
|
||||||
|
@ -287,7 +426,9 @@ impl Serializable for Metadata {
|
||||||
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> {
|
||||||
let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION };
|
let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION };
|
||||||
|
|
||||||
if protocol_version >= 74 {
|
if protocol_version >= 404 {
|
||||||
|
self.write_to113(buf)
|
||||||
|
} else if protocol_version >= 74 {
|
||||||
self.write_to19(buf)
|
self.write_to19(buf)
|
||||||
} else {
|
} else {
|
||||||
self.write_to18(buf)
|
self.write_to18(buf)
|
||||||
|
@ -319,6 +460,7 @@ pub enum Value {
|
||||||
Float(f32),
|
Float(f32),
|
||||||
String(String),
|
String(String),
|
||||||
FormatComponent(format::Component),
|
FormatComponent(format::Component),
|
||||||
|
OptionalFormatComponent(LenPrefixed<bool, format::Component>),
|
||||||
OptionalItemStack(Option<item::Stack>),
|
OptionalItemStack(Option<item::Stack>),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Vector([f32; 3]),
|
Vector([f32; 3]),
|
||||||
|
@ -329,6 +471,146 @@ pub enum Value {
|
||||||
OptionalUUID(Option<protocol::UUID>),
|
OptionalUUID(Option<protocol::UUID>),
|
||||||
Block(u16), // TODO: Proper type
|
Block(u16), // TODO: Proper type
|
||||||
NBTTag(nbt::NamedTag),
|
NBTTag(nbt::NamedTag),
|
||||||
|
Particle(ParticleData),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ParticleData {
|
||||||
|
AmbientEntityEffect,
|
||||||
|
AngryVillager,
|
||||||
|
Barrier,
|
||||||
|
Block {
|
||||||
|
block_state: protocol::VarInt,
|
||||||
|
},
|
||||||
|
Bubble,
|
||||||
|
Cloud,
|
||||||
|
Crit,
|
||||||
|
DamageIndicator,
|
||||||
|
DragonBreath,
|
||||||
|
DrippingLava,
|
||||||
|
DrippingWater,
|
||||||
|
Dust {
|
||||||
|
red: f32,
|
||||||
|
green: f32,
|
||||||
|
blue: f32,
|
||||||
|
scale: f32,
|
||||||
|
},
|
||||||
|
Effect,
|
||||||
|
ElderGuardian,
|
||||||
|
EnchantedHit,
|
||||||
|
Enchant,
|
||||||
|
EndRod,
|
||||||
|
EntityEffect,
|
||||||
|
ExplosionEmitter,
|
||||||
|
Explosion,
|
||||||
|
FallingDust {
|
||||||
|
block_state: protocol::VarInt,
|
||||||
|
},
|
||||||
|
Firework,
|
||||||
|
Fishing,
|
||||||
|
Flame,
|
||||||
|
HappyVillager,
|
||||||
|
Heart,
|
||||||
|
InstantEffect,
|
||||||
|
Item {
|
||||||
|
item: Option<item::Stack>,
|
||||||
|
},
|
||||||
|
ItemSlime,
|
||||||
|
ItemSnowball,
|
||||||
|
LargeSmoke,
|
||||||
|
Lava,
|
||||||
|
Mycelium,
|
||||||
|
Note,
|
||||||
|
Poof,
|
||||||
|
Portal,
|
||||||
|
Rain,
|
||||||
|
Smoke,
|
||||||
|
Spit,
|
||||||
|
SquidInk,
|
||||||
|
SweepAttack,
|
||||||
|
TotemOfUndying,
|
||||||
|
Underwater,
|
||||||
|
Splash,
|
||||||
|
Witch,
|
||||||
|
BubblePop,
|
||||||
|
CurrentDown,
|
||||||
|
BubbleColumnUp,
|
||||||
|
Nautilus,
|
||||||
|
Dolphin,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for ParticleData {
|
||||||
|
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, protocol::Error> {
|
||||||
|
let id = protocol::VarInt::read_from(buf)?.0;
|
||||||
|
Ok(match id {
|
||||||
|
0 => ParticleData::AmbientEntityEffect,
|
||||||
|
1 => ParticleData::AngryVillager,
|
||||||
|
2 => ParticleData::Barrier,
|
||||||
|
3 => ParticleData::Block {
|
||||||
|
block_state: Serializable::read_from(buf)?
|
||||||
|
},
|
||||||
|
4 => ParticleData::Bubble,
|
||||||
|
5 => ParticleData::Cloud,
|
||||||
|
6 => ParticleData::Crit,
|
||||||
|
7 => ParticleData::DamageIndicator,
|
||||||
|
8 => ParticleData::DragonBreath,
|
||||||
|
9 => ParticleData::DrippingLava,
|
||||||
|
10 => ParticleData::DrippingWater,
|
||||||
|
11 => ParticleData::Dust {
|
||||||
|
red: Serializable::read_from(buf)?,
|
||||||
|
green: Serializable::read_from(buf)?,
|
||||||
|
blue: Serializable::read_from(buf)?,
|
||||||
|
scale: Serializable::read_from(buf)?,
|
||||||
|
},
|
||||||
|
12 => ParticleData::Effect,
|
||||||
|
13 => ParticleData::ElderGuardian,
|
||||||
|
14 => ParticleData::EnchantedHit,
|
||||||
|
15 => ParticleData::Enchant,
|
||||||
|
16 => ParticleData::EndRod,
|
||||||
|
17 => ParticleData::EntityEffect,
|
||||||
|
18 => ParticleData::ExplosionEmitter,
|
||||||
|
19 => ParticleData::Explosion,
|
||||||
|
20 => ParticleData::FallingDust {
|
||||||
|
block_state: Serializable::read_from(buf)?,
|
||||||
|
},
|
||||||
|
21 => ParticleData::Firework,
|
||||||
|
22 => ParticleData::Fishing,
|
||||||
|
23 => ParticleData::Flame,
|
||||||
|
24 => ParticleData::HappyVillager,
|
||||||
|
25 => ParticleData::Heart,
|
||||||
|
26 => ParticleData::InstantEffect,
|
||||||
|
27 => ParticleData::Item {
|
||||||
|
item: Serializable::read_from(buf)?,
|
||||||
|
},
|
||||||
|
28 => ParticleData::ItemSlime,
|
||||||
|
29 => ParticleData::ItemSnowball,
|
||||||
|
30 => ParticleData::LargeSmoke,
|
||||||
|
31 => ParticleData::Lava,
|
||||||
|
32 => ParticleData::Mycelium,
|
||||||
|
33 => ParticleData::Note,
|
||||||
|
34 => ParticleData::Poof,
|
||||||
|
35 => ParticleData::Portal,
|
||||||
|
36 => ParticleData::Rain,
|
||||||
|
37 => ParticleData::Smoke,
|
||||||
|
38 => ParticleData::Spit,
|
||||||
|
39 => ParticleData::SquidInk,
|
||||||
|
40 => ParticleData::SweepAttack,
|
||||||
|
41 => ParticleData::TotemOfUndying,
|
||||||
|
42 => ParticleData::Underwater,
|
||||||
|
43 => ParticleData::Splash,
|
||||||
|
44 => ParticleData::Witch,
|
||||||
|
45 => ParticleData::BubblePop,
|
||||||
|
46 => ParticleData::CurrentDown,
|
||||||
|
47 => ParticleData::BubbleColumnUp,
|
||||||
|
48 => ParticleData::Nautilus,
|
||||||
|
49 => ParticleData::Dolphin,
|
||||||
|
_ => panic!("unrecognized particle data id {}", id),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_to<W: io::Write>(&self, _buf: &mut W) -> Result<(), protocol::Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MetaValue {
|
pub trait MetaValue {
|
||||||
|
@ -408,6 +690,19 @@ impl MetaValue for format::Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MetaValue for LenPrefixed<bool, format::Component> {
|
||||||
|
fn unwrap(value: &Value) -> &Self {
|
||||||
|
match *value {
|
||||||
|
Value::OptionalFormatComponent(ref val) => val,
|
||||||
|
_ => panic!("incorrect key"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn wrap(self) -> Value {
|
||||||
|
Value::OptionalFormatComponent(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl MetaValue for Option<item::Stack> {
|
impl MetaValue for Option<item::Stack> {
|
||||||
fn unwrap(value: &Value) -> &Self {
|
fn unwrap(value: &Value) -> &Self {
|
||||||
match *value {
|
match *value {
|
||||||
|
|
|
@ -43,6 +43,8 @@ pub struct World {
|
||||||
light_updates: VecDeque<LightUpdate>,
|
light_updates: VecDeque<LightUpdate>,
|
||||||
|
|
||||||
block_entity_actions: VecDeque<BlockEntityAction>,
|
block_entity_actions: VecDeque<BlockEntityAction>,
|
||||||
|
|
||||||
|
protocol_version: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -79,7 +81,12 @@ struct LightUpdate {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
pub fn new() -> World { Default::default() }
|
pub fn new(protocol_version: i32) -> World {
|
||||||
|
World {
|
||||||
|
protocol_version,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_chunk_loaded(&self, x: i32, z: i32) -> bool {
|
pub fn is_chunk_loaded(&self, x: i32, z: i32) -> bool {
|
||||||
self.chunks.contains_key(&CPos(x, z))
|
self.chunks.contains_key(&CPos(x, z))
|
||||||
|
@ -612,7 +619,7 @@ impl World {
|
||||||
|
|
||||||
for bi in 0 .. 4096 {
|
for bi in 0 .. 4096 {
|
||||||
let id = data.read_u16::<byteorder::LittleEndian>()?;
|
let id = data.read_u16::<byteorder::LittleEndian>()?;
|
||||||
section.blocks.set(bi, block::Block::by_vanilla_id(id as usize));
|
section.blocks.set(bi, block::Block::by_vanilla_id(id as usize, self.protocol_version));
|
||||||
|
|
||||||
// Spawn block entities
|
// Spawn block entities
|
||||||
let b = section.blocks.get(bi);
|
let b = section.blocks.get(bi);
|
||||||
|
@ -798,7 +805,7 @@ impl World {
|
||||||
|
|
||||||
for bi in 0 .. 4096 {
|
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);
|
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));
|
section.blocks.set(bi, block::Block::by_vanilla_id(id as usize, self.protocol_version));
|
||||||
|
|
||||||
// Spawn block entities
|
// Spawn block entities
|
||||||
let b = section.blocks.get(bi);
|
let b = section.blocks.get(bi);
|
||||||
|
@ -870,7 +877,7 @@ impl World {
|
||||||
let count = VarInt::read_from(&mut data)?.0;
|
let count = VarInt::read_from(&mut data)?.0;
|
||||||
for i in 0 .. count {
|
for i in 0 .. count {
|
||||||
let id = VarInt::read_from(&mut data)?.0;
|
let id = VarInt::read_from(&mut data)?.0;
|
||||||
let bl = block::Block::by_vanilla_id(id as usize);
|
let bl = block::Block::by_vanilla_id(id as usize, self.protocol_version);
|
||||||
mappings.insert(i as usize, bl);
|
mappings.insert(i as usize, bl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -880,7 +887,7 @@ impl World {
|
||||||
|
|
||||||
for bi in 0 .. 4096 {
|
for bi in 0 .. 4096 {
|
||||||
let id = m.get(bi);
|
let id = m.get(bi);
|
||||||
section.blocks.set(bi, mappings.get(&id).cloned().unwrap_or(block::Block::by_vanilla_id(id)));
|
section.blocks.set(bi, mappings.get(&id).cloned().unwrap_or(block::Block::by_vanilla_id(id, self.protocol_version)));
|
||||||
// Spawn block entities
|
// Spawn block entities
|
||||||
let b = section.blocks.get(bi);
|
let b = section.blocks.get(bi);
|
||||||
if block_entity::BlockEntityType::get_block_entity(b).is_some() {
|
if block_entity::BlockEntityType::get_block_entity(b).is_some() {
|
||||||
|
|
Loading…
Reference in New Issue