This commit is contained in:
rattatwinko
2025-04-26 15:19:44 +02:00
commit 4731a659b9
23 changed files with 486 additions and 0 deletions

View File

@@ -0,0 +1,187 @@
package org.server_info.server_info
import net.kyori.adventure.text.Component
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.scheduler.BukkitRunnable
import java.lang.management.ManagementFactory
import java.text.DecimalFormat
class Server_info : JavaPlugin() {
private var lastTickTime = System.currentTimeMillis()
private var tickCount = 0
private val tickTimeList = ArrayList<Long>()
private var tps = 20.0
private val df = DecimalFormat("#.##")
override fun onEnable() {
// Register command
getCommand("serverinfo")?.setExecutor(ServerInfoCommand(this))
// Start server info collectors
startTPSCalculation()
startTabListUpdater()
logger.info("ServerInfo plugin has been enabled!")
}
override fun onDisable() {
logger.info("ServerInfo plugin has been disabled!")
}
private fun startTPSCalculation() {
// Better TPS calculation method with moving average
object : BukkitRunnable() {
override fun run() {
tickCount++
val now = System.currentTimeMillis()
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
}
}
}.runTaskTimer(this, 1L, 1L) // Run every tick
}
private fun startTabListUpdater() {
object : BukkitRunnable() {
override fun run() {
updateTabList()
}
}.runTaskTimer(this, 20L, 20L) // Update every second
}
private fun updateTabList() {
val header = getTabListHeader()
val footer = getTabListFooter()
for (player in Bukkit.getOnlinePlayers()) {
player.sendPlayerListHeaderAndFooter(header, footer)
}
}
private fun getTabListHeader(): Component {
val serverName = server.name
return Component.text("§6§l$serverName\n§r§7Online players: §f${server.onlinePlayers.size}/${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
}
return Component.text(
"\n§7RAM: §f${usedMemory}MB / ${maxMemory}MB" +
"\n§7CPU: §f${df.format(cpuLoad)}%" +
"\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
}
// Format server info using string templates
info.append("§6===== SERVER INFO =====\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") // Added % sign
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
}
}
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)
} catch (e: Exception) {
logger.warning("Failed to get ping for player: ${e.message}")
-1
}
}
// Command executor inner class
private inner class ServerInfoCommand(private val plugin: Server_info) : CommandExecutor {
override fun onCommand(sender: CommandSender, cmd: Command, label: String, args: Array<out String>): Boolean {
if (cmd.name.equals("serverinfo", ignoreCase = true)) {
// Send 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")
}
return true
}
return false
}
}
}

View File

@@ -0,0 +1,15 @@
name: ServerInfo
version: 1.0-SNAPSHOT
main: org.server_info.server_info.Server_info
api-version: 1.19
description: Displays server performance metrics
commands:
serverinfo:
description: Shows server performance metrics
usage: /<command>
aliases: [sinfo, si]
permission: serverinfo.use
permissions:
serverinfo.use:
description: Allows using the serverinfo command
default: true