All versions
0.0.2
Release
SLib 0.0.24 months ago 55
Compatibility
Minecraft: Java Edition
1.20.1–1.20.6
Platform
Forge
Supported environments
Client and server
Changes
Fixes
- Fixed lazy
Network.<clinit>crash on dedicated server and on Refresh TradesNetworkRegistrylock conflict. Channel is now created eagerly byEntryconstructor and lazy accessors fall back to a thread-safegetInstance(). - Fixed
PlayerEvent.PlayerLoggedOutEvent.getPlayer()access on Forge 47.4.20 — switched togetEntity(). - Fixed
MinecraftServer.getServer()deprecation on dedicated server — replaced withanchor.getServer()for filtered broadcast. - Fixed
PacketDistributor.TRACKING_CHUNKtyping — now passesLevelChunkresolved fromServerLevel.getChunk(). - Fixed double-counted
rejectedcounter on null NBT tags during sanitize. - Fixed malformed-JSON negative depth leaking out of
countJsonDepth— now returnsInteger.MAX_VALUEplus amalformedflag. - Fixed escape-sequence double-counting in JSON string length scan.
- Fixed unverified
FMLJavaModLoadingContext.get()deprecation warning inEntry. - Fixed
HashMaprace surface inRegistry.CONSUMER_VERSIONS— switched toConcurrentHashMap. IsolationRegistryis now real LRU.LinkedHashMapwas created withaccessOrder = falseso eviction was FIFO; switched toaccessOrder = truewith an overriddenremoveEldestEntry. Modules that have not been touched recently are now actually evicted first, which matters oncemaxTrackedModules(1024) is reached by busy servers.IsolationRegistry.clearFailureStatenow also wipes the sliding-window timestamp deque when it drops a module out of COOLDOWN. The window used to keep the lastslidingWindowTicksworth of failures, so a module that recovered and then tripped once more was promoted to ISOLATED instantly because of stale history.Failover.updateGameTimeno longer takes theIsolationRegistrylock 1024 times per tick. NewclearExpired(currentGameTime)walks every module under a single synchronized block and resets expired COOLDOWN entries in one pass.PacketGuard.allowsCooldown(level, lastActionGameTime)was dead code — it never read the internallastCooldownGameTimemap. Removed;tryAcquirealready enforces the cooldown against internal state.PacketGuardno longer leaks per-player entries for disconnected players.tryAcquireandmarkRefreshednow callmaybeCleanup(currentGameTime); the first call to advance pastcleanupIntervalTicks(default 12000 ticks) drops entries that have been quiet forstaleTicks(default 24000 ticks) throughConcurrentHashMap.entrySet().removeIf.Boost.cancelScheduleno longer scans every player. Added a reverse indexplayersBySchedule(key -> set of player UUIDs) so cancellation is O(N players of that schedule), not O(all players × all schedules).Boost.schedulenow removes the entry fromscheduledTasksbefore running the user work. Previously the entry was removed infinally, so a re-entrantschedule(same-key)call from inside the task could not cancel the in-flight task; the old one is now cancelled the moment the scheduler starts running it.Boost.shutdownnow callsForkJoinPool.awaitQuiescence(2s)beforeawaitTermination(2s).ForkJoinPool.shutdown()alone does not drain queued tasks that are being cooperatively forked;awaitQuiescenceforces the pool to actually wind down.Networkconsumer-log spam is fixed. The direction-mismatch / null-sender / handler-crash branches used to log at WARN / ERROR on every single bad packet; an attacker holding a valid connection could fill the log file. Each branch now goes through a per-id 5-second throttle on the allocator, so each(id, kind)pair logs at most once every 5 seconds.Network.nextIdis now fully atomic. TheregistrationFrozencheck, allocator lookup, slot reservation,id -> modIdandid -> packetNamewrites, and the registration log entry all happen under a singlesynchronized (ALLOCATOR_LOCK). Previously the freeze check happened outside the lock and could race withfreezeRegistration().Network.registerSafenow throwsIllegalStateExceptionwith a clear message ifgetInstance()returns null instead of crashing with a NPE deep inSimpleChannel. The eagerEntryconstructor still creates the channel, so this is a defensive guard for modders who might instantiateNetworkclasses without running the entry point.Boost.CleanupHooks.onPlayerLoggedOutnow also callsRegistry.clearPlayerState(playerId)so every registeredPacketGuardreleases the disconnected player's rate-limit and cooldown slots immediately instead of waiting for the periodicmaybeCleanupsweep.Networkdropped the unusedimport java.util.Collection;that survived the cleanup pass and confused IDEs.
Improvements
- Added SBS (Senior Boost and Stabilization) layer:
Failover,SafeExecutor,IsolationRegistry,CrashReport,ModuleState,WarningScreen,SbsClient,SbsServer. - Added warning screen on client with
BlurBackgroundfake-blur,Roundedpanel and a single "Ignore and continue" button. - Added server-side crash logger that routes crashes through
IsolationRegistrywithout ever loading client classes. - Added Boost layer:
FrameMonitorsliding window (p50 / p95 / p99),TickBatcherwith dedupe,Boost.offload(...)overForkJoinPool,Boost.schedule/scheduleAsync/cancelSchedule/cancelAllSchedules/cancelPlayerSchedules. - Added per-player schedule binding through
scheduleForPlayer(playerId, ...)so logout can cancel related work. - Added
LevelEvent.UnloadandPlayerLoggedOutEventhooks in Boost that cancel pending schedules and clear the tick batcher automatically. - Added
BlurBackgrounddefensive disposal pattern withreleaseAllocated/resetState/trackResource, plusWarningScreen.init/onClosecleanup hooks. - Added LRU + TTL eviction to
TickBatcherandIsolationRegistryso dynamic keys (per-NPC UUIDs, per-chunk IDs) cannot grow the map without bound. - Added
SendToAll(message, filter, anchor)with strictIllegalArgumentExceptionfor missing anchor or filter instead of a silent fail. - Added
PlayerPreviewModel.dispose()cleanup of syntheticRemotePlayer, and guardedmc.getWindow()against null. - Added
ItemIcon.renderInto(GuiGraphics, ...)zero-allocation overload for callers that already own aGuiGraphics. - Per-mod packet allocator uses double-checked locking on a dedicated
ALLOCATOR_LOCK— external Forge calls are no longer performed under our lock. - Network dispatcher validates
NetworkDirectionandgetSender() != nullforPLAY_TO_SERVER, runs handlers insideenqueueWork, and catches anyThrowableso a buggy handler cannot kill the packet pipeline. IsolationRegistryexposesclearExpired(currentGameTime)for batch sweeps.Failoveris the only caller in-tree, but external modders can call it from a server tick handler if they want explicit control over cooldown reclamation.PacketGuardconstructor takes a newcleanupIntervalTicksandstaleTickspair. Defaults are 12000 / 24000 ticks (10 / 20 minutes). Mods that know their player turnover can tune both down for aggressive cleanup or up for long-lived cooldowns.Boost.scheduleandBoost.scheduleAsyncnow share a single implementation: the scheduler thread only enqueues a deferredoffload(...)so heavy work always lands on theForkJoinPoolinstead of blocking the single scheduler thread.
UI / Sync
- Slib now ships a single channel
slib:mainand is shared across every consumer mod throughNetwork.namespace(modId). Consumer mods should migrate toNetwork.getInstance()instead of any prior static accessor. Rounded.drawOutlinenow goes through the SDF shader (newIsOutline / OutlineThicknessuniforms), so outlined rounded rectangles actually keep their corners on the outline ring instead of being an "enlarged rounded box" under the inner body. AA is still feather-aware at every GUI scale.RoundedButtonno longer takes eight to nine positional integers per constructor. There is one constructor:RoundedButton(x, y, w, h, label, onPress, Style). Static factoriesprimary / primaryWithHoverOutline / secondary / dangercover the common cases.Sidebar.mouseOverhit-test was rewritten. The previous version calledMinecraft.getInstance().mouseHandler.xpos() * scale / window.widthper item per frame and was inconsistent withPoseStacktransforms.Sidebar.draw(gfx, style, x, y, width, items, activeIndex, itemH, gap, onSelect, mouseX, mouseY)now uses the sameSidebar.indexAtmath the olddrawpath did for visuals.TabBarextractedcomputeLayout(SafeArea, count, justify).drawandindexAtboth consume the sameLayoutrecord now, so the visual layout cannot drift from the hit-test layout.ItemIconremoved the legacyrender / renderSized / renderCountoverloads that created a freshGuiGraphicsper call. Only the zero-allocationrenderInto / renderSizedInto / renderCountIntooverloads remain.ScissorClipstores arecord Clip(int x, int y, int w, int h)on the stack instead of a newint[]per push.SearchBarrewritten onStringBuilderwith an explicit cursor, selection anchor,Backspace / Delete / Left / Right (with optional word jump) / Home / End,Ctrl+A select all,Ctrl+C / Ctrl+X / Ctrl+Vand click-to-position-cursor. The previousO(n²)per-keystroke string concatenation is gone.Style.hoverBorderColor(defaults toborderColorwhen unset) added so widgets can draw a distinct outline on hover without the consumer building a secondStyle.Stylesheet.Rule.hoverBorderColor(int)is wired throughStylesheet.applyRule.Slider.valueFormatis aComponenttemplate;%sis replaced with the formatted number. The literal template is drawn otherwise.- Full UI + UX engine, split into two packages. The
en.antinimus.slib.api.uipackage is "how it looks" — primitives, drawing, layout, styles. Theen.antinimus.slib.api.uxpackage is "how it behaves" — focus, navigation, feedback, events, data binding, scroll, modals. UI primitives:Layout(golden-ratio solver, modular 4dp scale, proportional gap shrink, bottom-anchored reserve, adaptive panel sizing, horizontal stack withSTART/CENTER/END/DISTRIBUTEjustify),SafeArea(content box withinset / intersect / bottomAnchor / bottomStrip / splitHorizontal / right(width) / left(width) / mid(width)),Theme(gold + progress + tab + stat + divider tokens,progressFillColor(ratio)auto-picks critical / low / mid / high),Rounded(SDF shader),ScissorClip(nested clip regions),BlurBackground,Metrics(pixel-scale snap),ItemIcon,Style(immutable object + builder +withXxx(...)modifiers; presetsStyle.GOLDandStyle.MODERN),Stylesheet(CSS-like cascade —Selector.tag("button").cornerRadius(8),Selector.clazz("primary").accentColor(...),Selector.id("submit"), applied viastylesheet.resolve(tag, id, classes, fallback)so a screen can re-skin an entire widget tree by changing oneStylesheet),Easing(LINEAR, EASE_IN, EASE_OUT, EASE_IN_OUT, EASE_IN_CUBIC, EASE_OUT_CUBIC, EASE_OUT_BACKovershoot,EASE_OUT_BOUNCE,EASE_OUT_ELASTIC),Animation+AnimationRunner+AnimationTicker(ClientTickEvent-driven runtime interpolation,valueOr(id, fallback)for read). Widgets inui:RoundedButton(primary / secondary factories),TabBar,ProgressBar,SectionHeader,StatRow,Divider,Slider,Toggle,Dropdown,Sidebar,SearchBar(placeholder + caret blink +Backspace+Ctrl+Vpaste, plus the full keyboard model above),Stepper(+/- numeric input),Card(rounded container with title / subtitle / divider). UX patterns inux:StateMachine<S, E>(typed FSM withtransition / selfTransition / onEnter / onExit),Binding<T>(observable value,setfires listeners,bind(other)two-way sync,observeOnce),EventBus(typed events withon(Class, Consumer)returningSubscriptionwith.cancel()),FocusManager(TAB navigation withFocusable.onFocus(boolean)+ Shift+TAB reverse, optionalonKeyper focusable,focusById(id), wrap-around toggle),RippleEffect(Material-style click wave withRippleEffect.trigger(x, y, w, h, style)and globaltick/renderAll),Tooltip(AUTO / ABOVE / BELOW / LEFT / RIGHT / FOLLOW_CURSORpositioning, configurable delay, auto-flip at screen edges, multi-line),Toast(Toast.success/info/warning/error(title, body), queue + max visible, slide-in / slide-out viaAnimationRunner, color-coded perLevel),ScrollContainer(vertical / horizontal withbeginDrag / updateDrag / endDrag / tickKineticfor inertia +scrollWheel, themed scrollbar),Modal(animated open / close + backdrop dim +trapsClick),KeyboardShortcut(typed(modifiers, key, Consumer<Press>)bindings withMOD_CTRL/MOD_ALT/MOD_SHIFT/MOD_SUPER,fromGlfwKey/fromGlfwModsparser,describeKey/describeModifiersfor display strings, conflict-safehandle),Drawer(LEFT / RIGHT / TOP / BOTTOMslide-in side panels with scoped animation IDs so multiple drawers coexist).WarningScreenis rewritten on top of these — overlap of the dismiss button with body text is now structurally impossible: the button lives in abottomReserveslot, sections are measured first, andLayout.stackdistributes Y-offsets inside the safe area with proportional gap shrink if the content overflows. Panel width followsclamp(viewportW / φ, minW, maxW)so the dialog scales gracefully with the window. - No public API change for consumer mods in the strict sense —
0.0.xis backward compatible.Style.hoverBorderColoris additive (defaults toborderColor),Sidebar.draw(gfx, style, x, y, width, items, activeIndex, itemH, gap, onSelect, mouseX, mouseY)is additive (the old overload is preserved and delegates with-1, -1). Consumer mods should still pinslib_version_range=[0.0.1,0.1.0).
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:CfkGe24d:uZ7jvU7w"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:CfkGe24d:uZ7jvU7w"
}

