All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.nukkit.command.defaults.StatusCommand Maven / Gradle / Ivy

There is a newer version: 1.20.40-r1
Show newest version
package cn.nukkit.command.defaults;

import cn.nukkit.Nukkit;
import cn.nukkit.command.CommandSender;
import cn.nukkit.command.data.CommandParameter;
import cn.nukkit.level.Level;
import cn.nukkit.math.NukkitMath;
import cn.nukkit.utils.TextFormat;
import com.sun.jna.platform.win32.COM.WbemcliUtil;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import oshi.SystemInfo;
import oshi.driver.windows.wmi.Win32ComputerSystem;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.hardware.NetworkIF;
import oshi.util.platform.windows.WmiQueryHandler;

import java.lang.management.ManagementFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author xtypr
 * @since 2015/11/11
 */
public final class StatusCommand extends TestCommand {
    private static final String UPTIME_FORMAT = TextFormat.RED + "%d" + TextFormat.GOLD + " days " +
            TextFormat.RED + "%d" + TextFormat.GOLD + " hours " +
            TextFormat.RED + "%d" + TextFormat.GOLD + " minutes " +
            TextFormat.RED + "%d" + TextFormat.GOLD + " seconds";
    private static final Map vmVendor = new HashMap<>(10, 0.99f);
    private static final Map vmMac = new HashMap<>(10, 0.99f);
    private static final String[] vmModelArray = new String[]{"Linux KVM", "Linux lguest", "OpenVZ", "Qemu",
            "Microsoft Virtual PC", "VMWare", "linux-vserver", "Xen", "FreeBSD Jail", "VirtualBox", "Parallels",
            "Linux Containers", "LXC", "Bochs"};

    static {
        vmVendor.put("bhyve", "bhyve");
        vmVendor.put("KVM", "KVM");
        vmVendor.put("TCG", "QEMU");
        vmVendor.put("Microsoft Hv", "Microsoft Hyper-V or Windows Virtual PC");
        vmVendor.put("lrpepyh vr", "Parallels");
        vmVendor.put("VMware", "VMware");
        vmVendor.put("XenVM", "Xen HVM");
        vmVendor.put("ACRN", "Project ACRN");
        vmVendor.put("QNXQVMBSQG", "QNX Hypervisor");
    }

    static {
        vmMac.put("00:50:56", "VMware ESX 3");
        vmMac.put("00:0C:29", "VMware ESX 3");
        vmMac.put("00:05:69", "VMware ESX 3");
        vmMac.put("00:03:FF", "Microsoft Hyper-V");
        vmMac.put("00:1C:42", "Parallels Desktop");
        vmMac.put("00:0F:4B", "Virtual Iron 4");
        vmMac.put("00:16:3E", "Xen or Oracle VM");
        vmMac.put("08:00:27", "VirtualBox");
        vmMac.put("02:42:AC", "Docker Container");
    }

    private final SystemInfo systemInfo = new SystemInfo();

    public StatusCommand(String name) {
        super(name, "%nukkit.command.status.description", "%nukkit.command.status.usage");
        this.setPermission("nukkit.command.status");
        this.getCommandParameters().clear();
        this.addCommandParameters("default", new CommandParameter[]{
                CommandParameter.newEnum("mode", true, new String[]{"full", "simple"})
        });
    }

    private static String formatKB(double bytes) {
        return NukkitMath.round((bytes / 1024 * 1000), 2) + " KB";
    }

    private static String formatKB(long bytes) {
        return NukkitMath.round((bytes / 1024d * 1000), 2) + " KB";
    }

    private static String formatMB(double bytes) {
        return NukkitMath.round((bytes / 1024 / 1024 * 1000), 2) + " MB";
    }

    private static String formatMB(long bytes) {
        return NukkitMath.round((bytes / 1024d / 1024 * 1000), 2) + " MB";
    }

    private static String formatFreq(long hz) {
        if (hz >= 1000000000) {
            return String.format("%.2fGHz", hz / 1000000000.0);
        } else if (hz >= 1000 * 1000) {
            return String.format("%.2fMHz", hz / 1000000.0);
        } else if (hz >= 1000) {
            return String.format("%.2fKHz", hz / 1000.0);
        } else {
            return String.format("%dHz", hz);
        }
    }

