TimingHistory() { this.endTime = System.currentTimeMillis() / 1000; this.startTime = TimingsManager.historyStart / 1000; if (timedTicks % 1200 != 0 || MINUTE_REPORTS.isEmpty()) { this.minuteReports = MINUTE_REPORTS.toArray(new MinuteReport[MINUTE_REPORTS.size() + 1]); this.minuteReports[this.minuteReports.length - 1] = new MinuteReport(); } else { this.minuteReports = MINUTE_REPORTS.toArray(new MinuteReport[MINUTE_REPORTS.size()]); } long ticks = 0; for (MinuteReport mp : this.minuteReports) { ticks += mp.ticksRecord.timed; } this.totalTicks = ticks; this.totalTime = FULL_SERVER_TICK.record.totalTime; this.entries = new TimingHistoryEntry[TimingsManager.HANDLERS.size()]; int i = 0; for (TimingHandler handler : TimingsManager.HANDLERS) { this.entries[i++] = new TimingHistoryEntry(handler); } final Map<EntityType, Counter> entityCounts = MRUMapCache.of(LoadingMap.of(Maps.newHashMap(), Counter.loader())); final Map<BlockType, Counter> tileEntityCounts = MRUMapCache.of(LoadingMap.of(Maps.newHashMap(), Counter.loader())); // Information about all loaded chunks/entities this.worlds = JSONUtil.mapArrayToObject( SpongeImpl.getGame().getServer().getWorlds(), (world) -> { return JSONUtil.singleObjectPair( String.valueOf(worldMap.get(world.getName())), JSONUtil.mapArray( world.getLoadedChunks(), (chunk) -> { entityCounts.clear(); tileEntityCounts.clear(); for (Entity entity : chunk.getEntities()) { if (entity.getType() == null) { SpongeImpl.getLogger().error("Entity is not registered {}", entity); continue; } entityCounts.get(entity.getType()).increment(); } for (TileEntity tileEntity : chunk.getTileEntities()) { tileEntityCounts.get(tileEntity.getBlock().getType()).increment(); } if (tileEntityCounts.isEmpty() && entityCounts.isEmpty()) { return null; } return JSONUtil.arrayOf( chunk.getPosition().getX(), chunk.getPosition().getZ(), JSONUtil.mapArrayToObject( entityCounts.entrySet(), (entry) -> { if (entry.getKey() == EntityTypes.UNKNOWN) { return null; } this.entityTypeSet.add(entry.getKey()); return JSONUtil.singleObjectPair( ((SpongeEntityType) entry.getKey()).entityTypeId, entry.getValue().count()); }), JSONUtil.mapArrayToObject( tileEntityCounts.entrySet(), (entry) -> { this.blockTypeSet.add(entry.getKey()); return JSONUtil.singleObjectPair( Block.getIdFromBlock((Block) entry.getKey()), entry.getValue().count()); })); })); }); }
public class TimingHistory { public static long lastMinuteTime; public static long timedTicks; public static long playerTicks; public static long entityTicks; public static long tileEntityTicks; public static long activatedEntityTicks; static int worldIdPool = 1; static Map<String, Integer> worldMap = LoadingMap.newHashMap((input) -> worldIdPool++); final long endTime; final long startTime; final long totalTicks; // Represents all time spent running the server this history final long totalTime; final MinuteReport[] minuteReports; final TimingHistoryEntry[] entries; final Set<BlockType> blockTypeSet = Sets.newHashSet(); final Set<EntityType> entityTypeSet = Sets.newHashSet(); final JsonObject worlds; TimingHistory() { this.endTime = System.currentTimeMillis() / 1000; this.startTime = TimingsManager.historyStart / 1000; if (timedTicks % 1200 != 0 || MINUTE_REPORTS.isEmpty()) { this.minuteReports = MINUTE_REPORTS.toArray(new MinuteReport[MINUTE_REPORTS.size() + 1]); this.minuteReports[this.minuteReports.length - 1] = new MinuteReport(); } else { this.minuteReports = MINUTE_REPORTS.toArray(new MinuteReport[MINUTE_REPORTS.size()]); } long ticks = 0; for (MinuteReport mp : this.minuteReports) { ticks += mp.ticksRecord.timed; } this.totalTicks = ticks; this.totalTime = FULL_SERVER_TICK.record.totalTime; this.entries = new TimingHistoryEntry[TimingsManager.HANDLERS.size()]; int i = 0; for (TimingHandler handler : TimingsManager.HANDLERS) { this.entries[i++] = new TimingHistoryEntry(handler); } final Map<EntityType, Counter> entityCounts = MRUMapCache.of(LoadingMap.of(Maps.newHashMap(), Counter.loader())); final Map<BlockType, Counter> tileEntityCounts = MRUMapCache.of(LoadingMap.of(Maps.newHashMap(), Counter.loader())); // Information about all loaded chunks/entities this.worlds = JSONUtil.mapArrayToObject( SpongeImpl.getGame().getServer().getWorlds(), (world) -> { return JSONUtil.singleObjectPair( String.valueOf(worldMap.get(world.getName())), JSONUtil.mapArray( world.getLoadedChunks(), (chunk) -> { entityCounts.clear(); tileEntityCounts.clear(); for (Entity entity : chunk.getEntities()) { if (entity.getType() == null) { SpongeImpl.getLogger().error("Entity is not registered {}", entity); continue; } entityCounts.get(entity.getType()).increment(); } for (TileEntity tileEntity : chunk.getTileEntities()) { tileEntityCounts.get(tileEntity.getBlock().getType()).increment(); } if (tileEntityCounts.isEmpty() && entityCounts.isEmpty()) { return null; } return JSONUtil.arrayOf( chunk.getPosition().getX(), chunk.getPosition().getZ(), JSONUtil.mapArrayToObject( entityCounts.entrySet(), (entry) -> { if (entry.getKey() == EntityTypes.UNKNOWN) { return null; } this.entityTypeSet.add(entry.getKey()); return JSONUtil.singleObjectPair( ((SpongeEntityType) entry.getKey()).entityTypeId, entry.getValue().count()); }), JSONUtil.mapArrayToObject( tileEntityCounts.entrySet(), (entry) -> { this.blockTypeSet.add(entry.getKey()); return JSONUtil.singleObjectPair( Block.getIdFromBlock((Block) entry.getKey()), entry.getValue().count()); })); })); }); } public static void resetTicks(boolean fullReset) { if (fullReset) { // Non full is simply for 1 minute reports timedTicks = 0; } lastMinuteTime = System.nanoTime(); playerTicks = 0; tileEntityTicks = 0; entityTicks = 0; activatedEntityTicks = 0; } JsonObject export() { return JSONUtil.objectBuilder() .add("s", this.startTime) .add("e", this.endTime) .add("tk", this.totalTicks) .add("tm", this.totalTime) .add("w", this.worlds) .add( "h", JSONUtil.mapArray( this.entries, (entry) -> entry.data.count == 0 ? null : entry.export())) .add("mp", JSONUtil.mapArray(this.minuteReports, MinuteReport::export)) .build(); } static class MinuteReport { final long time = System.currentTimeMillis() / 1000; final TicksRecord ticksRecord = new TicksRecord(); final PingRecord pingRecord = new PingRecord(); final TimingData fst = TimingsManager.FULL_SERVER_TICK.minuteData.clone(); final double tps = 1E9 / (System.nanoTime() - lastMinuteTime) * this.ticksRecord.timed; final double usedMemory = TimingsManager.FULL_SERVER_TICK.avgUsedMemory; final double freeMemory = TimingsManager.FULL_SERVER_TICK.avgFreeMemory; final double loadAvg = ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage(); public JsonArray export() { return JSONUtil.arrayOf( this.time, Math.round(this.tps * 100D) / 100D, Math.round(this.pingRecord.avg * 100D) / 100D, this.fst.export(), JSONUtil.arrayOf( this.ticksRecord.timed, this.ticksRecord.player, this.ticksRecord.entity, this.ticksRecord.activatedEntity, this.ticksRecord.tileEntity), this.usedMemory, this.freeMemory, this.loadAvg); } } static class TicksRecord { final long timed; final long player; final long entity; final long tileEntity; final long activatedEntity; TicksRecord() { this.timed = timedTicks - (TimingsManager.MINUTE_REPORTS.size() * 1200); this.player = playerTicks; this.entity = entityTicks; this.tileEntity = tileEntityTicks; this.activatedEntity = activatedEntityTicks; } } static class PingRecord { final double avg; PingRecord() { final Collection<Player> onlinePlayers = SpongeImpl.getGame().getServer().getOnlinePlayers(); int totalPing = 0; for (Player player : onlinePlayers) { totalPing += player.getConnection().getLatency(); } this.avg = onlinePlayers.isEmpty() ? 0 : totalPing / onlinePlayers.size(); } } static class Counter { int count = 0; private static final Function<?, Counter> LOADER = new LoadingMap.Feeder<Counter>() { @Override public Counter apply() { return new Counter(); } }; @SuppressWarnings("unchecked") static <T> Function<T, Counter> loader() { return (Function<T, Counter>) LOADER; } public int increment() { return ++this.count; } public int count() { return this.count; } } }