From bffbd2cc0651db542b1d109c18c7a89ab2691489 Mon Sep 17 00:00:00 2001
From: Lixfel <agga-games@gmx.de>
Date: Sun, 17 Mar 2024 16:58:34 +0100
Subject: [PATCH] 1.8 - 1.15 HullHider, fix Tab Hiding, fix appearing NameTags,
 improve Performance

untested

Signed-off-by: Lixfel <agga-games@gmx.de>
---
 FightSystem_14/build.gradle                   |  2 +
 .../fightsystem/utils/BlockIdWrapper14.java   | 28 +++++++++-
 .../fightsystem/utils/HullHiderWrapper18.java |  2 +-
 .../fightsystem/utils/BlockIdWrapper8.java    | 28 ++++++++++
 .../fightsystem/utils/HullHiderWrapper8.java  | 55 +++++++++++++++++++
 .../fightsystem/utils/BlockIdWrapper.java     | 12 ++++
 .../de/steamwar/fightsystem/utils/Hull.java   | 13 ++---
 .../steamwar/fightsystem/utils/HullHider.java | 17 +++---
 8 files changed, 136 insertions(+), 21 deletions(-)
 create mode 100644 FightSystem_8/src/de/steamwar/fightsystem/utils/HullHiderWrapper8.java

