Add Multiplayer support

This commit is contained in:
Snowiiii 2024-08-06 16:19:03 +02:00
parent d1c3b2f10c
commit 32569bafe5
34 changed files with 445 additions and 243 deletions

9
Cargo.lock generated
View File

@ -1164,6 +1164,7 @@ dependencies = [
"num-bigint", "num-bigint",
"num-derive", "num-derive",
"num-traits", "num-traits",
"pumpkin-entity",
"pumpkin-protocol", "pumpkin-protocol",
"pumpkin-registry", "pumpkin-registry",
"pumpkin-world", "pumpkin-world",
@ -1180,6 +1181,14 @@ dependencies = [
"uuid", "uuid",
] ]
[[package]]
name = "pumpkin-entity"
version = "0.1.0"
dependencies = [
"num-derive",
"num-traits",
]
[[package]] [[package]]
name = "pumpkin-macros" name = "pumpkin-macros"
version = "0.1.0" version = "0.1.0"

View File

@ -1,6 +1,6 @@
[workspace] [workspace]
resolver = "2" resolver = "2"
members = [ "pumpkin-macros/", "pumpkin-protocol/", "pumpkin-registry/", "pumpkin-world", "pumpkin/" ] members = [ "pumpkin-entity", "pumpkin-macros/", "pumpkin-protocol/", "pumpkin-registry/", "pumpkin-world", "pumpkin/" ]
[workspace.package] [workspace.package]
version = "0.1.0" version = "0.1.0"

View File

@ -0,0 +1,8 @@
[package]
name = "pumpkin-entity"
version.workspace = true
edition.workspace = true
[dependencies]
num-traits = "0.2"
num-derive = "0.4"

View File

@ -0,0 +1,8 @@
use num_derive::ToPrimitive;
// todo
#[derive(ToPrimitive, Clone)]
pub enum EntityType {
Zombie = 124,
Player = 128,
}

19
pumpkin-entity/src/lib.rs Normal file
View File

@ -0,0 +1,19 @@
use entity_type::EntityType;
pub mod entity_type;
pub type EntityId = i32;
pub struct Entity {
pub entity_id: EntityId,
pub entity_type: EntityType,
}
impl Entity {
pub fn new(entity_id: EntityId, entity_type: EntityType) -> Self {
Self {
entity_id,
entity_type,
}
}
}

View File

