diff --git a/examples/combat.rs b/examples/combat.rs index b4d9024..f623f7d 100644 --- a/examples/combat.rs +++ b/examples/combat.rs @@ -253,7 +253,11 @@ impl Config for Game { let mut vel = (victim.position() - e.state.attacker_pos).normalized(); let knockback_xz = if e.state.extra_knockback { 18.0 } else { 8.0 }; - let knockback_y = if e.state.extra_knockback { 8.432 } else { 6.432 }; + let knockback_y = if e.state.extra_knockback { + 8.432 + } else { + 6.432 + }; vel.x *= knockback_xz; vel.y = knockback_y; diff --git a/examples/conway.rs b/examples/conway.rs index c976c1c..73607fb 100644 --- a/examples/conway.rs +++ b/examples/conway.rs @@ -175,8 +175,8 @@ impl Config for Game { && (0..SIZE_Z as i32).contains(&position.z) && position.y == BOARD_Y { - server.state.board[position.x as usize + position.z as usize * SIZE_X] = - true; + server.state.board + [position.x as usize + position.z as usize * SIZE_X] = true; } } Event::ArmSwing(hand) => match hand { diff --git a/packet-inspector/src/main.rs b/packet-inspector/src/main.rs index 6ddbc7b..fd6da54 100644 --- a/packet-inspector/src/main.rs +++ b/packet-inspector/src/main.rs @@ -16,8 +16,8 @@ use valence::protocol::codec::Decoder; use valence::protocol::packets::handshake::{Handshake, HandshakeNextState}; use valence::protocol::packets::login::c2s::{EncryptionResponse, LoginStart}; use valence::protocol::packets::login::s2c::{LoginSuccess, S2cLoginPacket}; -use valence::protocol::packets::play::c2s::C2sPlayPacket; -use valence::protocol::packets::play::s2c::S2cPlayPacket; +use valence::protocol::packets::c2s::play::C2sPlayPacket; +use valence::protocol::packets::s2c::play::S2cPlayPacket; use valence::protocol::packets::status::c2s::{QueryPing, QueryRequest}; use valence::protocol::packets::status::s2c::{QueryPong, QueryResponse}; use valence::protocol::packets::{DecodePacket, EncodePacket}; diff --git a/src/biome.rs b/src/biome.rs index 8191860..1f88a50 100644 --- a/src/biome.rs +++ b/src/biome.rs @@ -2,7 +2,7 @@ use crate::ident; use crate::ident::Ident; -use crate::protocol_inner::packets::play::s2c::Biome as BiomeRegistryBiome; +use crate::protocol_inner::packets::s2c::play::Biome as BiomeRegistryBiome; /// Identifies a particular [`Biome`] on the server. /// @@ -48,7 +48,7 @@ pub struct Biome { impl Biome { pub(crate) fn to_biome_registry_item(&self, id: i32) -> BiomeRegistryBiome { - use crate::protocol_inner::packets::play::s2c::{ + use crate::protocol_inner::packets::s2c::play::{ BiomeAdditionsSound, BiomeEffects, BiomeMoodSound, BiomeMusic, BiomeParticle, BiomeParticleOptions, BiomeProperty, }; diff --git a/src/chunk.rs b/src/chunk.rs index 0e9ba58..6021a63 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -17,8 +17,8 @@ use crate::block_pos::BlockPos; pub use crate::chunk_pos::ChunkPos; use crate::config::Config; use crate::dimension::DimensionId; -use crate::protocol_inner::packets::play::s2c::{ - BlockUpdate, ChunkDataHeightmaps, ChunkData, S2cPlayPacket, ChunkSectionUpdate, +use crate::protocol_inner::packets::s2c::play::{ + BlockUpdate, ChunkData, ChunkDataHeightmaps, ChunkSectionUpdate, S2cPlayPacket, }; use crate::protocol_inner::{Encode, Nbt, VarInt, VarLong}; use crate::server::SharedServer; diff --git a/src/client.rs b/src/client.rs index 311abd8..ab7ac98 100644 --- a/src/client.rs +++ b/src/client.rs @@ -22,19 +22,19 @@ use crate::entity::{ StatusOrAnimation, }; use crate::player_textures::SignedPlayerTextures; -use crate::protocol_inner::packets::play::c2s::{ +use crate::protocol_inner::packets::c2s::play::{ C2sPlayPacket, DiggingStatus, InteractKind, PlayerCommandId, }; -pub use crate::protocol_inner::packets::play::s2c::TitleAnimationTimes as TitleAnimationTimes; -use crate::protocol_inner::packets::play::s2c::{ - EntityAnimation, BiomeRegistry, PlayerActionResponse, ChatType, ChatTypeChat, ChatTypeNarration, - ChatTypeRegistry, ChatTypeRegistryEntry, ClearTitles, DimensionTypeRegistry, - DimensionTypeRegistryEntry, Disconnect, EntityStatus, UnloadChunk, GameStateChange, - GameStateChangeReason, KeepAlive, GameJoin, MoveRelative, RotateAndMoveRelative, - Rotate, PlayerPositionLook, PlayerPositionLookFlags, RegistryCodec, EntitiesDestroy, - PlayerRespawn, EntitySetHeadYaw, S2cPlayPacket, ChunkRenderDistanceCenter, ChunkLoadDistance, - EntityTrackerUpdate, EntityVelocityUpdate, UpdateSubtitle, UpdateTitle, PlayerSpawnPosition, GameMessage, - EntityPosition, EntityAttributes, EntityAttributesProperty, +pub use crate::protocol_inner::packets::s2c::play::TitleAnimationTimes; +use crate::protocol_inner::packets::s2c::play::{ + BiomeRegistry, ChatType, ChatTypeChat, ChatTypeNarration, ChatTypeRegistry, + ChatTypeRegistryEntry, ChunkLoadDistance, ChunkRenderDistanceCenter, ClearTitles, + DimensionTypeRegistry, DimensionTypeRegistryEntry, Disconnect, EntitiesDestroy, + EntityAnimation, EntityAttributes, EntityAttributesProperty, EntityPosition, EntitySetHeadYaw, + EntityStatus, EntityTrackerUpdate, EntityVelocityUpdate, GameJoin, GameMessage, + GameStateChange, GameStateChangeReason, KeepAlive, MoveRelative, PlayerActionResponse, + PlayerPositionLook, PlayerPositionLookFlags, PlayerRespawn, PlayerSpawnPosition, RegistryCodec, + Rotate, RotateAndMoveRelative, S2cPlayPacket, UnloadChunk, UpdateSubtitle, UpdateTitle, }; use crate::protocol_inner::{BoundedInt, ByteAngle, Nbt, RawBytes, VarInt}; use crate::server::{C2sPacketChannels, NewClientData, SharedServer}; diff --git a/src/client/event.rs b/src/client/event.rs index badff92..6c5369f 100644 --- a/src/client/event.rs +++ b/src/client/event.rs @@ -4,9 +4,9 @@ use vek::Vec3; use crate::block_pos::BlockPos; use crate::entity::EntityId; -use crate::protocol_inner::packets::play::c2s::BlockFace; -pub use crate::protocol_inner::packets::play::c2s::{ChatMode, DisplayedSkinParts, Hand, MainHand}; -pub use crate::protocol_inner::packets::play::s2c::GameMode; +use crate::protocol_inner::packets::c2s::play::BlockFace; +pub use crate::protocol_inner::packets::c2s::play::{ChatMode, DisplayedSkinParts, Hand, MainHand}; +pub use crate::protocol_inner::packets::s2c::play::GameMode; /// Represents an action performed by a client. /// diff --git a/src/dimension.rs b/src/dimension.rs index 7d3f47a..2664255 100644 --- a/src/dimension.rs +++ b/src/dimension.rs @@ -1,7 +1,7 @@ //! Dimension configuration and identification. use crate::ident; -use crate::protocol_inner::packets::play::s2c::DimensionType; +use crate::protocol_inner::packets::s2c::play::DimensionType; /// Identifies a particular [`Dimension`] on the server. /// diff --git a/src/entity.rs b/src/entity.rs index 11f6b43..bcdec15 100644 --- a/src/entity.rs +++ b/src/entity.rs @@ -12,8 +12,8 @@ use uuid::Uuid; use vek::{Aabb, Vec3}; use crate::config::Config; -use crate::protocol_inner::packets::play::s2c::{ - EntitySpawn, ExperienceOrbSpawn, PlayerSpawn, S2cPlayPacket, EntityTrackerUpdate, +use crate::protocol_inner::packets::s2c::play::{ + EntitySpawn, EntityTrackerUpdate, ExperienceOrbSpawn, PlayerSpawn, S2cPlayPacket, }; use crate::protocol_inner::{ByteAngle, RawBytes, VarInt}; use crate::slotmap::{Key, SlotMap}; diff --git a/src/player_list.rs b/src/player_list.rs index 297e733..2ba4b8e 100644 --- a/src/player_list.rs +++ b/src/player_list.rs @@ -8,7 +8,7 @@ use uuid::Uuid; use crate::client::GameMode; use crate::player_textures::SignedPlayerTextures; -use crate::protocol_inner::packets::play::s2c::{ +use crate::protocol_inner::packets::s2c::play::{ PlayerListAddPlayer, PlayerListHeaderFooter, S2cPlayPacket, UpdatePlayerList, }; use crate::protocol_inner::packets::Property; diff --git a/src/protocol_inner/packets.rs b/src/protocol_inner/packets.rs index 3f54124..6828afe 100644 --- a/src/protocol_inner/packets.rs +++ b/src/protocol_inner/packets.rs @@ -2,6 +2,8 @@ //! //! See for more packet documentation. +#![macro_use] + use std::fmt; use std::io::{Read, Write}; @@ -12,6 +14,7 @@ use paste::paste; use serde::{Deserialize, Serialize}; use uuid::Uuid; use vek::Vec3; +// use {def_bitfield, def_enum, def_struct}; use crate::block_pos::BlockPos; use crate::ident::Ident; @@ -403,6 +406,10 @@ macro_rules! def_packet_group { } } +// Must be below the macro_rules!. +pub mod c2s; +pub mod s2c; + def_struct! { #[derive(PartialEq, Serialize, Deserialize)] Property { @@ -421,1511 +428,6 @@ def_struct! { } } -/// Packets and types used during the handshaking state. -pub mod handshake { - use super::*; - - def_struct! { - Handshake 0x00 { - protocol_version: VarInt, - server_adddress: BoundedString<0, 255>, - server_port: u16, - next_state: HandshakeNextState, - } - } - - def_enum! { - HandshakeNextState: VarInt { - Status = 1, - Login = 2, - } - } -} - -/// Packets and types used during the status state. -pub mod status { - pub mod s2c { - use super::super::*; - - def_struct! { - QueryResponse 0x00 { - json_response: String - } - } - - def_struct! { - QueryPong 0x01 { - /// Should be the same as the payload from ping. - payload: u64 - } - } - } - - pub mod c2s { - use super::super::*; - - def_struct! { - QueryRequest 0x00 {} - } - - def_struct! { - QueryPing 0x01 { - payload: u64 - } - } - } -} - -/// Packets and types used during the play state. -pub mod login { - pub mod s2c { - use super::super::*; - - def_struct! { - LoginDisconnect 0x00 { - reason: Text, - } - } - - def_struct! { - EncryptionRequest 0x01 { - /// Currently unused - server_id: BoundedString<0, 20>, - /// The RSA public key - public_key: Vec, - verify_token: BoundedArray, - } - } - - def_struct! { - LoginSuccess 0x02 { - uuid: Uuid, - username: BoundedString<3, 16>, - properties: Vec, - } - } - - def_struct! { - LoginCompression 0x03 { - threshold: VarInt - } - } - - def_struct! { - LoginPluginRequest 0x04 { - message_id: VarInt, - channel: Ident, - data: RawBytes, - } - } - - def_packet_group! { - S2cLoginPacket { - LoginDisconnect, - EncryptionRequest, - LoginSuccess, - LoginCompression, - LoginPluginRequest, - } - } - } - - pub mod c2s { - use super::super::*; - - def_struct! { - LoginStart 0x00 { - username: BoundedString<3, 16>, - sig_data: Option, - } - } - - def_struct! { - EncryptionResponse 0x01 { - shared_secret: BoundedArray, - token_or_sig: VerifyTokenOrMsgSig, - } - } - - def_enum! { - VerifyTokenOrMsgSig: u8 { - VerifyToken: BoundedArray = 1, - MsgSig: MessageSignature = 0, - } - } - - def_struct! { - MessageSignature { - salt: u64, - sig: Vec, // TODO: bounds? - } - } - - def_struct! { - LoginPluginResponse 0x02 { - message_id: VarInt, - data: Option, - } - } - - def_packet_group! { - C2sLoginPacket { - LoginStart, - EncryptionResponse, - LoginPluginResponse, - } - } - } -} - -/// Packets and types used during the play state. -pub mod play { - pub mod s2c { - use super::super::*; - - def_struct! { - EntitySpawn 0x00 { - entity_id: VarInt, - object_uuid: Uuid, - kind: VarInt, - position: Vec3, - pitch: ByteAngle, - yaw: ByteAngle, - head_yaw: ByteAngle, - data: VarInt, - velocity: Vec3, - } - } - - def_struct! { - ExperienceOrbSpawn 0x01 { - entity_id: VarInt, - position: Vec3, - count: i16, - } - } - - def_struct! { - PlayerSpawn 0x02 { - entity_id: VarInt, - player_uuid: Uuid, - position: Vec3, - yaw: ByteAngle, - pitch: ByteAngle, - } - } - - def_struct! { - EntityAnimation 0x03 { - entity_id: VarInt, - animation: u8, - } - } - - def_struct! { - PlayerActionResponse 0x05 { - sequence: VarInt, - } - } - - def_struct! { - BlockBreakingProgress 0x06 { - entity_id: VarInt, - location: BlockPos, - destroy_stage: BoundedInt, - } - } - - def_struct! { - BlockEntityUpdate 0x07 { - location: BlockPos, - kind: VarInt, // TODO: use enum here - data: nbt::Blob, - } - } - - def_struct! { - BlockEvent 0x08 { - location: BlockPos, - action_id: u8, - action_param: u8, - block_type: VarInt, // TODO: use BlockType type. - } - } - - def_struct! { - BlockUpdate 0x09 { - location: BlockPos, - block_id: VarInt, - } - } - - def_struct! { - BossBar 0x0a { - uuid: Uuid, - action: BossBarAction, - } - } - - def_enum! { - BossBarAction: VarInt { - Add: BossBarActionAdd = 0, - // TODO - } - } - - def_struct! { - BossBarActionAdd { - title: Text, - health: f32, - color: BossBarColor, - division: BossBarDivision, - /// TODO: bitmask - flags: u8, - } - } - - def_enum! { - BossBarColor: VarInt { - Pink = 0, - Blue = 1, - Red = 2, - Green = 3, - Yellow = 4, - Purple = 5, - White = 6, - } - } - - def_enum! { - BossBarDivision: VarInt { - NoDivision = 0, - SixNotches = 1, - TenNotches = 2, - TwelveNotches = 3, - TwentyNotches = 4, - } - } - - def_struct! { - SetDifficulty 0x0b { - difficulty: Difficulty, - locked: bool, - } - } - - def_enum! { - Difficulty: u8 { - Peaceful = 0, - Easy = 1, - Normal = 2, - Hard = 3, - } - } - - def_struct! { - ClearTitles 0x0d { - reset: bool, - } - } - - def_struct! { - Disconnect 0x17 { - reason: Text, - } - } - - def_struct! { - EntityStatus 0x18 { - entity_id: i32, - entity_status: u8, - } - } - - def_struct! { - UnloadChunk 0x1a { - chunk_x: i32, - chunk_z: i32 - } - } - - def_struct! { - GameStateChange 0x1b { - reason: GameStateChangeReason, - value: f32, - } - } - - def_enum! { - GameStateChangeReason: u8 { - NoRespawnBlockAvailable = 0, - EndRaining = 1, - BeginRaining = 2, - ChangeGameMode = 3, - WinGame = 4, - DemoEvent = 5, - ArrowHitPlayer = 6, - RainLevelChange = 7, - ThunderLevelChange = 8, - PlayPufferfishStingSound = 9, - PlayElderGuardianMobAppearance = 10, - EnableRespawnScreen = 11, - } - } - - def_struct! { - WorldBorderInitialize 0x1d { - x: f64, - z: f64, - old_diameter: f64, - new_diameter: f64, - speed: VarLong, - portal_teleport_boundary: VarInt, - warning_blocks: VarInt, - warning_time: VarInt, - } - } - - def_struct! { - KeepAlive 0x1e { - id: i64, - } - } - - def_struct! { - ChunkData 0x1f { - chunk_x: i32, - chunk_z: i32, - heightmaps: Nbt, - blocks_and_biomes: Vec, - block_entities: Vec, - trust_edges: bool, - sky_light_mask: BitVec, - block_light_mask: BitVec, - empty_sky_light_mask: BitVec, - empty_block_light_mask: BitVec, - sky_light_arrays: Vec<[u8; 2048]>, - block_light_arrays: Vec<[u8; 2048]>, - } - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct ChunkDataHeightmaps { - #[serde(rename = "MOTION_BLOCKING", serialize_with = "nbt::i64_array")] - pub motion_blocking: Vec, - } - - def_struct! { - ChunkDataBlockEntity { - packed_xz: i8, - y: i16, - kind: VarInt, - data: nbt::Blob, - } - } - - def_struct! { - GameJoin 0x23 { - /// Entity ID of the joining player - entity_id: i32, - is_hardcore: bool, - gamemode: GameMode, - previous_gamemode: GameMode, - dimension_names: Vec, - registry_codec: Nbt, - /// The name of the dimension type being spawned into. - dimension_type_name: Ident, - /// The name of the dimension being spawned into. - dimension_name: Ident, - /// Hash of the world's seed used for client biome noise. - hashed_seed: i64, - /// No longer used by the client. - max_players: VarInt, - view_distance: BoundedInt, - simulation_distance: VarInt, - /// If reduced debug info should be shown on the F3 screen. - reduced_debug_info: bool, - /// If player respawns should be instant or not. - enable_respawn_screen: bool, - is_debug: bool, - /// If this is a superflat world. - /// Superflat worlds have different void fog and horizon levels. - is_flat: bool, - last_death_location: Option<(Ident, BlockPos)>, - } - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct RegistryCodec { - #[serde(rename = "minecraft:dimension_type")] - pub dimension_type_registry: DimensionTypeRegistry, - #[serde(rename = "minecraft:worldgen/biome")] - pub biome_registry: BiomeRegistry, - #[serde(rename = "minecraft:chat_type")] - pub chat_type_registry: ChatTypeRegistry, - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct DimensionTypeRegistry { - #[serde(rename = "type")] - pub kind: Ident, - pub value: Vec, - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct DimensionTypeRegistryEntry { - pub name: Ident, - pub id: i32, - pub element: DimensionType, - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct DimensionType { - pub piglin_safe: bool, - pub has_raids: bool, - pub monster_spawn_light_level: i32, - pub monster_spawn_block_light_limit: i32, - pub natural: bool, - pub ambient_light: f32, - pub fixed_time: Option, - pub infiniburn: String, // TODO: tag type? - pub respawn_anchor_works: bool, - pub has_skylight: bool, - pub bed_works: bool, - pub effects: Ident, - pub min_y: i32, - pub height: i32, - pub logical_height: i32, - pub coordinate_scale: f64, - pub ultrawarm: bool, - pub has_ceiling: bool, - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct BiomeRegistry { - #[serde(rename = "type")] - pub kind: Ident, - pub value: Vec, - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct Biome { - pub name: Ident, - pub id: i32, - pub element: BiomeProperty, - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct BiomeProperty { - pub precipitation: String, - pub depth: f32, - pub temperature: f32, - pub scale: f32, - pub downfall: f32, - pub category: String, - pub temperature_modifier: Option, - pub effects: BiomeEffects, - pub particle: Option, - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct BiomeEffects { - pub sky_color: i32, - pub water_fog_color: i32, - pub fog_color: i32, - pub water_color: i32, - pub foliage_color: Option, - pub grass_color: Option, - pub grass_color_modifier: Option, - pub music: Option, - pub ambient_sound: Option, - pub additions_sound: Option, - pub mood_sound: Option, - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct BiomeMusic { - pub replace_current_music: bool, - pub sound: Ident, - pub max_delay: i32, - pub min_delay: i32, - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct BiomeAdditionsSound { - pub sound: Ident, - pub tick_chance: f64, - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct BiomeMoodSound { - pub sound: Ident, - pub tick_delay: i32, - pub offset: f64, - pub block_search_extent: i32, - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct BiomeParticle { - pub probability: f32, - pub options: BiomeParticleOptions, - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct BiomeParticleOptions { - #[serde(rename = "type")] - pub kind: Ident, - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct ChatTypeRegistry { - #[serde(rename = "type")] - pub kind: Ident, - pub value: Vec, - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct ChatTypeRegistryEntry { - pub name: Ident, - pub id: i32, - pub element: ChatType, - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct ChatType { - pub chat: ChatTypeChat, - pub narration: ChatTypeNarration, - } - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct ChatTypeChat {} - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct ChatTypeNarration { - pub priority: String, - } - - def_enum! { - #[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)] - GameMode: u8 { - #[default] - Survival = 0, - Creative = 1, - Adventure = 2, - Spectator = 3, - } - } - - def_struct! { - MoveRelative 0x26 { - entity_id: VarInt, - delta: Vec3, - on_ground: bool, - } - } - - def_struct! { - RotateAndMoveRelative 0x27 { - entity_id: VarInt, - delta: Vec3, - yaw: ByteAngle, - pitch: ByteAngle, - on_ground: bool, - } - } - - def_struct! { - Rotate 0x28 { - entity_id: VarInt, - yaw: ByteAngle, - pitch: ByteAngle, - on_ground: bool, - } - } - - def_struct! { - ChatMessage 0x30 { - message: Text, - /// Index into the chat type registry - kind: VarInt, - sender: Uuid, - // TODO more fields - } - } - - def_enum! { - UpdatePlayerList 0x34: VarInt { - AddPlayer: Vec = 0, - UpdateGameMode: Vec<(Uuid, GameMode)> = 1, - UpdateLatency: Vec<(Uuid, VarInt)> = 2, - UpdateDisplayName: Vec<(Uuid, Option)> = 3, - RemovePlayer: Vec = 4, - } - } - - def_struct! { - PlayerListAddPlayer { - uuid: Uuid, - username: BoundedString<3, 16>, - properties: Vec, - game_mode: GameMode, - ping: VarInt, - display_name: Option, - sig_data: Option, - } - } - - def_struct! { - PlayerPositionLook 0x36 { - position: Vec3, - yaw: f32, - pitch: f32, - flags: PlayerPositionLookFlags, - teleport_id: VarInt, - dismount_vehicle: bool, - } - } - - def_bitfield! { - PlayerPositionLookFlags: u8 { - x = 0, - y = 1, - z = 2, - y_rot = 3, - x_rot = 4, - } - } - - def_struct! { - EntitiesDestroy 0x38 { - entities: Vec, - } - } - - def_struct! { - PlayerRespawn 0x3b { - dimension_type_name: Ident, - dimension_name: Ident, - hashed_seed: u64, - game_mode: GameMode, - previous_game_mode: GameMode, - is_debug: bool, - is_flat: bool, - copy_metadata: bool, - last_death_location: Option<(Ident, BlockPos)>, - } - } - - def_struct! { - EntitySetHeadYaw 0x3c { - entity_id: VarInt, - head_yaw: ByteAngle, - } - } - - def_struct! { - ChunkSectionUpdate 0x3d { - chunk_section_position: i64, - invert_trust_edges: bool, - blocks: Vec, - } - } - - def_struct! { - UpdateSelectedSlot 0x47 { - slot: BoundedInt, - } - } - - def_struct! { - ChunkRenderDistanceCenter 0x48 { - chunk_x: VarInt, - chunk_z: VarInt, - } - } - - def_struct! { - ChunkLoadDistance 0x49 { - view_distance: BoundedInt, - } - } - - def_struct! { - PlayerSpawnPosition 0x4a { - location: BlockPos, - angle: f32, - } - } - - def_struct! { - EntityTrackerUpdate 0x4d { - entity_id: VarInt, - metadata: RawBytes, - } - } - - def_struct! { - EntityVelocityUpdate 0x4f { - entity_id: VarInt, - velocity: Vec3, - } - } - - def_struct! { - UpdateSubtitle 0x58 { - subtitle_text: Text, - } - } - - def_struct! { - WorldTimeUpdate 0x59 { - /// The age of the world in 1/20ths of a second. - world_age: i64, - /// The current time of day in 1/20ths of a second. - /// The value should be in the range \[0, 24000]. - /// 6000 is noon, 12000 is sunset, and 18000 is midnight. - time_of_day: i64, - } - } - - def_struct! { - UpdateTitle 0x5a { - text: Text, - } - } - - def_struct! { - #[derive(Copy, PartialEq, Eq)] - TitleAnimationTimes 0x5b { - /// Ticks to spend fading in. - fade_in: u32, - /// Ticks to keep the title displayed. - stay: u32, - /// Ticks to spend fading out. - fade_out: u32, - } - } - - def_struct! { - GameMessage 0x5f { - chat: Text, - /// Index into the chat type registry. - kind: VarInt, - } - } - - def_struct! { - PlayerListHeaderFooter 0x60 { - header: Text, - footer: Text, - } - } - - def_struct! { - EntityPosition 0x63 { - entity_id: VarInt, - position: Vec3, - yaw: ByteAngle, - pitch: ByteAngle, - on_ground: bool, - } - } - - def_struct! { - EntityAttributes 0x65 { - entity_id: VarInt, - properties: Vec, - } - } - - def_struct! { - EntityAttributesProperty { - key: Ident, - value: f64, - modifiers: Vec - } - } - - def_struct! { - EntityAttributesModifiers { - uuid: Uuid, - amount: f64, - operation: u8, - } - } - - def_packet_group! { - S2cPlayPacket { - EntitySpawn, - ExperienceOrbSpawn, - PlayerSpawn, - EntityAnimation, - PlayerActionResponse, - BlockBreakingProgress, - BlockEntityUpdate, - BlockEvent, - BlockUpdate, - BossBar, - ClearTitles, - Disconnect, - EntityStatus, - UnloadChunk, - GameStateChange, - KeepAlive, - ChunkData, - GameJoin, - MoveRelative, - RotateAndMoveRelative, - Rotate, - ChatMessage, - UpdatePlayerList, - PlayerPositionLook, - EntitiesDestroy, - PlayerRespawn, - EntitySetHeadYaw, - ChunkSectionUpdate, - UpdateSelectedSlot, - ChunkRenderDistanceCenter, - ChunkLoadDistance, - PlayerSpawnPosition, - EntityTrackerUpdate, - EntityVelocityUpdate, - UpdateSubtitle, - WorldTimeUpdate, - UpdateTitle, - TitleAnimationTimes, - GameMessage, - PlayerListHeaderFooter, - EntityPosition, - EntityAttributes, - } - } - } - - pub mod c2s { - use super::super::*; - - def_struct! { - TeleportConfirm 0x00 { - teleport_id: VarInt - } - } - - def_struct! { - QueryBlockNbt 0x01 { - transaction_id: VarInt, - location: BlockPos, - } - } - - def_enum! { - UpdateDifficulty 0x02: i8 { - Peaceful = 0, - Easy = 1, - Normal = 2, - Hard = 3, - } - } - - def_struct! { - CommandExecution 0x03 { - command: String, // TODO: bounded? - // TODO: timestamp, arg signatures - signed_preview: bool, - } - } - - def_struct! { - ChatMessage 0x04 { - message: BoundedString<0, 256>, - timestamp: u64, - salt: u64, - signature: Vec, - signed_preview: bool, - } - } - - def_struct! { - RequestChatPreview 0x05 { - query: i32, // TODO: is this an i32 or a varint? - message: BoundedString<0, 256>, - } - } - - def_enum! { - ClientStatus 0x06: VarInt { - /// Sent when ready to complete login and ready to respawn after death. - PerformRespawn = 0, - /// Sent when the statistics menu is opened. - RequestStatus = 1, - } - } - - def_struct! { - ClientSettings 0x07 { - /// e.g. en_US - locale: BoundedString<0, 16>, - /// Client-side render distance in chunks. - view_distance: BoundedInt, - chat_mode: ChatMode, - chat_colors: bool, - displayed_skin_parts: DisplayedSkinParts, - main_hand: MainHand, - /// Currently always false - enable_text_filtering: bool, - /// False if the client should not show up in the hover preview. - allow_server_listings: bool, - } - } - - def_enum! { - #[derive(Copy, PartialEq, Eq)] - ChatMode: VarInt { - Enabled = 0, - CommandsOnly = 1, - Hidden = 2, - } - } - - def_bitfield! { - DisplayedSkinParts: u8 { - cape = 0, - jacket = 1, - left_sleeve = 2, - right_sleeve = 3, - left_pants_leg = 4, - right_pants_leg = 5, - hat = 6, - } - } - - def_enum! { - #[derive(Copy, PartialEq, Eq)] - MainHand: VarInt { - Left = 0, - Right = 1, - } - } - - def_struct! { - RequestCommandCompletion 0x08 { - transaction_id: VarInt, - /// Text behind the cursor without the '/'. - text: BoundedString<0, 32500> - } - } - - def_struct! { - ButtonClick 0x09 { - window_id: i8, - button_id: i8, - } - } - - def_struct! { - ClickSlot 0x0a { - // TODO - } - } - - def_struct! { - CloseHandledScreen 0x0b { - window_id: u8, - } - } - - def_struct! { - CustomPayload 0x0c { - channel: Ident, - data: RawBytes, - } - } - - def_struct! { - BookUpdate 0x0d { - slot: VarInt, - entries: Vec, - title: Option, - } - } - - def_struct! { - QueryEntityNbt 0x0e { - transaction_id: VarInt, - entity_id: VarInt, - } - } - - def_struct! { - PlayerInteractEntity 0x0f { - entity_id: VarInt, - kind: InteractKind, - sneaking: bool, - } - } - - def_enum! { - InteractKind: VarInt { - Interact: Hand = 0, - Attack = 1, - InteractAt: (Vec3, Hand) = 2 - } - } - - def_enum! { - #[derive(Copy, PartialEq, Eq)] - Hand: VarInt { - Main = 0, - Off = 1, - } - } - - def_struct! { - JigsawGenerate 0x10 { - location: BlockPos, - levels: VarInt, - keep_jigsaws: bool, - } - } - - def_struct! { - KeepAlive 0x11 { - id: i64, - } - } - - def_struct! { - UpdateDifficultyLock 0x12 { - locked: bool - } - } - - def_struct! { - MovePlayerPosition 0x13 { - position: Vec3, - on_ground: bool, - } - } - - def_struct! { - MovePlayerPositionAndRotation 0x14 { - // Absolute position - position: Vec3, - /// Absolute rotation on X axis in degrees. - yaw: f32, - /// Absolute rotation on Y axis in degrees. - pitch: f32, - on_ground: bool, - } - } - - def_struct! { - MovePlayerRotation 0x15 { - /// Absolute rotation on X axis in degrees. - yaw: f32, - /// Absolute rotation on Y axis in degrees. - pitch: f32, - on_ground: bool, - } - } - - def_struct! { - MovePlayerOnGround 0x16 { - on_ground: bool - } - } - - def_struct! { - MoveVehicle 0x17 { - /// Absolute position - position: Vec3, - /// Degrees - yaw: f32, - /// Degrees - pitch: f32, - } - } - - def_struct! { - BoatPaddleState 0x18 { - left_paddle_turning: bool, - right_paddle_turning: bool, - } - } - - def_struct! { - PickFromInventory 0x19 { - slot_to_use: VarInt, - } - } - - def_struct! { - CraftRequest 0x1a { - window_id: i8, - recipe: Ident, - make_all: bool, - } - } - - def_enum! { - UpdatePlayerAbilities 0x1b: i8 { - NotFlying = 0, - Flying = 0b10, - } - } - - def_struct! { - PlayerAction 0x1c { - status: DiggingStatus, - location: BlockPos, - face: BlockFace, - sequence: VarInt, - } - } - - def_enum! { - DiggingStatus: VarInt { - StartedDigging = 0, - CancelledDigging = 1, - FinishedDigging = 2, - DropItemStack = 3, - DropItem = 4, - ShootArrowOrFinishEating = 5, - SwapItemInHand = 6, - } - } - - def_enum! { - #[derive(Copy, PartialEq, Eq)] - BlockFace: i8 { - /// -Y - Bottom = 0, - /// +Y - Top = 1, - /// -Z - North = 2, - /// +Z - South = 3, - /// -X - West = 4, - /// +X - East = 5, - } - } - - def_struct! { - PlayerCommand 0x1d { - entity_id: VarInt, - action_id: PlayerCommandId, - jump_boost: BoundedInt, - } - } - - def_enum! { - PlayerCommandId: VarInt { - StartSneaking = 0, - StopSneaking = 1, - LeaveBed = 2, - StartSprinting = 3, - StopSprinting = 4, - StartJumpWithHorse = 5, - StopJumpWithHorse = 6, - OpenHorseInventory = 7, - StartFlyingWithElytra = 8, - } - } - - def_struct! { - PlayerInput 0x1e { - sideways: f32, - forward: f32, - flags: PlayerInputFlags, - } - } - - def_bitfield! { - PlayerInputFlags: u8 { - jump = 0, - unmount = 1, - } - } - - def_struct! { - PlayPong 0x1f { - id: i32, - } - } - - def_struct! { - RecipeBookChangeSettings 0x20 { - book_id: RecipeBookId, - book_open: bool, - filter_active: bool, - } - } - - def_enum! { - RecipeBookId: VarInt { - Crafting = 0, - Furnace = 1, - BlastFurnace = 2, - Smoker = 3, - } - } - - def_struct! { - RecipeBookSeenRecipe 0x21 { - recipe_id: Ident, - } - } - - def_struct! { - RenameItem 0x22 { - item_name: BoundedString<0, 50>, - } - } - - def_enum! { - ResourcePackStatus 0x23: VarInt { - SuccessfullyLoaded = 0, - Declined = 1, - FailedDownload = 2, - Accepted = 3, - } - } - - def_enum! { - AdvancementTab 0x24: VarInt { - OpenedTab: Ident = 0, - ClosedScreen = 1, - } - } - - def_struct! { - SelectMerchantTrade 0x25 { - selected_slot: VarInt, - } - } - - def_struct! { - UpdateBeacon 0x26 { - // TODO: potion ids - primary_effect: Option, - secondary_effect: Option, - } - } - - def_struct! { - UpdateSelectedSlot 0x27 { - slot: BoundedInt, - } - } - - def_struct! { - UpdateCommandBlock 0x28 { - location: BlockPos, - command: String, - mode: CommandBlockMode, - flags: CommandBlockFlags, - } - } - - def_enum! { - CommandBlockMode: VarInt { - Sequence = 0, - Auto = 1, - Redstone = 2, - } - } - - def_bitfield! { - CommandBlockFlags: i8 { - track_output = 0, - is_conditional = 1, - automatic = 2, - } - } - - def_struct! { - UpdateCommandBlockMinecart 0x29 { - entity_id: VarInt, - command: String, - track_output: bool, - } - } - - def_struct! { - UpdateCreativeModeSlot 0x2a { - slot: i16, - // TODO: clicked_item: Slot, - } - } - - def_struct! { - UpdateJigsaw 0x2b { - location: BlockPos, - name: Ident, - target: Ident, - pool: Ident, - final_state: String, - joint_type: String, - } - } - - def_struct! { - UpdateStructureBlock 0x2c { - location: BlockPos, - action: StructureBlockAction, - mode: StructureBlockMode, - name: String, - offset_xyz: [BoundedInt; 3], - size_xyz: [BoundedInt; 3], - mirror: StructureBlockMirror, - rotation: StructureBlockRotation, - metadata: String, - integrity: f32, // TODO: bounded float between 0 and 1. - seed: VarLong, - flags: StructureBlockFlags, - } - } - - def_enum! { - StructureBlockAction: VarInt { - UpdateData = 0, - SaveStructure = 1, - LoadStructure = 2, - DetectSize = 3, - } - } - - def_enum! { - StructureBlockMode: VarInt { - Save = 0, - Load = 1, - Corner = 2, - Data = 3, - } - } - - def_enum! { - StructureBlockMirror: VarInt { - None = 0, - LeftRight = 1, - FrontBack = 2, - } - } - - def_enum! { - StructureBlockRotation: VarInt { - None = 0, - Clockwise90 = 1, - Clockwise180 = 2, - Counterclockwise90 = 3, - } - } - - def_bitfield! { - StructureBlockFlags: i8 { - ignore_entities = 0, - show_air = 1, - show_bounding_box = 2, - } - } - - def_struct! { - UpdateSign 0x2d { - location: BlockPos, - lines: [BoundedString<0, 384>; 4], - } - } - - def_struct! { - HandSwing 0x2e { - hand: Hand, - } - } - - def_struct! { - SpectatorTeleport 0x2f { - target: Uuid, - } - } - - def_struct! { - PlayerInteractBlock 0x30 { - hand: Hand, - location: BlockPos, - face: BlockFace, - cursor_pos: Vec3, - head_inside_block: bool, - sequence: VarInt, - } - } - - def_struct! { - PlayerInteractItem 0x31 { - hand: Hand, - sequence: VarInt, - } - } - - def_packet_group! { - C2sPlayPacket { - TeleportConfirm, - QueryBlockNbt, - UpdateDifficulty, - CommandExecution, - ChatMessage, - RequestChatPreview, - ClientStatus, - ClientSettings, - RequestCommandCompletion, - ButtonClick, - ClickSlot, - CloseHandledScreen, - CustomPayload, - BookUpdate, - QueryEntityNbt, - PlayerInteractEntity, - JigsawGenerate, - KeepAlive, - UpdateDifficultyLock, - MovePlayerPosition, - MovePlayerPositionAndRotation, - MovePlayerRotation, - MovePlayerOnGround, - MoveVehicle, - BoatPaddleState, - PickFromInventory, - CraftRequest, - UpdatePlayerAbilities, - PlayerAction, - PlayerCommand, - PlayerInput, - PlayPong, - RecipeBookChangeSettings, - RecipeBookSeenRecipe, - RenameItem, - ResourcePackStatus, - AdvancementTab, - SelectMerchantTrade, - UpdateBeacon, - UpdateSelectedSlot, - UpdateCommandBlock, - UpdateCommandBlockMinecart, - UpdateCreativeModeSlot, - UpdateJigsaw, - UpdateStructureBlock, - UpdateSign, - HandSwing, - SpectatorTeleport, - PlayerInteractBlock, - PlayerInteractItem, - } - } - } -} - #[cfg(test)] pub(crate) mod test { use super::*; diff --git a/src/protocol_inner/packets/c2s.rs b/src/protocol_inner/packets/c2s.rs new file mode 100644 index 0000000..e95534b --- /dev/null +++ b/src/protocol_inner/packets/c2s.rs @@ -0,0 +1,707 @@ +//! Client to server packets. + +use super::*; + +pub mod handshake { + use super::*; + + def_struct! { + Handshake 0x00 { + protocol_version: VarInt, + server_adddress: BoundedString<0, 255>, + server_port: u16, + next_state: HandshakeNextState, + } + } + + def_enum! { + HandshakeNextState: VarInt { + Status = 1, + Login = 2, + } + } +} + +pub mod status { + use super::*; + + def_struct! { + QueryRequest 0x00 {} + } + + def_struct! { + QueryPing 0x01 { + payload: u64 + } + } +} + +pub mod login { + use super::*; + + def_struct! { + LoginStart 0x00 { + username: BoundedString<3, 16>, + sig_data: Option, + } + } + + def_struct! { + EncryptionResponse 0x01 { + shared_secret: BoundedArray, + token_or_sig: VerifyTokenOrMsgSig, + } + } + + def_enum! { + VerifyTokenOrMsgSig: u8 { + VerifyToken: BoundedArray = 1, + MsgSig: MessageSignature = 0, + } + } + + def_struct! { + MessageSignature { + salt: u64, + sig: Vec, // TODO: bounds? + } + } + + def_struct! { + LoginPluginResponse 0x02 { + message_id: VarInt, + data: Option, + } + } + + def_packet_group! { + C2sLoginPacket { + LoginStart, + EncryptionResponse, + LoginPluginResponse, + } + } +} + +pub mod play { + use super::super::*; + + def_struct! { + TeleportConfirm 0x00 { + teleport_id: VarInt + } + } + + def_struct! { + QueryBlockNbt 0x01 { + transaction_id: VarInt, + location: BlockPos, + } + } + + def_enum! { + UpdateDifficulty 0x02: i8 { + Peaceful = 0, + Easy = 1, + Normal = 2, + Hard = 3, + } + } + + def_struct! { + CommandExecution 0x03 { + command: String, // TODO: bounded? + // TODO: timestamp, arg signatures + signed_preview: bool, + } + } + + def_struct! { + ChatMessage 0x04 { + message: BoundedString<0, 256>, + timestamp: u64, + salt: u64, + signature: Vec, + signed_preview: bool, + } + } + + def_struct! { + RequestChatPreview 0x05 { + query: i32, // TODO: is this an i32 or a varint? + message: BoundedString<0, 256>, + } + } + + def_enum! { + ClientStatus 0x06: VarInt { + /// Sent when ready to complete login and ready to respawn after death. + PerformRespawn = 0, + /// Sent when the statistics menu is opened. + RequestStatus = 1, + } + } + + def_struct! { + ClientSettings 0x07 { + /// e.g. en_US + locale: BoundedString<0, 16>, + /// Client-side render distance in chunks. + view_distance: BoundedInt, + chat_mode: ChatMode, + chat_colors: bool, + displayed_skin_parts: DisplayedSkinParts, + main_hand: MainHand, + /// Currently always false + enable_text_filtering: bool, + /// False if the client should not show up in the hover preview. + allow_server_listings: bool, + } + } + + def_enum! { + #[derive(Copy, PartialEq, Eq)] + ChatMode: VarInt { + Enabled = 0, + CommandsOnly = 1, + Hidden = 2, + } + } + + def_bitfield! { + DisplayedSkinParts: u8 { + cape = 0, + jacket = 1, + left_sleeve = 2, + right_sleeve = 3, + left_pants_leg = 4, + right_pants_leg = 5, + hat = 6, + } + } + + def_enum! { + #[derive(Copy, PartialEq, Eq)] + MainHand: VarInt { + Left = 0, + Right = 1, + } + } + + def_struct! { + RequestCommandCompletion 0x08 { + transaction_id: VarInt, + /// Text behind the cursor without the '/'. + text: BoundedString<0, 32500> + } + } + + def_struct! { + ButtonClick 0x09 { + window_id: i8, + button_id: i8, + } + } + + def_struct! { + ClickSlot 0x0a { + // TODO + } + } + + def_struct! { + CloseHandledScreen 0x0b { + window_id: u8, + } + } + + def_struct! { + CustomPayload 0x0c { + channel: Ident, + data: RawBytes, + } + } + + def_struct! { + BookUpdate 0x0d { + slot: VarInt, + entries: Vec, + title: Option, + } + } + + def_struct! { + QueryEntityNbt 0x0e { + transaction_id: VarInt, + entity_id: VarInt, + } + } + + def_struct! { + PlayerInteractEntity 0x0f { + entity_id: VarInt, + kind: InteractKind, + sneaking: bool, + } + } + + def_enum! { + InteractKind: VarInt { + Interact: Hand = 0, + Attack = 1, + InteractAt: (Vec3, Hand) = 2 + } + } + + def_enum! { + #[derive(Copy, PartialEq, Eq)] + Hand: VarInt { + Main = 0, + Off = 1, + } + } + + def_struct! { + JigsawGenerate 0x10 { + location: BlockPos, + levels: VarInt, + keep_jigsaws: bool, + } + } + + def_struct! { + KeepAlive 0x11 { + id: i64, + } + } + + def_struct! { + UpdateDifficultyLock 0x12 { + locked: bool + } + } + + def_struct! { + MovePlayerPosition 0x13 { + position: Vec3, + on_ground: bool, + } + } + + def_struct! { + MovePlayerPositionAndRotation 0x14 { + // Absolute position + position: Vec3, + /// Absolute rotation on X axis in degrees. + yaw: f32, + /// Absolute rotation on Y axis in degrees. + pitch: f32, + on_ground: bool, + } + } + + def_struct! { + MovePlayerRotation 0x15 { + /// Absolute rotation on X axis in degrees. + yaw: f32, + /// Absolute rotation on Y axis in degrees. + pitch: f32, + on_ground: bool, + } + } + + def_struct! { + MovePlayerOnGround 0x16 { + on_ground: bool + } + } + + def_struct! { + MoveVehicle 0x17 { + /// Absolute position + position: Vec3, + /// Degrees + yaw: f32, + /// Degrees + pitch: f32, + } + } + + def_struct! { + BoatPaddleState 0x18 { + left_paddle_turning: bool, + right_paddle_turning: bool, + } + } + + def_struct! { + PickFromInventory 0x19 { + slot_to_use: VarInt, + } + } + + def_struct! { + CraftRequest 0x1a { + window_id: i8, + recipe: Ident, + make_all: bool, + } + } + + def_enum! { + UpdatePlayerAbilities 0x1b: i8 { + NotFlying = 0, + Flying = 0b10, + } + } + + def_struct! { + PlayerAction 0x1c { + status: DiggingStatus, + location: BlockPos, + face: BlockFace, + sequence: VarInt, + } + } + + def_enum! { + DiggingStatus: VarInt { + StartedDigging = 0, + CancelledDigging = 1, + FinishedDigging = 2, + DropItemStack = 3, + DropItem = 4, + ShootArrowOrFinishEating = 5, + SwapItemInHand = 6, + } + } + + def_enum! { + #[derive(Copy, PartialEq, Eq)] + BlockFace: i8 { + /// -Y + Bottom = 0, + /// +Y + Top = 1, + /// -Z + North = 2, + /// +Z + South = 3, + /// -X + West = 4, + /// +X + East = 5, + } + } + + def_struct! { + PlayerCommand 0x1d { + entity_id: VarInt, + action_id: PlayerCommandId, + jump_boost: BoundedInt, + } + } + + def_enum! { + PlayerCommandId: VarInt { + StartSneaking = 0, + StopSneaking = 1, + LeaveBed = 2, + StartSprinting = 3, + StopSprinting = 4, + StartJumpWithHorse = 5, + StopJumpWithHorse = 6, + OpenHorseInventory = 7, + StartFlyingWithElytra = 8, + } + } + + def_struct! { + PlayerInput 0x1e { + sideways: f32, + forward: f32, + flags: PlayerInputFlags, + } + } + + def_bitfield! { + PlayerInputFlags: u8 { + jump = 0, + unmount = 1, + } + } + + def_struct! { + PlayPong 0x1f { + id: i32, + } + } + + def_struct! { + RecipeBookChangeSettings 0x20 { + book_id: RecipeBookId, + book_open: bool, + filter_active: bool, + } + } + + def_enum! { + RecipeBookId: VarInt { + Crafting = 0, + Furnace = 1, + BlastFurnace = 2, + Smoker = 3, + } + } + + def_struct! { + RecipeBookSeenRecipe 0x21 { + recipe_id: Ident, + } + } + + def_struct! { + RenameItem 0x22 { + item_name: BoundedString<0, 50>, + } + } + + def_enum! { + ResourcePackStatus 0x23: VarInt { + SuccessfullyLoaded = 0, + Declined = 1, + FailedDownload = 2, + Accepted = 3, + } + } + + def_enum! { + AdvancementTab 0x24: VarInt { + OpenedTab: Ident = 0, + ClosedScreen = 1, + } + } + + def_struct! { + SelectMerchantTrade 0x25 { + selected_slot: VarInt, + } + } + + def_struct! { + UpdateBeacon 0x26 { + // TODO: potion ids + primary_effect: Option, + secondary_effect: Option, + } + } + + def_struct! { + UpdateSelectedSlot 0x27 { + slot: BoundedInt, + } + } + + def_struct! { + UpdateCommandBlock 0x28 { + location: BlockPos, + command: String, + mode: CommandBlockMode, + flags: CommandBlockFlags, + } + } + + def_enum! { + CommandBlockMode: VarInt { + Sequence = 0, + Auto = 1, + Redstone = 2, + } + } + + def_bitfield! { + CommandBlockFlags: i8 { + track_output = 0, + is_conditional = 1, + automatic = 2, + } + } + + def_struct! { + UpdateCommandBlockMinecart 0x29 { + entity_id: VarInt, + command: String, + track_output: bool, + } + } + + def_struct! { + UpdateCreativeModeSlot 0x2a { + slot: i16, + // TODO: clicked_item: Slot, + } + } + + def_struct! { + UpdateJigsaw 0x2b { + location: BlockPos, + name: Ident, + target: Ident, + pool: Ident, + final_state: String, + joint_type: String, + } + } + + def_struct! { + UpdateStructureBlock 0x2c { + location: BlockPos, + action: StructureBlockAction, + mode: StructureBlockMode, + name: String, + offset_xyz: [BoundedInt; 3], + size_xyz: [BoundedInt; 3], + mirror: StructureBlockMirror, + rotation: StructureBlockRotation, + metadata: String, + integrity: f32, // TODO: bounded float between 0 and 1. + seed: VarLong, + flags: StructureBlockFlags, + } + } + + def_enum! { + StructureBlockAction: VarInt { + UpdateData = 0, + SaveStructure = 1, + LoadStructure = 2, + DetectSize = 3, + } + } + + def_enum! { + StructureBlockMode: VarInt { + Save = 0, + Load = 1, + Corner = 2, + Data = 3, + } + } + + def_enum! { + StructureBlockMirror: VarInt { + None = 0, + LeftRight = 1, + FrontBack = 2, + } + } + + def_enum! { + StructureBlockRotation: VarInt { + None = 0, + Clockwise90 = 1, + Clockwise180 = 2, + Counterclockwise90 = 3, + } + } + + def_bitfield! { + StructureBlockFlags: i8 { + ignore_entities = 0, + show_air = 1, + show_bounding_box = 2, + } + } + + def_struct! { + UpdateSign 0x2d { + location: BlockPos, + lines: [BoundedString<0, 384>; 4], + } + } + + def_struct! { + HandSwing 0x2e { + hand: Hand, + } + } + + def_struct! { + SpectatorTeleport 0x2f { + target: Uuid, + } + } + + def_struct! { + PlayerInteractBlock 0x30 { + hand: Hand, + location: BlockPos, + face: BlockFace, + cursor_pos: Vec3, + head_inside_block: bool, + sequence: VarInt, + } + } + + def_struct! { + PlayerInteractItem 0x31 { + hand: Hand, + sequence: VarInt, + } + } + + def_packet_group! { + C2sPlayPacket { + TeleportConfirm, + QueryBlockNbt, + UpdateDifficulty, + CommandExecution, + ChatMessage, + RequestChatPreview, + ClientStatus, + ClientSettings, + RequestCommandCompletion, + ButtonClick, + ClickSlot, + CloseHandledScreen, + CustomPayload, + BookUpdate, + QueryEntityNbt, + PlayerInteractEntity, + JigsawGenerate, + KeepAlive, + UpdateDifficultyLock, + MovePlayerPosition, + MovePlayerPositionAndRotation, + MovePlayerRotation, + MovePlayerOnGround, + MoveVehicle, + BoatPaddleState, + PickFromInventory, + CraftRequest, + UpdatePlayerAbilities, + PlayerAction, + PlayerCommand, + PlayerInput, + PlayPong, + RecipeBookChangeSettings, + RecipeBookSeenRecipe, + RenameItem, + ResourcePackStatus, + AdvancementTab, + SelectMerchantTrade, + UpdateBeacon, + UpdateSelectedSlot, + UpdateCommandBlock, + UpdateCommandBlockMinecart, + UpdateCreativeModeSlot, + UpdateJigsaw, + UpdateStructureBlock, + UpdateSign, + HandSwing, + SpectatorTeleport, + PlayerInteractBlock, + PlayerInteractItem, + } + } +} diff --git a/src/protocol_inner/packets/s2c.rs b/src/protocol_inner/packets/s2c.rs new file mode 100644 index 0000000..a5105b1 --- /dev/null +++ b/src/protocol_inner/packets/s2c.rs @@ -0,0 +1,794 @@ +//! Server to client packets. + +use super::*; + +pub mod status { + use super::*; + + def_struct! { + QueryResponse 0x00 { + json_response: String + } + } + + def_struct! { + QueryPong 0x01 { + /// Should be the same as the payload from ping. + payload: u64 + } + } +} + +pub mod login { + use super::*; + + def_struct! { + LoginDisconnect 0x00 { + reason: Text, + } + } + + def_struct! { + EncryptionRequest 0x01 { + /// Currently unused + server_id: BoundedString<0, 20>, + /// The RSA public key + public_key: Vec, + verify_token: BoundedArray, + } + } + + def_struct! { + LoginSuccess 0x02 { + uuid: Uuid, + username: BoundedString<3, 16>, + properties: Vec, + } + } + + def_struct! { + LoginCompression 0x03 { + threshold: VarInt + } + } + + def_struct! { + LoginPluginRequest 0x04 { + message_id: VarInt, + channel: Ident, + data: RawBytes, + } + } + + def_packet_group! { + S2cLoginPacket { + LoginDisconnect, + EncryptionRequest, + LoginSuccess, + LoginCompression, + LoginPluginRequest, + } + } +} + +pub mod play { + use super::*; + + def_struct! { + EntitySpawn 0x00 { + entity_id: VarInt, + object_uuid: Uuid, + kind: VarInt, + position: Vec3, + pitch: ByteAngle, + yaw: ByteAngle, + head_yaw: ByteAngle, + data: VarInt, + velocity: Vec3, + } + } + + def_struct! { + ExperienceOrbSpawn 0x01 { + entity_id: VarInt, + position: Vec3, + count: i16, + } + } + + def_struct! { + PlayerSpawn 0x02 { + entity_id: VarInt, + player_uuid: Uuid, + position: Vec3, + yaw: ByteAngle, + pitch: ByteAngle, + } + } + + def_struct! { + EntityAnimation 0x03 { + entity_id: VarInt, + animation: u8, + } + } + + def_struct! { + PlayerActionResponse 0x05 { + sequence: VarInt, + } + } + + def_struct! { + BlockBreakingProgress 0x06 { + entity_id: VarInt, + location: BlockPos, + destroy_stage: BoundedInt, + } + } + + def_struct! { + BlockEntityUpdate 0x07 { + location: BlockPos, + kind: VarInt, // TODO: use enum here + data: nbt::Blob, + } + } + + def_struct! { + BlockEvent 0x08 { + location: BlockPos, + action_id: u8, + action_param: u8, + block_type: VarInt, // TODO: use BlockType type. + } + } + + def_struct! { + BlockUpdate 0x09 { + location: BlockPos, + block_id: VarInt, + } + } + + def_struct! { + BossBar 0x0a { + uuid: Uuid, + action: BossBarAction, + } + } + + def_enum! { + BossBarAction: VarInt { + Add: BossBarActionAdd = 0, + // TODO + } + } + + def_struct! { + BossBarActionAdd { + title: Text, + health: f32, + color: BossBarColor, + division: BossBarDivision, + /// TODO: bitmask + flags: u8, + } + } + + def_enum! { + BossBarColor: VarInt { + Pink = 0, + Blue = 1, + Red = 2, + Green = 3, + Yellow = 4, + Purple = 5, + White = 6, + } + } + + def_enum! { + BossBarDivision: VarInt { + NoDivision = 0, + SixNotches = 1, + TenNotches = 2, + TwelveNotches = 3, + TwentyNotches = 4, + } + } + + def_struct! { + SetDifficulty 0x0b { + difficulty: Difficulty, + locked: bool, + } + } + + def_enum! { + Difficulty: u8 { + Peaceful = 0, + Easy = 1, + Normal = 2, + Hard = 3, + } + } + + def_struct! { + ClearTitles 0x0d { + reset: bool, + } + } + + def_struct! { + Disconnect 0x17 { + reason: Text, + } + } + + def_struct! { + EntityStatus 0x18 { + entity_id: i32, + entity_status: u8, + } + } + + def_struct! { + UnloadChunk 0x1a { + chunk_x: i32, + chunk_z: i32 + } + } + + def_struct! { + GameStateChange 0x1b { + reason: GameStateChangeReason, + value: f32, + } + } + + def_enum! { + GameStateChangeReason: u8 { + NoRespawnBlockAvailable = 0, + EndRaining = 1, + BeginRaining = 2, + ChangeGameMode = 3, + WinGame = 4, + DemoEvent = 5, + ArrowHitPlayer = 6, + RainLevelChange = 7, + ThunderLevelChange = 8, + PlayPufferfishStingSound = 9, + PlayElderGuardianMobAppearance = 10, + EnableRespawnScreen = 11, + } + } + + def_struct! { + WorldBorderInitialize 0x1d { + x: f64, + z: f64, + old_diameter: f64, + new_diameter: f64, + speed: VarLong, + portal_teleport_boundary: VarInt, + warning_blocks: VarInt, + warning_time: VarInt, + } + } + + def_struct! { + KeepAlive 0x1e { + id: i64, + } + } + + def_struct! { + ChunkData 0x1f { + chunk_x: i32, + chunk_z: i32, + heightmaps: Nbt, + blocks_and_biomes: Vec, + block_entities: Vec, + trust_edges: bool, + sky_light_mask: BitVec, + block_light_mask: BitVec, + empty_sky_light_mask: BitVec, + empty_block_light_mask: BitVec, + sky_light_arrays: Vec<[u8; 2048]>, + block_light_arrays: Vec<[u8; 2048]>, + } + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct ChunkDataHeightmaps { + #[serde(rename = "MOTION_BLOCKING", serialize_with = "nbt::i64_array")] + pub motion_blocking: Vec, + } + + def_struct! { + ChunkDataBlockEntity { + packed_xz: i8, + y: i16, + kind: VarInt, + data: nbt::Blob, + } + } + + def_struct! { + GameJoin 0x23 { + /// Entity ID of the joining player + entity_id: i32, + is_hardcore: bool, + gamemode: GameMode, + previous_gamemode: GameMode, + dimension_names: Vec, + registry_codec: Nbt, + /// The name of the dimension type being spawned into. + dimension_type_name: Ident, + /// The name of the dimension being spawned into. + dimension_name: Ident, + /// Hash of the world's seed used for client biome noise. + hashed_seed: i64, + /// No longer used by the client. + max_players: VarInt, + view_distance: BoundedInt, + simulation_distance: VarInt, + /// If reduced debug info should be shown on the F3 screen. + reduced_debug_info: bool, + /// If player respawns should be instant or not. + enable_respawn_screen: bool, + is_debug: bool, + /// If this is a superflat world. + /// Superflat worlds have different void fog and horizon levels. + is_flat: bool, + last_death_location: Option<(Ident, BlockPos)>, + } + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct RegistryCodec { + #[serde(rename = "minecraft:dimension_type")] + pub dimension_type_registry: DimensionTypeRegistry, + #[serde(rename = "minecraft:worldgen/biome")] + pub biome_registry: BiomeRegistry, + #[serde(rename = "minecraft:chat_type")] + pub chat_type_registry: ChatTypeRegistry, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct DimensionTypeRegistry { + #[serde(rename = "type")] + pub kind: Ident, + pub value: Vec, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct DimensionTypeRegistryEntry { + pub name: Ident, + pub id: i32, + pub element: DimensionType, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct DimensionType { + pub piglin_safe: bool, + pub has_raids: bool, + pub monster_spawn_light_level: i32, + pub monster_spawn_block_light_limit: i32, + pub natural: bool, + pub ambient_light: f32, + pub fixed_time: Option, + pub infiniburn: String, // TODO: tag type? + pub respawn_anchor_works: bool, + pub has_skylight: bool, + pub bed_works: bool, + pub effects: Ident, + pub min_y: i32, + pub height: i32, + pub logical_height: i32, + pub coordinate_scale: f64, + pub ultrawarm: bool, + pub has_ceiling: bool, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct BiomeRegistry { + #[serde(rename = "type")] + pub kind: Ident, + pub value: Vec, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct Biome { + pub name: Ident, + pub id: i32, + pub element: BiomeProperty, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct BiomeProperty { + pub precipitation: String, + pub depth: f32, + pub temperature: f32, + pub scale: f32, + pub downfall: f32, + pub category: String, + pub temperature_modifier: Option, + pub effects: BiomeEffects, + pub particle: Option, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct BiomeEffects { + pub sky_color: i32, + pub water_fog_color: i32, + pub fog_color: i32, + pub water_color: i32, + pub foliage_color: Option, + pub grass_color: Option, + pub grass_color_modifier: Option, + pub music: Option, + pub ambient_sound: Option, + pub additions_sound: Option, + pub mood_sound: Option, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct BiomeMusic { + pub replace_current_music: bool, + pub sound: Ident, + pub max_delay: i32, + pub min_delay: i32, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct BiomeAdditionsSound { + pub sound: Ident, + pub tick_chance: f64, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct BiomeMoodSound { + pub sound: Ident, + pub tick_delay: i32, + pub offset: f64, + pub block_search_extent: i32, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct BiomeParticle { + pub probability: f32, + pub options: BiomeParticleOptions, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct BiomeParticleOptions { + #[serde(rename = "type")] + pub kind: Ident, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct ChatTypeRegistry { + #[serde(rename = "type")] + pub kind: Ident, + pub value: Vec, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct ChatTypeRegistryEntry { + pub name: Ident, + pub id: i32, + pub element: ChatType, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct ChatType { + pub chat: ChatTypeChat, + pub narration: ChatTypeNarration, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct ChatTypeChat {} + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct ChatTypeNarration { + pub priority: String, + } + + def_enum! { + #[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)] + GameMode: u8 { + #[default] + Survival = 0, + Creative = 1, + Adventure = 2, + Spectator = 3, + } + } + + def_struct! { + MoveRelative 0x26 { + entity_id: VarInt, + delta: Vec3, + on_ground: bool, + } + } + + def_struct! { + RotateAndMoveRelative 0x27 { + entity_id: VarInt, + delta: Vec3, + yaw: ByteAngle, + pitch: ByteAngle, + on_ground: bool, + } + } + + def_struct! { + Rotate 0x28 { + entity_id: VarInt, + yaw: ByteAngle, + pitch: ByteAngle, + on_ground: bool, + } + } + + def_struct! { + ChatMessage 0x30 { + message: Text, + /// Index into the chat type registry + kind: VarInt, + sender: Uuid, + // TODO more fields + } + } + + def_enum! { + UpdatePlayerList 0x34: VarInt { + AddPlayer: Vec = 0, + UpdateGameMode: Vec<(Uuid, GameMode)> = 1, + UpdateLatency: Vec<(Uuid, VarInt)> = 2, + UpdateDisplayName: Vec<(Uuid, Option)> = 3, + RemovePlayer: Vec = 4, + } + } + + def_struct! { + PlayerListAddPlayer { + uuid: Uuid, + username: BoundedString<3, 16>, + properties: Vec, + game_mode: GameMode, + ping: VarInt, + display_name: Option, + sig_data: Option, + } + } + + def_struct! { + PlayerPositionLook 0x36 { + position: Vec3, + yaw: f32, + pitch: f32, + flags: PlayerPositionLookFlags, + teleport_id: VarInt, + dismount_vehicle: bool, + } + } + + def_bitfield! { + PlayerPositionLookFlags: u8 { + x = 0, + y = 1, + z = 2, + y_rot = 3, + x_rot = 4, + } + } + + def_struct! { + EntitiesDestroy 0x38 { + entities: Vec, + } + } + + def_struct! { + PlayerRespawn 0x3b { + dimension_type_name: Ident, + dimension_name: Ident, + hashed_seed: u64, + game_mode: GameMode, + previous_game_mode: GameMode, + is_debug: bool, + is_flat: bool, + copy_metadata: bool, + last_death_location: Option<(Ident, BlockPos)>, + } + } + + def_struct! { + EntitySetHeadYaw 0x3c { + entity_id: VarInt, + head_yaw: ByteAngle, + } + } + + def_struct! { + ChunkSectionUpdate 0x3d { + chunk_section_position: i64, + invert_trust_edges: bool, + blocks: Vec, + } + } + + def_struct! { + UpdateSelectedSlot 0x47 { + slot: BoundedInt, + } + } + + def_struct! { + ChunkRenderDistanceCenter 0x48 { + chunk_x: VarInt, + chunk_z: VarInt, + } + } + + def_struct! { + ChunkLoadDistance 0x49 { + view_distance: BoundedInt, + } + } + + def_struct! { + PlayerSpawnPosition 0x4a { + location: BlockPos, + angle: f32, + } + } + + def_struct! { + EntityTrackerUpdate 0x4d { + entity_id: VarInt, + metadata: RawBytes, + } + } + + def_struct! { + EntityVelocityUpdate 0x4f { + entity_id: VarInt, + velocity: Vec3, + } + } + + def_struct! { + UpdateSubtitle 0x58 { + subtitle_text: Text, + } + } + + def_struct! { + WorldTimeUpdate 0x59 { + /// The age of the world in 1/20ths of a second. + world_age: i64, + /// The current time of day in 1/20ths of a second. + /// The value should be in the range \[0, 24000]. + /// 6000 is noon, 12000 is sunset, and 18000 is midnight. + time_of_day: i64, + } + } + + def_struct! { + UpdateTitle 0x5a { + text: Text, + } + } + + def_struct! { + #[derive(Copy, PartialEq, Eq)] + TitleAnimationTimes 0x5b { + /// Ticks to spend fading in. + fade_in: u32, + /// Ticks to keep the title displayed. + stay: u32, + /// Ticks to spend fading out. + fade_out: u32, + } + } + + def_struct! { + GameMessage 0x5f { + chat: Text, + /// Index into the chat type registry. + kind: VarInt, + } + } + + def_struct! { + PlayerListHeaderFooter 0x60 { + header: Text, + footer: Text, + } + } + + def_struct! { + EntityPosition 0x63 { + entity_id: VarInt, + position: Vec3, + yaw: ByteAngle, + pitch: ByteAngle, + on_ground: bool, + } + } + + def_struct! { + EntityAttributes 0x65 { + entity_id: VarInt, + properties: Vec, + } + } + + def_struct! { + EntityAttributesProperty { + key: Ident, + value: f64, + modifiers: Vec + } + } + + def_struct! { + EntityAttributesModifiers { + uuid: Uuid, + amount: f64, + operation: u8, + } + } + + def_packet_group! { + S2cPlayPacket { + EntitySpawn, + ExperienceOrbSpawn, + PlayerSpawn, + EntityAnimation, + PlayerActionResponse, + BlockBreakingProgress, + BlockEntityUpdate, + BlockEvent, + BlockUpdate, + BossBar, + ClearTitles, + Disconnect, + EntityStatus, + UnloadChunk, + GameStateChange, + KeepAlive, + ChunkData, + GameJoin, + MoveRelative, + RotateAndMoveRelative, + Rotate, + ChatMessage, + UpdatePlayerList, + PlayerPositionLook, + EntitiesDestroy, + PlayerRespawn, + EntitySetHeadYaw, + ChunkSectionUpdate, + UpdateSelectedSlot, + ChunkRenderDistanceCenter, + ChunkLoadDistance, + PlayerSpawnPosition, + EntityTrackerUpdate, + EntityVelocityUpdate, + UpdateSubtitle, + WorldTimeUpdate, + UpdateTitle, + TitleAnimationTimes, + GameMessage, + PlayerListHeaderFooter, + EntityPosition, + EntityAttributes, + } + } +} diff --git a/src/server.rs b/src/server.rs index 4e952bd..1440a8c 100644 --- a/src/server.rs +++ b/src/server.rs @@ -34,16 +34,16 @@ use crate::dimension::{Dimension, DimensionId}; use crate::entity::Entities; use crate::player_textures::SignedPlayerTextures; use crate::protocol_inner::codec::{Decoder, Encoder}; -use crate::protocol_inner::packets::handshake::{Handshake, HandshakeNextState}; -use crate::protocol_inner::packets::login::c2s::{ +use crate::protocol_inner::packets::c2s::handshake::{Handshake, HandshakeNextState}; +use crate::protocol_inner::packets::c2s::login::{ EncryptionResponse, LoginStart, VerifyTokenOrMsgSig, }; -use crate::protocol_inner::packets::login::s2c::{EncryptionRequest, LoginSuccess, LoginCompression}; -use crate::protocol_inner::packets::play::c2s::C2sPlayPacket; -use crate::protocol_inner::packets::play::s2c::S2cPlayPacket; -use crate::protocol_inner::packets::status::c2s::{QueryPing, QueryRequest}; -use crate::protocol_inner::packets::status::s2c::{QueryPong, QueryResponse}; -use crate::protocol_inner::packets::{login, Property}; +use crate::protocol_inner::packets::c2s::play::C2sPlayPacket; +use crate::protocol_inner::packets::c2s::status::{QueryPing, QueryRequest}; +use crate::protocol_inner::packets::s2c::login::{EncryptionRequest, LoginCompression, LoginDisconnect, LoginSuccess}; +use crate::protocol_inner::packets::s2c::play::S2cPlayPacket; +use crate::protocol_inner::packets::s2c::status::{QueryPong, QueryResponse}; +use crate::protocol_inner::packets::Property; use crate::protocol_inner::{BoundedArray, BoundedString, VarInt}; use crate::util::valid_username; use crate::world::Worlds; @@ -723,7 +723,7 @@ async fn handle_login( if let Err(reason) = server.0.cfg.login(server, &npd).await { log::info!("Disconnect at login: \"{reason}\""); c.enc - .write_packet(&login::s2c::LoginDisconnect { reason }) + .write_packet(&LoginDisconnect { reason }) .await?; return Ok(None); }