feat: bedrock translations

This commit is contained in:
Alexander Medvedev 2026-05-03 21:31:46 +02:00
parent fb5ede696c
commit 9b88ef56e4
101 changed files with 46957 additions and 17951 deletions

View File

@ -101,7 +101,7 @@ pub fn build() -> TokenStream {
let max_level = enchantment.max_level;
let slots = enchantment.slots;
let slots = slots.iter().map(AttributeModifierSlot::to_tokens);
let Translate { translate, with: _ } = &*enchantment.description.0.content else {
let Translate { translate, bedrock_translate: _, with: _ } = &*enchantment.description.0.content else {
panic!()
};
let translate = translate.to_string();

View File

@ -84,6 +84,7 @@ impl ToTokens for ItemComponents {
let TextContent::Translate {
translate: text,
bedrock_translate: _,
with: _,
} = *self.item_name.clone().0.content
else {

View File

@ -1,33 +1,60 @@
use proc_macro2::TokenStream;
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use std::{collections::BTreeMap, fs};
/// Generates the `TokenStream` for translation key constants sourced from `en_us.json`.
pub fn build() -> TokenStream {
let en_us: BTreeMap<String, String> = serde_json::from_str(
&fs::read_to_string("../assets/en_us.json").expect("en_us is missing"),
let java_json: BTreeMap<String, String> = serde_json::from_str(
&fs::read_to_string("../assets/en_us_java.json").expect("en_us_java is missing"),
)
.unwrap();
let mut constants = TokenStream::new();
for (name, value) in &en_us {
let clean_name = name.to_uppercase().replace(['.', '-'], "_");
let ident = format_ident!("{}", clean_name);
if value.is_empty() {
constants.extend(quote! {
let mut java_constants = TokenStream::new();
for (name, value) in &java_json {
let ident = to_valid_ident(name);
java_constants.extend(quote! {
#[doc = #value]
pub const #ident: &str = #name;
});
} else {
constants.extend(quote! {
}
let bedrock_content = fs::read_to_string("../assets/en_us_bedrock.lang").expect("en_us_bedrock is missing");
let mut bedrock_constants = TokenStream::new();
for line in bedrock_content.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') || line.starts_with('/') {
continue;
}
if let Some((name, value)) = line.split_once('=') {
let name = name.trim();
let value = value.trim();
let ident = to_valid_ident(name);
bedrock_constants.extend(quote! {
#[doc = #value]
pub const #ident: &str = #name;
});
}
}
// --- Final Assembly ---
quote! {
#constants
pub mod java {
#java_constants
}
pub mod bedrock {
#bedrock_constants
}
}
}
fn to_valid_ident(name: &str) -> Ident {
let mut clean = name.to_uppercase().replace(['.', ':', '-'], "_");
if clean.chars().next().map_or(false, |c| c.is_ascii_digit()) {
clean.insert(0, '_');
}
format_ident!("{}", clean)
}

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@ use std::io::{Error, Read, Write};
use pumpkin_macros::packet;
use crate::{
codec::{var_int::VarInt, var_ulong::VarULong},
codec::var_ulong::VarULong,
serial::{PacketRead, PacketWrite},
};
@ -37,10 +37,7 @@ impl PacketRead for AnimateAction {
4 => Ok(Self::MagicCriticalHit),
128 => Ok(Self::RowRight),
129 => Ok(Self::RowLeft),
_ => Err(Error::other(format!(
"Invalid animate action ID: {}",
action
))),
_ => Err(Error::other(format!("Invalid animate action ID: {action}"))),
}
}
}

View File

@ -1,7 +1,7 @@
use pumpkin_macros::packet;
use uuid::Uuid;
use crate::{codec::var_uint::VarUInt, serial::PacketRead};
use crate::serial::PacketRead;
#[derive(Debug, PacketRead)]
#[packet(77)]

View File

@ -99,7 +99,7 @@ impl TryFrom<i32> for Action {
34 => Ok(Self::StartFlying),
35 => Ok(Self::StopFlying),
36 => Ok(Self::ClientAckServerData),
_ => Err(format!("Invalid action ID: {}", value)),
_ => Err(format!("Invalid action ID: {value}")),
}
}
}
@ -108,6 +108,6 @@ impl PacketRead for Action {
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
let action = VarInt::read(reader)?;
Action::try_from(action.0).map_err(Error::other)
Self::try_from(action.0).map_err(Error::other)
}
}

View File

@ -5,13 +5,8 @@ use pumpkin_util::math::{position::BlockPos, vector2::Vector2, vector3::Vector3}
use crate::{
codec::{
bitset::Bitset,
var_int::VarInt,
var_long::{VarLong, VarLongType},
var_uint::VarUInt,
var_ulong::VarULong,
bitset::Bitset, var_int::VarInt, var_long::VarLong, var_uint::VarUInt, var_ulong::VarULong,
},
packet::{self, Packet},
serial::PacketRead,
};
@ -40,6 +35,7 @@ pub struct SPlayerAuthInput {
}
impl PacketRead for SPlayerAuthInput {
#[expect(clippy::useless_let_if_seq)]
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
let pitch = f32::read(reader)?;
let yaw = f32::read(reader)?;
@ -129,7 +125,7 @@ pub struct PlayerBlockAction {
pub face: VarInt,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum InputMode {
Mouse = 1,
@ -138,7 +134,7 @@ pub enum InputMode {
MotionController = 4,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum PlayMode {
Normal = 0,
@ -148,7 +144,7 @@ pub enum PlayMode {
NumModes = 9,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum InteractionModel {
Touch = 0,
@ -156,7 +152,7 @@ pub enum InteractionModel {
Classic = 2,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum InputData {
Ascend = 0,
Descend = 1,

View File

@ -38,7 +38,7 @@ impl SText {
}
#[must_use]
pub fn translation(message: String, parameters: Vec<String>) -> Self {
pub const fn translation(message: String, parameters: Vec<String>) -> Self {
Self {
needs_translation: true,
r#type: TextPacketType::Translation,

View File

@ -168,7 +168,7 @@ impl PacketRead for VarLong {
let decoded = ((val >> 1) as i64) ^ -((val & 1) as i64);
Ok(VarLong(decoded))
Ok(Self(decoded))
}
}

View File

@ -12,6 +12,7 @@ use serde::de::{Error, MapAccess, SeqAccess, Visitor};
use serde::{Deserialize, Deserializer, Serialize};
use std::borrow::Cow;
use std::fmt::Formatter;
use std::fmt::Write;
use std::sync::LazyLock;
use style::Style;
@ -103,9 +104,11 @@ impl TextComponentBase {
pub fn to_pretty_console(self) -> String {
let mut text = match *self.content {
TextContent::Text { text } => text.into_owned(),
TextContent::Translate { translate, with } => {
translation_to_pretty(format!("minecraft:{translate}"), Locale::EnUs, with)
}
TextContent::Translate {
translate,
bedrock_translate: _,
with,
} => translation_to_pretty(format!("minecraft:{translate}"), Locale::EnUs, with),
TextContent::EntityNames {
selector,
separator: _,
@ -144,7 +147,7 @@ impl TextComponentBase {
if let Some(color) = &self.style.color {
match color {
Color::Named(named) => {
text.push_str(&format!("§{}", named.to_legacy_char()));
let _ = write!(text, "§{}", named.to_legacy_char());
}
Color::Rgb(_rgb) => {
// Bedrock doesn't strictly support Java's §x hex format.
@ -174,7 +177,11 @@ impl TextComponentBase {
// 2. Resolve Content
match &*self.content {
TextContent::Text { text: t } => text.push_str(t),
TextContent::Translate { translate, with } => {
TextContent::Translate {
translate,
bedrock_translate: _,
with,
} => {
// TODO
text.push_str(&get_translation_text(
translate.to_string(),
@ -211,9 +218,11 @@ impl TextComponentBase {
pub fn get_text(self, locale: Locale) -> String {
let mut text = match *self.content {
TextContent::Text { text } => text.into_owned(),
TextContent::Translate { translate, with } => {
get_translation_text(format!("minecraft:{translate}"), locale, with)
}
TextContent::Translate {
translate,
bedrock_translate: _,
with,
} => get_translation_text(format!("minecraft:{translate}"), locale, with),
TextContent::EntityNames {
selector,
separator: _,
@ -382,6 +391,37 @@ impl TextComponent {
Self(TextComponentBase {
content: Box::new(TextContent::Translate {
translate: key.into(),
bedrock_translate: None,
with: with.into().into_iter().map(|x| x.0).collect(),
}),
style: Box::new(Style::default()),
extra: vec![],
})
}
/// Creates a new text component with a translation key that has a Bedrock-specific fallback.
///
/// # Arguments
/// - `java_key` – The translation key for Java (e.g., "multiplayer.player.joined").
/// - `bedrock_key` – The translation key for Bedrock (e.g., "multiplayer.player.joined").
/// - `with` – The substitution parameters for the translation.
///
/// # Returns
/// A new `TextComponent` that will be translated natively on both clients.
#[must_use]
pub fn translate_cross<
K1: Into<Cow<'static, str>>,
K2: Into<Cow<'static, str>>,
W: Into<Vec<Self>>,
>(
java_key: K1,
bedrock_key: K2,
with: W,
) -> Self {
Self(TextComponentBase {
content: Box::new(TextContent::Translate {
translate: java_key.into(),
bedrock_translate: Some(bedrock_key.into()),
with: with.into().into_iter().map(|x| x.0).collect(),
}),
style: Box::new(Style::default()),
@ -1003,6 +1043,9 @@ pub enum TextContent {
Translate {
/// The translation key (e.g. "multiplayer.player.joined").
translate: Cow<'static, str>,
/// Bedrock translation key. If specified, Bedrock clients receive an `SText::translation` packet.
#[serde(skip, default)]
bedrock_translate: Option<Cow<'static, str>>,
/// Substitution parameters for the translation.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
with: Vec<TextComponentBase>,

View File

@ -14,10 +14,9 @@ use std::{
/// - Try to optimize code of '`to_translated`'
use crate::text::{TextComponentBase, TextContent, style::Style};
static VANILLA_EN_US_JSON: &str = include_str!("../../assets/en_us.json");
static VANILLA_EN_US_JSON: &str = include_str!("../../assets/en_us_java.json");
static PUMPKIN_BRB_JSON: &str = include_str!("../../assets/translations/brb.json");
static PUMPKIN_DE_DE_JSON: &str = include_str!("../../assets/translations/de_de.json");
static PUMPKIN_EN_US_JSON: &str = include_str!("../../assets/translations/en_us.json");
static PUMPKIN_ES_ES_JSON: &str = include_str!("../../assets/translations/es_es.json");
static PUMPKIN_FR_FR_JSON: &str = include_str!("../../assets/translations/fr_fr.json");
static PUMPKING_IT_IT_JSON: &str = include_str!("../../assets/translations/it_it.json");
@ -271,13 +270,11 @@ pub static TRANSLATIONS: LazyLock<Mutex<[HashMap<String, String>; Locale::COUNT]
let mut array: [HashMap<String, String>; Locale::COUNT] =
std::array::from_fn(|_| HashMap::new());
let vanilla_en_us: HashMap<String, String> =
serde_json::from_str(VANILLA_EN_US_JSON).expect("Could not parse en_us.json.");
serde_json::from_str(VANILLA_EN_US_JSON).expect("Could not parse en_us_java.json.");
let pumpkin_brb: HashMap<String, String> =
serde_json::from_str(PUMPKIN_BRB_JSON).expect("Could not parse brb.json.");
let pumpkin_de_de: HashMap<String, String> =
serde_json::from_str(PUMPKIN_DE_DE_JSON).expect("Could not parse de_de.json.");
let pumpkin_en_us: HashMap<String, String> =
serde_json::from_str(PUMPKIN_EN_US_JSON).expect("Could not parse en_us.json.");
let pumpkin_es_es: HashMap<String, String> =
serde_json::from_str(PUMPKIN_ES_ES_JSON).expect("Could not parse es_es.json.");
let pumpkin_fr_fr: HashMap<String, String> =
@ -328,9 +325,6 @@ pub static TRANSLATIONS: LazyLock<Mutex<[HashMap<String, String>; Locale::COUNT]
for (key, value) in pumpkin_de_de {
array[Locale::DeDe as usize].insert(format!("pumpkin:{key}"), value);
}
for (key, value) in pumpkin_en_us {
array[Locale::EnUs as usize].insert(format!("pumpkin:{key}"), value);
}
for (key, value) in pumpkin_es_es {
array[Locale::EsEs as usize].insert(format!("pumpkin:{key}"), value);
}

View File

@ -90,6 +90,10 @@ impl ScreenHandlerFactory for AnvilScreenFactory {
}
fn get_display_name(&self) -> TextComponent {
TextComponent::translate(translation::CONTAINER_REPAIR, &[])
TextComponent::translate_cross(
translation::java::CONTAINER_REPAIR,
translation::bedrock::CONTAINER_REPAIR,
&[],
)
}
}

View File

@ -38,7 +38,11 @@ impl ScreenHandlerFactory for BarrelScreenFactory {
}
fn get_display_name(&self) -> TextComponent {
TextComponent::translate(translation::CONTAINER_BARREL, &[])
TextComponent::translate_cross(
translation::java::CONTAINER_BARREL,
translation::bedrock::CONTAINER_BARREL,
&[],
)
}
}

View File

@ -259,7 +259,11 @@ impl BlockBehaviour for BedBlock {
{
args.player
.send_system_message_raw(
&TextComponent::translate(translation::BLOCK_MINECRAFT_BED_OBSTRUCTED, []),
&TextComponent::translate_cross(
translation::java::BLOCK_MINECRAFT_BED_OBSTRUCTED,
translation::java::BLOCK_MINECRAFT_BED_OBSTRUCTED,
[],
),
true,
)
.await;
@ -272,7 +276,11 @@ impl BlockBehaviour for BedBlock {
args.player
.send_system_message_raw(
&TextComponent::translate(translation::BLOCK_MINECRAFT_BED_OCCUPIED, []),
&TextComponent::translate_cross(
translation::java::BLOCK_MINECRAFT_BED_OCCUPIED,
translation::bedrock::TILE_BED_OCCUPIED,
[],
),
true,
)
.await;
@ -291,8 +299,9 @@ impl BlockBehaviour for BedBlock {
{
args.player
.send_system_message_raw(
&TextComponent::translate(
translation::BLOCK_MINECRAFT_BED_TOO_FAR_AWAY,
&TextComponent::translate_cross(
translation::java::BLOCK_MINECRAFT_BED_TOO_FAR_AWAY,
translation::java::BLOCK_MINECRAFT_BED_TOO_FAR_AWAY,
[],
),
true,
@ -313,8 +322,9 @@ impl BlockBehaviour for BedBlock {
.await
{
args.player
.send_system_message(&TextComponent::translate(
translation::BLOCK_MINECRAFT_SET_SPAWN,
.send_system_message(&TextComponent::translate_cross(
translation::java::BLOCK_MINECRAFT_SET_SPAWN,
translation::bedrock::TILE_BED_RESPAWNSET,
[],
))
.await;
@ -324,7 +334,11 @@ impl BlockBehaviour for BedBlock {
if !can_sleep(args.world).await {
args.player
.send_system_message_raw(
&TextComponent::translate(translation::BLOCK_MINECRAFT_BED_NO_SLEEP, []),
&TextComponent::translate_cross(
translation::java::BLOCK_MINECRAFT_BED_NO_SLEEP,
translation::java::BLOCK_MINECRAFT_BED_NO_SLEEP,
[],
),
true,
)
.await;
@ -343,8 +357,9 @@ impl BlockBehaviour for BedBlock {
{
args.player
.send_system_message_raw(
&TextComponent::translate(
translation::BLOCK_MINECRAFT_BED_NOT_SAFE,
&TextComponent::translate_cross(
translation::java::BLOCK_MINECRAFT_BED_NOT_SAFE,
translation::java::BLOCK_MINECRAFT_BED_NOT_SAFE,
[],
),
true,

View File

@ -75,7 +75,11 @@ impl ScreenHandlerFactory for BlastingFurnaceScreenFactory {
}
fn get_display_name(&self) -> pumpkin_util::text::TextComponent {
TextComponent::translate(translation::CONTAINER_BLAST_FURNACE, &[])
TextComponent::translate_cross(
translation::java::CONTAINER_BLAST_FURNACE,
translation::bedrock::TILE_BLAST_FURNACE_NAME,
&[],
)
}
}

View File

@ -38,7 +38,11 @@ impl ScreenHandlerFactory for BrewingScreenFactory {
}
fn get_display_name(&self) -> TextComponent {
TextComponent::translate(translation::CONTAINER_BREWING, &[])
TextComponent::translate_cross(
translation::java::CONTAINER_BREWING,
translation::bedrock::CONTAINER_BREWING,
&[],
)
}
}

View File

@ -57,9 +57,17 @@ impl ScreenHandlerFactory for ChestScreenFactory {
fn get_display_name(&self) -> TextComponent {
if self.0.size() > 27 {
TextComponent::translate(translation::CONTAINER_CHESTDOUBLE, &[])
TextComponent::translate_cross(
translation::java::CONTAINER_CHESTDOUBLE,
translation::bedrock::CONTAINER_CHESTDOUBLE,
&[],
)
} else {
TextComponent::translate(translation::CONTAINER_CHEST, &[])
TextComponent::translate_cross(
translation::java::CONTAINER_CHEST,
translation::bedrock::CONTAINER_CHEST,
&[],
)
}
}
}

View File

@ -45,6 +45,10 @@ impl ScreenHandlerFactory for CraftingTableScreenFactory {
}
fn get_display_name(&self) -> TextComponent {
TextComponent::translate(translation::CONTAINER_CRAFTING, &[])
TextComponent::translate_cross(
translation::java::CONTAINER_CRAFTING,
translation::bedrock::CONTAINER_CRAFTING,
&[],
)
}
}

View File

@ -38,7 +38,11 @@ impl ScreenHandlerFactory for EnderChestScreenFactory {
}
fn get_display_name(&self) -> TextComponent {
TextComponent::translate(translation::CONTAINER_ENDERCHEST, &[])
TextComponent::translate_cross(
translation::java::CONTAINER_ENDERCHEST,
translation::bedrock::CONTAINER_ENDERCHEST,
&[],
)
}
}

View File

@ -75,7 +75,11 @@ impl ScreenHandlerFactory for FurnaceScreenFactory {
}
fn get_display_name(&self) -> pumpkin_util::text::TextComponent {
TextComponent::translate(translation::CONTAINER_FURNACE, &[])
TextComponent::translate_cross(
translation::java::CONTAINER_FURNACE,
translation::bedrock::CONTAINER_FURNACE,
&[],
)
}
}

View File

@ -43,7 +43,11 @@ impl ScreenHandlerFactory for HopperBlockScreenFactory {
}
fn get_display_name(&self) -> TextComponent {
TextComponent::translate(translation::CONTAINER_HOPPER, &[])
TextComponent::translate_cross(
translation::java::CONTAINER_HOPPER,
translation::bedrock::CONTAINER_HOPPER,
&[],
)
}
}

View File

@ -47,7 +47,11 @@ impl ScreenHandlerFactory for DropperScreenFactory {
}
fn get_display_name(&self) -> TextComponent {
TextComponent::translate(translation::CONTAINER_DROPPER, &[])
TextComponent::translate_cross(
translation::java::CONTAINER_DROPPER,
translation::bedrock::CONTAINER_DROPPER,
&[],
)
}
}

View File

@ -38,7 +38,11 @@ impl ScreenHandlerFactory for ShulkerBoxScreenFactory {
}
fn get_display_name(&self) -> TextComponent {
TextComponent::translate(translation::CONTAINER_SHULKERBOX, &[])
TextComponent::translate_cross(
translation::java::CONTAINER_SHULKERBOX,
translation::bedrock::CONTAINER_SHULKERBOX,
&[],
)
}
}

View File

@ -74,7 +74,11 @@ impl ScreenHandlerFactory for SmokerScreenFactory {
}
fn get_display_name(&self) -> pumpkin_util::text::TextComponent {
TextComponent::translate(translation::CONTAINER_SMOKER, &[])
TextComponent::translate_cross(
translation::java::CONTAINER_SMOKER,
translation::bedrock::TILE_SMOKER_NAME,
&[],
)
}
}
#[pumpkin_block("minecraft:smoker")]

View File

@ -55,13 +55,15 @@ impl<'a> FindArg<'a> for BlockArgumentConsumer {
Some(Arg::Block(name)) => Block::from_name(name).map_or_else(
|| {
if name.starts_with("minecraft:") {
Err(CommandError::CommandFailed(TextComponent::translate(
translation::ARGUMENT_BLOCK_ID_INVALID,
Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::ARGUMENT_BLOCK_ID_INVALID,
translation::java::ARGUMENT_BLOCK_ID_INVALID,
[TextComponent::text((*name).to_string())],
)))
} else {
Err(CommandError::CommandFailed(TextComponent::translate(
translation::ARGUMENT_BLOCK_ID_INVALID,
Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::ARGUMENT_BLOCK_ID_INVALID,
translation::java::ARGUMENT_BLOCK_ID_INVALID,
[TextComponent::text("minecraft:".to_string() + *name)],
)))
}
@ -121,13 +123,15 @@ impl<'a> FindArg<'a> for BlockPredicateArgumentConsumer {
Block::from_name(name).map_or_else(
|| {
if name.starts_with("minecraft:") {
Err(CommandError::CommandFailed(TextComponent::translate(
translation::ARGUMENT_BLOCK_ID_INVALID,
Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::ARGUMENT_BLOCK_ID_INVALID,
translation::java::ARGUMENT_BLOCK_ID_INVALID,
[TextComponent::text((*name).to_string())],
)))
} else {
Err(CommandError::CommandFailed(TextComponent::translate(
translation::ARGUMENT_BLOCK_ID_INVALID,
Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::ARGUMENT_BLOCK_ID_INVALID,
translation::java::ARGUMENT_BLOCK_ID_INVALID,
[TextComponent::text("minecraft:".to_string() + *name)],
)))
}
@ -138,8 +142,9 @@ impl<'a> FindArg<'a> for BlockPredicateArgumentConsumer {
|tag| {
get_tag_ids(RegistryKey::Block, tag).map_or_else(
|| {
Err(CommandError::CommandFailed(TextComponent::translate(
translation::ARGUMENTS_BLOCK_TAG_UNKNOWN,
Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::ARGUMENTS_BLOCK_TAG_UNKNOWN,
translation::java::ARGUMENTS_BLOCK_TAG_UNKNOWN,
[TextComponent::text((*tag).to_string())],
)))
},

View File

@ -402,7 +402,11 @@ pub(crate) fn parse_target_selector_with_context(
parse_target_selector(raw_arg.value).map_err(|error| {
syntax_error_for_arg_with_cursor(
raw_arg,
TextComponent::translate(translation::ARGUMENT_ENTITY_INVALID, []),
TextComponent::translate_cross(
translation::java::ARGUMENT_ENTITY_INVALID,
translation::java::ARGUMENT_ENTITY_INVALID,
[],
),
error.cursor,
)
})
@ -415,7 +419,11 @@ pub(crate) fn ensure_player_only_selector(
if selector.includes_entities() {
Err(syntax_error_for_arg_with_cursor(
raw_arg,
TextComponent::translate(translation::ARGUMENT_PLAYER_ENTITIES, []),
TextComponent::translate_cross(
translation::java::ARGUMENT_PLAYER_ENTITIES,
translation::java::ARGUMENT_PLAYER_ENTITIES,
[],
),
0,
))
} else {
@ -483,7 +491,7 @@ mod test {
pumpkin_util::text::TextContent::Translate { translate, .. } => translate.as_ref(),
_ => "",
};
assert_eq!(translate_key, translation::ARGUMENT_PLAYER_ENTITIES);
assert_eq!(translate_key, translation::java::ARGUMENT_PLAYER_ENTITIES);
assert_eq!(error.context.unwrap().cursor, 4);
}
}

View File

@ -368,7 +368,11 @@ fn selector_suggestions() -> Vec<CommandSuggestion> {
fn syntax_player_unknown(raw_arg: RawArg<'_>) -> CommandSyntaxError {
syntax_error_for_arg_with_cursor(
raw_arg,
TextComponent::translate(translation::ARGUMENT_PLAYER_UNKNOWN, []),
TextComponent::translate_cross(
translation::java::ARGUMENT_PLAYER_UNKNOWN,
translation::java::ARGUMENT_PLAYER_UNKNOWN,
[],
),
0,
)
}
@ -416,7 +420,7 @@ mod test {
TextContent::Translate { translate, .. } => translate.as_ref(),
_ => "",
};
assert_eq!(translate_key, translation::ARGUMENT_PLAYER_UNKNOWN);
assert_eq!(translate_key, translation::java::ARGUMENT_PLAYER_UNKNOWN);
assert_eq!(error.context.unwrap().cursor, 4);
}
}

View File

@ -158,14 +158,16 @@ impl BlockPosArgumentConsumer {
let pos = Self::find_arg(args, name)?;
if world.level.try_get_chunk(&pos.chunk_position()).is_none() {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
"argument.pos.unloaded",
"argument.pos.unloaded",
[],
)));
}
if !world.is_in_build_limit(pos) {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
"argument.pos.outofworld",
"argument.pos.outofworld",
[],
)));
@ -181,7 +183,8 @@ impl BlockPosArgumentConsumer {
let pos = Self::find_arg(args, name)?;
if !World::is_valid(pos) {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
"argument.pos.outofbounds",
"argument.pos.outofbounds",
[],
)));

View File

@ -59,12 +59,14 @@ impl<'a> FindArg<'a> for ItemArgumentConsumer {
Some(Arg::Item(name)) => Item::from_registry_key(name).map_or_else(
|| {
if name.starts_with("minecraft:") {
Err(CommandError::CommandFailed(TextComponent::translate(
Err(CommandError::CommandFailed(TextComponent::translate_cross(
"argument.item.id.invalid",
"argument.item.id.invalid",
[TextComponent::text((*name).to_string())],
)))
} else {
Err(CommandError::CommandFailed(TextComponent::translate(
Err(CommandError::CommandFailed(TextComponent::translate_cross(
"argument.item.id.invalid",
"argument.item.id.invalid",
[TextComponent::text("minecraft:".to_string() + *name)],
)))
@ -141,15 +143,21 @@ impl<'a> FindArg<'a> for ItemPredicateArgumentConsumer {
Item::from_registry_key(name).map_or_else(
|| {
if name.starts_with("minecraft:") {
Err(CommandError::CommandFailed(TextComponent::translate(
Err(CommandError::CommandFailed(
TextComponent::translate_cross(
"argument.item.id.invalid",
"argument.item.id.invalid",
[TextComponent::text((*name).to_string())],
)))
),
))
} else {
Err(CommandError::CommandFailed(TextComponent::translate(
Err(CommandError::CommandFailed(
TextComponent::translate_cross(
"argument.item.id.invalid",
"argument.item.id.invalid",
[TextComponent::text("minecraft:".to_string() + *name)],
)))
),
))
}
},
|item| Ok(ItemPredicate::Item(item)),
@ -158,7 +166,8 @@ impl<'a> FindArg<'a> for ItemPredicateArgumentConsumer {
|tag| {
get_tag_ids(RegistryKey::Item, tag).map_or_else(
|| {
Err(CommandError::CommandFailed(TextComponent::translate(
Err(CommandError::CommandFailed(TextComponent::translate_cross(
"arguments.item.tag.unknown",
"arguments.item.tag.unknown",
[TextComponent::text((*tag).to_string())],
)))

View File

@ -8,12 +8,18 @@ use crate::world::World;
use pumpkin_data::translation;
use pumpkin_util::math::position::BlockPos;
pub const NOT_LOADED_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_POS_UNLOADED);
pub const OUT_OF_WORLD_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_POS_OUTOFWORLD);
pub const OUT_OF_BOUNDS_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_POS_OUTOFBOUNDS);
pub const NOT_LOADED_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_POS_UNLOADED,
translation::java::ARGUMENT_POS_UNLOADED,
);
pub const OUT_OF_WORLD_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_POS_OUTOFWORLD,
translation::java::ARGUMENT_POS_OUTOFWORLD,
);
pub const OUT_OF_BOUNDS_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_POS_OUTOFBOUNDS,
translation::java::ARGUMENT_POS_OUTOFBOUNDS,
);
/// An argument type for a 3-dimensional vector representing a block position.
///

View File

@ -12,8 +12,10 @@ pub mod block_pos;
pub mod rotation;
pub mod vec3;
pub const MIXED_TYPE_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_POS_MIXED);
pub const MIXED_TYPE_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_POS_MIXED,
translation::java::ARGUMENT_POS_MIXED,
);
/// Represents a single world coordinate.
#[derive(Debug, Clone, Copy, PartialEq)]

View File

@ -7,8 +7,10 @@ use crate::command::string_reader::StringReader;
use pumpkin_data::translation;
use pumpkin_util::math::vector3::Vector3;
pub const NOT_COMPLETE_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_ROTATION_INCOMPLETE);
pub const NOT_COMPLETE_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_ROTATION_INCOMPLETE,
translation::java::ARGUMENT_ROTATION_INCOMPLETE,
);
pub struct RotationArgumentType;

View File

@ -7,8 +7,10 @@ use crate::command::string_reader::StringReader;
use pumpkin_data::translation;
use pumpkin_util::math::vector3::Vector3;
pub const INCOMPLETE_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_POS3D_INCOMPLETE);
pub const INCOMPLETE_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_POS3D_INCOMPLETE,
translation::java::ARGUMENT_POS3D_INCOMPLETE,
);
/// An argument type for a 3-dimensional vector.
pub enum Vec3ArgumentType {

View File

@ -11,24 +11,34 @@ use pumpkin_data::translation;
use std::sync::Arc;
/// A [`CommandErrorType`] to tell that no entities could be found.
pub const NO_ENTITIES_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_ENTITY_NOTFOUND_ENTITY);
pub const NO_ENTITIES_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_ENTITY_NOTFOUND_ENTITY,
translation::java::ARGUMENT_ENTITY_NOTFOUND_ENTITY,
);
/// A [`CommandErrorType`] to tell that no players could be found.
pub const NO_PLAYERS_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_ENTITY_NOTFOUND_PLAYER);
pub const NO_PLAYERS_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_ENTITY_NOTFOUND_PLAYER,
translation::java::ARGUMENT_ENTITY_NOTFOUND_PLAYER,
);
/// A [`CommandErrorType`] to tell that only players are allowed for an entity selector.
pub const ONLY_PLAYERS_ALLOWED_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_PLAYER_ENTITIES);
pub const ONLY_PLAYERS_ALLOWED_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_PLAYER_ENTITIES,
translation::java::ARGUMENT_PLAYER_ENTITIES,
);
/// A [`CommandErrorType`] to tell that only 1 entity is allowed for an entity selector.
pub const NOT_SINGLE_ENTITY_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_ENTITY_TOOMANY);
pub const NOT_SINGLE_ENTITY_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_ENTITY_TOOMANY,
translation::java::ARGUMENT_ENTITY_TOOMANY,
);
/// A [`CommandErrorType`] to tell that only 1 player is allowed for an entity selector.
pub const NOT_SINGLE_PLAYER_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_PLAYER_TOOMANY);
pub const NOT_SINGLE_PLAYER_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_PLAYER_TOOMANY,
translation::java::ARGUMENT_PLAYER_TOOMANY,
);
pub const ENTITY_SELECTOR_PERMISSION: &str = "minecraft:command.selector";

View File

@ -8,8 +8,10 @@ use pumpkin_data::translation;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::text::TextComponent;
pub const INVALID_ERROR_TYPE: CommandErrorType<1> =
CommandErrorType::new(translation::ARGUMENT_ANCHOR_INVALID);
pub const INVALID_ERROR_TYPE: CommandErrorType<1> = CommandErrorType::new(
translation::java::ARGUMENT_ANCHOR_INVALID,
translation::java::ARGUMENT_ANCHOR_INVALID,
);
pub struct EntityAnchorArgumentType;

View File

@ -10,20 +10,34 @@ use pumpkin_util::math::bounds::{DoubleBounds, FloatDegreeBounds, IntBounds};
use pumpkin_util::text::TextComponent;
use std::str::FromStr;
pub const UNKNOWN_OPTION_ERROR_TYPE: CommandErrorType<1> =
CommandErrorType::new(translation::ARGUMENT_ENTITY_OPTIONS_UNKNOWN);
pub const INAPPLICABLE_OPTION_ERROR_TYPE: CommandErrorType<1> =
CommandErrorType::new(translation::ARGUMENT_ENTITY_OPTIONS_INAPPLICABLE);
pub const DISTANCE_NEGATIVE_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_ENTITY_OPTIONS_DISTANCE_NEGATIVE);
pub const LEVEL_NEGATIVE_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_ENTITY_OPTIONS_LEVEL_NEGATIVE);
pub const LIMIT_TOO_SMALL_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_ENTITY_OPTIONS_LIMIT_TOOSMALL);
pub const SORT_UNKNOWN_ERROR_TYPE: CommandErrorType<1> =
CommandErrorType::new(translation::ARGUMENT_ENTITY_OPTIONS_SORT_IRREVERSIBLE);
pub const GAMEMODE_INVALID_ERROR_TYPE: CommandErrorType<1> =
CommandErrorType::new(translation::ARGUMENT_ENTITY_OPTIONS_MODE_INVALID);
pub const UNKNOWN_OPTION_ERROR_TYPE: CommandErrorType<1> = CommandErrorType::new(
translation::java::ARGUMENT_ENTITY_OPTIONS_UNKNOWN,
translation::java::ARGUMENT_ENTITY_OPTIONS_UNKNOWN,
);
pub const INAPPLICABLE_OPTION_ERROR_TYPE: CommandErrorType<1> = CommandErrorType::new(
translation::java::ARGUMENT_ENTITY_OPTIONS_INAPPLICABLE,
translation::java::ARGUMENT_ENTITY_OPTIONS_INAPPLICABLE,
);
pub const DISTANCE_NEGATIVE_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_ENTITY_OPTIONS_DISTANCE_NEGATIVE,
translation::java::ARGUMENT_ENTITY_OPTIONS_DISTANCE_NEGATIVE,
);
pub const LEVEL_NEGATIVE_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_ENTITY_OPTIONS_LEVEL_NEGATIVE,
translation::java::ARGUMENT_ENTITY_OPTIONS_LEVEL_NEGATIVE,
);
pub const LIMIT_TOO_SMALL_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_ENTITY_OPTIONS_LIMIT_TOOSMALL,
translation::java::ARGUMENT_ENTITY_OPTIONS_LIMIT_TOOSMALL,
);
pub const SORT_UNKNOWN_ERROR_TYPE: CommandErrorType<1> = CommandErrorType::new(
translation::java::ARGUMENT_ENTITY_OPTIONS_SORT_IRREVERSIBLE,
translation::java::ARGUMENT_ENTITY_OPTIONS_SORT_IRREVERSIBLE,
);
pub const GAMEMODE_INVALID_ERROR_TYPE: CommandErrorType<1> = CommandErrorType::new(
translation::java::ARGUMENT_ENTITY_OPTIONS_MODE_INVALID,
translation::java::ARGUMENT_ENTITY_OPTIONS_MODE_INVALID,
);
/// Options to customize an [`EntitySelectorParser`].
///

View File

@ -17,23 +17,35 @@ use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::text::TextComponent;
use uuid::Uuid;
pub const INVALID_NAME_OR_UUID_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_ENTITY_INVALID);
pub const INVALID_NAME_OR_UUID_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_ENTITY_INVALID,
translation::java::ARGUMENT_ENTITY_INVALID,
);
pub const UNKNOWN_SELECTOR_TYPE_ERROR_TYPE: CommandErrorType<1> =
CommandErrorType::new(translation::ARGUMENT_ENTITY_SELECTOR_UNKNOWN);
pub const UNKNOWN_SELECTOR_TYPE_ERROR_TYPE: CommandErrorType<1> = CommandErrorType::new(
translation::java::ARGUMENT_ENTITY_SELECTOR_UNKNOWN,
translation::java::ARGUMENT_ENTITY_SELECTOR_UNKNOWN,
);
pub const SELECTORS_NOT_ALLOWED_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_ENTITY_SELECTOR_NOT_ALLOWED);
pub const SELECTORS_NOT_ALLOWED_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_ENTITY_SELECTOR_NOT_ALLOWED,
translation::java::ARGUMENT_ENTITY_SELECTOR_NOT_ALLOWED,
);
pub const MISSING_SELECTOR_TYPE_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_ENTITY_SELECTOR_MISSING);
pub const MISSING_SELECTOR_TYPE_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_ENTITY_SELECTOR_MISSING,
translation::java::ARGUMENT_ENTITY_SELECTOR_MISSING,
);
pub const EXPECTED_END_OF_OPTIONS_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_ENTITY_OPTIONS_UNTERMINATED);
pub const EXPECTED_END_OF_OPTIONS_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_ENTITY_OPTIONS_UNTERMINATED,
translation::java::ARGUMENT_ENTITY_OPTIONS_UNTERMINATED,
);
pub const EXPECTED_OPTION_VALUE_ERROR_TYPE: CommandErrorType<1> =
CommandErrorType::new(translation::ARGUMENT_ENTITY_OPTIONS_VALUELESS);
pub const EXPECTED_OPTION_VALUE_ERROR_TYPE: CommandErrorType<1> = CommandErrorType::new(
translation::java::ARGUMENT_ENTITY_OPTIONS_VALUELESS,
translation::java::ARGUMENT_ENTITY_OPTIONS_VALUELESS,
);
bitflags! {
/// A list of bit flags to set entity selector parser properties.

View File

@ -11,8 +11,10 @@ use crate::net::GameProfile;
use pumpkin_data::translation;
use uuid::Uuid;
pub const UNKNOWN_PLAYER_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_PLAYER_UNKNOWN);
pub const UNKNOWN_PLAYER_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_PLAYER_UNKNOWN,
translation::java::ARGUMENT_PLAYER_UNKNOWN,
);
/// A result from the [`GameProfileArgumentType`], which can be resolved into
/// one or more [`GameProfile`]s, successfully or not.

View File

@ -64,12 +64,18 @@ macro_rules! impl_copy_get {
};
}
const EMPTY_BOUNDS_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_RANGE_EMPTY);
const SWAPPED_BOUNDS_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_RANGE_SWAPPED);
const INVALID_IDENTIFIER_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_ID_INVALID);
const EMPTY_BOUNDS_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_RANGE_EMPTY,
translation::java::ARGUMENT_RANGE_EMPTY,
);
const SWAPPED_BOUNDS_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_RANGE_SWAPPED,
translation::java::ARGUMENT_RANGE_SWAPPED,
);
const INVALID_IDENTIFIER_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_ID_INVALID,
translation::java::ARGUMENT_ID_INVALID,
);
/// A trait to try getting a value from a [`StringReader`].
pub trait FromStringReader: Sized {

View File

@ -6,10 +6,14 @@ use crate::command::string_reader::StringReader;
use pumpkin_data::translation;
use pumpkin_util::text::TextComponent;
pub const INVALID_UNIT_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new(translation::ARGUMENT_TIME_INVALID_UNIT);
pub const TICK_COUNT_TOO_LOW_ERROR_TYPE: CommandErrorType<2> =
CommandErrorType::new(translation::ARGUMENT_TIME_TICK_COUNT_TOO_LOW);
pub const INVALID_UNIT_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(
translation::java::ARGUMENT_TIME_INVALID_UNIT,
translation::java::ARGUMENT_TIME_INVALID_UNIT,
);
pub const TICK_COUNT_TOO_LOW_ERROR_TYPE: CommandErrorType<2> = CommandErrorType::new(
translation::java::ARGUMENT_TIME_TICK_COUNT_TOO_LOW,
translation::java::ARGUMENT_TIME_TICK_COUNT_TOO_LOW,
);
/// Represents an argument type parsing a time value, which can
/// have one of these suffixes:

View File

@ -79,8 +79,9 @@ async fn ban_players(
}
if count == 0 {
Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_BAN_FAILED,
Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_BAN_FAILED,
translation::bedrock::COMMANDS_BAN_FAILED,
[],
)))
} else {
@ -123,8 +124,9 @@ async fn ban_profile(
// Send messages
sender
.send_message(TextComponent::translate(
translation::COMMANDS_BAN_SUCCESS,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_BAN_SUCCESS,
translation::bedrock::COMMANDS_BAN_SUCCESS,
[
TextComponent::text(profile.name.clone()),
TextComponent::text(reason),
@ -136,7 +138,11 @@ async fn ban_profile(
player
.kick(
DisconnectReason::Kicked,
TextComponent::translate(translation::MULTIPLAYER_DISCONNECT_BANNED, []),
TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_BANNED,
translation::bedrock::DISCONNECTIONSCREEN_TITLE_BANNEDBYHOST,
[],
),
)
.await;
}

View File

@ -83,8 +83,9 @@ async fn ban_ip(
let reason = reason.unwrap_or_else(|| "Banned by an operator.".to_string());
let Some(target_ip) = parse_ip(target, server).await else {
return Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_BANIP_INVALID,
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_BANIP_INVALID,
translation::bedrock::COMMANDS_BANIP_INVALID,
[],
)));
};
@ -92,8 +93,9 @@ async fn ban_ip(
let mut banned_ips = server.data.banned_ip_list.write().await;
if banned_ips.get_entry(&target_ip).is_some() {
return Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_BANIP_FAILED,
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_BANIP_FAILED,
translation::java::COMMANDS_BANIP_FAILED,
[],
)));
}
@ -117,8 +119,9 @@ async fn ban_ip(
.join(" ");
sender
.send_message(TextComponent::translate(
translation::COMMANDS_BANIP_SUCCESS,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_BANIP_SUCCESS,
translation::bedrock::COMMANDS_BANIP_SUCCESS,
[
TextComponent::text(target_ip.to_string()),
TextComponent::text(reason),
@ -127,8 +130,9 @@ async fn ban_ip(
.await;
sender
.send_message(TextComponent::translate(
translation::COMMANDS_BANIP_INFO,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_BANIP_INFO,
translation::java::COMMANDS_BANIP_INFO,
[
TextComponent::text(affected.len().to_string()),
TextComponent::text(names),
@ -141,7 +145,11 @@ async fn ban_ip(
target
.kick(
DisconnectReason::Kicked,
TextComponent::translate(translation::MULTIPLAYER_DISCONNECT_IP_BANNED, []),
TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_IP_BANNED,
translation::java::MULTIPLAYER_DISCONNECT_IP_BANNED,
[],
),
)
.await;
}

View File

@ -57,15 +57,20 @@ impl CommandExecutor for BanListCommandExecutor {
if entries.is_empty() {
source
.send_feedback(
TextComponent::translate(translation::COMMANDS_BANLIST_NONE, []),
TextComponent::translate_cross(
translation::java::COMMANDS_BANLIST_NONE,
translation::java::COMMANDS_BANLIST_NONE,
[],
),
false,
)
.await;
} else {
source
.send_feedback(
TextComponent::translate(
translation::COMMANDS_BANLIST_LIST,
TextComponent::translate_cross(
translation::java::COMMANDS_BANLIST_LIST,
translation::java::COMMANDS_BANLIST_LIST,
[TextComponent::text(entries.len().to_string())],
),
false,
@ -75,8 +80,9 @@ impl CommandExecutor for BanListCommandExecutor {
for entry in entries {
source
.send_feedback(
TextComponent::translate(
translation::COMMANDS_BANLIST_ENTRY,
TextComponent::translate_cross(
translation::java::COMMANDS_BANLIST_ENTRY,
translation::java::COMMANDS_BANLIST_ENTRY,
[
TextComponent::text(entry.name),
TextComponent::text(entry.source),

View File

@ -68,7 +68,8 @@ impl CommandExecutor for AddExecutor {
let text_component = TextComponentArgConsumer::find_arg(args, ARG_NAME)?;
if server.bossbars.lock().await.has_bossbar(&namespace) {
return Result::Err(CommandError::CommandFailed(TextComponent::translate(
return Result::Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.bossbar.create.failed",
"commands.bossbar.create.failed",
[TextComponent::text(namespace.clone())],
)));
@ -82,7 +83,8 @@ impl CommandExecutor for AddExecutor {
drop(bossbars);
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.bossbar.create.success",
"commands.bossbar.create.success",
[bossbar_prefix(bossbar.title.clone(), namespace.clone())],
))
@ -116,7 +118,8 @@ impl CommandExecutor for GetExecutor {
match self.0 {
CommandValueGet::Max => {
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.bossbar.get.max",
"commands.bossbar.get.max",
[
bossbar_prefix(
@ -132,7 +135,8 @@ impl CommandExecutor for GetExecutor {
CommandValueGet::Players => Ok(bossbar.players.len() as i32),
CommandValueGet::Value => {
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.bossbar.get.value",
"commands.bossbar.get.value",
[
bossbar_prefix(
@ -152,7 +156,8 @@ impl CommandExecutor for GetExecutor {
"commands.bossbar.get.visible.hidden"
};
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
state,
state,
[bossbar_prefix(
bossbar.bossbar_data.title.clone(),
@ -181,7 +186,8 @@ impl CommandExecutor for ListExecutor {
if bossbars.is_empty() {
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.bossbar.list.bars.none",
"commands.bossbar.list.bars.none",
[],
))
@ -207,7 +213,8 @@ impl CommandExecutor for ListExecutor {
}
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.bossbar.list.bars.some",
"commands.bossbar.list.bars.some",
[
TextComponent::text(bossbars.len().to_string()),
@ -242,7 +249,8 @@ impl CommandExecutor for RemoveExecutor {
};
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.bossbar.remove.success",
"commands.bossbar.remove.success",
[bossbar_prefix(
bossbar.bossbar_data.title.clone(),
@ -306,7 +314,8 @@ impl CommandExecutor for SetExecutor {
}
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.bossbar.set.color.success",
"commands.bossbar.set.color.success",
[bossbar_prefix(
bossbar.bossbar_data.title.clone(),
@ -319,7 +328,8 @@ impl CommandExecutor for SetExecutor {
}
CommandValueSet::Max => {
let Ok(max_value) = max_value_consumer().find_arg_default_name(args)? else {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
"parsing.int.invalid",
"parsing.int.invalid",
[TextComponent::text(i32::MAX.to_string())],
)));
@ -339,7 +349,8 @@ impl CommandExecutor for SetExecutor {
}
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.bossbar.set.max.success",
"commands.bossbar.set.max.success",
[
bossbar_prefix(
@ -369,7 +380,8 @@ impl CommandExecutor for SetExecutor {
}
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.bossbar.set.name.success",
"commands.bossbar.set.name.success",
[bossbar_prefix(text_component, namespace.to_string())],
))
@ -392,7 +404,8 @@ impl CommandExecutor for SetExecutor {
}
}
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.bossbar.set.players.success.none",
"commands.bossbar.set.players.success.none",
[bossbar_prefix(
bossbar.bossbar_data.title.clone(),
@ -429,7 +442,8 @@ impl CommandExecutor for SetExecutor {
.join(", ");
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.bossbar.set.players.success.some",
"commands.bossbar.set.players.success.some",
[
bossbar_prefix(
@ -459,7 +473,8 @@ impl CommandExecutor for SetExecutor {
}
}
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.bossbar.set.style.success",
"commands.bossbar.set.style.success",
[bossbar_prefix(
bossbar.bossbar_data.title.clone(),
@ -471,7 +486,8 @@ impl CommandExecutor for SetExecutor {
}
CommandValueSet::Value => {
let Ok(value) = value_consumer().find_arg_default_name(args)? else {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
"parsing.int.invalid",
"parsing.int.invalid",
[TextComponent::text(i32::MAX.to_string())],
)));
@ -491,7 +507,8 @@ impl CommandExecutor for SetExecutor {
}
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.bossbar.set.value.success",
"commands.bossbar.set.value.success",
[
bossbar_prefix(
@ -528,7 +545,8 @@ impl CommandExecutor for SetExecutor {
};
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
state,
state,
[bossbar_prefix(
bossbar.bossbar_data.title.clone(),
@ -635,8 +653,9 @@ fn bossbar_prefix(title: TextComponent, namespace: String) -> TextComponent {
fn handle_bossbar_error(error: BossbarUpdateError) -> CommandError {
match error {
BossbarUpdateError::InvalidResourceLocation(location) => {
CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_BOSSBAR_UNKNOWN,
CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_BOSSBAR_UNKNOWN,
translation::java::COMMANDS_BOSSBAR_UNKNOWN,
[TextComponent::text(location)],
))
}
@ -646,7 +665,7 @@ fn handle_bossbar_error(error: BossbarUpdateError) -> CommandError {
|var| format!("commands.bossbar.set.{value}.unchanged.{var}"),
);
CommandError::CommandFailed(TextComponent::translate(key, []))
CommandError::CommandFailed(TextComponent::translate_cross(key.clone(), key, []))
}
}
}

View File

@ -143,37 +143,43 @@ async fn clear_command_text_output(
targets: &[Arc<Player>],
) -> Result<TextComponent, TextComponent> {
match (targets, item_count == 0, max_count == 0) {
([target], true, _) => Err(TextComponent::translate(
translation::CLEAR_FAILED_SINGLE,
([target], true, _) => Err(TextComponent::translate_cross(
translation::java::CLEAR_FAILED_SINGLE,
translation::java::CLEAR_FAILED_SINGLE,
[target.get_display_name().await],
)),
(targets, true, _) => Err(TextComponent::translate(
translation::CLEAR_FAILED_MULTIPLE,
(targets, true, _) => Err(TextComponent::translate_cross(
translation::java::CLEAR_FAILED_MULTIPLE,
translation::java::CLEAR_FAILED_MULTIPLE,
[TextComponent::text(targets.len().to_string())],
)),
([target], false, false) => Ok(TextComponent::translate(
translation::COMMANDS_CLEAR_SUCCESS_SINGLE,
([target], false, false) => Ok(TextComponent::translate_cross(
translation::java::COMMANDS_CLEAR_SUCCESS_SINGLE,
translation::java::COMMANDS_CLEAR_SUCCESS_SINGLE,
[
TextComponent::text(item_count.to_string()),
target.get_display_name().await,
],
)),
(targets, false, false) => Ok(TextComponent::translate(
translation::COMMANDS_CLEAR_SUCCESS_MULTIPLE,
(targets, false, false) => Ok(TextComponent::translate_cross(
translation::java::COMMANDS_CLEAR_SUCCESS_MULTIPLE,
translation::java::COMMANDS_CLEAR_SUCCESS_MULTIPLE,
[
TextComponent::text(item_count.to_string()),
TextComponent::text(targets.len().to_string()),
],
)),
([target], false, true) => Ok(TextComponent::translate(
translation::COMMANDS_CLEAR_TEST_SINGLE,
([target], false, true) => Ok(TextComponent::translate_cross(
translation::java::COMMANDS_CLEAR_TEST_SINGLE,
translation::java::COMMANDS_CLEAR_TEST_SINGLE,
[
TextComponent::text(item_count.to_string()),
target.get_display_name().await,
],
)),
(targets, false, true) => Ok(TextComponent::translate(
translation::COMMANDS_CLEAR_TEST_MULTIPLE,
(targets, false, true) => Ok(TextComponent::translate_cross(
translation::java::COMMANDS_CLEAR_TEST_MULTIPLE,
translation::java::COMMANDS_CLEAR_TEST_MULTIPLE,
[
TextComponent::text(item_count.to_string()),
TextComponent::text(targets.len().to_string()),
@ -273,8 +279,9 @@ impl CommandExecutor for ItemCountExecutor {
let item = ItemPredicateArgumentConsumer::find_arg(args, ARG_ITEM)?;
let Ok(max) = count_consumer().find_arg_default_name(args)? else {
return Err(CommandError::CommandFailed(TextComponent::translate(
translation::PARSING_INT_INVALID,
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::PARSING_INT_INVALID,
translation::java::PARSING_INT_INVALID,
[TextComponent::text(i32::MAX.to_string())],
)));
};

View File

@ -39,15 +39,17 @@ async fn send_damage_result(
target_name: TextComponent,
) -> Result<i32, CommandError> {
if !success {
return Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_DAMAGE_INVULNERABLE,
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_DAMAGE_INVULNERABLE,
translation::java::COMMANDS_DAMAGE_INVULNERABLE,
[],
)));
}
sender
.send_message(TextComponent::translate(
translation::COMMANDS_DAMAGE_SUCCESS,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_DAMAGE_SUCCESS,
translation::bedrock::COMMANDS_DAMAGE_SUCCESS,
[TextComponent::text(amount.to_string()), target_name],
))
.await;

View File

@ -232,8 +232,9 @@ async fn display_data(
let display = TextComponent::text(format!("PNBT Data ({} bytes)", nbt.data.len()));
sender
.send_message(TextComponent::translate(
translation::COMMANDS_DATA_ENTITY_QUERY,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_DATA_ENTITY_QUERY,
translation::java::COMMANDS_DATA_ENTITY_QUERY,
[target_name, display],
))
.await;
@ -244,8 +245,9 @@ async fn display_data(
#[allow(dead_code)]
fn get_i32_result(tag: &NbtTag) -> Result<i32, CommandError> {
match tag {
NbtTag::End => Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_DATA_GET_UNKNOWN,
NbtTag::End => Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_DATA_GET_UNKNOWN,
translation::java::COMMANDS_DATA_GET_UNKNOWN,
[],
))),

View File

@ -44,9 +44,14 @@ impl CommandExecutor for DefaultGamemodeExecutor {
let gamemode_string = format!("gameMode.{gamemode_string}");
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.defaultgamemode.success",
[TextComponent::translate(gamemode_string, [])],
"commands.defaultgamemode.success",
[TextComponent::translate_cross(
gamemode_string.clone(),
gamemode_string,
[],
)],
))
.await;

View File

@ -51,7 +51,8 @@ impl CommandExecutor for Executor {
.await;
}
let msg = TextComponent::translate(
let msg = TextComponent::translate_cross(
"commands.deop.success",
"commands.deop.success",
[TextComponent::text(profile.name.clone())],
);
@ -64,7 +65,8 @@ impl CommandExecutor for Executor {
}
if succeeded_deops == 0 {
Err(CommandError::CommandFailed(TextComponent::translate(
Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.deop.failed",
"commands.deop.failed",
[],
)))

View File

@ -12,7 +12,7 @@ const DESCRIPTION: &str = "Query or change the difficulty of the world.";
const PERMISSION: &str = "minecraft:command.difficulty";
const FAILURE_ERROR_TYPE: CommandErrorType<1> =
CommandErrorType::new("commands.difficulty.failure");
CommandErrorType::new("commands.difficulty.failure", "commands.difficulty.failure");
struct DifficultyQueryExecutor;
@ -24,9 +24,14 @@ impl CommandExecutor for DifficultyQueryExecutor {
context
.source
.send_feedback(
TextComponent::translate(
TextComponent::translate_cross(
"commands.difficulty.query",
[TextComponent::translate(difficulty.translation_key(), [])],
"commands.difficulty.query",
[TextComponent::translate_cross(
difficulty.translation_key(),
difficulty.translation_key(),
[],
)],
),
false,
)
@ -59,9 +64,14 @@ impl CommandExecutor for DifficultySetExecutor {
context
.source
.send_feedback(
TextComponent::translate(
TextComponent::translate_cross(
"commands.difficulty.success",
[TextComponent::translate(difficulty.translation_key(), [])],
"commands.difficulty.success",
[TextComponent::translate_cross(
difficulty.translation_key(),
difficulty.translation_key(),
[],
)],
),
true,
)

View File

@ -113,23 +113,30 @@ impl CommandExecutor for GiveExecutor {
}
}
let translation_name = TextComponent::translate(effect.translation_key.to_string(), []);
let translation_name = TextComponent::translate_cross(
effect.translation_key.to_string(),
effect.translation_key.to_string(),
[],
);
if successes == 0 {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.effect.give.failed",
"commands.effect.give.failed",
[],
)));
} else if targets.len() == 1 {
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.effect.give.success.single",
"commands.effect.give.success.single",
[translation_name, targets[0].get_display_name().await],
))
.await;
} else {
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.effect.give.success.multiple",
"commands.effect.give.success.multiple",
[
translation_name,
@ -170,7 +177,8 @@ impl CommandExecutor for ClearExecutor {
//if the player or everyplayer don't have any effect
if succeeded_clears == 0 {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.effect.clear.everything.failed",
"commands.effect.clear.everything.failed",
[],
)));
@ -178,14 +186,16 @@ impl CommandExecutor for ClearExecutor {
//a player have at least 1 effect
else if targets.len() == 1 {
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.effect.clear.everything.success.single",
"commands.effect.clear.everything.success.single",
[targets[0].get_display_name().await],
))
.await;
} else {
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.effect.clear.everything.success.multiple",
"commands.effect.clear.everything.success.multiple",
[TextComponent::text(targets.len().to_string())],
))
@ -208,26 +218,37 @@ impl CommandExecutor for ClearExecutor {
}
if succeeded_clears == 0 {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.effect.clear.specific.failed",
"commands.effect.clear.specific.failed",
[],
)));
} else if targets.len() == 1 {
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.effect.clear.specific.success.single",
"commands.effect.clear.specific.success.single",
[
TextComponent::translate(effect.translation_key, []),
TextComponent::translate_cross(
effect.translation_key,
effect.translation_key,
[],
),
targets[0].get_display_name().await,
],
))
.await;
} else {
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.effect.clear.specific.success.multiple",
"commands.effect.clear.specific.success.multiple",
[
TextComponent::translate(effect.translation_key, []),
TextComponent::translate_cross(
effect.translation_key,
effect.translation_key,
[],
),
TextComponent::text(targets.len().to_string()),
],
))

View File

@ -31,14 +31,16 @@ impl CommandExecutor for Executor {
Ok(Ok(level)) => level,
Ok(Err(err)) => {
let err_msg = match err {
NotInBounds::LowerBound(val, min) => TextComponent::translate(
NotInBounds::LowerBound(val, min) => TextComponent::translate_cross(
"argument.integer.low",
"argument.integer.low",
&[
TextComponent::text(min.to_string()),
TextComponent::text(val.to_string()),
],
),
NotInBounds::UpperBound(val, max) => TextComponent::translate(
NotInBounds::UpperBound(val, max) => TextComponent::translate_cross(
"argument.integer.big",
"argument.integer.big",
&[
TextComponent::text(max.to_string()),
@ -52,7 +54,8 @@ impl CommandExecutor for Executor {
};
if level > enchantment.max_level {
let msg = TextComponent::translate(
let msg = TextComponent::translate_cross(
"commands.enchant.failed.level",
"commands.enchant.failed.level",
[
TextComponent::text(level.to_string()),
@ -68,9 +71,7 @@ impl CommandExecutor for Executor {
for target in targets {
// let Some(target) = target.get_living_entity() else {
// if only_one {
// let msg = TextComponent::translate(
// "commands.enchant.failed.entity",
// [targets[0].get_display_name().await],
// let msg = TextComponent::translate_cross(// "commands.enchant.failed.entity".clone(), // "commands.enchant.failed.entity", // [targets[0].get_display_name().await],
// );
// sender.send_message(msg).await;
// return Ok(());
@ -85,7 +86,8 @@ impl CommandExecutor for Executor {
let mut item = lock.lock().await;
if item.is_empty() {
if only_one {
let msg = TextComponent::translate(
let msg = TextComponent::translate_cross(
"commands.enchant.failed.itemless",
"commands.enchant.failed.itemless",
[targets[0].get_display_name().await],
);
@ -95,7 +97,8 @@ impl CommandExecutor for Executor {
}
if !enchantment.can_enchant(item.item) {
if only_one {
let msg = TextComponent::translate(
let msg = TextComponent::translate_cross(
"commands.enchant.failed.incompatible",
"commands.enchant.failed.incompatible",
[item.item.translated_name()],
);
@ -108,7 +111,8 @@ impl CommandExecutor for Executor {
item.enchant(enchantment, level);
success += 1;
} else if only_one {
let msg = TextComponent::translate(
let msg = TextComponent::translate_cross(
"commands.enchant.failed.incompatible",
"commands.enchant.failed.incompatible",
[item.item.translated_name()],
);
@ -120,12 +124,17 @@ impl CommandExecutor for Executor {
}
}
if success == 0 {
let msg = TextComponent::translate(translation::COMMANDS_ENCHANT_FAILED, []);
let msg = TextComponent::translate_cross(
translation::java::COMMANDS_ENCHANT_FAILED,
translation::java::COMMANDS_ENCHANT_FAILED,
[],
);
return Err(CommandError::CommandFailed(msg));
}
if only_one {
let msg = TextComponent::translate(
translation::COMMANDS_ENCHANT_SUCCESS_SINGLE,
let msg = TextComponent::translate_cross(
translation::java::COMMANDS_ENCHANT_SUCCESS_SINGLE,
translation::java::COMMANDS_ENCHANT_SUCCESS_SINGLE,
[
enchantment.get_fullname(level),
targets[0].get_display_name().await,
@ -133,8 +142,9 @@ impl CommandExecutor for Executor {
);
sender.send_message(msg).await;
} else {
let msg = TextComponent::translate(
translation::COMMANDS_ENCHANT_SUCCESS_MULTIPLE,
let msg = TextComponent::translate_cross(
translation::java::COMMANDS_ENCHANT_SUCCESS_MULTIPLE,
translation::java::COMMANDS_ENCHANT_SUCCESS_MULTIPLE,
[
enchantment.get_fullname(level),
TextComponent::text(targets.len().to_string()),

View File

@ -58,7 +58,8 @@ impl Executor {
};
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
translation_key,
translation_key,
[
target.get_display_name().await,
@ -87,7 +88,8 @@ impl Executor {
};
if targets.len() > 1 {
TextComponent::translate(
TextComponent::translate_cross(
format!("commands.experience.{mode_str}.{type_str}.success.multiple"),
format!("commands.experience.{mode_str}.{type_str}.success.multiple"),
[
TextComponent::text(amount.to_string()),
@ -95,7 +97,8 @@ impl Executor {
],
)
} else {
TextComponent::translate(
TextComponent::translate_cross(
format!("commands.experience.{mode_str}.{type_str}.success.single"),
format!("commands.experience.{mode_str}.{type_str}.success.single"),
[TextComponent::text(amount.to_string()), first_target_name],
)
@ -140,14 +143,16 @@ impl CommandExecutor for Executor {
if self.mode == Mode::Query {
let target = targets.first().ok_or_else(|| {
CommandError::CommandFailed(TextComponent::translate(
CommandError::CommandFailed(TextComponent::translate_cross(
"argument.player.unknown",
"argument.player.unknown",
[],
))
})?;
if targets.len() > 1 {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
"argument.player.toomany",
"argument.player.toomany",
[],
)));
@ -166,7 +171,8 @@ impl CommandExecutor for Executor {
}
if successes == 0 {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.experience.set.points.invalid",
"commands.experience.set.points.invalid",
[],
)));

View File

@ -286,8 +286,9 @@ impl CommandExecutor for Executor {
};
if !context.world.is_in_build_limit(from) || !context.world.is_in_build_limit(to) {
return Err(CommandError::CommandFailed(TextComponent::translate(
translation::ARGUMENT_POS_OUTOFBOUNDS,
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::ARGUMENT_POS_OUTOFBOUNDS,
translation::java::ARGUMENT_POS_OUTOFBOUNDS,
[],
)));
}
@ -302,8 +303,9 @@ impl CommandExecutor for Executor {
* (context.end_z - context.start_z + 1) as i64;
if total_blocks > max_block_modifications {
return Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_FILL_TOOBIG,
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_FILL_TOOBIG,
translation::java::COMMANDS_FILL_TOOBIG,
[
TextComponent::text(max_block_modifications.to_string()),
TextComponent::text(total_blocks.to_string()),
@ -325,15 +327,17 @@ impl CommandExecutor for Executor {
}
if context.placed_blocks == 0 {
return Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_FILL_FAILED,
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_FILL_FAILED,
translation::bedrock::COMMANDS_FILL_FAILED,
[],
)));
}
sender
.send_message(TextComponent::translate(
translation::COMMANDS_FILL_SUCCESS,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_FILL_SUCCESS,
translation::bedrock::COMMANDS_FILL_SUCCESS,
[TextComponent::text(context.placed_blocks.to_string())],
))
.await;

View File

@ -2,6 +2,7 @@ use crate::command::args::GetCloned;
use crate::command::args::gamemode::GamemodeArgumentConsumer;
use crate::TextComponent;
use pumpkin_data::translation;
use crate::command::args::players::PlayersArgumentConsumer;
@ -54,29 +55,34 @@ impl CommandExecutor for TargetExecutor {
let gamemode_string = format!("{gamemode:?}").to_lowercase();
let gamemode_string = format!("gameMode.{gamemode_string}");
// Checking if the target was the sender of this command.
let gamemode_comp = TextComponent::translate_cross(
gamemode_string.clone(),
gamemode_string.clone(),
[],
);
if sender.as_player().as_ref() == Some(target) {
target
.send_system_message(&TextComponent::translate(
"commands.gamemode.success.self",
[TextComponent::translate(gamemode_string, [])],
.send_system_message(&TextComponent::translate_cross(
translation::java::COMMANDS_GAMEMODE_SUCCESS_SELF,
translation::bedrock::COMMANDS_GAMEMODE_SUCCESS_SELF,
[gamemode_comp],
))
.await;
} else {
if server.level_info.load().game_rules.send_command_feedback {
target
.send_system_message(&TextComponent::translate(
"gameMode.changed",
[TextComponent::translate(gamemode_string.clone(), [])],
.send_system_message(&TextComponent::translate_cross(
translation::java::GAMEMODE_CHANGED,
translation::bedrock::GAMEMODE_CHANGED,
[gamemode_comp.clone()],
))
.await;
}
sender
.send_message(TextComponent::translate(
"commands.gamemode.success.other",
[
target.get_display_name().await,
TextComponent::translate(gamemode_string, []),
],
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_GAMEMODE_SUCCESS_OTHER,
translation::bedrock::COMMANDS_GAMEMODE_SUCCESS_OTHER,
[target.get_display_name().await, gamemode_comp],
))
.await;
}

View File

@ -40,7 +40,8 @@ impl CommandExecutor for QueryExecutor {
drop(level_info);
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.gamerule.query",
"commands.gamerule.query",
[key, value],
))
@ -93,7 +94,8 @@ impl CommandExecutor for SetExecutor {
let value_component = TextComponent::text(output_value);
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.gamerule.set",
"commands.gamerule.set",
[key, value_component],
))

View File

@ -81,7 +81,8 @@ impl CommandExecutor for Executor {
}
let msg = if targets.len() == 1 {
TextComponent::translate(
TextComponent::translate_cross(
"commands.give.success.single",
"commands.give.success.single",
[
TextComponent::text(item_count.to_string()),
@ -96,7 +97,8 @@ impl CommandExecutor for Executor {
],
)
} else {
TextComponent::translate(
TextComponent::translate_cross(
"commands.give.success.multiple",
"commands.give.success.multiple",
[
TextComponent::text(item_count.to_string()),

View File

@ -17,7 +17,8 @@ use pumpkin_util::text::color::{Color, NamedColor};
const NO_COMMANDS_ERROR_TYPE: LiteralCommandErrorType =
LiteralCommandErrorType::new("No commands are available to show help for");
const FAILED_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new("commands.help.failed");
const FAILED_ERROR_TYPE: CommandErrorType<0> =
CommandErrorType::new("commands.help.failed", "commands.help.failed");
const PLUGIN_NOT_FOUND_ERROR_TYPE: LiteralCommandErrorType =
LiteralCommandErrorType::new("Plugin not found or has no commands");

View File

@ -36,7 +36,11 @@ impl CommandExecutor for Executor {
let reason = match args.get(&ARG_REASON) {
Some(Arg::Msg(r)) => TextComponent::text(r.clone()),
_ => TextComponent::translate(translation::MULTIPLAYER_DISCONNECT_KICKED, []),
_ => TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_KICKED,
translation::java::MULTIPLAYER_DISCONNECT_KICKED,
[],
),
};
for target in targets {

View File

@ -27,13 +27,15 @@ impl CommandExecutor for TargetsExecutor {
}
let msg = if target_count == 1 {
TextComponent::translate(
translation::COMMANDS_KILL_SUCCESS_SINGLE,
TextComponent::translate_cross(
translation::java::COMMANDS_KILL_SUCCESS_SINGLE,
translation::java::COMMANDS_KILL_SUCCESS_SINGLE,
[targets[0].get_display_name().await],
)
} else {
TextComponent::translate(
translation::COMMANDS_KILL_SUCCESS_MULTIPLE,
TextComponent::translate_cross(
translation::java::COMMANDS_KILL_SUCCESS_MULTIPLE,
translation::java::COMMANDS_KILL_SUCCESS_MULTIPLE,
[TextComponent::text(target_count.to_string())],
)
};
@ -56,8 +58,9 @@ impl CommandExecutor for SelfExecutor {
context
.source
.send_feedback(
TextComponent::translate(
translation::COMMANDS_KILL_SUCCESS_SINGLE,
TextComponent::translate_cross(
translation::java::COMMANDS_KILL_SUCCESS_SINGLE,
translation::java::COMMANDS_KILL_SUCCESS_SINGLE,
[target.get_display_name().await],
),
true,

View File

@ -1,6 +1,6 @@
use std::sync::Arc;
use pumpkin_data::translation::{COMMANDS_LIST_NAMEANDID, COMMANDS_LIST_PLAYERS};
use pumpkin_data::translation::java::{COMMANDS_LIST_NAMEANDID, COMMANDS_LIST_PLAYERS};
use pumpkin_util::{
permission::{Permission, PermissionDefault, PermissionRegistry},
text::TextComponent,
@ -40,7 +40,8 @@ impl CommandExecutor for ListCommandExecutor {
context
.source
.send_feedback(
TextComponent::translate(
TextComponent::translate_cross(
COMMANDS_LIST_PLAYERS,
COMMANDS_LIST_PLAYERS,
[
TextComponent::text(players_len.to_string()),
@ -70,7 +71,8 @@ fn get_player_names_and_ids(players: &[Arc<Player>]) -> TextComponent {
let names_and_ids = players
.iter()
.map(|p| {
TextComponent::translate(
TextComponent::translate_cross(
COMMANDS_LIST_NAMEANDID,
COMMANDS_LIST_NAMEANDID,
&[
p.get_name(),

View File

@ -64,7 +64,8 @@ impl CommandExecutor for Executor {
}
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.op.success",
"commands.op.success",
[TextComponent::text(profile.name.clone())],
))
@ -78,7 +79,8 @@ impl CommandExecutor for Executor {
}
if successes == 0 {
Err(CommandError::CommandFailed(TextComponent::translate(
Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.op.failed",
"commands.op.failed",
[],
)))

View File

@ -43,7 +43,8 @@ impl CommandExecutor for Executor {
if let Some(idx) = idx {
lock.banned_players.remove(idx);
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.pardon.success",
"commands.pardon.success",
[TextComponent::text(target.name.clone())],
))
@ -56,7 +57,8 @@ impl CommandExecutor for Executor {
lock.save();
Ok(successes)
} else {
Err(CommandError::CommandFailed(TextComponent::translate(
Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.pardon.failed",
"commands.pardon.failed",
[],
)))

View File

@ -31,7 +31,8 @@ impl CommandExecutor for Executor {
};
let Ok(ip) = IpAddr::from_str(target) else {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.pardonip.invalid",
"commands.pardonip.invalid",
[],
)));
@ -43,14 +44,16 @@ impl CommandExecutor for Executor {
{
lock.banned_ips.remove(idx);
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.pardonip.success",
"commands.pardonip.success",
[TextComponent::text(ip.to_string())],
))
.await;
Ok(1)
} else {
Err(CommandError::CommandFailed(TextComponent::translate(
Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.pardonip.failed",
"commands.pardonip.failed",
[],
)))

View File

@ -74,7 +74,8 @@ impl CommandExecutor for Executor {
.await;
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.particle.success",
"commands.particle.success",
[TextComponent::text(format!("{particle:?}"))],
))

View File

@ -141,7 +141,8 @@ impl CommandExecutor for Executor {
// Send appropriate message based on results
if players_who_heard == 0 {
Err(CommandError::CommandFailed(TextComponent::translate(
Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.playsound.failed",
"commands.playsound.failed",
[],
)))
@ -149,8 +150,9 @@ impl CommandExecutor for Executor {
let sound_name = sound.to_name();
if players_who_heard == 1 {
sender
.send_message(TextComponent::translate(
translation::COMMANDS_PLAYSOUND_SUCCESS_SINGLE,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_PLAYSOUND_SUCCESS_SINGLE,
translation::java::COMMANDS_PLAYSOUND_SUCCESS_SINGLE,
[
TextComponent::text(sound_name),
targets[0].get_display_name().await,
@ -159,8 +161,9 @@ impl CommandExecutor for Executor {
.await;
} else {
sender
.send_message(TextComponent::translate(
translation::COMMANDS_PLAYSOUND_SUCCESS_MULTIPLE,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_PLAYSOUND_SUCCESS_MULTIPLE,
translation::java::COMMANDS_PLAYSOUND_SUCCESS_MULTIPLE,
[
TextComponent::text(sound_name),
TextComponent::text(players_who_heard.to_string()),

View File

@ -89,8 +89,9 @@ async fn rotate_entity(
async fn send_success_message(sender: &CommandSender, target: &dyn crate::entity::EntityBase) {
let target_name = target.get_display_name().await;
sender
.send_message(TextComponent::translate(
translation::COMMANDS_ROTATE_SUCCESS,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_ROTATE_SUCCESS,
translation::java::COMMANDS_ROTATE_SUCCESS,
[target_name],
))
.await;

View File

@ -2,7 +2,7 @@ use crate::command::argument_builder::{ArgumentBuilder, command};
use crate::command::context::command_context::CommandContext;
use crate::command::node::dispatcher::CommandDispatcher;
use crate::command::node::{CommandExecutor, CommandExecutorResult};
use pumpkin_data::translation::{CHAT_COPY_CLICK, COMMANDS_SEED_SUCCESS};
use pumpkin_data::translation::java::{CHAT_COPY_CLICK, COMMANDS_SEED_SUCCESS};
use pumpkin_util::PermissionLvl;
use pumpkin_util::permission::{Permission, PermissionDefault, PermissionRegistry};
use pumpkin_util::text::click::ClickEvent;
@ -16,11 +16,13 @@ const PERMISSION: &str = "minecraft:command.seed";
struct SeedCommandExecutor;
fn create_copy_on_click_text(content: String) -> TextComponent {
TextComponent::translate(
TextComponent::translate_cross(
COMMANDS_SEED_SUCCESS,
COMMANDS_SEED_SUCCESS,
[TextComponent::wrap_in_square_brackets(
TextComponent::text(content.clone())
.hover_event(HoverEvent::show_text(TextComponent::translate(
.hover_event(HoverEvent::show_text(TextComponent::translate_cross(
CHAT_COPY_CLICK,
CHAT_COPY_CLICK,
[],
)))

View File

@ -111,7 +111,8 @@ impl CommandExecutor for Executor {
if success {
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.setblock.success",
"commands.setblock.success",
[
TextComponent::text(pos.0.x.to_string()),
@ -122,7 +123,8 @@ impl CommandExecutor for Executor {
.await;
Ok(1)
} else {
Err(CommandError::CommandFailed(TextComponent::translate(
Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.setblock.failed",
"commands.setblock.failed",
[],
)))

View File

@ -33,12 +33,17 @@ impl CommandExecutor for SetIdleTimeoutExecutor {
{
if minutes == 0 {
context.source.send_feedback(
TextComponent::translate("commands.setidletimeout.success.disabled", []),
TextComponent::translate_cross(
"commands.setidletimeout.success.disabled",
"commands.setidletimeout.success.disabled",
[],
),
true,
)
} else {
context.source.send_feedback(
TextComponent::translate(
TextComponent::translate_cross(
"commands.setidletimeout.success",
"commands.setidletimeout.success",
[TextComponent::text(minutes.to_string())],
),

View File

@ -108,8 +108,9 @@ async fn setworldspawn(
)));
};
if world.dimension != Dimension::OVERWORLD && world.dimension != Dimension::OVERWORLD_CAVES {
return Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_SETWORLDSPAWN_FAILURE_NOT_OVERWORLD,
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_SETWORLDSPAWN_FAILURE_NOT_OVERWORLD,
translation::java::COMMANDS_SETWORLDSPAWN_FAILURE_NOT_OVERWORLD,
[],
)));
}
@ -150,8 +151,9 @@ async fn setworldspawn(
server.level_info.store(Arc::new(new_info));
sender
.send_message(TextComponent::translate(
translation::COMMANDS_SETWORLDSPAWN_SUCCESS_NEW,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_SETWORLDSPAWN_SUCCESS_NEW,
translation::java::COMMANDS_SETWORLDSPAWN_SUCCESS_NEW,
[
TextComponent::text(new_position.0.x.to_string()),
TextComponent::text(new_position.0.y.to_string()),

View File

@ -131,8 +131,9 @@ async fn set_spawnpoint(sender: &CommandSender, target: &Arc<Player>, pos: Block
.await;
sender
.send_message(TextComponent::translate(
translation::COMMANDS_SPAWNPOINT_SUCCESS_SINGLE_NEW,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_SPAWNPOINT_SUCCESS_SINGLE_NEW,
translation::java::COMMANDS_SPAWNPOINT_SUCCESS_SINGLE_NEW,
[
TextComponent::text(pos.0.x.to_string()),
TextComponent::text(pos.0.y.to_string()),

View File

@ -22,7 +22,11 @@ impl CommandExecutor for StopCommandExecutor {
context
.source
.send_feedback(
TextComponent::translate(translation::COMMANDS_STOP_STOPPING, [])
TextComponent::translate_cross(
translation::java::COMMANDS_STOP_STOPPING,
translation::bedrock::COMMANDS_STOP_START,
[],
)
.color_named(NamedColor::Red),
true,
)

View File

@ -45,23 +45,27 @@ impl CommandExecutor for Executor {
}
let text = match (category, sound) {
(Ok(c), Ok(s)) => TextComponent::translate(
translation::COMMANDS_STOPSOUND_SUCCESS_SOURCE_SOUND,
(Ok(c), Ok(s)) => TextComponent::translate_cross(
translation::java::COMMANDS_STOPSOUND_SUCCESS_SOURCE_SOUND,
translation::java::COMMANDS_STOPSOUND_SUCCESS_SOURCE_SOUND,
[
TextComponent::text(s.to_name()),
TextComponent::text(c.to_name()),
],
),
(Ok(c), Err(_)) => TextComponent::translate(
translation::COMMANDS_STOPSOUND_SUCCESS_SOURCE_ANY,
(Ok(c), Err(_)) => TextComponent::translate_cross(
translation::java::COMMANDS_STOPSOUND_SUCCESS_SOURCE_ANY,
translation::java::COMMANDS_STOPSOUND_SUCCESS_SOURCE_ANY,
[TextComponent::text(c.to_name())],
),
(Err(_), Ok(s)) => TextComponent::translate(
translation::COMMANDS_STOPSOUND_SUCCESS_SOURCELESS_SOUND,
(Err(_), Ok(s)) => TextComponent::translate_cross(
translation::java::COMMANDS_STOPSOUND_SUCCESS_SOURCELESS_SOUND,
translation::java::COMMANDS_STOPSOUND_SUCCESS_SOURCELESS_SOUND,
[TextComponent::text(s.to_name())],
),
(Err(_), Err(_)) => TextComponent::translate(
translation::COMMANDS_STOPSOUND_SUCCESS_SOURCELESS_ANY,
(Err(_), Err(_)) => TextComponent::translate_cross(
translation::java::COMMANDS_STOPSOUND_SUCCESS_SOURCELESS_ANY,
translation::java::COMMANDS_STOPSOUND_SUCCESS_SOURCELESS_ANY,
[],
),
};

View File

@ -68,8 +68,9 @@ impl CommandExecutor for Executor {
let name = entity.get_display_name().await;
world.spawn_entity(entity).await;
sender
.send_message(TextComponent::translate(
translation::COMMANDS_SUMMON_SUCCESS,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_SUMMON_SUCCESS,
translation::bedrock::COMMANDS_SUMMON_SUCCESS,
[name],
))
.await;

View File

@ -81,8 +81,9 @@ impl CommandExecutor for EntitiesToEntityExecutor {
let pitch = destination.pitch.load();
let world = destination.world.load_full();
if !World::is_valid(BlockPos(pos.floor_to_i32())) {
return Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_TELEPORT_INVALIDPOSITION,
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
[],
)));
}
@ -112,8 +113,9 @@ impl CommandExecutor for EntitiesToPosFacingPosExecutor {
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
if !World::is_valid(BlockPos(pos.floor_to_i32())) {
return Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_TELEPORT_INVALIDPOSITION,
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
[],
)));
}
@ -147,8 +149,9 @@ impl CommandExecutor for EntitiesToPosFacingEntityExecutor {
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
if !World::is_valid(BlockPos(pos.floor_to_i32())) {
return Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_TELEPORT_INVALIDPOSITION,
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
[],
)));
}
@ -183,8 +186,9 @@ impl CommandExecutor for EntitiesToPosWithRotationExecutor {
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
if !World::is_valid(BlockPos(pos.floor_to_i32())) {
return Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_TELEPORT_INVALIDPOSITION,
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
[],
)));
}
@ -219,8 +223,9 @@ impl CommandExecutor for EntitiesToPosExecutor {
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
if !World::is_valid(BlockPos(pos.floor_to_i32())) {
return Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_TELEPORT_INVALIDPOSITION,
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
[],
)));
}
@ -259,8 +264,9 @@ impl CommandExecutor for SelfToEntityExecutor {
match sender {
CommandSender::Player(player) => {
if !World::is_valid(BlockPos(pos.floor_to_i32())) {
return Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_TELEPORT_INVALIDPOSITION,
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
[],
)));
}
@ -271,8 +277,9 @@ impl CommandExecutor for SelfToEntityExecutor {
Ok(1)
}
_ => Err(CommandError::CommandFailed(TextComponent::translate(
translation::PERMISSIONS_REQUIRES_PLAYER,
_ => Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::PERMISSIONS_REQUIRES_PLAYER,
translation::java::PERMISSIONS_REQUIRES_PLAYER,
[],
))),
}
@ -295,8 +302,9 @@ impl CommandExecutor for SelfToPosExecutor {
let yaw = player.living_entity.entity.yaw.load();
let pitch = player.living_entity.entity.pitch.load();
if !World::is_valid(BlockPos(pos.floor_to_i32())) {
return Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_TELEPORT_INVALIDPOSITION,
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
translation::java::COMMANDS_TELEPORT_INVALIDPOSITION,
[],
)));
}
@ -307,8 +315,9 @@ impl CommandExecutor for SelfToPosExecutor {
Ok(1)
}
_ => Err(CommandError::CommandFailed(TextComponent::translate(
translation::PERMISSIONS_REQUIRES_PLAYER,
_ => Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::PERMISSIONS_REQUIRES_PLAYER,
translation::java::PERMISSIONS_REQUIRES_PLAYER,
[],
))),
}

View File

@ -64,14 +64,16 @@ impl TickExecutor {
if manager.is_sprinting() {
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TICK_STATUS_SPRINTING,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TICK_STATUS_SPRINTING,
translation::java::COMMANDS_TICK_STATUS_SPRINTING,
[],
))
.await;
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TICK_QUERY_RATE_SPRINTING,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TICK_QUERY_RATE_SPRINTING,
translation::java::COMMANDS_TICK_QUERY_RATE_SPRINTING,
[
TextComponent::text(format!("{tickrate:.1}")),
TextComponent::text(avg_mspt_str),
@ -83,8 +85,9 @@ impl TickExecutor {
let target_mspt_str = nanos_to_millis_string(manager.nanoseconds_per_tick());
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TICK_QUERY_RATE_RUNNING,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TICK_QUERY_RATE_RUNNING,
translation::java::COMMANDS_TICK_QUERY_RATE_RUNNING,
[
TextComponent::text(format!("{tickrate:.1}")),
TextComponent::text(avg_mspt_str),
@ -104,22 +107,25 @@ impl TickExecutor {
) {
if manager.is_frozen() {
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TICK_STATUS_FROZEN,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TICK_STATUS_FROZEN,
translation::java::COMMANDS_TICK_STATUS_FROZEN,
[],
))
.await;
} else if avg_tick_nanos > manager.nanoseconds_per_tick() {
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TICK_STATUS_LAGGING,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TICK_STATUS_LAGGING,
translation::java::COMMANDS_TICK_STATUS_LAGGING,
[],
))
.await;
} else {
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TICK_STATUS_RUNNING,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TICK_STATUS_RUNNING,
translation::java::COMMANDS_TICK_STATUS_RUNNING,
[],
))
.await;
@ -140,8 +146,9 @@ impl TickExecutor {
let p99_nanos = relevant_ticks[(sample_size as f32 * 0.99).floor() as usize];
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TICK_QUERY_PERCENTILES,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TICK_QUERY_PERCENTILES,
translation::java::COMMANDS_TICK_QUERY_PERCENTILES,
[
TextComponent::text(nanos_to_millis_string(p50_nanos)),
TextComponent::text(nanos_to_millis_string(p95_nanos)),
@ -160,15 +167,20 @@ impl TickExecutor {
) {
if manager.step_game_if_paused(server, ticks).await {
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TICK_STEP_SUCCESS,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TICK_STEP_SUCCESS,
translation::java::COMMANDS_TICK_STEP_SUCCESS,
[TextComponent::text(ticks.to_string())],
))
.await;
} else {
sender
.send_message(
TextComponent::translate(translation::COMMANDS_TICK_STEP_FAIL, [])
TextComponent::translate_cross(
translation::java::COMMANDS_TICK_STEP_FAIL,
translation::java::COMMANDS_TICK_STEP_FAIL,
[],
)
.color_named(NamedColor::Red),
)
.await;
@ -185,15 +197,17 @@ impl TickExecutor {
.await
{
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TICK_SPRINT_STOP_SUCCESS,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TICK_SPRINT_STOP_SUCCESS,
translation::java::COMMANDS_TICK_SPRINT_STOP_SUCCESS,
[],
))
.await;
}
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TICK_STATUS_SPRINTING,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TICK_STATUS_SPRINTING,
translation::java::COMMANDS_TICK_STATUS_SPRINTING,
[],
))
.await;
@ -207,8 +221,9 @@ impl TickExecutor {
) -> Result<i32, E> {
manager.set_tick_rate(server, rate).await;
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TICK_RATE_SUCCESS,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TICK_RATE_SUCCESS,
translation::java::COMMANDS_TICK_RATE_SUCCESS,
[TextComponent::text(format!("{rate:.1}"))],
))
.await;
@ -242,7 +257,7 @@ impl CommandExecutor for TickExecutor {
"commands.tick.status.running"
};
sender
.send_message(TextComponent::translate(message_key, []))
.send_message(TextComponent::translate_cross(message_key, message_key, []))
.await;
Ok(freeze as i32)
}
@ -262,8 +277,9 @@ impl CommandExecutor for TickExecutor {
SubCommand::StepStop => {
if manager.stop_stepping(server).await {
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TICK_SPRINT_STOP_SUCCESS,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TICK_SPRINT_STOP_SUCCESS,
translation::java::COMMANDS_TICK_SPRINT_STOP_SUCCESS,
[],
))
.await;
@ -271,8 +287,9 @@ impl CommandExecutor for TickExecutor {
} else {
// TODO: send feedback as error without Err
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TICK_SPRINT_STOP_FAIL,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TICK_SPRINT_STOP_FAIL,
translation::java::COMMANDS_TICK_SPRINT_STOP_FAIL,
[],
))
.await;
@ -296,8 +313,9 @@ impl CommandExecutor for TickExecutor {
SubCommand::SprintStop => {
if manager.stop_sprinting(server).await {
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TICK_SPRINT_STOP_SUCCESS,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TICK_SPRINT_STOP_SUCCESS,
translation::java::COMMANDS_TICK_SPRINT_STOP_SUCCESS,
[],
))
.await;
@ -306,8 +324,9 @@ impl CommandExecutor for TickExecutor {
// TODO: send feedback as error without Err
sender
.send_message(
TextComponent::translate(
translation::COMMANDS_TICK_SPRINT_STOP_FAIL,
TextComponent::translate_cross(
translation::java::COMMANDS_TICK_SPRINT_STOP_FAIL,
translation::java::COMMANDS_TICK_SPRINT_STOP_FAIL,
[],
)
.color(Color::Named(NamedColor::Red)),

View File

@ -67,8 +67,9 @@ impl CommandExecutor for QueryExecutor {
QueryMode::Day => level_time.query_day(),
};
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TIME_QUERY,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TIME_QUERY,
translation::java::COMMANDS_TIME_QUERY,
[TextComponent::text(curr_time.to_string())],
))
.await;
@ -112,8 +113,9 @@ impl CommandExecutor for ChangeExecutor {
level_time.send_time(world).await;
let curr_time = level_time.query_daytime();
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TIME_SET,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TIME_SET,
translation::bedrock::COMMANDS_TIME_SET,
[TextComponent::text(curr_time.to_string())],
))
.await;
@ -124,8 +126,9 @@ impl CommandExecutor for ChangeExecutor {
level_time.set_time(time_count.into());
level_time.send_time(world).await;
sender
.send_message(TextComponent::translate(
translation::COMMANDS_TIME_SET,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TIME_SET,
translation::bedrock::COMMANDS_TIME_SET,
[TextComponent::text(time_count.to_string())],
))
.await;

View File

@ -53,14 +53,22 @@ impl CommandExecutor for ClearOrResetExecutor {
} else {
"commands.title.cleared.single"
};
TextComponent::translate(text, [targets[0].get_display_name().await])
TextComponent::translate_cross(
text,
text,
[targets[0].get_display_name().await],
)
} else {
let text = if reset {
"commands.title.reset.multiple"
} else {
"commands.title.cleared.multiple"
};
TextComponent::translate(text, [TextComponent::text(targets.len().to_string())])
TextComponent::translate_cross(
text,
text,
[TextComponent::text(targets.len().to_string())],
)
})
.await;
@ -94,12 +102,14 @@ impl CommandExecutor for TitleExecutor {
let mode_name = format!("{mode:?}").to_lowercase();
sender
.send_message(if targets.len() == 1 {
TextComponent::translate(
TextComponent::translate_cross(
format!("commands.title.show.{mode_name}.single").clone(),
format!("commands.title.show.{mode_name}.single"),
[targets[0].get_display_name().await],
)
} else {
TextComponent::translate(
TextComponent::translate_cross(
format!("commands.title.show.{mode_name}.multiple"),
format!("commands.title.show.{mode_name}.multiple"),
[TextComponent::text(targets.len().to_string())],
)
@ -135,12 +145,14 @@ impl CommandExecutor for TimesTitleExecutor {
sender
.send_message(if targets.len() == 1 {
TextComponent::translate(
TextComponent::translate_cross(
"commands.title.times.single",
"commands.title.times.single",
[targets[0].get_display_name().await],
)
} else {
TextComponent::translate(
TextComponent::translate_cross(
"commands.title.times.multiple",
"commands.title.times.multiple",
[TextComponent::text(targets.len().to_string())],
)

View File

@ -97,7 +97,8 @@ impl CommandExecutor for TargetPlayerExecutor {
};
if players.is_empty() {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.transfer.error.no_players",
"commands.transfer.error.no_players",
[],
)));
@ -115,7 +116,8 @@ impl CommandExecutor for TargetPlayerExecutor {
if players.len() == 1 {
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.transfer.success.single",
"commands.transfer.success.single",
[
players[0].get_display_name().await,
@ -126,7 +128,8 @@ impl CommandExecutor for TargetPlayerExecutor {
.await;
} else {
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.transfer.success.multiple",
"commands.transfer.success.multiple",
[
TextComponent::text(players.len().to_string()),

View File

@ -52,8 +52,9 @@ impl CommandExecutor for Executor {
.set_weather_parameters(&world, processed_duration, 0, false, false)
.await;
sender
.send_message(TextComponent::translate(
translation::COMMANDS_WEATHER_SET_CLEAR,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_WEATHER_SET_CLEAR,
translation::java::COMMANDS_WEATHER_SET_CLEAR,
[],
))
.await;
@ -66,8 +67,9 @@ impl CommandExecutor for Executor {
.set_weather_parameters(&world, 0, processed_duration, true, false)
.await;
sender
.send_message(TextComponent::translate(
translation::COMMANDS_WEATHER_SET_RAIN,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_WEATHER_SET_RAIN,
translation::java::COMMANDS_WEATHER_SET_RAIN,
[],
))
.await;
@ -80,8 +82,9 @@ impl CommandExecutor for Executor {
.set_weather_parameters(&world, 0, processed_duration, true, true)
.await;
sender
.send_message(TextComponent::translate(
translation::COMMANDS_WEATHER_SET_THUNDER,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_WEATHER_SET_THUNDER,
translation::java::COMMANDS_WEATHER_SET_THUNDER,
[],
))
.await;

View File

@ -37,8 +37,9 @@ async fn kick_non_whitelisted_players(server: &Server) {
player
.kick(
DisconnectReason::Kicked,
TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_NOT_WHITELISTED,
TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_NOT_WHITELISTED,
translation::java::MULTIPLAYER_DISCONNECT_NOT_WHITELISTED,
&[],
),
)
@ -59,15 +60,17 @@ impl CommandExecutor for OnExecutor {
Box::pin(async move {
let previous = server.white_list.swap(true, Ordering::Relaxed);
if previous {
Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_WHITELIST_ALREADYON,
Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_WHITELIST_ALREADYON,
translation::java::COMMANDS_WHITELIST_ALREADYON,
&[],
)))
} else {
kick_non_whitelisted_players(server).await;
sender
.send_message(TextComponent::translate(
translation::COMMANDS_WHITELIST_ENABLED,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_WHITELIST_ENABLED,
translation::java::COMMANDS_WHITELIST_ENABLED,
&[],
))
.await;
@ -90,15 +93,17 @@ impl CommandExecutor for OffExecutor {
let previous = server.white_list.swap(false, Ordering::Relaxed);
if previous {
sender
.send_message(TextComponent::translate(
translation::COMMANDS_WHITELIST_DISABLED,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_WHITELIST_DISABLED,
translation::java::COMMANDS_WHITELIST_DISABLED,
&[],
))
.await;
Ok(1)
} else {
Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_WHITELIST_ALREADYOFF,
Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_WHITELIST_ALREADYOFF,
translation::java::COMMANDS_WHITELIST_ALREADYOFF,
&[],
)))
}
@ -119,8 +124,9 @@ impl CommandExecutor for ListExecutor {
let whitelist = &server.data.whitelist_config.read().await.whitelist;
if whitelist.is_empty() {
sender
.send_message(TextComponent::translate(
translation::COMMANDS_WHITELIST_NONE,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_WHITELIST_NONE,
translation::java::COMMANDS_WHITELIST_NONE,
[],
))
.await;
@ -136,8 +142,9 @@ impl CommandExecutor for ListExecutor {
let names_len = names.len() as i32;
sender
.send_message(TextComponent::translate(
translation::COMMANDS_WHITELIST_LIST,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_WHITELIST_LIST,
translation::java::COMMANDS_WHITELIST_LIST,
[
TextComponent::text(whitelist.len().to_string()),
TextComponent::text(names),
@ -163,8 +170,9 @@ impl CommandExecutor for ReloadExecutor {
*server.data.whitelist_config.write().await = WhitelistConfig::load();
kick_non_whitelisted_players(server).await;
sender
.send_message(TextComponent::translate(
translation::COMMANDS_WHITELIST_RELOADED,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_WHITELIST_RELOADED,
translation::java::COMMANDS_WHITELIST_RELOADED,
&[],
))
.await;
@ -204,8 +212,9 @@ impl CommandExecutor for AddExecutor {
.whitelist
.push(WhitelistEntry::new(profile.id, profile.name.clone()));
sender
.send_message(TextComponent::translate(
translation::COMMANDS_WHITELIST_ADD_SUCCESS,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_WHITELIST_ADD_SUCCESS,
translation::java::COMMANDS_WHITELIST_ADD_SUCCESS,
[TextComponent::text(profile.name.clone())],
))
.await;
@ -215,8 +224,9 @@ impl CommandExecutor for AddExecutor {
whitelist.save();
if successes == 0 {
Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_WHITELIST_ADD_FAILED,
Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_WHITELIST_ADD_FAILED,
translation::java::COMMANDS_WHITELIST_ADD_FAILED,
&[],
)))
} else {
@ -251,8 +261,9 @@ impl CommandExecutor for RemoveExecutor {
if let Some(i) = i {
whitelist.whitelist.remove(i);
sender
.send_message(TextComponent::translate(
translation::COMMANDS_WHITELIST_REMOVE_SUCCESS,
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_WHITELIST_REMOVE_SUCCESS,
translation::java::COMMANDS_WHITELIST_REMOVE_SUCCESS,
[TextComponent::text(player.name.clone())],
))
.await;
@ -266,8 +277,9 @@ impl CommandExecutor for RemoveExecutor {
kick_non_whitelisted_players(server).await;
if successes == 0 {
Err(CommandError::CommandFailed(TextComponent::translate(
translation::COMMANDS_WHITELIST_REMOVE_FAILED,
Err(CommandError::CommandFailed(TextComponent::translate_cross(
translation::java::COMMANDS_WHITELIST_REMOVE_FAILED,
translation::java::COMMANDS_WHITELIST_REMOVE_FAILED,
&[],
)))
} else {

View File

@ -59,7 +59,8 @@ impl CommandExecutor for GetExecutor {
let diameter = border.new_diameter.round() as i32;
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.worldborder.get",
"commands.worldborder.get",
[TextComponent::text(diameter.to_string())],
))
@ -95,14 +96,16 @@ impl CommandExecutor for SetExecutor {
};
if (distance - border.new_diameter).abs() < f64::EPSILON {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
NOTHING_CHANGED_EXCEPTION,
NOTHING_CHANGED_EXCEPTION,
[],
)));
}
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.worldborder.set.immediate",
"commands.worldborder.set.immediate",
[TextComponent::text(format!("{distance:.1}"))],
))
@ -148,7 +151,8 @@ impl CommandExecutor for SetTimeExecutor {
match distance.total_cmp(&border.new_diameter) {
std::cmp::Ordering::Equal => {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
NOTHING_CHANGED_EXCEPTION,
NOTHING_CHANGED_EXCEPTION,
[],
)));
@ -156,7 +160,8 @@ impl CommandExecutor for SetTimeExecutor {
std::cmp::Ordering::Less => {
let dist = format!("{distance:.1}");
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.worldborder.set.shrink",
"commands.worldborder.set.shrink",
[
TextComponent::text(dist),
@ -168,7 +173,8 @@ impl CommandExecutor for SetTimeExecutor {
std::cmp::Ordering::Greater => {
let dist = format!("{distance:.1}");
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.worldborder.set.grow",
"commands.worldborder.set.grow",
[
TextComponent::text(dist),
@ -214,7 +220,8 @@ impl CommandExecutor for AddExecutor {
};
if distance_add == 0.0 {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
NOTHING_CHANGED_EXCEPTION,
NOTHING_CHANGED_EXCEPTION,
[],
)));
@ -224,7 +231,8 @@ impl CommandExecutor for AddExecutor {
let dist = format!("{distance:.1}");
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.worldborder.set.immediate",
"commands.worldborder.set.immediate",
[TextComponent::text(dist)],
))
@ -277,7 +285,8 @@ impl CommandExecutor for AddTimeExecutor {
std::cmp::Ordering::Less => {
let dist = format!("{distance:.1}");
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.worldborder.set.shrink",
"commands.worldborder.set.shrink",
[
TextComponent::text(dist),
@ -289,7 +298,8 @@ impl CommandExecutor for AddTimeExecutor {
std::cmp::Ordering::Greater => {
let dist = format!("{distance:.1}");
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.worldborder.set.grow",
"commands.worldborder.set.grow",
[
TextComponent::text(dist),
@ -329,7 +339,8 @@ impl CommandExecutor for CenterExecutor {
let Vector2 { x, y } = Position2DArgumentConsumer.find_arg_default_name(args)?;
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.worldborder.center.success",
"commands.worldborder.center.success",
[
TextComponent::text(format!("{x:.2}")),
@ -369,7 +380,8 @@ impl CommandExecutor for DamageAmountExecutor {
};
if (damage_per_block - border.damage_per_block).abs() < f32::EPSILON {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.worldborder.damage.amount.failed",
"commands.worldborder.damage.amount.failed",
[],
)));
@ -377,7 +389,8 @@ impl CommandExecutor for DamageAmountExecutor {
let damage = format!("{damage_per_block:.2}");
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.worldborder.damage.amount.success",
"commands.worldborder.damage.amount.success",
[TextComponent::text(damage)],
))
@ -413,7 +426,8 @@ impl CommandExecutor for DamageBufferExecutor {
};
if (buffer - border.buffer).abs() < f32::EPSILON {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.worldborder.damage.amount.failed",
"commands.worldborder.damage.amount.failed",
[],
)));
@ -421,7 +435,8 @@ impl CommandExecutor for DamageBufferExecutor {
let buf = format!("{buffer:.2}");
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.worldborder.damage.buffer.success",
"commands.worldborder.damage.buffer.success",
[TextComponent::text(buf)],
))
@ -457,14 +472,16 @@ impl CommandExecutor for WarningDistanceExecutor {
};
if distance == border.warning_blocks {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.worldborder.warning.distance.failed",
"commands.worldborder.warning.distance.failed",
[],
)));
}
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.worldborder.warning.distance.success",
"commands.worldborder.warning.distance.success",
[TextComponent::text(distance.to_string())],
))
@ -500,14 +517,16 @@ impl CommandExecutor for WarningTimeExecutor {
};
if time == border.warning_time {
return Err(CommandError::CommandFailed(TextComponent::translate(
return Err(CommandError::CommandFailed(TextComponent::translate_cross(
"commands.worldborder.warning.time.failed",
"commands.worldborder.warning.time.failed",
[],
)));
}
sender
.send_message(TextComponent::translate(
.send_message(TextComponent::translate_cross(
"commands.worldborder.warning.time.success",
"commands.worldborder.warning.time.success",
[TextComponent::text(time.to_string())],
))

View File

@ -15,10 +15,14 @@ use pumpkin_util::text::color::{Color, NamedColor};
use std::pin::Pin;
use std::sync::Arc;
pub const REQUIRES_PLAYER: CommandErrorType<0> =
CommandErrorType::new(translation::PERMISSIONS_REQUIRES_PLAYER);
pub const REQUIRES_ENTITY: CommandErrorType<0> =
CommandErrorType::new(translation::PERMISSIONS_REQUIRES_ENTITY);
pub const REQUIRES_PLAYER: CommandErrorType<0> = CommandErrorType::new(
translation::java::PERMISSIONS_REQUIRES_PLAYER,
translation::java::PERMISSIONS_REQUIRES_PLAYER,
);
pub const REQUIRES_ENTITY: CommandErrorType<0> = CommandErrorType::new(
translation::java::PERMISSIONS_REQUIRES_ENTITY,
translation::java::PERMISSIONS_REQUIRES_ENTITY,
);
pub trait ReturnValueCallable: Send + Sync {
fn call(&self, value: ReturnValue) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
@ -407,8 +411,11 @@ impl CommandSource {
/// Sends a message to all online operators.
async fn send_to_ops(&self, message: TextComponent) {
let text =
TextComponent::translate("chat.type.admin", &[self.display_name.clone(), message])
let text = TextComponent::translate_cross(
"chat.type.admin",
"chat.type.admin",
&[self.display_name.clone(), message],
)
.color(Color::Named(NamedColor::Gray))
.italic();
let Some(server) = &self.server else {

View File

@ -106,7 +106,11 @@ fn render_syntax_error_messages(syntax_error: CommandSyntaxError) -> Vec<TextCom
}
context_message = context_message.add_child(
TextComponent::translate(translation::COMMAND_CONTEXT_HERE, [])
TextComponent::translate_cross(
translation::java::COMMAND_CONTEXT_HERE,
translation::java::COMMAND_CONTEXT_HERE,
[],
)
.color_named(NamedColor::Red)
.italic(),
);
@ -748,7 +752,7 @@ mod test {
let here_component = &context.extra[3];
if let TextContent::Translate { translate, .. } = here_component.content.as_ref() {
assert_eq!(translate, translation::COMMAND_CONTEXT_HERE);
assert_eq!(translate, translation::java::COMMAND_CONTEXT_HERE);
} else {
panic!("expected translate component for command.context.here");
}

View File

@ -1,62 +1,117 @@
use pumpkin_data::translation;
// These are akin to the translatable built-in exceptions in Minecraft.
pub const READER_EXPECTED_START_QUOTE: CommandErrorType<0> =
CommandErrorType::new(translation::PARSING_QUOTE_EXPECTED_START);
pub const READER_EXPECTED_END_QUOTE: CommandErrorType<0> =
CommandErrorType::new(translation::PARSING_QUOTE_EXPECTED_END);
pub const READER_INVALID_ESCAPE: CommandErrorType<1> =
CommandErrorType::new(translation::PARSING_QUOTE_ESCAPE);
pub const READER_INVALID_BOOL: CommandErrorType<1> =
CommandErrorType::new(translation::PARSING_BOOL_INVALID);
pub const READER_EXPECTED_BOOL: CommandErrorType<0> =
CommandErrorType::new(translation::PARSING_BOOL_EXPECTED);
pub const READER_INVALID_INT: CommandErrorType<1> =
CommandErrorType::new(translation::PARSING_INT_INVALID);
pub const READER_EXPECTED_INT: CommandErrorType<0> =
CommandErrorType::new(translation::PARSING_INT_EXPECTED);
pub const READER_INVALID_LONG: CommandErrorType<1> =
CommandErrorType::new(translation::PARSING_LONG_INVALID);
pub const READER_EXPECTED_LONG: CommandErrorType<0> =
CommandErrorType::new(translation::PARSING_LONG_EXPECTED);
pub const READER_INVALID_DOUBLE: CommandErrorType<1> =
CommandErrorType::new(translation::PARSING_DOUBLE_INVALID);
pub const READER_EXPECTED_DOUBLE: CommandErrorType<0> =
CommandErrorType::new(translation::PARSING_DOUBLE_EXPECTED);
pub const READER_INVALID_FLOAT: CommandErrorType<1> =
CommandErrorType::new(translation::PARSING_FLOAT_INVALID);
pub const READER_EXPECTED_FLOAT: CommandErrorType<0> =
CommandErrorType::new(translation::PARSING_FLOAT_EXPECTED);
pub const READER_EXPECTED_SYMBOL: CommandErrorType<1> =
CommandErrorType::new(translation::PARSING_EXPECTED);
pub const READER_EXPECTED_START_QUOTE: CommandErrorType<0> = CommandErrorType::new(
translation::java::PARSING_QUOTE_EXPECTED_START,
translation::java::PARSING_QUOTE_EXPECTED_START,
);
pub const READER_EXPECTED_END_QUOTE: CommandErrorType<0> = CommandErrorType::new(
translation::java::PARSING_QUOTE_EXPECTED_END,
translation::java::PARSING_QUOTE_EXPECTED_END,
);
pub const READER_INVALID_ESCAPE: CommandErrorType<1> = CommandErrorType::new(
translation::java::PARSING_QUOTE_ESCAPE,
translation::java::PARSING_QUOTE_ESCAPE,
);
pub const READER_INVALID_BOOL: CommandErrorType<1> = CommandErrorType::new(
translation::java::PARSING_BOOL_INVALID,
translation::java::PARSING_BOOL_INVALID,
);
pub const READER_EXPECTED_BOOL: CommandErrorType<0> = CommandErrorType::new(
translation::java::PARSING_BOOL_EXPECTED,
translation::java::PARSING_BOOL_EXPECTED,
);
pub const READER_INVALID_INT: CommandErrorType<1> = CommandErrorType::new(
translation::java::PARSING_INT_INVALID,
translation::java::PARSING_INT_INVALID,
);
pub const READER_EXPECTED_INT: CommandErrorType<0> = CommandErrorType::new(
translation::java::PARSING_INT_EXPECTED,
translation::java::PARSING_INT_EXPECTED,
);
pub const READER_INVALID_LONG: CommandErrorType<1> = CommandErrorType::new(
translation::java::PARSING_LONG_INVALID,
translation::java::PARSING_LONG_INVALID,
);
pub const READER_EXPECTED_LONG: CommandErrorType<0> = CommandErrorType::new(
translation::java::PARSING_LONG_EXPECTED,
translation::java::PARSING_LONG_EXPECTED,
);
pub const READER_INVALID_DOUBLE: CommandErrorType<1> = CommandErrorType::new(
translation::java::PARSING_DOUBLE_INVALID,
translation::java::PARSING_DOUBLE_INVALID,
);
pub const READER_EXPECTED_DOUBLE: CommandErrorType<0> = CommandErrorType::new(
translation::java::PARSING_DOUBLE_EXPECTED,
translation::java::PARSING_DOUBLE_EXPECTED,
);
pub const READER_INVALID_FLOAT: CommandErrorType<1> = CommandErrorType::new(
translation::java::PARSING_FLOAT_INVALID,
translation::java::PARSING_FLOAT_INVALID,
);
pub const READER_EXPECTED_FLOAT: CommandErrorType<0> = CommandErrorType::new(
translation::java::PARSING_FLOAT_EXPECTED,
translation::java::PARSING_FLOAT_EXPECTED,
);
pub const READER_EXPECTED_SYMBOL: CommandErrorType<1> = CommandErrorType::new(
translation::java::PARSING_EXPECTED,
translation::java::PARSING_EXPECTED,
);
pub const LITERAL_INCORRECT: CommandErrorType<1> =
CommandErrorType::new(translation::ARGUMENT_LITERAL_INCORRECT);
pub const LITERAL_INCORRECT: CommandErrorType<1> = CommandErrorType::new(
translation::java::ARGUMENT_LITERAL_INCORRECT,
translation::java::ARGUMENT_LITERAL_INCORRECT,
);
pub const DOUBLE_TOO_LOW: CommandErrorType<2> =
CommandErrorType::new(translation::ARGUMENT_DOUBLE_LOW);
pub const DOUBLE_TOO_HIGH: CommandErrorType<2> =
CommandErrorType::new(translation::ARGUMENT_DOUBLE_BIG);
pub const FLOAT_TOO_LOW: CommandErrorType<2> =
CommandErrorType::new(translation::ARGUMENT_FLOAT_LOW);
pub const FLOAT_TOO_HIGH: CommandErrorType<2> =
CommandErrorType::new(translation::ARGUMENT_FLOAT_BIG);
pub const INTEGER_TOO_LOW: CommandErrorType<2> =
CommandErrorType::new(translation::ARGUMENT_INTEGER_LOW);
pub const INTEGER_TOO_HIGH: CommandErrorType<2> =
CommandErrorType::new(translation::ARGUMENT_INTEGER_BIG);
pub const LONG_TOO_LOW: CommandErrorType<2> = CommandErrorType::new(translation::ARGUMENT_LONG_LOW);
pub const LONG_TOO_HIGH: CommandErrorType<2> =
CommandErrorType::new(translation::ARGUMENT_LONG_BIG);
pub const DOUBLE_TOO_LOW: CommandErrorType<2> = CommandErrorType::new(
translation::java::ARGUMENT_DOUBLE_LOW,
translation::java::ARGUMENT_DOUBLE_LOW,
);
pub const DOUBLE_TOO_HIGH: CommandErrorType<2> = CommandErrorType::new(
translation::java::ARGUMENT_DOUBLE_BIG,
translation::java::ARGUMENT_DOUBLE_BIG,
);
pub const FLOAT_TOO_LOW: CommandErrorType<2> = CommandErrorType::new(
translation::java::ARGUMENT_FLOAT_LOW,
translation::java::ARGUMENT_FLOAT_LOW,
);
pub const FLOAT_TOO_HIGH: CommandErrorType<2> = CommandErrorType::new(
translation::java::ARGUMENT_FLOAT_BIG,
translation::java::ARGUMENT_FLOAT_BIG,
);
pub const INTEGER_TOO_LOW: CommandErrorType<2> = CommandErrorType::new(
translation::java::ARGUMENT_INTEGER_LOW,
translation::java::ARGUMENT_INTEGER_LOW,
);
pub const INTEGER_TOO_HIGH: CommandErrorType<2> = CommandErrorType::new(
translation::java::ARGUMENT_INTEGER_BIG,
translation::java::ARGUMENT_INTEGER_BIG,
);
pub const LONG_TOO_LOW: CommandErrorType<2> = CommandErrorType::new(
translation::java::ARGUMENT_LONG_LOW,
translation::java::ARGUMENT_LONG_LOW,
);
pub const LONG_TOO_HIGH: CommandErrorType<2> = CommandErrorType::new(
translation::java::ARGUMENT_LONG_BIG,
translation::java::ARGUMENT_LONG_BIG,
);
pub const DISPATCHER_UNKNOWN_COMMAND: CommandErrorType<0> =
CommandErrorType::new(translation::COMMAND_UNKNOWN_COMMAND);
pub const DISPATCHER_UNKNOWN_ARGUMENT: CommandErrorType<0> =
CommandErrorType::new(translation::COMMAND_UNKNOWN_ARGUMENT);
pub const DISPATCHER_EXPECTED_ARGUMENT_SEPARATOR: CommandErrorType<0> =
CommandErrorType::new(translation::COMMAND_EXPECTED_SEPARATOR);
pub const DISPATCHER_PARSE_EXCEPTION: CommandErrorType<1> =
CommandErrorType::new(translation::COMMAND_EXCEPTION);
pub const DISPATCHER_UNKNOWN_COMMAND: CommandErrorType<0> = CommandErrorType::new(
translation::java::COMMAND_UNKNOWN_COMMAND,
translation::bedrock::COMMANDS_GENERIC_UNKNOWN,
);
pub const DISPATCHER_UNKNOWN_ARGUMENT: CommandErrorType<0> = CommandErrorType::new(
translation::java::COMMAND_UNKNOWN_ARGUMENT,
translation::java::COMMAND_UNKNOWN_ARGUMENT,
);
pub const DISPATCHER_EXPECTED_ARGUMENT_SEPARATOR: CommandErrorType<0> = CommandErrorType::new(
translation::java::COMMAND_EXPECTED_SEPARATOR,
translation::java::COMMAND_EXPECTED_SEPARATOR,
);
pub const DISPATCHER_PARSE_EXCEPTION: CommandErrorType<1> = CommandErrorType::new(
translation::java::COMMAND_EXCEPTION,
translation::bedrock::COMMANDS_GENERIC_SYNTAX,
);
use crate::command::errors::{
command_syntax_error::{CommandSyntaxError, ContextProvider},
@ -70,7 +125,7 @@ use pumpkin_util::text::TextComponent;
pub enum TemplateText {
/// This is first translated, then arguments are substituted (which are not constant)
/// when it needs to be displayed.
TranslationKey(&'static str),
TranslationKey(&'static str, &'static str),
/// Directly shows up to the user without any translation done.
Literal(&'static str),
@ -89,14 +144,21 @@ pub enum TemplateText {
/// - `CommandErrorType<N>` = `DynamicNCommandExceptionType`
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CommandErrorType<const N: usize> {
pub translation_key: &'static str,
pub java_translation_key: &'static str,
pub bedrock_translation_key: &'static str,
}
impl<const N: usize> CommandErrorType<N> {
/// Creates an error type from a given translation string.
#[must_use]
pub const fn new(translation_key: &'static str) -> Self {
Self { translation_key }
pub const fn new(
java_translation_key: &'static str,
bedrock_translation_key: &'static str,
) -> Self {
Self {
java_translation_key,
bedrock_translation_key,
}
}
/// Creates an error without context from itself and from a slice of `N` arguments.
@ -107,7 +169,11 @@ impl<const N: usize> CommandErrorType<N> {
) -> CommandSyntaxError {
CommandSyntaxError::create_without_context(
self,
TextComponent::translate(self.translation_key, args),
TextComponent::translate_cross(
self.java_translation_key,
self.bedrock_translation_key,
args.to_vec(),
),
)
}
@ -122,7 +188,11 @@ impl<const N: usize> CommandErrorType<N> {
{
CommandSyntaxError::create(
self,
TextComponent::translate(self.translation_key, args),
TextComponent::translate_cross(
self.java_translation_key,
self.bedrock_translation_key,
args.to_vec(),
),
context_provider,
)
}
@ -206,7 +276,7 @@ impl<T: AnyCommandErrorType> PartialEq<T> for dyn AnyCommandErrorType {
impl<const N: usize> Sealed for CommandErrorType<N> {}
impl<const N: usize> AnyCommandErrorType for CommandErrorType<N> {
fn text(&self) -> TemplateText {
TemplateText::TranslationKey(self.translation_key)
TemplateText::TranslationKey(self.java_translation_key, self.bedrock_translation_key)
}
fn argument_count(&self) -> usize {
@ -281,7 +351,7 @@ mod test {
const TEST_LITERAL_ERROR_TYPE: LiteralCommandErrorType =
LiteralCommandErrorType::new("Test error");
const TEST_TRANSLATABLE_ERROR_TYPE: CommandErrorType<1> =
CommandErrorType::new("this.key.is.arbitrary");
CommandErrorType::new("this.key.is.arbitrary", "this.key.is.arbitrary");
#[test]
fn create_literal_error() {
@ -322,7 +392,8 @@ mod test {
assert_eq!(error.error_type, &TEST_TRANSLATABLE_ERROR_TYPE);
assert_eq!(
error.message,
TextComponent::translate(
TextComponent::translate_cross(
"this.key.is.arbitrary",
"this.key.is.arbitrary",
[TextComponent::text("some argument")]
)

View File

@ -16,7 +16,7 @@ use crate::command::string_reader::StringReader;
use crate::command::suggestion::suggestions::{Suggestions, SuggestionsBuilder};
use crate::command::tree::Command;
use futures::future;
use pumpkin_data::translation::COMMAND_CONTEXT_HERE;
use pumpkin_data::translation::java::COMMAND_CONTEXT_HERE;
use pumpkin_protocol::java::client::play::CommandSuggestion;
use pumpkin_util::text::TextComponent;
use pumpkin_util::text::click::ClickEvent;
@ -440,7 +440,7 @@ impl CommandDispatcher {
}
error_text = error_text.add_child(
TextComponent::translate(COMMAND_CONTEXT_HERE, &[])
TextComponent::translate_cross(COMMAND_CONTEXT_HERE, COMMAND_CONTEXT_HERE, &[])
.color(Color::Named(NamedColor::Red))
.italic(),
);

View File

@ -1243,7 +1243,8 @@ impl LivingEntity {
if let Some(cause) = cause
&& source.is_some()
{
TextComponent::translate(
TextComponent::translate_cross(
format!("death.attack.{}.player", damage_type.message_id),
format!("death.attack.{}.player", damage_type.message_id),
[
dyn_self.get_display_name().await,
@ -1251,7 +1252,8 @@ impl LivingEntity {
],
)
} else {
TextComponent::translate(
TextComponent::translate_cross(
format!("death.attack.{}", damage_type.message_id),
format!("death.attack.{}", damage_type.message_id),
[dyn_self.get_display_name().await],
)
@ -1259,13 +1261,15 @@ impl LivingEntity {
}
DeathMessageType::FallVariants => {
//TODO
TextComponent::translate(
translation::DEATH_FELL_ACCIDENT_GENERIC,
TextComponent::translate_cross(
translation::java::DEATH_FELL_ACCIDENT_GENERIC,
translation::bedrock::DEATH_FELL_ACCIDENT_GENERIC,
[dyn_self.get_display_name().await],
)
}
DeathMessageType::IntentionalGameDesign => TextComponent::text("[")
.add_child(TextComponent::translate(
.add_child(TextComponent::translate_cross(
format!("death.attack.{}.message", damage_type.message_id),
format!("death.attack.{}.message", damage_type.message_id),
[dyn_self.get_display_name().await],
))

View File

@ -321,7 +321,8 @@ pub trait EntityBase: Send + Sync + NBTStorage + std::any::Any {
.load()
.as_ref()
.clone()
.unwrap_or(TextComponent::translate(
.unwrap_or(TextComponent::translate_cross(
format!("entity.minecraft.{}", entity.entity_type.resource_name),
format!("entity.minecraft.{}", entity.entity_type.resource_name),
[],
))
@ -331,16 +332,13 @@ pub trait EntityBase: Send + Sync + NBTStorage + std::any::Any {
Box::pin(async move {
// TODO: team color
let entity = self.get_entity();
let mut name =
entity
.custom_name
.load()
.as_ref()
.clone()
.unwrap_or(TextComponent::translate(
let mut name = entity.custom_name.load().as_ref().clone().unwrap_or(
TextComponent::translate_cross(
format!("entity.minecraft.{}", entity.entity_type.resource_name),
format!("entity.minecraft.{}", entity.entity_type.resource_name),
[],
));
),
);
let name_clone = name.clone();
name = name.hover_event(HoverEvent::show_entity(
entity.entity_uuid.to_string(),
@ -2247,7 +2245,7 @@ impl Entity {
} else {
self.bedrock_flags_two.fetch_and(mask, Ordering::Relaxed);
}
};
}
let world = self.world.load();
let chunk_pos = self.chunk_pos.load();
@ -2983,6 +2981,7 @@ pub enum Flag {
}
impl Flag {
#[must_use]
pub const fn to_bedrock(&self) -> Option<u32> {
match self {
Self::OnFire => Some(entity_data_flag::ON_FIRE),

View File

@ -1774,7 +1774,11 @@ impl Player {
if idle_duration >= Duration::from_secs(idle_timeout_minutes as u64 * 60) {
self.kick(
DisconnectReason::KickedForIdle,
TextComponent::translate(translation::MULTIPLAYER_DISCONNECT_IDLING, []),
TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_IDLING,
translation::java::MULTIPLAYER_DISCONNECT_IDLING,
[],
),
)
.await;
return;
@ -1790,7 +1794,11 @@ impl Player {
if self.wait_for_keep_alive.load(Ordering::Relaxed) {
self.kick(
DisconnectReason::Timeout,
TextComponent::translate(translation::DISCONNECT_TIMEOUT, []),
TextComponent::translate_cross(
translation::java::DISCONNECT_TIMEOUT,
translation::bedrock::DISCONNECT_TIMEOUT,
[],
),
)
.await;
return;
@ -2363,7 +2371,11 @@ impl Player {
drop(banned_players);
let kick_reason = reason.unwrap_or_else(|| {
TextComponent::translate(translation::MULTIPLAYER_DISCONNECT_BANNED, [])
TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_BANNED,
translation::bedrock::DISCONNECTIONSCREEN_TITLE_BANNEDBYHOST,
[],
)
});
self.kick(DisconnectReason::Kicked, kick_reason).await;
@ -2394,7 +2406,11 @@ impl Player {
drop(banned_ips);
let kick_reason = reason.unwrap_or_else(|| {
TextComponent::translate(translation::MULTIPLAYER_DISCONNECT_IP_BANNED, [])
TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_IP_BANNED,
translation::java::MULTIPLAYER_DISCONNECT_IP_BANNED,
[],
)
});
let affected = server.get_players_by_ip(target_ip).await;

View File

@ -155,7 +155,7 @@ impl BedrockClient {
let real_name = player_data.display_name;
// IMPORTANT: Bedrock allows spaces in names. While we could support this, it would significantly complicate parsing player arguments in commands, so we don't
let under_score_name = real_name.replace(" ", "_");
let under_score_name = real_name.replace(' ', "_");
let profile = GameProfile {
id: Uuid::parse_str(&player_data.uuid).map_err(|_| LoginError::InvalidUuid)?,

View File

@ -721,8 +721,7 @@ impl BedrockClient {
.await;
}
SAnimate::PACKET_ID => {
self.handle_animate(player, server, SAnimate::read(reader)?)
.await;
self.handle_animate(player, server, &SAnimate::read(reader)?);
}
SLoadingScreen::PACKET_ID => {
// Ignore for now

View File

@ -6,11 +6,7 @@ use std::{
use pumpkin_macros::send_cancellable;
use pumpkin_protocol::{
bedrock::{
client::{
chunk_radius_update::CChunkRadiusUpdate,
container_open::CContainerOpen,
level_event::{CLevelEvent, LevelEvent},
},
client::{chunk_radius_update::CChunkRadiusUpdate, container_open::CContainerOpen},
server::{
animate::{AnimateAction, SAnimate},
command_request::SCommandRequest,
@ -23,14 +19,10 @@ use pumpkin_protocol::{
text::SText,
},
},
codec::{var_int::VarInt, var_long::VarLong, var_ulong::VarULong},
java::client::play::{Animation, CEntityAnimation, CSetBlockDestroyStage, CSystemChatMessage},
};
use pumpkin_util::{
GameMode,
math::{position::BlockPos, vector3::Vector3},
text::TextComponent,
codec::{var_int::VarInt, var_long::VarLong},
java::client::play::{Animation, CSystemChatMessage},
};
use pumpkin_util::{GameMode, math::position::BlockPos, text::TextComponent};
use pumpkin_world::world::BlockFlags;
@ -197,16 +189,16 @@ impl BedrockClient {
.await;
}
pub async fn handle_animate(&self, player: &Arc<Player>, _server: &Server, packet: SAnimate) {
pub fn handle_animate(&self, player: &Arc<Player>, _server: &Server, packet: &SAnimate) {
if !player.has_client_loaded() {
return;
}
let entity = &player.living_entity.entity;
let world = entity.world.load();
let _world = entity.world.load();
// Broadcast the animation to other players
let java_animation = match packet.action {
let _java_animation = match packet.action {
AnimateAction::SwingArm => Some(Animation::SwingMainArm),
AnimateAction::WakeUp => Some(Animation::LeaveBed),
AnimateAction::CriticalHit => Some(Animation::CriticalEffect),
@ -291,6 +283,7 @@ impl BedrockClient {
}}
}
#[expect(clippy::match_same_arms)]
pub async fn handle_player_action(
&self,
player: &Arc<Player>,

View File

@ -19,14 +19,16 @@ impl JavaClient {
if self.connection_state.load() != ConnectionState::Status {
let protocol = version;
if protocol < LOWEST_SUPPORTED_MC_VERSION.protocol_version() as u32 {
self.kick(TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_OUTDATED_CLIENT,
self.kick(TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_OUTDATED_CLIENT,
translation::java::MULTIPLAYER_DISCONNECT_OUTDATED_CLIENT,
[TextComponent::text(CURRENT_MC_VERSION.to_string())],
))
.await;
} else if protocol > CURRENT_MC_VERSION.protocol_version() as u32 {
self.kick(TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_OUTDATED_SERVER,
self.kick(TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_OUTDATED_SERVER,
translation::java::MULTIPLAYER_DISCONNECT_OUTDATED_SERVER,
[TextComponent::text(CURRENT_MC_VERSION.to_string())],
))
.await;

View File

@ -34,8 +34,9 @@ impl JavaClient {
// TODO: If client is an operator or has otherwise suitable elevated permissions, allow the client to bypass this requirement.
let max_players = server.basic_config.max_players;
if max_players > 0 && server.get_player_count() >= max_players as usize {
self.kick(TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_SERVER_FULL,
self.kick(TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_SERVER_FULL,
translation::java::MULTIPLAYER_DISCONNECT_SERVER_FULL,
[],
))
.await;
@ -137,12 +138,14 @@ impl JavaClient {
Ok(new_profile) => *profile = new_profile,
Err(error) => {
self.kick(match error {
AuthError::FailedResponse => TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_AUTHSERVERS_DOWN,
AuthError::FailedResponse => TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_AUTHSERVERS_DOWN,
translation::java::MULTIPLAYER_DISCONNECT_AUTHSERVERS_DOWN,
[],
),
AuthError::UnverifiedUsername => TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_UNVERIFIED_USERNAME,
AuthError::UnverifiedUsername => TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_UNVERIFIED_USERNAME,
translation::java::MULTIPLAYER_DISCONNECT_UNVERIFIED_USERNAME,
[],
),
e => TextComponent::text(e.to_string()),
@ -161,8 +164,9 @@ impl JavaClient {
&profile.id,
&online_player.gameprofile.name
);
self.kick(TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_DUPLICATE_LOGIN,
self.kick(TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_DUPLICATE_LOGIN,
translation::java::MULTIPLAYER_DISCONNECT_DUPLICATE_LOGIN,
[],
))
.await;
@ -178,8 +182,9 @@ impl JavaClient {
&profile.id,
&online_player.gameprofile.name
);
self.kick(TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_DUPLICATE_LOGIN,
self.kick(TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_DUPLICATE_LOGIN,
translation::java::MULTIPLAYER_DISCONNECT_DUPLICATE_LOGIN,
[],
))
.await;

View File

@ -152,44 +152,57 @@ impl PumpkinError for ChatError {
match self {
Self::OversizedMessage => Some("Chat message too long".into()),
Self::IllegalCharacters => Some(
TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_ILLEGAL_CHARACTERS,
TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_ILLEGAL_CHARACTERS,
translation::java::MULTIPLAYER_DISCONNECT_ILLEGAL_CHARACTERS,
[],
)
.get_text(),
),
Self::UnsignedChat => Some(
TextComponent::translate(translation::MULTIPLAYER_DISCONNECT_UNSIGNED_CHAT, [])
TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_UNSIGNED_CHAT,
translation::java::MULTIPLAYER_DISCONNECT_UNSIGNED_CHAT,
[],
)
.get_text(),
),
Self::TooManyPendingChats => Some(
TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_TOO_MANY_PENDING_CHATS,
TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_TOO_MANY_PENDING_CHATS,
translation::java::MULTIPLAYER_DISCONNECT_TOO_MANY_PENDING_CHATS,
[],
)
.get_text(),
),
Self::ChatValidationFailed => Some(
TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_CHAT_VALIDATION_FAILED,
TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_CHAT_VALIDATION_FAILED,
translation::java::MULTIPLAYER_DISCONNECT_CHAT_VALIDATION_FAILED,
[],
)
.get_text(),
),
Self::OutOfOrderChat => Some(
TextComponent::translate(translation::MULTIPLAYER_DISCONNECT_OUT_OF_ORDER_CHAT, [])
TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_OUT_OF_ORDER_CHAT,
translation::java::MULTIPLAYER_DISCONNECT_OUT_OF_ORDER_CHAT,
[],
)
.get_text(),
),
Self::ExpiredPublicKey => Some(
TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_EXPIRED_PUBLIC_KEY,
TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_EXPIRED_PUBLIC_KEY,
translation::java::MULTIPLAYER_DISCONNECT_EXPIRED_PUBLIC_KEY,
[],
)
.get_text(),
),
Self::InvalidPublicKey => Some(
TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_INVALID_PUBLIC_KEY_SIGNATURE,
TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_INVALID_PUBLIC_KEY_SIGNATURE,
translation::java::MULTIPLAYER_DISCONNECT_INVALID_PUBLIC_KEY_SIGNATURE,
[],
)
.get_text(),
@ -240,9 +253,14 @@ impl JavaClient {
change_game_mode.game_mode.to_str().to_lowercase()
);
player
.send_system_message(&TextComponent::translate(
translation::COMMANDS_GAMEMODE_SUCCESS_SELF,
[TextComponent::translate(gamemode_string, [])],
.send_system_message(&TextComponent::translate_cross(
translation::java::COMMANDS_GAMEMODE_SUCCESS_SELF,
translation::bedrock::COMMANDS_GAMEMODE_SUCCESS_SELF,
[TextComponent::translate_cross(
gamemode_string.clone(),
gamemode_string,
[],
)],
))
.await;
}
@ -312,8 +330,9 @@ impl JavaClient {
// y = feet Y
let position = packet.position;
if position.x.is_nan() || position.y.is_nan() || position.z.is_nan() {
self.kick(TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_INVALID_PLAYER_MOVEMENT,
self.kick(TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_INVALID_PLAYER_MOVEMENT,
translation::java::MULTIPLAYER_DISCONNECT_INVALID_PLAYER_MOVEMENT,
[],
))
.await;
@ -426,8 +445,9 @@ impl JavaClient {
|| !packet.yaw.is_finite()
|| !packet.pitch.is_finite()
{
self.kick(TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_INVALID_PLAYER_MOVEMENT,
self.kick(TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_INVALID_PLAYER_MOVEMENT,
translation::java::MULTIPLAYER_DISCONNECT_INVALID_PLAYER_MOVEMENT,
[],
))
.await;
@ -556,8 +576,9 @@ impl JavaClient {
return;
}
if !rotation.yaw.is_finite() || !rotation.pitch.is_finite() {
self.kick(TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_INVALID_PLAYER_MOVEMENT,
self.kick(TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_INVALID_PLAYER_MOVEMENT,
translation::java::MULTIPLAYER_DISCONNECT_INVALID_PLAYER_MOVEMENT,
[],
))
.await;
@ -1595,8 +1616,9 @@ impl JavaClient {
}
if entity_id.0 == player.entity_id() {
self.kick(TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED,
self.kick(TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED,
translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED,
[],
))
.await;
@ -1609,8 +1631,9 @@ impl JavaClient {
.map(|p| Arc::clone(p) as Arc<dyn EntityBase>)
.or_else(|| world.get_entity_by_id(entity_id.0));
let Some(target) = target else {
self.kick(TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED,
self.kick(TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED,
translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED,
[],
))
.await;
@ -1684,10 +1707,7 @@ impl JavaClient {
}
if entity_id.0 == player.entity_id() {
self.kick(TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED,
[],
))
self.kick(TextComponent::translate_cross(translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED, translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED, [],))
.await;
return;
}
@ -1737,10 +1757,7 @@ impl JavaClient {
player.entity_id(),
event.entity_id
);
self.kick(TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED,
[],
))
self.kick(TextComponent::translate_cross(translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED, translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED, [],))
.await;
}
}
@ -2619,8 +2636,9 @@ impl JavaClient {
if location.0.y + face.to_offset().y > world.get_top_y() {
player
.send_system_message_raw(
&TextComponent::translate(
translation::BUILD_TOOHIGH,
&TextComponent::translate_cross(
translation::java::BUILD_TOOHIGH,
translation::bedrock::BUILD_TOOHIGH,
vec![TextComponent::text((world.get_top_y()).to_string())],
)
.color_named(NamedColor::Red),

View File

@ -177,13 +177,15 @@ pub async fn can_not_join(
let mut banned_players = server.data.banned_player_list.write().await;
if let Some(entry) = banned_players.get_entry(profile) {
let text = TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_BANNED_REASON,
let text = TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_BANNED_REASON,
translation::java::MULTIPLAYER_DISCONNECT_BANNED_REASON,
[TextComponent::text(entry.reason.clone())],
);
return Some(match entry.expires {
Some(expires) => text.add_child(TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_BANNED_EXPIRATION,
Some(expires) => text.add_child(TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_BANNED_EXPIRATION,
translation::java::MULTIPLAYER_DISCONNECT_BANNED_EXPIRATION,
[TextComponent::text(
expires.format(FORMAT_DESCRIPTION).unwrap(),
)],
@ -198,8 +200,9 @@ pub async fn can_not_join(
let whitelist = server.data.whitelist_config.read().await;
if ops.get_entry(&profile.id).is_none() && !whitelist.is_whitelisted(profile) {
return Some(TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_NOT_WHITELISTED,
return Some(TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_NOT_WHITELISTED,
translation::java::MULTIPLAYER_DISCONNECT_NOT_WHITELISTED,
&[],
));
}
@ -212,13 +215,15 @@ pub async fn can_not_join(
.await
.get_entry(&address.ip())
{
let text = TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_BANNED_IP_REASON,
let text = TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_BANNED_IP_REASON,
translation::java::MULTIPLAYER_DISCONNECT_BANNED_IP_REASON,
[TextComponent::text(entry.reason.clone())],
);
return Some(match entry.expires {
Some(expires) => text.add_child(TextComponent::translate(
translation::MULTIPLAYER_DISCONNECT_BANNED_IP_EXPIRATION,
Some(expires) => text.add_child(TextComponent::translate_cross(
translation::java::MULTIPLAYER_DISCONNECT_BANNED_IP_EXPIRATION,
translation::java::MULTIPLAYER_DISCONNECT_BANNED_IP_EXPIRATION,
[TextComponent::text(
expires.format(FORMAT_DESCRIPTION).unwrap(),
)],

View File

@ -160,8 +160,9 @@ impl ServerTickRateManager {
let tps = (total_sprinted_ticks as f64 * 1000.0) / time_spent_ms;
let mspt = time_spent_ms / total_sprinted_ticks as f64;
TextComponent::translate(
translation::COMMANDS_TICK_SPRINT_REPORT,
TextComponent::translate_cross(
translation::java::COMMANDS_TICK_SPRINT_REPORT,
translation::java::COMMANDS_TICK_SPRINT_REPORT,
[
TextComponent::text(format!("{tps:.2}")),
TextComponent::text(format!("{mspt:.2}")),
@ -169,7 +170,11 @@ impl ServerTickRateManager {
)
} else {
// This is the message for `/tick sprint stop` or a zero-tick sprint.
TextComponent::translate(translation::COMMANDS_TICK_SPRINT_STOP_SUCCESS, [])
TextComponent::translate_cross(
translation::java::COMMANDS_TICK_SPRINT_STOP_SUCCESS,
translation::java::COMMANDS_TICK_SPRINT_STOP_SUCCESS,
[],
)
};
// Construct the final component with the [Server: ...] wrapper

View File

@ -622,7 +622,11 @@ impl DragonFight {
fn make_bossbar(&self) -> Bossbar {
Bossbar {
uuid: self.bossbar_uuid,
title: TextComponent::translate("entity.minecraft.ender_dragon", []),
title: TextComponent::translate_cross(
"entity.minecraft.ender_dragon",
"entity.minecraft.ender_dragon",
[],
),
health: 1.0,
color: BossbarColor::Pink,
division: BossbarDivisions::NoDivision,

Some files were not shown because too many files have changed in this diff Show More