feat: add minecraft-plugin-development skillFeat/minecraft plugin development (#1454)

* feat: add minecraft-plugin-development skill

* docs: expand minecraft plugin skill patterns

* docs: add minecraft skill examples

* docs: generalize minecraft skill patterns

* docs: expand minecraft progression guidance

* docs: add minecraft plugin validation workflow

---------

Co-authored-by: jiang <helloworld@jiang.cn>
This commit is contained in:
Zixuan Jiang
2026-04-28 09:41:41 +08:00
committed by GitHub
parent 7b9e8229fb
commit ca56e9577d
10 changed files with 1317 additions and 0 deletions

View File

@@ -210,6 +210,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-skills) for guidelines on how to
| [microsoft-docs](../skills/microsoft-docs/SKILL.md) | Query official Microsoft documentation to find concepts, tutorials, and code examples across Azure, .NET, Agent Framework, Aspire, VS Code, GitHub, and more. Uses Microsoft Learn MCP as the default, with Context7 and Aspire MCP for content that lives outside learn.microsoft.com. | None | | [microsoft-docs](../skills/microsoft-docs/SKILL.md) | Query official Microsoft documentation to find concepts, tutorials, and code examples across Azure, .NET, Agent Framework, Aspire, VS Code, GitHub, and more. Uses Microsoft Learn MCP as the default, with Context7 and Aspire MCP for content that lives outside learn.microsoft.com. | None |
| [microsoft-skill-creator](../skills/microsoft-skill-creator/SKILL.md) | Create agent skills for Microsoft technologies using Learn MCP tools. Use when users want to create a skill that teaches agents about any Microsoft technology, library, framework, or service (Azure, .NET, M365, VS Code, Bicep, etc.). Investigates topics deeply, then generates a hybrid skill storing essential knowledge locally while enabling dynamic deeper investigation. | `references/skill-templates.md` | | [microsoft-skill-creator](../skills/microsoft-skill-creator/SKILL.md) | Create agent skills for Microsoft technologies using Learn MCP tools. Use when users want to create a skill that teaches agents about any Microsoft technology, library, framework, or service (Azure, .NET, M365, VS Code, Bicep, etc.). Investigates topics deeply, then generates a hybrid skill storing essential knowledge locally while enabling dynamic deeper investigation. | `references/skill-templates.md` |
| [migrating-oracle-to-postgres-stored-procedures](../skills/migrating-oracle-to-postgres-stored-procedures/SKILL.md) | Migrates Oracle PL/SQL stored procedures to PostgreSQL PL/pgSQL. Translates Oracle-specific syntax, preserves method signatures and type-anchored parameters, leverages orafce where appropriate, and applies COLLATE "C" for Oracle-compatible text sorting. Use when converting Oracle stored procedures or functions to PostgreSQL equivalents during a database migration. | None | | [migrating-oracle-to-postgres-stored-procedures](../skills/migrating-oracle-to-postgres-stored-procedures/SKILL.md) | Migrates Oracle PL/SQL stored procedures to PostgreSQL PL/pgSQL. Translates Oracle-specific syntax, preserves method signatures and type-anchored parameters, leverages orafce where appropriate, and applies COLLATE "C" for Oracle-compatible text sorting. Use when converting Oracle stored procedures or functions to PostgreSQL equivalents during a database migration. | None |
| [minecraft-plugin-development](../skills/minecraft-plugin-development/SKILL.md) | Use this skill when building or modifying Minecraft server plugins for Paper, Spigot, or Bukkit, including plugin.yml setup, commands, listeners, schedulers, player state, team or arena systems, persistent progression, economy or profile data, configuration files, Adventure text, and version-safe API usage. Trigger for requests like "build a Minecraft plugin", "add a Paper command", "fix a Bukkit listener", "create plugin.yml", "implement a minigame mechanic", "add a perk or quest system", or "debug server plugin behavior". | `references/bootstrap-registration.md`<br />`references/build-test-and-runtime-validation.md`<br />`references/config-data-and-async.md`<br />`references/maps-heroes-and-feature-modules.md`<br />`references/minigame-instance-flow.md`<br />`references/persistent-progression-and-events.md`<br />`references/project-patterns.md`<br />`references/state-sessions-and-phases.md` |
| [mkdocs-translations](../skills/mkdocs-translations/SKILL.md) | Generate a language translation for a mkdocs documentation stack. | None | | [mkdocs-translations](../skills/mkdocs-translations/SKILL.md) | Generate a language translation for a mkdocs documentation stack. | None |
| [model-recommendation](../skills/model-recommendation/SKILL.md) | Analyze chatmode or prompt files and recommend optimal AI models based on task complexity, required capabilities, and cost-efficiency | None | | [model-recommendation](../skills/model-recommendation/SKILL.md) | Analyze chatmode or prompt files and recommend optimal AI models based on task complexity, required capabilities, and cost-efficiency | None |
| [msstore-cli](../skills/msstore-cli/SKILL.md) | Microsoft Store Developer CLI (msstore) for publishing Windows applications to the Microsoft Store. Use when asked to configure Store credentials, list Store apps, check submission status, publish submissions, manage package flights, set up CI/CD for Store publishing, or integrate with Partner Center. Supports Windows App SDK/WinUI, UWP, .NET MAUI, Flutter, Electron, React Native, and PWA applications. | None | | [msstore-cli](../skills/msstore-cli/SKILL.md) | Microsoft Store Developer CLI (msstore) for publishing Windows applications to the Microsoft Store. Use when asked to configure Store credentials, list Store apps, check submission status, publish submissions, manage package flights, set up CI/CD for Store publishing, or integrate with Partner Center. Supports Windows App SDK/WinUI, UWP, .NET MAUI, Flutter, Electron, React Native, and PWA applications. | None |

View File