diff --git a/FightSystem_14/build.gradle b/FightSystem_14/build.gradle
index 29ff288..1d2a18c 100644
--- a/FightSystem_14/build.gradle
+++ b/FightSystem_14/build.gradle
@@ -46,6 +46,8 @@ dependencies {
     implementation project(":FightSystem_9")
     implementation project(":FightSystem_8")
 
+    compileOnly 'it.unimi.dsi:fastutil:8.5.6'
+
     compileOnly swdep("Spigot-1.14")
     compileOnly swdep("WorldEdit-1.15")
     compileOnly swdep("SpigotCore")
diff --git a/FightSystem_14/src/de/steamwar/fightsystem/utils/BlockIdWrapper14.java b/FightSystem_14/src/de/steamwar/fightsystem/utils/BlockIdWrapper14.java
index 934d9b6..f0554b1 100644
--- a/FightSystem_14/src/de/steamwar/fightsystem/utils/BlockIdWrapper14.java
+++ b/FightSystem_14/src/de/steamwar/fightsystem/utils/BlockIdWrapper14.java
@@ -21,18 +21,29 @@ package de.steamwar.fightsystem.utils;
 
 import com.comphenix.tinyprotocol.Reflection;
 import de.steamwar.core.Core;
+import de.steamwar.fightsystem.Config;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import org.bukkit.Material;
 import org.bukkit.World;
 import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
 
 public class BlockIdWrapper14 implements BlockIdWrapper {
 
-    private static final Class<?> worldServer = Reflection.getClass("{nms.server.level}.WorldServer");
     private static final Class<?> chunkProviderServer = Reflection.getClass("{nms.server.level}.ChunkProviderServer");
+    private static final Reflection.MethodInvoker getChunkProvider = Reflection.getTypedMethod(worldServer, null, chunkProviderServer);
+    private static final Class<?> playerChunkMap = Reflection.getClass("{nms.server.level}.PlayerChunkMap");
+    private static final Reflection.FieldAccessor<?> getPlayerChunkMap = Reflection.getField(chunkProviderServer, playerChunkMap,  0);
+    private static final Reflection.FieldAccessor<Int2ObjectMap> entityTrackers = Reflection.getField(playerChunkMap, Int2ObjectMap.class,  0);
     private static final Class<?> block = Reflection.getClass("{nms.world.level.block}.Block");
     private static final Class<?> iBlockData = Reflection.getClass("{nms.world.level.block.state}.IBlockData");
     private static final Class<?> blockPosition = Reflection.getClass("{nms.core}.BlockPosition");
 
+    private final Int2ObjectMap trackers;
+    public BlockIdWrapper14() {
+        trackers = entityTrackers.get(getPlayerChunkMap.get(getChunkProvider.invoke(getWorldHandle.invoke(Config.world))));
+    }
+
     private static final Reflection.MethodInvoker getCombinedId = Reflection.getTypedMethod(block, null, int.class, iBlockData);
     private static final Reflection.MethodInvoker getNMS = Reflection.getTypedMethod(Reflection.getClass("{obc}.block.CraftBlock"), "getNMS", iBlockData);
     @Override
@@ -41,11 +52,9 @@ public class BlockIdWrapper14 implements BlockIdWrapper {
     }
 
     private static final Reflection.MethodInvoker getByCombinedId = Reflection.getTypedMethod(block, null, iBlockData, int.class);
-    private static final Reflection.MethodInvoker getWorldHandle = Reflection.getTypedMethod(Reflection.getClass("{obc}.CraftWorld"), "getHandle", worldServer);
     private static final Reflection.ConstructorInvoker newBlockPosition = Reflection.getConstructor(blockPosition, int.class, int.class, int.class);
     private static final Reflection.MethodInvoker getTypeAndData = Reflection.getMethod(worldServer, null, blockPosition, iBlockData, int.class);
     private static final Reflection.MethodInvoker removeTileEntity = Reflection.getMethod(worldServer, Core.getVersion() > 15 ? "m" : "removeTileEntity", blockPosition);
-    private static final Reflection.MethodInvoker getChunkProvider = Reflection.getTypedMethod(worldServer, null, chunkProviderServer);
     private static final Reflection.MethodInvoker flagDirty = Reflection.getMethod(chunkProviderServer, null, blockPosition);
     @Override
     public void setBlock(World world, int x, int y, int z, int blockState) {
@@ -58,6 +67,19 @@ public class BlockIdWrapper14 implements BlockIdWrapper {
         flagDirty.invoke(getChunkProvider.invoke(nworld), pos);
     }
 
+    private static final Class<?> entityTracker = Reflection.getClass("{nms.server.level}.PlayerChunkMap$EntityTracker");
+    private static final Reflection.MethodInvoker updatePlayer = Reflection.getMethod(entityTracker, Core.getVersion() > 15 ? "b" : "updatePlayer", entityPlayer);
+    @Override
+    public void trackEntity(Player player, int entity) {
+        updatePlayer.invoke(trackers.get(entity), getPlayer.invoke(player));
+    }
+
+    private static final Reflection.MethodInvoker clearPlayer = Reflection.getMethod(entityTracker, Core.getVersion() > 15 ? "a" : "clear", entityPlayer);
+    @Override
+    public void untrackEntity(Player player, int entity) {
+        clearPlayer.invoke(trackers.get(entity), getPlayer.invoke(player));
+    }
+
     private static final Reflection.MethodInvoker getMaterialByBlock = Reflection.getTypedMethod(Reflection.getClass("{obc}.util.CraftMagicNumbers"), "getMaterial", Material.class, block);
     private static final Reflection.MethodInvoker getBlockByBlockData = Reflection.getTypedMethod(iBlockData, null, block);
     @Override
diff --git a/FightSystem_18/src/de/steamwar/fightsystem/utils/HullHiderWrapper18.java b/FightSystem_18/src/de/steamwar/fightsystem/utils/HullHiderWrapper18.java
index 6087aab..8f81cf9 100644
--- a/FightSystem_18/src/de/steamwar/fightsystem/utils/HullHiderWrapper18.java
+++ b/FightSystem_18/src/de/steamwar/fightsystem/utils/HullHiderWrapper18.java
@@ -42,7 +42,7 @@ public class HullHiderWrapper18 implements HullHiderWrapper {
 
         changes.removeIf(change -> {
             BlockData data = Config.world.getBlockData(change.getX(), change.getY(), change.getZ());
-            boolean unchanged = data.getMaterial() == Config.ObfuscateWith;
+            boolean unchanged = data.getMaterial() == Config.ObfuscateWith || Config.HiddenBlocks.contains(data.getMaterial());
             if(!unchanged)
                 blockdata.add(getState.invoke(data));
             return unchanged;
diff --git a/FightSystem_8/src/de/steamwar/fightsystem/utils/BlockIdWrapper8.java b/FightSystem_8/src/de/steamwar/fightsystem/utils/BlockIdWrapper8.java
index cbf186a..f77fe95 100644
--- a/FightSystem_8/src/de/steamwar/fightsystem/utils/BlockIdWrapper8.java
+++ b/FightSystem_8/src/de/steamwar/fightsystem/utils/BlockIdWrapper8.java
@@ -19,11 +19,25 @@
 
 package de.steamwar.fightsystem.utils;
 
+import com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.fightsystem.Config;
 import org.bukkit.Material;
 import org.bukkit.World;
 import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
 
 public class BlockIdWrapper8 implements BlockIdWrapper {
+
+    private static final Class<?> entityTracker = Reflection.getClass("{nms}.EntityTracker");
+    private static final Reflection.FieldAccessor<?> getEntityTracker = Reflection.getField(worldServer, entityTracker,  0);
+    private static final Class<?> intHashMap = Reflection.getClass("{nms}.IntHashMap");
+    private static final Reflection.FieldAccessor<?> getTrackedEntities = Reflection.getField(entityTracker, intHashMap,  0);
+
+    private final Object trackers;
+    public BlockIdWrapper8() {
+        trackers = getTrackedEntities.get(getEntityTracker.get(getWorldHandle.invoke(Config.world)));
+    }
+
     @Override
     @SuppressWarnings("deprecation")
     public int blockToId(Block block) {
@@ -39,6 +53,20 @@ public class BlockIdWrapper8 implements BlockIdWrapper {
         world.getBlockAt(x, y, z).setTypeIdAndData(blockState >> 4, (byte)(blockState & 0b1111), false);
     }
 
+    private static final Class<?> entityTrackerEntry = Reflection.getClass("{nms}.EntityTrackerEntry");
+    private static final Reflection.MethodInvoker get = Reflection.getTypedMethod(intHashMap, "get", Object.class, int.class);
+    private static final Reflection.MethodInvoker updatePlayer = Reflection.getMethod(entityTrackerEntry, "updatePlayer", entityPlayer);
+    @Override
+    public void trackEntity(Player player, int entity) {
+        updatePlayer.invoke(get.invoke(trackers, entity), getPlayer.invoke(player));
+    }
+
+    private static final Reflection.MethodInvoker clearPlayer = Reflection.getMethod(entityTrackerEntry, "a", entityPlayer);
+    @Override
+    public void untrackEntity(Player player, int entity) {
+        clearPlayer.invoke(get.invoke(trackers, entity), getPlayer.invoke(player));
+    }
+
     @Override
     @SuppressWarnings("deprecation")
     public Material idToMaterial(int blockState) {
diff --git a/FightSystem_8/src/de/steamwar/fightsystem/utils/HullHiderWrapper8.java b/FightSystem_8/src/de/steamwar/fightsystem/utils/HullHiderWrapper8.java
new file mode 100644
index 0000000..524842e
--- /dev/null
+++ b/FightSystem_8/src/de/steamwar/fightsystem/utils/HullHiderWrapper8.java
@@ -0,0 +1,55 @@
+/*
+ *   This file is a part of the SteamWar software.
+ *
+ *   Copyright (C) 2024  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 <https://www.gnu.org/licenses/>.
+ */
+
+package de.steamwar.fightsystem.utils;
+
+import com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.fightsystem.Config;
+import org.bukkit.Material;
+
+import java.util.List;
+
+public class HullHiderWrapper8 implements HullHiderWrapper {
+
+    private static final Reflection.ConstructorInvoker newMultiBlockChange = Reflection.getConstructor("{nms}.PacketPlayOutMultiBlockChange", int.class, short[].class, Reflection.getClass("{nms}.Chunk"));
+    private static final Reflection.MethodInvoker getHandle = Reflection.getMethod("{obc}.CraftChunk", "getHandle");
+    @Override
+    public Object generateBlockChangePacket(List<Hull.IntVector> changes) {
+        changes.removeIf(change -> {
+            Material material = Config.world.getBlockAt(change.getX(), change.getY(), change.getZ()).getType();
+            return material == Config.ObfuscateWith || Config.HiddenBlocks.contains(material);
+        });
+
+        if(changes.isEmpty())
+            return null;
+
+        Hull.IntVector chunk = changes.get(0);
+        chunk = new Hull.IntVector(chunk.getX() >> 4, chunk.getY() >> 4, chunk.getZ() >> 4);
+        int xOffset = 16*chunk.getX();
+        int zOffset = 16*chunk.getZ();
+        short[] pos = new short[changes.size()];
+
+        for(int i = 0; i < changes.size(); i++) {
+            Hull.IntVector change = changes.get(i);
+            pos[i] = (short) (((change.getX()-xOffset) << 12) + ((change.getZ()-zOffset) << 8) + change.getY());
+        }
+
+        return newMultiBlockChange.invoke(pos.length, pos, getHandle.invoke(Config.world.getChunkAt(chunk.getX(), chunk.getZ())));
+    }
+}
diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/utils/BlockIdWrapper.java b/FightSystem_Core/src/de/steamwar/fightsystem/utils/BlockIdWrapper.java
index 399e63d..a6b825d 100644
--- a/FightSystem_Core/src/de/steamwar/fightsystem/utils/BlockIdWrapper.java
+++ b/FightSystem_Core/src/de/steamwar/fightsystem/utils/BlockIdWrapper.java
@@ -19,16 +19,28 @@
 
 package de.steamwar.fightsystem.utils;
 
+import com.comphenix.tinyprotocol.Reflection;
 import de.steamwar.core.VersionDependent;
 import de.steamwar.fightsystem.FightSystem;
 import org.bukkit.Material;
 import org.bukkit.World;
 import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
 
 public interface BlockIdWrapper {
+    Class<?> worldServer = Reflection.getClass("{nms.server.level}.WorldServer");
+    Reflection.MethodInvoker getWorldHandle = Reflection.getTypedMethod(Reflection.getClass("{obc}.CraftWorld"), "getHandle", worldServer);
+
+    Class<?> craftPlayer = Reflection.getClass("{obc}.entity.CraftPlayer");
+    Class<?> entityPlayer = Reflection.getClass("{nms.server.level}.EntityPlayer");
+    Reflection.MethodInvoker getPlayer = Reflection.getTypedMethod(craftPlayer, "getHandle", entityPlayer);
+
     BlockIdWrapper impl = VersionDependent.getVersionImpl(FightSystem.getPlugin());
 
     Material idToMaterial(int blockState);
     int blockToId(Block block);
     void setBlock(World world, int x, int y, int z, int blockState);
+
+    void trackEntity(Player player, int entity);
+    void untrackEntity(Player player, int entity);
 }
diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java b/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java
index 3b96281..05ff3fc 100644
--- a/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java
+++ b/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java
@@ -106,19 +106,17 @@ public class Hull {
         return players.contains(player) && region.inRegion(location) && !visibility.get(new IntVector(location).toId(region));
     }
 
-    @SuppressWarnings("deprecation")
     public void addPlayer(Player player) {
         if(players.add(player)) {
             for(Entity entity : entities)
-                player.hideEntity(FightSystem.getPlugin(), entity);
+                BlockIdWrapper.impl.untrackEntity(player, entity.getEntityId());
         }
     }
 
-    @SuppressWarnings("deprecation")
     public void removePlayer(Player player, boolean activeRemoval) {
         if(players.remove(player) && activeRemoval) {
             for(Entity entity : entities)
-                player.showEntity(FightSystem.getPlugin(), entity);
+                BlockIdWrapper.impl.trackEntity(player, entity.getEntityId());
             // techhider triggers block change sending
         }
     }
@@ -128,12 +126,12 @@ public class Hull {
         if(region.inRegion(location) && !visibility.get(new IntVector(location).toId(region))) { //TODO more precise
             if(entities.add(entity)) {
                 for(Player player : players)
-                    player.hideEntity(FightSystem.getPlugin(), entity);
+                    BlockIdWrapper.impl.untrackEntity(player, entity.getEntityId());
             }
         } else {
             if(entities.remove(entity)) {
                 for(Player player : players)
-                    player.showEntity(FightSystem.getPlugin(), entity);
+                    BlockIdWrapper.impl.trackEntity(player, entity.getEntityId());
             }
         }
     }
@@ -180,7 +178,6 @@ public class Hull {
         FightSystem.getPlugin().getLogger().log(Level.INFO, () -> "[HullHider] initialisation finished: " + (System.currentTimeMillis() - start) + " ms, visible blocks: " + visibility.cardinality());
     }
 
-    @SuppressWarnings("deprecation")
     public void updateBlockVisibility(Block b, Material changedType) {
         IntVector root = new IntVector(b.getX(), b.getY(), b.getZ());
         if (root.notInRegion(region))
@@ -211,7 +208,7 @@ public class Hull {
             if(uncoveredSet.contains(new IntVector(entity.getLocation()))) { //TODO more precise
                 it.remove();
                 for(Player player : players)
-                    player.showEntity(FightSystem.getPlugin(), entity);
+                    BlockIdWrapper.impl.trackEntity(player, entity.getEntityId());
             }
         }
 
diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java b/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java
index 4daf4ad..c613a4a 100644
--- a/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java
+++ b/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java
@@ -56,13 +56,12 @@ import java.util.function.Function;
 
 public class HullHider implements Listener {
 
-    public static final boolean ENABLED = TechHiderWrapper.ENABLED && Core.getVersion() >= 18;
     private final Map<FightTeam, Hull> hullMap = new HashMap<>();
     private final Hull[] hulls;
     private final Map<Class<?>, BiFunction<Player, Object, Object>> packetHiders = new HashMap<>();
 
     public HullHider() {
-        if(!ENABLED) {
+        if(!TechHiderWrapper.ENABLED) {
             hulls = new Hull[0];
             return;
         }
@@ -77,8 +76,8 @@ public class HullHider implements Listener {
         if(Core.getVersion() >= 9 && Core.getVersion() < 18)
             posHiderGenerator("{nms.network.protocol.game}.PacketPlayOutCustomSoundEffect", int.class, 8.0);
 
-        new StateDependentListener(ENABLED, FightState.Schem, this);
-        new StateDependent(ENABLED, FightState.Schem) {
+        new StateDependentListener(TechHiderWrapper.ENABLED, FightState.Schem, this);
+        new StateDependent(TechHiderWrapper.ENABLED, FightState.Schem) {
             @Override
             public void enable() {
                 packetHiders.forEach(TinyProtocol.instance::addFilter);
@@ -91,11 +90,11 @@ public class HullHider implements Listener {
                 packetHiders.forEach(TinyProtocol.instance::removeFilter);
             }
         }.register();
-        new StateDependentTask(ENABLED, FightState.Schem, this::onTick, 0, 1);
+        new StateDependentTask(TechHiderWrapper.ENABLED, FightState.Schem, this::onTick, 0, 1);
     }
 
     public void initialize(FightTeam team) {
-        if(!ENABLED)
+        if(!TechHiderWrapper.ENABLED)
             return;
 
         hullMap.get(team).initialize();
@@ -113,7 +112,7 @@ public class HullHider implements Listener {
     }
 
     public void updatePlayer(Player player) {
-        if(!ENABLED)
+        if(!TechHiderWrapper.ENABLED)
             return;
 
         FightTeam team = Fight.getPlayerTeam(player);
@@ -145,7 +144,7 @@ public class HullHider implements Listener {
     }
 
     public boolean isBlockHidden(Player player, int x, int y, int z) {
-        if(!ENABLED)
+        if(!TechHiderWrapper.ENABLED)
             return false;
 
         for (Hull hull : hulls)
@@ -156,7 +155,7 @@ public class HullHider implements Listener {
     }
 
     public boolean blockPrecise(Player player, int chunkX, int chunkY, int chunkZ) {
-        if(!ENABLED)
+        if(!TechHiderWrapper.ENABLED)
             return false;
 
         for (Hull hull : hulls)
-- 
2.39.5