@ -1,4 +1,4 @@
use crate::{BitSet, VarInt, VarLong}; use crate::{BitSet, VarInt, VarLongType};
use bytes::{Buf, BufMut, BytesMut}; use bytes::{Buf, BufMut, BytesMut};
use core::str; use core::str;
use std::io::{self, Error, ErrorKind}; use std::io::{self, Error, ErrorKind};
@ -44,10 +44,10 @@ impl ByteBuffer {
} }
} }
value VarInt(value)
} }
pub fn get_var_long(&mut self) -> VarLong { pub fn get_var_long(&mut self) -> VarLongType {
let mut value: i64 = 0; let mut value: i64 = 0;
let mut position: i64 = 0; let mut position: i64 = 0;
@ -75,7 +75,7 @@ impl ByteBuffer {
} }
pub fn get_string_len(&mut self, max_size: usize) -> Result<String, io::Error> { pub fn get_string_len(&mut self, max_size: usize) -> Result<String, io::Error> {
let size = self.get_var_int(); let size = self.get_var_int().0;
if size as usize > max_size { if size as usize > max_size {
return Err(Error::new( return Err(Error::new(
ErrorKind::InvalidData, ErrorKind::InvalidData,
@ -121,7 +121,7 @@ impl ByteBuffer {
} }
pub fn put_string(&mut self, val: &str) { pub fn put_string(&mut self, val: &str) {
self.put_var_int(val.len() as VarInt); self.put_var_int(&val.len().into());
self.buffer.put(val.as_bytes()); self.buffer.put(val.as_bytes());
} }
@ -131,8 +131,8 @@ impl ByteBuffer {
} }
} }
pub fn put_var_int(&mut self, value: VarInt) { pub fn put_var_int(&mut self, value: &VarInt) {
let mut val = value; let mut val = value.0;
for _ in 0..5 { for _ in 0..5 {
let mut b: u8 = val as u8 & 0b01111111; let mut b: u8 = val as u8 & 0b01111111;
val >>= 7; val >>= 7;
@ -147,7 +147,7 @@ impl ByteBuffer {
} }
pub fn put_bit_set(&mut self, set: &BitSet) { pub fn put_bit_set(&mut self, set: &BitSet) {
self.put_var_int(set.0); self.put_var_int(&set.0);
for b in &set.1 { for b in &set.1 {
self.put_i64(*b); self.put_i64(*b);
} }
@ -173,7 +173,7 @@ impl ByteBuffer {
} }
pub fn get_list<T>(&mut self, val: impl Fn(&mut Self) -> T) -> Vec<T> { pub fn get_list<T>(&mut self, val: impl Fn(&mut Self) -> T) -> Vec<T> {
let len = self.get_var_int().try_into().unwrap(); let len = self.get_var_int().0 as usize;
let mut list = Vec::with_capacity(len); let mut list = Vec::with_capacity(len);
for _ in 0..len { for _ in 0..len {
list.push(val(self)); list.push(val(self));
@ -182,14 +182,14 @@ impl ByteBuffer {
} }
/// Writes a list to the buffer. /// Writes a list to the buffer.
pub fn put_list<T>(&mut self, list: &[T], write: impl Fn(&mut Self, &T)) { pub fn put_list<T>(&mut self, list: &[T], write: impl Fn(&mut Self, &T)) {
self.put_var_int(list.len().try_into().unwrap()); self.put_var_int(&list.len().into());
for v in list { for v in list {
write(self, v); write(self, v);
} }
} }
pub fn put_varint_arr(&mut self, v: &[i32]) { pub fn put_varint_arr(&mut self, v: &[i32]) {
self.put_list(v, |p, &v| p.put_var_int(v)) self.put_list(v, |p, &v| p.put_var_int(&v.into()))
} }
/* pub fn get_nbt(&mut self) -> Option<fastnbt::value::Value> { /* pub fn get_nbt(&mut self) -> Option<fastnbt::value::Value> {

View File

@ -1,10 +1,10 @@
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use crate::{ClientPacket, VarInt, VarInt32}; use crate::{ClientPacket, VarInt, VarIntType};
use super::{serializer, ByteBuffer}; use super::{serializer, ByteBuffer};
impl Serialize for VarInt32 { impl Serialize for VarInt {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
S: Serializer, S: Serializer,
@ -27,7 +27,7 @@ impl Serialize for VarInt32 {
} }
pub trait Packet { pub trait Packet {
const PACKET_ID: VarInt; const PACKET_ID: VarIntType;
} }
impl<P> ClientPacket for P impl<P> ClientPacket for P

View File

@ -1,6 +1,6 @@
use pumpkin_macros::packet; use pumpkin_macros::packet;
use crate::{bytebuf::ByteBuffer, ClientPacket, VarInt}; use crate::{bytebuf::ByteBuffer, ClientPacket, VarIntType};
#[packet(0x01)] #[packet(0x01)]
pub struct CEncryptionRequest<'a> { pub struct CEncryptionRequest<'a> {
@ -29,9 +29,9 @@ impl<'a> CEncryptionRequest<'a> {
impl<'a> ClientPacket for CEncryptionRequest<'a> { impl<'a> ClientPacket for CEncryptionRequest<'a> {
fn write(&self, bytebuf: &mut ByteBuffer) { fn write(&self, bytebuf: &mut ByteBuffer) {
bytebuf.put_string(self.server_id); bytebuf.put_string(self.server_id);
bytebuf.put_var_int(self.public_key.len() as VarInt); bytebuf.put_var_int(&(self.public_key.len() as VarIntType).into());
bytebuf.put_slice(self.public_key); bytebuf.put_slice(self.public_key);
bytebuf.put_var_int(self.verify_token.len() as VarInt); bytebuf.put_var_int(&(self.verify_token.len() as VarIntType).into());
bytebuf.put_slice(self.verify_token); bytebuf.put_slice(self.verify_token);
bytebuf.put_bool(self.should_authenticate); bytebuf.put_bool(self.should_authenticate);
} }

View File

@ -1,16 +1,16 @@
use pumpkin_macros::packet; use pumpkin_macros::packet;
use serde::Serialize; use serde::Serialize;
use crate::VarInt32; use crate::VarInt;
#[derive(Serialize)] #[derive(Serialize)]
#[packet(0x03)] #[packet(0x03)]
pub struct CSetCompression { pub struct CSetCompression {
threshold: VarInt32, threshold: VarInt,
} }
impl CSetCompression { impl CSetCompression {
pub fn new(threshold: VarInt32) -> Self { pub fn new(threshold: VarInt) -> Self {
Self { threshold } Self { threshold }
} }
} }

View File

@ -1,6 +1,6 @@
use pumpkin_macros::packet; use pumpkin_macros::packet;
use crate::{bytebuf::ByteBuffer, BitSet, ClientPacket, VarInt}; use crate::{bytebuf::ByteBuffer, BitSet, ClientPacket, VarInt, VarIntType};
#[packet(0x27)] #[packet(0x27)]
pub struct CChunkDataUpdateLight { pub struct CChunkDataUpdateLight {
@ -50,23 +50,23 @@ impl ClientPacket for CChunkDataUpdateLight {
bytebuf.put_i32(self.chunk_x); bytebuf.put_i32(self.chunk_x);
bytebuf.put_i32(self.chunk_y); bytebuf.put_i32(self.chunk_y);
bytebuf.put_slice(&self.heightmaps); bytebuf.put_slice(&self.heightmaps);
bytebuf.put_var_int(self.data.len() as VarInt); bytebuf.put_var_int(&(self.data.len() as VarIntType).into());
bytebuf.put_slice(&self.data); bytebuf.put_slice(&self.data);
bytebuf.put_list::<BlockEntity>(&self.block_entites, |p, v| { bytebuf.put_list::<BlockEntity>(&self.block_entites, |p, v| {
p.put_u8(v.packed_xz); p.put_u8(v.packed_xz);
p.put_i16(v.y); p.put_i16(v.y);
p.put_var_int(v.typee); p.put_var_int(&v.typee);
p.put_slice(&v.data); p.put_slice(&v.data);
}); });
bytebuf.put_bit_set(&self.sky_light_mask); bytebuf.put_bit_set(&self.sky_light_mask);
bytebuf.put_bit_set(&self.block_light_mask); bytebuf.put_bit_set(&self.block_light_mask);
bytebuf.put_bit_set(&self.empty_sky_light_mask); bytebuf.put_bit_set(&self.empty_sky_light_mask);
bytebuf.put_list::<SkyLight>(&self.sky_lights, |p, v| { bytebuf.put_list::<SkyLight>(&self.sky_lights, |p, v| {
p.put_var_int(v.array.len() as VarInt); p.put_var_int(&(v.array.len() as VarIntType).into());
p.put_slice(&v.array); p.put_slice(&v.array);
}); });
bytebuf.put_list::<BlockLight>(&self.block_lights, |p, v| { bytebuf.put_list::<BlockLight>(&self.block_lights, |p, v| {
p.put_var_int(v.array.len() as VarInt); p.put_var_int(&(v.array.len() as VarIntType).into());
p.put_slice(&v.array); p.put_slice(&v.array);
}); });
} }

View File

@ -83,13 +83,13 @@ impl ClientPacket for CLogin {
bytebuf.put_i32(self.entity_id); bytebuf.put_i32(self.entity_id);
bytebuf.put_bool(self.is_hardcore); bytebuf.put_bool(self.is_hardcore);
bytebuf.put_list(&self.dimension_names, |buf, v| buf.put_string(v)); bytebuf.put_list(&self.dimension_names, |buf, v| buf.put_string(v));
bytebuf.put_var_int(self.max_players); bytebuf.put_var_int(&self.max_players);
bytebuf.put_var_int(self.view_distance); bytebuf.put_var_int(&self.view_distance);
bytebuf.put_var_int(self.simulated_distance); bytebuf.put_var_int(&self.simulated_distance);
bytebuf.put_bool(self.reduced_debug_info); bytebuf.put_bool(self.reduced_debug_info);
bytebuf.put_bool(self.enabled_respawn_screen); bytebuf.put_bool(self.enabled_respawn_screen);
bytebuf.put_bool(self.limited_crafting); bytebuf.put_bool(self.limited_crafting);
bytebuf.put_var_int(self.dimension_type); bytebuf.put_var_int(&self.dimension_type);
bytebuf.put_string(&self.dimension_name); bytebuf.put_string(&self.dimension_name);
bytebuf.put_i64(self.hashed_seed); bytebuf.put_i64(self.hashed_seed);
bytebuf.put_u8(self.game_mode); bytebuf.put_u8(self.game_mode);
@ -101,7 +101,7 @@ impl ClientPacket for CLogin {
bytebuf.put_string(self.death_dimension_name.as_ref().unwrap()); bytebuf.put_string(self.death_dimension_name.as_ref().unwrap());
bytebuf.put_i64(self.death_loc.unwrap()); bytebuf.put_i64(self.death_loc.unwrap());
} }
bytebuf.put_var_int(self.portal_cooldown); bytebuf.put_var_int(&self.portal_cooldown);
bytebuf.put_bool(self.enforce_secure_chat); bytebuf.put_bool(self.enforce_secure_chat);
} }
} }

View File

@ -4,15 +4,16 @@ use crate::{bytebuf::ByteBuffer, ClientPacket, Property};
use super::PlayerAction; use super::PlayerAction;
#[derive(Clone)]
#[packet(0x3E)] #[packet(0x3E)]
pub struct CPlayerInfoUpdate<'a> { pub struct CPlayerInfoUpdate<'a> {
pub actions: i8, pub actions: i8,
pub players: &'a [Player<'a>], pub players: &'a [Player],
} }
pub struct Player<'a> { pub struct Player {
pub uuid: uuid::Uuid, pub uuid: uuid::Uuid,
pub actions: &'a [PlayerAction], pub actions: Vec<PlayerAction>,
} }
impl<'a> CPlayerInfoUpdate<'a> { impl<'a> CPlayerInfoUpdate<'a> {
@ -26,7 +27,7 @@ impl<'a> ClientPacket for CPlayerInfoUpdate<'a> {
bytebuf.put_i8(self.actions); bytebuf.put_i8(self.actions);
bytebuf.put_list::<Player>(self.players, |p, v| { bytebuf.put_list::<Player>(self.players, |p, v| {
p.put_uuid(v.uuid); p.put_uuid(v.uuid);
for action in v.actions { for action in &v.actions {
match action { match action {
PlayerAction::AddPlayer { name, properties } => { PlayerAction::AddPlayer { name, properties } => {
p.put_string(name); p.put_string(name);

View File

@ -0,0 +1,73 @@
use pumpkin_macros::packet;
use crate::{ClientPacket, VarInt};
#[derive(Clone)]
#[packet(0x01)]
pub struct CSpawnEntity {
entity_id: VarInt,
entity_uuid: uuid::Uuid,
typ: VarInt,
x: f64,
y: f64,
z: f64,
pitch: u8, // angle
yaw: u8, // angle
head_yaw: u8, // angle
data: VarInt,
velocity_x: i16,
velocity_y: i16,
velocity_z: i16,
}
impl CSpawnEntity {
pub fn new(
entity_id: VarInt,
entity_uuid: uuid::Uuid,
typ: VarInt,
x: f64,
y: f64,
z: f64,
pitch: u8, // angle
yaw: u8, // angle
head_yaw: u8, // angle
data: VarInt,
velocity_x: i16,
velocity_y: i16,
velocity_z: i16,
) -> Self {
Self {
entity_id,
entity_uuid,
typ,
x,
y,
z,
pitch,
yaw,
head_yaw,
data,
velocity_x,
velocity_y,
velocity_z,
}
}
}
impl ClientPacket for CSpawnEntity {
fn write(&self, bytebuf: &mut crate::bytebuf::ByteBuffer) {
bytebuf.put_var_int(&self.entity_id);
bytebuf.put_uuid(self.entity_uuid);
bytebuf.put_var_int(&self.typ);
bytebuf.put_f64(self.x);
bytebuf.put_f64(self.y);
bytebuf.put_f64(self.z);
bytebuf.put_u8(self.pitch);
bytebuf.put_u8(self.yaw);
bytebuf.put_u8(self.head_yaw);
bytebuf.put_var_int(&self.data);
bytebuf.put_i16(self.velocity_x);
bytebuf.put_i16(self.velocity_y);
bytebuf.put_i16(self.velocity_z);
}
}

View File

@ -1,7 +1,7 @@
use pumpkin_macros::packet; use pumpkin_macros::packet;
use serde::Serialize; use serde::Serialize;
use crate::VarInt32; use crate::VarInt;
#[derive(Serialize)] #[derive(Serialize)]
#[packet(0x40)] #[packet(0x40)]
@ -12,7 +12,7 @@ pub struct CSyncPlayerPostion {
yaw: f32, yaw: f32,
pitch: f32, pitch: f32,
flags: i8, flags: i8,
teleport_id: VarInt32, teleport_id: VarInt,
} }
impl CSyncPlayerPostion { impl CSyncPlayerPostion {
@ -23,7 +23,7 @@ impl CSyncPlayerPostion {
yaw: f32, yaw: f32,
pitch: f32, pitch: f32,
flags: i8, flags: i8,
teleport_id: VarInt32, teleport_id: VarInt,
) -> Self { ) -> Self {
Self { Self {
x, x,

View File

@ -6,6 +6,7 @@ mod c_play_disconnect;
mod c_player_abilities; mod c_player_abilities;
mod c_player_info_update; mod c_player_info_update;
mod c_set_held_item; mod c_set_held_item;
mod c_spawn_player;
mod c_sync_player_position; mod c_sync_player_position;
mod c_system_chat_message; mod c_system_chat_message;
mod player_action; mod player_action;
@ -18,6 +19,7 @@ pub use c_play_disconnect::*;
pub use c_player_abilities::*; pub use c_player_abilities::*;
pub use c_player_info_update::*; pub use c_player_info_update::*;
pub use c_set_held_item::*; pub use c_set_held_item::*;
pub use c_spawn_player::*;
pub use c_sync_player_position::*; pub use c_sync_player_position::*;
pub use c_system_chat_message::*; pub use c_system_chat_message::*;
pub use player_action::*; pub use player_action::*;

View File

@ -19,14 +19,15 @@ pub const MAX_PACKET_SIZE: i32 = 2097152;
/// usally uses a namespace like "minecraft:thing" /// usally uses a namespace like "minecraft:thing"
pub type Identifier = String; pub type Identifier = String;
pub type VarInt = i32; pub type VarIntType = i32;
pub type VarLong = i64; pub type VarLongType = i64;
pub struct BitSet(pub VarInt, pub Vec<i64>); pub struct BitSet(pub VarInt, pub Vec<i64>);
pub struct VarInt32(pub i32); #[derive(Debug, Clone, PartialEq)]
pub struct VarInt(pub VarIntType);
impl VarInt32 { impl VarInt {
/// The maximum number of bytes a `VarInt` could occupy when read from and /// The maximum number of bytes a `VarInt` could occupy when read from and
/// written to the Minecraft protocol. /// written to the Minecraft protocol.
pub const MAX_SIZE: usize = 5; pub const MAX_SIZE: usize = 5;
@ -83,16 +84,40 @@ impl VarInt32 {
let byte = r.get_u8(); let byte = r.get_u8();
val |= (i32::from(byte) & 0b01111111) << (i * 7); val |= (i32::from(byte) & 0b01111111) << (i * 7);
if byte & 0b10000000 == 0 { if byte & 0b10000000 == 0 {
return Ok(VarInt32(val)); return Ok(VarInt(val));
} }
} }
Err(VarIntDecodeError::TooLarge) Err(VarIntDecodeError::TooLarge)
} }
} }
impl From<i32> for VarInt32 { impl From<i32> for VarInt {
fn from(value: i32) -> Self { fn from(value: i32) -> Self {
VarInt32(value) VarInt(value)
}
}
impl From<u32> for VarInt {
fn from(value: u32) -> Self {
VarInt(value as i32)
}
}
impl From<u8> for VarInt {
fn from(value: u8) -> Self {
VarInt(value as i32)
}
}
impl From<usize> for VarInt {
fn from(value: usize) -> Self {
VarInt(value as i32)
}
}
impl From<VarInt> for i32 {
fn from(value: VarInt) -> Self {
value.0
} }
} }
@ -134,6 +159,7 @@ pub enum ConnectionState {
impl From<VarInt> for ConnectionState { impl From<VarInt> for ConnectionState {
fn from(value: VarInt) -> Self { fn from(value: VarInt) -> Self {
let value = value.0;
match value { match value {
1 => Self::Status, 1 => Self::Status,
2 => Self::Login, 2 => Self::Login,
@ -147,7 +173,6 @@ impl From<VarInt> for ConnectionState {
} }
pub struct RawPacket { pub struct RawPacket {
pub len: VarInt,
pub id: VarInt, pub id: VarInt,
pub bytebuf: ByteBuffer, pub bytebuf: ByteBuffer,
} }

View File

@ -2,7 +2,7 @@ use aes::cipher::{generic_array::GenericArray, BlockDecryptMut, BlockSizeUser, K
use bytes::{Buf, BytesMut}; use bytes::{Buf, BytesMut};
use crate::{ use crate::{
bytebuf::ByteBuffer, PacketError, RawPacket, VarInt32, VarIntDecodeError, MAX_PACKET_SIZE, bytebuf::ByteBuffer, PacketError, RawPacket, VarInt, VarIntDecodeError, MAX_PACKET_SIZE,
}; };
type Cipher = cfb8::Decryptor<aes::Aes128>; type Cipher = cfb8::Decryptor<aes::Aes128>;
@ -19,7 +19,7 @@ impl PacketDecoder {
pub fn decode(&mut self) -> Result<Option<RawPacket>, PacketError> { pub fn decode(&mut self) -> Result<Option<RawPacket>, PacketError> {
let mut r = &self.buf[..]; let mut r = &self.buf[..];
let packet_len = match VarInt32::decode_partial(&mut r) { let packet_len = match VarInt::decode_partial(&mut r) {
Ok(len) => len, Ok(len) => len,
Err(VarIntDecodeError::Incomplete) => return Ok(None), Err(VarIntDecodeError::Incomplete) => return Ok(None),
Err(VarIntDecodeError::TooLarge) => Err(PacketError::MailformedLength)?, Err(VarIntDecodeError::TooLarge) => Err(PacketError::MailformedLength)?,
@ -34,7 +34,7 @@ impl PacketDecoder {
return Ok(None); return Ok(None);
} }
let packet_len_len = VarInt32(packet_len).written_size(); let packet_len_len = VarInt(packet_len).written_size();
let mut data; let mut data;
@ -44,13 +44,10 @@ impl PacketDecoder {
data = self.buf.split_to(packet_len as usize); data = self.buf.split_to(packet_len as usize);
r = &data[..]; r = &data[..];
let packet_id = VarInt32::decode(&mut r) let packet_id = VarInt::decode(&mut r).map_err(|_| PacketError::DecodeID)?;
.map_err(|_| PacketError::DecodeID)?
.0;
data.advance(data.len() - r.len()); data.advance(data.len() - r.len());
Ok(Some(RawPacket { Ok(Some(RawPacket {
len: packet_len,
id: packet_id, id: packet_id,
bytebuf: ByteBuffer::new(data), bytebuf: ByteBuffer::new(data),
})) }))

View File

@ -3,7 +3,7 @@ use std::io::Write;
use aes::cipher::{generic_array::GenericArray, BlockEncryptMut, BlockSizeUser, KeyIvInit}; use aes::cipher::{generic_array::GenericArray, BlockEncryptMut, BlockSizeUser, KeyIvInit};
use bytes::{BufMut, BytesMut}; use bytes::{BufMut, BytesMut};
use crate::{bytebuf::ByteBuffer, ClientPacket, PacketError, VarInt32, MAX_PACKET_SIZE}; use crate::{bytebuf::ByteBuffer, ClientPacket, PacketError, VarInt, MAX_PACKET_SIZE};
type Cipher = cfb8::Encryptor<aes::Aes128>; type Cipher = cfb8::Encryptor<aes::Aes128>;
@ -22,7 +22,7 @@ impl PacketEncoder {
let mut writer = (&mut self.buf).writer(); let mut writer = (&mut self.buf).writer();
let mut packet_buf = ByteBuffer::empty(); let mut packet_buf = ByteBuffer::empty();
VarInt32(P::PACKET_ID) VarInt(P::PACKET_ID)
.encode(&mut writer) .encode(&mut writer)
.map_err(|_| PacketError::EncodeID)?; .map_err(|_| PacketError::EncodeID)?;
packet.write(&mut packet_buf); packet.write(&mut packet_buf);
@ -41,14 +41,14 @@ impl PacketEncoder {
Err(PacketError::TooLong)? Err(PacketError::TooLong)?
} }
let packet_len_size = VarInt32(packet_len as i32).written_size(); let packet_len_size = VarInt(packet_len as i32).written_size();
self.buf.put_bytes(0, packet_len_size); self.buf.put_bytes(0, packet_len_size);
self.buf self.buf
.copy_within(start_len..start_len + data_len, start_len + packet_len_size); .copy_within(start_len..start_len + data_len, start_len + packet_len_size);
let front = &mut self.buf[start_len..]; let front = &mut self.buf[start_len..];
VarInt32(packet_len as i32) VarInt(packet_len as i32)
.encode(front) .encode(front)
.map_err(|_| PacketError::EncodeID)?; .map_err(|_| PacketError::EncodeID)?;
Ok(()) Ok(())

View File

@ -12,7 +12,7 @@ pub struct SClientInformation {
} }
impl ServerPacket for SClientInformation { impl ServerPacket for SClientInformation {
const PACKET_ID: VarInt = 0x00; const PACKET_ID: VarInt = VarInt(0x00);
fn read(bytebuf: &mut ByteBuffer) -> Self { fn read(bytebuf: &mut ByteBuffer) -> Self {
Self { Self {
@ -34,7 +34,7 @@ pub struct SPluginMessage {
} }
impl ServerPacket for SPluginMessage { impl ServerPacket for SPluginMessage {
const PACKET_ID: VarInt = 0x02; const PACKET_ID: VarInt = VarInt(0x02);
fn read(bytebuf: &mut ByteBuffer) -> Self { fn read(bytebuf: &mut ByteBuffer) -> Self {
Self { Self {
@ -47,7 +47,7 @@ impl ServerPacket for SPluginMessage {
pub struct SAcknowledgeFinishConfig {} pub struct SAcknowledgeFinishConfig {}
impl ServerPacket for SAcknowledgeFinishConfig { impl ServerPacket for SAcknowledgeFinishConfig {
const PACKET_ID: VarInt = 0x03; const PACKET_ID: VarInt = VarInt(0x03);
fn read(_bytebuf: &mut ByteBuffer) -> Self { fn read(_bytebuf: &mut ByteBuffer) -> Self {
Self {} Self {}
@ -60,7 +60,7 @@ pub struct SKnownPacks {
} }
impl ServerPacket for SKnownPacks { impl ServerPacket for SKnownPacks {
const PACKET_ID: VarInt = 0x07; const PACKET_ID: VarInt = VarInt(0x07);
fn read(bytebuf: &mut ByteBuffer) -> Self { fn read(bytebuf: &mut ByteBuffer) -> Self {
Self { Self {

View File

@ -8,7 +8,7 @@ pub struct SHandShake {
} }
impl ServerPacket for SHandShake { impl ServerPacket for SHandShake {
const PACKET_ID: VarInt = 0x00; const PACKET_ID: VarInt = VarInt(0x00);
fn read(bytebuf: &mut ByteBuffer) -> Self { fn read(bytebuf: &mut ByteBuffer) -> Self {
Self { Self {

View File

@ -6,7 +6,7 @@ pub struct SLoginStart {
} }
impl ServerPacket for SLoginStart { impl ServerPacket for SLoginStart {
const PACKET_ID: VarInt = 0x00; const PACKET_ID: VarInt = VarInt(0x00);
fn read(bytebuf: &mut ByteBuffer) -> Self { fn read(bytebuf: &mut ByteBuffer) -> Self {
Self { Self {
@ -24,13 +24,13 @@ pub struct SEncryptionResponse {
} }
impl ServerPacket for SEncryptionResponse { impl ServerPacket for SEncryptionResponse {
const PACKET_ID: VarInt = 0x01; const PACKET_ID: VarInt = VarInt(0x01);
fn read(bytebuf: &mut ByteBuffer) -> Self { fn read(bytebuf: &mut ByteBuffer) -> Self {
let shared_secret_length = bytebuf.get_var_int(); let shared_secret_length = bytebuf.get_var_int();
let shared_secret = bytebuf.copy_to_bytes(shared_secret_length as usize); let shared_secret = bytebuf.copy_to_bytes(shared_secret_length.0 as usize);
let verify_token_length = bytebuf.get_var_int(); let verify_token_length = bytebuf.get_var_int();
let verify_token = bytebuf.copy_to_bytes(shared_secret_length as usize); let verify_token = bytebuf.copy_to_bytes(shared_secret_length.0 as usize);
Self { Self {
shared_secret_length, shared_secret_length,
shared_secret: shared_secret.to_vec(), shared_secret: shared_secret.to_vec(),
@ -47,7 +47,7 @@ pub struct SLoginPluginResponse<'a> {
} }
impl<'a> ServerPacket for SLoginPluginResponse<'a> { impl<'a> ServerPacket for SLoginPluginResponse<'a> {
const PACKET_ID: VarInt = 0x02; const PACKET_ID: VarInt = VarInt(0x02);
fn read(bytebuf: &mut ByteBuffer) -> Self { fn read(bytebuf: &mut ByteBuffer) -> Self {
Self { Self {
@ -64,7 +64,7 @@ pub struct SLoginAcknowledged {
} }
impl ServerPacket for SLoginAcknowledged { impl ServerPacket for SLoginAcknowledged {
const PACKET_ID: VarInt = 0x03; const PACKET_ID: VarInt = VarInt(0x03);
fn read(_bytebuf: &mut ByteBuffer) -> Self { fn read(_bytebuf: &mut ByteBuffer) -> Self {
Self {} Self {}

View File

@ -8,7 +8,7 @@ pub struct SConfirmTeleport {
} }
impl ServerPacket for SConfirmTeleport { impl ServerPacket for SConfirmTeleport {
const PACKET_ID: VarInt = 0x00; const PACKET_ID: VarInt = VarInt(0x00);
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self { fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self {
Self { Self {
@ -22,7 +22,7 @@ pub struct SChatCommand {
} }
impl ServerPacket for SChatCommand { impl ServerPacket for SChatCommand {
const PACKET_ID: VarInt = 0x04; const PACKET_ID: VarInt = VarInt(0x04);
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self { fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self {
Self { Self {
@ -39,7 +39,7 @@ pub struct SPlayerPosition {
} }
impl ServerPacket for SPlayerPosition { impl ServerPacket for SPlayerPosition {
const PACKET_ID: VarInt = 0x1A; const PACKET_ID: VarInt = VarInt(0x1A);
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self { fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self {
Self { Self {
@ -69,12 +69,12 @@ pub enum Action {
} }
impl ServerPacket for SPlayerCommand { impl ServerPacket for SPlayerCommand {
const PACKET_ID: VarInt = 0x25; const PACKET_ID: VarInt = VarInt(0x25);
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self { fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self {
Self { Self {
entitiy_id: bytebuf.get_var_int(), entitiy_id: bytebuf.get_var_int(),
action: Action::from_i32(bytebuf.get_var_int()).unwrap(), action: Action::from_i32(bytebuf.get_var_int().into()).unwrap(),
jump_boost: bytebuf.get_var_int(), jump_boost: bytebuf.get_var_int(),
} }
} }
@ -90,7 +90,7 @@ pub struct SPlayerPositionRotation {
} }
impl ServerPacket for SPlayerPositionRotation { impl ServerPacket for SPlayerPositionRotation {
const PACKET_ID: VarInt = 0x1B; const PACKET_ID: VarInt = VarInt(0x1B);
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self { fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self {
Self { Self {
@ -111,7 +111,7 @@ pub struct SPlayerRotation {
} }
impl ServerPacket for SPlayerRotation { impl ServerPacket for SPlayerRotation {
const PACKET_ID: VarInt = 0x1C; const PACKET_ID: VarInt = VarInt(0x1C);
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self { fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self {
Self { Self {

View File

@ -5,7 +5,7 @@ pub struct SStatusRequest {
} }
impl ServerPacket for SStatusRequest { impl ServerPacket for SStatusRequest {
const PACKET_ID: VarInt = 0x00; const PACKET_ID: VarInt = VarInt(0x00);
fn read(_bytebuf: &mut ByteBuffer) -> Self { fn read(_bytebuf: &mut ByteBuffer) -> Self {
Self {} Self {}
@ -17,7 +17,7 @@ pub struct SPingRequest {
} }
impl ServerPacket for SPingRequest { impl ServerPacket for SPingRequest {
const PACKET_ID: VarInt = 0x01; const PACKET_ID: VarInt = VarInt(0x01);
fn read(bytebuf: &mut ByteBuffer) -> Self { fn read(bytebuf: &mut ByteBuffer) -> Self {
Self { Self {

View File

@ -6,6 +6,7 @@ edition = "2021"
[dependencies] [dependencies]
pumpkin-world = { path = "../pumpkin-world"} pumpkin-world = { path = "../pumpkin-world"}
pumpkin-entity = { path = "../pumpkin-entity"}
pumpkin-protocol = { path = "../pumpkin-protocol"} pumpkin-protocol = { path = "../pumpkin-protocol"}
pumpkin-registry = { path = "../pumpkin-registry"} pumpkin-registry = { path = "../pumpkin-registry"}

View File

@ -35,14 +35,12 @@ impl Client {
} }
pub fn handle_status_request(&mut self, server: &mut Server, _status_request: SStatusRequest) { pub fn handle_status_request(&mut self, server: &mut Server, _status_request: SStatusRequest) {
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()));
} }
pub fn handle_ping_request(&mut self, _server: &mut Server, ping_request: SPingRequest) { pub fn handle_ping_request(&mut self, _server: &mut Server, ping_request: SPingRequest) {
dbg!("ping"); dbg!("ping");
self.send_packet(CPingResponse::new(ping_request.payload)) self.send_packet(CPingResponse::new(ping_request.payload));
.unwrap_or_else(|e| self.kick(&e.to_string()));
self.close(); self.close();
} }
@ -64,8 +62,7 @@ impl Client {
&verify_token, &verify_token,
server.base_config.online_mode, // TODO server.base_config.online_mode, // TODO
); );
self.send_packet(packet) self.send_packet(packet);
.unwrap_or_else(|e| self.kick(&e.to_string()));
} }
pub fn handle_encryption_response( pub fn handle_encryption_response(
@ -101,8 +98,7 @@ impl Client {
if let Some(profile) = self.gameprofile.as_ref().cloned() { if let Some(profile) = self.gameprofile.as_ref().cloned() {
let packet = CLoginSuccess::new(profile.id, profile.name, &profile.properties, false); let packet = CLoginSuccess::new(profile.id, profile.name, &profile.properties, false);
self.send_packet(packet) self.send_packet(packet);
.unwrap_or_else(|e| self.kick(&e.to_string()));
} else { } else {
self.kick("game profile is none"); self.kick("game profile is none");
} }
@ -121,16 +117,13 @@ impl Client {
_login_acknowledged: SLoginAcknowledged, _login_acknowledged: SLoginAcknowledged,
) { ) {
self.connection_state = ConnectionState::Config; self.connection_state = ConnectionState::Config;
server server.send_brand(self);
.send_brand(self)
.unwrap_or_else(|e| self.kick(&e.to_string()));
// known data packs // known data packs
self.send_packet(CKnownPacks::new(&[KnownPack { self.send_packet(CKnownPacks::new(&[KnownPack {
namespace: "minecraft", namespace: "minecraft",
id: "core", id: "core",
version: "1.21", version: "1.21",
}])) }]));
.unwrap_or_else(|e| self.kick(&e.to_string()));
dbg!("login achnowlaged"); dbg!("login achnowlaged");
} }
pub fn handle_client_information( pub fn handle_client_information(
@ -141,10 +134,10 @@ impl Client {
self.config = Some(PlayerConfig { self.config = Some(PlayerConfig {
locale: client_information.locale, locale: client_information.locale,
view_distance: client_information.view_distance, view_distance: client_information.view_distance,
chat_mode: ChatMode::from_i32(client_information.chat_mode).unwrap(), chat_mode: ChatMode::from_i32(client_information.chat_mode.into()).unwrap(),
chat_colors: client_information.chat_colors, chat_colors: client_information.chat_colors,
skin_parts: client_information.skin_parts, skin_parts: client_information.skin_parts,
main_hand: Hand::from_i32(client_information.main_hand).unwrap(), main_hand: Hand::from_i32(client_information.main_hand.into()).unwrap(),
text_filtering: client_information.text_filtering, text_filtering: client_information.text_filtering,
server_listing: client_information.server_listing, server_listing: client_information.server_listing,
}); });
@ -167,14 +160,12 @@ impl Client {
self.send_packet(CRegistryData::new( self.send_packet(CRegistryData::new(
&registry.registry_id, &registry.registry_id,
&registry.registry_entries, &registry.registry_entries,
)) ));
.unwrap_or_else(|e| self.kick(&e.to_string()));
} }
// We are done with configuring // We are done with configuring
dbg!("finish config"); dbg!("finish config");
self.send_packet(CFinishConfig::new()) self.send_packet(CFinishConfig::new());
.unwrap_or_else(|e| self.kick(&e.to_string()));
} }
pub fn handle_config_acknowledged( pub fn handle_config_acknowledged(

View File

@ -114,9 +114,18 @@ impl Client {
self.player.is_some() 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 /// Send a Clientbound Packet to the Client
/// e.g Postion, Rotation... has not been send pub fn send_packet<P: ClientPacket>(&mut self, packet: P) {
pub fn send_packet<P: ClientPacket>(&mut self, packet: P) -> Result<(), PacketError> { self.enc
.append_packet(packet)
.unwrap_or_else(|e| self.kick(&e.to_string()));
self.connection
.write_all(&self.enc.take())
.map_err(|_| PacketError::ConnectionWrite)
.unwrap_or_else(|e| self.kick(&e.to_string()));
}
pub fn try_send_packet<P: ClientPacket>(&mut self, packet: P) -> Result<(), PacketError> {
self.enc.append_packet(packet)?; self.enc.append_packet(packet)?;
self.connection self.connection
.write_all(&self.enc.take()) .write_all(&self.enc.take())
@ -134,14 +143,12 @@ impl Client {
player.z = z; player.z = z;
player.yaw = yaw; player.yaw = yaw;
player.pitch = pitch; player.pitch = pitch;
player.awaiting_teleport = Some(id); player.awaiting_teleport = Some(id.into());
self.send_packet(CSyncPlayerPostion::new(x, y, z, yaw, pitch, 0, id.into())) self.send_packet(CSyncPlayerPostion::new(x, y, z, yaw, pitch, 0, id.into()));
.unwrap_or_else(|e| self.kick(&e.to_string()));
} }
pub fn set_gamemode(&mut self, gamemode: GameMode) { pub fn set_gamemode(&mut self, gamemode: GameMode) {
self.send_packet(CGameEvent::new(3, gamemode.to_f32().unwrap())) self.send_packet(CGameEvent::new(3, gamemode.to_f32().unwrap()));
.unwrap_or_else(|e| self.kick(&e.to_string()));
} }
pub fn process_packets(&mut self, server: &mut Server) { pub fn process_packets(&mut self, server: &mut Server) {
@ -161,7 +168,7 @@ impl Client {
SHandShake::PACKET_ID => self.handle_handshake(server, SHandShake::read(bytebuf)), SHandShake::PACKET_ID => self.handle_handshake(server, SHandShake::read(bytebuf)),
_ => log::error!( _ => log::error!(
"Failed to handle packet id {} while in Handshake state", "Failed to handle packet id {} while in Handshake state",
packet.id packet.id.0
), ),
}, },
pumpkin_protocol::ConnectionState::Status => match packet.id { pumpkin_protocol::ConnectionState::Status => match packet.id {
@ -173,7 +180,7 @@ impl Client {
} }
_ => log::error!( _ => log::error!(
"Failed to handle packet id {} while in Status state", "Failed to handle packet id {} while in Status state",
packet.id packet.id.0
), ),
}, },
pumpkin_protocol::ConnectionState::Login => match packet.id { pumpkin_protocol::ConnectionState::Login => match packet.id {
@ -191,7 +198,7 @@ impl Client {
} }
_ => log::error!( _ => log::error!(
"Failed to handle packet id {} while in Login state", "Failed to handle packet id {} while in Login state",
packet.id packet.id.0
), ),
}, },
pumpkin_protocol::ConnectionState::Config => match packet.id { pumpkin_protocol::ConnectionState::Config => match packet.id {
@ -209,7 +216,7 @@ impl Client {
} }
_ => log::error!( _ => log::error!(
"Failed to handle packet id {} while in Config state", "Failed to handle packet id {} while in Config state",
packet.id packet.id.0
), ),
}, },
pumpkin_protocol::ConnectionState::Play => { pumpkin_protocol::ConnectionState::Play => {
@ -245,7 +252,7 @@ impl Client {
SPlayerCommand::PACKET_ID => { SPlayerCommand::PACKET_ID => {
self.handle_player_command(server, SPlayerCommand::read(bytebuf)) self.handle_player_command(server, SPlayerCommand::read(bytebuf))
} }
_ => log::error!("Failed to handle player packet id {}", packet.id), _ => log::error!("Failed to handle player packet id {}", packet.id.0),
} }
} }
@ -296,9 +303,8 @@ impl Client {
} }
} }
pub fn send_message(&mut self, text: TextComponent) { pub fn send_system_message(&mut self, text: TextComponent) {
self.send_packet(CSystemChatMessge::new(text, false)) self.send_packet(CSystemChatMessge::new(text, false));
.unwrap_or_else(|e| self.kick(&e.to_string()));
} }
/// Kicks the Client with a reason depending on the connection state /// Kicks the Client with a reason depending on the connection state
@ -306,17 +312,17 @@ impl Client {
dbg!(reason); dbg!(reason);
match self.connection_state { match self.connection_state {
ConnectionState::Login => { ConnectionState::Login => {
self.send_packet(CLoginDisconnect::new( self.try_send_packet(CLoginDisconnect::new(
&serde_json::to_string_pretty(&reason).unwrap(), &serde_json::to_string_pretty(&reason).unwrap(),
)) ))
.unwrap_or_else(|_| self.close()); .unwrap_or_else(|_| self.close());
} }
ConnectionState::Config => { ConnectionState::Config => {
self.send_packet(CConfigDisconnect::new(reason)) self.try_send_packet(CConfigDisconnect::new(reason))
.unwrap_or_else(|_| self.close()); .unwrap_or_else(|_| self.close());
} }
ConnectionState::Play => { ConnectionState::Play => {
self.send_packet(CPlayDisconnect::new(TextComponent { self.try_send_packet(CPlayDisconnect::new(TextComponent {
text: reason.to_string(), text: reason.to_string(),
})) }))
.unwrap_or_else(|_| self.close()); .unwrap_or_else(|_| self.close());

View File

@ -18,7 +18,7 @@ impl Client {
confirm_teleport: SConfirmTeleport, confirm_teleport: SConfirmTeleport,
) { ) {
let player = self.player.as_mut().unwrap(); let player = self.player.as_mut().unwrap();
if let Some(id) = player.awaiting_teleport { if let Some(id) = player.awaiting_teleport.clone() {
if id == confirm_teleport.teleport_id { if id == confirm_teleport.teleport_id {
} else { } else {
log::warn!("Teleport id does not match, Weird but okay"); log::warn!("Teleport id does not match, Weird but okay");
@ -71,13 +71,13 @@ impl Client {
} }
pub fn handle_chat_command(&mut self, server: &mut Server, command: SChatCommand) { pub fn handle_chat_command(&mut self, server: &mut Server, command: SChatCommand) {
handle_command(&mut CommandSender::Player(self), command.command, server); handle_command(&mut CommandSender::Player(self), command.command);
} }
pub fn handle_player_command(&mut self, _server: &mut Server, command: SPlayerCommand) { pub fn handle_player_command(&mut self, _server: &mut Server, command: SPlayerCommand) {
let player = self.player.as_mut().unwrap(); let player = self.player.as_mut().unwrap();
if command.entitiy_id != player.entity.entity_id { if command.entitiy_id != player.entity.entity_id.into() {
return; return;
} }

View File

@ -13,17 +13,13 @@ impl<'a> Command<'a> for GamemodeCommand {
const DESCRIPTION: &'a str = "Changes the gamemode for a Player"; const DESCRIPTION: &'a str = "Changes the gamemode for a Player";
fn on_execute( fn on_execute(sender: &mut super::CommandSender<'a>, command: String) {
sender: &mut super::CommandSender<'a>,
command: String,
_server: &mut crate::server::Server,
) {
let player = sender.as_mut_player().unwrap(); let player = sender.as_mut_player().unwrap();
let args: Vec<&str> = command.split_whitespace().collect(); let args: Vec<&str> = command.split_whitespace().collect();
if args.len() != 2 { if args.len() != 2 {
// todo red // todo red
player.send_message("Usage: /gamemode <mode>".into()); player.send_system_message("Usage: /gamemode <mode>".into());
return; return;
} }
@ -31,11 +27,11 @@ impl<'a> Command<'a> for GamemodeCommand {
match mode_str.parse() { match mode_str.parse() {
Ok(mode) => { Ok(mode) => {
player.set_gamemode(mode); player.set_gamemode(mode);
player.send_message(format!("Set own game mode to {mode_str}").into()); player.send_system_message(format!("Set own game mode to {mode_str}").into());
} }
Err(_) => { Err(_) => {
// todo red // todo red
player.send_message("Invalid gamemode".into()); player.send_system_message("Invalid gamemode".into());
} }
} }
} }

View File

@ -2,7 +2,7 @@ use gamemode::GamemodeCommand;
use pumpkin::PumpkinCommand; use pumpkin::PumpkinCommand;
use pumpkin_protocol::text::TextComponent; use pumpkin_protocol::text::TextComponent;
use crate::{client::Client, server::Server}; use crate::client::Client;
mod gamemode; mod gamemode;
mod pumpkin; mod pumpkin;
@ -13,7 +13,7 @@ pub trait Command<'a> {
const NAME: &'a str; const NAME: &'a str;
const DESCRIPTION: &'a str; const DESCRIPTION: &'a str;
fn on_execute(sender: &mut CommandSender<'a>, command: String, server: &mut Server); fn on_execute(sender: &mut CommandSender<'a>, command: String);
/// Specifies wether the Command Sender has to be a Player /// Specifies wether the Command Sender has to be a Player
fn player_required() -> bool { fn player_required() -> bool {
@ -31,7 +31,7 @@ impl<'a> CommandSender<'a> {
match self { match self {
// todo: add color and stuff to console // todo: add color and stuff to console
CommandSender::Console => log::info!("{}", text.text), CommandSender::Console => log::info!("{}", text.text),
CommandSender::Player(c) => c.send_message(text), CommandSender::Player(c) => c.send_system_message(text),
} }
} }
@ -55,15 +55,15 @@ impl<'a> CommandSender<'a> {
} }
} }
} }
pub fn handle_command(sender: &mut CommandSender, command: String, server: &mut Server) { pub fn handle_command(sender: &mut CommandSender, command: String) {
let command = command.to_lowercase(); let command = command.to_lowercase();
// an ugly mess i know // an ugly mess i know
if command.starts_with(PumpkinCommand::NAME) { if command.starts_with(PumpkinCommand::NAME) {
PumpkinCommand::on_execute(sender, command, server); PumpkinCommand::on_execute(sender, command);
return; return;
} }
if command.starts_with(GamemodeCommand::NAME) { if command.starts_with(GamemodeCommand::NAME) {
GamemodeCommand::on_execute(sender, command, server); GamemodeCommand::on_execute(sender, command);
return; return;
} }
// todo: red color // todo: red color

View File

@ -17,11 +17,7 @@ impl<'a> Command<'a> for PumpkinCommand {
const DESCRIPTION: &'a str = "Displays information about Pumpkin"; const DESCRIPTION: &'a str = "Displays information about Pumpkin";
fn on_execute( fn on_execute(sender: &mut super::CommandSender<'a>, _command: String) {
sender: &mut super::CommandSender<'a>,
_command: String,
_server: &mut crate::server::Server,
) {
let version = env!("CARGO_PKG_VERSION"); let version = env!("CARGO_PKG_VERSION");
let description = env!("CARGO_PKG_DESCRIPTION"); let description = env!("CARGO_PKG_DESCRIPTION");
sender.send_message(TextComponent::from(format!("Pumpkin {version}, {description} (Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL})"))) sender.send_message(TextComponent::from(format!("Pumpkin {version}, {description} (Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL})")))

View File

@ -1,13 +1 @@
pub mod player; pub mod player;
pub type EntityId = i32;
pub struct Entity {
pub entity_id: EntityId,
}
impl Entity {
pub fn new(entity_id: EntityId) -> Self {
Self { entity_id }
}
}

View File

@ -1,11 +1,10 @@
use std::str::FromStr; use std::str::FromStr;
use num_derive::{FromPrimitive, ToPrimitive}; use num_derive::{FromPrimitive, ToPrimitive};
use pumpkin_entity::{entity_type::EntityType, Entity, EntityId};
use pumpkin_protocol::VarInt; use pumpkin_protocol::VarInt;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::{Entity, EntityId};
pub struct Player { pub struct Player {
pub entity: Entity, pub entity: Entity,
pub x: f64, pub x: f64,
@ -25,9 +24,12 @@ pub struct Player {
} }
impl Player { impl Player {
pub fn new(entity: Entity) -> Self { pub fn new(entity_id: EntityId) -> Self {
Self { Self {
entity, entity: Entity {
entity_id,
entity_type: EntityType::Player,
},
x: 0.0, x: 0.0,
y: 0.0, y: 0.0,
z: 0.0, z: 0.0,

View File

@ -29,7 +29,7 @@ pub mod util;
#[cfg(not(target_os = "wasi"))] #[cfg(not(target_os = "wasi"))]
fn main() -> io::Result<()> { fn main() -> io::Result<()> {
use std::time::Instant; use std::{cell::RefCell, time::Instant};
let time = Instant::now(); let time = Instant::now();
let basic_config = BasicConfiguration::load("configuration.toml"); let basic_config = BasicConfiguration::load("configuration.toml");
@ -63,7 +63,7 @@ fn main() -> io::Result<()> {
let use_console = advanced_configuration.commands.use_console; let use_console = advanced_configuration.commands.use_console;
let mut connections: HashMap<Token, Client> = HashMap::new(); let mut connections: HashMap<Token, Rc<RefCell<Client>>> = HashMap::new();
let server = Arc::new(Mutex::new(Server::new(( let server = Arc::new(Mutex::new(Server::new((
basic_config, basic_config,
@ -73,7 +73,6 @@ fn main() -> io::Result<()> {
log::info!("You now can connect to the server"); log::info!("You now can connect to the server");
if use_console { if use_console {
let server_clone = server.clone();
thread::spawn(move || { thread::spawn(move || {
let stdin = std::io::stdin(); let stdin = std::io::stdin();
loop { loop {
@ -81,11 +80,7 @@ fn main() -> io::Result<()> {
stdin stdin
.read_line(&mut out) .read_line(&mut out)
.expect("Failed to read console line"); .expect("Failed to read console line");
handle_command( handle_command(&mut commands::CommandSender::Console, out);
&mut commands::CommandSender::Console,
out,
&mut server_clone.lock().unwrap(),
);
} }
}); });
} }
@ -127,14 +122,22 @@ fn main() -> io::Result<()> {
Interest::READABLE.add(Interest::WRITABLE), Interest::READABLE.add(Interest::WRITABLE),
)?; )?;
let rc_token = Rc::new(token); let rc_token = Rc::new(token);
let client = Client::new(Rc::clone(&rc_token), connection, addr); let client = Rc::new(RefCell::new(Client::new(
// server.add_client(rc_token, Rc::clone(&client)); Rc::clone(&rc_token),
connection,
addr,
)));
server
.lock()
.unwrap()
.add_client(rc_token, Rc::clone(&client));
connections.insert(token, 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.lock().unwrap(), event); client.poll(&mut server.lock().unwrap(), event);
client.closed client.closed
} else { } else {
@ -142,8 +145,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) {
// server.remove_client(&token); let mut client = client.borrow_mut();
server.lock().unwrap().remove_client(&token);
poll.registry().deregister(&mut client.connection)?; poll.registry().deregister(&mut client.connection)?;
} }
} }

View File

@ -1,5 +1,6 @@
use std::{ use std::{
cell::RefCell, cell::RefCell,
collections::HashMap,
io::Cursor, io::Cursor,
rc::Rc, rc::Rc,
sync::atomic::{AtomicI32, Ordering}, sync::atomic::{AtomicI32, Ordering},
@ -8,13 +9,17 @@ use std::{
use base64::{engine::general_purpose, Engine}; use base64::{engine::general_purpose, Engine};
use mio::{event::Event, Poll, Token}; use mio::{event::Event, Poll, Token};
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
use pumpkin_entity::{entity_type::EntityType, EntityId};
use pumpkin_protocol::{ use pumpkin_protocol::{
client::{ client::{
config::CPluginMessage, config::CPluginMessage,
play::{CChunkDataUpdateLight, CGameEvent, CLogin, CPlayerInfoUpdate, PlayerAction}, play::{
CChunkDataUpdateLight, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate,
CSpawnEntity, PlayerAction,
}, },
BitSet, ClientPacket, PacketError, Players, Sample, StatusResponse, VarInt, VarInt32, Version, },
CURRENT_MC_PROTOCOL, BitSet, ClientPacket, Players, Sample, StatusResponse, VarInt,
Version, CURRENT_MC_PROTOCOL,
}; };
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};
@ -23,10 +28,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
client::Client, client::Client,
configuration::{AdvancedConfiguration, BasicConfiguration}, configuration::{AdvancedConfiguration, BasicConfiguration},
entity::{ entity::player::{GameMode, Player},
player::{GameMode, Player},
Entity, EntityId,
},
}; };
pub const CURRENT_MC_VERSION: &str = "1.21"; pub const CURRENT_MC_VERSION: &str = "1.21";
@ -47,7 +49,7 @@ 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>>>, 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
@ -90,7 +92,7 @@ impl Server {
status_response, status_response,
status_response_json, status_response_json,
public_key_der, public_key_der,
// current_clients: HashMap::new(), current_clients: HashMap::new(),
base_config: config.0, base_config: config.0,
auth_client, auth_client,
advanced_config: config.1, advanced_config: config.1,
@ -103,34 +105,35 @@ impl Server {
client.poll(self, event) client.poll(self, event)
} }
pub fn add_client(&mut self, _token: Rc<Token>, _client: Rc<RefCell<Client>>) { pub fn add_client(&mut self, token: Rc<Token>, client: Rc<RefCell<Client>>) {
// self.current_clients.insert(token, client); self.current_clients.insert(token, client);
} }
pub fn remove_client(&mut self, _token: &Token) { pub fn remove_client(&mut self, token: &Token) {
// self.current_clients.remove(token); self.current_clients.remove(token);
} }
// here is where the magic happens
// 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 // 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_id);
client.player = Some(player); client.player = Some(player);
client // login packet for our new player
.send_packet(CLogin::new( client.send_packet(CLogin::new(
entity_id, entity_id,
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.into(),
self.base_config.view_distance as VarInt, // view distance todo self.base_config.view_distance.into(), // view distance todo
self.base_config.simulation_distance as VarInt, // sim view dinstance todo self.base_config.simulation_distance.into(), // sim view dinstance todo
false, false,
false, false,
false, false,
0, 0.into(),
"minecraft:overworld".into(), "minecraft:overworld".into(),
0, // seed 0, // seed
match self.base_config.default_gamemode { match self.base_config.default_gamemode {
@ -145,72 +148,145 @@ impl Server {
false, // deth loc false, // deth loc
None, None,
None, None,
0, 0.into(),
false, false,
)) ));
.unwrap_or_else(|e| client.kick(&e.to_string())); // player abilities
client.send_packet(CPlayerAbilities::new(0x02, 00.2, 0.1));
// teleport // teleport
client.teleport(10.0, 500.0, 10.0, 10.0, 10.0); let x = 10.0;
let y = 500.0;
let z = 10.0;
client.teleport(x, y, z, 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 // first send info update to our new player, So he can see his Skin
// TODO: send more actions, (chat. list, ping) // TODO: send more actions, (chat. list, ping)
client client.send_packet(CPlayerInfoUpdate::new(
.send_packet(CPlayerInfoUpdate::new(
0x01, 0x01,
&[pumpkin_protocol::client::play::Player { &[pumpkin_protocol::client::play::Player {
uuid: gameprofile.id, uuid: gameprofile.id,
actions: &[PlayerAction::AddPlayer { actions: vec![PlayerAction::AddPlayer {
name: gameprofile.name.clone(), name: gameprofile.name.clone(),
properties: gameprofile.properties.clone(), properties: gameprofile.properties.clone(),
}], }],
}], }],
)) ));
.unwrap_or_else(|e| client.kick(&e.to_string())); let gameprofile = client.gameprofile.as_ref().unwrap();
// TODO: Now we send this to all other client // send his info to everyone else
self.broadcast_packet(
client,
CPlayerInfoUpdate::new(
0x01,
&[pumpkin_protocol::client::play::Player {
uuid: gameprofile.id,
actions: vec![PlayerAction::AddPlayer {
name: gameprofile.name.clone(),
properties: gameprofile.properties.clone(),
}],
}],
),
);
// here we send all the infos of already joined players
let mut entries = Vec::new();
for (_, client) in self.current_clients.iter().filter(|c| c.0 != &client.token) {
let client = client.borrow();
if client.is_player() {
let gameprofile = client.gameprofile.as_ref().unwrap();
entries.push(pumpkin_protocol::client::play::Player {
uuid: gameprofile.id,
actions: vec![PlayerAction::AddPlayer {
name: gameprofile.name.clone(),
properties: gameprofile.properties.clone(),
}],
})
}
}
client.send_packet(CPlayerInfoUpdate::new(0x01, &entries));
// Start waiting for level chunks // Start waiting for level chunks
client client.send_packet(CGameEvent::new(13, 0.0));
.send_packet(CGameEvent::new(13, 0.0))
.unwrap_or_else(|e| client.kick(&e.to_string())); let gameprofile = client.gameprofile.as_ref().unwrap();
// spawn player for every client
self.broadcast_packet(
client,
CSpawnEntity::new(
entity_id.into(),
gameprofile.id,
EntityType::Player.to_i32().unwrap().into(),
x,
y,
z,
0,
0,
0,
0.into(),
0,
0,
0,
),
);
// spawn players for our client
let token = client.token.clone();
for (_, existing_client) in self.current_clients.iter().filter(|c| c.0 != &token) {
let existing_client = existing_client.borrow();
if let Some(player) = &existing_client.player {
let gameprofile = existing_client.gameprofile.as_ref().unwrap();
client.send_packet(CSpawnEntity::new(
player.entity_id().into(),
gameprofile.id,
EntityType::Player.to_i32().unwrap().into(),
x,
y,
z,
0,
0,
0,
0.into(),
0,
0,
0,
))
}
}
// Server::spawn_test_chunk(client); // Server::spawn_test_chunk(client);
} }
/// Sends a Packet to all Players /// Sends a Packet to all Players
fn _broadcast_packet<P>(&mut self, _packet: P) -> Result<(), PacketError> fn broadcast_packet<P>(&mut self, from: &Client, packet: P)
where where
P: ClientPacket, P: ClientPacket,
P: Clone, P: Clone,
{ {
/* for client in self.current_clients.values() { for (_, client) in self.current_clients.iter().filter(|c| c.0 != &from.token) {
// Check if client is a player // Check if client is a player
if client.borrow().is_player() { let mut client = client.borrow_mut();
if client.is_player() {
// we need to clone, Because we send a new packet to every client // we need to clone, Because we send a new packet to every client
client.borrow_mut().send_packet(packet.clone())?; client.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();
client client.send_packet(CChunkDataUpdateLight::new(
.send_packet(CChunkDataUpdateLight::new(
10, 10,
10, 10,
test_chunk.heightmap, test_chunk.heightmap,
Vec::new(), Vec::new(),
Vec::new(), Vec::new(),
BitSet(0, Vec::new()), BitSet(0.into(), Vec::new()),
BitSet(0, Vec::new()), BitSet(0.into(), Vec::new()),
BitSet(0, Vec::new()), BitSet(0.into(), Vec::new()),
Vec::new(), Vec::new(),
Vec::new(), Vec::new(),
)) ));
.unwrap_or_else(|e| client.kick(&e.to_string()))
} }
// move to world // move to world
@ -221,17 +297,17 @@ impl Server {
pub fn build_brand() -> Vec<u8> { pub fn build_brand() -> Vec<u8> {
let brand = "Pumpkin"; let brand = "Pumpkin";
let mut buf = vec![]; let mut buf = vec![];
let _ = VarInt32(brand.len() as i32).encode(&mut buf); let _ = VarInt(brand.len() as i32).encode(&mut buf);
buf.extend_from_slice(brand.as_bytes()); buf.extend_from_slice(brand.as_bytes());
buf buf
} }
pub fn send_brand(&self, client: &mut Client) -> Result<(), PacketError> { pub fn send_brand(&self, client: &mut Client) {
// send server brand // send server brand
client.send_packet(CPluginMessage::new( client.send_packet(CPluginMessage::new(
"minecraft:brand", "minecraft:brand",
&self.cached_server_brand, &self.cached_server_brand,
)) ));
} }
pub fn build_response(config: &BasicConfiguration) -> StatusResponse { pub fn build_response(config: &BasicConfiguration) -> StatusResponse {