Pvty Games Extensible API
Welcome! This guide shows how to integrate an external mini-game with Pvty Games: register your game, respond to lifecycle callbacks, and surface optional mods the lobby can enable prior to a match.
Quick Start
- Add the
pvtyGamesExtensibleAPIjar to your plugin's dependencies (compileOnly is sufficient). - Implement
ExternalGame, returning descriptiveGameMetadataand creatingExternalGameSessioninstances. - During your plugin's enable phase, acquire
PvtyGamesAPIand callregisterGamewith aGameRegistration. - Inside your session implementation respond to lifecycle events (
onInitialize,onStart,onPlayerEliminated, etc.).
public final class SkyLootGame implements ExternalGame {
private final GameMetadata metadata = GameMetadata.builder("skyloot", "Sky Loot Rush")
.description("Collect loot on floating islands before the timer expires.")
.minPlayers(4)
.maxPlayers(16)
.supportsSpectators(true)
.build();
@Override
public @NotNull GameMetadata metadata() {
return metadata;
}
@Override
public @NotNull ExternalGameSession createSession(@NotNull GameSessionCreationContext ctx) {
return new SkyLootSession();
}
}
Registration Lifecycle
Games are registered via PvtyGamesAPI. The API is typically obtained from the Pvty Games plugin:
public void onEnable() {
PvtyGamesAPI api = getServer()
.getServicesManager()
.load(PvtyGamesAPI.class);
if (api == null) {
getLogger().warning("Pvty Games API not available.");
return;
}
GameRegistration registration = GameRegistration.builder("skyloot", SkyLootGame::new)
.metadata(GameMetadata.builder("skyloot", "Sky Loot Rush")
.description("Collect loot on floating islands before the timer expires.")
.build())
.addTag("pve")
.addTag("parkour")
.build();
api.registerGame(registration);
}
Required:
- metadata() — must describe the game with unique
id. - createSession() — must return a new
ExternalGameSessioneach time. - GameRegistration.metadata(...) — must be populated when you call
registerGame.
Optional, but recommended:
- ExternalGame.availableMods() to expose toggles and numeric modifiers.
- ExternalGame.onRegistered() for setup after the platform loads.
- GameRegistration.addTag() to help the lobby categorise the game.
Session Lifecycle Callbacks
The host drives each session through a predictable set of callbacks on ExternalGameSession:
- onInitialize: Allocate resources, load worlds, place players in waiting lobbies.
- onCountdownTick: Update countdown scoreboards or play sounds.
- onStart: Begin the actual gameplay (start timers, spawn mobs, enable damage).
- onPlayerJoin / onPlayerLeave: Handle reconnects or voluntary departures.
- onPlayerEliminated: Update internal state when Pvty Games removes a player.
- onPlayerCompleted: Mark success for time-trial scenarios.
- onTick: Perform lightweight per-tick logic (keep work small to avoid lag).
- onGameStopping & onGameStopped: Clean up resources and reset worlds.
You can interact with players through GameSessionContext, which exposes helper methods to broadcast
messages, play sounds, award points, and update game modes. Use the session scheduler() to schedule
repeated tasks that automatically cancel when the game ends.
Mods & Rule Variations
Mods are defined via GameModDescriptor. They enable lobby hosts to customise your game before it runs.
At runtime, the chosen values are available through GameSessionContext.selectedMods().
GameModDescriptor hardcore = GameModDescriptor.builder("hardcore", "Hardcore Mode")
.description("Players have one life and cannot regenerate health.")
.kind(GameModKind.TOGGLE)
.defaultValue("false")
.build();
collection.add(hardcore);
Parity helpers such as GameModSelection.isEnabled(), intValue(), and doubleValue()
simplify reading selections.
Best Practices
- Use
GameSessionContext.scheduler()instead of Bukkit's global scheduler to ensure tasks end with the session. - Keep expensive work off the main thread to avoid TPS drops.
- Always handle
onGameStoppingto roll back world changes or cancel outstanding tasks. - Call
GameSessionContext.endGame()when your game reaches a natural conclusion.