Releases: Zynora-fr/KubeUI
Release list
KubeUI 0.4.0-1.21.1
Port of 0.4.0 to NeoForge 1.21.1 (KubeJS 2101.7.2-build.368) - full feature parity with the main
0.4.0 branch (see github.com/Zynora-fr/KubeUI for the
complete feature list: economy, machines & automation, world map, dialogue, storage, progression,
combat & status HUD, structures & dungeons, guilds & factions, social, housing & claims, settings
hub, the machine screen redesign, JS-only slash commands for guild/party/housing, the tabbed social
screen, custom leaderboards, scriptable guild/party rules, and KubeUIHud), rebuilt against
1.21.1's own real APIs. Real, version-specific adaptations along the way (not guessed - each
verified against this exact NeoForge 21.1.248/KubeJS 2101.7.2 build):
ResourceLocation(26.1.2'sIdentifieris this version'sIdentifier... this version is
ResourceLocation- the rename only exists going the other way) - same API surface otherwise
(tryParse/fromNamespaceAndPath/withDefaultNamespaceall real here too).- Block entity persistence is the pre-
ValueInput/ValueOutputsaveAdditional(CompoundTag, HolderLookup.Provider)/loadAdditional(...)shape;CompoundTagitself has nogetXxxOr(...)/
getListOrEmpty(...)/getCompoundOrEmpty(...)convenience API at all on this version - not even
through KubeJS, which doesn't add it either (26.1.2'sCompoundTaggained these natively as real
Mojang methods, nothing KubeJS-specific).KubeUINbtCompatreproduces the 26.1.2 semantics for
this project's own Java call sites;KubeUINbtView(a real public class, since KubeJS's own
CompoundTag-to-JS conversion falls back to normal reflection for any method name that isn't
itself a stored NBT key) does the same for scripts - every JS-facing touchpoint that used to hand
out a rawCompoundTag(KubeUIActionHandler#handle,KubeUIRemoteScreens#register, and a
handful ofKubeUIActionsmethods like.claimsOf/.guildMembers/.playerData) hands out this
wrapper instead, so a script written asdata.getStringOr('key', 'fallback')works unmodified
either way.KubeUINbtViewalso implements KubeJS's ownNBTSerializableso the reverse
direction - a script handing one straight back intoKubeUIActions.openRemote(...)- serializes
correctly instead of silently dropping the value. - The currency ledger's
KubeUICurrency.load(server)no longer clears already-registered
currencies: on a singleplayer/integrated server,server_scriptstop-level code (where
KubeUIActions.registerCurrency(...)normally lives) runs beforeServerAboutToStartEvent
fires this, so clearing it here was wiping out every currency a script had just registered
(/money balancealways reporting "no currencies registered" was the real, reported symptom).
Same fix landed on the main 0.4.0 branch - this wasn't 1.21.1-specific, just first caught here. AbstractContainerScreen's real override points arerenderBg(GuiGraphics, float, int, int)/
renderLabels(GuiGraphics, int, int)(different names and argument order from 26.1.2's
extractBackground/extractLabels); a plainScreensubclass's isrender(GuiGraphics, int, int, float).GuiGraphicsExtractor(this project's existing 26.1.2-API-shape compatibility
shim over the realGuiGraphics) is constructed explicitly at each of these bridge points.ClickType(notContainerInput) forAbstractContainerMenu#clicked;Commands.LEVEL_GAMEMASTERS
is a plain numeric op-level constant with noCommands.hasPermission(int)static helper - real
permission gating is.requires(source => source.hasPermission(Commands.LEVEL_GAMEMASTERS))(JS
commands) /player.hasPermissions(level)(plain Java) instead.Item#getBurnTime(RecipeType)/AbstractFurnaceBlockEntity.isFuel(...)(noLevel#fuelValues()
registry yet) for the machine block's real furnace-shaped fuel slot;BlockEntityType.Builder.of( factory, blocks...).build(null)(no bare public constructor yet) for block-entity-type
registration;DeferredRegister.Blocks#registerBlock(name, factory, Properties)takes a plain
Properties, not aSupplier<Properties>.
No functional or content changes versus the main 0.4.0 branch.
KubeUI 0.4.0
Nothing has actually shipped since v0.3.0 -
everything below landed as one continuous, still-unreleased development cycle, so it's kept as a
single 0.4.0 entry instead of a new version number for every batch of work.
Combat & status HUD: KubeUIActions.showBossBar(viewers, barId, name, health, maxHealth, phases)
draws a fully custom boss bar, top-center - phases is a list of {threshold, color, text} objects,
so a fight's bar can change color/subtitle as health crosses a threshold, something vanilla's own
boss bar has no concept of. KubeUIActions.defineStatus(id, name, icon)/.applyStatus(...) add
purely scriptable statuses on top of real vanilla MobEffects in one unified buff/debuff overlay -
a status here has no built-in behavior of its own, its entire "tick logic" lives in a script's own
KubeUICombatEvents.statusTick(event => {...}) handler. KubeUIActions.startCooldown(player, id, durationTicks) gives a script-defined cooldown (not tied to a real item, unlike vanilla's own
Player#getCooldowns()) a real HUD bar above the hotbar. A combat-log HUD (toggleable per player,
KubeUIActions.setCombatLogEnabled(...)) and an AOE/range indicator ring
(KubeUIActions.showAoeIndicator(player, radius, color)) round out the overlays. Every real hit is
tracked automatically off LivingDamageEvent.Post into a per-player "combat session" - once a
player goes a few seconds without landing or taking a hit, KubeUICombatEvents.combatEnded fires
with the session's damage dealt/taken and duration for a script's own post-fight recap, and
KubeUIActions.combatHistory(player) reports every tracked session back.
Structures & dungeons: KubeUIActions.lootTableBrowser(player, lootTableId) previews a real
vanilla LootTable's probable contents - genuinely rolled 200 times and tallied (a loot pool's
internal weights aren't generically readable, and plenty of entries are conditional on context a
preview doesn't have), always presented as "probable", never a guaranteed drop list.
KubeUIActions.defineStructure(id, name, difficulty, ...) declares a scriptable info zone - opt-in
per player, a toast on approach. KubeUIActions.defineDungeon(id, name, roomIds) tracks room/chest/
boss progress per player, with real progression locking (a room only unlocks once the previous one
in the list has actually been visited) and an automatic completion-time leaderboard the moment every
room's visited and the boss goes down. KubeUIActions.defineLootTable(id, entries)/.rollLoot(id, difficultyMultiplier) add a scriptable weighted-random loot table independent of vanilla's own (a
higher multiplier skews the odds toward rare entries, without hand-authoring a second table per
difficulty tier) - KubeUIActions.startLootVote(...)/.castLootVote(...) add real server-arbitrated
need/greed group loot voting on top of it (need always beats greed, ties broken randomly within the
winning group), the voting screen itself left to a script via the same generic server-push mechanism
every other custom screen in this mod already uses.
Economy: a scriptable, server-authoritative currency system independent of any physical item -
KubeUIActions.registerCurrency(id), .pay(player, currency, amount)/.charge(...) for atomic
credit/debit that never leaves a silent negative balance, .transferCurrency(from, to, currency, amount) with a configurable commission (.setCurrencyTransferTax(...)), and
.exchangeItemForCurrency(...)/.exchangeCurrencyForItem(...) to bridge it with real inventory
items. KubeUIActions.defineShop(shopId, name, stock) builds a full vendor - buy/sell, per-line
stock limits, and optional price fluctuation (a bounded random walk on a configurable interval) -
opened with KubeUI.shop(shopId). KubeUI.currencyHistory(currency) shows a player's own
transaction log, KubeUI.leaderboard(currency) the richest players server-wide (built on the
existing sortable table widget), and the new KubeUIEconomyEvents.transaction/.balanceChanged
event group (KubeUI's first server_scripts event group) lets a script react to every balance
change. Every shop in the project (including the "real"/emerald and "validated" reference demos)
now runs on this same currency instead of ad-hoc raw-item or in-memory gold tracking, so balances
actually persist across restarts everywhere. The real /money balance [currency]/pay/deposit/
withdraw command (deposit/withdraw need OP) gives players and admins chat access to the same
system without needing a script, and ServerEvents.commandRegistry (KubeJS's own real command
event - see kubeui_admin_commands_example.js) is the documented way to add further custom admin
commands on top of KubeUIActions.pay/.charge without touching Java at all. Fixed a real bug
where a singleplayer world's registered currencies could disappear (/money balance reporting "no
currencies registered" even with a KubeUIActions.registerCurrency(...) script loaded): on an
integrated server, server_scripts run before ServerAboutToStartEvent, and the currency
ledger's own startup load was unconditionally clearing the in-memory registry that scripts had
just populated.
Machines & automation: KubeUIActions.defineMachine(kind, name, inputItem, inputCount, outputItem, outputCount, processTicks, energyPerTick, redstoneMode) declares a recipe kind for the
one real, placeable kubeui:machine block (NeoForge's registries are frozen long before any
server_scripts reload runs, so a script can't register a genuinely separate block per kind the way
it can define one server-side) - KubeUIActions.giveMachineItem(player, kind) hands out a copy
already set to that kind, with the kind's own real display name stamped onto the item itself
(DataComponents.CUSTOM_NAME) so a Crusher and a Smelter already read as two different things in
the inventory/hotbar tooltip, not just after placing one and opening its screen - also now logs a
clear warning if a script calls this before defineMachine ever registered that kind, the real
cause behind a placed machine silently showing a generic name and a "No recipe" status. A machine
has four real slots (input/output/upgrade/fuel),
ticks its recipe while it has enough input and (if energyPerTick > 0) enough energy, and respects
redstoneMode ("ignore"/"requireSignal"/"disableOnSignal"). Energy comes from a real fuel
slot burning the same vanilla-furnace fuel table (level.fuelValues()) any real furnace does -
coal, charcoal, a lava bucket, planks, and everything else vanilla or another mod registers as fuel
all work, including the real "empty bucket left behind" behavior - or from
KubeUIActions.chargeMachine(level, pos, amount), the real integration point for a future energy-
mod bridge. KubeUIActions.registerMachineUpgrade(itemId, speedMultiplier, yieldBonus) lets an item
dropped in the upgrade slot speed up processing and add bonus output. .linkMachineOutput(level, from, to) chains one machine's output straight into another's input, and .setMachineNetwork(...)
groups machines for KubeUI.machineNetworkStatus(networkId)'s aggregated controller/stats view. The
machine screen lays the four slots out the same recognizable input-above-fuel-with-a-flame-between-
them, output-off-to-the-side-via-an-arrow shape a furnace-like screen always has, shows full slot
names, a live crafted-count, a small hand-drawn flame that lights up while fuel is actually burning,
a compact energy gauge, and the machine's real per-kind name in its title bar - all drawn as a
themed custom panel (matching every other KubeUI screen's colors) instead of a reused, mismatched
vanilla furnace sprite. A short status line ("No recipe" / "Blocked" / "No input" / "Needs fuel" /
"Output full" / "Running...") next to the crafted-count now says outright why a machine isn't
processing right now, instead of a player only ever finding out from a chat alert throttled to once
per 30 seconds. A block placed with no kind (or an unregistered one) - the real cause of "No
recipe" for anyone who only ever finds a generic, unconfigured kubeui:machine (creative inventory,
/give, ...) rather than one already given via giveMachineItem - now shows a real in-GUI picker
instead: every registered kind's real icon and name, synced the same way a trade screen's own offer
list already is, click one to become that kind on the spot. Fixed two real reported bugs along the
way: the screen's drawn slot frames and the
menu's actual item slots had drifted to different coordinates (putting every item and label in the
wrong spot), and the energy gauge was an oversized, disconnected bar with nothing tying it to the
fuel slot feeding it.
World map: KubeUI.worldMap() opens a full-screen, pannable (drag or arrow keys)/zoomable
(scroll wheel) map built on the same real per-block color sampling as the existing HUD minimap
widget (.map(id, radius)), backed by a persisted, progressively-revealed explored-cell cache -
areas you haven't actually visited stay blank rather than faked. KubeUIMapIcons.registerIconProvider(...)
lets other systems plot colored live-entity markers on it (KubeUI.registerMapIcon(entityTypeId, color) covers the common case). Waypoints (/kubeui map waypoints, or "Add Waypoint Here" on the
map itself) persist across sessions and can be shared to another player over the network. The
screen is now styled like a familiar minimap-mod layout: a docked top toolbar, a docked waypoint
sidebar (jump-to and remove per entry, no command needed), a bordered viewport with a compass tick
and a scale bar, and a live coordinate/biome/zoom readout under the cursor. Each sidebar entry also
has a "Trk" toggle - like Journeymap's own per-waypoint "show on HUD" option, it keeps that
waypoint's direction/distance visible top-center of the screen even with the map closed (fixed a
real gap where the HUD indicator itself already existed but ...
KubeUI 0.3.0-1.21.1
Port of 0.3.0 to NeoForge 1.21.1 (KubeJS 2101.7.2-build.368) - same feature set as 0.3.0 below,
rebuilt against 1.21.1's own rendering, input, scrolling, NBT, permissions, networking, recipe and
container-menu APIs. No functional or content changes versus 0.3.0.
KubeUI 0.3.0
Theming & accessibility: named theme presets ("dark"/"light"/"high-contrast", or a custom
one via KubeUI.registerThemePreset(...)) that fade between each other instead of snapping; a
colorblind-safe default .colorPicker() palette; .style({color, accent}) to override a single
element's colors beyond the global theme; KubeUI.setFontScale(...) to enlarge text independently
of box sizes; KubeUISidebar.setIconPack(...) to reskin the whole sidebar in one call;
/kubeui theme preview <name>; Key-suffixed translated variants of every text-bearing widget
(.buttonKey(), .toggleKey(), .textFieldKey(), .textAreaKey(), .tooltipKey(),
.narrationKey(), .badgeKey()); and a visible keyboard focus outline plus full keyboard
operability on every custom widget that was missing one (range slider, keybind capture field,
context menus).
Update checking: the player is told in chat, on joining a world, whether they're running the
latest KubeUI version - via NeoForge's own built-in update checker, sourced from the real
CurseForge project (no custom network code, no API key).
Server networking: the server can now push a screen to a specific player or broadcast one to
everyone (KubeUIActions.openRemote(...)/.broadcastUpdate(...), received via
KubeUIRemoteScreens.register(...)); .requirePermission(gate) gates a widget behind NeoForge's
real permission API (LuckPerms or equivalent can plug in); .screenId(id) lets the server know
which screen a player currently has open (KubeUIActions.getOpenScreenId(...)); actions can be
throttled per player (KubeUIActions.register(id, throttleMs, handler)) and schema-validated
before they ever reach a handler; screen.runServerAction(..., onAck) confirms an action actually
ran; and the server can show/hide a sidebar icon per player.
Config & persistence: KubeUI now has a real disk-backed config (config/kubeui-common.toml) -
the personal scale/font scale/theme a player sets are remembered across restarts by default;
.draggable() window positions persist across game launches, not just within a session;
/kubeui export//kubeui import moves a player's KubeUI preferences between installs;
KubeUIActions.playerData(player) gives a script real per-player server-side storage with no
setup; and KubeUI.configScreen(schema) builds a settings screen straight from a data description
instead of a hand-chained builder.
In-game script editor: /kubeui editor opens a real file manager/text editor for
kubejs/client_scripts without leaving the game - list, create, open, edit and delete any .js
file, then "Save & Reload" to see the change live immediately (the same reload behind /kubejs reload client-scripts). Replaces two earlier attempts (a click-to-build visual editor that didn't
hold up in real play, then a single fixed canvas file meant to be edited externally) with actual
in-game text editing.
Developer tooling: .when(condition, b => ...)/.repeat(count, (b, i) => ...) builder helpers;
KubeUI.describe(builder) for a human-readable widget/id listing; KubeUI.lint(builder) to catch
duplicate ids before a screen ever opens; KubeUI.toJson(builder)/.fromJson(json) and
screen.dumpTree() for a JSON snapshot of a screen's layout (a representative widget subset);
error/warning messages now name the widget type and id involved; a slow script callback (50ms+) is
now logged instead of just silently making the game stutter; /kubeui profile and
/kubeui stresstest for build-time timing; and a real NeoForge "Config" button next to KubeUI in
the Mods list (not the Fabric-only ModMenu the idea was originally framed around).
Robustness: a runaway recursive layout helper now fails with a clear error instead of a
StackOverflowError; .draggable()/persisted widget state and pending server-action
acknowledgements are now capped instead of growing unboundedly for a long-running client or a
server that never replies; and a player's per-action throttle state is now actually cleared on
disconnect (a real gap a security-focused audit found alongside the above).
Docs & community: a TUTORIAL.md walkthrough from the first KubeUI.builder(...)
to a complete server-backed screen; a templates/starter/ starter
project (generatable with node scripts/create-kubeui-script.js <dir>); a bigger worked example
(a quest board with real per-player server-side progress, testkubejs/*/kubeui_quest_board_example.js);
and a SECURITY.md covering the project's security policy.
Recipes: KubeUI.recipeScreen(recipeTypeId) opens a screen showing every recipe of a given
type (vanilla or a script's own custom one) automatically, and KubeUI.recipesFor(itemId, onResult)
looks up every recipe that accepts a given item as an ingredient - both work generically across
recipe types, real server-side data (not guessed or client-only), shown in a properly-sized,
scrollable screen. .recipeSlot(id, itemIds, onClick) is a new JEI-style widget that cycles
through a group of acceptable items instead of showing just one.
Custom recipes: /kubeui recipe-designer opens an in-game screen to define your own
crafting/furnace/blast-furnace/smoker/stonecutter/smithing-table recipes - pick a kind, then
arrange real items (any item from your own inventory, dragged in like the real thing) in that
kind's actual vanilla interface, and left-click the result slot to save. Saving/deleting doesn't
reload automatically anymore (a full data-pack reload on every click was a real lag spike) - a
"Reload Recipes" button in the designer applies everything you've changed in one go.
Custom trading: /kubeui trader-designer opens an in-game screen to build a custom trader
entirely without scripts - add trades in a real item-slot GUI (place the cost items and the result,
left-click the result to add it), toggle whether it has AI and whether it can move, then give
yourself the finished trader as a real item (using the actual villager spawn egg icon) and
right-click a block to spawn it, exactly like a vanilla spawn egg. Trading with it uses the real
villager trading screen too, laid out just like a real villager with a profession - a trader with
several trades shows them as a real list of cost/result icons (handy since one trader having
multiple trades is the common case), and picking a row pulls its cost items from your own inventory
into the payment slots automatically, same as clicking an offer on a real villager - left-click the
result to receive it and pay. A trader's trades are baked directly onto the spawned villager itself,
so they're still there after closing and reopening the game - not lost on a server restart like an
earlier build.
KubeUIActions.registerTradePool(poolId, trades, condition?) still defines a
weighted-random pool of trades from a script (with configurable stock/restocking and optional
reputation/quest gating) for anyone who wants that lower-level control, and
KubeUIActions.tagTradePool(entity, poolId) turns any entity into a trader that way instead.
/kubeui villager-trades <target> inspects an entity's current trades/stock either way, and
KubeUIActions.tradeHistory(player) reports what a player has already bought.
Quests: KubeUIActions.defineQuest(id, { title, description, requires, objectives, rewards })
defines a real, server-tracked quest - built-in objective types cover collecting items, killing a
kind of entity, visiting a position or structure, and reaching an XP level, and any other type is a
plain counter your own script bumps from whatever event it wants
(KubeUIActions.incrementQuestObjective(...)), no callback registration needed. requires chains
quests together (a quest only becomes available once its prerequisites are completed), and
progress lives on your own player data - it's still there after closing the game entirely.
KubeUI.questLog()//kubeui quest-log (also reachable as the shorter /quest) shows every quest
and your progress on each, grouped by status; a small always-on-screen tracker (Track button in
the log) shows your current objective without needing the log open. KubeUIActions.tagQuestGiver(entity, questIds)/
/kubeui tag-quest-giver turns any entity into a quest giver - right-clicking it opens a real
Accept/Turn-in screen, server-verified on every click so nothing can be granted twice.
/quest accept <questId>//quest complete <questId> do the same without needing a giver entity
at all, for a server that wants quests reachable purely by id.
/kubeui quest-editor composes a quest entirely in-game, no script required, and - unlike a
script, which re-declares its quests every boot - saves it for real so it survives a server
restart too.
Look & feel: KubeUIScreenBuilder.windowBackground(texture) gives a screen a real nine-slice
panel drawn behind its whole content (not just a decorative strip) - the quest screens, recipe
designer, trader designer, script editor, and recipe browser each now have their own real,
custom-drawn panel texture instead of the plain default look: a dark panel with rounded corners
and a soft glowing accent border, each screen's own accent color, sharing one consistent style so
it's obvious at a glance which screen is open. The custom trader's trade/payment screens no longer
reuse vanilla's villager trading texture either - they're now the same custom-drawn panel style,
with real hand-drawn slot frames instead of texture-baked ones.
Fixes: a script's runServerAction handler is now actually wrapped in a try/catch server-side
(a prior gap where a bug in one could take down the packet-handling thread); the quest board
reference example now actually takes the required items from the player's inventory on completion
instead of only checking a flag; a custom smelting/blasting/smoking recipe from the recipe designer
now actually clears any existing recipe for the same input first, so it can't be sile...
v0.2.0
What's changed
- No changes
See README.md for the exact Minecraft/NeoForge/KubeJS versions this build targets.
Warning
Release Drafter could not find a previous published release for Zynora-fr/KubeUI. This draft was created without a comparison baseline.
Important
Treat this draft as a manual starting point.
Review the proposed version, tag, and notes before publishing.
If you did not expect this to happen, open an issue.