
Список изменений
Allium v0.2.10a
Wiki: https://github.com/castledking/Allium/wiki
Highlights
- v0.2.9a shipped an unshaded jar and could not enable — The published v0.2.9a download was the thin jar with no bundled libraries, so every server failed on startup with
NoClassDefFoundError: com/zaxxer/hikari/HikariConfig. The plugin code was fine; the release pipeline picked the wrong file. If you downloaded v0.2.9a, replace it with this build. - No longer crashes on servers older than the API it was built against — Allium compiles against a recent Paper API but supports servers back to 1.21.1. Several references to blocks, attributes, game rules and entity types that only exist in newer versions were throwing
NoSuchFieldErrorat runtime — disabling the tree axe, the locator-bar tab list,/heal, handcuffs and creative-mode potion restrictions, and in some cases the whole plugin. Every such reference now resolves safely by name. - A full audit, not a spot fix — Rather than fixing only the crash that was reported, every
Material,Attribute,EntityType,GameRule(and every other Bukkit registry) constant referenced anywhere in the plugin was checked against the actual 1.21.1 API. All offenders are listed below and the jar is verified to load clean on 1.21.1.
Technical Details
The v0.2.9a release jar was the wrong artifact
mvn install leaves two jars in target/:
| file | size | contents |
|---|---|---|
Allium.jar | ~4.99 MB | shaded, HikariCP and H2 relocated under codes/castled/allium/libs/ |
Allium-0.2.9a.jar | ~2.04 MB | thin jar, 571 entries, no bundled libraries |
The release workflow selected the upload with find target -name "Allium*.jar" ... | head -n1. That pattern matches both, and find returns them in directory order rather than any meaningful one, so the build published the thin jar. Its class files still reference the un-relocated com.zaxxer.hikari package, hence the failure inside Database.<init> the moment onEnable reached the database layer.
The workflow now targets target/Allium.jar — the shade plugin's <finalName> — explicitly, and fails the build outright if that file is missing or if it does not contain codes/castled/allium/libs/hikari/HikariConfig.class. A silently thin jar cannot reach a release page again.
Worth knowing for anyone building locally: pom.xml declares <project.build.finalName> inside <properties>, which does nothing — the real setting is <build><finalName>. That mismatch is why two differently named jars exist side by side. Always take target/Allium.jar.
NoSuchFieldError on newer-than-runtime registry constants
Allium compiles against a much newer Paper API than the oldest server it supports at runtime (1.21.1). Every direct reference to a Material, Attribute, GameRule, EntityType or similar constant that only exists in a later version compiles cleanly and then throws NoSuchFieldError the moment the JVM resolves it. When the reference sits in a static field initialiser, the failure lands in <clinit> and the class can never be constructed — so a single missing constant can disable a whole feature or, if it is on the enable path, the entire plugin. (try/catch around the call does not help: NoSuchFieldError is an Error, not an Exception, and the existing handlers only caught Exception.)
Each offender was fixed at the reference, not by lowering the compile target:
- Tree axe —
PALE_OAK_LOG(1.21.4+),BUSH/FIREFLY_BUSH/LEAF_LITTER/WILDFLOWERS/SHORT_DRY_GRASS(1.21.5+).TreeAxeManagerbuilt its material sets from hard enum constants in static initialisers, so constructingTreeAxeListenercrashed and tookPluginStart.registerCommands— and the plugin — down. Now built through amaterials(String...)helper that resolves each name withMaterial.matchMaterialand skips what the server does not know. This also matches how the rest of the class already worked (getValidGroundTypeshas always switched on material names). - Locator bar —
GameRule.LOCATOR_BAR(1.21.6+). Read in four places, including a repeating global task (retryTabListManagerInit) that loggedGlobal task ... generated an exceptionevery cycle, and inPartyManagerplayer-visibility logic. All four now go throughApiCompat.isLocatorBarEnabled(world), which resolves the rule withGameRule.getByNameonce and reportstrue(the previous default) when the rule is absent. /healand handcuffs —Attribute.MAX_HEALTH/MOVEMENT_SPEED(renamed fromGENERIC_*in 1.21.3).ApiCompatresolves whichever spelling the server has via reflection;/healfalls back to 20 hearts and the handcuffs item skips the slow-down modifier when neither exists, rather than crashing the command or the item build.- Creative-mode potion restriction —
EntityType.SPLASH_POTION/LINGERING_POTION(split fromPOTIONin 1.21.5).CreativeManagernow comparesevent.getEntityType().name()as a string, so it loads on any version; older servers deliver both kinds asPOTIONand it treats them as splash potions. - Waypoint attribute —
Attribute.WAYPOINT_TRANSMIT_RANGE(1.21.6+). The Citizens NPC re-enforcement loop resolves it throughApiCompatand simply does nothing on servers that lack it.
The shared ApiCompat helper (codes.castled.allium.util.ApiCompat) centralises these lookups so future version-gated constants have one obvious home.
Verifying nothing else in the plugin has the same problem
Rather than guessing which constants were too new, this was done mechanically for every registry, not just Material: every getstatic org/bukkit/** reference was extracted from the compiled classes and diffed against the actual 1.21.1 paper-api. That covered Attribute, EntityType, Enchantment, Particle, Sound, Statistic, PotionEffectType, GameRule and ~25 others.
Two categories of false positive were ruled out:
- javac enum
switchtables. Aswitchover an enum compiles to a synthetic$SwitchMapwhose every entry is individually wrapped in aNoSuchFieldErrorhandler (e.g. 2,154 such handlers inFireballExplosion), so unknown constants are skipped as the table is built. Thesegetstatics are always immediately followed byordinal()and were filtered out. - Mixed-case constants like
ServicePriority.Highest— present in 1.21.1, only flagged by an uppercase-only extraction pass.
After the fixes, the sweep reports zero genuine missing references. As a final check the shaded jar was loaded against a real 1.21.1 paper-api and every previously-crashing class (TreeAxeManager, HandcuffsItem, Heal, CreativeManager) was force-initialised successfully, with ApiCompat resolving MAX_HEALTH → GENERIC_MAX_HEALTH and WAYPOINT_TRANSMIT_RANGE → null as expected.
Verified across the 1.21.1 API; the same mechanism degrades cleanly on every version up to the 1.21.11 API also present in the build environment.
