SpawnStatsCommand.java
package com.hypixel.hytale.server.spawning.commands;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.asset.type.environment.config.Environment;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg;
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractWorldCommand;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.server.npc.components.SpawnMarkerReference;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.server.spawning.SpawnRejection;
import com.hypixel.hytale.server.spawning.spawnmarkers.SpawnMarkerEntity;
import com.hypixel.hytale.server.spawning.world.ChunkEnvironmentSpawnData;
import com.hypixel.hytale.server.spawning.world.component.ChunkSpawnData;
import com.hypixel.hytale.server.spawning.world.component.WorldSpawnData;
import it.unimi.dsi.fastutil.ints.IntBinaryOperator;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.logging.Level;
import javax.annotation.Nonnull;
public class SpawnStatsCommand extends AbstractWorldCommand {
@Nonnull
private final FlagArg environmentsArg = this.withFlagArg("environments", "server.commands.spawning.stats.arg.environments.desc");
@Nonnull
private final FlagArg markersArg = this.withFlagArg("markers", "server.commands.spawning.stats.arg.markers.desc");
@Nonnull
private final FlagArg verboseArg = this.withFlagArg("verbose", "server.commands.spawning.stats.arg.verbose.desc");
public SpawnStatsCommand() {
super("stats", "server.commands.spawning.stats.desc");
}
protected void execute(@Nonnull CommandContext context, @Nonnull World world, @Nonnull Store<EntityStore> store) {
if ((Boolean)this.environmentsArg.get(context)) {
WorldSpawnData worldSpawnData = (WorldSpawnData)store.getResource(WorldSpawnData.getResourceType());
AtomicInteger filtered = new AtomicInteger();
boolean verbose = (Boolean)this.verboseArg.get(context);
worldSpawnData.forEachEnvironmentSpawnData((worldEnvironmentSpawnData) -> {
if (verbose || worldEnvironmentSpawnData.hasNPCs() && worldEnvironmentSpawnData.getExpectedNPCs() != 0.0) {
int environmentIndex = worldEnvironmentSpawnData.getEnvironmentIndex();
String name = ((Environment)Environment.getAssetMap().getAsset(environmentIndex)).getId();
Store<ChunkStore> chunkStore = world.getChunkStore().getStore();
double[] chunkExpected = new double[]{0.0};
worldEnvironmentSpawnData.getChunkRefSet().forEach((ref) -> {
ChunkEnvironmentSpawnData chunkEnvironmentSpawnData = ((ChunkSpawnData)chunkStore.getComponent(ref, ChunkSpawnData.getComponentType())).getEnvironmentSpawnData(environmentIndex);
chunkExpected[0] += chunkEnvironmentSpawnData.getExpectedNPCs();
});
String message = String.format("Environment: %-30s Exp %6.2f Act %4d Blk %s Chunk exp: %6.2f", name, worldEnvironmentSpawnData.getExpectedNPCs(), worldEnvironmentSpawnData.getActualNPCs(), worldEnvironmentSpawnData.getSegmentCount(), chunkExpected[0]);
((HytaleLogger.Api)NPCPlugin.get().getLogger().atInfo()).log(message);
worldEnvironmentSpawnData.forEachNpcStat((npcIndex, stats) -> {
int all = stats.getSpansTried();
double failPercent = all > 0 ? MathUtil.percent(all - stats.getSpansSuccess(), all) : 0.0;
String message1;
if (verbose) {
int successfulJobCount = stats.getSuccessfulJobCount();
int successfulBudget = stats.getSuccessfulJobTotalBudget();
int failedJobCount = stats.getFailedJobCount();
int failedBudget = stats.getFailedJobTotalBudget();
message1 = String.format(" NPC: %-30s Exp %6.2f Act %4d | Spns %8d: Succ %4d Lgt %5.1f Blk %5.1f Pos %5.1f Geo %5.1f Bre %5.1f Oth %5.1f | Fail%% %5.1f | Spwnbl %s | Succ Job Bgt %6d Avg Bgt %6.2f | Fail Jobs %6d Bgt %6d Avg Bgt %6.2f ", NPCPlugin.get().getName(npcIndex), stats.getExpected(), stats.getActual(), all, stats.getSpansSuccess(), MathUtil.percent(stats.getRejectionCount(SpawnRejection.OUTSIDE_LIGHT_RANGE), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.INVALID_SPAWN_BLOCK), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.NO_POSITION), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.INVALID_POSITION), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.NOT_BREATHABLE), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.OTHER), all), failPercent, !stats.isUnspawnable(), successfulBudget, successfulJobCount > 0 ? (double)successfulBudget / (double)successfulJobCount : 0.0, failedJobCount, failedBudget, failedJobCount > 0 ? (double)failedBudget / (double)failedJobCount : 0.0);
} else {
message1 = String.format(" NPC: %-30s Exp %6.2f Act %4d | Spns %8d: Succ %4d Lgt %5.1f Blk %5.1f Pos %5.1f Geo %5.1f Bre %5.1f Oth %5.1f | Fail%% %5.1f | Spwnbl %s ", NPCPlugin.get().getName(npcIndex), stats.getExpected(), stats.getActual(), all, stats.getSpansSuccess(), MathUtil.percent(stats.getRejectionCount(SpawnRejection.OUTSIDE_LIGHT_RANGE), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.INVALID_SPAWN_BLOCK), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.NO_POSITION), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.INVALID_POSITION), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.NOT_BREATHABLE), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.OTHER), all), failPercent, !stats.isUnspawnable());
}
NPCPlugin.get().getLogger().at(failPercent < 60.0 ? Level.INFO : Level.WARNING).log(message1);
});
} else {
filtered.getAndIncrement();
}
});
AtomicInteger trackedNPC = new AtomicInteger();
AtomicInteger totalNPC = new AtomicInteger();
store.forEachEntityParallel(NPCEntity.getComponentType(), (index, archetypeChunk, commandBuffer) -> {
totalNPC.getAndIncrement();
NPCEntity npc = (NPCEntity)archetypeChunk.getComponent(index, NPCEntity.getComponentType());
if (npc.getEnvironment() != -2147483648 && npc.getSpawnConfiguration() != -2147483648) {
trackedNPC.getAndIncrement();
}
});
int spawnJobsCompleted = worldSpawnData.getTotalSpawnJobsCompleted();
String message = String.format("Total: Exp %.2f Exp-Empty %.2f Act %d Job Pending Act %d Tracked %d Total %d Unspawnable %s AvgSegCount %s Chunks %s Filtered empty envs %d Active Jobs %d Total Jobs Run %d Avg Job Budget %.2f", worldSpawnData.getExpectedNPCs(), worldSpawnData.getExpectedNPCsInEmptyEnvironments(), worldSpawnData.getActualNPCs(), worldSpawnData.getTrackedCountFromJobs(), trackedNPC.get(), totalNPC.get(), worldSpawnData.isUnspawnable(), worldSpawnData.averageSegmentCount(), worldSpawnData.getChunkCount(), filtered.get(), worldSpawnData.getActiveSpawnJobs(), spawnJobsCompleted, spawnJobsCompleted > 0 ? (double)worldSpawnData.getTotalSpawnJobBudgetUsed() / (double)spawnJobsCompleted : 0.0);
((HytaleLogger.Api)NPCPlugin.get().getLogger().atInfo()).log(message);
}
if ((Boolean)this.markersArg.get(context)) {
AtomicInteger spawnMarkerCount = new AtomicInteger();
AtomicInteger inactiveSpawnMarkerCount = new AtomicInteger();
Object2IntOpenHashMap<String> spawnMarkerTypeCounts = new Object2IntOpenHashMap<String>();
store.forEachChunk(SpawnMarkerEntity.getComponentType(), (BiConsumer)((archetypeChunk, componentStoreCommandBuffer) -> {
for(int index = 0; index < archetypeChunk.size(); ++index) {
SpawnMarkerEntity entity = (SpawnMarkerEntity)archetypeChunk.getComponent(index, SpawnMarkerEntity.getComponentType());
spawnMarkerCount.getAndIncrement();
spawnMarkerTypeCounts.mergeInt(entity.getSpawnMarkerId(), 1, Integer::sum);
if (entity.getSpawnCount() == 0) {
inactiveSpawnMarkerCount.getAndIncrement();
}
}
}));
AtomicInteger spawnMarkerNPCCount = new AtomicInteger();
Object2IntOpenHashMap<String> roleCounts = new Object2IntOpenHashMap<String>();
HashMap<String, Object2IntMap<String>> roleCountsPerMarkerType = new HashMap();
store.forEachChunk(Archetype.of(NPCEntity.getComponentType(), SpawnMarkerReference.getComponentType()), (BiConsumer)((archetypeChunk, componentStoreCommandBuffer) -> {
for(int index = 0; index < archetypeChunk.size(); ++index) {
NPCEntity entity = (NPCEntity)archetypeChunk.getComponent(index, NPCEntity.getComponentType());
SpawnMarkerReference spawnMarkerReference = (SpawnMarkerReference)archetypeChunk.getComponent(index, SpawnMarkerReference.getComponentType());
spawnMarkerNPCCount.getAndIncrement();
String roleName = entity.getRoleName();
roleCounts.mergeInt(roleName, 1, Integer::sum);
Ref<EntityStore> markerRef = spawnMarkerReference.getReference().getEntity(componentStoreCommandBuffer);
SpawnMarkerEntity marker = (SpawnMarkerEntity)componentStoreCommandBuffer.getComponent(markerRef, SpawnMarkerEntity.getComponentType());
Object2IntMap<String> spawnedRoles = (Object2IntMap)roleCountsPerMarkerType.computeIfAbsent(marker.getSpawnMarkerId(), (key) -> new Object2IntOpenHashMap());
spawnedRoles.mergeInt(roleName, 1, (IntBinaryOperator)(Integer::sum));
}
}));
StringBuilder sb = new StringBuilder();
sb.append("Markers: ").append(spawnMarkerCount.get()).append(" (With zero spawns: ").append(inactiveSpawnMarkerCount.get()).append(")\nSpawned NPCs: ").append(spawnMarkerNPCCount.get());
roleCounts.object2IntEntrySet().fastForEach((stringEntry) -> sb.append("\n ").append((String)stringEntry.getKey()).append(": ").append(stringEntry.getIntValue()));
sb.append("\nRoles by marker type:");
roleCountsPerMarkerType.forEach((key, spawnedRoles) -> {
sb.append("\n ").append(key).append(" (Instances: ").append(spawnMarkerTypeCounts.getInt(key)).append(")");
spawnedRoles.object2IntEntrySet().forEach((entry) -> sb.append("\n ").append((String)entry.getKey()).append(": ").append(entry.getIntValue()));
});
((HytaleLogger.Api)NPCPlugin.get().getLogger().atInfo()).log(sb.toString());
}
context.sendMessage(Message.translation("server.commands.spawning.stats.results"));
}
}