@@ -0,0 +1,272 @@
---
name: minecraft-plugin-development
description: 'Use this skill when building or modifying Minecraft server plugins for Paper, Spigot, or Bukkit, including plugin.yml setup, commands, listeners, schedulers, player state, team or arena systems, persistent progression, economy or profile data, configuration files, Adventure text, and version-safe API usage. Trigger for requests like "build a Minecraft plugin", "add a Paper command", "fix a Bukkit listener", "create plugin.yml", "implement a minigame mechanic", "add a perk or quest system", or "debug server plugin behavior".'
---
# Minecraft Plugin Development
Use this skill for Minecraft server plugin work in the Paper, Spigot, and Bukkit ecosystem.
This skill is especially useful for gameplay-heavy plugins such as combat systems, wave or boss encounters, war or team modes, arenas, kit systems, cooldown-based abilities, scoreboards, and config-driven game rules.
For grounded implementation patterns drawn from real Paper plugins, load these references as needed:
- [`references/project-patterns.md`](references/project-patterns.md) for high-level architecture patterns seen in real gameplay plugins
- [`references/bootstrap-registration.md`](references/bootstrap-registration.md) for `onEnable`, command wiring, listener registration, and shutdown expectations
- [`references/state-sessions-and-phases.md`](references/state-sessions-and-phases.md) for player session modeling, game phases, match state, and reconnect-safe logic
- [`references/config-data-and-async.md`](references/config-data-and-async.md) for config managers, database-backed player data, async flushes, and UI refresh tasks
- [`references/maps-heroes-and-feature-modules.md`](references/maps-heroes-and-feature-modules.md) for map rotation, hero or class systems, and modular feature growth
- [`references/minigame-instance-flow.md`](references/minigame-instance-flow.md) for arena instances, countdowns, loot refreshes, wave systems, visibility isolation, and entity-to-game ownership
- [`references/persistent-progression-and-events.md`](references/persistent-progression-and-events.md) for long-running PvP servers with profiles, perks, buffs, quests, economy, custom domain events, and extension registries
- [`references/build-test-and-runtime-validation.md`](references/build-test-and-runtime-validation.md) for Maven or Gradle packaging, shaded dependencies, generated resources, soft dependencies, config validation commands, and first-round server test plans
## Scope
- In scope: Paper, Spigot, Bukkit plugin development
- In scope: `plugin.yml`, commands, tab completion, listeners, schedulers, configs, permissions, Adventure text, player state, minigame flow, arena instances, map copies, loot, waves, persistent profiles, perks, buffs, quests, economy, and PvP/PvE game loops
- In scope: Java-based server plugin architecture, debugging, refactoring, and feature implementation
- Out of scope by default: Fabric mods, Forge mods, client mods, Bedrock add-ons
If the user says "Minecraft plugin" but the stack is unclear, first determine whether the project is Paper/Spigot/Bukkit or a modding stack.
## Default Working Style
When this skill triggers:
1. Identify the server API and version target.
2. Identify the build system and Java version.
3. Inspect `plugin.yml`, the main plugin class, and command or listener registration.
4. Map the gameplay flow before editing code:
- player lifecycle
- game phases
- timers and scheduled tasks
- team, arena, or match state
- config and persistence
5. Make the smallest coherent change that keeps registration, config, and runtime behavior aligned.
If the plugin is gameplay-heavy or stateful, read [`references/project-patterns.md`](references/project-patterns.md) and [`references/state-sessions-and-phases.md`](references/state-sessions-and-phases.md) before editing.
If the task touches arena isolation, map instances, chest or resource refills, wave spawning, route voting, spectator visibility, or game-specific chat, also read [`references/minigame-instance-flow.md`](references/minigame-instance-flow.md).
If the task touches persistent player progression, profile saves, economy rewards, perks, buffs, quests, custom combat events, or long-running shared PvP servers, also read [`references/persistent-progression-and-events.md`](references/persistent-progression-and-events.md).
If the task touches build files, `plugin.yml` metadata, shaded dependencies, generated resource output, deployment to a test server, optional plugin integrations, or release validation, also read [`references/build-test-and-runtime-validation.md`](references/build-test-and-runtime-validation.md).
## Project Discovery Checklist
Check these first when present:
- `plugin.yml`
- `pom.xml`, `build.gradle`, or `build.gradle.kts`
- the plugin main class extending `JavaPlugin`
- command executors and tab completers
- listener classes
- config bootstrap code for `config.yml`, messages, kits, arenas, or custom YAML files
- generated resource output such as `target/classes`, `build/resources`, or copied plugin jars
- scheduler usage through Bukkit scheduler APIs
- any player data, team state, arena state, or match state containers
## Core Rules
### Prefer the concrete server API in the repo
- If the project already targets Paper APIs, keep using Paper-first APIs instead of downgrading to generic Bukkit unless compatibility is explicitly required.
- Do not assume an API exists across all versions. Check the existing dependency and surrounding code style first.
### Keep registration in sync
When adding commands, permissions, or listeners, update the relevant registration points in the same change:
- `plugin.yml`
- plugin startup registration in `onEnable`
- any permission checks in code
- any related config or message keys
### Respect main-thread boundaries
- Do not touch world state, entities, inventories, scoreboards, or most Bukkit API objects from async tasks unless the API explicitly permits it.
- Use async tasks for external I/O, heavy computation, or database work, then switch back to the main thread before applying gameplay changes.
### Model gameplay as state, not scattered booleans
For gameplay plugins, prefer explicit state objects over duplicated flags:
- match or game phase
- player role or class
- cooldown state
- team membership
- arena assignment
- alive, eliminated, spectating, or queued state
When the feature affects match-heavy minigames or persistent-brawl gameplay, look for hidden state transitions first before patching symptoms.
For multi-arena plugins, isolate per-game visibility, chat recipients, scoreboards, loot, and entity ownership. Do not let one arena observe or mutate another arena by accident.
### Favor config-driven values
When the feature includes damage, cooldowns, rewards, durations, messages, map settings, or toggles:
- prefer config-backed values over hardcoding
- provide sensible defaults
- keep key names stable and readable
- validate or sanitize missing values
### Be careful with reload behavior
- Avoid promising safe hot reload unless the code already supports it well.
- On config reload, ensure in-memory caches, scheduled tasks, and gameplay state are handled consistently.
## Implementation Patterns
### Commands
For new commands:
- add the command to `plugin.yml`
- implement executor and tab completion when needed
- validate sender type before casting to `Player`
- separate parsing, permission checks, and gameplay logic
- send clear player-facing feedback for invalid usage
Minimal registration shape:
```yaml
commands:
arena:
description: Join or leave an arena
usage: /arena <join|leave>
```
```java
@Override
public void onEnable() {
ArenaCommand command = new ArenaCommand(gameService);
PluginCommand arena = getCommand("arena");
if (arena != null) {
arena.setExecutor(command);
arena.setTabCompleter(command);
}
}
```
### Listeners
For event listeners:
- guard early and return early
- check whether the current player, arena, or game phase should handle the event
- avoid doing expensive work in hot events such as move, damage, or interact spam
- centralize repeated checks where practical
### Scheduled Tasks
For timers, rounds, countdowns, cooldowns, or periodic checks:
- store task handles when cancellation matters
- cancel tasks on plugin disable and when a match or arena ends
- avoid multiple overlapping tasks for the same gameplay concern unless explicitly intended
- prefer one authoritative game loop over many loosely coordinated repeating tasks
- ensure countdown or refill tasks self-cancel when the game leaves the expected state
Main-thread handoff shape:
```java
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
PlayerData data = repository.load(playerId);
Bukkit.getScheduler().runTask(plugin, () -> {
Player player = Bukkit.getPlayer(playerId);
if (player != null && player.isOnline()) {
scoreboard.update(player, data);
}
});
});
```
### Player and Match State
For per-player or per-match state:
- define ownership clearly
- clean up on quit, kick, death, match end, and plugin disable
- avoid memory leaks from stale maps keyed by `Player`
- prefer `UUID` for persistent tracking unless a live player object is strictly needed
### Text and Messages
When the project uses Adventure or MiniMessage:
- follow the existing formatting approach
- avoid mixing legacy color codes and Adventure styles without a reason
- keep message templates configurable when messages are gameplay-facing
## High-Risk Areas
Pay extra attention when editing:
- damage handling and custom combat logic
- death, respawn, spectator, and elimination flow
- arena join and leave flow
- scoreboard or boss bar updates
- inventory mutation and kit distribution
- async database or file access
- economy, quest, perk, and profile mutation
- custom event dispatch or extension registries
- version-sensitive API calls
- shutdown and cleanup in `onDisable`
- cross-arena visibility, chat, and broadcast isolation
- map copy, unload, and folder deletion logic
- mob, NPC, projectile, or temporary entity ownership
- chest or resource refill systems
## Output Expectations
When implementing or revising plugin code:
- produce runnable Java code, not pseudo-code, unless the user asks for design only
- mention any required updates to `plugin.yml`, config files, build files, or resources
- call out version assumptions explicitly
- point out thread-safety or API-compatibility risks when they exist
- preserve the project's existing conventions and folder structure
When the requested change touches plugin startup, async data, match flow, class systems, or rotating maps, consult the matching reference file before editing.
## Validation Checklist
Before finishing, verify as many of these as the task allows:
- the command, listener, or feature is registered correctly
- `plugin.yml` matches the implemented behavior
- imports and API types match the targeted server stack
- scheduler usage is safe
- config keys referenced in code exist or have defaults
- state cleanup paths exist for match end, player quit, and plugin disable
- per-arena chat, visibility, scoreboards, and broadcasts are isolated
- temporary worlds, mobs, tasks, and generated resources are cleaned up
- there are no obvious null, cast, or lifecycle hazards
## Common Gotchas
- Casting `CommandSender` to `Player` without checking
- Updating Bukkit state from async tasks
- Forgetting to register listeners or declare commands in `plugin.yml`
- Using `Player` objects as long-lived map keys when `UUID` is safer
- Leaving repeating tasks alive after a round, arena, or plugin shutdown
- Hardcoding gameplay constants that should live in config
- Assuming Paper-only APIs in a Spigot-targeted plugin
- Treating reload as free even though stateful plugins often break under reload
- Broadcasting, showing players, or applying scoreboard changes across unrelated game instances
- Loading or mutating chest/container blocks before their chunks are available
- Forgetting to unregister spawned mobs or temporary entities from the owning game
- Editing generated files under `target/classes` or `build/resources` instead of source files under `src/main/resources`
## Preferred Response Shape
For substantial requests, structure work like this:
1. Current plugin context and assumptions
2. Gameplay or lifecycle impact
3. Code changes
4. Required registration or config updates
5. Validation and remaining risks
For small requests, keep the answer concise but still mention any needed `plugin.yml`, config, or lifecycle updates.

