1616use imperazim \command \constraint \PermissionConstraint ;
1717use imperazim \command \result \CommandResult ;
1818use imperazim \command \result \CommandFailure ;
19+ use imperazim \command \HelpGenerator ;
1920
2021/**
2122 * Abstract base class for custom commands.
@@ -30,6 +31,15 @@ abstract class Command extends PMMPCommand {
3031 /** @var Plugin The plugin that owns this command */
3132 private readonly Plugin $ plugin ;
3233
34+ /** @var array Custom messages for command failures */
35+ private array $ messages = [];
36+
37+ /** @var float|null Command cooldown in seconds */
38+ private ?float $ cooldown = null ;
39+
40+ /** @var array Last usage timestamps per sender */
41+ private static array $ cooldowns = [];
42+
3343 /**
3444 * Constructor.
3545 *
@@ -44,6 +54,12 @@ public function __construct(Plugin $plugin) {
4454 $ aliases = $ config ["aliases " ] ?? [];
4555 $ permission = $ config ["permission " ] ?? DefaultPermissions::ROOT_USER ;
4656
57+ // Store custom messages
58+ $ this ->messages = $ config ["messages " ] ?? [];
59+
60+ // Store cooldown if provided
61+ $ this ->cooldown = $ config ["cooldown " ] ?? null ;
62+
4763 parent ::__construct (
4864 $ name ,
4965 $ description ,
@@ -81,6 +97,96 @@ public function getPlugin(): Plugin {
8197 return $ this ->plugin ;
8298 }
8399
100+ /**
101+ * Gets a custom message by key.
102+ *
103+ * @param string $key The message key
104+ * @param string $default Default message if key not found
105+ * @return string The message
106+ */
107+ public function getMessage (string $ key , string $ default = "" ): string {
108+ return $ this ->messages [$ key ] ?? $ default ;
109+ }
110+
111+ /**
112+ * Sets a custom message.
113+ *
114+ * @param string $key The message key
115+ * @param string $message The message text
116+ */
117+ public function setMessage (string $ key , string $ message ): void {
118+ $ this ->messages [$ key ] = $ message ;
119+ }
120+
121+ /**
122+ * Generates help text for this command.
123+ *
124+ * @param string $format The format style (detailed, compact, usage)
125+ * @return string The help text
126+ */
127+ public function getHelp (string $ format = "detailed " ): string {
128+ return HelpGenerator::generate ($ this , $ format );
129+ }
130+
131+ /**
132+ * Checks if sender is on cooldown.
133+ *
134+ * @param CommandSender $sender The sender to check
135+ * @return bool True if on cooldown
136+ */
137+ private function isOnCooldown (CommandSender $ sender ): bool {
138+ if ($ this ->cooldown === null ) {
139+ return false ;
140+ }
141+
142+ $ name = $ sender ->getName ();
143+ $ key = $ this ->getName () . ": " . $ name ;
144+
145+ if (!isset (self ::$ cooldowns [$ key ])) {
146+ return false ;
147+ }
148+
149+ $ elapsed = microtime (true ) - self ::$ cooldowns [$ key ];
150+ return $ elapsed < $ this ->cooldown ;
151+ }
152+
153+ /**
154+ * Gets remaining cooldown time.
155+ *
156+ * @param CommandSender $sender The sender to check
157+ * @return float Remaining seconds
158+ */
159+ private function getRemainingCooldown (CommandSender $ sender ): float {
160+ if ($ this ->cooldown === null ) {
161+ return 0.0 ;
162+ }
163+
164+ $ name = $ sender ->getName ();
165+ $ key = $ this ->getName () . ": " . $ name ;
166+
167+ if (!isset (self ::$ cooldowns [$ key ])) {
168+ return 0.0 ;
169+ }
170+
171+ $ elapsed = microtime (true ) - self ::$ cooldowns [$ key ];
172+ return max (0.0 , $ this ->cooldown - $ elapsed );
173+ }
174+
175+ /**
176+ * Updates sender's cooldown.
177+ *
178+ * @param CommandSender $sender The sender
179+ */
180+ private function updateCooldown (CommandSender $ sender ): void {
181+ if ($ this ->cooldown === null ) {
182+ return ;
183+ }
184+
185+ $ name = $ sender ->getName ();
186+ $ key = $ this ->getName () . ": " . $ name ;
187+ self ::$ cooldowns [$ key ] = microtime (true );
188+ }
189+
84190 /**
85191 * Executes the command.
86192 *
@@ -93,6 +199,16 @@ public function execute(
93199 string $ label ,
94200 array $ rawArgs
95201 ): void {
202+ // Check cooldown
203+ if ($ this ->isOnCooldown ($ sender )) {
204+ $ remaining = $ this ->getRemainingCooldown ($ sender );
205+ $ this ->onFailure (
206+ new CommandFailure ($ sender , CommandFailure::COOLDOWN , [
207+ "remaining " => $ remaining ,
208+ ])
209+ );
210+ return ;
211+ }
96212 // Handle subcommands
97213 if (!empty ($ rawArgs )) {
98214 $ key = strtolower (array_shift ($ rawArgs ));
@@ -133,6 +249,9 @@ public function execute(
133249 $ parsedArgs = $ this ->parseArguments ($ sender , $ rawArgs );
134250
135251 try {
252+ // Update cooldown before execution
253+ $ this ->updateCooldown ($ sender );
254+
136255 // Execute command logic
137256 $ this ->onExecute (new CommandResult ($ sender , $ parsedArgs , $ label ));
138257 } catch (\Throwable $ e ) {
0 commit comments