diff options
7 files changed, 176 insertions, 122 deletions
diff --git a/spark-fabric/src/main/java/me/lucko/spark/fabric/FabricWorldInfoProvider.java b/spark-fabric/src/main/java/me/lucko/spark/fabric/FabricWorldInfoProvider.java index d7dc7a7..eae9c86 100644 --- a/spark-fabric/src/main/java/me/lucko/spark/fabric/FabricWorldInfoProvider.java +++ b/spark-fabric/src/main/java/me/lucko/spark/fabric/FabricWorldInfoProvider.java @@ -20,8 +20,8 @@ package me.lucko.spark.fabric; -import it.unimi.dsi.fastutil.longs.LongIterator; -import it.unimi.dsi.fastutil.longs.LongSet; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import java.lang.reflect.Method; import me.lucko.spark.common.platform.world.AbstractChunkInfo; import me.lucko.spark.common.platform.world.CountMap; import me.lucko.spark.common.platform.world.WorldInfoProvider; @@ -29,6 +29,8 @@ import me.lucko.spark.fabric.mixin.ClientEntityManagerAccessor; import me.lucko.spark.fabric.mixin.ClientWorldAccessor; import me.lucko.spark.fabric.mixin.ServerEntityManagerAccessor; import me.lucko.spark.fabric.mixin.ServerWorldAccessor; +import me.lucko.spark.fabric.mixin.WorldAccessor; +import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.MinecraftClient; import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.Entity; @@ -40,30 +42,13 @@ import net.minecraft.util.math.ChunkPos; import net.minecraft.world.GameRules; import net.minecraft.world.entity.ClientEntityManager; import net.minecraft.world.entity.EntityIndex; -import net.minecraft.world.entity.EntityTrackingSection; -import net.minecraft.world.entity.SectionedEntityCache; +import net.minecraft.world.entity.EntityLookup; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.stream.Stream; public abstract class FabricWorldInfoProvider implements WorldInfoProvider { - protected List<FabricChunkInfo> getChunksFromCache(SectionedEntityCache<Entity> cache) { - LongSet loadedChunks = cache.getChunkPositions(); - List<FabricChunkInfo> list = new ArrayList<>(loadedChunks.size()); - - for (LongIterator iterator = loadedChunks.iterator(); iterator.hasNext(); ) { - long chunkPos = iterator.nextLong(); - Stream<EntityTrackingSection<Entity>> sections = cache.getTrackingSections(chunkPos); - - list.add(new FabricChunkInfo(chunkPos, sections)); - } - - return list; - } - public static final class Server extends FabricWorldInfoProvider { private final MinecraftServer server; @@ -78,10 +63,15 @@ public abstract class FabricWorldInfoProvider implements WorldInfoProvider { int chunks = 0; for (ServerWorld world : this.server.getWorlds()) { - ServerEntityManager<Entity> entityManager = ((ServerWorldAccessor) world).getEntityManager(); - EntityIndex<?> entityIndex = ((ServerEntityManagerAccessor) entityManager).getIndex(); - entities += entityIndex.size(); + if (FabricLoader.getInstance().isModLoaded("moonrise")) { + entities += MoonriseMethods.getEntityCount(((WorldAccessor) world).spark$getEntityLookup()); + } else { + ServerEntityManager<Entity> entityManager = ((ServerWorldAccessor) world).getEntityManager(); + EntityIndex<?> entityIndex = ((ServerEntityManagerAccessor) entityManager).getIndex(); + entities += entityIndex.size(); + } + chunks += world.getChunkManager().getLoadedChunkCount(); } @@ -93,11 +83,15 @@ public abstract class FabricWorldInfoProvider implements WorldInfoProvider { ChunksResult<FabricChunkInfo> data = new ChunksResult<>(); for (ServerWorld world : this.server.getWorlds()) { - ServerEntityManager<Entity> entityManager = ((ServerWorldAccessor) world).getEntityManager(); - SectionedEntityCache<Entity> cache = ((ServerEntityManagerAccessor) entityManager).getCache(); + Long2ObjectOpenHashMap<FabricChunkInfo> worldInfos = new Long2ObjectOpenHashMap<>(); + + for (Entity entity : ((WorldAccessor) world).spark$getEntityLookup().iterate()) { + FabricChunkInfo info = worldInfos.computeIfAbsent( + entity.getChunkPos().toLong(), FabricChunkInfo::new); + info.entityCounts.increment(entity.getType()); + } - List<FabricChunkInfo> list = getChunksFromCache(cache); - data.put(world.getRegistryKey().getValue().getPath(), list); + data.put(world.getRegistryKey().getValue().getPath(), List.copyOf(worldInfos.values())); } return data; @@ -140,10 +134,16 @@ public abstract class FabricWorldInfoProvider implements WorldInfoProvider { return null; } - ClientEntityManager<Entity> entityManager = ((ClientWorldAccessor) world).getEntityManager(); - EntityIndex<?> entityIndex = ((ClientEntityManagerAccessor) entityManager).getIndex(); + int entities; + + if (FabricLoader.getInstance().isModLoaded("moonrise")) { + entities = MoonriseMethods.getEntityCount(((WorldAccessor) world).spark$getEntityLookup()); + } else { + ClientEntityManager<Entity> entityManager = ((ClientWorldAccessor) world).getEntityManager(); + EntityIndex<?> entityIndex = ((ClientEntityManagerAccessor) entityManager).getIndex(); + entities = entityIndex.size(); + } - int entities = entityIndex.size(); int chunks = world.getChunkManager().getLoadedChunkCount(); return new CountsResult(-1, entities, -1, chunks); @@ -158,11 +158,14 @@ public abstract class FabricWorldInfoProvider implements WorldInfoProvider { ChunksResult<FabricChunkInfo> data = new ChunksResult<>(); - ClientEntityManager<Entity> entityManager = ((ClientWorldAccessor) world).getEntityManager(); - SectionedEntityCache<Entity> cache = ((ClientEntityManagerAccessor) entityManager).getCache(); + Long2ObjectOpenHashMap<FabricChunkInfo> worldInfos = new Long2ObjectOpenHashMap<>(); - List<FabricChunkInfo> list = getChunksFromCache(cache); - data.put(world.getRegistryKey().getValue().getPath(), list); + for (Entity entity : ((WorldAccessor) world).spark$getEntityLookup().iterate()) { + FabricChunkInfo info = worldInfos.computeIfAbsent(entity.getChunkPos().toLong(), FabricChunkInfo::new); + info.entityCounts.increment(entity.getType()); + } + + data.put(world.getRegistryKey().getValue().getPath(), List.copyOf(worldInfos.values())); return data; } @@ -197,17 +200,10 @@ public abstract class FabricWorldInfoProvider implements WorldInfoProvider { static final class FabricChunkInfo extends AbstractChunkInfo<EntityType<?>> { private final CountMap<EntityType<?>> entityCounts; - FabricChunkInfo(long chunkPos, Stream<EntityTrackingSection<Entity>> entities) { + FabricChunkInfo(long chunkPos) { super(ChunkPos.getPackedX(chunkPos), ChunkPos.getPackedZ(chunkPos)); this.entityCounts = new CountMap.Simple<>(new HashMap<>()); - entities.forEach(section -> { - if (section.getStatus().shouldTrack()) { - section.stream().forEach(entity -> - this.entityCounts.increment(entity.getType()) - ); - } - }); } @Override @@ -221,5 +217,28 @@ public abstract class FabricWorldInfoProvider implements WorldInfoProvider { } } + private static final class MoonriseMethods { + private static Method getEntityCount; + + private static Method getEntityCountMethod(EntityLookup<Entity> getter) { + if (getEntityCount == null) { + try { + getEntityCount = getter.getClass().getMethod("getEntityCount"); + } catch (final ReflectiveOperationException e) { + throw new RuntimeException("Cannot find Moonrise getEntityCount method", e); + } + } + return getEntityCount; + } + + private static int getEntityCount(EntityLookup<Entity> getter) { + try { + return (int) getEntityCountMethod(getter).invoke(getter); + } catch (final ReflectiveOperationException e) { + throw new RuntimeException("Failed to invoke Moonrise getEntityCount method", e); + } + } + } + } diff --git a/spark-fabric/src/main/java/me/lucko/spark/fabric/mixin/WorldAccessor.java b/spark-fabric/src/main/java/me/lucko/spark/fabric/mixin/WorldAccessor.java new file mode 100644 index 0000000..e50dc20 --- /dev/null +++ b/spark-fabric/src/main/java/me/lucko/spark/fabric/mixin/WorldAccessor.java @@ -0,0 +1,35 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) <luck@lucko.me> + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package me.lucko.spark.fabric.mixin; + +import net.minecraft.entity.Entity; +import net.minecraft.world.World; +import net.minecraft.world.entity.EntityLookup; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(World.class) +public interface WorldAccessor { + + @Invoker(value = "getEntityLookup") + EntityLookup<Entity> spark$getEntityLookup(); + +} diff --git a/spark-fabric/src/main/resources/spark.mixins.json b/spark-fabric/src/main/resources/spark.mixins.json index 27676a6..a8e280c 100644 --- a/spark-fabric/src/main/resources/spark.mixins.json +++ b/spark-fabric/src/main/resources/spark.mixins.json @@ -8,7 +8,8 @@ ], "mixins": [ "ServerEntityManagerAccessor", - "ServerWorldAccessor" + "ServerWorldAccessor", + "WorldAccessor" ], "plugin": "me.lucko.spark.fabric.plugin.FabricSparkMixinPlugin" }
\ No newline at end of file diff --git a/spark-forge/src/main/java/me/lucko/spark/forge/ForgeWorldInfoProvider.java b/spark-forge/src/main/java/me/lucko/spark/forge/ForgeWorldInfoProvider.java index 286605a..0d1724e 100644 --- a/spark-forge/src/main/java/me/lucko/spark/forge/ForgeWorldInfoProvider.java +++ b/spark-forge/src/main/java/me/lucko/spark/forge/ForgeWorldInfoProvider.java @@ -20,8 +20,7 @@ package me.lucko.spark.forge; -import it.unimi.dsi.fastutil.longs.LongIterator; -import it.unimi.dsi.fastutil.longs.LongSet; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import me.lucko.spark.common.platform.world.AbstractChunkInfo; import me.lucko.spark.common.platform.world.CountMap; import me.lucko.spark.common.platform.world.WorldInfoProvider; @@ -34,32 +33,14 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.entity.EntityLookup; -import net.minecraft.world.level.entity.EntitySection; -import net.minecraft.world.level.entity.EntitySectionStorage; import net.minecraft.world.level.entity.PersistentEntitySectionManager; import net.minecraft.world.level.entity.TransientEntitySectionManager; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.stream.Stream; public abstract class ForgeWorldInfoProvider implements WorldInfoProvider { - protected List<ForgeChunkInfo> getChunksFromCache(EntitySectionStorage<Entity> cache) { - LongSet loadedChunks = cache.getAllChunksWithExistingSections(); - List<ForgeChunkInfo> list = new ArrayList<>(loadedChunks.size()); - - for (LongIterator iterator = loadedChunks.iterator(); iterator.hasNext(); ) { - long chunkPos = iterator.nextLong(); - Stream<EntitySection<Entity>> sections = cache.getExistingSectionsInChunk(chunkPos); - - list.add(new ForgeChunkInfo(chunkPos, sections)); - } - - return list; - } - public static final class Server extends ForgeWorldInfoProvider { private final MinecraftServer server; @@ -89,11 +70,15 @@ public abstract class ForgeWorldInfoProvider implements WorldInfoProvider { ChunksResult<ForgeChunkInfo> data = new ChunksResult<>(); for (ServerLevel level : this.server.getAllLevels()) { - PersistentEntitySectionManager<Entity> entityManager = level.entityManager; - EntitySectionStorage<Entity> cache = entityManager.sectionStorage; + Long2ObjectOpenHashMap<ForgeChunkInfo> levelInfos = new Long2ObjectOpenHashMap<>(); - List<ForgeChunkInfo> list = getChunksFromCache(cache); - data.put(level.dimension().location().getPath(), list); + for (Entity entity : level.getEntities().getAll()) { + ForgeChunkInfo info = levelInfos.computeIfAbsent( + entity.chunkPosition().toLong(), ForgeChunkInfo::new); + info.entityCounts.increment(entity.getType()); + } + + data.put(level.dimension().location().getPath(), List.copyOf(levelInfos.values())); } return data; @@ -155,11 +140,14 @@ public abstract class ForgeWorldInfoProvider implements WorldInfoProvider { ChunksResult<ForgeChunkInfo> data = new ChunksResult<>(); - TransientEntitySectionManager<Entity> entityManager = level.entityStorage; - EntitySectionStorage<Entity> cache = entityManager.sectionStorage; + Long2ObjectOpenHashMap<ForgeChunkInfo> levelInfos = new Long2ObjectOpenHashMap<>(); - List<ForgeChunkInfo> list = getChunksFromCache(cache); - data.put(level.dimension().location().getPath(), list); + for (Entity entity : level.getEntities().getAll()) { + ForgeChunkInfo info = levelInfos.computeIfAbsent(entity.chunkPosition().toLong(), ForgeChunkInfo::new); + info.entityCounts.increment(entity.getType()); + } + + data.put(level.dimension().location().getPath(), List.copyOf(levelInfos.values())); return data; } @@ -191,20 +179,13 @@ public abstract class ForgeWorldInfoProvider implements WorldInfoProvider { } } - static final class ForgeChunkInfo extends AbstractChunkInfo<EntityType<?>> { + public static final class ForgeChunkInfo extends AbstractChunkInfo<EntityType<?>> { private final CountMap<EntityType<?>> entityCounts; - ForgeChunkInfo(long chunkPos, Stream<EntitySection<Entity>> entities) { + ForgeChunkInfo(long chunkPos) { super(ChunkPos.getX(chunkPos), ChunkPos.getZ(chunkPos)); this.entityCounts = new CountMap.Simple<>(new HashMap<>()); - entities.forEach(section -> { - if (section.getStatus().isAccessible()) { - section.getEntities().forEach(entity -> - this.entityCounts.increment(entity.getType()) - ); - } - }); } @Override diff --git a/spark-forge/src/main/resources/META-INF/accesstransformer.cfg b/spark-forge/src/main/resources/META-INF/accesstransformer.cfg index 2699a0e..3772274 100644 --- a/spark-forge/src/main/resources/META-INF/accesstransformer.cfg +++ b/spark-forge/src/main/resources/META-INF/accesstransformer.cfg @@ -5,3 +5,4 @@ public net.minecraft.client.multiplayer.ClientLevel f_171631_ # entityStorage public net.minecraft.world.level.entity.TransientEntitySectionManager f_157638_ # sectionStorage public net.minecraft.world.level.entity.TransientEntitySectionManager f_157637_ # entityStorage public net.minecraft.client.Minecraft f_91018_ # gameThread +public net.minecraft.client.multiplayer.ClientLevel m_142646_()Lnet/minecraft/world/level/entity/LevelEntityGetter; # getEntities diff --git a/spark-neoforge/src/main/java/me/lucko/spark/neoforge/NeoForgeWorldInfoProvider.java b/spark-neoforge/src/main/java/me/lucko/spark/neoforge/NeoForgeWorldInfoProvider.java index fe6a6c8..d172ecf 100644 --- a/spark-neoforge/src/main/java/me/lucko/spark/neoforge/NeoForgeWorldInfoProvider.java +++ b/spark-neoforge/src/main/java/me/lucko/spark/neoforge/NeoForgeWorldInfoProvider.java @@ -20,8 +20,8 @@ package me.lucko.spark.neoforge; -import it.unimi.dsi.fastutil.longs.LongIterator; -import it.unimi.dsi.fastutil.longs.LongSet; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import java.lang.reflect.Method; import me.lucko.spark.common.platform.world.AbstractChunkInfo; import me.lucko.spark.common.platform.world.CountMap; import me.lucko.spark.common.platform.world.WorldInfoProvider; @@ -34,32 +34,16 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.entity.EntityLookup; -import net.minecraft.world.level.entity.EntitySection; -import net.minecraft.world.level.entity.EntitySectionStorage; +import net.minecraft.world.level.entity.LevelEntityGetter; import net.minecraft.world.level.entity.PersistentEntitySectionManager; import net.minecraft.world.level.entity.TransientEntitySectionManager; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.stream.Stream; +import net.neoforged.fml.ModList; public abstract class NeoForgeWorldInfoProvider implements WorldInfoProvider { - protected List<ForgeChunkInfo> getChunksFromCache(EntitySectionStorage<Entity> cache) { - LongSet loadedChunks = cache.getAllChunksWithExistingSections(); - List<ForgeChunkInfo> list = new ArrayList<>(loadedChunks.size()); - - for (LongIterator iterator = loadedChunks.iterator(); iterator.hasNext(); ) { - long chunkPos = iterator.nextLong(); - Stream<EntitySection<Entity>> sections = cache.getExistingSectionsInChunk(chunkPos); - - list.add(new ForgeChunkInfo(chunkPos, sections)); - } - - return list; - } - public static final class Server extends NeoForgeWorldInfoProvider { private final MinecraftServer server; @@ -74,10 +58,15 @@ public abstract class NeoForgeWorldInfoProvider implements WorldInfoProvider { int chunks = 0; for (ServerLevel level : this.server.getAllLevels()) { - PersistentEntitySectionManager<Entity> entityManager = level.entityManager; - EntityLookup<Entity> entityIndex = entityManager.visibleEntityStorage; - entities += entityIndex.count(); + if (ModList.get().isLoaded("moonrise")) { + entities += MoonriseMethods.getEntityCount(level.getEntities()); + } else { + PersistentEntitySectionManager<Entity> entityManager = level.entityManager; + EntityLookup<Entity> entityIndex = entityManager.visibleEntityStorage; + entities += entityIndex.count(); + } + chunks += level.getChunkSource().getLoadedChunksCount(); } @@ -89,11 +78,15 @@ public abstract class NeoForgeWorldInfoProvider implements WorldInfoProvider { ChunksResult<ForgeChunkInfo> data = new ChunksResult<>(); for (ServerLevel level : this.server.getAllLevels()) { - PersistentEntitySectionManager<Entity> entityManager = level.entityManager; - EntitySectionStorage<Entity> cache = entityManager.sectionStorage; + Long2ObjectOpenHashMap<ForgeChunkInfo> levelInfos = new Long2ObjectOpenHashMap<>(); + + for (Entity entity : level.getEntities().getAll()) { + ForgeChunkInfo info = levelInfos.computeIfAbsent( + entity.chunkPosition().toLong(), ForgeChunkInfo::new); + info.entityCounts.increment(entity.getType()); + } - List<ForgeChunkInfo> list = getChunksFromCache(cache); - data.put(level.dimension().location().getPath(), list); + data.put(level.dimension().location().getPath(), List.copyOf(levelInfos.values())); } return data; @@ -137,10 +130,15 @@ public abstract class NeoForgeWorldInfoProvider implements WorldInfoProvider { return null; } - TransientEntitySectionManager<Entity> entityManager = level.entityStorage; - EntityLookup<Entity> entityIndex = entityManager.entityStorage; + int entities; + if (ModList.get().isLoaded("moonrise")) { + entities = MoonriseMethods.getEntityCount(level.getEntities()); + } else { + TransientEntitySectionManager<Entity> entityManager = level.entityStorage; + EntityLookup<Entity> entityIndex = entityManager.entityStorage; + entities = entityIndex.count(); + } - int entities = entityIndex.count(); int chunks = level.getChunkSource().getLoadedChunksCount(); return new CountsResult(-1, entities, -1, chunks); @@ -155,11 +153,14 @@ public abstract class NeoForgeWorldInfoProvider implements WorldInfoProvider { ChunksResult<ForgeChunkInfo> data = new ChunksResult<>(); - TransientEntitySectionManager<Entity> entityManager = level.entityStorage; - EntitySectionStorage<Entity> cache = entityManager.sectionStorage; + Long2ObjectOpenHashMap<ForgeChunkInfo> levelInfos = new Long2ObjectOpenHashMap<>(); - List<ForgeChunkInfo> list = getChunksFromCache(cache); - data.put(level.dimension().location().getPath(), list); + for (Entity entity : level.getEntities().getAll()) { + ForgeChunkInfo info = levelInfos.computeIfAbsent(entity.chunkPosition().toLong(), ForgeChunkInfo::new); + info.entityCounts.increment(entity.getType()); + } + + data.put(level.dimension().location().getPath(), List.copyOf(levelInfos.values())); return data; } @@ -194,17 +195,10 @@ public abstract class NeoForgeWorldInfoProvider implements WorldInfoProvider { public static final class ForgeChunkInfo extends AbstractChunkInfo<EntityType<?>> { private final CountMap<EntityType<?>> entityCounts; - ForgeChunkInfo(long chunkPos, Stream<EntitySection<Entity>> entities) { + ForgeChunkInfo(long chunkPos) { super(ChunkPos.getX(chunkPos), ChunkPos.getZ(chunkPos)); this.entityCounts = new CountMap.Simple<>(new HashMap<>()); - entities.forEach(section -> { - if (section.getStatus().isAccessible()) { - section.getEntities().forEach(entity -> - this.entityCounts.increment(entity.getType()) - ); - } - }); } @Override @@ -218,5 +212,27 @@ public abstract class NeoForgeWorldInfoProvider implements WorldInfoProvider { } } + private static final class MoonriseMethods { + private static Method getEntityCount; + + private static Method getEntityCountMethod(LevelEntityGetter<Entity> getter) { + if (getEntityCount == null) { + try { + getEntityCount = getter.getClass().getMethod("getEntityCount"); + } catch (final ReflectiveOperationException e) { + throw new RuntimeException("Cannot find Moonrise getEntityCount method", e); + } + } + return getEntityCount; + } + + private static int getEntityCount(LevelEntityGetter<Entity> getter) { + try { + return (int) getEntityCountMethod(getter).invoke(getter); + } catch (final ReflectiveOperationException e) { + throw new RuntimeException("Failed to invoke Moonrise getEntityCount method", e); + } + } + } } diff --git a/spark-neoforge/src/main/resources/META-INF/accesstransformer.cfg b/spark-neoforge/src/main/resources/META-INF/accesstransformer.cfg index 43d14fc..80f3f6e 100644 --- a/spark-neoforge/src/main/resources/META-INF/accesstransformer.cfg +++ b/spark-neoforge/src/main/resources/META-INF/accesstransformer.cfg @@ -2,6 +2,7 @@ public net.minecraft.server.level.ServerLevel entityManager # entityManager public net.minecraft.world.level.entity.PersistentEntitySectionManager sectionStorage # sectionStorage public net.minecraft.world.level.entity.PersistentEntitySectionManager visibleEntityStorage # visibleEntityStorage public net.minecraft.client.multiplayer.ClientLevel entityStorage # entityStorage +public net.minecraft.client.multiplayer.ClientLevel getEntities()Lnet/minecraft/world/level/entity/LevelEntityGetter; # getEntities public net.minecraft.world.level.entity.TransientEntitySectionManager sectionStorage # sectionStorage public net.minecraft.world.level.entity.TransientEntitySectionManager entityStorage # entityStorage public net.minecraft.client.Minecraft gameThread # gameThread |