From 12f39943e1d2607dc1fa7059344db9e04f7a52c8 Mon Sep 17 00:00:00 2001 From: Martix Date: Fri, 13 Nov 2026 22:05:14 +0100 Subject: [PATCH 1/5] Prevent module events when player or world is unavailable --- .../meteorclient/mixin/GameRendererMixin.java | 2 ++ .../meteorclient/mixin/MinecraftMixin.java | 4 +++ .../meteorclient/systems/modules/Module.java | 8 ++--- .../meteorclient/systems/modules/Modules.java | 34 +++++++++++++++++++ .../meteorclient/utils/player/InvUtils.java | 2 -- 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/GameRendererMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/GameRendererMixin.java index 5c5cddd3100..9fdad8a1b81 100644 --- a/src/main/java/meteordevelopment/meteorclient/mixin/GameRendererMixin.java +++ b/src/main/java/meteordevelopment/meteorclient/mixin/GameRendererMixin.java @@ -90,6 +90,7 @@ public abstract class GameRendererMixin { @Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;render3dHud(Lnet/minecraft/client/renderer/state/level/CameraRenderState;Lnet/minecraft/client/renderer/state/level/PlayerRenderState;Lnet/minecraft/client/renderer/state/OptionsRenderState;Z)V")) private void onRenderLevel(CallbackInfo ci, @Local(name = "projectionMatrix") Matrix4f projectionMatrix, @Local(name = "cameraState") CameraRenderState cameraState, @Local(name = "worldPartialTicks") float worldPartialTicks, @Local(name = "bobStack") PoseStack bobStack) { + Modules.get().updateModuleSubscriptions(); if (!Utils.canUpdate()) return; Profiler.get().push(MeteorClient.MOD_ID + "_render"); @@ -139,6 +140,7 @@ private void onRenderLevel(CallbackInfo ci, @Local(name = "projectionMatrix") Ma @Inject(method = "renderLevel", at = @At("TAIL")) private void onRenderLevelTail(CallbackInfo ci) { + Modules.get().updateModuleSubscriptions(); MeteorClient.EVENT_BUS.post(RenderAfterWorldEvent.get()); } diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/MinecraftMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/MinecraftMixin.java index 30ca66bafe6..6cf834fbdb4 100644 --- a/src/main/java/meteordevelopment/meteorclient/mixin/MinecraftMixin.java +++ b/src/main/java/meteordevelopment/meteorclient/mixin/MinecraftMixin.java @@ -117,6 +117,8 @@ private void onPreTick(CallbackInfo ci) { startUseItemCalled = false; + Modules.get().updateModuleSubscriptions(); + Profiler.get().push(MeteorClient.MOD_ID + "_pre_update"); MeteorClient.EVENT_BUS.post(TickEvent.Pre.get()); Profiler.get().pop(); @@ -127,6 +129,8 @@ private void onPreTick(CallbackInfo ci) { @Inject(at = @At("TAIL"), method = "tick") private void onTick(CallbackInfo ci) { + Modules.get().updateModuleSubscriptions(); + Profiler.get().push(MeteorClient.MOD_ID + "_post_update"); MeteorClient.EVENT_BUS.post(TickEvent.Post.get()); Profiler.get().pop(); diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/Module.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/Module.java index d0423ebc1eb..cbffd2874cb 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/Module.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/Module.java @@ -99,14 +99,14 @@ public void toggle() { onActivate(); } } else { - if (runInMainMenu || Utils.canUpdate()) { - if (autoSubscribe) MeteorClient.EVENT_BUS.unsubscribe(this); - onDeactivate(); - } + if (autoSubscribe) MeteorClient.EVENT_BUS.unsubscribe(this); + if (runInMainMenu || Utils.canUpdate()) onDeactivate(); active = false; Modules.get().removeActive(this); } + + Modules.get().markModuleSubscriptionsDirty(); } public void enable() { diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java index 99cdcb14e2b..b7bfb3765ae 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java @@ -64,6 +64,7 @@ public class Modules extends System { private final Set active = new ReferenceOpenHashSet<>(); private @Nullable Module moduleToBind; private boolean awaitingKeyRelease = false; + private boolean inGame, modulesSubscribed, moduleSubscriptionsDirty; public Modules() { super("modules"); @@ -323,6 +324,10 @@ private void onGameJoined(GameJoinedEvent event) { module.onActivate(); } } + + inGame = true; + modulesSubscribed = true; + moduleSubscriptionsDirty = false; } } @@ -335,6 +340,35 @@ private void onGameLeft(GameLeftEvent event) { module.onDeactivate(); } } + + inGame = false; + modulesSubscribed = false; + moduleSubscriptionsDirty = false; + } + } + + void markModuleSubscriptionsDirty() { + if (inGame) moduleSubscriptionsDirty = true; + } + + /** + * Keeps ordinary module listeners out of the event bus while the world or player is unavailable. + * This also covers disconnect paths that do not post a GameLeftEvent. + */ + public void updateModuleSubscriptions() { + synchronized (active) { + boolean canUpdate = Utils.canUpdate(); + if (!inGame || (canUpdate == modulesSubscribed && !moduleSubscriptionsDirty)) return; + + for (Module module : getAll()) { + if (module.runInMainMenu || !module.autoSubscribe) continue; + + if (canUpdate && module.isActive()) MeteorClient.EVENT_BUS.subscribe(module); + else MeteorClient.EVENT_BUS.unsubscribe(module); + } + + modulesSubscribed = canUpdate; + moduleSubscriptionsDirty = false; } } diff --git a/src/main/java/meteordevelopment/meteorclient/utils/player/InvUtils.java b/src/main/java/meteordevelopment/meteorclient/utils/player/InvUtils.java index 48f97756ed3..6fb21cd9991 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/player/InvUtils.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/player/InvUtils.java @@ -104,12 +104,10 @@ public static FindItemResult find(Item... items) { } public static FindItemResult find(Predicate isGood) { - if (mc.player == null) return new FindItemResult(0, 0); return find(isGood, 0, mc.player.getInventory().getContainerSize()); } public static FindItemResult find(Predicate isGood, int start, int end) { - if (mc.player == null) return new FindItemResult(0, 0); int slot = -1, count = 0; From 6c939d7bae794f7ede61b08dfccec45985786ff9 Mon Sep 17 00:00:00 2001 From: Martix Date: Fri, 13 Nov 2026 22:20:18 +0100 Subject: [PATCH 2/5] Separate module activation from event subscriptions --- .../meteorclient/systems/modules/Module.java | 62 ++++++++++++++++--- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/Module.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/Module.java index cbffd2874cb..c2cceff7dbc 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/Module.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/Module.java @@ -40,6 +40,9 @@ public abstract class Module implements ISerializable, Comparable Date: Fri, 13 Nov 2026 22:20:20 +0100 Subject: [PATCH 3/5] Synchronize module event subscription intent --- .../meteorclient/systems/modules/Modules.java | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java index b7bfb3765ae..4c3ff48f33a 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java @@ -64,7 +64,7 @@ public class Modules extends System { private final Set active = new ReferenceOpenHashSet<>(); private @Nullable Module moduleToBind; private boolean awaitingKeyRelease = false; - private boolean inGame, modulesSubscribed, moduleSubscriptionsDirty; + private boolean inGame, moduleSubscriptionsDirty = true, lastCanUpdate; public Modules() { super("modules"); @@ -318,56 +318,56 @@ private void onOpenScreen(OpenScreenEvent event) { @EventHandler private void onGameJoined(GameJoinedEvent event) { synchronized (active) { + inGame = true; + moduleSubscriptionsDirty = true; + for (Module module : getAll()) { - if (module.isActive() && !module.runInMainMenu) { - MeteorClient.EVENT_BUS.subscribe(module); - module.onActivate(); - } + if (module.isActive() && !module.runInMainMenu) module.activate(); } - inGame = true; - modulesSubscribed = true; - moduleSubscriptionsDirty = false; + updateModuleSubscriptions(); } } @EventHandler private void onGameLeft(GameLeftEvent event) { synchronized (active) { + inGame = false; + moduleSubscriptionsDirty = true; + for (Module module : getAll()) { - if (module.isActive() && !module.runInMainMenu) { - MeteorClient.EVENT_BUS.unsubscribe(module); - module.onDeactivate(); - } + if (!module.runInMainMenu) module.deactivate(); } - inGame = false; - modulesSubscribed = false; - moduleSubscriptionsDirty = false; + updateModuleSubscriptions(); } } + boolean isInGame() { + return inGame; + } + void markModuleSubscriptionsDirty() { - if (inGame) moduleSubscriptionsDirty = true; + moduleSubscriptionsDirty = true; } /** - * Keeps ordinary module listeners out of the event bus while the world or player is unavailable. - * This also covers disconnect paths that do not post a GameLeftEvent. + * Suspends event delivery while the player or world is unavailable without changing module + * activation state. Lifecycle callbacks are driven by explicit game join/leave events. */ public void updateModuleSubscriptions() { synchronized (active) { - boolean canUpdate = Utils.canUpdate(); - if (!inGame || (canUpdate == modulesSubscribed && !moduleSubscriptionsDirty)) return; + boolean canUpdate = inGame && Utils.canUpdate(); + if (!moduleSubscriptionsDirty && canUpdate == lastCanUpdate) return; for (Module module : getAll()) { - if (module.runInMainMenu || !module.autoSubscribe) continue; + if (module.runInMainMenu) continue; - if (canUpdate && module.isActive()) MeteorClient.EVENT_BUS.subscribe(module); - else MeteorClient.EVENT_BUS.unsubscribe(module); + if (canUpdate && module.isActive() && !module.isActivated()) module.activate(); + module.updateSubscription(); } - modulesSubscribed = canUpdate; + lastCanUpdate = canUpdate; moduleSubscriptionsDirty = false; } } From 8b55ba563cac964f46ababeb5046c7ebd7706526 Mon Sep 17 00:00:00 2001 From: Martix Date: Fri, 13 Nov 2026 22:20:22 +0100 Subject: [PATCH 4/5] Track Zoom event subscription intent --- .../meteorclient/systems/modules/render/Zoom.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/render/Zoom.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/render/Zoom.java index f8e9d6fdd8c..f12f9677581 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/render/Zoom.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/render/Zoom.java @@ -5,7 +5,6 @@ package meteordevelopment.meteorclient.systems.modules.render; -import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.events.meteor.MouseScrollEvent; import meteordevelopment.meteorclient.events.render.GetFovEvent; import meteordevelopment.meteorclient.events.render.Render3DEvent; @@ -87,7 +86,7 @@ public void onActivate() { value = zoom.get(); time = 0.001; - MeteorClient.EVENT_BUS.subscribe(this); + subscribeToEvents(); enabled = true; } @@ -105,7 +104,7 @@ private void onTick(TickEvent.Post event) { } if (time == 0) { - MeteorClient.EVENT_BUS.unsubscribe(this); + unsubscribeFromEvents(); enabled = false; onStop(); From 734c4969fa88d6d8b1675f99ac4544eb1644f4af Mon Sep 17 00:00:00 2001 From: Martix Date: Fri, 13 Nov 2026 22:25:28 +0100 Subject: [PATCH 5/5] Remove redundant module subscription reset --- .../meteordevelopment/meteorclient/systems/modules/Module.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/Module.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/Module.java index c2cceff7dbc..a15a146e4bd 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/Module.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/Module.java @@ -103,7 +103,6 @@ public void toggle() { Modules.get().removeActive(this); if (activated) deactivate(); - if (autoSubscribe) wantsSubscription = false; } Modules.get().markModuleSubscriptionsDirty();