Java tutorial
/* * This file is licensed under the MIT License (MIT). * * Copyright (c) 2014 Daniel Ennis <http://aikar.co> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package co.aikar.timings; import cn.nukkit.Player; import cn.nukkit.Server; import cn.nukkit.blockentity.BlockEntity; import cn.nukkit.entity.Entity; import cn.nukkit.level.Level; import cn.nukkit.level.format.generic.BaseFullChunk; import cn.nukkit.timings.JsonUtil; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import static co.aikar.timings.Timings.fullServerTickTimer; import static co.aikar.timings.TimingsManager.MINUTE_REPORTS; import java.lang.management.ManagementFactory; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; public class TimingsHistory { public static long lastMinuteTime; public static long timedTicks; public static long playerTicks; public static long entityTicks; public static long tileEntityTicks; public static long activatedEntityTicks; private static int levelIdPool = 1; static Map<String, Integer> levelMap = new HashMap<>(); static Map<Integer, String> entityMap = new HashMap<>(); static Map<Integer, String> blockEntityMap = new HashMap<>(); private final long endTime; private final long startTime; private final long totalTicks; // Represents all time spent running the server this history private final long totalTime; private final MinuteReport[] minuteReports; private final TimingsHistoryEntry[] entries; private final JsonObject levels = new JsonObject(); TimingsHistory() { 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 mr : this.minuteReports) { ticks += mr.ticksRecord.timed; } this.totalTicks = ticks; this.totalTime = fullServerTickTimer.record.totalTime; this.entries = new TimingsHistoryEntry[TimingsManager.TIMINGS.size()]; int i = 0; for (Timing timing : TimingsManager.TIMINGS) { this.entries[i++] = new TimingsHistoryEntry(timing); } final Map<Integer, AtomicInteger> entityCounts = new HashMap<>(); final Map<Integer, AtomicInteger> blockEntityCounts = new HashMap<>(); final Gson GSON = new Gson(); // Information about all loaded entities/block entities for (Level level : Server.getInstance().getLevels().values()) { JsonArray jsonLevel = new JsonArray(); for (BaseFullChunk chunk : level.getChunks().values()) { entityCounts.clear(); blockEntityCounts.clear(); //count entities for (Entity entity : chunk.getEntities().values()) { if (!entityCounts.containsKey(entity.getNetworkId())) entityCounts.put(entity.getNetworkId(), new AtomicInteger(0)); entityCounts.get(entity.getNetworkId()).incrementAndGet(); entityMap.put(entity.getNetworkId(), entity.getClass().getSimpleName()); } //count block entities for (BlockEntity blockEntity : chunk.getBlockEntities().values()) { if (!blockEntityCounts.containsKey(blockEntity.getBlock().getId())) blockEntityCounts.put(blockEntity.getBlock().getId(), new AtomicInteger(0)); blockEntityCounts.get(blockEntity.getBlock().getId()).incrementAndGet(); blockEntityMap.put(blockEntity.getBlock().getId(), blockEntity.getClass().getSimpleName()); } if (blockEntityCounts.isEmpty() && entityCounts.isEmpty()) { continue; } JsonArray jsonChunk = new JsonArray(); jsonChunk.add(chunk.getX()); jsonChunk.add(chunk.getZ()); jsonChunk.add(GSON .toJsonTree(JsonUtil.mapToObject(entityCounts.entrySet(), (entry) -> new JsonUtil.JSONPair(entry.getKey(), entry.getValue().get()))) .getAsJsonObject()); jsonChunk.add(GSON .toJsonTree(JsonUtil.mapToObject(blockEntityCounts.entrySet(), (entry) -> new JsonUtil.JSONPair(entry.getKey(), entry.getValue().get()))) .getAsJsonObject()); jsonLevel.add(jsonChunk); } if (!levelMap.containsKey(level.getName())) levelMap.put(level.getName(), levelIdPool++); levels.add(String.valueOf(levelMap.get(level.getName())), jsonLevel); } } static void resetTicks(boolean fullReset) { if (fullReset) { timedTicks = 0; } lastMinuteTime = System.nanoTime(); playerTicks = 0; tileEntityTicks = 0; entityTicks = 0; activatedEntityTicks = 0; } JsonObject export() { JsonObject json = new JsonObject(); json.addProperty("s", this.startTime); json.addProperty("e", this.endTime); json.addProperty("tk", this.totalTicks); json.addProperty("tm", this.totalTime); json.add("w", this.levels); json.add("h", JsonUtil.mapToArray(this.entries, (entry) -> { if (entry.data.count == 0) { return null; } return entry.export(); })); json.add("mp", JsonUtil.mapToArray(this.minuteReports, MinuteReport::export)); return json; } static class MinuteReport { final long time = System.currentTimeMillis() / 1000; final TicksRecord ticksRecord = new TicksRecord(); final PingRecord pingRecord = new PingRecord(); final TimingData fst = Timings.fullServerTickTimer.minuteData.clone(); final double tps = 1E9 / (System.nanoTime() - lastMinuteTime) * this.ticksRecord.timed; final double usedMemory = Timings.fullServerTickTimer.avgUsedMemory; final double freeMemory = Timings.fullServerTickTimer.avgFreeMemory; final double loadAvg = ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage(); JsonArray export() { return JsonUtil.toArray(this.time, Math.round(this.tps * 100D) / 100D, Math.round(this.pingRecord.avg * 100D) / 100D, this.fst.export(), JsonUtil.toArray(this.ticksRecord.timed, this.ticksRecord.player, this.ticksRecord.entity, this.ticksRecord.activatedEntity, this.ticksRecord.tileEntity), this.usedMemory, this.freeMemory, this.loadAvg); } } private static class TicksRecord { final long timed; final long player; final long entity; final long activatedEntity; final long tileEntity; TicksRecord() { this.timed = timedTicks - (TimingsManager.MINUTE_REPORTS.size() * 1200); this.player = playerTicks; this.entity = entityTicks; this.activatedEntity = activatedEntityTicks; this.tileEntity = tileEntityTicks; } } private static class PingRecord { final double avg; PingRecord() { final Collection<Player> onlinePlayers = Server.getInstance().getOnlinePlayers().values(); int totalPing = 0; for (Player player : onlinePlayers) { totalPing += player.getPing(); } this.avg = onlinePlayers.isEmpty() ? 0 : totalPing / onlinePlayers.size(); } } }