▶️ ЗАБЕРИ СВОИ 8 ПОДАРКОВ 🎁 ПРИ СОЗДАНИИ СВОЕГО МАЙНКРАФТ СЕРВЕРА
Allium

Allium

A modern, secure Essentials solution

Оцените первым
135
1
Все версииAllium v0.2.9a

Allium v0.2.9a

Release20.07.2026

Список изменений

Allium v0.2.9a

Wiki: https://github.com/castledking/Allium/wiki

Highlights

  • Allium Harvest — A new module for custom crops and spawner model overlays. Crops render as display entities using any Nexo or Oraxen model, so they never occupy a block state. Includes growth paths, fertilizers, sprinklers, an optional soil lifecycle, a developer API and 11 Bukkit events. Full documentation on the wiki.
  • Spawner models never touch the spawner block — Per-entity-type visual overlays for vanilla spawners. The real block is never replaced, hidden or altered, so mob spawning, silk touch, redstone and every other plugin's view of it stay exactly vanilla.
  • /minecraft: commands work again with a granted permission node — Fixed the v0.2.7a permission resolver denying vanilla commands to non-op players who held the matching minecraft.command.* node.
  • Class preloading — Allium now loads its own classes at startup, removing the NoClassDefFoundError failure mode that hit when the jar was replaced under a running server.
  • H2 is the only bundled JDBC driver — SQLite and MySQL are still supported but their drivers are no longer shipped, cutting roughly 14 MB from the jar.

Technical Details

Allium Harvest

81 classes, ~8,300 lines, 66 unit tests. Everything is configured under plugins/Allium/harvest/:

harvest/
  config.yml            storage, engine pacing, soil, sprinklers, spawners
  crops/tomato.yml      one file per crop
  fertilizers.yml       quality / yield / speed / variation / retainers
  sprinklers.yml        sprinkler tiers
  spawner-models.yml    per-entity-type spawner overlays

Mutual exclusivity is the idea the whole system is built around. Two places choose between outcomes — the growth path at planting, and the quality tier at harvest — and both must pick exactly one. mode: WEIGHTED_ONE draws a single number across the summed weights and walks entries until it lands, so one roll produces one winner. mode: INDEPENDENT rolls each entry separately, which is correct for bonus drops like seed returns but wrong for quality tiers, where it hands out a regular tomato and a silver-star tomato from the same harvest. Setting INDEPENDENT on a primary: table produces a reload warning naming the exact YAML path. Five loot modes ship: WEIGHTED_ONE, WEIGHTED_MULTIPLE_WITHOUT_REPLACEMENT, INDEPENDENT, GUARANTEED_ALL and SEQUENCE.

Growth paths are rolled once at planting and written to the database immediately, so a path survives restarts, chunk cycling, reloads and reconnects and is never re-rolled. Because a path controls both models and drops, a rare path can reuse the common path's early models and only reveal itself at maturity — players cannot tell a golden tomato from a normal one while it grows.

Multi-block crops occupy more than one cell via footprint:, given as offsets from the anchor. Cells are reserved transactionally: every target is checked before any is claimed. If the footprint is obstructed when the final stage comes due, the crop matures at its previous single-block stage rather than overwriting a player's blocks.

Growth clocks are REAL_TIME (wall-clock, including while unloaded) or LOADED_TIME (frozen on unload). Real-time crops catch up when their chunk returns, bounded by maximum-catch-up-stages so a year-old farm does not resolve a thousand stages at once; time still owed after the cap resumes rather than being discarded.

Fertilizers carry any combination of five effects — quality, yield, growth.speed-multiplier, variation and soil.retain-for. There is no built-in concept of levels; each tier is a separate entry tuned on its own numbers. quality and variation multiply weights before the draw, so they shift the odds toward rare outcomes while it stays one roll. variation and soil only work when applied to soil rather than to a growing crop, because the path is rolled the instant the seed goes in — applying one to an existing crop tells the player it must be worked into the soil first. yield deliberately does not touch additional: tables, since multiplying seed returns would let a yield fertilizer pay for itself.

