{"bloomVersion":"0.1.0","modules":[{"name":"bloom-api","description":"Bloom API: stable, platform-neutral contracts (platform model, capabilities, services, scheduling, text, utilities).","packages":[{"name":"io.github.crownalonedev.bloom.api","types":[{"name":"BloomException","qualifiedName":"io.github.crownalonedev.bloom.api.BloomException","kind":"class","signature":"public class BloomException extends RuntimeException","doc":"Base class for all exceptions thrown by Bloom. Bloom exceptions are unchecked and carry developer-facing messages that explain what went wrong and, where possible, how to fix it.","since":"0.1.0","members":[{"kind":"constructor","name":"BloomException","signature":"public BloomException(String message)","doc":"Creates an exception with a message.","params":[{"name":"message","type":"String","doc":"developer-facing description of the problem"}],"throws":[]},{"kind":"constructor","name":"BloomException","signature":"public BloomException(String message, Throwable cause)","doc":"Creates an exception with a message and cause.","params":[{"name":"message","type":"String","doc":"developer-facing description of the problem"},{"name":"cause","type":"Throwable","doc":"underlying cause"}],"throws":[]}]},{"name":"BloomStartupException","qualifiedName":"io.github.crownalonedev.bloom.api.BloomStartupException","kind":"class","signature":"public class BloomStartupException extends BloomException","doc":"Thrown when Bloom cannot start, for example because the server version is unsupported or the required platform adapter is missing from the plugin jar. The message always names the detected platform and the corrective action.","since":"0.1.0","members":[{"kind":"constructor","name":"BloomStartupException","signature":"public BloomStartupException(String message)","doc":"Creates a startup exception.","params":[{"name":"message","type":"String","doc":"developer-facing description of the problem and fix"}],"throws":[]},{"kind":"constructor","name":"BloomStartupException","signature":"public BloomStartupException(String message, Throwable cause)","doc":"Creates a startup exception with a cause.","params":[{"name":"message","type":"String","doc":"developer-facing description of the problem and fix"},{"name":"cause","type":"Throwable","doc":"underlying cause"}],"throws":[]}]},{"name":"BloomVersion","qualifiedName":"io.github.crownalonedev.bloom.api.BloomVersion","kind":"class","signature":"public final class BloomVersion","doc":"The Bloom release this code was built as. Generated at build time.","since":"0.1.0","members":[{"kind":"field","name":"VERSION","signature":"public static final String VERSION","doc":"The Bloom version string, e.g. \"0.1.0\" or \"0.2.0-SNAPSHOT\".","params":[],"throws":[]}]},{"name":"UnsupportedCapabilityException","qualifiedName":"io.github.crownalonedev.bloom.api.UnsupportedCapabilityException","kind":"class","signature":"public class UnsupportedCapabilityException extends BloomException","doc":"Thrown when code requires a platform capability that the running server does not provide, for example boss bars on a 1.8 server. Avoid this exception by checking capabilities().has(capability) first, or by choosing an API variant with a documented fallback.","since":"0.1.0","members":[{"kind":"constructor","name":"UnsupportedCapabilityException","signature":"public UnsupportedCapabilityException(Capability capability, String platformDescription, String feature)","doc":"Creates the exception.","params":[{"name":"capability","type":"Capability","doc":"the missing capability"},{"name":"platformDescription","type":"String","doc":"human-readable description of the running platform"},{"name":"feature","type":"String","doc":"what the caller attempted, e.g. \"Messenger.actionBar\""}],"throws":[]},{"kind":"method","name":"capability","signature":"public Capability capability()","returns":"Capability","doc":"Returns the capability that was required.","params":[],"returnsDoc":"the missing capability","throws":[]}]}]},{"name":"io.github.crownalonedev.bloom.api.lifecycle","types":[{"name":"LifecycleState","qualifiedName":"io.github.crownalonedev.bloom.api.lifecycle.LifecycleState","kind":"enum","signature":"public final enum LifecycleState","doc":"Lifecycle states of a Bloom instance.","since":"0.1.0","members":[{"kind":"method","name":"values","signature":"public static LifecycleState[] values()","returns":"LifecycleState[]","doc":"","params":[],"throws":[]},{"kind":"method","name":"valueOf","signature":"public static LifecycleState valueOf(String name)","returns":"LifecycleState","doc":"","params":[{"name":"name","type":"String","doc":""}],"throws":[]},{"kind":"enumConstant","name":"NEW","signature":"NEW","doc":"Created but not yet started.","params":[],"throws":[]},{"kind":"enumConstant","name":"STARTING","signature":"STARTING","doc":"Starting: detecting the platform and enabling the adapter.","params":[],"throws":[]},{"kind":"enumConstant","name":"RUNNING","signature":"RUNNING","doc":"Running; all services are usable.","params":[],"throws":[]},{"kind":"enumConstant","name":"STOPPING","signature":"STOPPING","doc":"Shutting down: services are being closed.","params":[],"throws":[]},{"kind":"enumConstant","name":"STOPPED","signature":"STOPPED","doc":"Shut down. Services must not be used.","params":[],"throws":[]},{"kind":"enumConstant","name":"FAILED","signature":"FAILED","doc":"Startup failed; the instance cannot be used.","params":[],"throws":[]}]},{"name":"Terminable","qualifiedName":"io.github.crownalonedev.bloom.api.lifecycle.Terminable","kind":"interface","signature":"public interface Terminable extends AutoCloseable","doc":"A resource that can be closed, such as a scheduled task, an event subscription or a service. Unlike AutoCloseable.close, close does not throw checked exceptions and must be idempotent: closing twice has no further effect.","since":"0.1.0","members":[{"kind":"method","name":"close","signature":"public void close()","returns":"void","doc":"Releases the resource. Calling this more than once has no further effect.","params":[],"throws":[]},{"kind":"method","name":"isClosed","signature":"public boolean isClosed()","returns":"boolean","doc":"Returns whether this resource has been closed.","params":[],"returnsDoc":"true once close has completed","throws":[]}]}]},{"name":"io.github.crownalonedev.bloom.api.platform","types":[{"name":"Capabilities","qualifiedName":"io.github.crownalonedev.bloom.api.platform.Capabilities","kind":"class","signature":"public final class Capabilities","doc":"An immutable set of capabilities available on the running platform.","since":"0.1.0","members":[{"kind":"method","name":"of","signature":"public static Capabilities of(Collection<Capability> present, String platformDescription)","returns":"Capabilities","doc":"Creates a capability set.","params":[{"name":"present","type":"Collection<Capability>","doc":"available capabilities"},{"name":"platformDescription","type":"String","doc":"description of the platform, used in error messages"}],"returnsDoc":"the capability set","throws":[]},{"kind":"method","name":"none","signature":"public static Capabilities none(String platformDescription)","returns":"Capabilities","doc":"Creates an empty capability set.","params":[{"name":"platformDescription","type":"String","doc":"description of the platform"}],"returnsDoc":"a set with no capabilities","throws":[]},{"kind":"method","name":"has","signature":"public boolean has(Capability capability)","returns":"boolean","doc":"Returns whether a capability is available.","params":[{"name":"capability","type":"Capability","doc":"capability to check"}],"returnsDoc":"true if available","throws":[]},{"kind":"method","name":"require","signature":"public void require(Capability capability, String feature)","returns":"void","doc":"Throws if a capability is unavailable.","params":[{"name":"capability","type":"Capability","doc":"required capability"},{"name":"feature","type":"String","doc":"description of the feature being used, included in the error message"}],"throws":[{"type":"UnsupportedCapabilityException","doc":"if the capability is unavailable"}]},{"kind":"method","name":"available","signature":"public Set<Capability> available()","returns":"Set<Capability>","doc":"Returns the available capabilities.","params":[],"returnsDoc":"unmodifiable set","throws":[]},{"kind":"method","name":"unavailable","signature":"public Set<Capability> unavailable()","returns":"Set<Capability>","doc":"Returns the capabilities that are not available.","params":[],"returnsDoc":"unmodifiable set","throws":[]},{"kind":"method","name":"platformDescription","signature":"public String platformDescription()","returns":"String","doc":"Returns the platform description used in error messages.","params":[],"returnsDoc":"platform description","throws":[]},{"kind":"method","name":"toString","signature":"public String toString()","returns":"String","doc":"","params":[],"throws":[]}]},{"name":"Capability","qualifiedName":"io.github.crownalonedev.bloom.api.platform.Capability","kind":"enum","signature":"public final enum Capability","doc":"Optional platform features whose availability depends on the server version or implementation. The active adapter decides which capabilities are present, in some cases by probing the running server (for example, legacy NBT access through reflection). Use Capabilities.has before calling features that are not universally available.","since":"0.1.0","members":[{"kind":"method","name":"values","signature":"public static Capability[] values()","returns":"Capability[]","doc":"","params":[],"throws":[]},{"kind":"method","name":"valueOf","signature":"public static Capability valueOf(String name)","returns":"Capability","doc":"","params":[{"name":"name","type":"String","doc":""}],"throws":[]},{"kind":"enumConstant","name":"ADVENTURE_TEXT","signature":"ADVENTURE_TEXT","doc":"Native Adventure text components (Paper).","params":[],"throws":[]},{"kind":"enumConstant","name":"MINIMESSAGE","signature":"MINIMESSAGE","doc":"Full MiniMessage parsing. Legacy servers get a documented subset converted to legacy colours instead.","params":[],"throws":[]},{"kind":"enumConstant","name":"HEX_COLORS","signature":"HEX_COLORS","doc":"RGB/hex colours in text (1.16+).","params":[],"throws":[]},{"kind":"enumConstant","name":"ACTION_BAR","signature":"ACTION_BAR","doc":"Action bar messages. Legacy 1.8 servers require a successful NMS reflection probe.","params":[],"throws":[]},{"kind":"enumConstant","name":"TITLES","signature":"TITLES","doc":"Title and subtitle messages.","params":[],"throws":[]},{"kind":"enumConstant","name":"TITLE_TIMINGS","signature":"TITLE_TIMINGS","doc":"Custom fade-in/stay/fade-out timings for titles.","params":[],"throws":[]},{"kind":"enumConstant","name":"BOSS_BAR","signature":"BOSS_BAR","doc":"Boss bars.","params":[],"throws":[]},{"kind":"enumConstant","name":"COMPONENT_INVENTORY_TITLES","signature":"COMPONENT_INVENTORY_TITLES","doc":"Component-based inventory titles (allows MiniMessage formatting and titles over 32 characters).","params":[],"throws":[]},{"kind":"enumConstant","name":"PERSISTENT_DATA_CONTAINER","signature":"PERSISTENT_DATA_CONTAINER","doc":"Bukkit PersistentDataContainer on items.","params":[],"throws":[]},{"kind":"enumConstant","name":"ITEM_NBT_TAGS","signature":"ITEM_NBT_TAGS","doc":"Custom NBT tags on items through version-specific reflection (legacy).","params":[],"throws":[]},{"kind":"enumConstant","name":"ITEM_FLAGS","signature":"ITEM_FLAGS","doc":"Item flags such as HIDE_ENCHANTS (1.8+).","params":[],"throws":[]},{"kind":"enumConstant","name":"UNBREAKABLE_ITEMS","signature":"UNBREAKABLE_ITEMS","doc":"Unbreakable items.","params":[],"throws":[]},{"kind":"enumConstant","name":"CUSTOM_MODEL_DATA","signature":"CUSTOM_MODEL_DATA","doc":"Custom model data on items (1.14+).","params":[],"throws":[]},{"kind":"enumConstant","name":"LEGACY_DATA_VALUES","signature":"LEGACY_DATA_VALUES","doc":"Numeric data/durability values selecting item variants, e.g. WOOL:14 (pre-1.13).","params":[],"throws":[]},{"kind":"method","name":"description","signature":"public String description()","returns":"String","doc":"Returns a short human-readable description.","params":[],"returnsDoc":"description used in error messages and diagnostics","throws":[]}]},{"name":"JavaRuntime","qualifiedName":"io.github.crownalonedev.bloom.api.platform.JavaRuntime","kind":"class","signature":"public final class JavaRuntime","doc":"Describes the Java runtime the server is running on.","since":"0.1.0","members":[{"kind":"method","name":"current","signature":"public static JavaRuntime current()","returns":"JavaRuntime","doc":"Describes the current JVM.","params":[],"returnsDoc":"the current runtime","throws":[]},{"kind":"method","name":"of","signature":"public static JavaRuntime of(String specificationVersion, String fullVersion, String vendor)","returns":"JavaRuntime","doc":"Creates a runtime description from system property values.","params":[{"name":"specificationVersion","type":"String","doc":"value of java.specification.version, e.g. \"1.8\" or \"21\""},{"name":"fullVersion","type":"String","doc":"value of java.version"},{"name":"vendor","type":"String","doc":"value of java.vendor"}],"returnsDoc":"the runtime description","throws":[]},{"kind":"method","name":"featureVersion","signature":"public int featureVersion()","returns":"int","doc":"Returns the Java feature version.","params":[],"returnsDoc":"e.g. 8, 21 or 25","throws":[]},{"kind":"method","name":"fullVersion","signature":"public String fullVersion()","returns":"String","doc":"Returns the full version string.","params":[],"returnsDoc":"e.g. \"1.8.0_472\" or \"21.0.11\"","throws":[]},{"kind":"method","name":"vendor","signature":"public String vendor()","returns":"String","doc":"Returns the runtime vendor.","params":[],"returnsDoc":"vendor name","throws":[]},{"kind":"method","name":"isAtLeast","signature":"public boolean isAtLeast(int feature)","returns":"boolean","doc":"Returns whether the runtime is at least the given feature version.","params":[{"name":"feature","type":"int","doc":"feature version to compare with"}],"returnsDoc":"true if featureVersion() >= feature","throws":[]},{"kind":"method","name":"toString","signature":"public String toString()","returns":"String","doc":"","params":[],"throws":[]}]},{"name":"MinecraftVersion","qualifiedName":"io.github.crownalonedev.bloom.api.platform.MinecraftVersion","kind":"class","signature":"public final class MinecraftVersion implements Comparable<MinecraftVersion>","doc":"An immutable Minecraft release version such as 1.8.8, 1.21.11 or 26.3. Both the classic 1.x.y scheme and Mojang's year-based scheme (26.1, 26.3) are represented as major.minor.patch. Because 1 < 26, natural ordering is correct across schemes. Pre-release suffixes (-pre3, -rc1) are ignored when parsing.","since":"0.1.0","members":[{"kind":"field","name":"V1_7_10","signature":"public static final MinecraftVersion V1_7_10","doc":"Minecraft 1.7.10, the only supported 1.7 release.","params":[],"throws":[]},{"kind":"field","name":"V1_8","signature":"public static final MinecraftVersion V1_8","doc":"Minecraft 1.8 (1.8.0).","params":[],"throws":[]},{"kind":"field","name":"V1_8_8","signature":"public static final MinecraftVersion V1_8_8","doc":"Minecraft 1.8.8, the most common legacy PvP version.","params":[],"throws":[]},{"kind":"field","name":"V1_8_9","signature":"public static final MinecraftVersion V1_8_9","doc":"Minecraft 1.8.9, the last 1.8 release.","params":[],"throws":[]},{"kind":"field","name":"V1_21_1","signature":"public static final MinecraftVersion V1_21_1","doc":"Minecraft 1.21.1.","params":[],"throws":[]},{"kind":"field","name":"V1_21_11","signature":"public static final MinecraftVersion V1_21_11","doc":"Minecraft 1.21.11, the last 1.x release.","params":[],"throws":[]},{"kind":"field","name":"V26_1","signature":"public static final MinecraftVersion V26_1","doc":"Minecraft 26.1, the first year-based release.","params":[],"throws":[]},{"kind":"method","name":"of","signature":"public static MinecraftVersion of(int major, int minor, int patch)","returns":"MinecraftVersion","doc":"Creates a version.","params":[{"name":"major","type":"int","doc":"major component, 1 for classic versions or the year for year-based versions"},{"name":"minor","type":"int","doc":"minor component"},{"name":"patch","type":"int","doc":"patch component"}],"returnsDoc":"the version","throws":[]},{"kind":"method","name":"of","signature":"public static MinecraftVersion of(int major, int minor)","returns":"MinecraftVersion","doc":"Creates a version with patch 0.","params":[{"name":"major","type":"int","doc":"major component"},{"name":"minor","type":"int","doc":"minor component"}],"returnsDoc":"the version","throws":[]},{"kind":"method","name":"parse","signature":"public static MinecraftVersion parse(String text)","returns":"MinecraftVersion","doc":"Parses a version from the start of a string such as \"1.8.8\", \"1.21.11-R0.1-SNAPSHOT\" or \"26.3.build.177-stable\".","params":[{"name":"text","type":"String","doc":"text beginning with a version"}],"returnsDoc":"the parsed version","throws":[{"type":"IllegalArgumentException","doc":"if the text does not start with a version"}]},{"kind":"method","name":"tryParse","signature":"public static Optional<MinecraftVersion> tryParse(String text)","returns":"Optional<MinecraftVersion>","doc":"Parses a version from the start of a string, returning empty instead of throwing.","params":[{"name":"text","type":"String","doc":"text beginning with a version, may be null"}],"returnsDoc":"the parsed version, or empty","throws":[]},{"kind":"method","name":"detect","signature":"public static Optional<MinecraftVersion> detect(String bukkitVersion, String serverVersion)","returns":"Optional<MinecraftVersion>","doc":"Determines the Minecraft version from the strings a Bukkit-family server reports. The \"(MC: x.y.z)\" marker in Server#getVersion() is preferred because it always contains the game version; Server#getBukkitVersion() is used as a fallback.","params":[{"name":"bukkitVersion","type":"String","doc":"value of Server#getBukkitVersion(), may be null"},{"name":"serverVersion","type":"String","doc":"value of Server#getVersion(), may be null"}],"returnsDoc":"the detected version, or empty if neither string contains one","throws":[]},{"kind":"method","name":"major","signature":"public int major()","returns":"int","doc":"Returns the major component.","params":[],"returnsDoc":"major component","throws":[]},{"kind":"method","name":"minor","signature":"public int minor()","returns":"int","doc":"Returns the minor component.","params":[],"returnsDoc":"minor component","throws":[]},{"kind":"method","name":"patch","signature":"public int patch()","returns":"int","doc":"Returns the patch component.","params":[],"returnsDoc":"patch component, 0 if the version string had none","throws":[]},{"kind":"method","name":"isAtLeast","signature":"public boolean isAtLeast(MinecraftVersion other)","returns":"boolean","doc":"Returns whether this version is the same as or newer than another.","params":[{"name":"other","type":"MinecraftVersion","doc":"version to compare with"}],"returnsDoc":"true if this >= other","throws":[]},{"kind":"method","name":"isBefore","signature":"public boolean isBefore(MinecraftVersion other)","returns":"boolean","doc":"Returns whether this version is older than another.","params":[{"name":"other","type":"MinecraftVersion","doc":"version to compare with"}],"returnsDoc":"true if this < other","throws":[]},{"kind":"method","name":"isBetween","signature":"public boolean isBetween(MinecraftVersion min, MinecraftVersion max)","returns":"boolean","doc":"Returns whether this version lies within an inclusive range.","params":[{"name":"min","type":"MinecraftVersion","doc":"inclusive lower bound"},{"name":"max","type":"MinecraftVersion","doc":"inclusive upper bound"}],"returnsDoc":"true if min <= this <= max","throws":[]},{"kind":"method","name":"isSameLine","signature":"public boolean isSameLine(MinecraftVersion other)","returns":"boolean","doc":"Returns whether this version shares major and minor components with another, e.g. 1.8.3 and 1.8.8.","params":[{"name":"other","type":"MinecraftVersion","doc":"version to compare with"}],"returnsDoc":"true if both belong to the same release line","throws":[]},{"kind":"method","name":"compareTo","signature":"public int compareTo(MinecraftVersion o)","returns":"int","doc":"","params":[{"name":"o","type":"MinecraftVersion","doc":""}],"throws":[]},{"kind":"method","name":"equals","signature":"public boolean equals(Object o)","returns":"boolean","doc":"","params":[{"name":"o","type":"Object","doc":""}],"throws":[]},{"kind":"method","name":"hashCode","signature":"public int hashCode()","returns":"int","doc":"","params":[],"throws":[]},{"kind":"method","name":"toString","signature":"public String toString()","returns":"String","doc":"Formats the version the way Mojang does: 1.8 rather than 1.8.0.","params":[],"returnsDoc":"the version string","throws":[]}]},{"name":"PlatformFamily","qualifiedName":"io.github.crownalonedev.bloom.api.platform.PlatformFamily","kind":"enum","signature":"public final enum PlatformFamily","doc":"The platform families Bloom ships adapters for. Each family is served by one adapter module.","since":"0.1.0","members":[{"kind":"method","name":"values","signature":"public static PlatformFamily[] values()","returns":"PlatformFamily[]","doc":"","params":[],"throws":[]},{"kind":"method","name":"valueOf","signature":"public static PlatformFamily valueOf(String name)","returns":"PlatformFamily","doc":"","params":[{"name":"name","type":"String","doc":""}],"throws":[]},{"kind":"enumConstant","name":"LEGACY","signature":"LEGACY","doc":"Bukkit/Spigot 1.7.10 to 1.8.9 on Java 8. Served by bloom-legacy.","params":[],"throws":[]},{"kind":"enumConstant","name":"MODERN","signature":"MODERN","doc":"Paper 1.21.x and 26.x on Java 21+. Served by bloom-modern.","params":[],"throws":[]},{"kind":"method","name":"id","signature":"public String id()","returns":"String","doc":"Returns the short identifier used in logs and configuration.","params":[],"returnsDoc":"\"legacy\" or \"modern\"","throws":[]},{"kind":"method","name":"adapterModule","signature":"public String adapterModule()","returns":"String","doc":"Returns the Bloom artifact that contains this family's adapter.","params":[],"returnsDoc":"artifact id, e.g. \"bloom-legacy\"","throws":[]}]},{"name":"PlatformInfo","qualifiedName":"io.github.crownalonedev.bloom.api.platform.PlatformInfo","kind":"class","signature":"public final class PlatformInfo","doc":"Immutable description of the server Bloom is running on.","since":"0.1.0","members":[{"kind":"constructor","name":"PlatformInfo","signature":"public PlatformInfo(ServerSoftware software, String serverName, String serverVersion, String bukkitVersion, MinecraftVersion minecraftVersion, JavaRuntime javaRuntime)","doc":"Creates a platform description.","params":[{"name":"software","type":"ServerSoftware","doc":"detected implementation"},{"name":"serverName","type":"String","doc":"value of Server#getName()"},{"name":"serverVersion","type":"String","doc":"value of Server#getVersion()"},{"name":"bukkitVersion","type":"String","doc":"value of Server#getBukkitVersion()"},{"name":"minecraftVersion","type":"MinecraftVersion","doc":"detected game version"},{"name":"javaRuntime","type":"JavaRuntime","doc":"running Java runtime"}],"throws":[]},{"kind":"method","name":"software","signature":"public ServerSoftware software()","returns":"ServerSoftware","doc":"Returns the detected server implementation.","params":[],"returnsDoc":"server software","throws":[]},{"kind":"method","name":"serverName","signature":"public String serverName()","returns":"String","doc":"Returns the name the server reports, e.g. \"Paper\" or \"CraftBukkit\".","params":[],"returnsDoc":"server name","throws":[]},{"kind":"method","name":"serverVersion","signature":"public String serverVersion()","returns":"String","doc":"Returns the raw server version string.","params":[],"returnsDoc":"e.g. \"git-Spigot-db6de12-18fbb24 (MC: 1.8.8)\"","throws":[]},{"kind":"method","name":"bukkitVersion","signature":"public String bukkitVersion()","returns":"String","doc":"Returns the raw Bukkit API version string.","params":[],"returnsDoc":"e.g. \"1.8.8-R0.1-SNAPSHOT\"","throws":[]},{"kind":"method","name":"minecraftVersion","signature":"public MinecraftVersion minecraftVersion()","returns":"MinecraftVersion","doc":"Returns the Minecraft version.","params":[],"returnsDoc":"game version","throws":[]},{"kind":"method","name":"javaRuntime","signature":"public JavaRuntime javaRuntime()","returns":"JavaRuntime","doc":"Returns the Java runtime.","params":[],"returnsDoc":"Java runtime","throws":[]},{"kind":"method","name":"describe","signature":"public String describe()","returns":"String","doc":"Returns a short description such as \"Paper 1.21.11 (Java 21.0.11)\".","params":[],"returnsDoc":"description for logs and error messages","throws":[]},{"kind":"method","name":"toString","signature":"public String toString()","returns":"String","doc":"","params":[],"throws":[]}]},{"name":"ServerSoftware","qualifiedName":"io.github.crownalonedev.bloom.api.platform.ServerSoftware","kind":"enum","signature":"public final enum ServerSoftware","doc":"Server implementations Bloom can recognise. Recognition does not imply support; see PlatformFamily and the compatibility matrix.","since":"0.1.0","members":[{"kind":"method","name":"values","signature":"public static ServerSoftware[] values()","returns":"ServerSoftware[]","doc":"","params":[],"throws":[]},{"kind":"method","name":"valueOf","signature":"public static ServerSoftware valueOf(String name)","returns":"ServerSoftware","doc":"","params":[{"name":"name","type":"String","doc":""}],"throws":[]},{"kind":"enumConstant","name":"CRAFTBUKKIT","signature":"CRAFTBUKKIT","doc":"CraftBukkit without Spigot patches.","params":[],"throws":[]},{"kind":"enumConstant","name":"SPIGOT","signature":"SPIGOT","doc":"Spigot.","params":[],"throws":[]},{"kind":"enumConstant","name":"PAPER_SPIGOT","signature":"PAPER_SPIGOT","doc":"PaperSpigot, the legacy (1.7/1.8) Paper fork of Spigot.","params":[],"throws":[]},{"kind":"enumConstant","name":"PAPER","signature":"PAPER","doc":"Paper (1.9 and newer).","params":[],"throws":[]},{"kind":"enumConstant","name":"PAPER_FORK","signature":"PAPER_FORK","doc":"A Paper fork such as Purpur or Pufferfish; Paper API is available.","params":[],"throws":[]},{"kind":"enumConstant","name":"FOLIA","signature":"FOLIA","doc":"Folia, Paper's regionised fork. Not supported by Bloom yet: its scheduler model differs.","params":[],"throws":[]},{"kind":"enumConstant","name":"MOCKBUKKIT","signature":"MOCKBUKKIT","doc":"MockBukkit test server (implements Paper API).","params":[],"throws":[]},{"kind":"enumConstant","name":"UNKNOWN","signature":"UNKNOWN","doc":"Unrecognised implementation.","params":[],"throws":[]},{"kind":"method","name":"displayName","signature":"public String displayName()","returns":"String","doc":"Returns a human-readable name.","params":[],"returnsDoc":"display name","throws":[]},{"kind":"method","name":"hasPaperApi","signature":"public boolean hasPaperApi()","returns":"boolean","doc":"Returns whether this implementation exposes the modern Paper API (Adventure, PDC, etc.).","params":[],"returnsDoc":"true for Paper, Paper forks, Folia and MockBukkit","throws":[]}]},{"name":"SupportDecision","qualifiedName":"io.github.crownalonedev.bloom.api.platform.SupportDecision","kind":"class","signature":"public final class SupportDecision","doc":"Result of SupportPolicy.evaluate.","since":"0.1.0","members":[{"kind":"method","name":"isSupported","signature":"public boolean isSupported()","returns":"boolean","doc":"Returns whether Bloom may start.","params":[],"returnsDoc":"true if an adapter family was selected","throws":[]},{"kind":"method","name":"family","signature":"public PlatformFamily family()","returns":"PlatformFamily","doc":"Returns the selected adapter family.","params":[],"returnsDoc":"family","throws":[{"type":"IllegalStateException","doc":"if unsupported"}]},{"kind":"method","name":"isUntested","signature":"public boolean isUntested()","returns":"boolean","doc":"Returns whether the platform is newer than the supported range and was only accepted because the developer opted in.","params":[],"returnsDoc":"true for an explicitly allowed untested version","throws":[]},{"kind":"method","name":"reason","signature":"public String reason()","returns":"String","doc":"Returns why the platform is unsupported.","params":[],"returnsDoc":"explanation, or null if supported","throws":[]}]},{"name":"SupportPolicy","qualifiedName":"io.github.crownalonedev.bloom.api.platform.SupportPolicy","kind":"class","signature":"public final class SupportPolicy","doc":"The single source of truth for which platforms Bloom will start on. Runtime support ranges are deliberately conservative. Being inside a range means an adapter exists and its code was compile-verified against that API; the test status of each version is tracked separately in docs/architecture/COMPATIBILITY.md.","since":"0.1.0","members":[{"kind":"field","name":"LEGACY","signature":"public static final VersionRange LEGACY","doc":"Legacy family: 1.7.10 through 1.8.9.","params":[],"throws":[]},{"kind":"field","name":"MODERN","signature":"public static final List<VersionRange> MODERN","doc":"Modern family ranges: Paper 1.21.1 - 1.21.11, then 26.1.x - 26.3.x.","params":[],"throws":[]},{"kind":"field","name":"LEGACY_MIN_JAVA","signature":"public static final int LEGACY_MIN_JAVA","doc":"Minimum Java feature version for the legacy family.","params":[],"throws":[]},{"kind":"field","name":"MODERN_MIN_JAVA","signature":"public static final int MODERN_MIN_JAVA","doc":"Minimum Java feature version for the modern family (the modern adapter is Java 21 bytecode).","params":[],"throws":[]},{"kind":"method","name":"newestModern","signature":"public static MinecraftVersion newestModern()","returns":"MinecraftVersion","doc":"Returns the newest Minecraft version covered by a modern range.","params":[],"returnsDoc":"newest supported modern version (patch may be Integer.MAX_VALUE)","throws":[]},{"kind":"method","name":"evaluate","signature":"public static SupportDecision evaluate(ServerSoftware software, MinecraftVersion version, JavaRuntime java, boolean allowNewerUntested)","returns":"SupportDecision","doc":"Decides whether and how Bloom can run on a platform.","params":[{"name":"software","type":"ServerSoftware","doc":"detected server implementation"},{"name":"version","type":"MinecraftVersion","doc":"detected Minecraft version"},{"name":"java","type":"JavaRuntime","doc":"running Java runtime"},{"name":"allowNewerUntested","type":"boolean","doc":"whether the developer opted into running the modern adapter on Paper versions newer than the newest supported one"}],"returnsDoc":"the decision, never null","throws":[]}]},{"name":"VersionRange","qualifiedName":"io.github.crownalonedev.bloom.api.platform.VersionRange","kind":"class","signature":"public final class VersionRange","doc":"An inclusive range of Minecraft versions.","since":"0.1.0","members":[{"kind":"method","name":"between","signature":"public static VersionRange between(MinecraftVersion min, MinecraftVersion max)","returns":"VersionRange","doc":"Creates an inclusive range.","params":[{"name":"min","type":"MinecraftVersion","doc":"inclusive lower bound"},{"name":"max","type":"MinecraftVersion","doc":"inclusive upper bound"}],"returnsDoc":"the range","throws":[]},{"kind":"method","name":"lineFrom","signature":"public static VersionRange lineFrom(int major, int minor, int minPatch)","returns":"VersionRange","doc":"Creates a range covering every patch release of one release line from a minimum patch, e.g. lineFrom(26, 3, 0) covers 26.3, 26.3.1, 26.3.2 and so on.","params":[{"name":"major","type":"int","doc":"major component"},{"name":"minor","type":"int","doc":"minor component"},{"name":"minPatch","type":"int","doc":"first included patch"}],"returnsDoc":"the range","throws":[]},{"kind":"method","name":"min","signature":"public MinecraftVersion min()","returns":"MinecraftVersion","doc":"Returns the inclusive lower bound.","params":[],"returnsDoc":"lower bound","throws":[]},{"kind":"method","name":"max","signature":"public MinecraftVersion max()","returns":"MinecraftVersion","doc":"Returns the inclusive upper bound.","params":[],"returnsDoc":"upper bound; its patch is Integer.MAX_VALUE for open release lines","throws":[]},{"kind":"method","name":"contains","signature":"public boolean contains(MinecraftVersion version)","returns":"boolean","doc":"Tests whether a version lies in this range.","params":[{"name":"version","type":"MinecraftVersion","doc":"version to test"}],"returnsDoc":"true if included","throws":[]},{"kind":"method","name":"toString","signature":"public String toString()","returns":"String","doc":"","params":[],"throws":[]}]}]},{"name":"io.github.crownalonedev.bloom.api.scheduler","types":[{"name":"BloomScheduler","qualifiedName":"io.github.crownalonedev.bloom.api.scheduler.BloomScheduler","kind":"interface","signature":"public interface BloomScheduler","doc":"Plugin-owned task scheduling with automatic cleanup. Every task is owned by the Bloom instance that created it and is cancelled when Bloom shuts down. Exceptions thrown by tasks are caught and logged with the task's origin; they never kill the scheduler. Threading rules • sync* tasks run on the main server thread. Use them for anything that touches worlds, entities, players, inventories or most other Bukkit API. • async* tasks run on a background thread. Use them for I/O, computation and network calls. Never modify worlds, entities or inventories from an async task: hop back with sync or mainThreadExecutor. Delays and periods are in server ticks (20 per second at full speed) unless a Duration overload is used. A delay of 0 means \"the next tick\".","since":"0.1.0","members":[{"kind":"method","name":"isPrimaryThread","signature":"public boolean isPrimaryThread()","returns":"boolean","doc":"Returns whether the calling thread is the main server thread.","params":[],"returnsDoc":"true on the main thread","throws":[]},{"kind":"method","name":"ensurePrimaryThread","signature":"public void ensurePrimaryThread(String operation)","returns":"void","doc":"Throws if not called from the main server thread. Use at the start of methods that touch game state.","params":[{"name":"operation","type":"String","doc":"what the caller is doing, used in the error message"}],"throws":[{"type":"IllegalStateException","doc":"if called from another thread"}]},{"kind":"method","name":"sync","signature":"public ScheduledTask sync(Runnable task)","returns":"ScheduledTask","doc":"Runs a task on the main thread on the next tick.","params":[{"name":"task","type":"Runnable","doc":"task to run"}],"returnsDoc":"task handle","throws":[]},{"kind":"method","name":"syncLater","signature":"public ScheduledTask syncLater(Runnable task, long delayTicks)","returns":"ScheduledTask","doc":"Runs a task on the main thread after a delay.","params":[{"name":"task","type":"Runnable","doc":"task to run"},{"name":"delayTicks","type":"long","doc":"delay in ticks, >= 0"}],"returnsDoc":"task handle","throws":[]},{"kind":"method","name":"syncLater","signature":"public ScheduledTask syncLater(Runnable task, Duration delay)","returns":"ScheduledTask","doc":"Runs a task on the main thread after a delay.","params":[{"name":"task","type":"Runnable","doc":"task to run"},{"name":"delay","type":"Duration","doc":"delay, rounded up to whole ticks"}],"returnsDoc":"task handle","throws":[]},{"kind":"method","name":"syncRepeating","signature":"public ScheduledTask syncRepeating(Consumer<ScheduledTask> task, long delayTicks, long periodTicks)","returns":"ScheduledTask","doc":"Runs a task repeatedly on the main thread. The task receives its own handle so it can cancel itself.","params":[{"name":"task","type":"Consumer<ScheduledTask>","doc":"task to run; receives its handle"},{"name":"delayTicks","type":"long","doc":"initial delay in ticks, >= 0"},{"name":"periodTicks","type":"long","doc":"period in ticks, >= 1"}],"returnsDoc":"task handle","throws":[]},{"kind":"method","name":"syncRepeating","signature":"public ScheduledTask syncRepeating(Consumer<ScheduledTask> task, Duration delay, Duration period)","returns":"ScheduledTask","doc":"Runs a task repeatedly on the main thread.","params":[{"name":"task","type":"Consumer<ScheduledTask>","doc":"task to run; receives its handle"},{"name":"delay","type":"Duration","doc":"initial delay"},{"name":"period","type":"Duration","doc":"period, at least one tick"}],"returnsDoc":"task handle","throws":[]},{"kind":"method","name":"async","signature":"public ScheduledTask async(Runnable task)","returns":"ScheduledTask","doc":"Runs a task on a background thread as soon as possible.","params":[{"name":"task","type":"Runnable","doc":"task to run; must not touch game state"}],"returnsDoc":"task handle","throws":[]},{"kind":"method","name":"asyncLater","signature":"public ScheduledTask asyncLater(Runnable task, long delayTicks)","returns":"ScheduledTask","doc":"Runs a task on a background thread after a delay.","params":[{"name":"task","type":"Runnable","doc":"task to run; must not touch game state"},{"name":"delayTicks","type":"long","doc":"delay in ticks"}],"returnsDoc":"task handle","throws":[]},{"kind":"method","name":"asyncRepeating","signature":"public ScheduledTask asyncRepeating(Consumer<ScheduledTask> task, long delayTicks, long periodTicks)","returns":"ScheduledTask","doc":"Runs a task repeatedly on a background thread.","params":[{"name":"task","type":"Consumer<ScheduledTask>","doc":"task to run; receives its handle; must not touch game state"},{"name":"delayTicks","type":"long","doc":"initial delay in ticks"},{"name":"periodTicks","type":"long","doc":"period in ticks, >= 1"}],"returnsDoc":"task handle","throws":[]},{"kind":"method","name":"supplySync","signature":"public <T> CompletableFuture<T> supplySync(Supplier<T> supplier)","returns":"CompletableFuture<T>","doc":"Computes a value on the main thread. If already on the main thread the supplier still runs on the next tick, so the behaviour is the same from every caller.","params":[{"name":"supplier","type":"Supplier<T>","doc":"computation; may touch game state"}],"returnsDoc":"future completed on the main thread with the result or the thrown exception","throws":[]},{"kind":"method","name":"supplyAsync","signature":"public <T> CompletableFuture<T> supplyAsync(Supplier<T> supplier)","returns":"CompletableFuture<T>","doc":"Computes a value on a background thread.","params":[{"name":"supplier","type":"Supplier<T>","doc":"computation; must not touch game state"}],"returnsDoc":"future completed with the result or the thrown exception","throws":[]},{"kind":"method","name":"mainThreadExecutor","signature":"public Executor mainThreadExecutor()","returns":"Executor","doc":"Returns an executor that runs tasks on the main thread. Useful for future.thenAcceptAsync(result -> ..., scheduler.mainThreadExecutor()).","params":[],"returnsDoc":"main-thread executor owned by this scheduler","throws":[]},{"kind":"method","name":"asyncExecutor","signature":"public Executor asyncExecutor()","returns":"Executor","doc":"Returns an executor that runs tasks on background threads owned by this scheduler.","params":[],"returnsDoc":"async executor","throws":[]},{"kind":"method","name":"activeTaskCount","signature":"public int activeTaskCount()","returns":"int","doc":"Returns the number of tasks that are scheduled and not yet finished or cancelled.","params":[],"returnsDoc":"active task count","throws":[]}]},{"name":"ScheduledTask","qualifiedName":"io.github.crownalonedev.bloom.api.scheduler.ScheduledTask","kind":"interface","signature":"public interface ScheduledTask extends Terminable","doc":"Handle to a task submitted to a BloomScheduler. close is equivalent to cancel.","since":"0.1.0","members":[{"kind":"method","name":"cancel","signature":"public void cancel()","returns":"void","doc":"Cancels the task. A one-shot task that already ran is unaffected; a repeating task will not run again. Idempotent.","params":[],"throws":[]},{"kind":"method","name":"isCancelled","signature":"public boolean isCancelled()","returns":"boolean","doc":"Returns whether the task was cancelled (explicitly or by scheduler shutdown).","params":[],"returnsDoc":"true if cancelled","throws":[]},{"kind":"method","name":"isDone","signature":"public boolean isDone()","returns":"boolean","doc":"Returns whether a one-shot task has finished running. Always false for repeating tasks that were not cancelled.","params":[],"returnsDoc":"true if finished","throws":[]},{"kind":"method","name":"isAsync","signature":"public boolean isAsync()","returns":"boolean","doc":"Returns whether the task runs off the main server thread.","params":[],"returnsDoc":"true for async tasks","throws":[]},{"kind":"method","name":"isRepeating","signature":"public boolean isRepeating()","returns":"boolean","doc":"Returns whether the task repeats.","params":[],"returnsDoc":"true for repeating tasks","throws":[]},{"kind":"method","name":"close","signature":"public default void close()","returns":"void","doc":"","params":[],"throws":[]},{"kind":"method","name":"isClosed","signature":"public default boolean isClosed()","returns":"boolean","doc":"","params":[],"throws":[]}]},{"name":"Ticks","qualifiedName":"io.github.crownalonedev.bloom.api.scheduler.Ticks","kind":"class","signature":"public final class Ticks","doc":"Conversions between server ticks and Durations. One tick is 50 ms at the nominal 20 TPS.","since":"0.1.0","members":[{"kind":"field","name":"PER_SECOND","signature":"public static final int PER_SECOND","doc":"Nominal ticks per second.","params":[],"throws":[]},{"kind":"field","name":"MILLIS_PER_TICK","signature":"public static final long MILLIS_PER_TICK","doc":"Nominal milliseconds per tick.","params":[],"throws":[]},{"kind":"method","name":"from","signature":"public static long from(Duration duration)","returns":"long","doc":"Converts a duration to ticks, rounding up so that a positive duration never becomes zero ticks.","params":[{"name":"duration","type":"Duration","doc":"non-negative duration"}],"returnsDoc":"number of ticks","throws":[{"type":"IllegalArgumentException","doc":"if the duration is negative"}]},{"kind":"method","name":"toDuration","signature":"public static Duration toDuration(long ticks)","returns":"Duration","doc":"Converts ticks to a duration.","params":[{"name":"ticks","type":"long","doc":"number of ticks"}],"returnsDoc":"the nominal duration","throws":[]},{"kind":"method","name":"seconds","signature":"public static long seconds(long seconds)","returns":"long","doc":"Converts seconds to ticks.","params":[{"name":"seconds","type":"long","doc":"seconds"}],"returnsDoc":"ticks","throws":[]}]}]},{"name":"io.github.crownalonedev.bloom.api.service","types":[{"name":"ServiceRegistry","qualifiedName":"io.github.crownalonedev.bloom.api.service.ServiceRegistry","kind":"interface","signature":"public interface ServiceRegistry","doc":"A simple, type-keyed registry of services owned by one Bloom instance. Services that implement Terminable or AutoCloseable are closed automatically, in reverse registration order, when Bloom shuts down. All methods are thread-safe.","since":"0.1.0","members":[{"kind":"method","name":"register","signature":"public <T> void register(Class<T> type, T service)","returns":"void","doc":"Registers a service.","params":[{"name":"type","type":"Class<T>","doc":"the key type, usually an interface the service implements"},{"name":"service","type":"T","doc":"the service instance"}],"throws":[{"type":"IllegalStateException","doc":"if a service is already registered for type or the registry is closed"}]},{"kind":"method","name":"find","signature":"public <T> Optional<T> find(Class<T> type)","returns":"Optional<T>","doc":"Looks up a service.","params":[{"name":"type","type":"Class<T>","doc":"key type"}],"returnsDoc":"the service, or empty if none is registered","throws":[]},{"kind":"method","name":"require","signature":"public <T> T require(Class<T> type)","returns":"T","doc":"Looks up a service that must exist.","params":[{"name":"type","type":"Class<T>","doc":"key type"}],"returnsDoc":"the service","throws":[{"type":"BloomException","doc":"if no service is registered, with a message listing the registered services"}]},{"kind":"method","name":"getOrRegister","signature":"public <T> T getOrRegister(Class<T> type, Supplier<? extends T> factory)","returns":"T","doc":"Returns the registered service, atomically creating and registering it first if absent.","params":[{"name":"type","type":"Class<T>","doc":"key type"},{"name":"factory","type":"Supplier<? extends T>","doc":"creates the service; called at most once per type"}],"returnsDoc":"the existing or newly created service","throws":[]},{"kind":"method","name":"isRegistered","signature":"public boolean isRegistered(Class<?> type)","returns":"boolean","doc":"Returns whether a service is registered.","params":[{"name":"type","type":"Class<?>","doc":"key type"}],"returnsDoc":"true if registered","throws":[]},{"kind":"method","name":"types","signature":"public Set<Class<?>> types()","returns":"Set<Class<?>>","doc":"Returns the registered key types in registration order.","params":[],"returnsDoc":"snapshot of registered types","throws":[]}]}]},{"name":"io.github.crownalonedev.bloom.api.text","types":[{"name":"Placeholder","qualifiedName":"io.github.crownalonedev.bloom.api.text.Placeholder","kind":"class","signature":"public final class Placeholder","doc":"A {key} placeholder and the value that replaces it. Values created with of are inserted as plain text : formatting codes or tags inside the value are neutralised, so untrusted input such as player chat cannot inject colours or MiniMessage tags. Use formatted only for trusted values that intentionally contain formatting. messenger.send(player, \"&dWelcome, {player}!\", Placeholder.of(\"player\", player.getName()));","since":"0.1.0","members":[{"kind":"method","name":"of","signature":"public static Placeholder of(String key, Object value)","returns":"Placeholder","doc":"Creates a plain-text placeholder. The value's toString() is used; null becomes \"null\".","params":[{"name":"key","type":"String","doc":"key without braces, e.g. \"player\""},{"name":"value","type":"Object","doc":"replacement value"}],"returnsDoc":"the placeholder","throws":[]},{"kind":"method","name":"formatted","signature":"public static Placeholder formatted(String key, String value)","returns":"Placeholder","doc":"Creates a placeholder whose value is parsed with the message's format. Only use with trusted values.","params":[{"name":"key","type":"String","doc":"key without braces"},{"name":"value","type":"String","doc":"replacement containing formatting in the same TextFormat as the message"}],"returnsDoc":"the placeholder","throws":[]},{"kind":"method","name":"key","signature":"public String key()","returns":"String","doc":"Returns the key without braces.","params":[],"returnsDoc":"key","throws":[]},{"kind":"method","name":"value","signature":"public String value()","returns":"String","doc":"Returns the replacement value.","params":[],"returnsDoc":"value","throws":[]},{"kind":"method","name":"isFormatted","signature":"public boolean isFormatted()","returns":"boolean","doc":"Returns whether the value is trusted formatted text rather than plain text.","params":[],"returnsDoc":"true if created with formatted","throws":[]},{"kind":"method","name":"toString","signature":"public String toString()","returns":"String","doc":"","params":[],"throws":[]}]},{"name":"TextFormat","qualifiedName":"io.github.crownalonedev.bloom.api.text.TextFormat","kind":"enum","signature":"public final enum TextFormat","doc":"The markup language used to write a message. Both formats work on every supported platform: • On modern Paper, MINIMESSAGE is parsed by the real MiniMessage library and LEGACY by Adventure's legacy serializer. • On legacy servers, LEGACY is native, and MINIMESSAGE is converted by Bloom's MiniMessage subset converter: named colours, hex colours (downsampled to the nearest legacy colour), decorations, <reset> and <newline>. Interactive tags (click, hover, insertion) and gradients are rendered as plain text content without their effect.","since":"0.1.0","members":[{"kind":"method","name":"values","signature":"public static TextFormat[] values()","returns":"TextFormat[]","doc":"","params":[],"throws":[]},{"kind":"method","name":"valueOf","signature":"public static TextFormat valueOf(String name)","returns":"TextFormat","doc":"","params":[{"name":"name","type":"String","doc":""}],"throws":[]},{"kind":"enumConstant","name":"LEGACY","signature":"LEGACY","doc":"Legacy colour codes with & (or §), e.g. \"&dHello &lworld\".","params":[],"throws":[]},{"kind":"enumConstant","name":"MINIMESSAGE","signature":"MINIMESSAGE","doc":"MiniMessage tags, e.g. \"<light_purple>Hello <bold>world\".","params":[],"throws":[]}]}]},{"name":"io.github.crownalonedev.bloom.api.util","types":[{"name":"Cooldowns","qualifiedName":"io.github.crownalonedev.bloom.api.util.Cooldowns","kind":"class","signature":"public final class Cooldowns<K>","doc":"Thread-safe cooldown tracking keyed by any value, typically a player UUID. private final Cooldowns<UUID> pearl = Cooldowns.of(Duration.ofSeconds(15)); if (!pearl.tryUse(player.getUniqueId())) { player.sendMessage(\"Wait \" + Durations.format(pearl.remaining(player.getUniqueId()))); return; } Expired entries are removed lazily when accessed and by prune. Cooldowns are kept in memory only.","since":"0.1.0","members":[{"kind":"method","name":"of","signature":"public static <K> Cooldowns<K> of(Duration defaultDuration)","returns":"Cooldowns<K>","doc":"Creates cooldowns with a default duration using the system clock.","params":[{"name":"defaultDuration","type":"Duration","doc":"positive duration applied by tryUse and start"}],"returnsDoc":"a new cooldown tracker","throws":[]},{"kind":"method","name":"of","signature":"public static <K> Cooldowns<K> of(Duration defaultDuration, Clock clock)","returns":"Cooldowns<K>","doc":"Creates cooldowns with a custom clock, mainly for tests.","params":[{"name":"defaultDuration","type":"Duration","doc":"positive default duration"},{"name":"clock","type":"Clock","doc":"time source"}],"returnsDoc":"a new cooldown tracker","throws":[]},{"kind":"method","name":"tryUse","signature":"public boolean tryUse(K key)","returns":"boolean","doc":"Starts the default cooldown if the key is not on cooldown.","params":[{"name":"key","type":"K","doc":"key to use"}],"returnsDoc":"true if the key was free and the cooldown was started; false if still cooling down","throws":[]},{"kind":"method","name":"tryUse","signature":"public boolean tryUse(K key, Duration duration)","returns":"boolean","doc":"Starts a cooldown of a specific duration if the key is not on cooldown. Atomic per key.","params":[{"name":"key","type":"K","doc":"key to use"},{"name":"duration","type":"Duration","doc":"positive cooldown duration"}],"returnsDoc":"true if the cooldown was started","throws":[]},{"kind":"method","name":"start","signature":"public void start(K key)","returns":"void","doc":"Starts (or restarts) the default cooldown unconditionally.","params":[{"name":"key","type":"K","doc":"key"}],"throws":[]},{"kind":"method","name":"start","signature":"public void start(K key, Duration duration)","returns":"void","doc":"Starts (or restarts) a cooldown unconditionally.","params":[{"name":"key","type":"K","doc":"key"},{"name":"duration","type":"Duration","doc":"positive duration"}],"throws":[]},{"kind":"method","name":"isActive","signature":"public boolean isActive(K key)","returns":"boolean","doc":"Returns whether a key is cooling down.","params":[{"name":"key","type":"K","doc":"key"}],"returnsDoc":"true if the cooldown has not expired","throws":[]},{"kind":"method","name":"remaining","signature":"public Duration remaining(K key)","returns":"Duration","doc":"Returns the remaining cooldown time.","params":[{"name":"key","type":"K","doc":"key"}],"returnsDoc":"remaining time, Duration.ZERO if not on cooldown","throws":[]},{"kind":"method","name":"expiry","signature":"public Optional<Instant> expiry(K key)","returns":"Optional<Instant>","doc":"Returns when the key's cooldown expires.","params":[{"name":"key","type":"K","doc":"key"}],"returnsDoc":"expiry instant, or empty if not on cooldown","throws":[]},{"kind":"method","name":"reset","signature":"public void reset(K key)","returns":"void","doc":"Clears a key's cooldown.","params":[{"name":"key","type":"K","doc":"key"}],"throws":[]},{"kind":"method","name":"clear","signature":"public void clear()","returns":"void","doc":"Clears all cooldowns.","params":[],"throws":[]},{"kind":"method","name":"prune","signature":"public int prune()","returns":"int","doc":"Removes expired entries. Call periodically if keys are rarely accessed again.","params":[],"returnsDoc":"number of removed entries","throws":[]},{"kind":"method","name":"size","signature":"public int size()","returns":"int","doc":"Returns the number of tracked entries, including expired entries not yet pruned.","params":[],"returnsDoc":"entry count","throws":[]},{"kind":"method","name":"defaultDuration","signature":"public Duration defaultDuration()","returns":"Duration","doc":"Returns the default duration.","params":[],"returnsDoc":"default duration","throws":[]}]},{"name":"Durations","qualifiedName":"io.github.crownalonedev.bloom.api.util.Durations","kind":"class","signature":"public final class Durations","doc":"Parsing and formatting of human-friendly durations such as \"1h30m\" or \"2d 4h\". Supported units: ms, s, m, h, d, w. Months and years are not supported because their length varies.","since":"0.1.0","members":[{"kind":"method","name":"parse","signature":"public static Duration parse(String text)","returns":"Duration","doc":"Parses a duration such as \"90s\", \"1h30m\", \"2d 4h\" or \"500ms\". A bare number is interpreted as seconds.","params":[{"name":"text","type":"String","doc":"text to parse"}],"returnsDoc":"the duration","throws":[{"type":"IllegalArgumentException","doc":"if the text is not a valid duration, with a message showing valid examples"}]},{"kind":"method","name":"format","signature":"public static String format(Duration duration)","returns":"String","doc":"Formats a duration compactly, e.g. \"1h 5m 3s\". Sub-second durations above zero format as \"<1s\"; zero formats as \"0s\".","params":[{"name":"duration","type":"Duration","doc":"non-negative duration"}],"returnsDoc":"compact representation","throws":[]},{"kind":"method","name":"formatLong","signature":"public static String formatLong(Duration duration)","returns":"String","doc":"Formats a duration in words, e.g. \"1 hour, 5 minutes and 3 seconds\".","params":[{"name":"duration","type":"Duration","doc":"non-negative duration"}],"returnsDoc":"long-form English representation","throws":[]}]},{"name":"ExpiringCache","qualifiedName":"io.github.crownalonedev.bloom.api.util.ExpiringCache","kind":"class","signature":"public final class ExpiringCache<K, V>","doc":"A small thread-safe cache with time-to-live expiry and a maximum size (least-recently-used eviction). Intended for modest caches such as recently looked-up player data. It uses a single lock, so it is not suitable for very hot, highly contended paths; for those use a dedicated caching library.","since":"0.1.0","members":[{"kind":"method","name":"create","signature":"public static <K, V> ExpiringCache<K,V> create(Duration ttl, int maxSize)","returns":"ExpiringCache<K,V>","doc":"Creates a cache.","params":[{"name":"ttl","type":"Duration","doc":"how long entries stay valid after being written"},{"name":"maxSize","type":"int","doc":"maximum number of entries"}],"returnsDoc":"the cache","throws":[]},{"kind":"method","name":"create","signature":"public static <K, V> ExpiringCache<K,V> create(Duration ttl, int maxSize, Clock clock)","returns":"ExpiringCache<K,V>","doc":"Creates a cache with a custom clock, mainly for tests.","params":[{"name":"ttl","type":"Duration","doc":"entry lifetime"},{"name":"maxSize","type":"int","doc":"maximum number of entries"},{"name":"clock","type":"Clock","doc":"time source"}],"returnsDoc":"the cache","throws":[]},{"kind":"method","name":"get","signature":"public Optional<V> get(K key)","returns":"Optional<V>","doc":"Returns a cached value if present and not expired.","params":[{"name":"key","type":"K","doc":"key"}],"returnsDoc":"value, or empty","throws":[]},{"kind":"method","name":"get","signature":"public V get(K key, Function<? super K,? extends V> loader)","returns":"V","doc":"Returns the cached value, computing and caching it when absent or expired. The loader runs while the cache lock is held, so it must be fast and must not access this cache.","params":[{"name":"key","type":"K","doc":"key"},{"name":"loader","type":"Function<? super K,? extends V>","doc":"computes a missing value; must not return null"}],"returnsDoc":"cached or computed value","throws":[]},{"kind":"method","name":"put","signature":"public void put(K key, V value)","returns":"void","doc":"Stores a value.","params":[{"name":"key","type":"K","doc":"key"},{"name":"value","type":"V","doc":"value, not null"}],"throws":[]},{"kind":"method","name":"invalidate","signature":"public void invalidate(K key)","returns":"void","doc":"Removes a value.","params":[{"name":"key","type":"K","doc":"key"}],"throws":[]},{"kind":"method","name":"clear","signature":"public void clear()","returns":"void","doc":"Removes all values.","params":[],"throws":[]},{"kind":"method","name":"prune","signature":"public int prune()","returns":"int","doc":"Removes expired entries.","params":[],"returnsDoc":"number of removed entries","throws":[]},{"kind":"method","name":"size","signature":"public int size()","returns":"int","doc":"Returns the number of entries, including expired entries not yet pruned.","params":[],"returnsDoc":"size","throws":[]}]}]}]},{"name":"bloom-bukkit","description":"Bloom for Bukkit-family servers: Bloom entry point, platform detection, adapter SPI, scheduler, messaging and events.","packages":[{"name":"io.github.crownalonedev.bloom.bukkit","types":[{"name":"Bloom","qualifiedName":"io.github.crownalonedev.bloom.bukkit.Bloom","kind":"class","signature":"public final class Bloom","doc":"The Bloom entry point for Bukkit-family plugins. public final class MyPlugin extends JavaPlugin { private Bloom bloom; @Override public void onEnable() { bloom = Bloom.enable(this); bloom.messages().send(getServer().getConsoleSender(), \"&dBloom is ready on {platform}\", Placeholder.of(\"platform\", bloom.platform().describe())); } @Override public void onDisable() { if (bloom != null) { bloom.shutdown(); } } } enable detects the server, selects the matching version adapter, starts core services and logs a startup banner. If the server is not supported it throws BloomStartupException with an explanation; let it propagate from onEnable (the server then disables the plugin) or catch it to disable gracefully. In embedded mode each plugin has its own relocated copy of Bloom, so get returns the instance of the calling plugin.","since":"0.1.0","members":[{"kind":"method","name":"enable","signature":"public static Bloom enable(Plugin plugin)","returns":"Bloom","doc":"Starts Bloom for a plugin with default options. Call from onEnable().","params":[{"name":"plugin","type":"Plugin","doc":"the plugin that embeds Bloom"}],"returnsDoc":"the running instance","throws":[{"type":"BloomStartupException","doc":"if the platform is unsupported or the adapter is missing"},{"type":"IllegalStateException","doc":"if Bloom is already running for this plugin"}]},{"kind":"method","name":"enable","signature":"public static Bloom enable(Plugin plugin, BloomOptions options)","returns":"Bloom","doc":"Starts Bloom for a plugin. Call from onEnable().","params":[{"name":"plugin","type":"Plugin","doc":"the plugin that embeds Bloom"},{"name":"options","type":"BloomOptions","doc":"startup options"}],"returnsDoc":"the running instance","throws":[{"type":"BloomStartupException","doc":"if the platform is unsupported or the adapter is missing"},{"type":"IllegalStateException","doc":"if Bloom is already running for this plugin"}]},{"kind":"method","name":"get","signature":"public static Bloom get()","returns":"Bloom","doc":"Returns the running instance.","params":[],"returnsDoc":"the instance created by enable","throws":[{"type":"IllegalStateException","doc":"if Bloom is not running"}]},{"kind":"method","name":"find","signature":"public static Optional<Bloom> find()","returns":"Optional<Bloom>","doc":"Returns the running instance, if any.","params":[],"returnsDoc":"the instance, or empty if not running","throws":[]},{"kind":"method","name":"shutdown","signature":"public void shutdown()","returns":"void","doc":"Shuts Bloom down: closes feature services (open menus are closed, databases flushed), unregisters event subscriptions, cancels scheduled tasks and disables the adapter. Call from onDisable(). Idempotent.","params":[],"throws":[]},{"kind":"method","name":"plugin","signature":"public Plugin plugin()","returns":"Plugin","doc":"Returns the owning plugin.","params":[],"returnsDoc":"plugin","throws":[]},{"kind":"method","name":"options","signature":"public BloomOptions options()","returns":"BloomOptions","doc":"Returns the options Bloom was started with.","params":[],"returnsDoc":"options","throws":[]},{"kind":"method","name":"state","signature":"public LifecycleState state()","returns":"LifecycleState","doc":"Returns the lifecycle state.","params":[],"returnsDoc":"state","throws":[]},{"kind":"method","name":"platform","signature":"public PlatformInfo platform()","returns":"PlatformInfo","doc":"Returns the detected platform.","params":[],"returnsDoc":"platform info","throws":[]},{"kind":"method","name":"capabilities","signature":"public Capabilities capabilities()","returns":"Capabilities","doc":"Returns the capabilities of the running platform.","params":[],"returnsDoc":"capabilities","throws":[]},{"kind":"method","name":"has","signature":"public boolean has(Capability capability)","returns":"boolean","doc":"Shortcut for capabilities().has(capability).","params":[{"name":"capability","type":"Capability","doc":"capability to check"}],"returnsDoc":"true if available","throws":[]},{"kind":"method","name":"adapter","signature":"public PlatformAdapter adapter()","returns":"PlatformAdapter","doc":"Returns the active version adapter. Advanced: intended for Bloom modules and diagnostics.","params":[],"returnsDoc":"adapter","throws":[]},{"kind":"method","name":"scheduler","signature":"public BloomScheduler scheduler()","returns":"BloomScheduler","doc":"Returns the task scheduler. Tasks are cancelled on shutdown.","params":[],"returnsDoc":"scheduler","throws":[]},{"kind":"method","name":"messages","signature":"public Messenger messages()","returns":"Messenger","doc":"Returns the default messenger, using the text format from BloomOptions.","params":[],"returnsDoc":"messenger","throws":[]},{"kind":"method","name":"events","signature":"public Events events()","returns":"Events","doc":"Returns the event subscription service. Subscriptions are removed on shutdown.","params":[],"returnsDoc":"events","throws":[]},{"kind":"method","name":"services","signature":"public ServiceRegistry services()","returns":"ServiceRegistry","doc":"Returns the service registry. Feature modules register their services here on first use (for example Menus.of(bloom)); closeable services are closed on shutdown in reverse order.","params":[],"returnsDoc":"services","throws":[]},{"kind":"method","name":"service","signature":"public <T> T service(Class<T> type)","returns":"T","doc":"Shortcut for services().require(type).","params":[{"name":"type","type":"Class<T>","doc":"service key type"}],"returnsDoc":"the service","throws":[]},{"kind":"method","name":"isUntestedPlatform","signature":"public boolean isUntestedPlatform()","returns":"boolean","doc":"Returns whether the server is newer than the supported range and Bloom only started because of BloomOptions.Builder.allowNewerUntestedVersions.","params":[],"returnsDoc":"true on an explicitly allowed untested version","throws":[]}]},{"name":"BloomOptions","qualifiedName":"io.github.crownalonedev.bloom.bukkit.BloomOptions","kind":"class","signature":"public final class BloomOptions","doc":"Startup options for Bloom.enable. Bloom bloom = Bloom.enable(this, BloomOptions.builder() .textFormat(TextFormat.MINIMESSAGE) .unsupportedFeaturePolicy(UnsupportedFeaturePolicy.FALLBACK_TO_CHAT) .build());","since":"0.1.0","members":[{"kind":"method","name":"defaults","signature":"public static BloomOptions defaults()","returns":"BloomOptions","doc":"Returns the default options.","params":[],"returnsDoc":"defaults: TextFormat.LEGACY, UnsupportedFeaturePolicy.SKIP, no untested versions, no debug, full startup banner","throws":[]},{"kind":"method","name":"builder","signature":"public static BloomOptions.Builder builder()","returns":"BloomOptions.Builder","doc":"Creates a builder initialised with the defaults.","params":[],"returnsDoc":"a builder","throws":[]},{"kind":"method","name":"textFormat","signature":"public TextFormat textFormat()","returns":"TextFormat","doc":"Returns the default text format for bloom.messages() and item/menu text.","params":[],"returnsDoc":"text format","throws":[]},{"kind":"method","name":"unsupportedFeaturePolicy","signature":"public UnsupportedFeaturePolicy unsupportedFeaturePolicy()","returns":"UnsupportedFeaturePolicy","doc":"Returns the default policy for unsupported features.","params":[],"returnsDoc":"policy","throws":[]},{"kind":"method","name":"allowNewerUntestedVersions","signature":"public boolean allowNewerUntestedVersions()","returns":"boolean","doc":"Returns whether Bloom may start on Paper versions newer than the newest supported one.","params":[],"returnsDoc":"opt-in flag","throws":[]},{"kind":"method","name":"debug","signature":"public boolean debug()","returns":"boolean","doc":"Returns whether debug diagnostics are logged.","params":[],"returnsDoc":"debug flag","throws":[]},{"kind":"method","name":"quietStartup","signature":"public boolean quietStartup()","returns":"boolean","doc":"Returns whether the startup banner is reduced to one line.","params":[],"returnsDoc":"quiet flag","throws":[]}]},{"name":"BloomOptions.Builder","qualifiedName":"io.github.crownalonedev.bloom.bukkit.BloomOptions.Builder","kind":"class","signature":"public static final class Builder","doc":"Builder for BloomOptions.","members":[{"kind":"method","name":"textFormat","signature":"public BloomOptions.Builder textFormat(TextFormat format)","returns":"BloomOptions.Builder","doc":"Sets the default text format.","params":[{"name":"format","type":"TextFormat","doc":"format used by bloom.messages() and builders that accept text"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"unsupportedFeaturePolicy","signature":"public BloomOptions.Builder unsupportedFeaturePolicy(UnsupportedFeaturePolicy policy)","returns":"BloomOptions.Builder","doc":"Sets the default unsupported-feature policy.","params":[{"name":"policy","type":"UnsupportedFeaturePolicy","doc":"policy"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"allowNewerUntestedVersions","signature":"public BloomOptions.Builder allowNewerUntestedVersions(boolean allow)","returns":"BloomOptions.Builder","doc":"Allows starting on Paper versions newer than this Bloom release supports. Bloom logs a prominent warning. Use only if you test your plugin on that version yourself.","params":[{"name":"allow","type":"boolean","doc":"opt-in flag"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"debug","signature":"public BloomOptions.Builder debug(boolean debug)","returns":"BloomOptions.Builder","doc":"Enables debug diagnostics (adapter probe details, relocation details).","params":[{"name":"debug","type":"boolean","doc":"flag"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"quietStartup","signature":"public BloomOptions.Builder quietStartup(boolean quiet)","returns":"BloomOptions.Builder","doc":"Reduces the startup banner to a single line.","params":[{"name":"quiet","type":"boolean","doc":"flag"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"build","signature":"public BloomOptions build()","returns":"BloomOptions","doc":"Builds the options.","params":[],"returnsDoc":"immutable options","throws":[]}]},{"name":"UnsupportedFeaturePolicy","qualifiedName":"io.github.crownalonedev.bloom.bukkit.UnsupportedFeaturePolicy","kind":"enum","signature":"public final enum UnsupportedFeaturePolicy","doc":"What Bloom does when code uses a feature the running platform lacks, such as action bars on 1.7.10.","since":"0.1.0","members":[{"kind":"method","name":"values","signature":"public static UnsupportedFeaturePolicy[] values()","returns":"UnsupportedFeaturePolicy[]","doc":"","params":[],"throws":[]},{"kind":"method","name":"valueOf","signature":"public static UnsupportedFeaturePolicy valueOf(String name)","returns":"UnsupportedFeaturePolicy","doc":"","params":[{"name":"name","type":"String","doc":""}],"throws":[]},{"kind":"enumConstant","name":"SKIP","signature":"SKIP","doc":"Skip the operation and log one warning per feature. The method returns false. Default.","params":[],"throws":[]},{"kind":"enumConstant","name":"FALLBACK_TO_CHAT","signature":"FALLBACK_TO_CHAT","doc":"Deliver text features (action bar, title) as a chat message instead; other features are skipped.","params":[],"throws":[]},{"kind":"enumConstant","name":"THROW","signature":"THROW","doc":"Throw UnsupportedCapabilityException.","params":[],"throws":[]}]}]},{"name":"io.github.crownalonedev.bloom.bukkit.event","types":[{"name":"Events","qualifiedName":"io.github.crownalonedev.bloom.bukkit.event.Events","kind":"class","signature":"public final class Events implements Terminable","doc":"Lambda-based event subscriptions that are automatically removed when Bloom shuts down. Events events = bloom.events(); events.subscribe(PlayerJoinEvent.class, event -> messages.send(event.getPlayer(), \"&dWelcome!\")); events.listen(PlayerInteractEvent.class) .priority(EventPriority.HIGH) .ignoreCancelled() .filter(event -> event.getItem() != null) .limit(1) .handler(event -> event.getPlayer().sendMessage(\"First interaction!\")); Handlers run on the thread that fires the event: the main thread for most events, a background thread for asynchronous events such as AsyncPlayerPreLoginEvent. Exceptions thrown by handlers are reported by the server with the plugin's name, like any listener exception.","since":"0.1.0","members":[{"kind":"constructor","name":"Events","signature":"public Events(Plugin plugin)","doc":"Creates the service. Internal: obtain it from bloom.events().","params":[{"name":"plugin","type":"Plugin","doc":"owning plugin"}],"throws":[]},{"kind":"method","name":"subscribe","signature":"public <E extends Event> Subscription subscribe(Class<E> type, Consumer<? super E> handler)","returns":"Subscription","doc":"Subscribes a handler at EventPriority.NORMAL, receiving cancelled events too.","params":[{"name":"type","type":"Class<E>","doc":"concrete event class (must declare a HandlerList)"},{"name":"handler","type":"Consumer<? super E>","doc":"handler"}],"returnsDoc":"the subscription","throws":[]},{"kind":"method","name":"listen","signature":"public <E extends Event> SubscriptionBuilder<E> listen(Class<E> type)","returns":"SubscriptionBuilder<E>","doc":"Starts building a subscription with priority, filters and limits.","params":[{"name":"type","type":"Class<E>","doc":"concrete event class"}],"returnsDoc":"a builder; call SubscriptionBuilder.handler to register","throws":[]},{"kind":"method","name":"register","signature":"public Subscription register(Listener listener)","returns":"Subscription","doc":"Registers a classic Listener with @EventHandler methods, tied to Bloom's lifecycle.","params":[{"name":"listener","type":"Listener","doc":"listener instance"}],"returnsDoc":"a subscription that unregisters the listener","throws":[]},{"kind":"method","name":"activeCount","signature":"public int activeCount()","returns":"int","doc":"Returns the number of active subscriptions created through this service.","params":[],"returnsDoc":"active count","throws":[]},{"kind":"method","name":"close","signature":"public void close()","returns":"void","doc":"Unregisters every subscription. Idempotent.","params":[],"throws":[]},{"kind":"method","name":"isClosed","signature":"public boolean isClosed()","returns":"boolean","doc":"","params":[],"throws":[]}]},{"name":"Subscription","qualifiedName":"io.github.crownalonedev.bloom.bukkit.event.Subscription","kind":"interface","signature":"public interface Subscription extends Terminable","doc":"An active event registration. close is equivalent to unsubscribe.","since":"0.1.0","members":[{"kind":"method","name":"unsubscribe","signature":"public void unsubscribe()","returns":"void","doc":"Stops receiving events. Idempotent and safe from any thread.","params":[],"throws":[]},{"kind":"method","name":"isActive","signature":"public boolean isActive()","returns":"boolean","doc":"Returns whether the subscription still receives events.","params":[],"returnsDoc":"true until unsubscribed","throws":[]},{"kind":"method","name":"handledCount","signature":"public long handledCount()","returns":"long","doc":"Returns how many events the handler has processed.","params":[],"returnsDoc":"handled event count","throws":[]},{"kind":"method","name":"close","signature":"public default void close()","returns":"void","doc":"","params":[],"throws":[]},{"kind":"method","name":"isClosed","signature":"public default boolean isClosed()","returns":"boolean","doc":"","params":[],"throws":[]}]},{"name":"SubscriptionBuilder","qualifiedName":"io.github.crownalonedev.bloom.bukkit.event.SubscriptionBuilder","kind":"class","signature":"public final class SubscriptionBuilder<E extends Event>","doc":"Configures an event subscription. Created by Events.listen.","since":"0.1.0","members":[{"kind":"method","name":"priority","signature":"public SubscriptionBuilder<E> priority(EventPriority newPriority)","returns":"SubscriptionBuilder<E>","doc":"Sets the listener priority.","params":[{"name":"newPriority","type":"EventPriority","doc":"priority (default EventPriority.NORMAL)"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"ignoreCancelled","signature":"public SubscriptionBuilder<E> ignoreCancelled()","returns":"SubscriptionBuilder<E>","doc":"Skips events that another listener already cancelled.","params":[],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"filter","signature":"public SubscriptionBuilder<E> filter(Predicate<? super E> predicate)","returns":"SubscriptionBuilder<E>","doc":"Only handles events that match a predicate. Multiple calls combine with logical AND.","params":[{"name":"predicate","type":"Predicate<? super E>","doc":"condition"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"limit","signature":"public SubscriptionBuilder<E> limit(long maxCalls)","returns":"SubscriptionBuilder<E>","doc":"Unsubscribes automatically after the handler processed a number of (filtered) events.","params":[{"name":"maxCalls","type":"long","doc":"positive number of calls"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"handler","signature":"public Subscription handler(Consumer<? super E> handler)","returns":"Subscription","doc":"Registers the handler.","params":[{"name":"handler","type":"Consumer<? super E>","doc":"event handler"}],"returnsDoc":"the active subscription","throws":[]}]}]},{"name":"io.github.crownalonedev.bloom.bukkit.platform","types":[{"name":"AdapterContext","qualifiedName":"io.github.crownalonedev.bloom.bukkit.platform.AdapterContext","kind":"class","signature":"public final class AdapterContext","doc":"Information passed to PlatformAdapter.enable.","since":"0.1.0","members":[{"kind":"constructor","name":"AdapterContext","signature":"public AdapterContext(Plugin plugin, PlatformInfo platform, Logger logger, boolean debug)","doc":"Creates a context.","params":[{"name":"plugin","type":"Plugin","doc":"owning plugin"},{"name":"platform","type":"PlatformInfo","doc":"detected platform"},{"name":"logger","type":"Logger","doc":"plugin logger"},{"name":"debug","type":"boolean","doc":"whether verbose diagnostics are enabled"}],"throws":[]},{"kind":"method","name":"plugin","signature":"public Plugin plugin()","returns":"Plugin","doc":"Returns the owning plugin.","params":[],"returnsDoc":"plugin","throws":[]},{"kind":"method","name":"platform","signature":"public PlatformInfo platform()","returns":"PlatformInfo","doc":"Returns the detected platform.","params":[],"returnsDoc":"platform","throws":[]},{"kind":"method","name":"logger","signature":"public Logger logger()","returns":"Logger","doc":"Returns the plugin logger.","params":[],"returnsDoc":"logger","throws":[]},{"kind":"method","name":"debug","signature":"public boolean debug()","returns":"boolean","doc":"Returns whether debug diagnostics are enabled.","params":[],"returnsDoc":"debug flag","throws":[]}]},{"name":"AdapterLoader","qualifiedName":"io.github.crownalonedev.bloom.bukkit.platform.AdapterLoader","kind":"class","signature":"public final class AdapterLoader","doc":"Loads the adapter for a platform family by class name. The class name is derived from the runtime package of this class, so it stays correct when plugins relocate Bloom (e.g. to com.example.libs.bloom). Only the selected family's class is ever loaded, which keeps Java 21 adapter bytecode away from Java 8 servers.","since":"0.1.0","members":[{"kind":"method","name":"adapterClassName","signature":"public static String adapterClassName(PlatformFamily family)","returns":"String","doc":"Returns the fully qualified adapter class name for a family, relative to Bloom's runtime root package.","params":[{"name":"family","type":"PlatformFamily","doc":"platform family"}],"returnsDoc":"class name, e.g. io.github.crownalonedev.bloom.platform.modern.ModernPlatformAdapter","throws":[]},{"kind":"method","name":"load","signature":"public static PlatformAdapter load(PlatformFamily family, PlatformInfo platform)","returns":"PlatformAdapter","doc":"Loads and instantiates an adapter.","params":[{"name":"family","type":"PlatformFamily","doc":"selected family"},{"name":"platform","type":"PlatformInfo","doc":"detected platform, used in error messages"}],"returnsDoc":"a new adapter instance (not yet enabled)","throws":[{"type":"BloomStartupException","doc":"if the adapter is missing or cannot be loaded, with a corrective message"}]}]},{"name":"ItemPlatform","qualifiedName":"io.github.crownalonedev.bloom.bukkit.platform.ItemPlatform","kind":"interface","signature":"public interface ItemPlatform","doc":"Adapter SPI for item operations that differ between versions. SPI: plugin code should use bloom-items. Methods guarded by a capability are only called by Bloom when it is present.","since":"0.1.0","members":[{"kind":"method","name":"create","signature":"public Optional<ItemStack> create(String materialName, short data, int amount)","returns":"Optional<ItemStack>","doc":"Creates an item from an exact material constant name of the running server.","params":[{"name":"materialName","type":"String","doc":"exact Material constant name on this server, e.g. \"RED_WOOL\" (modern) or \"WOOL\" (legacy)"},{"name":"data","type":"short","doc":"legacy data/durability value; must be 0 on platforms without LEGACY_DATA_VALUES"},{"name":"amount","type":"int","doc":"stack size"}],"returnsDoc":"the item, or empty if the material does not exist on this server","throws":[]},{"kind":"method","name":"displayName","signature":"public void displayName(ItemMeta meta, FormattedText name)","returns":"void","doc":"Sets the display name.","params":[{"name":"meta","type":"ItemMeta","doc":"item meta to modify"},{"name":"name","type":"FormattedText","doc":"prepared name"}],"throws":[]},{"kind":"method","name":"lore","signature":"public void lore(ItemMeta meta, List<FormattedText> lore)","returns":"void","doc":"Replaces the lore.","params":[{"name":"meta","type":"ItemMeta","doc":"item meta to modify"},{"name":"lore","type":"List<FormattedText>","doc":"prepared lore lines"}],"throws":[]},{"kind":"method","name":"addItemFlags","signature":"public List<String> addItemFlags(ItemMeta meta, Collection<String> flagNames)","returns":"List<String>","doc":"Adds item flags by Bukkit ItemFlag constant name. Requires ITEM_FLAGS.","params":[{"name":"meta","type":"ItemMeta","doc":"item meta to modify"},{"name":"flagNames","type":"Collection<String>","doc":"constant names, e.g. \"HIDE_ENCHANTS\""}],"returnsDoc":"names that do not exist on this server and were skipped","throws":[]},{"kind":"method","name":"unbreakable","signature":"public void unbreakable(ItemMeta meta, boolean unbreakable)","returns":"void","doc":"Sets the unbreakable flag. Requires UNBREAKABLE_ITEMS.","params":[{"name":"meta","type":"ItemMeta","doc":"item meta to modify"},{"name":"unbreakable","type":"boolean","doc":"value"}],"throws":[]},{"kind":"method","name":"customModelData","signature":"public void customModelData(ItemMeta meta, int value)","returns":"void","doc":"Sets custom model data. Requires CUSTOM_MODEL_DATA.","params":[{"name":"meta","type":"ItemMeta","doc":"item meta to modify"},{"name":"value","type":"int","doc":"model data value"}],"throws":[]},{"kind":"method","name":"glow","signature":"public void glow(ItemMeta meta)","returns":"void","doc":"Makes the item glow like an enchanted item without showing an enchantment. Modern servers use the native glint override; legacy servers add a hidden enchantment (visible on 1.7.10, which lacks item flags).","params":[{"name":"meta","type":"ItemMeta","doc":"item meta to modify"}],"throws":[]},{"kind":"method","name":"enchantment","signature":"public Optional<Enchantment> enchantment(String name)","returns":"Optional<Enchantment>","doc":"Resolves an enchantment by modern key (\"sharpness\", \"minecraft:sharpness\") or legacy Bukkit name (\"DAMAGE_ALL\").","params":[{"name":"name","type":"String","doc":"enchantment key or name, case-insensitive"}],"returnsDoc":"the enchantment, or empty if unknown on this server","throws":[]},{"kind":"method","name":"writeData","signature":"public ItemStack writeData(ItemStack item, String namespace, String key, String value)","returns":"ItemStack","doc":"Writes a string value into the item's persistent data. Requires PERSISTENT_DATA_CONTAINER or ITEM_NBT_TAGS.","params":[{"name":"item","type":"ItemStack","doc":"source item (not modified)"},{"name":"namespace","type":"String","doc":"owning namespace, lowercase plugin name"},{"name":"key","type":"String","doc":"key within the namespace, [a-z0-9._-]+"},{"name":"value","type":"String","doc":"value to store"}],"returnsDoc":"a copy of the item carrying the value","throws":[]},{"kind":"method","name":"readData","signature":"public Optional<String> readData(ItemStack item, String namespace, String key)","returns":"Optional<String>","doc":"Reads a string value from the item's persistent data.","params":[{"name":"item","type":"ItemStack","doc":"item to read"},{"name":"namespace","type":"String","doc":"owning namespace"},{"name":"key","type":"String","doc":"key within the namespace"}],"returnsDoc":"the value, or empty if absent","throws":[]},{"kind":"method","name":"removeData","signature":"public ItemStack removeData(ItemStack item, String namespace, String key)","returns":"ItemStack","doc":"Removes a value from the item's persistent data.","params":[{"name":"item","type":"ItemStack","doc":"source item (not modified)"},{"name":"namespace","type":"String","doc":"owning namespace"},{"name":"key","type":"String","doc":"key within the namespace"}],"returnsDoc":"a copy of the item without the value","throws":[]},{"kind":"method","name":"serialize","signature":"public String serialize(ItemStack item)","returns":"String","doc":"Serialises an item to a portable string for storage on the same platform family.","params":[{"name":"item","type":"ItemStack","doc":"item to serialise"}],"returnsDoc":"serialised form with a format prefix","throws":[]},{"kind":"method","name":"deserialize","signature":"public ItemStack deserialize(String data)","returns":"ItemStack","doc":"Deserialises an item produced by serialize on the same platform family.","params":[{"name":"data","type":"String","doc":"serialised form"}],"returnsDoc":"the item","throws":[{"type":"IllegalArgumentException","doc":"if the data is malformed or from another family"}]}]},{"name":"PlatformAdapter","qualifiedName":"io.github.crownalonedev.bloom.bukkit.platform.PlatformAdapter","kind":"interface","signature":"public interface PlatformAdapter","doc":"A version adapter: the only place where version-specific Bukkit/Paper/NMS behaviour lives. SPI: implemented by bloom-legacy and bloom-modern; not intended for plugin code. Implementations must have a public no-argument constructor and must not touch server state in it. Bloom loads an adapter class by name only after SupportPolicy selected its family, so classes of other adapters are never loaded.","since":"0.1.0","members":[{"kind":"method","name":"family","signature":"public PlatformFamily family()","returns":"PlatformFamily","doc":"Returns the family this adapter serves.","params":[],"returnsDoc":"platform family","throws":[]},{"kind":"method","name":"description","signature":"public String description()","returns":"String","doc":"Returns a short description for logs, e.g. \"legacy (Bukkit 1.7.10-1.8.9)\".","params":[],"returnsDoc":"description","throws":[]},{"kind":"method","name":"enable","signature":"public Capabilities enable(AdapterContext context)","returns":"Capabilities","doc":"Initialises the adapter and probes optional features. Called once on the main thread.","params":[{"name":"context","type":"AdapterContext","doc":"startup context"}],"returnsDoc":"capabilities available on this server","throws":[]},{"kind":"method","name":"disable","signature":"public void disable()","returns":"void","doc":"Releases adapter resources. Called once during Bloom shutdown, on the main thread.","params":[],"throws":[]},{"kind":"method","name":"text","signature":"public TextPlatform text()","returns":"TextPlatform","doc":"Returns text rendering and delivery.","params":[],"returnsDoc":"text platform","throws":[]},{"kind":"method","name":"players","signature":"public PlayerPlatform players()","returns":"PlayerPlatform","doc":"Returns player and inventory operations.","params":[],"returnsDoc":"player platform","throws":[]},{"kind":"method","name":"items","signature":"public ItemPlatform items()","returns":"ItemPlatform","doc":"Returns item operations.","params":[],"returnsDoc":"item platform","throws":[]}]},{"name":"PlatformDetector","qualifiedName":"io.github.crownalonedev.bloom.bukkit.platform.PlatformDetector","kind":"class","signature":"public final class PlatformDetector","doc":"Detects the server implementation and Minecraft version from what the server reports. Implementation detection uses class probes (without initialising classes) rather than the server name alone, because forks often report custom names.","since":"0.1.0","members":[{"kind":"method","name":"detect","signature":"public static PlatformInfo detect(Server server)","returns":"PlatformInfo","doc":"Detects the platform of a running server.","params":[{"name":"server","type":"Server","doc":"the server"}],"returnsDoc":"platform description","throws":[{"type":"BloomStartupException","doc":"if the Minecraft version cannot be determined"}]},{"kind":"method","name":"detect","signature":"public static PlatformInfo detect(String serverName, String serverVersion, String bukkitVersion, String serverClassName, PlatformDetector.ClassProbe probe, JavaRuntime java)","returns":"PlatformInfo","doc":"Detects a platform from raw values.","params":[{"name":"serverName","type":"String","doc":"Server#getName()"},{"name":"serverVersion","type":"String","doc":"Server#getVersion()"},{"name":"bukkitVersion","type":"String","doc":"Server#getBukkitVersion()"},{"name":"serverClassName","type":"String","doc":"runtime class name of the Server implementation"},{"name":"probe","type":"PlatformDetector.ClassProbe","doc":"class presence probe"},{"name":"java","type":"JavaRuntime","doc":"Java runtime"}],"returnsDoc":"platform description","throws":[{"type":"BloomStartupException","doc":"if the Minecraft version cannot be determined"}]},{"kind":"method","name":"detectSoftware","signature":"public static ServerSoftware detectSoftware(String serverName, String serverClassName, PlatformDetector.ClassProbe probe)","returns":"ServerSoftware","doc":"Identifies the server implementation.","params":[{"name":"serverName","type":"String","doc":"Server#getName()"},{"name":"serverClassName","type":"String","doc":"runtime class name of the server implementation"},{"name":"probe","type":"PlatformDetector.ClassProbe","doc":"class presence probe"}],"returnsDoc":"the implementation","throws":[]}]},{"name":"PlatformDetector.ClassProbe","qualifiedName":"io.github.crownalonedev.bloom.bukkit.platform.PlatformDetector.ClassProbe","kind":"interface","signature":"public static interface ClassProbe","doc":"Probe for class presence; abstracted so detection logic is unit-testable without a server.","members":[{"kind":"method","name":"exists","signature":"public boolean exists(String className)","returns":"boolean","doc":"Tests whether a class exists.","params":[{"name":"className","type":"String","doc":"fully qualified class name"}],"returnsDoc":"true if loadable","throws":[]}]},{"name":"PlayerPlatform","qualifiedName":"io.github.crownalonedev.bloom.bukkit.platform.PlayerPlatform","kind":"interface","signature":"public interface PlayerPlatform","doc":"Adapter SPI for player and inventory operations whose binary signatures differ between server versions. Examples: Bukkit.getOnlinePlayers() returns an array on 1.7.10 and a collection on 1.8+; InventoryView is a class before 1.21 and an interface after, so calling its methods from shared code would fail with IncompatibleClassChangeError.","since":"0.1.0","members":[{"kind":"method","name":"onlinePlayers","signature":"public Collection<? extends Player> onlinePlayers()","returns":"Collection<? extends Player>","doc":"Returns the online players.","params":[],"returnsDoc":"snapshot of online players","throws":[]},{"kind":"method","name":"openTopInventory","signature":"public Inventory openTopInventory(Player player)","returns":"Inventory","doc":"Returns the top inventory of the player's open inventory view.","params":[{"name":"player","type":"Player","doc":"player"}],"returnsDoc":"top inventory (the player's crafting inventory when no container is open)","throws":[]},{"kind":"method","name":"holder","signature":"public InventoryHolder holder(Inventory inventory)","returns":"InventoryHolder","doc":"Returns an inventory's holder as cheaply as possible. On Paper this avoids creating block-state snapshots, which matters because menu listeners inspect every inventory click on the server.","params":[{"name":"inventory","type":"Inventory","doc":"inventory"}],"returnsDoc":"holder, or null","throws":[]},{"kind":"method","name":"createInventory","signature":"public Inventory createInventory(InventoryHolder holder, int size, FormattedText title)","returns":"Inventory","doc":"Creates a chest inventory.","params":[{"name":"holder","type":"InventoryHolder","doc":"holder identifying the inventory"},{"name":"size","type":"int","doc":"slot count, a multiple of 9 between 9 and 54"},{"name":"title","type":"FormattedText","doc":"prepared title; legacy adapters truncate to 32 characters"}],"returnsDoc":"the inventory","throws":[]}]},{"name":"TextPlatform","qualifiedName":"io.github.crownalonedev.bloom.bukkit.platform.TextPlatform","kind":"interface","signature":"public interface TextPlatform","doc":"Adapter SPI for rendering and delivering text. Implemented by each platform adapter. SPI: plugin code should use Messenger. Methods guarded by a capability are only called by Bloom when that capability is present.","since":"0.1.0","members":[{"kind":"method","name":"sendMessage","signature":"public void sendMessage(CommandSender target, FormattedText text)","returns":"void","doc":"Sends a chat message.","params":[{"name":"target","type":"CommandSender","doc":"recipient (player or console)"},{"name":"text","type":"FormattedText","doc":"prepared text"}],"throws":[]},{"kind":"method","name":"sendActionBar","signature":"public void sendActionBar(Player player, FormattedText text)","returns":"void","doc":"Sends an action bar message. Requires ACTION_BAR.","params":[{"name":"player","type":"Player","doc":"recipient"},{"name":"text","type":"FormattedText","doc":"prepared text"}],"throws":[]},{"kind":"method","name":"sendTitle","signature":"public void sendTitle(Player player, FormattedText title, FormattedText subtitle, Duration fadeIn, Duration stay, Duration fadeOut)","returns":"void","doc":"Shows a title. Requires TITLES; timings are honoured only with TITLE_TIMINGS.","params":[{"name":"player","type":"Player","doc":"recipient"},{"name":"title","type":"FormattedText","doc":"prepared title"},{"name":"subtitle","type":"FormattedText","doc":"prepared subtitle"},{"name":"fadeIn","type":"Duration","doc":"fade-in"},{"name":"stay","type":"Duration","doc":"stay"},{"name":"fadeOut","type":"Duration","doc":"fade-out"}],"throws":[]},{"kind":"method","name":"createBossBar","signature":"public BloomBossBar createBossBar(FormattedText title, float progress, BossBarColor color, BossBarStyle style, TextPlatform.TitleRenderer renderer)","returns":"BloomBossBar","doc":"Creates a boss bar. Requires BOSS_BAR.","params":[{"name":"title","type":"FormattedText","doc":"prepared title"},{"name":"progress","type":"float","doc":"progress 0..1"},{"name":"color","type":"BossBarColor","doc":"colour"},{"name":"style","type":"BossBarStyle","doc":"style"},{"name":"renderer","type":"TextPlatform.TitleRenderer","doc":"renders later title updates given as templates in the messenger's format"}],"returnsDoc":"a functional boss bar","throws":[]},{"kind":"method","name":"toLegacyString","signature":"public String toLegacyString(FormattedText text)","returns":"String","doc":"Renders text to a legacy § string, e.g. for APIs that only accept strings.","params":[{"name":"text","type":"FormattedText","doc":"prepared text"}],"returnsDoc":"legacy formatted string","throws":[]}]},{"name":"TextPlatform.TitleRenderer","qualifiedName":"io.github.crownalonedev.bloom.bukkit.platform.TextPlatform.TitleRenderer","kind":"interface","signature":"public static interface TitleRenderer","doc":"Converts a title template into prepared text using the owning messenger's settings.","members":[{"kind":"method","name":"render","signature":"public FormattedText render(String template)","returns":"FormattedText","doc":"Prepares a template.","params":[{"name":"template","type":"String","doc":"template text"}],"returnsDoc":"prepared text","throws":[]}]}]},{"name":"io.github.crownalonedev.bloom.bukkit.text","types":[{"name":"BloomBossBar","qualifiedName":"io.github.crownalonedev.bloom.bukkit.text.BloomBossBar","kind":"interface","signature":"public interface BloomBossBar extends Terminable","doc":"A boss bar shown to a set of players. Created by Messenger.bossBar. Must be used from the main thread. Closing the bar hides it from all viewers; Bloom closes open bars on shutdown. On platforms without Capability.BOSS_BAR (with the SKIP policy) a no-op bar is returned whose isFunctional is false.","since":"0.1.0","members":[{"kind":"method","name":"title","signature":"public BloomBossBar title(String title)","returns":"BloomBossBar","doc":"Sets the title.","params":[{"name":"title","type":"String","doc":"title in the owning messenger's format"}],"returnsDoc":"this bar","throws":[]},{"kind":"method","name":"progress","signature":"public BloomBossBar progress(float progress)","returns":"BloomBossBar","doc":"Sets the progress.","params":[{"name":"progress","type":"float","doc":"value between 0.0 and 1.0"}],"returnsDoc":"this bar","throws":[{"type":"IllegalArgumentException","doc":"if out of range"}]},{"kind":"method","name":"color","signature":"public BloomBossBar color(BossBarColor color)","returns":"BloomBossBar","doc":"Sets the colour.","params":[{"name":"color","type":"BossBarColor","doc":"colour"}],"returnsDoc":"this bar","throws":[]},{"kind":"method","name":"style","signature":"public BloomBossBar style(BossBarStyle style)","returns":"BloomBossBar","doc":"Sets the style.","params":[{"name":"style","type":"BossBarStyle","doc":"style"}],"returnsDoc":"this bar","throws":[]},{"kind":"method","name":"show","signature":"public BloomBossBar show(Player player)","returns":"BloomBossBar","doc":"Shows the bar to a player.","params":[{"name":"player","type":"Player","doc":"viewer to add"}],"returnsDoc":"this bar","throws":[]},{"kind":"method","name":"hide","signature":"public BloomBossBar hide(Player player)","returns":"BloomBossBar","doc":"Hides the bar from a player.","params":[{"name":"player","type":"Player","doc":"viewer to remove"}],"returnsDoc":"this bar","throws":[]},{"kind":"method","name":"viewers","signature":"public Collection<UUID> viewers()","returns":"Collection<UUID>","doc":"Returns the current viewers.","params":[],"returnsDoc":"snapshot of viewer UUIDs","throws":[]},{"kind":"method","name":"isFunctional","signature":"public boolean isFunctional()","returns":"boolean","doc":"Returns whether this bar actually renders on the current platform.","params":[],"returnsDoc":"false for the no-op fallback bar","throws":[]}]},{"name":"BossBarColor","qualifiedName":"io.github.crownalonedev.bloom.bukkit.text.BossBarColor","kind":"enum","signature":"public final enum BossBarColor","doc":"Boss bar colours. Mapped to the platform's boss bar API by the adapter.","since":"0.1.0","members":[{"kind":"method","name":"values","signature":"public static BossBarColor[] values()","returns":"BossBarColor[]","doc":"","params":[],"throws":[]},{"kind":"method","name":"valueOf","signature":"public static BossBarColor valueOf(String name)","returns":"BossBarColor","doc":"","params":[{"name":"name","type":"String","doc":""}],"throws":[]},{"kind":"enumConstant","name":"PINK","signature":"PINK","doc":"Pink.","params":[],"throws":[]},{"kind":"enumConstant","name":"BLUE","signature":"BLUE","doc":"Blue.","params":[],"throws":[]},{"kind":"enumConstant","name":"RED","signature":"RED","doc":"Red.","params":[],"throws":[]},{"kind":"enumConstant","name":"GREEN","signature":"GREEN","doc":"Green.","params":[],"throws":[]},{"kind":"enumConstant","name":"YELLOW","signature":"YELLOW","doc":"Yellow.","params":[],"throws":[]},{"kind":"enumConstant","name":"PURPLE","signature":"PURPLE","doc":"Purple.","params":[],"throws":[]},{"kind":"enumConstant","name":"WHITE","signature":"WHITE","doc":"White.","params":[],"throws":[]}]},{"name":"BossBarStyle","qualifiedName":"io.github.crownalonedev.bloom.bukkit.text.BossBarStyle","kind":"enum","signature":"public final enum BossBarStyle","doc":"Boss bar segment styles.","since":"0.1.0","members":[{"kind":"method","name":"values","signature":"public static BossBarStyle[] values()","returns":"BossBarStyle[]","doc":"","params":[],"throws":[]},{"kind":"method","name":"valueOf","signature":"public static BossBarStyle valueOf(String name)","returns":"BossBarStyle","doc":"","params":[{"name":"name","type":"String","doc":""}],"throws":[]},{"kind":"enumConstant","name":"SOLID","signature":"SOLID","doc":"A single solid bar.","params":[],"throws":[]},{"kind":"enumConstant","name":"SEGMENTED_6","signature":"SEGMENTED_6","doc":"Six segments.","params":[],"throws":[]},{"kind":"enumConstant","name":"SEGMENTED_10","signature":"SEGMENTED_10","doc":"Ten segments.","params":[],"throws":[]},{"kind":"enumConstant","name":"SEGMENTED_12","signature":"SEGMENTED_12","doc":"Twelve segments.","params":[],"throws":[]},{"kind":"enumConstant","name":"SEGMENTED_20","signature":"SEGMENTED_20","doc":"Twenty segments.","params":[],"throws":[]}]},{"name":"FormattedText","qualifiedName":"io.github.crownalonedev.bloom.bukkit.text.FormattedText","kind":"class","signature":"public final class FormattedText","doc":"Text whose placeholders have been applied and which is ready for an adapter to render. For TextFormat.MINIMESSAGE, content is MiniMessage markup. For TextFormat.LEGACY, it is a §-formatted string (& codes already translated). Plugin code normally never creates this directly; Messenger and the item/menu builders do.","since":"0.1.0","members":[{"kind":"method","name":"prepare","signature":"public static FormattedText prepare(String template, TextFormat format, boolean hexSupported, Placeholder... placeholders)","returns":"FormattedText","doc":"Applies placeholders to a template.","params":[{"name":"template","type":"String","doc":"message template in format"},{"name":"format","type":"TextFormat","doc":"template format"},{"name":"hexSupported","type":"boolean","doc":"whether &#RRGGBB may produce real RGB colours"},{"name":"placeholders","type":"Placeholder[]","doc":"placeholder values"}],"returnsDoc":"prepared text","throws":[]},{"kind":"method","name":"ofPrepared","signature":"public static FormattedText ofPrepared(String content, TextFormat format)","returns":"FormattedText","doc":"Wraps already-prepared content.","params":[{"name":"content","type":"String","doc":"prepared content (MiniMessage markup or § string)"},{"name":"format","type":"TextFormat","doc":"format of content"}],"returnsDoc":"text","throws":[]},{"kind":"method","name":"content","signature":"public String content()","returns":"String","doc":"Returns the prepared content.","params":[],"returnsDoc":"MiniMessage markup or a §-formatted string, depending on format","throws":[]},{"kind":"method","name":"format","signature":"public TextFormat format()","returns":"TextFormat","doc":"Returns the content format.","params":[],"returnsDoc":"format","throws":[]},{"kind":"method","name":"isEmpty","signature":"public boolean isEmpty()","returns":"boolean","doc":"Returns whether the content is empty.","params":[],"returnsDoc":"true if empty","throws":[]},{"kind":"method","name":"toString","signature":"public String toString()","returns":"String","doc":"","params":[],"throws":[]}]},{"name":"Messenger","qualifiedName":"io.github.crownalonedev.bloom.bukkit.text.Messenger","kind":"class","signature":"public final class Messenger","doc":"Sends formatted text to players and the console on every supported platform. Obtain the default instance with bloom.messages(); derive variants with withPrefix, withFormat and withUnsupportedPolicy. Messengers are immutable and thread-safe. Messenger messages = bloom.messages().withPrefix(\"&d&lBloom &8» \"); messages.send(player, \"&7Welcome back, &f{player}&7!\", Placeholder.of(\"player\", player.getName())); messages.actionBar(player, \"&aSaved!\"); messages.title(player, Title.of(\"&dLevel up\", \"&7You reached level {level}\"), Placeholder.of(\"level\", 5)); Threads Chat, action bars and titles may be sent from any thread. Boss bars must be created and changed on the main thread. Unsupported features Action bars, titles and boss bars depend on capabilities. When one is missing, behaviour follows the UnsupportedFeaturePolicy: skip with a one-time warning (default), fall back to chat, or throw.","since":"0.1.0","members":[{"kind":"constructor","name":"Messenger","signature":"public Messenger(TextPlatform text, PlayerPlatform players, Capabilities capabilities, Logger logger, Consumer<AutoCloseable> closeOnShutdown, TextFormat format, UnsupportedFeaturePolicy policy)","doc":"Creates a messenger. Internal: plugin code obtains messengers from bloom.messages().","params":[{"name":"text","type":"TextPlatform","doc":"adapter text platform"},{"name":"players","type":"PlayerPlatform","doc":"adapter player platform"},{"name":"capabilities","type":"Capabilities","doc":"platform capabilities"},{"name":"logger","type":"Logger","doc":"plugin logger"},{"name":"closeOnShutdown","type":"Consumer<AutoCloseable>","doc":"registers boss bars so they are closed at shutdown"},{"name":"format","type":"TextFormat","doc":"default format"},{"name":"policy","type":"UnsupportedFeaturePolicy","doc":"default unsupported-feature policy"}],"throws":[]},{"kind":"method","name":"format","signature":"public TextFormat format()","returns":"TextFormat","doc":"Returns the text format this messenger parses templates with.","params":[],"returnsDoc":"format","throws":[]},{"kind":"method","name":"prefix","signature":"public String prefix()","returns":"String","doc":"Returns the chat prefix template.","params":[],"returnsDoc":"prefix, empty if none","throws":[]},{"kind":"method","name":"unsupportedPolicy","signature":"public UnsupportedFeaturePolicy unsupportedPolicy()","returns":"UnsupportedFeaturePolicy","doc":"Returns the unsupported-feature policy.","params":[],"returnsDoc":"policy","throws":[]},{"kind":"method","name":"withFormat","signature":"public Messenger withFormat(TextFormat newFormat)","returns":"Messenger","doc":"Returns a messenger that parses templates in another format.","params":[{"name":"newFormat","type":"TextFormat","doc":"format"}],"returnsDoc":"derived messenger","throws":[]},{"kind":"method","name":"withPrefix","signature":"public Messenger withPrefix(String newPrefix)","returns":"Messenger","doc":"Returns a messenger that prepends a prefix to chat messages (not to action bars, titles or boss bars). The prefix is written in this messenger's format.","params":[{"name":"newPrefix","type":"String","doc":"prefix template, e.g. \"&d[Shop] \""}],"returnsDoc":"derived messenger","throws":[]},{"kind":"method","name":"withUnsupportedPolicy","signature":"public Messenger withUnsupportedPolicy(UnsupportedFeaturePolicy newPolicy)","returns":"Messenger","doc":"Returns a messenger with another unsupported-feature policy.","params":[{"name":"newPolicy","type":"UnsupportedFeaturePolicy","doc":"policy"}],"returnsDoc":"derived messenger","throws":[]},{"kind":"method","name":"render","signature":"public FormattedText render(String template, Placeholder... placeholders)","returns":"FormattedText","doc":"Applies placeholders to a template using this messenger's format.","params":[{"name":"template","type":"String","doc":"template text"},{"name":"placeholders","type":"Placeholder[]","doc":"placeholders"}],"returnsDoc":"prepared text for adapter APIs","throws":[]},{"kind":"method","name":"legacy","signature":"public String legacy(String template, Placeholder... placeholders)","returns":"String","doc":"Renders a template to a legacy §-formatted string, for APIs that only accept strings (scoreboards, signs on old versions).","params":[{"name":"template","type":"String","doc":"template text"},{"name":"placeholders","type":"Placeholder[]","doc":"placeholders"}],"returnsDoc":"legacy string","throws":[]},{"kind":"method","name":"send","signature":"public void send(CommandSender recipient, String message, Placeholder... placeholders)","returns":"void","doc":"Sends a chat message with this messenger's prefix.","params":[{"name":"recipient","type":"CommandSender","doc":"player or console"},{"name":"message","type":"String","doc":"message template"},{"name":"placeholders","type":"Placeholder[]","doc":"placeholder values"}],"throws":[]},{"kind":"method","name":"send","signature":"public void send(Iterable<? extends CommandSender> recipients, String message, Placeholder... placeholders)","returns":"void","doc":"Sends a chat message to several recipients. The template is rendered once.","params":[{"name":"recipients","type":"Iterable<? extends CommandSender>","doc":"players or console"},{"name":"message","type":"String","doc":"message template"},{"name":"placeholders","type":"Placeholder[]","doc":"placeholder values"}],"throws":[]},{"kind":"method","name":"broadcast","signature":"public void broadcast(String message, Placeholder... placeholders)","returns":"void","doc":"Sends a chat message to every online player and the console.","params":[{"name":"message","type":"String","doc":"message template"},{"name":"placeholders","type":"Placeholder[]","doc":"placeholder values"}],"throws":[]},{"kind":"method","name":"actionBar","signature":"public boolean actionBar(Player player, String message, Placeholder... placeholders)","returns":"boolean","doc":"Shows an action bar message.","params":[{"name":"player","type":"Player","doc":"recipient"},{"name":"message","type":"String","doc":"message template (no prefix is added)"},{"name":"placeholders","type":"Placeholder[]","doc":"placeholder values"}],"returnsDoc":"true if delivered as an action bar or as a chat fallback","throws":[{"type":"UnsupportedCapabilityException","doc":"if unsupported and the policy is THROW"}]},{"kind":"method","name":"title","signature":"public boolean title(Player player, Title title, Placeholder... placeholders)","returns":"boolean","doc":"Shows a title.","params":[{"name":"player","type":"Player","doc":"recipient"},{"name":"title","type":"Title","doc":"title and subtitle templates with timings"},{"name":"placeholders","type":"Placeholder[]","doc":"placeholder values, applied to both lines"}],"returnsDoc":"true if delivered as a title or as a chat fallback","throws":[{"type":"UnsupportedCapabilityException","doc":"if unsupported and the policy is THROW"}]},{"kind":"method","name":"bossBar","signature":"public BloomBossBar bossBar(String title, float progress, BossBarColor color, BossBarStyle style, Placeholder... placeholders)","returns":"BloomBossBar","doc":"Creates a boss bar. Show it to players with BloomBossBar.show. Must be called on the main thread. Bloom closes the bar on shutdown.","params":[{"name":"title","type":"String","doc":"title template"},{"name":"progress","type":"float","doc":"progress between 0 and 1"},{"name":"color","type":"BossBarColor","doc":"colour"},{"name":"style","type":"BossBarStyle","doc":"style"},{"name":"placeholders","type":"Placeholder[]","doc":"placeholder values for the initial title"}],"returnsDoc":"a boss bar, or a non-functional bar (BloomBossBar.isFunctional is false) when boss bars are unsupported and the policy is not THROW","throws":[{"type":"UnsupportedCapabilityException","doc":"if unsupported and the policy is THROW"}]}]},{"name":"Title","qualifiedName":"io.github.crownalonedev.bloom.bukkit.text.Title","kind":"class","signature":"public final class Title","doc":"A title/subtitle pair with display timings. Timings require Capability.TITLE_TIMINGS; on 1.8 servers the client's default timings are used (fade in 0.5s, stay 3.5s, fade out 1s) and custom timings are ignored. messenger.title(player, Title.of(\"&dWelcome\", \"&7to the server\")); messenger.title(player, Title.of(\"<gold>Victory\").times(Duration.ofMillis(250), Duration.ofSeconds(2), Duration.ofMillis(500)));","since":"0.1.0","members":[{"kind":"field","name":"DEFAULT_FADE_IN","signature":"public static final Duration DEFAULT_FADE_IN","doc":"Default fade-in time, matching the vanilla client.","params":[],"throws":[]},{"kind":"field","name":"DEFAULT_STAY","signature":"public static final Duration DEFAULT_STAY","doc":"Default stay time, matching the vanilla client.","params":[],"throws":[]},{"kind":"field","name":"DEFAULT_FADE_OUT","signature":"public static final Duration DEFAULT_FADE_OUT","doc":"Default fade-out time, matching the vanilla client.","params":[],"throws":[]},{"kind":"method","name":"of","signature":"public static Title of(String title)","returns":"Title","doc":"Creates a title without a subtitle and with default timings.","params":[{"name":"title","type":"String","doc":"title text in the messenger's format"}],"returnsDoc":"the title","throws":[]},{"kind":"method","name":"of","signature":"public static Title of(String title, String subtitle)","returns":"Title","doc":"Creates a title with a subtitle and default timings.","params":[{"name":"title","type":"String","doc":"title text in the messenger's format"},{"name":"subtitle","type":"String","doc":"subtitle text in the messenger's format"}],"returnsDoc":"the title","throws":[]},{"kind":"method","name":"times","signature":"public Title times(Duration fadeIn, Duration stay, Duration fadeOut)","returns":"Title","doc":"Returns a copy with custom timings.","params":[{"name":"fadeIn","type":"Duration","doc":"fade-in duration"},{"name":"stay","type":"Duration","doc":"stay duration"},{"name":"fadeOut","type":"Duration","doc":"fade-out duration"}],"returnsDoc":"a new title","throws":[]},{"kind":"method","name":"title","signature":"public String title()","returns":"String","doc":"Returns the title template.","params":[],"returnsDoc":"title text","throws":[]},{"kind":"method","name":"subtitle","signature":"public String subtitle()","returns":"String","doc":"Returns the subtitle template.","params":[],"returnsDoc":"subtitle text, possibly empty","throws":[]},{"kind":"method","name":"fadeIn","signature":"public Duration fadeIn()","returns":"Duration","doc":"Returns the fade-in duration.","params":[],"returnsDoc":"fade-in","throws":[]},{"kind":"method","name":"stay","signature":"public Duration stay()","returns":"Duration","doc":"Returns the stay duration.","params":[],"returnsDoc":"stay","throws":[]},{"kind":"method","name":"fadeOut","signature":"public Duration fadeOut()","returns":"Duration","doc":"Returns the fade-out duration.","params":[],"returnsDoc":"fade-out","throws":[]}]}]}]},{"name":"bloom-items","description":"Bloom Items: item builder, cross-version materials, persistent item identity and serialization.","packages":[{"name":"io.github.crownalonedev.bloom.items","types":[{"name":"CustomItem","qualifiedName":"io.github.crownalonedev.bloom.items.CustomItem","kind":"class","signature":"public final class CustomItem","doc":"A custom item defined with Items.define.","since":"0.1.0","members":[{"kind":"method","name":"id","signature":"public String id()","returns":"String","doc":"Returns the custom id.","params":[],"returnsDoc":"id","throws":[]},{"kind":"method","name":"create","signature":"public ItemStack create()","returns":"ItemStack","doc":"Creates one item.","params":[],"returnsDoc":"a new stack of size 1","throws":[]},{"kind":"method","name":"create","signature":"public ItemStack create(int amount)","returns":"ItemStack","doc":"Creates a stack.","params":[{"name":"amount","type":"int","doc":"stack size, >= 1"}],"returnsDoc":"a new stack","throws":[]},{"kind":"method","name":"matches","signature":"public boolean matches(ItemStack item)","returns":"boolean","doc":"Returns whether an item is an instance of this custom item, by persistent id (not by name or lore).","params":[{"name":"item","type":"ItemStack","doc":"item, may be null"}],"returnsDoc":"true if the item carries this id","throws":[]},{"kind":"method","name":"toString","signature":"public String toString()","returns":"String","doc":"","params":[],"throws":[]}]},{"name":"ItemBuilder","qualifiedName":"io.github.crownalonedev.bloom.items.ItemBuilder","kind":"class","signature":"public final class ItemBuilder","doc":"Fluent, cross-version item builder. Created by Items.builder or Items.edit. Text (name, lore) uses the Bloom default TextFormat unless format is called, and renders white and non-italic unless the text sets its own colour. Features missing on the running server (e.g. custom model data on 1.8) follow the Bloom UnsupportedFeaturePolicy: skipped with a one-time warning by default. Builders are not thread-safe; build may be called repeatedly and returns a new item each time.","since":"0.1.0","members":[{"kind":"method","name":"amount","signature":"public ItemBuilder amount(int newAmount)","returns":"ItemBuilder","doc":"Sets the stack size.","params":[{"name":"newAmount","type":"int","doc":"amount, 1..64 (or the item's max stack size)"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"name","signature":"public ItemBuilder name(String template)","returns":"ItemBuilder","doc":"Sets the display name.","params":[{"name":"template","type":"String","doc":"name in the builder's text format, e.g. \"&dBlossom Blade\""}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"lore","signature":"public ItemBuilder lore(String... lines)","returns":"ItemBuilder","doc":"Replaces the lore.","params":[{"name":"lines","type":"String[]","doc":"lore lines in the builder's text format"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"lore","signature":"public ItemBuilder lore(List<String> lines)","returns":"ItemBuilder","doc":"Replaces the lore.","params":[{"name":"lines","type":"List<String>","doc":"lore lines in the builder's text format"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"addLore","signature":"public ItemBuilder addLore(String... lines)","returns":"ItemBuilder","doc":"Appends lore lines.","params":[{"name":"lines","type":"String[]","doc":"lines to append"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"enchant","signature":"public ItemBuilder enchant(String enchantment, int level)","returns":"ItemBuilder","doc":"Adds an enchantment by name. Accepts modern keys (\"sharpness\") and legacy Bukkit names (\"DAMAGE_ALL\"). Enchantments unknown on this server are skipped with a warning. Levels above the vanilla maximum are allowed.","params":[{"name":"enchantment","type":"String","doc":"enchantment key or name"},{"name":"level","type":"int","doc":"level, >= 1"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"enchant","signature":"public ItemBuilder enchant(Enchantment enchantment, int level)","returns":"ItemBuilder","doc":"Adds an enchantment object from the API you compile against.","params":[{"name":"enchantment","type":"Enchantment","doc":"enchantment"},{"name":"level","type":"int","doc":"level, >= 1"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"flags","signature":"public ItemBuilder flags(ItemFlagKey... newFlags)","returns":"ItemBuilder","doc":"Adds item flags. Requires ITEM_FLAGS (1.8+).","params":[{"name":"newFlags","type":"ItemFlagKey[]","doc":"flags"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"glow","signature":"public ItemBuilder glow()","returns":"ItemBuilder","doc":"Makes the item glow without visible enchantments (see ItemPlatform#glow).","params":[],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"unbreakable","signature":"public ItemBuilder unbreakable(boolean value)","returns":"ItemBuilder","doc":"Sets the unbreakable flag. Requires UNBREAKABLE_ITEMS.","params":[{"name":"value","type":"boolean","doc":"unbreakable"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"customModelData","signature":"public ItemBuilder customModelData(int value)","returns":"ItemBuilder","doc":"Sets custom model data. Requires CUSTOM_MODEL_DATA (modern only).","params":[{"name":"value","type":"int","doc":"model data"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"data","signature":"public ItemBuilder data(String key, String value)","returns":"ItemBuilder","doc":"Stores a string in the item's persistent data under the plugin namespace.","params":[{"name":"key","type":"String","doc":"key, [a-z0-9._-]+"},{"name":"value","type":"String","doc":"value"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"customId","signature":"public ItemBuilder customId(String id)","returns":"ItemBuilder","doc":"Marks the item with a custom id (see Items.customId).","params":[{"name":"id","type":"String","doc":"custom id"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"placeholders","signature":"public ItemBuilder placeholders(Placeholder... values)","returns":"ItemBuilder","doc":"Sets placeholders applied to the name and lore.","params":[{"name":"values","type":"Placeholder[]","doc":"placeholders"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"format","signature":"public ItemBuilder format(TextFormat textFormat)","returns":"ItemBuilder","doc":"Sets the text format for name and lore.","params":[{"name":"textFormat","type":"TextFormat","doc":"format"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"meta","signature":"public ItemBuilder meta(Consumer<ItemMeta> editor)","returns":"ItemBuilder","doc":"Applies a custom edit to the Bukkit ItemMeta after Bloom's own settings. Use for features Bloom does not wrap (leather colour, skull owner, ...). Code here must be valid for every server version you target.","params":[{"name":"editor","type":"Consumer<ItemMeta>","doc":"meta editor"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"build","signature":"public ItemStack build()","returns":"ItemStack","doc":"Builds a new item.","params":[],"returnsDoc":"the item","throws":[]}]},{"name":"ItemFlagKey","qualifiedName":"io.github.crownalonedev.bloom.items.ItemFlagKey","kind":"enum","signature":"public final enum ItemFlagKey","doc":"Cross-version item flags. Each key maps to the Bukkit ItemFlag constant(s) that implement it on different versions; flags that do not exist on the running server are skipped. Requires Capability.ITEM_FLAGS (1.8+).","since":"0.1.0","members":[{"kind":"method","name":"values","signature":"public static ItemFlagKey[] values()","returns":"ItemFlagKey[]","doc":"","params":[],"throws":[]},{"kind":"method","name":"valueOf","signature":"public static ItemFlagKey valueOf(String name)","returns":"ItemFlagKey","doc":"","params":[{"name":"name","type":"String","doc":""}],"throws":[]},{"kind":"enumConstant","name":"HIDE_ENCHANTS","signature":"HIDE_ENCHANTS","doc":"Hides enchantments.","params":[],"throws":[]},{"kind":"enumConstant","name":"HIDE_ATTRIBUTES","signature":"HIDE_ATTRIBUTES","doc":"Hides attribute modifiers such as attack damage.","params":[],"throws":[]},{"kind":"enumConstant","name":"HIDE_UNBREAKABLE","signature":"HIDE_UNBREAKABLE","doc":"Hides the \"Unbreakable\" line.","params":[],"throws":[]},{"kind":"enumConstant","name":"HIDE_DESTROYS","signature":"HIDE_DESTROYS","doc":"Hides \"Can destroy\" lines.","params":[],"throws":[]},{"kind":"enumConstant","name":"HIDE_PLACED_ON","signature":"HIDE_PLACED_ON","doc":"Hides \"Can be placed on\" lines.","params":[],"throws":[]},{"kind":"enumConstant","name":"HIDE_ADDITIONAL_TOOLTIP","signature":"HIDE_ADDITIONAL_TOOLTIP","doc":"Hides potion effects and other additional tooltip lines (1.8 HIDE_POTION_EFFECTS, modern HIDE_ADDITIONAL_TOOLTIP).","params":[],"throws":[]},{"kind":"enumConstant","name":"HIDE_DYE","signature":"HIDE_DYE","doc":"Hides leather armour dye colour (modern only).","params":[],"throws":[]},{"kind":"enumConstant","name":"HIDE_ARMOR_TRIM","signature":"HIDE_ARMOR_TRIM","doc":"Hides armour trims (modern only).","params":[],"throws":[]},{"kind":"enumConstant","name":"HIDE_STORED_ENCHANTS","signature":"HIDE_STORED_ENCHANTS","doc":"Hides stored enchantments on enchanted books (modern only).","params":[],"throws":[]},{"kind":"method","name":"bukkitNames","signature":"public List<String> bukkitNames()","returns":"List<String>","doc":"Returns the Bukkit ItemFlag names that implement this flag, newest first.","params":[],"returnsDoc":"candidate constant names","throws":[]}]},{"name":"Items","qualifiedName":"io.github.crownalonedev.bloom.items.Items","kind":"class","signature":"public final class Items","doc":"Cross-version item creation, identification and serialization. Items items = Items.of(bloom); ItemStack sword = items.builder(\"DIAMOND_SWORD\") .name(\"&dBlossom Blade\") .lore(\"&7Forged under falling petals.\") .enchant(\"sharpness\", 3) .flags(ItemFlagKey.HIDE_ENCHANTS) .unbreakable(true) .build(); CustomItem ruby = items.define(\"ruby\", items.builder(\"RED_DYE\").name(\"&cRuby\").glow()); player.getInventory().addItem(ruby.create(3)); boolean isRuby = ruby.matches(player.getItemInHand()); Identity Custom ids and data are stored as namespaced persistent data, never in names or lore: PersistentDataContainer on modern servers, NBT on legacy servers. The namespace is the owning plugin's lowercase name. Data written on one platform family cannot be read on the other.","since":"0.1.0","members":[{"kind":"field","name":"ID_KEY","signature":"public static final String ID_KEY","doc":"Data key used for custom item ids.","params":[],"throws":[]},{"kind":"method","name":"of","signature":"public static Items of(Bloom bloom)","returns":"Items","doc":"Returns the items service of a Bloom instance, creating it on first use.","params":[{"name":"bloom","type":"Bloom","doc":"running Bloom instance"}],"returnsDoc":"the service","throws":[]},{"kind":"method","name":"builder","signature":"public ItemBuilder builder(String materialKey)","returns":"ItemBuilder","doc":"Starts building an item.","params":[{"name":"materialKey","type":"String","doc":"modern material name (legacy names and NAME:DATA accepted); see MaterialMappings"}],"returnsDoc":"a builder","throws":[{"type":"IllegalArgumentException","doc":"if the material does not exist on this server"}]},{"kind":"method","name":"builder","signature":"public ItemBuilder builder(Material material)","returns":"ItemBuilder","doc":"Starts building from a Bukkit material constant of the API you compile against.","params":[{"name":"material","type":"Material","doc":"material"}],"returnsDoc":"a builder","throws":[]},{"kind":"method","name":"edit","signature":"public ItemBuilder edit(ItemStack item)","returns":"ItemBuilder","doc":"Starts editing a copy of an existing item. Name and lore are kept unless the builder sets them.","params":[{"name":"item","type":"ItemStack","doc":"item to copy"}],"returnsDoc":"a builder","throws":[]},{"kind":"method","name":"create","signature":"public Optional<ItemStack> create(String materialKey, int amount)","returns":"Optional<ItemStack>","doc":"Creates a plain item.","params":[{"name":"materialKey","type":"String","doc":"material key"},{"name":"amount","type":"int","doc":"stack size"}],"returnsDoc":"the item, or empty if the material does not exist on this server","throws":[]},{"kind":"method","name":"exists","signature":"public boolean exists(String materialKey)","returns":"boolean","doc":"Returns whether a material key resolves on this server.","params":[{"name":"materialKey","type":"String","doc":"material key"}],"returnsDoc":"true if create would succeed","throws":[]},{"kind":"method","name":"materials","signature":"public MaterialMappings materials()","returns":"MaterialMappings","doc":"Returns the material mappings, where custom keys can be registered.","params":[],"returnsDoc":"mappings","throws":[]},{"kind":"method","name":"namespace","signature":"public String namespace()","returns":"String","doc":"Returns the persistent data namespace (lowercase plugin name).","params":[],"returnsDoc":"namespace","throws":[]},{"kind":"method","name":"supportsData","signature":"public boolean supportsData()","returns":"boolean","doc":"Returns whether items can carry persistent data on this server.","params":[],"returnsDoc":"true with PersistentDataContainer (modern) or NBT access (legacy)","throws":[]},{"kind":"method","name":"setData","signature":"public ItemStack setData(ItemStack item, String key, String value)","returns":"ItemStack","doc":"Returns a copy of an item with a string value stored under this plugin's namespace.","params":[{"name":"item","type":"ItemStack","doc":"source item (unchanged)"},{"name":"key","type":"String","doc":"key, [a-z0-9._-]+"},{"name":"value","type":"String","doc":"value"}],"returnsDoc":"tagged copy","throws":[{"type":"UnsupportedCapabilityException","doc":"if items cannot carry data on this server"}]},{"kind":"method","name":"getData","signature":"public Optional<String> getData(ItemStack item, String key)","returns":"Optional<String>","doc":"Reads a value stored by setData.","params":[{"name":"item","type":"ItemStack","doc":"item, may be null"},{"name":"key","type":"String","doc":"key"}],"returnsDoc":"the value, or empty","throws":[]},{"kind":"method","name":"removeData","signature":"public ItemStack removeData(ItemStack item, String key)","returns":"ItemStack","doc":"Returns a copy of an item without a stored value.","params":[{"name":"item","type":"ItemStack","doc":"source item (unchanged)"},{"name":"key","type":"String","doc":"key"}],"returnsDoc":"copy without the key","throws":[]},{"kind":"method","name":"tag","signature":"public ItemStack tag(ItemStack item, String id)","returns":"ItemStack","doc":"Returns a copy of an item marked with a custom id.","params":[{"name":"item","type":"ItemStack","doc":"source item"},{"name":"id","type":"String","doc":"custom id, [a-z0-9._-]+"}],"returnsDoc":"tagged copy","throws":[]},{"kind":"method","name":"customId","signature":"public Optional<String> customId(ItemStack item)","returns":"Optional<String>","doc":"Returns the custom id of an item.","params":[{"name":"item","type":"ItemStack","doc":"item, may be null"}],"returnsDoc":"id, or empty if the item has none","throws":[]},{"kind":"method","name":"isCustom","signature":"public boolean isCustom(ItemStack item, String id)","returns":"boolean","doc":"Returns whether an item carries a custom id.","params":[{"name":"item","type":"ItemStack","doc":"item, may be null"},{"name":"id","type":"String","doc":"expected id"}],"returnsDoc":"true if the ids match","throws":[]},{"kind":"method","name":"define","signature":"public CustomItem define(String id, ItemBuilder template)","returns":"CustomItem","doc":"Defines a custom item. The template is built once and tagged with the id; CustomItem.create returns copies.","params":[{"name":"id","type":"String","doc":"unique id, [a-z0-9._-]+"},{"name":"template","type":"ItemBuilder","doc":"builder describing the item"}],"returnsDoc":"the definition","throws":[{"type":"IllegalStateException","doc":"if the id is already defined"}]},{"kind":"method","name":"custom","signature":"public Optional<CustomItem> custom(String id)","returns":"Optional<CustomItem>","doc":"Looks up a custom item definition.","params":[{"name":"id","type":"String","doc":"custom id"}],"returnsDoc":"the definition, or empty","throws":[]},{"kind":"method","name":"match","signature":"public Optional<CustomItem> match(ItemStack item)","returns":"Optional<CustomItem>","doc":"Finds the custom item definition an item stack belongs to.","params":[{"name":"item","type":"ItemStack","doc":"item, may be null"}],"returnsDoc":"the definition, or empty for plain items and unknown ids","throws":[]},{"kind":"method","name":"customItems","signature":"public Collection<CustomItem> customItems()","returns":"Collection<CustomItem>","doc":"Returns all custom item definitions.","params":[],"returnsDoc":"snapshot in definition order","throws":[]},{"kind":"method","name":"serialize","signature":"public String serialize(ItemStack item)","returns":"String","doc":"Serialises an item to a string for storage (databases, configs). The format is platform-family specific.","params":[{"name":"item","type":"ItemStack","doc":"non-empty item"}],"returnsDoc":"serialised item","throws":[]},{"kind":"method","name":"deserialize","signature":"public ItemStack deserialize(String data)","returns":"ItemStack","doc":"Deserialises an item created by serialize on the same platform family.","params":[{"name":"data","type":"String","doc":"serialised item"}],"returnsDoc":"the item","throws":[{"type":"IllegalArgumentException","doc":"if malformed or from the other platform family"}]}]},{"name":"MaterialMappings","qualifiedName":"io.github.crownalonedev.bloom.items.MaterialMappings","kind":"class","signature":"public final class MaterialMappings","doc":"Resolves Bloom material keys to the material names of the running server. Keys are modern (1.13+) material names , e.g. RED_WOOL, PLAYER_HEAD, OAK_PLANKS. Legacy names (WATCH) and legacy NAME:DATA specs (WOOL:14) are accepted too. Resolution order: • legacy servers: developer mapping → built-in modern→legacy table → the key itself → NAME:DATA • modern servers: developer mapping → the key itself → built-in legacy→modern table So an ambiguous name such as MELON always means the modern item (the block) on every server. Thread-safe.","since":"0.1.0","members":[{"kind":"method","name":"register","signature":"public MaterialMappings register(String key, String modernName, String legacyName, int legacyData)","returns":"MaterialMappings","doc":"Registers or replaces a mapping for a key.","params":[{"name":"key","type":"String","doc":"Bloom key used in code/configs, e.g. \"SHOP_ICON\" or a modern material name"},{"name":"modernName","type":"String","doc":"material name on modern servers, or null if not available there"},{"name":"legacyName","type":"String","doc":"material name on legacy servers, or null if not available there"},{"name":"legacyData","type":"int","doc":"legacy data value"}],"returnsDoc":"this instance","throws":[]},{"kind":"method","name":"candidates","signature":"public List<MaterialMappings.Candidate> candidates(String key, boolean legacyPlatform)","returns":"List<MaterialMappings.Candidate>","doc":"Returns the names to try, in order, for a key on the given platform family.","params":[{"name":"key","type":"String","doc":"material key"},{"name":"legacyPlatform","type":"boolean","doc":"whether the server uses legacy data values"}],"returnsDoc":"ordered, de-duplicated candidates (never empty)","throws":[]},{"kind":"method","name":"builtinSize","signature":"public static int builtinSize()","returns":"int","doc":"Returns the number of built-in table rows (for diagnostics).","params":[],"returnsDoc":"number of built-in modern→legacy mappings","throws":[]}]},{"name":"MaterialMappings.Candidate","qualifiedName":"io.github.crownalonedev.bloom.items.MaterialMappings.Candidate","kind":"class","signature":"public static final class Candidate","doc":"A concrete material name and legacy data value to try on the running server.","members":[{"kind":"method","name":"name","signature":"public String name()","returns":"String","doc":"Returns the server material name.","params":[],"returnsDoc":"material constant name","throws":[]},{"kind":"method","name":"data","signature":"public short data()","returns":"short","doc":"Returns the legacy data value.","params":[],"returnsDoc":"data value, 0 on modern servers","throws":[]},{"kind":"method","name":"equals","signature":"public boolean equals(Object o)","returns":"boolean","doc":"","params":[{"name":"o","type":"Object","doc":""}],"throws":[]},{"kind":"method","name":"hashCode","signature":"public int hashCode()","returns":"int","doc":"","params":[],"throws":[]},{"kind":"method","name":"toString","signature":"public String toString()","returns":"String","doc":"","params":[],"throws":[]}]}]}]},{"name":"bloom-gui","description":"Bloom GUI: inventory menus with buttons, layouts, pagination, navigation and duplication-safe event handling.","packages":[{"name":"io.github.crownalonedev.bloom.gui","types":[{"name":"Button","qualifiedName":"io.github.crownalonedev.bloom.gui.Button","kind":"class","signature":"public final class Button","doc":"An item in a menu slot with an optional click handler. Buttons are immutable; the with*/on* methods return modified copies, so one button can be reused in many menus. Button heal = Button.of(items.builder(\"GOLDEN_APPLE\").name(\"&aHeal\").build()) .permission(\"shop.heal\") .onClick(click -> { click.player().setHealth(20.0); click.close(); }); Button counter = Button.dynamic(view -> items.builder(\"PAPER\") .name(\"&fClicks: \" + view.state().getOrDefault(CLICKS, 0)).build()) .onClick(click -> { click.view().state().set(CLICKS, click.view().state().getOrDefault(CLICKS, 0) + 1); click.refresh(); });","since":"0.1.0","members":[{"kind":"method","name":"of","signature":"public static Button of(ItemStack item)","returns":"Button","doc":"Creates a button with a fixed item and no click action (a decoration).","params":[{"name":"item","type":"ItemStack","doc":"item to display (copied when rendered)"}],"returnsDoc":"the button","throws":[]},{"kind":"method","name":"of","signature":"public static Button of(ItemStack item, Consumer<ClickContext> onClick)","returns":"Button","doc":"Creates a button with a fixed item and a click action.","params":[{"name":"item","type":"ItemStack","doc":"item to display"},{"name":"onClick","type":"Consumer<ClickContext>","doc":"click handler, runs on the main thread"}],"returnsDoc":"the button","throws":[]},{"kind":"method","name":"dynamic","signature":"public static Button dynamic(Function<MenuView,ItemStack> icon)","returns":"Button","doc":"Creates a button whose item is computed for each viewer on every render (open, refresh, page change).","params":[{"name":"icon","type":"Function<MenuView,ItemStack>","doc":"computes the item; may return null for an empty slot"}],"returnsDoc":"the button","throws":[]},{"kind":"method","name":"onClick","signature":"public Button onClick(Consumer<ClickContext> onClick)","returns":"Button","doc":"Returns a copy with a click handler. Handlers run on the main thread; exceptions are logged and do not break the menu.","params":[{"name":"onClick","type":"Consumer<ClickContext>","doc":"click handler"}],"returnsDoc":"a new button","throws":[]},{"kind":"method","name":"visibleWhen","signature":"public Button visibleWhen(Predicate<MenuView> condition)","returns":"Button","doc":"Returns a copy that is only shown when a condition holds (evaluated on each render). Hidden buttons leave the slot to the menu's filler.","params":[{"name":"condition","type":"Predicate<MenuView>","doc":"visibility condition"}],"returnsDoc":"a new button","throws":[]},{"kind":"method","name":"permission","signature":"public Button permission(String node)","returns":"Button","doc":"Returns a copy that is hidden from players without a permission.","params":[{"name":"node","type":"String","doc":"permission node"}],"returnsDoc":"a new button","throws":[]},{"kind":"method","name":"permission","signature":"public Button permission(String node, ItemStack deniedItem)","returns":"Button","doc":"Returns a copy that shows deniedItem (without running the click handler) to players lacking a permission.","params":[{"name":"node","type":"String","doc":"permission node"},{"name":"deniedItem","type":"ItemStack","doc":"item shown instead"}],"returnsDoc":"a new button","throws":[]}]},{"name":"ClickContext","qualifiedName":"io.github.crownalonedev.bloom.gui.ClickContext","kind":"class","signature":"public final class ClickContext","doc":"Information about a button click, passed to Button.onClick handlers. The underlying Bukkit event is always cancelled; items never move in or out of a Bloom menu. Navigation methods (open, back, close) take effect on the next tick, because opening or closing inventories inside a click event is unsafe on Bukkit servers.","since":"0.1.0","members":[{"kind":"method","name":"player","signature":"public Player player()","returns":"Player","doc":"Returns the clicking player.","params":[],"returnsDoc":"player","throws":[]},{"kind":"method","name":"view","signature":"public MenuView view()","returns":"MenuView","doc":"Returns the player's view of the menu (page, state).","params":[],"returnsDoc":"view","throws":[]},{"kind":"method","name":"menu","signature":"public Menu menu()","returns":"Menu","doc":"Returns the menu definition.","params":[],"returnsDoc":"menu","throws":[]},{"kind":"method","name":"slot","signature":"public int slot()","returns":"int","doc":"Returns the clicked slot.","params":[],"returnsDoc":"slot index in the menu","throws":[]},{"kind":"method","name":"kind","signature":"public ClickKind kind()","returns":"ClickKind","doc":"Returns the kind of click.","params":[],"returnsDoc":"click kind","throws":[]},{"kind":"method","name":"hotbarButton","signature":"public int hotbarButton()","returns":"int","doc":"Returns the number key pressed for ClickKind.NUMBER_KEY clicks.","params":[],"returnsDoc":"hotbar slot 0-8, or -1","throws":[]},{"kind":"method","name":"menus","signature":"public Menus menus()","returns":"Menus","doc":"Returns the menu service.","params":[],"returnsDoc":"menus","throws":[]},{"kind":"method","name":"refresh","signature":"public void refresh()","returns":"void","doc":"Re-renders the current menu for this player.","params":[],"throws":[]},{"kind":"method","name":"open","signature":"public void open(Menu menu)","returns":"void","doc":"Opens another menu on the next tick. The current menu is kept in the history for back.","params":[{"name":"menu","type":"Menu","doc":"menu to open"}],"throws":[]},{"kind":"method","name":"back","signature":"public boolean back()","returns":"boolean","doc":"Goes back to the previous menu on the next tick.","params":[],"returnsDoc":"true if there is a previous menu","throws":[]},{"kind":"method","name":"close","signature":"public void close()","returns":"void","doc":"Closes the menu on the next tick.","params":[],"throws":[]}]},{"name":"ClickKind","qualifiedName":"io.github.crownalonedev.bloom.gui.ClickKind","kind":"enum","signature":"public final enum ClickKind","doc":"Version-independent click types. Mapped from Bukkit's ClickType by name, so constants added in newer versions never cause linkage errors on older servers.","since":"0.1.0","members":[{"kind":"method","name":"values","signature":"public static ClickKind[] values()","returns":"ClickKind[]","doc":"","params":[],"throws":[]},{"kind":"method","name":"valueOf","signature":"public static ClickKind valueOf(String name)","returns":"ClickKind","doc":"","params":[{"name":"name","type":"String","doc":""}],"throws":[]},{"kind":"enumConstant","name":"LEFT","signature":"LEFT","doc":"Left click.","params":[],"throws":[]},{"kind":"enumConstant","name":"RIGHT","signature":"RIGHT","doc":"Right click.","params":[],"throws":[]},{"kind":"enumConstant","name":"SHIFT_LEFT","signature":"SHIFT_LEFT","doc":"Shift + left click.","params":[],"throws":[]},{"kind":"enumConstant","name":"SHIFT_RIGHT","signature":"SHIFT_RIGHT","doc":"Shift + right click.","params":[],"throws":[]},{"kind":"enumConstant","name":"MIDDLE","signature":"MIDDLE","doc":"Middle click (creative pick).","params":[],"throws":[]},{"kind":"enumConstant","name":"NUMBER_KEY","signature":"NUMBER_KEY","doc":"Number key 1-9; see ClickContext.hotbarButton.","params":[],"throws":[]},{"kind":"enumConstant","name":"DROP","signature":"DROP","doc":"Drop key (Q).","params":[],"throws":[]},{"kind":"enumConstant","name":"CONTROL_DROP","signature":"CONTROL_DROP","doc":"Control + drop key.","params":[],"throws":[]},{"kind":"enumConstant","name":"SWAP_OFFHAND","signature":"SWAP_OFFHAND","doc":"Off-hand swap key (F), 1.16+.","params":[],"throws":[]},{"kind":"enumConstant","name":"OTHER","signature":"OTHER","doc":"Any other click (creative, window border, unknown).","params":[],"throws":[]},{"kind":"method","name":"isLeft","signature":"public boolean isLeft()","returns":"boolean","doc":"Returns whether this is a left click (with or without shift).","params":[],"returnsDoc":"true for LEFT and SHIFT_LEFT","throws":[]},{"kind":"method","name":"isRight","signature":"public boolean isRight()","returns":"boolean","doc":"Returns whether this is a right click (with or without shift).","params":[],"returnsDoc":"true for RIGHT and SHIFT_RIGHT","throws":[]},{"kind":"method","name":"isShift","signature":"public boolean isShift()","returns":"boolean","doc":"Returns whether shift was held.","params":[],"returnsDoc":"true for shift clicks","throws":[]}]},{"name":"ConfirmMenu","qualifiedName":"io.github.crownalonedev.bloom.gui.ConfirmMenu","kind":"class","signature":"public final class ConfirmMenu","doc":"Builds yes/no confirmation dialogs. Menu confirm = ConfirmMenu.builder() .title(\"&cDelete home '{home}'?\") .confirm(items.builder(\"LIME_WOOL\").name(\"&aDelete\").build(), click -> { homes.delete(click.player(), name); click.close(); }) .cancel(items.builder(\"RED_WOOL\").name(\"&cKeep\").build()) .build(); click.open(confirm); The default cancel action goes back to the previous menu, or closes the dialog if there is none. Clicking confirm repeatedly is guarded by the menu click debounce.","since":"0.1.0","members":[{"kind":"method","name":"builder","signature":"public static ConfirmMenu builder()","returns":"ConfirmMenu","doc":"Starts a dialog.","params":[],"returnsDoc":"builder","throws":[]},{"kind":"method","name":"title","signature":"public ConfirmMenu title(String template)","returns":"ConfirmMenu","doc":"Sets the title.","params":[{"name":"template","type":"String","doc":"title template"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"confirm","signature":"public ConfirmMenu confirm(ItemStack item, Consumer<ClickContext> action)","returns":"ConfirmMenu","doc":"Sets the confirm button.","params":[{"name":"item","type":"ItemStack","doc":"confirm item"},{"name":"action","type":"Consumer<ClickContext>","doc":"runs when confirmed"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"cancel","signature":"public ConfirmMenu cancel(ItemStack item)","returns":"ConfirmMenu","doc":"Sets the cancel item with the default action (back, or close).","params":[{"name":"item","type":"ItemStack","doc":"cancel item"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"cancel","signature":"public ConfirmMenu cancel(ItemStack item, Consumer<ClickContext> action)","returns":"ConfirmMenu","doc":"Sets the cancel button.","params":[{"name":"item","type":"ItemStack","doc":"cancel item"},{"name":"action","type":"Consumer<ClickContext>","doc":"runs when cancelled"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"info","signature":"public ConfirmMenu info(ItemStack item)","returns":"ConfirmMenu","doc":"Shows an informational item in the centre.","params":[{"name":"item","type":"ItemStack","doc":"info item"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"fill","signature":"public ConfirmMenu fill(Button button)","returns":"ConfirmMenu","doc":"Fills empty slots.","params":[{"name":"button","type":"Button","doc":"filler"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"build","signature":"public Menu build()","returns":"Menu","doc":"Builds the dialog as a 3-row menu (confirm at slot 11, info at 13, cancel at 15).","params":[],"returnsDoc":"menu","throws":[]}]},{"name":"Menu","qualifiedName":"io.github.crownalonedev.bloom.gui.Menu","kind":"class","signature":"public final class Menu","doc":"An immutable chest menu definition. One Menu can be opened by many players at once; each gets its own MenuView with its own page and MenuState. Menu shop = Menu.builder() .title(\"&d&lBlossom Shop\") .rows(3) .layout( \"#########\", \"# a b #\", \"#########\") .bind('#', Button.of(filler)) .bind('a', healButton) .bind('b', feedButton) .build(); Menus.of(bloom).open(player, shop); Titles may use {player}, {page} and {pages} placeholders. On 1.7/1.8 titles are limited to 32 characters including colour codes and are truncated with a warning.","since":"0.1.0","members":[{"kind":"method","name":"builder","signature":"public static Menu.Builder builder()","returns":"Menu.Builder","doc":"Starts a menu definition.","params":[],"returnsDoc":"a builder (defaults: 3 rows, empty title)","throws":[]},{"kind":"method","name":"title","signature":"public String title()","returns":"String","doc":"Returns the title template.","params":[],"returnsDoc":"title","throws":[]},{"kind":"method","name":"rows","signature":"public int rows()","returns":"int","doc":"Returns the number of rows.","params":[],"returnsDoc":"rows, 1-6","throws":[]},{"kind":"method","name":"size","signature":"public int size()","returns":"int","doc":"Returns the slot count.","params":[],"returnsDoc":"rows() * 9","throws":[]},{"kind":"method","name":"isPaginated","signature":"public boolean isPaginated()","returns":"boolean","doc":"Returns whether this menu has pages.","params":[],"returnsDoc":"true if built with Builder.paginate","throws":[]}]},{"name":"Menu.Builder","qualifiedName":"io.github.crownalonedev.bloom.gui.Menu.Builder","kind":"class","signature":"public static final class Builder","doc":"Builder for Menu. Not thread-safe.","members":[{"kind":"method","name":"title","signature":"public Menu.Builder title(String template)","returns":"Menu.Builder","doc":"Sets the title.","params":[{"name":"template","type":"String","doc":"title template; supports {player}, {page}, {pages}"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"rows","signature":"public Menu.Builder rows(int count)","returns":"Menu.Builder","doc":"Sets the number of rows.","params":[{"name":"count","type":"int","doc":"1 to 6"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"format","signature":"public Menu.Builder format(TextFormat textFormat)","returns":"Menu.Builder","doc":"Sets the text format of the title (defaults to the Bloom default format).","params":[{"name":"textFormat","type":"TextFormat","doc":"format"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"button","signature":"public Menu.Builder button(int slot, Button button)","returns":"Menu.Builder","doc":"Places a button, replacing any previous button in the slot.","params":[{"name":"slot","type":"int","doc":"slot index, 0..size-1"},{"name":"button","type":"Button","doc":"button"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"button","signature":"public Menu.Builder button(int row, int column, Button button)","returns":"Menu.Builder","doc":"Places a button by row and column (both zero-based).","params":[{"name":"row","type":"int","doc":"row, 0..rows-1"},{"name":"column","type":"int","doc":"column, 0..8"},{"name":"button","type":"Button","doc":"button"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"item","signature":"public Menu.Builder item(int slot, ItemStack item)","returns":"Menu.Builder","doc":"Places a decorative item.","params":[{"name":"slot","type":"int","doc":"slot index"},{"name":"item","type":"ItemStack","doc":"item"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"border","signature":"public Menu.Builder border(Button button)","returns":"Menu.Builder","doc":"Places a button in every border slot that is still empty (first/last row and first/last column).","params":[{"name":"button","type":"Button","doc":"border button, usually a filler pane"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"fill","signature":"public Menu.Builder fill(Button button)","returns":"Menu.Builder","doc":"Sets a filler used for every slot that ends up empty after buttons, pagination and hidden buttons are resolved. Content slots of paginated menus are only filled when the current page has no item there.","params":[{"name":"button","type":"Button","doc":"filler button"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"layout","signature":"public Menu.Builder layout(String... rowPatterns)","returns":"Menu.Builder","doc":"Describes the menu with one string per row (exactly 9 characters each). Characters are bound to buttons with bind; spaces are empty slots. Also sets the row count.","params":[{"name":"rowPatterns","type":"String[]","doc":"one pattern per row"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"bind","signature":"public Menu.Builder bind(char symbol, Button button)","returns":"Menu.Builder","doc":"Binds a layout character to a button.","params":[{"name":"symbol","type":"char","doc":"layout character"},{"name":"button","type":"Button","doc":"button"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"apply","signature":"public Menu.Builder apply(MenuTemplate template)","returns":"Menu.Builder","doc":"Applies a reusable template.","params":[{"name":"template","type":"MenuTemplate","doc":"template"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"paginate","signature":"public Menu.Builder paginate(Function<MenuView,List<Button>> source)","returns":"Menu.Builder","doc":"Makes the menu paginated. The source is called on every render and returns all content buttons; Bloom slices them into pages.","params":[{"name":"source","type":"Function<MenuView,List<Button>>","doc":"content for a view (the viewer is view.player())"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"contentSlots","signature":"public Menu.Builder contentSlots(int... slots)","returns":"Menu.Builder","doc":"Sets the slots that show paginated content, in order. Default: every slot without a button.","params":[{"name":"slots","type":"int[]","doc":"content slots"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"contentSlots","signature":"public Menu.Builder contentSlots(char symbol)","returns":"Menu.Builder","doc":"Uses every occurrence of a layout character as a content slot.","params":[{"name":"symbol","type":"char","doc":"layout character"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"previousPage","signature":"public Menu.Builder previousPage(int slot, ItemStack item)","returns":"Menu.Builder","doc":"Shows a \"previous page\" button in a slot when a previous page exists.","params":[{"name":"slot","type":"int","doc":"slot index"},{"name":"item","type":"ItemStack","doc":"button item"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"nextPage","signature":"public Menu.Builder nextPage(int slot, ItemStack item)","returns":"Menu.Builder","doc":"Shows a \"next page\" button in a slot when a next page exists.","params":[{"name":"slot","type":"int","doc":"slot index"},{"name":"item","type":"ItemStack","doc":"button item"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"onOpen","signature":"public Menu.Builder onOpen(Consumer<MenuView> handler)","returns":"Menu.Builder","doc":"Runs code after the menu was opened for a player (main thread).","params":[{"name":"handler","type":"Consumer<MenuView>","doc":"handler"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"onClose","signature":"public Menu.Builder onClose(Consumer<MenuView> handler)","returns":"Menu.Builder","doc":"Runs code after the menu was closed for a player, including when navigating to another menu, on disconnect and on plugin shutdown (main thread).","params":[{"name":"handler","type":"Consumer<MenuView>","doc":"handler"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"refreshEvery","signature":"public Menu.Builder refreshEvery(long ticks)","returns":"Menu.Builder","doc":"Re-renders the menu for each viewer every ticks ticks while it is open (e.g. countdowns).","params":[{"name":"ticks","type":"long","doc":"period, >= 1"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"build","signature":"public Menu build()","returns":"Menu","doc":"Builds the menu.","params":[],"returnsDoc":"immutable menu","throws":[]}]},{"name":"MenuState","qualifiedName":"io.github.crownalonedev.bloom.gui.MenuState","kind":"class","signature":"public final class MenuState","doc":"Per-player state of one open menu. It survives refreshes, page changes and back-navigation to the same view, and is discarded when the player closes the menu. Main thread only.","since":"0.1.0","members":[{"kind":"method","name":"get","signature":"public <T> Optional<T> get(StateKey<T> key)","returns":"Optional<T>","doc":"Returns a value.","params":[{"name":"key","type":"StateKey<T>","doc":"key"}],"returnsDoc":"the value, or empty","throws":[]},{"kind":"method","name":"getOrDefault","signature":"public <T> T getOrDefault(StateKey<T> key, T fallback)","returns":"T","doc":"Returns a value or a default.","params":[{"name":"key","type":"StateKey<T>","doc":"key"},{"name":"fallback","type":"T","doc":"value returned when absent"}],"returnsDoc":"the value or fallback","throws":[]},{"kind":"method","name":"set","signature":"public <T> void set(StateKey<T> key, T value)","returns":"void","doc":"Stores a value.","params":[{"name":"key","type":"StateKey<T>","doc":"key"},{"name":"value","type":"T","doc":"value, or null to remove"}],"throws":[]},{"kind":"method","name":"remove","signature":"public void remove(StateKey<?> key)","returns":"void","doc":"Removes a value.","params":[{"name":"key","type":"StateKey<?>","doc":"key"}],"throws":[]},{"kind":"method","name":"has","signature":"public boolean has(StateKey<?> key)","returns":"boolean","doc":"Returns whether a value is present.","params":[{"name":"key","type":"StateKey<?>","doc":"key"}],"returnsDoc":"true if present","throws":[]}]},{"name":"MenuTemplate","qualifiedName":"io.github.crownalonedev.bloom.gui.MenuTemplate","kind":"interface","signature":"public interface MenuTemplate","doc":"A reusable piece of menu layout, applied with Menu.Builder.apply. MenuTemplate branded = builder -> builder .border(Button.of(pinkPane)) .button(4, Button.of(logo)); Menu menu = Menu.builder().rows(5).apply(branded).title(\"&dProfile\").build();","since":"0.1.0","members":[{"kind":"method","name":"apply","signature":"public void apply(Menu.Builder builder)","returns":"void","doc":"Applies this template to a builder.","params":[{"name":"builder","type":"Menu.Builder","doc":"menu builder"}],"throws":[]}]},{"name":"MenuTemplates","qualifiedName":"io.github.crownalonedev.bloom.gui.MenuTemplates","kind":"class","signature":"public final class MenuTemplates","doc":"Ready-made MenuTemplates.","since":"0.1.0","members":[{"kind":"method","name":"border","signature":"public static MenuTemplate border(Button button)","returns":"MenuTemplate","doc":"Fills the border with a button (only empty slots).","params":[{"name":"button","type":"Button","doc":"border button"}],"returnsDoc":"template","throws":[]},{"kind":"method","name":"fill","signature":"public static MenuTemplate fill(Button button)","returns":"MenuTemplate","doc":"Fills every slot that ends up empty.","params":[{"name":"button","type":"Button","doc":"filler button"}],"returnsDoc":"template","throws":[]},{"kind":"method","name":"paged","signature":"public static MenuTemplate paged(int rows, Button border, ItemStack previous, ItemStack next, Button close)","returns":"MenuTemplate","doc":"Standard paginated layout for rows rows: content in the inner area, a border, previous/next buttons in the bottom-left and bottom-right corners and an optional close button in the bottom centre.","params":[{"name":"rows","type":"int","doc":"rows (3-6)"},{"name":"border","type":"Button","doc":"border button"},{"name":"previous","type":"ItemStack","doc":"previous-page item"},{"name":"next","type":"ItemStack","doc":"next-page item"},{"name":"close","type":"Button","doc":"close button, or null"}],"returnsDoc":"template; combine with Menu.Builder.paginate","throws":[]}]},{"name":"MenuView","qualifiedName":"io.github.crownalonedev.bloom.gui.MenuView","kind":"class","signature":"public final class MenuView","doc":"One player's instance of an open Menu: the current page, per-player MenuState and the rendered inventory. Main thread only.","since":"0.1.0","members":[{"kind":"method","name":"player","signature":"public Player player()","returns":"Player","doc":"Returns the viewer.","params":[],"returnsDoc":"player","throws":[]},{"kind":"method","name":"menu","signature":"public Menu menu()","returns":"Menu","doc":"Returns the menu definition.","params":[],"returnsDoc":"menu","throws":[]},{"kind":"method","name":"state","signature":"public MenuState state()","returns":"MenuState","doc":"Returns this view's state.","params":[],"returnsDoc":"state","throws":[]},{"kind":"method","name":"page","signature":"public int page()","returns":"int","doc":"Returns the zero-based current page.","params":[],"returnsDoc":"page","throws":[]},{"kind":"method","name":"pageCount","signature":"public int pageCount()","returns":"int","doc":"Returns the number of pages computed at the last render.","params":[],"returnsDoc":"page count, at least 1","throws":[]},{"kind":"method","name":"hasNextPage","signature":"public boolean hasNextPage()","returns":"boolean","doc":"Returns whether a next page exists.","params":[],"returnsDoc":"true if page() < pageCount() - 1","throws":[]},{"kind":"method","name":"hasPreviousPage","signature":"public boolean hasPreviousPage()","returns":"boolean","doc":"Returns whether a previous page exists.","params":[],"returnsDoc":"true if page() > 0","throws":[]},{"kind":"method","name":"page","signature":"public void page(int newPage)","returns":"void","doc":"Switches to a page (clamped to the valid range) and re-renders.","params":[{"name":"newPage","type":"int","doc":"zero-based page"}],"throws":[]},{"kind":"method","name":"nextPage","signature":"public void nextPage()","returns":"void","doc":"Switches to the next page if there is one.","params":[],"throws":[]},{"kind":"method","name":"previousPage","signature":"public void previousPage()","returns":"void","doc":"Switches to the previous page if there is one.","params":[],"throws":[]},{"kind":"method","name":"refresh","signature":"public void refresh()","returns":"void","doc":"Re-renders the menu for this viewer. Items are updated in place; if the rendered title changed (for example a page number in the title) the inventory is reopened on the next tick, keeping navigation history.","params":[],"throws":[]},{"kind":"method","name":"isOpen","signature":"public boolean isOpen()","returns":"boolean","doc":"Returns whether this view is currently shown to the player.","params":[],"returnsDoc":"true while open","throws":[]},{"kind":"method","name":"inventory","signature":"public Inventory inventory()","returns":"Inventory","doc":"Returns the rendered inventory.","params":[],"returnsDoc":"inventory, or null before the first render","throws":[]}]},{"name":"Menus","qualifiedName":"io.github.crownalonedev.bloom.gui.Menus","kind":"class","signature":"public final class Menus implements AutoCloseable","doc":"Opens and manages menus. Obtain with of; the service registers its own listeners, so no per-menu listener registration is needed. Safety guarantees • Every click and drag while a Bloom menu is open is cancelled (including shift-click, number-key swaps, double-click collection and clicks in the player's own inventory), and the cancellation is re-applied at HIGHEST priority if another plugin un-cancels it. Items cannot be taken out of or put into a menu. • Button handlers run at most once per click and are debounced per player (100 ms by default; see clickDebounce) to stop double-triggered purchases from spam clicking. • On legacy servers the player's inventory is resynchronised after cancelled clicks to remove client-side ghost items. • Sessions are cleaned up on close, disconnect and plugin shutdown; open menus are closed on shutdown. All methods must be called on the main thread.","since":"0.1.0","members":[{"kind":"method","name":"of","signature":"public static Menus of(Bloom bloom)","returns":"Menus","doc":"Returns the menu service of a Bloom instance, creating it (and registering its listeners) on first use.","params":[{"name":"bloom","type":"Bloom","doc":"running Bloom instance"}],"returnsDoc":"the service","throws":[]},{"kind":"method","name":"clickDebounce","signature":"public Menus clickDebounce(Duration debounce)","returns":"Menus","doc":"Sets the minimum time between two handled button clicks of the same player.","params":[{"name":"debounce","type":"Duration","doc":"debounce, Duration.ZERO to disable"}],"returnsDoc":"this service","throws":[]},{"kind":"method","name":"open","signature":"public MenuView open(Player player, Menu menu)","returns":"MenuView","doc":"Opens a menu. If the player is viewing another Bloom menu, it is kept in the history for back. Do not call this from inside a click handler; use ClickContext.open instead.","params":[{"name":"player","type":"Player","doc":"viewer"},{"name":"menu","type":"Menu","doc":"menu to open"}],"returnsDoc":"the player's view; MenuView.isOpen is false if another plugin cancelled the opening","throws":[]},{"kind":"method","name":"back","signature":"public boolean back(Player player)","returns":"boolean","doc":"Returns to the previous menu in the player's history.","params":[{"name":"player","type":"Player","doc":"viewer"}],"returnsDoc":"true if a previous menu was opened","throws":[]},{"kind":"method","name":"close","signature":"public void close(Player player)","returns":"void","doc":"Closes the player's Bloom menu, if one is open.","params":[{"name":"player","type":"Player","doc":"viewer"}],"throws":[]},{"kind":"method","name":"refresh","signature":"public void refresh(Player player)","returns":"void","doc":"Re-renders the player's open menu.","params":[{"name":"player","type":"Player","doc":"viewer"}],"throws":[]},{"kind":"method","name":"refreshAll","signature":"public int refreshAll(Menu menu)","returns":"int","doc":"Re-renders a menu for everyone currently viewing it.","params":[{"name":"menu","type":"Menu","doc":"menu"}],"returnsDoc":"number of refreshed viewers","throws":[]},{"kind":"method","name":"view","signature":"public Optional<MenuView> view(Player player)","returns":"Optional<MenuView>","doc":"Returns the player's open Bloom menu view.","params":[{"name":"player","type":"Player","doc":"player"}],"returnsDoc":"the view, or empty","throws":[]},{"kind":"method","name":"openCount","signature":"public int openCount()","returns":"int","doc":"Returns how many players have a Bloom menu open.","params":[],"returnsDoc":"open view count","throws":[]},{"kind":"method","name":"close","signature":"public void close()","returns":"void","doc":"Closes all open menus and unregisters the listeners. Called automatically when Bloom shuts down.","params":[],"throws":[]}]},{"name":"StateKey","qualifiedName":"io.github.crownalonedev.bloom.gui.StateKey","kind":"class","signature":"public final class StateKey<T>","doc":"A typed key for per-player menu state. static final StateKey<Integer> QUANTITY = StateKey.of(\"quantity\", Integer.class); int quantity = view.state().getOrDefault(QUANTITY, 1); view.state().set(QUANTITY, quantity + 1);","since":"0.1.0","members":[{"kind":"method","name":"of","signature":"public static <T> StateKey<T> of(String name, Class<T> type)","returns":"StateKey<T>","doc":"Creates a key. Keys are compared by identity, so declare them as constants.","params":[{"name":"name","type":"String","doc":"descriptive name used in toString()"},{"name":"type","type":"Class<T>","doc":"value type"}],"returnsDoc":"a new key","throws":[]},{"kind":"method","name":"name","signature":"public String name()","returns":"String","doc":"Returns the key name.","params":[],"returnsDoc":"name","throws":[]},{"kind":"method","name":"type","signature":"public Class<T> type()","returns":"Class<T>","doc":"Returns the value type.","params":[],"returnsDoc":"type","throws":[]},{"kind":"method","name":"toString","signature":"public String toString()","returns":"String","doc":"","params":[],"throws":[]}]}]}]},{"name":"bloom-config","description":"Bloom Config: YAML configuration with defaults, validation, object mapping, comment-preserving saves and migrations.","packages":[{"name":"io.github.crownalonedev.bloom.config","types":[{"name":"ConfigException","qualifiedName":"io.github.crownalonedev.bloom.config.ConfigException","kind":"class","signature":"public class ConfigException extends BloomException","doc":"Thrown for configuration problems: unreadable YAML, missing or mistyped values, failed validation or migration. Messages name the file, the key path and, when known, the line number.","since":"0.1.0","members":[{"kind":"constructor","name":"ConfigException","signature":"public ConfigException(String message)","doc":"Creates an exception with one problem.","params":[{"name":"message","type":"String","doc":"description including file/path/line"}],"throws":[]},{"kind":"constructor","name":"ConfigException","signature":"public ConfigException(String message, Throwable cause)","doc":"Creates an exception with a cause.","params":[{"name":"message","type":"String","doc":"description"},{"name":"cause","type":"Throwable","doc":"cause"}],"throws":[]},{"kind":"constructor","name":"ConfigException","signature":"public ConfigException(String summary, List<String> problems)","doc":"Creates an exception listing several problems (validation).","params":[{"name":"summary","type":"String","doc":"first line of the message"},{"name":"problems","type":"List<String>","doc":"individual problems"}],"throws":[]},{"kind":"method","name":"problems","signature":"public List<String> problems()","returns":"List<String>","doc":"Returns the individual problems.","params":[],"returnsDoc":"problems, at least one","throws":[]}]},{"name":"ConfigKey","qualifiedName":"io.github.crownalonedev.bloom.config.ConfigKey","kind":"annotation","signature":"public abstract @interface ConfigKey implements Annotation","doc":"Overrides the YAML key of a mapped field. Without it, field names are converted from camelCase to kebab-case (maxHomes → max-homes).","since":"0.1.0","members":[{"kind":"method","name":"value","signature":"public abstract String value()","returns":"String","doc":"Returns the YAML key.","params":[],"returnsDoc":"key","throws":[]}]},{"name":"ConfigMigration","qualifiedName":"io.github.crownalonedev.bloom.config.ConfigMigration","kind":"interface","signature":"public interface ConfigMigration","doc":"Upgrades a configuration from one version to the next. Registered with YamlConfig.Builder.migration. YamlConfig config = YamlConfig.builder(file) .defaults(() -> plugin.getResource(\"config.yml\")) .version(3) .migration(1, m -> m.move(\"prefix\", \"messages.prefix\")) // v1 -> v2 .migration(2, m -> m.set(\"storage.type\", m.has(\"mysql\") ? \"mysql\" : \"sqlite\")) // v2 -> v3 .build();","since":"0.1.0","members":[{"kind":"method","name":"migrate","signature":"public void migrate(MigrationContext context)","returns":"void","doc":"Applies the migration to the configuration values.","params":[{"name":"context","type":"MigrationContext","doc":"mutable view of the values being migrated"}],"throws":[]}]},{"name":"ConfigSection","qualifiedName":"io.github.crownalonedev.bloom.config.ConfigSection","kind":"class","signature":"public class ConfigSection","doc":"Typed, path-based access to a section of a YamlConfig. Paths are dot-separated (\"database.host\") and relative to this section. Defaults A value missing from the file falls back to the bundled defaults. Required getters (getString(path)) throw ConfigException when the value is missing in both; getters with a fallback argument return the fallback. Errors Type mismatches throw ConfigException naming the file, path, line and the expected type, e.g. config.yml line 7: 'limits.homes' expected a whole number but found 'ten'. All methods are thread-safe.","since":"0.1.0","members":[{"kind":"method","name":"path","signature":"public String path()","returns":"String","doc":"Returns this section's path from the root.","params":[],"returnsDoc":"path, empty for the root","throws":[]},{"kind":"method","name":"has","signature":"public boolean has(String path)","returns":"boolean","doc":"Returns whether a value exists in the file or the defaults.","params":[{"name":"path","type":"String","doc":"relative path"}],"returnsDoc":"true if present","throws":[]},{"kind":"method","name":"isSet","signature":"public boolean isSet(String path)","returns":"boolean","doc":"Returns whether a value exists in the file itself (ignoring defaults).","params":[{"name":"path","type":"String","doc":"relative path"}],"returnsDoc":"true if set in the file","throws":[]},{"kind":"method","name":"keys","signature":"public Set<String> keys()","returns":"Set<String>","doc":"Returns the keys of this section. If the section exists in the file, only the file's keys are returned (a section a user trimmed is not refilled from defaults); otherwise the defaults' keys.","params":[],"returnsDoc":"keys in file order","throws":[]},{"kind":"method","name":"section","signature":"public ConfigSection section(String path)","returns":"ConfigSection","doc":"Returns a child section.","params":[{"name":"path","type":"String","doc":"relative path"}],"returnsDoc":"the section","throws":[{"type":"ConfigException","doc":"if the path holds a value that is not a section, or is missing"}]},{"kind":"method","name":"optionalSection","signature":"public Optional<ConfigSection> optionalSection(String path)","returns":"Optional<ConfigSection>","doc":"Returns a child section if present.","params":[{"name":"path","type":"String","doc":"relative path"}],"returnsDoc":"the section, or empty","throws":[]},{"kind":"method","name":"getString","signature":"public String getString(String path)","returns":"String","doc":"Returns a required string.","params":[{"name":"path","type":"String","doc":"relative path"}],"returnsDoc":"value","throws":[]},{"kind":"method","name":"getString","signature":"public String getString(String path, String fallback)","returns":"String","doc":"Returns a string or a fallback.","params":[{"name":"path","type":"String","doc":"relative path"},{"name":"fallback","type":"String","doc":"returned when missing"}],"returnsDoc":"value","throws":[]},{"kind":"method","name":"getInt","signature":"public int getInt(String path)","returns":"int","doc":"Returns a required int.","params":[{"name":"path","type":"String","doc":"relative path"}],"returnsDoc":"value","throws":[]},{"kind":"method","name":"getInt","signature":"public int getInt(String path, int fallback)","returns":"int","doc":"Returns an int or a fallback.","params":[{"name":"path","type":"String","doc":"relative path"},{"name":"fallback","type":"int","doc":"returned when missing"}],"returnsDoc":"value","throws":[]},{"kind":"method","name":"getLong","signature":"public long getLong(String path)","returns":"long","doc":"Returns a required long.","params":[{"name":"path","type":"String","doc":"relative path"}],"returnsDoc":"value","throws":[]},{"kind":"method","name":"getLong","signature":"public long getLong(String path, long fallback)","returns":"long","doc":"Returns a long or a fallback.","params":[{"name":"path","type":"String","doc":"relative path"},{"name":"fallback","type":"long","doc":"returned when missing"}],"returnsDoc":"value","throws":[]},{"kind":"method","name":"getDouble","signature":"public double getDouble(String path)","returns":"double","doc":"Returns a required double.","params":[{"name":"path","type":"String","doc":"relative path"}],"returnsDoc":"value","throws":[]},{"kind":"method","name":"getDouble","signature":"public double getDouble(String path, double fallback)","returns":"double","doc":"Returns a double or a fallback.","params":[{"name":"path","type":"String","doc":"relative path"},{"name":"fallback","type":"double","doc":"returned when missing"}],"returnsDoc":"value","throws":[]},{"kind":"method","name":"getBoolean","signature":"public boolean getBoolean(String path)","returns":"boolean","doc":"Returns a required boolean (true/false, also yes/no, on/off).","params":[{"name":"path","type":"String","doc":"relative path"}],"returnsDoc":"value","throws":[]},{"kind":"method","name":"getBoolean","signature":"public boolean getBoolean(String path, boolean fallback)","returns":"boolean","doc":"Returns a boolean or a fallback.","params":[{"name":"path","type":"String","doc":"relative path"},{"name":"fallback","type":"boolean","doc":"returned when missing"}],"returnsDoc":"value","throws":[]},{"kind":"method","name":"getDuration","signature":"public Duration getDuration(String path)","returns":"Duration","doc":"Returns a required duration written like \"30s\", \"1h30m\", or a number of seconds.","params":[{"name":"path","type":"String","doc":"relative path"}],"returnsDoc":"value","throws":[]},{"kind":"method","name":"getDuration","signature":"public Duration getDuration(String path, Duration fallback)","returns":"Duration","doc":"Returns a duration or a fallback.","params":[{"name":"path","type":"String","doc":"relative path"},{"name":"fallback","type":"Duration","doc":"returned when missing"}],"returnsDoc":"value","throws":[]},{"kind":"method","name":"getEnum","signature":"public <E extends Enum<E>> E getEnum(String path, Class<E> type)","returns":"E","doc":"Returns a required enum constant (case-insensitive; - and spaces match _).","params":[{"name":"path","type":"String","doc":"relative path"},{"name":"type","type":"Class<E>","doc":"enum class"}],"returnsDoc":"value","throws":[]},{"kind":"method","name":"getStringList","signature":"public List<String> getStringList(String path)","returns":"List<String>","doc":"Returns a required list of strings. A single scalar is treated as a one-element list.","params":[{"name":"path","type":"String","doc":"relative path"}],"returnsDoc":"unmodifiable list","throws":[]},{"kind":"method","name":"getList","signature":"public <T> List<T> getList(String path, Class<T> elementType)","returns":"List<T>","doc":"Returns a required list with typed elements.","params":[{"name":"path","type":"String","doc":"relative path"},{"name":"elementType","type":"Class<T>","doc":"element type"}],"returnsDoc":"unmodifiable list","throws":[]},{"kind":"method","name":"get","signature":"public <T> T get(String path, Class<T> type)","returns":"T","doc":"Returns a required value converted to any supported type: scalars, enums, UUID, Duration, types with a registered TypeSerializer, and POJOs with a no-argument constructor (fields mapped from kebab-case keys; missing keys keep field defaults).","params":[{"name":"path","type":"String","doc":"relative path"},{"name":"type","type":"Class<T>","doc":"target type"}],"returnsDoc":"value","throws":[]},{"kind":"method","name":"get","signature":"public <T> T get(String path, Class<T> type, T fallback)","returns":"T","doc":"Returns a value converted to a type, or a fallback when missing. Present but invalid values still throw.","params":[{"name":"path","type":"String","doc":"relative path"},{"name":"type","type":"Class<T>","doc":"target type"},{"name":"fallback","type":"T","doc":"returned when missing"}],"returnsDoc":"value","throws":[]},{"kind":"method","name":"as","signature":"public <T> T as(Class<T> type)","returns":"T","doc":"Maps this whole section to a POJO (see get).","params":[{"name":"type","type":"Class<T>","doc":"POJO type"}],"returnsDoc":"mapped object","throws":[]},{"kind":"method","name":"set","signature":"public void set(String path, Object value)","returns":"void","doc":"Sets a value in memory (call YamlConfig.save to write it). Values are serialised with the registered serializers; null removes the key.","params":[{"name":"path","type":"String","doc":"relative path"},{"name":"value","type":"Object","doc":"value"}],"throws":[]},{"kind":"method","name":"remove","signature":"public void remove(String path)","returns":"void","doc":"Removes a value in memory.","params":[{"name":"path","type":"String","doc":"relative path"}],"throws":[]}]},{"name":"ConfigValidation","qualifiedName":"io.github.crownalonedev.bloom.config.ConfigValidation","kind":"class","signature":"public final class ConfigValidation","doc":"Collects validation rules and reports every problem at once. Created by YamlConfig.validate. config.validate() .require(\"database.type\") .oneOf(\"database.type\", \"sqlite\", \"mysql\", \"mariadb\") .range(\"limits.homes\", 1, 100) .type(\"cooldowns.pearl\", java.time.Duration.class) .check(); // throws ConfigException listing all problems with line numbers","since":"0.1.0","members":[{"kind":"method","name":"require","signature":"public ConfigValidation require(String path)","returns":"ConfigValidation","doc":"Requires a value to exist (in the file or defaults).","params":[{"name":"path","type":"String","doc":"path"}],"returnsDoc":"this validation","throws":[]},{"kind":"method","name":"type","signature":"public ConfigValidation type(String path, Class<?> type)","returns":"ConfigValidation","doc":"Requires a present value to convert to a type.","params":[{"name":"path","type":"String","doc":"path"},{"name":"type","type":"Class<?>","doc":"expected type"}],"returnsDoc":"this validation","throws":[]},{"kind":"method","name":"range","signature":"public ConfigValidation range(String path, double min, double max)","returns":"ConfigValidation","doc":"Requires a present numeric value to lie in an inclusive range.","params":[{"name":"path","type":"String","doc":"path"},{"name":"min","type":"double","doc":"minimum"},{"name":"max","type":"double","doc":"maximum"}],"returnsDoc":"this validation","throws":[]},{"kind":"method","name":"oneOf","signature":"public ConfigValidation oneOf(String path, String... allowed)","returns":"ConfigValidation","doc":"Requires a present text value to be one of the allowed values (case-insensitive).","params":[{"name":"path","type":"String","doc":"path"},{"name":"allowed","type":"String[]","doc":"allowed values"}],"returnsDoc":"this validation","throws":[]},{"kind":"method","name":"matches","signature":"public ConfigValidation matches(String path, Pattern pattern, String description)","returns":"ConfigValidation","doc":"Requires a present text value to match a regular expression.","params":[{"name":"path","type":"String","doc":"path"},{"name":"pattern","type":"Pattern","doc":"pattern the whole value must match"},{"name":"description","type":"String","doc":"human description of the expected format"}],"returnsDoc":"this validation","throws":[]},{"kind":"method","name":"check","signature":"public <T> ConfigValidation check(String path, Class<T> type, Predicate<T> rule, String message)","returns":"ConfigValidation","doc":"Adds a custom rule on a present value.","params":[{"name":"path","type":"String","doc":"path"},{"name":"type","type":"Class<T>","doc":"type to convert the value to"},{"name":"rule","type":"Predicate<T>","doc":"condition that must hold"},{"name":"message","type":"String","doc":"problem description if the rule fails, e.g. \"must be a power of two\""}],"returnsDoc":"this validation","throws":[]},{"kind":"method","name":"problems","signature":"public List<String> problems()","returns":"List<String>","doc":"Returns the problems found so far.","params":[],"returnsDoc":"problem descriptions","throws":[]},{"kind":"method","name":"check","signature":"public void check()","returns":"void","doc":"Throws if any rule failed.","params":[],"throws":[{"type":"ConfigException","doc":"listing every problem"}]}]},{"name":"MigrationContext","qualifiedName":"io.github.crownalonedev.bloom.config.MigrationContext","kind":"class","signature":"public final class MigrationContext","doc":"Mutable access to configuration values during a ConfigMigration. Paths are dot-separated.","since":"0.1.0","members":[{"kind":"method","name":"fromVersion","signature":"public int fromVersion()","returns":"int","doc":"Returns the version being migrated from.","params":[],"returnsDoc":"source version; the migration produces fromVersion() + 1","throws":[]},{"kind":"method","name":"has","signature":"public boolean has(String path)","returns":"boolean","doc":"Returns whether a value exists.","params":[{"name":"path","type":"String","doc":"path"}],"returnsDoc":"true if present","throws":[]},{"kind":"method","name":"get","signature":"public Object get(String path)","returns":"Object","doc":"Returns a raw value (string, number, boolean, list or map).","params":[{"name":"path","type":"String","doc":"path"}],"returnsDoc":"value, or null","throws":[]},{"kind":"method","name":"set","signature":"public void set(String path, Object value)","returns":"void","doc":"Sets a raw value; null removes it.","params":[{"name":"path","type":"String","doc":"path"},{"name":"value","type":"Object","doc":"value"}],"throws":[]},{"kind":"method","name":"remove","signature":"public void remove(String path)","returns":"void","doc":"Removes a value.","params":[{"name":"path","type":"String","doc":"path"}],"throws":[]},{"kind":"method","name":"move","signature":"public void move(String from, String to)","returns":"void","doc":"Moves a value (including whole sections). Does nothing if the source is missing; replaces the target.","params":[{"name":"from","type":"String","doc":"source path"},{"name":"to","type":"String","doc":"target path"}],"throws":[]}]},{"name":"TypeSerializer","qualifiedName":"io.github.crownalonedev.bloom.config.TypeSerializer","kind":"interface","signature":"public interface TypeSerializer<T>","doc":"Converts a custom type to and from plain YAML values (strings, numbers, booleans, lists and maps). TypeSerializer<Location> location = new TypeSerializer<Location>() { public Location deserialize(Object raw) { String[] p = raw.toString().split(\",\"); return new Location(Bukkit.getWorld(p[0]), Double.parseDouble(p[1]), ...); } public Object serialize(Location value) { return value.getWorld().getName() + \",\" + value.getX() + ...; } }; YamlConfig config = YamlConfig.builder(path).serializer(Location.class, location).build();","since":"0.1.0","members":[{"kind":"method","name":"deserialize","signature":"public T deserialize(Object raw)","returns":"T","doc":"Converts a YAML value to the type.","params":[{"name":"raw","type":"Object","doc":"value as loaded from YAML (never null)"}],"returnsDoc":"converted value","throws":[{"type":"IllegalArgumentException","doc":"with a short reason if the value cannot be converted; Bloom adds the file, key and line to the message"}]},{"kind":"method","name":"serialize","signature":"public Object serialize(T value)","returns":"Object","doc":"Converts the type to a YAML value.","params":[{"name":"value","type":"T","doc":"value (never null)"}],"returnsDoc":"a string, number, boolean, list or map","throws":[]}]},{"name":"YamlConfig","qualifiedName":"io.github.crownalonedev.bloom.config.YamlConfig","kind":"class","signature":"public final class YamlConfig extends ConfigSection","doc":"A YAML configuration file with bundled defaults, typed access, validation, migrations and safe writes. YamlConfig config = YamlConfig.builder(getDataFolder().toPath().resolve(\"config.yml\")) .defaults(() -> getResource(\"config.yml\")) .logger(getLogger()) .build(); // creates the file from defaults if missing, then loads it String prefix = config.getString(\"messages.prefix\"); int maxHomes = config.getInt(\"homes.max\", 3); Settings s = config.get(\"settings\", Settings.class); // POJO mapping Safety • A missing file is created by copying the defaults resource byte-for-byte (comments included). • Existing files are never rewritten implicitly, except by migrations (which first copy the file to a timestamped .bak) and by Builder.addMissingDefaults (opt-in, also backed up). • save and migrations edit the file's YAML node tree, so comments and formatting of untouched keys are preserved. Writes are atomic (temporary file + move). • reload keeps the previous values if the new file is invalid. • A file whose version is newer than the plugin supports is rejected rather than downgraded. Values are loaded with SnakeYAML's SafeConstructor (no arbitrary object construction), duplicate keys are rejected and alias expansion is limited. Thread-safe.","since":"0.1.0","members":[{"kind":"method","name":"builder","signature":"public static YamlConfig.Builder builder(Path file)","returns":"YamlConfig.Builder","doc":"Starts configuring a YAML file.","params":[{"name":"file","type":"Path","doc":"path of the config file"}],"returnsDoc":"builder","throws":[]},{"kind":"method","name":"file","signature":"public Path file()","returns":"Path","doc":"Returns the file.","params":[],"returnsDoc":"path","throws":[]},{"kind":"method","name":"version","signature":"public int version()","returns":"int","doc":"Returns the configured target version, or 0 when versioning is not used.","params":[],"returnsDoc":"version","throws":[]},{"kind":"method","name":"reload","signature":"public void reload()","returns":"void","doc":"Re-reads the file. On failure the previous values stay active and the exception is thrown.","params":[],"throws":[{"type":"ConfigException","doc":"if the file is invalid or a migration fails"}]},{"kind":"method","name":"save","signature":"public void save()","returns":"void","doc":"Writes the current values to the file, preserving comments and formatting of unchanged keys. Atomic.","params":[],"throws":[{"type":"ConfigException","doc":"on I/O failure"}]},{"kind":"method","name":"validate","signature":"public ConfigValidation validate()","returns":"ConfigValidation","doc":"Starts a validation of the current values.","params":[],"returnsDoc":"validation builder; call ConfigValidation.check to throw on problems","throws":[]},{"kind":"method","name":"toString","signature":"public String toString()","returns":"String","doc":"","params":[],"throws":[]}]},{"name":"YamlConfig.Builder","qualifiedName":"io.github.crownalonedev.bloom.config.YamlConfig.Builder","kind":"class","signature":"public static final class Builder","doc":"Builder for YamlConfig.","members":[{"kind":"method","name":"defaults","signature":"public YamlConfig.Builder defaults(Supplier<InputStream> source)","returns":"YamlConfig.Builder","doc":"Sets the bundled defaults, usually () -> plugin.getResource(\"config.yml\"). The stream is read once per load and closed.","params":[{"name":"source","type":"Supplier<InputStream>","doc":"supplies the defaults YAML; returning null is reported as a missing resource"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"version","signature":"public YamlConfig.Builder version(int currentVersion)","returns":"YamlConfig.Builder","doc":"Enables versioned migrations with the key config-version.","params":[{"name":"currentVersion","type":"int","doc":"version this plugin writes, >= 1"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"version","signature":"public YamlConfig.Builder version(String key, int currentVersion)","returns":"YamlConfig.Builder","doc":"Enables versioned migrations with a custom key. Files without the key are treated as version 1.","params":[{"name":"key","type":"String","doc":"version key at the top level"},{"name":"currentVersion","type":"int","doc":"version this plugin writes, >= 1"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"migration","signature":"public YamlConfig.Builder migration(int fromVersion, ConfigMigration migration)","returns":"YamlConfig.Builder","doc":"Registers the migration from fromVersion to fromVersion + 1.","params":[{"name":"fromVersion","type":"int","doc":"source version"},{"name":"migration","type":"ConfigMigration","doc":"migration"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"addMissingDefaults","signature":"public YamlConfig.Builder addMissingDefaults(boolean enabled)","returns":"YamlConfig.Builder","doc":"Writes settings that exist in the defaults but not in the file into the file on load (with their default comments). The file is backed up first. Off by default.","params":[{"name":"enabled","type":"boolean","doc":"flag"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"logger","signature":"public YamlConfig.Builder logger(Logger target)","returns":"YamlConfig.Builder","doc":"Sets a logger for informational messages (file created, migrated).","params":[{"name":"target","type":"Logger","doc":"logger, usually plugin.getLogger()"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"serializer","signature":"public <T> YamlConfig.Builder serializer(Class<T> type, TypeSerializer<T> serializer)","returns":"YamlConfig.Builder","doc":"Registers a serializer for a custom type.","params":[{"name":"type","type":"Class<T>","doc":"type"},{"name":"serializer","type":"TypeSerializer<T>","doc":"serializer"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"build","signature":"public YamlConfig build()","returns":"YamlConfig","doc":"Creates the config and loads it (creating the file from defaults if needed and running migrations).","params":[],"returnsDoc":"the loaded config","throws":[{"type":"ConfigException","doc":"if loading fails"}]}]}]}]},{"name":"bloom-storage","description":"Bloom Storage: asynchronous JDBC (SQLite, MySQL, MariaDB) with pooling, transactions, migrations and repositories.","packages":[{"name":"io.github.crownalonedev.bloom.storage","types":[{"name":"BlockingDatabase","qualifiedName":"io.github.crownalonedev.bloom.storage.BlockingDatabase","kind":"class","signature":"public final class BlockingDatabase implements SqlOperations","doc":"Synchronous database access for code that already runs off the main thread (for example inside scheduler().async(...) or a CompletableFuture chain). Obtain with Database.blocking. Every method throws IllegalStateException when called on the main server thread (as reported by the mainThreadCheck given to the database builder), because blocking JDBC calls there freeze the server.","since":"0.1.0","members":[{"kind":"method","name":"update","signature":"public int update(String sql, Object... params)","returns":"int","doc":"","params":[{"name":"sql","type":"String","doc":""},{"name":"params","type":"Object[]","doc":""}],"throws":[]},{"kind":"method","name":"insert","signature":"public Optional<Long> insert(String sql, Object... params)","returns":"Optional<Long>","doc":"","params":[{"name":"sql","type":"String","doc":""},{"name":"params","type":"Object[]","doc":""}],"throws":[]},{"kind":"method","name":"batch","signature":"public int[] batch(String sql, List<Object[]> rows)","returns":"int[]","doc":"","params":[{"name":"sql","type":"String","doc":""},{"name":"rows","type":"List<Object[]>","doc":""}],"throws":[]},{"kind":"method","name":"query","signature":"public <T> List<T> query(String sql, RowMapper<T> mapper, Object... params)","returns":"List<T>","doc":"","params":[{"name":"sql","type":"String","doc":""},{"name":"mapper","type":"RowMapper<T>","doc":""},{"name":"params","type":"Object[]","doc":""}],"throws":[]},{"kind":"method","name":"queryOne","signature":"public <T> Optional<T> queryOne(String sql, RowMapper<T> mapper, Object... params)","returns":"Optional<T>","doc":"","params":[{"name":"sql","type":"String","doc":""},{"name":"mapper","type":"RowMapper<T>","doc":""},{"name":"params","type":"Object[]","doc":""}],"throws":[]}]},{"name":"Database","qualifiedName":"io.github.crownalonedev.bloom.storage.Database","kind":"class","signature":"public final class Database implements Terminable","doc":"A pooled SQL database with an asynchronous API. Database db = Database.builder() .sqlite(getDataFolder().toPath().resolve(\"data.db\")) .mainThreadCheck(bloom.scheduler()::isPrimaryThread) .logger(getLogger()) .build(); db.migrate(migrations).join(); // acceptable in onEnable: the server is not ticking yet db.queryOne(\"SELECT kills FROM stats WHERE uuid = ?\", rs -> rs.getInt(1), player.getUniqueId()) .thenAcceptAsync(kills -> player.sendMessage(\"Kills: \" + kills.orElse(0)), bloom.scheduler().mainThreadExecutor()); // back on the main thread Threading Operations run on a bounded pool of database threads (one per connection). Returned futures complete on those threads: switch to the main thread with thenAcceptAsync(..., mainThreadExecutor()) before touching game state. When the queue is full (default 1000 pending operations) new operations fail immediately with StorageException instead of piling up unbounded work. Shutdown close stops accepting work, waits for queued operations (default up to 10 s), fails any operation that could not run, and closes the pool. Call it in onDisable; when a Bloom instance owns it (registered in the service registry) Bloom closes it automatically.","since":"0.1.0","members":[{"kind":"method","name":"builder","signature":"public static Database.Builder builder()","returns":"Database.Builder","doc":"Starts configuring a database.","params":[],"returnsDoc":"builder","throws":[]},{"kind":"method","name":"dialect","signature":"public Dialect dialect()","returns":"Dialect","doc":"Returns the dialect.","params":[],"returnsDoc":"dialect","throws":[]},{"kind":"method","name":"blocking","signature":"public BlockingDatabase blocking()","returns":"BlockingDatabase","doc":"Returns synchronous access for code already running off the main thread.","params":[],"returnsDoc":"blocking API","throws":[]},{"kind":"method","name":"pendingOperations","signature":"public int pendingOperations()","returns":"int","doc":"Returns the number of queued operations that have not started yet.","params":[],"returnsDoc":"queue size","throws":[]},{"kind":"method","name":"update","signature":"public CompletableFuture<Integer> update(String sql, Object... params)","returns":"CompletableFuture<Integer>","doc":"Executes an INSERT/UPDATE/DELETE/DDL statement asynchronously.","params":[{"name":"sql","type":"String","doc":"statement"},{"name":"params","type":"Object[]","doc":"parameters (see SqlOperations for supported types)"}],"returnsDoc":"future with the affected row count","throws":[]},{"kind":"method","name":"insert","signature":"public CompletableFuture<Optional<Long>> insert(String sql, Object... params)","returns":"CompletableFuture<Optional<Long>>","doc":"Executes an INSERT asynchronously and returns the generated key.","params":[{"name":"sql","type":"String","doc":"statement"},{"name":"params","type":"Object[]","doc":"parameters"}],"returnsDoc":"future with the generated key, if any","throws":[]},{"kind":"method","name":"batch","signature":"public CompletableFuture<int[]> batch(String sql, List<Object[]> rows)","returns":"CompletableFuture<int[]>","doc":"Executes a JDBC batch asynchronously.","params":[{"name":"sql","type":"String","doc":"statement"},{"name":"rows","type":"List<Object[]>","doc":"parameter rows"}],"returnsDoc":"future with per-row update counts","throws":[]},{"kind":"method","name":"query","signature":"public <T> CompletableFuture<List<T>> query(String sql, RowMapper<T> mapper, Object... params)","returns":"CompletableFuture<List<T>>","doc":"Runs a query asynchronously.","params":[{"name":"sql","type":"String","doc":"query"},{"name":"mapper","type":"RowMapper<T>","doc":"row mapper (runs on a database thread)"},{"name":"params","type":"Object[]","doc":"parameters"}],"returnsDoc":"future with all mapped rows","throws":[]},{"kind":"method","name":"queryOne","signature":"public <T> CompletableFuture<Optional<T>> queryOne(String sql, RowMapper<T> mapper, Object... params)","returns":"CompletableFuture<Optional<T>>","doc":"Runs a single-row query asynchronously.","params":[{"name":"sql","type":"String","doc":"query"},{"name":"mapper","type":"RowMapper<T>","doc":"row mapper (runs on a database thread)"},{"name":"params","type":"Object[]","doc":"parameters"}],"returnsDoc":"future with the first row, or empty","throws":[]},{"kind":"method","name":"transaction","signature":"public <T> CompletableFuture<T> transaction(Function<Transaction,T> work)","returns":"CompletableFuture<T>","doc":"Runs work in a transaction and returns its result. Commits when work returns, rolls back if it throws.","params":[{"name":"work","type":"Function<Transaction,T>","doc":"transaction body (runs on a database thread)"}],"returnsDoc":"future with the result","throws":[]},{"kind":"method","name":"inTransaction","signature":"public CompletableFuture<Void> inTransaction(Consumer<Transaction> work)","returns":"CompletableFuture<Void>","doc":"Runs work without a result in a transaction.","params":[{"name":"work","type":"Consumer<Transaction>","doc":"transaction body (runs on a database thread)"}],"returnsDoc":"future completed when committed","throws":[]},{"kind":"method","name":"withConnection","signature":"public <T> CompletableFuture<T> withConnection(Database.ConnectionWork<T> work)","returns":"CompletableFuture<T>","doc":"Gives raw JDBC access on a database thread. The connection is returned to the pool afterwards.","params":[{"name":"work","type":"Database.ConnectionWork<T>","doc":"connection callback"}],"returnsDoc":"future with the result","throws":[]},{"kind":"method","name":"migrate","signature":"public CompletableFuture<MigrationResult> migrate(List<Migration> migrations)","returns":"CompletableFuture<MigrationResult>","doc":"Applies pending migrations in version order, recording them in the history table (default bloom_schema_history; configurable with Builder.migrationTable).","params":[{"name":"migrations","type":"List<Migration>","doc":"all migrations of the plugin"}],"returnsDoc":"future with the result; fails if a migration fails or the database is newer than the migrations","throws":[]},{"kind":"method","name":"close","signature":"public void close()","returns":"void","doc":"Stops accepting operations, waits for queued work, fails work that could not run and closes the pool. Idempotent. Blocks for at most the configured shutdown timeout.","params":[],"throws":[]},{"kind":"method","name":"isClosed","signature":"public boolean isClosed()","returns":"boolean","doc":"","params":[],"throws":[]}]},{"name":"Database.Builder","qualifiedName":"io.github.crownalonedev.bloom.storage.Database.Builder","kind":"class","signature":"public static final class Builder","doc":"Builder for Database.","members":[{"kind":"method","name":"sqlite","signature":"public Database.Builder sqlite(Path file)","returns":"Database.Builder","doc":"Uses an SQLite file. Parent directories are created. The pool has one connection by default, which serialises writes and avoids SQLITE_BUSY.","params":[{"name":"file","type":"Path","doc":"database file"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"mysql","signature":"public Database.Builder mysql(String host, int port, String database, String user, String pass)","returns":"Database.Builder","doc":"Uses MySQL.","params":[{"name":"host","type":"String","doc":"host name"},{"name":"port","type":"int","doc":"port, usually 3306"},{"name":"database","type":"String","doc":"schema name"},{"name":"user","type":"String","doc":"user name"},{"name":"pass","type":"String","doc":"password"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"mariadb","signature":"public Database.Builder mariadb(String host, int port, String database, String user, String pass)","returns":"Database.Builder","doc":"Uses MariaDB (shade org.mariadb.jdbc:mariadb-java-client into your plugin).","params":[{"name":"host","type":"String","doc":"host name"},{"name":"port","type":"int","doc":"port, usually 3306"},{"name":"database","type":"String","doc":"schema name"},{"name":"user","type":"String","doc":"user name"},{"name":"pass","type":"String","doc":"password"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"jdbcUrl","signature":"public Database.Builder jdbcUrl(String url, String user, String pass, Dialect type)","returns":"Database.Builder","doc":"Uses a custom JDBC URL.","params":[{"name":"url","type":"String","doc":"JDBC URL"},{"name":"user","type":"String","doc":"user name, may be null"},{"name":"pass","type":"String","doc":"password, may be null"},{"name":"type","type":"Dialect","doc":"dialect of the database"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"poolSize","signature":"public Database.Builder poolSize(int size)","returns":"Database.Builder","doc":"Sets the number of connections and database threads (default 1 for SQLite, 8 otherwise).","params":[{"name":"size","type":"int","doc":"pool size, >= 1"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"queueCapacity","signature":"public Database.Builder queueCapacity(int capacity)","returns":"Database.Builder","doc":"Sets the maximum number of queued operations before new ones fail fast (default 1000).","params":[{"name":"capacity","type":"int","doc":"queue capacity"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"connectionTimeout","signature":"public Database.Builder connectionTimeout(Duration timeout)","returns":"Database.Builder","doc":"Sets how long to wait for a free connection (default 10 s).","params":[{"name":"timeout","type":"Duration","doc":"timeout"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"maxLifetime","signature":"public Database.Builder maxLifetime(Duration lifetime)","returns":"Database.Builder","doc":"Sets the maximum lifetime of pooled connections (default 30 min; keep it below the server's wait_timeout).","params":[{"name":"lifetime","type":"Duration","doc":"lifetime"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"shutdownTimeout","signature":"public Database.Builder shutdownTimeout(Duration timeout)","returns":"Database.Builder","doc":"Sets how long Database.close waits for queued work (default 10 s).","params":[{"name":"timeout","type":"Duration","doc":"timeout"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"poolName","signature":"public Database.Builder poolName(String name)","returns":"Database.Builder","doc":"Sets the pool and thread name prefix (default \"Bloom\"); use your plugin name.","params":[{"name":"name","type":"String","doc":"pool name"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"migrationTable","signature":"public Database.Builder migrationTable(String table)","returns":"Database.Builder","doc":"Sets the migration history table (default bloom_schema_history). Plugins sharing one MySQL schema must use different tables, e.g. \"myplugin_schema_history\".","params":[{"name":"table","type":"String","doc":"table name"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"driverClassName","signature":"public Database.Builder driverClassName(String className)","returns":"Database.Builder","doc":"Forces a JDBC driver class instead of auto-detection.","params":[{"name":"className","type":"String","doc":"driver class"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"property","signature":"public Database.Builder property(String key, String value)","returns":"Database.Builder","doc":"Adds a driver/data-source property (e.g. useSSL=true).","params":[{"name":"key","type":"String","doc":"property"},{"name":"value","type":"String","doc":"value"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"mainThreadCheck","signature":"public Database.Builder mainThreadCheck(BooleanSupplier check)","returns":"Database.Builder","doc":"Tells the database how to detect the main server thread, so BlockingDatabase can refuse to block it. With Bloom: bloom.scheduler()::isPrimaryThread.","params":[{"name":"check","type":"BooleanSupplier","doc":"returns true on the main thread"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"logger","signature":"public Database.Builder logger(Logger target)","returns":"Database.Builder","doc":"Sets the logger for migrations and shutdown messages.","params":[{"name":"target","type":"Logger","doc":"logger, usually plugin.getLogger()"}],"returnsDoc":"this builder","throws":[]},{"kind":"method","name":"build","signature":"public Database build()","returns":"Database","doc":"Opens the pool. Fails fast if the database is unreachable.","params":[],"returnsDoc":"the database","throws":[{"type":"StorageException","doc":"with a diagnostic message if the pool cannot start"}]}]},{"name":"Database.ConnectionWork","qualifiedName":"io.github.crownalonedev.bloom.storage.Database.ConnectionWork","kind":"interface","signature":"public static interface ConnectionWork<T>","doc":"Raw JDBC callback for withConnection.","members":[{"kind":"method","name":"apply","signature":"public T apply(Connection connection) throws SQLException","returns":"T","doc":"Uses the connection.","params":[{"name":"connection","type":"Connection","doc":"pooled connection; do not close it"}],"returnsDoc":"result","throws":[{"type":"SQLException","doc":"on JDBC errors"}]}]},{"name":"Dialect","qualifiedName":"io.github.crownalonedev.bloom.storage.Dialect","kind":"enum","signature":"public final enum Dialect","doc":"SQL dialects Bloom knows about. Used for driver defaults and small SQL helpers; Bloom does not rewrite your SQL.","since":"0.1.0","members":[{"kind":"method","name":"values","signature":"public static Dialect[] values()","returns":"Dialect[]","doc":"","params":[],"throws":[]},{"kind":"method","name":"valueOf","signature":"public static Dialect valueOf(String name)","returns":"Dialect","doc":"","params":[{"name":"name","type":"String","doc":""}],"throws":[]},{"kind":"enumConstant","name":"SQLITE","signature":"SQLITE","doc":"SQLite (file database).","params":[],"throws":[]},{"kind":"enumConstant","name":"MYSQL","signature":"MYSQL","doc":"MySQL. Bloom tries Connector/J 8+ first, then the 5.x driver bundled with legacy servers.","params":[],"throws":[]},{"kind":"enumConstant","name":"MARIADB","signature":"MARIADB","doc":"MariaDB (Connector/J must be shaded into your plugin).","params":[],"throws":[]},{"kind":"method","name":"driverClasses","signature":"public List<String> driverClasses()","returns":"List<String>","doc":"Returns candidate JDBC driver classes, in preference order.","params":[],"returnsDoc":"driver class names","throws":[]},{"kind":"method","name":"upsert","signature":"public String upsert(String table, List<String> columns, List<String> keyColumns)","returns":"String","doc":"Builds an insert-or-update statement with ? placeholders for every column, in order. • SQLite: INSERT OR REPLACE (works on the old SQLite bundled with 1.8 servers; note that it deletes and re-inserts the row, so ON DELETE CASCADE children are removed) • MySQL/MariaDB: INSERT ... ON DUPLICATE KEY UPDATE","params":[{"name":"table","type":"String","doc":"table name"},{"name":"columns","type":"List<String>","doc":"all columns, primary key columns included"},{"name":"keyColumns","type":"List<String>","doc":"primary/unique key columns (not updated on conflict)"}],"returnsDoc":"SQL","throws":[]}]},{"name":"Migration","qualifiedName":"io.github.crownalonedev.bloom.storage.Migration","kind":"class","signature":"public final class Migration","doc":"A numbered schema change, applied once per database by Database.migrate. List<Migration> migrations = Arrays.asList( Migration.of(1, \"create stats\", \"CREATE TABLE IF NOT EXISTS stats (uuid VARCHAR(36) PRIMARY KEY, kills INT NOT NULL DEFAULT 0)\"), Migration.of(2, \"add deaths\", tx -> tx.update(\"ALTER TABLE stats ADD COLUMN deaths INT NOT NULL DEFAULT 0\"))); database.migrate(migrations).join(); // blocking is acceptable in onEnable, before players join Each migration runs in its own transaction. Note that MySQL/MariaDB commit DDL statements immediately, so a failing multi-statement migration can leave earlier statements applied there; keep migrations small.","since":"0.1.0","members":[{"kind":"method","name":"of","signature":"public static Migration of(int version, String description, String... statements)","returns":"Migration","doc":"Creates a migration from SQL statements.","params":[{"name":"version","type":"int","doc":"unique positive version"},{"name":"description","type":"String","doc":"short description stored in the history table"},{"name":"statements","type":"String[]","doc":"statements to execute in order"}],"returnsDoc":"migration","throws":[]},{"kind":"method","name":"of","signature":"public static Migration of(int version, String description, Migration.Step step)","returns":"Migration","doc":"Creates a migration with custom logic (e.g. dialect-specific SQL or data transformation).","params":[{"name":"version","type":"int","doc":"unique positive version"},{"name":"description","type":"String","doc":"short description"},{"name":"step","type":"Migration.Step","doc":"migration body"}],"returnsDoc":"migration","throws":[]},{"kind":"method","name":"version","signature":"public int version()","returns":"int","doc":"Returns the version.","params":[],"returnsDoc":"version","throws":[]},{"kind":"method","name":"description","signature":"public String description()","returns":"String","doc":"Returns the description.","params":[],"returnsDoc":"description","throws":[]}]},{"name":"Migration.Step","qualifiedName":"io.github.crownalonedev.bloom.storage.Migration.Step","kind":"interface","signature":"public static interface Step","doc":"Migration body.","members":[{"kind":"method","name":"apply","signature":"public void apply(Transaction transaction)","returns":"void","doc":"Applies the change.","params":[{"name":"transaction","type":"Transaction","doc":"transaction to execute statements in"}],"throws":[]}]},{"name":"MigrationResult","qualifiedName":"io.github.crownalonedev.bloom.storage.MigrationResult","kind":"class","signature":"public final class MigrationResult","doc":"Outcome of Database.migrate.","since":"0.1.0","members":[{"kind":"method","name":"previousVersion","signature":"public int previousVersion()","returns":"int","doc":"Returns the schema version before migrating.","params":[],"returnsDoc":"version, 0 for a new database","throws":[]},{"kind":"method","name":"currentVersion","signature":"public int currentVersion()","returns":"int","doc":"Returns the schema version after migrating.","params":[],"returnsDoc":"version","throws":[]},{"kind":"method","name":"applied","signature":"public List<Integer> applied()","returns":"List<Integer>","doc":"Returns the versions applied by this run.","params":[],"returnsDoc":"applied versions in order","throws":[]},{"kind":"method","name":"toString","signature":"public String toString()","returns":"String","doc":"","params":[],"throws":[]}]},{"name":"Repository","qualifiedName":"io.github.crownalonedev.bloom.storage.Repository","kind":"interface","signature":"public interface Repository<K, V>","doc":"A minimal asynchronous repository. Bloom provides SqlRepository as a simple SQL implementation; implement this interface yourself for other stores (files, Redis, ...) so callers do not depend on the storage technology.","since":"0.1.0","members":[{"kind":"method","name":"find","signature":"public CompletableFuture<Optional<V>> find(K key)","returns":"CompletableFuture<Optional<V>>","doc":"Loads a value.","params":[{"name":"key","type":"K","doc":"key"}],"returnsDoc":"future with the value, or empty","throws":[]},{"kind":"method","name":"findAll","signature":"public CompletableFuture<List<V>> findAll()","returns":"CompletableFuture<List<V>>","doc":"Loads all values. Avoid on large tables.","params":[],"returnsDoc":"future with all values","throws":[]},{"kind":"method","name":"save","signature":"public CompletableFuture<Void> save(V value)","returns":"CompletableFuture<Void>","doc":"Inserts or updates a value.","params":[{"name":"value","type":"V","doc":"value"}],"returnsDoc":"future completed when saved","throws":[]},{"kind":"method","name":"delete","signature":"public CompletableFuture<Boolean> delete(K key)","returns":"CompletableFuture<Boolean>","doc":"Deletes a value.","params":[{"name":"key","type":"K","doc":"key"}],"returnsDoc":"future with true if a value was deleted","throws":[]}]},{"name":"RowMapper","qualifiedName":"io.github.crownalonedev.bloom.storage.RowMapper","kind":"interface","signature":"public interface RowMapper<T>","doc":"Maps the current row of a ResultSet to an object. Do not call next() inside the mapper. RowMapper<PlayerStats> mapper = rs -> new PlayerStats( UUID.fromString(rs.getString(\"uuid\")), rs.getInt(\"kills\"), rs.getInt(\"deaths\"));","since":"0.1.0","members":[{"kind":"method","name":"map","signature":"public T map(ResultSet row) throws SQLException","returns":"T","doc":"Maps the current row.","params":[{"name":"row","type":"ResultSet","doc":"result set positioned on a row"}],"returnsDoc":"mapped object","throws":[{"type":"SQLException","doc":"on column access errors"}]}]},{"name":"SqlOperations","qualifiedName":"io.github.crownalonedev.bloom.storage.SqlOperations","kind":"interface","signature":"public interface SqlOperations","doc":"Synchronous SQL operations. Implemented by Transaction (one connection, inside Database.transaction) and BlockingDatabase (for code already running off the main thread). Parameters ? placeholders are bound in order. Supported parameter types: null, String, numbers, Boolean, byte[], UUID (stored as its 36-character string), enums (stored by name()), java.time.Instant (stored as epoch milliseconds, a BIGINT), Duration (milliseconds) and any type the JDBC driver accepts in setObject.","since":"0.1.0","members":[{"kind":"method","name":"update","signature":"public int update(String sql, Object... params)","returns":"int","doc":"Executes an INSERT, UPDATE, DELETE or DDL statement.","params":[{"name":"sql","type":"String","doc":"statement with ? placeholders"},{"name":"params","type":"Object[]","doc":"parameter values"}],"returnsDoc":"affected row count","throws":[{"type":"StorageException","doc":"on failure"}]},{"kind":"method","name":"insert","signature":"public Optional<Long> insert(String sql, Object... params)","returns":"Optional<Long>","doc":"Executes an INSERT and returns the generated key.","params":[{"name":"sql","type":"String","doc":"insert statement"},{"name":"params","type":"Object[]","doc":"parameter values"}],"returnsDoc":"generated key, or empty if the statement generated none","throws":[{"type":"StorageException","doc":"on failure"}]},{"kind":"method","name":"batch","signature":"public int[] batch(String sql, List<Object[]> rows)","returns":"int[]","doc":"Executes a statement once per parameter row, as a JDBC batch.","params":[{"name":"sql","type":"String","doc":"statement with ? placeholders"},{"name":"rows","type":"List<Object[]>","doc":"parameter rows"}],"returnsDoc":"affected row counts per row (drivers may report -2, \"success, count unknown\")","throws":[{"type":"StorageException","doc":"on failure"}]},{"kind":"method","name":"query","signature":"public <T> List<T> query(String sql, RowMapper<T> mapper, Object... params)","returns":"List<T>","doc":"Runs a query and maps every row.","params":[{"name":"sql","type":"String","doc":"query"},{"name":"mapper","type":"RowMapper<T>","doc":"row mapper"},{"name":"params","type":"Object[]","doc":"parameter values"}],"returnsDoc":"mapped rows, possibly empty","throws":[{"type":"StorageException","doc":"on failure"}]},{"kind":"method","name":"queryOne","signature":"public <T> Optional<T> queryOne(String sql, RowMapper<T> mapper, Object... params)","returns":"Optional<T>","doc":"Runs a query expected to return at most one row.","params":[{"name":"sql","type":"String","doc":"query"},{"name":"mapper","type":"RowMapper<T>","doc":"row mapper"},{"name":"params","type":"Object[]","doc":"parameter values"}],"returnsDoc":"the first row, or empty","throws":[{"type":"StorageException","doc":"on failure"}]}]},{"name":"SqlRepository","qualifiedName":"io.github.crownalonedev.bloom.storage.SqlRepository","kind":"abstract class","signature":"public abstract class SqlRepository<K, V> implements Repository<K,V>","doc":"A Repository for one table with a single-column primary key. Subclasses describe the mapping; Bloom generates the SQL. Table and column names must be plain identifiers (they are validated to prevent accidental SQL injection through names). final class StatsRepository extends SqlRepository<UUID, PlayerStats> { StatsRepository(Database db) { super(db, \"player_stats\", \"uuid\", Arrays.asList(\"uuid\", \"kills\", \"deaths\"), rs -> new PlayerStats(UUID.fromString(rs.getString(\"uuid\")), rs.getInt(\"kills\"), rs.getInt(\"deaths\"))); } protected UUID keyOf(PlayerStats s) { return s.uuid(); } protected Object[] valuesOf(PlayerStats s) { return new Object[] {s.uuid(), s.kills(), s.deaths()}; } }","since":"0.1.0","members":[{"kind":"constructor","name":"SqlRepository","signature":"protected SqlRepository(Database database, String table, String keyColumn, List<String> columns, RowMapper<V> mapper)","doc":"Creates the repository.","params":[{"name":"database","type":"Database","doc":"database"},{"name":"table","type":"String","doc":"table name"},{"name":"keyColumn","type":"String","doc":"primary key column"},{"name":"columns","type":"List<String>","doc":"all columns including the key, in the order of valuesOf"},{"name":"mapper","type":"RowMapper<V>","doc":"maps a row to a value"}],"throws":[]},{"kind":"method","name":"keyOf","signature":"protected abstract K keyOf(V value)","returns":"K","doc":"Returns the key of a value.","params":[{"name":"value","type":"V","doc":"value"}],"returnsDoc":"key","throws":[]},{"kind":"method","name":"valuesOf","signature":"protected abstract Object[] valuesOf(V value)","returns":"Object[]","doc":"Returns the column values of a value, in the order of the constructor's columns.","params":[{"name":"value","type":"V","doc":"value"}],"returnsDoc":"column values","throws":[]},{"kind":"method","name":"database","signature":"protected Database database()","returns":"Database","doc":"Returns the database.","params":[],"returnsDoc":"database","throws":[]},{"kind":"method","name":"table","signature":"protected String table()","returns":"String","doc":"Returns the table name.","params":[],"returnsDoc":"table","throws":[]},{"kind":"method","name":"find","signature":"public CompletableFuture<Optional<V>> find(K key)","returns":"CompletableFuture<Optional<V>>","doc":"","params":[{"name":"key","type":"K","doc":""}],"throws":[]},{"kind":"method","name":"findAll","signature":"public CompletableFuture<List<V>> findAll()","returns":"CompletableFuture<List<V>>","doc":"","params":[],"throws":[]},{"kind":"method","name":"save","signature":"public CompletableFuture<Void> save(V value)","returns":"CompletableFuture<Void>","doc":"","params":[{"name":"value","type":"V","doc":""}],"throws":[]},{"kind":"method","name":"delete","signature":"public CompletableFuture<Boolean> delete(K key)","returns":"CompletableFuture<Boolean>","doc":"","params":[{"name":"key","type":"K","doc":""}],"throws":[]}]},{"name":"StorageException","qualifiedName":"io.github.crownalonedev.bloom.storage.StorageException","kind":"class","signature":"public class StorageException extends BloomException","doc":"Thrown (or used to complete futures exceptionally) when a database operation fails. The message includes the SQL statement (without parameter values) when relevant.","since":"0.1.0","members":[{"kind":"constructor","name":"StorageException","signature":"public StorageException(String message)","doc":"Creates an exception.","params":[{"name":"message","type":"String","doc":"description"}],"throws":[]},{"kind":"constructor","name":"StorageException","signature":"public StorageException(String message, Throwable cause)","doc":"Creates an exception with a cause.","params":[{"name":"message","type":"String","doc":"description"},{"name":"cause","type":"Throwable","doc":"cause, usually a SQLException"}],"throws":[]}]},{"name":"Transaction","qualifiedName":"io.github.crownalonedev.bloom.storage.Transaction","kind":"interface","signature":"public interface Transaction extends SqlOperations","doc":"Operations inside a database transaction. All statements share one connection; the transaction commits when the callback returns and rolls back if it throws. Only valid inside Database.transaction.","since":"0.1.0","members":[{"kind":"method","name":"dialect","signature":"public Dialect dialect()","returns":"Dialect","doc":"Returns the database dialect.","params":[],"returnsDoc":"dialect","throws":[]},{"kind":"method","name":"connection","signature":"public Connection connection()","returns":"Connection","doc":"Returns the underlying connection for advanced JDBC use. Do not close it or change auto-commit.","params":[],"returnsDoc":"connection","throws":[]}]}]}]},{"name":"bloom-testing","description":"Bloom Testing: server-free test doubles for plugin unit tests (manual scheduler, controllable clock).","packages":[{"name":"io.github.crownalonedev.bloom.testing","types":[{"name":"ManualScheduler","qualifiedName":"io.github.crownalonedev.bloom.testing.ManualScheduler","kind":"class","signature":"public final class ManualScheduler implements BloomScheduler, AutoCloseable","doc":"A deterministic BloomScheduler for unit tests. Nothing runs until you advance time with tick / tick; \"async\" tasks also run on the test thread during ticks, so tests need no sleeps or latches. It has the same ownership, error-isolation and cancellation behaviour as the real Bloom scheduler. ManualScheduler scheduler = ManualScheduler.create(); MyCountdown countdown = new MyCountdown(scheduler); // code under test takes a BloomScheduler countdown.start(3); scheduler.tick(60); assertTrue(countdown.isFinished()); Not thread-safe: use from the test thread only.","since":"0.1.0","members":[{"kind":"method","name":"create","signature":"public static ManualScheduler create()","returns":"ManualScheduler","doc":"Creates a scheduler at tick 0 whose calling thread counts as the main thread.","params":[],"returnsDoc":"scheduler","throws":[]},{"kind":"method","name":"create","signature":"public static ManualScheduler create(Logger logger)","returns":"ManualScheduler","doc":"Creates a scheduler that logs task failures to a specific logger.","params":[{"name":"logger","type":"Logger","doc":"logger for task exceptions"}],"returnsDoc":"scheduler","throws":[]},{"kind":"method","name":"tick","signature":"public void tick()","returns":"void","doc":"Advances one tick, running every task that is due.","params":[],"throws":[]},{"kind":"method","name":"tick","signature":"public void tick(long ticks)","returns":"void","doc":"Advances several ticks.","params":[{"name":"ticks","type":"long","doc":"number of ticks"}],"throws":[]},{"kind":"method","name":"advance","signature":"public void advance(Duration duration)","returns":"void","doc":"Advances by a duration (rounded up to whole ticks).","params":[{"name":"duration","type":"Duration","doc":"time to advance"}],"throws":[]},{"kind":"method","name":"currentTick","signature":"public long currentTick()","returns":"long","doc":"Returns the current tick.","params":[],"returnsDoc":"ticks advanced so far","throws":[]},{"kind":"method","name":"primaryThread","signature":"public void primaryThread(boolean primary)","returns":"void","doc":"Sets what isPrimaryThread reports, to test code paths that require (or forbid) the main thread.","params":[{"name":"primary","type":"boolean","doc":"value to report"}],"throws":[]},{"kind":"method","name":"isPrimaryThread","signature":"public boolean isPrimaryThread()","returns":"boolean","doc":"","params":[],"throws":[]},{"kind":"method","name":"ensurePrimaryThread","signature":"public void ensurePrimaryThread(String operation)","returns":"void","doc":"","params":[{"name":"operation","type":"String","doc":""}],"throws":[]},{"kind":"method","name":"sync","signature":"public ScheduledTask sync(Runnable task)","returns":"ScheduledTask","doc":"","params":[{"name":"task","type":"Runnable","doc":""}],"throws":[]},{"kind":"method","name":"syncLater","signature":"public ScheduledTask syncLater(Runnable task, long delayTicks)","returns":"ScheduledTask","doc":"","params":[{"name":"task","type":"Runnable","doc":""},{"name":"delayTicks","type":"long","doc":""}],"throws":[]},{"kind":"method","name":"syncLater","signature":"public ScheduledTask syncLater(Runnable task, Duration delay)","returns":"ScheduledTask","doc":"","params":[{"name":"task","type":"Runnable","doc":""},{"name":"delay","type":"Duration","doc":""}],"throws":[]},{"kind":"method","name":"syncRepeating","signature":"public ScheduledTask syncRepeating(Consumer<ScheduledTask> task, long delayTicks, long periodTicks)","returns":"ScheduledTask","doc":"","params":[{"name":"task","type":"Consumer<ScheduledTask>","doc":""},{"name":"delayTicks","type":"long","doc":""},{"name":"periodTicks","type":"long","doc":""}],"throws":[]},{"kind":"method","name":"syncRepeating","signature":"public ScheduledTask syncRepeating(Consumer<ScheduledTask> task, Duration delay, Duration period)","returns":"ScheduledTask","doc":"","params":[{"name":"task","type":"Consumer<ScheduledTask>","doc":""},{"name":"delay","type":"Duration","doc":""},{"name":"period","type":"Duration","doc":""}],"throws":[]},{"kind":"method","name":"async","signature":"public ScheduledTask async(Runnable task)","returns":"ScheduledTask","doc":"","params":[{"name":"task","type":"Runnable","doc":""}],"throws":[]},{"kind":"method","name":"asyncLater","signature":"public ScheduledTask asyncLater(Runnable task, long delayTicks)","returns":"ScheduledTask","doc":"","params":[{"name":"task","type":"Runnable","doc":""},{"name":"delayTicks","type":"long","doc":""}],"throws":[]},{"kind":"method","name":"asyncRepeating","signature":"public ScheduledTask asyncRepeating(Consumer<ScheduledTask> task, long delayTicks, long periodTicks)","returns":"ScheduledTask","doc":"","params":[{"name":"task","type":"Consumer<ScheduledTask>","doc":""},{"name":"delayTicks","type":"long","doc":""},{"name":"periodTicks","type":"long","doc":""}],"throws":[]},{"kind":"method","name":"supplySync","signature":"public <T> CompletableFuture<T> supplySync(Supplier<T> supplier)","returns":"CompletableFuture<T>","doc":"","params":[{"name":"supplier","type":"Supplier<T>","doc":""}],"throws":[]},{"kind":"method","name":"supplyAsync","signature":"public <T> CompletableFuture<T> supplyAsync(Supplier<T> supplier)","returns":"CompletableFuture<T>","doc":"","params":[{"name":"supplier","type":"Supplier<T>","doc":""}],"throws":[]},{"kind":"method","name":"mainThreadExecutor","signature":"public Executor mainThreadExecutor()","returns":"Executor","doc":"","params":[],"throws":[]},{"kind":"method","name":"asyncExecutor","signature":"public Executor asyncExecutor()","returns":"Executor","doc":"","params":[],"throws":[]},{"kind":"method","name":"activeTaskCount","signature":"public int activeTaskCount()","returns":"int","doc":"","params":[],"throws":[]},{"kind":"method","name":"close","signature":"public void close()","returns":"void","doc":"Cancels all tasks, like Bloom does on shutdown.","params":[],"throws":[]}]},{"name":"TestClock","qualifiedName":"io.github.crownalonedev.bloom.testing.TestClock","kind":"class","signature":"public final class TestClock extends Clock","doc":"A Clock that only moves when told to. Pass it to Cooldowns.of(duration, clock) or ExpiringCache.create(ttl, size, clock) to test time-based logic without sleeping. TestClock clock = TestClock.start(); Cooldowns<UUID> cooldowns = Cooldowns.of(Duration.ofSeconds(10), clock); cooldowns.tryUse(id); clock.advance(Duration.ofSeconds(10)); assertTrue(cooldowns.tryUse(id));","since":"0.1.0","members":[{"kind":"method","name":"start","signature":"public static TestClock start()","returns":"TestClock","doc":"Creates a clock at 2026-01-01T00:00:00Z.","params":[],"returnsDoc":"clock","throws":[]},{"kind":"method","name":"at","signature":"public static TestClock at(Instant instant)","returns":"TestClock","doc":"Creates a clock at a given instant.","params":[{"name":"instant","type":"Instant","doc":"start time"}],"returnsDoc":"clock","throws":[]},{"kind":"method","name":"advance","signature":"public void advance(Duration duration)","returns":"void","doc":"Moves time forward.","params":[{"name":"duration","type":"Duration","doc":"amount, may be negative to move backwards"}],"throws":[]},{"kind":"method","name":"set","signature":"public void set(Instant instant)","returns":"void","doc":"Sets the time.","params":[{"name":"instant","type":"Instant","doc":"new time"}],"throws":[]},{"kind":"method","name":"instant","signature":"public Instant instant()","returns":"Instant","doc":"","params":[],"throws":[]},{"kind":"method","name":"getZone","signature":"public ZoneId getZone()","returns":"ZoneId","doc":"","params":[],"throws":[]},{"kind":"method","name":"withZone","signature":"public Clock withZone(ZoneId zone)","returns":"Clock","doc":"","params":[{"name":"zone","type":"ZoneId","doc":""}],"throws":[]}]}]}]}]}
