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
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
@@ -12,6 +11,7 @@ import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.scheduler.BukkitRunnable
import java.lang.management.ManagementFactory
import java.text.DecimalFormat
import com.sun.management.OperatingSystemMXBean
class Server_info : JavaPlugin() {
@@ -21,25 +21,23 @@ class Server_info : JavaPlugin() {
private var tps = 20.0
private val df = DecimalFormat("#.##")
// Server title that appears at the top
private var serverTitle = "Paper"
private var cpuUsage = 0.0
private var lastCpuTime = 0L
private var lastSampleTime = 0L
override fun onEnable() {
// Save default config if it doesn't exist
saveDefaultConfig()
// Load configuration
loadConfiguration()
// Register command
getCommand("serverinfo")?.let { command ->
val commandExecutor = ServerInfoCommand(this)
command.setExecutor(commandExecutor)
command.tabCompleter = commandExecutor
}
// Start server info collectors
startTPSCalculation()
startCPUMonitor()
startTabListUpdater()
logger.info("ServerInfo plugin has been enabled!")
@@ -47,15 +45,11 @@ class Server_info : JavaPlugin() {
override fun onDisable() {
logger.info("ServerInfo plugin has been disabled!")
// Save configuration
saveConfig()
}
private fun loadConfiguration() {
// Load server title from config, default to "Paper" if not set
serverTitle = config.getString("server-title") ?: "Paper"
// Create config options if they don't exist
if (!config.contains("server-title")) {
config.set("server-title", serverTitle)
saveConfig()
@@ -66,12 +60,10 @@ class Server_info : JavaPlugin() {
serverTitle = title
config.set("server-title", title)
saveConfig()
// Update tablist immediately to reflect the change
updateTabList()
}
private fun startTPSCalculation() {
// Better TPS calculation method with moving average
object : BukkitRunnable() {
override fun run() {
tickCount++
@@ -80,28 +72,79 @@ class Server_info : JavaPlugin() {
val elapsed = now - lastTickTime
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) {
tickTimeList.add(elapsed)
}
// Keep only the last 600 data points (30 seconds at 20 TPS)
while (tickTimeList.size > 600) {
tickTimeList.removeAt(0)
}
// Calculate average tick time and TPS if we have data
if (tickTimeList.isNotEmpty()) {
val avgTickTime = tickTimeList.average()
tps = 1000.0 / avgTickTime
// Clamp TPS between 0 and 20
if (tps > 20.0) tps = 20.0
if (tps < 0.0) tps = 0.0
tps = tps.coerceIn(0.0, 20.0)
}
}
}.runTaskTimer(this, 1L, 1L) // Run every tick
}.runTaskTimer(this, 1L, 1L)
}
private fun startCPUMonitor() {
object : BukkitRunnable() {
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)
}
}
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() {
@@ -109,7 +152,7 @@ class Server_info : JavaPlugin() {
override fun run() {
updateTabList()
}
}.runTaskTimer(this, 20L, 20L) // Update every second
}.runTaskTimer(this, 20L, 20L)
}
private fun updateTabList() {
@@ -122,76 +165,73 @@ class Server_info : JavaPlugin() {
}
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 {
// Memory information
val runtime = Runtime.getRuntime()
val maxMemory = runtime.maxMemory() / (1024 * 1024)
val allocatedMemory = runtime.totalMemory() / (1024 * 1024)
val freeMemory = runtime.freeMemory() / (1024 * 1024)
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 {
tps >= 18.0 -> "§2" // Dark green
tps >= 15.0 -> "§e" // Yellow
else -> "§c" // Red
tps >= 18.0 -> "§2"
tps >= 15.0 -> "§e"
else -> "§c"
}
return Component.text(
"\n§7RAM: §f${usedMemory}MB / ${maxMemory}MB" +
"\n§7CPU: §f${df.format(cpuLoad)}%" +
"\n§7TPS: $tpsColor${df.format(tps)}"
"\n§7RAM: §f${formatMemory(usedMemory)} / ${formatMemory(maxMemory)}" +
"\n§7CPU: ${formatCPU(cpuUsage)}%" +
"\n§7TPS: $tpsColor${df.format(tps)}"
)
}
fun getServerInfo(): String {
val info = StringBuilder()
// Memory information
val runtime = Runtime.getRuntime()
val maxMemory = runtime.maxMemory() / (1024 * 1024)
val allocatedMemory = runtime.totalMemory() / (1024 * 1024)
val freeMemory = runtime.freeMemory() / (1024 * 1024)
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
return buildString {
append("§6--- ServerInfo ---\n")
append("§6Title: §f$serverTitle\n")
append("§aRAM Usage: §f${formatMemory(usedMemory)} / ${formatMemory(allocatedMemory)}\n")
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 {
return when {
tps >= 18.0 -> "§2${df.format(tps)}" // Dark green
tps >= 15.0 -> "§e${df.format(tps)}" // Yellow
else -> "§c${df.format(tps)}" // Red
tps >= 18.0 -> "§2${df.format(tps)}"
tps >= 15.0 -> "§e${df.format(tps)}"
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 {
return try {
// Get player ping using reflection
val entityPlayer = player.javaClass.getMethod("getHandle").invoke(player)
val pingField = entityPlayer.javaClass.getField("ping")
pingField.getInt(entityPlayer)
@@ -200,17 +240,14 @@ class Server_info : JavaPlugin() {
-1
}
}
// Command executor inner class with tab completion
// Create CommandClass that stores the Commands that can be used
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 {
if (cmd.name.equals("serverinfo", ignoreCase = true)) {
// If there are arguments, handle them
if (args.isNotEmpty()) {
when (args[0].lowercase()) {
"title", "settitle" -> {
// Check permission
if (!sender.hasPermission("serverinfo.admin")) {
sender.sendMessage("§cYou don't have permission to change the server title.")
return true
@@ -221,14 +258,12 @@ class Server_info : JavaPlugin() {
return true
}
// Join remaining args as the title
val newTitle = args.copyOfRange(1, args.size).joinToString(" ")
plugin.setServerTitle(newTitle)
sender.sendMessage("§aServer title changed to: §f$newTitle")
return true
}
"reload" -> {
// Check permission
if (!sender.hasPermission("serverinfo.admin")) {
sender.sendMessage("§cYou don't have permission to reload the plugin.")
return true
@@ -242,13 +277,12 @@ class Server_info : JavaPlugin() {
}
}
// Default behavior - show server info
sender.sendMessage(plugin.getServerInfo())
// If the sender is a player, show their ping
if (sender is Player) {
val ping = plugin.getPingForPlayer(sender)
sender.sendMessage("§aYour Ping: §f${ping}ms")
sender.sendMessage("§6-- Powered by Kotlin --")
}
return true
@@ -256,12 +290,7 @@ class Server_info : JavaPlugin() {
return false
}
override fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: Array<out String>
): List<String>? {
override fun onTabComplete(sender: CommandSender, command: Command, alias: String, args: Array<out String>): List<String>? {
if (command.name.equals("serverinfo", ignoreCase = true)) {
if (args.size == 1) {
val completions = mutableListOf<String>()

Binary file not shown.