|
| 1 | +# Early Input Guard |
| 2 | + |
| 3 | +A small client-side NeoForge mod that stops a class of startup crash in large modpacks: a mouse |
| 4 | +click or key press (or an automatic per-tick event) being dispatched to another mod's input/tick |
| 5 | +listener **before that mod has registered its keybinds** — so the listener dereferences a `null` |
| 6 | +`KeyMapping` and the game hard-crashes. |
| 7 | + |
| 8 | +- **Minecraft:** 1.21.1 |
| 9 | +- **Loader:** NeoForge (21.1.x) |
| 10 | +- **Side:** client only |
| 11 | + |
| 12 | +## The problem |
| 13 | + |
| 14 | +In heavily-modded packs the client renders frames and polls input while the loading screen is still |
| 15 | +up, and FML may finish loading in a degraded state. Two things can leave a mod's keybind fields |
| 16 | +`null` when its listeners run: |
| 17 | + |
| 18 | +1. **During the loading screen** — input is polled before every mod's `RegisterKeyMappingsEvent` |
| 19 | + handler has run. |
| 20 | +2. **In a broken mod state** — if any mod fails to construct, FML "cowardly refuses" to fire the |
| 21 | + client registration events (including `RegisterKeyMappingsEvent`) for the rest of the session, so |
| 22 | + keybinds are *never* assigned, yet NeoForge keeps dispatching input and per-tick events. |
| 23 | + |
| 24 | +A single click in either situation reaches a listener like Quark's `HotbarChangerModule` or Arknights |
| 25 | +Endfield's `KeyInputHandler` and crashes with: |
| 26 | + |
| 27 | +``` |
| 28 | +java.lang.NullPointerException: Cannot invoke "net.minecraft.client.KeyMapping.isDown()" |
| 29 | +because "...changeHotbarKey" is null |
| 30 | +``` |
| 31 | + |
| 32 | +## What it does |
| 33 | + |
| 34 | +It mixes into NeoForge's `ClientHooks` and short-circuits the relevant dispatch hooks while it is |
| 35 | +unsafe to deliver events to mod listeners: |
| 36 | + |
| 37 | +| Hook | Guarded | |
| 38 | +| --- | --- | |
| 39 | +| `onMouseButtonPre` / `onMouseButtonPost` | mouse clicks | |
| 40 | +| `onMouseScroll` | scroll | |
| 41 | +| `onKeyInput` | key presses | |
| 42 | +| `fireClientTickPre` / `fireClientTickPost` | per-tick mod events | |
| 43 | + |
| 44 | +**Suppression conditions:** |
| 45 | + |
| 46 | +- **Input** is dropped when mod loading has errored (`ModLoader.hasErrors()`) **or** a `LoadingOverlay` |
| 47 | + is active. |
| 48 | +- **Ticks** are dropped when mod loading has errored. Suppressing ticks during a *healthy* loading |
| 49 | + screen is opt-in via config (see below), since some mods legitimately tick during loading. |
| 50 | + |
| 51 | +This does **not** hide problems: NeoForge's loading-error screen still renders and its buttons still |
| 52 | +work (they dispatch through `Screen.mouseClicked`, a different path), so you can read the errors and |
| 53 | +quit cleanly instead of being thrown into a crash report. It also has no effect on a healthy, |
| 54 | +fully-loaded game — by then nothing is suppressed. |
| 55 | + |
| 56 | +## Config |
| 57 | + |
| 58 | +`config/earlyinputguard-client.toml`: |
| 59 | + |
| 60 | +| Option | Default | Effect | |
| 61 | +| --- | --- | --- | |
| 62 | +| `suppress_ticks_during_loading_screen` | `false` | Also suppress client tick dispatch while a loading screen is active, not just in a broken mod state. | |
| 63 | + |
| 64 | +## Building |
| 65 | + |
| 66 | +Requires JDK 21. |
| 67 | + |
| 68 | +```bash |
| 69 | +./gradlew build |
| 70 | +``` |
| 71 | + |
| 72 | +The mod jar is written to `build/libs/`. |
| 73 | + |
| 74 | +## License |
| 75 | + |
| 76 | +MIT |
0 commit comments