Add /defaultgamemode command (#588)

* Add Defaultgamemode command

* add force_gamemode in configuration.toml
Check if the force_gamemode is enabled while doing the command

* Delete useless import + adapt to the new TextComponent::translate.

* add a space...

* Save the default gamemode in the server

* refactor + delete a .clone

* Add #[must_use]
This commit is contained in:
ht06
2025-03-02 15:02:11 +01:00
committed by GitHub
parent 85d457fa88
commit 52e48ec0bd
4 changed files with 87 additions and 14 deletions

View File

@@ -11,7 +11,6 @@ use std::{
path::Path,
sync::LazyLock,
};
pub mod logging;
pub mod networking;
@@ -90,6 +89,8 @@ pub struct BasicConfiguration {
pub tps: f32,
/// The default game mode for players.
pub default_gamemode: GameMode,
/// If the server force the gamemode on join
pub force_gamemode: bool,
/// Whether to remove IPs from logs or not
pub scrub_ips: bool,
/// Whether to use a server favicon
@@ -115,6 +116,7 @@ impl Default for BasicConfiguration {
motd: "A Blazing fast Pumpkin Server!".to_string(),
tps: 20.0,
default_gamemode: GameMode::Survival,
force_gamemode: false,
scrub_ips: true,
use_favicon: true,
favicon_path: "icon.png".to_string(),

View File

@@ -0,0 +1,64 @@
use crate::command::args::gamemode::GamemodeArgumentConsumer;
use crate::command::args::{Arg, GetCloned};
use crate::command::dispatcher::CommandError::InvalidConsumption;
use crate::command::tree::builder::argument;
use crate::command::{
CommandError, CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree,
};
use async_trait::async_trait;
use pumpkin_config::BASIC_CONFIG;
use pumpkin_util::GameMode;
use pumpkin_util::text::TextComponent;
const NAMES: [&str; 1] = ["defaultgamemode"];
const DESCRIPTION: &str = "Change the default gamemode";
pub const ARG_GAMEMODE: &str = "gamemode";
pub struct DefaultGamemode {
pub gamemode: GameMode,
}
struct DefaultGamemodeExecutor;
#[async_trait]
impl CommandExecutor for DefaultGamemodeExecutor {
async fn execute<'a>(
&self,
sender: &mut CommandSender<'a>,
server: &crate::server::Server,
args: &ConsumedArgs<'a>,
) -> Result<(), CommandError> {
let Some(Arg::GameMode(gamemode)) = args.get_cloned(&ARG_GAMEMODE) else {
return Err(InvalidConsumption(Some(ARG_GAMEMODE.into())));
};
if BASIC_CONFIG.force_gamemode {
for player in server.get_all_players().await {
player.set_gamemode(gamemode).await;
}
}
let gamemode_string = format!("{gamemode:?}").to_lowercase();
let gamemode_string = format!("gameMode.{gamemode_string}");
sender
.send_message(TextComponent::translate(
"commands.defaultgamemode.success",
[TextComponent::translate(gamemode_string, [])],
))
.await;
//Change the default gamemode (not in configuration.toml)
server.defaultgamemode.lock().await.gamemode = gamemode;
Ok(())
}
}
#[must_use]
pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION)
.then(argument(ARG_GAMEMODE, GamemodeArgumentConsumer).execute(DefaultGamemodeExecutor))
}

View File

@@ -8,6 +8,7 @@ mod banlist;
mod bossbar;
mod clear;
mod damage;
pub mod defaultgamemode;
mod deop;
mod effect;
mod experience;
@@ -72,6 +73,7 @@ pub fn default_dispatcher() -> CommandDispatcher {
dispatcher.register(bossbar::init_command_tree(), PermissionLvl::Two);
dispatcher.register(say::init_command_tree(), PermissionLvl::Two);
dispatcher.register(gamemode::init_command_tree(), PermissionLvl::Two);
dispatcher.register(defaultgamemode::init_command_tree(), PermissionLvl::Two);
// Three
dispatcher.register(op::init_command_tree(), PermissionLvl::Three);
dispatcher.register(deop::init_command_tree(), PermissionLvl::Three);

View File

@@ -1,3 +1,15 @@
use crate::block::default_block_properties_manager;
use crate::block::properties::BlockPropertiesManager;
use crate::block::registry::BlockRegistry;
use crate::command::commands::default_dispatcher;
use crate::command::commands::defaultgamemode::DefaultGamemode;
use crate::entity::{Entity, EntityId};
use crate::item::registry::ItemRegistry;
use crate::net::EncryptionError;
use crate::world::custom_bossbar::CustomBossbars;
use crate::{
command::dispatcher::CommandDispatcher, entity::player::Player, net::Client, world::World,
};
use connection_cache::{CachedBranding, CachedStatus};
use crossbeam::atomic::AtomicCell;
use key_store::KeyStore;
@@ -28,18 +40,6 @@ use std::{
};
use tokio::sync::{Mutex, RwLock};
use crate::block::default_block_properties_manager;
use crate::block::properties::BlockPropertiesManager;
use crate::block::registry::BlockRegistry;
use crate::command::commands::default_dispatcher;
use crate::entity::{Entity, EntityId};
use crate::item::registry::ItemRegistry;
use crate::net::EncryptionError;
use crate::world::custom_bossbar::CustomBossbars;
use crate::{
command::dispatcher::CommandDispatcher, entity::player::Player, net::Client, world::World,
};
mod connection_cache;
mod key_store;
pub mod ticker;
@@ -80,6 +80,8 @@ pub struct Server {
pub auth_client: Option<reqwest::Client>,
/// The server's custom bossbars
pub bossbars: Mutex<CustomBossbars>,
/// The default gamemode when a player joins the server (reset every restart)
pub defaultgamemode: Mutex<DefaultGamemode>,
}
impl Server {
@@ -137,6 +139,9 @@ impl Server {
server_listing: Mutex::new(CachedStatus::new()),
server_branding: CachedBranding::new(),
bossbars: Mutex::new(CustomBossbars::new()),
defaultgamemode: Mutex::new(DefaultGamemode {
gamemode: BASIC_CONFIG.default_gamemode,
}),
}
}
@@ -175,7 +180,7 @@ impl Server {
/// You still have to spawn the Player in the World to make then to let them Join and make them Visible
pub async fn add_player(&self, client: Arc<Client>) -> (Arc<Player>, Arc<World>) {
let entity_id = self.new_entity_id();
let gamemode = BASIC_CONFIG.default_gamemode;
let gamemode = self.defaultgamemode.lock().await.gamemode;
// Basically the default world
// TODO: select default from config
let world = &self.worlds.read().await[0];