Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
2e31fda6de
@ -14,8 +14,10 @@ Pumpkin is currently under heavy development.
|
|||||||
|
|
||||||
## What Pumpkin aims to be:
|
## What Pumpkin aims to be:
|
||||||
- A fast, efficient, and scalable Minecraft server
|
- A fast, efficient, and scalable Minecraft server
|
||||||
|
- Use Multi-Threading
|
||||||
- Compatible with the latest Minecraft server version
|
- Compatible with the latest Minecraft server version
|
||||||
- Adherent to Vanilla game mechanics
|
- Adherent to Vanilla game mechanics
|
||||||
|
- Be Secure and prevent all Exploits found
|
||||||
- Highly configurable with the ability to disable unnecessary features
|
- Highly configurable with the ability to disable unnecessary features
|
||||||
- A platform for plugin development
|
- A platform for plugin development
|
||||||
|
|
||||||
@ -51,7 +53,7 @@ Pumpkin is currently under heavy development.
|
|||||||
- [x] Player Client brand
|
- [x] Player Client brand
|
||||||
- [x] Player Teleport
|
- [x] Player Teleport
|
||||||
- [x] Player Movement
|
- [x] Player Movement
|
||||||
- [ ] Player Animation
|
- [x] Player Animation
|
||||||
- [ ] Player Inventory
|
- [ ] Player Inventory
|
||||||
- [ ] Player Attack
|
- [ ] Player Attack
|
||||||
- Server
|
- Server
|
||||||
|
|||||||
@ -299,6 +299,17 @@ impl ByteBuffer {
|
|||||||
self.buffer.put_slice(src)
|
self.buffer.put_slice(src)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn put<T: Buf>(&mut self, src: T)
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.buffer.put(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reserve(&mut self, additional: usize) {
|
||||||
|
self.buffer.reserve(additional)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_slice(&mut self) -> BytesMut {
|
pub fn get_slice(&mut self) -> BytesMut {
|
||||||
self.buffer.split()
|
self.buffer.split()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,8 @@ use std::fmt::Display;
|
|||||||
use serde::{ser, Serialize};
|
use serde::{ser, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::VarInt;
|
||||||
|
|
||||||
use super::ByteBuffer;
|
use super::ByteBuffer;
|
||||||
|
|
||||||
pub struct Serializer {
|
pub struct Serializer {
|
||||||
@ -121,16 +123,22 @@ impl<'a> ser::Serializer for &'a mut Serializer {
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||||
unimplemented!()
|
self.output.put_bool(false);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
||||||
unimplemented!()
|
if let Some(len) = len {
|
||||||
|
self.output.put_var_int(&VarInt(len as i32));
|
||||||
}
|
}
|
||||||
fn serialize_some<T>(self, _value: &T) -> Result<Self::Ok, Self::Error>
|
Ok(self)
|
||||||
|
}
|
||||||
|
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||||
where
|
where
|
||||||
T: ?Sized + Serialize,
|
T: ?Sized + Serialize,
|
||||||
{
|
{
|
||||||
unimplemented!()
|
self.output.put_bool(true);
|
||||||
|
dbg!("aa");
|
||||||
|
value.serialize(self)
|
||||||
}
|
}
|
||||||
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
|
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
|
||||||
self.output.put_string(v);
|
self.output.put_string(v);
|
||||||
@ -213,16 +221,16 @@ impl<'a> ser::SerializeSeq for &'a mut Serializer {
|
|||||||
type Error = SerializerError;
|
type Error = SerializerError;
|
||||||
|
|
||||||
// Serialize a single element of the sequence.
|
// Serialize a single element of the sequence.
|
||||||
fn serialize_element<T>(&mut self, _value: &T) -> Result<(), Self::Error>
|
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
||||||
where
|
where
|
||||||
T: ?Sized + Serialize,
|
T: ?Sized + Serialize,
|
||||||
{
|
{
|
||||||
todo!()
|
value.serialize(&mut **self)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the sequence.
|
// Close the sequence.
|
||||||
fn end(self) -> Result<(), Self::Error> {
|
fn end(self) -> Result<(), Self::Error> {
|
||||||
todo!()
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
use pumpkin_macros::packet;
|
use pumpkin_macros::packet;
|
||||||
|
|
||||||
use crate::{bytebuf::ByteBuffer, ClientPacket, VarIntType};
|
use crate::{bytebuf::ByteBuffer, ClientPacket, VarInt};
|
||||||
|
|
||||||
#[packet(0x01)]
|
#[packet(0x01)]
|
||||||
pub struct CEncryptionRequest<'a> {
|
pub struct CEncryptionRequest<'a> {
|
||||||
server_id: &'a str, // 20
|
server_id: &'a str, // 20
|
||||||
|
public_key_length: VarInt,
|
||||||
public_key: &'a [u8],
|
public_key: &'a [u8],
|
||||||
|
verify_token_length: VarInt,
|
||||||
verify_token: &'a [u8],
|
verify_token: &'a [u8],
|
||||||
should_authenticate: bool,
|
should_authenticate: bool,
|
||||||
}
|
}
|
||||||
@ -19,7 +21,9 @@ impl<'a> CEncryptionRequest<'a> {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
server_id,
|
server_id,
|
||||||
|
public_key_length: public_key.len().into(),
|
||||||
public_key,
|
public_key,
|
||||||
|
verify_token_length: verify_token.len().into(),
|
||||||
verify_token,
|
verify_token,
|
||||||
should_authenticate,
|
should_authenticate,
|
||||||
}
|
}
|
||||||
@ -29,9 +33,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 VarIntType).into());
|
bytebuf.put_var_int(&self.public_key_length);
|
||||||
bytebuf.put_slice(self.public_key);
|
bytebuf.put_slice(self.public_key);
|
||||||
bytebuf.put_var_int(&(self.verify_token.len() as VarIntType).into());
|
bytebuf.put_var_int(&self.verify_token_length);
|
||||||
bytebuf.put_slice(self.verify_token);
|
bytebuf.put_slice(self.verify_token);
|
||||||
bytebuf.put_bool(self.should_authenticate);
|
bytebuf.put_bool(self.should_authenticate);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,10 +33,7 @@ impl<'a> ClientPacket for CLoginSuccess<'a> {
|
|||||||
bytebuf.put_list::<Property>(self.properties, |p, v| {
|
bytebuf.put_list::<Property>(self.properties, |p, v| {
|
||||||
p.put_string(&v.name);
|
p.put_string(&v.name);
|
||||||
p.put_string(&v.value);
|
p.put_string(&v.value);
|
||||||
// has signature ?
|
p.put_option(&v.signature, |p, v| p.put_string(v));
|
||||||
// todo: for some reason we get "got too many bytes error when using a signature"
|
|
||||||
p.put_bool(false);
|
|
||||||
// p.put_option(&v.signature, |p,v| p.put_string(v));
|
|
||||||
});
|
});
|
||||||
bytebuf.put_bool(self.strict_error_handling);
|
bytebuf.put_bool(self.strict_error_handling);
|
||||||
}
|
}
|
||||||
|
|||||||
31
pumpkin-protocol/src/client/play/c_entity_animation.rs
Normal file
31
pumpkin-protocol/src/client/play/c_entity_animation.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use num_derive::ToPrimitive;
|
||||||
|
use pumpkin_macros::packet;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::VarInt;
|
||||||
|
|
||||||
|
#[derive(Serialize, Clone)]
|
||||||
|
#[packet(0x03)]
|
||||||
|
pub struct CEntityAnimation {
|
||||||
|
entity_id: VarInt,
|
||||||
|
/// See `Animation`
|
||||||
|
animation: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CEntityAnimation {
|
||||||
|
pub fn new(entity_id: VarInt, animation: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
entity_id,
|
||||||
|
animation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ToPrimitive)]
|
||||||
|
pub enum Animation {
|
||||||
|
SwingMainArm,
|
||||||
|
LeaveBed,
|
||||||
|
SwingOffhand,
|
||||||
|
CriticalEffect,
|
||||||
|
MagicCriticaleffect,
|
||||||
|
}
|
||||||
33
pumpkin-protocol/src/client/play/c_entity_metadata.rs
Normal file
33
pumpkin-protocol/src/client/play/c_entity_metadata.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use pumpkin_macros::packet;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::VarInt;
|
||||||
|
|
||||||
|
#[derive(Serialize, Clone)]
|
||||||
|
#[packet(0x58)]
|
||||||
|
pub struct CSetEntityMetadata {
|
||||||
|
entity_id: VarInt,
|
||||||
|
metadata: Vec<Metadata>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CSetEntityMetadata {
|
||||||
|
pub fn new(entity_id: VarInt, metadata: Vec<Metadata>) -> Self {
|
||||||
|
Self {
|
||||||
|
entity_id,
|
||||||
|
metadata,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Clone)]
|
||||||
|
pub struct Metadata {
|
||||||
|
index: u8,
|
||||||
|
typ: VarInt,
|
||||||
|
value: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Metadata {
|
||||||
|
pub fn new(index: u8, typ: VarInt, value: u8) -> Self {
|
||||||
|
Self { index, typ, value }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,9 @@
|
|||||||
use pumpkin_macros::packet;
|
use pumpkin_macros::packet;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::{bytebuf::ByteBuffer, ClientPacket, VarInt};
|
use crate::VarInt;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
#[packet(0x2B)]
|
#[packet(0x2B)]
|
||||||
pub struct CLogin {
|
pub struct CLogin {
|
||||||
entity_id: i32,
|
entity_id: i32,
|
||||||
@ -20,9 +22,7 @@ pub struct CLogin {
|
|||||||
previous_gamemode: i8,
|
previous_gamemode: i8,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
is_flat: bool,
|
is_flat: bool,
|
||||||
has_death_loc: bool,
|
death_dimension_name: Option<(String, i64)>, // POSITION NOT STRING
|
||||||
death_dimension_name: Option<String>,
|
|
||||||
death_loc: Option<i64>, // POSITION NOT STRING
|
|
||||||
portal_cooldown: VarInt,
|
portal_cooldown: VarInt,
|
||||||
enforce_secure_chat: bool,
|
enforce_secure_chat: bool,
|
||||||
}
|
}
|
||||||
@ -46,9 +46,7 @@ impl CLogin {
|
|||||||
previous_gamemode: i8,
|
previous_gamemode: i8,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
is_flat: bool,
|
is_flat: bool,
|
||||||
has_death_loc: bool,
|
death_dimension_name: Option<(String, i64)>,
|
||||||
death_dimension_name: Option<String>,
|
|
||||||
death_loc: Option<i64>, // todo add block pos
|
|
||||||
portal_cooldown: VarInt,
|
portal_cooldown: VarInt,
|
||||||
enforce_secure_chat: bool,
|
enforce_secure_chat: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -69,39 +67,9 @@ impl CLogin {
|
|||||||
previous_gamemode,
|
previous_gamemode,
|
||||||
debug,
|
debug,
|
||||||
is_flat,
|
is_flat,
|
||||||
has_death_loc,
|
|
||||||
death_dimension_name,
|
death_dimension_name,
|
||||||
death_loc,
|
|
||||||
portal_cooldown,
|
portal_cooldown,
|
||||||
enforce_secure_chat,
|
enforce_secure_chat,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientPacket for CLogin {
|
|
||||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
|
||||||
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_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_string(&self.dimension_name);
|
|
||||||
bytebuf.put_i64(self.hashed_seed);
|
|
||||||
bytebuf.put_u8(self.game_mode);
|
|
||||||
bytebuf.put_i8(self.previous_gamemode);
|
|
||||||
bytebuf.put_bool(self.debug);
|
|
||||||
bytebuf.put_bool(self.is_flat);
|
|
||||||
bytebuf.put_bool(self.has_death_loc);
|
|
||||||
if self.has_death_loc {
|
|
||||||
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_bool(self.enforce_secure_chat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use pumpkin_macros::packet;
|
use pumpkin_macros::packet;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::{bytebuf::ByteBuffer, ClientPacket};
|
#[derive(Serialize)]
|
||||||
|
|
||||||
#[packet(0x38)]
|
#[packet(0x38)]
|
||||||
pub struct CPlayerAbilities {
|
pub struct CPlayerAbilities {
|
||||||
flags: i8,
|
flags: i8,
|
||||||
@ -18,11 +18,3 @@ impl CPlayerAbilities {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientPacket for CPlayerAbilities {
|
|
||||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
|
||||||
bytebuf.put_i8(self.flags);
|
|
||||||
bytebuf.put_f32(self.flying_speed);
|
|
||||||
bytebuf.put_f32(self.field_of_view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
70
pumpkin-protocol/src/client/play/c_player_chat_message.rs
Normal file
70
pumpkin-protocol/src/client/play/c_player_chat_message.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use num_derive::FromPrimitive;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::{text::TextComponent, VarInt};
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct PlayerChatMessage<'a> {
|
||||||
|
sender: uuid::Uuid,
|
||||||
|
index: VarInt,
|
||||||
|
message_signature: Option<&'a [u8]>,
|
||||||
|
message: String,
|
||||||
|
timestamp: i64,
|
||||||
|
salt: i64,
|
||||||
|
previous_messages: &'a [PreviousMessage<'a>], // max 20
|
||||||
|
unsigned_content: Option<TextComponent>,
|
||||||
|
/// See `FilterType`
|
||||||
|
filter_type: VarInt,
|
||||||
|
chat_type: VarInt,
|
||||||
|
sender_name: TextComponent,
|
||||||
|
target_name: Option<TextComponent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PlayerChatMessage<'a> {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn new(
|
||||||
|
sender: uuid::Uuid,
|
||||||
|
index: VarInt,
|
||||||
|
message_signature: Option<&'a [u8]>,
|
||||||
|
message: String,
|
||||||
|
timestamp: i64,
|
||||||
|
salt: i64,
|
||||||
|
previous_messages: &'a [PreviousMessage<'a>],
|
||||||
|
unsigned_content: Option<TextComponent>,
|
||||||
|
filter_type: VarInt,
|
||||||
|
chat_type: VarInt,
|
||||||
|
sender_name: TextComponent,
|
||||||
|
target_name: Option<TextComponent>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
sender,
|
||||||
|
index,
|
||||||
|
message_signature,
|
||||||
|
message,
|
||||||
|
timestamp,
|
||||||
|
salt,
|
||||||
|
previous_messages,
|
||||||
|
unsigned_content,
|
||||||
|
filter_type,
|
||||||
|
chat_type,
|
||||||
|
sender_name,
|
||||||
|
target_name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct PreviousMessage<'a> {
|
||||||
|
message_id: VarInt,
|
||||||
|
signature: Option<&'a [u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromPrimitive)]
|
||||||
|
pub enum FilterType {
|
||||||
|
/// Message is not filtered at all
|
||||||
|
PassThrough,
|
||||||
|
/// Message is fully filtered
|
||||||
|
FullyFiltered,
|
||||||
|
/// Only some characters in the message are filtered
|
||||||
|
PartiallyFiltered,
|
||||||
|
}
|
||||||
@ -34,10 +34,7 @@ impl<'a> ClientPacket for CPlayerInfoUpdate<'a> {
|
|||||||
p.put_list::<Property>(properties, |p, v| {
|
p.put_list::<Property>(properties, |p, v| {
|
||||||
p.put_string(&v.name);
|
p.put_string(&v.name);
|
||||||
p.put_string(&v.value);
|
p.put_string(&v.value);
|
||||||
// has signature ?
|
p.put_option(&v.signature, |p, v| p.put_string(v));
|
||||||
// todo: for some reason we get "got too many bytes error when using a signature"
|
|
||||||
p.put_bool(false);
|
|
||||||
// todo signature
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
PlayerAction::InitializeChat(_) => todo!(),
|
PlayerAction::InitializeChat(_) => todo!(),
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
mod c_change_difficulty;
|
mod c_change_difficulty;
|
||||||
mod c_chunk_data_update_light;
|
mod c_chunk_data_update_light;
|
||||||
|
mod c_entity_animation;
|
||||||
|
mod c_entity_metadata;
|
||||||
mod c_game_event;
|
mod c_game_event;
|
||||||
mod c_head_rot;
|
mod c_head_rot;
|
||||||
mod c_login;
|
mod c_login;
|
||||||
mod c_play_disconnect;
|
mod c_play_disconnect;
|
||||||
mod c_player_abilities;
|
mod c_player_abilities;
|
||||||
|
mod c_player_chat_message;
|
||||||
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_spawn_player;
|
||||||
@ -17,11 +20,14 @@ mod player_action;
|
|||||||
|
|
||||||
pub use c_change_difficulty::*;
|
pub use c_change_difficulty::*;
|
||||||
pub use c_chunk_data_update_light::*;
|
pub use c_chunk_data_update_light::*;
|
||||||
|
pub use c_entity_animation::*;
|
||||||
|
pub use c_entity_metadata::*;
|
||||||
pub use c_game_event::*;
|
pub use c_game_event::*;
|
||||||
pub use c_head_rot::*;
|
pub use c_head_rot::*;
|
||||||
pub use c_login::*;
|
pub use c_login::*;
|
||||||
pub use c_play_disconnect::*;
|
pub use c_play_disconnect::*;
|
||||||
pub use c_player_abilities::*;
|
pub use c_player_abilities::*;
|
||||||
|
pub use c_player_chat_message::*;
|
||||||
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_spawn_player::*;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use pumpkin_macros::packet;
|
use pumpkin_macros::packet;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(serde::Serialize)]
|
#[derive(Serialize)]
|
||||||
#[packet(0x01)]
|
#[packet(0x01)]
|
||||||
pub struct CPingResponse {
|
pub struct CPingResponse {
|
||||||
payload: i64, // must responde with the same as in `SPingRequest`
|
payload: i64, // must responde with the same as in `SPingRequest`
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use pumpkin_macros::packet;
|
use pumpkin_macros::packet;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(serde::Serialize)]
|
#[derive(Serialize)]
|
||||||
#[packet(0x00)]
|
#[packet(0x00)]
|
||||||
pub struct CStatusResponse<'a> {
|
pub struct CStatusResponse<'a> {
|
||||||
json_response: &'a str, // 32767
|
json_response: &'a str, // 32767
|
||||||
|
|||||||
@ -213,7 +213,7 @@ pub struct Sample {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// basicly game profile
|
// basicly game profile
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct Property {
|
pub struct Property {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
// base 64
|
// base 64
|
||||||
|
|||||||
@ -1,19 +1,49 @@
|
|||||||
use std::net::IpAddr;
|
use std::{collections::HashMap, net::IpAddr};
|
||||||
|
|
||||||
|
use base64::{engine::general_purpose, Engine};
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
use pumpkin_protocol::Property;
|
use pumpkin_protocol::Property;
|
||||||
use reqwest::StatusCode;
|
use reqwest::{StatusCode, Url};
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::server::Server;
|
use crate::{config::auth_config::TextureConfig, server::Server};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct ProfileTextures {
|
||||||
|
timestamp: i64,
|
||||||
|
profileId: Uuid,
|
||||||
|
profileName: String,
|
||||||
|
signatureRequired: bool,
|
||||||
|
textures: HashMap<String, Texture>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct Texture {
|
||||||
|
url: String,
|
||||||
|
metadata: Option<HashMap<String, String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum ProfileAction {
|
||||||
|
#[serde(rename = "FORCED_NAME_CHANGE")]
|
||||||
|
ForcedNameChange,
|
||||||
|
#[serde(rename = "USING_BANNED_SKIN")]
|
||||||
|
UsingBannedSkin,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
pub struct GameProfile {
|
pub struct GameProfile {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub properties: Vec<Property>,
|
pub properties: Vec<Property>,
|
||||||
|
#[serde(rename = "profileActions")]
|
||||||
|
pub profile_actions: Option<Vec<ProfileAction>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authenticate(
|
pub fn authenticate(
|
||||||
@ -22,8 +52,13 @@ pub fn authenticate(
|
|||||||
ip: &IpAddr,
|
ip: &IpAddr,
|
||||||
server: &mut Server,
|
server: &mut Server,
|
||||||
) -> Result<GameProfile, AuthError> {
|
) -> Result<GameProfile, AuthError> {
|
||||||
|
assert!(server.advanced_config.authentication.use_authentication);
|
||||||
assert!(server.auth_client.is_some());
|
assert!(server.auth_client.is_some());
|
||||||
let address = if server.base_config.prevent_proxy_connections {
|
let address = if server
|
||||||
|
.advanced_config
|
||||||
|
.authentication
|
||||||
|
.prevent_proxy_connections
|
||||||
|
{
|
||||||
format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}&ip={ip}")
|
format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}&ip={ip}")
|
||||||
} else {
|
} else {
|
||||||
format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}")
|
format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}")
|
||||||
@ -44,10 +79,32 @@ pub fn authenticate(
|
|||||||
Ok(profile)
|
Ok(profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unpack_textures(property: Property, config: &TextureConfig) {
|
||||||
|
// todo: no unwrap
|
||||||
|
let from64 = general_purpose::STANDARD.decode(property.value).unwrap();
|
||||||
|
let textures: ProfileTextures = serde_json::from_slice(&from64).unwrap();
|
||||||
|
dbg!(&textures);
|
||||||
|
for texture in textures.textures {
|
||||||
|
is_texture_url_valid(Url::parse(&texture.1.url).unwrap(), config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn auth_digest(bytes: &[u8]) -> String {
|
pub fn auth_digest(bytes: &[u8]) -> String {
|
||||||
BigInt::from_signed_bytes_be(bytes).to_str_radix(16)
|
BigInt::from_signed_bytes_be(bytes).to_str_radix(16)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_texture_url_valid(url: Url, config: &TextureConfig) -> bool {
|
||||||
|
let scheme = url.scheme();
|
||||||
|
if !config.allowed_url_schemes.contains(&scheme.to_string()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let domain = url.domain().unwrap_or("");
|
||||||
|
if !config.allowed_url_domains.contains(&domain.to_string()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum AuthError {
|
pub enum AuthError {
|
||||||
#[error("Authentication servers are down")]
|
#[error("Authentication servers are down")]
|
||||||
|
|||||||
@ -23,7 +23,10 @@ use crate::{
|
|||||||
server::Server,
|
server::Server,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{authentication::auth_digest, Client, EncryptionError, PlayerConfig};
|
use super::{
|
||||||
|
authentication::{auth_digest, unpack_textures},
|
||||||
|
Client, EncryptionError, PlayerConfig,
|
||||||
|
};
|
||||||
|
|
||||||
/// Processes incoming Packets from the Client to the Server
|
/// Processes incoming Packets from the Client to the Server
|
||||||
/// Implements the `Client` Packets
|
/// Implements the `Client` Packets
|
||||||
@ -45,12 +48,15 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_login_start(&mut self, server: &mut Server, login_start: SLoginStart) {
|
pub fn handle_login_start(&mut self, server: &mut Server, login_start: SLoginStart) {
|
||||||
|
// todo: do basic name validation
|
||||||
dbg!("login start");
|
dbg!("login start");
|
||||||
// default game profile, when no online mode
|
// default game profile, when no online mode
|
||||||
|
// todo: make offline uuid
|
||||||
self.gameprofile = Some(GameProfile {
|
self.gameprofile = Some(GameProfile {
|
||||||
id: login_start.uuid,
|
id: login_start.uuid,
|
||||||
name: login_start.name,
|
name: login_start.name,
|
||||||
properties: vec![],
|
properties: vec![],
|
||||||
|
profile_actions: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
// todo: check config for encryption
|
// todo: check config for encryption
|
||||||
@ -91,10 +97,40 @@ impl Client {
|
|||||||
&ip,
|
&ip,
|
||||||
server,
|
server,
|
||||||
) {
|
) {
|
||||||
Ok(p) => self.gameprofile = Some(p),
|
Ok(p) => {
|
||||||
|
// Check if player should join
|
||||||
|
if let Some(p) = &p.profile_actions {
|
||||||
|
if !server
|
||||||
|
.advanced_config
|
||||||
|
.authentication
|
||||||
|
.player_profile
|
||||||
|
.allow_banned_players
|
||||||
|
{
|
||||||
|
if !p.is_empty() {
|
||||||
|
self.kick("Your account can't join");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for allowed in server
|
||||||
|
.advanced_config
|
||||||
|
.authentication
|
||||||
|
.player_profile
|
||||||
|
.allowed_actions
|
||||||
|
.clone()
|
||||||
|
{
|
||||||
|
if !p.contains(&allowed) {
|
||||||
|
self.kick("Your account can't join");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.gameprofile = Some(p);
|
||||||
|
}
|
||||||
Err(e) => self.kick(&e.to_string()),
|
Err(e) => self.kick(&e.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for ele in self.gameprofile.as_ref().unwrap().properties.clone() {
|
||||||
|
unpack_textures(ele, &server.advanced_config.authentication.textures);
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
@ -28,7 +28,7 @@ use pumpkin_protocol::{
|
|||||||
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
|
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
|
||||||
play::{
|
play::{
|
||||||
SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition,
|
SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition,
|
||||||
SPlayerPositionRotation, SPlayerRotation,
|
SPlayerPositionRotation, SPlayerRotation, SSwingArm,
|
||||||
},
|
},
|
||||||
status::{SPingRequest, SStatusRequest},
|
status::{SPingRequest, SStatusRequest},
|
||||||
},
|
},
|
||||||
@ -39,7 +39,7 @@ use pumpkin_protocol::{
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
mod authentication;
|
pub mod authentication;
|
||||||
mod client_packet;
|
mod client_packet;
|
||||||
pub mod player_packet;
|
pub mod player_packet;
|
||||||
|
|
||||||
@ -258,6 +258,7 @@ impl Client {
|
|||||||
SPlayerCommand::PACKET_ID => {
|
SPlayerCommand::PACKET_ID => {
|
||||||
self.handle_player_command(server, SPlayerCommand::read(bytebuf).unwrap())
|
self.handle_player_command(server, SPlayerCommand::read(bytebuf).unwrap())
|
||||||
}
|
}
|
||||||
|
SSwingArm::PACKET_ID => self.handle_swing_arm(server, SSwingArm::read(bytebuf)),
|
||||||
_ => log::error!("Failed to handle player packet id {}", packet.id.0),
|
_ => log::error!("Failed to handle player packet id {}", packet.id.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,18 @@
|
|||||||
|
use num_traits::FromPrimitive;
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{
|
||||||
client::play::{CHeadRot, CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot},
|
client::play::{
|
||||||
|
Animation, CEntityAnimation, CHeadRot, CUpdateEntityPos, CUpdateEntityPosRot,
|
||||||
|
CUpdateEntityRot,
|
||||||
|
},
|
||||||
server::play::{
|
server::play::{
|
||||||
SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation,
|
SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation,
|
||||||
SPlayerRotation,
|
SPlayerRotation, SSwingArm,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::{handle_command, CommandSender},
|
commands::{handle_command, CommandSender},
|
||||||
|
entity::player::Hand,
|
||||||
server::Server,
|
server::Server,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -156,4 +161,14 @@ impl Client {
|
|||||||
pumpkin_protocol::server::play::Action::StartFlyingElytra => todo!(),
|
pumpkin_protocol::server::play::Action::StartFlyingElytra => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_swing_arm(&mut self, server: &mut Server, swing_arm: SSwingArm) {
|
||||||
|
let animation = match Hand::from_i32(swing_arm.hand.0).unwrap() {
|
||||||
|
Hand::Main => Animation::SwingMainArm,
|
||||||
|
Hand::Off => Animation::SwingOffhand,
|
||||||
|
};
|
||||||
|
let player = self.player.as_mut().unwrap();
|
||||||
|
let id = player.entity_id();
|
||||||
|
server.broadcast_packet_expect(self, CEntityAnimation::new(id.into(), animation as u8))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
93
pumpkin/src/config/auth_config.rs
Normal file
93
pumpkin/src/config/auth_config.rs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::client::authentication::ProfileAction;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Authentication {
|
||||||
|
/// Whether to use Mojang authentication.
|
||||||
|
pub use_authentication: bool,
|
||||||
|
|
||||||
|
/// Prevent proxy connections.
|
||||||
|
pub prevent_proxy_connections: bool,
|
||||||
|
|
||||||
|
/// Player profile handling.
|
||||||
|
pub player_profile: PlayerProfileConfig,
|
||||||
|
|
||||||
|
/// Texture handling.
|
||||||
|
pub textures: TextureConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct PlayerProfileConfig {
|
||||||
|
/// Allow players flagged by Mojang (banned, forced name change).
|
||||||
|
pub allow_banned_players: bool,
|
||||||
|
/// Depends on the value above
|
||||||
|
pub allowed_actions: Vec<ProfileAction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PlayerProfileConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
allow_banned_players: false,
|
||||||
|
allowed_actions: vec![
|
||||||
|
ProfileAction::ForcedNameChange,
|
||||||
|
ProfileAction::UsingBannedSkin,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct TextureConfig {
|
||||||
|
/// Whether to use player textures.
|
||||||
|
pub enabled: bool,
|
||||||
|
|
||||||
|
pub allowed_url_schemes: Vec<String>,
|
||||||
|
pub allowed_url_domains: Vec<String>,
|
||||||
|
|
||||||
|
/// Specific texture types.
|
||||||
|
pub types: TextureTypes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TextureConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
enabled: true,
|
||||||
|
allowed_url_schemes: vec!["http".into(), "https".into()],
|
||||||
|
allowed_url_domains: vec![".minecraft.net".into(), ".mojang.com".into()],
|
||||||
|
types: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct TextureTypes {
|
||||||
|
/// Use player skins.
|
||||||
|
pub skin: bool,
|
||||||
|
/// Use player capes.
|
||||||
|
pub cape: bool,
|
||||||
|
/// Use player elytras.
|
||||||
|
/// (i didn't know myself that there are custom elytras)
|
||||||
|
pub elytra: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TextureTypes {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
skin: true,
|
||||||
|
cape: true,
|
||||||
|
elytra: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Authentication {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
use_authentication: true,
|
||||||
|
prevent_proxy_connections: true,
|
||||||
|
player_profile: Default::default(),
|
||||||
|
textures: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,12 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
use auth_config::Authentication;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{entity::player::GameMode, server::Difficulty};
|
use crate::{entity::player::GameMode, server::Difficulty};
|
||||||
|
|
||||||
|
pub mod auth_config;
|
||||||
|
|
||||||
/// Current Config version of the Base Config
|
/// Current Config version of the Base Config
|
||||||
const CURRENT_BASE_VERSION: &str = "1.0.0";
|
const CURRENT_BASE_VERSION: &str = "1.0.0";
|
||||||
|
|
||||||
@ -12,19 +15,15 @@ const CURRENT_BASE_VERSION: &str = "1.0.0";
|
|||||||
/// This also allows you get some Performance or Resource boosts.
|
/// This also allows you get some Performance or Resource boosts.
|
||||||
/// Important: The Configuration should match Vanilla by default
|
/// Important: The Configuration should match Vanilla by default
|
||||||
pub struct AdvancedConfiguration {
|
pub struct AdvancedConfiguration {
|
||||||
/// Requires Online mode
|
|
||||||
/// Should player have skins
|
|
||||||
pub use_skins: bool,
|
|
||||||
/// Should chat be enabled
|
|
||||||
pub enable_chat: bool,
|
|
||||||
|
|
||||||
pub commands: Commands,
|
pub commands: Commands,
|
||||||
|
pub authentication: Authentication,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct Commands {
|
pub struct Commands {
|
||||||
// Are commands from the Console accepted ?
|
/// Are commands from the Console accepted ?
|
||||||
pub use_console: bool,
|
pub use_console: bool,
|
||||||
|
// todo commands...
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Commands {
|
impl Default for Commands {
|
||||||
@ -37,8 +36,7 @@ impl Default for Commands {
|
|||||||
impl Default for AdvancedConfiguration {
|
impl Default for AdvancedConfiguration {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
use_skins: true,
|
authentication: Authentication::default(),
|
||||||
enable_chat: true,
|
|
||||||
commands: Commands::default(),
|
commands: Commands::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,8 +72,6 @@ pub struct BasicConfiguration {
|
|||||||
pub online_mode: bool,
|
pub online_mode: bool,
|
||||||
/// Whether packet encryption is enabled. Required when online mode is enabled.
|
/// Whether packet encryption is enabled. Required when online mode is enabled.
|
||||||
pub encryption: bool,
|
pub encryption: bool,
|
||||||
/// Whether to prevent proxy connections.
|
|
||||||
pub prevent_proxy_connections: bool,
|
|
||||||
/// The server's description displayed on the status screen.
|
/// The server's description displayed on the status screen.
|
||||||
pub motd: String,
|
pub motd: String,
|
||||||
/// The default game mode for players.
|
/// The default game mode for players.
|
||||||
@ -99,7 +95,6 @@ impl Default for BasicConfiguration {
|
|||||||
hardcore: false,
|
hardcore: false,
|
||||||
online_mode: true,
|
online_mode: true,
|
||||||
encryption: true,
|
encryption: true,
|
||||||
prevent_proxy_connections: true,
|
|
||||||
motd: "A Blazing fast Pumpkin Server!".to_string(),
|
motd: "A Blazing fast Pumpkin Server!".to_string(),
|
||||||
default_gamemode: GameMode::Survival,
|
default_gamemode: GameMode::Survival,
|
||||||
}
|
}
|
||||||
@ -4,12 +4,12 @@ use std::io::{self};
|
|||||||
|
|
||||||
use client::Client;
|
use client::Client;
|
||||||
use commands::handle_command;
|
use commands::handle_command;
|
||||||
use configuration::AdvancedConfiguration;
|
use config::AdvancedConfiguration;
|
||||||
|
|
||||||
use std::{collections::HashMap, rc::Rc, thread};
|
use std::{collections::HashMap, rc::Rc, thread};
|
||||||
|
|
||||||
use client::interrupted;
|
use client::interrupted;
|
||||||
use configuration::BasicConfiguration;
|
use config::BasicConfiguration;
|
||||||
use server::Server;
|
use server::Server;
|
||||||
|
|
||||||
// Setup some tokens to allow us to identify which event is for which socket.
|
// Setup some tokens to allow us to identify which event is for which socket.
|
||||||
@ -17,7 +17,7 @@ const SERVER: Token = Token(0);
|
|||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod configuration;
|
pub mod config;
|
||||||
pub mod entity;
|
pub mod entity;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
@ -104,6 +104,9 @@ fn main() -> io::Result<()> {
|
|||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if let Err(e) = connection.set_nodelay(true) {
|
||||||
|
log::warn!("failed to set TCP_NODELAY {e}");
|
||||||
|
}
|
||||||
|
|
||||||
log::info!("Accepted connection from: {}", address);
|
log::info!("Accepted connection from: {}", address);
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::Client,
|
client::Client,
|
||||||
configuration::{AdvancedConfiguration, BasicConfiguration},
|
config::{AdvancedConfiguration, BasicConfiguration},
|
||||||
entity::player::{GameMode, Player},
|
entity::player::{GameMode, Player},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -144,8 +144,6 @@ impl Server {
|
|||||||
self.base_config.default_gamemode.to_i8().unwrap(),
|
self.base_config.default_gamemode.to_i8().unwrap(),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false, // deth loc
|
|
||||||
None,
|
|
||||||
None,
|
None,
|
||||||
0.into(),
|
0.into(),
|
||||||
false,
|
false,
|
||||||
@ -160,19 +158,7 @@ impl Server {
|
|||||||
client.teleport(x, y, z, 10.0, 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)
|
// also send his info to everyone else
|
||||||
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(
|
self.broadcast_packet(
|
||||||
client,
|
client,
|
||||||
CPlayerInfoUpdate::new(
|
CPlayerInfoUpdate::new(
|
||||||
@ -210,7 +196,7 @@ impl Server {
|
|||||||
let gameprofile = client.gameprofile.as_ref().unwrap();
|
let gameprofile = client.gameprofile.as_ref().unwrap();
|
||||||
|
|
||||||
// spawn player for every client
|
// spawn player for every client
|
||||||
self.broadcast_packet(
|
self.broadcast_packet_expect(
|
||||||
client,
|
client,
|
||||||
CSpawnEntity::new(
|
CSpawnEntity::new(
|
||||||
entity_id.into(),
|
entity_id.into(),
|
||||||
@ -252,12 +238,37 @@ impl Server {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// entity meta data
|
||||||
|
/* if let Some(config) = &client.config {
|
||||||
|
self.broadcast_packet(
|
||||||
|
client,
|
||||||
|
CSetEntityMetadata::new(entity_id.into(), vec![Metadata::new(18, VarInt(0), 0)]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// Server::spawn_test_chunk(client);
|
// Server::spawn_test_chunk(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a Packet to all Players
|
/// Sends a Packet to all Players
|
||||||
pub fn broadcast_packet<P>(&mut self, from: &Client, packet: P)
|
pub fn broadcast_packet<P>(&mut self, from: &mut Client, packet: P)
|
||||||
|
where
|
||||||
|
P: ClientPacket,
|
||||||
|
P: Clone,
|
||||||
|
{
|
||||||
|
// we can't borrow twice at same time
|
||||||
|
from.send_packet(packet.clone());
|
||||||
|
for (_, client) in self.current_clients.iter().filter(|c| c.0 != &from.token) {
|
||||||
|
// Check if client is a 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.send_packet(packet.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn broadcast_packet_expect<P>(&mut self, from: &mut Client, packet: P)
|
||||||
where
|
where
|
||||||
P: ClientPacket,
|
P: ClientPacket,
|
||||||
P: Clone,
|
P: Clone,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user