mirror of
https://github.com/github/awesome-copilot.git
synced 2026-04-30 12:15:56 +00:00
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:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user