feat(bedrock): add bossbar & scoreboard
This commit is contained in:
parent
330a68054f
commit
7fe103db67
75
pumpkin-protocol/src/bedrock/client/boss_event.rs
Normal file
75
pumpkin-protocol/src/bedrock/client/boss_event.rs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
use std::io::{Error, Write};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
codec::{var_int::VarInt, var_long::VarLong},
|
||||||
|
serial::PacketWrite,
|
||||||
|
};
|
||||||
|
use pumpkin_macros::packet;
|
||||||
|
|
||||||
|
#[packet(74)]
|
||||||
|
pub struct CBossEvent {
|
||||||
|
pub boss_entity_id: VarLong,
|
||||||
|
pub action: BossEventAction,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum BossEventAction {
|
||||||
|
Add {
|
||||||
|
title: String,
|
||||||
|
health_percent: f32,
|
||||||
|
screen_darken: u16,
|
||||||
|
color: VarInt,
|
||||||
|
overlay: VarInt,
|
||||||
|
},
|
||||||
|
Remove,
|
||||||
|
UpdateHealth(f32),
|
||||||
|
UpdateTitle(String),
|
||||||
|
UpdateProperties {
|
||||||
|
screen_darken: u16,
|
||||||
|
color: VarInt,
|
||||||
|
overlay: VarInt,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketWrite for CBossEvent {
|
||||||
|
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
|
||||||
|
self.boss_entity_id.write(writer)?;
|
||||||
|
match &self.action {
|
||||||
|
BossEventAction::Add {
|
||||||
|
title,
|
||||||
|
health_percent,
|
||||||
|
screen_darken,
|
||||||
|
color,
|
||||||
|
overlay,
|
||||||
|
} => {
|
||||||
|
VarInt(0).write(writer)?;
|
||||||
|
title.write(writer)?;
|
||||||
|
health_percent.write(writer)?;
|
||||||
|
screen_darken.write(writer)?;
|
||||||
|
color.write(writer)?;
|
||||||
|
overlay.write(writer)?;
|
||||||
|
}
|
||||||
|
BossEventAction::Remove => {
|
||||||
|
VarInt(2).write(writer)?;
|
||||||
|
}
|
||||||
|
BossEventAction::UpdateHealth(health) => {
|
||||||
|
VarInt(3).write(writer)?;
|
||||||
|
health.write(writer)?;
|
||||||
|
}
|
||||||
|
BossEventAction::UpdateTitle(title) => {
|
||||||
|
VarInt(4).write(writer)?;
|
||||||
|
title.write(writer)?;
|
||||||
|
}
|
||||||
|
BossEventAction::UpdateProperties {
|
||||||
|
screen_darken,
|
||||||
|
color,
|
||||||
|
overlay,
|
||||||
|
} => {
|
||||||
|
VarInt(5).write(writer)?;
|
||||||
|
screen_darken.write(writer)?;
|
||||||
|
color.write(writer)?;
|
||||||
|
overlay.write(writer)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
pub mod available_commands;
|
pub mod available_commands;
|
||||||
|
pub mod boss_event;
|
||||||
pub mod chunk_radius_update;
|
pub mod chunk_radius_update;
|
||||||
pub mod container_open;
|
pub mod container_open;
|
||||||
pub mod correct_player_move;
|
pub mod correct_player_move;
|
||||||
@ -18,6 +19,7 @@ pub mod player_hotbar;
|
|||||||
pub mod raknet;
|
pub mod raknet;
|
||||||
pub mod resource_pack_stack;
|
pub mod resource_pack_stack;
|
||||||
pub mod resource_packs_info;
|
pub mod resource_packs_info;
|
||||||
|
pub mod scoreboard;
|
||||||
pub mod set_actor_data;
|
pub mod set_actor_data;
|
||||||
pub mod set_actor_motion;
|
pub mod set_actor_motion;
|
||||||
pub mod set_health;
|
pub mod set_health;
|
||||||
@ -29,6 +31,7 @@ pub mod update_abilities;
|
|||||||
pub mod update_attributes;
|
pub mod update_attributes;
|
||||||
|
|
||||||
pub use available_commands::*;
|
pub use available_commands::*;
|
||||||
|
pub use boss_event::*;
|
||||||
pub use chunk_radius_update::*;
|
pub use chunk_radius_update::*;
|
||||||
pub use container_open::*;
|
pub use container_open::*;
|
||||||
pub use correct_player_move::*;
|
pub use correct_player_move::*;
|
||||||
@ -48,6 +51,7 @@ pub use player_hotbar::*;
|
|||||||
pub use raknet::*;
|
pub use raknet::*;
|
||||||
pub use resource_pack_stack::*;
|
pub use resource_pack_stack::*;
|
||||||
pub use resource_packs_info::*;
|
pub use resource_packs_info::*;
|
||||||
|
pub use scoreboard::*;
|
||||||
pub use set_actor_data::*;
|
pub use set_actor_data::*;
|
||||||
pub use set_actor_motion::*;
|
pub use set_actor_motion::*;
|
||||||
pub use set_health::*;
|
pub use set_health::*;
|
||||||
|
|||||||
55
pumpkin-protocol/src/bedrock/client/scoreboard.rs
Normal file
55
pumpkin-protocol/src/bedrock/client/scoreboard.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
use std::io::{Error, Write};
|
||||||
|
|
||||||
|
use crate::{codec::var_int::VarInt, serial::PacketWrite};
|
||||||
|
use pumpkin_macros::packet;
|
||||||
|
|
||||||
|
#[derive(PacketWrite)]
|
||||||
|
#[packet(107)]
|
||||||
|
pub struct CSetDisplayObjective {
|
||||||
|
pub display_slot: String,
|
||||||
|
pub objective_name: String,
|
||||||
|
pub display_name: String,
|
||||||
|
pub criteria_name: String,
|
||||||
|
pub sort_order: VarInt,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PacketWrite)]
|
||||||
|
#[packet(108)]
|
||||||
|
pub struct CSetScore {
|
||||||
|
pub action: VarInt, // 0 = change, 1 = remove
|
||||||
|
pub entries: Vec<ScoreEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ScoreEntry {
|
||||||
|
pub scoreboard_id: i64,
|
||||||
|
pub objective_name: String,
|
||||||
|
pub score: VarInt,
|
||||||
|
pub entry_type: VarInt, // 1 = player, 2 = entity, 3 = fake player
|
||||||
|
pub entity_unique_id: i64,
|
||||||
|
pub custom_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketWrite for ScoreEntry {
|
||||||
|
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
|
||||||
|
self.scoreboard_id.write(writer)?;
|
||||||
|
self.objective_name.write(writer)?;
|
||||||
|
self.score.write(writer)?;
|
||||||
|
self.entry_type.write(writer)?;
|
||||||
|
match self.entry_type.0 {
|
||||||
|
1 | 2 => {
|
||||||
|
self.entity_unique_id.write(writer)?;
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
self.custom_name.write(writer)?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PacketWrite)]
|
||||||
|
#[packet(106)]
|
||||||
|
pub struct CRemoveObjective {
|
||||||
|
pub objective_name: String,
|
||||||
|
}
|
||||||
@ -215,6 +215,7 @@ impl boss_bar::HostBossBar for PluginHostState {
|
|||||||
&pbb.bossbar.uuid,
|
&pbb.bossbar.uuid,
|
||||||
pbb.bossbar.color,
|
pbb.bossbar.color,
|
||||||
pbb.bossbar.division,
|
pbb.bossbar.division,
|
||||||
|
pbb.bossbar.flags,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
@ -243,6 +244,7 @@ impl boss_bar::HostBossBar for PluginHostState {
|
|||||||
&pbb.bossbar.uuid,
|
&pbb.bossbar.uuid,
|
||||||
pbb.bossbar.color,
|
pbb.bossbar.color,
|
||||||
pbb.bossbar.division,
|
pbb.bossbar.division,
|
||||||
|
pbb.bossbar.flags,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,7 +47,8 @@ impl scoreboard::HostScoreboard for PluginHostState {
|
|||||||
.scoreboard
|
.scoreboard
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.add_objective(&world, objective);
|
.add_objective(&world, objective)
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +62,8 @@ impl scoreboard::HostScoreboard for PluginHostState {
|
|||||||
.scoreboard
|
.scoreboard
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.remove_objective(&world, &name);
|
.remove_objective(&world, &name)
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +147,12 @@ impl scoreboard::HostScoreboard for PluginHostState {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
world.scoreboard.lock().await.update_score(&world, score);
|
world
|
||||||
|
.scoreboard
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.update_score(&world, score)
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,7 +167,8 @@ impl scoreboard::HostScoreboard for PluginHostState {
|
|||||||
.scoreboard
|
.scoreboard
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.remove_score(&world, &entity_name, &objective_name);
|
.remove_score(&world, &entity_name, &objective_name)
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
use crate::entity::player::Player;
|
use crate::entity::player::Player;
|
||||||
|
use crate::net::ClientPlatform;
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
use pumpkin_protocol::bedrock::client::boss_event::{BossEventAction, CBossEvent as BBossEvent};
|
||||||
|
use pumpkin_protocol::codec::var_int::VarInt;
|
||||||
|
use pumpkin_protocol::codec::var_long::VarLong;
|
||||||
use pumpkin_protocol::java::client::play::{BosseventAction, CBossEvent};
|
use pumpkin_protocol::java::client::play::{BosseventAction, CBossEvent};
|
||||||
use pumpkin_util::text::TextComponent;
|
use pumpkin_util::text::TextComponent;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@ -15,6 +19,21 @@ pub enum BossbarColor {
|
|||||||
White,
|
White,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BossbarColor {
|
||||||
|
#[must_use]
|
||||||
|
pub const fn to_bedrock(self) -> VarInt {
|
||||||
|
match self {
|
||||||
|
Self::Pink => VarInt(0),
|
||||||
|
Self::Blue => VarInt(1),
|
||||||
|
Self::Red => VarInt(2),
|
||||||
|
Self::Green => VarInt(3),
|
||||||
|
Self::Yellow => VarInt(4),
|
||||||
|
Self::Purple => VarInt(5),
|
||||||
|
Self::White => VarInt(6),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum BossbarDivisions {
|
pub enum BossbarDivisions {
|
||||||
NoDivision,
|
NoDivision,
|
||||||
@ -24,6 +43,19 @@ pub enum BossbarDivisions {
|
|||||||
Notches20,
|
Notches20,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BossbarDivisions {
|
||||||
|
#[must_use]
|
||||||
|
pub const fn to_bedrock(self) -> VarInt {
|
||||||
|
match self {
|
||||||
|
Self::NoDivision => VarInt(0),
|
||||||
|
Self::Notches6 => VarInt(1),
|
||||||
|
Self::Notches10 => VarInt(2),
|
||||||
|
Self::Notches12 => VarInt(3),
|
||||||
|
Self::Notches20 => VarInt(4),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct BossbarFlags: u8 {
|
pub struct BossbarFlags: u8 {
|
||||||
@ -62,37 +94,87 @@ impl Bossbar {
|
|||||||
/// Extra methods for [`Player`] to send and manage the bossbar.
|
/// Extra methods for [`Player`] to send and manage the bossbar.
|
||||||
impl Player {
|
impl Player {
|
||||||
pub async fn send_bossbar(&self, bossbar: &Bossbar) {
|
pub async fn send_bossbar(&self, bossbar: &Bossbar) {
|
||||||
let boss_action = BosseventAction::Add {
|
match &self.client {
|
||||||
title: bossbar.title.clone(),
|
ClientPlatform::Java(java) => {
|
||||||
health: bossbar.health,
|
let boss_action = BosseventAction::Add {
|
||||||
color: (bossbar.color as u8).into(),
|
title: bossbar.title.clone(),
|
||||||
division: (bossbar.division as u8).into(),
|
health: bossbar.health,
|
||||||
flags: bossbar.flags.bits(),
|
color: (bossbar.color as u8).into(),
|
||||||
};
|
division: (bossbar.division as u8).into(),
|
||||||
|
flags: bossbar.flags.bits(),
|
||||||
|
};
|
||||||
|
|
||||||
let packet = CBossEvent::new(&bossbar.uuid, boss_action);
|
let packet = CBossEvent::new(&bossbar.uuid, boss_action);
|
||||||
self.client.enqueue_packet(&packet).await;
|
java.enqueue_packet(&packet).await;
|
||||||
|
}
|
||||||
|
ClientPlatform::Bedrock(bedrock) => {
|
||||||
|
let packet = BBossEvent {
|
||||||
|
boss_entity_id: VarLong(bossbar.uuid.as_u128() as i64),
|
||||||
|
action: BossEventAction::Add {
|
||||||
|
title: bossbar.title.clone().get_text(),
|
||||||
|
health_percent: bossbar.health,
|
||||||
|
screen_darken: u16::from(bossbar.flags.contains(BossbarFlags::DARKEN_SKY)),
|
||||||
|
color: bossbar.color.to_bedrock(),
|
||||||
|
overlay: bossbar.division.to_bedrock(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
bedrock.send_game_packet(&packet).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_bossbar(&self, uuid: Uuid) {
|
pub async fn remove_bossbar(&self, uuid: Uuid) {
|
||||||
let boss_action = BosseventAction::Remove;
|
match &self.client {
|
||||||
|
ClientPlatform::Java(java) => {
|
||||||
|
let boss_action = BosseventAction::Remove;
|
||||||
|
|
||||||
let packet = CBossEvent::new(&uuid, boss_action);
|
let packet = CBossEvent::new(&uuid, boss_action);
|
||||||
self.client.enqueue_packet(&packet).await;
|
java.enqueue_packet(&packet).await;
|
||||||
|
}
|
||||||
|
ClientPlatform::Bedrock(bedrock) => {
|
||||||
|
let packet = BBossEvent {
|
||||||
|
boss_entity_id: VarLong(uuid.as_u128() as i64),
|
||||||
|
action: BossEventAction::Remove,
|
||||||
|
};
|
||||||
|
bedrock.send_game_packet(&packet).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_bossbar_health(&self, uuid: &Uuid, health: f32) {
|
pub async fn update_bossbar_health(&self, uuid: &Uuid, health: f32) {
|
||||||
let boss_action = BosseventAction::UpdateHealth(health);
|
match &self.client {
|
||||||
|
ClientPlatform::Java(java) => {
|
||||||
|
let boss_action = BosseventAction::UpdateHealth(health);
|
||||||
|
|
||||||
let packet = CBossEvent::new(uuid, boss_action);
|
let packet = CBossEvent::new(uuid, boss_action);
|
||||||
self.client.enqueue_packet(&packet).await;
|
java.enqueue_packet(&packet).await;
|
||||||
|
}
|
||||||
|
ClientPlatform::Bedrock(bedrock) => {
|
||||||
|
let packet = BBossEvent {
|
||||||
|
boss_entity_id: VarLong(uuid.as_u128() as i64),
|
||||||
|
action: BossEventAction::UpdateHealth(health),
|
||||||
|
};
|
||||||
|
bedrock.send_game_packet(&packet).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_bossbar_title(&self, uuid: &Uuid, title: TextComponent) {
|
pub async fn update_bossbar_title(&self, uuid: &Uuid, title: TextComponent) {
|
||||||
let boss_action = BosseventAction::UpdateTile(title);
|
match &self.client {
|
||||||
|
ClientPlatform::Java(java) => {
|
||||||
|
let boss_action = BosseventAction::UpdateTile(title);
|
||||||
|
|
||||||
let packet = CBossEvent::new(uuid, boss_action);
|
let packet = CBossEvent::new(uuid, boss_action);
|
||||||
self.client.enqueue_packet(&packet).await;
|
java.enqueue_packet(&packet).await;
|
||||||
|
}
|
||||||
|
ClientPlatform::Bedrock(bedrock) => {
|
||||||
|
let packet = BBossEvent {
|
||||||
|
boss_entity_id: VarLong(uuid.as_u128() as i64),
|
||||||
|
action: BossEventAction::UpdateTitle(title.get_text()),
|
||||||
|
};
|
||||||
|
bedrock.send_game_packet(&packet).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_bossbar_style(
|
pub async fn update_bossbar_style(
|
||||||
@ -100,20 +182,56 @@ impl Player {
|
|||||||
uuid: &Uuid,
|
uuid: &Uuid,
|
||||||
color: BossbarColor,
|
color: BossbarColor,
|
||||||
dividers: BossbarDivisions,
|
dividers: BossbarDivisions,
|
||||||
|
flags: BossbarFlags,
|
||||||
) {
|
) {
|
||||||
let boss_action = BosseventAction::UpdateStyle {
|
match &self.client {
|
||||||
color: (color as u8).into(),
|
ClientPlatform::Java(java) => {
|
||||||
dividers: (dividers as u8).into(),
|
let boss_action = BosseventAction::UpdateStyle {
|
||||||
};
|
color: (color as u8).into(),
|
||||||
|
dividers: (dividers as u8).into(),
|
||||||
|
};
|
||||||
|
|
||||||
let packet = CBossEvent::new(uuid, boss_action);
|
let packet = CBossEvent::new(uuid, boss_action);
|
||||||
self.client.enqueue_packet(&packet).await;
|
java.enqueue_packet(&packet).await;
|
||||||
|
}
|
||||||
|
ClientPlatform::Bedrock(bedrock) => {
|
||||||
|
let packet = BBossEvent {
|
||||||
|
boss_entity_id: VarLong(uuid.as_u128() as i64),
|
||||||
|
action: BossEventAction::UpdateProperties {
|
||||||
|
screen_darken: u16::from(flags.contains(BossbarFlags::DARKEN_SKY)),
|
||||||
|
color: color.to_bedrock(),
|
||||||
|
overlay: dividers.to_bedrock(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
bedrock.send_game_packet(&packet).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_bossbar_flags(&self, uuid: &Uuid, flags: BossbarFlags) {
|
pub async fn update_bossbar_flags(&self, uuid: &Uuid, flags: BossbarFlags) {
|
||||||
let boss_action = BosseventAction::UpdateFlags(flags.bits());
|
match &self.client {
|
||||||
|
ClientPlatform::Java(java) => {
|
||||||
|
let boss_action = BosseventAction::UpdateFlags(flags.bits());
|
||||||
|
|
||||||
let packet = CBossEvent::new(uuid, boss_action);
|
let packet = CBossEvent::new(uuid, boss_action);
|
||||||
self.client.enqueue_packet(&packet).await;
|
java.enqueue_packet(&packet).await;
|
||||||
|
}
|
||||||
|
ClientPlatform::Bedrock(bedrock) => {
|
||||||
|
// For Bedrock, flags are part of properties (screen darken)
|
||||||
|
// We don't have color and dividers here, so we might need more info or just skip if not critical
|
||||||
|
// Actually, properties includes screen_darken, color, and overlay.
|
||||||
|
// Since this method only has flags, we might need to store the current color/division on the player
|
||||||
|
// or retrieve them from the bossbar if we had a reference to it.
|
||||||
|
let packet = BBossEvent {
|
||||||
|
boss_entity_id: VarLong(uuid.as_u128() as i64),
|
||||||
|
action: BossEventAction::UpdateProperties {
|
||||||
|
screen_darken: u16::from(flags.contains(BossbarFlags::DARKEN_SKY)),
|
||||||
|
color: VarInt(0),
|
||||||
|
overlay: VarInt(0),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
bedrock.send_game_packet(&packet).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -294,6 +294,7 @@ impl CustomBossbars {
|
|||||||
&bossbar.bossbar_data.uuid,
|
&bossbar.bossbar_data.uuid,
|
||||||
bossbar.bossbar_data.color,
|
bossbar.bossbar_data.color,
|
||||||
bossbar.bossbar_data.division,
|
bossbar.bossbar_data.division,
|
||||||
|
bossbar.bossbar_data.flags,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
@ -333,6 +334,7 @@ impl CustomBossbars {
|
|||||||
&bossbar.bossbar_data.uuid,
|
&bossbar.bossbar_data.uuid,
|
||||||
bossbar.bossbar_data.color,
|
bossbar.bossbar_data.color,
|
||||||
bossbar.bossbar_data.division,
|
bossbar.bossbar_data.division,
|
||||||
|
bossbar.bossbar_data.flags,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,11 +2,15 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use pumpkin_data::scoreboard::ScoreboardDisplaySlot;
|
use pumpkin_data::scoreboard::ScoreboardDisplaySlot;
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{
|
||||||
NumberFormat,
|
BClientPacket, ClientPacket, NumberFormat,
|
||||||
|
bedrock::client::scoreboard::{
|
||||||
|
CRemoveObjective as BRemoveObjective, CSetDisplayObjective as BSetDisplayObjective,
|
||||||
|
CSetScore as BSetScore, ScoreEntry as BScoreEntry,
|
||||||
|
},
|
||||||
codec::var_int::VarInt,
|
codec::var_int::VarInt,
|
||||||
java::client::play::{
|
java::client::play::{
|
||||||
CDisplayObjective, CSetPlayerTeam, CUpdateObjectives, CUpdateScore, RenderType, TeamMethod,
|
CDisplayObjective, CSetPlayerTeam, CUpdateObjectives, CUpdateScore, Mode, RenderType,
|
||||||
TeamParameters,
|
TeamMethod, TeamParameters,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use pumpkin_util::text::{TextComponent, color::NamedColor};
|
use pumpkin_util::text::{TextComponent, color::NamedColor};
|
||||||
@ -22,7 +26,15 @@ pub struct Scoreboard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Scoreboard {
|
impl Scoreboard {
|
||||||
pub fn add_objective(&mut self, world: &World, objective: ScoreboardObjective<'static>) {
|
async fn broadcast_editioned<J: ClientPacket, B: BClientPacket>(
|
||||||
|
world: &World,
|
||||||
|
je_packet: &J,
|
||||||
|
be_packet: &B,
|
||||||
|
) {
|
||||||
|
world.broadcast_editioned(je_packet, be_packet).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_objective(&mut self, world: &World, objective: ScoreboardObjective<'static>) {
|
||||||
if self.objectives.contains_key(objective.name) {
|
if self.objectives.contains_key(objective.name) {
|
||||||
warn!(
|
warn!(
|
||||||
"Tried to create an objective which already exists: {}",
|
"Tried to create an objective which already exists: {}",
|
||||||
@ -30,22 +42,36 @@ impl Scoreboard {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
world.broadcast_packet_all(&CUpdateObjectives::new(
|
|
||||||
|
let je_update = CUpdateObjectives::new(
|
||||||
objective.name.to_string(),
|
objective.name.to_string(),
|
||||||
pumpkin_protocol::java::client::play::Mode::Add,
|
Mode::Add,
|
||||||
objective.display_name.clone(),
|
objective.display_name.clone(),
|
||||||
objective.render_type,
|
objective.render_type,
|
||||||
objective.number_format.clone(),
|
objective.number_format.clone(),
|
||||||
));
|
);
|
||||||
world.broadcast_packet_all(&CDisplayObjective::new(
|
|
||||||
ScoreboardDisplaySlot::Sidebar,
|
let be_update = BSetDisplayObjective {
|
||||||
objective.name.to_string(),
|
display_slot: "sidebar".to_string(), // Default to sidebar
|
||||||
));
|
objective_name: objective.name.to_string(),
|
||||||
|
display_name: objective.display_name.clone().get_text(),
|
||||||
|
criteria_name: "dummy".to_string(),
|
||||||
|
sort_order: VarInt(0),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::broadcast_editioned(world, &je_update, &be_update).await;
|
||||||
|
|
||||||
|
let je_display =
|
||||||
|
CDisplayObjective::new(ScoreboardDisplaySlot::Sidebar, objective.name.to_string());
|
||||||
|
// Bedrock's SetDisplayObjective already sets the slot.
|
||||||
|
|
||||||
|
world.broadcast_packet_all(&je_display);
|
||||||
|
|
||||||
self.objectives
|
self.objectives
|
||||||
.insert(objective.name.to_string(), objective);
|
.insert(objective.name.to_string(), objective);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_objective(&mut self, world: &World, name: &str) {
|
pub async fn remove_objective(&mut self, world: &World, name: &str) {
|
||||||
if !self.objectives.contains_key(name) {
|
if !self.objectives.contains_key(name) {
|
||||||
warn!(
|
warn!(
|
||||||
"Tried to remove an objective which does not exist: {}",
|
"Tried to remove an objective which does not exist: {}",
|
||||||
@ -53,18 +79,26 @@ impl Scoreboard {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
world.broadcast_packet_all(&CUpdateObjectives::new(
|
|
||||||
|
let je_packet = CUpdateObjectives::new(
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
pumpkin_protocol::java::client::play::Mode::Remove,
|
Mode::Remove,
|
||||||
TextComponent::empty(),
|
TextComponent::empty(),
|
||||||
pumpkin_protocol::java::client::play::RenderType::Integer,
|
RenderType::Integer,
|
||||||
None,
|
None,
|
||||||
));
|
);
|
||||||
|
|
||||||
|
let be_packet = BRemoveObjective {
|
||||||
|
objective_name: name.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::broadcast_editioned(world, &je_packet, &be_packet).await;
|
||||||
|
|
||||||
self.objectives.remove(name);
|
self.objectives.remove(name);
|
||||||
self.scores.remove(name);
|
self.scores.remove(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_score(&mut self, world: &World, score: ScoreboardScore<'static>) {
|
pub async fn update_score(&mut self, world: &World, score: ScoreboardScore<'static>) {
|
||||||
if !self.objectives.contains_key(score.objective_name) {
|
if !self.objectives.contains_key(score.objective_name) {
|
||||||
warn!(
|
warn!(
|
||||||
"Tried to place a score into an objective which does not exist: {}",
|
"Tried to place a score into an objective which does not exist: {}",
|
||||||
@ -72,13 +106,28 @@ impl Scoreboard {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
world.broadcast_packet_all(&CUpdateScore::new(
|
|
||||||
|
let je_packet = CUpdateScore::new(
|
||||||
score.entity_name.to_string(),
|
score.entity_name.to_string(),
|
||||||
score.objective_name.to_string(),
|
score.objective_name.to_string(),
|
||||||
score.value,
|
score.value,
|
||||||
score.display_name.clone(),
|
score.display_name.clone(),
|
||||||
score.number_format.clone(),
|
score.number_format.clone(),
|
||||||
));
|
);
|
||||||
|
|
||||||
|
let be_packet = BSetScore {
|
||||||
|
action: VarInt(0), // Change
|
||||||
|
entries: vec![BScoreEntry {
|
||||||
|
scoreboard_id: score.entity_name.as_ptr() as i64, // Hacky ID
|
||||||
|
objective_name: score.objective_name.to_string(),
|
||||||
|
score: score.value,
|
||||||
|
entry_type: VarInt(3), // Fake player/Literal
|
||||||
|
entity_unique_id: 0,
|
||||||
|
custom_name: score.entity_name.to_string(),
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::broadcast_editioned(world, &je_packet, &be_packet).await;
|
||||||
|
|
||||||
self.scores
|
self.scores
|
||||||
.entry(score.objective_name.to_string())
|
.entry(score.objective_name.to_string())
|
||||||
@ -86,11 +135,23 @@ impl Scoreboard {
|
|||||||
.insert(score.entity_name.to_string(), score);
|
.insert(score.entity_name.to_string(), score);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_score(&mut self, world: &World, entity_name: &str, objective_name: &str) {
|
pub async fn remove_score(&mut self, world: &World, entity_name: &str, objective_name: &str) {
|
||||||
world.broadcast_packet_all(&CUpdateScore::new_remove(
|
let je_packet =
|
||||||
entity_name.to_string(),
|
CUpdateScore::new_remove(entity_name.to_string(), objective_name.to_string());
|
||||||
objective_name.to_string(),
|
|
||||||
));
|
let be_packet = BSetScore {
|
||||||
|
action: VarInt(1), // Remove
|
||||||
|
entries: vec![BScoreEntry {
|
||||||
|
scoreboard_id: entity_name.as_ptr() as i64,
|
||||||
|
objective_name: objective_name.to_string(),
|
||||||
|
score: VarInt(0),
|
||||||
|
entry_type: VarInt(3),
|
||||||
|
entity_unique_id: 0,
|
||||||
|
custom_name: entity_name.to_string(),
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::broadcast_editioned(world, &je_packet, &be_packet).await;
|
||||||
|
|
||||||
if let Some(objective_scores) = self.scores.get_mut(objective_name) {
|
if let Some(objective_scores) = self.scores.get_mut(objective_name) {
|
||||||
objective_scores.remove(entity_name);
|
objective_scores.remove(entity_name);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user