diff --git a/docs/troubleshooting/common_issues.md b/docs/troubleshooting/common_issues.md index 57c513a48..98786f7fa 100644 --- a/docs/troubleshooting/common_issues.md +++ b/docs/troubleshooting/common_issues.md @@ -18,7 +18,7 @@ **Cause:** The server is currently not calculating hit boxes for blocks, we're working on that. -3. The Server is unresponsive +3. ### The Server is unresponsive **Issue:** You have to wait before reconnecting or can't do basic things while chunks are loading. diff --git a/pumpkin-protocol/src/client/play/c_update_objectives.rs b/pumpkin-protocol/src/client/play/c_update_objectives.rs index 3b1dadbb9..a43500e33 100644 --- a/pumpkin-protocol/src/client/play/c_update_objectives.rs +++ b/pumpkin-protocol/src/client/play/c_update_objectives.rs @@ -1,4 +1,4 @@ -use pumpkin_core::text::{style::Style, TextComponent}; +use pumpkin_core::text::TextComponent; use pumpkin_macros::packet; use crate::{ClientPacket, NumberFormat, VarInt}; diff --git a/pumpkin-protocol/src/client/play/c_update_score.rs b/pumpkin-protocol/src/client/play/c_update_score.rs index f7ecc0d7e..605d43b9e 100644 --- a/pumpkin-protocol/src/client/play/c_update_score.rs +++ b/pumpkin-protocol/src/client/play/c_update_score.rs @@ -1,8 +1,10 @@ use pumpkin_core::text::TextComponent; use pumpkin_macros::packet; +use serde::Serialize; use crate::{NumberFormat, VarInt}; +#[derive(Serialize)] #[packet(0x61)] pub struct CUpdateScore<'a> { entity_name: &'a str, diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index 7b498490e..1ed4cfdb8 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -1,5 +1,6 @@ use bytebuf::{packet_id::Packet, ByteBuffer, DeserializerError}; use bytes::Buf; +use pumpkin_core::text::{style::Style, TextComponent}; use serde::{Deserialize, Serialize}; use std::io::{self, Write}; use thiserror::Error; @@ -245,6 +246,7 @@ pub struct KnownPack<'a> { pub version: &'a str, } +#[derive(Serialize)] pub enum NumberFormat<'a> { /// Show nothing Blank, diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 394aad40c..78e9f1c31 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -19,8 +19,11 @@ use pumpkin_protocol::{ ClientPacket, VarInt, }; use pumpkin_world::level::Level; +use scoreboard::Scoreboard; use tokio::sync::mpsc; +pub mod scoreboard; + /// Represents a Minecraft world, containing entities, players, and the underlying level data. /// /// Each dimension (Overworld, Nether, End) typically has its own `World`. @@ -35,6 +38,7 @@ pub struct World { pub level: Arc>, /// A map of active players within the world, keyed by their unique token. pub current_players: Arc>>>, + pub scoreboard: Mutex, // TODO: entities } @@ -43,6 +47,7 @@ impl World { Self { level: Arc::new(Mutex::new(level)), current_players: Arc::new(Mutex::new(HashMap::new())), + scoreboard: Mutex::new(Scoreboard::new()), } } diff --git a/pumpkin/src/world/scoreboard.rs b/pumpkin/src/world/scoreboard.rs new file mode 100644 index 000000000..fa758d630 --- /dev/null +++ b/pumpkin/src/world/scoreboard.rs @@ -0,0 +1,123 @@ +use std::collections::HashMap; + +use pumpkin_core::text::TextComponent; +use pumpkin_protocol::{ + client::play::{CDisplayObjective, CUpdateObjectives, CUpdateScore, RenderType}, + NumberFormat, VarInt, +}; + +use super::World; + +pub struct Scoreboard { + objectives: HashMap>, + // teams: HashMap, +} + +impl Default for Scoreboard { + fn default() -> Self { + Self::new() + } +} + +impl Scoreboard { + pub fn new() -> Self { + Self { + objectives: HashMap::new(), + } + } + + pub fn add_objective(&mut self, world: &World, objective: ScoreboardObjective) { + if self.objectives.contains_key(objective.name) { + // Maybe make this an error ? + log::warn!( + "Tried to create Objective which does already exist, {}", + &objective.name + ); + return; + } + world.broadcast_packet_all(&CUpdateObjectives::new( + objective.name, + pumpkin_protocol::client::play::Mode::Add, + objective.display_name, + objective.render_type, + objective.number_format, + )); + world.broadcast_packet_all(&CDisplayObjective::new( + pumpkin_protocol::client::play::DisplaySlot::Sidebar, + "test", + )); + } + + pub fn update_score(&self, world: &World, score: ScoreboardScore) { + if self.objectives.contains_key(score.objective_name) { + log::warn!( + "Tried to place a score into a Objective which does not exist, {}", + &score.objective_name + ); + return; + } + world.broadcast_packet_all(&CUpdateScore::new( + score.entity_name, + score.objective_name, + score.value, + score.display_name, + score.number_format, + )); + } + + // pub fn add_team(&mut self, name: String) { + // if self.teams.contains_key(&name) { + // // Maybe make this an error ? + // log::warn!("Tried to create Team which does already exist, {}", name); + // } + // } +} + +pub struct ScoreboardObjective<'a> { + name: &'a str, + display_name: TextComponent<'a>, + render_type: RenderType, + number_format: Option>, +} + +impl<'a> ScoreboardObjective<'a> { + pub const fn new( + name: &'a str, + display_name: TextComponent<'a>, + render_type: RenderType, + number_format: Option>, + ) -> Self { + Self { + name, + display_name, + render_type, + number_format, + } + } +} + +pub struct ScoreboardScore<'a> { + entity_name: &'a str, + objective_name: &'a str, + value: VarInt, + display_name: Option>, + number_format: Option>, +} + +impl<'a> ScoreboardScore<'a> { + pub const fn new( + entity_name: &'a str, + objective_name: &'a str, + value: VarInt, + display_name: Option>, + number_format: Option>, + ) -> Self { + Self { + entity_name, + objective_name, + value, + display_name, + number_format, + } + } +}