This commit is contained in:
iceiix 2023-01-01 05:18:49 +01:00 committed by GitHub
commit f0a9012d0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 555 additions and 30 deletions

View File

@ -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]);

View File

@ -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

View File

@ -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),

View File

@ -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
}
}
);

View File

@ -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();

View File

@ -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,