Soil lifecycle is off by default. When enabled, a block starts its clock the first time something is planted on it — not when the farmland is placed — so enabling it does not write a row for every farm block on the server. Once lifetime elapses the block refuses new plantings until fed by a retainer; crops already growing are never destroyed. The record outlives the block for forget-exhausted-after, so breaking and replacing farmland resumes the same timer instead of resetting it. There is no scanning task at all: expiry is a stored timestamp compared against the current time on read, which makes wear exact across restarts and unloaded chunks at zero per-tick cost.

Sprinklers are ordinary placed blocks that Allium remembers. Overlapping sprinklers stack multiplicatively with each other and with speed fertilizers — two 0.8 sprinklers give 0.64, not 0.6 — so each source keeps its own plain meaning instead of needing a lookup table. A floor of 5% of the configured duration means no amount of stacking makes crops instant, and maximum-per-chunk (default 64) bounds coverage lookups so a player cannot degrade performance by carpeting a chunk.

Clickable crops. Display entities have no hitbox of their own, so a click aimed at one passes straight through. crop-visuals.clickable pairs each crop with an invisible Interaction entity sized by interaction-width / interaction-height, letting the plant itself be right-clicked to fertilize or harvest and punched to break — with no real block standing in for it.

Spawner models

Per-entity-type overlays configured in spawner-models.yml, with variants: selecting by stack size (highest qualifying minimum-stack wins). Stack size comes from a SpawnerProvider service; without a stacking plugin registered, every spawner reads as size 1.

The real spawner block is never replaced, never set to air, and its behaviour is never altered — the model is one ItemDisplay positioned to cover the cage. A model smaller than a full block leaves the cage partly visible by design, so models should be authored to cover it rather than expecting the plugin to hide it.

Visuals are derived state and the database row is authoritative. On chunk load, break, explosion, type change, restart or /harvest spawner refresh, the plugin converges to exactly one correct display per tracked spawner: orphaned entities with no record are removed and duplicates culled. Because reconciliation is idempotent, restarts and reloads never duplicate models. Discovery on chunk load reads the chunk's block-entity list rather than iterating blocks, bounded by maximum-block-entities-per-tick.

Harvest performance model

There is no task per crop, per spawner, per sprinkler or per soil block. One repeating async scan finds crops whose due time has passed and dispatches each stage advance to the region thread owning it, bounded per pass by growth-engine.checks-per-tick. Growth never forces a chunk load — only loaded chunks are scanned. All SQL runs on a dedicated single-thread executor with writes coalesced between flushes, so nothing touches the main or region threads. Folia is supported throughout: world and entity work goes to the correct region scheduler, and no Bukkit world state is touched asynchronously.

Crop display entities are not persisted as entities. They are non-persistent and rebuilt from the database on chunk load, which is why a crashed server never leaves orphaned models behind.

/minecraft: commands denied despite a granted permission node

v0.2.7a introduced a command permission resolver pipeline to stop CommandManager overriding native Brigadier permissions. A player granted minecraft.command.kill through LuckPerms still got You don't have permission to use /minecraft:kill.

The cause was adapter ordering against two disagreeing permission models. BrigadierCommandPermissionAdapter runs before BukkitCommandPermissionAdapter, and for a vanilla command it reflected out the Brigadier CommandNode and tested its requires predicate. Vanilla's predicate is an op-level check — source.hasPermission(2) — that knows nothing about the minecraft.command.<name> node CraftBukkit registers as the same command's Bukkit permission. So the Brigadier adapter returned a denial before the Bukkit adapter, which would have checked that node and passed, ever ran.

