Class Database

java.lang.Object
valorless.havenbags.Database

public class Database extends Object
Central data-access layer for HavenBags.

Manages the full lifecycle of Bag objects in memory and on disk/database: loading on startup, dirty-tracking via changedBags, periodic auto-saving, and shutdown persistence. Supports four storage backends selected at runtime via save-type in config.yml:

Bag instances are stored in a UUID-keyed in-memory map and are considered the single source of truth for runtime state. Mutating a bag through the public API (e.g. updateBag(java.lang.String, java.util.List<org.bukkit.inventory.ItemStack>, valorless.havenbags.Database.UpdateSource...),

invalid reference
#addTrusted
) automatically registers it in changedBags so it is included in the next auto-save cycle.
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static class 
    Lightweight view of a bag item paired with its current content list.
    static enum 
    Determines how a bag retrieval or update should be treated at the call site.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static HashMap<UUID,Bag>
    Bags that have been mutated since the last save cycle.
    static long
    Auto-save interval in server ticks, derived from auto-save.interval in config.yml (seconds × 20).
  • Constructor Summary

    Constructors
    Constructor
    Description
     
  • Method Summary

    Modifier and Type
    Method
    Description
    static Boolean
    Returns true if a bag with the given UUID exists in the in-memory map.
    static org.bukkit.entity.Player
    bagOpenBy(String uuid, org.bukkit.inventory.ItemStack bagItem)
    Returns the Player currently viewing the bag, or null if it is not open.
    static void
    Switches the active storage backend to type at runtime.
    static Boolean
    Clears the contents of every bag currently loaded in memory.
    static Boolean
    Clears the contents of the bag with the given UUID.
    static Boolean
    Clears the contents of all bags owned by the specified player.
    static boolean
    Returns true if a bag with the given UUID is present in the in-memory map.
    static Bag
    createBag(String uuid, String owner, List<org.bukkit.inventory.ItemStack> content, org.bukkit.entity.Player creator, org.bukkit.inventory.ItemStack bag)
    Creates a new bag, stores it in memory, and persists it immediately for MYSQLPLUS.
    static Bag
    Registers an already-constructed Bag into memory and persists it immediately for MYSQLPLUS.
    static Boolean
    deleteBag(String uuid, org.bukkit.entity.Player... player)
    Permanently deletes a bag from both the storage backend and the in-memory map.
    static List<com.google.gson.JsonObject>
    Deserialises a JSON array string into a list of JsonObject elements.
    static void
    Discards the current in-memory bag map and reloads all bags from the storage backend via loadData().
    static Bag
    Retrieves a loaded Bag by UUID.
    static List<String>
    Returns a list of all player UUID strings that own at least one bag.
    static List<String>
    getBags(String playerUUID)
    Returns a list of all bag UUID strings owned by the specified player.
    static List<Bag>
    getBagsData(String playerUUID)
    Returns a list of all Bag instances owned by the specified player.
    Returns the currently active DatabaseType.
    static MySQL
    Returns the active MySQL connection wrapper, or null if the current backend is not MySQL-based.
    static List<Bag>
    Returns all bags that are currently marked as open (i.e.
    static void
    Initialises the database layer on plugin enable.
    static boolean
    isBagOpen(String uuid, org.bukkit.inventory.ItemStack bagItem)
    Checks whether the bag with the given UUID is currently open.
    static boolean
    isBagOpen(org.bukkit.inventory.ItemStack bagItem)
    Checks whether the bag represented by the given ItemStack is currently open, using the UUID stored in its PDC.
    static boolean
    Returns true once loadData() has completed successfully and the in-memory bag map is available for use.
    static void
    Loads all bags from the active storage backend into the in-memory map.
    static void
    Marks the bag as closed and clears the viewer and GUI references.
    static void
    markBagOpen(String uuid, org.bukkit.inventory.ItemStack bagItem, org.bukkit.entity.Player player)
    Marks the bag as open and records the viewing player.
    static void
    markBagOpen(String uuid, org.bukkit.inventory.ItemStack bagItem, org.bukkit.entity.Player player, BagGUI gui)
    Marks the bag as open, records the viewing player, and attaches the associated BagGUI instance.
    static void
    Performs a lightweight config reload (interval only).
    static void
    Removes the bag with the given UUID from the in-memory map and the changedBags dirty set.
    static void
    Reset the tooltip-styles of ALL bags to the default one specified in the config.
    This is used when the tooltip-style is changed in the config, to update all bags to the new style.
    static void
    saveData(boolean shutdown, boolean... conversion)
    Persists all dirty bags to the active storage backend.
    protected static void
    Sets the active DatabaseType.
    protected static void
    setMysql(MySQL mysql)
    Sets the active MySQL connection wrapper.
    static void
    Closes all active database connections cleanly.
    static void
    updateBag(String uuid, List<org.bukkit.inventory.ItemStack> content, Database.UpdateSource... source)
    Updates a bag's content by UUID and marks it dirty for the next save cycle.
    static void
    updateBag(org.bukkit.inventory.ItemStack bagItem, List<org.bukkit.inventory.ItemStack> content, Database.UpdateSource... source)
    Updates a bag's content and visual metadata (texture, model data, item model) from the given bag ItemStack, then marks it dirty for the next save cycle.

    Methods inherited from class java.lang.Object

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

    • changedBags

      @Internal public static HashMap<UUID,Bag> changedBags
      Bags that have been mutated since the last save cycle.

      Populated by Bag.setChanged(boolean) (called with true) and by the various Database mutator methods. Drained and cleared on each saveData(boolean, boolean...) call. Marked Internal — external code should not manipulate this map directly; use the provided API methods instead.

    • interval

      public static long interval
      Auto-save interval in server ticks, derived from auto-save.interval in config.yml (seconds × 20).
  • Constructor Details

    • Database

      public Database()
  • Method Details

    • isReady

      public static boolean isReady()
      Returns true once loadData() has completed successfully and the in-memory bag map is available for use.
      Returns:
      true if the database layer is ready
    • init

      @DoNotCall("Meant to be called by HavenBags only.") @Internal public static void init()
      Initialises the database layer on plugin enable.

      Steps performed:

      1. Determines and sets the active DatabaseType from config.
      2. Opens the appropriate connection (MySQL / SQLite) if required.
      3. Calls loadData() to populate the in-memory bag map.
      4. Schedules the repeating auto-save task (skipped for MYSQLPLUS).
      Disables the plugin if an invalid save-type is configured.
    • shutdown

      @DoNotCall("Meant to be called by HavenBags only.") @Internal public static void shutdown()
      Closes all active database connections cleanly. Should be called from onDisable after saveData(boolean, boolean...) to ensure connections are released before the plugin unloads.
    • changeDatabase

      public static void changeDatabase(DatabaseType type)
      Switches the active storage backend to type at runtime.

      Disconnects/closes the current backend, cancels the existing auto-save task, then starts a new auto-save timer for the new backend. For DatabaseType.MYSQL and DatabaseType.MYSQLPLUS the timer is immediately cancelled since those backends use real-time or per-call saving.

      Parameters:
      type - the new DatabaseType to switch to
    • reload

      public static void reload()
      Performs a lightweight config reload (interval only).

      Note: bag data is not reloaded. Use forceReload() to fully reload bag data from the storage backend, at the cost of losing any unsaved in-memory changes.

    • forceReload

      public static void forceReload()
      Discards the current in-memory bag map and reloads all bags from the storage backend via loadData().

      Warning: any unsaved changes will be lost. Call saveData(boolean, boolean...) first if persistence is required.

    • contains

      public static boolean contains(@NotNull String uuid)
      Returns true if a bag with the given UUID is present in the in-memory map.
      Parameters:
      uuid - the bag UUID string to look up
      Returns:
      true if the bag is loaded
    • bagExists

      public static Boolean bagExists(@NotNull String uuid)
      Returns true if a bag with the given UUID exists in the in-memory map.

      Unlike contains(String), this method validates the UUID format and returns false (rather than throwing) for "null" or malformed strings.

      Parameters:
      uuid - the bag UUID string to check
      Returns:
      true if the bag exists, false for invalid/missing UUIDs
    • getBag

      public static Bag getBag(@NotNull String uuid, @Nullable Database.UpdateSource... source)
      Retrieves a loaded Bag by UUID.

      If source is Database.UpdateSource.PLAYER, the bag is also marked as open (Bag.setOpen(boolean)) to reflect that a player is actively using it. Returns null and logs a debug message if the bag is not found.

      Parameters:
      uuid - the bag UUID string
      source - optional — pass Database.UpdateSource.PLAYER to mark the bag open
      bagItem - the ItemStack representing the bag (used for context; may be null)
      Returns:
      the Bag instance, or null if not found
    • updateBag

      public static void updateBag(@NotNull String uuid, @NotNull List<org.bukkit.inventory.ItemStack> content, @Nullable Database.UpdateSource... source)
      Updates a bag's content by UUID and marks it dirty for the next save cycle.

      If source is Database.UpdateSource.PLAYER, the bag is also marked as closed after the content update.

      Parameters:
      uuid - the bag UUID string
      content - the new content list to store
      source - optional — pass Database.UpdateSource.PLAYER to also mark the bag closed
    • updateBag

      public static void updateBag(@NotNull org.bukkit.inventory.ItemStack bagItem, @NotNull List<org.bukkit.inventory.ItemStack> content, Database.UpdateSource... source)
      Updates a bag's content and visual metadata (texture, model data, item model) from the given bag ItemStack, then marks it dirty for the next save cycle.

      This overload reads the UUID from the item's PDC. If capacity-based-textures is enabled in config, the bag's texture is also updated to reflect fill level. If source is Database.UpdateSource.PLAYER, the bag is marked as closed.

      Parameters:
      bagItem - the bag ItemStack to read UUID and visual data from
      content - the new content list to store
      source - optional — pass Database.UpdateSource.PLAYER to also mark the bag closed
    • createBag

      public static Bag createBag(@NotNull String uuid, @NotNull String owner, @NotNull List<org.bukkit.inventory.ItemStack> content, org.bukkit.entity.Player creator, org.bukkit.inventory.ItemStack bag)
      Creates a new bag, stores it in memory, and persists it immediately for MYSQLPLUS. Fires a BagCreateEvent.

      Visual data (texture, model data, item model) and metadata (autopickup, blacklist, tooltip-style, etc.) are extracted from bag's PDC and applied to the new Bag instance.

      Parameters:
      uuid - UUID string to assign to the new bag
      owner - UUID string of the owning player
      content - initial content list
      creator - the Player creating the bag, or null
      bag - the bag ItemStack to read visual/PDC data from
      Returns:
      the newly created Bag instance
    • createBag

      public static Bag createBag(@NotNull Bag dat)
      Registers an already-constructed Bag into memory and persists it immediately for MYSQLPLUS. Fires a BagCreateEvent.
      Parameters:
      dat - the pre-built Bag to register; must not be null
      Returns:
      the same Bag instance
      Throws:
      IllegalArgumentException - if dat is null
    • loadData

      @DoNotCall("Meant to be called by HavenBags only.") @Internal public static void loadData()
      Loads all bags from the active storage backend into the in-memory map.

      Clears the ready flag for the duration of the load, then sets it back to true on completion. Existing in-memory data is replaced. Called automatically by init() and forceReload().

    • saveData

      public static void saveData(boolean shutdown, boolean... conversion)
      Persists all dirty bags to the active storage backend.

      Bags are collected for saving from two sources:

      • Full dump — all bags in memory are included when shutdown is true or when a non-empty conversion array is supplied (e.g. during a database conversion command).
      • Dirty-only — on a normal auto-save tick (saveData(false)), only bags registered in changedBags are saved. The map is drained as part of this process.

      SQLite and MYSQL saves run asynchronously during normal auto-save to avoid blocking the main thread; synchronous writes are used on shutdown and conversion to guarantee completion before the plugin unloads.

      Parameters:
      shutdown - true to perform a full synchronous save of all bags (used on plugin disable)
      conversion - optional flag — pass true to include all bags in a synchronous save for database conversion purposes
    • removeBag

      public static void removeBag(@NotNull String uuid)
      Removes the bag with the given UUID from the in-memory map and the changedBags dirty set. Does not delete data from the storage backend — use deleteBag(String, Player...) for that.
      Parameters:
      uuid - the bag UUID string to remove
    • deleteBag

      public static Boolean deleteBag(@NotNull String uuid, @Nullable org.bukkit.entity.Player... player)
      Permanently deletes a bag from both the storage backend and the in-memory map. Fires a BagDeleteEvent.
      Parameters:
      uuid - the bag UUID string to delete
      player - optional — the Player responsible for the deletion (attached to the fired event)
      Returns:
      true if deletion succeeded, false if the bag was not found or a storage error occurred
    • getBags

      public static List<String> getBags(@NotNull String playerUUID)
      Returns a list of all bag UUID strings owned by the specified player.
      Parameters:
      playerUUID - the player's UUID string
      Returns:
      unmodifiable list of bag UUID strings; empty if the player owns none
    • getBagsData

      public static List<Bag> getBagsData(@NotNull String playerUUID)
      Returns a list of all Bag instances owned by the specified player.
      Parameters:
      playerUUID - the player's UUID string
      Returns:
      unmodifiable list of Bag objects; empty if the player owns none
    • getBagOwners

      public static List<String> getBagOwners()
      Returns a list of all player UUID strings that own at least one bag.

      For the FILES backend this is derived from the bag directory structure; for SQL backends it is queried directly from the database.

      Returns:
      list of owner UUID strings; empty if none found or on error
    • isBagOpen

      public static boolean isBagOpen(@NotNull String uuid, org.bukkit.inventory.ItemStack bagItem)
      Checks whether the bag with the given UUID is currently open. Removes the bag item from the world (amount = 0) if the bag is not found.
      Parameters:
      uuid - the bag UUID string
      bagItem - the bag ItemStack (cleared if bag not found); may be null
      Returns:
      true if the bag is open
    • isBagOpen

      public static boolean isBagOpen(org.bukkit.inventory.ItemStack bagItem)
      Checks whether the bag represented by the given ItemStack is currently open, using the UUID stored in its PDC.
      Parameters:
      bagItem - the bag ItemStack
      Returns:
      true if the bag is open; false if not found or the item is not a used bag
    • bagOpenBy

      public static org.bukkit.entity.Player bagOpenBy(@NotNull String uuid, org.bukkit.inventory.ItemStack bagItem)
      Returns the Player currently viewing the bag, or null if it is not open.
      Parameters:
      uuid - the bag UUID string
      bagItem - the bag ItemStack (cleared if bag not found); may be null
      Returns:
      the viewing Player, or null
    • markBagOpen

      public static void markBagOpen(@NotNull String uuid, org.bukkit.inventory.ItemStack bagItem, org.bukkit.entity.Player player)
      Marks the bag as open and records the viewing player. For MYSQLPLUS, the updated state is persisted immediately.
      Parameters:
      uuid - the bag UUID string
      bagItem - the bag ItemStack (cleared if bag not found); may be null
      player - the Player opening the bag
    • markBagOpen

      public static void markBagOpen(@NotNull String uuid, org.bukkit.inventory.ItemStack bagItem, org.bukkit.entity.Player player, BagGUI gui)
      Marks the bag as open, records the viewing player, and attaches the associated BagGUI instance. For MYSQLPLUS, the updated state is persisted immediately.
      Parameters:
      uuid - the bag UUID string
      bagItem - the bag ItemStack (cleared if bag not found); may be null
      player - the Player opening the bag
      gui - the BagGUI instance managing the bag's inventory UI
    • markBagClosed

      public static void markBagClosed(@NotNull String uuid)
      Marks the bag as closed and clears the viewer and GUI references. For MYSQLPLUS, the updated state is persisted immediately.
      Parameters:
      uuid - the bag UUID string
    • getOpenBags

      public static List<Bag> getOpenBags()
      Returns all bags that are currently marked as open (i.e. being viewed by a player).
      Returns:
      unmodifiable list of open Bag instances
    • deserializeItemStackList

      public static List<com.google.gson.JsonObject> deserializeItemStackList(String json)
      Deserialises a JSON array string into a list of JsonObject elements. null JSON elements are preserved as null entries in the list to maintain slot alignment.
      Parameters:
      json - JSON array string produced by the bag content serialiser
      Returns:
      list of JsonObject entries, with null for empty slots
    • clearAllBagContents

      @DoNotCall public static Boolean clearAllBagContents()
      Clears the contents of every bag currently loaded in memory.

      Each bag's slots are replaced with null values. This is a destructive operation — use with caution.

      Returns:
      true if all bags were cleared successfully, false if an exception occurred
    • clearBagContentPlayer

      public static Boolean clearBagContentPlayer(@NotNull String playeruuid)
      Clears the contents of all bags owned by the specified player.
      Parameters:
      playeruuid - the owning player's UUID string
      Returns:
      true if all bags were cleared successfully, false if an exception occurred
    • clearBagContent

      public static Boolean clearBagContent(@NotNull String uuid)
      Clears the contents of the bag with the given UUID.

      If the bag has an open BagGUI, it is force-closed first. Slots are replaced with null values to preserve the bag's size.

      Parameters:
      uuid - the bag UUID string
      Returns:
      true if the bag was found and cleared, false otherwise
    • getDatabaseType

      public static DatabaseType getDatabaseType()
      Returns the currently active DatabaseType.
      Returns:
      the active database type
    • setDatabaseType

      @DoNotCall("Meant to be called by HavenBags only.") @Internal protected static void setDatabaseType(DatabaseType databaseType)
      Sets the active DatabaseType. Internal use only — call changeDatabase(DatabaseType) for a safe runtime switch.
      Parameters:
      databaseType - the new database type
    • getMysql

      public static MySQL getMysql()
      Returns the active MySQL connection wrapper, or null if the current backend is not MySQL-based.
      Returns:
      the MySQL instance, or null
    • setMysql

      @DoNotCall("Meant to be called by HavenBags only.") @Internal protected static void setMysql(MySQL mysql)
      Sets the active MySQL connection wrapper. Internal use only.
      Parameters:
      mysql - the MySQL instance to use
    • resetTooltipStyles

      @DoNotCall("This method is used internally to reset the tooltip-styles of all bags to the value in config.yml. It should not be called outside of HavenBags.") public static void resetTooltipStyles()
      Reset the tooltip-styles of ALL bags to the default one specified in the config.
      This is used when the tooltip-style is changed in the config, to update all bags to the new style.

      If the server is a version that does not support TooltipStyle, then all are set null.