Add Serverbound Movement Packets

- SPlayerPosition
- SPlayerPositionRotation
- SPlayerRotation
This commit is contained in:
Snowiiii 2024-08-04 20:46:09 +02:00
parent 05977223e3
commit 42bc84b23c
9 changed files with 209 additions and 22 deletions

View File

@ -35,7 +35,9 @@ pub enum PlayerAction {
}, },
InitializeChat(u8), InitializeChat(u8),
UpdateGameMode(u8), UpdateGameMode(u8),
UpdateListed(u8), UpdateListed {
listed: bool,
},
UpdateLatency(u8), UpdateLatency(u8),
UpdateDisplayName(u8), UpdateDisplayName(u8),
} }
@ -68,7 +70,7 @@ impl<'a> ClientPacket for CPlayerInfoUpdate<'a> {
} }
PlayerAction::InitializeChat(_) => todo!(), PlayerAction::InitializeChat(_) => todo!(),
PlayerAction::UpdateGameMode(_) => todo!(), PlayerAction::UpdateGameMode(_) => todo!(),
PlayerAction::UpdateListed(_) => todo!(), PlayerAction::UpdateListed { listed } => p.put_bool(*listed),
PlayerAction::UpdateLatency(_) => todo!(), PlayerAction::UpdateLatency(_) => todo!(),
PlayerAction::UpdateDisplayName(_) => todo!(), PlayerAction::UpdateDisplayName(_) => todo!(),
} }

View File

