From af701e496dfee0dfdf41257b45215e026c0d06ce Mon Sep 17 00:00:00 2001 From: Big-Iron-Cheems <52252627+Big-Iron-Cheems@users.noreply.github.com> Date: Thu, 3 Jul 2025 15:01:15 +0200 Subject: [PATCH 1/3] Add reflection based init for Commands and Modules This implementation provides ways for addons to register their own packages, and add commands/modules conditionally. --- .../meteorclient/commands/Commands.java | 95 ++++- .../meteorclient/systems/modules/Modules.java | 326 ++++++------------ 2 files changed, 193 insertions(+), 228 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/commands/Commands.java b/src/main/java/meteordevelopment/meteorclient/commands/Commands.java index de498a625c..808919d445 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/Commands.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/Commands.java @@ -7,14 +7,21 @@ import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import meteordevelopment.meteorclient.commands.commands.*; +import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.pathing.PathManagers; import meteordevelopment.meteorclient.utils.PostInit; import net.minecraft.command.CommandSource; +import org.reflections.Reflections; +import org.reflections.scanners.Scanners; +import org.reflections.util.ConfigurationBuilder; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.Map.Entry; +import java.util.function.Supplier; +import java.util.stream.Collectors; import static meteordevelopment.meteorclient.MeteorClient.mc; @@ -66,6 +73,86 @@ public static void init() { COMMANDS.sort(Comparator.comparing(Command::getName)); } + /** + * Registers a package to scan for commands. + * This should be called by addons during their initialization. + */ + public static void registerCommandPackage(String packageName) { + SCAN_PACKAGES.add(packageName); + } + + /** + * Registers a command to be loaded conditionally. + * + * @param commandClass The class of the command to register + * @param condition A supplier that returns true if the command should be loaded + */ + public static void registerConditionalCommand(Class commandClass, Supplier condition) { + CONDITIONAL_COMMANDS.put(commandClass.getName(), condition); + } + + public static void loadCommands() { + try { + long startTime = java.lang.System.currentTimeMillis(); + int totalCount = 0; + int skippedCount = 0; + + ConfigurationBuilder config = new ConfigurationBuilder() + .forPackages(SCAN_PACKAGES.toArray(new String[0])) + .setScanners(Scanners.SubTypes) + .setParallel(true) + .setExpandSuperTypes(false); + + Reflections reflections = new Reflections(config); + Set> commandClasses = reflections.getSubTypesOf(Command.class); + + commandClasses = commandClasses.stream() + .filter(commandClass -> SCAN_PACKAGES.stream().anyMatch(pkg -> commandClass.getName().startsWith(pkg))) + .collect(Collectors.toSet()); + + Map>> commandsByPackage = new HashMap<>(); + for (Class commandClass : commandClasses) { + String packageName = commandClass.getPackage().getName(); + commandsByPackage.computeIfAbsent(packageName, k -> new ArrayList<>()).add(commandClass); + } + + for (Class commandClass : commandClasses) { + String className = commandClass.getName(); + if (CONDITIONAL_COMMANDS.containsKey(className) && !CONDITIONAL_COMMANDS.get(className).get()) { + MeteorClient.LOG.info("Skipping conditional command: {}", className); + skippedCount++; + continue; + } + + try { + if (Modifier.isAbstract(commandClass.getModifiers()) || commandClass.isInterface()) continue; + + Constructor constructor = commandClass.getDeclaredConstructor(); + constructor.setAccessible(true); + Command command = constructor.newInstance(); + add(command); + totalCount++; + } catch (NoSuchMethodException ignored) { + MeteorClient.LOG.error("Command {} does not have a no-args constructor", commandClass.getName()); + } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { + MeteorClient.LOG.error("Failed to load command: {}", commandClass.getName(), e); + } + } + + long endTime = java.lang.System.currentTimeMillis(); + + for (Entry>> entry : commandsByPackage.entrySet()) { + String packageName = entry.getKey(); + int count = entry.getValue().size(); + MeteorClient.LOG.info("Found {} commands in {}", count, packageName); + } + + MeteorClient.LOG.info("Loaded {} commands ({} skipped) in {}ms", totalCount, skippedCount, endTime - startTime); + } catch (Exception e) { + MeteorClient.LOG.error("Failed to load commands", e); + } + } + public static void add(Command command) { COMMANDS.removeIf(existing -> existing.getName().equals(command.getName())); command.registerTo(DISPATCHER); diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java index f9394db09b..54e1ae335c 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java @@ -20,18 +20,8 @@ import meteordevelopment.meteorclient.systems.System; import meteordevelopment.meteorclient.systems.Systems; import meteordevelopment.meteorclient.systems.config.Config; -import meteordevelopment.meteorclient.systems.modules.combat.*; -import meteordevelopment.meteorclient.systems.modules.misc.*; -import meteordevelopment.meteorclient.systems.modules.misc.swarm.Swarm; -import meteordevelopment.meteorclient.systems.modules.movement.*; -import meteordevelopment.meteorclient.systems.modules.movement.elytrafly.ElytraFly; -import meteordevelopment.meteorclient.systems.modules.movement.speed.Speed; -import meteordevelopment.meteorclient.systems.modules.player.*; -import meteordevelopment.meteorclient.systems.modules.render.*; -import meteordevelopment.meteorclient.systems.modules.render.blockesp.BlockESP; -import meteordevelopment.meteorclient.systems.modules.render.marker.Marker; -import meteordevelopment.meteorclient.systems.modules.world.Timer; -import meteordevelopment.meteorclient.systems.modules.world.*; +import meteordevelopment.meteorclient.systems.modules.world.Excavator; +import meteordevelopment.meteorclient.systems.modules.world.InfinityMiner; import meteordevelopment.meteorclient.utils.Utils; import meteordevelopment.meteorclient.utils.misc.Keybind; import meteordevelopment.meteorclient.utils.misc.ValueComparableMap; @@ -43,17 +33,33 @@ import net.minecraft.nbt.NbtElement; import net.minecraft.nbt.NbtList; import org.lwjgl.glfw.GLFW; +import org.reflections.Reflections; +import org.reflections.scanners.Scanners; +import org.reflections.util.ConfigurationBuilder; import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; import java.util.*; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; +import java.util.stream.Collectors; import static meteordevelopment.meteorclient.MeteorClient.mc; public class Modules extends System { private static final List CATEGORIES = new ArrayList<>(); + private static final Set SCAN_PACKAGES = new HashSet<>(); + private static final Map> CONDITIONAL_MODULES = new HashMap<>(); + + static { + registerModulePackage("meteordevelopment.meteorclient.systems.modules"); + + registerConditionalModule(Excavator.class, () -> BaritoneUtils.IS_AVAILABLE); + registerConditionalModule(InfinityMiner.class, () -> BaritoneUtils.IS_AVAILABLE); + } - private final List modules = new ArrayList<>(); private final Map, Module> moduleInstances = new Reference2ReferenceOpenHashMap<>(); private final Map> groups = new Reference2ReferenceOpenHashMap<>(); @@ -71,12 +77,7 @@ public static Modules get() { @Override public void init() { - initCombat(); - initPlayer(); - initMovement(); - initRender(); - initWorld(); - initMisc(); + loadModules(); } @Override @@ -90,11 +91,90 @@ public void load(File folder) { super.load(folder); } + /** + * Registers a package to scan for modules. + * This should be called by addons during their initialization. + */ + public static void registerModulePackage(String packageName) { + SCAN_PACKAGES.add(packageName); + } + + /** + * Registers a module to be loaded conditionally. + * + * @param moduleClass The class of the module to register + * @param condition A supplier that returns true if the module should be loaded + */ + public static void registerConditionalModule(Class moduleClass, Supplier condition) { + CONDITIONAL_MODULES.put(moduleClass.getName(), condition); + } + + public void loadModules() { + try { + long startTime = java.lang.System.currentTimeMillis(); + + ConfigurationBuilder config = new ConfigurationBuilder() + .forPackages(SCAN_PACKAGES.toArray(new String[0])) + .setScanners(Scanners.SubTypes) + .setParallel(true) + .setExpandSuperTypes(false); + + Reflections reflections = new Reflections(config); + Set> moduleClasses = reflections.getSubTypesOf(Module.class); + + moduleClasses = moduleClasses.stream() + .filter(moduleClass -> SCAN_PACKAGES.stream().anyMatch(pkg -> moduleClass.getName().startsWith(pkg))) + .collect(Collectors.toSet()); + + Map>> modulesByPackage = new HashMap<>(); + for (Class moduleClass : moduleClasses) { + String packageName = moduleClass.getPackage().getName(); + modulesByPackage.computeIfAbsent(packageName, k -> new ArrayList<>()).add(moduleClass); + } + + int totalCount = 0; + int skippedCount = 0; + for (Class moduleClass : moduleClasses) { + String className = moduleClass.getName(); + if (CONDITIONAL_MODULES.containsKey(className) && !CONDITIONAL_MODULES.get(className).get()) { + MeteorClient.LOG.info("Skipping conditional module: {}", className); + skippedCount++; + continue; + } + + try { + if (Modifier.isAbstract(moduleClass.getModifiers()) || moduleClass.isInterface()) continue; + + Constructor constructor = moduleClass.getDeclaredConstructor(); + constructor.setAccessible(true); + Module module = constructor.newInstance(); + add(module); + totalCount++; + } catch (NoSuchMethodException ignored) { + MeteorClient.LOG.error("Module {} does not have a no-args constructor", moduleClass.getName()); + } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { + MeteorClient.LOG.error("Failed to load module: {}", moduleClass.getName(), e); + } + } + + long endTime = java.lang.System.currentTimeMillis(); + + for (Map.Entry>> entry : modulesByPackage.entrySet()) { + String packageName = entry.getKey(); + int count = entry.getValue().size(); + MeteorClient.LOG.info("Found {} modules in {}", count, packageName); + } + + MeteorClient.LOG.info("Loaded {} modules ({} skipped) in {}ms", totalCount, skippedCount, endTime - startTime); + } catch (Exception e) { + MeteorClient.LOG.error("Failed to load modules", e); + } + } + public void sortModules() { for (List modules : groups.values()) { modules.sort(Comparator.comparing(o -> o.title)); } - modules.sort(Comparator.comparing(o -> o.title)); } public static void registerCategory(Category category) { @@ -133,14 +213,6 @@ public Collection getAll() { return moduleInstances.values(); } - /** - * @deprecated Use {@link Modules#getAll()} instead. - */ - @Deprecated(forRemoval = true) - public List getList() { - return modules; - } - public int getCount() { return moduleInstances.size(); } @@ -243,12 +315,10 @@ private boolean onBinding(boolean isKey, int value, int modifiers) { if (moduleToBind.keybind.canBindTo(isKey, value, modifiers)) { moduleToBind.keybind.set(isKey, value, modifiers); moduleToBind.info("Bound to (highlight)%s(default).", moduleToBind.keybind); - } - else if (value == GLFW.GLFW_KEY_ESCAPE) { + } else if (value == GLFW.GLFW_KEY_ESCAPE) { moduleToBind.keybind.set(Keybind.none()); moduleToBind.info("Removed bind."); - } - else return false; + } else return false; MeteorClient.EVENT_BUS.post(ModuleBindChangedEvent.get(moduleToBind)); moduleToBind = null; @@ -378,201 +448,9 @@ public void add(Module module) { // Add the module moduleInstances.put(module.getClass(), module); - modules.add(module); getGroup(module.category).add(module); // Register color settings for the module module.settings.registerColorSettings(module); } - - private void initCombat() { - add(new AnchorAura()); - add(new AntiAnvil()); - add(new AntiBed()); - add(new ArrowDodge()); - add(new AutoAnvil()); - add(new AutoArmor()); - add(new AutoCity()); - add(new AutoEXP()); - add(new AutoTotem()); - add(new AutoTrap()); - add(new AutoWeapon()); - add(new AutoWeb()); - add(new BedAura()); - add(new BowAimbot()); - add(new BowSpam()); - add(new Burrow()); - add(new Criticals()); - add(new CrystalAura()); - add(new Hitboxes()); - add(new HoleFiller()); - add(new KillAura()); - add(new Offhand()); - add(new Quiver()); - add(new SelfAnvil()); - add(new SelfTrap()); - add(new SelfWeb()); - add(new Surround()); - } - - private void initPlayer() { - add(new AntiHunger()); - add(new AutoEat()); - add(new AutoClicker()); - add(new AutoFish()); - add(new AutoGap()); - add(new AutoMend()); - add(new AutoReplenish()); - add(new AutoTool()); - add(new BreakDelay()); - add(new ChestSwap()); - add(new EXPThrower()); - add(new FakePlayer()); - add(new FastUse()); - add(new GhostHand()); - add(new InstantRebreak()); - add(new LiquidInteract()); - add(new MiddleClickExtra()); - add(new Multitask()); - add(new NoInteract()); - add(new NoMiningTrace()); - add(new NoRotate()); - add(new NoStatusEffects()); - add(new OffhandCrash()); - add(new Portals()); - add(new PotionSaver()); - add(new Reach()); - add(new Rotation()); - add(new SpeedMine()); - } - - private void initMovement() { - add(new AirJump()); - add(new Anchor()); - add(new AntiAFK()); - add(new AntiVoid()); - add(new AutoJump()); - add(new AutoWalk()); - add(new AutoWasp()); - add(new Blink()); - add(new BoatFly()); - add(new ClickTP()); - add(new ElytraBoost()); - add(new ElytraFly()); - add(new EntityControl()); - add(new EntitySpeed()); - add(new FastClimb()); - add(new Flight()); - add(new GUIMove()); - add(new HighJump()); - add(new Jesus()); - add(new LongJump()); - add(new NoFall()); - add(new NoSlow()); - add(new Parkour()); - add(new ReverseStep()); - add(new SafeWalk()); - add(new Scaffold()); - add(new Slippy()); - add(new Sneak()); - add(new Speed()); - add(new Spider()); - add(new Sprint()); - add(new Step()); - add(new TridentBoost()); - add(new Velocity()); - } - - private void initRender() { - add(new BetterTab()); - add(new BetterTooltips()); - add(new BlockESP()); - add(new BlockSelection()); - add(new Blur()); - add(new BossStack()); - add(new Breadcrumbs()); - add(new BreakIndicators()); - add(new CameraTweaks()); - add(new Chams()); - add(new CityESP()); - add(new EntityOwner()); - add(new ESP()); - add(new Freecam()); - add(new FreeLook()); - add(new Fullbright()); - add(new HandView()); - add(new HoleESP()); - add(new ItemPhysics()); - add(new ItemHighlight()); - add(new LightOverlay()); - add(new LogoutSpots()); - add(new Marker()); - add(new Nametags()); - add(new NoRender()); - add(new PopChams()); - add(new StorageESP()); - add(new TimeChanger()); - add(new Tracers()); - add(new Trail()); - add(new Trajectories()); - add(new TunnelESP()); - add(new VoidESP()); - add(new WallHack()); - add(new WaypointsModule()); - add(new Xray()); - add(new Zoom()); - } - - private void initWorld() { - add(new AirPlace()); - add(new Ambience()); - add(new AutoBreed()); - add(new AutoBrewer()); - add(new AutoMount()); - add(new AutoNametag()); - add(new AutoShearer()); - add(new AutoSign()); - add(new AutoSmelter()); - add(new BuildHeight()); - add(new Collisions()); - add(new EChestFarmer()); - add(new EndermanLook()); - add(new Flamethrower()); - add(new HighwayBuilder()); - add(new LiquidFiller()); - add(new MountBypass()); - add(new NoGhostBlocks()); - add(new Nuker()); - add(new PacketMine()); - add(new StashFinder()); - add(new SpawnProofer()); - add(new Timer()); - add(new VeinMiner()); - - if (BaritoneUtils.IS_AVAILABLE) { - add(new Excavator()); - add(new InfinityMiner()); - } - } - - private void initMisc() { - add(new AntiPacketKick()); - add(new AutoLog()); - add(new AutoReconnect()); - add(new AutoRespawn()); - add(new BetterBeacons()); - add(new BetterChat()); - add(new BookBot()); - add(new DiscordPresence()); - add(new InventoryTweaks()); - add(new MessageAura()); - add(new NameProtect()); - add(new Notebot()); - add(new Notifier()); - add(new PacketCanceller()); - add(new ServerSpoof()); - add(new SoundBlocker()); - add(new Spam()); - add(new Swarm()); - } } From cdb8b060601505d877c7394847c058e1fa4af57f Mon Sep 17 00:00:00 2001 From: Big-Iron-Cheems <52252627+Big-Iron-Cheems@users.noreply.github.com> Date: Thu, 3 Jul 2025 15:10:05 +0200 Subject: [PATCH 2/3] Removed debug logging Only keeping the loaded count message as it is useful to ensure correct reflection loading. --- .../meteorclient/commands/Commands.java | 18 +----------------- .../meteorclient/systems/modules/Modules.java | 18 +----------------- 2 files changed, 2 insertions(+), 34 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/commands/Commands.java b/src/main/java/meteordevelopment/meteorclient/commands/Commands.java index 808919d445..d82069ea0e 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/Commands.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/Commands.java @@ -19,7 +19,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.*; -import java.util.Map.Entry; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -93,7 +92,6 @@ public static void registerConditionalCommand(Class commandCl public static void loadCommands() { try { - long startTime = java.lang.System.currentTimeMillis(); int totalCount = 0; int skippedCount = 0; @@ -110,12 +108,6 @@ public static void loadCommands() { .filter(commandClass -> SCAN_PACKAGES.stream().anyMatch(pkg -> commandClass.getName().startsWith(pkg))) .collect(Collectors.toSet()); - Map>> commandsByPackage = new HashMap<>(); - for (Class commandClass : commandClasses) { - String packageName = commandClass.getPackage().getName(); - commandsByPackage.computeIfAbsent(packageName, k -> new ArrayList<>()).add(commandClass); - } - for (Class commandClass : commandClasses) { String className = commandClass.getName(); if (CONDITIONAL_COMMANDS.containsKey(className) && !CONDITIONAL_COMMANDS.get(className).get()) { @@ -139,15 +131,7 @@ public static void loadCommands() { } } - long endTime = java.lang.System.currentTimeMillis(); - - for (Entry>> entry : commandsByPackage.entrySet()) { - String packageName = entry.getKey(); - int count = entry.getValue().size(); - MeteorClient.LOG.info("Found {} commands in {}", count, packageName); - } - - MeteorClient.LOG.info("Loaded {} commands ({} skipped) in {}ms", totalCount, skippedCount, endTime - startTime); + MeteorClient.LOG.info("Loaded {} commands ({} skipped)", totalCount, skippedCount); } catch (Exception e) { MeteorClient.LOG.error("Failed to load commands", e); } diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java index 54e1ae335c..6ca035d025 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java @@ -111,8 +111,6 @@ public static void registerConditionalModule(Class moduleClass public void loadModules() { try { - long startTime = java.lang.System.currentTimeMillis(); - ConfigurationBuilder config = new ConfigurationBuilder() .forPackages(SCAN_PACKAGES.toArray(new String[0])) .setScanners(Scanners.SubTypes) @@ -126,12 +124,6 @@ public void loadModules() { .filter(moduleClass -> SCAN_PACKAGES.stream().anyMatch(pkg -> moduleClass.getName().startsWith(pkg))) .collect(Collectors.toSet()); - Map>> modulesByPackage = new HashMap<>(); - for (Class moduleClass : moduleClasses) { - String packageName = moduleClass.getPackage().getName(); - modulesByPackage.computeIfAbsent(packageName, k -> new ArrayList<>()).add(moduleClass); - } - int totalCount = 0; int skippedCount = 0; for (Class moduleClass : moduleClasses) { @@ -157,15 +149,7 @@ public void loadModules() { } } - long endTime = java.lang.System.currentTimeMillis(); - - for (Map.Entry>> entry : modulesByPackage.entrySet()) { - String packageName = entry.getKey(); - int count = entry.getValue().size(); - MeteorClient.LOG.info("Found {} modules in {}", count, packageName); - } - - MeteorClient.LOG.info("Loaded {} modules ({} skipped) in {}ms", totalCount, skippedCount, endTime - startTime); + MeteorClient.LOG.info("Loaded {} modules ({} skipped)", totalCount, skippedCount); } catch (Exception e) { MeteorClient.LOG.error("Failed to load modules", e); } From ce297a1d5735e34dc4c68bf3ed681e56b5487cdc Mon Sep 17 00:00:00 2001 From: Big-Iron-Cheems <52252627+Big-Iron-Cheems@users.noreply.github.com> Date: Thu, 3 Jul 2025 15:31:16 +0200 Subject: [PATCH 3/3] oops, wrong change list --- .../meteorclient/commands/Commands.java | 46 +++---------------- 1 file changed, 7 insertions(+), 39 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/commands/Commands.java b/src/main/java/meteordevelopment/meteorclient/commands/Commands.java index d82069ea0e..10f00eb57a 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/Commands.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/Commands.java @@ -27,48 +27,16 @@ public class Commands { public static final CommandDispatcher DISPATCHER = new CommandDispatcher<>(); public static final List COMMANDS = new ArrayList<>(); + private static final Set SCAN_PACKAGES = new HashSet<>(); + private static final Map> CONDITIONAL_COMMANDS = new HashMap<>(); + + static { + registerCommandPackage("meteordevelopment.meteorclient.commands.commands"); + } @PostInit(dependencies = PathManagers.class) public static void init() { - add(new VClipCommand()); - add(new HClipCommand()); - add(new DismountCommand()); - add(new DisconnectCommand()); - add(new DamageCommand()); - add(new DropCommand()); - add(new EnchantCommand()); - add(new FakePlayerCommand()); - add(new FriendsCommand()); - add(new CommandsCommand()); - add(new InventoryCommand()); - add(new NbtCommand()); - add(new NotebotCommand()); - add(new PeekCommand()); - add(new EnderChestCommand()); - add(new ProfilesCommand()); - add(new ReloadCommand()); - add(new ResetCommand()); - add(new SayCommand()); - add(new ServerCommand()); - add(new SwarmCommand()); - add(new ToggleCommand()); - add(new SettingCommand()); - add(new SpectateCommand()); - add(new GamemodeCommand()); - add(new SaveMapCommand()); - add(new MacroCommand()); - add(new ModulesCommand()); - add(new BindsCommand()); - add(new GiveCommand()); - add(new NameHistoryCommand()); - add(new BindCommand()); - add(new FovCommand()); - add(new RotationCommand()); - add(new WaypointCommand()); - add(new InputCommand()); - add(new WaspCommand()); - add(new LocateCommand()); - + loadCommands(); COMMANDS.sort(Comparator.comparing(Command::getName)); }