Engine modes
Automatic, event-driven and polled modes, how the mode is resolved, and how the compatibility fallback works.
The engine has two input sources and three modes. The mode decides which source is allowed to dispatch an action; the other source still updates state.
The three modes
public enum Mode { AUTO("auto"), EVENT("event"), POLL("poll"); }
| Mode | Behaviour |
|---|---|
AUTO | Event hooks when they applied and no known input mod is present, otherwise polling |
EVENT | Event hooks only. Falls back to polling when they are unavailable |
POLL | High-rate sampling only. The hooks still update state but never dispatch |
The configured value maps to the mode like this:
engine_mode | Mode |
|---|---|
| 0 | AUTO |
| 1 | EVENT |
| 2 | POLL |
Resolving the mode
CompatibilityManager.resolveMode() decides:
public InputEngine.Mode resolveMode() { InputEngine.Mode configured = InputEngine.Mode.fromConfig(InputBoosterConfig.getEngineMode()); if (configured == InputEngine.Mode.EVENT && !eventSourceActive) { fallbackReason = "event hooks unavailable"; return InputEngine.Mode.POLL; } if (configured == InputEngine.Mode.AUTO) { if (!detected.isEmpty() && InputBoosterConfig.isCompatFallback()) { fallbackReason = "compatibility fallback: " + String.join(", ", detected); return InputEngine.Mode.POLL; } return eventSourceActive ? InputEngine.Mode.EVENT : InputEngine.Mode.POLL; } return configured; }
The mode can change at runtime
The mode is re-resolved in two situations:
| Trigger | Where |
|---|---|
| The first event arrives | InputEngine.onKeyState calls resolveMode() again |
engine_mode changed in the config | The client tick compares the configured value with the last one |
public void onKeyState(int code, boolean down) { CompatibilityManager compat = InputBoosterMod.compatibilityManager; if (compat != null) { boolean firstEvent = !compat.eventSourceActive(); compat.markEventSourceActive(); if (firstEvent) { eventDriven = true; setMode(compat.resolveMode()); } } state.setRaw(code, down); if (mode == Mode.POLL) return; ... }
The compatibility fallback
Some mods own input routing. When one of them is present and
compat_fallback is on, the engine keeps polling and leaves event handling
alone instead of competing for it.
Detection is a class-path lookup, not a mod-list dependency, so it works the same on Fabric and NeoForge without touching loader APIs:
private static final String[][] KNOWN_INPUT_MODS = { {"controlify", "dev.isxander.controlify.Controlify"}, {"midnightcontrols", "dev.lambdaurora.midnightcontrols.MidnightControls"}, {"controllable", "com.mrcrayfish.controllable.Controllable"}, {"sodium", "net.caffeinemc.mods.sodium.client.SodiumClientMod"}, {"iris", "net.irisshaders.iris.Iris"}, {"zoomify", "com.jab125.zoomify.Zoomify"}, };
The scan runs once during initialisation. A class that cannot be loaded simply means the mod is not installed.
Reading the resolved mode
The Compatibility tab of the settings screen shows the mode and the reason. The overlay shows the mode in its engine line, and the status line is:
public String statusLine() { return "Input: " + mode.label + (eventDriven ? " (event)" : " (poll)"); }
CompatibilityManager.statusLine() adds the detection result:
Engine: poll | sodium
Turning the fallback off
With compat_fallback=false, AUTO no longer downgrades to polling when an
input mod is detected. It picks EVENT when the hooks are live.
Forcing a mode
| Situation | Setting |
|---|---|
| The hooks are not applying and you want the poller | engine_mode=2 |
| You want to prove the hooks work | engine_mode=1 after the first event has fired |
| You are not sure | Leave it on 0 |
The mode is not part of a profile, so switching profiles does not change it.
Related pages
- Architecture — where the manager sits.
- Compatibility tab — the settings side.
- The overlay — seeing the mode live.