mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Add Multiplayer support
This commit is contained in:
9
Cargo.lock
generated
9
Cargo.lock
generated
@@ -1164,6 +1164,7 @@ dependencies = [
|
||||
"num-bigint",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"pumpkin-entity",
|
||||
"pumpkin-protocol",
|
||||
"pumpkin-registry",
|
||||
"pumpkin-world",
|
||||
@@ -1180,6 +1181,14 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pumpkin-entity"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pumpkin-macros"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[workspace]
|
||||
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]
|
||||
version = "0.1.0"
|
||||
|
||||
8
pumpkin-entity/Cargo.toml
Normal file
8
pumpkin-entity/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "pumpkin-entity"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
num-traits = "0.2"
|
||||
num-derive = "0.4"
|
||||
8
pumpkin-entity/src/entity_type.rs
Normal file
8
pumpkin-entity/src/entity_type.rs
Normal 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
19
pumpkin-entity/src/lib.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{BitSet, VarInt, VarLong};
|
||||
use crate::{BitSet, VarInt, VarLongType};
|
||||
use bytes::{Buf, BufMut, BytesMut};
|
||||
use core::str;
|
||||
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 position: i64 = 0;
|
||||
|
||||
@@ -75,7 +75,7 @@ impl ByteBuffer {
|
||||
}
|
||||
|
||||
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 {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
@@ -121,7 +121,7 @@ impl ByteBuffer {
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -131,8 +131,8 @@ impl ByteBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put_var_int(&mut self, value: VarInt) {
|
||||
let mut val = value;
|
||||
pub fn put_var_int(&mut self, value: &VarInt) {
|
||||
let mut val = value.0;
|
||||
for _ in 0..5 {
|
||||
let mut b: u8 = val as u8 & 0b01111111;
|
||||
val >>= 7;
|
||||
@@ -147,7 +147,7 @@ impl ByteBuffer {
|
||||
}
|
||||
|
||||
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 {
|
||||
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> {
|
||||
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);
|
||||
for _ in 0..len {
|
||||
list.push(val(self));
|
||||
@@ -182,14 +182,14 @@ impl ByteBuffer {
|
||||
}
|
||||
/// Writes a list to the buffer.
|
||||
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 {
|
||||
write(self, v);
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use serde::{Serialize, Serializer};
|
||||
|
||||
use crate::{ClientPacket, VarInt, VarInt32};
|
||||
use crate::{ClientPacket, VarInt, VarIntType};
|
||||
|
||||
use super::{serializer, ByteBuffer};
|
||||
|
||||
impl Serialize for VarInt32 {
|
||||
impl Serialize for VarInt {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
@@ -27,7 +27,7 @@ impl Serialize for VarInt32 {
|
||||
}
|
||||
|
||||
pub trait Packet {
|
||||
const PACKET_ID: VarInt;
|
||||
const PACKET_ID: VarIntType;
|
||||
}
|
||||
|
||||
impl<P> ClientPacket for P
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use pumpkin_macros::packet;
|
||||
|
||||
use crate::{bytebuf::ByteBuffer, ClientPacket, VarInt};
|
||||
use crate::{bytebuf::ByteBuffer, ClientPacket, VarIntType};
|
||||
|
||||
#[packet(0x01)]
|
||||
pub struct CEncryptionRequest<'a> {
|
||||
@@ -29,9 +29,9 @@ impl<'a> CEncryptionRequest<'a> {
|
||||
impl<'a> ClientPacket for CEncryptionRequest<'a> {
|
||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||
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_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_bool(self.should_authenticate);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
use pumpkin_macros::packet;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::VarInt32;
|
||||
use crate::VarInt;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x03)]
|
||||
pub struct CSetCompression {
|
||||
threshold: VarInt32,
|
||||
threshold: VarInt,
|
||||
}
|
||||
|
||||
impl CSetCompression {
|
||||
pub fn new(threshold: VarInt32) -> Self {
|
||||
pub fn new(threshold: VarInt) -> Self {
|
||||
Self { threshold }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use pumpkin_macros::packet;
|
||||
|
||||
use crate::{bytebuf::ByteBuffer, BitSet, ClientPacket, VarInt};
|
||||
use crate::{bytebuf::ByteBuffer, BitSet, ClientPacket, VarInt, VarIntType};
|
||||
|
||||
#[packet(0x27)]
|
||||
pub struct CChunkDataUpdateLight {
|
||||
@@ -50,23 +50,23 @@ impl ClientPacket for CChunkDataUpdateLight {
|
||||
bytebuf.put_i32(self.chunk_x);
|
||||
bytebuf.put_i32(self.chunk_y);
|
||||
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_list::<BlockEntity>(&self.block_entites, |p, v| {
|
||||
p.put_u8(v.packed_xz);
|
||||
p.put_i16(v.y);
|
||||
p.put_var_int(v.typee);
|
||||
p.put_var_int(&v.typee);
|
||||
p.put_slice(&v.data);
|
||||
});
|
||||
bytebuf.put_bit_set(&self.sky_light_mask);
|
||||
bytebuf.put_bit_set(&self.block_light_mask);
|
||||
bytebuf.put_bit_set(&self.empty_sky_light_mask);
|
||||
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);
|
||||
});
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -83,13 +83,13 @@ impl ClientPacket for CLogin {
|
||||
bytebuf.put_i32(self.entity_id);
|
||||
bytebuf.put_bool(self.is_hardcore);
|
||||
bytebuf.put_list(&self.dimension_names, |buf, v| buf.put_string(v));
|
||||
bytebuf.put_var_int(self.max_players);
|
||||
bytebuf.put_var_int(self.view_distance);
|
||||
bytebuf.put_var_int(self.simulated_distance);
|
||||
bytebuf.put_var_int(&self.max_players);
|
||||
bytebuf.put_var_int(&self.view_distance);
|
||||
bytebuf.put_var_int(&self.simulated_distance);
|
||||
bytebuf.put_bool(self.reduced_debug_info);
|
||||
bytebuf.put_bool(self.enabled_respawn_screen);
|
||||
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_i64(self.hashed_seed);
|
||||
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_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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,16 @@ use crate::{bytebuf::ByteBuffer, ClientPacket, Property};
|
||||
|
||||
use super::PlayerAction;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[packet(0x3E)]
|
||||
pub struct CPlayerInfoUpdate<'a> {
|
||||
pub actions: i8,
|
||||
pub players: &'a [Player<'a>],
|
||||
pub players: &'a [Player],
|
||||
}
|
||||
|
||||
pub struct Player<'a> {
|
||||
pub struct Player {
|
||||
pub uuid: uuid::Uuid,
|
||||
pub actions: &'a [PlayerAction],
|
||||
pub actions: Vec<PlayerAction>,
|
||||
}
|
||||
|
||||
impl<'a> CPlayerInfoUpdate<'a> {
|
||||
@@ -26,7 +27,7 @@ impl<'a> ClientPacket for CPlayerInfoUpdate<'a> {
|
||||
bytebuf.put_i8(self.actions);
|
||||
bytebuf.put_list::<Player>(self.players, |p, v| {
|
||||
p.put_uuid(v.uuid);
|
||||
for action in v.actions {
|
||||
for action in &v.actions {
|
||||
match action {
|
||||
PlayerAction::AddPlayer { name, properties } => {
|
||||
p.put_string(name);
|
||||
|
||||
73
pumpkin-protocol/src/client/play/c_spawn_player.rs
Normal file
73
pumpkin-protocol/src/client/play/c_spawn_player.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
use pumpkin_macros::packet;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::VarInt32;
|
||||
use crate::VarInt;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x40)]
|
||||
@@ -12,7 +12,7 @@ pub struct CSyncPlayerPostion {
|
||||
yaw: f32,
|
||||
pitch: f32,
|
||||
flags: i8,
|
||||
teleport_id: VarInt32,
|
||||
teleport_id: VarInt,
|
||||
}
|
||||
|
||||
impl CSyncPlayerPostion {
|
||||
@@ -23,7 +23,7 @@ impl CSyncPlayerPostion {
|
||||
yaw: f32,
|
||||
pitch: f32,
|
||||
flags: i8,
|
||||
teleport_id: VarInt32,
|
||||
teleport_id: VarInt,
|
||||
) -> Self {
|
||||
Self {
|
||||
x,
|
||||
|
||||
@@ -6,6 +6,7 @@ mod c_play_disconnect;
|
||||
mod c_player_abilities;
|
||||
mod c_player_info_update;
|
||||
mod c_set_held_item;
|
||||
mod c_spawn_player;
|
||||
mod c_sync_player_position;
|
||||
mod c_system_chat_message;
|
||||
mod player_action;
|
||||
@@ -18,6 +19,7 @@ pub use c_play_disconnect::*;
|
||||
pub use c_player_abilities::*;
|
||||
pub use c_player_info_update::*;
|
||||
pub use c_set_held_item::*;
|
||||
pub use c_spawn_player::*;
|
||||
pub use c_sync_player_position::*;
|
||||
pub use c_system_chat_message::*;
|
||||
pub use player_action::*;
|
||||
|
||||
@@ -19,14 +19,15 @@ pub const MAX_PACKET_SIZE: i32 = 2097152;
|
||||
|
||||
/// usally uses a namespace like "minecraft:thing"
|
||||
pub type Identifier = String;
|
||||
pub type VarInt = i32;
|
||||
pub type VarLong = i64;
|
||||
pub type VarIntType = i32;
|
||||
pub type VarLongType = 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
|
||||
/// written to the Minecraft protocol.
|
||||
pub const MAX_SIZE: usize = 5;
|
||||
@@ -83,16 +84,40 @@ impl VarInt32 {
|
||||
let byte = r.get_u8();
|
||||
val |= (i32::from(byte) & 0b01111111) << (i * 7);
|
||||
if byte & 0b10000000 == 0 {
|
||||
return Ok(VarInt32(val));
|
||||
return Ok(VarInt(val));
|
||||
}
|
||||
}
|
||||
Err(VarIntDecodeError::TooLarge)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for VarInt32 {
|
||||
impl From<i32> for VarInt {
|
||||
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 {
|
||||
fn from(value: VarInt) -> Self {
|
||||
let value = value.0;
|
||||
match value {
|
||||
1 => Self::Status,
|
||||
2 => Self::Login,
|
||||
@@ -147,7 +173,6 @@ impl From<VarInt> for ConnectionState {
|
||||
}
|
||||
|
||||
pub struct RawPacket {
|
||||
pub len: VarInt,
|
||||
pub id: VarInt,
|
||||
pub bytebuf: ByteBuffer,
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use aes::cipher::{generic_array::GenericArray, BlockDecryptMut, BlockSizeUser, K
|
||||
use bytes::{Buf, BytesMut};
|
||||
|
||||
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>;
|
||||
@@ -19,7 +19,7 @@ impl PacketDecoder {
|
||||
pub fn decode(&mut self) -> Result<Option<RawPacket>, PacketError> {
|
||||
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,
|
||||
Err(VarIntDecodeError::Incomplete) => return Ok(None),
|
||||
Err(VarIntDecodeError::TooLarge) => Err(PacketError::MailformedLength)?,
|
||||
@@ -34,7 +34,7 @@ impl PacketDecoder {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let packet_len_len = VarInt32(packet_len).written_size();
|
||||
let packet_len_len = VarInt(packet_len).written_size();
|
||||
|
||||
let mut data;
|
||||
|
||||
@@ -44,13 +44,10 @@ impl PacketDecoder {
|
||||
data = self.buf.split_to(packet_len as usize);
|
||||
|
||||
r = &data[..];
|
||||
let packet_id = VarInt32::decode(&mut r)
|
||||
.map_err(|_| PacketError::DecodeID)?
|
||||
.0;
|
||||
let packet_id = VarInt::decode(&mut r).map_err(|_| PacketError::DecodeID)?;
|
||||
|
||||
data.advance(data.len() - r.len());
|
||||
Ok(Some(RawPacket {
|
||||
len: packet_len,
|
||||
id: packet_id,
|
||||
bytebuf: ByteBuffer::new(data),
|
||||
}))
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::io::Write;
|
||||
use aes::cipher::{generic_array::GenericArray, BlockEncryptMut, BlockSizeUser, KeyIvInit};
|
||||
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>;
|
||||
|
||||
@@ -22,7 +22,7 @@ impl PacketEncoder {
|
||||
let mut writer = (&mut self.buf).writer();
|
||||
|
||||
let mut packet_buf = ByteBuffer::empty();
|
||||
VarInt32(P::PACKET_ID)
|
||||
VarInt(P::PACKET_ID)
|
||||
.encode(&mut writer)
|
||||
.map_err(|_| PacketError::EncodeID)?;
|
||||
packet.write(&mut packet_buf);
|
||||
@@ -41,14 +41,14 @@ impl PacketEncoder {
|
||||
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
|
||||
.copy_within(start_len..start_len + data_len, start_len + packet_len_size);
|
||||
|
||||
let front = &mut self.buf[start_len..];
|
||||
VarInt32(packet_len as i32)
|
||||
VarInt(packet_len as i32)
|
||||
.encode(front)
|
||||
.map_err(|_| PacketError::EncodeID)?;
|
||||
Ok(())
|
||||
|
||||
@@ -12,7 +12,7 @@ pub struct SClientInformation {
|
||||
}
|
||||
|
||||
impl ServerPacket for SClientInformation {
|
||||
const PACKET_ID: VarInt = 0x00;
|
||||
const PACKET_ID: VarInt = VarInt(0x00);
|
||||
|
||||
fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {
|
||||
@@ -34,7 +34,7 @@ pub struct SPluginMessage {
|
||||
}
|
||||
|
||||
impl ServerPacket for SPluginMessage {
|
||||
const PACKET_ID: VarInt = 0x02;
|
||||
const PACKET_ID: VarInt = VarInt(0x02);
|
||||
|
||||
fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {
|
||||
@@ -47,7 +47,7 @@ impl ServerPacket for SPluginMessage {
|
||||
pub struct SAcknowledgeFinishConfig {}
|
||||
|
||||
impl ServerPacket for SAcknowledgeFinishConfig {
|
||||
const PACKET_ID: VarInt = 0x03;
|
||||
const PACKET_ID: VarInt = VarInt(0x03);
|
||||
|
||||
fn read(_bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {}
|
||||
@@ -60,7 +60,7 @@ pub struct SKnownPacks {
|
||||
}
|
||||
|
||||
impl ServerPacket for SKnownPacks {
|
||||
const PACKET_ID: VarInt = 0x07;
|
||||
const PACKET_ID: VarInt = VarInt(0x07);
|
||||
|
||||
fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {
|
||||
|
||||
@@ -8,7 +8,7 @@ pub struct SHandShake {
|
||||
}
|
||||
|
||||
impl ServerPacket for SHandShake {
|
||||
const PACKET_ID: VarInt = 0x00;
|
||||
const PACKET_ID: VarInt = VarInt(0x00);
|
||||
|
||||
fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {
|
||||
|
||||
@@ -6,7 +6,7 @@ pub struct SLoginStart {
|
||||
}
|
||||
|
||||
impl ServerPacket for SLoginStart {
|
||||
const PACKET_ID: VarInt = 0x00;
|
||||
const PACKET_ID: VarInt = VarInt(0x00);
|
||||
|
||||
fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {
|
||||
@@ -24,13 +24,13 @@ pub struct SEncryptionResponse {
|
||||
}
|
||||
|
||||
impl ServerPacket for SEncryptionResponse {
|
||||
const PACKET_ID: VarInt = 0x01;
|
||||
const PACKET_ID: VarInt = VarInt(0x01);
|
||||
|
||||
fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||
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 = bytebuf.copy_to_bytes(shared_secret_length as usize);
|
||||
let verify_token = bytebuf.copy_to_bytes(shared_secret_length.0 as usize);
|
||||
Self {
|
||||
shared_secret_length,
|
||||
shared_secret: shared_secret.to_vec(),
|
||||
@@ -47,7 +47,7 @@ pub struct 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 {
|
||||
Self {
|
||||
@@ -64,7 +64,7 @@ pub struct SLoginAcknowledged {
|
||||
}
|
||||
|
||||
impl ServerPacket for SLoginAcknowledged {
|
||||
const PACKET_ID: VarInt = 0x03;
|
||||
const PACKET_ID: VarInt = VarInt(0x03);
|
||||
|
||||
fn read(_bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {}
|
||||
|
||||
@@ -8,7 +8,7 @@ pub struct SConfirmTeleport {
|
||||
}
|
||||
|
||||
impl ServerPacket for SConfirmTeleport {
|
||||
const PACKET_ID: VarInt = 0x00;
|
||||
const PACKET_ID: VarInt = VarInt(0x00);
|
||||
|
||||
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self {
|
||||
Self {
|
||||
@@ -22,7 +22,7 @@ pub struct SChatCommand {
|
||||
}
|
||||
|
||||
impl ServerPacket for SChatCommand {
|
||||
const PACKET_ID: VarInt = 0x04;
|
||||
const PACKET_ID: VarInt = VarInt(0x04);
|
||||
|
||||
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self {
|
||||
Self {
|
||||
@@ -39,7 +39,7 @@ pub struct SPlayerPosition {
|
||||
}
|
||||
|
||||
impl ServerPacket for SPlayerPosition {
|
||||
const PACKET_ID: VarInt = 0x1A;
|
||||
const PACKET_ID: VarInt = VarInt(0x1A);
|
||||
|
||||
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self {
|
||||
Self {
|
||||
@@ -69,12 +69,12 @@ pub enum Action {
|
||||
}
|
||||
|
||||
impl ServerPacket for SPlayerCommand {
|
||||
const PACKET_ID: VarInt = 0x25;
|
||||
const PACKET_ID: VarInt = VarInt(0x25);
|
||||
|
||||
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self {
|
||||
Self {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ pub struct SPlayerPositionRotation {
|
||||
}
|
||||
|
||||
impl ServerPacket for SPlayerPositionRotation {
|
||||
const PACKET_ID: VarInt = 0x1B;
|
||||
const PACKET_ID: VarInt = VarInt(0x1B);
|
||||
|
||||
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self {
|
||||
Self {
|
||||
@@ -111,7 +111,7 @@ pub struct SPlayerRotation {
|
||||
}
|
||||
|
||||
impl ServerPacket for SPlayerRotation {
|
||||
const PACKET_ID: VarInt = 0x1C;
|
||||
const PACKET_ID: VarInt = VarInt(0x1C);
|
||||
|
||||
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self {
|
||||
Self {
|
||||
|
||||
@@ -5,7 +5,7 @@ pub struct SStatusRequest {
|
||||
}
|
||||
|
||||
impl ServerPacket for SStatusRequest {
|
||||
const PACKET_ID: VarInt = 0x00;
|
||||
const PACKET_ID: VarInt = VarInt(0x00);
|
||||
|
||||
fn read(_bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {}
|
||||
@@ -17,7 +17,7 @@ pub struct SPingRequest {
|
||||
}
|
||||
|
||||
impl ServerPacket for SPingRequest {
|
||||
const PACKET_ID: VarInt = 0x01;
|
||||
const PACKET_ID: VarInt = VarInt(0x01);
|
||||
|
||||
fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {
|
||||
|
||||
@@ -6,6 +6,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
pumpkin-world = { path = "../pumpkin-world"}
|
||||
pumpkin-entity = { path = "../pumpkin-entity"}
|
||||
pumpkin-protocol = { path = "../pumpkin-protocol"}
|
||||
pumpkin-registry = { path = "../pumpkin-registry"}
|
||||
|
||||
|
||||
@@ -35,14 +35,12 @@ impl Client {
|
||||
}
|
||||
|
||||
pub fn handle_status_request(&mut self, server: &mut Server, _status_request: SStatusRequest) {
|
||||
self.send_packet(CStatusResponse::new(&server.status_response_json))
|
||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||
self.send_packet(CStatusResponse::new(&server.status_response_json));
|
||||
}
|
||||
|
||||
pub fn handle_ping_request(&mut self, _server: &mut Server, ping_request: SPingRequest) {
|
||||
dbg!("ping");
|
||||
self.send_packet(CPingResponse::new(ping_request.payload))
|
||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||
self.send_packet(CPingResponse::new(ping_request.payload));
|
||||
self.close();
|
||||
}
|
||||
|
||||
@@ -64,8 +62,7 @@ impl Client {
|
||||
&verify_token,
|
||||
server.base_config.online_mode, // TODO
|
||||
);
|
||||
self.send_packet(packet)
|
||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||
self.send_packet(packet);
|
||||
}
|
||||
|
||||
pub fn handle_encryption_response(
|
||||
@@ -101,8 +98,7 @@ impl Client {
|
||||
|
||||
if let Some(profile) = self.gameprofile.as_ref().cloned() {
|
||||
let packet = CLoginSuccess::new(profile.id, profile.name, &profile.properties, false);
|
||||
self.send_packet(packet)
|
||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||
self.send_packet(packet);
|
||||
} else {
|
||||
self.kick("game profile is none");
|
||||
}
|
||||
@@ -121,16 +117,13 @@ impl Client {
|
||||
_login_acknowledged: SLoginAcknowledged,
|
||||
) {
|
||||
self.connection_state = ConnectionState::Config;
|
||||
server
|
||||
.send_brand(self)
|
||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||
server.send_brand(self);
|
||||
// known data packs
|
||||
self.send_packet(CKnownPacks::new(&[KnownPack {
|
||||
namespace: "minecraft",
|
||||
id: "core",
|
||||
version: "1.21",
|
||||
}]))
|
||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||
}]));
|
||||
dbg!("login achnowlaged");
|
||||
}
|
||||
pub fn handle_client_information(
|
||||
@@ -141,10 +134,10 @@ impl Client {
|
||||
self.config = Some(PlayerConfig {
|
||||
locale: client_information.locale,
|
||||
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,
|
||||
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,
|
||||
server_listing: client_information.server_listing,
|
||||
});
|
||||
@@ -167,14 +160,12 @@ impl Client {
|
||||
self.send_packet(CRegistryData::new(
|
||||
®istry.registry_id,
|
||||
®istry.registry_entries,
|
||||
))
|
||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||
));
|
||||
}
|
||||
|
||||
// We are done with configuring
|
||||
dbg!("finish config");
|
||||
self.send_packet(CFinishConfig::new())
|
||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||
self.send_packet(CFinishConfig::new());
|
||||
}
|
||||
|
||||
pub fn handle_config_acknowledged(
|
||||
|
||||
@@ -114,9 +114,18 @@ impl Client {
|
||||
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
|
||||
/// e.g Postion, Rotation... has not been send
|
||||
pub fn send_packet<P: ClientPacket>(&mut self, packet: P) -> Result<(), PacketError> {
|
||||
/// Send a Clientbound Packet to the Client
|
||||
pub fn send_packet<P: ClientPacket>(&mut self, packet: P) {
|
||||
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.connection
|
||||
.write_all(&self.enc.take())
|
||||
@@ -134,14 +143,12 @@ impl Client {
|
||||
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.into()))
|
||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||
player.awaiting_teleport = Some(id.into());
|
||||
self.send_packet(CSyncPlayerPostion::new(x, y, z, yaw, pitch, 0, id.into()));
|
||||
}
|
||||
|
||||
pub fn set_gamemode(&mut self, gamemode: GameMode) {
|
||||
self.send_packet(CGameEvent::new(3, gamemode.to_f32().unwrap()))
|
||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||
self.send_packet(CGameEvent::new(3, gamemode.to_f32().unwrap()));
|
||||
}
|
||||
|
||||
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)),
|
||||
_ => log::error!(
|
||||
"Failed to handle packet id {} while in Handshake state",
|
||||
packet.id
|
||||
packet.id.0
|
||||
),
|
||||
},
|
||||
pumpkin_protocol::ConnectionState::Status => match packet.id {
|
||||
@@ -173,7 +180,7 @@ impl Client {
|
||||
}
|
||||
_ => log::error!(
|
||||
"Failed to handle packet id {} while in Status state",
|
||||
packet.id
|
||||
packet.id.0
|
||||
),
|
||||
},
|
||||
pumpkin_protocol::ConnectionState::Login => match packet.id {
|
||||
@@ -191,7 +198,7 @@ impl Client {
|
||||
}
|
||||
_ => log::error!(
|
||||
"Failed to handle packet id {} while in Login state",
|
||||
packet.id
|
||||
packet.id.0
|
||||
),
|
||||
},
|
||||
pumpkin_protocol::ConnectionState::Config => match packet.id {
|
||||
@@ -209,7 +216,7 @@ impl Client {
|
||||
}
|
||||
_ => log::error!(
|
||||
"Failed to handle packet id {} while in Config state",
|
||||
packet.id
|
||||
packet.id.0
|
||||
),
|
||||
},
|
||||
pumpkin_protocol::ConnectionState::Play => {
|
||||
@@ -245,7 +252,7 @@ impl Client {
|
||||
SPlayerCommand::PACKET_ID => {
|
||||
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) {
|
||||
self.send_packet(CSystemChatMessge::new(text, false))
|
||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||
pub fn send_system_message(&mut self, text: TextComponent) {
|
||||
self.send_packet(CSystemChatMessge::new(text, false));
|
||||
}
|
||||
|
||||
/// Kicks the Client with a reason depending on the connection state
|
||||
@@ -306,17 +312,17 @@ impl Client {
|
||||
dbg!(reason);
|
||||
match self.connection_state {
|
||||
ConnectionState::Login => {
|
||||
self.send_packet(CLoginDisconnect::new(
|
||||
self.try_send_packet(CLoginDisconnect::new(
|
||||
&serde_json::to_string_pretty(&reason).unwrap(),
|
||||
))
|
||||
.unwrap_or_else(|_| self.close());
|
||||
}
|
||||
ConnectionState::Config => {
|
||||
self.send_packet(CConfigDisconnect::new(reason))
|
||||
self.try_send_packet(CConfigDisconnect::new(reason))
|
||||
.unwrap_or_else(|_| self.close());
|
||||
}
|
||||
ConnectionState::Play => {
|
||||
self.send_packet(CPlayDisconnect::new(TextComponent {
|
||||
self.try_send_packet(CPlayDisconnect::new(TextComponent {
|
||||
text: reason.to_string(),
|
||||
}))
|
||||
.unwrap_or_else(|_| self.close());
|
||||
|
||||
@@ -18,7 +18,7 @@ impl Client {
|
||||
confirm_teleport: SConfirmTeleport,
|
||||
) {
|
||||
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 {
|
||||
} else {
|
||||
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) {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,17 +13,13 @@ impl<'a> Command<'a> for GamemodeCommand {
|
||||
|
||||
const DESCRIPTION: &'a str = "Changes the gamemode for a Player";
|
||||
|
||||
fn on_execute(
|
||||
sender: &mut super::CommandSender<'a>,
|
||||
command: String,
|
||||
_server: &mut crate::server::Server,
|
||||
) {
|
||||
fn on_execute(sender: &mut super::CommandSender<'a>, command: String) {
|
||||
let player = sender.as_mut_player().unwrap();
|
||||
let args: Vec<&str> = command.split_whitespace().collect();
|
||||
|
||||
if args.len() != 2 {
|
||||
// todo red
|
||||
player.send_message("Usage: /gamemode <mode>".into());
|
||||
player.send_system_message("Usage: /gamemode <mode>".into());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -31,11 +27,11 @@ impl<'a> Command<'a> for GamemodeCommand {
|
||||
match mode_str.parse() {
|
||||
Ok(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(_) => {
|
||||
// todo red
|
||||
player.send_message("Invalid gamemode".into());
|
||||
player.send_system_message("Invalid gamemode".into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use gamemode::GamemodeCommand;
|
||||
use pumpkin::PumpkinCommand;
|
||||
use pumpkin_protocol::text::TextComponent;
|
||||
|
||||
use crate::{client::Client, server::Server};
|
||||
use crate::client::Client;
|
||||
|
||||
mod gamemode;
|
||||
mod pumpkin;
|
||||
@@ -13,7 +13,7 @@ pub trait Command<'a> {
|
||||
const NAME: &'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
|
||||
fn player_required() -> bool {
|
||||
@@ -31,7 +31,7 @@ impl<'a> CommandSender<'a> {
|
||||
match self {
|
||||
// todo: add color and stuff to console
|
||||
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();
|
||||
// an ugly mess i know
|
||||
if command.starts_with(PumpkinCommand::NAME) {
|
||||
PumpkinCommand::on_execute(sender, command, server);
|
||||
PumpkinCommand::on_execute(sender, command);
|
||||
return;
|
||||
}
|
||||
if command.starts_with(GamemodeCommand::NAME) {
|
||||
GamemodeCommand::on_execute(sender, command, server);
|
||||
GamemodeCommand::on_execute(sender, command);
|
||||
return;
|
||||
}
|
||||
// todo: red color
|
||||
|
||||
@@ -17,11 +17,7 @@ impl<'a> Command<'a> for PumpkinCommand {
|
||||
|
||||
const DESCRIPTION: &'a str = "Displays information about Pumpkin";
|
||||
|
||||
fn on_execute(
|
||||
sender: &mut super::CommandSender<'a>,
|
||||
_command: String,
|
||||
_server: &mut crate::server::Server,
|
||||
) {
|
||||
fn on_execute(sender: &mut super::CommandSender<'a>, _command: String) {
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
let description = env!("CARGO_PKG_DESCRIPTION");
|
||||
sender.send_message(TextComponent::from(format!("Pumpkin {version}, {description} (Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL})")))
|
||||
|
||||
@@ -1,13 +1 @@
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use pumpkin_entity::{entity_type::EntityType, Entity, EntityId};
|
||||
use pumpkin_protocol::VarInt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{Entity, EntityId};
|
||||
|
||||
pub struct Player {
|
||||
pub entity: Entity,
|
||||
pub x: f64,
|
||||
@@ -25,9 +24,12 @@ pub struct Player {
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub fn new(entity: Entity) -> Self {
|
||||
pub fn new(entity_id: EntityId) -> Self {
|
||||
Self {
|
||||
entity,
|
||||
entity: Entity {
|
||||
entity_id,
|
||||
entity_type: EntityType::Player,
|
||||
},
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
|
||||
@@ -29,7 +29,7 @@ pub mod util;
|
||||
|
||||
#[cfg(not(target_os = "wasi"))]
|
||||
fn main() -> io::Result<()> {
|
||||
use std::time::Instant;
|
||||
use std::{cell::RefCell, time::Instant};
|
||||
|
||||
let time = Instant::now();
|
||||
let basic_config = BasicConfiguration::load("configuration.toml");
|
||||
@@ -63,7 +63,7 @@ fn main() -> io::Result<()> {
|
||||
|
||||
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((
|
||||
basic_config,
|
||||
@@ -73,7 +73,6 @@ fn main() -> io::Result<()> {
|
||||
log::info!("You now can connect to the server");
|
||||
|
||||
if use_console {
|
||||
let server_clone = server.clone();
|
||||
thread::spawn(move || {
|
||||
let stdin = std::io::stdin();
|
||||
loop {
|
||||
@@ -81,11 +80,7 @@ fn main() -> io::Result<()> {
|
||||
stdin
|
||||
.read_line(&mut out)
|
||||
.expect("Failed to read console line");
|
||||
handle_command(
|
||||
&mut commands::CommandSender::Console,
|
||||
out,
|
||||
&mut server_clone.lock().unwrap(),
|
||||
);
|
||||
handle_command(&mut commands::CommandSender::Console, out);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -127,14 +122,22 @@ fn main() -> io::Result<()> {
|
||||
Interest::READABLE.add(Interest::WRITABLE),
|
||||
)?;
|
||||
let rc_token = Rc::new(token);
|
||||
let client = Client::new(Rc::clone(&rc_token), connection, addr);
|
||||
// server.add_client(rc_token, Rc::clone(&client));
|
||||
let client = Rc::new(RefCell::new(Client::new(
|
||||
Rc::clone(&rc_token),
|
||||
connection,
|
||||
addr,
|
||||
)));
|
||||
server
|
||||
.lock()
|
||||
.unwrap()
|
||||
.add_client(rc_token, Rc::clone(&client));
|
||||
connections.insert(token, client);
|
||||
},
|
||||
|
||||
token => {
|
||||
// Maybe received an event for a TCP connection.
|
||||
let done = if let Some(client) = connections.get_mut(&token) {
|
||||
let mut client = client.borrow_mut();
|
||||
client.poll(&mut server.lock().unwrap(), event);
|
||||
client.closed
|
||||
} else {
|
||||
@@ -142,8 +145,9 @@ fn main() -> io::Result<()> {
|
||||
false
|
||||
};
|
||||
if done {
|
||||
if let Some(mut client) = connections.remove(&token) {
|
||||
// server.remove_client(&token);
|
||||
if let Some(client) = connections.remove(&token) {
|
||||
let mut client = client.borrow_mut();
|
||||
server.lock().unwrap().remove_client(&token);
|
||||
poll.registry().deregister(&mut client.connection)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
io::Cursor,
|
||||
rc::Rc,
|
||||
sync::atomic::{AtomicI32, Ordering},
|
||||
@@ -8,13 +9,17 @@ use std::{
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use mio::{event::Event, Poll, Token};
|
||||
use num_traits::ToPrimitive;
|
||||
use pumpkin_entity::{entity_type::EntityType, EntityId};
|
||||
use pumpkin_protocol::{
|
||||
client::{
|
||||
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 rsa::{rand_core::OsRng, traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
|
||||
@@ -23,10 +28,7 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::{
|
||||
client::Client,
|
||||
configuration::{AdvancedConfiguration, BasicConfiguration},
|
||||
entity::{
|
||||
player::{GameMode, Player},
|
||||
Entity, EntityId,
|
||||
},
|
||||
entity::player::{GameMode, Player},
|
||||
};
|
||||
|
||||
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
|
||||
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>
|
||||
entity_id: AtomicI32, // todo: place this into every world
|
||||
@@ -90,7 +92,7 @@ impl Server {
|
||||
status_response,
|
||||
status_response_json,
|
||||
public_key_der,
|
||||
// current_clients: HashMap::new(),
|
||||
current_clients: HashMap::new(),
|
||||
base_config: config.0,
|
||||
auth_client,
|
||||
advanced_config: config.1,
|
||||
@@ -103,114 +105,188 @@ impl Server {
|
||||
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 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);
|
||||
pub fn remove_client(&mut self, token: &Token) {
|
||||
self.current_clients.remove(token);
|
||||
}
|
||||
|
||||
// here is where the magic happens
|
||||
// todo: do this in a world
|
||||
pub fn spawn_player(&mut self, client: &mut Client) {
|
||||
// This code follows the vanilla packet order
|
||||
dbg!("spawning player");
|
||||
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
|
||||
.send_packet(CLogin::new(
|
||||
entity_id,
|
||||
self.base_config.hardcore,
|
||||
vec!["minecraft:overworld".into()],
|
||||
self.base_config.max_players as VarInt,
|
||||
self.base_config.view_distance as VarInt, // view distance todo
|
||||
self.base_config.simulation_distance as VarInt, // sim view dinstance todo
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
"minecraft:overworld".into(),
|
||||
0, // seed
|
||||
match self.base_config.default_gamemode {
|
||||
GameMode::Undefined => GameMode::Survival,
|
||||
game_mode => game_mode,
|
||||
}
|
||||
.to_u8()
|
||||
.unwrap(),
|
||||
self.base_config.default_gamemode.to_i8().unwrap(),
|
||||
false,
|
||||
false,
|
||||
false, // deth loc
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
false,
|
||||
))
|
||||
.unwrap_or_else(|e| client.kick(&e.to_string()));
|
||||
// login packet for our new player
|
||||
client.send_packet(CLogin::new(
|
||||
entity_id,
|
||||
self.base_config.hardcore,
|
||||
vec!["minecraft:overworld".into()],
|
||||
self.base_config.max_players.into(),
|
||||
self.base_config.view_distance.into(), // view distance todo
|
||||
self.base_config.simulation_distance.into(), // sim view dinstance todo
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
0.into(),
|
||||
"minecraft:overworld".into(),
|
||||
0, // seed
|
||||
match self.base_config.default_gamemode {
|
||||
GameMode::Undefined => GameMode::Survival,
|
||||
game_mode => game_mode,
|
||||
}
|
||||
.to_u8()
|
||||
.unwrap(),
|
||||
self.base_config.default_gamemode.to_i8().unwrap(),
|
||||
false,
|
||||
false,
|
||||
false, // deth loc
|
||||
None,
|
||||
None,
|
||||
0.into(),
|
||||
false,
|
||||
));
|
||||
// player abilities
|
||||
client.send_packet(CPlayerAbilities::new(0x02, 00.2, 0.1));
|
||||
|
||||
// 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();
|
||||
// first send info update to our new player, So he can see his Skin
|
||||
// TODO: send more actions, (chat. list, ping)
|
||||
client
|
||||
.send_packet(CPlayerInfoUpdate::new(
|
||||
client.send_packet(CPlayerInfoUpdate::new(
|
||||
0x01,
|
||||
&[pumpkin_protocol::client::play::Player {
|
||||
uuid: gameprofile.id,
|
||||
actions: vec![PlayerAction::AddPlayer {
|
||||
name: gameprofile.name.clone(),
|
||||
properties: gameprofile.properties.clone(),
|
||||
}],
|
||||
}],
|
||||
));
|
||||
let gameprofile = client.gameprofile.as_ref().unwrap();
|
||||
// send his info to everyone else
|
||||
self.broadcast_packet(
|
||||
client,
|
||||
CPlayerInfoUpdate::new(
|
||||
0x01,
|
||||
&[pumpkin_protocol::client::play::Player {
|
||||
uuid: gameprofile.id,
|
||||
actions: &[PlayerAction::AddPlayer {
|
||||
actions: vec![PlayerAction::AddPlayer {
|
||||
name: gameprofile.name.clone(),
|
||||
properties: gameprofile.properties.clone(),
|
||||
}],
|
||||
}],
|
||||
))
|
||||
.unwrap_or_else(|e| client.kick(&e.to_string()));
|
||||
// TODO: Now we send this to all other client
|
||||
),
|
||||
);
|
||||
|
||||
// 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
|
||||
client
|
||||
.send_packet(CGameEvent::new(13, 0.0))
|
||||
.unwrap_or_else(|e| client.kick(&e.to_string()));
|
||||
client.send_packet(CGameEvent::new(13, 0.0));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// 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
|
||||
P: ClientPacket,
|
||||
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
|
||||
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
|
||||
client.borrow_mut().send_packet(packet.clone())?;
|
||||
client.send_packet(packet.clone());
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// todo: do this in a world
|
||||
fn _spawn_test_chunk(client: &mut Client) {
|
||||
let test_chunk = TestChunk::new();
|
||||
client
|
||||
.send_packet(CChunkDataUpdateLight::new(
|
||||
10,
|
||||
10,
|
||||
test_chunk.heightmap,
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
BitSet(0, Vec::new()),
|
||||
BitSet(0, Vec::new()),
|
||||
BitSet(0, Vec::new()),
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
))
|
||||
.unwrap_or_else(|e| client.kick(&e.to_string()))
|
||||
client.send_packet(CChunkDataUpdateLight::new(
|
||||
10,
|
||||
10,
|
||||
test_chunk.heightmap,
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
BitSet(0.into(), Vec::new()),
|
||||
BitSet(0.into(), Vec::new()),
|
||||
BitSet(0.into(), Vec::new()),
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
));
|
||||
}
|
||||
|
||||
// move to world
|
||||
@@ -221,17 +297,17 @@ impl Server {
|
||||
pub fn build_brand() -> Vec<u8> {
|
||||
let brand = "Pumpkin";
|
||||
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
|
||||
}
|
||||
|
||||
pub fn send_brand(&self, client: &mut Client) -> Result<(), PacketError> {
|
||||
pub fn send_brand(&self, client: &mut Client) {
|
||||
// send server brand
|
||||
client.send_packet(CPluginMessage::new(
|
||||
"minecraft:brand",
|
||||
&self.cached_server_brand,
|
||||
))
|
||||
));
|
||||
}
|
||||
|
||||
pub fn build_response(config: &BasicConfiguration) -> StatusResponse {
|
||||
|
||||
Reference in New Issue
Block a user