View File

@@ -0,0 +1,100 @@
# Bootstrap And Registration
Use this reference when changing `onEnable`, `onDisable`, command setup, event wiring, or startup ordering.
## Bootstrap pattern from real plugins
### Match-heavy minigame bootstrap
Common traits:
- connect database first
- register configuration serialization when needed
- initialize multiple config managers
- create gameplay managers and GUI helpers
- apply lobby UI to already-online players
- register many listeners explicitly
- start repeating tasks
- register commands and tab completers last
This pattern works well when startup must assemble many gameplay subsystems before the server can safely accept interactions.
### Persistent class-brawl bootstrap
Common traits:
- save default config and bundled resources first
- construct config wrapper and progression config
- connect database and create repository/service layers
- construct scoreboard, map, hero, and game services
- wire service dependencies together
- start background tasks
- preload async cache, then schedule main-thread UI refreshes
- register listeners and commands after service graph is ready
This pattern works well when player data and async services are first-class dependencies.
## Command registration rules
Observed practices:
- Match-heavy minigames often declare all commands in `plugin.yml`, then set executors and tab completers in code.
- Persistent brawl modes may declare a small command surface such as `leave`, then null-check `getCommand` before assigning the executor.
Recommended rules:
- always add new commands to `plugin.yml`
- if a command may be optional or renamed, null-check `getCommand`
- if tab completion exists, register it in the same change
- keep usage, permission, and permission-message aligned with actual code behavior
## Listener registration rules
Observed practices:
- both plugins register listeners in one place during startup
- listener constructors receive only the services they actually need
Recommended rules:
- keep registration centralized in the main plugin class or a clearly named bootstrap helper
- inject services explicitly instead of having listeners discover globals everywhere
- if a listener depends on a task, GUI, or manager, construct that dependency first
## Repeating tasks and startup ordering
Observed practices:
- Match-heavy minigames often start periodic gameplay tasks directly from startup for boundary checks and spectator UI refreshes
- Persistent brawl modes often start background tasks via services such as player data and game services
Recommended rules:
- start repeating tasks only after required state holders exist
- cancel them in shutdown
- if the task mutates gameplay state, ensure it runs on the main thread
- if the task does I/O or cache rebuilds, prefer async execution and hop back to main thread for Bukkit work
## Shutdown expectations
Observed practices:
- Match-heavy minigames delete games, unload maps, flush pending stats, close DB resources, and clear sidebars
- Persistent brawl modes shut down game service, player data service, map manager, then disconnect database
Recommended rules:
- stop tasks
- flush dirty data
- detach or clear UI objects
- unload temporary worlds if your plugin creates them
- close database pools last
## Contribution guidance for this skill
When generating code for startup or shutdown, mention:
- which config or resource files must exist
- which commands or listeners must be registered
- what tasks must be started or canceled
- what resources require cleanup on disable