For vanilla commands the Bukkit permission now wins: if command.getPermission() is set and testPermissionSilent(player) passes, the adapter returns allowed with ResolutionType.VANILLA immediately. Otherwise it falls through to the existing requirement test unchanged. Vanilla detection uses three signals — the wrapper class name, a helpCommandNamespace of minecraft, or the pipeline resolving the owning namespace to minecraft — so it works whether the command arrives as /kill or /minecraft:kill.

This was deliberately not wired to allium.hide.bypass. That permission governs tab-completion visibility of namespaced commands, and treating it as a permission override would mean anyone who can see minecraft: commands can also run all of them. Reading the real permission node keeps LuckPerms authoritative.

Covered by a regression test (vanillaBukkitPermissionBeatsOpLevelBrigadierRequirement) reproducing the exact case: a kill node with a false requirement plus a granted minecraft.command.kill.

Class preloading

A plugin jar is read lazily — a class is only pulled off disk the first time it is needed. Replacing Allium.jar on a running server leaves the classloader pointing at a file whose contents changed underneath it, so anything not yet loaded fails with NoClassDefFoundError. The typical victim is a nested class used only when a particular message is first formatted, and the typical moment is inside the restart command being used to apply the update.

ClassPreloader walks the jar during onEnable and resolves every codes.castled.allium.* class with initialize = false. That reads and defines each class — the part that needs the jar — without running static initialisers, so no side effects fire early and nothing observable about startup changes. Initialisation still happens naturally on first real use, by which point the class no longer needs the file on disk. Costs roughly 150ms; toggle with preload-classes in config.yml.

This is damage control, not a licence to hot-swap. It cannot help with classes owned by other plugins or the server, cannot make a running instance pick up new code, and cannot help if the jar is replaced before it runs. The supported update path is still plugins/update/ plus a restart.

Build and dependencies

H2 is the only bundled JDBC driver. It backs both the core plugin database and the default harvest storage, ships shaded and relocated, and works with no downloads. SQLite and MySQL are still supported backends but their drivers are no longer bundled — together they cost roughly 14 MB, most of it SQLite native binaries for CPU architectures a given server will never run. Admins supplying them put the jar in the server's libraries/ folder; a missing driver produces an explicit startup message naming it and where to put it, rather than an opaque connection failure. Hikari and H2 are both relocated so they cannot clash with another plugin's copy.

snakeyaml 2.3 pinned explicitly. DiscordSRV ships a shaded fat jar bundling a snakeyaml predating LoaderOptions#setMaxAliasesForCollections. Declaring the modern version ahead of DiscordSRV keeps Bukkit's YamlConfiguration working on the compile and test classpath.

Nexo added as a provided dependency and softdepend alongside Oraxen. Items resolve through the plugins' public APIs rather than by running /nexo give or similar, and integrations register only when the plugin is actually enabled — Allium runs fine with neither installed. To try Harvest vanilla-only, replace the nexo: references in the shipped configs with items such as minecraft:wheat_seeds.

Commands and permissions

/harvest (alias /hf) with reload, give, crop, spawner, soil and debug subtrees. allium.harvest.admin grants everything. Player-facing nodes default to true: allium.harvest.crop.plant, allium.harvest.crop.harvest, allium.harvest.sprinkler.place. Admin nodes default to op.

/harvest crop plant <crop> [path] forces a specific path, which is the fastest way to test a rare variant without rolling for it.

Reload is best-effort rather than all-or-nothing: definitions that parse cleanly are activated and entries with fatal problems are skipped, so one broken crop does not block every other change in the file. Errors name the file, the exact YAML path and the problem. storage.* and enabled still need a full restart.

Файлы

Allium.jar(4.65 MiB)
Основной
Скачать

Метаданные

Канал релиза

Release

Номер версии

0.2.9a

Загрузчики

Bukkit
Folia
Paper
Purpur
Spigot

Версии игры

1.20–26.2

Загрузок

1

Дата публикации

20.07.2026

Загрузил

ID версии

Главная