Class ChestActivityLogger

java.lang.Object
com.enhancedechest.service.ChestActivityLogger

public final class ChestActivityLogger extends Object
A bounded, batched audit pipeline designed for busy servers.

The Bukkit-thread side captures each occupied slot into immutable strings/numbers exactly once. No ItemStack, ItemMeta, Bukkit registry lookup or component serializer crosses the thread boundary. Diffing, grid rendering, rotation and all file I/O happen on one dedicated worker. The queue is bounded so a stalled disk can never grow the heap without limit.

Item identity is derived in memory. The diff only ever compares the OPEN and CLOSE halves of the same cycle, so identity needs no cross-cycle, cross-server or on-disk stability — only "same item content produces the same string". Material plus ItemMeta.hashCode() gives that without cloning the stack or serializing its data components, which is what makes the capture cheap enough to sit on a region thread. Two different stacks of the same material whose metadata hashes collide would be totalled as one line in a single cycle; that is an acceptable trade for a human-readable log and cannot affect stored chest contents.

Container contents are rendered, not accounted. A shulker box is still one entry in a snapshot; what it holds is spelled out in the text of that entry only. That placement is the whole reason the feature is affordable: the text is built inside buildMetaId(org.bukkit.inventory.ItemStack, org.bukkit.Material, org.bukkit.inventory.meta.ItemMeta, int), which runs on a META_CACHE miss and never again for that exact shulker, whereas folding the inner items into ChestActivityLogger.Snapshot.totals() would have to unpack every container on every capture — up to 27 items per occupied slot, on a region thread. The diff still sees a repacked shulker change, because ItemMeta.hashCode() already covers the container component.

  • Constructor Summary

    Constructors
    Constructor
    Description
    ChestActivityLogger(Path dataFolder, org.slf4j.Logger logger, Telemetry telemetry, boolean enabled, boolean logUnchanged, boolean containerContents, boolean chestContents, int queueCapacity, int maxFileSizeMb, int retentionDays)
     
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    closed(String actorName, UUID actor, UUID owner, int chestIndex, org.bukkit.inventory.ItemStack[] contents)
    Captures CLOSE on the Bukkit-owned thread and offers immutable work to the bounded async queue.
    void
    opened(String actorName, UUID actor, UUID owner, int chestIndex, org.bukkit.inventory.ItemStack[] contents)
    Captures OPEN on the Bukkit-owned thread.
    void
    setChestContents(boolean chestContents)
    When true, each entry also carries a HAVE line under both headers listing what the chest held at that moment.
    void
    setContainerContents(boolean containerContents)
    When true, a shulker box's line also spells out what it holds.
    void
    setEnabled(boolean enabled)
     
    void
    setLogUnchanged(boolean logUnchanged)
    When false, a visit whose CLOSE contents are identical to its OPEN contents is discarded instead of written.
    void
    Stops accepting new cycles, drains the bounded queue, flushes and closes the active log.

    Methods inherited from class java.lang.Object

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

    • ChestActivityLogger

      public ChestActivityLogger(Path dataFolder, org.slf4j.Logger logger, Telemetry telemetry, boolean enabled, boolean logUnchanged, boolean containerContents, boolean chestContents, int queueCapacity, int maxFileSizeMb, int retentionDays)
  • Method Details

    • setEnabled

      public void setEnabled(boolean enabled)
    • setLogUnchanged

      public void setLogUnchanged(boolean logUnchanged)
      When false, a visit whose CLOSE contents are identical to its OPEN contents is discarded instead of written. Most visits change nothing, and those entries otherwise bury the ones that matter.
    • setContainerContents

      public void setContainerContents(boolean containerContents)
      When true, a shulker box's line also spells out what it holds. Flipping this drops META_CACHE, whose detail strings were rendered under the old setting — the identities in a snapshot taken before the flip still compare by value, so a visit straddling a reload is diffed correctly, it just loses the reference-equality shortcut for one cycle.
    • setChestContents

      public void setChestContents(boolean chestContents)
      When true, each entry also carries a HAVE line under both headers listing what the chest held at that moment. Purely a formatting choice: both snapshots are captured and queued either way, so this costs nothing on a Bukkit thread and everything on disk.
    • opened

      public void opened(String actorName, UUID actor, UUID owner, int chestIndex, org.bukkit.inventory.ItemStack[] contents)
      Captures OPEN on the Bukkit-owned thread.
    • closed

      public void closed(String actorName, UUID actor, UUID owner, int chestIndex, org.bukkit.inventory.ItemStack[] contents)
      Captures CLOSE on the Bukkit-owned thread and offers immutable work to the bounded async queue. Under extreme sustained disk failure the newest cycle is dropped instead of blocking a server tick or allowing an unbounded queue to exhaust heap; the worker writes an explicit SYSTEM line with the number dropped as soon as it can make progress again.
    • shutdown

      public void shutdown()
      Stops accepting new cycles, drains the bounded queue, flushes and closes the active log.