Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/universeclouddev/Universe/llms.txt

Use this file to discover all available pages before exploring further.

The :minecraft:api module is a lightweight, JVM-8-compatible library that lets any Minecraft plugin — yours or a third party’s — interact with the Universe cluster at runtime. It contains no external dependencies and is intentionally not relocated inside Universe’s own shadow JARs, so the same classes are available on the classpath regardless of which Universe plugin flavor is loaded on the server. You do not need to build against the specific Paper, Spigot, or Folia plugin. You depend only on :minecraft:api, call Universe.getAPI(), and let the loaded platform plugin supply the implementation.

Adding the Dependency

Add :minecraft:api to your plugin’s build script using implementation (not compileOnly) so the API classes are available at compile time and correctly resolved against the classes Universe places on the runtime classpath.
dependencies {
    implementation("gg.scala.universe:minecraft-api:<version>")
}
Universe.getAPI() returns the platform-specific implementation registered by whichever Universe plugin is loaded — ModernUniverseAPIImpl, LegacyUniverseAPIImpl, or FoliaUniverseAPIImpl. Your code always depends on the UniverseAPI interface; you never reference the implementation classes directly.

Registering with Universe

The Universe object is the single static entry point. The platform plugin calls Universe.register(api) in onEnable() and Universe.unregister() in onDisable(). If you are writing a dependent plugin — one that uses the API but does not provide it — you only need to call Universe.getAPI().
// In your plugin's onEnable() — only needed if you are providing an implementation
Universe.register(myApiImpl)

// In your plugin's onDisable()
Universe.unregister()
For the common case where you are consuming the API, just guard with a null check:
val api = Universe.getAPI() ?: run {
    logger.warning("Universe is not loaded — skipping cluster features")
    return
}
You can also check availability without obtaining the instance:
if (!Universe.isAvailable()) {
    logger.warning("Universe API is not available on this server")
    return
}

UniverseAPI Interface

UniverseAPI is the root interface. It exposes metadata about the current server’s connection and provides access to the three manager interfaces.
interface UniverseAPI {

    /** The URL of the Universe Master REST API. */
    fun getMasterUrl(): String

    /** The instance ID of this server, if running as a Universe instance. */
    fun getInstanceId(): String?

    /** Whether this plugin is currently connected to the Universe master. */
    fun isConnected(): Boolean

    /** Manager for instance lifecycle operations (start, stop, execute commands). */
    fun getInstanceManager(): InstanceManager

    /** Manager for configuration operations (list, get, create configurations). */
    fun getConfigurationManager(): ConfigurationManager

    /** Manager for template operations (list, sync templates). */
    fun getTemplateManager(): TemplateManager
}

Manager Interfaces

InstanceManager is the most commonly used manager. All methods communicate with the Universe Master via REST and return CompletableFuture for non-blocking use.
interface InstanceManager {

    /** Start a new instance from a named configuration. Returns the new instance ID. */
    fun startInstance(configurationName: String): CompletableFuture<String>

    /** Start a new instance from a full Configuration object. Returns the new instance ID. */
    fun startInstance(configuration: Configuration): CompletableFuture<String>

    /** Stop an instance by ID. */
    fun stopInstance(instanceId: String): CompletableFuture<Void>

    /** Send a console command to an instance's stdin. */
    fun executeCommand(instanceId: String, command: String): CompletableFuture<Void>

    /** Get a single instance by ID. Returns Optional.empty() if not found. */
    fun getInstance(instanceId: String): CompletableFuture<Optional<InstanceInfo>>

    /** List all instances in the cluster. */
    fun getInstances(): CompletableFuture<List<InstanceInfo>>

    /** List instances filtered by state. */
    fun getInstancesByState(state: InstanceState): CompletableFuture<List<InstanceInfo>>

    /** Get the current state of an instance. Returns Optional.empty() if not found. */
    fun getInstanceState(instanceId: String): CompletableFuture<Optional<InstanceState>>

    /**
     * Report this server's own state to the Master.
     * Used internally by the platform plugin; generally not needed externally.
     */
    fun reportState(state: InstanceState): CompletableFuture<Void>
}
ConfigurationManager lets you list, read, create, and delete instance configurations stored in the Master.
interface ConfigurationManager {

    /** List all configurations. */
    fun getConfigurations(): CompletableFuture<List<Configuration>>

    /** Get a configuration by name. Returns null if not found. */
    fun getConfiguration(name: String): CompletableFuture<Configuration?>

    /** Create or update a configuration. */
    fun saveConfiguration(configuration: Configuration): CompletableFuture<Void>

    /** Delete a configuration by name. */
    fun deleteConfiguration(name: String): CompletableFuture<Void>
}
TemplateManager provides read access to templates and lets you trigger a cluster-wide sync.
interface TemplateManager {

    /** List all templates. */
    fun getTemplates(): CompletableFuture<List<Template>>

