diff --git a/EntityTrackerFixer/plugin.yml b/EntityTrackerFixer/plugin.yml index 87bcc46..5df67ac 100644 --- a/EntityTrackerFixer/plugin.yml +++ b/EntityTrackerFixer/plugin.yml @@ -1,6 +1,6 @@ name: EntityTrackerFixer main: net.minemora.entitytrackerfixer.EntityTrackerFixer -version: 1.3.1 +version: 1.3.2 softdepend: [Vault] api-version: 1.14 author: Esmorall diff --git a/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_14_R1/tasks/UntrackerTask.java b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_14_R1/tasks/UntrackerTask.java index 76289ce..403f65e 100644 --- a/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_14_R1/tasks/UntrackerTask.java +++ b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_14_R1/tasks/UntrackerTask.java @@ -35,7 +35,7 @@ public class UntrackerTask extends BukkitRunnable { } } - @SuppressWarnings("deprecation") + @SuppressWarnings({ "deprecation", "resource" }) @Override public void run() { if(MinecraftServer.getServer().recentTps[0] > ConfigMain.getMinTps()) { diff --git a/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_15_R1/tasks/UntrackerTask.java b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_15_R1/tasks/UntrackerTask.java index 8106372..7814cee 100644 --- a/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_15_R1/tasks/UntrackerTask.java +++ b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_15_R1/tasks/UntrackerTask.java @@ -35,7 +35,7 @@ public class UntrackerTask extends BukkitRunnable { } } - @SuppressWarnings("deprecation") + @SuppressWarnings({ "deprecation", "resource" }) @Override public void run() { if(MinecraftServer.getServer().recentTps[0] > ConfigMain.getMinTps()) { 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 index ea6a43b..5feea49 100644 --- a/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/tasks/UntrackerTask.java +++ b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R1/tasks/UntrackerTask.java @@ -36,7 +36,7 @@ public class UntrackerTask extends BukkitRunnable { } } - @SuppressWarnings("deprecation") + @SuppressWarnings({ "deprecation", "resource" }) @Override public void run() { if(MinecraftServer.getServer().recentTps[0] > ConfigMain.getMinTps()) { diff --git a/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R2/NMSEntityTracker.java b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R2/NMSEntityTracker.java new file mode 100644 index 0000000..6d8373f --- /dev/null +++ b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R2/NMSEntityTracker.java @@ -0,0 +1,48 @@ +package net.minemora.entitytrackerfixer.v1_16_R2; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Set; + +import net.minecraft.server.v1_16_R2.ChunkProviderServer; +import net.minecraft.server.v1_16_R2.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_R2.Entity.class}); + removeEntityMethod = ReflectionUtils.getPrivateMethod(PlayerChunkMap.class, "removeEntity", + new Class[] {net.minecraft.server.v1_16_R2.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_R2.Entity entity : trackList) { + 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_R2.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_R2/NMSHandler.java b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R2/NMSHandler.java new file mode 100644 index 0000000..3f1f1e2 --- /dev/null +++ b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R2/NMSHandler.java @@ -0,0 +1,23 @@ +package net.minemora.entitytrackerfixer.v1_16_R2; + +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitTask; + +import net.minemora.entitytrackerfixer.config.ConfigMain; +import net.minemora.entitytrackerfixer.nms.NMS; +import net.minemora.entitytrackerfixer.v1_16_R2.tasks.CheckTask; +import net.minemora.entitytrackerfixer.v1_16_R2.tasks.UntrackerTask; + +public class NMSHandler implements NMS { + + @Override + public BukkitTask startUntrackerTask(Plugin plugin) { + return new UntrackerTask().runTaskTimer(plugin, ConfigMain.getUntrackTicks(), ConfigMain.getUntrackTicks()); + } + + @Override + public BukkitTask startUCheckTask(Plugin plugin) { + return new CheckTask().runTaskTimer(plugin, ConfigMain.getUntrackTicks() + 1, ConfigMain.getCheckFrequency()); + } + +} diff --git a/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R2/entityTick/EntityTickManager.java b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R2/entityTick/EntityTickManager.java new file mode 100644 index 0000000..51fb9dc --- /dev/null +++ b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R2/entityTick/EntityTickManager.java @@ -0,0 +1,72 @@ +package net.minemora.entitytrackerfixer.v1_16_R2.entityTick; + +import java.util.Set; + +import org.bukkit.craftbukkit.v1_16_R2.entity.CraftEntity; +import org.bukkit.entity.Entity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.world.ChunkLoadEvent; + +import net.minecraft.server.v1_16_R2.EntityInsentient; +import net.minecraft.server.v1_16_R2.MinecraftServer; +import net.minemora.entitytrackerfixer.EntityTrackerFixer; + +public class EntityTickManager implements Listener { + + private static EntityTickManager instance; + + private EntityTickManager() { + EntityTrackerFixer.plugin.getServer().getPluginManager().registerEvents(this, EntityTrackerFixer.plugin); + } + + public void disableTicking(net.minecraft.server.v1_16_R2.Entity entity) { + if(entity == null) { + return; + } + if(!entity.valid) { + return; + } + entity.activatedTick = -2147483648L; + if(entity instanceof EntityInsentient) { + //System.out.println("disable tick for insentient entity currently aware is = " + ((EntityInsentient)entity).aware + " should be true"); + ((EntityInsentient)entity).aware = false; + } + } + + public void enableTicking(Set entities) { + for(net.minecraft.server.v1_16_R2.Entity entity : entities) { + if(entity == null) { + continue; + } + if(!entity.valid) { + continue; + } + entity.activatedTick = MinecraftServer.currentTick; + if(entity instanceof EntityInsentient) { + //System.out.println("enabling tick for insentient entity currently aware is = " + ((EntityInsentient)entity).aware + " should be false"); + ((EntityInsentient)entity).aware = true; + } + } + } + + @EventHandler + public void onChunkLoad(ChunkLoadEvent event) { + for(Entity entity : event.getChunk().getEntities()) { + net.minecraft.server.v1_16_R2.Entity nms = ((CraftEntity)entity).getHandle(); + if(nms instanceof EntityInsentient) { + if(!((EntityInsentient)nms).aware) { + ((EntityInsentient)nms).aware = true; + } + } + } + } + + public static EntityTickManager getInstance() { + if(instance == null) { + instance = new EntityTickManager(); + } + return instance; + } + +} diff --git a/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R2/tasks/CheckTask.java b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R2/tasks/CheckTask.java new file mode 100644 index 0000000..1124ba9 --- /dev/null +++ b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R2/tasks/CheckTask.java @@ -0,0 +1,64 @@ +package net.minemora.entitytrackerfixer.v1_16_R2.tasks; + +import java.util.HashSet; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_16_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R2.entity.CraftEntity; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import net.minecraft.server.v1_16_R2.ChunkProviderServer; +import net.minecraft.server.v1_16_R2.WorldServer; +import net.minemora.entitytrackerfixer.config.ConfigMain; +import net.minemora.entitytrackerfixer.v1_16_R2.NMSEntityTracker; +import net.minemora.entitytrackerfixer.v1_16_R2.entityTick.EntityTickManager; + +public class CheckTask extends BukkitRunnable { + + @Override + public void run() { + if(UntrackerTask.isRunning()) { + return; + } + if(ConfigMain.isEnableOnAllWorlds()) { + for(World world : Bukkit.getWorlds()) { + checkWorld(world.getName()); + } + } + else { + 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)) { + net.minecraft.server.v1_16_R2.Entity nms = ((CraftEntity)ent).getHandle(); + if(cps.playerChunkMap.trackedEntities.containsKey(nms.getId()) || !nms.valid) { + continue; + } + trackAgain.add(nms); + } + } + NMSEntityTracker.trackEntities(cps, trackAgain); + if(ConfigMain.isDisableTickUntracked()) { + EntityTickManager.getInstance().enableTicking(trackAgain); + } + } + +} \ No newline at end of file diff --git a/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R2/tasks/UntrackerTask.java b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R2/tasks/UntrackerTask.java new file mode 100644 index 0000000..f6133d9 --- /dev/null +++ b/EntityTrackerFixer/src/net/minemora/entitytrackerfixer/v1_16_R2/tasks/UntrackerTask.java @@ -0,0 +1,123 @@ +package net.minemora.entitytrackerfixer.v1_16_R2.tasks; + +import java.lang.reflect.Field; +import java.util.HashSet; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_16_R2.CraftWorld; +import org.bukkit.scheduler.BukkitRunnable; + +import net.minecraft.server.v1_16_R2.ChunkProviderServer; +import net.minecraft.server.v1_16_R2.EntityArmorStand; +import net.minecraft.server.v1_16_R2.EntityComplexPart; +import net.minecraft.server.v1_16_R2.EntityEnderDragon; +import net.minecraft.server.v1_16_R2.EntityPlayer; +import net.minecraft.server.v1_16_R2.MinecraftServer; +import net.minecraft.server.v1_16_R2.WorldServer; +import net.minecraft.server.v1_16_R2.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_R2.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", "resource" }) + @Override + public void run() { + if(MinecraftServer.getServer().recentTps[0] > ConfigMain.getMinTps()) { + return; + } + running = true; + if(ConfigMain.isEnableOnAllWorlds()) { + for(World world : Bukkit.getWorlds()) { + untrackProcess(world.getName()); + } + } + else { + 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_R2.Entity nmsEnt = (net.minecraft.server.v1_16_R2.Entity) trackerField.get(et); + if(nmsEnt instanceof EntityPlayer || nmsEnt instanceof EntityEnderDragon || nmsEnt instanceof EntityComplexPart) { + 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(ws.entitiesById.get(id)); + } + } + + if(ConfigMain.isLogToConsole()) { + if(removed > 0) { + 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; + } + +}