Server Performance Monitor, Plethora of Performance Improvements
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
package gui
|
||||
|
||||
import java.awt.*
|
||||
import javax.swing.JPanel
|
||||
|
||||
|
||||
/**
|
||||
* @author Rodrigo, Maritaria
|
||||
* @author Ceikry (rewrote into Kotlin, adapted for specific use case)
|
||||
*/
|
||||
class GraphView() : JPanel() {
|
||||
private val values: MutableList<Int> = ArrayList(10)
|
||||
fun setValues(newValues: Collection<Int>) {
|
||||
values.clear()
|
||||
addValues(newValues)
|
||||
}
|
||||
|
||||
fun addValues(newValues: Collection<Int>) {
|
||||
if(values.size > 200) values.removeFirst()
|
||||
values.addAll(newValues)
|
||||
updateUI()
|
||||
}
|
||||
|
||||
override fun paintComponent(graphics: Graphics) {
|
||||
super.paintComponent(graphics)
|
||||
if (graphics !is Graphics2D) {
|
||||
graphics.drawString("Graphics is not Graphics2D, unable to render", 0, 0)
|
||||
return
|
||||
}
|
||||
val g: Graphics2D = graphics
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
|
||||
val length: Int = values.size
|
||||
val width: Int = width
|
||||
val height: Int = height
|
||||
val maxScore: Double = maxScore
|
||||
val minScore: Double = minScore
|
||||
val scoreRange: Double = maxScore - minScore
|
||||
|
||||
// draw white background
|
||||
g.color = Color.WHITE
|
||||
g.fillRect(
|
||||
padding + labelPadding,
|
||||
padding,
|
||||
width - (2 * padding) - labelPadding,
|
||||
height - (2 * padding) - labelPadding
|
||||
)
|
||||
g.color = Color.BLACK
|
||||
val fontMetrics: FontMetrics = g.fontMetrics
|
||||
val fontHeight: Int = fontMetrics.height
|
||||
|
||||
// create hatch marks and grid lines for y axis.
|
||||
for (i in 0 until numberYDivisions + 1) {
|
||||
val x1: Int = padding + labelPadding
|
||||
val x2: Int = pointWidth + padding + labelPadding
|
||||
val y: Int =
|
||||
height - (((i * (height - (padding * 2) - labelPadding)) / numberYDivisions) + padding + labelPadding)
|
||||
if (length > 0) {
|
||||
g.color = gridColor
|
||||
g.drawLine(padding + labelPadding + 1 + pointWidth, y, width - padding, y)
|
||||
g.color = Color.BLACK
|
||||
val tickValue: Int = (minScore + ((scoreRange * i) / numberYDivisions)).toInt()
|
||||
val yLabel: String = tickValue.toString() + ""
|
||||
val labelWidth: Int = fontMetrics.stringWidth(yLabel)
|
||||
g.drawString(yLabel, x1 - labelWidth - 5, y + (fontHeight / 2) - 3)
|
||||
}
|
||||
g.drawLine(x1, y, x2, y)
|
||||
}
|
||||
|
||||
// create x and y axes
|
||||
g.drawLine(padding + labelPadding, height - padding - labelPadding, padding + labelPadding, padding)
|
||||
g.drawLine(
|
||||
padding + labelPadding,
|
||||
height - padding - labelPadding,
|
||||
width - padding,
|
||||
height - padding - labelPadding
|
||||
)
|
||||
val oldStroke: Stroke = g.stroke
|
||||
g.color = lineColor
|
||||
g.stroke = graphStroke
|
||||
val xScale: Double = (width.toDouble() - (2 * padding) - labelPadding) / (length - 1)
|
||||
val yScale: Double = (height.toDouble() - (2 * padding) - labelPadding) / scoreRange
|
||||
val graphPoints: MutableList<Point> = ArrayList(length)
|
||||
for (i in 0 until length) {
|
||||
val x1: Int = ((i * xScale) + padding + labelPadding).toInt()
|
||||
val y1: Int = ((maxScore - values.get(i)) * yScale + padding).toInt()
|
||||
graphPoints.add(Point(x1, y1))
|
||||
}
|
||||
for (i in 0 until graphPoints.size - 1) {
|
||||
val x1: Int = graphPoints[i].x
|
||||
val y1: Int = graphPoints[i].y
|
||||
val x2: Int = graphPoints[i + 1].x
|
||||
val y2: Int = graphPoints[i + 1].y
|
||||
if(y2 != 0) {
|
||||
g.drawLine(x1, y1, x2, y2)
|
||||
}
|
||||
}
|
||||
val drawDots: Boolean = width > (length * pointWidth)
|
||||
if (drawDots) {
|
||||
g.stroke = oldStroke
|
||||
g.color = pointColor
|
||||
for (graphPoint: Point in graphPoints) {
|
||||
val x: Int = graphPoint.x - pointWidth / 2
|
||||
val y: Int = graphPoint.y - pointWidth / 2
|
||||
g.fillOval(x, y, pointWidth, pointWidth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val minScore: Double
|
||||
get() = values.stream().min { obj: Int, integer: Int? -> obj.compareTo((integer)!!) }.orElse(0)
|
||||
.toDouble()
|
||||
private val maxScore: Double
|
||||
get() {
|
||||
return values.stream().max { obj: Int, integer: Int? -> obj.compareTo((integer)!!) }.orElse(0)
|
||||
.toDouble()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val padding: Int = 25
|
||||
private const val labelPadding: Int = 25
|
||||
private const val pointWidth: Int = 4
|
||||
private const val numberYDivisions: Int = 10
|
||||
private val lineColor: Color = Color(44, 102, 230, 180)
|
||||
private val pointColor: Color = Color(100, 100, 100, 180)
|
||||
private val gridColor: Color = Color(200, 200, 200, 200)
|
||||
private val graphStroke: Stroke = BasicStroke(2f)
|
||||
}
|
||||
|
||||
init {
|
||||
preferredSize = Dimension(padding * 2 + 300, padding * 2 + 200)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package gui
|
||||
|
||||
sealed class GuiEvent {
|
||||
class UpdatePulseCount(val amount: Int) : GuiEvent()
|
||||
class UpdateTickTime(val time: Long) : GuiEvent()
|
||||
class UpdateQueuedPackets(val amount: Int) : GuiEvent()
|
||||
class AddDebugMessage(val message: String) : GuiEvent()
|
||||
class AddDefaultMessage(val message: String) : GuiEvent()
|
||||
class AddAIPMessage(val message: String): GuiEvent()
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package gui
|
||||
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.world.GameWorld
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Component
|
||||
import java.awt.Dimension
|
||||
import javax.swing.*
|
||||
import javax.swing.border.Border
|
||||
|
||||
object ServerMonitor : JFrame("Server Monitor"){
|
||||
|
||||
val eventQueue = ArrayList<GuiEvent>()
|
||||
val pulseCount = JLabel("0")
|
||||
val tickTime = JLabel("0")
|
||||
val queuedPackets = JLabel("0")
|
||||
val pulseList = JTextArea("")
|
||||
val debugLog = JTextArea("")
|
||||
val defaultLog = JTextArea("")
|
||||
val AIPlog = JTextArea("")
|
||||
val tickTimeGraph = GraphView()
|
||||
val pulseCountGraph = GraphView()
|
||||
val packetQueueGraph = GraphView()
|
||||
|
||||
init {
|
||||
isVisible = false
|
||||
layout = BorderLayout()
|
||||
size = Dimension(800,640)
|
||||
preferredSize = Dimension(800,640)
|
||||
minimumSize = Dimension(800,640)
|
||||
isResizable = false
|
||||
|
||||
val tabs = JTabbedPane()
|
||||
tabs.addTab("Overview", constructOverviewTab())
|
||||
tabs.addTab("Graphs", constructGraphsTab())
|
||||
add(tabs)
|
||||
|
||||
pack()
|
||||
}
|
||||
|
||||
private fun constructGraphsTab(): JPanel {
|
||||
val graphPanel = JPanel()
|
||||
|
||||
graphPanel.placeAt(0,10,800,590)
|
||||
|
||||
val tickTimeBorder = BorderFactory.createTitledBorder("Tick Time")
|
||||
val pulseCountBorder = BorderFactory.createTitledBorder("Pulse Count")
|
||||
val packetQueueBorder = BorderFactory.createTitledBorder("Queued Packets")
|
||||
|
||||
tickTimeGraph.placeAt(0, 20, 300, 200)
|
||||
tickTimeGraph.border = tickTimeBorder
|
||||
|
||||
pulseCountGraph.placeAt(300,20,300,200)
|
||||
pulseCountGraph.border = pulseCountBorder
|
||||
|
||||
packetQueueGraph.placeAt(0,220,300,200)
|
||||
packetQueueGraph.border = packetQueueBorder
|
||||
|
||||
graphPanel.add(tickTimeGraph)
|
||||
graphPanel.add(pulseCountGraph)
|
||||
graphPanel.add(packetQueueGraph)
|
||||
return graphPanel
|
||||
}
|
||||
|
||||
private fun constructOverviewTab(): JPanel {
|
||||
val overviewPanel = JPanel(null)
|
||||
overviewPanel.placeAt(0,10,800,590)
|
||||
|
||||
val pulseLabel = JLabel("Pulse Count: ")
|
||||
pulseLabel.placeAt(0,20,100,10)
|
||||
pulseCount.placeAt(150,20,100,10)
|
||||
|
||||
val tickLabel = JLabel("Tick Time: ")
|
||||
tickLabel.placeAt(0,35,100,10)
|
||||
tickTime.placeAt(150,35,100,10)
|
||||
|
||||
val packetLabel = JLabel("Queued Packets: ")
|
||||
packetLabel.placeAt(0,50,200,10)
|
||||
queuedPackets.placeAt(150, 50, 100, 10)
|
||||
|
||||
val scrollPane = JScrollPane(pulseList)
|
||||
scrollPane.placeAt(400,10,390,275)
|
||||
|
||||
val border = BorderFactory.createTitledBorder("Pulses")
|
||||
scrollPane.border = border
|
||||
|
||||
overviewPanel.add(pulseLabel)
|
||||
overviewPanel.add(pulseCount)
|
||||
overviewPanel.add(tickLabel)
|
||||
overviewPanel.add(tickTime)
|
||||
overviewPanel.add(packetLabel)
|
||||
overviewPanel.add(queuedPackets)
|
||||
overviewPanel.add(scrollPane)
|
||||
|
||||
val logPane = constructLogPane()
|
||||
logPane.placeAt(0,280,800,300)
|
||||
overviewPanel.add(logPane)
|
||||
|
||||
return overviewPanel
|
||||
}
|
||||
|
||||
private fun constructLogPane() : JTabbedPane {
|
||||
val logPane = JTabbedPane()
|
||||
|
||||
debugLog.isEditable = false
|
||||
val debugPanel = JPanel(BorderLayout())
|
||||
val debugScrollpane = JScrollPane(debugLog)
|
||||
debugScrollpane.placeAt(0,300,800,300)
|
||||
debugPanel.add(debugScrollpane, BorderLayout.CENTER)
|
||||
|
||||
defaultLog.isEditable = false
|
||||
val defaultPanel = JPanel(BorderLayout())
|
||||
val defaultScrollpane = JScrollPane(defaultLog)
|
||||
defaultScrollpane.placeAt(0,300,800,300)
|
||||
defaultPanel.add(defaultScrollpane, BorderLayout.CENTER)
|
||||
|
||||
AIPlog.isEditable = false
|
||||
val AIPPanel = JPanel(BorderLayout())
|
||||
val AIPscrollpane = JScrollPane(AIPlog)
|
||||
AIPscrollpane.placeAt(0,300,800,300)
|
||||
AIPPanel.add(AIPscrollpane, BorderLayout.CENTER)
|
||||
|
||||
logPane.addTab("Default",defaultPanel)
|
||||
logPane.addTab("Debug",debugPanel)
|
||||
logPane.addTab("AIP",AIPPanel)
|
||||
|
||||
return logPane
|
||||
}
|
||||
|
||||
|
||||
private fun Component.placeAt(x: Int, y: Int, width: Int, height: Int){
|
||||
this.setBounds(x,y,width,height)
|
||||
}
|
||||
|
||||
fun open() {
|
||||
isVisible = true
|
||||
Thread() {
|
||||
while (true) {
|
||||
SwingUtilities.invokeLater {
|
||||
val removeList = ArrayList<GuiEvent>()
|
||||
eventQueue.toTypedArray().forEach { event ->
|
||||
when (event) {
|
||||
is GuiEvent.UpdatePulseCount -> {
|
||||
pulseCount.text = event.amount.toString()
|
||||
pulseCountGraph.addValues(intArrayOf(event.amount).toList())
|
||||
}
|
||||
is GuiEvent.UpdateTickTime -> {
|
||||
tickTime.text = event.time.toString() + "ms"
|
||||
tickTimeGraph.addValues(intArrayOf(event.time.toInt()).toList())
|
||||
}
|
||||
is GuiEvent.AddDefaultMessage -> defaultLog.text = defaultLog.text + event.message + "\n"
|
||||
is GuiEvent.AddAIPMessage -> AIPlog.text = AIPlog.text + event.message + "\n"
|
||||
is GuiEvent.AddDebugMessage -> debugLog.text = debugLog.text + event.message + "\n"
|
||||
is GuiEvent.UpdateQueuedPackets -> {
|
||||
queuedPackets.text = event.amount.toString()
|
||||
packetQueueGraph.addValues(intArrayOf(event.amount).toList())
|
||||
}
|
||||
}
|
||||
removeList.add(event)
|
||||
}
|
||||
|
||||
pulseList.text = GameWorld.Pulser.TASKS.toArray().filterNotNull().map { it.javaClass.simpleName + "\n" }
|
||||
.filter { it.replace("\n", "").isNotEmpty() }.sorted().joinToString("")
|
||||
eventQueue.removeAll(removeList)
|
||||
repaint()
|
||||
}
|
||||
Thread.sleep(600L)
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user