Class CooldownValidator
- All Implemented Interfaces:
CommandValidator,PlayerCacheSaver,PlayerCacheManager.ExpiringPlayerCache
Player-cache lifecycle (case 48, @since 6.3.0): an instance registers itself with PlayerCacheManager at its first validate(CommandContext), so quits and the periodic
sweep reach its per-player state. It is released when the last executor that has dispatched
through it unloads -- BaseCommandExecutor's teardown decides that and calls releasePlayerCacheRegistration() -- and it registers again on its next use, so a chain reused
after a release keeps being swept.
- Since:
- 6.2.0
- Version:
- 2.0.0
- Author:
- wisdomme
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprivate static final classAn executor-scoped cooldown key (#539): the executor instance, compared by identity and held weakly so an active cooldown never keeps an unloaded module's executor -- and with it the module's class loader -- reachable, plusmethod.toString().Nested classes/interfaces inherited from interface com.ultikits.ultitools.abstracts.command.validation.CommandValidator
CommandValidator.ValidationResult -
Field Summary
FieldsModifier and TypeFieldDescriptionMap of player UUID -> (cooldown key -> cooldown end timestamp).private final intprivate static final Loggerprivate static final intprivate booleanTrue once this instance has registeredcooldownswith the livePlayerCacheManagerfor quit-based and time-based sweeping (GEN-08, D-03). -
Constructor Summary
ConstructorsConstructorDescriptionCreates a cooldown validator with no default cooldown.CooldownValidator(int defaultCooldownSeconds) Creates a cooldown validator with a default cooldown. -
Method Summary
Modifier and TypeMethodDescriptionvoidapplyCooldown(CommandContext context) Applies cooldown after command execution.static StringbindingKey(CmdCD cmdCD) The cache key of a config-bound@CmdCD: its config class and key.voidCleans up expired cooldowns to prevent memory leaks.voidclearCooldown(UUID playerId, Object executor, String methodKey) Clears one executor's cooldown of a method for a player (#539).voidclearCooldown(UUID playerId, String methodKey) Clears a specific cooldown for a player, on every executor this validator serves.voidclearCooldowns(UUID playerId) Clears all cooldowns for a player.private static ObjectcooldownKey(Object executor, String methodKey) The key an active cooldown is stored under (#539): the executor instance and the method when the dispatch names an executor, the method string alone when it does not.private voidAttempts lazy first-use registration of this instance with the livePlayerCacheManagersingleton.private IntegergetCooldownSeconds(Method method, CommandContext context) getName()Gets the name of this validator for debugging and logging purposes.intgetOrder()Gets the order of this validator.longgetRemainingCooldown(UUID playerId, Object executor, String methodKey) Gets one executor's remaining cooldown of a method, in seconds (#539).longgetRemainingCooldown(UUID playerId, String methodKey) Gets the remaining cooldown time in seconds, on any executor this validator serves.private static booleanisBoundMapping(Method method, CommandContext context) private static booleanWhether a stored cooldown key belongs tomethodKey, whichever executor it names -- the match the string-keyed accessors use.private static booleanA key whose executor has been garbage collected can never be matched again.voidonComplete(CommandContext context, boolean commandSucceeded) Post-action hook that applies the cooldown recorded for this invocation.final voidStopsPlayerCacheManagertracking of this instance and re-arms its lazy registration, so the nextvalidate(CommandContext)registers it again (case 48).private static longremainingSeconds(long endTime) private static voidreportUnresolvedBinding(CommandContext context, IllegalStateException cause) voidsavePlayerData(UUID playerId) PlayerCacheSaverhook: fired byPlayerCacheManager.onPlayerQuit(UUID)("the quit sweep") for the quitting player -- before the generic@PlayerCachefield sweep removes the (by then already-empty)cooldownsentry.voidPlayerCacheManager.ExpiringPlayerCacheopt-in: delegates to the pre-existingcleanupExpired()time-based half.unresolvedBinding(CommandContext context, Method method) Fails the command closed for a config-bound@CmdCDthis validator never had resolved -- reachable only when aCooldownValidatoris added after the module loaded, or an executor is registered outside the module loader.validate(CommandContext context) Validates the command context.Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface com.ultikits.ultitools.abstracts.command.validation.CommandValidator
onRefused, shouldValidate
-
Field Details
-
ORDER
private static final int ORDER- See Also:
-
LOGGER
-
cooldowns
Map of player UUID -> (cooldown key -> cooldown end timestamp).The cooldown key is an
CooldownValidator.ExecutorMethodKey-- the dispatching executor instance, held weakly and compared by identity, plusmethod.toString()-- whenever the context carries an executor, which every dispatch throughBaseCommandExecutordoes (#539). A context without an executor (a validator driven directly, outside any executor) keys by themethod.toString()string alone, exactly as before 6.3.0.saveBeforeRemove = truesosavePlayerData(UUID)-- which delegates to the pre-existingclearCooldowns(UUID)-- fires on quit; see that method's javadoc for why (GEN-08, D-03). -
playerCacheRegistered
private volatile boolean playerCacheRegisteredTrue once this instance has registeredcooldownswith the livePlayerCacheManagerfor 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 barenew CooldownValidator()(the shape a unit test, orBaseCommandExecutor's own constructor, uses) must never even attempt contact with a core plugin that may not exist yet. Guarding on a liveUltiTools.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.Cleared by
releasePlayerCacheRegistration()when the last executor that has dispatched through this instance unloads, so a reused instance registers again on its nextvalidate(CommandContext)instead of staying unswept (case 48). -
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:CommandValidatorValidates the command context.- Specified by:
validatein interfaceCommandValidator- Parameters:
context- the command context to validate- Returns:
- the validation result
-
applyCooldown
Applies cooldown after command execution. Should be called after successful command execution.- Parameters:
context- the command context
-
cooldownKey
The key an active cooldown is stored under (#539): the executor instance and the method when the dispatch names an executor, the method string alone when it does not. -
isKeyFor
Whether a stored cooldown key belongs tomethodKey, whichever executor it names -- the match the string-keyed accessors use. -
onComplete
Post-action hook that applies the cooldown recorded for this invocation. Delegates toapplyCooldown(CommandContext)and is invoked only by a chain that actually ran this validator for the current dispatch -- seeCommandValidator.onComplete(CommandContext, boolean).- Specified by:
onCompletein interfaceCommandValidator- Parameters:
context- the command contextcommandSucceeded- 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 livePlayerCacheManagersingleton. Safe to call unconditionally on everyvalidate(CommandContext)invocation: a no-op onceplayerCacheRegisteredis true, and a cheap, safely-no-op-on-failure retry otherwise (see that field's javadoc). -
releasePlayerCacheRegistration
@Internal public final void releasePlayerCacheRegistration()StopsPlayerCacheManagertracking of this instance and re-arms its lazy registration, so the nextvalidate(CommandContext)registers it again (case 48).Called by
BaseCommandExecutor's unload teardown once no other loaded executor that has dispatched still uses this validator. Not intended for module code: releasing a validator that a loaded executor still uses stops the quit sweep from reaching it until its next dispatch.- Since:
- 6.3.0
-
sweepExpired
public void sweepExpired()PlayerCacheManager.ExpiringPlayerCacheopt-in: delegates to the pre-existingcleanupExpired()time-based half. Invoked once perPlayerCacheManager.sweepExpiredEntries()pass -- the periodic sweep this instance only receives once registered (seeensurePlayerCacheRegistered()) -- independently of whether any player has quit (GEN-08, D-03).- Specified by:
sweepExpiredin interfacePlayerCacheManager.ExpiringPlayerCache- Since:
- 6.3.0
-
savePlayerData
PlayerCacheSaverhook: fired byPlayerCacheManager.onPlayerQuit(UUID)("the quit sweep") for the quitting player -- before the generic@PlayerCachefield sweep removes the (by then already-empty)cooldownsentry. Delegates to the pre-existingclearCooldowns(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:
savePlayerDatain interfacePlayerCacheSaver- Parameters:
playerId- the UUID of the player quitting- Since:
- 6.3.0
-
clearCooldowns
Clears all cooldowns for a player.- Parameters:
playerId- the player's UUID
-
clearCooldown
Clears a specific cooldown for a player, on every executor this validator serves.As of 6.3.0 an active cooldown is keyed by the executor instance as well as the method (#539). With one executor per validator -- the shape both
BaseCommandExecutorconstructors create -- this clears exactly that executor's cooldown, as before. When one validator serves several executors, it clears the method's cooldown on all of them; useclearCooldown(UUID, Object, String)to address one.- Parameters:
playerId- the player's UUIDmethodKey- the method key,method.toString()of the mapping method
-
clearCooldown
Clears one executor's cooldown of a method for a player (#539).- Parameters:
playerId- the player's UUIDexecutor- the executor instance the cooldown was recorded onmethodKey- the method key,method.toString()of the mapping method- Since:
- 6.3.0
-
getRemainingCooldown
Gets the remaining cooldown time in seconds, on any executor this validator serves.With one executor per validator this is that executor's remaining time, as before 6.3.0. When one validator serves several executors it is the longest remaining time among them; use
getRemainingCooldown(UUID, Object, String)to address one (#539).- Parameters:
playerId- the player's UUIDmethodKey- the method key,method.toString()of the mapping method- Returns:
- remaining seconds, or 0 if not on cooldown
-
getRemainingCooldown
Gets one executor's remaining cooldown of a method, in seconds (#539).- Parameters:
playerId- the player's UUIDexecutor- the executor instance the cooldown was recorded onmethodKey- the method key,method.toString()of the mapping method- Returns:
- remaining seconds, or 0 if not on cooldown
- Since:
- 6.3.0
-
remainingSeconds
private static long remainingSeconds(long endTime) -
isLive
A key whose executor has been garbage collected can never be matched again. -
cleanupExpired
public void cleanupExpired()Cleans up expired cooldowns to prevent memory leaks. Should be called periodically. Also drops the cooldowns of an executor that has since been garbage collected (a module that unloaded), which no dispatch can reach any more. -
isBoundMapping
- Returns:
- whether the
@CmdCDthat governsmethodis bound to a config key
-
getCooldownSeconds
-
unresolvedBinding
Fails the command closed for a config-bound@CmdCDthis validator never had resolved -- reachable only when aCooldownValidatoris 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 escapeonCommandas 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
-
bindingKey
The cache key of a config-bound@CmdCD: its config class and key.- Parameters:
cmdCD- the annotation- Returns:
configClassName#key, ornullwhencmdCDis unbound- Since:
- 6.3.0
-
getOrder
public int getOrder()Description copied from interface:CommandValidatorGets the order of this validator. Lower values are executed first.- Specified by:
getOrderin interfaceCommandValidator- Returns:
- the order value
-
getName
Description copied from interface:CommandValidatorGets the name of this validator for debugging and logging purposes.- Specified by:
getNamein interfaceCommandValidator- Returns:
- the validator name
-