Support for 1.17 (and older versions of 1.16) + Things related to the /etf command.

+ Added support for **1.17**.
+ Added again support for 1.16, 1.16.1, 1.16.2 and 1.16.3.
+ Added error message if "/etf" command is used without arguments.
+ Added error message if "/etf" command arguments are not valid.
This commit is contained in:
cascaseno 2021-06-21 13:12:40 +02:00
parent d63d359cd5
commit e9073ec6f3
18 changed files with 992 additions and 22 deletions

View File

@ -1,6 +1,6 @@
name: EntityTrackerFixer
main: net.minemora.entitytrackerfixer.EntityTrackerFixer
version: 1.3.3
version: 1.3.4 (Unofficial)
softdepend: [Vault]
api-version: 1.14
author: Esmorall
@ -8,7 +8,7 @@ description: Untrack entities that are not used at all by the server.
commands:
entitytrackerfixer:
aliases: [etf]
description: Main Command of EntityTrackerFixer
description: Main command of EntityTrackerFixer.
usage: /etf
permissions:
entitytrackerfixer.*:

View File

@ -13,20 +13,22 @@ public class CommandETF implements CommandExecutor {
@Override
public boolean onCommand(CommandSender commandSender, Command command, String label, String[] args) {
if(args.length == 0) {
return true;
}
if(args[0].equalsIgnoreCase("reload")) {
if(commandSender instanceof Player) {
if (!VaultManager.hasPermission((Player) commandSender, "entitytrackerfixer.reload")) {
commandSender.sendMessage(ChatColor.RED + "You don't have permission to use that command.");
return true;
}
}
EntityTrackerFixer.plugin.reload();
commandSender.sendMessage(ChatColor.GREEN + "The config has been reloaded sucessfully!");
}
return true;
if(commandSender instanceof Player) {
if(!VaultManager.hasPermission((Player) commandSender, "entitytrackerfixer.reload")) {
commandSender.sendMessage(ChatColor.RED + "You don't have permission to use that command.");
return true;
}
}
if(args.length == 0) {
commandSender.sendMessage(ChatColor.RED + "No arguments provided. Available: " + ChatColor.GOLD + "/etf reload" + ChatColor.RED + ".");
return true;
}
if(args[0].equalsIgnoreCase("reload")) {
EntityTrackerFixer.plugin.reload();
commandSender.sendMessage(ChatColor.GREEN + "The config has been reloaded successfully!");
return true;
}
commandSender.sendMessage(ChatColor.RED + "Invalid argument. Available: " + ChatColor.GOLD + "/etf reload" + ChatColor.RED + ".");
return true;
}
}

View File

@ -0,0 +1,49 @@
package net.minemora.entitytrackerfixer.v1_16_R1;
import net.minecraft.server.v1_16_R1.ChunkProviderServer;
import net.minecraft.server.v1_16_R1.PlayerChunkMap;
import net.minemora.entitytrackerfixer.util.ReflectionUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;
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<net.minecraft.server.v1_16_R1.Entity> trackList) {
try {
for (net.minecraft.server.v1_16_R1.Entity entity : trackList) {
addEntityMethod.invoke(cps.playerChunkMap, entity);
}
} catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
public static void untrackEntities(ChunkProviderServer cps, Set<net.minecraft.server.v1_16_R1.Entity> 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();
}
}
}

View File

@ -0,0 +1,22 @@
package net.minemora.entitytrackerfixer.v1_16_R1;
import net.minemora.entitytrackerfixer.config.ConfigMain;
import net.minemora.entitytrackerfixer.nms.NMS;
import net.minemora.entitytrackerfixer.v1_16_R1.tasks.CheckTask;
import net.minemora.entitytrackerfixer.v1_16_R1.tasks.UntrackerTask;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitTask;
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());
}
}

View File

@ -0,0 +1,71 @@
package net.minemora.entitytrackerfixer.v1_16_R1.entityTick;
import net.minecraft.server.v1_16_R1.EntityInsentient;
import net.minecraft.server.v1_16_R1.MinecraftServer;
import net.minemora.entitytrackerfixer.EntityTrackerFixer;
import org.bukkit.craftbukkit.v1_16_R1.entity.CraftEntity;
import org.bukkit.entity.Entity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkLoadEvent;
import java.util.Set;
public class EntityTickManager implements Listener {
private static EntityTickManager instance;
private EntityTickManager() {
EntityTrackerFixer.plugin.getServer().getPluginManager().registerEvents(this, EntityTrackerFixer.plugin);
}
public static EntityTickManager getInstance() {
if (instance == null) {
instance = new EntityTickManager();
}
return instance;
}
public void disableTicking(net.minecraft.server.v1_16_R1.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<net.minecraft.server.v1_16_R1.Entity> entities) {
for (net.minecraft.server.v1_16_R1.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_R1.Entity nms = ((CraftEntity) entity).getHandle();
if (nms instanceof EntityInsentient) {
if (!((EntityInsentient) nms).aware) {
((EntityInsentient) nms).aware = true;
}
}
}
}
}

