package com.github.games647.lagmonitor.storage; import com.github.games647.lagmonitor.LagMonitor; import com.github.games647.lagmonitor.NativeManager; import com.github.games647.lagmonitor.util.LagUtils; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; import java.nio.file.Path; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.logging.Level; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.entity.Player; import static com.github.games647.lagmonitor.util.LagUtils.round; public class MonitorSaveTask implements Runnable { protected final LagMonitor plugin; protected final Storage storage; public MonitorSaveTask(LagMonitor plugin, Storage storage) { this.plugin = plugin; this.storage = storage; } @Override public void run() { try { int monitorId = save(); if (monitorId == -1) { //error occurred return; } Map<UUID, WorldData> worldsData = getWorldData(); if (!storage.saveWorlds(monitorId, worldsData.values())) { //error occurred return; } List<PlayerData> playerData = getPlayerData(worldsData); storage.savePlayers(playerData); } catch (ExecutionException | InterruptedException ex) { plugin.getLogger().log(Level.SEVERE, "Error saving monitoring data", ex); } } private List<PlayerData> getPlayerData(final Map<UUID, WorldData> worldsData) throws InterruptedException, ExecutionException { Future<List<PlayerData>> playerFuture = Bukkit.getScheduler() .callSyncMethod(plugin, () -> { Collection<? extends Player> onlinePlayers = Bukkit.getOnlinePlayers(); List<PlayerData> playerData = Lists.newArrayListWithCapacity(onlinePlayers.size()); for (Player player : onlinePlayers) { UUID worldId = player.getWorld().getUID(); int worldRowId = 0; WorldData worldData = worldsData.get(worldId); if (worldData != null) { worldRowId = worldData.getRowId(); } int lastPing = (int) plugin.getPingManager().getHistory(player.getName()).getLastSample(); String playerName = player.getName(); UUID playerId = player.getUniqueId(); playerData.add(new PlayerData(worldRowId, playerId, playerName, lastPing)); } return playerData; }); return playerFuture.get(); } private Map<UUID, WorldData> getWorldData() throws ExecutionException, InterruptedException { //this is not thread-safe and have to run sync Future<Map<UUID, WorldData>> worldFuture = Bukkit.getScheduler() .callSyncMethod(plugin, () -> { List<World> worlds = Bukkit.getWorlds(); Map<UUID, WorldData> worldsData = Maps.newHashMapWithExpectedSize(worlds.size()); for (World world : worlds) { worldsData.put(world.getUID(), WorldData.fromWorld(world)); } return worldsData; }); Map<UUID, WorldData> worldsData = worldFuture.get(); //this can run async because it's thread-safe worldsData.values().parallelStream() .forEach(data -> { Path worldFolder = Bukkit.getWorld(data.getWorldName()).getWorldFolder().toPath(); int worldSize = LagUtils.byteToMega(LagUtils.getFolderSize(plugin.getLogger(), worldFolder)); data.setWorldSize(worldSize); }); return worldsData; } private int save() { Runtime runtime = Runtime.getRuntime(); int maxMemory = LagUtils.byteToMega(runtime.maxMemory()); //we need the free ram not the free heap int usedRam = LagUtils.byteToMega(runtime.totalMemory() - runtime.freeMemory()); int freeRam = maxMemory - usedRam; float freeRamPct = round((freeRam * 100) / maxMemory, 4); OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean(); float loadAvg = round(osBean.getSystemLoadAverage(), 4); if (loadAvg < 0) { //windows doesn't support this loadAvg = 0; } NativeManager nativeData = plugin.getNativeData(); float systemUsage = round(nativeData.getCPULoad() * 100, 4); float processUsage = round(nativeData.getProcessCPULoad() * 100, 4); int totalOsMemory = LagUtils.byteToMega(nativeData.getTotalMemory()); int freeOsRam = LagUtils.byteToMega(nativeData.getFreeMemory()); float freeOsRamPct = round((freeOsRam * 100) / totalOsMemory, 4); return storage.saveMonitor(processUsage, systemUsage, freeRam, freeRamPct, freeOsRam, freeOsRamPct, loadAvg); } }