View File

@@ -0,0 +1,132 @@
# Build, Test, And Runtime Validation
Use this reference when a Minecraft plugin task touches build configuration, packaging, generated resources, deployment to a local test server, optional plugin integrations, reload behavior, or release readiness.
This is especially useful for Maven or Gradle Paper plugins with many YAML resources, shaded libraries, soft dependencies such as PlaceholderAPI or MythicMobs, and admin commands that validate runtime configuration.
## Build metadata and generated resources
Plugin projects often keep source resources under `src/main/resources` while build tools copy filtered output into `target/classes` or `build/resources`.
Guidance:
- Edit source resources, not generated copies.
- Treat `target/classes`, `build/classes`, `build/resources`, and copied server plugin jars as build output.
- When `plugin.yml` uses filtered values such as `${project.version}`, confirm resource filtering is enabled in Maven or Gradle.
- Keep `plugin.yml` `name`, `main`, `api-version`, commands, permissions, `depend`, `softdepend`, and `libraries` aligned with code and build files.
- If the package or main class changes, update `plugin.yml` in the same change.
- If command names change, update command registration, permission checks, tab completers, and usage text together.
Maven shape to recognize:
```xml
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
```
## Shading and dependency scope
Paper plugins often combine server-provided APIs, optional plugin APIs, and libraries that must be bundled.
Guidance:
- Keep `paper-api`, Bukkit, Spigot, and optional plugin APIs as `provided` unless the project has a clear reason to bundle them.
- Shade libraries that the server will not provide, such as small UI helpers or standalone utility libraries.
- Relocate shaded libraries when they are likely to conflict with other plugins.
- Disable or review dependency-reduced POM output when it creates noisy or misleading repo changes.
- Do not shade plugin APIs that are listed in `depend` or `softdepend`.
- If `plugin.yml` uses Paper `libraries`, ensure the server version supports that mechanism and the dependency should be loaded by Paper instead of shaded.
Common relocation example:
```xml
<relocation>
<pattern>fr.mrmicky.fastboard</pattern>
<shadedPattern>com.example.plugin.libs.fastboard</shadedPattern>
</relocation>
```
## Optional plugin integrations
Plugins that integrate with PlaceholderAPI, MythicMobs, WorldEdit, economy plugins, permissions, NPC systems, or custom model plugins should degrade cleanly when the integration is optional.
Guidance:
- Put required plugins in `depend` and optional integrations in `softdepend`.
- Check `PluginManager#isPluginEnabled` before registering listeners that reference optional plugin event classes.
- Keep optional integration code isolated in a listener, adapter, or service.
- Log a clear warning when a soft dependency is missing and gameplay will be reduced.
- Ensure optional integration data flows through the plugin's normal state machine instead of bypassing rewards, inventory, quest, or cleanup logic.
For MythicMobs-style resource mobs:
- Match by stable internal mob names, not display names.
- Resolve the player killer carefully because the direct killer may be a projectile or other entity.
- Clear raw drops only after the plugin has accepted ownership of the reward path.
- Keep custom mob rewards tied to the same cleanup and persistence logic as normal gameplay rewards.
## Runtime config validation
Config-heavy plugins benefit from a non-destructive validation command that can run on startup, reload, and before a test round.
Useful validation surfaces:
- missing worlds, lobby points, spawn points, regions, or trial rooms
- zero-radius or zero-volume regions
- overlapping regions whose priority may be ambiguous
- unknown perk, kit, objective, mob, tier, material, or sidebar identifiers
- disabled systems still referenced by sidebar lines, rewards, objectives, or commands
- cooldowns, reward shares, and percentage sums outside safe ranges
Guidance:
- Return warnings and errors instead of crashing on every imperfect config.
- Cache the latest validation result so an admin command can display it after reload.
- Include the config file and path in each issue.
- Validate after reload before refreshing runtime services.
- Keep auto-fixes conservative; changing map coordinates, rewards, or progression rules silently can surprise server operators.
## Reload and service refresh
Stateful plugin reload is risky but sometimes required for active server iteration.
Guidance:
- Reload typed config models before rebuilding services that consume them.
- Restart repeating tasks that depend on changed config.
- Refresh online player state after reload: region, sidebar, action bar, NPC visibility, perk selections, and active objective state.
- Avoid stacking duplicate schedulers, boss bars, holograms, NPCs, or listeners after reload.
- Prefer explicit `stop()` and `start()` methods on services that own tasks or spawned entities.
- If reload is only partially supported, say exactly which systems require a server restart.
## First-round server test plan
For gameplay plugins, include a short manual test plan when code changes are hard to prove with unit tests.
Recommended checklist:
- Start the target Paper version with required dependencies installed.
- Confirm the plugin loads without console exceptions.
- Run the plugin's validation command if it has one.
- Test core commands and tab completion as both player and console where relevant.
- Test join, leave, death, respawn, disconnect, reconnect, and plugin disable cleanup.
- Test one happy path for the changed feature.
- Test one failure path such as missing permission, cooldown, invalid target, missing dependency, or bad config.
- Confirm generated entities, mobs, NPCs, holograms, scoreboards, boss bars, and temporary worlds are cleaned up.
- Confirm profile saves, rewards, ranking updates, and UI refreshes happen once and on the expected thread.
## Review checklist
Before finishing build or deployment-related changes, verify:
- source resources were changed instead of generated output
- generated README or marketplace files are updated if the repository requires it
- build files and `plugin.yml` agree on version, main class, dependencies, and commands
- shaded dependencies are relocated when conflict-prone
- optional integrations have guards and clear degraded behavior
- validation commands or test checklists cover the changed gameplay path
- local build output is not committed unless the repository intentionally tracks it

