Imported from Leonn170709/THM-Addons (
AGENTS.md). Install upstream withnpx skills add Leonn170709/THM-Addons. Copyright stays with the author.
AGENTS.md
This file provides guidance to Codex when working with code in this repository.
What this is
THM Addons is a Meteor Client addon (Fabric mod) for Minecraft 1.21.11 — highway automation,
travel, PvP, and QoL modules. It's not a standalone mod: it registers modules/HUDs/commands into
Meteor Client via MeteorAddon, and several modules require Baritone to be present.
Code comments & setting descriptions
Keep both as short as possible. A comment states the one non-obvious fact (a constraint, a
gotcha) and stops — no rationale essay, no restating what the code already says. A Setting
.description(...) is a short user-facing label, not documentation — say what it does, not how,
why, or which other settings it interacts with. If it needs more than one sentence, it's too long.
Build & run
./gradlew build— full build, jar lands inbuild/libs../gradlew runClient— launch a dev client with the addon loaded (via Fabric Loom)../gradlew test— JUnit 5 unit tests insrc/test/java(also run bybuild). They run without a game instance: only test code that doesn't need Minecraft's bootstrap/registries (vanilla value classes likeBlockPosare fine). Pull testable logic out of modules intoutils/classes (e.g.AdaptiveRate,GhostBlockProbe) instead of testing aModule. In-game behavior still needs a manual client run. IntelliJ setup:docs/running-tests-intellij.md.secrets.properties(git-ignored, copy fromsecrets.properties.example) holds real API URLs. Building without it falls back to placeholderexample.comURLs — the build still succeeds, API-backed features just won't resolve to anything real.
API secrets — encrypted vault, generated file, hardened client
src/main/java/xyz/thm/addon/utils/APIUtils.java is a normal, checked-in file — edit it directly;
the build never touches it. The 7 endpoint URLs live in GeneratedApiEndpoints.java (git-ignored,
not under version control), regenerated by the generateApiEndpoints task every build from
secrets.properties (or secrets.properties.example as a fallback): each URL is AES/GCM-encrypted
with a fresh random key baked into that one file, so a decompiled jar doesn't hand over a plaintext
endpoint. APIUtils calls GeneratedApiEndpoints.xxxUrl() to decrypt one on demand.
Every outbound request — API calls and player-configured webhooks alike — goes through
TrustedHttp, not raw HttpURLConnection: it rejects non-http(s) schemes, resolves the hostname
and blocks loopback/RFC1918/link-local/CGNAT/metadata targets (SSRF), only follows same-host
redirects (capped at 3 hops), caps response/request bodies at MAX_JSON_BYTES, and refuses to let
a webhook body carry the API token or the cracked-account password. If an HTTPS (API/IMAGE) request
fails before anything is sent (DNS/TCP/TLS), it retries via 1.1.1.1 DNS-over-HTTPS over a direct
TLS socket (exchangeDirect, same cert/hostname checks) and sticks to that for the session. Logs
never contain the API URL/host (TrustedHttp.describe redacts them). The API token itself
(Authorization: Bearer <token>) is still required server-side on every request, GET included —
encryption here raises the bar against casual reverse-engineering, it isn't the auth boundary.
Credit header
After creating or editing files, run tools/scripts/add-credits.sh <file ...> on every changed file.
It prepends the standard THM Addons license/credit header (see any existing file's top comment for
the exact text) if it's missing. It only handles .java/.c/.kts, .md, and
.sh/.properties/.yml/.toml-style comment syntax; other extensions are left alone. The script is
idempotent, so include all changed files with supported extensions even when they already have a
header.
Architecture
Entry point: THMAddon.java implements MeteorAddon. onInitialize() is where every
module, command, HUD widget, and GUI theme gets registered — that method is the map of everything
this addon ships. Main.java is unrelated: it's the jar's Main-Class, only run if someone
double-clicks the jar directly (outside Fabric), and just shows a "wrong way to run this" dialog.
Startup gating: onInitialize() checks for required mods (Baritone) before registering
anything and hard-exits (System.exit(1)) if missing. The popup goes through utils/StartupDialog,
which forks a second JVM running Main (Swing): the game's JVM is -Djava.awt.headless=true, so
neither Swing nor Fabric Loader's own error window can open a window in it — Loader forks for the
same reason. TinyFileDialogs is only the fallback (it shells out to zenity/kdialog on Linux and
shows nothing when neither exists); last resort is the log plus stderr. Baritone-dependent modules (THMHwyMonitor,
HighwayTools) are registered conditionally behind BaritoneUtils.IS_AVAILABLE rather than being
a hard requirement — the addon still loads without Baritone, just with fewer modules.
Package layout (src/main/java/xyz/thm/addon/):
modules/— one class per MeteorModule(highway, PvP, utility, etc.). Follow the existing pattern:SettingGroups +Setting<T>builders frommeteordevelopment.meteorclient.settings,@EventHandlermethods for Meteor/Fabric events.ModuleManager.javahere is a helper, not a registry — actual registration happens inTHMAddon.onInitialize().mixin/— split by target:meteor/mixins patch Meteor Client's own classes (to extend modules Meteor doesn't expose hooks for),sodium//xaero/patch those respective mods,accessor/holds@Mixinaccessor interfaces for otherwise-private vanilla/Meteor fields, and the rest patch vanilla. Mixin classes must be listed in the matchingthm-addon*.mixins.jsonfile or they silently never load.hud/— HUD widgets registered viaHud.get().register(X.INFO).commands/— chat commands registered viaCommands.add().gui/(+gui/themes,gui/widgets) — custom GUI themes and screens.HighwayBuilderScreenreplaces HighwayBuilder's flat settings list (tabs + search + status/start/stop/profiles). The swap happens inMinecraftClientMixinonsetScreen, not onGuiTheme.moduleScreen: third-party themes (e.g. Catppuccin) override that method with their own module screen.system/—THMSystem(persisted addon-wide config/state) andTHMTab(the "THM Addon" tab in Meteor's GUI, separate from module categories).utils/— shared helpers; notable ones:THMUtils,ThmMembers/CapeManager(backed byAPIUtils),utils/server/(server status/reconnect),utils/kitbot/(KitBot chat integration),utils/webp(WebP image decoding for capes/icons),PacketPlaceTracker(shared one-packet-per-block tracking for packet placing: re-send only after the server reports air; use it from any module with a packet place mode instead of re-sending every tick).waveycapes/— self-contained cape physics simulation (sim/,util/) plus its own mixins.settings/— custom Meteor setting widget types (e.g.StringMultiSelect) beyond the stock ones.
Categories vs. tabs: Modules register under Meteor Categorys (THMAddon.MAIN = "THM
Highway", THMAddon.PVP = "THM PVP") via onRegisterCategories(). THMTab is a separate,
addon-specific settings screen — don't confuse the two when adding a new module's config surface.
Rendering: Every render call goes through the THM helpers — never event.renderer.* or
Renderer2D directly, so the optimizations live in one place:
utils/RenderUtilsTHM— blocks, boxes, lines, tracers, entity boxes, fade/pulse/shrink modes, 2D backgrounds.renderBlocks/renderBlockSetdrop the faces two adjacent blocks share, so use them for any multi-block render instead of a box-per-position loop.renderBlockFadedandrenderBackground2Dreuse a scratchColor— never allocate aColor/SettingColorper block per frame in a render handler.utils/render/GhostRenderer— entities with their real model and skin.submit()draws them in the vanilla entity pass (blocks occlude, glass and portals don't);renderThroughWalls()draws after the world with a depth offset.
Shaders: .fsh files in src/main/resources/assets/thm-addon/shaders/ are main-menu
background shaders, auto-discovered by ShaderManager (no registration needed). Format/porting
rules: docs/main-menu-shader-spec.md; use the /shadertoy-port command to port one from
Shadertoy.
Repository workflows
- For a THM release, follow
.claude/commands/thm-release.md. It writes the next GitHub release body from commit history and uses diffs rather than commit messages, which are frequently useless here. - For a Shadertoy port, follow
.claude/commands/shadertoy-port.md. It ports a Shadertoy shader into a.fshfile for the main-menu shader pool; it requires the user to paste GLSL because Shadertoy blocks scripted fetches, and it only supports single-pass shaders without buffers or feedback textures.
Docs worth reading before touching these areas
docs/highway-settings.md— user-facing behavior of HighwayBuilder/HwyMonitor/Highway Profiles.docs/main-menu-shader-spec.md— shader port format and constraints.docs/highwaybuilder-stats-screenshot-simulation.md,docs/hwymonitor-reconnect-simulation.md,docs/highwaybuilder-reconnect-module-cache-change-log.txt— simulation/behavior notes for those specific features.FEATURES.md— the authoritative module/HUD/command list; update it when adding or renaming a user-facing module, HUD widget, or command.meteor-addon-list.json— what meteoraddons.com shows for this addon (onlydescription,tags,supported_versions,icon,discord,homepageare read; everything else is scraped fromfabric.mod.json). Update it on major changes only: a new MC version branch, a whole feature area added or dropped, a moved icon, a new Discord invite. Constraints: tags come from a fixed whitelist (PvP, Utility, Theme, Render, Movement, Building, World, Misc, QoL, Exploit, Fun, Automation) and invalid ones are silently dropped, the icon must be an httpsraw.githubusercontent.comURL, and the description must stay under 333 characters. The scanner reads it from the default branch, so it only takes effect once pushed.