feat(plugin): native bedrock features
This commit is contained in:
parent
e0d16c74f2
commit
00cb2f9240
@ -1,4 +1,4 @@
|
|||||||
/* This file is generated. Do not edit manually. */
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum ScoreboardDisplaySlot {
|
pub enum ScoreboardDisplaySlot {
|
||||||
List,
|
List,
|
||||||
Sidebar,
|
Sidebar,
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit 160251fb0b15ad529bf7c4939140bb5fa0c07035
|
Subproject commit 8e006930be6fa6b5e60201c92d1c0393448ca606
|
||||||
@ -75,7 +75,7 @@ pub enum Mode {
|
|||||||
Update,
|
Update,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum RenderType {
|
pub enum RenderType {
|
||||||
Integer,
|
Integer,
|
||||||
Hearts,
|
Hearts,
|
||||||
|
|||||||
@ -385,7 +385,7 @@ impl KnownPack<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum NumberFormat {
|
pub enum NumberFormat {
|
||||||
/// Show nothing.
|
/// Show nothing.
|
||||||
Blank,
|
Blank,
|
||||||
|
|||||||
@ -60,15 +60,12 @@ impl CommandExecutor for ObjectivesAddExecutor {
|
|||||||
return Err(DUPLICATE_OBJECTIVE_ERROR.create_without_context());
|
return Err(DUPLICATE_OBJECTIVE_ERROR.create_without_context());
|
||||||
}
|
}
|
||||||
|
|
||||||
let obj_name_static = Box::leak(objective_name.to_string().into_boxed_str());
|
|
||||||
let criterion_static = Box::leak(criterion.to_string().into_boxed_str());
|
|
||||||
|
|
||||||
let new_objective = ScoreboardObjective::new(
|
let new_objective = ScoreboardObjective::new(
|
||||||
obj_name_static,
|
objective_name,
|
||||||
display_name.clone(),
|
display_name.clone(),
|
||||||
RenderType::Integer,
|
RenderType::Integer,
|
||||||
None,
|
None,
|
||||||
criterion_static,
|
criterion,
|
||||||
);
|
);
|
||||||
|
|
||||||
scoreboard.add_objective(world, new_objective).await;
|
scoreboard.add_objective(world, new_objective).await;
|
||||||
@ -128,8 +125,8 @@ impl CommandExecutor for PlayersEnableExecutor {
|
|||||||
let number_format = current_score.and_then(|s| s.number_format.clone());
|
let number_format = current_score.and_then(|s| s.number_format.clone());
|
||||||
|
|
||||||
let updated_score = ScoreboardScore {
|
let updated_score = ScoreboardScore {
|
||||||
entity_name: Box::leak(player_name.clone().into_boxed_str()),
|
entity_name: player_name.clone(),
|
||||||
objective_name: Box::leak(objective_name.to_string().into_boxed_str()),
|
objective_name: objective_name.to_string(),
|
||||||
value: VarInt(value),
|
value: VarInt(value),
|
||||||
display_name,
|
display_name,
|
||||||
number_format,
|
number_format,
|
||||||
|
|||||||
@ -122,7 +122,7 @@ impl CommandExecutor for TeamAddExecutor {
|
|||||||
players: vec![],
|
players: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
scoreboard.add_team(world, new_team);
|
scoreboard.add_team(world, new_team).await;
|
||||||
|
|
||||||
context
|
context
|
||||||
.source
|
.source
|
||||||
@ -158,7 +158,7 @@ impl CommandExecutor for TeamRemoveExecutor {
|
|||||||
|
|
||||||
let team_display_name = team.display_name.clone();
|
let team_display_name = team.display_name.clone();
|
||||||
|
|
||||||
scoreboard.remove_team(world, team_name);
|
scoreboard.remove_team(world, team_name).await;
|
||||||
|
|
||||||
context
|
context
|
||||||
.source
|
.source
|
||||||
@ -200,7 +200,9 @@ impl CommandExecutor for TeamEmptyExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for player in &players_to_remove {
|
for player in &players_to_remove {
|
||||||
scoreboard.remove_player_from_team(world, team_name, player);
|
scoreboard
|
||||||
|
.remove_player_from_team(world, team_name, player)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
context
|
context
|
||||||
@ -262,7 +264,9 @@ impl CommandExecutor for TeamJoinExecutor {
|
|||||||
|
|
||||||
let count = entity_names.len();
|
let count = entity_names.len();
|
||||||
for name in &entity_names {
|
for name in &entity_names {
|
||||||
scoreboard.add_player_to_team(world, team_name, name.clone());
|
scoreboard
|
||||||
|
.add_player_to_team(world, team_name, name.clone())
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let msg = if count == 1 {
|
let msg = if count == 1 {
|
||||||
@ -327,7 +331,9 @@ impl CommandExecutor for TeamLeaveExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(team_name) = found_team {
|
if let Some(team_name) = found_team {
|
||||||
scoreboard.remove_player_from_team(world, &team_name, name);
|
scoreboard
|
||||||
|
.remove_player_from_team(world, &team_name, name)
|
||||||
|
.await;
|
||||||
removed_count += 1;
|
removed_count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -474,7 +480,7 @@ impl CommandExecutor for TeamModifyColorResetExecutor {
|
|||||||
|
|
||||||
team.color = NamedColor::White;
|
team.color = NamedColor::White;
|
||||||
let team_display_name = team.display_name.clone();
|
let team_display_name = team.display_name.clone();
|
||||||
scoreboard.update_team(world, team);
|
scoreboard.update_team(world, team).await;
|
||||||
|
|
||||||
context
|
context
|
||||||
.source
|
.source
|
||||||
@ -519,7 +525,7 @@ impl CommandExecutor for TeamModifyColorExecutor {
|
|||||||
|
|
||||||
team.color = new_color;
|
team.color = new_color;
|
||||||
let team_display_name = team.display_name.clone();
|
let team_display_name = team.display_name.clone();
|
||||||
scoreboard.update_team(world, team);
|
scoreboard.update_team(world, team).await;
|
||||||
|
|
||||||
context
|
context
|
||||||
.source
|
.source
|
||||||
@ -565,7 +571,7 @@ impl CommandExecutor for TeamModifyDisplayNameExecutor {
|
|||||||
|
|
||||||
team.display_name = new_display_name.clone();
|
team.display_name = new_display_name.clone();
|
||||||
let team_display_name = team.display_name.clone();
|
let team_display_name = team.display_name.clone();
|
||||||
scoreboard.update_team(world, team);
|
scoreboard.update_team(world, team).await;
|
||||||
|
|
||||||
context
|
context
|
||||||
.source
|
.source
|
||||||
@ -607,7 +613,7 @@ impl CommandExecutor for TeamModifyPrefixExecutor {
|
|||||||
let new_prefix = TextComponent::text(new_prefix_str.to_string());
|
let new_prefix = TextComponent::text(new_prefix_str.to_string());
|
||||||
team.player_prefix = new_prefix.clone();
|
team.player_prefix = new_prefix.clone();
|
||||||
let team_display_name = team.display_name.clone();
|
let team_display_name = team.display_name.clone();
|
||||||
scoreboard.update_team(world, team);
|
scoreboard.update_team(world, team).await;
|
||||||
|
|
||||||
context
|
context
|
||||||
.source
|
.source
|
||||||
@ -649,7 +655,7 @@ impl CommandExecutor for TeamModifySuffixExecutor {
|
|||||||
let new_suffix = TextComponent::text(new_suffix_str.to_string());
|
let new_suffix = TextComponent::text(new_suffix_str.to_string());
|
||||||
team.player_suffix = new_suffix.clone();
|
team.player_suffix = new_suffix.clone();
|
||||||
let team_display_name = team.display_name.clone();
|
let team_display_name = team.display_name.clone();
|
||||||
scoreboard.update_team(world, team);
|
scoreboard.update_team(world, team).await;
|
||||||
|
|
||||||
context
|
context
|
||||||
.source
|
.source
|
||||||
@ -703,7 +709,7 @@ impl CommandExecutor for TeamModifyFriendlyFireExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let team_display_name = team.display_name.clone();
|
let team_display_name = team.display_name.clone();
|
||||||
scoreboard.update_team(world, team);
|
scoreboard.update_team(world, team).await;
|
||||||
|
|
||||||
let key = if value {
|
let key = if value {
|
||||||
translation::java::COMMANDS_TEAM_OPTION_FRIENDLYFIRE_ENABLED
|
translation::java::COMMANDS_TEAM_OPTION_FRIENDLYFIRE_ENABLED
|
||||||
@ -761,7 +767,7 @@ impl CommandExecutor for TeamModifySeeFriendlyInvisiblesExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let team_display_name = team.display_name.clone();
|
let team_display_name = team.display_name.clone();
|
||||||
scoreboard.update_team(world, team);
|
scoreboard.update_team(world, team).await;
|
||||||
|
|
||||||
let key = if value {
|
let key = if value {
|
||||||
translation::java::COMMANDS_TEAM_OPTION_SEEFRIENDLYINVISIBLES_ENABLED
|
translation::java::COMMANDS_TEAM_OPTION_SEEFRIENDLYINVISIBLES_ENABLED
|
||||||
@ -829,7 +835,7 @@ impl CommandExecutor for TeamModifyNametagVisibilityExecutor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let team_display_name = team.display_name.clone();
|
let team_display_name = team.display_name.clone();
|
||||||
scoreboard.update_team(world, team);
|
scoreboard.update_team(world, team).await;
|
||||||
|
|
||||||
context
|
context
|
||||||
.source
|
.source
|
||||||
@ -931,7 +937,7 @@ impl CommandExecutor for TeamModifyCollisionRuleExecutor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let team_display_name = team.display_name.clone();
|
let team_display_name = team.display_name.clone();
|
||||||
scoreboard.update_team(world, team);
|
scoreboard.update_team(world, team).await;
|
||||||
|
|
||||||
context
|
context
|
||||||
.source
|
.source
|
||||||
|
|||||||
@ -69,8 +69,8 @@ impl CommandExecutor for SimpleTriggerExecutor {
|
|||||||
let new_value = current_value + 1;
|
let new_value = current_value + 1;
|
||||||
|
|
||||||
let updated_score = ScoreboardScore {
|
let updated_score = ScoreboardScore {
|
||||||
entity_name: Box::leak(player_name.clone().into_boxed_str()),
|
entity_name: player_name.clone(),
|
||||||
objective_name: Box::leak(objective_name.to_string().into_boxed_str()),
|
objective_name: objective_name.to_string(),
|
||||||
value: VarInt(new_value),
|
value: VarInt(new_value),
|
||||||
display_name: None,
|
display_name: None,
|
||||||
number_format: None,
|
number_format: None,
|
||||||
@ -139,8 +139,8 @@ impl CommandExecutor for AddTriggerExecutor {
|
|||||||
let new_value = current_value + value;
|
let new_value = current_value + value;
|
||||||
|
|
||||||
let updated_score = ScoreboardScore {
|
let updated_score = ScoreboardScore {
|
||||||
entity_name: Box::leak(player_name.clone().into_boxed_str()),
|
entity_name: player_name.clone(),
|
||||||
objective_name: Box::leak(objective_name.to_string().into_boxed_str()),
|
objective_name: objective_name.to_string(),
|
||||||
value: VarInt(new_value),
|
value: VarInt(new_value),
|
||||||
display_name: None,
|
display_name: None,
|
||||||
number_format: None,
|
number_format: None,
|
||||||
@ -204,8 +204,8 @@ impl CommandExecutor for SetTriggerExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let updated_score = ScoreboardScore {
|
let updated_score = ScoreboardScore {
|
||||||
entity_name: Box::leak(player_name.clone().into_boxed_str()),
|
entity_name: player_name.clone(),
|
||||||
objective_name: Box::leak(objective_name.to_string().into_boxed_str()),
|
objective_name: objective_name.to_string(),
|
||||||
value: VarInt(value),
|
value: VarInt(value),
|
||||||
display_name: None,
|
display_name: None,
|
||||||
number_format: None,
|
number_format: None,
|
||||||
|
|||||||
@ -10,6 +10,8 @@ use std::sync::atomic::{AtomicBool, AtomicI8, AtomicI32, AtomicU8, AtomicU32, Or
|
|||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use crate::world::scoreboard::{BedrockScoreboard, Scoreboard};
|
||||||
|
use advancement::PlayerAdvancement;
|
||||||
use arc_swap::ArcSwap;
|
use arc_swap::ArcSwap;
|
||||||
use crossbeam::atomic::AtomicCell;
|
use crossbeam::atomic::AtomicCell;
|
||||||
use crossbeam::channel::Receiver;
|
use crossbeam::channel::Receiver;
|
||||||
@ -37,7 +39,162 @@ use tokio::task::JoinHandle;
|
|||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use advancement::PlayerAdvancement;
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum CustomScoreboard {
|
||||||
|
Java(Scoreboard),
|
||||||
|
Bedrock(BedrockScoreboard),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Scoreboard> for CustomScoreboard {
|
||||||
|
fn from(sb: Scoreboard) -> Self {
|
||||||
|
Self::Java(sb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BedrockScoreboard> for CustomScoreboard {
|
||||||
|
fn from(sb: BedrockScoreboard) -> Self {
|
||||||
|
Self::Bedrock(sb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct JavaPlayer<'a>(pub &'a Player);
|
||||||
|
|
||||||
|
impl JavaPlayer<'_> {
|
||||||
|
pub async fn send_packet<C: pumpkin_protocol::ClientPacket + Sync>(&self, packet: &C) {
|
||||||
|
if let ClientPlatform::Java(client) = self.0.client.as_ref() {
|
||||||
|
client.enqueue_packet(packet).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_custom_payload(&self, channel: &str, data: &[u8]) {
|
||||||
|
if let ClientPlatform::Java(java) = self.0.client.as_ref() {
|
||||||
|
java.enqueue_packet(&CCustomPayload::new(channel, data))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_stats(&self) {
|
||||||
|
self.0.send_stats().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_scoreboard(&self, scoreboard: Option<Scoreboard>) {
|
||||||
|
*self.0.custom_scoreboard.lock().await = scoreboard.map(CustomScoreboard::Java);
|
||||||
|
self.0.send_scoreboard().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn reset_scoreboard(&self) {
|
||||||
|
self.set_scoreboard(None).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_scoreboard(&self) -> Option<Scoreboard> {
|
||||||
|
let guard = self.0.custom_scoreboard.lock().await;
|
||||||
|
if let Some(CustomScoreboard::Java(sb)) = guard.as_ref() {
|
||||||
|
Some(sb.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct BedrockPlayer<'a>(pub &'a Player);
|
||||||
|
|
||||||
|
impl BedrockPlayer<'_> {
|
||||||
|
pub async fn send_packet<P: pumpkin_protocol::BClientPacket + Sync>(&self, packet: &P) {
|
||||||
|
if let ClientPlatform::Bedrock(client) = self.0.client.as_ref() {
|
||||||
|
client.send_game_packet(packet).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_scoreboard(&self, scoreboard: Option<BedrockScoreboard>) {
|
||||||
|
*self.0.custom_scoreboard.lock().await = scoreboard.map(CustomScoreboard::Bedrock);
|
||||||
|
self.0.send_scoreboard().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn reset_scoreboard(&self) {
|
||||||
|
self.set_scoreboard(None).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_scoreboard(&self) -> Option<BedrockScoreboard> {
|
||||||
|
let guard = self.0.custom_scoreboard.lock().await;
|
||||||
|
if let Some(CustomScoreboard::Bedrock(sb)) = guard.as_ref() {
|
||||||
|
Some(sb.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn client_data(&self) -> Option<Arc<pumpkin_protocol::bedrock::server::login::ClientData>> {
|
||||||
|
if let ClientPlatform::Bedrock(client) = self.0.client.as_ref() {
|
||||||
|
let data = client.client_data.load();
|
||||||
|
(**data).clone()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn device_os(&self) -> Option<i32> {
|
||||||
|
self.client_data().map(|d| d.device_os)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn device_id(&self) -> Option<String> {
|
||||||
|
self.client_data().map(|d| d.device_id.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn device_model(&self) -> Option<String> {
|
||||||
|
self.client_data().map(|d| d.device_model.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn game_version(&self) -> Option<String> {
|
||||||
|
self.client_data().map(|d| d.game_version.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn language_code(&self) -> Option<String> {
|
||||||
|
self.client_data().map(|d| d.language_code.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn current_input_mode(&self) -> Option<i32> {
|
||||||
|
self.client_data().map(|d| d.current_input_mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn default_input_mode(&self) -> Option<i32> {
|
||||||
|
self.client_data().map(|d| d.default_input_mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn ui_profile(&self) -> Option<i32> {
|
||||||
|
self.client_data().map(|d| d.ui_profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn gui_scale(&self) -> Option<i32> {
|
||||||
|
self.client_data().map(|d| d.gui_scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn max_view_distance(&self) -> Option<i32> {
|
||||||
|
self.client_data().map(|d| d.max_view_distance)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn memory_tier(&self) -> Option<i32> {
|
||||||
|
self.client_data().map(|d| d.memory_tier)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn graphics_mode(&self) -> Option<i32> {
|
||||||
|
self.client_data().map(|d| d.graphics_mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
use pumpkin_data::attributes::Attributes;
|
use pumpkin_data::attributes::Attributes;
|
||||||
use pumpkin_data::block_properties::{BlockProperties, HorizontalFacing};
|
use pumpkin_data::block_properties::{BlockProperties, HorizontalFacing};
|
||||||
use pumpkin_data::damage::DamageType;
|
use pumpkin_data::damage::DamageType;
|
||||||
@ -594,6 +751,7 @@ pub struct Player {
|
|||||||
pub tab_list_listed: AtomicBool,
|
pub tab_list_listed: AtomicBool,
|
||||||
pub per_player_time: AtomicCell<Option<(u64, bool)>>,
|
pub per_player_time: AtomicCell<Option<(u64, bool)>>,
|
||||||
pub per_player_weather: AtomicCell<Option<PlayerWeather>>,
|
pub per_player_weather: AtomicCell<Option<PlayerWeather>>,
|
||||||
|
pub custom_scoreboard: Mutex<Option<CustomScoreboard>>,
|
||||||
pub compass_target: AtomicCell<Option<pumpkin_util::math::position::BlockPos>>,
|
pub compass_target: AtomicCell<Option<pumpkin_util::math::position::BlockPos>>,
|
||||||
pub respawn_location: AtomicCell<Option<pumpkin_util::math::position::BlockPos>>,
|
pub respawn_location: AtomicCell<Option<pumpkin_util::math::position::BlockPos>>,
|
||||||
pub hidden_players: Mutex<std::collections::HashSet<uuid::Uuid>>,
|
pub hidden_players: Mutex<std::collections::HashSet<uuid::Uuid>>,
|
||||||
@ -853,6 +1011,7 @@ impl Player {
|
|||||||
tab_list_listed: AtomicBool::new(true),
|
tab_list_listed: AtomicBool::new(true),
|
||||||
per_player_time: AtomicCell::new(None),
|
per_player_time: AtomicCell::new(None),
|
||||||
per_player_weather: AtomicCell::new(None),
|
per_player_weather: AtomicCell::new(None),
|
||||||
|
custom_scoreboard: Mutex::new(None),
|
||||||
compass_target: AtomicCell::new(None),
|
compass_target: AtomicCell::new(None),
|
||||||
respawn_location: AtomicCell::new(None),
|
respawn_location: AtomicCell::new(None),
|
||||||
hidden_players: Mutex::new(std::collections::HashSet::new()),
|
hidden_players: Mutex::new(std::collections::HashSet::new()),
|
||||||
@ -2760,14 +2919,16 @@ impl Player {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_player_time(&self, world: &World, time: u64, relative: bool) {
|
pub async fn set_player_time(&self, time: u64, relative: bool) {
|
||||||
|
let world = self.world();
|
||||||
self.per_player_time.store(Some((time, relative)));
|
self.per_player_time.store(Some((time, relative)));
|
||||||
self.send_time(world).await;
|
self.send_time(&world).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn reset_player_time(&self, world: &World) {
|
pub async fn reset_player_time(&self) {
|
||||||
|
let world = self.world();
|
||||||
self.per_player_time.store(None);
|
self.per_player_time.store(None);
|
||||||
self.send_time(world).await;
|
self.send_time(&world).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_player_time(&self) -> Option<u64> {
|
pub fn get_player_time(&self) -> Option<u64> {
|
||||||
@ -2790,6 +2951,66 @@ impl Player {
|
|||||||
self.per_player_weather.load()
|
self.per_player_weather.load()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn send_editioned<
|
||||||
|
J: pumpkin_protocol::ClientPacket + Sync,
|
||||||
|
B: pumpkin_protocol::BClientPacket + Sync,
|
||||||
|
>(
|
||||||
|
&self,
|
||||||
|
je_packet: &J,
|
||||||
|
be_packet: &B,
|
||||||
|
) {
|
||||||
|
match self.client.as_ref() {
|
||||||
|
ClientPlatform::Java(client) => client.enqueue_packet(je_packet).await,
|
||||||
|
ClientPlatform::Bedrock(client) => client.enqueue_packet(be_packet).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_client_packet<C: pumpkin_protocol::ClientPacket + Sync>(&self, packet: &C) {
|
||||||
|
if let ClientPlatform::Java(client) = self.client.as_ref() {
|
||||||
|
client.enqueue_packet(packet).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_java(&self) -> Option<JavaPlayer<'_>> {
|
||||||
|
matches!(self.client.as_ref(), ClientPlatform::Java(_)).then(|| JavaPlayer(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_bedrock(&self) -> Option<BedrockPlayer<'_>> {
|
||||||
|
matches!(self.client.as_ref(), ClientPlatform::Bedrock(_)).then(|| BedrockPlayer(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn reset_scoreboard(&self) {
|
||||||
|
*self.custom_scoreboard.lock().await = None;
|
||||||
|
self.send_scoreboard().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_scoreboard(&self) {
|
||||||
|
let guard = self.custom_scoreboard.lock().await;
|
||||||
|
match guard.as_ref() {
|
||||||
|
Some(CustomScoreboard::Java(custom))
|
||||||
|
if matches!(self.client.as_ref(), ClientPlatform::Java(_)) =>
|
||||||
|
{
|
||||||
|
custom.send_to_player(self).await;
|
||||||
|
}
|
||||||
|
Some(CustomScoreboard::Bedrock(custom))
|
||||||
|
if matches!(self.client.as_ref(), ClientPlatform::Bedrock(_)) =>
|
||||||
|
{
|
||||||
|
custom.send_to_player(self).await;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
drop(guard);
|
||||||
|
self.world()
|
||||||
|
.scoreboard
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.send_to_player(self)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn set_compass_target(&self, pos: pumpkin_util::math::position::BlockPos) {
|
pub async fn set_compass_target(&self, pos: pumpkin_util::math::position::BlockPos) {
|
||||||
use pumpkin_protocol::java::client::play::CPlayerSpawnPosition;
|
use pumpkin_protocol::java::client::play::CPlayerSpawnPosition;
|
||||||
self.compass_target.store(Some(pos));
|
self.compass_target.store(Some(pos));
|
||||||
@ -2822,6 +3043,85 @@ impl Player {
|
|||||||
!self.hidden_players.lock().await.contains(other_id)
|
!self.hidden_players.lock().await.contains(other_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn can_see_player(&self, other_id: &uuid::Uuid) -> bool {
|
||||||
|
self.can_see(other_id).await
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Experience & Leveling API ---
|
||||||
|
pub async fn add_experience(self: &Arc<Self>, points: i32) {
|
||||||
|
self.add_experience_points(points).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_levels(&self, levels: i32) {
|
||||||
|
self.add_experience_levels(levels).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_experience_level(&self) -> i32 {
|
||||||
|
self.experience_level.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_experience_progress(&self) -> f32 {
|
||||||
|
self.experience_progress.load()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_total_experience(&self) -> i32 {
|
||||||
|
self.experience_points.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_experience_progress(&self, progress: f32) {
|
||||||
|
let level = self.get_experience_level();
|
||||||
|
let max_points = experience::points_in_level(level);
|
||||||
|
let points = (progress.clamp(0.0, 1.0) * max_points as f32) as i32;
|
||||||
|
self.set_experience(level, progress, points).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_total_experience(&self, points: i32) {
|
||||||
|
self.set_experience_points(points).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Item Cooldown System ---
|
||||||
|
pub async fn set_item_cooldown(&self, item_id: &str, ticks: i32) {
|
||||||
|
self.start_cooldown(item_id.to_string(), ticks).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_item_cooldown(&self, item_id: &str) -> Option<i32> {
|
||||||
|
let cooldowns = self.item_cooldowns.lock().await;
|
||||||
|
if let Some(cooldown) = cooldowns.get(item_id) {
|
||||||
|
let current_tick = self.tick_counter.load(Ordering::Relaxed);
|
||||||
|
let elapsed = current_tick - cooldown.start_tick;
|
||||||
|
if elapsed < cooldown.duration {
|
||||||
|
return Some(cooldown.duration - elapsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn has_item_cooldown(&self, item_id: &str) -> bool {
|
||||||
|
self.is_on_cooldown(item_id).await
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Tab List & Display Names ---
|
||||||
|
pub fn set_tab_list_ping(&self, latency_ms: i32) {
|
||||||
|
self.set_tab_list_latency(latency_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Hunger & Saturation Aliases ---
|
||||||
|
pub fn get_food_saturation(&self) -> f32 {
|
||||||
|
self.get_saturation()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_food_saturation(&self, saturation: f32) {
|
||||||
|
self.set_saturation(saturation).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_food_exhaustion(&self) -> f32 {
|
||||||
|
self.get_exhaustion()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_food_exhaustion(&self, exhaustion: f32) {
|
||||||
|
self.set_exhaustion(exhaustion).await;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_target_block(
|
pub async fn get_target_block(
|
||||||
&self,
|
&self,
|
||||||
world: &Arc<World>,
|
world: &Arc<World>,
|
||||||
@ -3653,14 +3953,6 @@ impl Player {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a custom payload packet to this player (Java edition only).
|
|
||||||
pub async fn send_custom_payload(&self, channel: &str, data: &[u8]) {
|
|
||||||
if let ClientPlatform::Java(java) = self.client.as_ref() {
|
|
||||||
java.enqueue_packet(&CCustomPayload::new(channel, data))
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn drop_item(&self, item_stack: ItemStack) {
|
pub async fn drop_item(&self, item_stack: ItemStack) {
|
||||||
self.increment_stat(
|
self.increment_stat(
|
||||||
statistics::StatisticCategory::Dropped,
|
statistics::StatisticCategory::Dropped,
|
||||||
|
|||||||
@ -45,7 +45,15 @@ pub type EntityResource = WasmResource<Arc<dyn EntityBase>>;
|
|||||||
pub type WorldResource = WasmResource<Arc<World>>;
|
pub type WorldResource = WasmResource<Arc<World>>;
|
||||||
pub type ChunkResource = WasmResource<(Arc<World>, Weak<pumpkin_world::chunk::ChunkData>)>;
|
pub type ChunkResource = WasmResource<(Arc<World>, Weak<pumpkin_world::chunk::ChunkData>)>;
|
||||||
pub type WorldBorderResource = WasmResource<Arc<World>>;
|
pub type WorldBorderResource = WasmResource<Arc<World>>;
|
||||||
pub type ScoreboardResource = WasmResource<Arc<World>>;
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum ScoreboardProvider {
|
||||||
|
World(Arc<World>),
|
||||||
|
Player(Arc<Player>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ScoreboardResource = WasmResource<ScoreboardProvider>;
|
||||||
|
pub type BedrockScoreboardResource = WasmResource<Arc<Player>>;
|
||||||
pub type GuiResource = WasmResource<Arc<Mutex<PluginGui>>>;
|
pub type GuiResource = WasmResource<Arc<Mutex<PluginGui>>>;
|
||||||
pub type BossBarResource = WasmResource<
|
pub type BossBarResource = WasmResource<
|
||||||
Arc<Mutex<crate::plugin::loader::wasm::wasm_host::wit::v0_1::boss_bar::PluginBossBar>>,
|
Arc<Mutex<crate::plugin::loader::wasm::wasm_host::wit::v0_1::boss_bar::PluginBossBar>>,
|
||||||
@ -176,12 +184,22 @@ impl PluginHostState {
|
|||||||
|
|
||||||
pub fn add_scoreboard<T>(
|
pub fn add_scoreboard<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
provider: Arc<World>,
|
provider: ScoreboardProvider,
|
||||||
) -> wasmtime::Result<wasmtime::component::Resource<T>> {
|
) -> wasmtime::Result<wasmtime::component::Resource<T>> {
|
||||||
let resource = self.resource_table.push(ScoreboardResource { provider })?;
|
let resource = self.resource_table.push(ScoreboardResource { provider })?;
|
||||||
Ok(wasmtime::component::Resource::new_own(resource.rep()))
|
Ok(wasmtime::component::Resource::new_own(resource.rep()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_bedrock_scoreboard<T>(
|
||||||
|
&mut self,
|
||||||
|
provider: Arc<Player>,
|
||||||
|
) -> wasmtime::Result<wasmtime::component::Resource<T>> {
|
||||||
|
let resource = self
|
||||||
|
.resource_table
|
||||||
|
.push(BedrockScoreboardResource { provider })?;
|
||||||
|
Ok(wasmtime::component::Resource::new_own(resource.rep()))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_gui<T>(
|
pub fn add_gui<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
provider: Arc<Mutex<PluginGui>>,
|
provider: Arc<Mutex<PluginGui>>,
|
||||||
|
|||||||
@ -1059,21 +1059,13 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
|||||||
relative: bool,
|
relative: bool,
|
||||||
) -> wasmtime::Result<()> {
|
) -> wasmtime::Result<()> {
|
||||||
let player = player_from_resource(self, &player)?;
|
let player = player_from_resource(self, &player)?;
|
||||||
player
|
player.set_player_time(time, relative).await;
|
||||||
.set_player_time(
|
|
||||||
&player.living_entity.entity.world.load_full(),
|
|
||||||
time,
|
|
||||||
relative,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn reset_player_time(&mut self, player: Resource<Player>) -> wasmtime::Result<()> {
|
async fn reset_player_time(&mut self, player: Resource<Player>) -> wasmtime::Result<()> {
|
||||||
let player = player_from_resource(self, &player)?;
|
let player = player_from_resource(self, &player)?;
|
||||||
player
|
player.reset_player_time().await;
|
||||||
.reset_player_time(&player.living_entity.entity.world.load_full())
|
|
||||||
.await;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1222,6 +1214,55 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
|||||||
Ok(player.can_see(&other.gameprofile.id).await)
|
Ok(player.can_see(&other.gameprofile.id).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn can_see_player(
|
||||||
|
&mut self,
|
||||||
|
player: Resource<Player>,
|
||||||
|
other: Resource<Player>,
|
||||||
|
) -> wasmtime::Result<bool> {
|
||||||
|
let player = player_from_resource(self, &player)?;
|
||||||
|
let other = player_from_resource(self, &other)?;
|
||||||
|
Ok(player.can_see(&other.gameprofile.id).await)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_tab_list_ping(
|
||||||
|
&mut self,
|
||||||
|
player: Resource<Player>,
|
||||||
|
latency_ms: i32,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let player = player_from_resource(self, &player)?;
|
||||||
|
player.set_tab_list_ping(latency_ms);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_item_cooldown(
|
||||||
|
&mut self,
|
||||||
|
player: Resource<Player>,
|
||||||
|
item_id: String,
|
||||||
|
ticks: i32,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let player = player_from_resource(self, &player)?;
|
||||||
|
player.set_item_cooldown(&item_id, ticks).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_item_cooldown(
|
||||||
|
&mut self,
|
||||||
|
player: Resource<Player>,
|
||||||
|
item_id: String,
|
||||||
|
) -> wasmtime::Result<Option<i32>> {
|
||||||
|
let player = player_from_resource(self, &player)?;
|
||||||
|
Ok(player.get_item_cooldown(&item_id).await)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn has_item_cooldown(
|
||||||
|
&mut self,
|
||||||
|
player: Resource<Player>,
|
||||||
|
item_id: String,
|
||||||
|
) -> wasmtime::Result<bool> {
|
||||||
|
let player = player_from_resource(self, &player)?;
|
||||||
|
Ok(player.has_item_cooldown(&item_id).await)
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_target_block(
|
async fn get_target_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
player: Resource<Player>,
|
player: Resource<Player>,
|
||||||
@ -2198,6 +2239,39 @@ impl pumpkin::plugin::player::HostJavaPlayer for PluginHostState {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_scoreboard(
|
||||||
|
&mut self,
|
||||||
|
player_res: Resource<pumpkin::plugin::player::JavaPlayer>,
|
||||||
|
) -> wasmtime::Result<Resource<pumpkin::plugin::scoreboard::Scoreboard>> {
|
||||||
|
let player = self
|
||||||
|
.resource_table
|
||||||
|
.get::<crate::plugin::loader::wasm::wasm_host::state::JavaPlayerResource>(
|
||||||
|
&Resource::new_own(player_res.rep()),
|
||||||
|
)
|
||||||
|
.map_err(|_| wasmtime::Error::msg("invalid java-player resource handle"))?
|
||||||
|
.provider
|
||||||
|
.clone();
|
||||||
|
self.add_scoreboard(
|
||||||
|
crate::plugin::loader::wasm::wasm_host::state::ScoreboardProvider::Player(player),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn reset_scoreboard(
|
||||||
|
&mut self,
|
||||||
|
player_res: Resource<pumpkin::plugin::player::JavaPlayer>,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let player = self
|
||||||
|
.resource_table
|
||||||
|
.get::<crate::plugin::loader::wasm::wasm_host::state::JavaPlayerResource>(
|
||||||
|
&Resource::new_own(player_res.rep()),
|
||||||
|
)
|
||||||
|
.map_err(|_| wasmtime::Error::msg("invalid java-player resource handle"))?
|
||||||
|
.provider
|
||||||
|
.clone();
|
||||||
|
player.reset_scoreboard().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn drop(
|
async fn drop(
|
||||||
&mut self,
|
&mut self,
|
||||||
rep: Resource<pumpkin::plugin::player::JavaPlayer>,
|
rep: Resource<pumpkin::plugin::player::JavaPlayer>,
|
||||||
@ -2543,6 +2617,37 @@ impl pumpkin::plugin::player::HostBedrockPlayer for PluginHostState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_scoreboard(
|
||||||
|
&mut self,
|
||||||
|
player_res: Resource<pumpkin::plugin::player::BedrockPlayer>,
|
||||||
|
) -> wasmtime::Result<Resource<pumpkin::plugin::scoreboard::BedrockScoreboard>> {
|
||||||
|
let player = self
|
||||||
|
.resource_table
|
||||||
|
.get::<crate::plugin::loader::wasm::wasm_host::state::BedrockPlayerResource>(
|
||||||
|
&Resource::new_own(player_res.rep()),
|
||||||
|
)
|
||||||
|
.map_err(|_| wasmtime::Error::msg("invalid bedrock-player resource handle"))?
|
||||||
|
.provider
|
||||||
|
.clone();
|
||||||
|
self.add_bedrock_scoreboard(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn reset_scoreboard(
|
||||||
|
&mut self,
|
||||||
|
player_res: Resource<pumpkin::plugin::player::BedrockPlayer>,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let player = self
|
||||||
|
.resource_table
|
||||||
|
.get::<crate::plugin::loader::wasm::wasm_host::state::BedrockPlayerResource>(
|
||||||
|
&Resource::new_own(player_res.rep()),
|
||||||
|
)
|
||||||
|
.map_err(|_| wasmtime::Error::msg("invalid bedrock-player resource handle"))?
|
||||||
|
.provider
|
||||||
|
.clone();
|
||||||
|
player.reset_scoreboard().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn drop(
|
async fn drop(
|
||||||
&mut self,
|
&mut self,
|
||||||
rep: Resource<pumpkin::plugin::player::BedrockPlayer>,
|
rep: Resource<pumpkin::plugin::player::BedrockPlayer>,
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
use wasmtime::component::Resource;
|
use wasmtime::component::Resource;
|
||||||
|
|
||||||
use crate::plugin::loader::wasm::wasm_host::{
|
use crate::plugin::loader::wasm::wasm_host::{
|
||||||
state::{PluginHostState, ScoreboardResource},
|
state::{BedrockScoreboardResource, PluginHostState, ScoreboardProvider, ScoreboardResource},
|
||||||
wit::v0_1::pumpkin::{
|
wit::v0_1::pumpkin::{
|
||||||
self,
|
self,
|
||||||
plugin::scoreboard::{
|
plugin::scoreboard::{
|
||||||
self, CollisionRule, DisplaySlot, NametagVisibility, RenderType, TeamSettings,
|
self, CollisionRule, DisplaySlot, HostBedrockScoreboard, NametagVisibility, RenderType,
|
||||||
|
TeamSettings,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -21,6 +22,15 @@ impl PluginHostState {
|
|||||||
.get::<ScoreboardResource>(&Resource::new_own(res.rep()))
|
.get::<ScoreboardResource>(&Resource::new_own(res.rep()))
|
||||||
.map_err(wasmtime::Error::from)
|
.map_err(wasmtime::Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_bedrock_scoreboard_res(
|
||||||
|
&self,
|
||||||
|
res: &Resource<scoreboard::BedrockScoreboard>,
|
||||||
|
) -> wasmtime::Result<&BedrockScoreboardResource> {
|
||||||
|
self.resource_table
|
||||||
|
.get::<BedrockScoreboardResource>(&Resource::new_own(res.rep()))
|
||||||
|
.map_err(wasmtime::Error::from)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl scoreboard::Host for PluginHostState {}
|
impl scoreboard::Host for PluginHostState {}
|
||||||
@ -33,7 +43,7 @@ impl scoreboard::HostScoreboard for PluginHostState {
|
|||||||
display_name: Resource<pumpkin::plugin::text::TextComponent>,
|
display_name: Resource<pumpkin::plugin::text::TextComponent>,
|
||||||
render_type: RenderType,
|
render_type: RenderType,
|
||||||
) -> wasmtime::Result<()> {
|
) -> wasmtime::Result<()> {
|
||||||
let world = self.get_scoreboard_res(&res)?.provider.clone();
|
let provider = self.get_scoreboard_res(&res)?.provider.clone();
|
||||||
let display_name = self.get_text_provider(&display_name)?;
|
let display_name = self.get_text_provider(&display_name)?;
|
||||||
|
|
||||||
let rt = match render_type {
|
let rt = match render_type {
|
||||||
@ -41,19 +51,80 @@ impl scoreboard::HostScoreboard for PluginHostState {
|
|||||||
RenderType::Hearts => pumpkin_protocol::java::client::play::RenderType::Hearts,
|
RenderType::Hearts => pumpkin_protocol::java::client::play::RenderType::Hearts,
|
||||||
};
|
};
|
||||||
|
|
||||||
let objective = ScoreboardObjective::new(
|
let objective = ScoreboardObjective::new(name, display_name, rt, None, "dummy");
|
||||||
Box::leak(name.into_boxed_str()),
|
|
||||||
display_name,
|
match provider {
|
||||||
rt,
|
ScoreboardProvider::World(world) => {
|
||||||
None,
|
|
||||||
"dummy",
|
|
||||||
);
|
|
||||||
world
|
world
|
||||||
.scoreboard
|
.scoreboard
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.add_objective(&world, objective)
|
.add_objective(world.as_ref(), objective)
|
||||||
.await;
|
.await;
|
||||||
|
}
|
||||||
|
ScoreboardProvider::Player(player) => {
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if !matches!(
|
||||||
|
*custom_guard,
|
||||||
|
Some(crate::entity::player::CustomScoreboard::Java(_))
|
||||||
|
) {
|
||||||
|
*custom_guard = Some(crate::entity::player::CustomScoreboard::Java(
|
||||||
|
crate::world::scoreboard::Scoreboard::default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
|
||||||
|
custom_guard.as_mut()
|
||||||
|
{
|
||||||
|
sb.add_objective(player.as_ref(), objective).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_objective(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::Scoreboard>,
|
||||||
|
name: String,
|
||||||
|
display_name: Resource<pumpkin::plugin::text::TextComponent>,
|
||||||
|
render_type: RenderType,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let provider = self.get_scoreboard_res(&res)?.provider.clone();
|
||||||
|
let display_name = self.get_text_provider(&display_name)?;
|
||||||
|
|
||||||
|
let rt = match render_type {
|
||||||
|
RenderType::Integer => pumpkin_protocol::java::client::play::RenderType::Integer,
|
||||||
|
RenderType::Hearts => pumpkin_protocol::java::client::play::RenderType::Hearts,
|
||||||
|
};
|
||||||
|
|
||||||
|
let objective = ScoreboardObjective::new(name, display_name, rt, None, "dummy");
|
||||||
|
|
||||||
|
match provider {
|
||||||
|
ScoreboardProvider::World(world) => {
|
||||||
|
world
|
||||||
|
.scoreboard
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.update_objective(world.as_ref(), objective)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ScoreboardProvider::Player(player) => {
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if !matches!(
|
||||||
|
*custom_guard,
|
||||||
|
Some(crate::entity::player::CustomScoreboard::Java(_))
|
||||||
|
) {
|
||||||
|
*custom_guard = Some(crate::entity::player::CustomScoreboard::Java(
|
||||||
|
crate::world::scoreboard::Scoreboard::default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
|
||||||
|
custom_guard.as_mut()
|
||||||
|
{
|
||||||
|
sb.update_objective(player.as_ref(), objective).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,13 +133,25 @@ impl scoreboard::HostScoreboard for PluginHostState {
|
|||||||
res: Resource<scoreboard::Scoreboard>,
|
res: Resource<scoreboard::Scoreboard>,
|
||||||
name: String,
|
name: String,
|
||||||
) -> wasmtime::Result<()> {
|
) -> wasmtime::Result<()> {
|
||||||
let world = self.get_scoreboard_res(&res)?.provider.clone();
|
let provider = self.get_scoreboard_res(&res)?.provider.clone();
|
||||||
|
match provider {
|
||||||
|
ScoreboardProvider::World(world) => {
|
||||||
world
|
world
|
||||||
.scoreboard
|
.scoreboard
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.remove_objective(&world, &name)
|
.remove_objective(world.as_ref(), &name)
|
||||||
.await;
|
.await;
|
||||||
|
}
|
||||||
|
ScoreboardProvider::Player(player) => {
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
|
||||||
|
custom_guard.as_mut()
|
||||||
|
{
|
||||||
|
sb.remove_objective(player.as_ref(), &name).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,14 +161,393 @@ impl scoreboard::HostScoreboard for PluginHostState {
|
|||||||
slot: DisplaySlot,
|
slot: DisplaySlot,
|
||||||
objective_name: String,
|
objective_name: String,
|
||||||
) -> wasmtime::Result<()> {
|
) -> wasmtime::Result<()> {
|
||||||
let world = self.get_scoreboard_res(&res)?.provider.clone();
|
let provider = self.get_scoreboard_res(&res)?.provider.clone();
|
||||||
let slot = match slot {
|
let slot = map_display_slot(slot);
|
||||||
|
|
||||||
|
match provider {
|
||||||
|
ScoreboardProvider::World(world) => {
|
||||||
|
world
|
||||||
|
.scoreboard
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.set_display_objective(world.as_ref(), slot, Some(&objective_name))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ScoreboardProvider::Player(player) => {
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if !matches!(
|
||||||
|
*custom_guard,
|
||||||
|
Some(crate::entity::player::CustomScoreboard::Java(_))
|
||||||
|
) {
|
||||||
|
*custom_guard = Some(crate::entity::player::CustomScoreboard::Java(
|
||||||
|
crate::world::scoreboard::Scoreboard::default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
|
||||||
|
custom_guard.as_mut()
|
||||||
|
{
|
||||||
|
sb.set_display_objective(player.as_ref(), slot, Some(&objective_name))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn clear_display_slot(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::Scoreboard>,
|
||||||
|
slot: DisplaySlot,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let provider = self.get_scoreboard_res(&res)?.provider.clone();
|
||||||
|
let slot = map_display_slot(slot);
|
||||||
|
|
||||||
|
match provider {
|
||||||
|
ScoreboardProvider::World(world) => {
|
||||||
|
world
|
||||||
|
.scoreboard
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.clear_display_objective(world.as_ref(), slot)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ScoreboardProvider::Player(player) => {
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
|
||||||
|
custom_guard.as_mut()
|
||||||
|
{
|
||||||
|
sb.clear_display_objective(player.as_ref(), slot).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_score(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::Scoreboard>,
|
||||||
|
entity_name: String,
|
||||||
|
objective_name: String,
|
||||||
|
value: i32,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let provider = self.get_scoreboard_res(&res)?.provider.clone();
|
||||||
|
let score = ScoreboardScore::new(entity_name, objective_name, VarInt(value), None, None);
|
||||||
|
match provider {
|
||||||
|
ScoreboardProvider::World(world) => {
|
||||||
|
world
|
||||||
|
.scoreboard
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.update_score(world.as_ref(), score)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ScoreboardProvider::Player(player) => {
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if !matches!(
|
||||||
|
*custom_guard,
|
||||||
|
Some(crate::entity::player::CustomScoreboard::Java(_))
|
||||||
|
) {
|
||||||
|
*custom_guard = Some(crate::entity::player::CustomScoreboard::Java(
|
||||||
|
crate::world::scoreboard::Scoreboard::default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
|
||||||
|
custom_guard.as_mut()
|
||||||
|
{
|
||||||
|
sb.update_score(player.as_ref(), score).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add_score(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::Scoreboard>,
|
||||||
|
entity_name: String,
|
||||||
|
objective_name: String,
|
||||||
|
delta: i32,
|
||||||
|
) -> wasmtime::Result<i32> {
|
||||||
|
let provider = self.get_scoreboard_res(&res)?.provider.clone();
|
||||||
|
let new_val = match provider {
|
||||||
|
ScoreboardProvider::World(world) => {
|
||||||
|
world
|
||||||
|
.scoreboard
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.add_score(world.as_ref(), entity_name, objective_name, delta)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
ScoreboardProvider::Player(player) => {
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if !matches!(
|
||||||
|
*custom_guard,
|
||||||
|
Some(crate::entity::player::CustomScoreboard::Java(_))
|
||||||
|
) {
|
||||||
|
*custom_guard = Some(crate::entity::player::CustomScoreboard::Java(
|
||||||
|
crate::world::scoreboard::Scoreboard::default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let Some(crate::entity::player::CustomScoreboard::Java(sb)) = custom_guard.as_mut()
|
||||||
|
else {
|
||||||
|
return Err(wasmtime::Error::msg("Invalid scoreboard state"));
|
||||||
|
};
|
||||||
|
sb.add_score(player.as_ref(), entity_name, objective_name, delta)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(new_val)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove_score(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::Scoreboard>,
|
||||||
|
entity_name: String,
|
||||||
|
objective_name: String,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let provider = self.get_scoreboard_res(&res)?.provider.clone();
|
||||||
|
match provider {
|
||||||
|
ScoreboardProvider::World(world) => {
|
||||||
|
world
|
||||||
|
.scoreboard
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.remove_score(world.as_ref(), &entity_name, &objective_name)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ScoreboardProvider::Player(player) => {
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
|
||||||
|
custom_guard.as_mut()
|
||||||
|
{
|
||||||
|
sb.remove_score(player.as_ref(), &entity_name, &objective_name)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn reset_entity_scores(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::Scoreboard>,
|
||||||
|
entity_name: String,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let provider = self.get_scoreboard_res(&res)?.provider.clone();
|
||||||
|
match provider {
|
||||||
|
ScoreboardProvider::World(world) => {
|
||||||
|
world
|
||||||
|
.scoreboard
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.reset_scores_for_entity(world.as_ref(), &entity_name)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ScoreboardProvider::Player(player) => {
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
|
||||||
|
custom_guard.as_mut()
|
||||||
|
{
|
||||||
|
sb.reset_scores_for_entity(player.as_ref(), &entity_name)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_team(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::Scoreboard>,
|
||||||
|
name: String,
|
||||||
|
settings: TeamSettings,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let provider = self.get_scoreboard_res(&res)?.provider.clone();
|
||||||
|
let team = map_team_settings(name, &settings, self)?;
|
||||||
|
match provider {
|
||||||
|
ScoreboardProvider::World(world) => {
|
||||||
|
world
|
||||||
|
.scoreboard
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.add_team(world.as_ref(), team)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ScoreboardProvider::Player(player) => {
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if !matches!(
|
||||||
|
*custom_guard,
|
||||||
|
Some(crate::entity::player::CustomScoreboard::Java(_))
|
||||||
|
) {
|
||||||
|
*custom_guard = Some(crate::entity::player::CustomScoreboard::Java(
|
||||||
|
crate::world::scoreboard::Scoreboard::default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
|
||||||
|
custom_guard.as_mut()
|
||||||
|
{
|
||||||
|
sb.add_team(player.as_ref(), team).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove_team(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::Scoreboard>,
|
||||||
|
name: String,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let provider = self.get_scoreboard_res(&res)?.provider.clone();
|
||||||
|
match provider {
|
||||||
|
ScoreboardProvider::World(world) => {
|
||||||
|
world
|
||||||
|
.scoreboard
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.remove_team(world.as_ref(), &name)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ScoreboardProvider::Player(player) => {
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
|
||||||
|
custom_guard.as_mut()
|
||||||
|
{
|
||||||
|
sb.remove_team(player.as_ref(), &name).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_team(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::Scoreboard>,
|
||||||
|
name: String,
|
||||||
|
settings: TeamSettings,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let provider = self.get_scoreboard_res(&res)?.provider.clone();
|
||||||
|
let team = map_team_settings(name, &settings, self)?;
|
||||||
|
match provider {
|
||||||
|
ScoreboardProvider::World(world) => {
|
||||||
|
world
|
||||||
|
.scoreboard
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.update_team(world.as_ref(), team)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ScoreboardProvider::Player(player) => {
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
|
||||||
|
custom_guard.as_mut()
|
||||||
|
{
|
||||||
|
sb.update_team(player.as_ref(), team).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add_player_to_team(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::Scoreboard>,
|
||||||
|
team_name: String,
|
||||||
|
player_name: String,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let provider = self.get_scoreboard_res(&res)?.provider.clone();
|
||||||
|
match provider {
|
||||||
|
ScoreboardProvider::World(world) => {
|
||||||
|
world
|
||||||
|
.scoreboard
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.add_player_to_team(world.as_ref(), &team_name, player_name)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ScoreboardProvider::Player(player) => {
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
|
||||||
|
custom_guard.as_mut()
|
||||||
|
{
|
||||||
|
sb.add_player_to_team(player.as_ref(), &team_name, player_name)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove_player_from_team(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::Scoreboard>,
|
||||||
|
team_name: String,
|
||||||
|
player_name: String,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let provider = self.get_scoreboard_res(&res)?.provider.clone();
|
||||||
|
match provider {
|
||||||
|
ScoreboardProvider::World(world) => {
|
||||||
|
world
|
||||||
|
.scoreboard
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.remove_player_from_team(world.as_ref(), &team_name, &player_name)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ScoreboardProvider::Player(player) => {
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
|
||||||
|
custom_guard.as_mut()
|
||||||
|
{
|
||||||
|
sb.remove_player_from_team(player.as_ref(), &team_name, &player_name)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn clear_team_players(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::Scoreboard>,
|
||||||
|
team_name: String,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let provider = self.get_scoreboard_res(&res)?.provider.clone();
|
||||||
|
match provider {
|
||||||
|
ScoreboardProvider::World(world) => {
|
||||||
|
world
|
||||||
|
.scoreboard
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.clear_team_players(world.as_ref(), &team_name)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ScoreboardProvider::Player(player) => {
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
|
||||||
|
custom_guard.as_mut()
|
||||||
|
{
|
||||||
|
sb.clear_team_players(player.as_ref(), &team_name).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn drop(&mut self, rep: Resource<scoreboard::Scoreboard>) -> wasmtime::Result<()> {
|
||||||
|
self.resource_table
|
||||||
|
.delete::<ScoreboardResource>(Resource::new_own(rep.rep()))
|
||||||
|
.map_err(wasmtime::Error::from)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn map_display_slot(slot: DisplaySlot) -> pumpkin_data::scoreboard::ScoreboardDisplaySlot {
|
||||||
|
match slot {
|
||||||
DisplaySlot::PlayerList => pumpkin_data::scoreboard::ScoreboardDisplaySlot::List,
|
DisplaySlot::PlayerList => pumpkin_data::scoreboard::ScoreboardDisplaySlot::List,
|
||||||
DisplaySlot::Sidebar => pumpkin_data::scoreboard::ScoreboardDisplaySlot::Sidebar,
|
DisplaySlot::Sidebar => pumpkin_data::scoreboard::ScoreboardDisplaySlot::Sidebar,
|
||||||
DisplaySlot::BelowName => pumpkin_data::scoreboard::ScoreboardDisplaySlot::BelowName,
|
DisplaySlot::BelowName => pumpkin_data::scoreboard::ScoreboardDisplaySlot::BelowName,
|
||||||
DisplaySlot::SidebarTeamBlack => {
|
DisplaySlot::SidebarTeamBlack => pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamBlack,
|
||||||
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamBlack
|
|
||||||
}
|
|
||||||
DisplaySlot::SidebarTeamDarkBlue => {
|
DisplaySlot::SidebarTeamDarkBlue => {
|
||||||
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamDarkBlue
|
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamDarkBlue
|
||||||
}
|
}
|
||||||
@ -101,24 +563,14 @@ impl scoreboard::HostScoreboard for PluginHostState {
|
|||||||
DisplaySlot::SidebarTeamDarkPurple => {
|
DisplaySlot::SidebarTeamDarkPurple => {
|
||||||
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamDarkPurple
|
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamDarkPurple
|
||||||
}
|
}
|
||||||
DisplaySlot::SidebarTeamGold => {
|
DisplaySlot::SidebarTeamGold => pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamGold,
|
||||||
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamGold
|
DisplaySlot::SidebarTeamGray => pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamGray,
|
||||||
}
|
|
||||||
DisplaySlot::SidebarTeamGray => {
|
|
||||||
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamGray
|
|
||||||
}
|
|
||||||
DisplaySlot::SidebarTeamDarkGray => {
|
DisplaySlot::SidebarTeamDarkGray => {
|
||||||
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamDarkGray
|
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamDarkGray
|
||||||
}
|
}
|
||||||
DisplaySlot::SidebarTeamBlue => {
|
DisplaySlot::SidebarTeamBlue => pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamBlue,
|
||||||
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamBlue
|
DisplaySlot::SidebarTeamGreen => pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamGreen,
|
||||||
}
|
DisplaySlot::SidebarTeamAqua => pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamAqua,
|
||||||
DisplaySlot::SidebarTeamGreen => {
|
|
||||||
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamGreen
|
|
||||||
}
|
|
||||||
DisplaySlot::SidebarTeamAqua => {
|
|
||||||
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamAqua
|
|
||||||
}
|
|
||||||
DisplaySlot::SidebarTeamRed => pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamRed,
|
DisplaySlot::SidebarTeamRed => pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamRed,
|
||||||
DisplaySlot::SidebarTeamLightPurple => {
|
DisplaySlot::SidebarTeamLightPurple => {
|
||||||
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamLightPurple
|
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamLightPurple
|
||||||
@ -126,129 +578,7 @@ impl scoreboard::HostScoreboard for PluginHostState {
|
|||||||
DisplaySlot::SidebarTeamYellow => {
|
DisplaySlot::SidebarTeamYellow => {
|
||||||
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamYellow
|
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamYellow
|
||||||
}
|
}
|
||||||
DisplaySlot::SidebarTeamWhite => {
|
DisplaySlot::SidebarTeamWhite => pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamWhite,
|
||||||
pumpkin_data::scoreboard::ScoreboardDisplaySlot::TeamWhite
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
world
|
|
||||||
.scoreboard
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.set_display_objective(&world, slot, &objective_name)
|
|
||||||
.await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn update_score(
|
|
||||||
&mut self,
|
|
||||||
res: Resource<scoreboard::Scoreboard>,
|
|
||||||
entity_name: String,
|
|
||||||
objective_name: String,
|
|
||||||
value: i32,
|
|
||||||
) -> wasmtime::Result<()> {
|
|
||||||
let world = self.get_scoreboard_res(&res)?.provider.clone();
|
|
||||||
let score = ScoreboardScore::new(
|
|
||||||
Box::leak(entity_name.into_boxed_str()),
|
|
||||||
Box::leak(objective_name.into_boxed_str()),
|
|
||||||
VarInt(value),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
world
|
|
||||||
.scoreboard
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.update_score(&world, score)
|
|
||||||
.await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn remove_score(
|
|
||||||
&mut self,
|
|
||||||
res: Resource<scoreboard::Scoreboard>,
|
|
||||||
entity_name: String,
|
|
||||||
objective_name: String,
|
|
||||||
) -> wasmtime::Result<()> {
|
|
||||||
let world = self.get_scoreboard_res(&res)?.provider.clone();
|
|
||||||
world
|
|
||||||
.scoreboard
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.remove_score(&world, &entity_name, &objective_name)
|
|
||||||
.await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_team(
|
|
||||||
&mut self,
|
|
||||||
res: Resource<scoreboard::Scoreboard>,
|
|
||||||
name: String,
|
|
||||||
settings: TeamSettings,
|
|
||||||
) -> wasmtime::Result<()> {
|
|
||||||
let world = self.get_scoreboard_res(&res)?.provider.clone();
|
|
||||||
let team = map_team_settings(name, &settings, self)?;
|
|
||||||
world.scoreboard.lock().await.add_team(&world, team);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn remove_team(
|
|
||||||
&mut self,
|
|
||||||
res: Resource<scoreboard::Scoreboard>,
|
|
||||||
name: String,
|
|
||||||
) -> wasmtime::Result<()> {
|
|
||||||
let world = self.get_scoreboard_res(&res)?.provider.clone();
|
|
||||||
world.scoreboard.lock().await.remove_team(&world, &name);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn update_team(
|
|
||||||
&mut self,
|
|
||||||
res: Resource<scoreboard::Scoreboard>,
|
|
||||||
name: String,
|
|
||||||
settings: TeamSettings,
|
|
||||||
) -> wasmtime::Result<()> {
|
|
||||||
let world = self.get_scoreboard_res(&res)?.provider.clone();
|
|
||||||
let team = map_team_settings(name, &settings, self)?;
|
|
||||||
world.scoreboard.lock().await.update_team(&world, team);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_player_to_team(
|
|
||||||
&mut self,
|
|
||||||
res: Resource<scoreboard::Scoreboard>,
|
|
||||||
team_name: String,
|
|
||||||
player_name: String,
|
|
||||||
) -> wasmtime::Result<()> {
|
|
||||||
let world = self.get_scoreboard_res(&res)?.provider.clone();
|
|
||||||
world
|
|
||||||
.scoreboard
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.add_player_to_team(&world, &team_name, player_name);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn remove_player_from_team(
|
|
||||||
&mut self,
|
|
||||||
res: Resource<scoreboard::Scoreboard>,
|
|
||||||
team_name: String,
|
|
||||||
player_name: String,
|
|
||||||
) -> wasmtime::Result<()> {
|
|
||||||
let world = self.get_scoreboard_res(&res)?.provider.clone();
|
|
||||||
world
|
|
||||||
.scoreboard
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.remove_player_from_team(&world, &team_name, &player_name);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn drop(&mut self, rep: Resource<scoreboard::Scoreboard>) -> wasmtime::Result<()> {
|
|
||||||
self.resource_table
|
|
||||||
.delete::<ScoreboardResource>(Resource::new_own(rep.rep()))
|
|
||||||
.map_err(wasmtime::Error::from)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,3 +666,247 @@ const fn map_named_color(
|
|||||||
pumpkin::plugin::common::NamedColor::White => pumpkin_util::text::color::NamedColor::White,
|
pumpkin::plugin::common::NamedColor::White => pumpkin_util::text::color::NamedColor::White,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HostBedrockScoreboard for PluginHostState {
|
||||||
|
async fn add_objective(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::BedrockScoreboard>,
|
||||||
|
name: String,
|
||||||
|
display_name: String,
|
||||||
|
sort_order: scoreboard::BedrockSortOrder,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let player = self.get_bedrock_scoreboard_res(&res)?.provider.clone();
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if !matches!(
|
||||||
|
*custom_guard,
|
||||||
|
Some(crate::entity::player::CustomScoreboard::Bedrock(_))
|
||||||
|
) {
|
||||||
|
*custom_guard = Some(crate::entity::player::CustomScoreboard::Bedrock(
|
||||||
|
crate::world::scoreboard::BedrockScoreboard::default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let Some(crate::entity::player::CustomScoreboard::Bedrock(sb)) = custom_guard.as_mut()
|
||||||
|
else {
|
||||||
|
return Err(wasmtime::Error::msg("Invalid scoreboard state"));
|
||||||
|
};
|
||||||
|
sb.add_objective(
|
||||||
|
player.as_ref(),
|
||||||
|
crate::world::scoreboard::BedrockObjective {
|
||||||
|
name,
|
||||||
|
display_name,
|
||||||
|
sort_order: match sort_order {
|
||||||
|
scoreboard::BedrockSortOrder::Ascending => {
|
||||||
|
crate::world::scoreboard::BedrockSortOrder::Ascending
|
||||||
|
}
|
||||||
|
scoreboard::BedrockSortOrder::Descending => {
|
||||||
|
crate::world::scoreboard::BedrockSortOrder::Descending
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_objective(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::BedrockScoreboard>,
|
||||||
|
name: String,
|
||||||
|
display_name: String,
|
||||||
|
sort_order: scoreboard::BedrockSortOrder,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let player = self.get_bedrock_scoreboard_res(&res)?.provider.clone();
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if !matches!(
|
||||||
|
*custom_guard,
|
||||||
|
Some(crate::entity::player::CustomScoreboard::Bedrock(_))
|
||||||
|
) {
|
||||||
|
*custom_guard = Some(crate::entity::player::CustomScoreboard::Bedrock(
|
||||||
|
crate::world::scoreboard::BedrockScoreboard::default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let Some(crate::entity::player::CustomScoreboard::Bedrock(sb)) = custom_guard.as_mut()
|
||||||
|
else {
|
||||||
|
return Err(wasmtime::Error::msg("Invalid scoreboard state"));
|
||||||
|
};
|
||||||
|
sb.update_objective(
|
||||||
|
player.as_ref(),
|
||||||
|
crate::world::scoreboard::BedrockObjective {
|
||||||
|
name,
|
||||||
|
display_name,
|
||||||
|
sort_order: match sort_order {
|
||||||
|
scoreboard::BedrockSortOrder::Ascending => {
|
||||||
|
crate::world::scoreboard::BedrockSortOrder::Ascending
|
||||||
|
}
|
||||||
|
scoreboard::BedrockSortOrder::Descending => {
|
||||||
|
crate::world::scoreboard::BedrockSortOrder::Descending
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove_objective(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::BedrockScoreboard>,
|
||||||
|
name: String,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let player = self.get_bedrock_scoreboard_res(&res)?.provider.clone();
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Bedrock(sb)) = custom_guard.as_mut() {
|
||||||
|
sb.remove_objective(player.as_ref(), &name).await;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_display_slot(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::BedrockScoreboard>,
|
||||||
|
slot: scoreboard::BedrockDisplaySlot,
|
||||||
|
objective_name: String,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let player = self.get_bedrock_scoreboard_res(&res)?.provider.clone();
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if !matches!(
|
||||||
|
*custom_guard,
|
||||||
|
Some(crate::entity::player::CustomScoreboard::Bedrock(_))
|
||||||
|
) {
|
||||||
|
*custom_guard = Some(crate::entity::player::CustomScoreboard::Bedrock(
|
||||||
|
crate::world::scoreboard::BedrockScoreboard::default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let Some(crate::entity::player::CustomScoreboard::Bedrock(sb)) = custom_guard.as_mut()
|
||||||
|
else {
|
||||||
|
return Err(wasmtime::Error::msg("Invalid scoreboard state"));
|
||||||
|
};
|
||||||
|
let b_slot = match slot {
|
||||||
|
scoreboard::BedrockDisplaySlot::PlayerList => {
|
||||||
|
crate::world::scoreboard::BedrockDisplaySlot::PlayerList
|
||||||
|
}
|
||||||
|
scoreboard::BedrockDisplaySlot::Sidebar => {
|
||||||
|
crate::world::scoreboard::BedrockDisplaySlot::Sidebar
|
||||||
|
}
|
||||||
|
scoreboard::BedrockDisplaySlot::BelowName => {
|
||||||
|
crate::world::scoreboard::BedrockDisplaySlot::BelowName
|
||||||
|
}
|
||||||
|
};
|
||||||
|
sb.set_display_objective(player.as_ref(), b_slot, Some(&objective_name))
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn clear_display_slot(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::BedrockScoreboard>,
|
||||||
|
slot: scoreboard::BedrockDisplaySlot,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let player = self.get_bedrock_scoreboard_res(&res)?.provider.clone();
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Bedrock(sb)) = custom_guard.as_mut() {
|
||||||
|
let b_slot = match slot {
|
||||||
|
scoreboard::BedrockDisplaySlot::PlayerList => {
|
||||||
|
crate::world::scoreboard::BedrockDisplaySlot::PlayerList
|
||||||
|
}
|
||||||
|
scoreboard::BedrockDisplaySlot::Sidebar => {
|
||||||
|
crate::world::scoreboard::BedrockDisplaySlot::Sidebar
|
||||||
|
}
|
||||||
|
scoreboard::BedrockDisplaySlot::BelowName => {
|
||||||
|
crate::world::scoreboard::BedrockDisplaySlot::BelowName
|
||||||
|
}
|
||||||
|
};
|
||||||
|
sb.clear_display_objective(player.as_ref(), b_slot).await;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_score(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::BedrockScoreboard>,
|
||||||
|
entity_name: String,
|
||||||
|
objective_name: String,
|
||||||
|
value: i32,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let player = self.get_bedrock_scoreboard_res(&res)?.provider.clone();
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if !matches!(
|
||||||
|
*custom_guard,
|
||||||
|
Some(crate::entity::player::CustomScoreboard::Bedrock(_))
|
||||||
|
) {
|
||||||
|
*custom_guard = Some(crate::entity::player::CustomScoreboard::Bedrock(
|
||||||
|
crate::world::scoreboard::BedrockScoreboard::default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let Some(crate::entity::player::CustomScoreboard::Bedrock(sb)) = custom_guard.as_mut()
|
||||||
|
else {
|
||||||
|
return Err(wasmtime::Error::msg("Invalid scoreboard state"));
|
||||||
|
};
|
||||||
|
sb.update_score(player.as_ref(), &entity_name, &objective_name, value)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add_score(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::BedrockScoreboard>,
|
||||||
|
entity_name: String,
|
||||||
|
objective_name: String,
|
||||||
|
delta: i32,
|
||||||
|
) -> wasmtime::Result<i32> {
|
||||||
|
let player = self.get_bedrock_scoreboard_res(&res)?.provider.clone();
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if !matches!(
|
||||||
|
*custom_guard,
|
||||||
|
Some(crate::entity::player::CustomScoreboard::Bedrock(_))
|
||||||
|
) {
|
||||||
|
*custom_guard = Some(crate::entity::player::CustomScoreboard::Bedrock(
|
||||||
|
crate::world::scoreboard::BedrockScoreboard::default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let Some(crate::entity::player::CustomScoreboard::Bedrock(sb)) = custom_guard.as_mut()
|
||||||
|
else {
|
||||||
|
return Err(wasmtime::Error::msg("Invalid scoreboard state"));
|
||||||
|
};
|
||||||
|
let new_val = sb
|
||||||
|
.add_score(player.as_ref(), entity_name, objective_name, delta)
|
||||||
|
.await;
|
||||||
|
Ok(new_val)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove_score(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::BedrockScoreboard>,
|
||||||
|
entity_name: String,
|
||||||
|
objective_name: String,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let player = self.get_bedrock_scoreboard_res(&res)?.provider.clone();
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Bedrock(sb)) = custom_guard.as_mut() {
|
||||||
|
sb.remove_score(player.as_ref(), &entity_name, &objective_name)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn reset_entity_scores(
|
||||||
|
&mut self,
|
||||||
|
res: Resource<scoreboard::BedrockScoreboard>,
|
||||||
|
entity_name: String,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let player = self.get_bedrock_scoreboard_res(&res)?.provider.clone();
|
||||||
|
let mut custom_guard = player.custom_scoreboard.lock().await;
|
||||||
|
if let Some(crate::entity::player::CustomScoreboard::Bedrock(sb)) = custom_guard.as_mut() {
|
||||||
|
sb.reset_scores_for_entity(player.as_ref(), &entity_name)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn drop(
|
||||||
|
&mut self,
|
||||||
|
_res: Resource<scoreboard::BedrockScoreboard>,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -495,7 +495,11 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState {
|
|||||||
world: Resource<World>,
|
world: Resource<World>,
|
||||||
) -> wasmtime::Result<Resource<pumpkin::plugin::scoreboard::Scoreboard>> {
|
) -> wasmtime::Result<Resource<pumpkin::plugin::scoreboard::Scoreboard>> {
|
||||||
let world_provider = self.get_world_res(&world)?.provider.clone();
|
let world_provider = self.get_world_res(&world)?.provider.clone();
|
||||||
self.add_scoreboard(world_provider)
|
self.add_scoreboard(
|
||||||
|
crate::plugin::loader::wasm::wasm_host::state::ScoreboardProvider::World(
|
||||||
|
world_provider,
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn play_sound(
|
async fn play_sound(
|
||||||
|
|||||||
@ -3407,6 +3407,9 @@ impl World {
|
|||||||
// Sends initial time
|
// Sends initial time
|
||||||
player.send_time(self).await;
|
player.send_time(self).await;
|
||||||
|
|
||||||
|
// Sends initial scoreboard state
|
||||||
|
player.send_scoreboard().await;
|
||||||
|
|
||||||
let (spawn_block_pos, yaw, pitch) = {
|
let (spawn_block_pos, yaw, pitch) = {
|
||||||
let level_info_lock = self.level_info.load();
|
let level_info_lock = self.level_info.load();
|
||||||
(
|
(
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -14,6 +14,7 @@ pub fn build() -> TokenStream {
|
|||||||
let variants = array_to_tokenstream(&sound_categories);
|
let variants = array_to_tokenstream(&sound_categories);
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum ScoreboardDisplaySlot {
|
pub enum ScoreboardDisplaySlot {
|
||||||
#variants
|
#variants
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user