diff --git a/gradle.properties b/gradle.properties index 6f80cab..ab25d13 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ apiVersion=1.21.11 release=2 # 0 for skip release, 1 for pre-release, 2 for release -luminolRef=185a0de504efb71744801ba5c71942a3d566f6fe +luminolRef=80dfcadee21db699a0a653e0d7998ef5341d7213 org.gradle.configuration-cache=true org.gradle.caching=true diff --git a/lophine-server/src/main/java/fun/bm/lophine/config/modules/experiment/GlobalEntitiesCounter.java b/lophine-server/src/main/java/fun/bm/lophine/config/modules/experiment/GlobalEntitiesCounter.java index a2c028a..8cc83f8 100644 --- a/lophine-server/src/main/java/fun/bm/lophine/config/modules/experiment/GlobalEntitiesCounter.java +++ b/lophine-server/src/main/java/fun/bm/lophine/config/modules/experiment/GlobalEntitiesCounter.java @@ -17,4 +17,11 @@ public class GlobalEntitiesCounter implements IConfigModule { @HotReloadUnsupported @ConfigInfo(name = "async", comments = "Enable Asynchronous(maybe cause bugs)") public static boolean async = false; + + @HotReloadUnsupported + @ConfigInfo(name = "always_count", comments = """ + Always count entities. + If you want to count entities loaded by chunk loader, + you must to enabled it.""") + public static boolean alwaysCount = false; } diff --git a/lophine-server/src/main/java/fun/bm/lophine/utils/EntitiesCounterUtil.java b/lophine-server/src/main/java/fun/bm/lophine/utils/EntitiesCounterUtil.java index 5bc0c75..d245906 100644 --- a/lophine-server/src/main/java/fun/bm/lophine/utils/EntitiesCounterUtil.java +++ b/lophine-server/src/main/java/fun/bm/lophine/utils/EntitiesCounterUtil.java @@ -19,6 +19,7 @@ import org.bukkit.event.entity.CreatureSpawnEvent; import org.jetbrains.annotations.Nullable; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.CompletableFuture; @@ -35,8 +36,11 @@ public class EntitiesCounterUtil { private static int lastUsedId = 0; + private static final int CLEANUP_INTERVAL = 200; // each 200 ids used + public static int generateUniqueId() { synchronized (UniqueIds) { + if (lastUsedId % CLEANUP_INTERVAL == 0) runCleanUp(); int id = lastUsedId; while (UniqueIds.contains(id)) { id++; @@ -60,6 +64,21 @@ public class EntitiesCounterUtil { } } + private static void runCleanUp() { + int i = lastUsedId; + Set logged = new HashSet<>(); + synchronized (globalLoadedEntities) { + for (WeakHashMap> collection : globalLoadedEntities.values()) { + logged.addAll(collection.keySet()); + } + } + + for (int num : Set.copyOf(UniqueIds)) { + if (logged.contains(num) || num > i) continue; + UniqueIds.remove(num); + } + } + public static void addDataToLoaded(ServerLevel level, ReferenceList data, int uniqueId) { WeakHashMap> data0 = globalLoadedEntities.computeIfAbsent(level, k -> new WeakHashMap<>()); if (data0.containsKey(uniqueId)) return; @@ -87,10 +106,19 @@ public class EntitiesCounterUtil { public static void tick(ServerLevel level) { Runnable task = () -> { - WeakHashMap> data0 = globalLoadedEntities.get(level); + WeakHashMap> data0; + synchronized (globalLoadedEntities) { + data0 = globalLoadedEntities.get(level); + if (data0 == null) return; + } Object2IntOpenHashMap map = new Object2IntOpenHashMap<>(); - for (ReferenceList data : data0.values()) { - for (Entity entity : GlobalEntitiesCounter.enabled ? data.copy() : data) { + List> snapshot; + synchronized (data0) { + snapshot = List.copyOf(data0.values()); + } + + for (ReferenceList data : snapshot) { + for (Entity entity : GlobalEntitiesCounter.async ? data.copy() : data) { if (entity == null || entity.isRemoved() || !entity.isAlive()) continue; // Lophine start - Copy from net/minecraft/world/level/NaturalSpawner MobCategory category = entity.getType().getCategory();