View File

@@ -0,0 +1,120 @@
# Config, Data, And Async Patterns
Use this reference when a task touches config loading, persistence, caches, scoreboards, or background refreshes.
## Config patterns from real plugins
### Multi-config minigame layer
Large minigames may use multiple config-oriented classes:
- `PluginConfigManager`
- `GameConfigurationManager`
- `KillMessagesConfig`
- `MessagesConfig`
- `LevelsConfig`
- `MapConfig`
- `PlayerConfig`
This pattern works well when gameplay systems each own a distinct config surface.
Tradeoff:
- easy to grow per-domain config
- harder to keep defaults and reload semantics consistent unless carefully managed
### Defensive wrapper config layer
Persistent brawl modes may use a defensive wrapper around the main config and separate loaders for other domain files such as progression and heroes.
Observed strengths:
- fallback values
- warning logs on invalid config
- one wrapper for core lobby and material settings
Guidance:
- if the project is still compact, prefer a typed wrapper with fallbacks
- split into many config managers only when domain complexity truly requires it
## Database and player data patterns
### Match-heavy minigame observations
- uses a database manager plus stats and brand-related storage
- flushes pending updates before disconnecting
### Persistent-brawl observations
- separates `DatabaseManager`, repository, and service
- keeps in-memory cache keyed by `UUID`
- tracks dirty players
- flushes asynchronously
- rebuilds leaderboards from cached data
- uses dirty sets for sidebar and rank UI so scoreboards are refreshed in batches
- snapshots leaderboard data before presenting it to gameplay or UI code
Guidance:
- use a repository or DAO boundary when persistence logic grows
- keep a cache for hot player stats
- record dirty entries instead of writing on every event
- flush on intervals and on shutdown
- debounce or batch leaderboard rebuilds when kills, deaths, or ranks can change frequently
- publish immutable snapshots or copies from async-maintained caches
## Async rules for Paper plugins
Observed safe persistent-brawl pattern:
- preload cache asynchronously
- when data is ready, switch to the main thread for Bukkit-side UI refreshes
- async leaderboard rebuilds avoid blocking gameplay
Recommended rules:
- async:
- SQL
- file-heavy processing
- leaderboard rebuilds
- cache recomputation
- main thread:
- teleport
- inventory changes
- entity or world mutation
- scoreboard and visible UI changes unless the API is explicitly thread-safe
## Background task patterns
Observed persistent-brawl patterns:
- async periodic leaderboard rebuild checks
- async dirty-player flush task
- throttled UI refresh strategy with dirty sets
- main-thread scoreboard flush from UUID dirty sets
- async cache preload followed by main-thread scoreboard, tab name, and leaderboard refresh
Observed match-heavy minigame patterns:
- main-thread periodic game-stage task
- per-session cooldown ticking
- time-sensitive display refreshes tied to game progression
Guidance:
- use async tasks for data maintenance
- use main-thread tasks for gameplay progression
- if a task can be event-driven plus dirty-flagged, prefer that over brute-force refreshing everything every tick
- copy mutable cached data before saving or sorting it asynchronously
- avoid starting async work from inside an async repeating task unless you need distinct lifecycle control
## Config contribution guidance
When generating config-aware code:
- provide defaults
- validate inputs
- document required keys
- avoid silently crashing on invalid material names or missing locations
- mention whether reload is safe for the specific subsystem

View File

@@ -0,0 +1,120 @@
# Maps, Heroes, And Feature Modules
Use this reference when building map systems, rotating arenas, class systems, kits, or modular gameplay features.
## Map patterns from real plugins
### Per-game map-instance usage
Observed traits:
- per-game map instances
- allowed map filtering through configuration
- gameplay objects tied to the active map instance
- match mode can constrain which maps are used
This works well for isolated match instances where each game owns its world and objectives.
### Persistent battlefield map rotation
Observed traits:
- one active combat world at a time
- source maps copied into temporary active worlds
- old worlds unloaded and deleted after rotation
- rotation warnings broadcast before swap
- spawn leaderboards refreshed after rotation
This works well for a persistent shared mode where the world rotates on a timer.
### Sky-island arena map usage
Observed traits:
- one game owns one copied map instance
- teams are assigned to configured island spawn locations
- chests are grouped by island, middle, or center role
- countdowns control cage removal, game start, refill timing, and game end
- visibility and chat are scoped to players in the same game instance
This works well for SkyWars-style modes where every match needs isolated islands, loot, spectators, and cleanup.
### Dungeon-node map usage
Observed traits:
- one game owns a lobby map plus temporary combat or event node maps
- route choices are generated from stage metadata
- players vote or confirm before advancing
- each combat node owns its wave queue and active mob set
- old node maps are unloaded only after players move to the new map or lobby
This works well for PvE roguelike, dungeon, wave, or boss progression plugins.
## Guidance for map architecture
- Per-instance minigame:
- use a `GameMap` owned by a game object
- Shared rotating battlefield:
- use a `MapManager` with one active world plus a rotation timer
- Temporary copied worlds:
- always teleport players out before unload
- clean folders after unload
- reapply gamerules after world creation
- Sky-island match:
- group spawn and chest locations by team or island role
- reset cages, inventories, scoreboards, and spectators per match
- Dungeon or route-node mode:
- treat every node as a temporary stage with explicit load, enter, clear, and unload steps
- keep mob ownership tied to the game, not just the world
## Class and hero system patterns
Observed class-system traits:
- `HeroRegistry`
- `HeroService`
- definitions grouped by theme
- hero tier progression
- hero skill config and handler
- selector GUI separated from assignment logic
Observed minigame power-selection parallels:
- brands and special items function like modular player powers
- selection limits and categories are explicit
- match rules can constrain available choices
Guidance:
- keep definitions separate from runtime assignment
- use registries for discoverable content
- store unlock rules and tiers in data, not hardcoded listener branches
- separate:
- what a class is
- how it is selected
- how it is applied
- how its active skills are triggered
## Feature module pattern
Good candidate modules for separate packages:
- map rotation
- hero or class system
- item powers
- boss systems
- match rules
- shops and GUIs
- scoreboards
- progression
Do not merge all these into one listener or one “game utils” class.
## Practical heuristic
If a feature has all three of these, it deserves its own module:
- custom data model
- config or definitions
- one or more listeners, commands, or scheduled tasks

