protectionblocks-1.0.9.jar
Curse Maven Snippet
What's new
π v1.0.9 β Admin UX polish & several fixes
Focus release on tightening the admin experience: the ConfigScreen and the block management GUI got a batch of small-but-important quality-of-life improvements, and a handful of nasty bugs from 1.0.8 got hunted down (including one where "Sin permisos" for an ally silently didn't apply). New admin command /protection grantfreetime to bypass the "once per player" limit on the free-time bonus when testing.
π New admin tools
/protection grantfreetime <player> <hours>β OP-only. Overrides theVeteranPlayerTrackerpool of the target player: sets their remaining free time to the given number of hours (0 = cut without removing the veteran flag). Bypasses the "once per player" rule so you can re-test the mechanic on any player, including those who already burned through their bonus. After applying, the mod broadcasts a freshSyncFuelStatePacketto all the target's blocks so any open GUI reflects the change instantly./protection diagextended β the block diagnostic now dumps the full free-time state:graceGlobal,gracePersonal,graceEffective(all with a human-readable "23h 45m 12s remaining" or "expired X ago"),isInPersonalGrace,lastConsumeMs, plusownerIsVeteranand the currentnewPlayerGraceHoursconfig value. Everything you need to answer "why is / isn't this player's block in grace right now?" with a single command.
π¨ Admin ConfigScreen polish
In-GUI toast notifications
Chat confirmations were unreadable behind the dark admin panel. Replaced by proper toast cards that float centered just above the Save/Cancel buttons.
- Card style follows the ModernUI system β dark background, 2px accent border on top (green for success, amber for warning), horizontally centered icon (β/β /βΉ) + text.
- Auto-dismiss after 4s with a fade-out in the final 500ms.
- Server-side
sendSystemMessageto the admin sender was suppressed for the confirmed actions (config saved, grace granted, grace cancelled). The toast now owns the feedback loop; the chat stays clean. - The broadcast to other players (global grace grant/cancel) is untouched β they still get the chat message.
Auto-disable everything when currency mode is OFF
Toggling off "Activar modo moneda" now visually and functionally disables the rest of the Currency tab in real time, so admins can't accidentally change values that won't matter.
- All EditBoxes call
setEditable(false)as well as.active = falseβ in vanilla 1.20.1,.activealone doesn't visibly gray out an EditBox; you have to flip the editable flag to get the mutedtextColorUneditable. - All buttons gray out via the vanilla
.activeflag. LabelWidgetnow respects its own.active: when off it forces a muted0x555F6Acolor, overriding any inlineStylethe Component might carry (so the blue-accented "Tiempo gratis" hint header also dims properly).- The 5 tier-cost lines and the grace countdown, both drawn manually each frame, also render muted when currency mode is OFF for coherence.
- The checkbox is a subclass with an
onPressoverride that flipsdraft.enableCurrencyModeAND runssetCurrencyDependentActive(selected)immediately β before this the draft was only committed on Save so the toggle appeared "dead" until you saved.
Global grace broadcasts to all online players
- Grant global free time β all online players (not just the admin) receive a chat message: "π The admin granted X of global free time β your blocks won't consume fuel during that period." (5 langs, in gold).
- Cancel global free time β same treatment: "Global free time was cancelled by the admin." (in yellow).
- Also, granting global grace now broadcasts a fresh
SyncFuelStatePacketto every loaded block just like cancel already did. Before, the cancel side was instant on other players' open GUIs while grant was not β the label update on other clients had to wait for the next fuel tick or a GUI reopen.
π¨ Block management GUI polish
Fuel label: "1 item = HH:MM:SS" instead of "0.017x/min"
The fuel state line used to say "Combustible: agotado (0.017x Iron ingot/min)" β an unintuitive ratio the admin had to divide mentally to understand. It now says "Combustible: agotado (1 Iron ingot = 00H:58M:49S)" β the duration a single unit lasts, formatted as a stable-width clock.
- New helper
formatOneUnitDurationHMS(costPerMin) = 60 / costPerMinseconds, formatted asNNH:NNM:NNSwith zero-padding. - The
H,M,Sletters render in a distinct pastel-blue accent color (#AED6F1), while the digits keep the fuel state color (green/amber/red). Built via styledComponentargs toComponent.translatablesodrawString(Component)respects the per-segmentStyle. - Ratio β€ 0 renders
--.
Ally picker input polish
- Typing now auto-focuses the search input right when the picker opens β no more click-to-focus. Fixed by deferring the
setFocusedto the first render frame after opening; before,Screen.mouseClickedwas resetting the focus back to the "+ Add ally" button at the end of the click cycle, silently overriding our call. - New
CenteredEditBoxsubclass: text is centered horizontally and vertically in the input via a translate beforesuper.render. Vanilla unborderedEditBoxpins the text to the top-left corner; the shift math replicates what bordered mode would do.
π Fixed
- "Sin Permisos" silently didn't apply. Setting an ally to "no permissions" via the dropdown left them with their previous rights β they could still build/break as before. Root cause:
EnumSet.copyOf(Collection)throwsIllegalArgumentExceptionwhen the collection is not itself anEnumSetAND is empty (documented but easy to miss).UpdatePermissionsPacketdecodes into a plainHashSet; an empty "no permissions" arrived as an emptyHashSet;AllyData.setPermissionstriedEnumSet.copyOf(emptyHashSet)and threw. The exception was silently swallowed inside the packet handler'senqueueWork, soupdateAllyPermissionsnever ran. Fixed by building anEnumSet.noneOf(PermissionType.class)and usingaddAllβ works for both empty and non-empty inputs without special-casing. - Grant global grace was NOT real-time on other players. Cancel broadcast a fresh
SyncFuelStatePacketto every loaded block so open GUIs updated instantly; grant broadcast the chat message but forgot the fuel state. Result: cancelling was instant on other players' open GUIs, granting only appeared after the next fuel tick or a GUI reopen. Extracted the two-line broadcast logic into a helper (broadcastFreshFuelStateAll) and called from both paths. - Cancel of global grace was clearing personal grace timestamps too. A previous attempt to fix the "grace shows 24h after cancel" symptom accidentally also cleared the per-player free time pool. Reverted: cancel only touches
GracePeriodManager(the global admin grace),VeteranPlayerTrackeris untouched. The "shows 24h after cancel" symptom is now fixed a different way β the broadcast of the fresh fuel state (see above) makes clients re-readgetEffectiveGraceUntilMs = max(0, personalOfOwner)immediately, so what the client sees is the actual remaining personal grace (not the stale cached global timestamp). - Toast card was showing two β marks. The lang strings started with a hardcoded "β β¦" carried over from when notifications were plain drawString. The card renders its own icon per type; the extra β in the text was redundant. Stripped in all 5 langs.
- Free-time hint clarified. "(los depΓ³sitos se consumen al terminar)" was ambiguous β several users interpreted it as "there IS consumption". Changed in all 5 langs to "(el combustible NO se consume mientras tanto)" β explicit statement that fuel is preserved for the duration of the grace window. Same rewording (with the appropriate wording) applied to en_us, de_de, fr_fr, it_it.
π§ Under the hood
VeteranPlayerTracker: newforceSetGraceUntilMs(uuid, endMs)that overrides the pool ignoring the "already veteran" rule β used by/protection grantfreetime. Kept private-friendly so gameplay code keeps usinggrantOnce.DepositFuelPacket.broadcastStatewas package-private; upgraded topublicso the command layer can call it directly instead of duplicating the packet build.LabelWidget.renderWidget: adds an active-aware branch. Active β keeps the Component's Style. Inactive β renders the plainStringignoring Style, forcing a muted color.- New
CenteredEditBoxin the GUI package: only overridesrenderwith a translate. Hitbox, focus, keyboard handling, everything else inherited from vanillaEditBox. ConfigScreen:currencyDependentWidgetslist +setCurrencyDependentActive(boolean). All labels routed through a newaddCurrencyLabelhelper so they get registered in the list automatically.setEditableis toggled on EditBoxes in addition to.activeβ vanilla 1.20.1 doesn't gray out EditBoxes on.active = falsealone.- Ally-picker focus fix: new
pickerFocusPendingflag.openAddAllyPickersets it totrue,renderAddAllyPickerapplies the focus on the first frame after the picker opens (when the button'smouseClickedcycle has finished and can no longer overwrite it).
π¦ Compatibility
- Minecraft 1.20.1
- Forge 47.x (built against 47.4.8)
- Fully compatible with 1.0.8 worlds β no data migration.
Β© 2026 vicxelcode β MIT License
This mod has no additional files

