Add Clientbound Player Info Update

Also adds support for Skins!
This commit is contained in:
Snowiiii 2024-08-04 18:03:43 +02:00
parent dbcb3c8052
commit 05977223e3
7 changed files with 91 additions and 17 deletions

View File

@ -39,6 +39,7 @@ Pumpkin is currently under heavy development.
- [ ] World Borders - [ ] World Borders
- [ ] World Saving - [ ] World Saving
- Player - Player
- [x] Player Skin
- [x] Player Client brand - [x] Player Client brand
- [x] Player Teleport - [x] Player Teleport
- [ ] Player Movement - [ ] Player Movement

View File

@ -112,6 +112,7 @@ impl ByteBuffer {
} }
pub fn put_uuid(&mut self, v: uuid::Uuid) { pub fn put_uuid(&mut self, v: uuid::Uuid) {
// thats the vanilla way
let pair = v.as_u64_pair(); let pair = v.as_u64_pair();
self.put_u64(pair.0); self.put_u64(pair.0);
self.put_u64(pair.1); self.put_u64(pair.1);

View File

@ -1,6 +1,4 @@
use serde::Deserialize; use crate::{bytebuf::ByteBuffer, ClientPacket, Property, VarInt};
use crate::{bytebuf::ByteBuffer, ClientPacket, VarInt};
pub struct CLoginDisconnect<'a> { pub struct CLoginDisconnect<'a> {
reason: &'a str, reason: &'a str,
@ -83,15 +81,6 @@ impl<'a> CLoginSuccess<'a> {
} }
} }
#[derive(Deserialize, Clone, Debug)]
pub struct Property {
pub name: String,
// base 64
pub value: String,
// base 64
pub signature: Option<String>,
}
impl<'a> ClientPacket for CLoginSuccess<'a> { impl<'a> ClientPacket for CLoginSuccess<'a> {
const PACKET_ID: VarInt = 0x02; const PACKET_ID: VarInt = 0x02;
@ -102,6 +91,7 @@ impl<'a> ClientPacket for CLoginSuccess<'a> {
p.put_string(&v.name); p.put_string(&v.name);
p.put_string(&v.value); p.put_string(&v.value);
// has signature ? // has signature ?
// todo: for some reason we get "got too many bytes error when using a signature"
p.put_bool(false); p.put_bool(false);
// p.put_option(&v.signature, |p,v| p.put_string(v)); // p.put_option(&v.signature, |p,v| p.put_string(v));
}); });

View File

@ -1,4 +1,4 @@
use crate::{bytebuf::ByteBuffer, text::Text, BitSet, ClientPacket, VarInt}; use crate::{bytebuf::ByteBuffer, text::Text, BitSet, ClientPacket, Property, VarInt};
pub struct SetHeldItem { pub struct SetHeldItem {
slot: i8, slot: i8,
@ -18,6 +18,65 @@ impl ClientPacket for SetHeldItem {
} }
} }
pub struct CPlayerInfoUpdate<'a> {
actions: i8,
players: &'a [Player<'a>],
}
pub struct Player<'a> {
pub uuid: uuid::Uuid,
pub actions: &'a [PlayerAction],
}
pub enum PlayerAction {
AddPlayer {
name: String,
properties: Vec<Property>,
},
InitializeChat(u8),
UpdateGameMode(u8),
UpdateListed(u8),
UpdateLatency(u8),
UpdateDisplayName(u8),
}
impl<'a> CPlayerInfoUpdate<'a> {
pub fn new(actions: i8, players: &'a [Player]) -> Self {
Self { actions, players }
}
}
impl<'a> ClientPacket for CPlayerInfoUpdate<'a> {
const PACKET_ID: VarInt = 0x3E;
fn write(&self, bytebuf: &mut ByteBuffer) {
bytebuf.put_i8(self.actions);
bytebuf.put_list::<Player>(self.players, |p, v| {
p.put_uuid(v.uuid);
for action in v.actions {
match action {
PlayerAction::AddPlayer { name, properties } => {
p.put_string(name);
p.put_list::<Property>(properties, |p, v| {
p.put_string(&v.name);
p.put_string(&v.value);
// has signature ?
// todo: for some reason we get "got too many bytes error when using a signature"
p.put_bool(false);
// todo signature
});
}
PlayerAction::InitializeChat(_) => todo!(),
PlayerAction::UpdateGameMode(_) => todo!(),
PlayerAction::UpdateListed(_) => todo!(),
PlayerAction::UpdateLatency(_) => todo!(),
PlayerAction::UpdateDisplayName(_) => todo!(),
}
}
});
}
}
pub struct CPlayerAbilities { pub struct CPlayerAbilities {
flags: i8, flags: i8,
flying_speed: f32, flying_speed: f32,

View File

@ -2,7 +2,7 @@ use std::io::{self, Write};
use bytebuf::ByteBuffer; use bytebuf::ByteBuffer;
use bytes::Buf; use bytes::Buf;
use serde::Serialize; use serde::{Deserialize, Serialize};
use thiserror::Error; use thiserror::Error;
pub mod bytebuf; pub mod bytebuf;
@ -183,6 +183,16 @@ pub struct Sample {
pub id: String, // uuid pub id: String, // uuid
} }
// basicly game profile
#[derive(Deserialize, Clone, Debug)]
pub struct Property {
pub name: String,
// base 64
pub value: String,
// base 64
pub signature: Option<String>,
}
pub struct KnownPack<'a> { pub struct KnownPack<'a> {
pub namespace: &'a str, pub namespace: &'a str,
pub id: &'a str, pub id: &'a str,

View File

@ -1,7 +1,7 @@
use std::{net::IpAddr}; use std::net::IpAddr;
use num_bigint::BigInt; use num_bigint::BigInt;
use pumpkin_protocol::client::login::Property; use pumpkin_protocol::Property;
use reqwest::StatusCode; use reqwest::StatusCode;
use serde::Deserialize; use serde::Deserialize;
use thiserror::Error; use thiserror::Error;

View File

@ -8,7 +8,7 @@ use mio::{event::Event, Poll};
use pumpkin_protocol::{ use pumpkin_protocol::{
client::{ client::{
config::CPluginMessage, config::CPluginMessage,
play::{CChunkDataUpdateLight, CGameEvent, CLogin}, play::{CChunkDataUpdateLight, CGameEvent, CLogin, CPlayerInfoUpdate, PlayerAction},
}, },
BitSet, PacketError, Players, Sample, StatusResponse, VarInt, VarInt32, Version, BitSet, PacketError, Players, Sample, StatusResponse, VarInt, VarInt32, Version,
}; };
@ -129,6 +129,19 @@ impl Server {
.unwrap_or_else(|e| client.kick(&e.to_string())); .unwrap_or_else(|e| client.kick(&e.to_string()));
// teleport // teleport
client.teleport(10.0, 500.0, 10.0, 10.0, 10.0); client.teleport(10.0, 500.0, 10.0, 10.0, 10.0);
let gameprofile = client.gameprofile.as_ref().unwrap();
client
.send_packet(CPlayerInfoUpdate::new(
0x01,
&[pumpkin_protocol::client::play::Player {
uuid: gameprofile.id,
actions: &[PlayerAction::AddPlayer {
name: gameprofile.name.clone(),
properties: gameprofile.properties.clone(),
}],
}],
))
.unwrap_or_else(|e| client.kick(&e.to_string()));
// Start waiting for level chunks // Start waiting for level chunks
client client