View File

@@ -0,0 +1,187 @@
# Minigame Instance Flow
Use this reference when building arena-style PvP games, SkyWars-style island games, dungeon or wave modes, rotating map instances, or any plugin where multiple players, worlds, mobs, scoreboards, and timers belong to a specific game instance.
## Instance ownership pattern
Good minigame plugins make ownership explicit:
- a global `GameManager` or `GameService` tracks active games
- each `Game` owns its map or world instance
- players are mapped to exactly one active game or lobby state
- temporary mobs, projectiles, NPCs, or spawned objects are mapped back to their owning game
- scoreboards, visibility, chat recipients, loot, and timers are scoped to the owning game
Recommended maps:
- `Map<String, Game>` for active games by generated ID
- `Map<UUID, Game>` or `Map<UUID, PlayerSession>` for player ownership
- `Map<UUID, Game>` for spawned mob or entity ownership when using MythicMobs, custom NPCs, or boss waves
Avoid relying on world name, inventory contents, scoreboard text, or entity display names as the only source of truth.
## State machine for arena games
Common PvP arena states:
- `WAITING`
- `STARTING`
- `STARTED`
- `ENDED`
Common persistent-brawl states:
- `LOBBY`
- `RESPAWNING`
- `IN_BRAWL`
Common dungeon or route-node states:
- lobby or route-selection phase
- active combat node
- peaceful room, shop, event, or rest node
- victory or cleanup phase
Guidance:
- gate every event listener by state before applying gameplay effects
- when transitioning state, update timers, scoreboards, visibility, player inventory, and protected areas together
- use state changes as the single place to start countdowns, refill tasks, end countdowns, and wave spawning
- if a task only makes sense in one state, it should cancel itself when the game leaves that state
## Player isolation
Multi-arena plugins must isolate more than teleport destinations.
Check these surfaces:
- player visibility with `showPlayer` and `hidePlayer`
- chat recipients, especially in `AsyncChatEvent`
- scoreboard instances and sidebar updates
- tab list names or ranks
- boss bars and title broadcasts
- lobby items and game-only inventory state
- damage, block, item pickup, item drop, interact, teleport, respawn, and quit events
Rules:
- if two players are not in the same visibility group, hide them from each other from both directions
- lobby players should not receive arena chat or arena broadcasts
- arena players should not receive unrelated lobby or other-arena game messages
- spectator players should be excluded from combat, pickup, block, and interaction logic unless explicitly allowed
## Countdown and repeating task lifecycle
Typical tasks:
- start countdown
- end countdown
- chest or resource refill countdown
- scoreboard or sidebar flush
- phase or wave delay
- map rotation ticker
- dirty data flush
Rules:
- store `BukkitTask` handles when cancellation matters
- do not start a second task for the same game concern without canceling or checking the first
- countdown tasks should read the current game state on every tick and self-cancel when stale
- delay tasks should re-check that the game still exists and is still running before acting
- clean up tasks on game end and plugin shutdown
## Loot and resource refill systems
Sky-island and arena plugins often need config-driven resource distribution.
Useful structure:
- group container locations by role, such as island, middle, center, normal, rare, or overpowered
- load weighted loot from config
- build a total loot pool for a group
- shuffle items before placing them
- choose random empty inventory slots with a bounded retry loop
- validate missing or invalid loot config by warning and falling back safely
Container safety:
- load the chunk before reading a configured chest or barrel
- verify the block is a `Container` before casting
- if the configured block is missing, either recreate it intentionally or warn and skip it
- clear previous contents before refill if the design expects a full reset
- avoid heavy loot generation in hot events
## Wave, mob, and route-node systems
Dungeon-style plugins commonly combine route selection with combat waves.
Recommended model:
- keep a queue of pending mob IDs for the current wave
- keep a set of active mob UUIDs for spawned mobs
- register each spawned mob UUID to the owning game
- on mob death, remove it from the active set and unregister ownership
- a wave is clear only when both the pending queue and active set are empty
- after a wave clears, schedule the next wave or return to the route/lobby phase
Route voting guidance:
- only alive and active players should vote or proceed
- prune vote maps against currently active player UUIDs before checking thresholds
- handle missing stage templates with a fallback or a clean victory/end path
- peaceful rooms should have a clear proceed gate instead of silently advancing
Cleanup rules:
- unregister mobs on death, game stop, and plugin disable
- unload old stage maps only after players are safely teleported out
- clear wave queues and active mob sets when a game ends
- never let a stale mob death event advance a completed or deleted game
## Map instance and rotation rules
For copied temporary worlds:
- copy the source map into a unique active folder
- create or load the world from that folder
- configure gamerules after the world exists
- set autosave intentionally
- teleport players out before unload
- unload the world before deleting its folder
- delete old folders after unload and log failures
For rotating shared battlefields:
- warn players before rotation
- snapshot current participants before loading the next map
- move participants to the new spawn and reset transient class or ability state
- refresh scoreboards, tab names, leaderboards, and visibility after rotation
- prevent scoreboard refresh from running every tick if the displayed countdown changes slowly
## Listener heuristics
For every listener touching gameplay state:
- return early if the entity or player has no session
- resolve the owning game once and reuse it
- check the game state before mutating blocks, inventory, health, drops, or scoreboards
- check spectator, safe-zone, team, and combat-tag rules before allowing damage
- keep world-to-game and entity-to-game checks consistent
- do not assume `Player#getKiller()` is enough for custom combat; use combat tags when projectile, skill, or ability damage matters
## Third-party plugin integrations
Common integrations:
- PlaceholderAPI for scoreboards, tab names, or external placeholders
- MythicMobs for custom mob spawning and skills
- menu plugins for lobby selectors
- database libraries for player progression
Rules:
- declare hard dependencies in `depend` only when startup cannot work without them
- use `softdepend` and runtime checks for optional features
- keep optional integration registration isolated from core startup
- when casting external skills or spawning external mobs, handle missing IDs and API failures gracefully
- prefer service boundaries so the rest of the game code does not directly depend on every integration API

View File

