A mod's settings button now opens ModSettings: a header with the mod's
icon, name, version, tags and description, a Reset button and an on/off
switch, then its properties as rows, with nested namespaces as titled
sections and a live preview card beside them for HUD mods.
PanelScreen holds the sidebar-and-panel frame that the mod menu, mod
settings and config editor share. PropertyList lays out any config as
rows. The inputs share one field look: Toggle is a switch, Select steps
with arrows, and the number, keybinding and color inputs are boxes. The
color picker opens to the left and closes on outside clicks.
Every property type now implements reset(), which was a no-op, and
ModLayout keeps its settings in order instead of Map.of's. ConfigEditor
moves onto the same frame and rows and drops its HUD preview, which added
a new element every frame.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The mod menu is now a translucent panel beside a navigation rail, with a
header showing how many mods are enabled, a search box, category filters
built from the mods' tags and a three-column grid of mod cards. Each card
shows the mod's icon, name and description with a settings button and an
on/off switch. The layout shrinks to fit smaller windows.
New reusable pieces: Panel, Label, ChipGroup, SearchInput,
Toggle.drawSwitch, Theme helpers for panel, border, muted text and body
font, and Fonts.ellipsize. The sidebar and card icons are Lucide SVGs, and
seven mod icons were redrawn in the same style.
Scroll can skip its background and be cleared, and no longer passes clicks
outside its bounds to children scrolled out of view. Input checks overflow
and places the cursor at the scale its text is drawn at. Screens that are
not redesigned yet move the wider sidebar so it keeps its gap.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
drawRoundedBorder draws an outline inset into the given bounds so it lines
up with a rounded rectangle of the same size and radius, and a new
drawRoundedRectangle overload draws both with separate background and border
colors. Both shapes share one SVG-to-texture helper.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Emotes loads assets/saturnclient/player_animations/*.json with
emotes-core into Saturn's own EmoteRegistry (not the library's shared
one, so other mods can't replace them), and lists the ones that loaded
for the wheel.
- The emote wheel now also sends the emote to the server, so other
players see it. It only played locally before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Stop sending the Minecraft access token to the Saturn server. The
connect loop now:
1. fetches GET /versions and compares this client's mod version:
supported -> connect; deprecated -> warn, connect; neither -> warn,
don't connect (warnings are shown once per status change)
2. requests auth_challenge with the username to get a server id
3. calls Mojang's session `join` with the access token and server id
(the token only goes to Mojang); a refused token gives up
4. requests auth_verify, which the server confirms with `hasJoined`
Add SaturnProvider.getModVersion() and showWarning() for the version
check and its toast.
Refs saturnclientmc/saturnclient#7
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Move to session-java 0.2.0 (Gitea package registry) and rework
ServiceClient around a single connect loop:
- Reconnect with exponential backoff (1 s -> 60 s) when the connection
drops, e.g. on every server redeploy; give up only when retrying cannot
help (no Minecraft session, or the server rejects the token)
- Publish the session only after authentication succeeds, and replace
(rather than append to) owned cosmetics and clear cached players on
reconnect
- Time out every request after 10 s and connecting after 10 s, so a lost
response can no longer hang the player-lookup worker forever
- Register onClientStopping once, and keep a local session reference in
request callbacks
- Allow -Dsaturn.serverUri to point the client at a local server
Refs saturnclientmc/saturnclient#4, saturnclientmc/saturnclient#5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
SaturnPlayer stored non-Saturn players as null, which looked the same as
"not fetched yet", so every render re-queued them and flooded the server
with get_player requests. Remember negative lookups and re-check them
after 60 s, retry failed lookups after 5 s, queue each player at most
once, skip lookups while disconnected, and replace the hand-rolled worker
thread with a single-thread executor.
getPlayer now returns null only when the server says the player isn't on
Saturn and throws on failure, so failures are retried instead of cached.
Make state shared between network callbacks and the render thread safe:
ConcurrentHashMap for players, CopyOnWriteArrayList for owned cosmetics,
volatile session/uuid and cosmetic fields, and run emote notifications
on the client thread.
Refs saturnclientmc/saturnclient#2, saturnclientmc/saturnclient#6
Co-Authored-By: Claude Opus 5.5 <[email protected]>