feat(command): add scoreboard/trigger command
This commit is contained in:
parent
6219d36c64
commit
dab8474e71
@ -226,6 +226,7 @@ pub mod game_profile;
|
||||
pub mod hex_color;
|
||||
pub mod identifier;
|
||||
pub mod nbt;
|
||||
pub mod objective;
|
||||
pub mod range;
|
||||
pub mod resource_key;
|
||||
pub mod slot;
|
||||
|
||||
50
pumpkin/src/command/argument_types/objective.rs
Normal file
50
pumpkin/src/command/argument_types/objective.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use crate::command::{
|
||||
argument_types::argument_type::{ArgumentType, JavaClientArgumentType},
|
||||
context::command_context::CommandContext,
|
||||
errors::command_syntax_error::CommandSyntaxError,
|
||||
string_reader::StringReader,
|
||||
suggestion::suggestions::{Suggestions, SuggestionsBuilder},
|
||||
};
|
||||
use std::pin::Pin;
|
||||
|
||||
/// Represents an argument type parsing a scoreboard objective name.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ObjectiveArgumentType;
|
||||
|
||||
impl ArgumentType for ObjectiveArgumentType {
|
||||
type Item = String;
|
||||
|
||||
fn parse(&self, reader: &mut StringReader) -> Result<Self::Item, CommandSyntaxError> {
|
||||
let name = reader.read_unquoted_string();
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
fn client_side_parser(&'_ self) -> JavaClientArgumentType {
|
||||
JavaClientArgumentType::Objective
|
||||
}
|
||||
|
||||
fn list_suggestions<'a>(
|
||||
&'a self,
|
||||
context: &'a CommandContext,
|
||||
mut builder: SuggestionsBuilder,
|
||||
) -> Pin<Box<dyn Future<Output = Suggestions> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
let scoreboard = context.world().scoreboard.lock().await;
|
||||
for objective_name in scoreboard.get_objectives().keys() {
|
||||
builder = builder.filter_and_suggest_one(objective_name.as_str());
|
||||
}
|
||||
builder.build()
|
||||
})
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<String> {
|
||||
vec!["objective".to_string(), "trigger_obj".to_string()]
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectiveArgumentType {
|
||||
/// Returns a [`CommandContext`]'s parsed `String` argument as a string slice.
|
||||
pub fn get<'a>(context: &'a CommandContext, name: &str) -> Result<&'a str, CommandSyntaxError> {
|
||||
Ok(context.get_argument::<String>(name)?.as_str())
|
||||
}
|
||||
}
|
||||
@ -44,6 +44,7 @@ mod saveall;
|
||||
mod saveoff;
|
||||
mod saveon;
|
||||
mod say;
|
||||
mod scoreboard;
|
||||
mod seed;
|
||||
mod setblock;
|
||||
mod setidletimeout;
|
||||
@ -61,6 +62,7 @@ mod time;
|
||||
mod title;
|
||||
mod tps;
|
||||
mod transfer;
|
||||
mod trigger;
|
||||
mod weather;
|
||||
mod whitelist;
|
||||
mod worldborder;
|
||||
@ -169,6 +171,8 @@ pub async fn default_dispatcher(
|
||||
tag::register(&mut dispatcher, registry);
|
||||
tick::register(&mut dispatcher, registry);
|
||||
advancement::register(&mut dispatcher, registry);
|
||||
trigger::register(&mut dispatcher, registry);
|
||||
scoreboard::register(&mut dispatcher, registry);
|
||||
dispatcher
|
||||
}
|
||||
|
||||
|
||||
209
pumpkin/src/command/commands/scoreboard.rs
Normal file
209
pumpkin/src/command/commands/scoreboard.rs
Normal file
@ -0,0 +1,209 @@
|
||||
use crate::command::argument_builder::{ArgumentBuilder, argument, command, literal};
|
||||
use crate::command::argument_types::core::string::StringArgumentType;
|
||||
use crate::command::argument_types::entity::EntityArgumentType;
|
||||
use crate::command::argument_types::objective::ObjectiveArgumentType;
|
||||
use crate::command::context::command_context::CommandContext;
|
||||
use crate::command::errors::error_types::CommandErrorType;
|
||||
use crate::command::node::dispatcher::CommandDispatcher;
|
||||
use crate::command::node::{CommandExecutor, CommandExecutorResult};
|
||||
use crate::world::scoreboard::{ScoreboardObjective, ScoreboardScore};
|
||||
use pumpkin_data::translation;
|
||||
use pumpkin_protocol::codec::var_int::VarInt;
|
||||
use pumpkin_protocol::java::client::play::RenderType;
|
||||
use pumpkin_util::PermissionLvl;
|
||||
use pumpkin_util::permission::{Permission, PermissionDefault, PermissionRegistry};
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
const DESCRIPTION: &str = "Manages scoreboard objectives and players.";
|
||||
const PERMISSION: &str = "minecraft:command.scoreboard";
|
||||
|
||||
const ARG_OBJECTIVE: &str = "objective";
|
||||
const ARG_CRITERION: &str = "criterion";
|
||||
const ARG_DISPLAY_NAME: &str = "display_name";
|
||||
const ARG_TARGETS: &str = "targets";
|
||||
|
||||
const DUPLICATE_OBJECTIVE_ERROR: CommandErrorType<0> = CommandErrorType::new(
|
||||
translation::java::COMMANDS_SCOREBOARD_OBJECTIVES_ADD_DUPLICATE,
|
||||
translation::java::COMMANDS_SCOREBOARD_OBJECTIVES_ADD_DUPLICATE,
|
||||
);
|
||||
|
||||
const INVALID_ENABLE_ERROR: CommandErrorType<0> = CommandErrorType::new(
|
||||
translation::java::COMMANDS_SCOREBOARD_PLAYERS_ENABLE_INVALID,
|
||||
translation::java::COMMANDS_SCOREBOARD_PLAYERS_ENABLE_INVALID,
|
||||
);
|
||||
|
||||
const FAILED_ENABLE_ERROR: CommandErrorType<0> = CommandErrorType::new(
|
||||
translation::java::COMMANDS_SCOREBOARD_PLAYERS_ENABLE_FAILED,
|
||||
translation::java::COMMANDS_SCOREBOARD_PLAYERS_ENABLE_FAILED,
|
||||
);
|
||||
|
||||
struct ObjectivesAddExecutor {
|
||||
has_display_name: bool,
|
||||
}
|
||||
|
||||
impl CommandExecutor for ObjectivesAddExecutor {
|
||||
fn execute<'a>(&'a self, context: &'a CommandContext) -> CommandExecutorResult<'a> {
|
||||
Box::pin(async move {
|
||||
let objective_name = StringArgumentType::get(context, ARG_OBJECTIVE)?;
|
||||
let criterion = StringArgumentType::get(context, ARG_CRITERION)?;
|
||||
|
||||
let display_name = if self.has_display_name {
|
||||
TextComponent::text(StringArgumentType::get(context, ARG_DISPLAY_NAME)?.to_string())
|
||||
} else {
|
||||
TextComponent::text(objective_name.to_string())
|
||||
};
|
||||
|
||||
let world = context.world();
|
||||
let mut scoreboard = world.scoreboard.lock().await;
|
||||
|
||||
if scoreboard.get_objectives().contains_key(objective_name) {
|
||||
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(
|
||||
obj_name_static,
|
||||
display_name.clone(),
|
||||
RenderType::Integer,
|
||||
None,
|
||||
criterion_static,
|
||||
);
|
||||
|
||||
scoreboard.add_objective(world, new_objective).await;
|
||||
|
||||
context
|
||||
.source
|
||||
.send_feedback(
|
||||
TextComponent::translate_cross(
|
||||
translation::java::COMMANDS_SCOREBOARD_OBJECTIVES_ADD_SUCCESS,
|
||||
translation::java::COMMANDS_SCOREBOARD_OBJECTIVES_ADD_SUCCESS,
|
||||
[display_name],
|
||||
),
|
||||
true,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(1)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct PlayersEnableExecutor;
|
||||
|
||||
impl CommandExecutor for PlayersEnableExecutor {
|
||||
fn execute<'a>(&'a self, context: &'a CommandContext) -> CommandExecutorResult<'a> {
|
||||
Box::pin(async move {
|
||||
let targets = EntityArgumentType::get_players(context, ARG_TARGETS).await?;
|
||||
let objective_name = ObjectiveArgumentType::get(context, ARG_OBJECTIVE)?;
|
||||
|
||||
let world = context.world();
|
||||
let mut scoreboard = world.scoreboard.lock().await;
|
||||
|
||||
let objective = scoreboard
|
||||
.get_objectives()
|
||||
.get(objective_name)
|
||||
.ok_or_else(|| INVALID_ENABLE_ERROR.create_without_context())?;
|
||||
|
||||
if objective.criterion != "trigger" {
|
||||
return Err(INVALID_ENABLE_ERROR.create_without_context());
|
||||
}
|
||||
|
||||
let objective_display_name = objective.display_name.clone();
|
||||
|
||||
let mut enabled_count = 0;
|
||||
for player in &targets {
|
||||
let player_name = &player.gameprofile.name;
|
||||
let current_score = scoreboard
|
||||
.get_scores()
|
||||
.get(objective_name)
|
||||
.and_then(|m| m.get(player_name));
|
||||
|
||||
let is_already_enabled = current_score.is_some_and(|s| !s.locked);
|
||||
|
||||
if !is_already_enabled {
|
||||
let value = current_score.map_or(0, |s| s.value.0);
|
||||
let display_name = current_score.and_then(|s| s.display_name.clone());
|
||||
let number_format = current_score.and_then(|s| s.number_format.clone());
|
||||
|
||||
let updated_score = ScoreboardScore {
|
||||
entity_name: Box::leak(player_name.clone().into_boxed_str()),
|
||||
objective_name: Box::leak(objective_name.to_string().into_boxed_str()),
|
||||
value: VarInt(value),
|
||||
display_name,
|
||||
number_format,
|
||||
locked: false,
|
||||
};
|
||||
|
||||
scoreboard.update_score(world, updated_score).await;
|
||||
enabled_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if enabled_count == 0 {
|
||||
return Err(FAILED_ENABLE_ERROR.create_without_context());
|
||||
}
|
||||
|
||||
let msg = if targets.len() == 1 {
|
||||
TextComponent::translate_cross(
|
||||
translation::java::COMMANDS_SCOREBOARD_PLAYERS_ENABLE_SUCCESS_SINGLE,
|
||||
translation::java::COMMANDS_SCOREBOARD_PLAYERS_ENABLE_SUCCESS_SINGLE,
|
||||
[
|
||||
objective_display_name,
|
||||
TextComponent::text(targets[0].gameprofile.name.clone()),
|
||||
],
|
||||
)
|
||||
} else {
|
||||
TextComponent::translate_cross(
|
||||
translation::java::COMMANDS_SCOREBOARD_PLAYERS_ENABLE_SUCCESS_MULTIPLE,
|
||||
translation::java::COMMANDS_SCOREBOARD_PLAYERS_ENABLE_SUCCESS_MULTIPLE,
|
||||
[
|
||||
objective_display_name,
|
||||
TextComponent::text(targets.len().to_string()),
|
||||
],
|
||||
)
|
||||
};
|
||||
|
||||
context.source.send_feedback(msg, true).await;
|
||||
|
||||
Ok(enabled_count)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
|
||||
registry.register_permission_or_panic(Permission::new(
|
||||
PERMISSION,
|
||||
DESCRIPTION,
|
||||
PermissionDefault::Op(PermissionLvl::Two),
|
||||
));
|
||||
|
||||
dispatcher.register(
|
||||
command("scoreboard", DESCRIPTION)
|
||||
.requires(PERMISSION)
|
||||
.then(
|
||||
literal("objectives").then(
|
||||
literal("add").then(
|
||||
argument(ARG_OBJECTIVE, StringArgumentType::SingleWord).then(
|
||||
argument(ARG_CRITERION, StringArgumentType::SingleWord)
|
||||
.executes(ObjectivesAddExecutor {
|
||||
has_display_name: false,
|
||||
})
|
||||
.then(
|
||||
argument(ARG_DISPLAY_NAME, StringArgumentType::GreedyPhrase)
|
||||
.executes(ObjectivesAddExecutor {
|
||||
has_display_name: true,
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.then(literal("players").then(literal("enable").then(
|
||||
argument(ARG_TARGETS, EntityArgumentType::Players).then(
|
||||
argument(ARG_OBJECTIVE, ObjectiveArgumentType).executes(PlayersEnableExecutor),
|
||||
),
|
||||
))),
|
||||
);
|
||||
}
|
||||
256
pumpkin/src/command/commands/trigger.rs
Normal file
256
pumpkin/src/command/commands/trigger.rs
Normal file
@ -0,0 +1,256 @@
|
||||
use crate::command::argument_builder::{ArgumentBuilder, argument, command, literal};
|
||||
use crate::command::argument_types::core::integer::IntegerArgumentType;
|
||||
use crate::command::argument_types::objective::ObjectiveArgumentType;
|
||||
use crate::command::context::command_context::CommandContext;
|
||||
use crate::command::errors::error_types::CommandErrorType;
|
||||
use crate::command::node::dispatcher::CommandDispatcher;
|
||||
use crate::command::node::{CommandExecutor, CommandExecutorResult};
|
||||
use crate::world::scoreboard::ScoreboardScore;
|
||||
use pumpkin_data::translation;
|
||||
use pumpkin_protocol::codec::var_int::VarInt;
|
||||
use pumpkin_util::permission::{Permission, PermissionDefault, PermissionRegistry};
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
const DESCRIPTION: &str = "Modifies a trigger scoreboard objective.";
|
||||
const PERMISSION: &str = "minecraft:command.trigger";
|
||||
|
||||
const ARG_OBJECTIVE: &str = "objective";
|
||||
const ARG_VALUE: &str = "value";
|
||||
|
||||
const INVALID_TRIGGER_ERROR: CommandErrorType<0> = CommandErrorType::new(
|
||||
translation::java::COMMANDS_TRIGGER_FAILED_INVALID,
|
||||
translation::java::COMMANDS_TRIGGER_FAILED_INVALID,
|
||||
);
|
||||
|
||||
const UNPRIMED_TRIGGER_ERROR: CommandErrorType<0> = CommandErrorType::new(
|
||||
translation::java::COMMANDS_TRIGGER_FAILED_UNPRIMED,
|
||||
translation::java::COMMANDS_TRIGGER_FAILED_UNPRIMED,
|
||||
);
|
||||
|
||||
struct SimpleTriggerExecutor;
|
||||
|
||||
impl CommandExecutor for SimpleTriggerExecutor {
|
||||
fn execute<'a>(&'a self, context: &'a CommandContext) -> CommandExecutorResult<'a> {
|
||||
Box::pin(async move {
|
||||
let player = context.source.player_or_err()?;
|
||||
let player_name = &player.gameprofile.name;
|
||||
let objective_name = ObjectiveArgumentType::get(context, ARG_OBJECTIVE)?;
|
||||
|
||||
let world = context.world();
|
||||
let mut scoreboard = world.scoreboard.lock().await;
|
||||
|
||||
let objective = scoreboard
|
||||
.get_objectives()
|
||||
.get(objective_name)
|
||||
.ok_or_else(|| INVALID_TRIGGER_ERROR.create_without_context())?;
|
||||
|
||||
if objective.criterion != "trigger" {
|
||||
return Err(INVALID_TRIGGER_ERROR.create_without_context());
|
||||
}
|
||||
|
||||
let objective_display_name = objective.display_name.clone();
|
||||
|
||||
let is_locked = scoreboard
|
||||
.get_scores()
|
||||
.get(objective_name)
|
||||
.and_then(|m| m.get(player_name))
|
||||
.is_none_or(|s| s.locked);
|
||||
|
||||
if is_locked {
|
||||
return Err(UNPRIMED_TRIGGER_ERROR.create_without_context());
|
||||
}
|
||||
|
||||
let current_value = scoreboard
|
||||
.get_scores()
|
||||
.get(objective_name)
|
||||
.and_then(|m| m.get(player_name))
|
||||
.map_or(0, |s| s.value.0);
|
||||
|
||||
let new_value = current_value + 1;
|
||||
|
||||
let updated_score = ScoreboardScore {
|
||||
entity_name: Box::leak(player_name.clone().into_boxed_str()),
|
||||
objective_name: Box::leak(objective_name.to_string().into_boxed_str()),
|
||||
value: VarInt(new_value),
|
||||
display_name: None,
|
||||
number_format: None,
|
||||
locked: true,
|
||||
};
|
||||
|
||||
scoreboard.update_score(world, updated_score).await;
|
||||
|
||||
context
|
||||
.source
|
||||
.send_feedback(
|
||||
TextComponent::translate_cross(
|
||||
translation::java::COMMANDS_TRIGGER_SIMPLE_SUCCESS,
|
||||
translation::java::COMMANDS_TRIGGER_SIMPLE_SUCCESS,
|
||||
[objective_display_name],
|
||||
),
|
||||
false,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(new_value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct AddTriggerExecutor;
|
||||
|
||||
impl CommandExecutor for AddTriggerExecutor {
|
||||
fn execute<'a>(&'a self, context: &'a CommandContext) -> CommandExecutorResult<'a> {
|
||||
Box::pin(async move {
|
||||
let player = context.source.player_or_err()?;
|
||||
let player_name = &player.gameprofile.name;
|
||||
let objective_name = ObjectiveArgumentType::get(context, ARG_OBJECTIVE)?;
|
||||
let value = IntegerArgumentType::get(context, ARG_VALUE)?;
|
||||
|
||||
let world = context.world();
|
||||
let mut scoreboard = world.scoreboard.lock().await;
|
||||
|
||||
let objective = scoreboard
|
||||
.get_objectives()
|
||||
.get(objective_name)
|
||||
.ok_or_else(|| INVALID_TRIGGER_ERROR.create_without_context())?;
|
||||
|
||||
if objective.criterion != "trigger" {
|
||||
return Err(INVALID_TRIGGER_ERROR.create_without_context());
|
||||
}
|
||||
|
||||
let objective_display_name = objective.display_name.clone();
|
||||
|
||||
let is_locked = scoreboard
|
||||
.get_scores()
|
||||
.get(objective_name)
|
||||
.and_then(|m| m.get(player_name))
|
||||
.is_none_or(|s| s.locked);
|
||||
|
||||
if is_locked {
|
||||
return Err(UNPRIMED_TRIGGER_ERROR.create_without_context());
|
||||
}
|
||||
|
||||
let current_value = scoreboard
|
||||
.get_scores()
|
||||
.get(objective_name)
|
||||
.and_then(|m| m.get(player_name))
|
||||
.map_or(0, |s| s.value.0);
|
||||
|
||||
let new_value = current_value + value;
|
||||
|
||||
let updated_score = ScoreboardScore {
|
||||
entity_name: Box::leak(player_name.clone().into_boxed_str()),
|
||||
objective_name: Box::leak(objective_name.to_string().into_boxed_str()),
|
||||
value: VarInt(new_value),
|
||||
display_name: None,
|
||||
number_format: None,
|
||||
locked: true,
|
||||
};
|
||||
|
||||
scoreboard.update_score(world, updated_score).await;
|
||||
|
||||
context
|
||||
.source
|
||||
.send_feedback(
|
||||
TextComponent::translate_cross(
|
||||
translation::java::COMMANDS_TRIGGER_ADD_SUCCESS,
|
||||
translation::java::COMMANDS_TRIGGER_ADD_SUCCESS,
|
||||
[
|
||||
objective_display_name,
|
||||
TextComponent::text(value.to_string()),
|
||||
],
|
||||
),
|
||||
false,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(new_value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct SetTriggerExecutor;
|
||||
|
||||
impl CommandExecutor for SetTriggerExecutor {
|
||||
fn execute<'a>(&'a self, context: &'a CommandContext) -> CommandExecutorResult<'a> {
|
||||
Box::pin(async move {
|
||||
let player = context.source.player_or_err()?;
|
||||
let player_name = &player.gameprofile.name;
|
||||
let objective_name = ObjectiveArgumentType::get(context, ARG_OBJECTIVE)?;
|
||||
let value = IntegerArgumentType::get(context, ARG_VALUE)?;
|
||||
|
||||
let world = context.world();
|
||||
let mut scoreboard = world.scoreboard.lock().await;
|
||||
|
||||
let objective = scoreboard
|
||||
.get_objectives()
|
||||
.get(objective_name)
|
||||
.ok_or_else(|| INVALID_TRIGGER_ERROR.create_without_context())?;
|
||||
|
||||
if objective.criterion != "trigger" {
|
||||
return Err(INVALID_TRIGGER_ERROR.create_without_context());
|
||||
}
|
||||
|
||||
let objective_display_name = objective.display_name.clone();
|
||||
|
||||
let is_locked = scoreboard
|
||||
.get_scores()
|
||||
.get(objective_name)
|
||||
.and_then(|m| m.get(player_name))
|
||||
.is_none_or(|s| s.locked);
|
||||
|
||||
if is_locked {
|
||||
return Err(UNPRIMED_TRIGGER_ERROR.create_without_context());
|
||||
}
|
||||
|
||||
let updated_score = ScoreboardScore {
|
||||
entity_name: Box::leak(player_name.clone().into_boxed_str()),
|
||||
objective_name: Box::leak(objective_name.to_string().into_boxed_str()),
|
||||
value: VarInt(value),
|
||||
display_name: None,
|
||||
number_format: None,
|
||||
locked: true,
|
||||
};
|
||||
|
||||
scoreboard.update_score(world, updated_score).await;
|
||||
|
||||
context
|
||||
.source
|
||||
.send_feedback(
|
||||
TextComponent::translate_cross(
|
||||
translation::java::COMMANDS_TRIGGER_SET_SUCCESS,
|
||||
translation::java::COMMANDS_TRIGGER_SET_SUCCESS,
|
||||
[
|
||||
objective_display_name,
|
||||
TextComponent::text(value.to_string()),
|
||||
],
|
||||
),
|
||||
false,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
|
||||
registry.register_permission_or_panic(Permission::new(
|
||||
PERMISSION,
|
||||
DESCRIPTION,
|
||||
PermissionDefault::Allow,
|
||||
));
|
||||
|
||||
dispatcher.register(
|
||||
command("trigger", DESCRIPTION).requires(PERMISSION).then(
|
||||
argument(ARG_OBJECTIVE, ObjectiveArgumentType)
|
||||
.executes(SimpleTriggerExecutor)
|
||||
.then(literal("add").then(
|
||||
argument(ARG_VALUE, IntegerArgumentType::any()).executes(AddTriggerExecutor),
|
||||
))
|
||||
.then(literal("set").then(
|
||||
argument(ARG_VALUE, IntegerArgumentType::any()).executes(SetTriggerExecutor),
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -41,8 +41,13 @@ impl scoreboard::HostScoreboard for PluginHostState {
|
||||
RenderType::Hearts => pumpkin_protocol::java::client::play::RenderType::Hearts,
|
||||
};
|
||||
|
||||
let objective =
|
||||
ScoreboardObjective::new(Box::leak(name.into_boxed_str()), display_name, rt, None);
|
||||
let objective = ScoreboardObjective::new(
|
||||
Box::leak(name.into_boxed_str()),
|
||||
display_name,
|
||||
rt,
|
||||
None,
|
||||
"dummy",
|
||||
);
|
||||
world
|
||||
.scoreboard
|
||||
.lock()
|
||||
|
||||
@ -26,6 +26,16 @@ pub struct Scoreboard {
|
||||
}
|
||||
|
||||
impl Scoreboard {
|
||||
#[must_use]
|
||||
pub const fn get_objectives(&self) -> &HashMap<String, ScoreboardObjective<'static>> {
|
||||
&self.objectives
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_scores(&self) -> &HashMap<String, HashMap<String, ScoreboardScore<'static>>> {
|
||||
&self.scores
|
||||
}
|
||||
|
||||
async fn broadcast_editioned<J: ClientPacket, B: BClientPacket>(
|
||||
world: &World,
|
||||
je_packet: &J,
|
||||
@ -281,6 +291,7 @@ pub struct ScoreboardObjective<'a> {
|
||||
pub display_name: TextComponent,
|
||||
pub render_type: RenderType,
|
||||
pub number_format: Option<NumberFormat>,
|
||||
pub criterion: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> ScoreboardObjective<'a> {
|
||||
@ -290,12 +301,14 @@ impl<'a> ScoreboardObjective<'a> {
|
||||
display_name: TextComponent,
|
||||
render_type: RenderType,
|
||||
number_format: Option<NumberFormat>,
|
||||
criterion: &'a str,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
display_name,
|
||||
render_type,
|
||||
number_format,
|
||||
criterion,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -306,6 +319,7 @@ pub struct ScoreboardScore<'a> {
|
||||
pub value: VarInt,
|
||||
pub display_name: Option<TextComponent>,
|
||||
pub number_format: Option<NumberFormat>,
|
||||
pub locked: bool,
|
||||
}
|
||||
|
||||
impl<'a> ScoreboardScore<'a> {
|
||||
@ -323,6 +337,7 @@ impl<'a> ScoreboardScore<'a> {
|
||||
value,
|
||||
display_name,
|
||||
number_format,
|
||||
locked: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user