    private static String formatUptime(long uptime) {
        long days = TimeUnit.MILLISECONDS.toDays(uptime);
        uptime -= TimeUnit.DAYS.toMillis(days);
        long hours = TimeUnit.MILLISECONDS.toHours(uptime);
        uptime -= TimeUnit.HOURS.toMillis(hours);
        long minutes = TimeUnit.MILLISECONDS.toMinutes(uptime);
        uptime -= TimeUnit.MINUTES.toMillis(minutes);
        long seconds = TimeUnit.MILLISECONDS.toSeconds(uptime);
        return String.format(UPTIME_FORMAT, days, hours, minutes, seconds);
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    private static String isInVM(HardwareAbstractionLayer hardware) {
        // CPU型号检测
        String vendor = hardware.getProcessor().getProcessorIdentifier().getVendor().trim();
        if (vmVendor.containsKey(vendor)) {
            return vmVendor.get(vendor);
        }

        // MAC地址检测
        List nifs = hardware.getNetworkIFs();
        for (NetworkIF nif : nifs) {
            String mac = nif.getMacaddr().toUpperCase();
            String oui = mac.length() > 7 ? mac.substring(0, 8) : mac;
            if (vmMac.containsKey(oui)) {
                return vmVendor.get(oui);
            }
        }

        // 模型检测
        String model = hardware.getComputerSystem().getModel();
        for (String vm : vmModelArray) {
            if (model.contains(vm)) {
                return vm;
            }
        }
        String manufacturer = hardware.getComputerSystem().getManufacturer();
        if ("Microsoft Corporation".equals(manufacturer) && "Virtual Machine".equals(model)) {
            return "Microsoft Hyper-V";
        }

        //内存型号检测
        if (hardware.getMemory().getPhysicalMemory().get(0).getManufacturer().equals("QEMU")) {
            return "QEMU";
        }

        //检查Windows系统参数
        //Wmi虚拟机查询只能在Windows上使用,Linux上不执行这个部分即可
        if (System.getProperties().getProperty("os.name").toUpperCase().contains("WINDOWS")) {
            WbemcliUtil.WmiQuery computerSystemQuery = new WbemcliUtil.WmiQuery("Win32_ComputerSystem", ComputerSystemEntry.class);
            WbemcliUtil.WmiResult result = WmiQueryHandler.createInstance().queryWMI(computerSystemQuery);
            var tmp = result.getValue(ComputerSystemEntry.HYPERVISORPRESENT, 0);
            if (tmp != null && tmp.toString().equals("true")) {
                return "Hyper-V";
            }
        }

        return null;

    }

    @Override
    public boolean execute(CommandSender sender, String commandLabel, String[] args) {
        if (!this.testPermission(sender)) {
            return false;
        }

        var simpleMode = args.length == 0 || !args[0].equalsIgnoreCase("full");
        var server = sender.getServer();

        if (simpleMode) {
            sender.sendMessage(TextFormat.GREEN + "---- " + TextFormat.WHITE + "Server status" + TextFormat.GREEN + " ----");

            long time = System.currentTimeMillis() - Nukkit.START_TIME;

            sender.sendMessage(TextFormat.GOLD + "Uptime: " + formatUptime(time));

            TextFormat tpsColor = TextFormat.GREEN;
            float tps = server.getTicksPerSecond();
            if (tps < 12) {
                tpsColor = TextFormat.RED;
            } else if (tps < 17) {
                tpsColor = TextFormat.GOLD;
            }

            sender.sendMessage(TextFormat.GOLD + "Current TPS: " + tpsColor + NukkitMath.round(tps, 2));

            sender.sendMessage(TextFormat.GOLD + "Load: " + tpsColor + server.getTickUsage() + "%");


            Runtime runtime = Runtime.getRuntime();
            double totalMB = NukkitMath.round(((double) runtime.totalMemory()) / 1024 / 1024, 2);
            double usedMB = NukkitMath.round((double) (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024, 2);
            double maxMB = NukkitMath.round(((double) runtime.maxMemory()) / 1024 / 1024, 2);
            double usage = usedMB / maxMB * 100;
            TextFormat usageColor = TextFormat.GREEN;

            if (usage > 85) {
                usageColor = TextFormat.GOLD;
            }

            sender.sendMessage(TextFormat.GOLD + "Used VM memory: " + usageColor + usedMB + " MB. (" + NukkitMath.round(usage, 2) + "%)");

            sender.sendMessage(TextFormat.GOLD + "Total VM memory: " + TextFormat.RED + totalMB + " MB.");


            TextFormat playerColor = TextFormat.GREEN;
            if (((float) server.getOnlinePlayers().size() / (float) server.getMaxPlayers()) > 0.85) {
                playerColor = TextFormat.GOLD;
            }

            sender.sendMessage(TextFormat.GOLD + "Players: " + playerColor + server.getOnlinePlayers().size() + TextFormat.GREEN + " online, " +
                    TextFormat.RED + server.getMaxPlayers() + TextFormat.GREEN + " max. ");

            for (Level level : server.getLevels().values()) {
                sender.sendMessage(
                        TextFormat.GOLD + "World \"" + level.getFolderName() + "\"" + (!Objects.equals(level.getFolderName(), level.getName()) ? " (" + level.getName() + ")" : "") + ": " +
                                TextFormat.RED + level.getChunks().size() + TextFormat.GREEN + " chunks, " +
                                TextFormat.RED + level.getEntities().length + TextFormat.GREEN + " entities, " +
                                TextFormat.RED + level.getBlockEntities().size() + TextFormat.GREEN + " blockEntities." +
                                " Time " + ((level.getTickRate() > 1 || level.getTickRateTime() > 40) ? TextFormat.RED : TextFormat.YELLOW) + NukkitMath.round(level.getTickRateTime(), 2) + "ms" +
                                (" [delayOpt " + (level.tickRateOptDelay - 1) + "]") +
                                (level.getTickRate() > 1 ? " (tick rate " + (19 - level.getTickRate()) + ")" : "")
                );
            }
        } else {
            // 完整模式
            sender.sendMessage(TextFormat.GREEN + "---- " + TextFormat.WHITE + "Server status" + TextFormat.GREEN + " ----");

            // PNX服务器信息
            {
                sender.sendMessage(TextFormat.YELLOW + ">>> " + TextFormat.WHITE + "PNX Server Info" + TextFormat.YELLOW + " <<<" + TextFormat.RESET);
                // 运行时间
                long time = System.currentTimeMillis() - Nukkit.START_TIME;
                sender.sendMessage(TextFormat.GOLD + "Uptime: " + formatUptime(time));
                // TPS
                TextFormat tpsColor = TextFormat.GREEN;
                float tps = server.getTicksPerSecond();
                if (tps < 12) {
                    tpsColor = TextFormat.RED;
                } else if (tps < 17) {
                    tpsColor = TextFormat.GOLD;
                }
                sender.sendMessage(TextFormat.GOLD + "Current TPS: " + tpsColor + NukkitMath.round(tps, 2));
                // 游戏刻负载
                sender.sendMessage(TextFormat.GOLD + "Tick Load: " + tpsColor + server.getTickUsage() + "%");
                // 在线玩家情况
                var playerColor = TextFormat.GREEN;
                if (((float) server.getOnlinePlayers().size() / (float) server.getMaxPlayers()) > 0.85) {
                    playerColor = TextFormat.GOLD;
                }
                sender.sendMessage(TextFormat.GOLD + "Players: " + playerColor + server.getOnlinePlayers().size() + TextFormat.GREEN + " online, " +
                        TextFormat.RED + server.getMaxPlayers() + TextFormat.GREEN + " max. ");
                // 各个世界的情况
                for (Level level : server.getLevels().values()) {
                    sender.sendMessage(
                            TextFormat.GOLD + "World \"" + level.getFolderName() + "\"" + (!Objects.equals(level.getFolderName(), level.getName()) ? " (" + level.getName() + ")" : "") + ": " +
                                    TextFormat.RED + level.getChunks().size() + TextFormat.GREEN + " chunks, " +
                                    TextFormat.RED + level.getEntities().length + TextFormat.GREEN + " entities, " +
                                    TextFormat.RED + level.getBlockEntities().size() + TextFormat.GREEN + " blockEntities." +
                                    " Time " + ((level.getTickRate() > 1 || level.getTickRateTime() > 40) ? TextFormat.RED : TextFormat.YELLOW) + NukkitMath.round(level.getTickRateTime(), 2) + "ms" +
                                    (" [delayOpt " + (level.tickRateOptDelay - 1) + "]") +
                                    (level.getTickRate() > 1 ? " (tick rate " + (19 - level.getTickRate()) + ")" : "")
                    );
                }
                sender.sendMessage("");
            }
            // 操作系统&JVM信息
            {
                var os = systemInfo.getOperatingSystem();
                var mxBean = ManagementFactory.getRuntimeMXBean();
                sender.sendMessage(TextFormat.YELLOW + ">>> " + TextFormat.WHITE + "OS & JVM Info" + TextFormat.YELLOW + " <<<" + TextFormat.RESET);
                sender.sendMessage(TextFormat.GOLD + "OS: " + TextFormat.AQUA + os.getFamily() + " " + os.getManufacturer() + " " +
                        os.getVersionInfo().getVersion() + " " + os.getVersionInfo().getCodeName() + " " + os.getBitness() + "bit, " +
                        "build " + os.getVersionInfo().getBuildNumber());
                sender.sendMessage(TextFormat.GOLD + "JVM: " + TextFormat.AQUA + mxBean.getVmName() + " " + mxBean.getVmVendor() + " " + mxBean.getVmVersion());
                try {
                    var vm = isInVM(systemInfo.getHardware());
                    if (vm == null) {
                        sender.sendMessage(TextFormat.GOLD + "Virtual environment: " + TextFormat.GREEN + "no");
                    } else {
                        sender.sendMessage(TextFormat.GOLD + "Virtual environment: " + TextFormat.YELLOW + "yes (" + vm + ")");
                    }
                } catch (Exception ignore) {

                }
                sender.sendMessage("");
            }
            // 网络信息
            {
                var network = server.getNetwork();
                if (network.getHardWareNetworkInterfaces() != null) {
                    sender.sendMessage(TextFormat.YELLOW + ">>> " + TextFormat.WHITE + "Network Info" + TextFormat.YELLOW + " <<<" + TextFormat.RESET);
                    sender.sendMessage(TextFormat.GOLD + "Network upload: " + TextFormat.GREEN + formatKB(network.getUpload()) + "/s");
                    sender.sendMessage(TextFormat.GOLD + "Network download: " + TextFormat.GREEN + formatKB(network.getDownload()) + "/s");
                    sender.sendMessage(TextFormat.GOLD + "Network hardware list: ");
                    ObjectArrayList list;
                    for (var each : network.getHardWareNetworkInterfaces()) {
                        list = new ObjectArrayList<>(each.getIPv4addr().length + each.getIPv6addr().length);
                        list.addElements(0, each.getIPv4addr());
                        list.addElements(list.size(), each.getIPv6addr());
                        sender.sendMessage(TextFormat.AQUA + "  " + each.getDisplayName());
                        sender.sendMessage(TextFormat.RESET + "    " + formatKB(each.getSpeed()) + "/s " + TextFormat.GRAY + String.join(", ", list));
                    }
                    sender.sendMessage("");
                }
            }
            // CPU信息
            {
                var cpu = systemInfo.getHardware().getProcessor();
                sender.sendMessage(TextFormat.YELLOW + ">>> " + TextFormat.WHITE + "CPU Info" + TextFormat.YELLOW + " <<<" + TextFormat.RESET);
                sender.sendMessage(TextFormat.GOLD + "CPU: " + TextFormat.AQUA + cpu.getProcessorIdentifier().getName() + TextFormat.GRAY +
                        " (" + formatFreq(cpu.getMaxFreq()) + " baseline; " + cpu.getPhysicalProcessorCount() + " cores, " + cpu.getLogicalProcessorCount() + " logical cores)");
                sender.sendMessage(TextFormat.GOLD + "Thread count: " + TextFormat.GREEN + Thread.getAllStackTraces().size());
                sender.sendMessage(TextFormat.GOLD + "CPU Features: " + TextFormat.RESET + (cpu.getProcessorIdentifier().isCpu64bit() ? "64bit, " : "32bit, ") +
                        cpu.getProcessorIdentifier().getModel() + ", micro-arch: " + cpu.getProcessorIdentifier().getMicroarchitecture());
                sender.sendMessage("");
            }
            // 内存信息
            {
                var globalMemory = systemInfo.getHardware().getMemory();
                var physicalMemories = globalMemory.getPhysicalMemory();
                var virtualMemory = globalMemory.getVirtualMemory();
                long allPhysicalMemory = globalMemory.getTotal() / 1000;
                long usedPhysicalMemory = (globalMemory.getTotal() - globalMemory.getAvailable()) / 1000;
                long allVirtualMemory = virtualMemory.getVirtualMax() / 1000;
                long usedVirtualMemory = virtualMemory.getVirtualInUse() / 1000;
                sender.sendMessage(TextFormat.YELLOW + ">>> " + TextFormat.WHITE + "Memory Info" + TextFormat.YELLOW + " <<<" + TextFormat.RESET);
                //JVM内存
                var runtime = Runtime.getRuntime();
                double totalMB = NukkitMath.round(((double) runtime.totalMemory()) / 1024 / 1024, 2);
                double usedMB = NukkitMath.round((double) (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024, 2);
                double maxMB = NukkitMath.round(((double) runtime.maxMemory()) / 1024 / 1024, 2);
                double usage = usedMB / maxMB * 100;
                var usageColor = TextFormat.GREEN;
                if (usage > 85) {
                    usageColor = TextFormat.GOLD;
                }
                sender.sendMessage(TextFormat.GOLD + "JVM memory: ");
                sender.sendMessage(TextFormat.GOLD + "  Used JVM memory: " + usageColor + usedMB + " MB. (" + NukkitMath.round(usage, 2) + "%)");
                sender.sendMessage(TextFormat.GOLD + "  Total JVM memory: " + TextFormat.RED + totalMB + " MB.");
                sender.sendMessage(TextFormat.GOLD + "  Maximum JVM memory: " + TextFormat.RED + maxMB + " MB.");
                // 操作系统内存
                usage = (double) usedVirtualMemory / allVirtualMemory * 100;
                usageColor = TextFormat.GREEN;
                if (usage > 85) {
                    usageColor = TextFormat.GOLD;
                }
                sender.sendMessage(TextFormat.GOLD + "OS memory: ");
                sender.sendMessage(TextFormat.GOLD + "  Physical memory: " + TextFormat.GREEN + usageColor + formatMB(usedPhysicalMemory) + " / " + formatMB(allPhysicalMemory) + ". (" + NukkitMath.round(usage, 2) + "%)");
                usage = (double) usedVirtualMemory / allVirtualMemory * 100;
                usageColor = TextFormat.GREEN;
                if (usage > 85) {
                    usageColor = TextFormat.GOLD;
                }
                sender.sendMessage(TextFormat.GOLD + "  Virtual memory: " + TextFormat.GREEN + usageColor + formatMB(usedVirtualMemory) + " / " + formatMB(allVirtualMemory) + ". (" + NukkitMath.round(usage, 2) + "%)");
                sender.sendMessage(TextFormat.GOLD + "  Hardware list: ");
                for (var each : physicalMemories) {
                    sender.sendMessage(TextFormat.AQUA + "    " + each.getBankLabel() + " @ " + formatFreq(each.getClockSpeed()) + TextFormat.WHITE + " " + formatMB(each.getCapacity() / 1000));
                    sender.sendMessage(TextFormat.GRAY + "      " + each.getMemoryType() + ", " + each.getManufacturer());
                }
                sender.sendMessage("");
            }
        }


        return true;
    }

    public enum ComputerSystemEntry {
        HYPERVISORPRESENT
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy