diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java b/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java
index 8d51340..954ac97 100644
--- a/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java
+++ b/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java
@@ -93,6 +93,7 @@ public class FightSystem extends JavaPlugin {
new ClickAnalyzer();
new HotbarKit.HotbarKitListener();
new OneShotStateDependent(ArenaMode.All, FightState.PreSchemSetup, () -> Fight.playSound(SWSound.BLOCK_NOTE_PLING.getSound(), 100.0f, 2.0f));
+ new AntiCheat();
new EnterHandler();
techHider = new TechHiderWrapper();
diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/listener/AntiCheat.java b/FightSystem_Core/src/de/steamwar/fightsystem/listener/AntiCheat.java
new file mode 100644
index 0000000..8534579
--- /dev/null
+++ b/FightSystem_Core/src/de/steamwar/fightsystem/listener/AntiCheat.java
@@ -0,0 +1,118 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2022 SteamWar.de-Serverteam
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+ */
+
+package de.steamwar.fightsystem.listener;
+
+import com.comphenix.tinyprotocol.Reflection;
+import com.comphenix.tinyprotocol.TinyProtocol;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.sql.SWException;
+import de.steamwar.techhider.ProtocolUtils;
+import de.steamwar.techhider.TechHider;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+
+public class AntiCheat {
+
+ public AntiCheat() {
+ TinyProtocol.instance.addFilter(teleport, this::onTeleport);
+ TinyProtocol.instance.addFilter(position, this::onMove);
+ TinyProtocol.instance.addFilter(positionLook, this::onMove);
+ TinyProtocol.instance.addFilter(blockDig, this::onDig);
+ }
+
+ private static final Class> flying = Reflection.getClass("{nms.network.protocol.game}.PacketPlayInFlying");
+ private static final Reflection.FieldAccessor moveX = Reflection.getField(flying, double.class, 0);
+ private static final Reflection.FieldAccessor moveY = Reflection.getField(flying, double.class, 1);
+ private static final Reflection.FieldAccessor moveZ = Reflection.getField(flying, double.class, 2);
+ private static final Class> position = Reflection.getClass("{nms.network.protocol.game}.PacketPlayInFlying$PacketPlayInPosition");
+ private static final Class> positionLook = Reflection.getClass("{nms.network.protocol.game}.PacketPlayInFlying$PacketPlayInPositionLook");
+
+ private static final Class> teleport = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPosition");
+ private static final Reflection.FieldAccessor tpX = Reflection.getField(teleport, double.class, 0);
+ private static final Reflection.FieldAccessor tpY = Reflection.getField(teleport, double.class, 1);
+ private static final Reflection.FieldAccessor tpZ = Reflection.getField(teleport, double.class, 2);
+
+ private final ConcurrentHashMap lastMovement = new ConcurrentHashMap<>();
+ private Object onTeleport(Player player, Object packet) {
+ //TODO Possible false flag through crouching into forbidden area?
+ //TODO circumvention through fast consecutive movements? (checks are synchronous)
+ //TODO uncertainty through moving pistons/desync etc.? (slow limit with timer to check for occurances?)
+ //TODO: Clearing with movement events?
+ Movement last = lastMovement.get(player);
+ if(last != null && tpX.get(packet) == last.x && tpY.get(packet) == last.y && tpZ.get(packet) == last.z) {
+ FightSystem.getPlugin().getLogger().log(Level.INFO, player.getName() + " was backported after small movement");
+ }
+ return packet;
+ }
+
+ private Object onMove(Player player, Object packet) {
+ //TODO Idee: Messung kleiner Movements, bei nächstem Movement mit folgendem Teleport für illegal movement trigger
+ Location location = player.getLocation();
+ double x = moveX.get(packet);
+ double dx = x - location.getX();
+ double y = moveY.get(packet);
+ double dy = x - location.getY();
+ double z = moveZ.get(packet);
+ double dz = z - location.getZ();
+ double lSqared = dx*dx + dy*dy + dz*dz;
+ lastMovement.compute(player, (p, previous) -> {
+ if(lSqared <= 0.00390625) {
+ return new Movement(x, y, z);
+ } else if(previous != null && previous.last) {
+ previous.last = false;
+ return previous;
+ }
+ return null;
+ });
+ return packet;
+ }
+
+ private static final Class> blockDig = Reflection.getClass("{nms.network.protocol.game}.PacketPlayInBlockDig");
+ private static final Reflection.FieldAccessor> digPosition = Reflection.getField(blockDig, TechHider.blockPosition, 0);
+ private Object onDig(Player player, Object packet) {
+ Object pos = digPosition.get(packet);
+ int x = TechHider.blockPositionX.get(pos);
+ int z = TechHider.blockPositionZ.get(pos);
+ if(!player.getWorld().isChunkLoaded(ProtocolUtils.posToChunk(x), ProtocolUtils.posToChunk(z))) {
+ Bukkit.getScheduler().runTask(FightSystem.getPlugin(), () -> player.kickPlayer(null));
+ SWException.log(player.getName() + " tried to dig an unloaded block", "");
+ return null;
+ }
+ return packet;
+ }
+
+ private static class Movement {
+ private final double x;
+ private final double y;
+ private final double z;
+ private boolean last;
+
+ private Movement(double x, double y, double z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ last = true;
+ }
+ }
+}