1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 14:26:04 +00:00

ClientboundPlayerInfoUpdatePacket

This commit is contained in:
mat 2022-10-30 18:58:12 -05:00
parent a3c5fdf13f
commit 87dbee46e2
9 changed files with 348 additions and 75 deletions

View file

@ -2,7 +2,7 @@ use azalea_buf::McBuf;
use std::collections::HashMap;
use uuid::Uuid;
#[derive(McBuf, Debug, Clone)]
#[derive(McBuf, Debug, Clone, Default)]
pub struct GameProfile {
pub uuid: Uuid,
pub name: String,

View file

@ -1,6 +1,45 @@
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::{quote, ToTokens};
use syn::{self, parse_macro_input, Data, DeriveInput, FieldsNamed, Ident};
use syn::{
self, parse_macro_input, punctuated::Punctuated, token::Comma, Data, DeriveInput, Field,
FieldsNamed, Ident,
};
fn read_named_fields(
named: &Punctuated<Field, Comma>,
) -> (Vec<proc_macro2::TokenStream>, Vec<&Option<Ident>>) {
let read_fields = named
.iter()
.map(|f| {
let field_name = &f.ident;
let field_type = &f.ty;
// do a different buf.write_* for each field depending on the type
// if it's a string, use buf.write_string
match field_type {
syn::Type::Path(_) | syn::Type::Array(_) => {
if f.attrs.iter().any(|a| a.path.is_ident("var")) {
quote! {
let #field_name = azalea_buf::McBufVarReadable::var_read_from(buf)?;
}
} else {
quote! {
let #field_name = azalea_buf::McBufReadable::read_from(buf)?;
}
}
}
_ => panic!(
"Error reading field {}: {}",
field_name.clone().unwrap(),
field_type.to_token_stream()
),
}
})
.collect::<Vec<_>>();
let read_field_names = named.iter().map(|f| &f.ident).collect::<Vec<_>>();
(read_fields, read_field_names)
}
fn create_impl_mcbufreadable(ident: &Ident, data: &Data) -> proc_macro2::TokenStream {
match data {
@ -10,34 +49,7 @@ fn create_impl_mcbufreadable(ident: &Ident, data: &Data) -> proc_macro2::TokenSt
_ => panic!("#[derive(McBuf)] can only be used on structs with named fields"),
};
let read_fields = named
.iter()
.map(|f| {
let field_name = &f.ident;
let field_type = &f.ty;
// do a different buf.write_* for each field depending on the type
// if it's a string, use buf.write_string
match field_type {
syn::Type::Path(_) | syn::Type::Array(_) => {
if f.attrs.iter().any(|a| a.path.is_ident("var")) {
quote! {
let #field_name = azalea_buf::McBufVarReadable::var_read_from(buf)?;
}
} else {
quote! {
let #field_name = azalea_buf::McBufReadable::read_from(buf)?;
}
}
}
_ => panic!(
"Error reading field {}: {}",
field_name.clone().unwrap(),
field_type.to_token_stream()
),
}
})
.collect::<Vec<_>>();
let read_field_names = named.iter().map(|f| &f.ident).collect::<Vec<_>>();
let (read_fields, read_field_names) = read_named_fields(named);
quote! {
impl azalea_buf::McBufReadable for #ident {
@ -78,9 +90,16 @@ fn create_impl_mcbufreadable(ident: &Ident, data: &Data) -> proc_macro2::TokenSt
}
}
}
let reader = match variant.fields {
syn::Fields::Named(_) => {
panic!("writing named fields in enums is not supported")
let reader = match &variant.fields {
syn::Fields::Named(f) => {
let (read_fields, read_field_names) = read_named_fields(&f.named);
quote! {
#(#read_fields)*
Ok(#ident::#variant_name {
#(#read_field_names: #read_field_names),*
})
}
}
syn::Fields::Unnamed(_) => quote! {
Ok(Self::#variant_name(azalea_buf::McBufReadable::read_from(buf)?))
@ -105,13 +124,12 @@ fn create_impl_mcbufreadable(ident: &Ident, data: &Data) -> proc_macro2::TokenSt
quote! {
impl azalea_buf::McBufReadable for #ident {
fn read_from(buf: &mut std::io::Cursor<&[u8]>) -> Result<Self, azalea_buf::BufReadError>
{
fn read_from(buf: &mut std::io::Cursor<&[u8]>) -> Result<Self, azalea_buf::BufReadError> {
let id = azalea_buf::McBufVarReadable::var_read_from(buf)?;
match id {
#match_contents
// you'd THINK this throws an error, but mojang decided to make it default for some reason
_ => #first_reader
_ => {#first_reader}
}
}
}
@ -121,6 +139,41 @@ fn create_impl_mcbufreadable(ident: &Ident, data: &Data) -> proc_macro2::TokenSt
}
}
fn write_named_fields(
named: &Punctuated<Field, Comma>,
ident_name: Option<&Ident>,
) -> proc_macro2::TokenStream {
let write_fields = named.iter().map(|f| {
let field_name = &f.ident;
let field_type = &f.ty;
let ident_dot_field = match ident_name {
Some(ident) => quote! { &#ident.#field_name },
None => quote! { #field_name },
};
// do a different buf.write_* for each field depending on the type
// if it's a string, use buf.write_string
match field_type {
syn::Type::Path(_) | syn::Type::Array(_) => {
if f.attrs.iter().any(|attr| attr.path.is_ident("var")) {
quote! {
azalea_buf::McBufVarWritable::var_write_into(#ident_dot_field, buf)?;
}
} else {
quote! {
azalea_buf::McBufWritable::write_into(#ident_dot_field, buf)?;
}
}
}
_ => panic!(
"Error writing field {}: {}",
field_name.clone().unwrap(),
field_type.to_token_stream()
),
}
});
quote! { #(#write_fields)* }
}
fn create_impl_mcbufwritable(ident: &Ident, data: &Data) -> proc_macro2::TokenStream {
match data {
syn::Data::Struct(syn::DataStruct { fields, .. }) => {
@ -129,38 +182,13 @@ fn create_impl_mcbufwritable(ident: &Ident, data: &Data) -> proc_macro2::TokenSt
_ => panic!("#[derive(McBuf)] can only be used on structs with named fields"),
};
let write_fields = named
.iter()
.map(|f| {
let field_name = &f.ident;
let field_type = &f.ty;
// do a different buf.write_* for each field depending on the type
// if it's a string, use buf.write_string
match field_type {
syn::Type::Path(_) | syn::Type::Array(_) => {
if f.attrs.iter().any(|attr| attr.path.is_ident("var")) {
quote! {
azalea_buf::McBufVarWritable::var_write_into(&self.#field_name, buf)?;
}
} else {
quote! {
azalea_buf::McBufWritable::write_into(&self.#field_name, buf)?;
}
}
}
_ => panic!(
"Error writing field {}: {}",
field_name.clone().unwrap(),
field_type.to_token_stream()
),
}
})
.collect::<Vec<_>>();
let write_fields =
write_named_fields(named, Some(&Ident::new("self", Span::call_site())));
quote! {
impl azalea_buf::McBufWritable for #ident {
fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> {
#(#write_fields)*
#write_fields
Ok(())
}
}
@ -197,12 +225,24 @@ fn create_impl_mcbufwritable(ident: &Ident, data: &Data) -> proc_macro2::TokenSt
}
}
let variant_name = &variant.ident;
match &variant.fields {
syn::Fields::Named(_) => {
panic!("Enum variants with named fields are not supported yet");
syn::Fields::Named(f) => {
is_data_enum = true;
let field_names = f
.named
.iter()
.map(|f| f.ident.clone().unwrap())
.collect::<Vec<_>>();
let write_fields = write_named_fields(&f.named, None);
match_arms.extend(quote! {
Self::#variant_name { #(#field_names),* } => {
azalea_buf::McBufVarWritable::var_write_into(&#variant_discrim, buf)?;
#write_fields
}
});
}
syn::Fields::Unit => {
let variant_name = &variant.ident;
match_arms.extend(quote! {
Self::#variant_name => {
azalea_buf::McBufVarWritable::var_write_into(&#variant_discrim, buf)?;
@ -211,7 +251,6 @@ fn create_impl_mcbufwritable(ident: &Ident, data: &Data) -> proc_macro2::TokenSt
}
syn::Fields::Unnamed(_) => {
is_data_enum = true;
let variant_name = &variant.ident;
match_arms.extend(quote! {
Self::#variant_name(data) => {
azalea_buf::McBufVarWritable::var_write_into(&#variant_discrim, buf)?;

View file

@ -1,4 +1,6 @@
use azalea_buf::McBuf;
use std::io::{Cursor, Read};
use azalea_buf::{BufReadError, McBuf};
/// Represents Java's BitSet, a list of bits.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, McBuf)]
@ -101,6 +103,29 @@ impl BitSet {
pub fn set(&mut self, bit_index: usize) {
self.data[bit_index / 64] |= 1u64 << (bit_index % 64);
}
/// Read a BitSet with a known length.
pub fn read_fixed(buf: &mut Cursor<&[u8]>, length: usize) -> Result<Self, BufReadError> {
let mut data = vec![0; length.div_ceil(8)];
buf.read_exact(&mut data)?;
Ok(BitSet::from(data))
}
}
impl From<Vec<u64>> for BitSet {
fn from(data: Vec<u64>) -> Self {
BitSet { data }
}
}
impl From<Vec<u8>> for BitSet {
fn from(data: Vec<u8>) -> Self {
let mut words = vec![0; data.len().div_ceil(8)];
for (i, byte) in data.iter().enumerate() {
words[i / 8] |= (*byte as u64) << ((i % 8) * 8);
}
BitSet { data: words }
}
}
#[cfg(test)]

View file

@ -1,8 +1,9 @@
use azalea_buf::{BufReadError, McBufReadable, McBufWritable};
use std::io::{Cursor, Write};
#[derive(Hash, Copy, Clone, Debug)]
#[derive(Hash, Copy, Clone, Debug, Default)]
pub enum GameType {
#[default]
SURVIVAL,
CREATIVE,
ADVENTURE,

View file

@ -1,6 +1,6 @@
use super::clientbound_player_chat_packet::ChatTypeBound;
use azalea_buf::McBuf;
use azalea_chat::component::Component;
use azalea_chat::Component;
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]

View file

@ -1,5 +1,201 @@
use azalea_buf::McBuf;
use crate::packets::login::serverbound_hello_packet::RemoteChatSessionData;
use azalea_auth::game_profile::{GameProfile, ProfilePropertyValue};
use azalea_buf::{
BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable,
};
use azalea_chat::Component;
use azalea_core::{BitSet, GameType};
use azalea_protocol_macros::ClientboundGamePacket;
use std::{
collections::HashMap,
io::{Cursor, Write},
};
use uuid::Uuid;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundPlayerInfoUpdatePacket {}
#[derive(Clone, Debug, ClientboundGamePacket)]
pub struct ClientboundPlayerInfoUpdatePacket {
pub actions: ActionEnumSet,
pub entries: Vec<PlayerInfoEntry>,
}
#[derive(Clone, Debug, Default)]
pub struct PlayerInfoEntry {
pub profile_id: Uuid,
pub profile: GameProfile,
pub listed: bool,
pub latency: i32,
pub game_mode: GameType,
pub display_name: Option<Component>,
pub chat_session: Option<RemoteChatSessionData>,
}
#[derive(Clone, Debug, McBuf)]
pub struct AddPlayerAction {
pub name: String,
pub properties: HashMap<String, ProfilePropertyValue>,
}
#[derive(Clone, Debug, McBuf)]
pub struct InitializeChatAction {
pub chat_session: Option<RemoteChatSessionData>,
}
#[derive(Clone, Debug, McBuf)]
pub struct UpdateGameModeAction {
pub game_mode: GameType,
}
#[derive(Clone, Debug, McBuf)]
pub struct UpdateListedAction {
pub listed: bool,
}
#[derive(Clone, Debug, McBuf)]
pub struct UpdateLatencyAction {
#[var]
pub latency: i32,
}
#[derive(Clone, Debug, McBuf)]
pub struct UpdateDisplayNameAction {
pub display_name: Option<Component>,
}
impl McBufReadable for ClientboundPlayerInfoUpdatePacket {
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
let actions = ActionEnumSet::read_from(buf)?;
let entries = vec![];
let entry_count = u32::var_read_from(buf)?;
for _ in 0..entry_count {
let profile_id = Uuid::read_from(buf)?;
let mut entry = PlayerInfoEntry::default();
entry.profile_id = profile_id;
if actions.add_player {
let action = AddPlayerAction::read_from(buf)?;
entry.profile.name = action.name;
entry.profile.properties = action.properties;
}
if actions.initialize_chat {
let action = InitializeChatAction::read_from(buf)?;
entry.chat_session = action.chat_session;
}
if actions.update_game_mode {
let action = UpdateGameModeAction::read_from(buf)?;
entry.game_mode = action.game_mode;
}
if actions.update_listed {
let action = UpdateListedAction::read_from(buf)?;
entry.listed = action.listed;
}
if actions.update_latency {
let action = UpdateLatencyAction::read_from(buf)?;
entry.latency = action.latency;
}
if actions.update_display_name {
let action = UpdateDisplayNameAction::read_from(buf)?;
entry.display_name = action.display_name;
}
entries.push(entry);
}
Ok(ClientboundPlayerInfoUpdatePacket { actions, entries })
}
}
impl McBufWritable for ClientboundPlayerInfoUpdatePacket {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
self.actions.write_into(buf)?;
(self.entries.len() as u32).var_write_into(buf)?;
for entry in &self.entries {
entry.profile_id.write_into(buf)?;
if self.actions.add_player {
AddPlayerAction {
name: entry.profile.name,
properties: entry.profile.properties,
}
.write_into(buf)?;
}
if self.actions.initialize_chat {
InitializeChatAction {
chat_session: entry.chat_session,
}
.write_into(buf)?;
}
if self.actions.update_game_mode {
UpdateGameModeAction {
game_mode: entry.game_mode,
}
.write_into(buf)?;
}
if self.actions.update_listed {
UpdateListedAction {
listed: entry.listed,
}
.write_into(buf)?;
}
if self.actions.update_latency {
UpdateLatencyAction {
latency: entry.latency,
}
.write_into(buf)?;
}
if self.actions.update_display_name {
UpdateDisplayNameAction {
display_name: entry.display_name,
}
.write_into(buf)?;
}
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct ActionEnumSet {
pub add_player: bool,
pub initialize_chat: bool,
pub update_game_mode: bool,
pub update_listed: bool,
pub update_latency: bool,
pub update_display_name: bool,
}
impl McBufReadable for ActionEnumSet {
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
let mut set = BitSet::read_fixed(buf, 6)?;
Ok(ActionEnumSet {
add_player: set.index(0),
initialize_chat: set.index(1),
update_game_mode: set.index(2),
update_listed: set.index(3),
update_latency: set.index(4),
update_display_name: set.index(5),
})
}
}
impl McBufWritable for ActionEnumSet {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
let mut set = BitSet::new(6);
if self.add_player {
set.set(0);
}
if self.initialize_chat {
set.set(1);
}
if self.update_game_mode {
set.set(2);
}
if self.update_listed {
set.set(3);
}
if self.update_latency {
set.set(4);
}
if self.update_display_name {
set.set(5);
}
set.write_into(buf)
}
}

View file

@ -22,6 +22,7 @@ pub mod clientbound_custom_payload_packet;
pub mod clientbound_custom_sound_packet;
pub mod clientbound_delete_chat_packet;
pub mod clientbound_disconnect_packet;
pub mod clientbound_disguised_chat_packet;
pub mod clientbound_entity_event_packet;
pub mod clientbound_explode_packet;
pub mod clientbound_forget_level_chunk_packet;
@ -50,6 +51,8 @@ pub mod clientbound_player_chat_packet;
pub mod clientbound_player_combat_end_packet;
pub mod clientbound_player_combat_enter_packet;
pub mod clientbound_player_combat_kill_packet;
pub mod clientbound_player_info_remove_packet;
pub mod clientbound_player_info_update_packet;
pub mod clientbound_player_look_at_packet;
pub mod clientbound_player_position_packet;
pub mod clientbound_recipe_packet;
@ -232,6 +235,7 @@ declare_state_packets!(
0x16: clientbound_custom_sound_packet::ClientboundCustomSoundPacket,
0x17: clientbound_delete_chat_packet::ClientboundDeleteChatPacket,
0x18: clientbound_disconnect_packet::ClientboundDisconnectPacket,
0x19: clientbound_disguised_chat_packet::ClientboundDisguisedChatPacket,
0x1a: clientbound_entity_event_packet::ClientboundEntityEventPacket,
0x1b: clientbound_explode_packet::ClientboundExplodePacket,
0x1c: clientbound_forget_level_chunk_packet::ClientboundForgetLevelChunkPacket,
@ -260,6 +264,8 @@ declare_state_packets!(
0x33: clientbound_player_combat_end_packet::ClientboundPlayerCombatEndPacket,
0x34: clientbound_player_combat_enter_packet::ClientboundPlayerCombatEnterPacket,
0x35: clientbound_player_combat_kill_packet::ClientboundPlayerCombatKillPacket,
0x36: clientbound_player_info_remove_packet::ClientboundPlayerInfoRemovePacket,
0x37: clientbound_player_info_update_packet::ClientboundPlayerInfoUpdatePacket,
0x38: clientbound_player_look_at_packet::ClientboundPlayerLookAtPacket,
0x39: clientbound_player_position_packet::ClientboundPlayerPositionPacket,
0x3a: clientbound_recipe_packet::ClientboundRecipePacket,

View file

@ -63,6 +63,12 @@ def burger_type_to_rust_type(burger_type, field_name: Optional[str] = None, inst
elif burger_type == 'metadata':
field_type_rs = 'EntityMetadata'
uses.add('azalea_entity::EntityMetadata')
elif burger_type == 'bitset':
if instruction:
length = instruction['length']
field_type_rs = f'todo!("fixed bitset of length {length}")'
else:
field_type_rs = 'todo!("fixed bitset")'
elif burger_type == 'abstract':
field_type_rs = 'todo!()'
elif burger_type == 'enum':

View file

@ -11,7 +11,7 @@ mappings = lib.download.get_mappings_for_version(version_id)
burger_data = lib.extract.get_burger_data_for_version(version_id)
burger_packets_data = burger_data[0]['packets']['packet']
packet_id, direction, state = int(sys.argv[1]), sys.argv[2], sys.argv[3]
packet_id, direction, state = int(sys.argv[1], 0), sys.argv[2], sys.argv[3]
print(
f'Generating code for packet id: {packet_id} with direction {direction} and state {state}')
lib.code.packet.generate_packet(burger_packets_data, mappings,