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::sync::atomic::{AtomicBool, AtomicI32, Ordering};
|
||||
|
||||
pub const SUPPORTED_PROTOCOLS: [i32; 27] = [
|
||||
758, 757, 756, 754, 753, 751, 736, 735, 578, 575, 498, 490, 485, 480, 477, 452, 451, 404, 340,
|
||||
316, 315, 210, 109, 107, 74, 47, 5,
|
||||
pub const SUPPORTED_PROTOCOLS: [i32; 28] = [
|
||||
759, 758, 757, 756, 754, 753, 751, 736, 735, 578, 575, 498, 490, 485, 480, 477, 452, 451, 404,
|
||||
340, 316, 315, 210, 109, 107, 74, 47, 5,
|
||||
];
|
||||
|
||||
static CURRENT_PROTOCOL_VERSION: AtomicI32 = AtomicI32::new(SUPPORTED_PROTOCOLS[0]);
|
||||
|
|
|
@ -911,6 +911,13 @@ state_packets!(
|
|||
field nodes: LenPrefixed<VarInt, packet::CommandNode> =,
|
||||
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
|
||||
/// or just a system message. The Type field controls the location the
|
||||
/// 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
|
||||
/// 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 {
|
||||
/// The entity id the client will be referenced by
|
||||
field entity_id: i32 =,
|
||||
|
@ -1878,6 +1927,13 @@ state_packets!(
|
|||
field has_id: bool =,
|
||||
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 {
|
||||
field text: String =,
|
||||
}
|
||||
|
@ -2359,15 +2415,34 @@ state_packets!(
|
|||
}
|
||||
login Login {
|
||||
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
|
||||
/// 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 {
|
||||
field username: String =,
|
||||
}
|
||||
/// EncryptionResponse is sent as a reply to EncryptionRequest. All
|
||||
/// packets following this one must be encrypted with AES/CFB8
|
||||
/// 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 {
|
||||
/// The key for the AES/CFB8 cipher encrypted with the
|
||||
/// public key
|
||||
|
@ -2414,6 +2489,11 @@ state_packets!(
|
|||
/// LoginSuccess is sent by the server if the player successfully
|
||||
/// authenicates with the session servers (online mode) or straight
|
||||
/// after LoginStart (offline mode).
|
||||
packet LoginSuccess_Sig {
|
||||
field uuid: UUID =,
|
||||
field username: String =,
|
||||
field properties: LenPrefixed<VarInt, packet::LoginProperty> =,
|
||||
}
|
||||
packet LoginSuccess_String {
|
||||
/// String encoding of a uuid (with hyphens)
|
||||
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)]
|
||||
pub struct SpawnProperty {
|
||||
pub name: String,
|
||||
|
@ -2969,19 +3085,37 @@ impl Serializable for PlayerInfoData {
|
|||
}
|
||||
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 {
|
||||
uuid,
|
||||
name,
|
||||
properties: props,
|
||||
gamemode: Serializable::read_from(buf)?,
|
||||
ping: Serializable::read_from(buf)?,
|
||||
display: {
|
||||
if bool::read_from(buf)? {
|
||||
Some(Serializable::read_from(buf)?)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
gamemode,
|
||||
ping,
|
||||
display,
|
||||
timestamp,
|
||||
public_key,
|
||||
signature,
|
||||
};
|
||||
m.players.push(p);
|
||||
}
|
||||
|
@ -3033,6 +3167,9 @@ pub enum PlayerDetail {
|
|||
gamemode: VarInt,
|
||||
ping: VarInt,
|
||||
display: Option<format::Component>,
|
||||
timestamp: Option<u64>,
|
||||
public_key: Option<LenPrefixedBytes<VarInt>>,
|
||||
signature: Option<LenPrefixedBytes<VarInt>>,
|
||||
},
|
||||
UpdateGamemode {
|
||||
uuid: UUID,
|
||||
|
@ -3423,6 +3560,12 @@ pub enum CommandProperty {
|
|||
EntitySummon,
|
||||
Dimension,
|
||||
UUID,
|
||||
ResourceOrTag {
|
||||
registry: String,
|
||||
},
|
||||
Resource {
|
||||
registry: String,
|
||||
},
|
||||
ForgeModId,
|
||||
ForgeEnum {
|
||||
cls: String,
|
||||
|
@ -3456,8 +3599,75 @@ impl Serializable for CommandNode {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
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 {
|
||||
None
|
||||
};
|
||||
|
@ -3556,11 +3766,17 @@ impl Serializable for CommandNode {
|
|||
"minecraft:entity_summon" => CommandProperty::EntitySummon,
|
||||
"minecraft:dimension" => CommandProperty::Dimension,
|
||||
"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:enum" => CommandProperty::ForgeEnum {
|
||||
cls: Serializable::read_from(buf)?,
|
||||
},
|
||||
_ => panic!("unsupported command node parser {}", parse),
|
||||
_ => panic!("unsupported command node parser {:?}", parse),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -18,6 +18,7 @@ mod v1_16_4;
|
|||
mod v1_17_1;
|
||||
mod v1_18_1;
|
||||
mod v1_18_2;
|
||||
mod v1_19;
|
||||
mod v1_7_10;
|
||||
mod v1_8_9;
|
||||
mod v1_9;
|
||||
|
@ -29,6 +30,7 @@ mod v1_9_2;
|
|||
pub fn protocol_name_to_protocol_version(s: String) -> i32 {
|
||||
match s.as_ref() {
|
||||
"" => SUPPORTED_PROTOCOLS[0],
|
||||
"1.19" => 759,
|
||||
"1.18.2" => 758,
|
||||
"1.18.1" => 757,
|
||||
"1.17.1" => 756,
|
||||
|
@ -75,6 +77,7 @@ pub fn translate_internal_packet_id_for_version(
|
|||
to_internal: bool,
|
||||
) -> i32 {
|
||||
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),
|
||||
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),
|
||||
|
|
|
@ -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::entity;
|
||||
use crate::format;
|
||||
use crate::nbt;
|
||||
use crate::protocol::{self, forge, mojang, packet};
|
||||
use crate::render;
|
||||
use crate::resources;
|
||||
|
@ -133,9 +134,19 @@ impl Server {
|
|||
next: protocol::VarInt(2),
|
||||
})?;
|
||||
conn.state = protocol::State::Login;
|
||||
conn.write_packet(protocol::packet::login::serverbound::LoginStart {
|
||||
username: profile.username.clone(),
|
||||
})?;
|
||||
if protocol_version >= 759 {
|
||||
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;
|
||||
let (server_id, public_key, verify_token);
|
||||
|
@ -190,6 +201,26 @@ impl Server {
|
|||
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) => {
|
||||
return Err(protocol::Error::Disconnect(val.reason))
|
||||
}
|
||||
|
@ -208,7 +239,17 @@ impl Server {
|
|||
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 {
|
||||
shared_secret: protocol::LenPrefixedBytes::new(shared_e),
|
||||
verify_token: protocol::LenPrefixedBytes::new(token_e),
|
||||
|
@ -250,6 +291,16 @@ impl Server {
|
|||
write.state = protocol::State::Play;
|
||||
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) => {
|
||||
return Err(protocol::Error::Disconnect(val.reason))
|
||||
}
|
||||
|
@ -584,6 +635,7 @@ impl Server {
|
|||
self pck {
|
||||
PluginMessageClientbound_i16 => on_plugin_message_clientbound_i16,
|
||||
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 => on_game_join_worldnames_ishard,
|
||||
JoinGame_WorldNames => on_game_join_worldnames,
|
||||
|
@ -592,6 +644,7 @@ impl Server {
|
|||
JoinGame_i32 => on_game_join_i32,
|
||||
JoinGame_i8 => on_game_join_i8,
|
||||
JoinGame_i8_NoDebug => on_game_join_i8_nodebug,
|
||||
ServerData => on_server_data,
|
||||
Respawn_Gamemode => on_respawn_gamemode,
|
||||
Respawn_HashedSeed => on_respawn_hashedseed,
|
||||
Respawn_WorldName => on_respawn_worldname,
|
||||
|
@ -631,6 +684,7 @@ impl Server {
|
|||
UpdateSign_u16 => on_sign_update_u16,
|
||||
PlayerInfo => on_player_info,
|
||||
PlayerInfo_String => on_player_info_string,
|
||||
SystemChatMessage => on_system_chat_message,
|
||||
ServerMessage_NoPosition => on_servermessage_noposition,
|
||||
ServerMessage_Position => on_servermessage_position,
|
||||
ServerMessage_Sender => on_servermessage_sender,
|
||||
|
@ -1079,6 +1133,45 @@ impl Server {
|
|||
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(
|
||||
&mut self,
|
||||
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) {
|
||||
self.respawn(respawn.gamemode)
|
||||
}
|
||||
|
@ -1851,6 +1951,9 @@ impl Server {
|
|||
display,
|
||||
gamemode,
|
||||
ping,
|
||||
timestamp: _,
|
||||
public_key: _,
|
||||
signature: _,
|
||||
} => {
|
||||
let info = self.players.entry(uuid.clone()).or_insert(PlayerInfo {
|
||||
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(
|
||||
&mut self,
|
||||
m: packet::play::clientbound::ServerMessage_NoPosition,
|
||||
|
@ -1961,7 +2069,7 @@ impl Server {
|
|||
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() {
|
||||
let x = block_entity.1.get("x").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>) {
|
||||
if let Some(crate::nbt::NamedTag(_, crate::nbt::Tag::Compound(tags))) = dimension_tags {
|
||||
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
|
||||
self.load_dimension_type_tags(&tags);
|
||||
}
|
||||
}
|
||||
|
||||
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)]
|
||||
fn load_chunk19_to_118(
|
||||
&mut self,
|
||||
|
|
Loading…
Reference in New Issue