From 41c09737c6b87ae9dafe5297ccdd2478e3af9628 Mon Sep 17 00:00:00 2001 From: Esmorall Date: Fri, 26 Jun 2020 14:52:17 -0300 Subject: [PATCH] 1.16.1 support test --- .../v1_16_R1/NMSEntityTracker.java | 51 ++++++++ .../v1_16_R1/NMSHandler.java | 33 ++++++ .../entityTick/EntityTickManager.java | 105 ++++++++++++++++ .../entityTick/EntityTickWorldCache.java | 38 ++++++ .../v1_16_R1/tasks/CheckTask.java | 52 ++++++++ .../v1_16_R1/tasks/UntrackerTask.java | 112 ++++++++++++++++++ 6 files changed, 391 insertions(+) create mode 100644 EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/NMSEntityTracker.java create mode 100644 EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/NMSHandler.java create mode 100644 EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/entityTick/EntityTickManager.java create mode 100644 EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/entityTick/EntityTickWorldCache.java create mode 100644 EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/tasks/CheckTask.java create mode 100644 EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/tasks/UntrackerTask.java diff --git a/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/NMSEntityTracker.java b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/NMSEntityTracker.java new file mode 100644 index 0000000..3ac893b --- /dev/null +++ b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/NMSEntityTracker.java @@ -0,0 +1,51 @@ +package net.minemora.entitytrackerfixer.v1_16_R1; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Set; + +import net.minecraft.server.v1_16_R1.ChunkProviderServer; +import net.minecraft.server.v1_16_R1.PlayerChunkMap; +import net.minemora.entitytrackerfixer.util.ReflectionUtils; + +public final class NMSEntityTracker { + + private static Method addEntityMethod; + private static Method removeEntityMethod; + + static { + try { + addEntityMethod = ReflectionUtils.getPrivateMethod(PlayerChunkMap.class, "addEntity", + new Class[] {net.minecraft.server.v1_16_R1.Entity.class}); + removeEntityMethod = ReflectionUtils.getPrivateMethod(PlayerChunkMap.class, "removeEntity", + new Class[] {net.minecraft.server.v1_16_R1.Entity.class}); + } catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) { + e.printStackTrace(); + } + } + + private NMSEntityTracker() {} + + public static void trackEntities(ChunkProviderServer cps, Set trackList) { + try { + for(net.minecraft.server.v1_16_R1.Entity entity : trackList) { + if(cps.playerChunkMap.trackedEntities.containsKey(entity.getId())) { + continue; + } + addEntityMethod.invoke(cps.playerChunkMap, entity); + } + } catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + e.printStackTrace(); + } + } + + public static void untrackEntities(ChunkProviderServer cps, Set untrackList) { + try { + for(net.minecraft.server.v1_16_R1.Entity entity : untrackList) { + removeEntityMethod.invoke(cps.playerChunkMap, entity); + } + } catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + e.printStackTrace(); + } + } +} diff --git a/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/NMSHandler.java b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/NMSHandler.java new file mode 100644 index 0000000..0dad050 --- /dev/null +++ b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/NMSHandler.java @@ -0,0 +1,33 @@ +package net.minemora.entitytrackerfixer.v1_16_R1; + +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_16_R1.CraftWorld; +import org.bukkit.plugin.Plugin; + +import net.minemora.entitytrackerfixer.config.ConfigMain; +import net.minemora.entitytrackerfixer.nms.NMS; +import net.minemora.entitytrackerfixer.v1_16_R1.entityTick.EntityTickManager; +import net.minemora.entitytrackerfixer.v1_16_R1.entityTick.EntityTickWorldCache; +import net.minemora.entitytrackerfixer.v1_16_R1.tasks.CheckTask; +import net.minemora.entitytrackerfixer.v1_16_R1.tasks.UntrackerTask; + +public class NMSHandler implements NMS { + + @Override + public void startTasks(Plugin plugin) { + new UntrackerTask().runTaskTimer(plugin, ConfigMain.getUntrackTicks(), ConfigMain.getUntrackTicks()); + new CheckTask().runTaskTimer(plugin, ConfigMain.getUntrackTicks() + 1, ConfigMain.getCheckFrequency()); + } + + @Override + public void loadWorldCache() { + for(String worldName : ConfigMain.getWorlds()) { + if(Bukkit.getWorld(worldName) == null) { + continue; + } + EntityTickManager.getInstance().getCache().put(worldName, + new EntityTickWorldCache(((CraftWorld)Bukkit.getWorld(worldName)).getHandle())); + } + } + +} diff --git a/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/entityTick/EntityTickManager.java b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/entityTick/EntityTickManager.java new file mode 100644 index 0000000..827abe9 --- /dev/null +++ b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/entityTick/EntityTickManager.java @@ -0,0 +1,105 @@ +package net.minemora.entitytrackerfixer.v1_16_R1.entityTick; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; + +import net.minecraft.server.v1_16_R1.WorldServer; +import net.minemora.entitytrackerfixer.EntityTrackerFixer; +import net.minemora.entitytrackerfixer.util.ReflectionUtils; +import net.minemora.entitytrackerfixer.v1_16_R1.tasks.UntrackerTask; + +public class EntityTickManager extends BukkitRunnable { + + private static Field tickingEntitiesField; + private static Field entityCount; + + static { + try { + tickingEntitiesField = ReflectionUtils.getClassPrivateField(WorldServer.class, "tickingEntities"); + entityCount = ReflectionUtils.getClassPrivateField(net.minecraft.server.v1_16_R1.Entity.class, "entityCount"); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + } + + private static EntityTickManager instance; + + private Map cache = new HashMap<>(); + + private EntityTickManager(Plugin plugin) { + this.runTaskTimer(plugin, 61, 61); + } + + public void disableTicking(int id, String worldName) { + cache.get(worldName).getToTick().remove(id); + cache.get(worldName).getToUntick().add(id); + } + + public void enableTicking(net.minecraft.server.v1_16_R1.Entity entity, String worldName) { + cache.get(worldName).getToUntick().remove(entity.getId()); + cache.get(worldName).getToTick().put(entity.getId(), entity); + } + + @Override + public void run() { + if(UntrackerTask.isRunning()) { + return; + } + for(String worldName : cache.keySet()) { + EntityTickWorldCache ewc = cache.get(worldName); + WorldServer ws = ewc.getWorldServer(); + if(ws.m_()) { + continue; + } + try { + if(tickingEntitiesField.getBoolean(ws)) { + continue; + } + //System.out.println("unticking: " + ewc.getToUntick().size() + " entities, ticking again: " + ewc.getToTick().size() + " entities"); + for(int i : ewc.getToUntick()) { + ws.entitiesById.remove(i); + } + ewc.getToUntick().clear(); + for(int i : ewc.getToTick().keySet()) { + net.minecraft.server.v1_16_R1.Entity entity = ewc.getToTick().get(i); + if(entity == null) { + continue; + } + if(!entity.valid) { + continue; + } + if(ws.entitiesById.containsValue(entity)) { + continue; + } + if(ws.entitiesById.containsKey(i)) { + int id = ((AtomicInteger)entityCount.get(null)).incrementAndGet(); + ws.entitiesById.put(id, entity); + } + else { + ws.entitiesById.put(i, entity); + } + } + ewc.getToTick().clear(); + } catch (IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + public static EntityTickManager getInstance() { + if(instance == null) { + instance = new EntityTickManager(EntityTrackerFixer.plugin); + } + return instance; + } + + public Map getCache() { + return cache; + } + +} diff --git a/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/entityTick/EntityTickWorldCache.java b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/entityTick/EntityTickWorldCache.java new file mode 100644 index 0000000..61cef5c --- /dev/null +++ b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/entityTick/EntityTickWorldCache.java @@ -0,0 +1,38 @@ +package net.minemora.entitytrackerfixer.v1_16_R1.entityTick; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import net.minecraft.server.v1_16_R1.WorldServer; + +public class EntityTickWorldCache { + + private String worldName; + private WorldServer worldServer; + + private Set toUntick = new HashSet<>(); + private Map toTick = new HashMap<>(); + + public EntityTickWorldCache(WorldServer worldServer) { + this.worldName = worldServer.getWorld().getName(); + this.worldServer = worldServer; + } + + public Set getToUntick() { + return toUntick; + } + + public Map getToTick() { + return toTick; + } + + public String getWorldName() { + return worldName; + } + + public WorldServer getWorldServer() { + return worldServer; + } +} diff --git a/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/tasks/CheckTask.java b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/tasks/CheckTask.java new file mode 100644 index 0000000..865a99e --- /dev/null +++ b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/tasks/CheckTask.java @@ -0,0 +1,52 @@ +package net.minemora.entitytrackerfixer.v1_16_R1.tasks; + +import java.util.HashSet; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_16_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R1.entity.CraftEntity; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import net.minecraft.server.v1_16_R1.ChunkProviderServer; +import net.minecraft.server.v1_16_R1.WorldServer; +import net.minemora.entitytrackerfixer.config.ConfigMain; +import net.minemora.entitytrackerfixer.v1_16_R1.NMSEntityTracker; +import net.minemora.entitytrackerfixer.v1_16_R1.entityTick.EntityTickManager; + +public class CheckTask extends BukkitRunnable { + + @Override + public void run() { + if(UntrackerTask.isRunning()) { + return; + } + for(String worldName : ConfigMain.getWorlds()) { + if(Bukkit.getWorld(worldName) == null) { + continue; + } + checkWorld(worldName); + } + } + + public void checkWorld(String worldName) { + WorldServer ws = ((CraftWorld)Bukkit.getWorld(worldName)).getHandle(); + ChunkProviderServer cps = ws.getChunkProvider(); + + Set trackAgain = new HashSet<>(); + + int d = ConfigMain.getTrackingRange(); + for(Player player : Bukkit.getWorld(worldName).getPlayers()) { + for(Entity ent : player.getNearbyEntities(d, d, d)) { + trackAgain.add(((CraftEntity)ent).getHandle()); + if(ConfigMain.isDisableTickUntracked()) { + EntityTickManager.getInstance().enableTicking(((CraftEntity)ent).getHandle(), worldName); + } + } + } + NMSEntityTracker.trackEntities(cps, trackAgain); + } + +} \ No newline at end of file diff --git a/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/tasks/UntrackerTask.java b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/tasks/UntrackerTask.java new file mode 100644 index 0000000..7572768 --- /dev/null +++ b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/tasks/UntrackerTask.java @@ -0,0 +1,112 @@ +package net.minemora.entitytrackerfixer.v1_16_R1.tasks; + +import java.lang.reflect.Field; +import java.util.HashSet; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_16_R1.CraftWorld; +import org.bukkit.scheduler.BukkitRunnable; + +import net.minecraft.server.v1_16_R1.EntityItemFrame; +import net.minecraft.server.v1_16_R1.ChunkProviderServer; +import net.minecraft.server.v1_16_R1.EntityArmorStand; +import net.minecraft.server.v1_16_R1.EntityEnderDragon; +import net.minecraft.server.v1_16_R1.EntityPlayer; +import net.minecraft.server.v1_16_R1.MinecraftServer; +import net.minecraft.server.v1_16_R1.WorldServer; +import net.minecraft.server.v1_16_R1.PlayerChunkMap.EntityTracker; +import net.minemora.entitytrackerfixer.EntityTrackerFixer; +import net.minemora.entitytrackerfixer.config.ConfigMain; +import net.minemora.entitytrackerfixer.util.ReflectionUtils; +import net.minemora.entitytrackerfixer.v1_16_R1.entityTick.EntityTickManager; + +public class UntrackerTask extends BukkitRunnable { + + private static boolean running = false; + + private static Field trackerField; + + static { + try { + trackerField = ReflectionUtils.getClassPrivateField(EntityTracker.class, "tracker"); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + } + + @SuppressWarnings("deprecation") + @Override + public void run() { + if(MinecraftServer.getServer().recentTps[0] > ConfigMain.getMinTps()) { + return; + } + running = true; + for(String worldName : ConfigMain.getWorlds()) { + untrackProcess(worldName); + } + running = false; + } + + private void untrackProcess(String worldName) { + if(Bukkit.getWorld(worldName) == null) { + return; + } + //Set toRemove = new HashSet<>(); + Set toRemove = new HashSet<>(); + int removed = 0; + WorldServer ws = ((CraftWorld)Bukkit.getWorld(worldName)).getHandle(); + ChunkProviderServer cps = ws.getChunkProvider(); + + try { + for(EntityTracker et : cps.playerChunkMap.trackedEntities.values()) { + net.minecraft.server.v1_16_R1.Entity nmsEnt = (net.minecraft.server.v1_16_R1.Entity) trackerField.get(et); + if(nmsEnt instanceof EntityPlayer || nmsEnt instanceof EntityItemFrame || nmsEnt instanceof EntityEnderDragon) { + continue; + } + if(nmsEnt instanceof EntityArmorStand && nmsEnt.getBukkitEntity().getCustomName() != null) { + continue; + } + boolean remove = false; + if(et.trackedPlayers.size() == 0) { + remove = true; + } + else if(et.trackedPlayers.size() == 1) { + for(EntityPlayer ep : et.trackedPlayers) { + if(!ep.getBukkitEntity().isOnline()) { + remove = true; + } + } + if(!remove) { + continue; + } + } + if(remove) { + //System.out.println("untracked: " + nmsEnt.getBukkitEntity().getType().name()); + toRemove.add(nmsEnt.getId()); + removed++; + } + } + } catch (IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + + for(int id : toRemove) { + cps.playerChunkMap.trackedEntities.remove(id); + if(ConfigMain.isDisableTickUntracked()) { + EntityTickManager.getInstance().disableTicking(id, worldName); + } + } + + if(ConfigMain.isLogToConsole()) { + EntityTrackerFixer.plugin.getLogger().info("Untracked " + removed + " entities in " + worldName); + } + + //System.out.println("cache now contains " + UntrackedEntitiesCache.getInstance().getCache(worldName).size() + " entities"); + } + + public static boolean isRunning() { + return running; + } + +}