mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
fix world y offset & bedrock update (#1121)
This commit is contained in:
Binary file not shown.
@@ -841,8 +841,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
let start_id = id as u16 + i as u16;
|
||||
block_state_to_bedrock.push((state.id, start_id))
|
||||
} else {
|
||||
let start_id = id as u16 + state_count as u16 - 1;
|
||||
block_state_to_bedrock.push((state.id, start_id))
|
||||
block_state_to_bedrock.push((state.id, id as u16))
|
||||
}
|
||||
}
|
||||
//else {
|
||||
|
||||
15
pumpkin-protocol/src/bedrock/client/correct_player_move.rs
Normal file
15
pumpkin-protocol/src/bedrock/client/correct_player_move.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use pumpkin_macros::packet;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::{codec::var_ulong::VarULong, serial::PacketWrite};
|
||||
|
||||
#[derive(PacketWrite)]
|
||||
#[packet(161)]
|
||||
pub struct CCorrectPlayerMove {
|
||||
// https://mojang.github.io/bedrock-protocol-docs/html/CorrectPlayerMovePredictionPacket.html
|
||||
pub prediction_type: u8,
|
||||
pub pos: Vector3<f32>,
|
||||
pub pos_delta: Vector3<f32>,
|
||||
pub on_ground: bool,
|
||||
pub tick: VarULong,
|
||||
}
|
||||
@@ -27,22 +27,26 @@ impl<'a> PacketWrite for CLevelChunk<'a> {
|
||||
|
||||
VarInt(self.dimension).write(writer)?;
|
||||
let sub_chunk_count = self.chunk.section.sections.len() as u32;
|
||||
assert_eq!(sub_chunk_count, 24);
|
||||
VarUInt(sub_chunk_count).write(writer)?;
|
||||
self.cache_enabled.write(writer)?;
|
||||
|
||||
let mut chunk_data = Vec::new();
|
||||
let data_write = &mut chunk_data;
|
||||
|
||||
let min_y = (self.chunk.section.min_y >> 4) as i8;
|
||||
|
||||
// Blocks
|
||||
for (i, sub_chunk) in self.chunk.section.sections.iter().enumerate() {
|
||||
// Version 9
|
||||
// [version:byte][num_storages:byte][sub_chunk_index:byte][block storage1]...[blockStorageN]
|
||||
let y = i as i8 + min_y;
|
||||
let num_storages = 1;
|
||||
data_write.write_all(&[VERSION, num_storages, ((i as i8) - 4) as u8])?;
|
||||
data_write.write_all(&[VERSION, num_storages, y as _])?;
|
||||
let network_repr = sub_chunk.block_states.convert_be_network();
|
||||
(network_repr.bits_per_entry << 1 | 1).write(data_write)?;
|
||||
|
||||
for data in network_repr.packed_data.iter() {
|
||||
for data in network_repr.packed_data {
|
||||
data.write(data_write)?;
|
||||
}
|
||||
|
||||
@@ -63,7 +67,8 @@ impl<'a> PacketWrite for CLevelChunk<'a> {
|
||||
// Biomes
|
||||
for i in 0..sub_chunk_count {
|
||||
let num_storages = 1;
|
||||
data_write.write_all(&[VERSION, num_storages, ((i as i8) - 4) as u8])?;
|
||||
let y = i as i8 + min_y;
|
||||
data_write.write_all(&[VERSION, num_storages, y as _])?;
|
||||
|
||||
for _ in 0..num_storages {
|
||||
1u8.write(data_write)?;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod chunk_radius_update;
|
||||
pub mod container_open;
|
||||
pub mod correct_player_move;
|
||||
pub mod creative_content;
|
||||
pub mod disconnect_player;
|
||||
pub mod gamerules_changed;
|
||||
@@ -14,7 +15,9 @@ pub mod player_hotbar;
|
||||
pub mod raknet;
|
||||
pub mod resource_pack_stack;
|
||||
pub mod resource_packs_info;
|
||||
pub mod set_actor_motion;
|
||||
pub mod set_player_gamemode;
|
||||
pub mod set_time;
|
||||
pub mod start_game;
|
||||
pub mod update_abilities;
|
||||
pub mod update_artributes;
|
||||
|
||||
12
pumpkin-protocol/src/bedrock/client/set_actor_motion.rs
Normal file
12
pumpkin-protocol/src/bedrock/client/set_actor_motion.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
use pumpkin_macros::packet;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::{codec::var_ulong::VarULong, serial::PacketWrite};
|
||||
|
||||
#[derive(PacketWrite)]
|
||||
#[packet(40)]
|
||||
pub struct CSetActorMotion {
|
||||
pub target_runtime_id: VarULong,
|
||||
pub motion: Vector3<f32>,
|
||||
pub tick: VarULong,
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::io::{Error, Write};
|
||||
|
||||
use crate::{
|
||||
bedrock::client::gamerules_changed::GameRules,
|
||||
codec::{
|
||||
@@ -7,7 +9,7 @@ use crate::{
|
||||
serial::PacketWrite,
|
||||
};
|
||||
use pumpkin_macros::packet;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_util::{GameMode, math::vector3::Vector3};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(PacketWrite)]
|
||||
@@ -16,7 +18,7 @@ pub struct CStartGame {
|
||||
// https://mojang.github.io/bedrock-protocol-docs/html/StartGamePacket.html
|
||||
pub entity_id: VarLong,
|
||||
pub runtime_entity_id: VarULong,
|
||||
pub player_gamemode: VarInt,
|
||||
pub player_gamemode: GameMode,
|
||||
pub position: Vector3<f32>,
|
||||
pub pitch: f32,
|
||||
pub yaw: f32,
|
||||
@@ -48,6 +50,7 @@ pub struct CStartGame {
|
||||
|
||||
pub enable_clientside_generation: bool,
|
||||
pub blocknetwork_ids_are_hashed: bool,
|
||||
pub tick_death_system_enabled: bool,
|
||||
pub server_auth_sounds: bool,
|
||||
}
|
||||
|
||||
@@ -64,7 +67,7 @@ pub struct LevelSettings {
|
||||
|
||||
// Level Settings
|
||||
pub generator_type: VarInt,
|
||||
pub world_gamemode: VarInt,
|
||||
pub world_gamemode: GameMode,
|
||||
pub hardcore: bool,
|
||||
pub difficulty: VarInt,
|
||||
pub spawn_position: NetworkPos,
|
||||
@@ -81,8 +84,8 @@ pub struct LevelSettings {
|
||||
pub has_confirmed_platform_locked_content: bool,
|
||||
pub was_multiplayer_intended: bool,
|
||||
pub was_lan_broadcasting_intended: bool,
|
||||
pub xbox_live_broadcast_setting: VarInt,
|
||||
pub platform_broadcast_setting: VarInt,
|
||||
pub xbox_live_broadcast_setting: GamePublishSetting,
|
||||
pub platform_broadcast_setting: GamePublishSetting,
|
||||
pub commands_enabled: bool,
|
||||
pub is_texture_packs_required: bool,
|
||||
|
||||
@@ -127,7 +130,7 @@ pub struct Experiments {
|
||||
pub experiments_ever_toggled: bool,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum GamePublishSetting {
|
||||
NoMultiPlay = 0,
|
||||
InviteOnly = 1,
|
||||
@@ -136,6 +139,12 @@ pub enum GamePublishSetting {
|
||||
Public = 4,
|
||||
}
|
||||
|
||||
impl PacketWrite for GamePublishSetting {
|
||||
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
|
||||
VarInt(*self as i32).write(writer)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PacketWrite)]
|
||||
pub struct GG {
|
||||
pub name: String,
|
||||
|
||||
50
pumpkin-protocol/src/bedrock/client/update_abilities.rs
Normal file
50
pumpkin-protocol/src/bedrock/client/update_abilities.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use pumpkin_macros::packet;
|
||||
|
||||
use crate::serial::PacketWrite;
|
||||
|
||||
#[packet(187)]
|
||||
#[derive(PacketWrite)]
|
||||
pub struct CUpdateAbilities {
|
||||
// https://mojang.github.io/bedrock-protocol-docs/html/UpdateAbilitiesPacket.html
|
||||
// https://mojang.github.io/bedrock-protocol-docs/html/SerializedAbilitiesData.html
|
||||
pub target_player_raw_id: i64,
|
||||
pub player_permission: u8,
|
||||
pub command_permission: u8,
|
||||
pub layers: Vec<AbilityLayer>,
|
||||
}
|
||||
|
||||
#[derive(PacketWrite)]
|
||||
pub struct AbilityLayer {
|
||||
// https://mojang.github.io/bedrock-protocol-docs/html/SerializedAbilitiesData__SerializedLayer.html
|
||||
pub serialized_layer: u16,
|
||||
pub abilities_set: u32,
|
||||
pub ability_value: u32,
|
||||
pub fly_speed: f32,
|
||||
pub vertical_fly_speed: f32,
|
||||
pub walk_speed: f32,
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
pub enum Ability {
|
||||
Build = 0,
|
||||
Mine = 1,
|
||||
DoorsAndSwitches = 2,
|
||||
OpenContainers = 3,
|
||||
AttackPlayers = 4,
|
||||
AttackMobs = 5,
|
||||
OperatorCommands = 6,
|
||||
Teleport = 7,
|
||||
Invulnerable = 8,
|
||||
Flying = 9,
|
||||
MayFly = 10,
|
||||
Instabuild = 11,
|
||||
Lightning = 12,
|
||||
FlySpeed = 13,
|
||||
WalkSpeed = 14,
|
||||
Muted = 15,
|
||||
WorldBuilder = 16,
|
||||
NoClip = 17,
|
||||
PrivilegedBuilder = 18,
|
||||
VerticalFlySpeed = 19,
|
||||
AbilityCount = 20,
|
||||
}
|
||||
@@ -27,6 +27,15 @@ impl FrameSet {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FrameSet {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sequence: u24(0),
|
||||
frames: Vec::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Frame {
|
||||
pub reliability: RakReliability,
|
||||
@@ -42,6 +51,20 @@ pub struct Frame {
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
pub fn new_unreliable(payload: Vec<u8>) -> Self {
|
||||
Self {
|
||||
reliability: RakReliability::Unreliable,
|
||||
payload,
|
||||
reliable_number: 0,
|
||||
sequence_index: 0,
|
||||
order_index: 0,
|
||||
order_channel: 0,
|
||||
split_size: 0,
|
||||
split_id: 0,
|
||||
split_index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(reader: &mut R) -> Result<Vec<Self>, Error> {
|
||||
let mut frames = Vec::new();
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ pub mod packet_encoder;
|
||||
pub mod server;
|
||||
|
||||
pub const UDP_HEADER_SIZE: u16 = 28;
|
||||
pub const MTU: usize = 1400;
|
||||
|
||||
pub const RAKNET_MAGIC: [u8; 16] = [
|
||||
0x00, 0xff, 0xff, 0x0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0x12, 0x34, 0x56, 0x78,
|
||||
|
||||
19
pumpkin-protocol/src/bedrock/server/command_request.rs
Normal file
19
pumpkin-protocol/src/bedrock/server/command_request.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use pumpkin_macros::packet;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
codec::{var_int::VarInt, var_uint::VarUInt},
|
||||
serial::PacketRead,
|
||||
};
|
||||
|
||||
#[derive(Debug, PacketRead)]
|
||||
#[packet(77)]
|
||||
pub struct SCommandRequest {
|
||||
// https://mojang.github.io/bedrock-protocol-docs/html/CommandRequestPacket.html
|
||||
pub command: String,
|
||||
pub command_type: VarUInt,
|
||||
pub command_uuid: Uuid,
|
||||
pub request_id: String,
|
||||
pub is_internal_source: bool,
|
||||
pub version: VarInt,
|
||||
}
|
||||
@@ -1,15 +1,34 @@
|
||||
use std::io::{Error, Read};
|
||||
|
||||
use pumpkin_macros::packet;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::{codec::var_ulong::VarULong, serial::PacketRead};
|
||||
|
||||
#[derive(Debug, PacketRead)]
|
||||
#[derive(Debug)]
|
||||
#[packet(33)]
|
||||
pub struct SInteraction {
|
||||
// https://mojang.github.io/bedrock-protocol-docs/html/InteractPacket.html
|
||||
pub action: Action,
|
||||
pub target_runtime_id: VarULong,
|
||||
pub position: Vector3<f32>,
|
||||
}
|
||||
|
||||
impl PacketRead for SInteraction {
|
||||
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
|
||||
let action = Action::read(reader)?;
|
||||
let target_runtime_id = VarULong::read(reader)?;
|
||||
let mut position = Vector3::default();
|
||||
|
||||
if matches!(action, Action::InteractUpdate | Action::StopRiding) {
|
||||
position = Vector3::read(reader)?;
|
||||
}
|
||||
Ok(Self {
|
||||
action,
|
||||
target_runtime_id,
|
||||
position,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
19
pumpkin-protocol/src/bedrock/server/loading_screen.rs
Normal file
19
pumpkin-protocol/src/bedrock/server/loading_screen.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use pumpkin_macros::packet;
|
||||
|
||||
use crate::{codec::var_int::VarInt, serial::PacketRead};
|
||||
|
||||
#[derive(PacketRead)]
|
||||
#[packet(312)]
|
||||
pub struct SLoadingScreen {
|
||||
// https://mojang.github.io/bedrock-protocol-docs/html/ServerboundLoadingScreenPacket.html
|
||||
// Loading Screen Packet Type
|
||||
// 0: Inavil, 1: Start, 2: End
|
||||
status: VarInt,
|
||||
_id: Option<u32>,
|
||||
}
|
||||
|
||||
impl SLoadingScreen {
|
||||
pub fn is_loading_done(&self) -> bool {
|
||||
self.status.0 == 2
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
pub mod client_cache_status;
|
||||
pub mod command_request;
|
||||
pub mod container_close;
|
||||
pub mod interaction;
|
||||
pub mod loading_screen;
|
||||
pub mod login;
|
||||
pub mod player_auth_input;
|
||||
pub mod raknet;
|
||||
|
||||
@@ -2,7 +2,7 @@ use pumpkin_macros::packet;
|
||||
use pumpkin_util::math::{vector2::Vector2, vector3::Vector3};
|
||||
|
||||
use crate::{
|
||||
codec::{var_uint::VarUInt, var_ulong::VarULong},
|
||||
codec::{bitset::Bitset, var_uint::VarUInt, var_ulong::VarULong},
|
||||
serial::PacketRead,
|
||||
};
|
||||
|
||||
@@ -15,12 +15,7 @@ pub struct SPlayerAuthInput {
|
||||
pub position: Vector3<f32>,
|
||||
pub move_vec: Vector2<f32>,
|
||||
pub head_rotation: f32,
|
||||
pub bit1: u8,
|
||||
pub bit2: u8,
|
||||
pub bit3: u8,
|
||||
pub bit4: u8,
|
||||
pub bit5: u8,
|
||||
pub bit6: u8,
|
||||
pub input_data: Bitset<65>,
|
||||
pub input_mode: VarUInt,
|
||||
pub play_mode: VarUInt,
|
||||
pub new_interaction_model: VarUInt,
|
||||
@@ -29,9 +24,10 @@ pub struct SPlayerAuthInput {
|
||||
pub pos_delta: Vector3<f32>,
|
||||
pub analog_move: Vector2<f32>,
|
||||
pub camera_orientation: Vector3<f32>,
|
||||
//pub raw_move_vec: Vector2<f32>,
|
||||
pub raw_move_vec: Vector2<f32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum InputData {
|
||||
// https://mojang.github.io/bedrock-protocol-docs/html/enums.html#PlayerAuthInputPacket::InputData
|
||||
Ascend = 0,
|
||||
@@ -101,6 +97,12 @@ pub enum InputData {
|
||||
InputNum = 65,
|
||||
}
|
||||
|
||||
impl From<InputData> for usize {
|
||||
fn from(value: InputData) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
pub enum InputMode {
|
||||
Undefined = 0,
|
||||
Mouse = 1,
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::{codec::var_int::VarInt, serial::PacketRead};
|
||||
#[derive(PacketRead, Debug)]
|
||||
#[packet(69)]
|
||||
pub struct SRequestChunkRadius {
|
||||
// https://mojang.github.io/bedrock-protocol-docs/html/RequestChunkRadiusPacket.html
|
||||
pub chunk_radius: VarInt,
|
||||
pub max_radius: u8,
|
||||
}
|
||||
|
||||
@@ -34,6 +34,19 @@ impl SText {
|
||||
filtered_message: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn system_message(message: String) -> Self {
|
||||
Self {
|
||||
r#type: TextPacketType::SystemMessage,
|
||||
localize: false,
|
||||
player_name: String::new(),
|
||||
message,
|
||||
parameters: Vec::new(),
|
||||
sender_xuid: String::new(),
|
||||
platform_id: String::new(),
|
||||
filtered_message: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PacketRead for SText {
|
||||
|
||||
54
pumpkin-protocol/src/codec/bitset.rs
Normal file
54
pumpkin-protocol/src/codec/bitset.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
use std::io::{Error, ErrorKind, Read};
|
||||
|
||||
use crate::serial::PacketRead;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Bitset<const N: usize> {
|
||||
pub bits: u128,
|
||||
}
|
||||
|
||||
impl<const N: usize> Bitset<N> {
|
||||
pub fn get<T: Into<usize>>(&self, index: T) -> bool {
|
||||
let index: usize = index.into();
|
||||
if index > N {
|
||||
panic!("")
|
||||
}
|
||||
(self.bits & (1 << index)) != 0
|
||||
}
|
||||
|
||||
pub fn set<T: Into<usize>>(&mut self, index: T, value: bool) {
|
||||
let index: usize = index.into();
|
||||
if index > N {
|
||||
panic!("")
|
||||
}
|
||||
if value {
|
||||
self.bits |= 1 << index;
|
||||
} else {
|
||||
self.bits &= !(1 << index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Default for Bitset<N> {
|
||||
fn default() -> Self {
|
||||
if N > 80 {
|
||||
panic!()
|
||||
}
|
||||
Self { bits: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> PacketRead for Bitset<N> {
|
||||
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
|
||||
let mut bitset = Bitset::<N>::default();
|
||||
|
||||
for i in 0..N.div_ceil(8) {
|
||||
let byte = u8::read(reader)?;
|
||||
bitset.bits |= (u128::from(byte) & 0x7F) << (i * 7);
|
||||
if byte & 0x80 == 0 {
|
||||
return Ok(bitset);
|
||||
}
|
||||
}
|
||||
Err(Error::new(ErrorKind::InvalidData, ""))
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod bedrock_block_pos;
|
||||
pub mod bit_set;
|
||||
pub mod bitset;
|
||||
pub mod data_component;
|
||||
pub mod item_stack_seralizer;
|
||||
pub mod little_endian;
|
||||
|
||||
@@ -20,7 +20,7 @@ pub type VarULongType = u64;
|
||||
/**
|
||||
* A variable-length long type used by the Minecraft network protocol.
|
||||
*/
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct VarULong(pub VarULongType);
|
||||
|
||||
impl VarULong {
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::{
|
||||
};
|
||||
|
||||
use pumpkin_util::math::{vector2::Vector2, vector3::Vector3};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{codec::var_uint::VarUInt, serial::PacketRead};
|
||||
|
||||
@@ -175,23 +176,45 @@ impl PacketRead for SocketAddr {
|
||||
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
|
||||
match u8::read(reader)? {
|
||||
4 => {
|
||||
let mut ip = [0; 4];
|
||||
reader.read_exact(&mut ip)?;
|
||||
let ip = u32::read_be(reader)?;
|
||||
let port = u16::read_be(reader)?;
|
||||
Ok(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(ip), port)))
|
||||
}
|
||||
6 => {
|
||||
// Addr family
|
||||
u16::read(reader)?;
|
||||
let port = u16::read_be(reader)?;
|
||||
let flowinfo = u32::read_be(reader)?;
|
||||
|
||||
let mut ip = [0; 16];
|
||||
reader.read_exact(&mut ip)?;
|
||||
let port = u16::read_be(reader)?;
|
||||
let ip = Ipv6Addr::from(ip);
|
||||
|
||||
let scope_id = u32::read_be(reader)?;
|
||||
|
||||
Ok(SocketAddr::V6(SocketAddrV6::new(
|
||||
Ipv6Addr::from(ip),
|
||||
port,
|
||||
0, // flowinfo
|
||||
0, // scope_id
|
||||
ip, port, flowinfo, scope_id,
|
||||
)))
|
||||
}
|
||||
_ => Err(Error::other("Invalid socket address version")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PacketRead for Uuid {
|
||||
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
|
||||
let mut bytes = [0; 16];
|
||||
reader.read_exact(&mut bytes)?;
|
||||
Ok(Uuid::from_bytes(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PacketRead> PacketRead for Option<T> {
|
||||
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
|
||||
Ok(if bool::read(reader)? {
|
||||
Some(T::read(reader)?)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@ use std::{
|
||||
net::SocketAddr,
|
||||
};
|
||||
|
||||
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
||||
use pumpkin_util::{
|
||||
GameMode,
|
||||
math::{position::BlockPos, vector3::Vector3},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
codec::{var_int::VarInt, var_uint::VarUInt},
|
||||
@@ -152,16 +155,34 @@ impl<T: PacketWrite> PacketWrite for Option<T> {
|
||||
impl PacketWrite for SocketAddr {
|
||||
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
|
||||
match self {
|
||||
// version, addr, port
|
||||
SocketAddr::V4(addr) => {
|
||||
writer.write_all(&[4])?;
|
||||
4u8.write(writer)?;
|
||||
writer.write_all(&addr.ip().octets())?;
|
||||
addr.port().write_be(writer)
|
||||
}
|
||||
// version, addr_family, port, flow_info, addr, scope_id
|
||||
SocketAddr::V6(addr) => {
|
||||
writer.write_all(&[6])?;
|
||||
6u8.write(writer)?;
|
||||
10u16.write(writer)?;
|
||||
addr.port().write_be(writer)?;
|
||||
addr.flowinfo().write_be(writer)?;
|
||||
writer.write_all(&addr.ip().octets())?;
|
||||
addr.scope_id().write_be(writer)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.write_all(&self.port().to_be_bytes())
|
||||
impl PacketWrite for GameMode {
|
||||
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
|
||||
VarInt(match self {
|
||||
Self::Survival => 0,
|
||||
Self::Creative => 1,
|
||||
Self::Adventure => 2,
|
||||
// I have no idea why
|
||||
Self::Spectator => 6,
|
||||
})
|
||||
.write(writer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,9 +119,9 @@ pub fn lerp_progress<T: Float>(value: T, start: T, end: T) -> T {
|
||||
}
|
||||
|
||||
pub fn clamped_lerp(start: f64, end: f64, delta: f64) -> f64 {
|
||||
if delta < 0f64 {
|
||||
if delta < 0.0 {
|
||||
start
|
||||
} else if delta > 1f64 {
|
||||
} else if delta > 1.0 {
|
||||
end
|
||||
} else {
|
||||
lerp(delta, start, end)
|
||||
|
||||
@@ -228,19 +228,18 @@ impl BlockPos {
|
||||
pub fn chunk_and_chunk_relative_position(&self) -> (Vector2<i32>, Vector3<i32>) {
|
||||
let (z_chunk, z_rem) = self.0.z.div_rem_euclid(&16);
|
||||
let (x_chunk, x_rem) = self.0.x.div_rem_euclid(&16);
|
||||
let chunk_coordinate = Vector2 {
|
||||
x: x_chunk,
|
||||
y: z_chunk,
|
||||
};
|
||||
|
||||
// Since we divide by 16, remnant can never exceed u8
|
||||
let relative = Vector3 {
|
||||
x: x_rem,
|
||||
z: z_rem,
|
||||
|
||||
y: self.0.y,
|
||||
};
|
||||
(chunk_coordinate, relative)
|
||||
(
|
||||
Vector2 {
|
||||
x: x_chunk,
|
||||
y: z_chunk,
|
||||
},
|
||||
// Since we divide by 16, remnant can never exceed u8
|
||||
Vector3 {
|
||||
x: x_rem,
|
||||
z: z_rem,
|
||||
y: self.0.y,
|
||||
},
|
||||
)
|
||||
}
|
||||
pub fn section_relative_position(&self) -> Vector3<i32> {
|
||||
let (_z_chunk, z_rem) = self.0.z.div_rem_euclid(&16);
|
||||
|
||||
@@ -464,6 +464,8 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
let section_count = chunks[0].1.read().await.section.sections.len();
|
||||
|
||||
for i in 0..5 {
|
||||
println!("Iteration {}", i + 1);
|
||||
// Mark the chunks as dirty so we save them again
|
||||
@@ -509,6 +511,10 @@ mod tests {
|
||||
let chunk = chunk.read().await;
|
||||
for read_chunk in read_chunks.iter() {
|
||||
let read_chunk = read_chunk.read().await;
|
||||
|
||||
// Before this commit the chunks got an extra section after saving and reading
|
||||
// so we prevent that from happening in the future :)
|
||||
assert_eq!(read_chunk.section.sections.len(), section_count);
|
||||
if read_chunk.position == chunk.position {
|
||||
let original = chunk.section.dump_blocks();
|
||||
let read = read_chunk.section.dump_blocks();
|
||||
|
||||
@@ -134,37 +134,31 @@ impl ChunkData {
|
||||
}
|
||||
|
||||
let light_engine = ChunkLight {
|
||||
block_light: (0..chunk_data.sections.len() + 2)
|
||||
.map(|index| {
|
||||
chunk_data
|
||||
.sections
|
||||
.iter()
|
||||
.find(|section| {
|
||||
section.y as i32 == index as i32 + chunk_data.min_y_section - 1
|
||||
})
|
||||
.and_then(|section| section.block_light.clone())
|
||||
block_light: chunk_data
|
||||
.sections
|
||||
.iter()
|
||||
.map(|x| {
|
||||
x.block_light
|
||||
.clone()
|
||||
.map(LightContainer::new)
|
||||
.unwrap_or_default()
|
||||
})
|
||||
.collect(),
|
||||
sky_light: (0..chunk_data.sections.len() + 2)
|
||||
.map(|index| {
|
||||
chunk_data
|
||||
.sections
|
||||
.iter()
|
||||
.find(|section| {
|
||||
section.y as i32 == index as i32 + chunk_data.min_y_section - 1
|
||||
})
|
||||
.and_then(|section| section.sky_light.clone())
|
||||
sky_light: chunk_data
|
||||
.sections
|
||||
.iter()
|
||||
.map(|x| {
|
||||
x.sky_light
|
||||
.clone()
|
||||
.map(LightContainer::new)
|
||||
.unwrap_or_default()
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let sub_chunks = chunk_data
|
||||
.sections
|
||||
.into_iter()
|
||||
.filter(|section| section.y >= chunk_data.min_y_section as i8)
|
||||
.map(|section| SubChunk {
|
||||
block_states: section
|
||||
.block_states
|
||||
@@ -202,30 +196,23 @@ impl ChunkData {
|
||||
}
|
||||
|
||||
async fn internal_to_bytes(&self) -> Result<Bytes, ChunkSerializingError> {
|
||||
let sections: Vec<_> = (0..self.section.sections.len() + 2)
|
||||
.map(|i| {
|
||||
let has_blocks = i >= 1 && i - 1 < self.section.sections.len();
|
||||
let section = has_blocks.then(|| &self.section.sections[i - 1]);
|
||||
|
||||
ChunkSectionNBT {
|
||||
y: (i as i8) - 1i8 + section_coords::block_to_section(self.section.min_y) as i8,
|
||||
block_states: section.map(|section| section.block_states.to_disk_nbt()),
|
||||
biomes: section.map(|section| section.biomes.to_disk_nbt()),
|
||||
block_light: match self.light_engine.block_light[i].clone() {
|
||||
LightContainer::Empty(_) => None,
|
||||
LightContainer::Full(data) => Some(data),
|
||||
},
|
||||
sky_light: match self.light_engine.sky_light[i].clone() {
|
||||
LightContainer::Empty(_) => None,
|
||||
LightContainer::Full(data) => Some(data),
|
||||
},
|
||||
}
|
||||
})
|
||||
.filter(|nbt| {
|
||||
nbt.block_states.is_some()
|
||||
|| nbt.biomes.is_some()
|
||||
|| nbt.block_light.is_some()
|
||||
|| nbt.sky_light.is_some()
|
||||
let sections: Vec<_> = self
|
||||
.section
|
||||
.sections
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, section)| ChunkSectionNBT {
|
||||
y: (i as i8) + section_coords::block_to_section(self.section.min_y) as i8,
|
||||
block_states: Some(section.block_states.to_disk_nbt()),
|
||||
biomes: Some(section.biomes.to_disk_nbt()),
|
||||
block_light: match self.light_engine.block_light[i].clone() {
|
||||
LightContainer::Empty(_) => None,
|
||||
LightContainer::Full(data) => Some(data),
|
||||
},
|
||||
sky_light: match self.light_engine.sky_light[i].clone() {
|
||||
LightContainer::Empty(_) => None,
|
||||
LightContainer::Full(data) => Some(data),
|
||||
},
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
@@ -547,15 +547,12 @@ impl ChunkData {
|
||||
}
|
||||
|
||||
pub fn get_highest_non_empty_subchunk(&self) -> usize {
|
||||
let idx = self
|
||||
.section
|
||||
.sections
|
||||
.iter()
|
||||
.rev()
|
||||
.position(|sub_chunk| sub_chunk.block_states.non_air_block_count() != 0)
|
||||
.map(|idx| (self.section.sections.len() - idx).saturating_sub(1));
|
||||
|
||||
idx.unwrap_or_default()
|
||||
for (i, sub_chunk) in self.section.sections.iter().enumerate().rev() {
|
||||
if sub_chunk.block_states.non_air_block_count() != 0 {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -427,18 +427,25 @@ impl BlockPalette {
|
||||
let mut current_word: u32 = 0;
|
||||
let mut current_index_in_word = 0;
|
||||
|
||||
for key in data.cube.as_flattened().as_flattened().iter() {
|
||||
let key_index = key_to_index_map.get(key).unwrap();
|
||||
debug_assert!((1 << bits_per_entry) > *key_index);
|
||||
for x in 0..16 {
|
||||
for y in 0..16 {
|
||||
for z in 0..16 {
|
||||
// Java has it in y, z, x order, so we need to convert it back to x, y, z
|
||||
// Please test your code on bedrock before merging
|
||||
let key = data.get(x, z, y);
|
||||
let key_index = key_to_index_map.get(&key).unwrap();
|
||||
debug_assert!((1 << bits_per_entry) > *key_index);
|
||||
|
||||
current_word |=
|
||||
(*key_index as u32) << (bits_per_entry as u32 * current_index_in_word);
|
||||
current_index_in_word += 1;
|
||||
current_word |= (*key_index as u32)
|
||||
<< (bits_per_entry as u32 * current_index_in_word);
|
||||
current_index_in_word += 1;
|
||||
|
||||
if current_index_in_word == blocks_per_word as u32 {
|
||||
packed_data.push(current_word);
|
||||
current_word = 0;
|
||||
current_index_in_word = 0;
|
||||
if current_index_in_word == blocks_per_word as u32 {
|
||||
packed_data.push(current_word);
|
||||
current_word = 0;
|
||||
current_index_in_word = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,20 +19,20 @@ impl Cylindrical {
|
||||
pub fn for_each_changed_chunk(
|
||||
old_cylindrical: Cylindrical,
|
||||
new_cylindrical: Cylindrical,
|
||||
mut newly_included: impl FnMut(Vector2<i32>),
|
||||
mut just_removed: impl FnMut(Vector2<i32>),
|
||||
newly_included: &mut Vec<Vector2<i32>>,
|
||||
just_removed: &mut Vec<Vector2<i32>>,
|
||||
) {
|
||||
for new_cylindrical_chunk in new_cylindrical.all_chunks_within() {
|
||||
if !old_cylindrical.is_within_distance(new_cylindrical_chunk.x, new_cylindrical_chunk.y)
|
||||
{
|
||||
newly_included(new_cylindrical_chunk);
|
||||
newly_included.push(new_cylindrical_chunk);
|
||||
}
|
||||
}
|
||||
|
||||
for old_cylindrical_chunk in old_cylindrical.all_chunks_within() {
|
||||
if !new_cylindrical.is_within_distance(old_cylindrical_chunk.x, old_cylindrical_chunk.y)
|
||||
{
|
||||
just_removed(old_cylindrical_chunk);
|
||||
just_removed.push(old_cylindrical_chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,9 @@ impl Cylindrical {
|
||||
}
|
||||
|
||||
pub fn is_within_distance(&self, x: i32, z: i32) -> bool {
|
||||
if self.view_distance.get() == 1 {
|
||||
return false;
|
||||
}
|
||||
let rel_x = ((x - self.center.x).abs() as i64 - 2).max(0);
|
||||
let rel_z = ((z - self.center.y).abs() as i64 - 2).max(0);
|
||||
|
||||
@@ -64,6 +67,9 @@ impl Cylindrical {
|
||||
|
||||
/// Returns an iterator of all chunks within this cylinder
|
||||
pub fn all_chunks_within(&self) -> Vec<Vector2<i32>> {
|
||||
if self.view_distance.get() == 1 {
|
||||
return Vec::new();
|
||||
}
|
||||
// I came up with this values by testing
|
||||
// for view distances 2-32 it usually gives 5 - 20 chunks more than needed if the player is on ground
|
||||
// this looks scary but this few calculations are definitely faster than ~5 reallocations
|
||||
|
||||
@@ -13,6 +13,7 @@ use super::{
|
||||
};
|
||||
use crate::chunk::format::LightContainer;
|
||||
use crate::generation::proto_chunk::TerrainCache;
|
||||
use crate::generation::section_coords;
|
||||
use crate::level::Level;
|
||||
use crate::world::BlockRegistryExt;
|
||||
use crate::{chunk::ChunkLight, dimension::Dimension};
|
||||
@@ -83,11 +84,7 @@ impl WorldGenerator for VanillaGenerator {
|
||||
) -> ChunkData {
|
||||
let generation_settings = gen_settings_from_dimension(&self.dimension);
|
||||
|
||||
let height: usize = match self.dimension {
|
||||
Dimension::Overworld => 384,
|
||||
Dimension::Nether | Dimension::End => 256,
|
||||
};
|
||||
let sub_chunks = height / BlockPalette::SIZE;
|
||||
let sub_chunks = generation_settings.shape.height as usize / BlockPalette::SIZE;
|
||||
let sections = (0..sub_chunks).map(|_| SubChunk::default()).collect();
|
||||
let mut sections = ChunkSections::new(sections, generation_settings.shape.min_y as i32);
|
||||
|
||||
@@ -122,26 +119,25 @@ impl WorldGenerator for VanillaGenerator {
|
||||
}
|
||||
}
|
||||
for y in 0..generation_settings.shape.height {
|
||||
let relative_y = (y as i32 - sections.min_y) as usize;
|
||||
let section_index = relative_y / BlockPalette::SIZE;
|
||||
let relative_y = y as usize;
|
||||
let section_index = section_coords::block_to_section(relative_y);
|
||||
let relative_y = relative_y % BlockPalette::SIZE;
|
||||
if let Some(section) = sections.sections.get_mut(section_index) {
|
||||
for z in 0..BlockPalette::SIZE {
|
||||
for x in 0..BlockPalette::SIZE {
|
||||
let absolute_y = generation_settings.shape.min_y as i32 + y as i32;
|
||||
let block = proto_chunk
|
||||
.get_block_state(&Vector3::new(x as i32, absolute_y, z as i32));
|
||||
section.block_states.set(x, relative_y, z, block.0);
|
||||
.get_block_state_raw(&Vector3::new(x as i32, y as i32, z as i32));
|
||||
section.block_states.set(x, relative_y, z, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ChunkData {
|
||||
light_engine: ChunkLight {
|
||||
sky_light: (0..sections.sections.len() + 2)
|
||||
sky_light: (0..sections.sections.len())
|
||||
.map(|_| LightContainer::new_filled(15))
|
||||
.collect(),
|
||||
block_light: (0..sections.sections.len() + 2)
|
||||
block_light: (0..sections.sections.len())
|
||||
.map(|_| LightContainer::new_empty(15))
|
||||
.collect(),
|
||||
},
|
||||
|
||||
@@ -378,6 +378,12 @@ impl<'a> ProtoChunk<'a> {
|
||||
state.is_air()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_block_state_raw(&self, local_pos: &Vector3<i32>) -> u16 {
|
||||
let index = self.local_pos_to_block_index(local_pos);
|
||||
self.flat_block_map[index]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_block_state(&self, local_pos: &Vector3<i32>) -> RawBlockState {
|
||||
let local_pos = Vector3::new(
|
||||
@@ -388,8 +394,7 @@ impl<'a> ProtoChunk<'a> {
|
||||
if local_pos.y < 0 || local_pos.y >= self.height() as i32 {
|
||||
return RawBlockState(Block::VOID_AIR.default_state.id);
|
||||
}
|
||||
let index = self.local_pos_to_block_index(&local_pos);
|
||||
RawBlockState(self.flat_block_map[index])
|
||||
RawBlockState(self.get_block_state_raw(&local_pos))
|
||||
}
|
||||
|
||||
pub fn set_block_state(&mut self, pos: &Vector3<i32>, block_state: &BlockState) {
|
||||
|
||||
@@ -23,7 +23,7 @@ pub type BlockId = u16;
|
||||
pub type BlockStateId = u16;
|
||||
|
||||
pub const CURRENT_MC_VERSION: &str = "1.21.8";
|
||||
pub const CURRENT_BEDROCK_MC_VERSION: &str = "1.21.94";
|
||||
pub const CURRENT_BEDROCK_MC_VERSION: &str = "1.21.100";
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! global_path {
|
||||
|
||||
@@ -29,6 +29,7 @@ use crate::item::items::dye::DyeItem;
|
||||
use crate::item::items::glowing_ink_sac::GlowingInkSacItem;
|
||||
use crate::item::items::honeycomb::HoneyCombItem;
|
||||
use crate::item::items::ink_sac::InkSacItem;
|
||||
use crate::net::ClientPlatform;
|
||||
use crate::world::World;
|
||||
|
||||
type SignProperties = pumpkin_data::block_properties::OakSignLikeProperties;
|
||||
@@ -55,7 +56,7 @@ impl BlockBehaviour for SignBlock {
|
||||
}
|
||||
|
||||
async fn player_placed(&self, args: PlayerPlacedArgs<'_>) {
|
||||
match args.player.client.as_ref() {
|
||||
match &args.player.client {
|
||||
crate::net::ClientPlatform::Java(java) => {
|
||||
java.send_sign_packet(*args.position, true).await;
|
||||
}
|
||||
@@ -100,12 +101,12 @@ impl BlockBehaviour for SignBlock {
|
||||
|
||||
let is_facing_front_text =
|
||||
is_facing_front_text(args.world, args.position, args.block, args.player).await;
|
||||
match args.player.client.as_ref() {
|
||||
crate::net::ClientPlatform::Java(java) => {
|
||||
match &args.player.client {
|
||||
ClientPlatform::Java(java) => {
|
||||
java.send_sign_packet(*args.position, is_facing_front_text)
|
||||
.await;
|
||||
}
|
||||
crate::net::ClientPlatform::Bedrock(_bedrock) => todo!(),
|
||||
ClientPlatform::Bedrock(_bedrock) => todo!(),
|
||||
}
|
||||
|
||||
BlockActionResult::SuccessServer
|
||||
|
||||
@@ -1412,7 +1412,7 @@ impl Entity {
|
||||
}
|
||||
|
||||
pub async fn set_sneaking(&self, sneaking: bool) {
|
||||
assert!(self.sneaking.load(Relaxed) != sneaking);
|
||||
//assert!(self.sneaking.load(Relaxed) != sneaking);
|
||||
self.sneaking.store(sneaking, Relaxed);
|
||||
self.set_flag(Flag::Sneaking, sneaking).await;
|
||||
if sneaking {
|
||||
@@ -1554,7 +1554,7 @@ impl Entity {
|
||||
}
|
||||
|
||||
pub async fn set_sprinting(&self, sprinting: bool) {
|
||||
assert!(self.sprinting.load(Relaxed) != sprinting);
|
||||
//assert!(self.sprinting.load(Relaxed) != sprinting);
|
||||
self.sprinting.store(sprinting, Relaxed);
|
||||
self.set_flag(Flag::Sprinting, sprinting).await;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use core::f32;
|
||||
use std::collections::VecDeque;
|
||||
use std::f64::consts::TAU;
|
||||
use std::num::NonZeroU8;
|
||||
@@ -12,6 +11,10 @@ use crossbeam::atomic::AtomicCell;
|
||||
use log::warn;
|
||||
use pumpkin_protocol::bedrock::client::level_chunk::CLevelChunk;
|
||||
use pumpkin_protocol::bedrock::client::set_time::CSetTime;
|
||||
use pumpkin_protocol::bedrock::client::update_abilities::{
|
||||
Ability, AbilityLayer, CUpdateAbilities,
|
||||
};
|
||||
use pumpkin_protocol::bedrock::server::text::SText;
|
||||
use pumpkin_world::chunk::{ChunkData, ChunkEntityData};
|
||||
use pumpkin_world::inventory::Inventory;
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
@@ -200,7 +203,7 @@ pub struct Player {
|
||||
/// The player's game profile information, including their username and UUID.
|
||||
pub gameprofile: GameProfile,
|
||||
/// The client connection associated with the player.
|
||||
pub client: Arc<ClientPlatform>,
|
||||
pub client: ClientPlatform,
|
||||
/// The player's inventory.
|
||||
pub inventory: Arc<PlayerInventory>,
|
||||
/// The player's configuration settings. Changes when the player changes their settings.
|
||||
@@ -278,7 +281,7 @@ pub struct Player {
|
||||
|
||||
impl Player {
|
||||
pub async fn new(
|
||||
client: Arc<ClientPlatform>,
|
||||
client: ClientPlatform,
|
||||
gameprofile: GameProfile,
|
||||
config: PlayerConfig,
|
||||
world: Arc<World>,
|
||||
@@ -303,7 +306,7 @@ impl Player {
|
||||
let living_entity = LivingEntity::new(Entity::new(
|
||||
player_uuid,
|
||||
world,
|
||||
Vector3::new(0.0, 0.0, 0.0),
|
||||
Vector3::new(0.0, 100.0, 0.0),
|
||||
&EntityType::PLAYER,
|
||||
matches!(gamemode, GameMode::Creative | GameMode::Spectator),
|
||||
));
|
||||
@@ -341,11 +344,12 @@ impl Player {
|
||||
// TODO: Send the CPlayerSpawnPosition packet when the client connects with proper values
|
||||
respawn_point: AtomicCell::new(None),
|
||||
sleeping_since: AtomicCell::new(None),
|
||||
// We want this to be an impossible watched section so that `player_chunker::update_position`
|
||||
// We want this to be an impossible watched section so that `chunker::update_position`
|
||||
// will mark chunks as watched for a new join rather than a respawn.
|
||||
// (We left shift by one so we can search around that chunk)
|
||||
watched_section: AtomicCell::new(Cylindrical::new(
|
||||
Vector2::new(i32::MAX >> 1, i32::MAX >> 1),
|
||||
Vector2::new(0, 0),
|
||||
// Since 1 is not possible in vanilla it is used as uninit
|
||||
NonZeroU8::new(1).unwrap(),
|
||||
)),
|
||||
wait_for_keep_alive: AtomicBool::new(false),
|
||||
@@ -758,7 +762,7 @@ impl Player {
|
||||
|
||||
let chunk_of_chunks = {
|
||||
let mut chunk_manager = self.chunk_manager.lock().await;
|
||||
if let ClientPlatform::Java(_) = self.client.as_ref() {
|
||||
if let ClientPlatform::Java(_) = self.client {
|
||||
// Java clients can only send a limited amount of chunks per tick.
|
||||
// If we have sent too many chunks without receiving an ack, we stop sending chunks.
|
||||
chunk_manager
|
||||
@@ -771,7 +775,7 @@ impl Player {
|
||||
|
||||
if let Some(chunk_of_chunks) = chunk_of_chunks {
|
||||
let chunk_count = chunk_of_chunks.len();
|
||||
match self.client.as_ref() {
|
||||
match &self.client {
|
||||
ClientPlatform::Java(java_client) => {
|
||||
java_client.send_packet_now(&CChunkBatchStart).await;
|
||||
for chunk in chunk_of_chunks {
|
||||
@@ -845,7 +849,7 @@ impl Player {
|
||||
// TODO This should only be handled by the ClientPlatform
|
||||
let now = Instant::now();
|
||||
if now.duration_since(self.last_keep_alive_time.load()) >= Duration::from_secs(15) {
|
||||
if matches!(self.client.as_ref(), ClientPlatform::Bedrock(_)) {
|
||||
if matches!(self.client, ClientPlatform::Bedrock(_)) {
|
||||
return;
|
||||
}
|
||||
// We never got a response from the last keep alive we sent.
|
||||
@@ -962,28 +966,77 @@ impl Player {
|
||||
|
||||
/// Updates the current abilities the player has.
|
||||
pub async fn send_abilities_update(&self) {
|
||||
let mut b = 0i8;
|
||||
let abilities = &self.abilities.lock().await;
|
||||
match &self.client {
|
||||
ClientPlatform::Java(java) => {
|
||||
let mut b = 0;
|
||||
let abilities = &self.abilities.lock().await;
|
||||
|
||||
if abilities.invulnerable {
|
||||
b |= 1;
|
||||
if abilities.invulnerable {
|
||||
b |= 1;
|
||||
}
|
||||
if abilities.flying {
|
||||
b |= 2;
|
||||
}
|
||||
if abilities.allow_flying {
|
||||
b |= 4;
|
||||
}
|
||||
if abilities.creative {
|
||||
b |= 8;
|
||||
}
|
||||
java.enqueue_packet(&CPlayerAbilities::new(
|
||||
b,
|
||||
abilities.fly_speed,
|
||||
abilities.walk_speed,
|
||||
))
|
||||
.await;
|
||||
}
|
||||
ClientPlatform::Bedrock(bedrock) => {
|
||||
let mut ability_value = 0;
|
||||
let abilities = &self.abilities.lock().await;
|
||||
|
||||
if abilities.invulnerable {
|
||||
ability_value |= 1 << Ability::Invulnerable as u32;
|
||||
}
|
||||
|
||||
if abilities.flying {
|
||||
ability_value |= 1 << Ability::Flying as u32;
|
||||
}
|
||||
|
||||
if abilities.allow_flying {
|
||||
ability_value |= 1 << Ability::MayFly as u32;
|
||||
}
|
||||
|
||||
if abilities.creative {
|
||||
ability_value |= 1 << Ability::OperatorCommands as u32;
|
||||
ability_value |= 1 << Ability::Teleport as u32;
|
||||
ability_value |= 1 << Ability::Invulnerable as u32;
|
||||
}
|
||||
|
||||
// Todo: Integrate this into the system
|
||||
ability_value |= 1 << Ability::AttackMobs as u32;
|
||||
ability_value |= 1 << Ability::AttackPlayers as u32;
|
||||
ability_value |= 1 << Ability::Build as u32;
|
||||
ability_value |= 1 << Ability::DoorsAndSwitches as u32;
|
||||
ability_value |= 1 << Ability::Instabuild as u32;
|
||||
ability_value |= 1 << Ability::Mine as u32;
|
||||
|
||||
let packet = CUpdateAbilities {
|
||||
target_player_raw_id: self.entity_id().into(),
|
||||
player_permission: 2,
|
||||
command_permission: 4,
|
||||
layers: vec![AbilityLayer {
|
||||
serialized_layer: 1,
|
||||
abilities_set: (1 << Ability::AbilityCount as u32) - 1,
|
||||
ability_value,
|
||||
fly_speed: 0.05,
|
||||
vertical_fly_speed: 1.0,
|
||||
walk_speed: 0.1,
|
||||
}],
|
||||
};
|
||||
|
||||
bedrock.send_game_packet(&packet).await;
|
||||
}
|
||||
}
|
||||
if abilities.flying {
|
||||
b |= 2;
|
||||
}
|
||||
if abilities.allow_flying {
|
||||
b |= 4;
|
||||
}
|
||||
if abilities.creative {
|
||||
b |= 8;
|
||||
}
|
||||
self.client
|
||||
.enqueue_packet(&CPlayerAbilities::new(
|
||||
b,
|
||||
abilities.fly_speed,
|
||||
abilities.walk_speed,
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Updates the client of the player's current permission level.
|
||||
@@ -1026,7 +1079,7 @@ impl Player {
|
||||
/// Sends the world time to only this player.
|
||||
pub async fn send_time(&self, world: &World) {
|
||||
let l_world = world.level_time.lock().await;
|
||||
match self.client.as_ref() {
|
||||
match &self.client {
|
||||
ClientPlatform::Java(java_client) => {
|
||||
java_client
|
||||
.enqueue_packet(&CUpdateTime::new(
|
||||
@@ -1058,7 +1111,7 @@ impl Player {
|
||||
}
|
||||
|
||||
self.watched_section.store(Cylindrical::new(
|
||||
Vector2::new(i32::MAX >> 1, i32::MAX >> 1),
|
||||
Vector2::new(0, 0),
|
||||
NonZeroU8::new(1).unwrap(),
|
||||
));
|
||||
}
|
||||
@@ -1489,13 +1542,33 @@ impl Player {
|
||||
}
|
||||
|
||||
pub async fn send_system_message(&self, text: &TextComponent) {
|
||||
self.send_system_message_raw(text, false).await;
|
||||
match &self.client {
|
||||
ClientPlatform::Java(client) => {
|
||||
client
|
||||
.enqueue_packet(&CSystemChatMessage::new(text, false))
|
||||
.await;
|
||||
}
|
||||
ClientPlatform::Bedrock(client) => {
|
||||
client
|
||||
.send_game_packet(&SText::system_message(text.clone().get_text()))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_system_message_raw(&self, text: &TextComponent, overlay: bool) {
|
||||
self.client
|
||||
.enqueue_packet(&CSystemChatMessage::new(text, overlay))
|
||||
.await;
|
||||
match &self.client {
|
||||
ClientPlatform::Java(client) => {
|
||||
client
|
||||
.enqueue_packet(&CSystemChatMessage::new(text, overlay))
|
||||
.await;
|
||||
}
|
||||
ClientPlatform::Bedrock(client) => {
|
||||
client
|
||||
.send_game_packet(&SText::system_message(text.clone().get_text()))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn tick_experience(&self) {
|
||||
@@ -1873,9 +1946,11 @@ impl Player {
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn reset_state(&self) {
|
||||
self.living_entity.reset_state().await;
|
||||
impl PartialEq for Player {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.gameprofile.id == other.gameprofile.id
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ use pumpkin_protocol::{
|
||||
resource_pack_stack::CResourcePackStackPacket, resource_packs_info::CResourcePacksInfo,
|
||||
start_game::Experiments,
|
||||
},
|
||||
frame_set::FrameSet,
|
||||
server::{login::SLogin, request_network_settings::SRequestNetworkSettings},
|
||||
},
|
||||
codec::var_uint::VarUInt,
|
||||
@@ -78,30 +79,40 @@ impl BedrockClient {
|
||||
// String::from_utf8_unchecked(general_purpose::URL_SAFE_NO_PAD.decode(raw_token[1]).unwrap())
|
||||
//};
|
||||
|
||||
// TODO: Batch these
|
||||
self.send_game_packet(&CPlayStatus::LoginSuccess).await;
|
||||
self.send_game_packet(&CResourcePacksInfo::new(
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
uuid::Uuid::default(),
|
||||
String::new(),
|
||||
))
|
||||
let mut frame_set = FrameSet::default();
|
||||
|
||||
self.write_game_packet_to_set(&CPlayStatus::LoginSuccess, &mut frame_set)
|
||||
.await;
|
||||
self.write_game_packet_to_set(
|
||||
&CResourcePacksInfo::new(
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
uuid::Uuid::default(),
|
||||
String::new(),
|
||||
),
|
||||
&mut frame_set,
|
||||
)
|
||||
.await;
|
||||
self.send_game_packet(&CResourcePackStackPacket::new(
|
||||
false,
|
||||
VarUInt(0),
|
||||
VarUInt(0),
|
||||
CURRENT_BEDROCK_MC_VERSION.to_string(),
|
||||
Experiments {
|
||||
names_size: 0,
|
||||
experiments_ever_toggled: false,
|
||||
},
|
||||
false,
|
||||
))
|
||||
self.write_game_packet_to_set(
|
||||
&CResourcePackStackPacket::new(
|
||||
false,
|
||||
VarUInt(0),
|
||||
VarUInt(0),
|
||||
CURRENT_BEDROCK_MC_VERSION.to_string(),
|
||||
Experiments {
|
||||
names_size: 0,
|
||||
experiments_ever_toggled: false,
|
||||
},
|
||||
false,
|
||||
),
|
||||
&mut frame_set,
|
||||
)
|
||||
.await;
|
||||
|
||||
self.send_frame_set(frame_set, 0x84).await;
|
||||
|
||||
if let Some((player, world)) = server
|
||||
.add_player(
|
||||
ClientPlatform::Bedrock(self.clone()),
|
||||
|
||||
@@ -13,7 +13,7 @@ use pumpkin_config::networking::compression::CompressionInfo;
|
||||
use pumpkin_protocol::{
|
||||
BClientPacket, PacketDecodeError, RawPacket,
|
||||
bedrock::{
|
||||
RAKNET_ACK, RAKNET_GAME_PACKET, RAKNET_NACK, RAKNET_VALID, RakReliability, SubClient,
|
||||
MTU, RAKNET_ACK, RAKNET_GAME_PACKET, RAKNET_NACK, RakReliability, SubClient,
|
||||
ack::Ack,
|
||||
client::disconnect_player::CDisconnectPlayer,
|
||||
frame_set::{Frame, FrameSet},
|
||||
@@ -21,8 +21,10 @@ use pumpkin_protocol::{
|
||||
packet_encoder::UDPNetworkEncoder,
|
||||
server::{
|
||||
client_cache_status::SClientCacheStatus,
|
||||
command_request::SCommandRequest,
|
||||
container_close::SContainerClose,
|
||||
interaction::SInteraction,
|
||||
loading_screen::SLoadingScreen,
|
||||
login::SLogin,
|
||||
player_auth_input::SPlayerAuthInput,
|
||||
raknet::{
|
||||
@@ -77,7 +79,7 @@ pub struct BedrockClient {
|
||||
/// The packet decoder for incoming packets.
|
||||
network_reader: Mutex<UDPNetworkDecoder>,
|
||||
|
||||
use_frame_sets: AtomicBool,
|
||||
_use_frame_sets: AtomicBool,
|
||||
output_sequence_number: AtomicU32,
|
||||
output_reliable_number: AtomicU32,
|
||||
output_split_number: AtomicU16,
|
||||
@@ -113,7 +115,7 @@ impl BedrockClient {
|
||||
tasks: TaskTracker::new(),
|
||||
outgoing_packet_queue_send: send,
|
||||
outgoing_packet_queue_recv: Some(recv),
|
||||
use_frame_sets: AtomicBool::new(false),
|
||||
_use_frame_sets: AtomicBool::new(false),
|
||||
output_sequence_number: AtomicU32::new(0),
|
||||
output_reliable_number: AtomicU32::new(0),
|
||||
output_split_number: AtomicU16::new(0),
|
||||
@@ -168,7 +170,7 @@ impl BedrockClient {
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn process_packet(self: &Arc<Self>, server: &Server, packet: Cursor<Vec<u8>>) {
|
||||
pub async fn process_packet(self: &Arc<Self>, server: &Arc<Server>, packet: Cursor<Vec<u8>>) {
|
||||
let packet = self.get_packet_payload(packet).await;
|
||||
if let Some(packet) = packet
|
||||
&& let Err(error) = self.handle_packet_payload(server, packet).await
|
||||
@@ -192,7 +194,6 @@ impl BedrockClient {
|
||||
.set_compression((compression.threshold as usize, compression.level));
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_async)]
|
||||
pub async fn kick(&self, reason: DisconnectReason, message: String) {
|
||||
self.send_game_packet(&CDisconnectPlayer::new(reason as i32, message))
|
||||
.await;
|
||||
@@ -244,31 +245,6 @@ impl BedrockClient {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn send_raknet_packet_now<P: BClientPacket>(&self, packet: &P) {
|
||||
let mut packet_buf = Vec::new();
|
||||
let writer = &mut packet_buf;
|
||||
Self::write_raw_packet(packet, writer).unwrap();
|
||||
|
||||
if !self.use_frame_sets.load(Ordering::Relaxed) {
|
||||
// Sent the packet directly
|
||||
if let Err(err) = self
|
||||
.network_writer
|
||||
.lock()
|
||||
.await
|
||||
.write_packet(&packet_buf, self.address, &self.socket)
|
||||
.await
|
||||
{
|
||||
// It is expected that the packet will fail if we are closed
|
||||
if !self.closed.load(Ordering::Relaxed) {
|
||||
log::warn!("Failed to send packet to client: {err}");
|
||||
// We now need to close the connection to the client since the stream is in an
|
||||
// unknown state
|
||||
self.close().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_offline_packet<P: BClientPacket>(
|
||||
packet: &P,
|
||||
addr: SocketAddr,
|
||||
@@ -290,6 +266,17 @@ impl BedrockClient {
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn write_game_packet_to_set<P: BClientPacket>(
|
||||
&self,
|
||||
packet: &P,
|
||||
frame_set: &mut FrameSet,
|
||||
) {
|
||||
let mut payload = Vec::new();
|
||||
self.write_game_packet(packet, &mut payload).await.unwrap();
|
||||
|
||||
frame_set.frames.push(Frame::new_unreliable(payload));
|
||||
}
|
||||
|
||||
pub async fn send_framed_packet<P: BClientPacket>(
|
||||
&self,
|
||||
packet: &P,
|
||||
@@ -309,10 +296,10 @@ impl BedrockClient {
|
||||
let mut split_id = 0;
|
||||
let mut order_index = 0;
|
||||
|
||||
let count = if packet_buf.len() > 1340 {
|
||||
let count = if packet_buf.len() > MTU {
|
||||
reliability = RakReliability::ReliableOrdered;
|
||||
split_id = self.output_split_number.fetch_add(1, Ordering::Relaxed);
|
||||
split_size = (packet_buf.len() as u32).div_ceil(1340);
|
||||
split_size = packet_buf.len().div_ceil(MTU) as u32;
|
||||
split_size as usize
|
||||
} else {
|
||||
1
|
||||
@@ -324,14 +311,14 @@ impl BedrockClient {
|
||||
|
||||
for i in 0..count {
|
||||
let end = if i + 1 == count {
|
||||
packet_buf.len() % 1340
|
||||
packet_buf.len() % MTU
|
||||
} else {
|
||||
1340
|
||||
MTU
|
||||
};
|
||||
let chunk = &packet_buf[i * 1340..i * 1340 + end];
|
||||
let chunk = &packet_buf[i * MTU..i * MTU + end];
|
||||
|
||||
let mut frame_set = FrameSet {
|
||||
sequence: u24(self.output_sequence_number.fetch_add(1, Ordering::Relaxed)),
|
||||
sequence: u24(0),
|
||||
frames: Vec::with_capacity(1),
|
||||
};
|
||||
|
||||
@@ -357,25 +344,30 @@ impl BedrockClient {
|
||||
|
||||
frame_set.frames.push(frame);
|
||||
|
||||
let mut frame_set_buf = Vec::new();
|
||||
frame_set
|
||||
.write_packet_data(&mut frame_set_buf, if i == 0 { 0x84 } else { 0x8c })
|
||||
.unwrap();
|
||||
let id = if i == 0 { 0x84 } else { 0x8c };
|
||||
self.send_frame_set(frame_set, id).await;
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(err) = self
|
||||
.network_writer
|
||||
.lock()
|
||||
.await
|
||||
.write_packet(&frame_set_buf, self.address, &self.socket)
|
||||
.await
|
||||
{
|
||||
// It is expected that the packet will fail if we are closed
|
||||
if !self.closed.load(Ordering::Relaxed) {
|
||||
log::warn!("Failed to send packet to client: {err}");
|
||||
// We now need to close the connection to the client since the stream is in an
|
||||
// unknown state
|
||||
self.close().await;
|
||||
}
|
||||
pub async fn send_frame_set(&self, mut frame_set: FrameSet, id: u8) {
|
||||
frame_set.sequence = u24(self.output_sequence_number.fetch_add(1, Ordering::Relaxed));
|
||||
let mut frame_set_buf = Vec::new();
|
||||
frame_set.write_packet_data(&mut frame_set_buf, id).unwrap();
|
||||
|
||||
// I dont know if thats the right place to make encryption & decoding
|
||||
if let Err(err) = self
|
||||
.network_writer
|
||||
.lock()
|
||||
.await
|
||||
.write_packet(&frame_set_buf, self.address, &self.socket)
|
||||
.await
|
||||
{
|
||||
// It is expected that the packet will fail if we are closed
|
||||
if !self.closed.load(Ordering::Relaxed) {
|
||||
log::warn!("Failed to send packet to client: {err}");
|
||||
// We now need to close the connection to the client since the stream is in an
|
||||
// unknown state
|
||||
self.closed.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -403,44 +395,29 @@ impl BedrockClient {
|
||||
.write_packet(&packet_buf, self.address, &self.socket)
|
||||
.await
|
||||
{
|
||||
// It is expected that the packet will fail if we are closed
|
||||
if !self.closed.load(Ordering::Relaxed) {
|
||||
log::warn!("Failed to send packet to client: {err}");
|
||||
// We now need to close the connection to the client since the stream is in an
|
||||
// unknown state
|
||||
self.close().await;
|
||||
}
|
||||
log::warn!("Failed to send packet to client: {err}");
|
||||
self.close().await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn handle_packet_payload(
|
||||
self: &Arc<Self>,
|
||||
server: &Server,
|
||||
server: &Arc<Server>,
|
||||
packet: Bytes,
|
||||
) -> Result<(), Error> {
|
||||
let payload = &mut Cursor::new(packet);
|
||||
let reader = &mut Cursor::new(packet);
|
||||
|
||||
let id = u8::read(payload)?;
|
||||
|
||||
let is_valid = id & RAKNET_VALID == RAKNET_VALID;
|
||||
if !is_valid {
|
||||
// Offline packets just have Packet ID + Payload
|
||||
return Err(Error::other("not valid online packet"));
|
||||
}
|
||||
self.use_frame_sets.store(true, Ordering::Relaxed);
|
||||
|
||||
match id {
|
||||
match u8::read(reader)? {
|
||||
RAKNET_ACK => {
|
||||
Self::handle_ack(&Ack::read(payload)?);
|
||||
Self::handle_ack(&Ack::read(reader)?);
|
||||
}
|
||||
RAKNET_NACK => {
|
||||
dbg!("received nack, client is missing packets");
|
||||
}
|
||||
0x80..0x8d => {
|
||||
self.handle_frame_set(server, FrameSet::read(payload)?)
|
||||
.await;
|
||||
self.handle_frame_set(server, FrameSet::read(reader)?).await;
|
||||
}
|
||||
_ => {
|
||||
id => {
|
||||
log::warn!("Bedrock: Received unknown packet header {id}");
|
||||
}
|
||||
}
|
||||
@@ -449,7 +426,7 @@ impl BedrockClient {
|
||||
|
||||
fn handle_ack(_ack: &Ack) {}
|
||||
|
||||
async fn handle_frame_set(self: &Arc<Self>, server: &Server, frame_set: FrameSet) {
|
||||
async fn handle_frame_set(self: &Arc<Self>, server: &Arc<Server>, frame_set: FrameSet) {
|
||||
// TODO: Send all ACKs in short intervals in batches
|
||||
self.send_ack(&Ack::new(vec![frame_set.sequence.0])).await;
|
||||
// TODO
|
||||
@@ -460,7 +437,7 @@ impl BedrockClient {
|
||||
|
||||
async fn handle_frame(
|
||||
self: &Arc<Self>,
|
||||
server: &Server,
|
||||
server: &Arc<Server>,
|
||||
mut frame: Frame,
|
||||
) -> Result<(), Error> {
|
||||
if frame.split_size > 0 {
|
||||
@@ -509,7 +486,7 @@ impl BedrockClient {
|
||||
|
||||
async fn handle_game_packet(
|
||||
self: &Arc<Self>,
|
||||
server: &Server,
|
||||
server: &Arc<Server>,
|
||||
packet: RawPacket,
|
||||
) -> Result<(), Error> {
|
||||
let payload = &mut Cursor::new(&packet.payload);
|
||||
@@ -536,33 +513,42 @@ impl BedrockClient {
|
||||
pub async fn handle_play_packet(
|
||||
&self,
|
||||
player: &Arc<Player>,
|
||||
_server: &Server,
|
||||
server: &Arc<Server>,
|
||||
packet: RawPacket,
|
||||
) {
|
||||
let payload = &mut &packet.payload[..];
|
||||
let reader = &mut &packet.payload[..];
|
||||
match packet.id {
|
||||
SPlayerAuthInput::PACKET_ID => {
|
||||
if let Ok(input_packet) = SPlayerAuthInput::read(payload) {
|
||||
if let Ok(input_packet) = SPlayerAuthInput::read(reader) {
|
||||
self.player_pos_update(player, input_packet).await;
|
||||
}
|
||||
}
|
||||
SLoadingScreen::PACKET_ID => {
|
||||
if SLoadingScreen::read(reader).unwrap().is_loading_done() {
|
||||
player.set_client_loaded(true);
|
||||
}
|
||||
}
|
||||
SRequestChunkRadius::PACKET_ID => {
|
||||
self.handle_request_chunk_radius(
|
||||
player,
|
||||
SRequestChunkRadius::read(payload).unwrap(),
|
||||
SRequestChunkRadius::read(reader).unwrap(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
SInteraction::PACKET_ID => {
|
||||
self.handle_interaction(player, SInteraction::read(payload).unwrap())
|
||||
self.handle_interaction(player, SInteraction::read(reader).unwrap())
|
||||
.await;
|
||||
}
|
||||
SContainerClose::PACKET_ID => {
|
||||
self.handle_container_close(player, SContainerClose::read(payload).unwrap())
|
||||
self.handle_container_close(player, SContainerClose::read(reader).unwrap())
|
||||
.await;
|
||||
}
|
||||
SText::PACKET_ID => {
|
||||
self.handle_chat_message(player, SText::read(payload).unwrap())
|
||||
self.handle_chat_message(player, SText::read(reader).unwrap())
|
||||
.await;
|
||||
}
|
||||
SCommandRequest::PACKET_ID => {
|
||||
self.handle_chat_command(player, server, SCommandRequest::read(reader).unwrap())
|
||||
.await;
|
||||
}
|
||||
_ => {
|
||||
@@ -573,7 +559,7 @@ impl BedrockClient {
|
||||
|
||||
async fn handle_raknet_packet(
|
||||
self: &Arc<Self>,
|
||||
server: &Server,
|
||||
server: &Arc<Server>,
|
||||
packet_id: i32,
|
||||
mut payload: Cursor<Vec<u8>>,
|
||||
) -> Result<(), Error> {
|
||||
@@ -667,9 +653,8 @@ impl BedrockClient {
|
||||
Err(err) => {
|
||||
if !matches!(err, PacketDecodeError::ConnectionClosed) {
|
||||
log::warn!("Failed to decode packet from client: {err}");
|
||||
let _text = format!("Error while reading incoming packet {err}");
|
||||
self.close().await;
|
||||
//self.kick(client).await;
|
||||
let text = format!("Error while reading incoming packet {err}");
|
||||
self.kick(DisconnectReason::BadPacket, text).await;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
use std::{num::NonZero, sync::Arc};
|
||||
use std::{
|
||||
num::{NonZero, NonZeroU32},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use pumpkin_config::{BASIC_CONFIG, advanced_config};
|
||||
use pumpkin_macros::send_cancellable;
|
||||
use pumpkin_protocol::{
|
||||
bedrock::{
|
||||
client::{chunk_radius_update::CChunkRadiusUpdate, container_open::CContainerOpen},
|
||||
client::{
|
||||
chunk_radius_update::CChunkRadiusUpdate, container_open::CContainerOpen,
|
||||
network_chunk_publisher_update::CNetworkChunkPublisherUpdate,
|
||||
set_actor_motion::CSetActorMotion,
|
||||
},
|
||||
server::{
|
||||
command_request::SCommandRequest,
|
||||
container_close::SContainerClose,
|
||||
interaction::{Action, SInteraction},
|
||||
player_auth_input::SPlayerAuthInput,
|
||||
player_auth_input::{InputData, SPlayerAuthInput},
|
||||
request_chunk_radius::SRequestChunkRadius,
|
||||
text::SText,
|
||||
},
|
||||
},
|
||||
codec::{bedrock_block_pos::NetworkPos, var_long::VarLong},
|
||||
codec::{bedrock_block_pos::NetworkPos, var_long::VarLong, var_ulong::VarULong},
|
||||
java::client::play::CSystemChatMessage,
|
||||
};
|
||||
use pumpkin_util::{
|
||||
@@ -22,8 +30,12 @@ use pumpkin_util::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
entity::player::Player, net::bedrock::BedrockClient,
|
||||
plugin::player::player_chat::PlayerChatEvent, server::seasonal_events, world::chunker,
|
||||
command::CommandSender,
|
||||
entity::{EntityBase, player::Player},
|
||||
net::{DisconnectReason, bedrock::BedrockClient},
|
||||
plugin::player::{player_chat::PlayerChatEvent, player_command_send::PlayerCommandSendEvent},
|
||||
server::{Server, seasonal_events},
|
||||
world::chunker,
|
||||
};
|
||||
|
||||
impl BedrockClient {
|
||||
@@ -32,32 +44,96 @@ impl BedrockClient {
|
||||
player: &Arc<Player>,
|
||||
packet: SRequestChunkRadius,
|
||||
) {
|
||||
dbg!(&packet);
|
||||
player.config.write().await.view_distance =
|
||||
NonZero::new(packet.chunk_radius.0 as u8).unwrap();
|
||||
self.send_game_packet(&CChunkRadiusUpdate {
|
||||
chunk_radius: packet.chunk_radius,
|
||||
})
|
||||
.await;
|
||||
let chunk_radius = packet.chunk_radius;
|
||||
if chunk_radius.0 < 1 {
|
||||
self.kick(
|
||||
DisconnectReason::Kicked,
|
||||
"Cannot have zero or negative view distance!".to_string(),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
self.send_game_packet(&CChunkRadiusUpdate { chunk_radius })
|
||||
.await;
|
||||
|
||||
let old_view_distance = {
|
||||
let mut config = player.config.write().await;
|
||||
let old_view_distance = config.view_distance;
|
||||
config.view_distance = NonZero::new(chunk_radius.0 as u8).unwrap();
|
||||
old_view_distance
|
||||
};
|
||||
|
||||
if old_view_distance.get() != chunk_radius.0 as u8 {
|
||||
log::debug!(
|
||||
"Player {} updated their render distance: {} -> {}.",
|
||||
player.gameprofile.name,
|
||||
old_view_distance,
|
||||
chunk_radius.0
|
||||
);
|
||||
self.send_game_packet(&CNetworkChunkPublisherUpdate::new(
|
||||
player.get_entity().block_pos.load(),
|
||||
chunk_radius.0 as u32,
|
||||
))
|
||||
.await;
|
||||
chunker::update_position(player).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn player_pos_update(&self, player: &Arc<Player>, packet: SPlayerAuthInput) {
|
||||
let pos = packet.position;
|
||||
player.living_entity.entity.set_pos(pos.to_f64());
|
||||
if !player.has_client_loaded() {
|
||||
return;
|
||||
}
|
||||
let config = player.config.read().await;
|
||||
let view_distance = config.view_distance;
|
||||
self.send_game_packet(&CNetworkChunkPublisherUpdate::new(
|
||||
BlockPos::new(
|
||||
packet.position.x.floor() as i32,
|
||||
packet.position.y.floor() as i32,
|
||||
packet.position.z.floor() as i32,
|
||||
),
|
||||
NonZeroU32::from(view_distance).into(),
|
||||
))
|
||||
.await;
|
||||
let new_pos = packet.position.to_f64();
|
||||
let old_pos = player.position();
|
||||
|
||||
chunker::update_position(player).await;
|
||||
//self.send_game_packet(&CMovePlayer {
|
||||
// player_runtime_id: VarULong(player.entity_id() as u64),
|
||||
// position: packet.position + Vector3::new(10.0, 0.0, 0.0),
|
||||
// pitch: packet.pitch,
|
||||
// yaw: packet.yaw,
|
||||
// y_head_rotation: packet.head_rotation,
|
||||
// position_mode: 1,
|
||||
// on_ground: false,
|
||||
// riding_runtime_id: VarULong(0),
|
||||
// tick: packet.client_tick,
|
||||
//})
|
||||
//.await;
|
||||
if new_pos != old_pos {
|
||||
player.living_entity.entity.set_pos(new_pos);
|
||||
chunker::update_position(player).await;
|
||||
}
|
||||
|
||||
let input_data = packet.input_data;
|
||||
let entity = player.get_entity();
|
||||
|
||||
if input_data.get(InputData::StartSprinting) {
|
||||
entity.set_sprinting(true).await;
|
||||
} else if input_data.get(InputData::StopSprinting) {
|
||||
entity.set_sprinting(false).await;
|
||||
}
|
||||
|
||||
if input_data.get(InputData::StartFlying) {
|
||||
player.abilities.lock().await.flying = true;
|
||||
player.send_abilities_update().await;
|
||||
} else if input_data.get(InputData::StopFlying) {
|
||||
player.abilities.lock().await.flying = false;
|
||||
player.send_abilities_update().await;
|
||||
}
|
||||
|
||||
if input_data.get(InputData::StartSneaking) {
|
||||
entity.set_sneaking(true).await;
|
||||
} else if input_data.get(InputData::StopSneaking) {
|
||||
entity.set_sneaking(false).await;
|
||||
}
|
||||
|
||||
if !player.abilities.lock().await.flying {
|
||||
self.send_game_packet(&CSetActorMotion {
|
||||
target_runtime_id: VarULong(entity.entity_id as _),
|
||||
motion: packet.pos_delta + Vector3::new(0.0, -0.08, 0.0),
|
||||
tick: packet.client_tick,
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn handle_interaction(&self, _player: &Arc<Player>, packet: SInteraction) {
|
||||
@@ -65,7 +141,7 @@ impl BedrockClient {
|
||||
self.send_game_packet(&CContainerOpen {
|
||||
container_id: 0,
|
||||
container_type: 0xff,
|
||||
position: NetworkPos(BlockPos(Vector3::new(0, 0, 0))),
|
||||
position: NetworkPos(packet.position.to_block_pos()),
|
||||
target_entity_id: VarLong(-1),
|
||||
})
|
||||
.await;
|
||||
@@ -124,4 +200,46 @@ impl BedrockClient {
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
pub async fn handle_chat_command(
|
||||
&self,
|
||||
player: &Arc<Player>,
|
||||
server: &Arc<Server>,
|
||||
command: SCommandRequest,
|
||||
) {
|
||||
let player_clone = player.clone();
|
||||
let server_clone: Arc<Server> = server.clone();
|
||||
send_cancellable! {{
|
||||
PlayerCommandSendEvent {
|
||||
player: player.clone(),
|
||||
command: command.command.clone(),
|
||||
cancelled: false
|
||||
};
|
||||
|
||||
'after: {
|
||||
let command = event.command;
|
||||
let command_clone = command.clone();
|
||||
// Some commands can take a long time to execute. If they do, they block packet processing for the player.
|
||||
// That's why we will spawn a task instead.
|
||||
server.spawn_task(async move {
|
||||
let dispatcher = server_clone.command_dispatcher.read().await;
|
||||
dispatcher
|
||||
.handle_command(
|
||||
&mut CommandSender::Player(player_clone),
|
||||
&server_clone,
|
||||
&command_clone,
|
||||
)
|
||||
.await;
|
||||
});
|
||||
|
||||
if advanced_config().commands.log_console {
|
||||
log::info!(
|
||||
"Player ({}): executed command /{}",
|
||||
player.gameprofile.name,
|
||||
command
|
||||
);
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ impl BedrockClient {
|
||||
edition: "MCPE",
|
||||
// TODO The default motd is to long to be displayed completely
|
||||
motd_line_1: "Pumpkin Server",
|
||||
protocol_version: 819,
|
||||
protocol_version: 827,
|
||||
version_name: CURRENT_BEDROCK_MC_VERSION,
|
||||
player_count,
|
||||
// A large number looks wreird on the client worlds window
|
||||
|
||||
@@ -322,7 +322,7 @@ impl Server {
|
||||
};
|
||||
|
||||
let mut player = Player::new(
|
||||
Arc::new(client),
|
||||
client,
|
||||
profile,
|
||||
config.clone().unwrap_or_default(),
|
||||
world.clone(),
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
use std::{
|
||||
num::{NonZeroU8, NonZeroU32},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{num::NonZeroU8, sync::Arc};
|
||||
|
||||
use pumpkin_config::BASIC_CONFIG;
|
||||
use pumpkin_protocol::{
|
||||
bedrock::client::network_chunk_publisher_update::CNetworkChunkPublisherUpdate,
|
||||
java::client::play::{CCenterChunk, CUnloadChunk},
|
||||
};
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_protocol::java::client::play::{CCenterChunk, CUnloadChunk};
|
||||
use pumpkin_world::cylindrical_chunk_iterator::Cylindrical;
|
||||
|
||||
use crate::{entity::player::Player, net::ClientPlatform};
|
||||
@@ -24,7 +17,6 @@ pub async fn get_view_distance(player: &Player) -> NonZeroU8 {
|
||||
|
||||
pub async fn update_position(player: &Arc<Player>) {
|
||||
let entity = &player.living_entity.entity;
|
||||
let pos = entity.pos.load();
|
||||
|
||||
let view_distance = get_view_distance(player).await;
|
||||
let new_chunk_center = entity.chunk_pos.load();
|
||||
@@ -33,35 +25,20 @@ pub async fn update_position(player: &Arc<Player>) {
|
||||
let new_cylindrical = Cylindrical::new(new_chunk_center, view_distance);
|
||||
|
||||
if old_cylindrical != new_cylindrical {
|
||||
match player.client.as_ref() {
|
||||
ClientPlatform::Java(client) => {
|
||||
client
|
||||
.send_packet_now(&CCenterChunk {
|
||||
chunk_x: new_chunk_center.x.into(),
|
||||
chunk_z: new_chunk_center.y.into(),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
ClientPlatform::Bedrock(client) => {
|
||||
client
|
||||
.send_game_packet(&CNetworkChunkPublisherUpdate::new(
|
||||
BlockPos::new(pos.x as i32, pos.y as i32, pos.z as i32),
|
||||
NonZeroU32::from(view_distance).get(),
|
||||
))
|
||||
.await;
|
||||
}
|
||||
if let ClientPlatform::Java(java) = &player.client {
|
||||
java.send_packet_now(&CCenterChunk {
|
||||
chunk_x: new_chunk_center.x.into(),
|
||||
chunk_z: new_chunk_center.y.into(),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
let mut loading_chunks = Vec::new();
|
||||
let mut unloading_chunks = Vec::new();
|
||||
Cylindrical::for_each_changed_chunk(
|
||||
old_cylindrical,
|
||||
new_cylindrical,
|
||||
|chunk_pos| {
|
||||
loading_chunks.push(chunk_pos);
|
||||
},
|
||||
|chunk_pos| {
|
||||
unloading_chunks.push(chunk_pos);
|
||||
},
|
||||
&mut loading_chunks,
|
||||
&mut unloading_chunks,
|
||||
);
|
||||
|
||||
// Make sure the watched section and the chunk watcher updates are async atomic. We want to
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::num::NonZeroU32;
|
||||
use std::sync::Weak;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
use std::time::Duration;
|
||||
@@ -52,19 +53,20 @@ use pumpkin_data::{BlockDirection, BlockState};
|
||||
use pumpkin_inventory::screen_handler::InventoryPlayer;
|
||||
use pumpkin_macros::send_cancellable;
|
||||
use pumpkin_nbt::{compound::NbtCompound, to_bytes_unnamed};
|
||||
use pumpkin_protocol::bedrock::client::chunk_radius_update::CChunkRadiusUpdate;
|
||||
use pumpkin_protocol::bedrock::client::network_chunk_publisher_update::CNetworkChunkPublisherUpdate;
|
||||
use pumpkin_protocol::bedrock::frame_set::FrameSet;
|
||||
use pumpkin_protocol::{
|
||||
BClientPacket, ClientPacket, IdOr, SoundEvent,
|
||||
bedrock::{
|
||||
client::{
|
||||
chunk_radius_update::CChunkRadiusUpdate,
|
||||
creative_content::{CreativeContent, Group},
|
||||
gamerules_changed::GameRules,
|
||||
inventory_content::CInventoryContent,
|
||||
play_status::CPlayStatus,
|
||||
start_game::{Experiments, GamePublishSetting, LevelSettings},
|
||||
update_artributes::{Attribute, CUpdateAttributes},
|
||||
},
|
||||
network_item::{ItemInstanceUserData, NetworkItemDescriptor, NetworkItemStackDescriptor},
|
||||
network_item::NetworkItemDescriptor,
|
||||
server::text::SText,
|
||||
},
|
||||
codec::{
|
||||
@@ -390,7 +392,7 @@ impl World {
|
||||
let current_players = self.players.read().await;
|
||||
|
||||
for (_, player) in current_players.iter() {
|
||||
match player.client.as_ref() {
|
||||
match &player.client {
|
||||
ClientPlatform::Java(client) => client.enqueue_packet(je_packet).await,
|
||||
ClientPlatform::Bedrock(client) => client.send_game_packet(be_packet).await,
|
||||
}
|
||||
@@ -717,8 +719,8 @@ impl World {
|
||||
|
||||
for i in self.players.read().await.values() {
|
||||
let center = i.living_entity.entity.chunk_pos.load();
|
||||
for dx in -8i32..=8 {
|
||||
for dy in -8i32..=8 {
|
||||
for dx in -8..=8 {
|
||||
for dy in -8..=8 {
|
||||
// if dx.abs() <= 2 || dy.abs() <= 2 || dx.abs() >= 6 || dy.abs() >= 6 { // this is only for debug, spawning runs too slow
|
||||
// continue;
|
||||
// }
|
||||
@@ -732,10 +734,8 @@ impl World {
|
||||
}
|
||||
}
|
||||
|
||||
let mut spawning_chunks = Vec::with_capacity(spawning_chunks_map.len());
|
||||
for i in spawning_chunks_map {
|
||||
spawning_chunks.push(i);
|
||||
}
|
||||
let mut spawning_chunks: Vec<(Vector2<i32>, Arc<RwLock<ChunkData>>)> =
|
||||
spawning_chunks_map.into_iter().collect();
|
||||
|
||||
let get_chunks_clock = spawn_entity_clock_start.elapsed();
|
||||
// log::debug!("spawning chunks size {}", spawning_chunks.len());
|
||||
@@ -1085,10 +1085,8 @@ impl World {
|
||||
) {
|
||||
// this.level.tickThunder(chunk);
|
||||
//TODO check in simulation distance
|
||||
if self.weather.lock().await.raining
|
||||
&& self.weather.lock().await.thundering
|
||||
&& rng().random_range(0..100_000) == 0
|
||||
{
|
||||
let weather = self.weather.lock().await;
|
||||
if weather.raining && weather.thundering && rng().random_range(0..100_000) == 0 {
|
||||
let rand_value = rng().random::<i32>() >> 2;
|
||||
let delta = Vector3::new(rand_value & 15, rand_value >> 16 & 15, rand_value >> 8 & 15);
|
||||
let random_pos = Vector3::new(
|
||||
@@ -1191,14 +1189,14 @@ impl World {
|
||||
custom_biome_name: String::new(),
|
||||
dimension: VarInt(0),
|
||||
generator_type: VarInt(1),
|
||||
world_gamemode: VarInt(server.defaultgamemode.lock().await.gamemode as i32),
|
||||
world_gamemode: server.defaultgamemode.lock().await.gamemode,
|
||||
hardcore: base_config.hardcore,
|
||||
difficulty: VarInt(level_info.difficulty as i32),
|
||||
spawn_position: NetworkPos(BlockPos(Vector3::new(
|
||||
spawn_position: NetworkPos(BlockPos::new(
|
||||
level_info.spawn_x,
|
||||
level_info.spawn_y,
|
||||
level_info.spawn_z,
|
||||
))),
|
||||
)),
|
||||
has_achievements_disabled: false,
|
||||
editor_world_type: VarInt(0),
|
||||
is_created_in_editor: false,
|
||||
@@ -1212,8 +1210,8 @@ impl World {
|
||||
has_confirmed_platform_locked_content: false,
|
||||
was_multiplayer_intended: true,
|
||||
was_lan_broadcasting_intended: true,
|
||||
xbox_live_broadcast_setting: VarInt(GamePublishSetting::Public as _),
|
||||
platform_broadcast_setting: VarInt(GamePublishSetting::Public as _),
|
||||
xbox_live_broadcast_setting: GamePublishSetting::Public,
|
||||
platform_broadcast_setting: GamePublishSetting::Public,
|
||||
commands_enabled: level_info.allow_commands,
|
||||
is_texture_packs_required: false,
|
||||
rule_data: GameRules {
|
||||
@@ -1256,11 +1254,12 @@ impl World {
|
||||
drop(weather);
|
||||
|
||||
let client = player.client.bedrock();
|
||||
|
||||
client
|
||||
.send_game_packet(&CStartGame {
|
||||
entity_id: VarLong(runtime_id as i64),
|
||||
entity_id: VarLong(runtime_id as _),
|
||||
runtime_entity_id: VarULong(runtime_id),
|
||||
player_gamemode: VarInt(player.gamemode.load() as i32),
|
||||
player_gamemode: player.gamemode.load(),
|
||||
position: Vector3::new(0.0, 100.0, 0.0),
|
||||
pitch: 0.0,
|
||||
yaw: 0.0,
|
||||
@@ -1290,9 +1289,16 @@ impl World {
|
||||
// TODO The client needs extra biome data for this
|
||||
enable_clientside_generation: false,
|
||||
blocknetwork_ids_are_hashed: false,
|
||||
tick_death_system_enabled: false,
|
||||
server_auth_sounds: false,
|
||||
})
|
||||
.await;
|
||||
client
|
||||
.send_game_packet(&CChunkRadiusUpdate {
|
||||
chunk_radius: VarInt(player.config.read().await.view_distance.get().into()),
|
||||
})
|
||||
.await;
|
||||
chunker::update_position(&player).await;
|
||||
client
|
||||
.send_game_packet(&CreativeContent {
|
||||
groups: &[Group {
|
||||
@@ -1304,52 +1310,48 @@ impl World {
|
||||
})
|
||||
.await;
|
||||
|
||||
let mut frame_set = FrameSet::default();
|
||||
client
|
||||
.send_game_packet(&CChunkRadiusUpdate {
|
||||
chunk_radius: VarInt(16),
|
||||
})
|
||||
.write_game_packet_to_set(
|
||||
&CNetworkChunkPublisherUpdate::new(
|
||||
BlockPos::new(0, 100, 0),
|
||||
NonZeroU32::from(player.config.read().await.view_distance).into(),
|
||||
),
|
||||
&mut frame_set,
|
||||
)
|
||||
.await;
|
||||
|
||||
chunker::update_position(&player).await;
|
||||
|
||||
client
|
||||
.send_game_packet(&CUpdateAttributes {
|
||||
runtime_id: VarULong(runtime_id),
|
||||
attributes: vec![Attribute {
|
||||
min_value: 0.0,
|
||||
max_value: f32::MAX,
|
||||
current_value: 0.1,
|
||||
default_min_value: 0.0,
|
||||
default_max_value: f32::MAX,
|
||||
default_value: 0.1,
|
||||
name: "minecraft:movement".to_string(),
|
||||
modifiers_list_size: VarUInt(0),
|
||||
}],
|
||||
player_tick: VarULong(0),
|
||||
})
|
||||
.write_game_packet_to_set(
|
||||
&CUpdateAttributes {
|
||||
runtime_id: VarULong(runtime_id),
|
||||
attributes: vec![Attribute {
|
||||
min_value: 0.0,
|
||||
max_value: f32::MAX,
|
||||
current_value: 0.1,
|
||||
default_min_value: 0.0,
|
||||
default_max_value: f32::MAX,
|
||||
default_value: 0.1,
|
||||
name: "minecraft:movement".to_string(),
|
||||
modifiers_list_size: VarUInt(0),
|
||||
}],
|
||||
player_tick: VarULong(0),
|
||||
},
|
||||
&mut frame_set,
|
||||
)
|
||||
.await;
|
||||
|
||||
client.send_game_packet(&CPlayStatus::PlayerSpawn).await;
|
||||
|
||||
client
|
||||
.send_game_packet(&CInventoryContent {
|
||||
inventory_id: VarUInt(124),
|
||||
slots: vec![
|
||||
NetworkItemStackDescriptor {
|
||||
id: VarInt(2),
|
||||
stack_size: 64,
|
||||
aux_value: VarUInt(0),
|
||||
net_id: Some(VarInt(2)),
|
||||
block_runtime_id: VarInt(2),
|
||||
user_data_buffer: ItemInstanceUserData::default()
|
||||
};
|
||||
36
|
||||
],
|
||||
container_name: 0,
|
||||
dynamic_id: None,
|
||||
storage_item: NetworkItemStackDescriptor::default(),
|
||||
})
|
||||
.write_game_packet_to_set(&CPlayStatus::PlayerSpawn, &mut frame_set)
|
||||
.await;
|
||||
client.send_frame_set(frame_set, 0x84).await;
|
||||
|
||||
{
|
||||
let mut abilities = player.abilities.lock().await;
|
||||
abilities.set_for_gamemode(player.gamemode.load());
|
||||
};
|
||||
|
||||
player.send_abilities_update().await;
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
@@ -1689,7 +1691,7 @@ impl World {
|
||||
yaw: f32,
|
||||
pitch: f32,
|
||||
) {
|
||||
if let ClientPlatform::Java(client) = player.client.as_ref() {
|
||||
if let ClientPlatform::Java(client) = &player.client {
|
||||
self.worldborder.lock().await.init_client(client).await;
|
||||
}
|
||||
|
||||
@@ -1781,7 +1783,7 @@ impl World {
|
||||
))
|
||||
.await;
|
||||
|
||||
player.reset_state().await;
|
||||
player.living_entity.reset_state().await;
|
||||
|
||||
log::debug!("Sending player abilities to {}", player.gameprofile.name);
|
||||
player.send_abilities_update().await;
|
||||
@@ -1870,9 +1872,9 @@ impl World {
|
||||
let mut receiver = self.level.receive_chunks(chunks.clone());
|
||||
|
||||
let level = self.level.clone();
|
||||
let player1 = player.clone();
|
||||
let world = self.clone();
|
||||
let world1 = self.clone();
|
||||
let player1 = player.clone();
|
||||
|
||||
player.clone().spawn_task(async move {
|
||||
'main: loop {
|
||||
@@ -2316,7 +2318,7 @@ impl World {
|
||||
.remove(&player.gameprofile.id)
|
||||
.unwrap();
|
||||
let uuid = player.gameprofile.id;
|
||||
self.broadcast_packet_except(&[player.gameprofile.id], &CRemovePlayerInfo::new(&[uuid]))
|
||||
self.broadcast_packet_all(&CRemovePlayerInfo::new(&[uuid]))
|
||||
.await;
|
||||
self.broadcast_packet_all(&CRemoveEntities::new(&[player.entity_id().into()]))
|
||||
.await;
|
||||
|
||||
@@ -196,20 +196,20 @@ impl SpawnState {
|
||||
let mut local_mob_cap = LocalMobCapCalculator::new(world);
|
||||
let mut counter = MobCounts::default();
|
||||
for entity in entities.read().await.values() {
|
||||
let entity_type = &entity.get_entity().entity_type;
|
||||
#[allow(clippy::overly_complex_bool_expr)]
|
||||
if entity_type.mob && false || entity_type.category == &MobCategory::MISC {
|
||||
let entity = entity.get_entity();
|
||||
let entity_type = entity.entity_type;
|
||||
if !entity_type.mob || entity_type.category == &MobCategory::MISC {
|
||||
// TODO (mob.isPersistenceRequired() || mob.requiresCustomPersistence())
|
||||
continue;
|
||||
}
|
||||
let entity_pos = &entity.get_entity().block_pos.load();
|
||||
let biome = world.level.get_rough_biome(entity_pos).await;
|
||||
let entity_pos = entity.block_pos.load();
|
||||
let biome = world.level.get_rough_biome(&entity_pos).await;
|
||||
if let Some(cost) = biome.spawn_costs.get(entity_type.resource_name) {
|
||||
potential.add_charge(entity_pos, cost.charge);
|
||||
potential.add_charge(&entity_pos, cost.charge);
|
||||
}
|
||||
if entity_type.mob {
|
||||
local_mob_cap
|
||||
.add_mob(&entity.get_entity().chunk_pos.load(), entity_type.category)
|
||||
.add_mob(&entity.chunk_pos.load(), entity_type.category)
|
||||
.await;
|
||||
}
|
||||
counter.add(entity_type.category);
|
||||
|
||||
@@ -35,7 +35,7 @@ impl LevelTime {
|
||||
pub async fn send_time(&self, world: &World) {
|
||||
let current_players = world.players.read().await;
|
||||
for player in current_players.values() {
|
||||
match player.client.as_ref() {
|
||||
match &player.client {
|
||||
ClientPlatform::Java(java_client) => {
|
||||
java_client
|
||||
.enqueue_packet(&CUpdateTime::new(self.world_age, self.time_of_day, true))
|
||||
|
||||
Reference in New Issue
Block a user