A reference Bukkit/Paper plugin that exercises every public extension surface of the MyPet 4 API. Each demonstrated feature lives in its own focused file so a third-party developer can grep for the API surface they care about and read 20–60 lines.
Every registration in ExamplePlugin#onEnable corresponds to one extension
point of the MyPet 4 API. The package layout (skills/, upgrades/,
requirements/, leashing/, experience/, listeners/, commands/) maps
1:1 to the API packages.
| API surface | Demo file | What it does |
|---|---|---|
SkillManager#registerSkill |
skills/GlowImpl.java |
Per-pet on-hit skill that applies the vanilla GLOWING effect. |
SkillManager#registerUpgradeParser |
upgrades/GlowUpgrade.java |
Reads the skill's per-level config out of .st.json. |
SkillManager#registerCodec + Skill#getState + Skill#applyState |
skills/Glow.java (State record), skills/GlowImpl.java, ExamplePlugin.java |
Persists Glow's runtime trigger count across server restarts. One codec owns both directions of the NBT round-trip. |
SkilltreeManager#registerRequirement |
requirements/MinHealthRequirement.java |
Custom skilltree gate: pet must have ≥ N max-health. |
LeashFlagManager#registerLeashFlag |
leashing/HealthBelowFlag.java |
Custom leash gate: target mob must be at ≤ N% HP. |
PetExperience#addModifier (ExperienceModifier) |
experience/PermissionBonusXpModifier.java |
Per-pet XP boost gated by a permission. |
Bukkit event bus on Pet*Event |
listeners/PetEventLogger.java |
One handler per event flavor: lifecycle wire-up, observation, veto-with-cooldown, mutation, visual side-effect. |
MyPetApi.getPetManager() / getPlayerManager() + StoredPet#skillState (live + persisted) |
commands/PetStatsCommand.java |
/petapiexample reads live and persisted state; demonstrates the scheduler hop required after getStoredPets(...) so the continuation doesn't crash on Folia. |
The @SkillName, @RequirementName, @LeashFlagName annotation pattern is
the same across every registry: the manager walks the class hierarchy to find
the annotation and uses its value as the lookup key. Once you know the recipe
for one, you know the recipe for all of them.
| File | Purpose |
|---|---|
skills/Glow.java |
Skill interface with @SkillName("Glow"). Extends Skill, OnHitSkill. Declares the upgrade-aware getters and the nested State record. |
skills/GlowImpl.java |
Skill implementation. Constructed once per pet via the (Pet) constructor. Exposes its trigger count through getState / applyState; the codec handles all NBT I/O. |
upgrades/GlowUpgrade.java |
The data carrier — one instance per Upgrades.<level> block. |
requirements/MinHealthRequirement.java |
@RequirementName("MinHealth") — gates a skilltree on pet.getMaxHealth(). |
leashing/HealthBelowFlag.java |
@LeashFlagName("HealthBelow") — gates leashing on the target's HP percent. |
experience/PermissionBonusXpModifier.java |
ExperienceModifier subclass; per-pet permission-gated XP boost. |
listeners/PetEventLogger.java |
Listener wired to PetActivatedEvent, PetLevelUpEvent, PetCallEvent, PetExpEvent, PetFeedEvent. |
commands/PetStatsCommand.java |
/petapiexample [stats|list|stored] reads live + persisted pet state. |
ExamplePlugin.java |
Bukkit plugin entry. Calls each registry once, in order. |
resources/plugin.yml |
Bukkit plugin descriptor. depend: [MyPet] is mandatory. |
resources/glow-example.st.json |
Sample skilltree referencing the new Glow skill, the new MinHealth requirement, and the built-in Damage skill. |
Skilltree JSON files put requirements in a top-level Requirements array. The
loader splits each entry on : — head is the requirement name, tail is fed
into a Settings instance. Read keyed values via the typed accessors
(settings.getInt("min"), settings.getDouble("min"), settings.getString("name"),
settings.getBoolean("strict")) — each returns an Optional so a missing key
short-circuits cleanly.
"Requirements": [
"PetLevel:min=5",
"MinHealth:min=20"
]See glow-example.st.json for a working example.
Leash flags are configured per pet type in MyPet's config.yml, not in
skilltree files. The format is the same name:k=v:k=v shape as requirements.
MyPet:
Pets:
Wolf:
LeashRequirements:
- "HealthBelow:percent=50"When the leash attempt fails, the plugin renders the flag's
getMissingMessage Component to the player.
The MyPet API is consumed as a published snapshot artifact:
compileOnly("de.keyle:mypet-api:4.0.0-SNAPSHOT")A plain ./gradlew build is sufficient — no sibling project needs to be built
first. Output: build/libs/MyPetAPIExample-1.0.0.jar.
Because 4.0.0-SNAPSHOT is a changing version, Gradle caches it for 24h by
default. To pick up a freshly-published API change immediately:
./gradlew build --refresh-dependenciesTo iterate against the working tree of a sibling MyPet/ checkout instead of
the published snapshot, swap the dependency line for
compileOnly(files("../MyPet/api/build/libs/api.jar")) and run
./gradlew :api:assemble in ../MyPet first.
There is no test framework configured; ./gradlew build (which runs
compileJava, processResources, and jar) is the only verification step.
- Drop
MyPetAPIExample-1.0.0.jarinto a Paper server'splugins/folder alongside MyPet. - Copy
glow-example.st.jsonfrom this project's resources into the server'splugins/MyPet/skilltrees/folder. - (Optional) Add
HealthBelowto a pet type'sLeashRequirementsin MyPet'sconfig.ymlto exercise the leash-flag gate. - Restart the server. The console should log every registration:
[MyPetAPIExample] Glow skill, upgrade parser, state parser, MinHealth requirement, HealthBelow leash flag, event listener and /petapiexample command registered. - In game:
- Tame a mob and assign the example tree:
/mypet skilltree assign glow-example. - Hit a mob to see Glow trigger; run
/petapiexample statsto see the live trigger count climb. - Run
/petapiexample listto see all active pets server-wide, or/petapiexample storedto fetch your stored pets and see their persisted trigger count. - Grant
mypetapiexample.xpboostto a player to give their pet a +10% XP multiplier.
- Tame a mob and assign the example tree:
- Annotation-driven registration is uniform. Every registry uses the same
shape: an annotation on the type carries the canonical name, and the manager
walks the class hierarchy to find it. Renaming a skill / requirement / leash
flag is one annotation change. Missing or duplicated annotations now fail
server boot with an
IllegalArgumentExceptioninstead of a silent warning — catch the exception at registration time, or fix the annotation. - One codec owns both directions of persistence.
SkillManager#registerCodectakes aSkillStateCodecwhosewriteandreadmethods sit next to each other in the same object — so the NBT key names a stateful skill uses live in exactly one place. The live skill produces the state forwriteviaSkill#getStateand absorbs the state fromreadviaSkill#applyState. Settingsis positional-string, not YAML. BothRequirementandLeashFlagparse the samekey=value:key=valueshape. Read keyed values via typed accessors (getInt,getDouble,getString,getBoolean) — each returns anOptionalfor the missing-key case. For positional iteration over keyless entries, callsettings.entries(). Usesetting.asString()only when the syntax doesn't fit a typed accessor (e.g. a"50%"suffix). The keys are lower-cased.getStoredPetsreturns aCompletableFuturethat completes off-thread. Touching Bukkit API in the continuation is undefined behavior on Paper and crashes on Folia. Hop to the player's region scheduler — seePetStatsCommand#showStoredfor the canonical recipe.PluginHookis also a public surface but isn't demonstrated here (writing one requires a real external plugin to wrap). Seede.Keyle.MyPet.api.util.hooks.typesfor the available hook interfaces:AllowedHook,BeaconHook,EconomyHook,FlyHook,LeashEntityHook,LeashHook,MonsterExperienceHook,MountInsideHook,PartyHook,PermissionGroupHook,PlayerVersusEntityHook,PlayerVersusPlayerHook,VanishedHook.