From fbe37017c59f9c5b8e088ad657ca7c4a13a2f2c5 Mon Sep 17 00:00:00 2001 From: altrisi Date: Fri, 28 Jul 2023 20:57:48 +0200 Subject: [PATCH] Refactor spawn reporting (#1519) Not all that could be done for now --- src/main/java/carpet/CarpetServer.java | 2 +- .../java/carpet/commands/SpawnCommand.java | 65 ++-- .../carpet/mixins/NaturalSpawnerMixin.java | 19 +- .../carpet/mixins/ServerChunkCacheMixin.java | 35 +- .../mixins/ServerLevel_scarpetMixin.java | 4 +- src/main/java/carpet/utils/SpawnReporter.java | 311 +++++++++--------- src/main/java/carpet/utils/WoolTool.java | 2 +- 7 files changed, 203 insertions(+), 235 deletions(-) diff --git a/src/main/java/carpet/CarpetServer.java b/src/main/java/carpet/CarpetServer.java index b2e52487..90897bbd 100644 --- a/src/main/java/carpet/CarpetServer.java +++ b/src/main/java/carpet/CarpetServer.java @@ -87,7 +87,7 @@ public class CarpetServer // static for now - easier to handle all around the co { CarpetServer.minecraft_server = server; // shoudl not be needed - that bit needs refactoring, but not now. - SpawnReporter.reset_spawn_stats(server, true); + SpawnReporter.resetSpawnStats(server, true); forEachManager(sm -> sm.attachServer(server)); extensions.forEach(e -> e.onServerLoaded(server)); diff --git a/src/main/java/carpet/commands/SpawnCommand.java b/src/main/java/carpet/commands/SpawnCommand.java index b3edf1c5..be13686d 100644 --- a/src/main/java/carpet/commands/SpawnCommand.java +++ b/src/main/java/carpet/commands/SpawnCommand.java @@ -28,6 +28,7 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.MobCategory; import net.minecraft.world.item.DyeColor; +import net.minecraft.world.level.levelgen.structure.BoundingBox; import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger; import static com.mojang.brigadier.arguments.IntegerArgumentType.integer; @@ -52,17 +53,18 @@ public class SpawnCommand then(literal("tracking"). executes( (c) -> printTrackingReport(c.getSource())). then(literal("start"). - executes( (c) -> startTracking(c.getSource(), null, null)). + executes( (c) -> startTracking(c.getSource(), null)). then(argument("from", BlockPosArgument.blockPos()). then(argument("to", BlockPosArgument.blockPos()). executes( (c) -> startTracking( c.getSource(), - BlockPosArgument.getSpawnablePos(c, "from"), - BlockPosArgument.getSpawnablePos(c, "to")))))). + BoundingBox.fromCorners( + BlockPosArgument.getSpawnablePos(c, "from"), + BlockPosArgument.getSpawnablePos(c, "to"))))))). then(literal("stop"). executes( (c) -> stopTracking(c.getSource()))). then(argument("type", word()). - suggests( (c, b) -> suggest(Arrays.stream(MobCategory.values()).map(MobCategory::getName),b)). + suggests( (c, b) -> suggest(Arrays.stream(SpawnReporter.cachedMobCategories()).map(MobCategory::getName),b)). executes( (c) -> recentSpawnsForType(c.getSource(), getString(c, "type"))))). then(literal("test"). executes( (c)-> runTest(c.getSource(), 72000, null)). @@ -85,7 +87,7 @@ public class SpawnCommand then(literal("reset"). executes( (c) -> resetSpawnRates(c.getSource()))). then(argument("type", word()). - suggests( (c, b) -> suggest(Arrays.stream(MobCategory.values()).map(MobCategory::getName),b)). + suggests( (c, b) -> suggest(Arrays.stream(SpawnReporter.cachedMobCategories()).map(MobCategory::getName),b)). then(argument("rounds", integer(0)). suggests( (c, b) -> suggest(new String[]{"1"},b)). executes( (c) -> setSpawnRates( @@ -102,20 +104,20 @@ public class SpawnCommand then(literal("entities"). executes( (c) -> generalMobcaps(c.getSource()) ). then(argument("type", string()). - suggests( (c, b)->suggest(Arrays.stream(MobCategory.values()).map(MobCategory::getName), b)). + suggests( (c, b)->suggest(Arrays.stream(SpawnReporter.cachedMobCategories()).map(MobCategory::getName), b)). executes( (c) -> listEntitiesOfType(c.getSource(), getString(c, "type"), false)). then(literal("all").executes( (c) -> listEntitiesOfType(c.getSource(), getString(c, "type"), true))))); dispatcher.register(literalargumentbuilder); } - private static final Map MOB_CATEGORY_MAP = Arrays.stream(MobCategory.values()).collect(Collectors.toMap(MobCategory::getName, Function.identity())); + private static final Map MOB_CATEGORY_MAP = Arrays.stream(SpawnReporter.cachedMobCategories()).collect(Collectors.toMap(MobCategory::getName, Function.identity())); private static MobCategory getCategory(String string) throws CommandSyntaxException { - if (!Arrays.stream(MobCategory.values()).map(MobCategory::getName).collect(Collectors.toSet()).contains(string)) + if (!Arrays.stream(SpawnReporter.cachedMobCategories()).map(MobCategory::getName).collect(Collectors.toSet()).contains(string)) { - throw new SimpleCommandExceptionType(Messenger.c("r Wrong mob type: "+string+" should be "+ Arrays.stream(MobCategory.values()).map(MobCategory::getName).collect(Collectors.joining(", ")))).create(); + throw new SimpleCommandExceptionType(Messenger.c("r Wrong mob type: "+string+" should be "+ Arrays.stream(SpawnReporter.cachedMobCategories()).map(MobCategory::getName).collect(Collectors.joining(", ")))).create(); } return MOB_CATEGORY_MAP.get(string.toLowerCase(Locale.ROOT)); } @@ -129,45 +131,26 @@ public class SpawnCommand private static int printTrackingReport(CommandSourceStack source) { - Messenger.send(source, SpawnReporter.tracking_report(source.getLevel())); + Messenger.send(source, SpawnReporter.makeTrackingReport(source.getLevel())); return 1; } - private static int startTracking(CommandSourceStack source, BlockPos a, BlockPos b) + private static int startTracking(CommandSourceStack source, BoundingBox filter) { - if (SpawnReporter.track_spawns != 0L) + if (SpawnReporter.trackingSpawns()) { Messenger.m(source, "r You are already tracking spawning."); return 0; } - BlockPos lsl = null; - BlockPos usl = null; - if (a != null && b != null) - { - lsl = new BlockPos( - Math.min(a.getX(), b.getX()), - Math.min(a.getY(), b.getY()), - Math.min(a.getZ(), b.getZ()) ); - usl = new BlockPos( - Math.max(a.getX(), b.getX()), - Math.max(a.getY(), b.getY()), - Math.max(a.getZ(), b.getZ()) ); - } - SpawnReporter.reset_spawn_stats(source.getServer(), false); - SpawnReporter.track_spawns = (long) source.getServer().getTickCount(); - SpawnReporter.lower_spawning_limit = lsl; - SpawnReporter.upper_spawning_limit = usl; + SpawnReporter.startTracking(source.getServer(), filter); Messenger.m(source, "gi Spawning tracking started."); return 1; } private static int stopTracking(CommandSourceStack source) { - Messenger.send(source, SpawnReporter.tracking_report(source.getLevel())); - SpawnReporter.reset_spawn_stats(source.getServer(),false); - SpawnReporter.track_spawns = 0L; - SpawnReporter.lower_spawning_limit = null; - SpawnReporter.upper_spawning_limit = null; + Messenger.send(source, SpawnReporter.makeTrackingReport(source.getLevel())); + SpawnReporter.stopTracking(source.getServer()); Messenger.m(source, "gi Spawning tracking stopped."); return 1; } @@ -175,17 +158,15 @@ public class SpawnCommand private static int recentSpawnsForType(CommandSourceStack source, String mob_type) throws CommandSyntaxException { MobCategory cat = getCategory(mob_type); - Messenger.send(source, SpawnReporter.recent_spawns(source.getLevel(), cat)); + Messenger.send(source, SpawnReporter.getRecentSpawns(source.getLevel(), cat)); return 1; } private static int runTest(CommandSourceStack source, int ticks, String counter) { - //stop tracking - SpawnReporter.reset_spawn_stats(source.getServer(),false); - //start tracking - SpawnReporter.track_spawns = (long) source.getServer().getTickCount(); - //counter reset + // Start tracking + SpawnReporter.startTracking(source.getServer(), null); + // Reset counter if (counter == null) { HopperCounter.resetAll(source.getServer(), false); @@ -221,12 +202,12 @@ public class SpawnCommand { if (domock) { - SpawnReporter.initialize_mocking(); + SpawnReporter.initializeMocking(); Messenger.m(source, "gi Mob spawns will now be mocked."); } else { - SpawnReporter.stop_mocking(); + SpawnReporter.stopMocking(); Messenger.m(source, "gi Normal mob spawning."); } return 1; diff --git a/src/main/java/carpet/mixins/NaturalSpawnerMixin.java b/src/main/java/carpet/mixins/NaturalSpawnerMixin.java index 4f9eb3dd..fc470860 100644 --- a/src/main/java/carpet/mixins/NaturalSpawnerMixin.java +++ b/src/main/java/carpet/mixins/NaturalSpawnerMixin.java @@ -161,7 +161,7 @@ public class NaturalSpawnerMixin // we used the mob - next time we will create a new one when needed ((LevelInterface) world).getPrecookedMobs().remove(entity_1.getType()); - if (SpawnReporter.track_spawns > 0L && SpawnReporter.local_spawns != null) + if (SpawnReporter.trackingSpawns() && SpawnReporter.local_spawns != null) { SpawnReporter.registerSpawn( //world.method_27983(), // getDimensionType //dimension.getType(), // getDimensionType @@ -169,7 +169,7 @@ public class NaturalSpawnerMixin group, //entity_1.getType().getSpawnGroup(), entity_1.blockPosition()); } - if (!SpawnReporter.mock_spawns) + if (!SpawnReporter.mockSpawns) world.addFreshEntityWithPassengers(entity_1); //world.spawnEntity(entity_1); } @@ -180,7 +180,7 @@ public class NaturalSpawnerMixin )) private static SpawnGroupData spawnEntity(Mob mobEntity, ServerLevelAccessor serverWorldAccess, DifficultyInstance difficulty, MobSpawnType spawnReason, SpawnGroupData entityData, CompoundTag entityTag) { - if (!SpawnReporter.mock_spawns) // WorldAccess + if (!SpawnReporter.mockSpawns) // WorldAccess return mobEntity.finalizeSpawn(serverWorldAccess, difficulty, spawnReason, entityData, entityTag); return null; } @@ -273,7 +273,7 @@ public class NaturalSpawnerMixin private static void checkSpawns(ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnState info, boolean spawnAnimals, boolean spawnMonsters, boolean shouldSpawnAnimals, CallbackInfo ci) { - if (SpawnReporter.track_spawns > 0L) + if (SpawnReporter.trackingSpawns()) { MobCategory[] var6 = SPAWNING_CATEGORIES; int var7 = var6.length; @@ -288,23 +288,20 @@ public class NaturalSpawnerMixin int int_3 = newCap * int_2 / MAGIC_NUMBER; //current spawning limits int mobCount = info.getMobCategoryCounts().getInt(entityCategory); - if (SpawnReporter.track_spawns > 0L && !SpawnReporter.first_chunk_marker.contains(entityCategory)) + if (SpawnReporter.trackingSpawns() && !SpawnReporter.first_chunk_marker.contains(entityCategory)) { SpawnReporter.first_chunk_marker.add(entityCategory); //first chunk with spawn eligibility for that category Pair, MobCategory> key = Pair.of(dim, entityCategory); - int spawnTries = SpawnReporter.spawn_tries.get(entityCategory); - SpawnReporter.spawn_attempts.put(key, - SpawnReporter.spawn_attempts.get(key) + spawnTries); + SpawnReporter.spawn_attempts.addTo(key, spawnTries); - SpawnReporter.spawn_cap_count.put(key, - SpawnReporter.spawn_cap_count.get(key) + mobCount); + SpawnReporter.spawn_cap_count.addTo(key, mobCount); } - if (mobCount <= int_3 || SpawnReporter.mock_spawns) //TODO this will not float with player based mobcaps + if (mobCount <= int_3 || SpawnReporter.mockSpawns) //TODO this will not float with player based mobcaps { //place 0 to indicate there were spawn attempts for a category //if (entityCategory != EntityCategory.CREATURE || world.getServer().getTicks() % 400 == 0) diff --git a/src/main/java/carpet/mixins/ServerChunkCacheMixin.java b/src/main/java/carpet/mixins/ServerChunkCacheMixin.java index 2724b650..815ed551 100644 --- a/src/main/java/carpet/mixins/ServerChunkCacheMixin.java +++ b/src/main/java/carpet/mixins/ServerChunkCacheMixin.java @@ -1,6 +1,7 @@ package carpet.mixins; import carpet.utils.SpawnReporter; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; import org.apache.commons.lang3.tuple.Pair; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -10,7 +11,6 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.util.HashMap; import java.util.HashSet; import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.DistanceManager; @@ -39,17 +39,15 @@ public abstract class ServerChunkCacheMixin //((WorldInterface)world).getPrecookedMobs().clear(); not needed because mobs are compared with predefined BBs SpawnReporter.chunkCounts.put(dim, j); - if (SpawnReporter.track_spawns > 0L) + if (SpawnReporter.trackingSpawns()) { //local spawns now need to be tracked globally cause each calll is just for chunk - SpawnReporter.local_spawns = new HashMap<>(); + SpawnReporter.local_spawns = new Object2LongOpenHashMap<>(); SpawnReporter.first_chunk_marker = new HashSet<>(); - for (MobCategory cat : MobCategory.values()) + for (MobCategory cat : SpawnReporter.cachedMobCategories()) { Pair, MobCategory> key = Pair.of(dim, cat); - SpawnReporter.overall_spawn_ticks.put(key, - SpawnReporter.overall_spawn_ticks.get(key)+ - SpawnReporter.spawn_tries.get(cat)); + SpawnReporter.overall_spawn_ticks.addTo(key, SpawnReporter.spawn_tries.get(cat)); } } return j; @@ -59,11 +57,11 @@ public abstract class ServerChunkCacheMixin @Inject(method = "tickChunks", at = @At("RETURN")) private void onFinishSpawnWorldCycle(CallbackInfo ci) { - LevelData levelProperties_1 = this.level.getLevelData(); // levelProperies class - boolean boolean_3 = levelProperties_1.getGameTime() % 400L == 0L; - if (SpawnReporter.track_spawns > 0L && SpawnReporter.local_spawns != null) + LevelData levelData = this.level.getLevelData(); // levelProperies class + boolean boolean_3 = levelData.getGameTime() % 400L == 0L; + if (SpawnReporter.trackingSpawns() && SpawnReporter.local_spawns != null) { - for (MobCategory cat: MobCategory.values()) + for (MobCategory cat: SpawnReporter.cachedMobCategories()) { ResourceKey dim = level.dimension(); // getDimensionType; Pair, MobCategory> key = Pair.of(dim, cat); @@ -73,19 +71,15 @@ public abstract class ServerChunkCacheMixin if (!cat.isPersistent() || boolean_3) // isAnimal { // fill mobcaps for that category so spawn got cancelled - SpawnReporter.spawn_ticks_full.put(key, - SpawnReporter.spawn_ticks_full.get(key)+ spawnTries); + SpawnReporter.spawn_ticks_full.addTo(key, spawnTries); } } - else if (SpawnReporter.local_spawns.get(cat) > 0) + else if (SpawnReporter.local_spawns.getLong(cat) > 0) { // tick spawned mobs for that type - SpawnReporter.spawn_ticks_succ.put(key, - SpawnReporter.spawn_ticks_succ.get(key)+spawnTries); - SpawnReporter.spawn_ticks_spawns.put(key, - SpawnReporter.spawn_ticks_spawns.get(key)+ - SpawnReporter.local_spawns.get(cat)); + SpawnReporter.spawn_ticks_succ.addTo(key, spawnTries); + SpawnReporter.spawn_ticks_spawns.addTo(key, SpawnReporter.local_spawns.getLong(cat)); // this will be off comparing to 1.13 as that would succeed if // ANY tries in that round were successful. // there will be much more difficult to mix in @@ -95,8 +89,7 @@ public abstract class ServerChunkCacheMixin else // spawn no mobs despite trying { //tick didn's spawn mobs of that type - SpawnReporter.spawn_ticks_fail.put(key, - SpawnReporter.spawn_ticks_fail.get(key)+spawnTries); + SpawnReporter.spawn_ticks_fail.addTo(key, spawnTries); } } } diff --git a/src/main/java/carpet/mixins/ServerLevel_scarpetMixin.java b/src/main/java/carpet/mixins/ServerLevel_scarpetMixin.java index 927f3a2f..4fb5295d 100644 --- a/src/main/java/carpet/mixins/ServerLevel_scarpetMixin.java +++ b/src/main/java/carpet/mixins/ServerLevel_scarpetMixin.java @@ -25,6 +25,7 @@ import net.minecraft.world.level.storage.WritableLevelData; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -37,7 +38,6 @@ import static carpet.script.CarpetEventServer.Event.EXPLOSION; import static carpet.script.CarpetEventServer.Event.LIGHTNING; import static carpet.script.CarpetEventServer.Event.CHUNK_UNLOADED; - @Mixin(ServerLevel.class) public abstract class ServerLevel_scarpetMixin extends Level implements ServerWorldInterface { @@ -94,11 +94,13 @@ public abstract class ServerLevel_scarpetMixin extends Level implements ServerWo private ServerLevelData serverLevelData; @Shadow @Final private PersistentEntitySectionManager entityManager; + @Unique @Override public ServerLevelData getWorldPropertiesCM(){ return serverLevelData; } + @Unique @Override public LevelEntityGetter getEntityLookupCMPublic() { return entityManager.getEntityGetter(); diff --git a/src/main/java/carpet/utils/SpawnReporter.java b/src/main/java/carpet/utils/SpawnReporter.java index 7db763a5..0dc7c819 100644 --- a/src/main/java/carpet/utils/SpawnReporter.java +++ b/src/main/java/carpet/utils/SpawnReporter.java @@ -32,6 +32,7 @@ import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.entity.EntityTypeTest; import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.structures.NetherFortressStructure; import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.Nullable; @@ -42,59 +43,48 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Locale; public class SpawnReporter { - public static boolean mock_spawns = false; - - public static Long track_spawns = 0L; + private static final MobCategory[] CACHED_MOBCATEGORY_VALUES = MobCategory.values(); + public static boolean mockSpawns = false; + public static final HashMap, Integer> chunkCounts = new HashMap<>(); - public static final HashMap, MobCategory>, Object2LongMap>> spawn_stats = new HashMap<>(); + public static final HashMap, MobCategory>, Object2LongOpenHashMap>> spawn_stats = new HashMap<>(); public static double mobcap_exponent = 0.0D; - public static final HashMap, MobCategory>, Long> spawn_attempts = new HashMap<>(); - public static final HashMap, MobCategory>, Long> overall_spawn_ticks = new HashMap<>(); - public static final HashMap, MobCategory>, Long> spawn_ticks_full = new HashMap<>(); - public static final HashMap, MobCategory>, Long> spawn_ticks_fail = new HashMap<>(); - public static final HashMap, MobCategory>, Long> spawn_ticks_succ = new HashMap<>(); - public static final HashMap, MobCategory>, Long> spawn_ticks_spawns = new HashMap<>(); - public static final HashMap, MobCategory>, Long> spawn_cap_count = new HashMap<>(); + public static final Object2LongOpenHashMap, MobCategory>> spawn_attempts = new Object2LongOpenHashMap<>(); + public static final Object2LongOpenHashMap, MobCategory>> overall_spawn_ticks = new Object2LongOpenHashMap<>(); + public static final Object2LongOpenHashMap, MobCategory>> spawn_ticks_full = new Object2LongOpenHashMap<>(); + public static final Object2LongOpenHashMap, MobCategory>> spawn_ticks_fail = new Object2LongOpenHashMap<>(); + public static final Object2LongOpenHashMap, MobCategory>> spawn_ticks_succ = new Object2LongOpenHashMap<>(); + public static final Object2LongOpenHashMap, MobCategory>> spawn_ticks_spawns = new Object2LongOpenHashMap<>(); + public static final Object2LongOpenHashMap, MobCategory>> spawn_cap_count = new Object2LongOpenHashMap<>(); public static final HashMap, MobCategory>, EvictingQueue, BlockPos>>> spawned_mobs = new HashMap<>(); public static final HashMap spawn_tries = new HashMap<>(); - public static BlockPos lower_spawning_limit = null; - public static BlockPos upper_spawning_limit = null; - // in case game gets each thread for each world - these need to belong to workd. - public static HashMap local_spawns = null; // per world - public static HashSet first_chunk_marker = null; - static { - reset_spawn_stats(null, true); - } + private static int spawnTrackingStartTime = 0; + private static BoundingBox trackedSpawningArea = null; + // in case game gets each thread for each world - these need to belong to workd. + public static Object2LongOpenHashMap local_spawns = null; // per world + public static HashSet first_chunk_marker = null; public static void registerSpawn(Mob mob, MobCategory cat, BlockPos pos) { - if (lower_spawning_limit != null) + if (trackedSpawningArea != null && !trackedSpawningArea.isInside(pos)) { - if (!( (lower_spawning_limit.getX() <= pos.getX() && pos.getX() <= upper_spawning_limit.getX()) && - (lower_spawning_limit.getY() <= pos.getY() && pos.getY() <= upper_spawning_limit.getY()) && - (lower_spawning_limit.getZ() <= pos.getZ() && pos.getZ() <= upper_spawning_limit.getZ()) - )) - { - return; - } + return; } Pair, MobCategory> key = Pair.of(mob.level().dimension(), cat); - long count = spawn_stats.get(key).getOrDefault(mob.getType(), 0L); - spawn_stats.get(key).put(mob.getType(), count + 1); + spawn_stats.get(key).addTo(mob.getType(), 1); spawned_mobs.get(key).put(Pair.of(mob.getType(), pos)); if (!local_spawns.containsKey(cat)) { CarpetSettings.LOG.error("Rogue spawn detected for category "+cat.getName()+" for mob "+mob.getType().getDescription().getString()+". If you see this message let carpet peeps know about it on github issues."); local_spawns.put(cat, 0L); } - local_spawns.put(cat, local_spawns.get(cat)+1); + local_spawns.addTo(cat, 1); } public static final int MAGIC_NUMBER = (int)Math.pow(17.0D, 2.0D); @@ -120,18 +110,18 @@ public class SpawnReporter } List shortCodes = new ArrayList<>(); - for (MobCategory enumcreaturetype : MobCategory.values()) + for (MobCategory category : cachedMobCategories()) { - int cur = dimCounts.getOrDefault(enumcreaturetype, -1); - int max = (int)(chunkcount * ((double)enumcreaturetype.getMaxInstancesPerChunk() / MAGIC_NUMBER)); // from ServerChunkManager.CHUNKS_ELIGIBLE_FOR_SPAWNING + int cur = dimCounts.getOrDefault(category, -1); + int max = (int)(chunkcount * ((double)category.getMaxInstancesPerChunk() / MAGIC_NUMBER)); // from ServerChunkManager.CHUNKS_ELIGIBLE_FOR_SPAWNING String color = Messenger.heatmap_color(cur, max); - String mobColor = Messenger.creatureTypeColor(enumcreaturetype); + String mobColor = Messenger.creatureTypeColor(category); if (multiline) { - int rounds = spawn_tries.get(enumcreaturetype); - lst.add(Messenger.c(String.format("w %s: ", enumcreaturetype.getName()), + int rounds = spawn_tries.get(category); + lst.add(Messenger.c(String.format("w %s: ", category.getName()), (cur < 0) ? "g -" : (color + " " + cur), "g / ", mobColor + " " + max, - (rounds == 1) ? "w " : String.format("gi (%d rounds/tick)", spawn_tries.get(enumcreaturetype)) + (rounds == 1) ? "w " : String.format("gi (%d rounds/tick)", spawn_tries.get(category)) )); } else @@ -144,7 +134,7 @@ public class SpawnReporter } if (!multiline) { - if (shortCodes.size()>0) + if (shortCodes.size() > 0) { shortCodes.remove(shortCodes.size() - 1); lst.add(Messenger.c(shortCodes.toArray(new Object[0]))); @@ -158,18 +148,18 @@ public class SpawnReporter return lst; } - public static List recent_spawns(Level world, MobCategory creature_type) + public static List getRecentSpawns(Level world, MobCategory category) { List lst = new ArrayList<>(); - if ((track_spawns == 0L)) + if (!trackingSpawns()) { lst.add(Messenger.s("Spawn tracking not started")); return lst; } - String type_code = creature_type.getName(); + String categoryName = category.getName(); - lst.add(Messenger.s(String.format("Recent %s spawns:",type_code))); - for (Pair, BlockPos> pair : spawned_mobs.get(Pair.of(world.dimension(), creature_type)).keySet()) // getDImTYpe + lst.add(Messenger.s(String.format("Recent %s spawns:", categoryName))); + for (Pair, BlockPos> pair : spawned_mobs.get(Pair.of(world.dimension(), category)).keySet()) { lst.add( Messenger.c( "w - ", @@ -178,7 +168,7 @@ public class SpawnReporter )); } - if (lst.size()==1) + if (lst.size() == 1) { lst.add(Messenger.s(" - Nothing spawned yet, sorry.")); } @@ -186,37 +176,37 @@ public class SpawnReporter } - public static List show_mobcaps(BlockPos pos, ServerLevel worldIn) + public static List handleWoolAction(BlockPos pos, ServerLevel worldIn) { DyeColor under = WoolTool.getWoolColorAtPosition(worldIn, pos.below()); if (under == null) { - if (track_spawns > 0L) + if (trackingSpawns()) { - return tracking_report(worldIn); + return makeTrackingReport(worldIn); } else { return printMobcapsForDimension(worldIn, true ); } } - MobCategory creature_type = get_type_code_from_wool_code(under); - if (creature_type != null) + MobCategory category = getCategoryFromWoolColor(under); + if (category != null) { - if (track_spawns > 0L) + if (trackingSpawns()) { - return recent_spawns(worldIn, creature_type); + return getRecentSpawns(worldIn, category); } else { - return printEntitiesByType(creature_type, worldIn, true); + return printEntitiesByType(category, worldIn, true); } } - if (track_spawns > 0L) + if (trackingSpawns()) { - return tracking_report(worldIn); + return makeTrackingReport(worldIn); } else { @@ -225,7 +215,7 @@ public class SpawnReporter } - public static MobCategory get_type_code_from_wool_code(DyeColor color) + public static MobCategory getCategoryFromWoolColor(DyeColor color) { return switch (color) { @@ -238,13 +228,13 @@ public class SpawnReporter }; } - public static List printEntitiesByType(MobCategory cat, Level worldIn, boolean all) //Class entityType) + public static List printEntitiesByType(MobCategory cat, ServerLevel worldIn, boolean all) { List lst = new ArrayList<>(); - lst.add( Messenger.s(String.format("Loaded entities for %s class:", cat))); - for (Entity entity : ((ServerLevel)worldIn).getEntities(EntityTypeTest.forClass(Entity.class), (e) -> e.getType().getCategory()==cat)) + lst.add( Messenger.s(String.format("Loaded entities for %s category:", cat))); + for (Entity entity : worldIn.getEntities(EntityTypeTest.forClass(Entity.class), (e) -> e.getType().getCategory() == cat)) { - boolean persistent = entity instanceof Mob && ( ((Mob) entity).isPersistenceRequired() || ((Mob) entity).requiresCustomPersistence()); + boolean persistent = entity instanceof Mob mob && ( mob.isPersistenceRequired() || mob.requiresCustomPersistence()); if (!all && persistent) continue; @@ -252,104 +242,122 @@ public class SpawnReporter BlockPos pos = entity.blockPosition(); lst.add( Messenger.c( "w - ", - Messenger.tp(persistent?"gb":"wb",pos), - String.format(persistent?"g : %s":"w : %s", type.getDescription().getString()) + Messenger.tp(persistent ? "gb" : "wb", pos), + String.format(persistent ? "g : %s" : "w : %s", type.getDescription().getString()) )); } - if (lst.size()==1) + if (lst.size() == 1) { lst.add(Messenger.s(" - Empty.")); } return lst; } - public static void initialize_mocking() + public static void initializeMocking() { - mock_spawns = true; - + mockSpawns = true; } - public static void stop_mocking() + public static void stopMocking() { - mock_spawns = false; + mockSpawns = false; } - public static void reset_spawn_stats(MinecraftServer server, boolean full) - { - spawn_stats.clear(); - spawned_mobs.clear(); - for (MobCategory enumcreaturetype : MobCategory.values()) + public static void resetSpawnStats(MinecraftServer server, boolean full) + { + if (full) { - if (full) - { - spawn_tries.put(enumcreaturetype, 1); - } - if (server != null) for (ResourceKey dim : server.levelKeys()) - { - Pair, MobCategory> key = Pair.of(dim, enumcreaturetype); - overall_spawn_ticks.put(key, 0L); - spawn_attempts.put(key, 0L); - spawn_ticks_full.put(key, 0L); - spawn_ticks_fail.put(key, 0L); - spawn_ticks_succ.put(key, 0L); - spawn_ticks_spawns.put(key, 0L); - spawn_cap_count.put(key, 0L); + for (MobCategory category : cachedMobCategories()) + spawn_tries.put(category, 1); + } + overall_spawn_ticks.clear(); + spawn_attempts.clear(); + spawn_ticks_full.clear(); + spawn_ticks_fail.clear(); + spawn_ticks_succ.clear(); + spawn_ticks_spawns.clear(); + spawn_cap_count.clear(); + + // can't fast-path to clear given different worlds could have different amount of worlds + for (MobCategory category : cachedMobCategories()) { + for (ResourceKey world : server.levelKeys()) { + Pair, MobCategory> key = Pair.of(world, category); spawn_stats.put(key, new Object2LongOpenHashMap<>()); spawned_mobs.put(key, new EvictingQueue<>()); } } - track_spawns = 0L; + spawnTrackingStartTime = 0; + } + + public static MobCategory[] cachedMobCategories() { + return CACHED_MOBCATEGORY_VALUES; + } + + public static boolean trackingSpawns() { + return spawnTrackingStartTime != 0L; + } + + public static void startTracking(MinecraftServer server, BoundingBox trackedArea) { + resetSpawnStats(server, false); + spawnTrackingStartTime = server.getTickCount(); + trackedSpawningArea = trackedArea; + } + + public static void stopTracking(MinecraftServer server) { + resetSpawnStats(server, false); + SpawnReporter.spawnTrackingStartTime = 0; + trackedSpawningArea = null; } private static String getWorldCode(ResourceKey world) { if (world == Level.OVERWORLD) return ""; - return "("+world.location().getPath().toUpperCase(Locale.ROOT).replace("THE_","").charAt(0)+")"; + return "("+Character.toUpperCase(world.location().getPath().charAt("THE_".length()))+")"; } - public static List tracking_report(Level worldIn) + public static List makeTrackingReport(Level worldIn) { - List report = new ArrayList<>(); - if (track_spawns == 0L) + if (!trackingSpawns()) { report.add(Messenger.c( - "w Spawn tracking disabled, type '", + "w Spawn tracking is disabled, type '", "wi /spawn tracking start","/spawn tracking start", "w ' to enable")); return report; } - long duration = worldIn.getServer().getTickCount() - track_spawns; + int duration = worldIn.getServer().getTickCount() - spawnTrackingStartTime; report.add(Messenger.c("bw --------------------")); - String simulated = mock_spawns?"[SIMULATED] ":""; - String location = (lower_spawning_limit != null)?String.format("[in (%d, %d, %d)x(%d, %d, %d)]", - lower_spawning_limit.getX(),lower_spawning_limit.getY(),lower_spawning_limit.getZ(), - upper_spawning_limit.getX(),upper_spawning_limit.getY(),upper_spawning_limit.getZ() ):""; + String simulated = mockSpawns ? "[SIMULATED] " : ""; + String location = (trackedSpawningArea != null) ? String.format("[in (%d, %d, %d)x(%d, %d, %d)]", + trackedSpawningArea.minX(), trackedSpawningArea.minY(), trackedSpawningArea.minZ(), + trackedSpawningArea.maxX(), trackedSpawningArea.maxY(), trackedSpawningArea.maxZ() ):""; report.add(Messenger.s(String.format("%sSpawn statistics %s: for %.1f min", simulated, location, (duration/72000.0)*60))); - for (MobCategory enumcreaturetype : MobCategory.values()) + + for (MobCategory category : cachedMobCategories()) { - //String type_code = String.format("%s", enumcreaturetype); - for (ResourceKey dim : worldIn.getServer().levelKeys()) //String world_code: new String[] {"", " (N)", " (E)"}) + for (ResourceKey dim : worldIn.getServer().levelKeys()) { - Pair, MobCategory> code = Pair.of(dim, enumcreaturetype); - if (spawn_ticks_spawns.get(code) > 0L) + Pair, MobCategory> key = Pair.of(dim, category); + if (spawn_ticks_spawns.getLong(key) > 0L) { - double hours = overall_spawn_ticks.get(code)/72000.0; + double hours = overall_spawn_ticks.getLong(key)/72000.0; + long spawnAttemptsForCategory = spawn_attempts.getLong(key); report.add(Messenger.s(String.format(" > %s%s (%.1f min), %.1f m/t, %%{%.1fF %.1f- %.1f+}; %.2f s/att", - enumcreaturetype.getName().substring(0,3), getWorldCode(dim), + category.getName().substring(0,3), getWorldCode(dim), 60*hours, - (1.0D*spawn_cap_count.get(code))/ spawn_attempts.get(code), - (100.0D*spawn_ticks_full.get(code))/ spawn_attempts.get(code), - (100.0D*spawn_ticks_fail.get(code))/ spawn_attempts.get(code), - (100.0D*spawn_ticks_succ.get(code))/ spawn_attempts.get(code), - (1.0D*spawn_ticks_spawns.get(code))/(spawn_ticks_fail.get(code)+spawn_ticks_succ.get(code)) + (1.0D * spawn_cap_count.getLong(key)) / spawnAttemptsForCategory, + (100.0D * spawn_ticks_full.getLong(key)) / spawnAttemptsForCategory, + (100.0D * spawn_ticks_fail.getLong(key)) / spawnAttemptsForCategory, + (100.0D * spawn_ticks_succ.getLong(key)) / spawnAttemptsForCategory, + (1.0D * spawn_ticks_spawns.getLong(key)) / (spawn_ticks_fail.getLong(key) + spawn_ticks_succ.getLong(key)) ))); - for (EntityType type: spawn_stats.get(code).keySet()) + for (Object2LongMap.Entry> entry: spawn_stats.get(key).object2LongEntrySet()) { report.add(Messenger.s(String.format(" - %s: %d spawns, %d per hour", - type.getDescription().getString(), - spawn_stats.get(code).getLong(type), - (72000 * spawn_stats.get(code).getLong(type)/duration )))); + entry.getKey().getDescription().getString(), + entry.getLongValue(), + (72000 * entry.getLongValue()/duration )))); } } } @@ -357,8 +365,6 @@ public class SpawnReporter return report; } - - public static void killEntity(LivingEntity entity) { if (entity.isPassenger()) @@ -390,51 +396,48 @@ public class SpawnReporter public static List report(BlockPos pos, ServerLevel worldIn) { List rep = new ArrayList<>(); - int x = pos.getX(); int y = pos.getY(); int z = pos.getZ(); + int x = pos.getX(); + int y = pos.getY(); + int z = pos.getZ(); ChunkAccess chunk = worldIn.getChunk(pos); int lc = chunk.getHeight(Heightmap.Types.WORLD_SURFACE, x, z) + 1; - String where = String.format((y >= lc) ? "%d blocks above it." : "%d blocks below it.", Mth.abs(y-lc)); - if (y == lc) where = "right at it."; - rep.add(Messenger.s(String.format("Maximum spawn Y value for (%+d, %+d) is %d. You are "+where, x, z, lc ))); + String relativeHeight = (y == lc) ? "right at it." : String.format("%d blocks %s it.", Mth.abs(y - lc), (y >= lc) ? "above" : "below"); + rep.add(Messenger.s(String.format("Maximum spawn Y value for (%+d, %+d) is %d. You are " + relativeHeight, x, z, lc))); rep.add(Messenger.s("Spawns:")); - for (MobCategory enumcreaturetype : MobCategory.values()) + for (MobCategory category : cachedMobCategories()) { - String type_code = String.format("%s", enumcreaturetype).substring(0, 3); - List lst = getSpawnEntries(worldIn, worldIn.structureManager(), worldIn.getChunkSource().getGenerator(), enumcreaturetype, pos, worldIn.getBiome(pos) );// ((ChunkGenerator)worldIn.getChunkManager().getChunkGenerator()).getEntitySpawnList(, worldIn.getStructureAccessor(), enumcreaturetype, pos); + String categoryCode = String.valueOf(category).substring(0, 3); + List lst = getSpawnEntries(worldIn, worldIn.structureManager(), worldIn.getChunkSource().getGenerator(), category, pos, worldIn.getBiome(pos)); if (lst != null && !lst.isEmpty()) { for (MobSpawnSettings.SpawnerData spawnEntry : lst) { - if (SpawnPlacements.getPlacementType(spawnEntry.type)==null) + if (SpawnPlacements.getPlacementType(spawnEntry.type) == null) continue; // vanilla bug - boolean canspawn = NaturalSpawner.isSpawnPositionOk(SpawnPlacements.getPlacementType(spawnEntry.type), worldIn, pos, spawnEntry.type); - int will_spawn = -1; - boolean fits; - boolean fits1; + boolean canSpawn = NaturalSpawner.isSpawnPositionOk(SpawnPlacements.getPlacementType(spawnEntry.type), worldIn, pos, spawnEntry.type); + int willSpawn = -1; + boolean fits = false; Mob mob; try { mob = (Mob) spawnEntry.type.create(worldIn); } - catch (Exception exception) + catch (Exception e) { - CarpetSettings.LOG.warn("Exception while creating mob for spawn reporter", exception); + CarpetSettings.LOG.warn("Exception while creating mob for spawn reporter", e); return rep; } - boolean fits_true = false; - boolean fits_false = false; - - if (canspawn) + if (canSpawn) { - will_spawn = 0; + willSpawn = 0; for (int attempt = 0; attempt < 50; ++attempt) { float f = x + 0.5F; float f1 = z + 0.5F; mob.moveTo(f, y, f1, worldIn.random.nextFloat() * 360.0F, 0.0F); - fits1 = worldIn.noCollision(mob); + fits = worldIn.noCollision(mob); EntityType etype = mob.getType(); for (int i = 0; i < 20; ++i) @@ -453,20 +456,12 @@ public class SpawnReporter continue; } } - will_spawn += 1; + willSpawn += 1; } } mob.finalizeSpawn(worldIn, worldIn.getCurrentDifficultyAt(mob.blockPosition()), MobSpawnType.NATURAL, null, null); // the code invokes onInitialSpawn after getCanSpawHere - fits = fits1 && worldIn.noCollision(mob); - if (fits) - { - fits_true = true; - } - else - { - fits_false = true; - } + fits = fits && worldIn.noCollision(mob); killEntity(mob); @@ -474,32 +469,32 @@ public class SpawnReporter { mob = (Mob) spawnEntry.type.create(worldIn); } - catch (Exception exception) + catch (Exception e) { - CarpetSettings.LOG.warn("Exception while creating mob for spawn reporter", exception); + CarpetSettings.LOG.warn("Exception while creating mob for spawn reporter", e); return rep; } } } - String creature_name = mob.getType().getDescription().getString(); - String pack_size = String.format("%d", mob.getMaxSpawnClusterSize());//String.format("%d-%d", animal.minGroupCount, animal.maxGroupCount); + String mobTypeName = mob.getType().getDescription().getString(); + //String pack_size = Integer.toString(mob.getMaxSpawnClusterSize());//String.format("%d-%d", animal.minGroupCount, animal.maxGroupCount); int weight = spawnEntry.getWeight().asInt(); - if (canspawn) + if (canSpawn) { - String c = (fits_true && will_spawn>0)?"e":"gi"; + String color = (fits && willSpawn > 0) ? "e" : "gi"; rep.add(Messenger.c( - String.format("%s %s: %s (%d:%d-%d/%d), can: ",c,type_code,creature_name,weight,spawnEntry.minCount, spawnEntry.maxCount, mob.getMaxSpawnClusterSize()), + String.format("%s %s: %s (%d:%d-%d/%d), can: ", color, categoryCode, mobTypeName, weight, spawnEntry.minCount, spawnEntry.maxCount, mob.getMaxSpawnClusterSize()), "l YES", - c+" , fit: ", - ((fits_true && fits_false)?"y YES and NO":(fits_true?"l YES":"r NO")), - c+" , will: ", - ((will_spawn>0)?"l ":"r ")+Math.round((double)will_spawn)/10+"%" + color + " , fit: ", + (fits ? "l YES" : "r NO"), + color + " , will: ", + ((willSpawn > 0)?"l ":"r ") + Math.round((double)willSpawn) / 10 + "%" )); } else { - rep.add(Messenger.c(String.format("gi %s: %s (%d:%d-%d/%d), can: ",type_code,creature_name,weight,spawnEntry.minCount, spawnEntry.maxCount, mob.getMaxSpawnClusterSize()), "n NO")); + rep.add(Messenger.c(String.format("gi %s: %s (%d:%d-%d/%d), can: ", categoryCode, mobTypeName, weight, spawnEntry.minCount, spawnEntry.maxCount, mob.getMaxSpawnClusterSize()), "n NO")); } killEntity(mob); } diff --git a/src/main/java/carpet/utils/WoolTool.java b/src/main/java/carpet/utils/WoolTool.java index 23f16c56..7d3fa7fa 100644 --- a/src/main/java/carpet/utils/WoolTool.java +++ b/src/main/java/carpet/utils/WoolTool.java @@ -75,7 +75,7 @@ public class WoolTool break; case BLACK: if (!"false".equals(CarpetSettings.commandSpawn)) - Messenger.send(placer, SpawnReporter.show_mobcaps(pos, worldIn)); + Messenger.send(placer, SpawnReporter.handleWoolAction(pos, worldIn)); break; case BROWN: if (!"false".equals(CarpetSettings.commandDistance))