Translate almost everything
The command output in Console/RCON is a bit broken now, i will fix that tomorrow, But in-game it works great
This commit is contained in:
parent
1a3b7e0ad6
commit
e7f1420da3
@ -42,14 +42,15 @@ impl TextComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn translate<P>(key: P, with: Vec<Cow<'static, str>>) -> Self
|
pub fn translate<K, W>(key: K, with: W) -> Self
|
||||||
where
|
where
|
||||||
P: Into<Cow<'static, str>>,
|
K: Into<Cow<'static, str>>,
|
||||||
|
W: Into<Vec<Cow<'static, str>>>,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
content: TextContent::Translate {
|
content: TextContent::Translate {
|
||||||
translate: key.into(),
|
translate: key.into(),
|
||||||
with,
|
with: with.into(),
|
||||||
},
|
},
|
||||||
style: Style::default(),
|
style: Style::default(),
|
||||||
extra: vec![],
|
extra: vec![],
|
||||||
@ -61,9 +62,19 @@ impl TextComponent {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_text(self) -> String {
|
||||||
|
match self.content {
|
||||||
|
TextContent::Text { text } => text.into_owned(),
|
||||||
|
TextContent::Translate { translate, with: _ } => translate.into_owned(),
|
||||||
|
TextContent::EntityNames {
|
||||||
|
selector,
|
||||||
|
separator: _,
|
||||||
|
} => selector.into_owned(),
|
||||||
|
TextContent::Keybind { keybind } => keybind.into_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_pretty_console(self) -> String {
|
pub fn to_pretty_console(self) -> String {
|
||||||
let style = self.style;
|
|
||||||
let color = style.color;
|
|
||||||
let mut text = match self.content {
|
let mut text = match self.content {
|
||||||
TextContent::Text { text } => text.into_owned(),
|
TextContent::Text { text } => text.into_owned(),
|
||||||
TextContent::Translate { translate, with: _ } => translate.into_owned(),
|
TextContent::Translate { translate, with: _ } => translate.into_owned(),
|
||||||
@ -73,6 +84,8 @@ impl TextComponent {
|
|||||||
} => selector.into_owned(),
|
} => selector.into_owned(),
|
||||||
TextContent::Keybind { keybind } => keybind.into_owned(),
|
TextContent::Keybind { keybind } => keybind.into_owned(),
|
||||||
};
|
};
|
||||||
|
let style = self.style;
|
||||||
|
let color = style.color;
|
||||||
if let Some(color) = color {
|
if let Some(color) = color {
|
||||||
text = color.console_color(&text).to_string();
|
text = color.console_color(&text).to_string();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,12 +51,8 @@ impl CommandExecutor for DeopExecutor {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let player_name = &player.gameprofile.name;
|
let player_name = &player.gameprofile.name;
|
||||||
let message = format!("Revoked {player_name}'s server operator status.");
|
let msg = TextComponent::translate("commands.deop.success", [player_name.clone().into()]);
|
||||||
let msg = TextComponent::text(message);
|
|
||||||
sender.send_message(msg).await;
|
sender.send_message(msg).await;
|
||||||
player
|
|
||||||
.send_system_message(&TextComponent::text("You are no longer a server operator."))
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,10 +144,10 @@ impl CommandExecutor for SetblockExecutor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
sender
|
sender
|
||||||
.send_message(TextComponent::text(format!(
|
.send_message(TextComponent::translate(
|
||||||
"Placed {} blocks of {} from {from} to {to}",
|
"commands.fill.success",
|
||||||
placed_blocks, block.name
|
[placed_blocks.to_string().into()],
|
||||||
)))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -38,18 +38,15 @@ impl CommandExecutor for GamemodeTargetSelf {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Player(target) = sender {
|
if let Player(target) = sender {
|
||||||
if target.gamemode.load() == gamemode {
|
if target.gamemode.load() != gamemode {
|
||||||
target
|
|
||||||
.send_system_message(&TextComponent::text(format!(
|
|
||||||
"You already in {gamemode:?} gamemode"
|
|
||||||
)))
|
|
||||||
.await;
|
|
||||||
} else {
|
|
||||||
target.set_gamemode(gamemode).await;
|
target.set_gamemode(gamemode).await;
|
||||||
|
let gamemode_string = format!("{gamemode:?}");
|
||||||
|
// TODO: translate gamemode
|
||||||
target
|
target
|
||||||
.send_system_message(&TextComponent::text(format!(
|
.send_system_message(&TextComponent::translate(
|
||||||
"Game mode was set to {gamemode:?}"
|
"commands.gamemode.success.self",
|
||||||
)))
|
[gamemode_string.into()],
|
||||||
|
))
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -79,23 +76,25 @@ impl CommandExecutor for GamemodeTargetPlayer {
|
|||||||
let target_count = targets.len();
|
let target_count = targets.len();
|
||||||
|
|
||||||
for target in targets {
|
for target in targets {
|
||||||
if target.gamemode.load() == gamemode {
|
if target.gamemode.load() != gamemode {
|
||||||
if target_count == 1 {
|
|
||||||
sender
|
|
||||||
.send_message(TextComponent::text(format!(
|
|
||||||
"{} is already in {:?} gamemode",
|
|
||||||
target.gameprofile.name, gamemode
|
|
||||||
)))
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
target.set_gamemode(gamemode).await;
|
target.set_gamemode(gamemode).await;
|
||||||
|
let gamemode_string = format!("{gamemode:?}");
|
||||||
|
// TODO: translate gamemode
|
||||||
|
target
|
||||||
|
.send_system_message(&TextComponent::translate(
|
||||||
|
"gameMode.changed",
|
||||||
|
[gamemode_string.clone().into()],
|
||||||
|
))
|
||||||
|
.await;
|
||||||
if target_count == 1 {
|
if target_count == 1 {
|
||||||
sender
|
sender
|
||||||
.send_message(TextComponent::text(format!(
|
.send_message(TextComponent::translate(
|
||||||
"{}'s Game mode was set to {:?}",
|
"commands.gamemode.success.other",
|
||||||
target.gameprofile.name, gamemode
|
[
|
||||||
)))
|
target.gameprofile.name.clone().into(),
|
||||||
|
gamemode_string.into(),
|
||||||
|
],
|
||||||
|
))
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,19 +55,26 @@ impl CommandExecutor for GiveExecutor {
|
|||||||
target.give_items(item, item_count as u32).await;
|
target.give_items(item, item_count as u32).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
sender
|
let msg = if targets.len() == 1 {
|
||||||
.send_message(TextComponent::text(match targets {
|
TextComponent::translate(
|
||||||
[target] => format!(
|
"commands.give.success.single",
|
||||||
"Gave {item_count} {} to {}",
|
[
|
||||||
item_name, target.gameprofile.name
|
item_count.to_string().into(),
|
||||||
),
|
item_name.to_string().into(),
|
||||||
_ => format!(
|
targets[0].gameprofile.name.to_string().into(),
|
||||||
"Gave {item_count} {} to {} players",
|
],
|
||||||
item_name,
|
)
|
||||||
targets.len()
|
} else {
|
||||||
),
|
TextComponent::translate(
|
||||||
}))
|
"commands.give.success.single",
|
||||||
.await;
|
[
|
||||||
|
item_count.to_string().into(),
|
||||||
|
item_name.to_string().into(),
|
||||||
|
targets.len().to_string().into(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
};
|
||||||
|
sender.send_message(msg).await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,22 +29,18 @@ impl CommandExecutor for KickExecutor {
|
|||||||
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
return Err(InvalidConsumption(Some(ARG_TARGET.into())));
|
||||||
};
|
};
|
||||||
|
|
||||||
let target_count = targets.len();
|
|
||||||
|
|
||||||
for target in targets {
|
for target in targets {
|
||||||
target
|
target
|
||||||
.kick(TextComponent::text("Kicked by an operator"))
|
.kick(TextComponent::translate(
|
||||||
|
"multiplayer.disconnect.kicked",
|
||||||
|
[],
|
||||||
|
))
|
||||||
.await;
|
.await;
|
||||||
|
let name = &target.gameprofile.name;
|
||||||
|
let msg = TextComponent::text(format!("Kicked: {name}"));
|
||||||
|
sender.send_message(msg.color_named(NamedColor::Blue)).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let msg = if target_count == 1 {
|
|
||||||
TextComponent::text("Player has been kicked.")
|
|
||||||
} else {
|
|
||||||
TextComponent::text(format!("{target_count} players have been kicked."))
|
|
||||||
};
|
|
||||||
|
|
||||||
sender.send_message(msg.color_named(NamedColor::Blue)).await;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,15 +29,19 @@ impl CommandExecutor for KillExecutor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let target_count = targets.len();
|
let target_count = targets.len();
|
||||||
|
let mut name = String::new();
|
||||||
for target in targets {
|
for target in targets {
|
||||||
target.living_entity.kill().await;
|
target.living_entity.kill().await;
|
||||||
|
name.clone_from(&target.gameprofile.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
let msg = if target_count == 1 {
|
let msg = if target_count == 1 {
|
||||||
TextComponent::text("Entity has been killed.")
|
TextComponent::translate("commands.kill.success.single", [name.into()])
|
||||||
} else {
|
} else {
|
||||||
TextComponent::text(format!("{target_count} entities have been killed."))
|
TextComponent::translate(
|
||||||
|
"commands.kill.success.multiple",
|
||||||
|
[target_count.to_string().into()],
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
sender.send_message(msg.color_named(NamedColor::Blue)).await;
|
sender.send_message(msg.color_named(NamedColor::Blue)).await;
|
||||||
|
|||||||
@ -27,18 +27,16 @@ impl CommandExecutor for ListExecutor {
|
|||||||
) -> Result<(), CommandError> {
|
) -> Result<(), CommandError> {
|
||||||
let players: Vec<Arc<Player>> = server.get_all_players().await;
|
let players: Vec<Arc<Player>> = server.get_all_players().await;
|
||||||
|
|
||||||
let message = if players.is_empty() {
|
sender
|
||||||
"There are no players online.".to_owned()
|
.send_message(TextComponent::translate(
|
||||||
} else {
|
"commands.list.players",
|
||||||
format!(
|
[
|
||||||
"There are {} of a max of {} players online: {}",
|
players.len().to_string().into(),
|
||||||
players.len(),
|
BASIC_CONFIG.max_players.to_string().into(),
|
||||||
BASIC_CONFIG.max_players,
|
get_player_names(players).into(),
|
||||||
get_player_names(players)
|
],
|
||||||
)
|
))
|
||||||
};
|
.await;
|
||||||
|
|
||||||
sender.send_message(TextComponent::text(message)).await;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,6 +39,13 @@ impl CommandExecutor for OpExecutor {
|
|||||||
.op_permission_level
|
.op_permission_level
|
||||||
.min(sender.permission_lvl());
|
.min(sender.permission_lvl());
|
||||||
|
|
||||||
|
if player.permission_lvl.load() == new_level {
|
||||||
|
sender
|
||||||
|
.send_message(TextComponent::translate("commands.op.failed", []))
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(op) = config
|
if let Some(op) = config
|
||||||
.ops
|
.ops
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
@ -62,11 +69,11 @@ impl CommandExecutor for OpExecutor {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let player_name = &player.gameprofile.name;
|
let player_name = &player.gameprofile.name;
|
||||||
let message = format!("Made {player_name} a server operator.");
|
sender
|
||||||
let msg = TextComponent::text(message);
|
.send_message(TextComponent::translate(
|
||||||
sender.send_message(msg).await;
|
"commands.op.success",
|
||||||
player
|
[player_name.clone().into()],
|
||||||
.send_system_message(&TextComponent::text("You are now a server operator."))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use pumpkin_util::text::color::NamedColor;
|
|
||||||
use pumpkin_util::text::TextComponent;
|
use pumpkin_util::text::TextComponent;
|
||||||
|
|
||||||
use crate::command::args::arg_block::BlockArgumentConsumer;
|
use crate::command::args::arg_block::BlockArgumentConsumer;
|
||||||
@ -67,9 +66,16 @@ impl CommandExecutor for SetblockExecutor {
|
|||||||
|
|
||||||
sender
|
sender
|
||||||
.send_message(if success {
|
.send_message(if success {
|
||||||
TextComponent::text(format!("Placed block {} at {pos}", block.name,))
|
TextComponent::translate(
|
||||||
|
"commands.setblock.success",
|
||||||
|
[
|
||||||
|
pos.0.x.to_string().into(),
|
||||||
|
pos.0.y.to_string().into(),
|
||||||
|
pos.0.z.to_string().into(),
|
||||||
|
],
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
TextComponent::text(format!("Kept block at {pos}")).color_named(NamedColor::Red)
|
TextComponent::translate("commands.setblock.failed", [])
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,9 @@ impl CommandExecutor for StopExecutor {
|
|||||||
_args: &ConsumedArgs<'a>,
|
_args: &ConsumedArgs<'a>,
|
||||||
) -> Result<(), CommandError> {
|
) -> Result<(), CommandError> {
|
||||||
sender
|
sender
|
||||||
.send_message(TextComponent::text("Stopping Server").color_named(NamedColor::Red))
|
.send_message(
|
||||||
|
TextComponent::translate("commands.stop.stopping", []).color_named(NamedColor::Red),
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// TODO: Gracefully stop
|
// TODO: Gracefully stop
|
||||||
|
|||||||
@ -62,19 +62,19 @@ impl CommandExecutor for TimeQueryExecutor {
|
|||||||
let msg = match mode {
|
let msg = match mode {
|
||||||
QueryMode::DayTime => {
|
QueryMode::DayTime => {
|
||||||
let curr_time = level_time.query_daytime();
|
let curr_time = level_time.query_daytime();
|
||||||
format!("Daytime is: {curr_time}")
|
TextComponent::translate("commands.time.query", [curr_time.to_string().into()])
|
||||||
}
|
}
|
||||||
QueryMode::GameTime => {
|
QueryMode::GameTime => {
|
||||||
let curr_time = level_time.query_gametime();
|
let curr_time = level_time.query_gametime();
|
||||||
format!("Gametime is: {curr_time}")
|
TextComponent::translate("commands.time.query", [curr_time.to_string().into()])
|
||||||
}
|
}
|
||||||
QueryMode::Day => {
|
QueryMode::Day => {
|
||||||
let curr_time = level_time.query_day();
|
let curr_time = level_time.query_day();
|
||||||
format!("Day is: {curr_time}")
|
TextComponent::translate("commands.time.query", [curr_time.to_string().into()])
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
sender.send_message(TextComponent::text(msg)).await;
|
sender.send_message(msg).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,17 +124,17 @@ impl CommandExecutor for TimeChangeExecutor {
|
|||||||
level_time.add_time(time_count.into());
|
level_time.add_time(time_count.into());
|
||||||
level_time.send_time(world).await;
|
level_time.send_time(world).await;
|
||||||
let curr_time = level_time.query_daytime();
|
let curr_time = level_time.query_daytime();
|
||||||
format!("Added {time_count} time for result: {curr_time}")
|
TextComponent::translate("commands.time.add", [curr_time.to_string().into()])
|
||||||
}
|
}
|
||||||
Mode::Set(_) => {
|
Mode::Set(_) => {
|
||||||
// set
|
// set
|
||||||
level_time.set_time(time_count.into());
|
level_time.set_time(time_count.into());
|
||||||
level_time.send_time(world).await;
|
level_time.send_time(world).await;
|
||||||
format!("Changed time to: {time_count}")
|
TextComponent::translate("commands.time.set", [time_count.to_string().into()])
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
sender.send_message(TextComponent::text(msg)).await;
|
sender.send_message(msg).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,8 @@ const NAMES: [&str; 1] = ["worldborder"];
|
|||||||
|
|
||||||
const DESCRIPTION: &str = "Worldborder command.";
|
const DESCRIPTION: &str = "Worldborder command.";
|
||||||
|
|
||||||
|
const NOTHING_CHANGED_EXCEPTION: &str = "commands.worldborder.set.failed.nochange";
|
||||||
|
|
||||||
fn distance_consumer() -> BoundedNumArgumentConsumer<f64> {
|
fn distance_consumer() -> BoundedNumArgumentConsumer<f64> {
|
||||||
BoundedNumArgumentConsumer::new().min(0.0).name("distance")
|
BoundedNumArgumentConsumer::new().min(0.0).name("distance")
|
||||||
}
|
}
|
||||||
@ -65,9 +67,10 @@ impl CommandExecutor for WorldborderGetExecutor {
|
|||||||
|
|
||||||
let diameter = border.new_diameter.round() as i32;
|
let diameter = border.new_diameter.round() as i32;
|
||||||
sender
|
sender
|
||||||
.send_message(TextComponent::text(format!(
|
.send_message(TextComponent::translate(
|
||||||
"The world border is currently {diameter} block(s) wide"
|
"commands.worldborder.get",
|
||||||
)))
|
[diameter.to_string().into()],
|
||||||
|
))
|
||||||
.await;
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -105,17 +108,19 @@ impl CommandExecutor for WorldborderSetExecutor {
|
|||||||
if (distance - border.new_diameter).abs() < f64::EPSILON {
|
if (distance - border.new_diameter).abs() < f64::EPSILON {
|
||||||
sender
|
sender
|
||||||
.send_message(
|
.send_message(
|
||||||
TextComponent::text("Nothing changed. The world border is already that size")
|
TextComponent::translate(NOTHING_CHANGED_EXCEPTION, [])
|
||||||
.color(Color::Named(NamedColor::Red)),
|
.color(Color::Named(NamedColor::Red)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let dist = format!("{distance:.1}");
|
||||||
sender
|
sender
|
||||||
.send_message(TextComponent::text(format!(
|
.send_message(TextComponent::translate(
|
||||||
"Set the world border to {distance:.1} block(s) wide"
|
"commands.worldborder.set.immediate",
|
||||||
)))
|
[dist.into()],
|
||||||
|
))
|
||||||
.await;
|
.await;
|
||||||
border.set_diameter(world, distance, None).await;
|
border.set_diameter(world, distance, None).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -167,22 +172,28 @@ impl CommandExecutor for WorldborderSetTimeExecutor {
|
|||||||
std::cmp::Ordering::Equal => {
|
std::cmp::Ordering::Equal => {
|
||||||
sender
|
sender
|
||||||
.send_message(
|
.send_message(
|
||||||
TextComponent::text(
|
TextComponent::translate(NOTHING_CHANGED_EXCEPTION, [])
|
||||||
"Nothing changed. The world border is already that size",
|
.color(Color::Named(NamedColor::Red)),
|
||||||
)
|
|
||||||
.color(Color::Named(NamedColor::Red)),
|
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
std::cmp::Ordering::Less => {
|
std::cmp::Ordering::Less => {
|
||||||
sender.send_message(TextComponent::text(format!("Shrinking the world border to {distance:.2} blocks wide over {time} second(s)"))).await;
|
let dist = format!("{distance:.1}");
|
||||||
|
sender
|
||||||
|
.send_message(TextComponent::translate(
|
||||||
|
"commands.worldborder.set.shrink",
|
||||||
|
[dist.into(), time.to_string().into()],
|
||||||
|
))
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
std::cmp::Ordering::Greater => {
|
std::cmp::Ordering::Greater => {
|
||||||
|
let dist = format!("{distance:.1}");
|
||||||
sender
|
sender
|
||||||
.send_message(TextComponent::text(format!(
|
.send_message(TextComponent::translate(
|
||||||
"Growing the world border to {distance:.2} blocks wide over {time} seconds"
|
"commands.worldborder.set.grow",
|
||||||
)))
|
[dist.into(), time.to_string().into()],
|
||||||
|
))
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -226,7 +237,7 @@ impl CommandExecutor for WorldborderAddExecutor {
|
|||||||
if distance == 0.0 {
|
if distance == 0.0 {
|
||||||
sender
|
sender
|
||||||
.send_message(
|
.send_message(
|
||||||
TextComponent::text("Nothing changed. The world border is already that size")
|
TextComponent::translate(NOTHING_CHANGED_EXCEPTION, [])
|
||||||
.color(Color::Named(NamedColor::Red)),
|
.color(Color::Named(NamedColor::Red)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@ -235,10 +246,12 @@ impl CommandExecutor for WorldborderAddExecutor {
|
|||||||
|
|
||||||
let distance = border.new_diameter + distance;
|
let distance = border.new_diameter + distance;
|
||||||
|
|
||||||
|
let dist = format!("{distance:.1}");
|
||||||
sender
|
sender
|
||||||
.send_message(TextComponent::text(format!(
|
.send_message(TextComponent::translate(
|
||||||
"Set the world border to {distance:.1} block(s) wide"
|
"commands.worldborder.set.immediate",
|
||||||
)))
|
[dist.into()],
|
||||||
|
))
|
||||||
.await;
|
.await;
|
||||||
border.set_diameter(world, distance, None).await;
|
border.set_diameter(world, distance, None).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -292,22 +305,28 @@ impl CommandExecutor for WorldborderAddTimeExecutor {
|
|||||||
std::cmp::Ordering::Equal => {
|
std::cmp::Ordering::Equal => {
|
||||||
sender
|
sender
|
||||||
.send_message(
|
.send_message(
|
||||||
TextComponent::text(
|
TextComponent::translate(NOTHING_CHANGED_EXCEPTION, [])
|
||||||
"Nothing changed. The world border is already that size",
|
.color(Color::Named(NamedColor::Red)),
|
||||||
)
|
|
||||||
.color(Color::Named(NamedColor::Red)),
|
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
std::cmp::Ordering::Less => {
|
std::cmp::Ordering::Less => {
|
||||||
sender.send_message(TextComponent::text(format!("Shrinking the world border to {distance:.2} blocks wide over {time} second(s)"))).await;
|
let dist = format!("{distance:.1}");
|
||||||
|
sender
|
||||||
|
.send_message(TextComponent::translate(
|
||||||
|
"commands.worldborder.set.shrink",
|
||||||
|
[dist.into(), time.to_string().into()],
|
||||||
|
))
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
std::cmp::Ordering::Greater => {
|
std::cmp::Ordering::Greater => {
|
||||||
|
let dist = format!("{distance:.1}");
|
||||||
sender
|
sender
|
||||||
.send_message(TextComponent::text(format!(
|
.send_message(TextComponent::translate(
|
||||||
"Growing the world border to {distance:.2} blocks wide over {time} seconds"
|
"commands.worldborder.set.grow",
|
||||||
)))
|
[dist.into(), time.to_string().into()],
|
||||||
|
))
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -338,9 +357,10 @@ impl CommandExecutor for WorldborderCenterExecutor {
|
|||||||
let Vector2 { x, z } = Position2DArgumentConsumer.find_arg_default_name(args)?;
|
let Vector2 { x, z } = Position2DArgumentConsumer.find_arg_default_name(args)?;
|
||||||
|
|
||||||
sender
|
sender
|
||||||
.send_message(TextComponent::text(format!(
|
.send_message(TextComponent::translate(
|
||||||
"Set the center of world border to {x:.2}, {z:.2}"
|
"commands.worldborder.center.success",
|
||||||
)))
|
[format!("{x:.2}").into(), format!("{z:.2}").into()],
|
||||||
|
))
|
||||||
.await;
|
.await;
|
||||||
border.set_center(world, x, z).await;
|
border.set_center(world, x, z).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -379,19 +399,19 @@ impl CommandExecutor for WorldborderDamageAmountExecutor {
|
|||||||
if (damage_per_block - border.damage_per_block).abs() < f32::EPSILON {
|
if (damage_per_block - border.damage_per_block).abs() < f32::EPSILON {
|
||||||
sender
|
sender
|
||||||
.send_message(
|
.send_message(
|
||||||
TextComponent::text(
|
TextComponent::translate("commands.worldborder.damage.amount.failed", [])
|
||||||
"Nothing changed. The world border damage is already that amount",
|
.color(Color::Named(NamedColor::Red)),
|
||||||
)
|
|
||||||
.color(Color::Named(NamedColor::Red)),
|
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let damage = format!("{damage_per_block:.2}");
|
||||||
sender
|
sender
|
||||||
.send_message(TextComponent::text(format!(
|
.send_message(TextComponent::translate(
|
||||||
"Set the world border damage to {damage_per_block:.2} per block each second"
|
"commands.worldborder.damage.amount.success",
|
||||||
)))
|
[damage.into()],
|
||||||
|
))
|
||||||
.await;
|
.await;
|
||||||
border.damage_per_block = damage_per_block;
|
border.damage_per_block = damage_per_block;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -430,19 +450,19 @@ impl CommandExecutor for WorldborderDamageBufferExecutor {
|
|||||||
if (buffer - border.buffer).abs() < f32::EPSILON {
|
if (buffer - border.buffer).abs() < f32::EPSILON {
|
||||||
sender
|
sender
|
||||||
.send_message(
|
.send_message(
|
||||||
TextComponent::text(
|
TextComponent::translate("commands.worldborder.damage.buffer.failed", [])
|
||||||
"Nothing changed. The world border damage buffer is already that distance",
|
.color(Color::Named(NamedColor::Red)),
|
||||||
)
|
|
||||||
.color(Color::Named(NamedColor::Red)),
|
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let buf = format!("{buffer:.2}");
|
||||||
sender
|
sender
|
||||||
.send_message(TextComponent::text(format!(
|
.send_message(TextComponent::translate(
|
||||||
"Set the world border damage buffer to {buffer:.2} block(s)"
|
"commands.worldborder.damage.buffer.success",
|
||||||
)))
|
[buf.into()],
|
||||||
|
))
|
||||||
.await;
|
.await;
|
||||||
border.buffer = buffer;
|
border.buffer = buffer;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -481,19 +501,18 @@ impl CommandExecutor for WorldborderWarningDistanceExecutor {
|
|||||||
if distance == border.warning_blocks {
|
if distance == border.warning_blocks {
|
||||||
sender
|
sender
|
||||||
.send_message(
|
.send_message(
|
||||||
TextComponent::text(
|
TextComponent::translate("commands.worldborder.warning.distance.failed", [])
|
||||||
"Nothing changed. The world border warning is already that distance",
|
.color(Color::Named(NamedColor::Red)),
|
||||||
)
|
|
||||||
.color(Color::Named(NamedColor::Red)),
|
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
sender
|
sender
|
||||||
.send_message(TextComponent::text(format!(
|
.send_message(TextComponent::translate(
|
||||||
"Set the world border warning distance to {distance} block(s)"
|
"commands.worldborder.warning.distance.success",
|
||||||
)))
|
[distance.to_string().into()],
|
||||||
|
))
|
||||||
.await;
|
.await;
|
||||||
border.set_warning_distance(world, distance).await;
|
border.set_warning_distance(world, distance).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -532,19 +551,18 @@ impl CommandExecutor for WorldborderWarningTimeExecutor {
|
|||||||
if time == border.warning_time {
|
if time == border.warning_time {
|
||||||
sender
|
sender
|
||||||
.send_message(
|
.send_message(
|
||||||
TextComponent::text(
|
TextComponent::translate("commands.worldborder.warning.time.failed", [])
|
||||||
"Nothing changed. The world border warning is already that amount of time",
|
.color(Color::Named(NamedColor::Red)),
|
||||||
)
|
|
||||||
.color(Color::Named(NamedColor::Red)),
|
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
sender
|
sender
|
||||||
.send_message(TextComponent::text(format!(
|
.send_message(TextComponent::translate(
|
||||||
"Set the world border warning time to {time} second(s)"
|
"commands.worldborder.warning.time.success",
|
||||||
)))
|
[time.to_string().into()],
|
||||||
|
))
|
||||||
.await;
|
.await;
|
||||||
border.set_warning_delay(world, time).await;
|
border.set_warning_delay(world, time).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -419,7 +419,8 @@ impl Player {
|
|||||||
.wait_for_keep_alive
|
.wait_for_keep_alive
|
||||||
.load(std::sync::atomic::Ordering::Relaxed)
|
.load(std::sync::atomic::Ordering::Relaxed)
|
||||||
{
|
{
|
||||||
self.kick(TextComponent::text("Timeout")).await;
|
self.kick(TextComponent::translate("disconnect.timeout", []))
|
||||||
|
.await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.wait_for_keep_alive
|
self.wait_for_keep_alive
|
||||||
@ -697,8 +698,12 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_system_message(&self, text: &TextComponent) {
|
pub async fn send_system_message(&self, text: &TextComponent) {
|
||||||
|
self.send_system_message_raw(text, false).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_system_message_raw(&self, text: &TextComponent, overlay: bool) {
|
||||||
self.client
|
self.client
|
||||||
.send_packet(&CSystemChatMessage::new(text, false))
|
.send_packet(&CSystemChatMessage::new(text, overlay))
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -248,7 +248,7 @@ impl Client {
|
|||||||
|
|
||||||
let mut enc = self.enc.lock().await;
|
let mut enc = self.enc.lock().await;
|
||||||
if let Err(error) = enc.append_packet(packet) {
|
if let Err(error) = enc.append_packet(packet) {
|
||||||
self.kick(&error.to_string()).await;
|
self.kick(&TextComponent::text(error.to_string())).await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,7 +336,7 @@ impl Client {
|
|||||||
i32::from(packet.id),
|
i32::from(packet.id),
|
||||||
error
|
error
|
||||||
);
|
);
|
||||||
self.kick(&text).await;
|
self.kick(&TextComponent::text(text)).await;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -564,23 +564,20 @@ impl Client {
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `reason`: A string describing the reason for kicking the client.
|
/// * `reason`: A string describing the reason for kicking the client.
|
||||||
pub async fn kick(&self, reason: &str) {
|
pub async fn kick(&self, reason: &TextComponent) {
|
||||||
log::info!("Kicking Client id {} for {}", self.id, reason);
|
let text = reason.clone().get_text();
|
||||||
|
log::info!("Kicking Client id {} for {}", self.id, &text);
|
||||||
let result = match self.connection_state.load() {
|
let result = match self.connection_state.load() {
|
||||||
ConnectionState::Login => {
|
ConnectionState::Login => {
|
||||||
|
// TextComponent implements Serialze and writes in bytes instead of String, thats the reasib we only use content
|
||||||
self.try_send_packet(&CLoginDisconnect::new(
|
self.try_send_packet(&CLoginDisconnect::new(
|
||||||
&serde_json::to_string_pretty(&reason).unwrap_or_else(|_| String::new()),
|
&serde_json::to_string(&reason.content).unwrap_or_else(|_| String::new()),
|
||||||
))
|
))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
ConnectionState::Config => self.try_send_packet(&CConfigDisconnect::new(reason)).await,
|
ConnectionState::Config => self.try_send_packet(&CConfigDisconnect::new(&text)).await,
|
||||||
// This way players get kicked when players using client functions (e.g. poll, send_packet)
|
// This way players get kicked when players using client functions (e.g. poll, send_packet)
|
||||||
ConnectionState::Play => {
|
ConnectionState::Play => self.try_send_packet(&CPlayDisconnect::new(reason)).await,
|
||||||
self.try_send_packet(&CPlayDisconnect::new(&TextComponent::text(
|
|
||||||
reason.to_owned(),
|
|
||||||
)))
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
log::warn!("Can't kick in {:?} State", self.connection_state);
|
log::warn!("Can't kick in {:?} State", self.connection_state);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -14,6 +14,7 @@ use pumpkin_protocol::{
|
|||||||
},
|
},
|
||||||
ConnectionState,
|
ConnectionState,
|
||||||
};
|
};
|
||||||
|
use pumpkin_util::text::TextComponent;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub async fn handle_client_information_config(
|
pub async fn handle_client_information_config(
|
||||||
@ -22,8 +23,10 @@ impl Client {
|
|||||||
) {
|
) {
|
||||||
log::debug!("Handling client settings");
|
log::debug!("Handling client settings");
|
||||||
if client_information.view_distance <= 0 {
|
if client_information.view_distance <= 0 {
|
||||||
self.kick("Cannot have zero or negative view distance!")
|
self.kick(&TextComponent::text(
|
||||||
.await;
|
"Cannot have zero or negative view distance!",
|
||||||
|
))
|
||||||
|
.await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +47,8 @@ impl Client {
|
|||||||
server_listing: client_information.server_listing,
|
server_listing: client_information.server_listing,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
self.kick("Invalid hand or chat type").await;
|
self.kick(&TextComponent::text("Invalid hand or chat type"))
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +62,7 @@ impl Client {
|
|||||||
log::debug!("got a client brand");
|
log::debug!("got a client brand");
|
||||||
match str::from_utf8(&plugin_message.data) {
|
match str::from_utf8(&plugin_message.data) {
|
||||||
Ok(brand) => *self.brand.lock().await = Some(brand.to_string()),
|
Ok(brand) => *self.brand.lock().await = Some(brand.to_string()),
|
||||||
Err(e) => self.kick(&e.to_string()).await,
|
Err(e) => self.kick(&TextComponent::text(e.to_string())).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use std::num::NonZeroI32;
|
use std::num::NonZeroI32;
|
||||||
|
|
||||||
use pumpkin_protocol::{server::handshake::SHandShake, ConnectionState, CURRENT_MC_PROTOCOL};
|
use pumpkin_protocol::{server::handshake::SHandShake, ConnectionState, CURRENT_MC_PROTOCOL};
|
||||||
|
use pumpkin_util::text::TextComponent;
|
||||||
|
|
||||||
use crate::{net::Client, server::CURRENT_MC_VERSION};
|
use crate::{net::Client, server::CURRENT_MC_VERSION};
|
||||||
|
|
||||||
@ -17,11 +18,19 @@ impl Client {
|
|||||||
let protocol = version;
|
let protocol = version;
|
||||||
match protocol.cmp(&NonZeroI32::from(CURRENT_MC_PROTOCOL).get()) {
|
match protocol.cmp(&NonZeroI32::from(CURRENT_MC_PROTOCOL).get()) {
|
||||||
std::cmp::Ordering::Less => {
|
std::cmp::Ordering::Less => {
|
||||||
self.kick(&format!("Client outdated ({protocol}), Server uses Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL}")).await;
|
self.kick(&TextComponent::translate(
|
||||||
|
"multiplayer.disconnect.outdated_client",
|
||||||
|
[CURRENT_MC_VERSION.to_string().into()],
|
||||||
|
))
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
std::cmp::Ordering::Equal => {}
|
std::cmp::Ordering::Equal => {}
|
||||||
std::cmp::Ordering::Greater => {
|
std::cmp::Ordering::Greater => {
|
||||||
self.kick(&format!("Server outdated, Server uses Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL}")).await;
|
self.kick(&TextComponent::translate(
|
||||||
|
"multiplayer.disconnect.incompatible",
|
||||||
|
[CURRENT_MC_VERSION.to_string().into()],
|
||||||
|
))
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -93,13 +93,17 @@ impl Client {
|
|||||||
// TODO: If client is an operator or otherwise suitable elevated permissions, allow client to bypass this requirement.
|
// TODO: If client is an operator or otherwise suitable elevated permissions, allow client to bypass this requirement.
|
||||||
let max_players = BASIC_CONFIG.max_players;
|
let max_players = BASIC_CONFIG.max_players;
|
||||||
if max_players > 0 && server.get_player_count().await >= max_players as usize {
|
if max_players > 0 && server.get_player_count().await >= max_players as usize {
|
||||||
self.kick("The server is currently full, please try again later")
|
self.kick(&TextComponent::translate(
|
||||||
.await;
|
"multiplayer.disconnect.server_full",
|
||||||
|
[],
|
||||||
|
))
|
||||||
|
.await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_valid_player_name(&login_start.name) {
|
if !is_valid_player_name(&login_start.name) {
|
||||||
self.kick("Invalid characters in username").await;
|
self.kick(&TextComponent::text("Invalid characters in username"))
|
||||||
|
.await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// default game profile, when no online mode
|
// default game profile, when no online mode
|
||||||
@ -122,7 +126,7 @@ impl Client {
|
|||||||
self.finish_login(&profile).await;
|
self.finish_login(&profile).await;
|
||||||
*gameprofile = Some(profile);
|
*gameprofile = Some(profile);
|
||||||
}
|
}
|
||||||
Err(error) => self.kick(&error.to_string()).await,
|
Err(error) => self.kick(&TextComponent::text(error.to_string())).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -165,14 +169,14 @@ impl Client {
|
|||||||
let shared_secret = server.decrypt(&encryption_response.shared_secret).unwrap();
|
let shared_secret = server.decrypt(&encryption_response.shared_secret).unwrap();
|
||||||
|
|
||||||
if let Err(error) = self.set_encryption(Some(&shared_secret)).await {
|
if let Err(error) = self.set_encryption(Some(&shared_secret)).await {
|
||||||
self.kick(&error.to_string()).await;
|
self.kick(&TextComponent::text(error.to_string())).await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut gameprofile = self.gameprofile.lock().await;
|
let mut gameprofile = self.gameprofile.lock().await;
|
||||||
|
|
||||||
let Some(profile) = gameprofile.as_mut() else {
|
let Some(profile) = gameprofile.as_mut() else {
|
||||||
self.kick("No Game profile").await;
|
self.kick(&TextComponent::text("No Game profile")).await;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -184,7 +188,7 @@ impl Client {
|
|||||||
{
|
{
|
||||||
Ok(new_profile) => *profile = new_profile,
|
Ok(new_profile) => *profile = new_profile,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.kick(&e.to_string()).await;
|
self.kick(&TextComponent::text(e.to_string())).await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,15 +197,22 @@ impl Client {
|
|||||||
// Don't allow duplicate UUIDs
|
// Don't allow duplicate UUIDs
|
||||||
if let Some(online_player) = &server.get_player_by_uuid(profile.id).await {
|
if let Some(online_player) = &server.get_player_by_uuid(profile.id).await {
|
||||||
log::debug!("Player (IP '{}', username '{}') tried to log in with the same UUID ('{}') as an online player (IP '{}', username '{}')", &self.address.lock().await, &profile.name, &profile.id, &online_player.client.address.lock().await, &online_player.gameprofile.name);
|
log::debug!("Player (IP '{}', username '{}') tried to log in with the same UUID ('{}') as an online player (IP '{}', username '{}')", &self.address.lock().await, &profile.name, &profile.id, &online_player.client.address.lock().await, &online_player.gameprofile.name);
|
||||||
self.kick("You are already connected to this server").await;
|
self.kick(&TextComponent::translate(
|
||||||
|
"multiplayer.disconnect.duplicate_login",
|
||||||
|
[],
|
||||||
|
))
|
||||||
|
.await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't allow a duplicate username
|
// Don't allow a duplicate username
|
||||||
if let Some(online_player) = &server.get_player_by_name(&profile.name).await {
|
if let Some(online_player) = &server.get_player_by_name(&profile.name).await {
|
||||||
log::debug!("A player (IP '{}', attempted username '{}') tried to log in with the same username as an online player (UUID '{}', IP '{}', username '{}')", &self.address.lock().await, &profile.name, &profile.id, &online_player.client.address.lock().await, &online_player.gameprofile.name);
|
log::debug!("A player (IP '{}', attempted username '{}') tried to log in with the same username as an online player (UUID '{}', IP '{}', username '{}')", &self.address.lock().await, &profile.name, &profile.id, &online_player.client.address.lock().await, &online_player.gameprofile.name);
|
||||||
self.kick("A player with this username is already connected")
|
self.kick(&TextComponent::translate(
|
||||||
.await;
|
"multiplayer.disconnect.duplicate_login",
|
||||||
|
[],
|
||||||
|
))
|
||||||
|
.await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +311,7 @@ impl Client {
|
|||||||
*self.gameprofile.lock().await = Some(profile);
|
*self.gameprofile.lock().await = Some(profile);
|
||||||
*address = new_address;
|
*address = new_address;
|
||||||
}
|
}
|
||||||
Err(error) => self.kick(&error.to_string()).await,
|
Err(error) => self.kick(&TextComponent::text(error.to_string())).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,9 +15,7 @@ use pumpkin_config::ADVANCED_CONFIG;
|
|||||||
use pumpkin_data::entity::EntityType;
|
use pumpkin_data::entity::EntityType;
|
||||||
use pumpkin_inventory::player::PlayerInventory;
|
use pumpkin_inventory::player::PlayerInventory;
|
||||||
use pumpkin_inventory::InventoryError;
|
use pumpkin_inventory::InventoryError;
|
||||||
use pumpkin_protocol::client::play::{
|
use pumpkin_protocol::client::play::{CSetContainerSlot, CSetHeldItem, CSpawnEntity};
|
||||||
CSetContainerSlot, CSetHeldItem, CSpawnEntity, CSystemChatMessage,
|
|
||||||
};
|
|
||||||
use pumpkin_protocol::codec::slot::Slot;
|
use pumpkin_protocol::codec::slot::Slot;
|
||||||
use pumpkin_protocol::codec::var_int::VarInt;
|
use pumpkin_protocol::codec::var_int::VarInt;
|
||||||
use pumpkin_protocol::server::play::SCookieResponse as SPCookieResponse;
|
use pumpkin_protocol::server::play::SCookieResponse as SPCookieResponse;
|
||||||
@ -81,11 +79,8 @@ impl PumpkinError for BlockPlacingError {
|
|||||||
|
|
||||||
fn severity(&self) -> log::Level {
|
fn severity(&self) -> log::Level {
|
||||||
match self {
|
match self {
|
||||||
Self::BlockOutOfReach
|
Self::BlockOutOfWorld | Self::InvalidGamemode | Self::NoBaseBlock => log::Level::Trace,
|
||||||
| Self::BlockOutOfWorld
|
Self::BlockOutOfReach | Self::InvalidBlockFace => log::Level::Warn,
|
||||||
| Self::InvalidBlockFace
|
|
||||||
| Self::InvalidGamemode
|
|
||||||
| Self::NoBaseBlock => log::Level::Warn,
|
|
||||||
Self::InventoryInvalid => log::Level::Error,
|
Self::InventoryInvalid => log::Level::Error,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,7 +136,11 @@ impl Player {
|
|||||||
// y = feet Y
|
// y = feet Y
|
||||||
let position = packet.position;
|
let position = packet.position;
|
||||||
if position.x.is_nan() || position.y.is_nan() || position.z.is_nan() {
|
if position.x.is_nan() || position.y.is_nan() || position.z.is_nan() {
|
||||||
self.kick(TextComponent::text("Invalid movement")).await;
|
self.kick(TextComponent::translate(
|
||||||
|
"multiplayer.disconnect.invalid_player_movement",
|
||||||
|
[],
|
||||||
|
))
|
||||||
|
.await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let position = Vector3::new(
|
let position = Vector3::new(
|
||||||
@ -199,15 +198,20 @@ impl Player {
|
|||||||
}
|
}
|
||||||
// y = feet Y
|
// y = feet Y
|
||||||
let position = packet.position;
|
let position = packet.position;
|
||||||
if position.x.is_nan() || position.y.is_nan() || position.z.is_nan() {
|
if position.x.is_nan()
|
||||||
self.kick(TextComponent::text("Invalid movement")).await;
|
|| position.y.is_nan()
|
||||||
|
|| position.z.is_nan()
|
||||||
|
|| packet.yaw.is_infinite()
|
||||||
|
|| packet.pitch.is_infinite()
|
||||||
|
{
|
||||||
|
self.kick(TextComponent::translate(
|
||||||
|
"multiplayer.disconnect.invalid_player_movement",
|
||||||
|
[],
|
||||||
|
))
|
||||||
|
.await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if packet.yaw.is_infinite() || packet.pitch.is_infinite() {
|
|
||||||
self.kick(TextComponent::text("Invalid rotation")).await;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let position = Vector3::new(
|
let position = Vector3::new(
|
||||||
Self::clamp_horizontal(position.x),
|
Self::clamp_horizontal(position.x),
|
||||||
Self::clamp_vertical(position.y),
|
Self::clamp_vertical(position.y),
|
||||||
@ -280,7 +284,11 @@ impl Player {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if !rotation.yaw.is_finite() || !rotation.pitch.is_finite() {
|
if !rotation.yaw.is_finite() || !rotation.pitch.is_finite() {
|
||||||
self.kick(TextComponent::text("Invalid rotation")).await;
|
self.kick(TextComponent::translate(
|
||||||
|
"multiplayer.disconnect.invalid_player_movement",
|
||||||
|
[],
|
||||||
|
))
|
||||||
|
.await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let entity = &self.living_entity.entity;
|
let entity = &self.living_entity.entity;
|
||||||
@ -528,8 +536,11 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if message.chars().any(|c| c == '§' || c < ' ' || c == '\x7F') {
|
if message.chars().any(|c| c == '§' || c < ' ' || c == '\x7F') {
|
||||||
self.kick(TextComponent::text("Illegal characters in chat"))
|
self.kick(TextComponent::translate(
|
||||||
.await;
|
"multiplayer.disconnect.illegal_characters",
|
||||||
|
[],
|
||||||
|
))
|
||||||
|
.await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -705,8 +716,11 @@ impl Player {
|
|||||||
self.entity_id(),
|
self.entity_id(),
|
||||||
entity_id.0
|
entity_id.0
|
||||||
);
|
);
|
||||||
self.kick(TextComponent::text("Interacted with invalid entity id"))
|
self.kick(TextComponent::translate(
|
||||||
.await;
|
"multiplayer.disconnect.invalid_entity_attacked",
|
||||||
|
[],
|
||||||
|
))
|
||||||
|
.await;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1134,16 +1148,15 @@ impl Player {
|
|||||||
|
|
||||||
//check max world build height
|
//check max world build height
|
||||||
if location.0.y + face.to_offset().y >= WORLD_MAX_Y.into() {
|
if location.0.y + face.to_offset().y >= WORLD_MAX_Y.into() {
|
||||||
self.client
|
self.send_system_message_raw(
|
||||||
.send_packet(&CSystemChatMessage::new(
|
&TextComponent::translate(
|
||||||
&TextComponent::translate(
|
"build.tooHigh",
|
||||||
"build.tooHigh",
|
vec![(WORLD_MAX_Y - 1).to_string().into()],
|
||||||
vec![(WORLD_MAX_Y - 1).to_string().into()],
|
)
|
||||||
)
|
.color_named(NamedColor::Red),
|
||||||
.color_named(NamedColor::Red),
|
true,
|
||||||
true,
|
)
|
||||||
))
|
.await;
|
||||||
.await;
|
|
||||||
self.client
|
self.client
|
||||||
.send_packet(&CAcknowledgeBlockChange::new(use_item_on.sequence))
|
.send_packet(&CAcknowledgeBlockChange::new(use_item_on.sequence))
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@ -745,8 +745,11 @@ impl World {
|
|||||||
|
|
||||||
// Handle join message
|
// Handle join message
|
||||||
// TODO: Config
|
// TODO: Config
|
||||||
let msg_txt = format!("{} joined the game.", player.gameprofile.name.as_str());
|
let msg_comp = TextComponent::translate(
|
||||||
let msg_comp = TextComponent::text(msg_txt).color_named(NamedColor::Yellow);
|
"multiplayer.player.joined",
|
||||||
|
[player.gameprofile.name.clone().into()],
|
||||||
|
)
|
||||||
|
.color_named(NamedColor::Yellow);
|
||||||
for player in current_players.values() {
|
for player in current_players.values() {
|
||||||
player.send_system_message(&msg_comp).await;
|
player.send_system_message(&msg_comp).await;
|
||||||
}
|
}
|
||||||
@ -787,12 +790,15 @@ impl World {
|
|||||||
|
|
||||||
// Send disconnect message / quit message to players in the same world
|
// Send disconnect message / quit message to players in the same world
|
||||||
// TODO: Config
|
// TODO: Config
|
||||||
let disconn_msg_txt = format!("{} left the game.", player.gameprofile.name.as_str());
|
let disconn_msg = TextComponent::translate(
|
||||||
let disconn_msg_cmp = TextComponent::text(disconn_msg_txt).color_named(NamedColor::Yellow);
|
"multiplayer.player.left",
|
||||||
|
[player.gameprofile.name.clone().into()],
|
||||||
|
)
|
||||||
|
.color_named(NamedColor::Yellow);
|
||||||
for player in self.current_players.lock().await.values() {
|
for player in self.current_players.lock().await.values() {
|
||||||
player.send_system_message(&disconn_msg_cmp).await;
|
player.send_system_message(&disconn_msg).await;
|
||||||
}
|
}
|
||||||
log::info!("{}", disconn_msg_cmp.to_pretty_console());
|
log::info!("{}", disconn_msg.to_pretty_console());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a living entity to the world.
|
/// Adds a living entity to the world.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user