stevenarella/src/protocol/packet.rs

1182 lines
46 KiB
Rust

// Copyright 2015 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use format;
state_packets!(
handshake Handshaking {
serverbound Serverbound {
// Handshake is the first packet sent in the protocol.
// Its used for deciding if the request is a client
// is requesting status information about the server
// (MOTD, players etc) or trying to login to the server.
//
// The host and port fields are not used by the vanilla
// server but are there for virtual server hosting to
// be able to redirect a client to a target server with
// a single address + port.
//
// Some modified servers/proxies use the handshake field
// differently, packing information into the field other
// than the hostname due to the protocol not providing
// any system for custom information to be transfered
// by the client to the server until after login.
Handshake => 0x00 {
// The protocol version of the connecting client
protocol_version: VarInt =,
// The hostname the client connected to
host: String =,
// The port the client connected to
port: u16 =,
// The next protocol state the client wants
next: VarInt =,
}
}
clientbound Clientbound {
}
}
play Play {
serverbound Serverbound {
// TabComplete is sent by the client when the client presses tab in
// the chat box.
TabComplete => 0x00 {
text: String =,
has_target: bool =,
target: Option<types::Position> = when(|p: &TabComplete| p.has_target == true),
}
// ChatMessage is sent by the client when it sends a chat message or
// executes a command (prefixed by '/').
ChatMessage => 0x01 {
message: String =,
}
// ClientStatus is sent to update the client's status
ClientStatus => 0x02 {
action_id: VarInt =,
}
// ClientSettings is sent by the client to update its current settings.
ClientSettings => 0x03 {
locale: String =,
view_distance: u8 =,
chat_mode: u8 =,
chat_colors: bool =,
displayed_skin_parts: u8 =,
main_hand: VarInt =,
}
// ConfirmTransactionServerbound is a reply to ConfirmTransaction.
ConfirmTransactionServerbound => 0x04 {
id: u8 =,
action_number: i16 =,
accepted: bool =,
}
// EnchantItem is sent when the client enchants an item.
EnchantItem => 0x05 {
id: u8 =,
enchantment: u8 =,
}
// ClickWindow is sent when the client clicks in a window.
ClickWindow => 0x06 {
id: u8 =,
slot: i16 =,
button: u8 =,
action_number: u16 =,
mode: u8 =,
clicked_item: Option<item::Stack> =,
}
// CloseWindow is sent when the client closes a window.
CloseWindow => 0x07 {
id: u8 =,
}
// PluginMessageServerbound is used for custom messages between the client
// and server. This is mainly for plugins/mods but vanilla has a few channels
// registered too.
PluginMessageServerbound => 0x08 {
channel: String =,
data: Vec<u8> =,
}
// UseEntity is sent when the user interacts (right clicks) or attacks
// (left clicks) an entity.
UseEntity => 0x09 {
target_id: VarInt =,
ty: VarInt =,
target_x: f32 = when(|p: &UseEntity| p.ty.0 == 2),
target_y: f32 = when(|p: &UseEntity| p.ty.0 == 2),
target_z: f32 = when(|p: &UseEntity| p.ty.0 == 2),
hand: VarInt = when(|p: &UseEntity| p.ty.0 == 0 || p.ty.0 == 2),
}
// KeepAliveServerbound is sent by a client as a response to a
// KeepAliveClientbound. If the client doesn't reply the server
// may disconnect the client.
KeepAliveServerbound => 0x0A {
id: VarInt =,
}
// PlayerPosition is used to update the player's position.
PlayerPosition => 0x0B {
x: f64 =,
y: f64 =,
z: f64 =,
on_ground: bool =,
}
// PlayerPositionLook is a combination of PlayerPosition and
// PlayerLook.
PlayerPositionLook => 0x0C {
x: f64 =,
y: f64 =,
z: f64 =,
yaw: f32 =,
pitch: f32 =,
on_ground: bool =,
}
// PlayerLook is used to update the player's rotation.
PlayerLook => 0x0D {
yaw: f32 =,
pitch: f32 =,
on_ground: bool =,
}
// Player is used to update whether the player is on the ground or not.
Player => 0x0E {
on_ground: bool =,
}
// ClientAbilities is used to modify the players current abilities.
// Currently flying is the only one
ClientAbilities => 0x0F {
flags: u8 =,
flying_speed: f32 =,
walking_speed: f32 =,
}
// PlayerDigging is sent when the client starts/stops digging a block.
// It also can be sent for droppping items and eating/shooting.
PlayerDigging => 0x10 {
status: u8 =,
location: types::Position =,
face: u8 =,
}
// PlayerAction is sent when a player preforms various actions.
PlayerAction => 0x11 {
entity_id: VarInt =,
action_id: VarInt =,
jump_boost: VarInt =,
}
// SteerVehicle is sent by the client when steers or preforms an action
// on a vehicle.
SteerVehicle => 0x12 {
sideways: f32 =,
forward: f32 =,
flags: u8 =,
}
// ResourcePackStatus informs the server of the client's current progress
// in activating the requested resource pack
ResourcePackStatus => 0x13 {
hash: String =,
result: VarInt =,
}
// HeldItemChange is sent when the player changes the currently active
// hotbar slot.
HeldItemChange => 0x14 {
slot: i16 =,
}
// CreativeInventoryAction is sent when the client clicks in the creative
// inventory. This is used to spawn items in creative.
CreativeInventoryAction => 0x15 {
slot: i16 =,
clicked_item: Option<item::Stack> =,
}
// SetSign sets the text on a sign after placing it.
SetSign => 0x16 {
location: types::Position =,
line1: String =,
line2: String =,
line3: String =,
line4: String =,
}
// ArmSwing is sent by the client when the player left clicks (to swing their
// arm).
ArmSwing => 0x17 {
hand: VarInt =,
}
// SpectateTeleport is sent by clients in spectator mode to teleport to a player.
SpectateTeleport => 0x18 {
target: UUID =,
}
// PlayerBlockPlacement is sent when the client tries to place a block.
PlayerBlockPlacement => 0x19 {
location: types::Position =,
face: VarInt =,
hand: VarInt =,
cursor_x: u8 =,
cursor_y: u8 =,
cursor_z: u8 =,
}
// UseItem is sent when the client tries to use an item.
UseItem => 0x1A {
hand: VarInt =,
}
}
clientbound Clientbound {
// SpawnObject is used to spawn an object or vehicle into the world when it
// is in range of the client.
SpawnObject => 0x00 {
entity_id: VarInt =,
uuid: UUID =,
ty: u8 =,
x: i32 =,
y: i32 =,
z: i32 =,
pitch: i8 =,
yaw: i8 =,
data: i32 =,
velocity_x: i16 =,
velocity_y: i16 =,
velocity_z: i16 =,
}
// SpawnExperienceOrb spawns a single experience orb into the world when
// it is in range of the client. The count controls the amount of experience
// gained when collected.
SpawnExperienceOrb => 0x01 {
entity_id: VarInt =,
x: i32 =,
y: i32 =,
z: i32 =,
count: i16 =,
}
// SpawnGlobalEntity spawns an entity which is visible from anywhere in the
// world. Currently only used for lightning.
SpawnGlobalEntity => 0x02 {
entity_id: VarInt =,
ty: u8 =,
x: i32 =,
y: i32 =,
z: i32 =,
}
// SpawnMob is used to spawn a living entity into the world when it is in
// range of the client.
SpawnMob => 0x03 {
entity_id: VarInt =,
uuid: UUID =,
ty: u8 =,
x: i32 =,
y: i32 =,
z: i32 =,
yaw: i8 =,
pitch: i8 =,
head_pitch: i8 =,
velocity_x: i16 =,
velocity_y: i16 =,
velocity_z: i16 =,
metadata: types::Metadata =,
}
// 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.
SpawnPainting => 0x04 {
entity_id: VarInt =,
title: String =,
location: types::Position =,
direction: u8 =,
}
// SpawnPlayer is used to spawn a player when they are in range of the client.
// This packet alone isn't enough to display the player as the skin and username
// information is in the player information packet.
SpawnPlayer => 0x05 {
entity_id: VarInt =,
uuid: UUID =,
x: i32 =,
y: i32 =,
z: i32 =,
yaw: i8 =,
pitch: i8 =,
metadata: types::Metadata =,
}
// Animation is sent by the server to play an animation on a specific entity.
Animation => 0x06 {
entity_id: VarInt =,
animation_id: u8 =,
}
// Statistics is used to update the statistics screen for the client.
Statistics => 0x07 {
statistices: LenPrefixed<VarInt, packet::Statistic> =,
}
// BlockBreakAnimation is used to create and update the block breaking
// animation played when a player starts digging a block.
BlockBreakAnimation => 0x08 {
entity_id: VarInt =,
location: types::Position =,
stage: i8 =,
}
// UpdateBlockEntity updates the nbt tag of a block entity in the
// world.
UpdateBlockEntity => 0x09 {
location: types::Position =,
action: u8 =,
nbt: Option<nbt::NamedTag> =,
}
// BlockAction triggers different actions depending on the target block.
BlockAction => 0x0A {
location: types::Position =,
byte1: u8 =,
byte2: u8 =,
block_type: VarInt =,
}
// BlockChange is used to update a single block on the client.
BlockChange => 0x0B {
location: types::Position =,
block_id: VarInt =,
}
// BossBar displays and/or changes a boss bar that is displayed on the
// top of the client's screen. This is normally used for bosses such as
// the ender dragon or the wither.
BossBar => 0x0C {
uuid: UUID =,
action: VarInt =,
title: format::Component = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 3),
health: f32 = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 2),
color: VarInt = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 4),
style: VarInt = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 4),
flags: u8 = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 5),
}
// ServerDifficulty changes the displayed difficulty in the client's menu
// as well as some ui changes for hardcore.
ServerDifficulty => 0x0D {
difficulty: u8 =,
}
// TabCompleteReply is sent as a reply to a tab completion request.
// The matches should be possible completions for the command/chat the
// player sent.
TabCompleteReply => 0x0E {
matches: LenPrefixed<VarInt, String> =,
}
// 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
// message is displayed at and when the message is displayed.
ServerMessage => 0x0F {
message: format::Component =,
// 0 - Chat message, 1 - System message, 2 - Action bar message
position: u8 =,
}
// MultiBlockChange is used to update a batch of blocks in a single packet.
MultiBlockChange => 0x10 {
chunk_x: i32 =,
chunk_y: i32 =,
records: LenPrefixed<VarInt, packet::BlockChangeRecord> =,
}
// ConfirmTransaction notifies the client whether a transaction was successful
// or failed (e.g. due to lag).
ConfirmTransaction => 0x11 {
id: u8 =,
action_number: i16 =,
accepted: bool =,
}
// WindowClose forces the client to close the window with the given id,
// e.g. a chest getting destroyed.
WindowClose => 0x12 {
id: u8 =,
}
// WindowOpen tells the client to open the inventory window of the given
// type. The ID is used to reference the instance of the window in
// other packets.
WindowOpen => 0x13 {
id: u8 =,
ty: String =,
title: format::Component =,
slot_count: u8 =,
entity_id: i32 = when(|p: &WindowOpen| p.ty == "EntityHorse"),
}
// WindowItems sets every item in a window.
WindowItems => 0x14 {
id: u8 =,
items: LenPrefixed<i16, Option<item::Stack>> =,
}
// WindowProperty changes the value of a property of a window. Properties
// vary depending on the window type.
WindowProperty => 0x15 {
id: u8 =,
property: i16 =,
value: i16 =,
}
// WindowSetSlot changes an itemstack in one of the slots in a window.
WindowSetSlot => 0x16 {
id: u8 =,
property: i16 =,
item: Option<item::Stack> =,
}
// SetCooldown disables a set item (by id) for the set number of ticks
SetCooldown => 0x17 {
item_id: VarInt =,
ticks: VarInt =,
}
// PluginMessageClientbound is used for custom messages between the client
// and server. This is mainly for plugins/mods but vanilla has a few channels
// registered too.
PluginMessageClientbound => 0x18 {
channel: String =,
data: Vec<u8> =,
}
// Disconnect causes the client to disconnect displaying the passed reason.
Disconnect => 0x19 {
reason: format::Component =,
}
// EntityAction causes an entity to preform an action based on the passed
// id.
EntityAction => 0x1A {
entity_id: i32 =,
action_id: u8 =,
}
// Explosion is sent when an explosion is triggered (tnt, creeper etc).
// This plays the effect and removes the effected blocks.
Explosion => 0x1B {
x: f32 =,
y: f32 =,
z: f32 =,
radius: f32 =,
records: LenPrefixed<i32, packet::ExplosionRecord> =,
velocity_x: f32 =,
velocity_y: f32 =,
velocity_z: f32 =,
}
// ChunkUnload tells the client to unload the chunk at the specified
// position.
ChunkUnload => 0x1C {
x: i32 =,
z: i32 =,
}
// SetCompression updates the compression threshold.
SetCompression => 0x1D {
threshold: VarInt =,
}
// ChangeGameState is used to modify the game's state like gamemode or
// weather.
ChangeGameState => 0x1E {
reason: u8 =,
value: f32 =,
}
// KeepAliveClientbound is sent by a server to check if the
// client is still responding and keep the connection open.
// The client should reply with the KeepAliveServerbound
// packet setting ID to the same as this one.
KeepAliveClientbound => 0x1F {
id: VarInt =,
}
// ChunkData sends or updates a single chunk on the client. If New is set
// then biome data should be sent too.
ChunkData => 0x20 {
chunk_x: i32 =,
chunk_z: i32 =,
new: bool =,
bitmask: VarInt =,
data: LenPrefixedBytes<VarInt> =,
}
// Effect plays a sound effect or particle at the target location with the
// volume (of sounds) being relative to the player's position unless
// DisableRelative is set to true.
Effect => 0x21 {
effect_id: i32 =,
location: types::Position =,
data: i32 =,
disable_relative: bool =,
}
// Particle spawns particles at the target location with the various
// modifiers.
Particle => 0x22 {
particle_id: i32 =,
long_distance: bool =,
x: f32 =,
y: f32 =,
z: f32 =,
offset_x: f32 =,
offset_y: f32 =,
offset_z: f32 =,
speed: f32 =,
count: i32 =,
data1: VarInt = when(|p: &Particle| p.particle_id == 36 || p.particle_id == 37 || p.particle_id == 38),
data2: VarInt = when(|p: &Particle| p.particle_id == 36),
}
// SoundEffect plays the named sound at the target location.
SoundEffect => 0x23 {
name: String =,
x: i32 =,
y: i32 =,
z: i32 =,
volume: f32 =,
pitch: u8 =,
}
// JoinGame is sent after completing the login process. This
// sets the initial state for the client.
JoinGame => 0x24 {
// The entity id the client will be referenced by
entity_id: i32 =,
// The starting gamemode of the client
gamemode: u8 =,
// The dimension the client is starting in
dimension: i8 =,
// The difficuilty setting for the server
difficulty: u8 =,
// The max number of players on the server
max_players: u8 =,
// The level type of the server
level_type: String =,
// Whether the client should reduce the amount of debug
// information it displays in F3 mode
reduced_debug_info: bool =,
}
// Maps updates a single map's contents
Maps => 0x25 {
item_damage: VarInt =,
scale: i8 =,
tracking_position: bool =,
icons: LenPrefixed<VarInt, packet::MapIcon> =,
columns: u8 =,
rows: Option<u8> = when(|p: &Maps| p.columns > 0),
x: Option<u8> = when(|p: &Maps| p.columns > 0),
z: Option<u8> = when(|p: &Maps| p.columns > 0),
data: Option<LenPrefixedBytes<VarInt>> = when(|p: &Maps| p.columns > 0),
}
// EntityMove moves the entity with the id by the offsets provided.
EntityMove => 0x26 {
entity_id: VarInt =,
delta_x: i8 =,
delta_y: i8 =,
delta_z: i8 =,
on_ground: bool =,
}
// EntityLookAndMove is a combination of EntityMove and EntityLook.
EntityLookAndMove => 0x27 {
entity_id: VarInt =,
delta_x: i8 =,
delta_y: i8 =,
delta_z: i8 =,
yaw: i8 =,
pitch: i8 =,
on_ground: bool =,
}
// EntityLook rotates the entity to the new angles provided.
EntityLook => 0x28 {
entity_id: VarInt =,
yaw: i8 =,
pitch: i8 =,
on_ground: bool =,
}
// Entity does nothing. It is a result of subclassing used in Minecraft.
Entity => 0x29 {
entity_id: VarInt =,
}
// SignEditorOpen causes the client to open the editor for a sign so that
// it can write to it. Only sent in vanilla when the player places a sign.
SignEditorOpen => 0x2A {
location: types::Position =,
}
// PlayerAbilities is used to modify the players current abilities. Flying,
// creative, god mode etc.
PlayerAbilities => 0x2B {
flags: u8 =,
flying_speed: f32 =,
walking_speed: f32 =,
}
// CombatEvent is used for... you know, I never checked. I have no
// clue.
CombatEvent => 0x2C {
event: VarInt =,
direction: Option<VarInt> = when(|p: &CombatEvent| p.event.0 == 1),
player_id: Option<VarInt> = when(|p: &CombatEvent| p.event.0 == 2),
entity_id: Option<i32> = when(|p: &CombatEvent| p.event.0 == 1 || p.event.0 == 2),
message: Option<format::Component> = when(|p: &CombatEvent| p.event.0 == 2),
}
// PlayerInfo is sent by the server for every player connected to the server
// to provide skin and username information as well as ping and gamemode info.
PlayerInfo => 0x2D {
inner: packet::PlayerInfoData =, // Ew but watcha gonna do?
}
// 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
// otherwise will reject future packets.
TeleportPlayer => 0x2E {
x: f64 =,
y: f64 =,
z: f64 =,
yaw: f32 =,
pitch: f32 =,
flags: u8 =,
}
// EntityUsedBed is sent by the server when a player goes to bed.
EntityUsedBed => 0x2F {
entity_id: VarInt =,
location: types::Position =,
}
// EntityDestroy destroys the entities with the ids in the provided slice.
EntityDestroy => 0x30 {
entity_ids: LenPrefixed<VarInt, VarInt> =,
}
// EntityRemoveEffect removes an effect from an entity.
EntityRemoveEffect => 0x31 {
entity_id: VarInt =,
effect_id: i8 =,
}
// ResourcePackSend causes the client to check its cache for the requested
// resource packet and download it if its missing. Once the resource pack
// is obtained the client will use it.
ResourcePackSend => 0x32 {
url: String =,
hash: String =,
}
// Respawn is sent to respawn the player after death or when they move worlds.
Respawn => 0x33 {
dimension: i32 =,
difficulty: u8 =,
gamemode: u8 =,
level_type: String =,
}
// EntityHeadLook rotates an entity's head to the new angle.
EntityHeadLook => 0x34 {
entity_id: VarInt =,
head_yaw: i8 =,
}
// WorldBorder configures the world's border.
WorldBorder => 0x35 {
action: VarInt =,
old_radius: Option<f64> = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 1),
new_radius: Option<f64> = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 1 || p.action.0 == 0),
speed: Option<VarLong> = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 1),
x: Option<f64> = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 2),
z: Option<f64> = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 2),
portal_boundary: Option<VarInt> = when(|p: &WorldBorder| p.action.0 == 3),
warning_time: Option<VarInt> = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 4),
warning_blocks: Option<VarInt> = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 5),
}
// Camera causes the client to spectate the entity with the passed id.
// Use the player's id to de-spectate.
Camera => 0x36 {
target_id: VarInt =,
}
// SetCurrentHotbarSlot changes the player's currently selected hotbar item.
SetCurrentHotbarSlot => 0x37 {
slot: u8 =,
}
// ScoreboardDisplay is used to set the display position of a scoreboard.
ScoreboardDisplay => 0x38 {
position: u8 =,
name: String =,
}
// EntityMetadata updates the metadata for an entity.
EntityMetadata => 0x39 {
entity_id: VarInt =,
metadata: types::Metadata =,
}
// EntityAttach attaches to entities together, either by mounting or leashing.
// -1 can be used at the EntityID to deattach.
EntityAttach => 0x3A {
entity_id: i32 =,
vehicle: i32 =,
leash: bool =,
}
// EntityVelocity sets the velocity of an entity in 1/8000 of a block
// per a tick.
EntityVelocity => 0x3B {
entity_id: VarInt =,
velocity_x: i16 =,
velocity_y: i16 =,
velocity_z: i16 =,
}
// EntityEquipment is sent to display an item on an entity, like a sword
// or armor. Slot 0 is the held item and slots 1 to 4 are boots, leggings
// chestplate and helmet respectively.
EntityEquipment => 0x3C {
entity_id: VarInt =,
slot: VarInt =,
item: Option<item::Stack> =,
}
// SetExperience updates the experience bar on the client.
SetExperience => 0x3D {
experience_bar: f32 =,
level: VarInt =,
total_experience: VarInt =,
}
// UpdateHealth is sent by the server to update the player's health and food.
UpdateHealth => 0x3E {
health: f32 =,
food: VarInt =,
food_saturation: f32 =,
}
// ScoreboardObjective creates/updates a scoreboard objective.
ScoreboardObjective => 0x3F {
name: String =,
mode: u8 =,
value: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2),
ty: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2),
}
// Teams creates and updates teams
Teams => 0x40 {
name: String =,
mode: u8 =,
display_name: Option<String> = when(|p: &Teams| p.mode == 0 || p.mode == 2),
prefix: Option<String> = when(|p: &Teams| p.mode == 0 || p.mode == 2),
suffix: Option<String> = when(|p: &Teams| p.mode == 0 || p.mode == 2),
flags: Option<u8> = when(|p: &Teams| p.mode == 0 || p.mode == 2),
name_tag_visibility: Option<String> = when(|p: &Teams| p.mode == 0 || p.mode == 2),
collision_rule: Option<String> = when(|p: &Teams| p.mode == 0 || p.mode == 2),
color: Option<u8> = when(|p: &Teams| p.mode == 0 || p.mode == 2),
players: Option<LenPrefixed<VarInt, String>> = when(|p: &Teams| p.mode == 0 || p.mode == 3 || p.mode == 4),
}
// UpdateScore is used to update or remove an item from a scoreboard
// objective.
UpdateScore => 0x41 {
name: String =,
action: u8 =,
object_name: String =,
value: Option<VarInt> = when(|p: &UpdateScore| p.action != 1),
}
// SpawnPosition is sent to change the player's current spawn point. Currently
// only used by the client for the compass.
SpawnPosition => 0x42 {
location: types::Position =,
}
// TimeUpdate is sent to sync the world's time to the client, the client
// will manually tick the time itself so this doesn't need to sent repeatedly
// but if the server or client has issues keeping up this can fall out of sync
// so it is a good idea to sent this now and again
TimeUpdate => 0x43 {
world_age: i64 =,
time_of_day: i64 =,
}
// Title configures an on-screen title.
Title => 0x44 {
action: VarInt =,
title: Option<format::Component> = when(|p: &Title| p.action.0 == 0),
sub_title: Option<format::Component> = when(|p: &Title| p.action.0 == 1),
fade_in: Option<format::Component> = when(|p: &Title| p.action.0 == 2),
fade_stay: Option<format::Component> = when(|p: &Title| p.action.0 == 2),
fade_out: Option<format::Component> = when(|p: &Title| p.action.0 == 2),
}
// UpdateSign sets or changes the text on a sign.
UpdateSign => 0x45 {
location: types::Position =,
line1: format::Component =,
line2: format::Component =,
line3: format::Component =,
line4: format::Component =,
}
// PlayerListHeaderFooter updates the header/footer of the player list.
PlayerListHeaderFooter => 0x46 {
header: format::Component =,
footer: format::Component =,
}
// CollectItem causes the collected item to fly towards the collector. This
// does not destroy the entity.
CollectItem => 0x47 {
collected_entity_id: VarInt =,
collector_entity_id: VarInt =,
}
// EntityTeleport teleports the entity to the target location. This is
// sent if the entity moves further than EntityMove allows.
EntityTeleport => 0x48 {
entity_id: VarInt =,
x: i32 =,
y: i32 =,
z: i32 =,
yaw: i8 =,
pitch: i8 =,
on_ground: bool =,
}
// EntityProperties updates the properties for an entity.
EntityProperties => 0x49 {
entity_id: VarInt =,
properties: LenPrefixed<i32, packet::EntityProperty> =,
}
// EntityEffect applies a status effect to an entity for a given duration.
EntityEffect => 0x4A {
entity_id: VarInt =,
effect_id: i8 =,
amplifier: i8 =,
duration: VarInt =,
hide_particles: bool =,
}
}
}
login Login {
serverbound Serverbound {
// LoginStart is sent immeditately after switching into the login
// state. The passed username is used by the server to authenticate
// the player in online mode.
LoginStart => 0x00 {
username: String =,
}
// EncryptionResponse is sent as a reply to EncryptionRequest. All
// packets following this one must be encrypted with AES/CFB8
// encryption.
EncryptionResponse => 0x01 {
// The key for the AES/CFB8 cipher encrypted with the
// public key
shared_secret: LenPrefixedBytes<VarInt> =,
// The verify token from the request encrypted with the
// public key
verify_token: LenPrefixedBytes<VarInt> =,
}
}
clientbound Clientbound {
// LoginDisconnect is sent by the server if there was any issues
// authenticating the player during login or the general server
// issues (e.g. too many players).
LoginDisconnect => 0x00 {
reason: format::Component =,
}
// EncryptionRequest is sent by the server if the server is in
// online mode. If it is not sent then its assumed the server is
// in offline mode.
EncryptionRequest => 0x01 {
// Generally empty, left in from legacy auth
// but is still used by the client if provided
server_id: String =,
// A RSA Public key serialized in x.509 PRIX format
public_key: LenPrefixedBytes<VarInt> =,
// Token used by the server to verify encryption is working
// correctly
verify_token: LenPrefixedBytes<VarInt> =,
}
// LoginSuccess is sent by the server if the player successfully
// authenicates with the session servers (online mode) or straight
// after LoginStart (offline mode).
LoginSuccess => 0x02 {
// String encoding of a uuid (with hyphens)
uuid: String =,
username: String =,
}
// SetInitialCompression sets the compression threshold during the
// login state.
SetInitialCompression => 0x03 {
// Threshold where a packet should be sent compressed
threshold: VarInt =,
}
}
}
status Status {
serverbound Serverbound {
// StatusRequest is sent by the client instantly after
// switching to the Status protocol state and is used
// to signal the server to send a StatusResponse to the
// client
StatusRequest => 0x00 {
empty: () =,
}
// StatusPing is sent by the client after recieving a
// StatusResponse. The client uses the time from sending
// the ping until the time of recieving a pong to measure
// the latency between the client and the server.
StatusPing => 0x01 {
ping: i64 =,
}
}
clientbound Clientbound {
// StatusResponse is sent as a reply to a StatusRequest.
// The Status should contain a json encoded structure with
// version information, a player sample, a description/MOTD
// and optionally a favicon.
//
// The structure is as follows
// {
// "version": {
// "name": "1.8.3",
// "protocol": 47,
// },
// "players": {
// "max": 20,
// "online": 1,
// "sample": [
// {"name": "Thinkofdeath", "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"}
// ]
// },
// "description": "Hello world",
// "favicon": "data:image/png;base64,<data>"
// }
StatusResponse => 0x00 {
status: String =,
}
// StatusPong is sent as a reply to a StatusPing.
// The Time field should be exactly the same as the
// one sent by the client.
StatusPong => 0x01 {
ping: i64 =,
}
}
}
);
#[derive(Debug, Default)]
pub struct Statistic {
pub name: String,
pub value: VarInt,
}
impl Serializable for Statistic {
fn read_from(buf: &mut io::Read) -> Result<Self, io::Error> {
Ok(Statistic {
name: try!(Serializable::read_from(buf)),
value: try!(Serializable::read_from(buf)),
})
}
fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> {
try!(self.name.write_to(buf));
self.value.write_to(buf)
}
}
#[derive(Debug, Default)]
pub struct BlockChangeRecord {
pub xz: u8,
pub y: u8,
pub block_id: VarInt,
}
impl Serializable for BlockChangeRecord {
fn read_from(buf: &mut io::Read) -> Result<Self, io::Error> {
Ok(BlockChangeRecord {
xz: try!(Serializable::read_from(buf)),
y: try!(Serializable::read_from(buf)),
block_id: try!(Serializable::read_from(buf)),
})
}
fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> {
try!(self.xz.write_to(buf));
try!(self.y.write_to(buf));
self.block_id.write_to(buf)
}
}
#[derive(Debug, Default)]
pub struct ExplosionRecord {
pub x: i8,
pub y: i8,
pub z: i8,
}
impl Serializable for ExplosionRecord {
fn read_from(buf: &mut io::Read) -> Result<Self, io::Error> {
Ok(ExplosionRecord {
x: try!(Serializable::read_from(buf)),
y: try!(Serializable::read_from(buf)),
z: try!(Serializable::read_from(buf)),
})
}
fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> {
try!(self.x.write_to(buf));
try!(self.y.write_to(buf));
self.z.write_to(buf)
}
}
#[derive(Debug)]
pub struct MapIcon {
pub direction_type: i8,
pub x: i8,
pub z: i8,
}
impl Serializable for MapIcon {
fn read_from(buf: &mut io::Read) -> Result<Self, io::Error> {
Ok(MapIcon {
direction_type: try!(Serializable::read_from(buf)),
x: try!(Serializable::read_from(buf)),
z: try!(Serializable::read_from(buf)),
})
}
fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> {
try!(self.direction_type.write_to(buf));
try!(self.x.write_to(buf));
self.z.write_to(buf)
}
}
impl Default for MapIcon {
fn default() -> Self {
MapIcon {
direction_type: 0,
x: 0,
z: 0,
}
}
}
#[derive(Debug, Default)]
pub struct EntityProperty {
pub key: String,
pub value: f64,
pub modifiers: LenPrefixed<VarInt, PropertyModifier>,
}
impl Serializable for EntityProperty {
fn read_from(buf: &mut io::Read) -> Result<Self, io::Error> {
Ok(EntityProperty {
key: try!(Serializable::read_from(buf)),
value: try!(Serializable::read_from(buf)),
modifiers: try!(Serializable::read_from(buf)),
})
}
fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> {
try!(self.key.write_to(buf));
try!(self.value.write_to(buf));
self.modifiers.write_to(buf)
}
}
#[derive(Debug, Default)]
pub struct PropertyModifier {
pub uuid: UUID,
pub amount: f64,
pub operation: i8,
}
impl Serializable for PropertyModifier {
fn read_from(buf: &mut io::Read) -> Result<Self, io::Error> {
Ok(PropertyModifier {
uuid: try!(Serializable::read_from(buf)),
amount: try!(Serializable::read_from(buf)),
operation: try!(Serializable::read_from(buf)),
})
}
fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> {
try!(self.uuid.write_to(buf));
try!(self.amount.write_to(buf));
self.operation.write_to(buf)
}
}
#[derive(Debug)]
pub struct PlayerInfoData {
pub action: VarInt,
pub players: Vec<PlayerDetail>,
}
impl Serializable for PlayerInfoData {
fn read_from(buf: &mut io::Read) -> Result<Self, io::Error> {
let mut m = PlayerInfoData {
action: try!(Serializable::read_from(buf)),
players: Vec::new(),
};
let len = try!(VarInt::read_from(buf));
for _ in 0..len.0 {
let uuid = try!(UUID::read_from(buf));
match m.action.0 {
0 => {
let name = try!(String::read_from(buf));
let mut props = Vec::new();
let plen = try!(VarInt::read_from(buf)).0;
for _ in 0..plen {
let mut prop = PlayerProperty {
name: try!(String::read_from(buf)),
value: try!(String::read_from(buf)),
signature: Default::default(),
};
if try!(bool::read_from(buf)) {
prop.signature = Some(try!(String::read_from(buf)));
}
props.push(prop);
}
let p = PlayerDetail::Add {
uuid: uuid,
name: name,
properties: props,
gamemode: try!(Serializable::read_from(buf)),
ping: try!(Serializable::read_from(buf)),
display: {
if try!(bool::read_from(buf)) {
Some(try!(Serializable::read_from(buf)))
} else {
None
}
},
};
m.players.push(p);
}
1 => {
m.players.push(PlayerDetail::UpdateGamemode {
uuid: uuid,
gamemode: try!(Serializable::read_from(buf)),
})
}
2 => {
m.players.push(PlayerDetail::UpdateLatency {
uuid: uuid,
ping: try!(Serializable::read_from(buf)),
})
}
3 => {
m.players.push(PlayerDetail::UpdateDisplayName {
uuid: uuid,
display: {
if try!(bool::read_from(buf)) {
Some(try!(Serializable::read_from(buf)))
} else {
None
}
},
})
}
4 => {
m.players.push(PlayerDetail::Remove { uuid: uuid })
}
_ => panic!(),
}
}
Ok(m)
}
fn write_to(&self, _: &mut io::Write) -> Result<(), io::Error> {
unimplemented!() // I'm lazy
}
}
impl Default for PlayerInfoData {
fn default() -> Self {
PlayerInfoData {
action: VarInt(0),
players: Vec::new(),
}
}
}
#[derive(Debug)]
pub enum PlayerDetail {
Add {
uuid: UUID,
name: String,
properties: Vec<PlayerProperty>,
gamemode: VarInt,
ping: VarInt,
display: Option<format::Component>,
},
UpdateGamemode {
uuid: UUID,
gamemode: VarInt,
},
UpdateLatency {
uuid: UUID,
ping: VarInt,
},
UpdateDisplayName {
uuid: UUID,
display: Option<format::Component>,
},
Remove {
uuid: UUID,
},
}
#[derive(Debug)]
pub struct PlayerProperty {
pub name: String,
pub value: String,
pub signature: Option<String>,
}