GlymeraLumberTower - Changelog
v3.0.0 (2026-05-30)
Performance fix. The fire-tick tree search was far too expensive and scaled badly with many towers — on a populated server (for example 10 players x 3 towers each) the old cost could saturate the world's main thread and cause server-wide lag.
Root cause
- The "nearest trunk" search brute-force scanned the entire fireRadius cube on every attempt. At the default fireRadius=60 that is a 121x121x121 box — roughly 1.8 million block reads per scan — and it runs on the world's main (tick) thread.
- A tower that had free chest space but no tree left in range never started its cooldown (only a successful shot did), so it re-scanned that whole cube every single second, forever. Idle towers were the most expensive ones of all.
Fix
- Single rate gate. Every scan attempt — hit, miss, or full chest — now stamps the per-tower timer, so a tower scans at most once per cooldownMs (default 5 s) instead of every second. This removes the endless idle re-scan.
- Ring search with early-out. The cube scan is replaced by an outward Chebyshev-ring search. Because every block in ring d is at least d blocks away, once a candidate is found that is closer than the current ring the search stops — the work is now proportional to the distance to the nearest tree (typically a few hundred reads), not to the cube volume. It still returns the exact nearest bottom-trunk.
- Per-column early break at the tree base, plus a tighter inner loop.
- Default fireRadius lowered 60 to 32. Existing config.json files keep their saved value; lower it there as well for the full benefit in the worst case (a tower with no tree anywhere in range).
Result
- With a tree in range, per-scan cost drops by roughly one to two orders of magnitude.
- Idle towers drop by a further ~5x from the rate-gate fix.
- Many towers no longer pile their scans onto the single main thread.
No gameplay changes: harvesting, replanting, drop vacuuming, the recipe and the tower-break protection all behave exactly as before.
v1.0.0 (2026-05-21)
Initial release.
Tower Structure
- Static 9-block-tall tower placed via a single inventory item: 17x Rock_Gold_Brick_* blocks plus 4x Furniture_Dungeon_Chest_Epic as the foot
- Default layout shipped as default-template.json inside the JAR, extracted on first start
- Re-capturable via /lumbertower capture and /lumbertower captureat
Automated Tree Harvesting
- Fire-tick loop runs every 1 second; per-tower cooldown is 5 seconds between shots
- 60-block Chebyshev cube scan picks the nearest bottom-trunk in range
- Green particle bolt flies from the tower tip to the trunk at ~50 blocks/s, with shot and impact sounds
- On impact the trunk column is felled top-to-bottom via world.breakBlock(x, y, z, 0x100)
- Trunk drops are spawned manually via ItemComponent.generateItemDrops
- Pre-shot check pauses firing when all four chests are completely full
Drop Vacuuming
- 1.5 seconds after a successful chop, the tower scans all item entities in its fire radius
- Filter accepts only tree-related item ids — unrelated drops stay put
- Round-robin chest insertion; items that don't fit anywhere stay on the ground
- Visual feedback: a small green particle burst at the tower tip on each vacuum cycle
Automatic Replanting
- On each successful chop, plants a Plant_Sapling_<WoodType> on the nearest soil block within radius 5
- One-sapling-per-tree gate: skips the replant if any sapling already sits within 10 blocks
- Soil detection uses the Type=Soil tag rather than the Soil_* name prefix
- Species with no matching sapling (Burnt, Fir) are silently skipped
Atomic Tower Break (Exploit-Proof)
- The four chest blocks and the floor positions directly underneath them are protected from BreakBlockEvent and DamageBlockEvent for non-owners
- Without this, a player could undermine the floor under a chest, the chest would physics-drop as an item, and could be combined with a fresh placement to produce unlimited chests — exploit closed
- Owner/OP break on any tower block triggers an atomic demolition: the entire 21-block structure is removed, the 4 chest inventories spawned as drops, one item returned to hotbar
- Survival players never lose their stored items when picking the tower back up
Recipe
- Crafted at the Workbench (Workbench_Survival) from 3x Ingredient_Life_Essence_Concentrated + 5x Ingredient_Bar_Gold in 5 seconds
- recipesEnabled: false in config.json blocks the workbench recipe for admin-gated distribution
Plugin Logic Architecture
- Forked from GlymeraObelisk v2.0.0, replacing the lightning-strike-on-hostile-NPCs logic with tree-felling. The chest, vacuum, replant, and atomic-break logic are net-new
- Single all-in-one JAR with IncludesAssetPack: true
- Persistence uses atomic .tmp + ATOMIC_MOVE writes to prevent zero-byte files on crash
Hytale Stable-5 Migration
All Glymera plugins updated for Hytale Server 0.5.x (Stable 5)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
All Glymera plugins have been updated for compatibility with Hytale Server 0.5.x (Stable 5). The previous versions targeted Hytale Server 0.4.x (Stable 4).
This document covers what changed in the Hytale Server API between Stable 4 and Stable 5, and which adjustments were required across plugins so that things keep working.
◆ Why a new major version
Stable 5 introduced a number of source-incompatible API changes. A plugin compiled against Stable 4 will either fail to load, fail silently on first use, or — in a few cases — crash the world thread. Because the surface of change is large, every plugin received a new major version number, even when the only functional behaviour change was a manifest flag.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
◆ What changed in Hytale Stable 5
▸ Vector math
- Internal Hytale vector types were replaced by JOML.
- com.hypixel.hytale.math.vector.Vector3d/3i/3f → org.joml.Vector3d/3i/3f
- Rotation helpers now use the Rotation3fc interface where the older API took a concrete Rotation3f.
▸ Particle / Sound API
- SpawnParticleSystem constructors gained additional arguments.
- PlaySoundEvent3D / ambient sound registration semantics tightened — long-running or looped sounds attached to a block must now use the block-state AmbientSoundEventId path; firing them with PlaySoundEvent3D from a tick handler no longer behaves correctly.
▸ Asset pack registration
- AssetModule.registerPack(...) now requires a PackSource parameter (use PackSource.MODS for plugin-installed packs).
- The IncludesAssetPack manifest flag is now strictly enforced. If a plugin both declares IncludesAssetPack: true and registers a runtime pack from its setup code, Hytale detects this as a duplicate registration and aborts boot with "Duplicate asset pack ... Remove the duplicate." For affected plugins IncludesAssetPack is now set to false; the actual asset pack is installed by the plugin at startup as before.
▸ HUD / UI system
- The HudManager API has moved to a keyed model — HUD entries now require a stable key and explicit lifecycle hooks.
- InteractiveCustomUIPage event bindings are wiped if the page is rebuilt while the client is processing a click. Plugins refreshing a HUD per tick had to throttle the rebuild rate (commonly to 1 Hz) so button presses are not lost.
▸ Player handles
- The Player component and the PlayerRef reference have been split. Plugins previously juggling one or the other now need to consider both.
- ServerPlayerListPlayer requires a worldUuid field — entries without it are silently dropped from the tab list.
▸ Entity utilities
- EntityUtils.toHolder(...) was removed. Lookups need to go through the entity store directly or via Ref<EntityStore>.
▸ Damage system
- Resistance fields on EntityEffect (damageResistanceValuesRaw, damageResistanceValues) now expect ResistanceModifier[], not StaticModifier[]. Plugins that previously injected resistances via reflection now use the ResistanceModifier(ResistanceCalculationType, float) constructor.
- The corresponding JSON CalculationType value "Multiplicative" is no longer accepted for damage resistance — it must be "Percent". Migrating large packs needed a bulk rewrite of Potion/Effect JSONs.
- Lethal-damage detection: EntityStatValue#getMin() is no longer a usable death-threshold. Lethal checks now compare the post-damage health against 0.0f directly. Plugins relying on the old behaviour would silently fail to fire and the player just died.
▸ ECS / system tick safety
- It is no longer legal to call mutating store methods (addComponent, removeEntity, …) directly from inside a system's tick handler. Doing so now throws IllegalStateException: Store is currently processing! Such calls must be deferred: either via the system's CommandBuffer, or by scheduling onto the next world tick with world.execute(...).
▸ Items, weapons and recipes
- Custom swords must declare Tags.Type: "Weapon" (not "Tool"), and they need their own complete InteractionVars block — these are no longer inherited from the parent template under Stable 5. A custom weapon without its own InteractionVars will spawn but cannot deal damage.
- Recipe bench IDs and recipe categories are now validated against the asset registry. Mistyped values cause Hytale to silently drop the recipe with no error message.
▸ Cosmetics
- CosmeticsToHide now only accepts a restricted, enumerated set of values. Custom values are rejected at asset-load time.
▸ Server authentication
After every Stable update the server's OAuth credentials must be refreshed:
If the server is still on cached OAUTH_STORE credentials after a Stable update, every incoming player connection is rejected with serverAuthUnavailable and the client only sees "QUIC handshake failed".
- auth logout
- auth login device
- Open the printed verification URL, complete the device authorisation
- auth select <profile>
▸ Manifest / ServerVersion
- Stable 5 enforces a recognisable ServerVersion declaration in each plugin's manifest.json. A malformed value prevents the server from booting. Glymera plugins declare "ServerVersion": "*" to remain agnostic across patch versions.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
◆ Plugin-data path
The plugin-data directory used by all Glymera plugins is now:
mods/de.glymera_<PluginName>/
(Previously it wasplugins/<PluginName>/.) Existing configuration files are not migrated automatically — if you want to keep custom configuration from an older install, move the relevant files from the old plugins/ location into the new one before first launch.
