chore: added gradient and rainbow to TextComponent
ofc did also a bunch of other things, the JavaClient implementation should be cleaner now and does not lock the player anymore
This commit is contained in:
parent
2853a1e741
commit
622d0599df
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -2064,9 +2064,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.105"
|
version = "1.0.106"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7"
|
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@ -2312,9 +2312,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.43"
|
version = "1.0.44"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
|
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@ -2338,7 +2338,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.10.0-rc.7"
|
version = "0.10.0-rc.7"
|
||||||
source = "git+https://github.com/rust-random/rand#6e80534233d4bb09ed0abc086edeacaae6f54dc0"
|
source = "git+https://github.com/rust-random/rand#7a81944044f3f1d2dc5c85a8ca9bd30429f46bd0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chacha20",
|
"chacha20",
|
||||||
"getrandom 0.4.0-rc.0",
|
"getrandom 0.4.0-rc.0",
|
||||||
@ -3302,9 +3302,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.19.0"
|
version = "1.20.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
|
checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.3.4",
|
"getrandom 0.3.4",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
|||||||
@ -45,7 +45,7 @@ futures = { version = "0.3", default-features = false, features = ["executor"] }
|
|||||||
rayon = "1.11"
|
rayon = "1.11"
|
||||||
crossbeam = "0.8"
|
crossbeam = "0.8"
|
||||||
|
|
||||||
uuid = { version = "1.19", features = ["serde", "v3", "v4"] }
|
uuid = { version = "1.20", features = ["serde", "v3", "v4"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
||||||
|
|||||||
@ -1,45 +0,0 @@
|
|||||||
### Pumpkin Text
|
|
||||||
Here we build Mojang's Textcomponent, Which is used across many places, Often where text is send to the player.
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- Colors
|
|
||||||
- [x] RBG
|
|
||||||
- [x] Black
|
|
||||||
- [x] Dark Blue
|
|
||||||
- [x] Dark Green
|
|
||||||
- [x] Dark Aqua
|
|
||||||
- [x] Dark Red
|
|
||||||
- [x] Dark Purple
|
|
||||||
- [x] Gold
|
|
||||||
- [x] Gray
|
|
||||||
- [x] Dark Gray
|
|
||||||
- [x] Blue
|
|
||||||
- [x] Green
|
|
||||||
- [x] Aqua
|
|
||||||
- [x] Red
|
|
||||||
- [x] Light Purple
|
|
||||||
- [x] Yellow
|
|
||||||
- [x] White
|
|
||||||
- Style
|
|
||||||
- [x] Bold
|
|
||||||
- [x] Italic
|
|
||||||
- [x] Underlined
|
|
||||||
- [x] Strikethrough
|
|
||||||
- [x] Obfuscated
|
|
||||||
- [x] Insertion
|
|
||||||
- [x] Shadow Color
|
|
||||||
- Click Event
|
|
||||||
- [x] Open URL
|
|
||||||
- [x] Run Command
|
|
||||||
- [x] Suggest Command
|
|
||||||
- [x] Change Pange (Book)
|
|
||||||
- [x] Copy to Clipboard
|
|
||||||
- Hover Event
|
|
||||||
- [x] Show Text
|
|
||||||
- [x] Show Item
|
|
||||||
- [x] ShowEntity
|
|
||||||
- Fonts
|
|
||||||
- [x] Default
|
|
||||||
- [x] Uniform (Unicode)
|
|
||||||
- [x] Alt
|
|
||||||
- [x] Illageralt
|
|
||||||
@ -16,6 +16,25 @@ pub enum Color {
|
|||||||
Named(NamedColor),
|
Named(NamedColor),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hsv_to_rgb(h: f32, s: f32, v: f32) -> (u8, u8, u8) {
|
||||||
|
let c = v * s;
|
||||||
|
let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
|
||||||
|
let m = v - c;
|
||||||
|
let (r, g, b) = match (h as i32 / 60) % 6 {
|
||||||
|
0 => (c, x, 0.0),
|
||||||
|
1 => (x, c, 0.0),
|
||||||
|
2 => (0.0, c, x),
|
||||||
|
3 => (0.0, x, c),
|
||||||
|
4 => (x, 0.0, c),
|
||||||
|
_ => (c, 0.0, x),
|
||||||
|
};
|
||||||
|
(
|
||||||
|
((r + m) * 255.0) as u8,
|
||||||
|
((g + m) * 255.0) as u8,
|
||||||
|
((b + m) * 255.0) as u8,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Color {
|
impl<'de> Deserialize<'de> for Color {
|
||||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
let s = String::deserialize(deserializer)?;
|
let s = String::deserialize(deserializer)?;
|
||||||
@ -75,9 +94,9 @@ impl Color {
|
|||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, Copy, Eq, Hash, PartialEq)]
|
#[derive(Debug, Deserialize, Clone, Copy, Eq, Hash, PartialEq)]
|
||||||
pub struct RGBColor {
|
pub struct RGBColor {
|
||||||
red: u8,
|
pub red: u8,
|
||||||
green: u8,
|
pub green: u8,
|
||||||
blue: u8,
|
pub blue: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RGBColor {
|
impl RGBColor {
|
||||||
@ -142,6 +161,29 @@ pub enum NamedColor {
|
|||||||
White,
|
White,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NamedColor {
|
||||||
|
pub fn to_rgb(&self) -> RGBColor {
|
||||||
|
match self {
|
||||||
|
NamedColor::Black => RGBColor::new(0, 0, 0),
|
||||||
|
NamedColor::DarkBlue => RGBColor::new(0, 0, 170),
|
||||||
|
NamedColor::DarkGreen => RGBColor::new(0, 170, 0),
|
||||||
|
NamedColor::DarkAqua => RGBColor::new(0, 170, 170),
|
||||||
|
NamedColor::DarkRed => RGBColor::new(170, 0, 0),
|
||||||
|
NamedColor::DarkPurple => RGBColor::new(170, 0, 170),
|
||||||
|
NamedColor::Gold => RGBColor::new(255, 170, 0),
|
||||||
|
NamedColor::Gray => RGBColor::new(170, 170, 170),
|
||||||
|
NamedColor::DarkGray => RGBColor::new(85, 85, 85),
|
||||||
|
NamedColor::Blue => RGBColor::new(85, 85, 255),
|
||||||
|
NamedColor::Green => RGBColor::new(85, 255, 85),
|
||||||
|
NamedColor::Aqua => RGBColor::new(85, 255, 255),
|
||||||
|
NamedColor::Red => RGBColor::new(255, 85, 85),
|
||||||
|
NamedColor::LightPurple => RGBColor::new(255, 85, 255),
|
||||||
|
NamedColor::Yellow => RGBColor::new(255, 255, 85),
|
||||||
|
NamedColor::White => RGBColor::new(255, 255, 255),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<&str> for NamedColor {
|
impl TryFrom<&str> for NamedColor {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use crate::text::color::ARGBColor;
|
use crate::text::color::{ARGBColor, hsv_to_rgb};
|
||||||
use crate::translation::{
|
use crate::translation::{
|
||||||
Locale, get_translation, get_translation_text, reorder_substitutions, translation_to_pretty,
|
Locale, get_translation, get_translation_text, reorder_substitutions, translation_to_pretty,
|
||||||
};
|
};
|
||||||
@ -352,6 +352,82 @@ impl TextComponent {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Applies a color gradient to the text
|
||||||
|
pub fn gradient_named(self, colors: &[color::NamedColor]) -> Self {
|
||||||
|
let rgb_colors: Vec<color::RGBColor> = colors.iter().map(|c| c.to_rgb()).collect();
|
||||||
|
self.gradient(&rgb_colors)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Applies a color gradient to the text
|
||||||
|
pub fn gradient(self, colors: &[color::RGBColor]) -> Self {
|
||||||
|
if colors.len() < 2 {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.apply_color_effect(|i, len| {
|
||||||
|
if len <= 1 {
|
||||||
|
return colors[0];
|
||||||
|
}
|
||||||
|
let total_segments = colors.len() - 1;
|
||||||
|
let position = i as f32 / (len - 1) as f32;
|
||||||
|
let segment_f = position * total_segments as f32;
|
||||||
|
let segment_index = (segment_f.floor() as usize).min(total_segments - 1);
|
||||||
|
|
||||||
|
let local_t = segment_f - segment_index as f32;
|
||||||
|
let start = colors[segment_index];
|
||||||
|
let end = colors[segment_index + 1];
|
||||||
|
|
||||||
|
// LERP logic
|
||||||
|
color::RGBColor::new(
|
||||||
|
(start.red as f32 + (end.red as f32 - start.red as f32) * local_t) as u8,
|
||||||
|
(start.green as f32 + (end.green as f32 - start.green as f32) * local_t) as u8,
|
||||||
|
(start.blue as f32 + (end.blue as f32 - start.blue as f32) * local_t) as u8,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Applies a rainbow effect to the text
|
||||||
|
pub fn rainbow(self) -> Self {
|
||||||
|
self.apply_color_effect(|i, len| {
|
||||||
|
let hue = (i as f32 / len as f32) * 360.0;
|
||||||
|
let (r, g, b) = hsv_to_rgb(hue, 1.0, 1.0);
|
||||||
|
color::RGBColor::new(r, g, b)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_color_effect<F>(mut self, color_gen: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(usize, usize) -> color::RGBColor,
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
let raw_text = self.0.clone().get_text(Locale::EnUs);
|
||||||
|
let chars: Vec<char> = raw_text.chars().collect();
|
||||||
|
let len = chars.len();
|
||||||
|
|
||||||
|
if len == 0 {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut colored_extra = Vec::new();
|
||||||
|
for (i, c) in chars.into_iter().enumerate() {
|
||||||
|
let rgb = color_gen(i, len);
|
||||||
|
|
||||||
|
let mut char_base = TextComponentBase {
|
||||||
|
content: TextContent::Text {
|
||||||
|
text: Cow::Owned(c.to_string()),
|
||||||
|
},
|
||||||
|
style: self.0.style.clone(),
|
||||||
|
extra: vec![],
|
||||||
|
};
|
||||||
|
char_base.style.color = Some(Color::Rgb(rgb));
|
||||||
|
colored_extra.push(char_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.0.content = TextContent::Text { text: "".into() };
|
||||||
|
self.0.extra = colored_extra;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Makes the text bold
|
/// Makes the text bold
|
||||||
pub fn bold(mut self) -> Self {
|
pub fn bold(mut self) -> Self {
|
||||||
self.0.style.bold = Some(true);
|
self.0.style.bold = Some(true);
|
||||||
|
|||||||
@ -886,7 +886,6 @@ impl ProtoChunk {
|
|||||||
block_registry: &dyn BlockRegistryExt,
|
block_registry: &dyn BlockRegistryExt,
|
||||||
random_config: &GlobalRandomConfig,
|
random_config: &GlobalRandomConfig,
|
||||||
) {
|
) {
|
||||||
// 1. Efficiently collect unique biomes from the flat map
|
|
||||||
let (center_x, center_z, min_y, height, biomes_in_chunk) = {
|
let (center_x, center_z, min_y, height, biomes_in_chunk) = {
|
||||||
let chunk = cache.get_center_chunk();
|
let chunk = cache.get_center_chunk();
|
||||||
let mut unique_biomes = Vec::with_capacity(4); // Usually few biomes per chunk
|
let mut unique_biomes = Vec::with_capacity(4); // Usually few biomes per chunk
|
||||||
@ -911,13 +910,9 @@ impl ProtoChunk {
|
|||||||
let population_seed =
|
let population_seed =
|
||||||
Xoroshiro::get_population_seed(random_config.seed, start_block_x, start_block_z);
|
Xoroshiro::get_population_seed(random_config.seed, start_block_x, start_block_z);
|
||||||
|
|
||||||
// Vanilla has 11 steps (0..10)
|
|
||||||
for step in 0..11 {
|
for step in 0..11 {
|
||||||
// --- STEP A: STRUCTURE PIECES ---
|
|
||||||
// Structures are handled first because features (like trees) need to avoid clipping into them
|
|
||||||
Self::generate_structure_step(cache, step, population_seed, random_config.seed as i64);
|
Self::generate_structure_step(cache, step, population_seed, random_config.seed as i64);
|
||||||
|
|
||||||
// --- STEP B: PLACED FEATURES ---
|
|
||||||
let mut features_to_run = Vec::new();
|
let mut features_to_run = Vec::new();
|
||||||
for biome_id in &biomes_in_chunk {
|
for biome_id in &biomes_in_chunk {
|
||||||
if let Some(biome) = Biome::from_id(*biome_id)
|
if let Some(biome) = Biome::from_id(*biome_id)
|
||||||
@ -930,7 +925,6 @@ impl ProtoChunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CRITICAL: Determinism. Sort features so the index 'p' always refers to the same feature.
|
|
||||||
features_to_run.sort_unstable();
|
features_to_run.sort_unstable();
|
||||||
features_to_run.dedup();
|
features_to_run.dedup();
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,7 @@ pub type BlockStateId = u16;
|
|||||||
pub const CURRENT_MC_VERSION: &str = "1.21.11";
|
pub const CURRENT_MC_VERSION: &str = "1.21.11";
|
||||||
pub const LOWEST_SUPPRORTED_PROTOCOL_VERSION: u32 = 773;
|
pub const LOWEST_SUPPRORTED_PROTOCOL_VERSION: u32 = 773;
|
||||||
|
|
||||||
pub const CURRENT_BEDROCK_MC_VERSION: &str = "1.21.130";
|
pub const CURRENT_BEDROCK_MC_VERSION: &str = "1.21.132";
|
||||||
pub const CURRENT_BEDROCK_MC_PROTOCOL: u32 = 898;
|
pub const CURRENT_BEDROCK_MC_PROTOCOL: u32 = 898;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
|||||||
@ -381,6 +381,8 @@ pub struct Player {
|
|||||||
pub keep_alive_id: AtomicI64,
|
pub keep_alive_id: AtomicI64,
|
||||||
/// The last time we sent a keep alive packet.
|
/// The last time we sent a keep alive packet.
|
||||||
pub last_keep_alive_time: AtomicCell<Instant>,
|
pub last_keep_alive_time: AtomicCell<Instant>,
|
||||||
|
/// The ping in millis.
|
||||||
|
pub ping: AtomicU32,
|
||||||
/// The amount of ticks since the player's last attack.
|
/// The amount of ticks since the player's last attack.
|
||||||
pub last_attacked_ticks: AtomicU32,
|
pub last_attacked_ticks: AtomicU32,
|
||||||
/// The player's last known experience level.
|
/// The player's last known experience level.
|
||||||
@ -486,6 +488,7 @@ impl Player {
|
|||||||
wait_for_keep_alive: AtomicBool::new(false),
|
wait_for_keep_alive: AtomicBool::new(false),
|
||||||
keep_alive_id: AtomicI64::new(0),
|
keep_alive_id: AtomicI64::new(0),
|
||||||
last_keep_alive_time: AtomicCell::new(std::time::Instant::now()),
|
last_keep_alive_time: AtomicCell::new(std::time::Instant::now()),
|
||||||
|
ping: AtomicU32::new(0),
|
||||||
last_attacked_ticks: AtomicU32::new(0),
|
last_attacked_ticks: AtomicU32::new(0),
|
||||||
client_loaded: AtomicBool::new(false),
|
client_loaded: AtomicBool::new(false),
|
||||||
client_loaded_timeout: AtomicU32::new(60),
|
client_loaded_timeout: AtomicU32::new(60),
|
||||||
@ -1402,15 +1405,8 @@ impl Player {
|
|||||||
|
|
||||||
self.set_client_loaded(false);
|
self.set_client_loaded(false);
|
||||||
let uuid = self.gameprofile.id;
|
let uuid = self.gameprofile.id;
|
||||||
current_world.remove_player(self, false).await;
|
let player = current_world.remove_player(self, false).await.unwrap();
|
||||||
new_world.players.write().await.insert(uuid, Arc::new(Self::new(
|
new_world.players.write().await.insert(uuid, player);
|
||||||
self.client.clone(),
|
|
||||||
self.gameprofile.clone(),
|
|
||||||
self.config.read().await.clone(),
|
|
||||||
new_world.clone(),
|
|
||||||
self.gamemode.load(),
|
|
||||||
)
|
|
||||||
.await));
|
|
||||||
self.unload_watched_chunks(¤t_world).await;
|
self.unload_watched_chunks(¤t_world).await;
|
||||||
|
|
||||||
self.chunk_manager.lock().await.change_world(¤t_world.level, &new_world.level);
|
self.chunk_manager.lock().await.change_world(¤t_world.level, &new_world.level);
|
||||||
|
|||||||
@ -2,18 +2,16 @@
|
|||||||
#![allow(unused_labels)]
|
#![allow(unused_labels)]
|
||||||
|
|
||||||
use crate::logging::{GzipRollingLogger, PumpkinCommandCompleter, ReadlineLogWrapper};
|
use crate::logging::{GzipRollingLogger, PumpkinCommandCompleter, ReadlineLogWrapper};
|
||||||
use crate::net::DisconnectReason;
|
|
||||||
use crate::net::bedrock::BedrockClient;
|
use crate::net::bedrock::BedrockClient;
|
||||||
use crate::net::java::JavaClient;
|
use crate::net::java::{JavaClient, PacketHandlerResult};
|
||||||
|
use crate::net::{ClientPlatform, DisconnectReason};
|
||||||
use crate::net::{lan_broadcast::LANBroadcast, query, rcon::RCONServer};
|
use crate::net::{lan_broadcast::LANBroadcast, query, rcon::RCONServer};
|
||||||
use crate::server::{Server, ticker::Ticker};
|
use crate::server::{Server, ticker::Ticker};
|
||||||
use log::{Level, LevelFilter};
|
use log::{Level, LevelFilter};
|
||||||
use net::authentication::fetch_mojang_public_keys;
|
|
||||||
use plugin::PluginManager;
|
use plugin::PluginManager;
|
||||||
use plugin::server::server_command::ServerCommandEvent;
|
use plugin::server::server_command::ServerCommandEvent;
|
||||||
use pumpkin_config::{AdvancedConfiguration, BasicConfiguration};
|
use pumpkin_config::{AdvancedConfiguration, BasicConfiguration};
|
||||||
use pumpkin_macros::send_cancellable;
|
use pumpkin_macros::send_cancellable;
|
||||||
use pumpkin_protocol::ConnectionState::Play;
|
|
||||||
use pumpkin_util::permission::{PermissionManager, PermissionRegistry};
|
use pumpkin_util::permission::{PermissionManager, PermissionRegistry};
|
||||||
use pumpkin_util::text::TextComponent;
|
use pumpkin_util::text::TextComponent;
|
||||||
use rustyline::Editor;
|
use rustyline::Editor;
|
||||||
@ -279,13 +277,6 @@ impl PumpkinServer {
|
|||||||
tcp_listener = Some(listener);
|
tcp_listener = Some(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
if server.basic_config.allow_chat_reports {
|
|
||||||
let mojang_public_keys =
|
|
||||||
fetch_mojang_public_keys(&server.advanced_config.networking.authentication)
|
|
||||||
.unwrap();
|
|
||||||
*server.mojang_public_keys.lock().await = mojang_public_keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ticker
|
// Ticker
|
||||||
{
|
{
|
||||||
let ticker_server = server.clone();
|
let ticker_server = server.clone();
|
||||||
@ -405,33 +396,36 @@ impl PumpkinServer {
|
|||||||
format!("{client_addr}")
|
format!("{client_addr}")
|
||||||
};
|
};
|
||||||
log::debug!("Accepted connection from Java Edition: {formatted_address} (id {client_id})");
|
log::debug!("Accepted connection from Java Edition: {formatted_address} (id {client_id})");
|
||||||
|
|
||||||
let mut java_client = JavaClient::new(connection, client_addr, client_id);
|
|
||||||
java_client.start_outgoing_packet_task();
|
|
||||||
let java_client = Arc::new(java_client);
|
|
||||||
|
|
||||||
let server_clone = self.server.clone();
|
let server_clone = self.server.clone();
|
||||||
|
|
||||||
tasks.spawn(async move {
|
tasks.spawn(async move {
|
||||||
java_client.process_packets(&server_clone).await;
|
let mut java_client = JavaClient::new(connection, client_addr, client_id);
|
||||||
|
java_client.start_outgoing_packet_task();
|
||||||
|
let login_result = java_client.handle_login_sequence(&server_clone).await;
|
||||||
|
|
||||||
|
match login_result {
|
||||||
|
PacketHandlerResult::Stop => {
|
||||||
java_client.close();
|
java_client.close();
|
||||||
java_client.await_tasks().await;
|
java_client.await_tasks().await;
|
||||||
|
},
|
||||||
let player = java_client.player.lock().await;
|
PacketHandlerResult::ReadyToPlay(profile,config) => {
|
||||||
if let Some(player) = player.as_ref() {
|
if let Some((player, world)) = server_clone
|
||||||
log::debug!("Cleaning up player for id {client_id}");
|
.add_player(ClientPlatform::Java(java_client), profile, Some(config))
|
||||||
|
|
||||||
if let Err(e) = server_clone.player_data_storage
|
|
||||||
.handle_player_leave(player)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
log::error!("Failed to save player data on disconnect: {e}");
|
world
|
||||||
|
.spawn_java_player(&server_clone.basic_config, &player, &server_clone)
|
||||||
|
.await;
|
||||||
|
if let ClientPlatform::Java(client) = &player.client {
|
||||||
|
client.progress_player_packets(&player, &server_clone).await;
|
||||||
|
// Close when done
|
||||||
|
client.close();
|
||||||
|
client.await_tasks().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
player.remove().await;
|
player.remove().await;
|
||||||
server_clone.remove_player(player).await;
|
server_clone.remove_player(&player).await;
|
||||||
} else if java_client.connection_state.load() == Play {
|
}
|
||||||
log::error!("No player found for id {client_id}. This should not happen!");
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
use std::{num::NonZeroU8, sync::Arc};
|
use std::num::NonZeroU8;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
entity::player::ChatMode,
|
entity::player::ChatMode,
|
||||||
net::{ClientPlatform, PlayerConfig, can_not_join, java::JavaClient},
|
net::{
|
||||||
|
PlayerConfig, can_not_join,
|
||||||
|
java::{JavaClient, PacketHandlerResult},
|
||||||
|
},
|
||||||
server::Server,
|
server::Server,
|
||||||
};
|
};
|
||||||
use core::str;
|
use core::str;
|
||||||
@ -189,7 +192,7 @@ impl JavaClient {
|
|||||||
self.send_packet_now(&CFinishConfig).await;
|
self.send_packet_now(&CFinishConfig).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_config_acknowledged(self: &Arc<Self>, server: &Server) {
|
pub async fn handle_config_acknowledged(&self, server: &Server) -> PacketHandlerResult {
|
||||||
log::debug!("Handling config acknowledgement");
|
log::debug!("Handling config acknowledgement");
|
||||||
self.connection_state.store(ConnectionState::Play);
|
self.connection_state.store(ConnectionState::Play);
|
||||||
|
|
||||||
@ -199,19 +202,10 @@ impl JavaClient {
|
|||||||
|
|
||||||
if let Some(reason) = can_not_join(&profile, &address, server).await {
|
if let Some(reason) = can_not_join(&profile, &address, server).await {
|
||||||
self.kick(reason).await;
|
self.kick(reason).await;
|
||||||
return;
|
return PacketHandlerResult::Stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = self.config.lock().await;
|
let config = self.config.lock().await;
|
||||||
|
PacketHandlerResult::ReadyToPlay(profile, config.clone().unwrap_or_default())
|
||||||
if let Some((player, world)) = server
|
|
||||||
.add_player(ClientPlatform::Java(self.clone()), profile, config.clone())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
world
|
|
||||||
.spawn_java_player(&server.basic_config, player.clone(), server)
|
|
||||||
.await;
|
|
||||||
*self.player.lock().await = Some(player);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,7 +79,6 @@ pub struct JavaClient {
|
|||||||
pub address: Mutex<SocketAddr>,
|
pub address: Mutex<SocketAddr>,
|
||||||
/// The client's brand or modpack information, Optional.
|
/// The client's brand or modpack information, Optional.
|
||||||
pub brand: Mutex<Option<String>>,
|
pub brand: Mutex<Option<String>>,
|
||||||
pub player: Mutex<Option<Arc<Player>>>,
|
|
||||||
/// A collection of tasks associated with this client. The tasks await completion when removing the client.
|
/// A collection of tasks associated with this client. The tasks await completion when removing the client.
|
||||||
tasks: TaskTracker,
|
tasks: TaskTracker,
|
||||||
/// An notifier that is triggered when this client is closed.
|
/// An notifier that is triggered when this client is closed.
|
||||||
@ -94,6 +93,12 @@ pub struct JavaClient {
|
|||||||
network_reader: Mutex<TCPNetworkDecoder<BufReader<OwnedReadHalf>>>,
|
network_reader: Mutex<TCPNetworkDecoder<BufReader<OwnedReadHalf>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum PacketHandlerResult {
|
||||||
|
Stop,
|
||||||
|
// Signal to spawn the player
|
||||||
|
ReadyToPlay(GameProfile, PlayerConfig),
|
||||||
|
}
|
||||||
|
|
||||||
impl JavaClient {
|
impl JavaClient {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(tcp_stream: TcpStream, address: SocketAddr, id: u64) -> Self {
|
pub fn new(tcp_stream: TcpStream, address: SocketAddr, id: u64) -> Self {
|
||||||
@ -114,7 +119,6 @@ impl JavaClient {
|
|||||||
network_writer: Arc::new(Mutex::new(TCPNetworkEncoder::new(BufWriter::new(write)))),
|
network_writer: Arc::new(Mutex::new(TCPNetworkEncoder::new(BufWriter::new(write)))),
|
||||||
network_reader: Mutex::new(TCPNetworkDecoder::new(BufReader::new(read))),
|
network_reader: Mutex::new(TCPNetworkDecoder::new(BufReader::new(read))),
|
||||||
brand: Mutex::new(None),
|
brand: Mutex::new(None),
|
||||||
player: Mutex::new(None),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn set_encryption(
|
pub async fn set_encryption(
|
||||||
@ -158,9 +162,15 @@ impl JavaClient {
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `server`: A reference to the `Server` instance.
|
/// * `server`: A reference to the `Server` instance.
|
||||||
pub async fn process_packets(self: &Arc<Self>, server: &Arc<Server>) {
|
pub async fn handle_login_sequence(&self, server: &Server) -> PacketHandlerResult {
|
||||||
while let Some(packet) = self.get_packet().await {
|
while let Some(packet) = self.get_packet().await {
|
||||||
if let Err(error) = self.handle_packet(server, &packet).await {
|
match self.handle_packet(server, &packet).await {
|
||||||
|
Ok(result) => {
|
||||||
|
if let Some(result) = result {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
let text = format!("Error while reading incoming packet {error}");
|
let text = format!("Error while reading incoming packet {error}");
|
||||||
log::error!(
|
log::error!(
|
||||||
"Failed to read incoming packet with id {}: {}",
|
"Failed to read incoming packet with id {}: {}",
|
||||||
@ -171,6 +181,29 @@ impl JavaClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PacketHandlerResult::Stop
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn progress_player_packets(&self, player: &Arc<Player>, server: &Arc<Server>) {
|
||||||
|
while let Some(packet) = self.get_packet().await {
|
||||||
|
match self.handle_play_packet(player, server, &packet).await {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(e) => {
|
||||||
|
if e.is_kick() {
|
||||||
|
if let Some(kick_reason) = e.client_kick_reason() {
|
||||||
|
self.kick(TextComponent::text(kick_reason)).await;
|
||||||
|
} else {
|
||||||
|
self.kick(TextComponent::text(format!(
|
||||||
|
"Error while handling incoming packet {e}"
|
||||||
|
)))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn await_tasks(&self) {
|
pub async fn await_tasks(&self) {
|
||||||
self.tasks.close();
|
self.tasks.close();
|
||||||
@ -327,10 +360,10 @@ impl JavaClient {
|
|||||||
///
|
///
|
||||||
/// Returns a `DeserializerError` if an error occurs during packet deserialization.
|
/// Returns a `DeserializerError` if an error occurs during packet deserialization.
|
||||||
pub async fn handle_packet(
|
pub async fn handle_packet(
|
||||||
self: &Arc<Self>,
|
&self,
|
||||||
server: &Arc<Server>,
|
server: &Server,
|
||||||
packet: &RawPacket,
|
packet: &RawPacket,
|
||||||
) -> Result<(), ReadingError> {
|
) -> Result<Option<PacketHandlerResult>, ReadingError> {
|
||||||
match self.connection_state.load() {
|
match self.connection_state.load() {
|
||||||
ConnectionState::HandShake => self.handle_handshake_packet(packet).await,
|
ConnectionState::HandShake => self.handle_handshake_packet(packet).await,
|
||||||
ConnectionState::Status => self.handle_status_packet(server, packet).await,
|
ConnectionState::Status => self.handle_status_packet(server, packet).await,
|
||||||
@ -339,37 +372,20 @@ impl JavaClient {
|
|||||||
self.handle_login_packet(server, packet).await
|
self.handle_login_packet(server, packet).await
|
||||||
}
|
}
|
||||||
ConnectionState::Config => self.handle_config_packet(server, packet).await,
|
ConnectionState::Config => self.handle_config_packet(server, packet).await,
|
||||||
ConnectionState::Play => {
|
ConnectionState::Play => Ok(None),
|
||||||
if let Some(player) = self.player.lock().await.as_ref() {
|
|
||||||
match self.handle_play_packet(player, server, packet).await {
|
|
||||||
Ok(()) => {}
|
|
||||||
Err(e) => {
|
|
||||||
if e.is_kick() {
|
|
||||||
if let Some(kick_reason) = e.client_kick_reason() {
|
|
||||||
self.kick(TextComponent::text(kick_reason)).await;
|
|
||||||
} else {
|
|
||||||
self.kick(TextComponent::text(format!(
|
|
||||||
"Error while handling incoming packet {e}"
|
|
||||||
)))
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e.log();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_handshake_packet(&self, packet: &RawPacket) -> Result<(), ReadingError> {
|
async fn handle_handshake_packet(
|
||||||
|
&self,
|
||||||
|
packet: &RawPacket,
|
||||||
|
) -> Result<Option<PacketHandlerResult>, ReadingError> {
|
||||||
log::debug!("Handling handshake group");
|
log::debug!("Handling handshake group");
|
||||||
let payload = &packet.payload[..];
|
let payload = &packet.payload[..];
|
||||||
match packet.id {
|
match packet.id {
|
||||||
0 => {
|
0 => {
|
||||||
self.handle_handshake(SHandShake::read(payload)?).await;
|
self.handle_handshake(SHandShake::read(payload)?).await;
|
||||||
Ok(())
|
Ok(None)
|
||||||
}
|
}
|
||||||
_ => Err(ReadingError::Message(format!(
|
_ => Err(ReadingError::Message(format!(
|
||||||
"Failed to handle packet id {} in Handshake State",
|
"Failed to handle packet id {} in Handshake State",
|
||||||
@ -382,18 +398,18 @@ impl JavaClient {
|
|||||||
&self,
|
&self,
|
||||||
server: &Server,
|
server: &Server,
|
||||||
packet: &RawPacket,
|
packet: &RawPacket,
|
||||||
) -> Result<(), ReadingError> {
|
) -> Result<Option<PacketHandlerResult>, ReadingError> {
|
||||||
log::debug!("Handling status group");
|
log::debug!("Handling status group");
|
||||||
let payload = &packet.payload[..];
|
let payload = &packet.payload[..];
|
||||||
match packet.id {
|
match packet.id {
|
||||||
SStatusRequest::PACKET_ID => {
|
SStatusRequest::PACKET_ID => {
|
||||||
self.handle_status_request(server).await;
|
self.handle_status_request(server).await;
|
||||||
Ok(())
|
Ok(None)
|
||||||
}
|
}
|
||||||
SStatusPingRequest::PACKET_ID => {
|
SStatusPingRequest::PACKET_ID => {
|
||||||
self.handle_ping_request(SStatusPingRequest::read(payload)?)
|
self.handle_ping_request(SStatusPingRequest::read(payload)?)
|
||||||
.await;
|
.await;
|
||||||
Ok(())
|
Ok(None)
|
||||||
}
|
}
|
||||||
_ => Err(ReadingError::Message(format!(
|
_ => Err(ReadingError::Message(format!(
|
||||||
"Failed to handle java client packet id {} in Status State",
|
"Failed to handle java client packet id {} in Status State",
|
||||||
@ -456,7 +472,7 @@ impl JavaClient {
|
|||||||
&self,
|
&self,
|
||||||
server: &Server,
|
server: &Server,
|
||||||
packet: &RawPacket,
|
packet: &RawPacket,
|
||||||
) -> Result<(), ReadingError> {
|
) -> Result<Option<PacketHandlerResult>, ReadingError> {
|
||||||
log::debug!("Handling login group for id");
|
log::debug!("Handling login group for id");
|
||||||
let payload = &packet.payload[..];
|
let payload = &packet.payload[..];
|
||||||
match packet.id {
|
match packet.id {
|
||||||
@ -485,14 +501,14 @@ impl JavaClient {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_config_packet(
|
async fn handle_config_packet(
|
||||||
self: &Arc<Self>,
|
&self,
|
||||||
server: &Server,
|
server: &Server,
|
||||||
packet: &RawPacket,
|
packet: &RawPacket,
|
||||||
) -> Result<(), ReadingError> {
|
) -> Result<Option<PacketHandlerResult>, ReadingError> {
|
||||||
log::debug!("Handling config group");
|
log::debug!("Handling config group");
|
||||||
let payload = &packet.payload[..];
|
let payload = &packet.payload[..];
|
||||||
match packet.id {
|
match packet.id {
|
||||||
@ -505,7 +521,7 @@ impl JavaClient {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
SAcknowledgeFinishConfig::PACKET_ID => {
|
SAcknowledgeFinishConfig::PACKET_ID => {
|
||||||
self.handle_config_acknowledged(server).await;
|
return Ok(Some(self.handle_config_acknowledged(server).await));
|
||||||
}
|
}
|
||||||
SKnownPacks::PACKET_ID => {
|
SKnownPacks::PACKET_ID => {
|
||||||
self.handle_known_packs(SKnownPacks::read(payload)?).await;
|
self.handle_known_packs(SKnownPacks::read(payload)?).await;
|
||||||
@ -524,7 +540,7 @@ impl JavaClient {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::too_many_lines)]
|
#[expect(clippy::too_many_lines)]
|
||||||
|
|||||||
@ -1407,6 +1407,12 @@ impl JavaClient {
|
|||||||
if player.wait_for_keep_alive.load(Ordering::Relaxed)
|
if player.wait_for_keep_alive.load(Ordering::Relaxed)
|
||||||
&& keep_alive.keep_alive_id == player.keep_alive_id.load(Ordering::Relaxed)
|
&& keep_alive.keep_alive_id == player.keep_alive_id.load(Ordering::Relaxed)
|
||||||
{
|
{
|
||||||
|
let ping = player.last_keep_alive_time.load().elapsed();
|
||||||
|
// Vanilla logic
|
||||||
|
player.ping.store(
|
||||||
|
(player.ping.load(Ordering::Relaxed) * 3 + ping.as_millis() as u32) / 4,
|
||||||
|
Ordering::Relaxed,
|
||||||
|
);
|
||||||
player.wait_for_keep_alive.store(false, Ordering::Relaxed);
|
player.wait_for_keep_alive.store(false, Ordering::Relaxed);
|
||||||
} else {
|
} else {
|
||||||
self.kick(TextComponent::text(
|
self.kick(TextComponent::text(
|
||||||
|
|||||||
@ -86,15 +86,10 @@ impl Default for PlayerConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum PacketHandlerState {
|
|
||||||
PacketReady,
|
|
||||||
Stop,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is just a Wrapper for both Java & Bedrock connections
|
/// This is just a Wrapper for both Java & Bedrock connections
|
||||||
#[derive(Clone)]
|
#[expect(clippy::large_enum_variant)]
|
||||||
pub enum ClientPlatform {
|
pub enum ClientPlatform {
|
||||||
Java(Arc<JavaClient>),
|
Java(JavaClient),
|
||||||
Bedrock(Arc<BedrockClient>),
|
Bedrock(Arc<BedrockClient>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +114,7 @@ impl ClientPlatform {
|
|||||||
/// This function should only be used where you know that the client is java!
|
/// This function should only be used where you know that the client is java!
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn java(&self) -> &Arc<JavaClient> {
|
pub fn java(&self) -> &JavaClient {
|
||||||
if let Self::Java(client) = self {
|
if let Self::Java(client) = self {
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use crate::command::commands::defaultgamemode::DefaultGamemode;
|
|||||||
use crate::data::player_server::ServerPlayerData;
|
use crate::data::player_server::ServerPlayerData;
|
||||||
use crate::entity::{EntityBase, NBTStorage};
|
use crate::entity::{EntityBase, NBTStorage};
|
||||||
use crate::item::registry::ItemRegistry;
|
use crate::item::registry::ItemRegistry;
|
||||||
|
use crate::net::authentication::fetch_mojang_public_keys;
|
||||||
use crate::net::{ClientPlatform, DisconnectReason, EncryptionError, GameProfile, PlayerConfig};
|
use crate::net::{ClientPlatform, DisconnectReason, EncryptionError, GameProfile, PlayerConfig};
|
||||||
use crate::plugin::player::player_login::PlayerLoginEvent;
|
use crate::plugin::player::player_login::PlayerLoginEvent;
|
||||||
use crate::plugin::server::server_broadcast::ServerBroadcastEvent;
|
use crate::plugin::server::server_broadcast::ServerBroadcastEvent;
|
||||||
@ -39,7 +40,7 @@ use std::sync::Arc;
|
|||||||
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicI64, AtomicU32};
|
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicI64, AtomicU32};
|
||||||
use std::{future::Future, sync::atomic::Ordering, time::Duration};
|
use std::{future::Future, sync::atomic::Ordering, time::Duration};
|
||||||
use tokio::sync::{Mutex, OnceCell, RwLock};
|
use tokio::sync::{Mutex, OnceCell, RwLock};
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::{JoinHandle, JoinSet};
|
||||||
use tokio_util::task::TaskTracker;
|
use tokio_util::task::TaskTracker;
|
||||||
|
|
||||||
mod connection_cache;
|
mod connection_cache;
|
||||||
@ -172,6 +173,12 @@ impl Server {
|
|||||||
|
|
||||||
let tick_rate_manager = Arc::new(ServerTickRateManager::new(basic_config.tps));
|
let tick_rate_manager = Arc::new(ServerTickRateManager::new(basic_config.tps));
|
||||||
|
|
||||||
|
let mojang_public_keys = if basic_config.allow_chat_reports {
|
||||||
|
fetch_mojang_public_keys(&advanced_config.networking.authentication).unwrap()
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
|
||||||
let server = Self {
|
let server = Self {
|
||||||
basic_config,
|
basic_config,
|
||||||
advanced_config,
|
advanced_config,
|
||||||
@ -198,7 +205,7 @@ impl Server {
|
|||||||
tick_count: AtomicI32::new(0),
|
tick_count: AtomicI32::new(0),
|
||||||
tasks: TaskTracker::new(),
|
tasks: TaskTracker::new(),
|
||||||
server_guid: rand::random(),
|
server_guid: rand::random(),
|
||||||
mojang_public_keys: Mutex::new(Vec::new()),
|
mojang_public_keys: Mutex::new(mojang_public_keys),
|
||||||
world_info_writer: Arc::new(AnvilLevelInfo),
|
world_info_writer: Arc::new(AnvilLevelInfo),
|
||||||
level_info: level_info.clone(),
|
level_info: level_info.clone(),
|
||||||
_locker: Arc::new(locker),
|
_locker: Arc::new(locker),
|
||||||
@ -697,21 +704,21 @@ impl Server {
|
|||||||
}
|
}
|
||||||
/// Ticks the game logic for all worlds. This is the part that is affected by `/tick freeze`.
|
/// Ticks the game logic for all worlds. This is the part that is affected by `/tick freeze`.
|
||||||
pub async fn tick_worlds(self: &Arc<Self>) {
|
pub async fn tick_worlds(self: &Arc<Self>) {
|
||||||
let worlds = self.worlds.read().await.clone();
|
let worlds = self.worlds.read().await;
|
||||||
let mut handles = Vec::with_capacity(worlds.len());
|
let mut set = JoinSet::new();
|
||||||
for world in &worlds {
|
|
||||||
|
for world in worlds.iter() {
|
||||||
let world = world.clone();
|
let world = world.clone();
|
||||||
let server = self.clone();
|
let server = self.clone();
|
||||||
handles.push(tokio::spawn(async move {
|
|
||||||
|
set.spawn(async move {
|
||||||
world.tick(&server).await;
|
world.tick(&server).await;
|
||||||
}));
|
});
|
||||||
}
|
|
||||||
for handle in handles {
|
|
||||||
// Wait for all world ticks to complete
|
|
||||||
let _ = handle.await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global periodic tasks
|
set.join_all().await;
|
||||||
|
|
||||||
|
// Global tasks
|
||||||
if let Err(e) = self.player_data_storage.tick(self).await {
|
if let Err(e) = self.player_data_storage.tick(self).await {
|
||||||
log::error!("Error ticking player data: {e}");
|
log::error!("Error ticking player data: {e}");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use pumpkin_protocol::java::client::play::{
|
use pumpkin_protocol::java::client::play::{
|
||||||
CInitializeWorldBorder, CSetBorderCenter, CSetBorderLerpSize, CSetBorderSize,
|
CInitializeWorldBorder, CSetBorderCenter, CSetBorderLerpSize, CSetBorderSize,
|
||||||
CSetBorderWarningDelay, CSetBorderWarningDistance,
|
CSetBorderWarningDelay, CSetBorderWarningDistance,
|
||||||
@ -46,7 +44,7 @@ impl Worldborder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn init_client(&self, client: &Arc<JavaClient>) {
|
pub async fn init_client(&self, client: &JavaClient) {
|
||||||
client
|
client
|
||||||
.enqueue_packet(&CInitializeWorldBorder::new(
|
.enqueue_packet(&CInitializeWorldBorder::new(
|
||||||
self.center_x,
|
self.center_x,
|
||||||
|
|||||||
@ -1427,7 +1427,7 @@ impl World {
|
|||||||
pub async fn spawn_java_player(
|
pub async fn spawn_java_player(
|
||||||
&self,
|
&self,
|
||||||
base_config: &BasicConfiguration,
|
base_config: &BasicConfiguration,
|
||||||
player: Arc<Player>,
|
player: &Arc<Player>,
|
||||||
server: &Server,
|
server: &Server,
|
||||||
) {
|
) {
|
||||||
let dimensions: Vec<ResourceLocation> = server
|
let dimensions: Vec<ResourceLocation> = server
|
||||||
@ -1478,10 +1478,7 @@ impl World {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Send the current ticking state to the new player so they are in sync.
|
// Send the current ticking state to the new player so they are in sync.
|
||||||
server
|
server.tick_rate_manager.update_joining_player(player).await;
|
||||||
.tick_rate_manager
|
|
||||||
.update_joining_player(&player)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Permissions, i.e. the commands a player may use.
|
// Permissions, i.e. the commands a player may use.
|
||||||
player.send_permission_lvl_update().await;
|
player.send_permission_lvl_update().await;
|
||||||
@ -1490,12 +1487,12 @@ impl World {
|
|||||||
player.send_difficulty_update().await;
|
player.send_difficulty_update().await;
|
||||||
{
|
{
|
||||||
let command_dispatcher = server.command_dispatcher.read().await;
|
let command_dispatcher = server.command_dispatcher.read().await;
|
||||||
client_suggestions::send_c_commands_packet(&player, &command_dispatcher).await;
|
client_suggestions::send_c_commands_packet(player, &command_dispatcher).await;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Spawn in initial chunks
|
// Spawn in initial chunks
|
||||||
// This is made before the player teleport so that the player doesn't glitch out when spawning
|
// This is made before the player teleport so that the player doesn't glitch out when spawning
|
||||||
chunker::update_position(&player).await;
|
chunker::update_position(player).await;
|
||||||
|
|
||||||
// Teleport
|
// Teleport
|
||||||
let (position, yaw, pitch) = if player.has_played_before.load(Ordering::Relaxed) {
|
let (position, yaw, pitch) = if player.has_played_before.load(Ordering::Relaxed) {
|
||||||
@ -1775,7 +1772,7 @@ impl World {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
player.send_active_effects().await;
|
player.send_active_effects().await;
|
||||||
self.send_player_equipment(&player).await;
|
self.send_player_equipment(player).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_player_equipment(&self, from: &Player) {
|
async fn send_player_equipment(&self, from: &Player) {
|
||||||
@ -2370,16 +2367,13 @@ impl World {
|
|||||||
///
|
///
|
||||||
/// - This function assumes `broadcast_packet_expect` and `remove_entity` are defined elsewhere.
|
/// - This function assumes `broadcast_packet_expect` and `remove_entity` are defined elsewhere.
|
||||||
/// - The disconnect message sending is currently optional. Consider making it a configurable option.
|
/// - The disconnect message sending is currently optional. Consider making it a configurable option.
|
||||||
pub async fn remove_player(&self, player: &Arc<Player>, fire_event: bool) {
|
pub async fn remove_player(
|
||||||
if self
|
&self,
|
||||||
.players
|
player: &Arc<Player>,
|
||||||
.write()
|
fire_event: bool,
|
||||||
.await
|
) -> Option<Arc<Player>> {
|
||||||
.remove(&player.gameprofile.id)
|
let player = self.players.write().await.remove(&player.gameprofile.id);
|
||||||
.is_none()
|
if let Some(player) = player {
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let uuid = player.gameprofile.id;
|
let uuid = player.gameprofile.id;
|
||||||
self.broadcast_packet_all(&CRemovePlayerInfo::new(&[uuid]))
|
self.broadcast_packet_all(&CRemovePlayerInfo::new(&[uuid]))
|
||||||
.await;
|
.await;
|
||||||
@ -2406,6 +2400,8 @@ impl World {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds an entity to the world.
|
/// Adds an entity to the world.
|
||||||
pub async fn spawn_entity(&self, entity: Arc<dyn EntityBase>) {
|
pub async fn spawn_entity(&self, entity: Arc<dyn EntityBase>) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user