aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/com/ambientaddons/features/display/PingOverlay.kt
blob: d8689be203788bfd52aa78247069e34946e0bd0e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package com.ambientaddons.features.display

import AmbientAddons.Companion.config
import AmbientAddons.Companion.mc
import com.ambientaddons.events.ReceivePacketEvent
import com.ambientaddons.gui.GuiElement
import com.ambientaddons.utils.Alignment
import com.ambientaddons.utils.Extensions.renderWidth
import com.ambientaddons.utils.Extensions.withModPrefix
import com.ambientaddons.utils.SBLocation
import com.ambientaddons.utils.dungeon.TextStyle
import com.ambientaddons.utils.render.OverlayUtils
import gg.essential.universal.UChat
import net.minecraft.client.renderer.GlStateManager
import net.minecraft.network.play.client.C16PacketClientStatus
import net.minecraft.network.play.server.S01PacketJoinGame
import net.minecraft.network.play.server.S03PacketTimeUpdate
import net.minecraft.network.play.server.S37PacketStatistics
import net.minecraftforge.client.event.RenderGameOverlayEvent
import net.minecraftforge.event.world.WorldEvent
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.minecraftforge.fml.common.gameevent.TickEvent
import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent
import kotlin.math.max

// Modified from SkytilsMod/Skytils, under AGPL 3.0
object PingOverlay {
    val element = GuiElement("ping", 55, 20)
    private val alpha = 1 / 3.0
    private var averageTps: Double? = null
    private var tpsCount = 0
    private var lastTpsSample: Long? = null

    private var averagePing: Double? = null
    private var pingCount = 0
    private var pingStartTime: Long? = null

    private var ticks = 0
    private var isPinging = false
    private var chatNextPing = false

    @SubscribeEvent
    fun onPacketReceived(event: ReceivePacketEvent) {
        if (event.packet is S03PacketTimeUpdate) {
            val currentTime = System.currentTimeMillis()
            lastTpsSample?.let { lastTime ->
                val time = currentTime - lastTime
                val instantTps = (20000.0 / time).coerceIn(0.0, 20.0)
                tpsCount++
                averageTps = instantTps * alpha + (averageTps ?: instantTps) * (1 - alpha)
            }
            lastTpsSample = currentTime
        } else if (config.shouldPing != 0 && event.packet is S37PacketStatistics) {
            isPinging = false
            pingStartTime?.let { startTime ->
                val instantPing = (System.nanoTime() - startTime) / 1e6
                pingCount++
                averagePing = instantPing * alpha + (averagePing ?: instantPing) * (1 - alpha)
            }
            if (chatNextPing) {
                printPing()
                chatNextPing = false
            }
        } else if (event.packet is S01PacketJoinGame) reset()
    }

    @SubscribeEvent
    fun onWorldLoad(event: WorldEvent.Load) {
        reset()
    }

    private fun reset() {
        isPinging = false
        tpsCount = 0
        averageTps = null
        pingCount = 0
        averagePing = null
    }

    private fun printPing() {
        val pingValue = averagePing?.let { "${colorizePing(it) }%.1f".format(it) } ?: "§e?"
        val tpsValue = averageTps?.let { "${colorizeTps(it) }%.1f".format(it) } ?: "§e?"
        if (shouldPing()) {
            UChat.chat("$pingValue §7ms ($tpsValue §7tps)".withModPrefix())
        } else {
            UChat.chat("$tpsValue §7tps".withModPrefix())
        }
    }

    fun sendPing(isFromCommand: Boolean) {
        if (isFromCommand) chatNextPing = true
        if (!isPinging) {
            isPinging = true
            mc.thePlayer.sendQueue.networkManager.sendPacket(C16PacketClientStatus(C16PacketClientStatus.EnumState.REQUEST_STATS),
                {
                    pingStartTime = System.nanoTime()
                }
            )
        } else if (isFromCommand && !shouldPing()) {
            UChat.chat("§cAlready pinging!".withModPrefix())
        }
    }

    @SubscribeEvent
    fun onTick(event: ClientTickEvent) {
        if (!shouldPing() || event.phase != TickEvent.Phase.START) return
        if (ticks % 40 == 0) {
            sendPing(false)
        }
        ticks++
    }

    private fun colorizeTps(tps: Double) = when {
        (tps > 19) -> "§a"
        (tps > 18) -> "§2"
        (tps > 17) -> "§e"
        (tps > 15) -> "§6"
        else -> "§c"
    }

    private fun colorizePing(ping: Double) = when {
        (ping < 50) -> "§a"
        (ping < 100) -> "§2"
        (ping < 150) -> "§e"
        (ping < 250) -> "§6"
        else -> "§c"
    }

    private fun shouldPing(): Boolean =
        mc.theWorld != null && (config.shouldPing == 3 || (config.shouldPing == 2 && SBLocation.onHypixel) || (config.shouldPing == 1 && SBLocation.inSkyblock))

    @SubscribeEvent
    fun onRenderOverlay(event: RenderGameOverlayEvent) {
        if (config.pingDisplay == 0 || event.type != RenderGameOverlayEvent.ElementType.TEXT) return
        val textStyle = TextStyle.fromInt(config.pingDisplay - 1) ?: return
        GlStateManager.pushMatrix()
        GlStateManager.translate(element.position.x, element.position.y, 500.0)
        GlStateManager.scale(element.position.scale, element.position.scale, 1.0)
        val tpsValue = averageTps?.let { "${colorizeTps(it) }%.1f".format(it) } ?: "§e?"
        if (shouldPing()) {
            val pingValue = averagePing?.let { "${colorizePing(it)}%.1f".format(it) } ?: "§e?"
            val valueWidth = max(pingValue.renderWidth(), tpsValue.renderWidth())
            OverlayUtils.drawString(0, 0, "§bPing:", textStyle, Alignment.Left)
            OverlayUtils.drawString(0, 10, "§bTPS:", textStyle, Alignment.Left)
            OverlayUtils.drawString(30 + valueWidth, 0, pingValue, textStyle, Alignment.Right)
            OverlayUtils.drawString(30 + valueWidth, 10, tpsValue, textStyle, Alignment.Right)
        } else {
            OverlayUtils.drawString(0, 0, "§bTPS:", textStyle, Alignment.Left)
            OverlayUtils.drawString(30 + tpsValue.renderWidth(), 0, tpsValue, textStyle, Alignment.Right)
        }
        GlStateManager.popMatrix()
    }


}