java.lang.Object
com.ultikits.ultitools.abstracts.command.validation.validators.CooldownValidator
All Implemented Interfaces:
CommandValidator, PlayerCacheSaver, PlayerCacheManager.ExpiringPlayerCache

public class CooldownValidator extends Object implements CommandValidator, PlayerCacheManager.ExpiringPlayerCache, PlayerCacheSaver
Validates and manages command cooldowns for players. Thread-safe implementation using ConcurrentHashMap.
Since:
6.2.0
Version:
2.0.0
Author:
wisdomme
  • Field Details

    • ORDER

      private static final int ORDER
      See Also:
    • LOGGER

      private static final Logger LOGGER
    • cooldowns

      private final Map<UUID,Map<String,Long>> cooldowns
      Map of player UUID -> (method name -> cooldown end timestamp)

      saveBeforeRemove = true so savePlayerData(UUID) -- which delegates to the pre-existing clearCooldowns(UUID) -- fires on quit; see that method's javadoc for why (GEN-08, D-03).

    • playerCacheRegistered

      private volatile boolean playerCacheRegistered
      True once this instance has registered cooldowns with the live PlayerCacheManager for quit-based and time-based sweeping (GEN-08, D-03).

      Set from validate(CommandContext) -- the first production call this validator receives after construction -- rather than from the constructor: a bare new CooldownValidator() (the shape a unit test, or BaseCommandExecutor's own constructor, uses) must never even attempt contact with a core plugin that may not exist yet. Guarding on a live UltiTools.getInstance() rather than an unconditional "did I try" flag means a first attempt made before the core plugin is up is retried on the next call instead of being permanently abandoned -- PlayerCacheManager.tryRegister(java.lang.Object) is itself idempotent per instance, so a redundant retry after the flag is already true is simply never made.

    • defaultCooldownSeconds

      private final int defaultCooldownSeconds
  • Constructor Details

    • CooldownValidator

      public CooldownValidator()
      Creates a cooldown validator with no default cooldown.
    • CooldownValidator

      public CooldownValidator(int defaultCooldownSeconds)
      Creates a cooldown validator with a default cooldown.
      Parameters:
      defaultCooldownSeconds - the default cooldown in seconds
  • Method Details

    • validate

      Description copied from interface: CommandValidator
      Validates the command context.
      Specified by:
      validate in interface CommandValidator
      Parameters:
      context - the command context to validate
      Returns:
      the validation result
    • applyCooldown

      public void applyCooldown(CommandContext context)
      Applies cooldown after command execution. Should be called after successful command execution.
      Parameters:
      context - the command context
    • onComplete

      public void onComplete(CommandContext context, boolean commandSucceeded)
      Post-action hook that applies the cooldown recorded for this invocation. Delegates to applyCooldown(CommandContext) and is invoked only by a chain that actually ran this validator for the current dispatch -- see CommandValidator.onComplete(CommandContext, boolean).
      Specified by:
      onComplete in interface CommandValidator
      Parameters:
      context - the command context
      commandSucceeded - ignored -- the cooldown applies whether the mapped method succeeded or threw
      Since:
      6.3.0
    • ensurePlayerCacheRegistered

      private void ensurePlayerCacheRegistered()
      Attempts lazy first-use registration of this instance with the live PlayerCacheManager singleton. Safe to call unconditionally on every validate(CommandContext) invocation: a no-op once playerCacheRegistered is true, and a cheap, safely-no-op-on-failure retry otherwise (see that field's javadoc).
    • sweepExpired

      public void sweepExpired()
      PlayerCacheManager.ExpiringPlayerCache opt-in: delegates to the pre-existing cleanupExpired() time-based half. Invoked once per PlayerCacheManager.sweepExpiredEntries() pass -- the periodic sweep this instance only receives once registered (see ensurePlayerCacheRegistered()) -- independently of whether any player has quit (GEN-08, D-03).
      Specified by:
      sweepExpired in interface PlayerCacheManager.ExpiringPlayerCache
      Since:
      6.3.0
    • savePlayerData

      public void savePlayerData(UUID playerId)
      PlayerCacheSaver hook: fired by PlayerCacheManager.onPlayerQuit(UUID) ("the quit sweep") for the quitting player -- before the generic @PlayerCache field sweep removes the (by then already-empty) cooldowns entry. Delegates to the pre-existing clearCooldowns(UUID) rather than re-deriving its predicate, giving that previously zero-caller method a real, quit-path-reached production call site (GEN-08). The interface is named for persistence, but its contract is simply "run this before the generic removal" -- there is nothing here to persist, and reusing the hook for an idempotent, already-correct cleanup method is the same predicate the generic sweep would otherwise perform standalone.
      Specified by:
      savePlayerData in interface PlayerCacheSaver
      Parameters:
      playerId - the UUID of the player quitting
      Since:
      6.3.0
    • clearCooldowns

      public void clearCooldowns(UUID playerId)
      Clears all cooldowns for a player.
      Parameters:
      playerId - the player's UUID
    • clearCooldown

      public void clearCooldown(UUID playerId, String methodKey)
      Clears a specific cooldown for a player.
      Parameters:
      playerId - the player's UUID
      methodKey - the method key
    • getRemainingCooldown

      public long getRemainingCooldown(UUID playerId, String methodKey)
      Gets the remaining cooldown time in seconds.
      Parameters:
      playerId - the player's UUID
      methodKey - the method key
      Returns:
      remaining seconds, or 0 if not on cooldown
    • cleanupExpired

      public void cleanupExpired()
      Cleans up expired cooldowns to prevent memory leaks. Should be called periodically.
    • isBoundMapping

      private static boolean isBoundMapping(Method method, CommandContext context)
      Returns:
      whether the @CmdCD that governs method is bound to a config key
    • getCooldownSeconds

      private Integer getCooldownSeconds(Method method, CommandContext context)
    • unresolvedBinding

      private CommandValidator.ValidationResult unresolvedBinding(CommandContext context, Method method)
      Fails the command closed for a config-bound @CmdCD this validator never had resolved -- reachable only when a CooldownValidator is added after the module loaded, or an executor is registered outside the module loader. Treating it as "no cooldown" would be a silent no-op, and throwing would escape onCommand as Bukkit's generic internal error; instead the player gets the same message shape as a failed command, and the cause is logged at SEVERE and sent to the error report collector like any other command failure (#531 gate-1 WR-02).
    • reportUnresolvedBinding

      private static void reportUnresolvedBinding(CommandContext context, IllegalStateException cause)
    • bindingKey

      @Internal public static String bindingKey(CmdCD cmdCD)
      The cache key of a config-bound @CmdCD: its config class and key.
      Parameters:
      cmdCD - the annotation
      Returns:
      configClassName#key, or null when cmdCD is unbound
      Since:
      6.3.0
    • getOrder

      public int getOrder()
      Description copied from interface: CommandValidator
      Gets the order of this validator. Lower values are executed first.
      Specified by:
      getOrder in interface CommandValidator
      Returns:
      the order value
    • getName

      public String getName()
      Description copied from interface: CommandValidator
      Gets the name of this validator for debugging and logging purposes.
      Specified by:
      getName in interface CommandValidator
      Returns:
      the validator name