    /** List templates in a specific group. */
    fun getTemplatesByGroup(group: String): CompletableFuture<List<Template>>

    /** Trigger a template sync to all nodes. */
    fun syncTemplates(): CompletableFuture<Void>
}

InstanceInfo Data Class

InstanceInfo is returned by every instance query. All fields are directly accessible in Kotlin; Java callers use generated getters.
FieldTypeDescription
idStringUnique 6-character instance ID
configurationNameStringName of the configuration this instance was created from
wrapperNodeIdStringID of the Wrapper node running this instance
hostAddressStringAdvertised host address (IP or DNS name)
allocatedPortIntPort assigned to this instance
stateStringRaw state string — use getStateEnum() for a typed value
lastHeartbeatLongEpoch milliseconds of the last heartbeat report
processPidLong?OS PID of the instance process, if available
allocatedRamMBIntRAM allocated to the instance in megabytes
allocatedCpuIntCPU shares allocated to the instance
runtimeStringRuntime name (screen, tmux, docker, etc.)
playersIntCurrent online player count (from heartbeat)
maxPlayersIntConfigured maximum players (from heartbeat)
InstanceInfo also provides three convenience members:
// Typed state enum
fun getStateEnum(): InstanceState

// Full "host:port" address string
fun getAddress(): String

// True when state == "ONLINE"
fun isOnline(): Boolean
A fluent InstanceInfo.Builder is available for constructing instances in tests:
val info = InstanceInfo.Builder()
    .id("abc123")
    .configurationName("lobby")
    .hostAddress("10.0.0.5")
    .allocatedPort(25565)
    .state(InstanceState.ONLINE)
    .players(12)
    .maxPlayers(100)
    .build()

InstanceState Enum

enum class InstanceState {
    CREATING,  // Instance has been requested and is being set up
    ONLINE,    // Instance is running and heartbeating
    OFFLINE,   // Instance reported clean shutdown
    STOPPED    // Instance was stopped or terminated abnormally
}

Full Usage Example

The example below shows a typical Kotlin plugin that uses the API to list all online lobby servers, start a new one if fewer than two exist, and react to state changes.
import gg.scala.universe.minecraft.api.Universe
import gg.scala.universe.minecraft.api.InstanceState
import org.bukkit.plugin.java.JavaPlugin

class MyPlugin : JavaPlugin() {

    override fun onEnable() {
        val api = Universe.getAPI() ?: run {
            logger.warning("Universe is not available — skipping cluster logic")
            return
        }

        logger.info("Connected to Universe Master at ${api.getMasterUrl()}")
        logger.info("This server's instance ID: ${api.getInstanceId()}")

        // List all ONLINE instances
        api.instanceManager
            .getInstancesByState(InstanceState.ONLINE)
            .thenAccept { onlineInstances ->
                logger.info("Online instances: ${onlineInstances.size}")
                onlineInstances.forEach { instance ->
                    logger.info("  ${instance.id} @ ${instance.getAddress()}${instance.players}/${instance.maxPlayers} players")
                }
            }

        // Get a specific instance by ID
        api.instanceManager
            .getInstance("abc123")
            .thenAccept { optional ->
                if (optional.isPresent) {
                    val info = optional.get()
                    logger.info("Instance ${info.id} is ${info.getStateEnum()} (runtime: ${info.runtime})")
                } else {
                    logger.info("Instance abc123 not found")
                }
            }

        // Start a new lobby instance if fewer than two exist
        api.instanceManager
            .getInstances()
            .thenAccept { all ->
                val lobbies = all.filter { it.configurationName == "lobby" && it.isOnline() }
                if (lobbies.size < 2) {
                    api.instanceManager
                        .startInstance("lobby")
                        .thenAccept { newId ->
                            logger.info("Started new lobby instance: $newId")
                        }
                }
            }

        // Trigger a template sync across all nodes
        api.templateManager
            .syncTemplates()
            .thenRun { logger.info("Template sync triggered") }
    }

    override fun onDisable() {
        Universe.unregister()
    }
}
Universe.getAPI() always returns null if the Universe plugin is not loaded or has not finished enabling. Guard every call with a null check or use Universe.isAvailable() before interacting with the API. If your plugin declares depend: [Universe] in plugin.yml, the Universe plugin is guaranteed to have called Universe.register(api) before your onEnable() runs.

Java Usage

Universe.getAPI() and Universe.isAvailable() are annotated with @JvmStatic. UniverseAPI interface methods follow standard Java naming conventions (getInstanceManager() etc.) so Java plugins can call them directly without any Kotlin dependency.
UniverseAPI api = Universe.getAPI();
if (api == null) return;

api.getInstanceManager().getInstances().thenAccept(instances -> {
    for (InstanceInfo info : instances) {
        System.out.println(info.getId() + " -> " + info.getAddress());
    }
});

Build docs developers (and LLMs) love