From 4eada7550ae00eb62529c3ec897e8e6e20cfaa35 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Wed, 18 Dec 2024 22:20:03 -0500 Subject: [PATCH] MVP for new CheckManager; faster iteration; structure to support reloading, adding/unloading checks --- build.gradle.kts | 5 +- .../java/ac/grim/grimac/GrimExternalAPI.java | 6 + .../java/ac/grim/grimac/checks/Check.java | 37 +- .../grimac/checks/type/BlockBreakCheck.java | 6 + .../grimac/checks/type/BlockPlaceCheck.java | 6 + .../grim/grimac/checks/type/PacketCheck.java | 5 + .../grimac/checks/type/PositionCheck.java | 5 + .../checks/type/PostPredictionCheck.java | 5 + .../grimac/checks/type/RotationCheck.java | 5 + .../grim/grimac/checks/type/VehicleCheck.java | 6 + .../ac/grim/grimac/manager/CheckManager.java | 666 ++++++++++++------ .../grimac/manager/PunishmentManager.java | 6 +- .../manager/config/ConfigManagerFileImpl.java | 31 + .../ac/grim/grimac/player/GrimPlayer.java | 9 +- 14 files changed, 586 insertions(+), 212 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index cdd1d94c87..67ae2eeac3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -67,8 +67,9 @@ dependencies { implementation("com.zaxxer:HikariCP:4.0.3") //implementation("com.github.grimanticheat:grimapi:1193c4fa41") - // Used for local testing: implementation("ac.grim.grimac:GRIMAPI:1.0") - implementation("com.github.grimanticheat:grimapi:fc5634e444") + // Used for local testing: + implementation("ac.grim.grimac:GrimAPI:1.0") +// implementation("com.github.grimanticheat:grimapi:fc5634e444") implementation("org.jetbrains:annotations:24.1.0") compileOnly("org.geysermc.floodgate:api:2.2.3-SNAPSHOT") diff --git a/src/main/java/ac/grim/grimac/GrimExternalAPI.java b/src/main/java/ac/grim/grimac/GrimExternalAPI.java index d97110b4ef..409c7c2b2b 100644 --- a/src/main/java/ac/grim/grimac/GrimExternalAPI.java +++ b/src/main/java/ac/grim/grimac/GrimExternalAPI.java @@ -114,6 +114,12 @@ public ConfigManager getConfigManager() { return configManager; } + @Override + public boolean hasStarted() { + // TODO implement this + throw new UnsupportedOperationException("Not implemented yet"); + } + private ConfigManager configManager = null; private final ConfigManagerFileImpl configManagerFile = new ConfigManagerFileImpl(); private boolean started = false; diff --git a/src/main/java/ac/grim/grimac/checks/Check.java b/src/main/java/ac/grim/grimac/checks/Check.java index 9821a9a7da..d8d8a380d9 100644 --- a/src/main/java/ac/grim/grimac/checks/Check.java +++ b/src/main/java/ac/grim/grimac/checks/Check.java @@ -3,6 +3,8 @@ import ac.grim.grimac.GrimAPI; import ac.grim.grimac.api.AbstractCheck; import ac.grim.grimac.api.config.ConfigManager; +import ac.grim.grimac.api.dynamic.DefaultUnloadedBehavior; +import ac.grim.grimac.api.dynamic.UnloadedBehavior; import ac.grim.grimac.api.events.FlagEvent; import ac.grim.grimac.player.GrimPlayer; import ac.grim.grimac.utils.common.ConfigReloadObserver; @@ -15,6 +17,8 @@ import lombok.Setter; import org.bukkit.Bukkit; +import java.lang.reflect.Method; + // Class from https://github.com/Tecnio/AntiCheatBase/blob/master/src/main/java/me/tecnio/anticheat/check/Check.java @Getter public class Check implements AbstractCheck, ConfigReloadObserver { @@ -40,6 +44,16 @@ public boolean isExperimental() { return experimental; } + @Override + public UnloadedBehavior getUnloadedBehavior() { + return DefaultUnloadedBehavior.INSTANCE; + } + + @Override + public int getCheckMask() { + throw new UnsupportedOperationException("Override this method to show which check types your check is!"); + } + public Check(final GrimPlayer player) { this.player = player; @@ -112,7 +126,6 @@ public final void reward() { violations = Math.max(0, violations - decay); } - @Override public void reload(ConfigManager configuration) { decay = configuration.getDoubleElse(configName + ".decay", decay); setbackVL = configuration.getDoubleElse(configName + ".setbackvl", setbackVL); @@ -185,6 +198,28 @@ public boolean isTickPacketIncludingNonMovement(PacketTypeCommon packetType) { return isFlying(packetType); } + public interface UnloadedCheckHandler { + // Return what the check's methods should return when unloaded + Object handleUnloadedCall(Method method, Object[] args); + } + + // Default behavior - do nothing and return false/0/null + public static final UnloadedCheckHandler DEFAULT_HANDLER = (method, args) -> { + // Return appropriate "no-op" value based on return type + Class returnType = method.getReturnType(); + if (returnType == boolean.class) return false; + if (returnType == int.class) return 0; + if (returnType == void.class) return null; + // etc... + return null; + }; + + private UnloadedCheckHandler unloadedHandler = DEFAULT_HANDLER; + + public void setUnloadedHandler(UnloadedCheckHandler handler) { + this.unloadedHandler = handler; + } + @Override public void reload() { reload(GrimAPI.INSTANCE.getConfigManager().getConfig()); diff --git a/src/main/java/ac/grim/grimac/checks/type/BlockBreakCheck.java b/src/main/java/ac/grim/grimac/checks/type/BlockBreakCheck.java index ce3326c2c9..3069774b8d 100644 --- a/src/main/java/ac/grim/grimac/checks/type/BlockBreakCheck.java +++ b/src/main/java/ac/grim/grimac/checks/type/BlockBreakCheck.java @@ -1,7 +1,13 @@ package ac.grim.grimac.checks.type; +import ac.grim.grimac.api.CheckType; import ac.grim.grimac.utils.anticheat.update.BlockBreak; public interface BlockBreakCheck extends PostPredictionCheck { default void onBlockBreak(final BlockBreak blockBreak) {} + + @Override + default int getCheckMask() { + return CheckType.BLOCK_PLACE.getMask(); + } } diff --git a/src/main/java/ac/grim/grimac/checks/type/BlockPlaceCheck.java b/src/main/java/ac/grim/grimac/checks/type/BlockPlaceCheck.java index 0247e5a14d..3162f1417b 100644 --- a/src/main/java/ac/grim/grimac/checks/type/BlockPlaceCheck.java +++ b/src/main/java/ac/grim/grimac/checks/type/BlockPlaceCheck.java @@ -1,5 +1,6 @@ package ac.grim.grimac.checks.type; +import ac.grim.grimac.api.CheckType; import ac.grim.grimac.api.config.ConfigManager; import ac.grim.grimac.checks.Check; import ac.grim.grimac.player.GrimPlayer; @@ -97,4 +98,9 @@ protected SimpleCollisionBox getCombinedBox(final BlockPlace place) { return combined; } + + @Override + public int getCheckMask() { + return CheckType.BLOCK_PLACE.getMask(); + } } diff --git a/src/main/java/ac/grim/grimac/checks/type/PacketCheck.java b/src/main/java/ac/grim/grimac/checks/type/PacketCheck.java index c9a09c4fe1..cd8011b7d9 100644 --- a/src/main/java/ac/grim/grimac/checks/type/PacketCheck.java +++ b/src/main/java/ac/grim/grimac/checks/type/PacketCheck.java @@ -1,10 +1,15 @@ package ac.grim.grimac.checks.type; import ac.grim.grimac.api.AbstractCheck; +import ac.grim.grimac.api.CheckType; import com.github.retrooper.packetevents.event.PacketReceiveEvent; import com.github.retrooper.packetevents.event.PacketSendEvent; public interface PacketCheck extends AbstractCheck { default void onPacketReceive(final PacketReceiveEvent event) {} default void onPacketSend(final PacketSendEvent event) {} + @Override + default int getCheckMask() { + return CheckType.PACKET.getMask(); + } } diff --git a/src/main/java/ac/grim/grimac/checks/type/PositionCheck.java b/src/main/java/ac/grim/grimac/checks/type/PositionCheck.java index 85adf8e1d8..f94e8acac7 100644 --- a/src/main/java/ac/grim/grimac/checks/type/PositionCheck.java +++ b/src/main/java/ac/grim/grimac/checks/type/PositionCheck.java @@ -1,10 +1,15 @@ package ac.grim.grimac.checks.type; import ac.grim.grimac.api.AbstractCheck; +import ac.grim.grimac.api.CheckType; import ac.grim.grimac.utils.anticheat.update.PositionUpdate; public interface PositionCheck extends AbstractCheck { default void onPositionUpdate(final PositionUpdate positionUpdate) { } + @Override + default int getCheckMask() { + return CheckType.POSITION.getMask(); + } } diff --git a/src/main/java/ac/grim/grimac/checks/type/PostPredictionCheck.java b/src/main/java/ac/grim/grimac/checks/type/PostPredictionCheck.java index ec66449226..0fe5077ed2 100644 --- a/src/main/java/ac/grim/grimac/checks/type/PostPredictionCheck.java +++ b/src/main/java/ac/grim/grimac/checks/type/PostPredictionCheck.java @@ -1,9 +1,14 @@ package ac.grim.grimac.checks.type; +import ac.grim.grimac.api.CheckType; import ac.grim.grimac.utils.anticheat.update.PredictionComplete; public interface PostPredictionCheck extends PacketCheck { default void onPredictionComplete(final PredictionComplete predictionComplete) { } + @Override + default int getCheckMask() { + return CheckType.POST_PREDICTION.getMask(); + } } diff --git a/src/main/java/ac/grim/grimac/checks/type/RotationCheck.java b/src/main/java/ac/grim/grimac/checks/type/RotationCheck.java index 5b1a0b847c..12fcc9d8ed 100644 --- a/src/main/java/ac/grim/grimac/checks/type/RotationCheck.java +++ b/src/main/java/ac/grim/grimac/checks/type/RotationCheck.java @@ -1,10 +1,15 @@ package ac.grim.grimac.checks.type; import ac.grim.grimac.api.AbstractCheck; +import ac.grim.grimac.api.CheckType; import ac.grim.grimac.utils.anticheat.update.RotationUpdate; public interface RotationCheck extends AbstractCheck { default void process(final RotationUpdate rotationUpdate) { } + @Override + default int getCheckMask() { + return CheckType.ROTATION.getMask(); + } } diff --git a/src/main/java/ac/grim/grimac/checks/type/VehicleCheck.java b/src/main/java/ac/grim/grimac/checks/type/VehicleCheck.java index adfbb07de4..e5d706ee9c 100644 --- a/src/main/java/ac/grim/grimac/checks/type/VehicleCheck.java +++ b/src/main/java/ac/grim/grimac/checks/type/VehicleCheck.java @@ -1,9 +1,15 @@ package ac.grim.grimac.checks.type; import ac.grim.grimac.api.AbstractCheck; +import ac.grim.grimac.api.CheckType; import ac.grim.grimac.utils.anticheat.update.VehiclePositionUpdate; public interface VehicleCheck extends AbstractCheck { void process(final VehiclePositionUpdate vehicleUpdate); + + @Override + default int getCheckMask() { + return CheckType.VEHICLE.getMask(); + } } diff --git a/src/main/java/ac/grim/grimac/manager/CheckManager.java b/src/main/java/ac/grim/grimac/manager/CheckManager.java index 08dcef49f3..9adab3ba43 100644 --- a/src/main/java/ac/grim/grimac/manager/CheckManager.java +++ b/src/main/java/ac/grim/grimac/manager/CheckManager.java @@ -1,6 +1,9 @@ package ac.grim.grimac.manager; import ac.grim.grimac.api.AbstractCheck; +import ac.grim.grimac.api.CheckType; +import ac.grim.grimac.api.dynamic.DefaultUnloadedBehavior; +import ac.grim.grimac.api.dynamic.UnloadedBehavior; import ac.grim.grimac.checks.impl.aim.AimDuplicateLook; import ac.grim.grimac.checks.impl.aim.AimModulo360; import ac.grim.grimac.checks.impl.aim.processor.AimProcessor; @@ -37,6 +40,7 @@ import ac.grim.grimac.player.GrimPlayer; import ac.grim.grimac.predictionengine.GhostBlockDetector; import ac.grim.grimac.predictionengine.SneakingEstimator; +import ac.grim.grimac.utils.anticheat.LogUtil; import ac.grim.grimac.utils.anticheat.update.*; import ac.grim.grimac.utils.latency.CompensatedCooldown; import ac.grim.grimac.utils.latency.CompensatedFireworks; @@ -44,287 +48,518 @@ import ac.grim.grimac.utils.team.TeamHandler; import com.github.retrooper.packetevents.event.PacketReceiveEvent; import com.github.retrooper.packetevents.event.PacketSendEvent; -import com.google.common.collect.ClassToInstanceMap; -import com.google.common.collect.ImmutableClassToInstanceMap; import org.bukkit.Bukkit; import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionDefault; +import java.lang.reflect.Proxy; +import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + public class CheckManager { private static boolean inited; private static final AtomicBoolean initedAtomic = new AtomicBoolean(false); - ClassToInstanceMap packetChecks; - ClassToInstanceMap positionCheck; - ClassToInstanceMap rotationCheck; - ClassToInstanceMap vehicleCheck; - ClassToInstanceMap prePredictionChecks; - ClassToInstanceMap blockBreakChecks; - ClassToInstanceMap blockPlaceCheck; - ClassToInstanceMap postPredictionCheck; + private final GrimPlayer player; + + // Fast arrays for iteration + private PacketCheck[] packetChecks; + private PositionCheck[] positionChecks; + private RotationCheck[] rotationChecks; + private VehicleCheck[] vehicleChecks; + private PacketCheck[] prePredictionChecks; + private BlockBreakCheck[] blockBreakChecks; + private BlockPlaceCheck[] blockPlaceChecks; + private PostPredictionCheck[] postPredictionChecks; - public ClassToInstanceMap allChecks; + // Lookup map for getting specific checks + public Map, AbstractCheck> loadedChecks = new HashMap<>(); + public Map, AbstractCheck> unloadedProxies = new HashMap<>(); public CheckManager(GrimPlayer player) { - // Include post checks in the packet check too - packetChecks = new ImmutableClassToInstanceMap.Builder() - .put(PacketOrderProcessor.class, player.packetOrderProcessor) - .put(Reach.class, new Reach(player)) - .put(HitboxMiss.class, new HitboxMiss(player)) - .put(HitboxBlock.class, new HitboxBlock(player)) - .put(HitboxEntity.class, new HitboxEntity(player)) - .put(PacketEntityReplication.class, new PacketEntityReplication(player)) - .put(PacketChangeGameState.class, new PacketChangeGameState(player)) - .put(CompensatedInventory.class, new CompensatedInventory(player)) - .put(PacketPlayerAbilities.class, new PacketPlayerAbilities(player)) - .put(PacketWorldBorder.class, new PacketWorldBorder(player)) - .put(ActionManager.class, player.actionManager) - .put(TeamHandler.class, new TeamHandler(player)) - .put(ClientBrand.class, new ClientBrand(player)) - .put(NoFallA.class, new NoFallA(player)) - .put(BadPacketsO.class, new BadPacketsO(player)) - .put(BadPacketsA.class, new BadPacketsA(player)) - .put(BadPacketsB.class, new BadPacketsB(player)) - .put(BadPacketsC.class, new BadPacketsC(player)) - .put(BadPacketsD.class, new BadPacketsD(player)) - .put(BadPacketsE.class, new BadPacketsE(player)) - .put(BadPacketsF.class, new BadPacketsF(player)) - .put(BadPacketsG.class, new BadPacketsG(player)) - .put(BadPacketsI.class, new BadPacketsI(player)) - .put(BadPacketsJ.class, new BadPacketsJ(player)) - .put(BadPacketsK.class, new BadPacketsK(player)) - .put(BadPacketsL.class, new BadPacketsL(player)) - .put(BadPacketsN.class, new BadPacketsN(player)) - .put(BadPacketsP.class, new BadPacketsP(player)) - .put(BadPacketsQ.class, new BadPacketsQ(player)) - .put(BadPacketsR.class, new BadPacketsR(player)) - .put(BadPacketsS.class, new BadPacketsS(player)) - .put(BadPacketsT.class, new BadPacketsT(player)) - .put(BadPacketsU.class, new BadPacketsU(player)) - .put(BadPacketsW.class, new BadPacketsW(player)) - .put(BadPacketsY.class, new BadPacketsY(player)) - .put(InventoryA.class, new InventoryA(player)) - .put(InventoryB.class, new InventoryB(player)) - .put(InventoryE.class, new InventoryE(player)) - .put(InventoryF.class, new InventoryF(player)) - .put(InventoryG.class, new InventoryG(player)) - .put(MultiActionsA.class, new MultiActionsA(player)) - .put(MultiActionsB.class, new MultiActionsB(player)) - .put(MultiActionsC.class, new MultiActionsC(player)) - .put(MultiActionsD.class, new MultiActionsD(player)) - .put(MultiActionsE.class, new MultiActionsE(player)) - .put(PacketOrderB.class, new PacketOrderB(player)) - .put(PacketOrderC.class, new PacketOrderC(player)) - .put(PacketOrderD.class, new PacketOrderD(player)) - .put(PacketOrderO.class, new PacketOrderO(player)) - .put(NoSlowB.class, new NoSlowB(player)) - .put(SetbackBlocker.class, new SetbackBlocker(player)) // Must be last class otherwise we can't check while blocking packets - .build(); - positionCheck = new ImmutableClassToInstanceMap.Builder() - .put(PredictionRunner.class, new PredictionRunner(player)) - .put(CompensatedCooldown.class, new CompensatedCooldown(player)) - .build(); - rotationCheck = new ImmutableClassToInstanceMap.Builder() - .put(AimProcessor.class, new AimProcessor(player)) - .put(AimModulo360.class, new AimModulo360(player)) - .put(AimDuplicateLook.class, new AimDuplicateLook(player)) -// .put(Baritone.class, new Baritone(player)) - .build(); - vehicleCheck = new ImmutableClassToInstanceMap.Builder() - .put(VehiclePredictionRunner.class, new VehiclePredictionRunner(player)) - .build(); - - postPredictionCheck = new ImmutableClassToInstanceMap.Builder() - .put(NegativeTimerCheck.class, new NegativeTimerCheck(player)) - .put(ExplosionHandler.class, new ExplosionHandler(player)) - .put(KnockbackHandler.class, new KnockbackHandler(player)) - .put(GhostBlockDetector.class, new GhostBlockDetector(player)) - .put(InventoryD.class, new InventoryD(player)) - .put(Phase.class, new Phase(player)) - .put(Post.class, new Post(player)) - .put(PacketOrderA.class, new PacketOrderA(player)) - .put(PacketOrderE.class, new PacketOrderE(player)) - .put(PacketOrderF.class, new PacketOrderF(player)) - .put(PacketOrderG.class, new PacketOrderG(player)) - .put(PacketOrderH.class, new PacketOrderH(player)) - .put(PacketOrderI.class, new PacketOrderI(player)) - .put(PacketOrderJ.class, new PacketOrderJ(player)) - .put(PacketOrderK.class, new PacketOrderK(player)) - .put(PacketOrderL.class, new PacketOrderL(player)) - .put(PacketOrderM.class, new PacketOrderM(player)) - .put(NoFallB.class, new NoFallB(player)) - .put(OffsetHandler.class, new OffsetHandler(player)) - .put(SuperDebug.class, new SuperDebug(player)) - .put(DebugHandler.class, new DebugHandler(player)) - .put(EntityControl.class, new EntityControl(player)) - .put(NoSlowA.class, new NoSlowA(player)) - .put(NoSlowC.class, new NoSlowC(player)) - .put(NoSlowD.class, new NoSlowD(player)) - .put(NoSlowE.class, new NoSlowE(player)) - .put(MultiInteractA.class, new MultiInteractA(player)) - .put(MultiInteractB.class, new MultiInteractB(player)) - .put(SetbackTeleportUtil.class, new SetbackTeleportUtil(player)) // Avoid teleporting to new position, update safe pos last - .put(CompensatedFireworks.class, player.compensatedFireworks) - .put(SneakingEstimator.class, new SneakingEstimator(player)) - .put(LastInstanceManager.class, player.lastInstanceManager) - .build(); - - blockPlaceCheck = new ImmutableClassToInstanceMap.Builder() - .put(InventoryC.class, new InventoryC(player)) - .put(InvalidPlaceA.class, new InvalidPlaceA(player)) - .put(InvalidPlaceB.class, new InvalidPlaceB(player)) - .put(AirLiquidPlace.class, new AirLiquidPlace(player)) - .put(MultiPlace.class, new MultiPlace(player)) - .put(MultiActionsF.class, new MultiActionsF(player)) - .put(FarPlace.class, new FarPlace(player)) - .put(FabricatedPlace.class, new FabricatedPlace(player)) - .put(PositionPlace.class, new PositionPlace(player)) - .put(PacketOrderN.class, new PacketOrderN(player)) - .put(DuplicateRotPlace.class, new DuplicateRotPlace(player)) - .put(LineOfSightPlace.class, new LineOfSightPlace(player)) - .put(GhostBlockMitigation.class, new GhostBlockMitigation(player)) - .build(); - - prePredictionChecks = new ImmutableClassToInstanceMap.Builder() - .put(TimerCheck.class, new TimerCheck(player)) - .put(TickTimer.class, new TickTimer(player)) - .put(CrashA.class, new CrashA(player)) - .put(CrashB.class, new CrashB(player)) - .put(CrashC.class, new CrashC(player)) - .put(CrashD.class, new CrashD(player)) - .put(CrashE.class, new CrashE(player)) - .put(CrashF.class, new CrashF(player)) - .put(CrashG.class, new CrashG(player)) - .put(CrashH.class, new CrashH(player)) - .put(ExploitA.class, new ExploitA(player)) - .put(ExploitB.class, new ExploitB(player)) - .put(ExploitC.class, new ExploitC(player)) - .put(VehicleTimer.class, new VehicleTimer(player)) - .build(); - - blockBreakChecks = new ImmutableClassToInstanceMap.Builder() - .put(BadPacketsX.class, new BadPacketsX(player)) - .put(BadPacketsZ.class, new BadPacketsZ(player)) - .put(FastBreak.class, new FastBreak(player)) - .build(); - - allChecks = new ImmutableClassToInstanceMap.Builder() - .putAll(packetChecks) - .putAll(positionCheck) - .putAll(rotationCheck) - .putAll(vehicleCheck) - .putAll(postPredictionCheck) - .putAll(blockPlaceCheck) - .putAll(prePredictionChecks) - .putAll(blockBreakChecks) - .build(); + this.player = player; + // Packet Checks + Map, PacketCheck> packetCheckMap = new HashMap<>(); + addCheck(PacketOrderProcessor.class, player.packetOrderProcessor, packetCheckMap); + addCheck(Reach.class, new Reach(player), packetCheckMap); + addCheck(HitboxMiss.class, new HitboxMiss(player), packetCheckMap); + addCheck(HitboxBlock.class, new HitboxBlock(player), packetCheckMap); + addCheck(HitboxEntity.class, new HitboxEntity(player), packetCheckMap); + addCheck(PacketEntityReplication.class, new PacketEntityReplication(player), packetCheckMap); + addCheck(PacketChangeGameState.class, new PacketChangeGameState(player), packetCheckMap); + addCheck(CompensatedInventory.class, new CompensatedInventory(player), packetCheckMap); + addCheck(PacketPlayerAbilities.class, new PacketPlayerAbilities(player), packetCheckMap); + addCheck(PacketWorldBorder.class, new PacketWorldBorder(player), packetCheckMap); + addCheck(ActionManager.class, player.actionManager, packetCheckMap); + addCheck(TeamHandler.class, new TeamHandler(player), packetCheckMap); + addCheck(ClientBrand.class, new ClientBrand(player), packetCheckMap); + addCheck(NoFallA.class, new NoFallA(player), packetCheckMap); + addCheck(BadPacketsO.class, new BadPacketsO(player), packetCheckMap); + addCheck(BadPacketsA.class, new BadPacketsA(player), packetCheckMap); + addCheck(BadPacketsB.class, new BadPacketsB(player), packetCheckMap); + addCheck(BadPacketsC.class, new BadPacketsC(player), packetCheckMap); + addCheck(BadPacketsD.class, new BadPacketsD(player), packetCheckMap); + addCheck(BadPacketsE.class, new BadPacketsE(player), packetCheckMap); + addCheck(BadPacketsF.class, new BadPacketsF(player), packetCheckMap); + addCheck(BadPacketsG.class, new BadPacketsG(player), packetCheckMap); + addCheck(BadPacketsI.class, new BadPacketsI(player), packetCheckMap); + addCheck(BadPacketsJ.class, new BadPacketsJ(player), packetCheckMap); + addCheck(BadPacketsK.class, new BadPacketsK(player), packetCheckMap); + addCheck(BadPacketsL.class, new BadPacketsL(player), packetCheckMap); + addCheck(BadPacketsN.class, new BadPacketsN(player), packetCheckMap); + addCheck(BadPacketsP.class, new BadPacketsP(player), packetCheckMap); + addCheck(BadPacketsQ.class, new BadPacketsQ(player), packetCheckMap); + addCheck(BadPacketsR.class, new BadPacketsR(player), packetCheckMap); + addCheck(BadPacketsS.class, new BadPacketsS(player), packetCheckMap); + addCheck(BadPacketsT.class, new BadPacketsT(player), packetCheckMap); + addCheck(BadPacketsU.class, new BadPacketsU(player), packetCheckMap); + addCheck(BadPacketsW.class, new BadPacketsW(player), packetCheckMap); + addCheck(BadPacketsY.class, new BadPacketsY(player), packetCheckMap); + addCheck(InventoryA.class, new InventoryA(player), packetCheckMap); + addCheck(InventoryB.class, new InventoryB(player), packetCheckMap); + addCheck(InventoryE.class, new InventoryE(player), packetCheckMap); + addCheck(InventoryF.class, new InventoryF(player), packetCheckMap); + addCheck(InventoryG.class, new InventoryG(player), packetCheckMap); + addCheck(MultiActionsA.class, new MultiActionsA(player), packetCheckMap); + addCheck(MultiActionsB.class, new MultiActionsB(player), packetCheckMap); + addCheck(MultiActionsC.class, new MultiActionsC(player), packetCheckMap); + addCheck(MultiActionsD.class, new MultiActionsD(player), packetCheckMap); + addCheck(MultiActionsE.class, new MultiActionsE(player), packetCheckMap); + addCheck(PacketOrderB.class, new PacketOrderB(player), packetCheckMap); + addCheck(PacketOrderC.class, new PacketOrderC(player), packetCheckMap); + addCheck(PacketOrderD.class, new PacketOrderD(player), packetCheckMap); + addCheck(PacketOrderO.class, new PacketOrderO(player), packetCheckMap); + addCheck(NoSlowB.class, new NoSlowB(player), packetCheckMap); + addCheck(SetbackBlocker.class, new SetbackBlocker(player), packetCheckMap); + packetChecks = packetCheckMap.values().toArray(new PacketCheck[0]); + loadedChecks.putAll(packetCheckMap); + + // Position Checks + Map, PositionCheck> positionCheckMap = new HashMap<>(); + addCheck(PredictionRunner.class, new PredictionRunner(player), positionCheckMap); + addCheck(CompensatedCooldown.class, new CompensatedCooldown(player), positionCheckMap); + positionChecks = positionCheckMap.values().toArray(new PositionCheck[0]); + loadedChecks.putAll(positionCheckMap); + + // Rotation Checks + Map, RotationCheck> rotationCheckMap = new HashMap<>(); + addCheck(AimProcessor.class, new AimProcessor(player), rotationCheckMap); + addCheck(AimModulo360.class, new AimModulo360(player), rotationCheckMap); + addCheck(AimDuplicateLook.class, new AimDuplicateLook(player), rotationCheckMap); + rotationChecks = rotationCheckMap.values().toArray(new RotationCheck[0]); + loadedChecks.putAll(rotationCheckMap); + + // Vehicle Checks + Map, VehicleCheck> vehicleCheckMap = new HashMap<>(); + addCheck(VehiclePredictionRunner.class, new VehiclePredictionRunner(player), vehicleCheckMap); + vehicleChecks = vehicleCheckMap.values().toArray(new VehicleCheck[0]); + loadedChecks.putAll(vehicleCheckMap); + + // Pre Prediction Checks + Map, PacketCheck> prePredictionCheckMap = new HashMap<>(); + addCheck(TimerCheck.class, new TimerCheck(player), prePredictionCheckMap); + addCheck(TickTimer.class, new TickTimer(player), prePredictionCheckMap); + addCheck(CrashA.class, new CrashA(player), prePredictionCheckMap); + addCheck(CrashB.class, new CrashB(player), prePredictionCheckMap); + addCheck(CrashC.class, new CrashC(player), prePredictionCheckMap); + addCheck(CrashD.class, new CrashD(player), prePredictionCheckMap); + addCheck(CrashE.class, new CrashE(player), prePredictionCheckMap); + addCheck(CrashF.class, new CrashF(player), prePredictionCheckMap); + addCheck(CrashG.class, new CrashG(player), prePredictionCheckMap); + addCheck(CrashH.class, new CrashH(player), prePredictionCheckMap); + addCheck(ExploitA.class, new ExploitA(player), prePredictionCheckMap); + addCheck(ExploitB.class, new ExploitB(player), prePredictionCheckMap); + addCheck(ExploitC.class, new ExploitC(player), prePredictionCheckMap); + addCheck(VehicleTimer.class, new VehicleTimer(player), prePredictionCheckMap); + prePredictionChecks = prePredictionCheckMap.values().toArray(new PacketCheck[0]); + loadedChecks.putAll(prePredictionCheckMap); + + // Block Break Checks + Map, BlockBreakCheck> blockBreakCheckMap = new HashMap<>(); + addCheck(BadPacketsX.class, new BadPacketsX(player), blockBreakCheckMap); + addCheck(BadPacketsZ.class, new BadPacketsZ(player), blockBreakCheckMap); + addCheck(FastBreak.class, new FastBreak(player), blockBreakCheckMap); + blockBreakChecks = blockBreakCheckMap.values().toArray(new BlockBreakCheck[0]); + loadedChecks.putAll(blockBreakCheckMap); + + // Block Place Checks + Map, BlockPlaceCheck> blockPlaceCheckMap = new HashMap<>(); + addCheck(InventoryC.class, new InventoryC(player), blockPlaceCheckMap); + addCheck(InvalidPlaceA.class, new InvalidPlaceA(player), blockPlaceCheckMap); + addCheck(InvalidPlaceB.class, new InvalidPlaceB(player), blockPlaceCheckMap); + addCheck(AirLiquidPlace.class, new AirLiquidPlace(player), blockPlaceCheckMap); + addCheck(MultiPlace.class, new MultiPlace(player), blockPlaceCheckMap); + addCheck(MultiActionsF.class, new MultiActionsF(player), blockPlaceCheckMap); + addCheck(FarPlace.class, new FarPlace(player), blockPlaceCheckMap); + addCheck(FabricatedPlace.class, new FabricatedPlace(player), blockPlaceCheckMap); + addCheck(PositionPlace.class, new PositionPlace(player), blockPlaceCheckMap); + addCheck(PacketOrderN.class, new PacketOrderN(player), blockPlaceCheckMap); + addCheck(DuplicateRotPlace.class, new DuplicateRotPlace(player), blockPlaceCheckMap); + addCheck(LineOfSightPlace.class, new LineOfSightPlace(player), blockPlaceCheckMap); + addCheck(GhostBlockMitigation.class, new GhostBlockMitigation(player), blockPlaceCheckMap); + blockPlaceChecks = blockPlaceCheckMap.values().toArray(new BlockPlaceCheck[0]); + loadedChecks.putAll(blockPlaceCheckMap); + + // Post Prediction Checks + Map, PostPredictionCheck> postPredictionCheckMap = new HashMap<>(); + addCheck(NegativeTimerCheck.class, new NegativeTimerCheck(player), postPredictionCheckMap); + addCheck(ExplosionHandler.class, new ExplosionHandler(player), postPredictionCheckMap); + addCheck(KnockbackHandler.class, new KnockbackHandler(player), postPredictionCheckMap); + addCheck(GhostBlockDetector.class, new GhostBlockDetector(player), postPredictionCheckMap); + addCheck(InventoryD.class, new InventoryD(player), postPredictionCheckMap); + addCheck(Phase.class, new Phase(player), postPredictionCheckMap); + addCheck(Post.class, new Post(player), postPredictionCheckMap); + addCheck(PacketOrderA.class, new PacketOrderA(player), postPredictionCheckMap); + addCheck(PacketOrderE.class, new PacketOrderE(player), postPredictionCheckMap); + addCheck(PacketOrderF.class, new PacketOrderF(player), postPredictionCheckMap); + addCheck(PacketOrderG.class, new PacketOrderG(player), postPredictionCheckMap); + addCheck(PacketOrderH.class, new PacketOrderH(player), postPredictionCheckMap); + addCheck(PacketOrderI.class, new PacketOrderI(player), postPredictionCheckMap); + addCheck(PacketOrderJ.class, new PacketOrderJ(player), postPredictionCheckMap); + addCheck(PacketOrderK.class, new PacketOrderK(player), postPredictionCheckMap); + addCheck(PacketOrderL.class, new PacketOrderL(player), postPredictionCheckMap); + addCheck(PacketOrderM.class, new PacketOrderM(player), postPredictionCheckMap); + addCheck(NoFallB.class, new NoFallB(player), postPredictionCheckMap); + addCheck(OffsetHandler.class, new OffsetHandler(player), postPredictionCheckMap); + addCheck(SuperDebug.class, new SuperDebug(player), postPredictionCheckMap); + addCheck(DebugHandler.class, new DebugHandler(player), postPredictionCheckMap); + addCheck(EntityControl.class, new EntityControl(player), postPredictionCheckMap); + addCheck(NoSlowA.class, new NoSlowA(player), postPredictionCheckMap); + addCheck(NoSlowC.class, new NoSlowC(player), postPredictionCheckMap); + addCheck(NoSlowD.class, new NoSlowD(player), postPredictionCheckMap); + addCheck(NoSlowE.class, new NoSlowE(player), postPredictionCheckMap); + addCheck(MultiInteractA.class, new MultiInteractA(player), postPredictionCheckMap); + addCheck(MultiInteractB.class, new MultiInteractB(player), postPredictionCheckMap); + addCheck(SetbackTeleportUtil.class, new SetbackTeleportUtil(player), postPredictionCheckMap); + addCheck(CompensatedFireworks.class, player.compensatedFireworks, postPredictionCheckMap); + addCheck(SneakingEstimator.class, new SneakingEstimator(player), postPredictionCheckMap); + addCheck(LastInstanceManager.class, player.lastInstanceManager, postPredictionCheckMap); + postPredictionChecks = postPredictionCheckMap.values().toArray(new PostPredictionCheck[0]); + loadedChecks.putAll(postPredictionCheckMap); init(); } + /** + * Adds a check to the appropriate map if the player is not exempt. + */ + private void addCheck( + Class checkClass, // The class type of the check + T checkInstance, // The instance of the check + Map, T> checkMap // The map to store the check + ) { + if (checkMap.get(checkClass) != null) { + LogUtil.warn("Attempted to add " + checkClass + " twice to the same checktype map for player + " + player.getName() + ", ignoring!"); +// } else if (checkInstance.getCheckName() == null || checkInstance.getCheckName() == "") { +// LogUtil.warn("Attempted to add a check with no or null name for player " + player.getName() + ", ignoring!"); + } else { + // existing behaviour has essentially been to always treat checks with null names and as core/non-unloadable and with no exempt permissions + // should we change how to handle this? + if (checkInstance.getCheckName() != null) { + // register permissions here to prevent NPE when checking if player is exempt + String permissionName = "grim.exempt." + checkInstance.getCheckName().toLowerCase(); + Permission permission = Bukkit.getPluginManager().getPermission(permissionName); + if (permission == null) { + Bukkit.getPluginManager().addPermission(new Permission(permissionName, PermissionDefault.FALSE)); + } else { + permission.setDefault(PermissionDefault.FALSE); + } + + // returns true if the check is exempt for the player + // Currently only checks for permission, in the future we will not add to map if: + // 1. Permission exempts player + // 2. Client version exempts player + if (!isExempt(checkClass)) { + checkMap.put(checkClass, checkInstance); + } + } else { + checkMap.put(checkClass, checkInstance); + } + } + } + + /** + * Performs a topological sort of checks based on their dependencies + */ + private List topologicalSort(Collection checks) { + Map, Set>> graph = new HashMap<>(); + Map, Integer> inDegree = new HashMap<>(); + + // Build dependency graph + for (AbstractCheck check : checks) { + Class checkClass = check.getClass(); + graph.putIfAbsent(checkClass, new HashSet<>()); + inDegree.putIfAbsent(checkClass, 0); + + // Add loadAfter dependencies + for (Class dep : check.getLoadAfter()) { + graph.computeIfAbsent(dep, k -> new HashSet<>()).add(checkClass); + inDegree.merge(checkClass, 1, Integer::sum); + } + + // Add loadBefore reverse dependencies + for (Class dep : check.getLoadBefore()) { + graph.computeIfAbsent(checkClass, k -> new HashSet<>()).add(dep); + inDegree.merge(dep, 1, Integer::sum); + } + + // Add direct dependencies + for (Class dep : check.getDependencies()) { + graph.computeIfAbsent(dep, k -> new HashSet<>()).add(checkClass); + inDegree.merge(checkClass, 1, Integer::sum); + } + } + + // Perform topological sort using Kahn's algorithm + Queue> queue = new LinkedList<>(); + Map, AbstractCheck> checkMap = checks.stream() + .collect(Collectors.toMap(AbstractCheck::getClass, c -> c)); + + inDegree.forEach((check, degree) -> { + if (degree == 0) queue.add(check); + }); + + List sorted = new ArrayList<>(); + while (!queue.isEmpty()) { + Class current = queue.poll(); + AbstractCheck check = checkMap.get(current); + if (check != null) { + sorted.add(check); + } + + for (Class dependent : graph.getOrDefault(current, Collections.emptySet())) { + inDegree.merge(dependent, -1, Integer::sum); + if (inDegree.get(dependent) == 0) { + queue.add(dependent); + } + } + } + + if (sorted.size() != checks.size()) { + throw new IllegalStateException("Circular dependency detected in checks"); + } + + return sorted; + } + + /** + * Rebuilds all check arrays maintaining dependency order + */ + private void rebuildCheckArrays() { + List sorted = topologicalSort(loadedChecks.values()); + + // Use lists first since we don't know final size + List newPacketChecks = new ArrayList<>(); + List newPositionChecks = new ArrayList<>(); + List newRotationChecks = new ArrayList<>(); + List newVehicleChecks = new ArrayList<>(); + List newPrePredictionChecks = new ArrayList<>(); + List newBlockBreakChecks = new ArrayList<>(); + List newBlockPlaceChecks = new ArrayList<>(); + List newPostPredictionChecks = new ArrayList<>(); + + // Single pass, add to all applicable lists + for (AbstractCheck check : sorted) { + // A check can be multiple types, so no else-if + if (check.isCheckType(CheckType.PACKET)) newPacketChecks.add((PacketCheck) check); + if (check.isCheckType(CheckType.POSITION)) newPositionChecks.add((PositionCheck) check); + if (check.isCheckType(CheckType.ROTATION)) newRotationChecks.add((RotationCheck) check); + if (check.isCheckType(CheckType.VEHICLE)) newVehicleChecks.add((VehicleCheck) check); + if (check.isCheckType(CheckType.PRE_PREDICTION)) newPrePredictionChecks.add((PacketCheck) check); + if (check.isCheckType(CheckType.BLOCK_BREAK)) newBlockBreakChecks.add((BlockBreakCheck) check); + if (check.isCheckType(CheckType.BLOCK_PLACE)) newBlockPlaceChecks.add((BlockPlaceCheck) check); + if (check.isCheckType(CheckType.POST_PREDICTION)) newPostPredictionChecks.add((PostPredictionCheck) check); + + } + + // Convert lists to arrays atomically + this.packetChecks = newPacketChecks.toArray(new PacketCheck[0]); + this.positionChecks = newPositionChecks.toArray(new PositionCheck[0]); + this.rotationChecks = newRotationChecks.toArray(new RotationCheck[0]); + this.vehicleChecks = newVehicleChecks.toArray(new VehicleCheck[0]); + this.prePredictionChecks = newPrePredictionChecks.toArray(new PacketCheck[0]); + this.blockBreakChecks = newBlockBreakChecks.toArray(new BlockBreakCheck[0]); + this.blockPlaceChecks = newBlockPlaceChecks.toArray(new BlockPlaceCheck[0]); + this.postPredictionChecks = newPostPredictionChecks.toArray(new PostPredictionCheck[0]); + } + + /** + * Registers and loads a check, resolving dependencies + * @throws IllegalStateException if dependencies cannot be satisfied + */ + public void registerCheck(AbstractCheck check) { + Class checkClass = check.getClass(); + + // Verify dependencies are loaded + for (Class dep : check.getDependencies()) { + if (!isCheckLoaded(dep)) { + throw new IllegalStateException("Missing required dependency: " + dep.getSimpleName()); + } + } + + // Load the check + if (!check.onLoad()) { + throw new IllegalStateException("Failed to load " + checkClass.getSimpleName()); + } + + loadedChecks.put(checkClass, check); + rebuildCheckArrays(); + } + + private boolean isCheckLoaded(Class dep) { + return loadedChecks.containsKey(dep); + } + + /** + * Unloads a check and its dependents + */ + public void unregisterCheck(Class checkClass) { + AbstractCheck check = loadedChecks.remove(checkClass); + if (check != null) { + // Unload dependents first + for (AbstractCheck other : loadedChecks.values()) { + if (other.getDependencies().contains(checkClass)) { + unregisterCheck(other.getClass()); + } + } + check.onUnload(); + rebuildCheckArrays(); + } + } + + /** + * Reloads a check and its dependents + */ + public void reloadCheck(Class checkClass) { + AbstractCheck check = loadedChecks.get(checkClass); + if (check != null) { + unregisterCheck(checkClass); + registerCheck(check); + } + } + + /** + * Check if a player is exempt from a specific check type. + */ + private boolean isExempt(Class checkClass) { + // Example logic for exemptions + String permission = "grim.exempt." + checkClass.getSimpleName().toLowerCase(); + return +// this.player.bukkitPlayer.hasPermission(permission) || + !checkClassAppliesToPlayerVersion(checkClass); + } + + private boolean checkClassAppliesToPlayerVersion(Class checkClass) { + // Example: Logic to determine if a check is compatible with the player's version + return true; // Replace with actual version check logic + } + @SuppressWarnings("unchecked") public T getPositionCheck(Class check) { - return (T) positionCheck.get(check); +// return (T) positionCheck.get(check); + return (T) loadedChecks.get(check); } @SuppressWarnings("unchecked") public T getRotationCheck(Class check) { - return (T) rotationCheck.get(check); +// return (T) rotationCheck.get(check); + return (T) loadedChecks.get(check); } @SuppressWarnings("unchecked") public T getVehicleCheck(Class check) { - return (T) vehicleCheck.get(check); +// return (T) vehicleCheck.get(check); + return (T) loadedChecks.get(check); } public void onPrePredictionReceivePacket(final PacketReceiveEvent packet) { - for (PacketCheck check : prePredictionChecks.values()) { + for (PacketCheck check : prePredictionChecks) { check.onPacketReceive(packet); } } public void onPacketReceive(final PacketReceiveEvent packet) { - for (PacketCheck check : packetChecks.values()) { + for (PacketCheck check : packetChecks) { check.onPacketReceive(packet); } - for (PostPredictionCheck check : postPredictionCheck.values()) { + for (PostPredictionCheck check : postPredictionChecks) { check.onPacketReceive(packet); } - for (BlockPlaceCheck check : blockPlaceCheck.values()) { + for (BlockPlaceCheck check : blockPlaceChecks) { check.onPacketReceive(packet); } - for (BlockBreakCheck check : blockBreakChecks.values()) { + for (BlockBreakCheck check : blockBreakChecks) { check.onPacketReceive(packet); } } public void onPacketSend(final PacketSendEvent packet) { - for (PacketCheck check : prePredictionChecks.values()) { + for (PacketCheck check : prePredictionChecks) { check.onPacketSend(packet); } - for (PacketCheck check : packetChecks.values()) { + for (PacketCheck check : packetChecks) { check.onPacketSend(packet); } - for (PostPredictionCheck check : postPredictionCheck.values()) { + for (PostPredictionCheck check : postPredictionChecks) { check.onPacketSend(packet); } - for (BlockPlaceCheck check : blockPlaceCheck.values()) { + for (BlockPlaceCheck check : blockPlaceChecks) { check.onPacketSend(packet); } - for (BlockBreakCheck check : blockBreakChecks.values()) { + for (BlockBreakCheck check : blockBreakChecks) { check.onPacketSend(packet); } } public void onPositionUpdate(final PositionUpdate position) { - for (PositionCheck check : positionCheck.values()) { + for (PositionCheck check : positionChecks) { check.onPositionUpdate(position); } } public void onRotationUpdate(final RotationUpdate rotation) { - for (RotationCheck check : rotationCheck.values()) { + for (RotationCheck check : rotationChecks) { check.process(rotation); } - for (BlockPlaceCheck check : blockPlaceCheck.values()) { + for (BlockPlaceCheck check : blockPlaceChecks) { check.process(rotation); } } public void onVehiclePositionUpdate(final VehiclePositionUpdate update) { - for (VehicleCheck check : vehicleCheck.values()) { + for (VehicleCheck check : vehicleChecks) { check.process(update); } } public void onPredictionFinish(final PredictionComplete complete) { - for (PostPredictionCheck check : postPredictionCheck.values()) { + for (PostPredictionCheck check : postPredictionChecks) { check.onPredictionComplete(complete); } - for (BlockPlaceCheck check : blockPlaceCheck.values()) { + for (BlockPlaceCheck check : blockPlaceChecks) { check.onPredictionComplete(complete); } - for (BlockBreakCheck check : blockBreakChecks.values()) { + for (BlockBreakCheck check : blockBreakChecks) { check.onPredictionComplete(complete); } } public void onBlockPlace(final BlockPlace place) { - for (BlockPlaceCheck check : blockPlaceCheck.values()) { + for (BlockPlaceCheck check : blockPlaceChecks) { check.onBlockPlace(place); } } public void onPostFlyingBlockPlace(final BlockPlace place) { - for (BlockPlaceCheck check : blockPlaceCheck.values()) { + for (BlockPlaceCheck check : blockPlaceChecks) { check.onPostFlyingBlockPlace(place); } } public void onBlockBreak(final BlockBreak blockBreak) { - for (BlockBreakCheck check : blockBreakChecks.values()) { + for (BlockBreakCheck check : blockBreakChecks) { check.onBlockBreak(blockBreak); } } @@ -335,17 +570,22 @@ public ExplosionHandler getExplosionHandler() { @SuppressWarnings("unchecked") public T getPacketCheck(Class check) { - return (T) packetChecks.get(check); + return getCheck(check); } @SuppressWarnings("unchecked") public T getBlockPlaceCheck(Class check) { - return (T) blockPlaceCheck.get(check); + return getCheck(check); } @SuppressWarnings("unchecked") public T getPrePredictionCheck(Class check) { - return (T) prePredictionChecks.get(check); + return getCheck(check); + } + + @SuppressWarnings("unchecked") + public T getPostPredictionCheck(Class check) { + return getCheck(check); } private PacketEntityReplication packetEntityReplication = null; @@ -390,9 +630,40 @@ public OffsetHandler getOffsetHandler() { return getPostPredictionCheck(OffsetHandler.class); } + /** + * Gets a check instance, creating an unloaded proxy if the check isn't loaded. + * This ensures calls to unloaded checks fail gracefully rather than throwing exceptions. + * + * @param checkClass The class of check to retrieve + * @return The check instance or an unloaded proxy + */ + // Type-safe check access with unloaded handling @SuppressWarnings("unchecked") - public T getPostPredictionCheck(Class check) { - return (T) postPredictionCheck.get(check); + public T getCheck(Class checkClass) { + T check = (T) loadedChecks.get(checkClass); + if (check == null) { + return (T) unloadedProxies.computeIfAbsent(checkClass, + this::createUnloadedProxy); + } + return check; + } + + /** + * Creates a proxy that handles calls to an unloaded check. + * The proxy uses the check's provider to determine unloaded behavior. + * + * @param checkClass The check class to create a proxy for + * @return A proxy implementing the check's interface + */ + private T createUnloadedProxy(Class checkClass) { + // Get the unloaded behavior from a cached instance or use default + UnloadedBehavior behavior = DefaultUnloadedBehavior.INSTANCE; + + return (T) Proxy.newProxyInstance( + checkClass.getClassLoader(), + new Class[] { checkClass }, + (proxy, method, args) -> behavior.handleUnloadedCall(method, args) + ); } private void init() { @@ -401,18 +672,5 @@ private void init() { // Slow thread safe check if (!initedAtomic.compareAndSet(false, true)) return; inited = true; - - for (AbstractCheck check : allChecks.values()) { - if (check.getCheckName() != null) { - String permissionName = "grim.exempt." + check.getCheckName().toLowerCase(); - Permission permission = Bukkit.getPluginManager().getPermission(permissionName); - - if (permission == null) { - Bukkit.getPluginManager().addPermission(new Permission(permissionName, PermissionDefault.FALSE)); - } else { - permission.setDefault(PermissionDefault.FALSE); - } - } - } } } diff --git a/src/main/java/ac/grim/grimac/manager/PunishmentManager.java b/src/main/java/ac/grim/grimac/manager/PunishmentManager.java index 0b2a63817b..978d9f3480 100644 --- a/src/main/java/ac/grim/grimac/manager/PunishmentManager.java +++ b/src/main/java/ac/grim/grimac/manager/PunishmentManager.java @@ -43,7 +43,8 @@ public void reload(ConfigManager config) { groups.clear(); // To support reloading - for (AbstractCheck check : player.checkManager.allChecks.values()) { + // TODO unloaded checks? + for (AbstractCheck check : player.checkManager.loadedChecks.values()) { check.setEnabled(false); } @@ -64,7 +65,8 @@ public void reload(ConfigManager config) { exclude = true; command = command.substring(1); } - for (AbstractCheck check : player.checkManager.allChecks.values()) { // o(n) * o(n)? + // TODO unloaded checks? + for (AbstractCheck check : player.checkManager.loadedChecks.values()) { // o(n) * o(n)? if (check.getCheckName() != null && (check.getCheckName().toLowerCase(Locale.ROOT).contains(command) || check.getAlternativeName().toLowerCase(Locale.ROOT).contains(command))) { // Some checks have equivalent names like AntiKB and AntiKnockback diff --git a/src/main/java/ac/grim/grimac/manager/config/ConfigManagerFileImpl.java b/src/main/java/ac/grim/grimac/manager/config/ConfigManagerFileImpl.java index 2a7837a1ae..0f0e467650 100644 --- a/src/main/java/ac/grim/grimac/manager/config/ConfigManagerFileImpl.java +++ b/src/main/java/ac/grim/grimac/manager/config/ConfigManagerFileImpl.java @@ -7,11 +7,13 @@ import ac.grim.grimac.utils.anticheat.LogUtil; import github.scarsz.configuralize.DynamicConfig; import github.scarsz.configuralize.Language; +import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.List; +import java.util.Map; public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable { @@ -318,6 +320,11 @@ public String getStringElse(String key, String otherwise) { return config.getStringElse(key, otherwise); } + @Override + public @Nullable String getString(String s) { + return ""; + } + @Override public List getStringList(String key) { return config.getStringList(key); @@ -353,4 +360,28 @@ public T get(String key) { return config.get(key); } + @Override + public @Nullable T getElse(String s, T t) { + // TODO implement this + throw new UnsupportedOperationException("Not implemented yet"); + } + + @Override + public @Nullable Map getMap(String s) { + // TODO implement this + throw new UnsupportedOperationException("Not implemented yet"); + } + + @Override + public @Nullable Map getMapElse(String s, Map map) { + // TODO implement this + throw new UnsupportedOperationException("Not implemented yet"); + } + + @Override + public boolean hasLoaded() { + // TODO implement this + throw new UnsupportedOperationException("Not implemented yet"); + } + } diff --git a/src/main/java/ac/grim/grimac/player/GrimPlayer.java b/src/main/java/ac/grim/grimac/player/GrimPlayer.java index 66ea19a9ea..b2f4e904e2 100644 --- a/src/main/java/ac/grim/grimac/player/GrimPlayer.java +++ b/src/main/java/ac/grim/grimac/player/GrimPlayer.java @@ -537,7 +537,8 @@ public void updatePermissions() { this.noModifyPacketPermission = bukkitPlayer.hasPermission("grim.nomodifypacket"); this.noSetbackPermission = bukkitPlayer.hasPermission("grim.nosetback"); FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(), t -> { - for (AbstractCheck check : checkManager.allChecks.values()) { + // todo, do we care about updating perms on unloaded checks? + for (AbstractCheck check : checkManager.loadedChecks.values()) { if (check instanceof Check) { ((Check) check).updateExempted(); } @@ -764,9 +765,10 @@ public boolean isVanillaMath() { return trigHandler.isVanillaMath(); } + // TODO rename to getLoadedChecks() ? @Override public Collection getChecks() { - return checkManager.allChecks.values(); + return checkManager.loadedChecks.values(); } public void runNettyTaskInMs(Runnable runnable, int ms) { @@ -789,7 +791,8 @@ public void reload(ConfigManager config) { cancelDuplicatePacket = config.getBooleanElse("cancel-duplicate-packet", true); exemptElytra = config.getBooleanElse("exempt-elytra", false); // reload all checks - for (AbstractCheck value : checkManager.allChecks.values()) value.reload(config); + // TODO, reload unloaded checks? + for (AbstractCheck check : checkManager.loadedChecks.values()) check.reload(); // reload punishment manager punishmentManager.reload(config); }