@@ -0,0 +1,121 @@
# Persistent Progression And Event Systems
Use this reference when working on long-running Minecraft server plugins where player progress, economy, perks, buffs, quests, or custom combat events persist beyond a single match.
This is most relevant for Pit-style PvP servers, MMORPG-like hubs, persistent brawl modes, faction-adjacent gameplay, and any plugin where the same player profile is mutated by many independent systems.
## Persistent profile boundaries
Large progression plugins often separate durable profile data from temporary runtime state.
Durable examples:
- level, prestige, experience, coins, renown, bounty, stats
- owned perks, selected perks, quest progress, trade limits, mail, medals
- inventory-like storage such as saved kits, ender chests, warehouses, or backups
- schema or profile format version
Runtime-only examples:
- combat timer
- current damage attribution map
- active buffs
- temporary streak counters
- editing mode
- last damage timestamp
- cached live `ItemStack` references
Guidance:
- Make save boundaries explicit with transient fields, ignored serializer fields, or dedicated DTOs.
- Use `UUID` as the durable identity for profile lookup and persistence.
- Avoid saving live Bukkit objects directly unless the project already has a serializer for them.
- Keep a profile version field when the profile shape may migrate over time.
- Prefer maps keyed by internal names for hot lookups such as perks, quests, buffs, or unlock states.
## Economy and progression mutation
Progression features rarely change only one number. A kill reward may affect coins, experience, bounty, streak, quests, assist credit, scoreboard lines, boss bars, and persistence dirty state.
Guidance:
- Route rewards through one service or domain method instead of duplicating math in listeners.
- Fire or call a domain-level event before finalizing rewards if other systems can modify the result.
- Clamp and validate player-facing amounts such as coins, XP, level, health, bounty, or cooldowns.
- Mark profiles dirty after mutation and save in batches when the codebase uses dirty tracking.
- Update visible UI from the main thread after persistence or reward computation completes.
## Custom domain events
Complex plugins often wrap Bukkit events in plugin-specific events such as damage, kill, assist, reward, quest completion, profile loaded, potion effect, or streak-change events.
Use custom events when:
- multiple systems need to observe or modify the same gameplay decision
- raw Bukkit events are too low-level for the plugin's rules
- rewards, assists, buffs, or perks need a consistent extension point
- tests or future features would benefit from a single domain signal
Guidance:
- Keep Bukkit listeners thin: translate the raw event into the plugin's domain model, then delegate.
- Define whether the custom event is cancellable or mutable.
- Document whether listeners may change damage, rewards, target state, or side effects.
- Avoid firing custom events from async threads if handlers may touch Bukkit state.
- Prevent recursion when a custom event handler triggers another raw Bukkit action.
## Perk, buff, quest, and event registries
Feature-heavy plugins often use factories or registries to discover and expose extension modules:
- perk registries grouped by interfaces such as damage, kill, assist, respawn, shoot, or tick hooks
- buff registries with optional Bukkit listener registration
- timed event registries split into normal and major events
- quest or medal registries keyed by stable internal names
Guidance:
- Require a stable internal name for every registered module.
- Keep display names separate from internal names so localization or formatting does not break saved data.
- Register Bukkit listeners only for modules that actually implement `Listener`.
- Prefer explicit registration when startup reliability matters; use reflection scanning only if the project already relies on it.
- Fail loudly on duplicate internal names.
- Expose read-only views when other systems only need to inspect registered modules.
## Timed events and rotating server activity
Long-running servers often schedule global events independently of arena matches.
Guidance:
- Store active event, next event, cooldown, and preparation phase explicitly.
- Separate preparation, active, inactive, and cleanup hooks.
- Avoid overlapping global events unless the design supports it.
- Keep boss bar, scoreboard, and broadcast updates tied to the active event state.
- Cancel or expire timers when the event is replaced, forced, or the plugin disables.
- Use configured online-player thresholds before starting high-impact events.
## Version and dependency boundaries
Persistent server plugins often depend on multiple optional and required plugins such as permissions, economy, points, protocol, world edit, database drivers, packet libraries, or custom server forks.
Guidance:
- Keep `plugin.yml` `depend` and `softdepend` aligned with startup code.
- Detect optional dependencies before using their APIs.
- Provide a degraded path or clear warning when a soft dependency is absent.
- Isolate NMS, packet, reflection, and custom fork calls behind adapters or utility classes.
- Check the target Minecraft version before adding APIs that do not exist on that server line.
## Review checklist
Before finishing changes to persistent progression code, verify:
- profile mutations happen through the intended service or domain method
- durable and runtime fields are not accidentally mixed during serialization
- custom events fire once and on the correct thread
- perk, buff, quest, or event modules have stable internal names
- duplicate modules cannot silently overwrite each other
- economy and progression changes update UI and dirty-save state consistently
- optional dependencies are checked before use
- scheduled global events clean up boss bars, tasks, and active state

View File

