lootspawnmanager-0.2.4.jar
Curse Maven Snippet
What's new
Changelog
All notable changes to the Loot Spawn Manager mod are documented in this file.
[0.2.4] - 2026-08-28 (Terrain Map, Item Browser, World-Folder Storage)
Three features and one structural change. The structural one matters most: every hit zone in the interface is now computed once, so the bug class that produced the whole 0.2.3 audit can no longer be written.
Upgrade note: the wire protocol moves "10" → "11". Four packets were added and the config packets grew fields, so a 0.2.3 client would decode the tail of a settings packet as garbage. Data on disk is unaffected. templates.json and points.json are unchanged from 0.2.3; the map's tile cache is new and lives beside them.
Added
- Terrain map. The map screen drew a coordinate grid and dots; now it draws the world. The server samples the top block of loaded chunks (
Heightmap.WORLD_SURFACE→getMapColor), shades land by the drop to the column to its north and water by depth — the same two rules vanilla maps use, and the reason the result reads as relief instead of colour blobs.- A tile is 64×64 samples, one byte each in vanilla's
MaterialColor.id * 4 + brightnesspacking: 4 KiB regardless of terrain or zoom. Four LOD levels cover 64 to 512 blocks per tile, so zooming out makes packets rarer, not heavier. - Only loaded chunks are read. Reading an unloaded one means loading it, and panning a map means thousands of them. What was never loaded stays blank — but every sampled tile is written to disk, so the map accumulates as the server lives, and re-sampling never erases columns whose chunks went away.
- Requests are queued and served on a per-tick budget; a client that pans quickly makes the map fill slower, never the server stutter.
- Block placement and breaking mark their tile stale immediately; everything else (explosions, pistons, growth) is picked up by
mapTileTtlSeconds.
- A tile is 64×64 samples, one byte each in vanilla's
- Selection panel on the map. Clicking a point opens a panel in the legend column with its name and editable X / Y / Z, plus a jump into the full point editor. This closed a real hole: dragging a point only ever changed X and Z, the point screen showed coordinates read-only, and height could not be set anywhere in the interface — the only route was a bulk shift by
dy, which moves everything. - Copy and paste points. Ctrl+C on a selected point, Ctrl+V elsewhere on the map. The copy carries binding, timer, schedule, particle radius and the point's own loot; statistics and next-spawn time deliberately do not come along, and the first spawn is scheduled a full interval out. The server does the copying — otherwise binding, timer, schedule and loot would be four separate packets each needing the id of a point that does not exist yet. The name is derived server-side, the only place that can guarantee it is free:
loot_3→loot_4,Base→Base 2, climbing until free. - Item browser. A grid of every registered item with search over both ID and display name, multi-select, and one shared chance / min / max / weight for the whole selection. Filling a pool of thirty items used to be thirty visits to
AddItemScreenand thirty correctly typed IDs. Everything goes in oneC2SAddItemsBatchPacketwith one sync push at the end — sending one packet per item is the mistake that was fixed in the other direction back in 0.2.1. - Data can live in the world folder.
storeInWorldFoldermoves groups, templates, points and map tiles out ofconfig/and into the save. Existing data is copied, not moved, and only when the target is empty, so switching back is safe and fresher world data is never overwritten. Applies on the next server start.config.jsonandpermissions.jsonstay in the instance — they configure the server, not the world. - Four more settings in the server settings screen: terrain on/off, tiles per tick, tile refresh interval, and the world-folder switch.
Changed
- Every hit zone is computed once. New
HitList: a rectangle is created inrender()and the same object drives the highlight, the tooltip and the click. Seven screens migrated, joining the two that already worked this way — and those two were the only screens the 0.2.3 audit found nothing in.SpawnPointListScreen.mouseClickedwent from 166 lines to 34, and what is left is the rename guard, the picker dispatch and the scrollbar. Three dead helpers went with it. - The interface is neutral translucent grey. The navy palette read as a separate application pasted over the game. Alpha now grows with depth — panel
0xC8, header0xD0, hover0xD8, input0xE0— so layers separate without a single border, and the accents are the only colour on screen instead of competing with a blue field. GroupDetailScreenrow actions are laid out from measured text instead of the fixed offsets-118/-76/-58/-26, which existed in three places and gave[R]eighteen pixels — enough for English and nothing else.- Glyphs the 1.16.5 font cannot draw were removed from the last places they hid: the pin, clock and revert arrow in point cards, and the NBT tick in the loot list.
Fixed
- The map tile queue flooded itself. The client asked for every visible tile every 300 ms: roughly 330 requests a second against the 80 the server can serve. The queue grew to its ceiling, further requests were dropped silently, and the budget went on re-checking tiles that were already fresh. It now asks only for what is missing or locally stale, measured by the client's clock — comparing against a server timestamp would have been wrong on any two machines whose clocks differ.
- The settings screen overflowed its own panel. Eleven rows at a fixed step against a height that
modalHclamps to the screen: at GUI scale 4 the rows ran over the buttons. The step is now derived from the actual window height with a floor of 15 px, and the top of the rows is computed in one place instead of two. - An empty map tile was cached as fresh and not re-sampled until its TTL expired, so an area that loaded a minute later appeared two minutes late. Tiles with no readable column are no longer cached at all.
- Map textures were held after the map closed. They are released on close now while the bytes stay, so coming back re-uploads from memory instead of re-downloading.
- The item catalogue never rebuilt. It is static for the process, so reconnecting to a server with a different modpack listed items that were not there. It now rebuilds when the registry size changes.
- The item browser's footer could push its fields past the panel in longer languages; fields shrink first, spacing second, and only then are labels clipped.
RenderSystem.enableBlend()in the terrain pass was never turned off.
[0.2.3] - 2026-08-28 (Loot Lifetime, Server Settings, Interface Audit)
Two rounds of work: an audit of the interface after tester feedback, and the functional gap that audit made visible — spawned loot despawned after five minutes no matter what respawn interval you set.
Upgrade note: the wire protocol moves "9" → "10". C2SUpdateTemplateSettingsPacket gained a field, and a 0.2.1 client would decode the tail of that packet as garbage rather than fail, so the bump is what makes the mismatch loud. Data on disk is unaffected: templates.json gains itemLifespanSeconds, and its default of 0 means "vanilla behaviour", so existing templates spawn exactly as before until you change them.
Added
- Loot lifetime, per template.
LootDroppernow setsItemEntity.lifespaninstead of silently leaving the vanilla 6000 ticks. A template can carry its own value;0falls back to a new server-wide default, and0there means vanilla. This closes the quietest failure the mod had: with a 30-minute respawn interval a point stood empty for 25 of those 30 minutes,ONLY_IF_EMPTYandREPLACE_OLDhad nothing left to check by the time they ran, and nothing in the interface said so. - The field is in the template editor, third row of Spawn Behavior, with the effective value beside it —
now 300 s— which turns amber when loot dies before the next spawn arrives. The problem is visible where it is created, not only in the health check. - Two health checks for it: the gap between lifetime and interval, and pile-up risk for
ADDITIVEwith a lifetime far longer than the interval. - Server Settings screen (Tools tab). Seven values that lived only in
config.jsonand needed a restart: default loot lifetime, unloaded-chunk retry, out-of-schedule retry, point save interval, region cap, particle radius, sync chunk size. Writing them savesconfig.jsonand applies at once. Level ADMIN — these affect the whole server, not one object — and other online admins receive the new values so nobody edits a stale screen. S2CConfigPacket/C2SSetConfigPacket. The config lives on the server, so without the first one the settings screen would be showing numbers it invented. Values are clamped twice: on construction, because a client can send anything, and again inModConfig.clamp()before the write — the same path a hand-edited file goes through.unloadedRetryMs. A point whose chunk is not loaded now retries soon instead of burning a whole respawn interval.LootDropper.spawnLootreturnsnullfor "could not run" as distinct from an empty list for "ran, dropped nothing"; the two were indistinguishable before, so walking into a fresh region meant waiting a full cycle.ClippedButton. VanillaWidget.renderButtondraws its label centred and unclipped, so any longer translation spilled out of the button on both sides. All 59 button sites use it now. It is a backstop, not a substitute for correct widths — a…in English still means the button is too narrow.GuiHelper.clip()/drawHeadingClipped()— one pixel-width truncation used by text, buttons and headings alike.
Fixed
- The Spawn Points panel was 150 px wide, always.
SpawnPointListScreencomputed its width from the uninitialised fieldpanelWinstead of the constantPANEL_W(520). The floor incontentW()made the wrong value stable, so it looked deliberate. Everything downstream is laid out from that width: the schedule badge landed left of its own card, statistics printed over the point name, six action icons marched into the name, and tile mode collapsed to one column. - Tile mode had no click handling at all — clicks fell through to the card branch, where the row index comes from
(my − listY) / CARD_Hand the icon zones frompx + panelW. A click on the right of a tile opened the delete confirmation for a different point, and "clear loot" and "test spawn" fired at the wrong point with no confirmation at all. - Scroll units in tile mode. Render clamped by tile rows; all three mouse handlers clamped by point count, so the wheel scrolled far past the end and the scrollbar dragged on a different scale than it drew.
- Labels drawn over their own fields. In the template editor the label x and the field x were two independent magic offsets tuned for English: "Max items:" fits 62 px, «Макс. предметів:» needs 84. Both rows are now laid out left-to-right from measured text, and if a row cannot fit, the labels shrink instead of the fields being covered. Verified against all nine languages.
- Statistics badges sat under the search field on the main menu — 9 px of overlap that hid two thirds of them.
- Five coordinates left absolute after the tab-bar refactor, so the tab bar painted over them: the point and template counters, the simulator's iteration field, a note in the point-loot screen, and the back button's tooltip zone, which was registered 22 px above its own button.
- Toasts were a solid blue block. The border
fillran after the background and covered it; two later one-pixel strips were all that remained of the panel. - Three modals drew at raw
W/Hwhile positioning themselves with the clamped helpers, so they ran off the screen at GUI scale 3–4. The bind picker went further and used different formulas in render and in click handling — the row under the cursor was not the row that opened. - The map legend truncated silently and toggled the wrong template: the render skipped a deleted template without advancing the cursor, the click loop counted it. Both now read one list, and the legend scrolls.
- Confirmation text was clipped to one line with an ellipsis — the only warning before an irreversible action. It wraps now, and the dialog grows to fit.
- A corrupt JSON file killed server startup.
loadListcaughtIOExceptionbut Gson throwsJsonSyntaxException, aRuntimeException. One truncatedpoints.jsonand you could not get in to restore a backup. The file is skipped with a log line naming the backup folder. - Command permissions ignored delegation. The whole
/lootmanagertree required OP 2, so a player with delegated EDIT could open the GUI with the keybind but could not run a single command — including/lm menu. Commands now read the same level the packets do;permstays ADMIN-only. - Emoji rendered as empty squares. 34 keys in every language file opened with glyphs the 1.16.5 font has no coverage for — section headers, tool names, the grid toggle. Removed; the mod's own font-independent
drawIconremains for icons. - Hardcoded English in five places that every other string had left behind: the region generator's parameter label and estimate, the shift estimate,
UNBOUNDin the map popup, and the loot-grid tooltip.
Changed
- Tooltip coverage is now complete. Every button and field in the mod explains itself, including the single-letter action icons where
[C]means both Copy and Clear depending on the screen. Screens whose rows scroll use the runtimehoverTooltipKeypath; fixed widgets useTooltipZonewith the same coordinates passed to the widget. Language files went from 366 to 486 keys, all nine complete. defaultParticleRadiusactually applies. The client used a local constant and ignored the server value entirely.- README corrected. It claimed in one section that the client mod is optional and in another that "every client needs it — Forge refuses a connection without it". The code is unambiguous: nothing is registered,
IGNORESERVERONLYis declared, and the channel accepts clients that lack it. The second claim was stale and would have made admins force an unnecessary download on every player.
Verified
- All 52 packets have symmetric
encode/decodebyte sequences; every C2S handler checks permissions and runs insideenqueueWork. - Every scheduler mutation path keeps the priority queue consistent — per-object operations pass the same instance, bulk operations rebuild.
- Language files: no missing or extra keys in any of the nine, and format specifiers match English everywhere.
[0.2.1] - 2026-08-25 (Interface Rebuild — Fixes)
A review pass over the code 0.2.0 itself introduced. Three of the findings made the two new screens unusable, so this is a corrective release rather than a feature one.
Upgrade note: the wire protocol moves "8" → "9". This is not optional housekeeping — S2CEntityBatchPacket was registered ahead of two existing packets, which shifts their discriminator IDs. A 0.2.0 client would pass the version handshake and then decode those packets as the wrong type, so the bump is what makes the mismatch fail loudly instead of silently. Data on disk is unaffected.
Fixed
- Scrolling pushed content off the panel instead of scrolling it. Both new screens walked a manual draw cursor, and both advanced it for rows that were skipped by the scroll window. With the list scrolled by five rows the first visible row was drawn five row-heights too low.
PointDetailScreenandToolsScreenwere unusable the moment you scrolled — which is to say, the headline results of the last two releases did not work. - Visible-row count was a division by one constant while rows are 12, 16 and 18 px tall, so
ToolsScreendrew more rows than fit and they spilled past the panel. - The point detail scrollbar was driven by the wrong quantity — the number of clickable zones (about 8) rather than the number of rows (about 18) — and its mouse wheel was capped at a hardcoded 20 rows.
- "Change template" opened the point list. The template picker lives inside
SpawnPointListScreenand is driven by a private field, so there was no way to reach it from another screen; the button simply did nothing useful.PointDetailScreennow has its own picker with type-to-filter, an empty-template warning and item counts. - Creating an entity whose name already existed pushed the wrong object. The delta looked the new object up by name, and
getPointByNamereturns the first match while names are not unique anywhere. The new entity stayed invisible until the next full sync. The wand names points after their coordinates, so two clicks on one block hit this exactly. All five call sites now use the objectcreate*/duplicate*already returns. ImportScreendrew more rows than its dialog holds — the row count came from the full screen height rather than the clamped modal height, giving 23 rows in a box that fits 8.
Changed
- Mass operations no longer force a full resync. Enable/disable a selection, delete a selection and set a group-wide timer now send
S2CEntityBatchPacket— one JSON array cut by the same byte budget the full sync uses. Sending one delta per object was not an option: on a 500-point selection that is the very mistake fixed in the client→server direction earlier. Nine full syncs remain, and each genuinely replaces everything: login, open menu, reload, undo, restore, import, region generate, shift points, group/template delete. - In-flight feedback extended from 2 screens to 8, covering every per-object mutation that has a row to dim: templates, points, schedule, particles, map drag, tiles. Tiles dim and refuse repeat clicks the same way cards do.
- Pending marks are cleared when the mod GUI closes.
tick()only runs while a screen is open, so a mark left in flight used to hang until the next time you opened the interface. - Clearing a point's loot no longer broadcasts a delta. It removes world entities and changes no field on the point, so an unchanged object was being sent to every admin.
Added
RowList— a scrolling list of variable-height rows. Heights are declared with the row and the cursor only advances for rows actually drawn, so neither of the two bugs above can be written a third time. Both scrolling screens use it.S2CEntityBatchPacket— a multi-object delta for mass operations.
Known limitations
TemplateDetailScreenandToolsScreenclamp their position but not their internal column offsets, so at the narrowest widths the right-hand fields clip.- Still verified by compilation and static review only. This release exists because that is not enough: the three critical findings were in code the previous release reported as done.
[0.2.0] - 2026-08-24 (Interface Rebuild)
A UX review of all 16 screens found that most usability problems came from three structural causes rather than from individual screens, so they were fixed at the root rather than patched screen by screen.
Upgrade note: the wire protocol moves "7" → "8"; clients must be updated with the server. Data on disk is unaffected.
Changed — three foundations
1. Delta synchronization instead of a full resync. The server used to push the entire data set after every single action — 34 call sites. The client answered that by re-running init(), which is why scroll position, search text and any open dialog were thrown away on every click; some of that state had to be preserved by hand, which treated the symptom.
A new S2CEntityPacket (kind + op + id + json) now carries one changed object, and LootManagerBaseScreen.onDataChanged() only marks caches stale — init() is not called. 22 handlers moved to SyncHelper.push*; the 13 remaining full syncs are the ones that really do change everything (login, open menu, undo, restore, import, region generate, shift, batches, group/template delete, mass timer). Every one of the 26 mutating handlers was cross-checked against its broadcast — no gaps.
This also fixed a related bug: the resync used to reach only the player who acted, so a second admin's screen went quietly stale. Deltas go to everyone with the mod and VIEW or above.
One consequence had to be handled explicitly. The scheduler mutates spawn timers every second and deliberately does not push deltas — that would be hundreds of packets per tick. Timers used to refresh as a side effect of the constant resyncs; without them a card would have been stuck on "Ready" forever. The spawn event already carries the point id and timestamp, so the client now recomputes exactly what the server computed, at no extra traffic.
2. GuiLayout — one shared geometry. Twelve screens hard-centred a fixed width (380…540 px) with no check at all. At GUI scale 4 the logical screen is 480 px wide while the point list panel is 520 px plus a scrollbar: the result was negative and the panel hung off both edges. contentW/contentX/modalW/modalX never return an off-screen position, and the list panels now narrow their contents too. Verified across widths 960 / 640 / 480 / 400 / 341.
GuiLayout.Rect addresses the second half of the problem: coordinates used to be computed twice, independently, in render and in mouseClicked with magic numbers. That is where the icons that highlighted two pixels away from where they clicked came from.
3. A persistent tab bar. MainMenuScreen was the only hub, so getting from a point to the map meant Back → Main → Map. Groups / Points / Map / Tools are now one click from anywhere; ten screens declare their section. The map's own dimension tabs were moved below the new strip.
Added — Point detail screen, tiles, and pending state
PointDetailScreen— everything about one point in one place: position, binding, timing, loot, statistics, actions. Replaces the nine single-letter icons crammed into a 48 px card, which were dense already and left nowhere to put a new action. Clicking a card opens it; before this, clicking a card did nothing at all unless the timer field happened to be filled. Its rows are drawn manually againstGuiLayout.Rectrather than as Minecraft widgets: widgets are positioned ininit()and do not move with the scroll, and this content is taller than the screen at GUI scale 3–4.- Tile view for the point list (
⊞ Tiles/≡ List). A card fills a row and shows 6–9 points per screen; a tile carries the same key information in a fifth of the area, so dozens fit. With several hundred points that is the difference between a minute of scrolling and seeing everything at once. ClientPending— an action in flight dims its card and ignores further clicks until the delta arrives, with a 5 s timeout that reports the failure instead of staying silent. On a laggy server people pressed twice, and the second press sometimes undid the first.- Type-to-filter in the template picker — a picker is an overlay that appears and disappears while a widget lives from
init()toinit(), so typing filters directly. ToolsScreenrebuilt as one scrolling column. It used to be five fixed-height sections stacked vertically, each truncating its own content: backups showed 5 of the 20 kept, the event feed 20 of 50, the health check as many warnings as fit, and the item lookup only the first 3 templates. On a short screen the lower sections did not fit at all. Nothing is cut off now, and a lookup result row is clickable to teleport to its first point.
Fixed — interface
- A click on a point card did nothing. The handler ended in a bare
return truewhenever the timer field was empty, which is the normal state — while everywhere else in the mod a row click drills down. - A hidden mode silently overwrote timers. With a value left in the "Timer override" field, the next click on any card rewrote that point's timer; only small grey footer text said so, the field was never cleared, and the card gave no signal. Now the hint turns amber and names the value, the hovered card gets an amber stripe, and the field clears after applying.
- Three full scans of every point, every frame.
MainMenuScreencounted its three badges with three separatestream().count()passes over all points — 9 000 iterations 60 times a second at 3 000 points, for labels that change every few minutes. The map legend did the same. Both now use one cached pass, the patternToolsScreenalready used for its health check. - A point bound to an empty template looked healthy — green status, spawning nothing. Only Tools → Health Check revealed it. It now shows amber with an
EMPTYbadge. - The inline rename field could not be clicked into — any click committed the rename, so the cursor could never be placed with the mouse.
- Icon highlight was 2 px off from the clickable area on the point card's second row.
Enterdid not confirm creation of a group, template or point.- Long names overflowed
ConfirmDialog, which has a fixed width and drew unclipped. - The revert-bind icon had no hover state —
rvHovwas computed and never used, so it did not read as clickable. The test-spawn icon showed the wrong tooltip. - Scrollable lists with no scrollbar —
ImportScreennow has a real one; both template pickers show a count and a "more below" hint; the map's picker no longer runs off-screen. Shift Pointsran on one click — the only mass operation without a confirmation dialog.- The region generator capped at a hardcoded 1000 while the server read
ModConfig.maxRegionPoints; raising the config did nothing. The limit is sent to the client. - Batch bar labels on the map (
Enable,Disable,Delete,Bind All) were hardcoded English — they sit in aString[], which the localization scan did not reach.
Known limitations
TemplateDetailScreenandToolsScreenclamp their position but not their internal column offsets, so at the very smallest widths their right-hand fields clip. A fluid re-flow of those rows is a design task, not a mechanical one.- Verified by compilation and static review only — this release has not been run in-game, and a rebuilt interface is exactly the kind of change where a few pixels only show up on screen.
[0.1.0] - 2026-08-23 (Server-Side, Hardening & Full Localization)
Two review passes over all 105 source files, followed by fixes: first the code, then the localization. Four findings were server-crash or data-loss class. The network layer and the whole GUI text pipeline were reworked as a result, and the mod no longer needs to be installed on clients.
Upgrade note: the mod no longer registers any items, so existing Loot Spawn Wands and Region Brushes in inventories or chests will disappear on world load. Re-create them with /lm tool wand / /lm tool brush on any item. The wire protocol changed ("6" → "7") and two packets were removed, so clients that do have the mod must be updated alongside the server. On first start after upgrading, any point whose respawn timer already elapsed while the server was down is spread randomly across its own respawn window instead of firing all at once — expect the first minutes to look calmer than before.
Changed — the client mod is now optional
The mod used to be mandatory on every client. Three things forced that, and all three are gone:
- Two registered items.
loot_spawn_wandandregion_brushwere real registry entries, and Forge synchronizes a registry snapshot at login — entries the client does not have drop the connection. This was the hard blocker; no flag works around it. Both items are removed. Their behaviour now lives on a marker in the item's NBT:/lm tool wandor/lm tool brushturns whatever you are holding into that tool (with a lore line so you can tell it apart), and/lm tool cleargives the item back. The marker travels with the stack, so a tool can be stored in a chest or handed to another admin, and holding an ordinary item keeps working as usual. Right-click behaviour moved from the twoItemsubclasses intoToolInteractHandler(PlayerInteractEvent.RightClickBlock). - Exact protocol match.
NetworkRegistry.newSimpleChannel(..., PROTOCOL::equals, PROTOCOL::equals)rejected a client that reportsABSENT. Both predicates are nowNetworkRegistry.acceptMissingOr(PROTOCOL). - No server-only declaration. Added the
ExtensionPoint.DISPLAYTESTregistration withFMLNetworkConstants.IGNORESERVERONLY, so Forge stops comparing mod lists for this mod.
The server now checks PacketHandler.hasMod(player) before sending anything, so a modless client is never handed packets it cannot decode; SyncHelper.reply() falls back to a plain chat message for those players. What a modless client loses: the admin GUI, the map, the simulator and particle visualization — commands cover the rest. The loot itself is plain vanilla ItemEntity, so ordinary players notice nothing at all.
Security
- Remote OOM before any permission check —
C2SBatchSetActivePacket.decode()read the list length as a rawintand immediately didnew ArrayList<>(size).decoderuns on the netty thread beforeenqueueWork, i.e. beforePermissionCheck— so any connected player, with no permissions at all, could sendInteger.MAX_VALUEand exhaust server memory. NewPacketUtil.readSize()clamps every length;readBlob()additionally rejects a declared size larger than 512 KiB or larger than the bytes actually remaining in the buffer. Audited across everydecode()in the mod — no raw length-driven allocation left. - Server crash from a non-ASCII point name —
PacketBuffer.writeUtf(s, max)compares bytes, whileTextFieldWidget.setMaxLengthlimits characters. A point name of 33+ Cyrillic characters (or 17+ CJK) overflowed the 64-byte cap inS2CSpawnEventPacket. Encoding runs synchronously on the server thread insideSpawnScheduler.tick(), and that broadcast block had notry/catch, so theEncoderExceptionpropagated throughServerTickEventand took the server down. All 48 packets now go throughPacketUtil.writeString/readString, which sizes the write cap asmaxChars × 4; the broadcast is wrapped defensively. - Permanent save failure from a crafted move packet —
C2SMovePointPacketaccepted anydouble. Gson refuses to serializeNaN/Infinity, so one poisoned point made every subsequentsave()throw — silently, because the exception died inside theCompletableFuturefromenqueueWork. Nothing reached disk until restart. Coordinates are now rejected unless finite and clamped to world bounds, andwriteAsynclogs a serialization failure instead of swallowing it. - Item flood from a zero respawn timer —
C2SCreateTemplatePacketpassedrespawnSecondsthrough unvalidated. At0a bound point became due on every scheduler tick, i.e. once per second. Clamped to1 … 30 days, matching the checkC2SSetTimerPacketalready had. - Server thread freeze from unbounded template settings —
C2SUpdateTemplateSettingsPacketvalidated none of its six numeric fields.LootDropper.pickItemsloops exactlymaxWeightedPickstimes per spawn, soInteger.MAX_VALUEmeant a hung thread and anOutOfMemoryError. Now clamped: picks1–64, max items1–256, radii0–128, priority±100.
Fixed
- Mass spawn burst after downtime — respawn times are stored as absolute wall-clock, so after any outage every point was overdue and the first tick tried to spawn all of them.
DataManager.normalizeOverdue()now spreads overdue points randomly across their own respawn interval on load, and the scheduler caps work at 200 spawns and 20 event broadcasts per tick. - Deleting a group left points bound to nothing —
deleteTemplateclearedtemplateIdon affected points,deleteGroupdid not. Those points kept atemplateIdpointing at a deleted template: the GUI showed them as bound,countActive()counted them, and the scheduler silently dropped them from the queue forever. Both paths now clear the binding and reset the timer. - Backup restore failures were invisible —
SyncHelper.notifyAdminsstarts withif (except == null …) return;, and both error paths inC2SRestoreBackupPacketcalled it withnull, so nobody was told anything. The helper now falls back toServerLifecycleHooks.getCurrentServer(), and the requesting player gets an explicit success or failure message. - Silent server-side rejections — every handler answered a failed check with a bare
return, while the client had already closed the screen and looked like the action succeeded. 37 handlers moved toPermissionCheck.requireEdit/requireAdmin/requireView, which explain the refusal; add-item, test-spawn, teleport, simulate and restore now reply with the specific reason. - Toasts were invisible outside the mod GUI —
Toast.renderAllwas only called fromLootManagerBaseScreen.render, so a notification sent during normal play (another admin deleting a group, a rejected packet) expired unseen after 3 s. NewClientToastOverlaydraws them on the HUD whenever no mod screen is open. - 0 % chance items could still drop — the roll used
nextFloat() * 100f <= chance, andnextFloat()can return exactly0.0. Now strict<. - Item counts above the stack size were truncated to a byte —
ItemStack.save()writesCountas a byte, so a stack of 9999 arrived at the client as 15.placeItemclamps toitem.getMaxStackSize(). - Simulator numbers did not match reality —
simulate()reimplemented item picking and ignoredmaxItemsPerSpawn, over-reporting drops for templates with more items than the limit. Both paths now shareLootDropper.roll()and apply the same cap. - Wand and Region Brush ignored delegated permissions — both checked
player.hasPermissions(2)directly, so an admin granted ADMIN via/lm perm grantgot the tool and could not use it. Both usePermissionChecknow, as does the scheduler's event broadcast filter. - Chinese localization was completely broken —
zh_cn.jsonhad unescaped quotes inside 15 values ("点击"+ 新建组"创建"), which made the whole file unparseable. Chinese players saw raw keys for the entire interface, not just those lines. Inner quotes replaced with 「」. tooltip.btn.mapexisted in no language file — the Map button's tooltip rendered as a raw key for everyone. Added to all nine.- 21 keys missing from 7 languages — including the item names themselves and the whole Tools tab. All nine files now carry an identical key set.
- Scroll position and search text were wiped after every action —
init()ranscroll.reset()and rebuilt the search field, andinit()re-runs on every data sync, which the server sends after every mutation. Editing one point in a list of 300 threw you back to the top with the filter cleared. Scroll is now kept (clampToinrenderkeeps it valid) and the search text is restored before the responder is attached. - The inline rename field could not be clicked into — all three screens with inline rename began
mouseClickedwith an unconditional commit, so a click meant to place the cursor ended the rename instead. Clicks inside the field now reach it. - Icon highlight was 2 px off from the clickable area — on the point card, row-2 icons are drawn at
row2Y - 2and the click test usesy + 16, but the hover test usedrow2Y. The top 2 px clicked without lighting up; the bottom 2 px lit up without clicking. - Long names overflowed the confirmation dialog —
ConfirmDialoghas a fixed 290 px width and drew title and body unclipped. - Particle radius was unvalidated — the field accepts four digits and neither client, packet, nor server checked the value; 9999 meant drawing particles for points thousands of blocks away. Clamped to
1–256, the same bounddefaultParticleRadiusalready had. The client also caps visualization at 64 points and now checks vertical distance — the range test was 2D, so a point 200 blocks above counted as "nearby". - GROUND appeared under two different abbreviations — the list printed
name().substring(0, 3)(GRO) but fell back to a hardcoded"GRD". /lm reloadlost up to 30 s of state — point saves are debounced, and reload re-read the older file. It now flushes points first.- Import accepted malformed files — missing or
nullnames caused an NPE inexportGroupand in name lookup. Defaults are substituted andnullentries skipped. - Backup filenames collided — the timestamp has second resolution, and deleting a group writes an undo snapshot and a backup in the same moment, so the second overwrote the first. Colliding names get a
_2,_3… suffix. Wand-created points had the same problem and are now namedwand_X_Y_Z.
Added
/lm tool wand | brush | clearandAdminTool— the NBT marker described above.ToolInteractHandler— right-click behaviour for marked items, replacing the two removedItemsubclasses.PacketHandler.hasMod(player)— guard before sending to a possibly-modless client.PacketUtil— one place for every packet bound: string caps in characters, list-length and blob-length limits, coordinate clamping, finiteness checks.C2SBatchDeletePointsPacket+DataManager.batchDeletePoints()— one undo snapshot, one disk write and one log line for a whole selection.S2CSyncChunkPacket— a single chunk type carrying groups, templates or points.ClientToastOverlay— HUD rendering for notifications outside the mod GUI.- Dynamic tooltips —
LootManagerBaseScreen.hoverTooltipKeylets a screen set a tooltip duringrender()for an element that has no fixed position.TooltipZoneis anchored to coordinates fixed ininit(), which is why the point card's single-letter action icons (X,C,B,R,T,S,I) never had explanations — the keys existed but were unused. All nine are wired up now. GuiHelper.tr(key, args…)anddrawCenteredClipped().PermissionCheck.requireView/requireEdit/requireAdmin— check and explain the refusal.DataManager.saveGroups()/saveTemplates()— targeted writes.- 191 new translation keys, each translated into all nine languages.
Changed
- Sync reworked around a byte budget. Chunking counted items — but one template with two hundred entries weighs as much as a hundred points, which is exactly how oversized packets appeared; groups and templates were not chunked at all and rode along in the begin packet, where a few hundred templates could reach the vanilla 1 MiB payload limit and drop the client.
S2CSyncBeginPacketnow carries only counters, and the sender fills each chunk until it hits the budget. Packet size no longer depends on content. config.json:pointsPerPacketreplaced bysyncChunkBytes(default 32768, clamped 4096–262144). The old key is ignored if present.save()split intosaveGroups()/saveTemplates()/savePointsOnly(). It used to serialize every group, template and point on the server thread on every click; 26 call sites now write only the file they touched. Fullsave()remains for undo, restore, import and group deletion.AdminLoggerwrites on its own daemon thread instead of doing blocking file I/O inside the tick.- Map batch operations (enable / disable / delete) send batch packets instead of one packet per point — a 500-point selection meant 500 full saves and 500 full resyncs. Selections are split into packets of 512 ids, and mass delete now asks for confirmation like every other delete path.
C2SBatchSetActivePacketrequires EDIT instead of ADMIN, matching the per-point packet it replaces.- All GUI text moved into the language files — 205 hardcoded English strings across 15 files. What remains are two
%.0fnumber formats.GuiHelper.formatTimerandformatTimestampwere returning"Ready"and"5m ago"straight from the method body; time units come from the language file now, sinceh/m/sare not universal. - Network protocol:
"6"→"7"(incompatible with v0.0.9 clients).
Removed
S2CDataSyncPacket— superseded by chunked sync and never sent, though still registered, with an unreachable client handler and a redundant import in 25 files.S2CSyncPointsChunkPacket— replaced byS2CSyncChunkPacket.LootSpawnWand,RegionBrush,ModItemsand both item models — replaced by the NBT marker. Theiritem.lootspawnmanager.*language keys are gone too.
Known limitations
- Spawn modes remain mutually exclusive:
RADIUScannot be combined withONLY_IF_EMPTYorREPLACE_OLD, and the occupancy check radius is still hardcoded to 2 blocks regardless ofmaxSpawnRadius. Splitting placement from occupancy needs a data migration. - Region generation has no command equivalent, so the Region Brush needs the client mod to finish the job — without it the brush reports the selected corners and stops there. Spawn mode, weights, schedules, per-point loot and point deletion are likewise GUI-only.
- Still no automated tests. The refactor made the pure logic easier to cover:
LootDropper.roll(),normalizeOverdue(),PacketUtil,SyncHelper.chunkify(),isInScheduleWindow(). - Verified by compilation and static review only — not run on a live server.
[0.0.9] - 2026-06-16 (Performance & Admin Tools)
Added
- Visual loot grid editor —
[⊞ Grid]toggle in Template Detail's Loot Pool section header switches from the item list to a 9-column icon grid (20×20 px slots). Hover any slot to see a tooltip with item name, count range, chance%, and weight. Click the[×]icon that appears in the top-right corner of a hovered slot to remove the item.[≡ List]reverts to the table view. Toggle persists within a session. - Per-template export —
[📤 Export]button in Template Detail footer. SendsC2SExportTemplatePacketto server (requires ADMIN); writes the template's JSON (items + settings) toconfig/lootmanager/exports/template_NAME_TIMESTAMP.json. Server sends a chat message with the filename. Can be shared between servers or used as a backup of specific templates.
Changed
- Async disk I/O —
DataManager.save()andsavePointsOnly()now serialize data in-memory on the server thread (~1 ms) then hand off file writes to a dedicated single-threadLootSpawnManager-Saveexecutor (~5-50 ms, off server thread). Eliminates disk stalls during admin actions on busy servers. On shutdown, a final save is submitted last in the queue andawaitTermination(30 s)ensures all writes complete before the JVM exits. - Network protocol:
"5"→"6"(incompatible with v0.0.8 clients)
[0.0.8] - 2026-06-16 (Security & Backup Restore)
Added
- Backup Restore UI — Tools tab now shows up to 5 recent backups with timestamps. Click
[Restore]→ ConfirmDialog → server atomically replaces all data from the snapshot, rebuilds the scheduler, notifies other admins, and resyncs all clients. No server restart required.BackupManager.listBackups()/readBackup(filename)with path-traversal protectionDataManager.restoreFromSnapshot(json)— full field migration for old snapshots- Packets:
C2SRequestBackupListPacket,S2CBackupListPacket,C2SRestoreBackupPacket ClientState.backupListstores the latest list from server
- Input validation on item packets —
C2SAddItemPacketandC2SAddPointItemPacketnow reject: empty item ID,minCount < 1,maxCount < minCount,chanceoutside 0–100, negative weight. Malformed packets are silently dropped server-side.
Fixed
- Path traversal in ImportExportManager —
importFile()now rejects filenames containing..,/,\and verifies the resolved path stays inside the import directory. Prevents directory escape via crafted filenames. String.isBlank()→trim().isEmpty()— two packets used a Java 11+ API (isBlank) causing compile failure under the required JDK 8.AdminLoggerrace condition —log()is nowsynchronized; prevents log-line interleaving when two admins trigger actions simultaneously.
Changed
- Network protocol:
"4"→"5"(incompatible with v0.0.7 clients)
[0.0.7] - 2026-06-16 (Map & Multi-Admin)
Added
- Interactive Map screen —
[Map]button in SpawnPointListScreen opens a top-down coordinate grid of all spawn points in the current dimension:- Zoom with scroll wheel, pan by left-drag on empty space
- Each point rendered as a colored dot (green = active, red = inactive)
- Click a dot to open its SpawnPointListScreen row; Shift+click to select
- Drag to reposition — drag a dot to new coordinates → sends
C2SMovePointPacket - Template legend: click a template chip to toggle visibility of its points
- Grid lines every 100 blocks, coordinate labels, crosshair at origin
- ConfirmDialog — universal modal overlay for destructive operations:
- Delete group (shows template count warning)
- Delete template (shows bound point count warning)
- Delete point
- Restore backup
- Enter = confirm, Escape = cancel;
isPauseScreen()returns false so the game keeps ticking while the dialog is open
- Multi-admin notifications — when any admin mutates data, all other connected admins with ≥ VIEW permission receive a toast notification:
- Group delete, template delete → warning toast naming the deleted object
- Backup restore → warning toast naming the filename
S2CAdminNotifyPacket(TYPE_INFO / TYPE_WARNING) +SyncHelper.notifyAdmins()
Fixed
- TemplateDetailScreen per-frame GC —
ForgeRegistries.ITEMS.getValue(new ResourceLocation(...))was called every render frame per item. Replaced withitemStackCache(Map rebuilt once per template change). Eliminates allocation spike at 60 fps with large loot pools. - ToolsScreen O(T+P) per frame —
collectWarnings()walked all templates and points on every render call. Now guarded bywarningsDirtyflag; only recomputed on data sync or screen init. - Dead code in ShiftPointsScreen.cycleTmpl() — removed redundant
else if (tmplIdx == -1) tmplIdx = -1branch.
Changed
- Network protocol:
"3"→"4"(incompatible with v0.0.6 clients)
[0.0.6] - Unreleased (Performance & Permissions Hardening)
Changed
- SpawnScheduler — усунено lock contention.
tick()більше неsynchronizedцілком; розбито на три фази: drain (під локом, мікросекунди) → spawn/world-I/O- broadcast (ПОЗА локом) → requeue (під локом). Мережеві потоки (
addOrUpdate/removeз packet-handler-ів) більше не блокуються на час спавну (раніше — до 50мс/спавн фрізу GUI при високій частоті).
- broadcast (ПОЗА локом) → requeue (під локом). Мережеві потоки (
- Чистий comparator черги — додано
SpawnPoint.cachedPriority(transient), заповнюється при add/requeue; comparator більше не звертається до DataManager під час порівнянь (прибрано side-lookup і потенційний O(N) при rebuild). - Config-файл
config/lootmanager/config.json(ModConfig) — винесено хардкоди в tunables:maxRegionPoints,pointsPerPacket,pointsFlushIntervalMs,scheduleSkipRetryMs,defaultParticleRadius. Створюється з дефолтами, значення clamp-ляться у безпечні межі.
Fixed
- Permission delegation працює всюди — 20 застарілих C2S-пакетів переведено з
player.hasPermissions(2)наPermissionCheck.canEdit/canAdmin. Тепер EDIT-модератор без OP (через/lm perm grant <player> 2) може робити CRUD; небезпечні/файлові/масові операції (region-генерація, import/export, batch) вимагають ADMIN.
[0.0.5] - Unreleased (Scale + Loot Flexibility)
Added
- Chunked синхронізація — замість одного гігантського snapshot-пакета (упирався в ~1МБ ліміт Forge при ~2-3к точок і дисконектив гравця) дані тепер летять як: 1×
S2CSyncBeginPacket(групи+шаблони+meta) + N×S2CSyncPointsChunkPacketпо ≤10 точок на пакет. Клієнт збирає уClientSyncAssembler(BitSet від дублікатів). Стеля піднята з ~3к до десятків тисяч точок.SyncHelper.sendFull(player, openGui)замінив усі 27 прямих send-ів S2CDataSyncPacket
- Per-point кастомний лут — кожна точка має власний
customItemsсписок- прапорець
useCustomLoot. Коли увімкнено й не порожній —LootDropperбере предмети звідси замістьtemplate.items(режим/picks беруться з шаблону). Дозволяє унікальний лут (босовий ящик) без окремого шаблону.
- Нова іконка
[I]на row 2 картки точки →PointLootScreen - Зелена крапка на
[I]коли custom loot активний - Toggle "Custom loot: ON/OFF", список з видаленням, порожній стан
- Пакети:
C2SAddPointItemPacket,C2SRemovePointItemPacket,C2SSetPointCustomLootPacket
- прапорець
- Вибір предмета з руки — кнопка "From hand" у AddItemScreen бере item ID
- повний NBT з основної руки гравця одним кліком
- Live пошук-підказки — при введенні в поле Item ID показуються до 6 збігів з
ForgeRegistries.ITEMS; клік на підказку підставляє повний ID - Універсальний AddItemScreen — один екран працює і для шаблонів (
C2SAddItemPacket), і для точок (C2SAddPointItemPacket) через окремі конструктори
Changed
LootDropper.pickItems()приймає явний список предметів (підтримка custom-луту)DataManager.load()мігруєcustomItems/pastTemplateIdsдля старих збережень- Debounced збереження точок — scheduler більше не пише
points.jsonщосекунди; позначає dirty, фактичний запис раз на ~30с (flushPointsIfDue) + повний save на кожній адмін-дії та при зупинці сервера. ~90% менше disk I/O при 1000+ точок - HashMap-індекси в ClientDataCache —
getTemplate/getGroupтепер O(1) замість O(N) лінійного пошуку (важливо для render-циклів 60 fps при 1000+ точок) - Версія мода → 0.0.5
Fixed
C2SCreateGroupPacketдосі слав rawS2CDataSyncPacket(оминав chunking) — переведено наSyncHelper.sendFull+PermissionCheck.canEditLootDropper.spawnLoot()повертає порожній список якщо template == null (захист від unbound/видаленого шаблону)
[0.0.4] - Unreleased (Power Tools)
Added
- Test Spawn button —
[?]фіолетова іконка на картці точки в SpawnPointListScreen. Одинарний клік → разовий спавн ігноруючи таймер. Server повідомляє кількість заспавнених предметів через actionbar. - Template priority — нове поле
priority(default 0) на шаблонах. SpawnScheduler PriorityQueue має tie-break: коли дві точки готові в одному тіку, перевагу отримує шаблон з вищим priority. Поле "Prio" доступне у TemplateDetailScreen → Section "Item Selection". - Schedule windows — кожна точка може мати
activeHourStart/activeHourEnd(24h, real-time hours). Поза вікном точка не спавнить (планується на +1 хв). Підтримується wrap-around (22 → 6 = 22,23,0,1,2,3,4,5). Badge⏰ 22:00-06:00на картці точки (фіолетовий якщо зараз у вікні, сірий якщо ні). - Mass shift points — Tools tab →
📐 Shift Points…модал:- dx / dy / dz поля
- dim filter (cycle через всі виміри + ALL)
- template filter (ALL / Unbound only / окремий шаблон)
- Real-time estimate "~ N points"
- Snapshot для undo перед операцією
- C2STestSpawnPacket / C2SShiftPointsPacket / C2SSetPointSchedulePacket — 3 нові C2S пакети, всі через
PermissionCheck
Changed
C2SUpdateTemplateSettingsPacketтепер містить полеpriority(back-compat конструктор без priority лишається)DataManager.updateTemplateSettings()— overload з priority параметром
[0.0.3] - Unreleased (Admin Experience)
Added
- Region Brush — окремий item для масового створення точок:
- RMB на блок 1 → corner1 (NBT в ItemStack)
- RMB на блок 2 → corner2 + відкриває GUI генерації
- Shift+RMB → скидає виділення
- 3 паттерни: Grid (з spacing), Random (випадково в об'ємі), Floor (по нижній грані)
- Естимейт кількості точок з кольоровим попередженням (>1000 заблоковано)
- Авто prefix-name + опційне прив'язування до шаблону
- Permission system —
config/lootmanager/permissions.json:- 4 рівні: NONE / VIEW / EDIT / ADMIN
- OP level 2+ автоматично отримує ADMIN
- Команди:
/lm perm grant <player> <level>,/lm perm revoke,/lm perm list - Model: модератор без OP може бути EDIT (керує точками без shutdown сервера)
- Per-point bind rollback:
- Кожна точка тримає історію 3 останніх прив'язок шаблонів
- Іконка
[↶]з'являється праворуч від stats badge коли є що відкатити - Один клік відновлює попередню прив'язку
/lm permкоманди — управління permissions з консолі / чату- Loot Spawn Wand — special admin item for fast point creation
- Right-click block: creates spawn point at top face
- Shift+RMB: removes nearest point within 5 blocks
- Available via Tools tab → "Give me a Wand"
- Teleport to Point —
[T]icon on every point card; closes GUI and teleports to point's dimension and coordinates - Multi-level Undo — stack of last 10 mass operations with labels ("Set timer 300s for group", "Delete group X", "Batch enable 12 points")
- Server sends back remaining undo count via S2CUndoStatusPacket
- Live Event Feed — all admins receive S2CSpawnEventPacket broadcasts when points successfully spawn loot; ToolsScreen shows last 20 events
- Per-point Statistics —
totalSpawns,spawnsLast24h, auto-resetting 24h window; shown as badge on point cards - Reverse Item Lookup — Tools tab "Find by Item": type item ID, see which templates contain it + how many points are bound; click a result to teleport
- Health Check — Tools tab lists data issues: unbound points, empty templates, invalid radius, broken template references
- Card-based GUI — SpawnPointListScreen redesigned with 3-row cards (status dot, name, badges, action icons) replacing 12-column table
- Section-based Editor — TemplateDetailScreen split into 3 logical sections: ⚡ Spawn Behavior / 🎲 Item Selection / 📦 Loot Pool
- Design System v2 — 4-level typography, semantic color palette, 15-icon enum,
drawSection/drawBadge/drawCard/drawStatusDotprimitives - Toast notifications — popup queue (success/warning/error/info) instead of inline error text; auto-fade in 3s
Changed
- ParticleType resolution:
ClientTickHandler.resolveParticleType()actually readstemplate.particleTypeinstead of always rendering END_ROD - ParticleSettingsScreen: replaced 1.17-only types (ELECTRIC_SPARK, GLOW) with 1.16.5-compatible types (PORTAL, SMOKE, HAPPY_VILLAGER)
SpawnPoint.nextSpawnEpochMs/lastSpawnEpochMs→volatilefor safe GUI readsDataManager.load()now migrates old saves (fills missingparticleType,spawnMode,items,dimension)LootDropper.spawnLoot()now returnsList<String>of spawned item IDsapplySettings()(TemplateDetailScreen): per-field validation with toast feedback ("Max radius must be ≥ MinR (N)") instead of silent ignore
Fixed
setHint()calls removed (method doesn't exist in 1.16.5 TextFieldWidget)varkeyword replaced with explicitStream<Path>for Java 8 compatibility- Simulator iteration count clamped to 1–10000 client-side
- AddItemScreen: live green/red border around item ID field based on validity
C2SSetTimerPacketrejectsseconds < 1server-side
[0.0.1] - 2026-05-30
Added
-
Core Spawn Management System
- Create unlimited loot groups to organize templates
- Define templates with configurable respawn timers (in seconds)
- Bind spawn points to templates for loot generation
- Individual timer overrides per spawn point
-
5 Spawn Modes
- GROUND: Spawn at exact coordinates
- RADIUS: Randomize within min/max radius (in blocks)
- ONLY_IF_EMPTY: Check 2-block radius before spawning
- REPLACE_OLD: Clear area then spawn
- ADDITIVE: Cumulative spawning up to maxItemsPerSpawn limit
-
Flexible Item Selection
- Chance-based mode: Each item checked independently (0-100% per item)
- Weighted pool mode: Select items by relative weight with configurable pick count
- Per-item NBT tag support for enchantments and custom properties
- Min/Max stack count per item
-
Admin Tools
- Audit logging: All operations logged to
logs/adminlog_YYYY-MM-DD.txtwith timestamps - Automatic backups: Snapshots before mass operations, keeps 20 most recent
- Single-level undo: Revert last bulk operation (timer set, group delete, etc.)
- Particle visualization: Render spawn points with configurable particle types and radius
- Audit logging: All operations logged to
-
Full GUI Interface
- Main menu with group list and search
- Group detail view with template management
- Template editor with spawn mode, radius, and item list
- Spawn point list with dimension filter and search
- Add item dialog with live icon preview
- Spawn point simulator: Test drop probabilities across N iterations
- Particle settings dialog for visualization customization
- Scrollable lists with proper UI layout (no overlap)
- Context tooltips on all buttons and fields in selected language
-
Data Persistence
- JSON-based storage in
config/lootmanager/ - Atomic writes (temp file → rename) for corruption prevention
- Import/Export groups to share configurations
- Backup and restore functionality
- JSON-based storage in
-
Network & Server
- SimpleChannel-based packet synchronization
- Priority queue scheduler with 1-second tick granularity
- Server-side spawn processing, client-side admin visualization only
- Chunk loading verification before spawning
-
Localization (9 Languages)
- 🇬🇧 English (en_us)
- 🇺🇦 Ukrainian (uk_ua)
- 🇩🇪 German (de_de)
- 🇫🇷 French (fr_fr)
- 🇪🇸 Spanish (es_es)
- 🇧🇷 Portuguese Brazil (pt_br)
- 🇨🇳 Chinese Simplified (zh_cn)
- 🇯🇵 Japanese (ja_jp)
- 🇵🇱 Polish (pl_pl)
- All UI strings translated with context-aware hover tooltips
-
Command Interface
/lootmanager menu— Open management GUI/lootmanager reload— Reload from disk/lootmanager group create <name>— Create loot group/lootmanager template create <group> <name> <secs>— Create template/lootmanager template additem <template> <item> <min> <max> <chance>— Add item/lootmanager point create <name>— Create point at your position/lootmanager point bind <point> <template>— Bind template/lootmanager point clear <name>— Clear spawned items/lootmanager timer set <template> <secs>— Set template timer/lootmanager timer setall <group> <secs>— Set group timer/lootmanager show all//lootmanager hide— Toggle visualization/lootmanager spawn test <point>— Test spawn at point- Alias:
/lmfor all commands
Technical Specifications
- Minecraft Version: Forge 1.16.5
- Java: JDK 8 (Java 8)
- Gradle: 8.11 with wrapper
- Network Protocol: SimpleChannel v2
- Scheduler: Priority queue-based, 1-second resolution
- Storage: Atomic JSON writes to prevent corruption
- Permissions: Requires OP level 2
Known Limitations
- Single-level undo (one revert per operation)
- Spawn radius uses circular calculation (not rectangular)
- Chunk must be loaded for spawning to occur
- Import files must be manually placed in
config/lootmanager/imports/
Future Roadmap (Planned)
- <input class="task-list-item-checkbox" disabled="disabled" type="checkbox" />Save fully async
- <input class="task-list-item-checkbox" disabled="disabled" type="checkbox" />DataManager split into focused services; unit test coverage
- <input class="task-list-item-checkbox" disabled="disabled" type="checkbox" />Visual chest-grid loot editor
- <input class="task-list-item-checkbox" disabled="disabled" type="checkbox" />Cross-dimension template binding
- <input class="task-list-item-checkbox" disabled="disabled" type="checkbox" />Vanilla datapack loot-table import
- <input class="task-list-item-checkbox" disabled="disabled" type="checkbox" />API for other mods to integrate custom loot sources
All Relations
- All Relations
- Embedded Library
- Optional Dependency
- Required Dependency
- Tool
- Incompatible
- Include
This mod has no related projects