View File

@ -0,0 +1,62 @@
package net.minemora.entitytrackerfixer.v1_16_R1.tasks;
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;
import org.bukkit.Bukkit;
import org.bukkit.World;
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 java.util.HashSet;
import java.util.Set;
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<net.minecraft.server.v1_16_R1.Entity> 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_R1.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);
}
}
}

View File

@ -0,0 +1,114 @@
package net.minemora.entitytrackerfixer.v1_16_R1.tasks;
import net.minecraft.server.v1_16_R1.*;
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;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_16_R1.CraftWorld;
import org.bukkit.scheduler.BukkitRunnable;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
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();
}
}
public static boolean isRunning() {
return running;
}
@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<net.minecraft.server.v1_14_R1.Entity> toRemove = new HashSet<>();
Set<Integer> 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 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");
}
}

View File

@ -0,0 +1,49 @@
package net.minemora.entitytrackerfixer.v1_16_R2;
import net.minecraft.server.v1_16_R2.ChunkProviderServer;
import net.minecraft.server.v1_16_R2.PlayerChunkMap;
import net.minemora.entitytrackerfixer.util.ReflectionUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;
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<net.minecraft.server.v1_16_R2.Entity> 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<net.minecraft.server.v1_16_R2.Entity> 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();
}
}
}

View File

@ -0,0 +1,22 @@
package net.minemora.entitytrackerfixer.v1_16_R2;
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;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitTask;
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());
}
}

View File

@ -0,0 +1,71 @@
package net.minemora.entitytrackerfixer.v1_16_R2.entityTick;
import net.minecraft.server.v1_16_R2.EntityInsentient;
import net.minecraft.server.v1_16_R2.MinecraftServer;
import net.minemora.entitytrackerfixer.EntityTrackerFixer;
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 java.util.Set;
public class EntityTickManager implements Listener {
private static EntityTickManager instance;
private EntityTickManager() {
EntityTrackerFixer.plugin.getServer().getPluginManager().registerEvents(this, EntityTrackerFixer.plugin);
}
public static EntityTickManager getInstance() {
if (instance == null) {
instance = new EntityTickManager();
}
return instance;
}
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<net.minecraft.server.v1_16_R2.Entity> 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;
}
}
}
}
}

View File

@ -0,0 +1,62 @@
package net.minemora.entitytrackerfixer.v1_16_R2.tasks;
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;
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 java.util.HashSet;
import java.util.Set;
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<net.minecraft.server.v1_16_R2.Entity> 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);
}
}
}

View File

@ -0,0 +1,114 @@
package net.minemora.entitytrackerfixer.v1_16_R2.tasks;
import net.minecraft.server.v1_16_R2.*;
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;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_16_R2.CraftWorld;
import org.bukkit.scheduler.BukkitRunnable;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
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();
}
}
public static boolean isRunning() {
return running;
}
@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<net.minecraft.server.v1_14_R2.Entity> toRemove = new HashSet<>();
Set<Integer> 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");
}
}

View File

@ -0,0 +1,48 @@
package net.minemora.entitytrackerfixer.v1_17_R1;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;
import net.minecraft.server.level.ChunkProviderServer;
import net.minecraft.server.level.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.world.entity.Entity.class});
removeEntityMethod = ReflectionUtils.getPrivateMethod(PlayerChunkMap.class, "removeEntity",
new Class[] {net.minecraft.world.entity.Entity.class});
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) {
e.printStackTrace();
}
}
private NMSEntityTracker() {}
public static void trackEntities(ChunkProviderServer cps, Set<net.minecraft.world.entity.Entity> trackList) {
try {
for(net.minecraft.world.entity.Entity entity : trackList) {
addEntityMethod.invoke(cps.a, entity);
}
} catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
public static void untrackEntities(ChunkProviderServer cps, Set<net.minecraft.world.entity.Entity> untrackList) {
try {
for(net.minecraft.world.entity.Entity entity : untrackList) {
removeEntityMethod.invoke(cps.a, entity);
}
} catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,23 @@
package net.minemora.entitytrackerfixer.v1_17_R1;
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_17_R1.tasks.CheckTask;
import net.minemora.entitytrackerfixer.v1_17_R1.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());
}
}

View File