@@ -0,0 +1,143 @@
# Real Project Patterns
This reference captures architecture patterns observed in real Paper gameplay plugins:
- match-heavy, multi-phase minigames with maps, teams, power selections, boss waves, scoreboards, and match-mode overlays
- class-based persistent brawl modes with hero progression, map rotation, player data services, and async leaderboard refreshes
Use this file when the user asks for repo structure, feature placement, or how to organize a growing Minecraft plugin.
## Pattern 1: Central plugin bootstrap with explicit subsystems
Both styles keep one main plugin class responsible for constructing and wiring core subsystems.
Observed examples:
- A match-heavy minigame wires database, config managers, game manager, match manager, GUI objects, sidebar services, listeners, commands, and repeating tasks from `onEnable`.
- A persistent class-brawl plugin wires config, progression, database, repository, player data service, scoreboard manager, map manager, hero registry, hero service, game service, and hero skill handler from `onEnable`.
Guidance:
- Keep plugin startup readable and ordered.
- Construct core services before registering listeners or commands that depend on them.
- Treat the main plugin class as an orchestration root, not a dumping ground for gameplay logic.
## Pattern 2: Stateful gameplay belongs in dedicated session or manager objects
Both styles avoid storing all gameplay data directly on listeners.
Observed examples:
- Match-heavy minigames use `GameManager`, `MatchManager`, `Game`, `PlayerSession`, `GamePhase`, and `MatchSession` style objects.
- Persistent class-brawl modes use `GameService`, `PlayerSession`, `PlayerState`, `HeroService`, and `MapManager`.
Guidance:
- Put long-lived state into session, manager, or service classes.
- Keep listeners thin: validate the event, delegate to the relevant service, and exit.
- Prefer explicit gameplay state models over scattered flags.
## Pattern 3: Complex plugins grow by domain modules
As plugin scope grows, both styles split features into domain-oriented packages.
Observed match-heavy minigame domains:
- `Command`
- `Game`
- `Match`
- `GameConfig`
- `InitialListener`
- `Shop`
- `tasks`
- `Database`
Observed persistent class-brawl domains:
- `bootstrap`
- `command`
- `data`
- `game`
- `hero`
- `listener`
- `map`
- `gui`
Guidance:
- Group by gameplay domain or subsystem, not by vague “utils” buckets.
- Split once a feature has its own state, configuration, and lifecycle.
- Keep package names aligned with how you reason about the game.
## Pattern 4: Two valid architecture styles emerge
Match-heavy minigames lean toward manager-centric gameplay orchestration.
- Good fit for:
- rounds
- team elimination
- map-specific game instances
- match overlays on top of base game rules
Persistent class-brawl modes lean toward service-centric runtime orchestration.
- Good fit for:
- a persistent combat lobby
- class selection and respawn loops
- progression and player data integration
- rotating maps with a shared game mode
Guidance:
- Prefer manager-centric architecture for per-match or per-arena instances.
- Prefer service-centric architecture for one persistent shared mode with reusable systems.
## Pattern 5: Real plugins need both gameplay and operational layers
Real gameplay plugins combine gameplay code with operational subsystems:
- config loading
- database connectivity
- scoreboard refreshes
- map loading
- command and listener registration
- shutdown cleanup
Guidance:
- Do not design only the combat mechanic.
- Also design startup, shutdown, persistence, UI refreshes, and failure paths.
## Pattern 6: Instance-heavy plugins need explicit isolation layers
SkyWars-style and dungeon-style plugins need a clear boundary around each active game instance.
Common isolated surfaces:
- player-to-game session ownership
- world or map instance ownership
- temporary mob or entity ownership
- game-specific chat recipients
- visibility groups
- per-game scoreboards and titles
- resource refill or wave timers
Guidance:
- Treat `Game` as the owner of anything that should not leak into another arena.
- Keep global managers responsible for lookup and lifecycle, not for every gameplay rule.
- When adding a new listener, first decide whether it is global, lobby-only, or game-instance-only.
- If the plugin can run multiple games at once, every event should resolve the owning game before mutating gameplay state.
## When to reuse which pattern
- New minigame with rounds, teams, and map instances:
- prefer `GameManager` + `Game` + `PlayerSession` + optional `MatchManager`
- Persistent PvP lobby with class selection and map rotation:
- prefer `GameService` + `PlayerSession` + `HeroService` + `MapManager`
- SkyWars-style isolated arena:
- prefer `GameManager` + `Game` + `GameMap` + countdown tasks + per-game scoreboard and loot managers
- PvE dungeon or wave progression:
- prefer `Game` + lobby map + `RoundManager` or stage manager + entity ownership map + explicit cleanup hooks
- Plugin already large and messy:
- use the domain package split pattern before adding more features

View File

@@ -0,0 +1,121 @@
# State, Sessions, And Phases
Use this reference when designing player state, match flow, cooldowns, respawn logic, class selection, or round progression.
## Session modeling in real plugins
### Rich minigame session
Feature-heavy minigames often keep rich per-player runtime state in `PlayerSession`, including:
- current team
- kill and final kill counters
- death flag
- persistent potion-like buffs
- selected brands grouped by category
- per-brand cooldowns
- special item cooldowns
This is a strong pattern for feature-heavy minigames where one player can carry many temporary gameplay modifiers.
### Lean persistent-brawl session
Persistent brawl modes often keep a leaner `PlayerSession`, including:
- `PlayerState`
- selected hero
- fall protection state
- safe-zone tracking
- generic cooldown map
This is a strong pattern when the plugin has one dominant game loop and most feature-specific behavior lives in services.
## Rule 1: model player state explicitly
Do not infer state from inventory contents, world, or scoreboard text alone.
Prefer an enum or similarly explicit model such as:
- `LOBBY`
- `RESPAWNING`
- `IN_BRAWL`
- queueing
- spectating
- eliminated
- reconnect-pending
## Rule 2: use sessions as the source of temporary truth
Good session contents:
- cooldowns
- selected class or hero
- selected team
- in-round modifiers
- safe-zone knowledge
- reconnect markers
- fall protection or spawn protection
Avoid placing these as scattered listener fields.
## Rule 3: gameplay phases deserve first-class objects
Observed match-heavy minigame pattern:
- `GamePhase`
- scheduled phase transitions
- boss and bed-destruction events tied to phases
- phase-dependent spawners and world border changes
Guidance:
- for round-based modes, use a phase enum or state machine
- trigger map-wide events from phase transitions, not random listeners
- keep the “what happens at phase N” logic centralized
## Rule 4: match overlays should not be mixed blindly into normal rounds
Observed match overlay pattern:
- `MatchManager`
- `MatchSession`
- separate handling for participants, observers, reconnects, brand rules, and round interception
Guidance:
- if tournament or match mode changes the base game rules, isolate it behind a dedicated manager
- keep “normal game” and “match override” behavior composable, not tangled
## Rule 5: reconnect and cleanup paths matter
Observed concerns:
- match participants may disconnect and reconnect
- game sessions can outlive the current online `Player` object
- cleanup must happen on quit, death, round end, and plugin shutdown
Guidance:
- prefer `UUID` for durable identity
- if you must keep live `Player` references, also define detach/reattach behavior
- always think through quit, reconnect, death, respawn, and shutdown together
## Rule 6: cooldowns should be centralized
Observed patterns:
- Some match-heavy minigames tick cooldown maps during stage task updates
- Some persistent-brawl modes store cooldown expiry timestamps in session data
Choose based on your mode:
- tick-down integers:
- good for per-second synchronized gameplay pacing
- expiry timestamps:
- good for lightweight cooldown checks across arbitrary actions
## Practical design heuristic
If your feature changes what a player is allowed to do for some period of time, it probably belongs in session state.
If your feature changes what the whole match is doing, it probably belongs in game or match state.