Refactor spawn reporting (#1519)

Not all that could be done for now
This commit is contained in:
altrisi authored and GitHub committed 2023-07-28 20:57:48 +02:00
1 parent efddb904a0
commit fbe37017c5
7 files changed
+203 -235

No files matched your search

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