@ -0,0 +1,72 @@
package net.minemora.entitytrackerfixer.v1_17_R1.entityTick;
import java.util.Set;
import org.bukkit.craftbukkit.v1_17_R1.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.world.entity.EntityInsentient;
import net.minecraft.server.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.world.entity.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<net.minecraft.world.entity.Entity> entities) {
for(net.minecraft.world.entity.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.world.entity.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;
}
}

View File

@ -0,0 +1,64 @@
package net.minemora.entitytrackerfixer.v1_17_R1.tasks;
import java.util.HashSet;
import java.util.Set;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import net.minecraft.server.level.ChunkProviderServer;
import net.minecraft.server.level.WorldServer;
import net.minemora.entitytrackerfixer.config.ConfigMain;
import net.minemora.entitytrackerfixer.v1_17_R1.NMSEntityTracker;
import net.minemora.entitytrackerfixer.v1_17_R1.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<net.minecraft.world.entity.Entity> trackAgain = new HashSet<>();
int d = ConfigMain.getTrackingRange();
for(Player player : Bukkit.getWorld(worldName).getPlayers()) {
for(Entity ent : player.getNearbyEntities(d, d, d)) {
net.minecraft.world.entity.Entity nms = ((CraftEntity)ent).getHandle();
if(cps.a.G.containsKey(nms.getId()) || !nms.valid) {
continue;
}
trackAgain.add(nms);
}
}
NMSEntityTracker.trackEntities(cps, trackAgain);
if(ConfigMain.isDisableTickUntracked()) {
EntityTickManager.getInstance().enableTicking(trackAgain);
}
}
}

View File

@ -0,0 +1,124 @@
package net.minemora.entitytrackerfixer.v1_17_R1.tasks;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
import net.minecraft.server.network.ServerPlayerConnection;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
import org.bukkit.scheduler.BukkitRunnable;
import net.minecraft.server.level.ChunkProviderServer;
import net.minecraft.world.entity.decoration.EntityArmorStand;
import net.minecraft.world.entity.boss.EntityComplexPart;
import net.minecraft.world.entity.boss.enderdragon.EntityEnderDragon;
import net.minecraft.server.level.EntityPlayer;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.WorldServer;
import net.minecraft.server.level.PlayerChunkMap.EntityTracker;
import net.minemora.entitytrackerfixer.EntityTrackerFixer;
import net.minemora.entitytrackerfixer.config.ConfigMain;
import net.minemora.entitytrackerfixer.util.ReflectionUtils;
import net.minemora.entitytrackerfixer.v1_17_R1.entityTick.EntityTickManager;
public class UntrackerTask extends BukkitRunnable {
private static boolean running = false;
private static Field trackerField;
static {
try {
trackerField = ReflectionUtils.getClassPrivateField(EntityTracker.class, "c");
} 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<net.minecraft.server.v1_14_R2.Entity> toRemove = new HashSet<>();
Set<Integer> toRemove = new HashSet<>();
int removed = 0;
WorldServer ws = ((CraftWorld)Bukkit.getWorld(worldName)).getHandle();
ChunkProviderServer cps = ws.getChunkProvider();
try {
for(EntityTracker et : cps.a.G.values()) {
net.minecraft.world.entity.Entity nmsEnt = (net.minecraft.world.entity.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.f.size() == 0) {
remove = true;
}
else if(et.f.size() == 1) {
for(ServerPlayerConnection ep : et.f) {
if(!ep.d().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.a.G.remove(id);
if(ConfigMain.isDisableTickUntracked()) {
EntityTickManager.getInstance().disableTicking(ws.getEntities().a(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;
}
}

View File

@ -1,13 +1,14 @@
# EntityTrackerFixer
Untrack entities that are not used at all by the server
Untrack entities that are not used at all by the server.
Did you try everything to fix the lag on your server 1.14.4 or 1.15 but it is still lagging?
Did you try everything to fix the lag on your server 1.14.4 or 1.17 but it is still lagging?
Do timings indicate some random entity is causing lag?
This plugin may help you
This plugin may help you.
Available from 1.14.4 to 1.17.
How does it works?
Minecraft tracks a lot of entities, even if they are outside the tracking range of the player, that's a normal behavior but is a tps killer for 1.14.4 and 1.15 servers with more than 30 players. So what this plugin do is untrack those entities every configured ticks and track them again if the player is near.
Minecraft tracks a lot of entities, even if they are outside the tracking range of the player, that's a normal behavior but is a tps killer for 1.14.4 and 1.17 servers with more than 30 players. So what this plugin do is untrack those entities every configured ticks and track them again if the player is near.
Report any bug in the issues section.
@ -15,7 +16,7 @@ ________________________________________
Do you like this plugin?
feel free to donate buying something
Feel free to donate buying something
on my server store:
https://tienda.minemora.net/