Pumpkin/pumpkin/src/command/commands/time.rs
2026-05-03 21:31:46 +02:00

169 lines
5.6 KiB
Rust

use pumpkin_data::translation;
use pumpkin_util::text::TextComponent;
use crate::command::CommandResult;
use crate::command::args::{FindArg, time::TimeArgumentConsumer};
use crate::command::dispatcher::CommandError;
use crate::command::tree::builder::{argument, literal};
use crate::command::{CommandExecutor, CommandSender, ConsumedArgs, tree::CommandTree};
const NAMES: [&str; 1] = ["time"];
const DESCRIPTION: &str = "Query the world time.";
const ARG_TIME: &str = "time";
#[derive(Clone, Copy)]
enum PresetTime {
Day,
Noon,
Night,
Midnight,
}
impl PresetTime {
const fn to_ticks(self) -> i32 {
match self {
Self::Day => 1000,
Self::Noon => 6000,
Self::Night => 13000,
Self::Midnight => 18000,
}
}
}
#[derive(Clone, Copy)]
enum Mode {
Add,
Set(Option<PresetTime>),
}
#[derive(Clone, Copy)]
enum QueryMode {
DayTime,
GameTime,
Day,
}
struct QueryExecutor(QueryMode);
impl CommandExecutor for QueryExecutor {
fn execute<'a>(
&'a self,
sender: &'a CommandSender,
server: &'a crate::server::Server,
_args: &'a ConsumedArgs<'a>,
) -> CommandResult<'a> {
Box::pin(async move {
let mode = self.0;
// TODO: Maybe ask player for world, or get the current world
let worlds = server.worlds.load();
let world = worlds
.first()
.expect("There should always be at least one world");
let level_time = world.level_time.lock().await;
let curr_time = match mode {
QueryMode::DayTime => level_time.query_daytime(),
QueryMode::GameTime => level_time.query_gametime(),
QueryMode::Day => level_time.query_day(),
};
sender
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TIME_QUERY,
translation::java::COMMANDS_TIME_QUERY,
[TextComponent::text(curr_time.to_string())],
))
.await;
Ok(curr_time as i32)
})
}
}
struct ChangeExecutor(Mode);
impl CommandExecutor for ChangeExecutor {
fn execute<'a>(
&'a self,
sender: &'a CommandSender,
server: &'a crate::server::Server,
args: &'a ConsumedArgs<'a>,
) -> CommandResult<'a> {
Box::pin(async move {
let time_count = if let Mode::Set(Some(preset)) = &self.0 {
preset.to_ticks()
} else if let Ok(ticks) = TimeArgumentConsumer::find_arg(args, ARG_TIME) {
ticks
} else {
return Err(CommandError::CommandFailed(TextComponent::text(
"Invalid time specified.",
)));
};
let mode = self.0;
// TODO: Maybe ask player for world, or get the current world
let worlds = server.worlds.load();
let world = worlds
.first()
.expect("There should always be at least one world");
let mut level_time = world.level_time.lock().await;
match mode {
Mode::Add => {
// add
level_time.add_time(time_count.into());
level_time.send_time(world).await;
let curr_time = level_time.query_daytime();
sender
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TIME_SET,
translation::bedrock::COMMANDS_TIME_SET,
[TextComponent::text(curr_time.to_string())],
))
.await;
Ok(curr_time as i32)
}
Mode::Set(_) => {
// set
level_time.set_time(time_count.into());
level_time.send_time(world).await;
sender
.send_message(TextComponent::translate_cross(
translation::java::COMMANDS_TIME_SET,
translation::bedrock::COMMANDS_TIME_SET,
[TextComponent::text(time_count.to_string())],
))
.await;
Ok(time_count)
}
}
})
}
}
pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION)
.then(
literal("add")
.then(argument(ARG_TIME, TimeArgumentConsumer).execute(ChangeExecutor(Mode::Add))),
)
.then(
literal("query")
.then(literal("daytime").execute(QueryExecutor(QueryMode::DayTime)))
.then(literal("gametime").execute(QueryExecutor(QueryMode::GameTime)))
.then(literal("day").execute(QueryExecutor(QueryMode::Day))),
)
.then(
literal("set")
.then(literal("day").execute(ChangeExecutor(Mode::Set(Some(PresetTime::Day)))))
.then(literal("noon").execute(ChangeExecutor(Mode::Set(Some(PresetTime::Noon)))))
.then(literal("night").execute(ChangeExecutor(Mode::Set(Some(PresetTime::Night)))))
.then(
literal("midnight")
.execute(ChangeExecutor(Mode::Set(Some(PresetTime::Midnight)))),
)
.then(
argument(ARG_TIME, TimeArgumentConsumer)
.execute(ChangeExecutor(Mode::Set(None))),
),
)
}