Skip to content

Commit

Permalink
refactor: once<T> handler and no stateless handling (#5072)
Browse files Browse the repository at this point in the history
Handling that ignores the running parameter is a very insecure way to handle events, especially when dealing with parents. This forces you to override the running parameter or create/use a different EventListener instead.
  • Loading branch information
1zun4 authored Dec 28, 2024
1 parent 4f7fa67 commit 5141201
Show file tree
Hide file tree
Showing 30 changed files with 123 additions and 244 deletions.
22 changes: 15 additions & 7 deletions src/main/kotlin/net/ccbluex/liquidbounce/event/EventListener.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ typealias Handler<T> = (T) -> Unit
class EventHook<T : Event>(
val handlerClass: EventListener,
val handler: Handler<T>,
val ignoreNotRunning: Boolean,
val priority: Short = 0
)

Expand Down Expand Up @@ -69,24 +68,34 @@ interface EventListener {
}

inline fun <reified T : Event> EventListener.handler(
ignoreNotRunning: Boolean = false,
priority: Short = 0,
noinline handler: Handler<T>
): EventHook<T> {
return EventManager.registerEventHook(T::class.java,
EventHook(this, handler, ignoreNotRunning, priority)
EventHook(this, handler, priority)
)
}

inline fun <reified T : Event> EventListener.once(
priority: Short = 0,
noinline handler: Handler<T>
): EventHook<T> {
lateinit var eventHook: EventHook<T>
eventHook = EventHook(this, {
handler(it)
EventManager.unregisterEventHook(T::class.java, eventHook)
}, priority)
return EventManager.registerEventHook(T::class.java, eventHook)
}

/**
* Registers an event hook for events of type [T] and launches a sequence
*/
inline fun <reified T : Event> EventListener.sequenceHandler(
ignoreNotRunning: Boolean = false,
priority: Short = 0,
noinline eventHandler: SuspendableHandler<T>
) {
handler<T>(ignoreNotRunning, priority) { event -> Sequence(this, eventHandler, event) }
handler<T>(priority) { event -> Sequence(this, eventHandler, event) }
}

/**
Expand All @@ -98,8 +107,7 @@ fun EventListener.tickHandler(eventHandler: SuspendableHandler<DummyEvent>) {
// and can be used in the event handler function. This is a very useful pattern to use in Kotlin.
var sequence: TickSequence? = TickSequence(this, eventHandler)

// Ignore condition makes sense because we do not want our sequence to run after we do not handle events anymore
handler<GameTickEvent>(ignoreNotRunning = true) {
SequenceManager.handler<GameTickEvent> {
// Check if we should start or stop the sequence
if (this.running) {
// Check if the sequence is already running
Expand Down
18 changes: 6 additions & 12 deletions src/main/kotlin/net/ccbluex/liquidbounce/event/EventManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@
package net.ccbluex.liquidbounce.event

import net.ccbluex.liquidbounce.event.events.*
import net.ccbluex.liquidbounce.utils.client.EventScheduler
import net.ccbluex.liquidbounce.features.misc.HideAppearance.isDestructed
import net.ccbluex.liquidbounce.utils.client.logger
import net.ccbluex.liquidbounce.utils.kotlin.sortedInsert
import net.minecraft.client.MinecraftClient
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.reflect.KClass

Expand Down Expand Up @@ -176,13 +175,6 @@ object EventManager {
registry[eventClass]?.remove(eventHook as EventHook<in Event>)
}

/**
* Unregisters event handlers.
*/
fun unregisterEventHooks(eventClass: Class<out Event>, hooks: Collection<EventHook<in Event>>) {
registry[eventClass]?.removeAll(hooks.toHashSet())
}

fun unregisterEventHandler(eventListener: EventListener) {
registry.values.forEach {
it.removeIf { it.handlerClass == eventListener }
Expand All @@ -201,12 +193,14 @@ object EventManager {
* @param event to call
*/
fun <T : Event> callEvent(event: T): T {
if (isDestructed) {
return event
}

val target = registry[event.javaClass] ?: return event

for (eventHook in target) {
EventScheduler.process(event)

if (!eventHook.ignoreNotRunning && !eventHook.handlerClass.running) {
if (!eventHook.handlerClass.running) {
continue
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ object CosmeticService : EventListener, Configurable("Cosmetics") {
}

@Suppress("unused")
private val sessionHandler = handler<SessionEvent>(ignoreNotRunning = true) { event ->
private val sessionHandler = handler<SessionEvent> { event ->
val session = event.session

// Check if the account is valid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import kotlin.concurrent.thread
*/
object HideAppearance : EventListener {

val shiftChronometer = Chronometer()
private val shiftChronometer = Chronometer()

var isHidingNow = false
set(value) {
Expand All @@ -68,7 +68,7 @@ object HideAppearance : EventListener {
}

@Suppress("unused")
val keyHandler = handler<KeyboardKeyEvent>(ignoreNotRunning = true) {
private val keyHandler = handler<KeyboardKeyEvent> {
val keyCode = it.keyCode
val modifier = it.mods

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,11 @@ import net.ccbluex.liquidbounce.event.EventListener
import net.ccbluex.liquidbounce.event.EventManager
import net.ccbluex.liquidbounce.event.SequenceManager.cancelAllSequences
import net.ccbluex.liquidbounce.event.events.*
import net.ccbluex.liquidbounce.event.handler
import net.ccbluex.liquidbounce.features.module.modules.misc.antibot.ModuleAntiBot
import net.ccbluex.liquidbounce.lang.LanguageManager
import net.ccbluex.liquidbounce.lang.translation
import net.ccbluex.liquidbounce.script.ScriptApiRequired
import net.ccbluex.liquidbounce.utils.client.inGame
import net.ccbluex.liquidbounce.utils.client.logger
import net.ccbluex.liquidbounce.utils.client.notification
import net.ccbluex.liquidbounce.utils.client.toLowerCamelCase
import net.ccbluex.liquidbounce.utils.client.*
import net.ccbluex.liquidbounce.utils.input.InputBind
import net.minecraft.client.util.InputUtil

Expand Down Expand Up @@ -135,7 +131,7 @@ open class ClientModule(
* If the module is running and in game. Can be overridden to add additional checks.
*/
override val running: Boolean
get() = (super.running && inGame && enabled) || disableActivation
get() = super.running && inGame && (enabled || disableActivation)

val bind by bind("Bind", InputBind(InputUtil.Type.KEYSYM, bind, bindAction))
.doNotIncludeWhen { !AutoConfig.includeConfiguration.includeBinds }
Expand Down Expand Up @@ -176,7 +172,7 @@ open class ClientModule(
@ScriptApiRequired
open val settings by lazy { inner.associateBy { it.name } }

private var calledSinceStartup = false
internal var calledSinceStartup = false

/**
* Called when module is turned on
Expand All @@ -193,24 +189,6 @@ open class ClientModule(
*/
open fun init() {}

/**
* Handles disconnect and if [disableOnQuit] is true disables module
*/
@Suppress("unused")
val onDisconnect = handler<DisconnectEvent>(ignoreNotRunning = true) {
if (enabled && disableOnQuit) {
enabled = false
}
}

@Suppress("unused")
val onWorldChange = handler<WorldChangeEvent>(ignoreNotRunning = true) {
if (enabled && !calledSinceStartup && it.world != null) {
calledSinceStartup = true
enable()
}
}

/**
* If we want a module to have the requires bypass option, we specifically call it
* on init. This will add the option and enable the feature.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package net.ccbluex.liquidbounce.features.module
import net.ccbluex.liquidbounce.LiquidBounce
import net.ccbluex.liquidbounce.config.ConfigSystem
import net.ccbluex.liquidbounce.event.EventListener
import net.ccbluex.liquidbounce.event.events.DisconnectEvent
import net.ccbluex.liquidbounce.event.events.KeyboardKeyEvent
import net.ccbluex.liquidbounce.event.events.MouseButtonEvent
import net.ccbluex.liquidbounce.event.events.WorldChangeEvent
Expand All @@ -31,8 +32,6 @@ import net.ccbluex.liquidbounce.features.module.modules.client.ModuleRichPresenc
import net.ccbluex.liquidbounce.features.module.modules.client.ModuleTargets
import net.ccbluex.liquidbounce.features.module.modules.combat.*
import net.ccbluex.liquidbounce.features.module.modules.combat.aimbot.ModuleAutoBow
import net.ccbluex.liquidbounce.features.module.modules.combat.aimbot.ModuleDroneControl
import net.ccbluex.liquidbounce.features.module.modules.combat.aimbot.ModuleProjectileAimbot
import net.ccbluex.liquidbounce.features.module.modules.combat.autoarmor.ModuleAutoArmor
import net.ccbluex.liquidbounce.features.module.modules.combat.criticals.ModuleCriticals
import net.ccbluex.liquidbounce.features.module.modules.combat.crystalaura.ModuleCrystalAura
Expand Down Expand Up @@ -84,6 +83,7 @@ import net.ccbluex.liquidbounce.features.module.modules.world.packetmine.ModuleP
import net.ccbluex.liquidbounce.features.module.modules.world.scaffold.ModuleScaffold
import net.ccbluex.liquidbounce.features.module.modules.world.traps.ModuleAutoTrap
import net.ccbluex.liquidbounce.script.ScriptApiRequired
import net.ccbluex.liquidbounce.utils.client.logger
import net.ccbluex.liquidbounce.utils.client.mc
import net.ccbluex.liquidbounce.utils.input.InputBind
import net.ccbluex.liquidbounce.utils.kotlin.mapArray
Expand Down Expand Up @@ -142,11 +142,45 @@ object ModuleManager : EventListener, Iterable<ClientModule> by modules {
}
}

/**
* Handles world change and enables modules that are not enabled yet
*/
@Suppress("unused")
val worldHandler = handler<WorldChangeEvent> {
private val handleWorldChange = handler<WorldChangeEvent> { event ->
// Delayed start handling
if (event.world != null) {
for (module in modules) {
if (!module.enabled || module.calledSinceStartup) continue

try {
module.calledSinceStartup = true
module.enable()
} catch (e: Exception) {
logger.error("Failed to enable module ${module.name}", e)
}
}
}

// Store modules configuration after world change, happens on disconnect as well
ConfigSystem.storeConfigurable(modulesConfigurable)
}

/**
* Handles disconnect and if [Module.disableOnQuit] is true disables module
*/
@Suppress("unused")
private val handleDisconnect = handler<DisconnectEvent> {
for (module in modules) {
if (module.disableOnQuit) {
try {
module.enabled = false
} catch (e: Exception) {
logger.error("Failed to disable module ${module.name}", e)
}
}
}
}

/**
* Register inbuilt client modules
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ object ModuleVelocity : ClientModule("Velocity", Category.COMBAT) {
private val delay by intRange("Delay", 0..0, 0..40, "ticks")
private val pauseOnFlag by int("PauseOnFlag", 0, 0..20, "ticks")

private var pause = 0
internal var pause = 0

@Suppress("unused")
private val countHandler = handler<GameTickEvent>(ignoreNotRunning = true) {
private val pauseHandler = handler<GameTickEvent> {
if (pause > 0) {
pause--
}
Expand All @@ -76,7 +76,7 @@ object ModuleVelocity : ClientModule("Velocity", Category.COMBAT) {
private val packetHandler = sequenceHandler<PacketEvent>(priority = 1) { event ->
val packet = event.packet

if (!event.original) {
if (!event.original || pause > 0) {
return@sequenceHandler
}

Expand Down Expand Up @@ -105,7 +105,4 @@ object ModuleVelocity : ClientModule("Velocity", Category.COMBAT) {
}
}

override val running: Boolean
get() = super.running && pause == 0

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ import net.ccbluex.liquidbounce.features.module.modules.combat.velocity.ModuleVe
* Velocity for AAC4.4.2, pretty sure, it works on other versions
*/

internal object VelocityAAC442 : Choice("AAC4.4.2") {

override val parent: ChoiceConfigurable<Choice>
get() = modes
internal object VelocityAAC442 : VelocityMode("AAC4.4.2") {

private val reduce by float("Reduce", 0.62f, 0f..1f)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ import net.minecraft.network.packet.s2c.play.EntityVelocityUpdateS2CPacket
* BlocksMC velocity
* @author liquidsquid1
*/
internal object VelocityBlocksMC : Choice("BlocksMC") {

override val parent: ChoiceConfigurable<Choice>
get() = modes
internal object VelocityBlocksMC : VelocityMode("BlocksMC") {

@Suppress("unused")
private val packetHandler = handler<PacketEvent> { event ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,10 @@
*/
package net.ccbluex.liquidbounce.features.module.modules.combat.velocity.mode

import net.ccbluex.liquidbounce.config.types.Choice
import net.ccbluex.liquidbounce.config.types.ChoiceConfigurable
import net.ccbluex.liquidbounce.event.events.AttackEntityEvent
import net.ccbluex.liquidbounce.event.handler
import net.ccbluex.liquidbounce.features.module.modules.combat.velocity.ModuleVelocity.modes

internal object VelocityDexland : Choice("Dexland") {

override val parent: ChoiceConfigurable<Choice>
get() = modes
internal object VelocityDexland : VelocityMode("Dexland") {

private val hReduce by float("HReduce", 0.3f, 0f..1f)
private val times by int("AttacksToWork", 4, 1..10)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ import net.minecraft.network.packet.s2c.play.ExplosionS2CPacket
*
* https://github.com/GrimAnticheat/Grim/issues/1133
*/
internal object VelocityExemptGrim117 : Choice("ExemptGrim117") {

override val parent: ChoiceConfigurable<Choice>
get() = modes
internal object VelocityExemptGrim117 : VelocityMode("ExemptGrim117") {

private var alternativeBypass by boolean("AlternativeBypass", true)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ import net.ccbluex.liquidbounce.utils.entity.moving
* Works because of a silly exemption from Hylex
* @author @liquidsquid1
*/
object VelocityHylex : Choice("Hylex") {
override val parent: ChoiceConfigurable<Choice>
get() = modes
object VelocityHylex : VelocityMode("Hylex") {

@Suppress("unused")
private val attackHandler = handler<AttackEntityEvent> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ import net.ccbluex.liquidbounce.event.tickHandler
import net.ccbluex.liquidbounce.features.module.modules.combat.velocity.ModuleVelocity.modes
import net.minecraft.client.gui.screen.ingame.InventoryScreen

object VelocityIntave : Choice("Intave") {
override val parent: ChoiceConfigurable<Choice>
get() = modes
object VelocityIntave : VelocityMode("Intave") {

private class ReduceOnAttack(parent: EventListener?) : ToggleableConfigurable(
parent, "ReduceOnAttack",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@ import net.ccbluex.liquidbounce.features.module.modules.combat.velocity.ModuleVe
/**
* Jump Reset mode. A technique most players use to minimize the amount of knockback they get.
*/
internal object VelocityJumpReset : Choice("JumpReset") {

override val parent: ChoiceConfigurable<Choice>
get() = modes
internal object VelocityJumpReset : VelocityMode("JumpReset") {

object JumpByReceivedHits : ToggleableConfigurable(ModuleVelocity, "JumpByReceivedHits", false) {
val hitsUntilJump by int("HitsUntilJump", 2, 0..10)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package net.ccbluex.liquidbounce.features.module.modules.combat.velocity.mode

import net.ccbluex.liquidbounce.config.types.Choice
import net.ccbluex.liquidbounce.config.types.ChoiceConfigurable
import net.ccbluex.liquidbounce.features.module.modules.combat.velocity.ModuleVelocity.modes
import net.ccbluex.liquidbounce.features.module.modules.combat.velocity.ModuleVelocity.pause

abstract class VelocityMode(name: String) : Choice(name) {

override val parent: ChoiceConfigurable<VelocityMode>
get() = modes

override val running: Boolean
get() = super.running && pause == 0

}
Loading

0 comments on commit 5141201

Please sign in to comment.