diff --git a/src-theme/src/integration/events.ts b/src-theme/src/integration/events.ts
index 89061c6373b..02e8d9b95e9 100644
--- a/src-theme/src/integration/events.ts
+++ b/src-theme/src/integration/events.ts
@@ -43,6 +43,17 @@ export interface KeyEvent {
mods: number;
}
+export interface KeyBindingEvent {
+ key: string;
+ action: number;
+ mods: number;
+}
+
+export interface KeyBindingCPSEvent {
+ key: string;
+ cps: number;
+}
+
export interface TargetChangeEvent {
target: PlayerData | null;
}
diff --git a/src-theme/src/routes/hud/elements/keystrokes/Key.svelte b/src-theme/src/routes/hud/elements/keystrokes/Key.svelte
index 34ae0ea728d..525fbe62427 100644
--- a/src-theme/src/routes/hud/elements/keystrokes/Key.svelte
+++ b/src-theme/src/routes/hud/elements/keystrokes/Key.svelte
@@ -1,24 +1,43 @@
-
- {key?.key.localized ?? "???"}
+
+ {#if showName}
+ {key?.key.localized ?? "???"}
+ {/if}
+ {#if showCPS}
+ {cps}
+ {/if}
\ No newline at end of file
+ .keystrokes {
+ display: flex;
+ flex-wrap: wrap;
+ width: calc(50px * 3 + 5px * 2);
+ gap: 5px;
+ }
+
+ .nil {
+ width: 50px;
+ }
+
diff --git a/src/main/kotlin/net/ccbluex/liquidbounce/api/oauth/OAuthClient.kt b/src/main/kotlin/net/ccbluex/liquidbounce/api/oauth/OAuthClient.kt
index b0dedc01603..ba76f9430c4 100644
--- a/src/main/kotlin/net/ccbluex/liquidbounce/api/oauth/OAuthClient.kt
+++ b/src/main/kotlin/net/ccbluex/liquidbounce/api/oauth/OAuthClient.kt
@@ -33,7 +33,7 @@ object OAuthClient {
private var authCodeContinuation: Continuation
? = null
fun runWithScope(block: suspend CoroutineScope.() -> Unit) {
- scope.launch { block() }
+ scope.launch(block = block)
}
suspend fun startAuth(onUrl: (String) -> Unit): ClientAccount {
diff --git a/src/main/kotlin/net/ccbluex/liquidbounce/event/EventManager.kt b/src/main/kotlin/net/ccbluex/liquidbounce/event/EventManager.kt
index 86197ffa4ac..a40b77c79ea 100644
--- a/src/main/kotlin/net/ccbluex/liquidbounce/event/EventManager.kt
+++ b/src/main/kotlin/net/ccbluex/liquidbounce/event/EventManager.kt
@@ -50,6 +50,8 @@ val ALL_EVENT_CLASSES: Array> = arrayOf(
KeyEvent::class,
MouseRotationEvent::class,
KeybindChangeEvent::class,
+ KeyBindingEvent::class,
+ KeyBindingCPSEvent::class,
AttackEvent::class,
SessionEvent::class,
ScreenEvent::class,
diff --git a/src/main/kotlin/net/ccbluex/liquidbounce/event/events/GameEvents.kt b/src/main/kotlin/net/ccbluex/liquidbounce/event/events/GameEvents.kt
index 0fdc3a5025b..47109de2b91 100644
--- a/src/main/kotlin/net/ccbluex/liquidbounce/event/events/GameEvents.kt
+++ b/src/main/kotlin/net/ccbluex/liquidbounce/event/events/GameEvents.kt
@@ -49,7 +49,15 @@ class MouseRotationEvent(var cursorDeltaX: Double, var cursorDeltaY: Double) : C
@Nameable("keybindChange")
@WebSocketEvent
-class KeybindChangeEvent: Event()
+class KeybindChangeEvent : Event()
+
+@Nameable("keybinding")
+@WebSocketEvent
+class KeyBindingEvent(val key: InputUtil.Key, val action: Int, val mods: Int) : Event()
+
+@Nameable("keybindingCPS")
+@WebSocketEvent
+class KeyBindingCPSEvent(val key: InputUtil.Key, val cps: Int) : Event()
@Nameable("useCooldown")
class UseCooldownEvent(var cooldown: Int) : Event()
diff --git a/src/main/kotlin/net/ccbluex/liquidbounce/utils/input/InputTracker.kt b/src/main/kotlin/net/ccbluex/liquidbounce/utils/input/InputTracker.kt
index 5a53a7c2fe8..25b69a134a7 100644
--- a/src/main/kotlin/net/ccbluex/liquidbounce/utils/input/InputTracker.kt
+++ b/src/main/kotlin/net/ccbluex/liquidbounce/utils/input/InputTracker.kt
@@ -19,12 +19,15 @@
package net.ccbluex.liquidbounce.utils.input
+import net.ccbluex.liquidbounce.event.EventManager
import net.ccbluex.liquidbounce.event.Listenable
-import net.ccbluex.liquidbounce.event.events.MouseButtonEvent
+import net.ccbluex.liquidbounce.event.events.*
import net.ccbluex.liquidbounce.event.handler
import net.ccbluex.liquidbounce.utils.client.mc
import net.minecraft.client.option.KeyBinding
import net.minecraft.client.util.InputUtil
+import net.minecraft.client.util.InputUtil.Type.KEYSYM
+import net.minecraft.client.util.InputUtil.Type.MOUSE
import org.lwjgl.glfw.GLFW
/**
@@ -34,8 +37,71 @@ import org.lwjgl.glfw.GLFW
*/
object InputTracker : Listenable {
- // Tracks the state of each mouse button (pressed or not).
- private val mouseStates = mutableMapOf()
+ private val trackers = listOf(
+ KeyBindingTracker(mc.options.forwardKey),
+ KeyBindingTracker(mc.options.backKey),
+ KeyBindingTracker(mc.options.leftKey),
+ KeyBindingTracker(mc.options.rightKey),
+ KeyBindingTracker(mc.options.jumpKey),
+ KeyBindingTracker(mc.options.attackKey),
+ KeyBindingTracker(mc.options.useKey),
+ )
+
+ override fun children(): List = trackers
+
+ // Tracks CPS
+ class KeyBindingTracker internal constructor(val keyBinding: KeyBinding) : Listenable {
+ // Records clicks in latest 20 ticks (1 sec)
+ private val countByTick = IntArray(20)
+ private var tickIndex = 0
+ private var currentCount = 0
+
+ // Sum of countByTick
+ var cps = 0
+ private set
+
+ var pressed = false
+ private set(value) {
+ if (value) {
+ currentCount++
+ }
+ field = value
+ }
+
+ @Suppress("NOTHING_TO_INLINE")
+ private inline fun setPressed(action: Int) {
+ when (action) {
+ GLFW.GLFW_RELEASE -> pressed = false
+ GLFW.GLFW_PRESS -> pressed = true
+ }
+ }
+
+ val keyHandler = handler {
+ if (keyBinding.boundKey.category == KEYSYM && keyBinding.boundKey.code == it.key.code) {
+ setPressed(it.action)
+ EventManager.callEvent(KeyBindingEvent(keyBinding.boundKey, it.action, it.mods))
+ }
+ }
+
+ val mouseHandler = handler {
+ if (keyBinding.boundKey.category == MOUSE && keyBinding.boundKey.code == it.button) {
+ setPressed(it.action)
+ EventManager.callEvent(KeyBindingEvent(keyBinding.boundKey, it.action, it.mods))
+ }
+ }
+
+ val tickHandler = handler {
+ cps -= countByTick[tickIndex]
+ countByTick[tickIndex] = currentCount
+ cps += currentCount
+ currentCount = 0
+ tickIndex = (tickIndex + 1) % countByTick.size
+ EventManager.callEvent(KeyBindingCPSEvent(keyBinding.boundKey, cps))
+ }
+ }
+
+ // Tracks the state of each mouse button.
+ private val mouseStates = IntArray(32)
/**
* Extension property that checks if a key binding is pressed on either the keyboard or mouse.
@@ -68,7 +134,7 @@ object InputTracker : Listenable {
*/
@Suppress("unused")
private val handleMouseAction = handler {
- mouseStates[it.button] = it.action == GLFW.GLFW_PRESS
+ mouseStates[it.button] = it.action
}
/**
@@ -77,5 +143,5 @@ object InputTracker : Listenable {
* @param button The GLFW code of the mouse button.
* @return True if the mouse button is pressed, false otherwise.
*/
- fun isMouseButtonPressed(button: Int): Boolean = mouseStates.getOrDefault(button, false)
+ fun isMouseButtonPressed(button: Int): Boolean = mouseStates[button] == GLFW.GLFW_PRESS
}