@ -49,7 +49,6 @@ impl PacketDecoder {
.0; .0;
data.advance(data.len() - r.len()); data.advance(data.len() - r.len());
dbg!(packet_id);
Ok(Some(RawPacket { Ok(Some(RawPacket {
len: packet_len, len: packet_len,
id: packet_id, id: packet_id,

View File

@ -13,3 +13,65 @@ impl ServerPacket for SConfirmTeleport {
} }
} }
} }
pub struct SPlayerPosition {
pub x: f64,
pub feet_y: f64,
pub z: f64,
pub ground: bool,
}
impl ServerPacket for SPlayerPosition {
const PACKET_ID: VarInt = 0x1A;
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self {
Self {
x: bytebuf.get_f64(),
feet_y: bytebuf.get_f64(),
z: bytebuf.get_f64(),
ground: bytebuf.get_bool(),
}
}
}
pub struct SPlayerPositionRotation {
pub x: f64,
pub feet_y: f64,
pub z: f64,
pub yaw: f32,
pub pitch: f32,
pub ground: bool,
}
impl ServerPacket for SPlayerPositionRotation {
const PACKET_ID: VarInt = 0x1B;
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self {
Self {
x: bytebuf.get_f64(),
feet_y: bytebuf.get_f64(),
z: bytebuf.get_f64(),
yaw: bytebuf.get_f32(),
pitch: bytebuf.get_f32(),
ground: bytebuf.get_bool(),
}
}
}
pub struct SPlayerRotation {
pub yaw: f32,
pub pitch: f32,
pub ground: bool,
}
impl ServerPacket for SPlayerRotation {
const PACKET_ID: VarInt = 0x1C;
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self {
Self {
yaw: bytebuf.get_f32(),
pitch: bytebuf.get_f32(),
ground: bytebuf.get_bool(),
}
}
}

View File

@ -72,8 +72,6 @@ impl ClientPacketProcessor for Client {
} }
fn handle_status_request(&mut self, server: &mut Server, _status_request: SStatusRequest) { fn handle_status_request(&mut self, server: &mut Server, _status_request: SStatusRequest) {
dbg!("sending status");
self.send_packet(CStatusResponse::new(&server.status_response_json)) self.send_packet(CStatusResponse::new(&server.status_response_json))
.unwrap_or_else(|e| self.kick(&e.to_string())); .unwrap_or_else(|e| self.kick(&e.to_string()));
} }

View File

@ -25,7 +25,7 @@ use pumpkin_protocol::{
config::{SAcknowledgeFinishConfig, SClientInformation, SKnownPacks, SPluginMessage}, config::{SAcknowledgeFinishConfig, SClientInformation, SKnownPacks, SPluginMessage},
handshake::SHandShake, handshake::SHandShake,
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
play::SConfirmTeleport, play::{SConfirmTeleport, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation},
status::{SPingRequest, SStatusRequest}, status::{SPingRequest, SStatusRequest},
}, },
text::Text, text::Text,
@ -109,10 +109,13 @@ impl Client {
Ok(()) Ok(())
} }
pub fn is_player(&self) -> bool {
self.player.is_some()
}
/// Im many cases we want to kick the Client when an Packet Error occours, But especially in the Client state we will not try to kick when not important packets /// Im many cases we want to kick the Client when an Packet Error occours, But especially in the Client state we will not try to kick when not important packets
/// e.g Postion, Rotation... has not been send /// e.g Postion, Rotation... has not been send
pub fn send_packet<P: ClientPacket>(&mut self, packet: P) -> Result<(), PacketError> { pub fn send_packet<P: ClientPacket>(&mut self, packet: P) -> Result<(), PacketError> {
dbg!("sending packet");
self.enc.append_packet(packet)?; self.enc.append_packet(packet)?;
self.connection self.connection
.write_all(&self.enc.take()) .write_all(&self.enc.take())
@ -121,10 +124,16 @@ impl Client {
} }
pub fn teleport(&mut self, x: f64, y: f64, z: f64, yaw: f32, pitch: f32) { pub fn teleport(&mut self, x: f64, y: f64, z: f64, yaw: f32, pitch: f32) {
assert!(self.player.is_some()); assert!(self.is_player());
// todo // todo
let id = 0; let id = 0;
self.player.as_mut().unwrap().awaiting_teleport = Some(0); let player = self.player.as_mut().unwrap();
player.x = x;
player.y = y;
player.z = z;
player.yaw = yaw;
player.pitch = pitch;
player.awaiting_teleport = Some(id);
self.send_packet(CSyncPlayerPostion::new(x, y, z, yaw, pitch, 0, id)) self.send_packet(CSyncPlayerPostion::new(x, y, z, yaw, pitch, 0, id))
.unwrap_or_else(|e| self.kick(&e.to_string())); .unwrap_or_else(|e| self.kick(&e.to_string()));
} }
@ -140,7 +149,6 @@ impl Client {
/// Handles an incoming decoded Packet /// Handles an incoming decoded Packet
pub fn handle_packet(&mut self, server: &mut Server, packet: &mut RawPacket) { pub fn handle_packet(&mut self, server: &mut Server, packet: &mut RawPacket) {
dbg!("Handling packet");
let bytebuf = &mut packet.bytebuf; let bytebuf = &mut packet.bytebuf;
match self.connection_state { match self.connection_state {
pumpkin_protocol::ConnectionState::HandShake => match packet.id { pumpkin_protocol::ConnectionState::HandShake => match packet.id {
@ -216,6 +224,15 @@ impl Client {
SConfirmTeleport::PACKET_ID => { SConfirmTeleport::PACKET_ID => {
self.handle_confirm_teleport(server, SConfirmTeleport::read(bytebuf)) self.handle_confirm_teleport(server, SConfirmTeleport::read(bytebuf))
} }
SPlayerPosition::PACKET_ID => {
self.handle_position(server, SPlayerPosition::read(bytebuf))
}
SPlayerPositionRotation::PACKET_ID => {
self.handle_position_rotation(server, SPlayerPositionRotation::read(bytebuf))
}
SPlayerRotation::PACKET_ID => {
self.handle_rotation(server, SPlayerRotation::read(bytebuf))
}
_ => log::error!("Failed to handle player packet id {}", packet.id), _ => log::error!("Failed to handle player packet id {}", packet.id),
} }
} }

View File

@ -1,4 +1,6 @@
use pumpkin_protocol::server::play::SConfirmTeleport; use pumpkin_protocol::server::play::{
SConfirmTeleport, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation,
};
use crate::server::Server; use crate::server::Server;
@ -7,6 +9,15 @@ use super::Client;
// implement player packets // implement player packets
pub trait PlayerPacketProcessor { pub trait PlayerPacketProcessor {
fn handle_confirm_teleport(&mut self, server: &mut Server, confirm_teleport: SConfirmTeleport); fn handle_confirm_teleport(&mut self, server: &mut Server, confirm_teleport: SConfirmTeleport);
fn handle_position(&mut self, server: &mut Server, position: SPlayerPosition);
fn handle_position_rotation(
&mut self,
server: &mut Server,
position_rotation: SPlayerPositionRotation,
);
fn handle_rotation(&mut self, server: &mut Server, rotation: SPlayerRotation);
} }
impl PlayerPacketProcessor for Client { impl PlayerPacketProcessor for Client {
@ -26,4 +37,45 @@ impl PlayerPacketProcessor for Client {
self.kick("Send Teleport confirm, but we did not teleport") self.kick("Send Teleport confirm, but we did not teleport")
} }
} }
fn handle_position(&mut self, _server: &mut Server, position: SPlayerPosition) {
if position.x.is_nan() || position.feet_y.is_nan() || position.z.is_nan() {
self.kick("Invalid movement");
}
let player = self.player.as_mut().unwrap();
player.x = position.x;
player.y = position.feet_y;
player.z = position.z;
}
fn handle_position_rotation(
&mut self,
_server: &mut Server,
position_rotation: SPlayerPositionRotation,
) {
if position_rotation.x.is_nan()
|| position_rotation.feet_y.is_nan()
|| position_rotation.z.is_nan()
{
self.kick("Invalid movement");
}
if !position_rotation.yaw.is_finite() || !position_rotation.pitch.is_finite() {
self.kick("Invalid rotation");
}
let player = self.player.as_mut().unwrap();
player.x = position_rotation.x;
player.y = position_rotation.feet_y;
player.z = position_rotation.z;
player.yaw = position_rotation.yaw;
player.pitch = position_rotation.pitch;
}
fn handle_rotation(&mut self, _server: &mut Server, rotation: SPlayerRotation) {
if !rotation.yaw.is_finite() || !rotation.pitch.is_finite() {
self.kick("Invalid rotation");
}
let player = self.player.as_mut().unwrap();
player.yaw = rotation.yaw;
player.pitch = rotation.pitch;
}
} }

View File

@ -4,6 +4,14 @@ use super::{Entity, EntityId};
pub struct Player { pub struct Player {
pub entity: Entity, pub entity: Entity,
pub x: f64,
pub y: f64,
pub z: f64,
pub yaw: f32,
pub pitch: f32,
// Client side value, Should be not trusted
pub on_ground: bool,
// Current awaiting teleport id, None if did not teleport // Current awaiting teleport id, None if did not teleport
pub awaiting_teleport: Option<VarInt>, pub awaiting_teleport: Option<VarInt>,
@ -13,6 +21,12 @@ impl Player {
pub fn new(entity: Entity) -> Self { pub fn new(entity: Entity) -> Self {
Self { Self {
entity, entity,
x: 0.0,
y: 0.0,
z: 0.0,
yaw: 0.0,
pitch: 0.0,
on_ground: false,
awaiting_teleport: None, awaiting_teleport: None,
} }
} }

View File

@ -17,7 +17,7 @@ pub mod util;
#[cfg(not(target_os = "wasi"))] #[cfg(not(target_os = "wasi"))]
fn main() -> io::Result<()> { fn main() -> io::Result<()> {
use std::{collections::HashMap, rc::Rc}; use std::{cell::RefCell, collections::HashMap, rc::Rc};
use client::Client; use client::Client;
use configuration::AdvancedConfiguration; use configuration::AdvancedConfiguration;
@ -51,7 +51,7 @@ fn main() -> io::Result<()> {
// Unique token for each incoming connection. // Unique token for each incoming connection.
let mut unique_token = Token(SERVER.0 + 1); let mut unique_token = Token(SERVER.0 + 1);
let mut connections: HashMap<Token, Client> = HashMap::new(); let mut connections: HashMap<Token, Rc<RefCell<Client>>> = HashMap::new();
log::info!("You now can connect to the server"); log::info!("You now can connect to the server");
@ -94,13 +94,20 @@ fn main() -> io::Result<()> {
token, token,
Interest::READABLE.add(Interest::WRITABLE), Interest::READABLE.add(Interest::WRITABLE),
)?; )?;
let rc_token = Rc::new(token);
connections.insert(token, Client::new(Rc::new(token), connection, addr)); let client = Rc::new(RefCell::new(Client::new(
Rc::clone(&rc_token),
connection,
addr,
)));
server.add_client(rc_token, Rc::clone(&client));
connections.insert(token, client);
}, },
token => { token => {
// Maybe received an event for a TCP connection. // Maybe received an event for a TCP connection.
let done = if let Some(client) = connections.get_mut(&token) { let done = if let Some(client) = connections.get_mut(&token) {
let mut client = client.borrow_mut();
client.poll(&mut server, event); client.poll(&mut server, event);
client.closed client.closed
} else { } else {
@ -108,7 +115,9 @@ fn main() -> io::Result<()> {
false false
}; };
if done { if done {
if let Some(mut client) = connections.remove(&token) { if let Some(client) = connections.remove(&token) {
let mut client = client.borrow_mut();
server.remove_client(&token);
poll.registry().deregister(&mut client.connection)?; poll.registry().deregister(&mut client.connection)?;
} }
} }

View File

@ -1,16 +1,19 @@
use std::{ use std::{
cell::RefCell,
collections::HashMap,
io::Cursor, io::Cursor,
rc::Rc,
sync::atomic::{AtomicI32, Ordering}, sync::atomic::{AtomicI32, Ordering},
}; };
use base64::{engine::general_purpose, Engine}; use base64::{engine::general_purpose, Engine};
use mio::{event::Event, Poll}; use mio::{event::Event, Poll, Token};
use pumpkin_protocol::{ use pumpkin_protocol::{
client::{ client::{
config::CPluginMessage, config::CPluginMessage,
play::{CChunkDataUpdateLight, CGameEvent, CLogin, CPlayerInfoUpdate, PlayerAction}, play::{CChunkDataUpdateLight, CGameEvent, CLogin, CPlayerInfoUpdate, PlayerAction},
}, },
BitSet, PacketError, Players, Sample, StatusResponse, VarInt, VarInt32, Version, BitSet, ClientPacket, PacketError, Players, Sample, StatusResponse, VarInt, VarInt32, Version,
}; };
use pumpkin_world::chunk::TestChunk; use pumpkin_world::chunk::TestChunk;
use rsa::{rand_core::OsRng, traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey}; use rsa::{rand_core::OsRng, traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
@ -41,6 +44,8 @@ pub struct Server {
/// Cache the Server brand buffer so we don't have to rebuild them every time a player joins /// Cache the Server brand buffer so we don't have to rebuild them every time a player joins
pub cached_server_brand: Vec<u8>, pub cached_server_brand: Vec<u8>,
pub current_clients: HashMap<Rc<Token>, Rc<RefCell<Client>>>,
// todo replace with HashMap <World, Player> // todo replace with HashMap <World, Player>
entity_id: AtomicI32, // todo: place this into every world entity_id: AtomicI32, // todo: place this into every world
pub base_config: BasicConfiguration, pub base_config: BasicConfiguration,
@ -83,6 +88,7 @@ impl Server {
status_response, status_response,
status_response_json, status_response_json,
public_key_der, public_key_der,
current_clients: HashMap::new(),
base_config: config.0, base_config: config.0,
auth_client, auth_client,
advanced_config: config.1, advanced_config: config.1,
@ -95,8 +101,17 @@ impl Server {
client.poll(self, event) client.poll(self, event)
} }
pub fn add_client(&mut self, token: Rc<Token>, client: Rc<RefCell<Client>>) {
self.current_clients.insert(token, client);
}
pub fn remove_client(&mut self, token: &Token) {
self.current_clients.remove(token);
}
// todo: do this in a world // todo: do this in a world
pub fn spawn_player(&mut self, client: &mut Client) { pub fn spawn_player(&mut self, client: &mut Client) {
// This code follows the vanilla packet order
dbg!("spawning player"); dbg!("spawning player");
let entity_id = self.new_entity_id(); let entity_id = self.new_entity_id();
let player = Player::new(Entity { entity_id }); let player = Player::new(Entity { entity_id });
@ -108,16 +123,16 @@ impl Server {
self.base_config.hardcore, self.base_config.hardcore,
vec!["minecraft:overworld".into()], vec!["minecraft:overworld".into()],
self.base_config.max_players as VarInt, self.base_config.max_players as VarInt,
8, // view distance todo self.base_config.view_distances as VarInt, // view distance todo
8, // sim view dinstance todo self.base_config.simulation_distance as VarInt, // sim view dinstance todo
false, false,
false, false,
false, false,
0, 0,
"minecraft:overworld".into(), "minecraft:overworld".into(),
0, // seed 0, // seed
GameMode::Spectator.to_byte() as u8, GameMode::Survival.to_byte() as u8,
GameMode::Spectator.to_byte(), GameMode::Survival.to_byte(),
false, false,
false, false,
false, // deth loc false, // deth loc
@ -130,6 +145,8 @@ impl Server {
// 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(); let gameprofile = client.gameprofile.as_ref().unwrap();
// first send info update to our new player, So he can see his Skin
// TODO: send more actions, (chat. list, ping)
client client
.send_packet(CPlayerInfoUpdate::new( .send_packet(CPlayerInfoUpdate::new(
0x01, 0x01,
@ -142,6 +159,7 @@ impl Server {
}], }],
)) ))
.unwrap_or_else(|e| client.kick(&e.to_string())); .unwrap_or_else(|e| client.kick(&e.to_string()));
// TODO: Now we send this to all other client
// Start waiting for level chunks // Start waiting for level chunks
client client
@ -151,6 +169,22 @@ impl Server {
// Server::spawn_test_chunk(client); // Server::spawn_test_chunk(client);
} }
/// Sends a Packet to all Players
fn broadcast_packet<P>(&mut self, packet: P) -> Result<(), PacketError>
where
P: ClientPacket,
P: Clone,
{
for client in self.current_clients.values() {
// Check if client is a player
if client.borrow().is_player() {
// we need to clone, Because we send a new packet to every client
client.borrow_mut().send_packet(packet.clone())?;
}
}
Ok(())
}
// todo: do this in a world // todo: do this in a world
fn _spawn_test_chunk(client: &mut Client) { fn _spawn_test_chunk(client: &mut Client) {
let test_chunk = TestChunk::new(); let test_chunk = TestChunk::new();