Merge b43395d511
into ecf829c544
This commit is contained in:
commit
f0a9012d0e
|
@ -39,9 +39,9 @@ use std::io::{Read, Write};
|
||||||
use std::net::TcpStream;
|
use std::net::TcpStream;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
|
||||||
|
|
||||||
pub const SUPPORTED_PROTOCOLS: [i32; 27] = [
|
pub const SUPPORTED_PROTOCOLS: [i32; 28] = [
|
||||||
758, 757, 756, 754, 753, 751, 736, 735, 578, 575, 498, 490, 485, 480, 477, 452, 451, 404, 340,
|
759, 758, 757, 756, 754, 753, 751, 736, 735, 578, 575, 498, 490, 485, 480, 477, 452, 451, 404,
|
||||||
316, 315, 210, 109, 107, 74, 47, 5,
|
340, 316, 315, 210, 109, 107, 74, 47, 5,
|
||||||
];
|
];
|
||||||
|
|
||||||
static CURRENT_PROTOCOL_VERSION: AtomicI32 = AtomicI32::new(SUPPORTED_PROTOCOLS[0]);
|
static CURRENT_PROTOCOL_VERSION: AtomicI32 = AtomicI32::new(SUPPORTED_PROTOCOLS[0]);
|
||||||
|
|
|
@ -911,6 +911,13 @@ state_packets!(
|
||||||
field nodes: LenPrefixed<VarInt, packet::CommandNode> =,
|
field nodes: LenPrefixed<VarInt, packet::CommandNode> =,
|
||||||
field root_index: VarInt =,
|
field root_index: VarInt =,
|
||||||
}
|
}
|
||||||
|
/// SystemChatMessage is sent by the server for system messages, the system/chat
|
||||||
|
/// distinction exists to respect the user's chat visibility settings.
|
||||||
|
packet SystemChatMessage {
|
||||||
|
field message: format::Component =,
|
||||||
|
/// Whether the message is an actionbar or chat message
|
||||||
|
field overlay: bool =,
|
||||||
|
}
|
||||||
/// ServerMessage is a message sent by the server. It could be from a player
|
/// ServerMessage is a message sent by the server. It could be from a player
|
||||||
/// or just a system message. The Type field controls the location the
|
/// or just a system message. The Type field controls the location the
|
||||||
/// message is displayed at and when the message is displayed.
|
/// message is displayed at and when the message is displayed.
|
||||||
|
@ -1344,6 +1351,48 @@ state_packets!(
|
||||||
}
|
}
|
||||||
/// JoinGame is sent after completing the login process. This
|
/// JoinGame is sent after completing the login process. This
|
||||||
/// sets the initial state for the client.
|
/// sets the initial state for the client.
|
||||||
|
packet JoinGame_WorldNames_IsHard_SimDist_HasDeath {
|
||||||
|
/// The entity id the client will be referenced by
|
||||||
|
field entity_id: i32 =,
|
||||||
|
/// Whether hardcore mode is enabled
|
||||||
|
field is_hardcore: bool =,
|
||||||
|
/// The starting gamemode of the client
|
||||||
|
field gamemode: u8 =,
|
||||||
|
/// The previous gamemode of the client
|
||||||
|
field previous_gamemode: u8 =,
|
||||||
|
/// Identifiers for all worlds on the server
|
||||||
|
field world_names: LenPrefixed<VarInt, String> =,
|
||||||
|
/// Represents a dimension registry
|
||||||
|
field registry_codec: Option<nbt::NamedTag> =,
|
||||||
|
/// Name of the dimension type being spawned into
|
||||||
|
field dimension_type: String =,
|
||||||
|
/// The world being spawned into
|
||||||
|
field world_name: String =,
|
||||||
|
/// Truncated SHA-256 hash of world's seed
|
||||||
|
field hashed_seed: i64 =,
|
||||||
|
/// The max number of players on the server
|
||||||
|
field max_players: VarInt =,
|
||||||
|
/// The render distance (2-32)
|
||||||
|
field view_distance: VarInt =,
|
||||||
|
/// The distance the client will process entities
|
||||||
|
field simulation_distance: VarInt =,
|
||||||
|
/// Whether the client should reduce the amount of debug
|
||||||
|
/// information it displays in F3 mode
|
||||||
|
field reduced_debug_info: bool =,
|
||||||
|
/// Whether to prompt or immediately respawn
|
||||||
|
field enable_respawn_screen: bool =,
|
||||||
|
/// Whether the world is in debug mode
|
||||||
|
field is_debug: bool =,
|
||||||
|
/// Whether the world is a superflat world
|
||||||
|
field is_flat: bool =,
|
||||||
|
/// If true, then the next two fields are present.
|
||||||
|
field has_death_location: bool =,
|
||||||
|
/// Name of the dimension the player died in
|
||||||
|
field death_dimension_name: String = when(|p: &JoinGame_WorldNames_IsHard_SimDist_HasDeath| p.has_death_location),
|
||||||
|
/// The location that the player died at
|
||||||
|
field death_location: Position = when(|p: &JoinGame_WorldNames_IsHard_SimDist_HasDeath| p.has_death_location),
|
||||||
|
}
|
||||||
|
|
||||||
packet JoinGame_WorldNames_IsHard_SimDist {
|
packet JoinGame_WorldNames_IsHard_SimDist {
|
||||||
/// The entity id the client will be referenced by
|
/// The entity id the client will be referenced by
|
||||||
field entity_id: i32 =,
|
field entity_id: i32 =,
|
||||||
|
@ -1878,6 +1927,13 @@ state_packets!(
|
||||||
field has_id: bool =,
|
field has_id: bool =,
|
||||||
field tab_id: String = when(|p: &SelectAdvancementTab| p.has_id),
|
field tab_id: String = when(|p: &SelectAdvancementTab| p.has_id),
|
||||||
}
|
}
|
||||||
|
packet ServerData {
|
||||||
|
field has_motd: bool =,
|
||||||
|
field motd: Option<format::Component> = when(|p: &ServerData| p.has_motd),
|
||||||
|
field has_icon: bool =,
|
||||||
|
field icon: Option<String> = when(|p: &ServerData| p.has_icon),
|
||||||
|
field previews_chat: bool =,
|
||||||
|
}
|
||||||
packet ActionBar {
|
packet ActionBar {
|
||||||
field text: String =,
|
field text: String =,
|
||||||
}
|
}
|
||||||
|
@ -2359,15 +2415,34 @@ state_packets!(
|
||||||
}
|
}
|
||||||
login Login {
|
login Login {
|
||||||
serverbound Serverbound {
|
serverbound Serverbound {
|
||||||
/// LoginStart is sent immeditately after switching into the login
|
/// LoginStart is sent immediately after switching into the login
|
||||||
/// state. The passed username is used by the server to authenticate
|
/// state. The passed username is used by the server to authenticate
|
||||||
/// the player in online mode.
|
/// the player in online mode.
|
||||||
|
packet LoginStart_Sig {
|
||||||
|
field username: String =,
|
||||||
|
field has_sign_data: bool =,
|
||||||
|
field timestamp: Option<u64> = when(|p: &LoginStart_Sig| p.has_sign_data),
|
||||||
|
field public_key: Option<LenPrefixedBytes<VarInt>> = when(|p: &LoginStart_Sig| p.has_sign_data),
|
||||||
|
field signature: Option<LenPrefixedBytes<VarInt>> = when(|p: &LoginStart_Sig| p.has_sign_data),
|
||||||
|
}
|
||||||
packet LoginStart {
|
packet LoginStart {
|
||||||
field username: String =,
|
field username: String =,
|
||||||
}
|
}
|
||||||
/// EncryptionResponse is sent as a reply to EncryptionRequest. All
|
/// EncryptionResponse is sent as a reply to EncryptionRequest. All
|
||||||
/// packets following this one must be encrypted with AES/CFB8
|
/// packets following this one must be encrypted with AES/CFB8
|
||||||
/// encryption.
|
/// encryption.
|
||||||
|
packet EncryptionResponse_Sig {
|
||||||
|
/// The key for the AES/CFB8 cipher encrypted with the
|
||||||
|
/// public key
|
||||||
|
field shared_secret: LenPrefixedBytes<VarInt> =,
|
||||||
|
/// Whether or not the Verify Token should be sent. If not, then the salt and signature will be sent.
|
||||||
|
field has_verify_token: bool =,
|
||||||
|
/// The verify token from the request encrypted with the
|
||||||
|
/// public key
|
||||||
|
field verify_token: Option<LenPrefixedBytes<VarInt>> = when(|p: &EncryptionResponse_Sig| p.has_verify_token),
|
||||||
|
field salt: Option<u64> = when(|p: &EncryptionResponse_Sig| !p.has_verify_token),
|
||||||
|
field signature: Option<LenPrefixedBytes<VarInt>> = when(|p: &EncryptionResponse_Sig| !p.has_verify_token),
|
||||||
|
}
|
||||||
packet EncryptionResponse {
|
packet EncryptionResponse {
|
||||||
/// The key for the AES/CFB8 cipher encrypted with the
|
/// The key for the AES/CFB8 cipher encrypted with the
|
||||||
/// public key
|
/// public key
|
||||||
|
@ -2414,6 +2489,11 @@ state_packets!(
|
||||||
/// LoginSuccess is sent by the server if the player successfully
|
/// LoginSuccess is sent by the server if the player successfully
|
||||||
/// authenicates with the session servers (online mode) or straight
|
/// authenicates with the session servers (online mode) or straight
|
||||||
/// after LoginStart (offline mode).
|
/// after LoginStart (offline mode).
|
||||||
|
packet LoginSuccess_Sig {
|
||||||
|
field uuid: UUID =,
|
||||||
|
field username: String =,
|
||||||
|
field properties: LenPrefixed<VarInt, packet::LoginProperty> =,
|
||||||
|
}
|
||||||
packet LoginSuccess_String {
|
packet LoginSuccess_String {
|
||||||
/// String encoding of a uuid (with hyphens)
|
/// String encoding of a uuid (with hyphens)
|
||||||
field uuid: String =,
|
field uuid: String =,
|
||||||
|
@ -2491,6 +2571,42 @@ state_packets!(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||||
|
pub struct LoginProperty {
|
||||||
|
pub name: String,
|
||||||
|
pub value: String,
|
||||||
|
pub is_signed: bool,
|
||||||
|
pub signature: Option<String>,
|
||||||
|
}
|
||||||
|
impl Serializable for LoginProperty {
|
||||||
|
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
|
||||||
|
let name: String = Serializable::read_from(buf)?;
|
||||||
|
let value: String = Serializable::read_from(buf)?;
|
||||||
|
let is_signed: bool = Serializable::read_from(buf)?;
|
||||||
|
let signature: Option<String> = if is_signed {
|
||||||
|
Some(Serializable::read_from(buf)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(LoginProperty {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
is_signed,
|
||||||
|
signature,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), Error> {
|
||||||
|
self.name.write_to(buf)?;
|
||||||
|
self.value.write_to(buf)?;
|
||||||
|
if self.is_signed {
|
||||||
|
self.signature.as_ref().unwrap().write_to(buf)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||||
pub struct SpawnProperty {
|
pub struct SpawnProperty {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -2969,19 +3085,37 @@ impl Serializable for PlayerInfoData {
|
||||||
}
|
}
|
||||||
props.push(prop);
|
props.push(prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let gamemode: VarInt = Serializable::read_from(buf)?;
|
||||||
|
let ping: VarInt = Serializable::read_from(buf)?;
|
||||||
|
let display: Option<format::Component> = if bool::read_from(buf)? {
|
||||||
|
Some(Serializable::read_from(buf)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let mut timestamp: Option<u64> = None;
|
||||||
|
let mut public_key: Option<LenPrefixedBytes<VarInt>> = None;
|
||||||
|
let mut signature: Option<LenPrefixedBytes<VarInt>> = None;
|
||||||
|
|
||||||
|
if super::current_protocol_version() >= 759 {
|
||||||
|
let has_sig_data: bool = Serializable::read_from(buf)?;
|
||||||
|
if has_sig_data {
|
||||||
|
timestamp = Some(Serializable::read_from(buf)?);
|
||||||
|
public_key = Some(Serializable::read_from(buf)?);
|
||||||
|
signature = Some(Serializable::read_from(buf)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let p = PlayerDetail::Add {
|
let p = PlayerDetail::Add {
|
||||||
uuid,
|
uuid,
|
||||||
name,
|
name,
|
||||||
properties: props,
|
properties: props,
|
||||||
gamemode: Serializable::read_from(buf)?,
|
gamemode,
|
||||||
ping: Serializable::read_from(buf)?,
|
ping,
|
||||||
display: {
|
display,
|
||||||
if bool::read_from(buf)? {
|
timestamp,
|
||||||
Some(Serializable::read_from(buf)?)
|
public_key,
|
||||||
} else {
|
signature,
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
m.players.push(p);
|
m.players.push(p);
|
||||||
}
|
}
|
||||||
|
@ -3033,6 +3167,9 @@ pub enum PlayerDetail {
|
||||||
gamemode: VarInt,
|
gamemode: VarInt,
|
||||||
ping: VarInt,
|
ping: VarInt,
|
||||||
display: Option<format::Component>,
|
display: Option<format::Component>,
|
||||||
|
timestamp: Option<u64>,
|
||||||
|
public_key: Option<LenPrefixedBytes<VarInt>>,
|
||||||
|
signature: Option<LenPrefixedBytes<VarInt>>,
|
||||||
},
|
},
|
||||||
UpdateGamemode {
|
UpdateGamemode {
|
||||||
uuid: UUID,
|
uuid: UUID,
|
||||||
|
@ -3423,6 +3560,12 @@ pub enum CommandProperty {
|
||||||
EntitySummon,
|
EntitySummon,
|
||||||
Dimension,
|
Dimension,
|
||||||
UUID,
|
UUID,
|
||||||
|
ResourceOrTag {
|
||||||
|
registry: String,
|
||||||
|
},
|
||||||
|
Resource {
|
||||||
|
registry: String,
|
||||||
|
},
|
||||||
ForgeModId,
|
ForgeModId,
|
||||||
ForgeEnum {
|
ForgeEnum {
|
||||||
cls: String,
|
cls: String,
|
||||||
|
@ -3456,8 +3599,75 @@ impl Serializable for CommandNode {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let parser: Option<String> = if node_type == CommandNodeType::Argument {
|
let parser: Option<String> = if node_type == CommandNodeType::Argument {
|
||||||
Serializable::read_from(buf)?
|
if super::current_protocol_version() >= 759 {
|
||||||
|
let parser_id: VarInt = Serializable::read_from(buf)?;
|
||||||
|
// https://wiki.vg/Command_Data#Parsers
|
||||||
|
// "In 1.19, Parsers are identified by a varint id.
|
||||||
|
// Pre-1.19, parsers are identified by a string id."
|
||||||
|
// Map ID to string
|
||||||
|
let parsers = [
|
||||||
|
"brigadier:bool",
|
||||||
|
"brigadier:float",
|
||||||
|
"brigadier:double",
|
||||||
|
"brigadier:integer",
|
||||||
|
"brigadier:long",
|
||||||
|
"brigadier:string",
|
||||||
|
"minecraft:entity",
|
||||||
|
"minecraft:game_profile",
|
||||||
|
"minecraft:block_pos",
|
||||||
|
"minecraft:column_pos",
|
||||||
|
"minecraft:vec3",
|
||||||
|
"minecraft:vec2",
|
||||||
|
"minecraft:block_state",
|
||||||
|
"minecraft:block_predicate",
|
||||||
|
"minecraft:item_stack",
|
||||||
|
"minecraft:item_predicate",
|
||||||
|
"minecraft:color",
|
||||||
|
"minecraft:component",
|
||||||
|
"minecraft:message",
|
||||||
|
"minecraft:nbt",
|
||||||
|
"minecraft:nbt_tag",
|
||||||
|
"minecraft:nbt_path",
|
||||||
|
"minecraft:objective",
|
||||||
|
"minecraft:objective_criteria",
|
||||||
|
"minecraft:operation",
|
||||||
|
"minecraft:particle",
|
||||||
|
"minecraft:angle",
|
||||||
|
"minecraft:rotation",
|
||||||
|
"minecraft:scoreboard_slot",
|
||||||
|
"minecraft:score_holder",
|
||||||
|
"minecraft:swizzle",
|
||||||
|
"minecraft:team",
|
||||||
|
"minecraft:item_slot",
|
||||||
|
"minecraft:resource_location",
|
||||||
|
"minecraft:mob_effect",
|
||||||
|
"minecraft:function",
|
||||||
|
"minecraft:entity_anchor",
|
||||||
|
"minecraft:int_range",
|
||||||
|
"minecraft:float_range",
|
||||||
|
"minecraft:item_enchantment",
|
||||||
|
"minecraft:entity_summon",
|
||||||
|
"minecraft:dimension",
|
||||||
|
"minecraft:time",
|
||||||
|
"minecraft:resource_or_tag",
|
||||||
|
"minecraft:resource",
|
||||||
|
"(added in 1.19) Template mirror",
|
||||||
|
"(added in 1.19) Template rotation",
|
||||||
|
"minecraft:uuid",
|
||||||
|
];
|
||||||
|
if parser_id.0 < 0 || parser_id.0 > parsers.len().try_into().unwrap() {
|
||||||
|
panic!(
|
||||||
|
"command node identifier out of range {} > {}",
|
||||||
|
parser_id.0,
|
||||||
|
parsers.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(parsers[parser_id.0 as usize].to_string())
|
||||||
|
} else {
|
||||||
|
Serializable::read_from(buf)?
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -3556,11 +3766,17 @@ impl Serializable for CommandNode {
|
||||||
"minecraft:entity_summon" => CommandProperty::EntitySummon,
|
"minecraft:entity_summon" => CommandProperty::EntitySummon,
|
||||||
"minecraft:dimension" => CommandProperty::Dimension,
|
"minecraft:dimension" => CommandProperty::Dimension,
|
||||||
"minecraft:uuid" => CommandProperty::UUID,
|
"minecraft:uuid" => CommandProperty::UUID,
|
||||||
|
"minecraft:resource_or_tag" => CommandProperty::ResourceOrTag {
|
||||||
|
registry: Serializable::read_from(buf)?,
|
||||||
|
},
|
||||||
|
"minecraft:resource" => CommandProperty::Resource {
|
||||||
|
registry: Serializable::read_from(buf)?,
|
||||||
|
},
|
||||||
"forge:modid" => CommandProperty::ForgeModId,
|
"forge:modid" => CommandProperty::ForgeModId,
|
||||||
"forge:enum" => CommandProperty::ForgeEnum {
|
"forge:enum" => CommandProperty::ForgeEnum {
|
||||||
cls: Serializable::read_from(buf)?,
|
cls: Serializable::read_from(buf)?,
|
||||||
},
|
},
|
||||||
_ => panic!("unsupported command node parser {}", parse),
|
_ => panic!("unsupported command node parser {:?}", parse),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -18,6 +18,7 @@ mod v1_16_4;
|
||||||
mod v1_17_1;
|
mod v1_17_1;
|
||||||
mod v1_18_1;
|
mod v1_18_1;
|
||||||
mod v1_18_2;
|
mod v1_18_2;
|
||||||
|
mod v1_19;
|
||||||
mod v1_7_10;
|
mod v1_7_10;
|
||||||
mod v1_8_9;
|
mod v1_8_9;
|
||||||
mod v1_9;
|
mod v1_9;
|
||||||
|
@ -29,6 +30,7 @@ mod v1_9_2;
|
||||||
pub fn protocol_name_to_protocol_version(s: String) -> i32 {
|
pub fn protocol_name_to_protocol_version(s: String) -> i32 {
|
||||||
match s.as_ref() {
|
match s.as_ref() {
|
||||||
"" => SUPPORTED_PROTOCOLS[0],
|
"" => SUPPORTED_PROTOCOLS[0],
|
||||||
|
"1.19" => 759,
|
||||||
"1.18.2" => 758,
|
"1.18.2" => 758,
|
||||||
"1.18.1" => 757,
|
"1.18.1" => 757,
|
||||||
"1.17.1" => 756,
|
"1.17.1" => 756,
|
||||||
|
@ -75,6 +77,7 @@ pub fn translate_internal_packet_id_for_version(
|
||||||
to_internal: bool,
|
to_internal: bool,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
match version {
|
match version {
|
||||||
|
759 => v1_19::translate_internal_packet_id(state, dir, id, to_internal),
|
||||||
758 => v1_18_2::translate_internal_packet_id(state, dir, id, to_internal),
|
758 => v1_18_2::translate_internal_packet_id(state, dir, id, to_internal),
|
||||||
757 => v1_18_1::translate_internal_packet_id(state, dir, id, to_internal),
|
757 => v1_18_1::translate_internal_packet_id(state, dir, id, to_internal),
|
||||||
756 => v1_17_1::translate_internal_packet_id(state, dir, id, to_internal),
|
756 => v1_17_1::translate_internal_packet_id(state, dir, id, to_internal),
|
||||||
|
|
|
@ -0,0 +1,194 @@
|
||||||
|
protocol_packet_ids!(
|
||||||
|
handshake Handshaking {
|
||||||
|
serverbound Serverbound {
|
||||||
|
0x00 => Handshake
|
||||||
|
}
|
||||||
|
clientbound Clientbound {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
play Play {
|
||||||
|
serverbound Serverbound {
|
||||||
|
0x00 => TeleportConfirm
|
||||||
|
0x01 => QueryBlockNBT
|
||||||
|
0x02 => SetDifficulty
|
||||||
|
//TODO 0x03 => ChatCommand
|
||||||
|
0x04 => ChatMessage
|
||||||
|
//TODO 0x05 => ChatPreview
|
||||||
|
//TODO 0x06 => ClientCommand
|
||||||
|
0x07 => ClientSettings_Filtering
|
||||||
|
0x08 => TabComplete
|
||||||
|
0x09 => ClickWindowButton
|
||||||
|
0x0a => ClickWindow_State
|
||||||
|
0x0b => CloseWindow
|
||||||
|
0x0c => PluginMessageServerbound
|
||||||
|
0x0d => EditBook_Pages
|
||||||
|
0x0e => QueryEntityNBT
|
||||||
|
0x0f => UseEntity_Sneakflag
|
||||||
|
0x10 => GenerateStructure
|
||||||
|
0x11 => KeepAliveServerbound_i64
|
||||||
|
0x12 => LockDifficulty
|
||||||
|
0x13 => PlayerPosition
|
||||||
|
0x14 => PlayerPositionLook
|
||||||
|
0x15 => PlayerLook
|
||||||
|
0x16 => Player
|
||||||
|
0x17 => VehicleMove
|
||||||
|
0x18 => SteerBoat
|
||||||
|
0x19 => PickItem
|
||||||
|
0x1a => CraftRecipeRequest
|
||||||
|
0x1b => ClientAbilities_u8
|
||||||
|
0x1c => PlayerDigging
|
||||||
|
0x1d => PlayerAction
|
||||||
|
0x1e => SteerVehicle
|
||||||
|
0x1f => WindowPong
|
||||||
|
0x20 => SetDisplayedRecipe
|
||||||
|
0x21 => SetRecipeBookState
|
||||||
|
0x22 => NameItem
|
||||||
|
0x23 => ResourcePackStatus
|
||||||
|
0x24 => AdvancementTab
|
||||||
|
0x25 => SelectTrade
|
||||||
|
0x26 => SetBeaconEffect
|
||||||
|
0x27 => HeldItemChange
|
||||||
|
0x28 => UpdateCommandBlock
|
||||||
|
0x29 => UpdateCommandBlockMinecart
|
||||||
|
0x2a => CreativeInventoryAction
|
||||||
|
0x2b => UpdateJigsawBlock_Joint
|
||||||
|
0x2c => UpdateStructureBlock
|
||||||
|
0x2d => SetSign
|
||||||
|
0x2e => ArmSwing
|
||||||
|
0x2f => SpectateTeleport
|
||||||
|
0x30 => PlayerBlockPlacement_insideblock
|
||||||
|
0x31 => UseItem
|
||||||
|
}
|
||||||
|
clientbound Clientbound {
|
||||||
|
0x00 => SpawnObject_VarInt
|
||||||
|
0x01 => SpawnExperienceOrb
|
||||||
|
0x02 => SpawnPlayer_f64_NoMeta
|
||||||
|
0x03 => Animation
|
||||||
|
0x04 => Statistics
|
||||||
|
0x05 => AcknowledgePlayerDigging // TODO: Acknowledge Block Change
|
||||||
|
0x06 => BlockBreakAnimation
|
||||||
|
0x07 => UpdateBlockEntity_VarInt
|
||||||
|
0x08 => BlockAction
|
||||||
|
0x09 => BlockChange_VarInt
|
||||||
|
0x0a => BossBar
|
||||||
|
0x0b => ServerDifficulty_Locked
|
||||||
|
0x0c => ServerMessage_Sender // TODO: Chat Preview
|
||||||
|
0x0d => ClearTitles
|
||||||
|
0x0e => TabCompleteReply
|
||||||
|
0x0f => DeclareCommands
|
||||||
|
0x10 => WindowClose
|
||||||
|
0x11 => WindowItems_StateCarry
|
||||||
|
0x12 => WindowProperty
|
||||||
|
0x13 => WindowSetSlot_State
|
||||||
|
0x14 => SetCooldown
|
||||||
|
0x15 => PluginMessageClientbound
|
||||||
|
0x16 => NamedSoundEffect
|
||||||
|
0x17 => Disconnect
|
||||||
|
0x18 => EntityAction
|
||||||
|
0x19 => Explosion_VarInt
|
||||||
|
0x1a => ChunkUnload
|
||||||
|
0x1b => ChangeGameState
|
||||||
|
0x1c => WindowOpenHorse
|
||||||
|
0x1d => WorldBorderInit
|
||||||
|
0x1e => KeepAliveClientbound_i64
|
||||||
|
0x1f => ChunkData_AndLight
|
||||||
|
0x20 => Effect
|
||||||
|
0x21 => Particle_f64
|
||||||
|
0x22 => UpdateLight_Arrays
|
||||||
|
0x23 => JoinGame_WorldNames_IsHard_SimDist_HasDeath
|
||||||
|
0x24 => Maps
|
||||||
|
0x25 => TradeList_WithRestock
|
||||||
|
0x26 => EntityMove_i16
|
||||||
|
0x27 => EntityLookAndMove_i16
|
||||||
|
0x28 => EntityLook_VarInt
|
||||||
|
0x29 => VehicleTeleport
|
||||||
|
0x2a => OpenBook
|
||||||
|
0x2b => WindowOpen_VarInt
|
||||||
|
0x2c => SignEditorOpen
|
||||||
|
0x2d => WindowPing
|
||||||
|
0x2e => CraftRecipeResponse
|
||||||
|
0x2f => PlayerAbilities
|
||||||
|
//0x30 => PlayerChatMessage // TODO
|
||||||
|
0x31 => CombatEventEnd
|
||||||
|
0x32 => CombatEventEnter
|
||||||
|
0x33 => CombatEventDeath
|
||||||
|
0x34 => PlayerInfo
|
||||||
|
0x35 => FacePlayer
|
||||||
|
0x36 => TeleportPlayer_WithDismount
|
||||||
|
0x37 => UnlockRecipes_WithBlastSmoker
|
||||||
|
0x38 => EntityDestroy
|
||||||
|
0x39 => EntityRemoveEffect_VarInt
|
||||||
|
0x3a => ResourcePackSend_Prompt
|
||||||
|
0x3b => Respawn_NBT
|
||||||
|
0x3c => EntityHeadLook
|
||||||
|
0x3d => MultiBlockChange_Packed
|
||||||
|
0x3e => SelectAdvancementTab
|
||||||
|
0x3f => ServerData
|
||||||
|
0x40 => ActionBar
|
||||||
|
0x41 => WorldBorderCenter
|
||||||
|
0x42 => WorldBorderLerpSize
|
||||||
|
0x43 => WorldBorderSize
|
||||||
|
0x44 => WorldBorderWarningDelay
|
||||||
|
0x45 => WorldBorderWarningReach
|
||||||
|
0x46 => Camera
|
||||||
|
0x47 => SetCurrentHotbarSlot
|
||||||
|
0x48 => UpdateViewPosition
|
||||||
|
0x49 => UpdateViewDistance
|
||||||
|
0x4a => SpawnPosition_Angle
|
||||||
|
//0x4b => SetDisplayChatPreview // TODO
|
||||||
|
0x4c => ScoreboardDisplay
|
||||||
|
0x4d => EntityMetadata
|
||||||
|
0x4e => EntityAttach
|
||||||
|
0x4f => EntityVelocity
|
||||||
|
0x50 => EntityEquipment_Array
|
||||||
|
0x51 => SetExperience
|
||||||
|
0x52 => UpdateHealth
|
||||||
|
0x53 => ScoreboardObjective
|
||||||
|
0x54 => SetPassengers
|
||||||
|
0x55 => Teams_VarInt
|
||||||
|
0x56 => UpdateScore_VarInt
|
||||||
|
0x57 => UpdateSimulationDistance
|
||||||
|
0x58 => TitleSubtitle
|
||||||
|
0x59 => TimeUpdate
|
||||||
|
0x5a => Title
|
||||||
|
0x5b => TitleTimes
|
||||||
|
0x5c => EntitySoundEffect
|
||||||
|
0x5d => SoundEffect
|
||||||
|
0x5e => StopSound
|
||||||
|
0x5f => SystemChatMessage
|
||||||
|
0x60 => PlayerListHeaderFooter
|
||||||
|
0x61 => NBTQueryResponse
|
||||||
|
0x62 => CollectItem
|
||||||
|
0x63 => EntityTeleport_f64
|
||||||
|
0x64 => Advancements
|
||||||
|
0x65 => EntityProperties_VarIntVarInt
|
||||||
|
0x66 => EntityEffect_VarInt
|
||||||
|
0x67 => DeclareRecipes
|
||||||
|
0x68 => Tags_Nested
|
||||||
|
}
|
||||||
|
}
|
||||||
|
login Login {
|
||||||
|
serverbound Serverbound {
|
||||||
|
0x00 => LoginStart_Sig
|
||||||
|
0x01 => EncryptionResponse_Sig
|
||||||
|
0x02 => LoginPluginResponse
|
||||||
|
}
|
||||||
|
clientbound Clientbound {
|
||||||
|
0x00 => LoginDisconnect
|
||||||
|
0x01 => EncryptionRequest
|
||||||
|
0x02 => LoginSuccess_Sig
|
||||||
|
0x03 => SetInitialCompression
|
||||||
|
0x04 => LoginPluginRequest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
status Status {
|
||||||
|
serverbound Serverbound {
|
||||||
|
0x00 => StatusRequest
|
||||||
|
0x01 => StatusPing
|
||||||
|
}
|
||||||
|
clientbound Clientbound {
|
||||||
|
0x00 => StatusResponse
|
||||||
|
0x01 => StatusPong
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
|
@ -15,6 +15,7 @@
|
||||||
use crate::ecs;
|
use crate::ecs;
|
||||||
use crate::entity;
|
use crate::entity;
|
||||||
use crate::format;
|
use crate::format;
|
||||||
|
use crate::nbt;
|
||||||
use crate::protocol::{self, forge, mojang, packet};
|
use crate::protocol::{self, forge, mojang, packet};
|
||||||
use crate::render;
|
use crate::render;
|
||||||
use crate::resources;
|
use crate::resources;
|
||||||
|
@ -133,9 +134,19 @@ impl Server {
|
||||||
next: protocol::VarInt(2),
|
next: protocol::VarInt(2),
|
||||||
})?;
|
})?;
|
||||||
conn.state = protocol::State::Login;
|
conn.state = protocol::State::Login;
|
||||||
conn.write_packet(protocol::packet::login::serverbound::LoginStart {
|
if protocol_version >= 759 {
|
||||||
username: profile.username.clone(),
|
conn.write_packet(protocol::packet::login::serverbound::LoginStart_Sig {
|
||||||
})?;
|
username: profile.username.clone(),
|
||||||
|
has_sign_data: false,
|
||||||
|
public_key: None,
|
||||||
|
signature: None,
|
||||||
|
timestamp: None,
|
||||||
|
})?;
|
||||||
|
} else {
|
||||||
|
conn.write_packet(protocol::packet::login::serverbound::LoginStart {
|
||||||
|
username: profile.username.clone(),
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
let (server_id, public_key, verify_token);
|
let (server_id, public_key, verify_token);
|
||||||
|
@ -190,6 +201,26 @@ impl Server {
|
||||||
Some(rx),
|
Some(rx),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
protocol::packet::Packet::LoginSuccess_Sig(val) => {
|
||||||
|
warn!("Server is running in offline mode");
|
||||||
|
debug!(
|
||||||
|
"Login: {} {:?} {:?}",
|
||||||
|
val.username, val.uuid, val.properties
|
||||||
|
);
|
||||||
|
let mut read = conn.clone();
|
||||||
|
let mut write = conn;
|
||||||
|
read.state = protocol::State::Play;
|
||||||
|
write.state = protocol::State::Play;
|
||||||
|
let rx = Self::spawn_reader(read);
|
||||||
|
return Ok(Server::new(
|
||||||
|
protocol_version,
|
||||||
|
forge_mods,
|
||||||
|
val.uuid,
|
||||||
|
resources,
|
||||||
|
Arc::new(RwLock::new(Some(write))),
|
||||||
|
Some(rx),
|
||||||
|
));
|
||||||
|
}
|
||||||
protocol::packet::Packet::LoginDisconnect(val) => {
|
protocol::packet::Packet::LoginDisconnect(val) => {
|
||||||
return Err(protocol::Error::Disconnect(val.reason))
|
return Err(protocol::Error::Disconnect(val.reason))
|
||||||
}
|
}
|
||||||
|
@ -208,7 +239,17 @@ impl Server {
|
||||||
profile.join_server(&server_id, &shared, &public_key)?;
|
profile.join_server(&server_id, &shared, &public_key)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if protocol_version >= 47 {
|
if protocol_version >= 759 {
|
||||||
|
conn.write_packet(
|
||||||
|
protocol::packet::login::serverbound::EncryptionResponse_Sig {
|
||||||
|
shared_secret: protocol::LenPrefixedBytes::new(shared_e),
|
||||||
|
verify_token: Some(protocol::LenPrefixedBytes::new(token_e)),
|
||||||
|
has_verify_token: true,
|
||||||
|
salt: None,
|
||||||
|
signature: None,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
} else if protocol_version >= 47 {
|
||||||
conn.write_packet(protocol::packet::login::serverbound::EncryptionResponse {
|
conn.write_packet(protocol::packet::login::serverbound::EncryptionResponse {
|
||||||
shared_secret: protocol::LenPrefixedBytes::new(shared_e),
|
shared_secret: protocol::LenPrefixedBytes::new(shared_e),
|
||||||
verify_token: protocol::LenPrefixedBytes::new(token_e),
|
verify_token: protocol::LenPrefixedBytes::new(token_e),
|
||||||
|
@ -250,6 +291,16 @@ impl Server {
|
||||||
write.state = protocol::State::Play;
|
write.state = protocol::State::Play;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
protocol::packet::Packet::LoginSuccess_Sig(val) => {
|
||||||
|
debug!(
|
||||||
|
"Login: {} {:?} {:?}",
|
||||||
|
val.username, val.uuid, val.properties
|
||||||
|
);
|
||||||
|
uuid = val.uuid;
|
||||||
|
read.state = protocol::State::Play;
|
||||||
|
write.state = protocol::State::Play;
|
||||||
|
break;
|
||||||
|
}
|
||||||
protocol::packet::Packet::LoginDisconnect(val) => {
|
protocol::packet::Packet::LoginDisconnect(val) => {
|
||||||
return Err(protocol::Error::Disconnect(val.reason))
|
return Err(protocol::Error::Disconnect(val.reason))
|
||||||
}
|
}
|
||||||
|
@ -584,6 +635,7 @@ impl Server {
|
||||||
self pck {
|
self pck {
|
||||||
PluginMessageClientbound_i16 => on_plugin_message_clientbound_i16,
|
PluginMessageClientbound_i16 => on_plugin_message_clientbound_i16,
|
||||||
PluginMessageClientbound => on_plugin_message_clientbound_1,
|
PluginMessageClientbound => on_plugin_message_clientbound_1,
|
||||||
|
JoinGame_WorldNames_IsHard_SimDist_HasDeath => on_game_join_worldnames_ishard_simdist_hasdeath,
|
||||||
JoinGame_WorldNames_IsHard_SimDist => on_game_join_worldnames_ishard_simdist,
|
JoinGame_WorldNames_IsHard_SimDist => on_game_join_worldnames_ishard_simdist,
|
||||||
JoinGame_WorldNames_IsHard => on_game_join_worldnames_ishard,
|
JoinGame_WorldNames_IsHard => on_game_join_worldnames_ishard,
|
||||||
JoinGame_WorldNames => on_game_join_worldnames,
|
JoinGame_WorldNames => on_game_join_worldnames,
|
||||||
|
@ -592,6 +644,7 @@ impl Server {
|
||||||
JoinGame_i32 => on_game_join_i32,
|
JoinGame_i32 => on_game_join_i32,
|
||||||
JoinGame_i8 => on_game_join_i8,
|
JoinGame_i8 => on_game_join_i8,
|
||||||
JoinGame_i8_NoDebug => on_game_join_i8_nodebug,
|
JoinGame_i8_NoDebug => on_game_join_i8_nodebug,
|
||||||
|
ServerData => on_server_data,
|
||||||
Respawn_Gamemode => on_respawn_gamemode,
|
Respawn_Gamemode => on_respawn_gamemode,
|
||||||
Respawn_HashedSeed => on_respawn_hashedseed,
|
Respawn_HashedSeed => on_respawn_hashedseed,
|
||||||
Respawn_WorldName => on_respawn_worldname,
|
Respawn_WorldName => on_respawn_worldname,
|
||||||
|
@ -631,6 +684,7 @@ impl Server {
|
||||||
UpdateSign_u16 => on_sign_update_u16,
|
UpdateSign_u16 => on_sign_update_u16,
|
||||||
PlayerInfo => on_player_info,
|
PlayerInfo => on_player_info,
|
||||||
PlayerInfo_String => on_player_info_string,
|
PlayerInfo_String => on_player_info_string,
|
||||||
|
SystemChatMessage => on_system_chat_message,
|
||||||
ServerMessage_NoPosition => on_servermessage_noposition,
|
ServerMessage_NoPosition => on_servermessage_noposition,
|
||||||
ServerMessage_Position => on_servermessage_position,
|
ServerMessage_Position => on_servermessage_position,
|
||||||
ServerMessage_Sender => on_servermessage_sender,
|
ServerMessage_Sender => on_servermessage_sender,
|
||||||
|
@ -1079,6 +1133,45 @@ impl Server {
|
||||||
let _ = conn.as_mut().unwrap().write_plugin_message(channel, data); // TODO handle errors
|
let _ = conn.as_mut().unwrap().write_plugin_message(channel, data); // TODO handle errors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_game_join_worldnames_ishard_simdist_hasdeath(
|
||||||
|
&mut self,
|
||||||
|
join: packet::play::clientbound::JoinGame_WorldNames_IsHard_SimDist_HasDeath,
|
||||||
|
) {
|
||||||
|
let mut found_dimension = false;
|
||||||
|
if let Some(nbt::NamedTag(_, nbt::Tag::Compound(registries))) = join.registry_codec {
|
||||||
|
if let Some(nbt::Tag::Compound(dimension_types)) =
|
||||||
|
registries.get("minecraft:dimension_type")
|
||||||
|
{
|
||||||
|
if let Some(nbt::Tag::List(list)) = dimension_types.get("value") {
|
||||||
|
for item in list {
|
||||||
|
if let nbt::Tag::Compound(item) = item {
|
||||||
|
if let Some(nbt::Tag::String(name)) = item.get("name") {
|
||||||
|
debug!("Dimension {:?} = {:?}", name, item);
|
||||||
|
if name == &join.world_name {
|
||||||
|
if let Some(nbt::Tag::Compound(dimension_type)) =
|
||||||
|
item.get("element")
|
||||||
|
{
|
||||||
|
self.world.load_dimension_type_tags(dimension_type);
|
||||||
|
found_dimension = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _biome_registry = registries.get("minecraft:worldgen/biome");
|
||||||
|
let _chat_type_registry = registries.get("minecraft:chat_type");
|
||||||
|
}
|
||||||
|
if !found_dimension {
|
||||||
|
warn!("Failed to find world name {} in dimension types of JoinGame - expect broken offsets", join.world_name);
|
||||||
|
//self.world.min_y = -64;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.on_game_join(join.gamemode, join.entity_id)
|
||||||
|
}
|
||||||
|
|
||||||
fn on_game_join_worldnames_ishard_simdist(
|
fn on_game_join_worldnames_ishard_simdist(
|
||||||
&mut self,
|
&mut self,
|
||||||
join: packet::play::clientbound::JoinGame_WorldNames_IsHard_SimDist,
|
join: packet::play::clientbound::JoinGame_WorldNames_IsHard_SimDist,
|
||||||
|
@ -1160,6 +1253,13 @@ impl Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_server_data(&mut self, server_data: packet::play::clientbound::ServerData) {
|
||||||
|
if let Some(motd) = server_data.motd {
|
||||||
|
info!("Server MOTD: {:?}", motd);
|
||||||
|
}
|
||||||
|
// TODO: show server_data.icon (base64 string)
|
||||||
|
}
|
||||||
|
|
||||||
fn on_respawn_hashedseed(&mut self, respawn: packet::play::clientbound::Respawn_HashedSeed) {
|
fn on_respawn_hashedseed(&mut self, respawn: packet::play::clientbound::Respawn_HashedSeed) {
|
||||||
self.respawn(respawn.gamemode)
|
self.respawn(respawn.gamemode)
|
||||||
}
|
}
|
||||||
|
@ -1851,6 +1951,9 @@ impl Server {
|
||||||
display,
|
display,
|
||||||
gamemode,
|
gamemode,
|
||||||
ping,
|
ping,
|
||||||
|
timestamp: _,
|
||||||
|
public_key: _,
|
||||||
|
signature: _,
|
||||||
} => {
|
} => {
|
||||||
let info = self.players.entry(uuid.clone()).or_insert(PlayerInfo {
|
let info = self.players.entry(uuid.clone()).or_insert(PlayerInfo {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
|
@ -1936,6 +2039,11 @@ impl Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_system_chat_message(&mut self, m: packet::play::clientbound::SystemChatMessage) {
|
||||||
|
// TODO: flag as system message (vs chat message)
|
||||||
|
self.on_servermessage(&m.message, None, None);
|
||||||
|
}
|
||||||
|
|
||||||
fn on_servermessage_noposition(
|
fn on_servermessage_noposition(
|
||||||
&mut self,
|
&mut self,
|
||||||
m: packet::play::clientbound::ServerMessage_NoPosition,
|
m: packet::play::clientbound::ServerMessage_NoPosition,
|
||||||
|
@ -1961,7 +2069,7 @@ impl Server {
|
||||||
self.received_chat_at = Some(Instant::now());
|
self.received_chat_at = Some(Instant::now());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_block_entities(&mut self, block_entities: Vec<Option<crate::nbt::NamedTag>>) {
|
fn load_block_entities(&mut self, block_entities: Vec<Option<nbt::NamedTag>>) {
|
||||||
for block_entity in block_entities.into_iter().flatten() {
|
for block_entity in block_entities.into_iter().flatten() {
|
||||||
let x = block_entity.1.get("x").unwrap().as_int().unwrap();
|
let x = block_entity.1.get("x").unwrap().as_int().unwrap();
|
||||||
let y = block_entity.1.get("y").unwrap().as_int().unwrap();
|
let y = block_entity.1.get("y").unwrap().as_int().unwrap();
|
||||||
|
|
|
@ -1023,19 +1023,23 @@ impl World {
|
||||||
|
|
||||||
pub fn load_dimension_type(&mut self, dimension_tags: Option<crate::nbt::NamedTag>) {
|
pub fn load_dimension_type(&mut self, dimension_tags: Option<crate::nbt::NamedTag>) {
|
||||||
if let Some(crate::nbt::NamedTag(_, crate::nbt::Tag::Compound(tags))) = dimension_tags {
|
if let Some(crate::nbt::NamedTag(_, crate::nbt::Tag::Compound(tags))) = dimension_tags {
|
||||||
info!("Dimension type: {:?}", tags);
|
self.load_dimension_type_tags(&tags);
|
||||||
|
|
||||||
if let Some(crate::nbt::Tag::Int(min_y)) = tags.get("min_y") {
|
|
||||||
self.min_y = *min_y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(crate::nbt::Tag::Int(height)) = tags.get("height") {
|
|
||||||
self.height = *height;
|
|
||||||
}
|
|
||||||
// TODO: More tags https://wiki.vg/Protocol#Login_.28play.29
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_dimension_type_tags(&mut self, tags: &HashMap<String, crate::nbt::Tag>) {
|
||||||
|
info!("Dimension type: {:?}", tags);
|
||||||
|
|
||||||
|
if let Some(crate::nbt::Tag::Int(min_y)) = tags.get("min_y") {
|
||||||
|
self.min_y = *min_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(crate::nbt::Tag::Int(height)) = tags.get("height") {
|
||||||
|
self.height = *height;
|
||||||
|
}
|
||||||
|
// TODO: More tags https://wiki.vg/Protocol#Login_.28play.29
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::or_fun_call)]
|
#[allow(clippy::or_fun_call)]
|
||||||
fn load_chunk19_to_118(
|
fn load_chunk19_to_118(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
Loading…
Reference in New Issue