fixed stuff (added some more info) check commit diff

This commit is contained in:
rattatwinko
2025-04-28 15:04:06 +02:00
parent b6cc8ea7b2
commit 5f185849bb
7 changed files with 103 additions and 74 deletions

View File

@@ -1,7 +1,6 @@
package org.server_info.server_info package org.server_info.server_info
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.command.Command import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor import org.bukkit.command.CommandExecutor
@@ -12,6 +11,7 @@ import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.scheduler.BukkitRunnable import org.bukkit.scheduler.BukkitRunnable
import java.lang.management.ManagementFactory import java.lang.management.ManagementFactory
import java.text.DecimalFormat import java.text.DecimalFormat
import com.sun.management.OperatingSystemMXBean
class Server_info : JavaPlugin() { class Server_info : JavaPlugin() {
@@ -21,25 +21,23 @@ class Server_info : JavaPlugin() {
private var tps = 20.0 private var tps = 20.0
private val df = DecimalFormat("#.##") private val df = DecimalFormat("#.##")
// Server title that appears at the top
private var serverTitle = "Paper" private var serverTitle = "Paper"
private var cpuUsage = 0.0
private var lastCpuTime = 0L
private var lastSampleTime = 0L
override fun onEnable() { override fun onEnable() {
// Save default config if it doesn't exist
saveDefaultConfig() saveDefaultConfig()
// Load configuration
loadConfiguration() loadConfiguration()
// Register command
getCommand("serverinfo")?.let { command -> getCommand("serverinfo")?.let { command ->
val commandExecutor = ServerInfoCommand(this) val commandExecutor = ServerInfoCommand(this)
command.setExecutor(commandExecutor) command.setExecutor(commandExecutor)
command.tabCompleter = commandExecutor command.tabCompleter = commandExecutor
} }
// Start server info collectors
startTPSCalculation() startTPSCalculation()
startCPUMonitor()
startTabListUpdater() startTabListUpdater()
logger.info("ServerInfo plugin has been enabled!") logger.info("ServerInfo plugin has been enabled!")
@@ -47,15 +45,11 @@ class Server_info : JavaPlugin() {
override fun onDisable() { override fun onDisable() {
logger.info("ServerInfo plugin has been disabled!") logger.info("ServerInfo plugin has been disabled!")
// Save configuration
saveConfig() saveConfig()
} }
private fun loadConfiguration() { private fun loadConfiguration() {
// Load server title from config, default to "Paper" if not set
serverTitle = config.getString("server-title") ?: "Paper" serverTitle = config.getString("server-title") ?: "Paper"
// Create config options if they don't exist
if (!config.contains("server-title")) { if (!config.contains("server-title")) {
config.set("server-title", serverTitle) config.set("server-title", serverTitle)
saveConfig() saveConfig()
@@ -66,12 +60,10 @@ class Server_info : JavaPlugin() {
serverTitle = title serverTitle = title
config.set("server-title", title) config.set("server-title", title)
saveConfig() saveConfig()
// Update tablist immediately to reflect the change
updateTabList() updateTabList()
} }
private fun startTPSCalculation() { private fun startTPSCalculation() {
// Better TPS calculation method with moving average
object : BukkitRunnable() { object : BukkitRunnable() {
override fun run() { override fun run() {
tickCount++ tickCount++
@@ -80,28 +72,79 @@ class Server_info : JavaPlugin() {
val elapsed = now - lastTickTime val elapsed = now - lastTickTime
lastTickTime = now lastTickTime = now
// Store data point - time per tick in ms
// Only add if we've gone through at least one tick to avoid startup anomalies
if (tickCount > 1) { if (tickCount > 1) {
tickTimeList.add(elapsed) tickTimeList.add(elapsed)
} }
// Keep only the last 600 data points (30 seconds at 20 TPS)
while (tickTimeList.size > 600) { while (tickTimeList.size > 600) {
tickTimeList.removeAt(0) tickTimeList.removeAt(0)
} }
// Calculate average tick time and TPS if we have data
if (tickTimeList.isNotEmpty()) { if (tickTimeList.isNotEmpty()) {
val avgTickTime = tickTimeList.average() val avgTickTime = tickTimeList.average()
tps = 1000.0 / avgTickTime tps = 1000.0 / avgTickTime
tps = tps.coerceIn(0.0, 20.0)
}
}
}.runTaskTimer(this, 1L, 1L)
}
// Clamp TPS between 0 and 20 private fun startCPUMonitor() {
if (tps > 20.0) tps = 20.0 object : BukkitRunnable() {
if (tps < 0.0) tps = 0.0 override fun run() {
updateCPUUsage()
}
}.runTaskTimer(this, 20L, 20L) // Update every second
}
private fun updateCPUUsage() {
try {
val osBean = ManagementFactory.getOperatingSystemMXBean()
if (osBean is com.sun.management.OperatingSystemMXBean) {
// Modern JVM with com.sun.management API
cpuUsage = osBean.processCpuLoad * 100
} else {
// Fallback to more basic metrics
val runtime = Runtime.getRuntime()
val processors = runtime.availableProcessors()
// Get JVM CPU time if available
val bean = ManagementFactory.getThreadMXBean()
if (bean.isThreadCpuTimeSupported && bean.isThreadCpuTimeEnabled) {
var totalCpuTime = 0L
for (threadId in bean.allThreadIds) {
val cpuTime = bean.getThreadCpuTime(threadId)
if (cpuTime >= 0) totalCpuTime += cpuTime
}
val now = System.currentTimeMillis()
if (lastCpuTime > 0L && lastSampleTime > 0L) {
val elapsedCpu = totalCpuTime - lastCpuTime
val elapsedTime = (now - lastSampleTime) * 1000000 // Convert to nanoseconds
if (elapsedTime > 0) {
// Calculate CPU usage as a percentage across all cores
cpuUsage = (elapsedCpu * 100.0) / (elapsedTime * processors)
cpuUsage = cpuUsage.coerceIn(0.0, 100.0)
} }
} }
}.runTaskTimer(this, 1L, 1L) // Run every tick
lastCpuTime = totalCpuTime
lastSampleTime = now
} else {
// If CPU time not supported, use a basic estimate ( If this happens please check the server for issues! )
val threadCount = ManagementFactory.getThreadMXBean().threadCount
logger.warning(/* msg = */ "§4Accuarte CPU Monitoring isnt supported! Check Server for Issues!")
cpuUsage = (threadCount * 100.0) / (processors * 20.0)
cpuUsage = cpuUsage.coerceIn(0.0, 100.0)
}
}
} catch (e: Exception) {
logger.warning("Failed to calculate CPU usage: ${e.message}")
cpuUsage = 0.0
}
} }
private fun startTabListUpdater() { private fun startTabListUpdater() {
@@ -109,7 +152,7 @@ class Server_info : JavaPlugin() {
override fun run() { override fun run() {
updateTabList() updateTabList()
} }
}.runTaskTimer(this, 20L, 20L) // Update every second }.runTaskTimer(this, 20L, 20L)
} }
private fun updateTabList() { private fun updateTabList() {
@@ -122,76 +165,73 @@ class Server_info : JavaPlugin() {
} }
private fun getTabListHeader(): Component { private fun getTabListHeader(): Component {
return Component.text("§6§l$serverTitle\n§r§7Online players: §f${server.onlinePlayers.size}/${server.maxPlayers}") return Component.text("§6§l$serverTitle\n§r§7Online players: §f${server.onlinePlayers.size} / §d${server.maxPlayers}")
} }
private fun getTabListFooter(): Component { private fun getTabListFooter(): Component {
// Memory information
val runtime = Runtime.getRuntime() val runtime = Runtime.getRuntime()
val maxMemory = runtime.maxMemory() / (1024 * 1024) val maxMemory = runtime.maxMemory() / (1024 * 1024)
val allocatedMemory = runtime.totalMemory() / (1024 * 1024) val allocatedMemory = runtime.totalMemory() / (1024 * 1024)
val freeMemory = runtime.freeMemory() / (1024 * 1024) val freeMemory = runtime.freeMemory() / (1024 * 1024)
val usedMemory = allocatedMemory - freeMemory val usedMemory = allocatedMemory - freeMemory
// CPU usage
val osBean = ManagementFactory.getOperatingSystemMXBean()
var cpuLoad = osBean.systemLoadAverage
if (cpuLoad < 0) {
cpuLoad = osBean.availableProcessors * ManagementFactory.getThreadMXBean().threadCount / 100.0
}
val tpsColor = when { val tpsColor = when {
tps >= 18.0 -> "§2" // Dark green tps >= 18.0 -> "§2"
tps >= 15.0 -> "§e" // Yellow tps >= 15.0 -> "§e"
else -> "§c" // Red else -> "§c"
} }
return Component.text( return Component.text(
"\n§7RAM: §f${usedMemory}MB / ${maxMemory}MB" + "\n§7RAM: §f${formatMemory(usedMemory)} / ${formatMemory(maxMemory)}" +
"\n§7CPU: §f${df.format(cpuLoad)}%" + "\n§7CPU: ${formatCPU(cpuUsage)}%" +
"\n§7TPS: $tpsColor${df.format(tps)}" "\n§7TPS: $tpsColor${df.format(tps)}"
) )
} }
fun getServerInfo(): String { fun getServerInfo(): String {
val info = StringBuilder()
// Memory information
val runtime = Runtime.getRuntime() val runtime = Runtime.getRuntime()
val maxMemory = runtime.maxMemory() / (1024 * 1024) val maxMemory = runtime.maxMemory() / (1024 * 1024)
val allocatedMemory = runtime.totalMemory() / (1024 * 1024) val allocatedMemory = runtime.totalMemory() / (1024 * 1024)
val freeMemory = runtime.freeMemory() / (1024 * 1024) val freeMemory = runtime.freeMemory() / (1024 * 1024)
val usedMemory = allocatedMemory - freeMemory val usedMemory = allocatedMemory - freeMemory
// CPU usage return buildString {
val osBean = ManagementFactory.getOperatingSystemMXBean() append("§6--- ServerInfo ---\n")
var cpuLoad = osBean.systemLoadAverage append("§6Title: §f$serverTitle\n")
if (cpuLoad < 0) { append("§aRAM Usage: §f${formatMemory(usedMemory)} / ${formatMemory(allocatedMemory)}\n")
cpuLoad = osBean.availableProcessors * ManagementFactory.getThreadMXBean().threadCount / 100.0 append("§aMax RAM: §f${formatMemory(maxMemory)}\n")
append("§aCPU Load: ${formatCPU(cpuUsage)}%\n")
append("§aTPS: ${formatTPS(tps)}\n")
} }
// Format server info using string templates
info.append("§6===== SERVER INFO =====\n")
info.append("§6Title: §f$serverTitle\n")
info.append("§aRAM Usage: §f${usedMemory}MB / ${allocatedMemory}MB\n")
info.append("§aMax RAM: §f${maxMemory}MB\n")
info.append("§aCPU Load: §f${df.format(cpuLoad)}%\n")
info.append("§aTPS: ${formatTPS(tps)}\n")
return info.toString()
} }
private fun formatTPS(tps: Double): String { private fun formatTPS(tps: Double): String {
return when { return when {
tps >= 18.0 -> "§2${df.format(tps)}" // Dark green tps >= 18.0 -> "§2${df.format(tps)}"
tps >= 15.0 -> "§e${df.format(tps)}" // Yellow tps >= 15.0 -> "§e${df.format(tps)}"
else -> "§c${df.format(tps)}" // Red else -> "§c${df.format(tps)}"
}
}
private fun formatMemory(valueInMB: Long): String {
return if (valueInMB >= 1000) {
val gb = valueInMB / 1024.0
"${df.format(gb)} GB"
} else {
"$valueInMB MB"
}
}
private fun formatCPU(cpu: Double): String {
return when {
cpu <= 30.0 -> "§2${df.format(cpu)}"
cpu <= 70.0 -> "§e${df.format(cpu)}"
else -> "§c${df.format(cpu)}"
} }
} }
fun getPingForPlayer(player: Player): Int { fun getPingForPlayer(player: Player): Int {
return try { return try {
// Get player ping using reflection
val entityPlayer = player.javaClass.getMethod("getHandle").invoke(player) val entityPlayer = player.javaClass.getMethod("getHandle").invoke(player)
val pingField = entityPlayer.javaClass.getField("ping") val pingField = entityPlayer.javaClass.getField("ping")
pingField.getInt(entityPlayer) pingField.getInt(entityPlayer)
@@ -200,17 +240,14 @@ class Server_info : JavaPlugin() {
-1 -1
} }
} }
// Create CommandClass that stores the Commands that can be used
// Command executor inner class with tab completion
private inner class ServerInfoCommand(private val plugin: Server_info) : CommandExecutor, TabCompleter { private inner class ServerInfoCommand(private val plugin: Server_info) : CommandExecutor, TabCompleter {
override fun onCommand(sender: CommandSender, cmd: Command, label: String, args: Array<out String>): Boolean { override fun onCommand(sender: CommandSender, cmd: Command, label: String, args: Array<out String>): Boolean {
if (cmd.name.equals("serverinfo", ignoreCase = true)) { if (cmd.name.equals("serverinfo", ignoreCase = true)) {
// If there are arguments, handle them
if (args.isNotEmpty()) { if (args.isNotEmpty()) {
when (args[0].lowercase()) { when (args[0].lowercase()) {
"title", "settitle" -> { "title", "settitle" -> {
// Check permission
if (!sender.hasPermission("serverinfo.admin")) { if (!sender.hasPermission("serverinfo.admin")) {
sender.sendMessage("§cYou don't have permission to change the server title.") sender.sendMessage("§cYou don't have permission to change the server title.")
return true return true
@@ -221,14 +258,12 @@ class Server_info : JavaPlugin() {
return true return true
} }
// Join remaining args as the title
val newTitle = args.copyOfRange(1, args.size).joinToString(" ") val newTitle = args.copyOfRange(1, args.size).joinToString(" ")
plugin.setServerTitle(newTitle) plugin.setServerTitle(newTitle)
sender.sendMessage("§aServer title changed to: §f$newTitle") sender.sendMessage("§aServer title changed to: §f$newTitle")
return true return true
} }
"reload" -> { "reload" -> {
// Check permission
if (!sender.hasPermission("serverinfo.admin")) { if (!sender.hasPermission("serverinfo.admin")) {
sender.sendMessage("§cYou don't have permission to reload the plugin.") sender.sendMessage("§cYou don't have permission to reload the plugin.")
return true return true
@@ -242,13 +277,12 @@ class Server_info : JavaPlugin() {
} }
} }
// Default behavior - show server info
sender.sendMessage(plugin.getServerInfo()) sender.sendMessage(plugin.getServerInfo())
// If the sender is a player, show their ping
if (sender is Player) { if (sender is Player) {
val ping = plugin.getPingForPlayer(sender) val ping = plugin.getPingForPlayer(sender)
sender.sendMessage("§aYour Ping: §f${ping}ms") sender.sendMessage("§aYour Ping: §f${ping}ms")
sender.sendMessage("§6-- Powered by Kotlin --")
} }
return true return true
@@ -256,12 +290,7 @@ class Server_info : JavaPlugin() {
return false return false
} }
override fun onTabComplete( override fun onTabComplete(sender: CommandSender, command: Command, alias: String, args: Array<out String>): List<String>? {
sender: CommandSender,
command: Command,
alias: String,
args: Array<out String>
): List<String>? {
if (command.name.equals("serverinfo", ignoreCase = true)) { if (command.name.equals("serverinfo", ignoreCase = true)) {
if (args.size == 1) { if (args.size == 1) {
val completions = mutableListOf<String>() val completions = mutableListOf<String>()

Binary file not shown.