Class O2Effects

java.lang.Object
net.pottercraft.ollivanders2.effect.O2Effects
All Implemented Interfaces:
org.bukkit.event.Listener

public class O2Effects extends Object implements org.bukkit.event.Listener
Central manager for all magical effects applied to players.

O2Effects manages the complete effect lifecycle: adding effects, processing them each game tick, removing effects, and persisting effects across player logouts. This class implements Bukkit's Listener interface to hook into server events and distribute them to active effects.

Thread Safety: This class uses a Semaphore and EffectsData wrapper to ensure thread-safe access to effect maps, preventing race conditions when multiple threads access player effects simultaneously. All public methods are synchronized or use the internal EffectsData synchronization.

Data Structure: Effects are stored in two separate maps:

  • Active Effects: O2Effect objects for online players, actively processed each tick
  • Saved Effects: Effect type and duration pairs for offline players, restored on login

Effect Stacking: When an effect is added to a player who already has that effect type, the durations are combined rather than replacing the effect, allowing effect stacking.

See Also:
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final String
    Serialization label for duration
    static final String
    Serialization label for isPermanent
    static final String
    Labels for serializing effect data to JSON format.
    static final String
    Serialization label for effectType
  • Constructor Summary

    Constructors
    Constructor
    Description
    O2Effects(@NotNull Ollivanders2 plugin)
    Constructor
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    addEffect(@NotNull O2Effect effect)
    Add an effect to a player, applying effect stacking and configuration checks.
    void
    ageAllEffects(@NotNull UUID pid, int amount)
    Reduce the duration of all non-permanent effects on a player by a specified amount.
    void
    ageEffect(@NotNull UUID pid, @NotNull O2EffectType effectType, int amount)
    Reduce the duration of a specific effect on a player by a specified amount.
    void
    ageEffectByPercent(@NotNull UUID pid, @NotNull O2EffectType effectType, int percent)
    Reduce the duration of a specific effect on a player by a percentage of its current duration.
    static boolean
    commandUsage(@NotNull org.bukkit.command.CommandSender sender)
    Display the effect command syntax and usage help to an admin.
    @Nullable O2Effect
    deserializeEffect(@NotNull Map<String,String> effectAttributes)
    Deserialize an effect from a string attribute map.
    @Nullable String
    Get the detection text for an active effect that can be detected by the Informous spell.
    @Nullable String
    Detect an active effect on the target player using mind-reading perception.
    getEffect(@NotNull UUID pid, @NotNull O2EffectType effectType)
    Get an active effect object for a player by effect type.
    @NotNull List<O2EffectType>
    getEffects(@NotNull UUID pid)
    Get a list of all effect types currently active on a player.
    boolean
    hasEffect(@NotNull UUID pid, @NotNull O2EffectType effectType)
    Check if a player has a specific active effect.
    boolean
    hasEffects(@NotNull UUID pid)
    Check if a player has any active effects.
    void
    Load all saved effects from disk on server startup.
    void
    onAsyncPlayerChatEvent(@NotNull org.bukkit.event.player.AsyncPlayerChatEvent event)
    Distribute chat messages to all active effects on the chatting player.
    void
    onDeath(@NotNull UUID pid)
    Reset all effects when a player dies.
    void
    Clean up the effects system when the plugin is disabled.
    void
    Initialize the effects system on plugin startup.
    void
    onEntityDamageByEntityEvent(@NotNull org.bukkit.event.entity.EntityDamageByEntityEvent event)
    Distribute entity damage events to all active effects on the damaged player.
    void
    onEntityPickupItemEvent(@NotNull org.bukkit.event.entity.EntityPickupItemEvent event)
    Distribute item pickup events to all active effects on the player picking up the item.
    void
    onEntityTargetEvent(@NotNull org.bukkit.event.entity.EntityTargetEvent event)
    Distribute entity target events to all active effects on the targeted entity.
    void
    onJoin(@NotNull UUID pid)
    Apply all saved effects when a player joins the server.
    void
    Distribute spell projectile move events to all active effects on all players.
    void
    onPlayerBedEnterEvent(@NotNull org.bukkit.event.player.PlayerBedEnterEvent event)
    Distribute bed enter events to all active effects on the player.
    void
    onPlayerDropItemEvent(@NotNull org.bukkit.event.player.PlayerDropItemEvent event)
    Distribute item drop events to all active effects on the dropping player.
    void
    onPlayerInteractEvent(@NotNull org.bukkit.event.player.PlayerInteractEvent event)
    Distribute player interact events to all active effects on the interacting player.
    void
    onPlayerItemConsumeEvent(@NotNull org.bukkit.event.player.PlayerItemConsumeEvent event)
    Distribute item consumption events to all active effects on the consuming player.
    void
    onPlayerItemHeldEvent(@NotNull org.bukkit.event.player.PlayerItemHeldEvent event)
    Distribute item held change events to all active effects on the player.
    void
    onPlayerMoveEvent(@NotNull org.bukkit.event.player.PlayerMoveEvent event)
    Distribute player move events to all active effects on the moving player.
    void
    onPlayerQuitEvent(@NotNull org.bukkit.event.player.PlayerQuitEvent event)
    Distribute player quit events to all active effects on the departing player.
    void
    onPlayerToggleFlightEvent(@NotNull org.bukkit.event.player.PlayerToggleFlightEvent event)
    Distribute player flight toggle events to all active effects.
    void
    onPlayerToggleSneakEvent(@NotNull org.bukkit.event.player.PlayerToggleSneakEvent event)
    Distribute player sneak toggle events to all active effects.
    void
    onPlayerToggleSprintEvent(@NotNull org.bukkit.event.player.PlayerToggleSprintEvent event)
    Distribute player sprint toggle events to all active effects.
    void
    onPlayerVelocityEvent(@NotNull org.bukkit.event.player.PlayerVelocityEvent event)
    Distribute velocity change events to all active effects on the affected player.
    void
    onProjectileHitEvent(@NotNull org.bukkit.event.entity.ProjectileHitEvent event)
    Distribute projectile hit events to all active effects on all players.
    void
    onProjectileLaunchEvent(@NotNull org.bukkit.event.entity.ProjectileLaunchEvent event)
    Distribute projectile launch events to all active effects on all players.
    void
    onQuit(@NotNull UUID pid)
    Save all active effects when a player quits the server.
    void
    Remove all active and saved effects from all players system-wide.
    void
    removeEffect(@NotNull UUID pid, @NotNull O2EffectType effectType)
    Remove an effect from a player, cleaning up state and message handling.
    static boolean
    runCommand(@NotNull org.bukkit.command.CommandSender sender, @NotNull String[] args, @NotNull Ollivanders2 p)
    Handle the /ollivanders2 effect admin command for effect toggling.
    void
    Persist all active and saved effects to disk in JSON format.
    @NotNull List<Map<String,String>>
    Serialize all active and saved effects across all players in the game.
    void
    upkeep(@NotNull UUID pid)
    Process game heartbeat upkeep for all effects on a player.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • effectPIDLabel

      public static final String effectPIDLabel
      Labels for serializing effect data to JSON format.

      String keys used when converting O2Effect objects to and from JSON representation for persistent storage. These constants are used in serialization and deserialization methods to maintain consistent field naming across saves and loads.

      See Also:
    • effectPermanentLabel

      public static final String effectPermanentLabel
      Serialization label for isPermanent
      See Also:
    • effectDurationLabel

      public static final String effectDurationLabel
      Serialization label for duration
      See Also:
    • effectTypeLabel

      public static final String effectTypeLabel
      Serialization label for effectType
      See Also:
  • Constructor Details

    • O2Effects

      public O2Effects(@NotNull @NotNull Ollivanders2 plugin)
      Constructor
      Parameters:
      plugin - a callback to the plugin
  • Method Details

    • onEnable

      public void onEnable()
      Initialize the effects system on plugin startup.

      Reads configuration to determine which effects are enabled or disabled. Currently only lycanthropy configuration is loaded, as other effects are enabled by default. This allows servers to disable specific effects if they conflict with gameplay or other plugins.

    • onEntityDamageByEntityEvent

      public void onEntityDamageByEntityEvent(@NotNull @NotNull org.bukkit.event.entity.EntityDamageByEntityEvent event)
      Distribute entity damage events to all active effects on the damaged player.

      When a player takes damage from another entity, this event handler distributes the damage event to all active effects on that player. Effects can use this event to implement damage prevention, redirection, mitigation, or other protective mechanics. This handler processes at HIGHEST priority to ensure effects are evaluated before other plugins can modify the event.

      Only players who are the damage target are processed; damage from other entity types is ignored. Each active effect on the damaged player receives the event via its doOnEntityDamageByEntityEvent() callback method.

      Parameters:
      event - the entity damage by entity event, containing damage amount, source entity, and target player
    • onPlayerInteractEvent

      public void onPlayerInteractEvent(@NotNull @NotNull org.bukkit.event.player.PlayerInteractEvent event)
      Distribute player interact events to all active effects on the interacting player.

      When a player interacts with the world (right-click on blocks/entities, left-click, etc.), this handler distributes the interaction event to all active effects on that player. Effects can use this to intercept interactions, prevent certain actions, or trigger special behaviors based on what the player is interacting with.

      Parameters:
      event - the player interact event
    • onAsyncPlayerChatEvent

      public void onAsyncPlayerChatEvent(@NotNull @NotNull org.bukkit.event.player.AsyncPlayerChatEvent event)
      Distribute chat messages to all active effects on the chatting player.

      When a player sends a chat message, this handler distributes it to all active effects. Effects can use this to modify chat messages, prevent chat, or respond to specific keywords. This uses the deprecated AsyncPlayerChatEvent which processes asynchronously.

      Parameters:
      event - the async player chat event
    • onPlayerBedEnterEvent

      public void onPlayerBedEnterEvent(@NotNull @NotNull org.bukkit.event.player.PlayerBedEnterEvent event)
      Distribute bed enter events to all active effects on the player.

      When a player attempts to enter a bed, this handler distributes the event to all active effects. Effects can use this to prevent sleeping or trigger special sleep-related mechanics.

      Parameters:
      event - the player bed enter event
    • onPlayerToggleFlightEvent

      public void onPlayerToggleFlightEvent(@NotNull @NotNull org.bukkit.event.player.PlayerToggleFlightEvent event)
      Distribute player flight toggle events to all active effects.

      When a player attempts to toggle flight mode (if allowed), this handler distributes the event to all active effects. Effects can use this to prevent or modify flight behavior.

      Parameters:
      event - the player toggle flight event
    • onPlayerToggleSneakEvent

      public void onPlayerToggleSneakEvent(@NotNull @NotNull org.bukkit.event.player.PlayerToggleSneakEvent event)
      Distribute player sneak toggle events to all active effects.

      When a player toggles sneak mode, this handler distributes the event to all active effects. Effects can use this to prevent sneaking or enforce sneaking behavior.

      Parameters:
      event - the player toggle sneak event
    • onPlayerToggleSprintEvent

      public void onPlayerToggleSprintEvent(@NotNull @NotNull org.bukkit.event.player.PlayerToggleSprintEvent event)
      Distribute player sprint toggle events to all active effects.

      When a player toggles sprint mode, this handler distributes the event to all active effects. Effects can use this to prevent sprinting or enforce sprinting behavior.

      Parameters:
      event - the player toggle sprint event
    • onPlayerVelocityEvent

      public void onPlayerVelocityEvent(@NotNull @NotNull org.bukkit.event.player.PlayerVelocityEvent event)
      Distribute velocity change events to all active effects on the affected player.

      When a player's velocity is modified (knockback, launcher, etc.), this handler distributes the event to all active effects. Effects can use this to modify or negate velocity changes.

      Parameters:
      event - the player velocity event
    • onEntityPickupItemEvent

      public void onEntityPickupItemEvent(@NotNull @NotNull org.bukkit.event.entity.EntityPickupItemEvent event)
      Distribute item pickup events to all active effects on the player picking up the item.

      When a player picks up an item, this handler distributes the event to all active effects. Effects can use this to prevent item pickup or trigger special behavior on pickup.

      Parameters:
      event - the entity pickup item event
    • onPlayerItemHeldEvent

      public void onPlayerItemHeldEvent(@NotNull @NotNull org.bukkit.event.player.PlayerItemHeldEvent event)
      Distribute item held change events to all active effects on the player.

      When a player changes which hotbar slot is selected, this handler distributes the event to all active effects. Effects can use this to track item changes or prevent slot switching.

      Parameters:
      event - the player item held event
    • onPlayerItemConsumeEvent

      public void onPlayerItemConsumeEvent(@NotNull @NotNull org.bukkit.event.player.PlayerItemConsumeEvent event)
      Distribute item consumption events to all active effects on the consuming player.

      When a player consumes an item (food, potion, etc.), this handler distributes the event to all active effects. Effects can use this to prevent consumption or modify what happens when items are consumed.

      Parameters:
      event - the player item consume event
    • onPlayerDropItemEvent

      public void onPlayerDropItemEvent(@NotNull @NotNull org.bukkit.event.player.PlayerDropItemEvent event)
      Distribute item drop events to all active effects on the dropping player.

      When a player drops an item from their inventory, this handler distributes the event to all active effects. Effects can use this to prevent item dropping or track dropped items.

      Parameters:
      event - the player drop item event
    • onPlayerMoveEvent

      public void onPlayerMoveEvent(@NotNull @NotNull org.bukkit.event.player.PlayerMoveEvent event)
      Distribute player move events to all active effects on the moving player.

      When a player moves, this handler distributes the movement event to all active effects. Effects can use this to prevent movement, teleport the player, or trigger location-based behavior.

      Parameters:
      event - the player move event
    • onOllivandersSpellProjectileMoveEvent

      public void onOllivandersSpellProjectileMoveEvent(@NotNull @NotNull OllivandersSpellProjectileMoveEvent event)
      Distribute spell projectile move events to all active effects on all players.

      When a custom Ollivanders spell projectile moves, this handler distributes the event to all active effects on all online players. Effects can use this to interact with projectiles or trigger special mechanics when projectiles are nearby.

      Parameters:
      event - the Ollivanders spell projectile move event
    • onEntityTargetEvent

      public void onEntityTargetEvent(@NotNull @NotNull org.bukkit.event.entity.EntityTargetEvent event)
      Distribute entity target events to all active effects on the targeted entity.

      When an entity attempts to target another entity, this handler distributes the event to all active effects on the targeted entity. Effects can use this to prevent targeting or change what the entity is targeting.

      Parameters:
      event - the entity target event
    • onPlayerQuitEvent

      public void onPlayerQuitEvent(@NotNull @NotNull org.bukkit.event.player.PlayerQuitEvent event)
      Distribute player quit events to all active effects on the departing player.

      When a player leaves the server, this handler distributes the quit event to all active effects on that player. Effects can use this to perform cleanup or save state information.

      Parameters:
      event - the player quit event
    • onProjectileLaunchEvent

      public void onProjectileLaunchEvent(@NotNull @NotNull org.bukkit.event.entity.ProjectileLaunchEvent event)
      Distribute projectile launch events to all active effects on all players.

      When any projectile is launched in the world, this handler distributes the event to all active effects on all online players. Effects can use this to interact with projectiles, prevent launches, or trigger projectile-based mechanics.

      Parameters:
      event - the projectile launch event
    • onProjectileHitEvent

      public void onProjectileHitEvent(@NotNull @NotNull org.bukkit.event.entity.ProjectileHitEvent event)
      Distribute projectile hit events to all active effects on all players.

      When any projectile hits an entity or block, this handler distributes the event to all active effects on all online players. Effects can use this to intercept projectile impacts or trigger special mechanics when projectiles hit.

      Parameters:
      event - the projectile hit event
    • getEffects

      @NotNull public @NotNull List<O2EffectType> getEffects(@NotNull @NotNull UUID pid)
      Get a list of all effect types currently active on a player.

      Returns the effect type enumeration values for all active effects on the specified player. This returns only the effect types, not the actual O2Effect objects with their state and duration. Returns an empty list if the player has no active effects.

      Parameters:
      pid - the unique ID of the player
      Returns:
      a list of O2EffectType values for all active effects, empty if none
    • hasEffect

      public boolean hasEffect(@NotNull @NotNull UUID pid, @NotNull @NotNull O2EffectType effectType)
      Check if a player has a specific active effect.

      Determines if a particular effect is currently active on the player. Only searches the player's active effects; saved effects for offline players are not checked. This method is useful for conditional logic that depends on specific magical effects being applied.

      Parameters:
      pid - the unique ID of the player to check
      effectType - the specific effect type to look for
      Returns:
      true if the player has this effect active, false otherwise
    • hasEffects

      public boolean hasEffects(@NotNull @NotNull UUID pid)
      Check if a player has any active effects.

      Determines if a player currently has one or more active effects applied. This is a convenience method for checking if a player is affected by any magical effect without needing to check specific effect types. Only checks active effects; saved effects for offline players are not considered.

      Parameters:
      pid - the unique ID of the player to check
      Returns:
      true if the player has any active effects, false if they have no active effects
    • onJoin

      public void onJoin(@NotNull @NotNull UUID pid)
      Apply all saved effects when a player joins the server.

      When a player logs back in, this method retrieves any effect objects that were saved when they logged out and re-adds them to the player via addEffect(). The saved effects are then cleared from persistent storage. Each saved effect retains its remaining duration from when it was saved, allowing players to have their effects restored exactly as they left them. This method is called by the player management system when a player enters the server.

      Parameters:
      pid - the unique ID of the joining player
    • onQuit

      public void onQuit(@NotNull @NotNull UUID pid)
      Save all active effects when a player quits the server.

      When a player logs out, this method persists all their currently active effects to saved storage. Each active effect's type and remaining duration are recorded, allowing the effects to be restored when the player logs back in via the onJoin() method. Effects that have been killed will not be saved. This method is called by the player management system when a player leaves the server.

      Parameters:
      pid - the unique ID of the departing player
    • onDeath

      public void onDeath(@NotNull @NotNull UUID pid)
      Reset all effects when a player dies.

      When a player dies, this method performs a complete cleanup of all their effects. All active effects are terminated by calling their kill() method, and both the active effects and saved effects are cleared. This ensures the player starts fresh with no lingering effects when they respawn. This method is called by the player management system when a player's death is processed.

      Parameters:
      pid - the unique ID of the deceased player
    • onDisable

      public void onDisable()
      Clean up the effects system when the plugin is disabled.

      Called when the plugin is being shut down or unloaded by the server. This method persists all currently active effects to disk to ensure they are not lost if the server is restarted. All effects across all players (both online and offline) are serialized and saved to the effects JSON file via the saveEffects() method. This guarantees that the game state can be recovered when the plugin is re-enabled.

    • saveEffects

      public void saveEffects()
      Persist all active and saved effects to disk in JSON format.

      Serializes all magical effects currently active across all players (both online and offline) and writes them to the effects JSON file for persistent storage. This is typically called during server shutdown or periodic autosaves to ensure no effects are lost if the server crashes.

    • loadEffects

      public void loadEffects()
      Load all saved effects from disk on server startup.

      Reads the effects JSON file and reconstructs all previously saved effects, restoring them to the game state. Each effect is deserialized and added back to its target player. If the file doesn't exist or is empty, logs an informational message and continues without error. This is called by onEnable() during server initialization.

    • serializeEffects

      @NotNull public @NotNull List<Map<String,String>> serializeEffects()
      Serialize all active and saved effects across all players in the game.

      Converts all magical effects currently active on all online players and all saved effects for offline players into a serialized list format suitable for persistence and storage. Each effect is individually serialized into a map with its type and remaining duration as strings. This method iterates through all effect maps (both active and saved) and serializes each effect. This is typically called when saving global game state or during server shutdown to persist all active effects.

      Returns:
      a list of serialized effect maps, where each map contains effect type and duration as strings
    • deserializeEffect

      @Nullable public @Nullable O2Effect deserializeEffect(@NotNull @NotNull Map<String,String> effectAttributes)
      Deserialize an effect from a string attribute map.

      Reconstructs an O2Effect object from its serialized string representation. Parses the effect type, target player ID, duration, and permanent status from the attribute map. All required fields must be present for successful deserialization; if any are missing or invalid, returns null. This is used when loading effects from persistent storage.

      Parameters:
      effectAttributes - a map of effect attributes (type, PID, permanent flag, duration) expected keys: Effect_type, Effect_target_pid, Effect_is_permanent, Effect_duration
      Returns:
      the reconstructed O2Effect object, or null if deserialization fails due to missing or invalid fields
    • addEffect

      public void addEffect(@NotNull @NotNull O2Effect effect)
      Add an effect to a player, applying effect stacking and configuration checks.

      Applies a new effect to the player after verifying it is enabled via configuration. If the player already has the same effect type active, the durations are combined instead of replacing the effect, allowing effect stacking. Shape-shifting effects are mutually exclusive; if the player already has one shape-shifting effect, new shape-shifting effects are rejected. Once added, the effect is stored in the player's active effects map and processed on each game tick. If the effect has an affected player message, it is sent to the player with a 5-tick delay.

      This method is thread-safe and synchronized. Only enabled effects are added; disabled effects are silently rejected. Shape-shifting effects include transformations and other appearance-changing effects.

      Parameters:
      effect - the O2Effect object to add to the player (must not be null)
    • removeEffect

      public void removeEffect(@NotNull @NotNull UUID pid, @NotNull @NotNull O2EffectType effectType)
      Remove an effect from a player, cleaning up state and message handling.

      Terminates an active effect on the player by calling its kill() method and executing its doRemove() cleanup logic. The effect is then removed from the player's active effects map. If the specified effect type is not found on the player, a debug message is logged but no error is thrown. This method is primarily called internally by the upkeep system when effects expire or are disabled via configuration.

      This method is thread-safe and synchronized. The effect's kill() and doRemove() methods handle any cleanup specific to that effect type (e.g., removing potion effects, restoring player appearance).

      Parameters:
      pid - the unique ID of the player whose effect should be removed
      effectType - the type of effect to remove from the player
    • getEffect

      public O2Effect getEffect(@NotNull @NotNull UUID pid, @NotNull @NotNull O2EffectType effectType)
      Get an active effect object for a player by effect type.

      Retrieves the O2Effect object for a specific effect type that is currently active on the player. This allows direct access to the effect's state, duration, and other properties. Returns null if the player does not have the specified effect type active. This method is thread-safe and synchronized to prevent race conditions when accessing effect objects.

      Parameters:
      pid - the unique ID of the player whose effect should be retrieved
      effectType - the type of effect to get
      Returns:
      the O2Effect object if the effect is active on the player, null if not found
      See Also:
    • upkeep

      public void upkeep(@NotNull @NotNull UUID pid)
      Process game heartbeat upkeep for all effects on a player.

      Runs the periodic game tick processing for all active effects on the specified player. This method calls checkEffect() on each active effect to allow them to perform their per-tick logic and update their state. After checking, any effects that have been killed or are no longer enabled are automatically removed via removeEffect(). This method is called once per game tick for each online player by the OllivandersScheduler to keep effects synchronized with the server heartbeat.

      Note: This method only processes online players; effects for offline players are stored and restored when the player rejoins via onJoin().

      Parameters:
      pid - the unique ID of the player whose effects should be processed
      See Also:
    • ageAllEffects

      public void ageAllEffects(@NotNull @NotNull UUID pid, int amount)
      Reduce the duration of all non-permanent effects on a player by a specified amount.

      Decrements the duration of every non-permanent effect currently active on the player by the given amount. Permanent effects are skipped and remain unaffected. This is commonly used during the game heartbeat to age all effects by the number of ticks that have passed since the last update. If an effect's duration reaches zero or below, it will be removed during the next upkeep cycle.

      Parameters:
      pid - the unique ID of the player whose effects should be aged
      amount - the number of ticks to subtract from all non-permanent effect durations
      See Also:
    • ageEffect

      public void ageEffect(@NotNull @NotNull UUID pid, @NotNull @NotNull O2EffectType effectType, int amount)
      Reduce the duration of a specific effect on a player by a specified amount.

      Decrements the duration of a specific effect currently active on the player by the given amount. The effect is located by its type, and if found, its duration is directly reduced. If the effect is not found, this method does nothing. The effect map is updated after the duration change. If the effect's duration reaches zero or below, it will be removed during the next upkeep cycle.

      Parameters:
      pid - the unique ID of the player whose effect should be aged
      effectType - the type of effect to age
      amount - the number of ticks to subtract from the effect's duration
      See Also:
    • ageEffectByPercent

      public void ageEffectByPercent(@NotNull @NotNull UUID pid, @NotNull @NotNull O2EffectType effectType, int percent)
      Reduce the duration of a specific effect on a player by a percentage of its current duration.

      Decrements the duration of a specific effect by reducing it based on a percentage of its current value. The calculation works as follows:

      • Reduction amount = current duration × (percent / 100)
      • New duration = current duration - reduction amount

      The percent parameter is clamped to a minimum of 1; values less than 1 are set to 1. Values of 100 or greater completely set the duration to 0. Permanent effects are skipped and remain unaffected. If the effect is not found on the player, this method does nothing. If the effect's duration reaches zero or below after percentage reduction, it will be removed during the next upkeep cycle.

      Parameters:
      pid - the unique ID of the player whose effect should be aged
      effectType - the type of effect to age by percentage
      percent - the percentage of the effect's current duration to subtract (clamped to minimum 1, values ≥ 100 completely remove the effect)
      See Also:
    • detectEffectWithInformous

      @Nullable public @Nullable String detectEffectWithInformous(@NotNull @NotNull UUID pid)
      Get the detection text for an active effect that can be detected by the Informous spell.

      Searches through all active effects on the player to find the first one that has detectable information for the Informous spell. Each effect can define informousText that describes what a caster would learn about it when using Informous. Only the first detectable effect found is returned. If no active effects have informousText configured, this method returns null. This is commonly used by the INFORMOUS spell to provide intelligence about what effects are on a target player.

      Parameters:
      pid - the unique ID of the player to check for detectable effects
      Returns:
      the informousText of the first detectable effect found, or null if no detectable effects exist
      See Also:
    • detectEffectWithLegilimens

      @Nullable public @Nullable String detectEffectWithLegilimens(@NotNull @NotNull UUID pid)
      Detect an active effect on the target player using mind-reading perception.

      Searches for detectable magical effects affecting the target player for the LEGILIMENS (mind-reading) spell. Returns the legilimensText property of the first active effect that has detectable mental traces. This method is used by the LEGILIMENS spell to provide intelligence about the target player's condition, revealing hidden effects that can be mentally perceived. Returns null if the target has no mentally-detectable effects.

      Parameters:
      pid - the unique ID of the player to probe for detectable effects
      Returns:
      the mental perception text of the first detectable effect, or null if no mentally-detectable effects are found
      See Also:
    • runCommand

      public static boolean runCommand(@NotNull @NotNull org.bukkit.command.CommandSender sender, @NotNull @NotNull String[] args, @NotNull @NotNull Ollivanders2 p)
      Handle the /ollivanders2 effect admin command for effect toggling.

      Processes the effect subcommand for administrative management of magical effects on players. The command requires admin permission (Ollivanders2.admin) and can be used to toggle effects on the command sender or a specified target player. The effect is toggled: if the effect is currently applied to the target player, it is removed; if not applied, a new effect instance is created and applied with a 60-second duration.

      Command Syntax and Behavior:

      • /ollivanders2 effect effect_name - toggles the named effect on the command sender
      • /ollivanders2 effect effect_name player_name - toggles the named effect on the specified player
      • Effect names are case-insensitive and must match a valid O2EffectType enum constant
      • Only enabled effects (configured in server config) can be applied
      • Sends feedback messages confirming the action or explaining errors
      Parameters:
      sender - the player issuing the command (must have Ollivanders2.admin permission)
      args - the command arguments: [effect_name] or [effect_name, player_name]
      p - a callback to the Ollivanders2 plugin for server access
      Returns:
      true if the command was processed successfully (even if effect toggle failed), false if sender lacks permission or wrong argument count
      See Also:
    • commandUsage

      public static boolean commandUsage(@NotNull @NotNull org.bukkit.command.CommandSender sender)
      Display the effect command syntax and usage help to an admin.

      Sends formatted help text to the command sender displaying the complete syntax for the effect subcommand. Two usage patterns are shown: toggling an effect on the command sender themselves, and toggling an effect on a specified target player. This is typically called when the command is invoked with invalid arguments.

      Parameters:
      sender - the admin requesting the usage help or receiving syntax error feedback
      Returns:
      always true to indicate the command was processed
      See Also:
    • removeAllEffects

      public void removeAllEffects()
      Remove all active and saved effects from all players system-wide.

      Clears all effects from both the active effects list (online players) and saved effects list (offline players), performing a complete global reset of the effects system. This is a destructive operation that should only be used in specific contexts: test cleanup to reset game state between test runs, or during server-wide game resets where all effects must be wiped. This method logs a debug message when invoked and delegates to the internal EffectsData.killAllEffects() method for the actual cleanup.