From 406c5a075cddbc329e028cad417d7a720c5eae73 Mon Sep 17 00:00:00 2001 From: Water-OR Date: Sat, 12 Apr 2025 23:06:10 +0800 Subject: [PATCH 1/6] refactor: remove some type parameter from chat-component utils --- .../chat_component/ChatComponentBuildScope.kt | 25 +++++++++++-------- .../chat_component/ChatComponentUtils.kt | 8 +++--- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatComponentBuildScope.kt b/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatComponentBuildScope.kt index 96e3119..dd432b8 100644 --- a/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatComponentBuildScope.kt +++ b/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatComponentBuildScope.kt @@ -28,26 +28,29 @@ object ChatComponentBuildScope { get() = ChatComponentEmpty() @Suppress("UNUSED") - fun ChatComponentBuildScope.empty( - configure: ChatStyle.() -> Unit + fun empty( + configure: ChatStyle.() -> Unit = {} ): IChatComponent = empty withChatStyle configure - infix operator fun C.plus( + infix operator fun IChatComponent.plus( o: String - ): C = - apply { appendSibling(ChatComponentText(o)) } + ): IChatComponent = + plus { it.appendSibling(o()) } - infix operator fun C.plus( + infix operator fun IChatComponent.plus( o: IChatComponent - ): C = - apply { appendSibling(o) } + ): IChatComponent = + plus { it.appendSibling(o) } - inline infix operator fun C.plus( - configure: (C) -> Unit - ): C = + inline infix operator fun IChatComponent.plus( + configure: (IChatComponent) -> Unit + ): IChatComponent = apply(configure) + operator fun String.invoke(): IChatComponent = + ChatComponentText(this) + inline infix operator fun String.invoke( configure: ChatStyle.() -> Unit ): IChatComponent = diff --git a/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatComponentUtils.kt b/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatComponentUtils.kt index 2cb12c3..a939587 100644 --- a/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatComponentUtils.kt +++ b/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatComponentUtils.kt @@ -30,13 +30,13 @@ inline fun buildChat( ): IChatComponent = ChatComponentBuildScope.builder() -inline fun C.withChatStyle( +inline fun IChatComponent.withChatStyle( chatStyle: ChatStyle, configure: ChatStyle.() -> Unit -): C = +): IChatComponent = apply { this.chatStyle = chatStyle.apply(configure) } -inline infix fun C.withChatStyle( +inline infix fun IChatComponent.withChatStyle( configure: ChatStyle.() -> Unit -): C = +): IChatComponent = withChatStyle(chatStyle, configure) \ No newline at end of file From 4e0e71d76181919d0b7c634d63740f20d3dd7eed Mon Sep 17 00:00:00 2001 From: Water-OR Date: Sun, 13 Apr 2025 00:34:46 +0800 Subject: [PATCH 2/6] bump: upgrade dependency com.github.Water-OR:llvg-utils to version 1.8 --- build-mod.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-mod.gradle.kts b/build-mod.gradle.kts index 2521bea..7377c0e 100644 --- a/build-mod.gradle.kts +++ b/build-mod.gradle.kts @@ -132,7 +132,7 @@ dependencies { runtimeOnly("com.github.boopwdn:EnchAddons:main-SNAPSHOT:dev") { isChanging = true } - shade("com.github.Water-OR:llvg-utils:1.6") + shade("com.github.Water-OR:llvg-utils:1.8") if (platform.isLegacyForge) { compileOnly("org.spongepowered:mixin:0.7.11-SNAPSHOT") From 42c38e7ea717ebe06a092f1746fed4b04148315c Mon Sep 17 00:00:00 2001 From: Water-OR Date: Mon, 14 Apr 2025 19:08:13 +0800 Subject: [PATCH 3/6] feat: improve exec-event manager --- src/main/kotlin/net/llvg/exec/ExeClient.kt | 2 + .../exec/api/command/ExeCCommandManager.kt | 12 + .../llvg/exec/api/event/ExeCEventListener.kt | 2 +- .../llvg/exec/api/event/ExeCEventManager.kt | 313 ++++++++++-------- .../net/llvg/exec/api/event/ExeCEventUtils.kt | 12 + .../exec/api/feature/ExeCFeatureManager.kt | 12 + .../net/llvg/exec/utils/registry/Registry.kt | 26 +- 7 files changed, 225 insertions(+), 154 deletions(-) diff --git a/src/main/kotlin/net/llvg/exec/ExeClient.kt b/src/main/kotlin/net/llvg/exec/ExeClient.kt index aea7443..e995258 100644 --- a/src/main/kotlin/net/llvg/exec/ExeClient.kt +++ b/src/main/kotlin/net/llvg/exec/ExeClient.kt @@ -21,6 +21,7 @@ package net.llvg.exec import net.llvg.exec.api.command.ExeCCommandManager import net.llvg.exec.api.config.ExeClientConfig +import net.llvg.exec.api.event.ExeCEventManager import net.llvg.exec.api.feature.ExeCFeatureManager import net.llvg.exec.utils.classNameLogger import net.llvg.exec.vanilla.utils.chat_component.ChatComponentBuildScope @@ -35,6 +36,7 @@ object ExeClient { @JvmStatic fun initialize() { + ExeCEventManager ExeClientConfig ExeCFeatureManager ExeCCommandManager diff --git a/src/main/kotlin/net/llvg/exec/api/command/ExeCCommandManager.kt b/src/main/kotlin/net/llvg/exec/api/command/ExeCCommandManager.kt index ff57db9..dc3704e 100644 --- a/src/main/kotlin/net/llvg/exec/api/command/ExeCCommandManager.kt +++ b/src/main/kotlin/net/llvg/exec/api/command/ExeCCommandManager.kt @@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableSortedMap import net.llvg.exec.ExeClient import net.llvg.exec.utils.classNameLogger +import net.llvg.exec.utils.registry.RegisterEvent import net.llvg.exec.utils.registry.Registry import net.llvg.exec.vanilla.utils.chat_component.buildChat import net.llvg.exec.vanilla.utils.player @@ -38,6 +39,17 @@ object ExeCCommandManager : Registry( ExeCCommandHelp, ExeCCommandThrow ) { + override fun event( + elements: MutableList + ): RegisterEvent = + Event.Impl { + elements += it + } + + sealed interface Event : RegisterEvent { + fun interface Impl : Event + } + init { ClientCommandHandler.instance.registerCommand(ExeCInternalCommand) } diff --git a/src/main/kotlin/net/llvg/exec/api/event/ExeCEventListener.kt b/src/main/kotlin/net/llvg/exec/api/event/ExeCEventListener.kt index daa77cf..8285976 100644 --- a/src/main/kotlin/net/llvg/exec/api/event/ExeCEventListener.kt +++ b/src/main/kotlin/net/llvg/exec/api/event/ExeCEventListener.kt @@ -23,7 +23,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope sealed class ExeCEventListener( - private val owner: ExeCEventListenable, + val owner: ExeCEventListenable, private val always: Boolean, private val priority: Int ) : Comparable> { diff --git a/src/main/kotlin/net/llvg/exec/api/event/ExeCEventManager.kt b/src/main/kotlin/net/llvg/exec/api/event/ExeCEventManager.kt index e739fa1..129b879 100644 --- a/src/main/kotlin/net/llvg/exec/api/event/ExeCEventManager.kt +++ b/src/main/kotlin/net/llvg/exec/api/event/ExeCEventManager.kt @@ -19,41 +19,38 @@ package net.llvg.exec.api.event -import com.google.common.collect.ImmutableList import java.util.TreeSet +import java.util.concurrent.locks.Lock +import java.util.concurrent.locks.ReadWriteLock +import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.concurrent.withLock import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import net.llvg.loliutils.exception.cast +import net.llvg.loliutils.exception.tryExtend import org.apache.logging.log4j.LogManager private typealias EventType = Class private typealias BlockListener = ExeCEventListener.Block private typealias AsyncListener = ExeCEventListener.Async -private typealias BlockListenerSet = MutableSet> -private typealias AsyncListenerSet = MutableSet> object ExeCEventManager { private val logger = LogManager.getLogger(ExeCEventManager::class.java.simpleName) - private val blockNormalStorage: MutableMap = HashMap() - private val blockForcedStorage: MutableMap = HashMap() - private val asyncNormalStorage: MutableMap = HashMap() - private val asyncForcedStorage: MutableMap = HashMap() - - private val cache: MutableMap, List>> = HashMap() - - private operator fun > MutableMap>.invoke( - key: EventType - ): MutableSet = - synchronized(this) { - getOrPut(key) { TreeSet() } - } + private val blockStorage = TypedStorage>() + private val asyncStorage = TypedStorage>() @JvmStatic fun register( @@ -62,23 +59,8 @@ object ExeCEventManager { listener: ExeCEventListener<*> ) { when (listener) { - is BlockListener -> { - val storage = if (forced) blockForcedStorage else blockNormalStorage - val listeners = storage(type) - - synchronized(listeners) { - listeners.add(listener) - } - } - - is AsyncListener -> { - val storage = if (forced) asyncForcedStorage else asyncNormalStorage - val listeners = storage(type) - - synchronized(listeners) { - listeners.add(listener) - } - } + is BlockListener -> blockStorage.put(type, listener, forced) + is AsyncListener -> asyncStorage.put(type, listener, forced) } } @@ -90,19 +72,6 @@ object ExeCEventManager { event: ExeCEvent, wait: Boolean ) { - val job = scope.launch { - post(type, event) - } - - if (wait) runBlocking { - job.join() - } - } - - private fun CoroutineScope.post( - type: EventType, - event: ExeCEvent - ): Job { if (!type.isInstance(event)) { val exception = IllegalArgumentException( "event $event(loader=${event.javaClass.classLoader}) is not in type $type(loader=${type.classLoader})" @@ -110,113 +79,185 @@ object ExeCEventManager { logger.error(exception) throw exception } - val (blockCache, asyncCache) = cache.getOrPut(type) { - val blockBuilder = ImmutableList.builder() - val asyncBuilder = ImmutableList.builder() + + val asyncListeners = asyncStorage[type] + val blockListeners = blockStorage[type] + + val job = scope.launch(Dispatchers.Default) { + val jobs: MutableList = ArrayList() - blockBuilder.add(blockForcedStorage(type)) - asyncBuilder.add(asyncForcedStorage(type)) + asyncListeners.traverse { + jobs += launch { + trigger(it, event) + } + } - forEachSuperClass(type) { - blockBuilder.add(blockNormalStorage(it)) - asyncBuilder.add(asyncNormalStorage(it)) + blockListeners.traverse { + trigger(it, event) } - blockBuilder.build() to asyncBuilder.build() + jobs.joinAll() } - val curr = Dispatchers.Default - return launch(curr) { - val jobs: MutableList = ArrayList() - asyncCache.forEachFlat { - launch(it.dispatcher) { - try { - cast>(it).run { - action(event) - } - } catch (e: Throwable) { - logger.info( - "An error occur during active async listener {} of event {}", - it, - type, - e - ) - } - }.let(jobs::add) + if (wait) runBlocking { + job.join() + } + } + + private fun CoroutineScope.trigger( + listener: AsyncListener<*>, + event: ExeCEvent + ): Job = launch(listener.dispatcher) { + try { + cast>(listener).run { + action(event) } - - blockCache.forEachFlat { - try { - cast>(it).action(event) - } catch (e: Throwable) { - logger.info( - "An error occur during active block listener {} of event {}", - it, - type, - e - ) - } + } catch (e: Throwable) { + logger.info( + "An error occur during active async listener own by {} of event {}", + listener.owner, + event, + e + ) + } + } + + private fun trigger( + listener: BlockListener<*>, + event: ExeCEvent + ) { + try { + cast>(listener).run { + action(event) } - - jobs.joinAll() + } catch (e: Throwable) { + logger.info( + "An error occur during active block listener own by {} of event {}", + listener.owner, + event, + e + ) } } } -private fun forEachSuperClass( - clazz: Class, - action: (Class) -> Unit -) { - fun CoroutineScope.forEachInterface( - visited: MutableSet>, - clazz: Class, - action: (Class) -> Unit - ): Job? { - if (clazz in visited) return null - return launch { - val jobs: MutableList = ArrayList() - - launch { - action(clazz) - }.let(jobs::add) - - clazz.interfaces.forEach { - launch { - if (ExeCEvent::class.java.isAssignableFrom(it)) forEachInterface( - visited, - it.asSubclass(ExeCEvent::class.java), - action - )?.join() - }.let(jobs::add) +private interface Traversable { + fun traverse( + action: (T) -> Unit + ) +} + +private class CompactTraversable( + vararg val traversables: Traversable +) : Traversable { + override fun traverse( + action: (T) -> Unit + ) { + for (it in traversables) { + it.traverse(action) + } + } +} + +private class IterableWithLock( + private val lock: Lock, + private val iterable: Iterable +) : Traversable { + override fun traverse( + action: (T) -> Unit + ) { + lock.withLock { + for (it in iterable) { + action(it) } - - jobs.joinAll() } } +} + +private class TypedStorage { + private val forced: MutableMap>> = HashMap() + private val normal: MutableMap>> = HashMap() - runBlocking { - val visited: MutableSet> = HashSet() - - var type: Class = clazz - val jobs: MutableList = ArrayList() + private val cacheVals: MutableMap> = HashMap() + private val cacheLock: MutableMap = HashMap() + + private infix fun MutableMap>>.query( + key: EventType + ): Pair> = + get(key) ?: synchronized(this) { + getOrPut(key) { ReentrantReadWriteLock() to TreeSet() } + } + + fun put( + key: EventType, + value: T, + force: Boolean + ) { + val (lock, listeners) = (if (force) forced else normal) query key + lock.writeLock().withLock { + listeners += value + } + } + + operator fun get( + key: EventType + ): Traversable { + val cache: Traversable - while (true) { - launch { - forEachInterface(visited, type, action)?.join() - }.let(jobs::add) - - val supT = type.superclass ?: break - if (ExeCEvent::class.java.isAssignableFrom(supT)) { - type = supT.asSubclass(ExeCEvent::class.java) - } else break + runBlocking(Dispatchers.Default) { + cache = cacheOrBuild(key) } - jobs.joinAll() + val (forceLock, forceListener) = forced[key] ?: return cache + + return CompactTraversable( + IterableWithLock( + forceLock.readLock(), + forceListener + ), + cache + ) } -} - -private inline fun > List>.forEachFlat( - action: (L) -> Unit -) { - for (it in this) for (listener in it) action(listener) -} + + private fun cacheLock( + key: EventType + ): Mutex = + cacheLock[key] ?: synchronized(cacheLock) { + cacheLock.getOrPut(key) { Mutex() } + } + + private suspend fun cacheOrBuild( + key: EventType + ): Traversable = + cacheVals[key] ?: cacheLock(key).withLock { + cacheVals[key]?.let { + return it + } + + val deferredList: MutableList>> = ArrayList() + coroutineScope { + deferredList += async { + val (lock, listeners) = normal query key + IterableWithLock(lock.readLock(), listeners) + } + + key.superclass?.tryExtend()?.let { + deferredList += async { + cacheOrBuild(it) + } + } + + for (i in key.interfaces) { + val it = i.tryExtend() ?: continue + + deferredList += async { + cacheOrBuild(it) + } + } + } + + val result = CompactTraversable(*deferredList.awaitAll().toTypedArray()) + cacheVals.putIfAbsent(key, result) + return result + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/llvg/exec/api/event/ExeCEventUtils.kt b/src/main/kotlin/net/llvg/exec/api/event/ExeCEventUtils.kt index 0e8c8be..bb0e876 100644 --- a/src/main/kotlin/net/llvg/exec/api/event/ExeCEventUtils.kt +++ b/src/main/kotlin/net/llvg/exec/api/event/ExeCEventUtils.kt @@ -72,4 +72,16 @@ inline fun E.post( this, wait ) +} + +@Suppress("UNUSED") +fun E.post( + type: Class, + wait: Boolean +) { + ExeCEventManager.post( + type, + this, + wait + ) } \ No newline at end of file diff --git a/src/main/kotlin/net/llvg/exec/api/feature/ExeCFeatureManager.kt b/src/main/kotlin/net/llvg/exec/api/feature/ExeCFeatureManager.kt index 8f1d48b..01bc10e 100644 --- a/src/main/kotlin/net/llvg/exec/api/feature/ExeCFeatureManager.kt +++ b/src/main/kotlin/net/llvg/exec/api/feature/ExeCFeatureManager.kt @@ -20,11 +20,23 @@ package net.llvg.exec.api.feature import net.llvg.exec.feature.freecam.FreeCam +import net.llvg.exec.utils.registry.RegisterEvent import net.llvg.exec.utils.registry.Registry object ExeCFeatureManager : Registry>( FreeCam ) { + override fun event( + elements: MutableList> + ): RegisterEvent> = + Event.Impl { + elements += it + } + + sealed interface Event : RegisterEvent> { + fun interface Impl : Event + } + init { elements.forEach { it.initialize() diff --git a/src/main/kotlin/net/llvg/exec/utils/registry/Registry.kt b/src/main/kotlin/net/llvg/exec/utils/registry/Registry.kt index 40888be..66d6256 100644 --- a/src/main/kotlin/net/llvg/exec/utils/registry/Registry.kt +++ b/src/main/kotlin/net/llvg/exec/utils/registry/Registry.kt @@ -25,29 +25,21 @@ import net.llvg.exec.api.event.post abstract class Registry( vararg initial: T ) { - protected open fun event(): InnerRegisterEvent = - object : InnerRegisterEvent { - override val elements: MutableList = ArrayList() - } + protected abstract fun event( + elements: MutableList + ): RegisterEvent protected val elements: List = ImmutableList .builder() .apply { add(*initial) - val event = event() - event.post(true) - addAll(event.elements) - } - .build() - - protected interface InnerRegisterEvent : RegisterEvent { - val elements: MutableList + val result: MutableList = ArrayList() - override fun register( - element: T - ) { - elements += element - } + val event = event(result) + event.post(event.javaClass, true) + + addAll(result) } + .build() } \ No newline at end of file From e64f6d412ebee4fa652ca8e4177a10ebee40c556 Mon Sep 17 00:00:00 2001 From: Water-OR Date: Mon, 14 Apr 2025 21:52:18 +0800 Subject: [PATCH 4/6] refactor: restructure chat-component build-scope --- src/main/kotlin/net/llvg/exec/ExeClient.kt | 34 +-- .../command/ExeCCommandChatComponentScope.kt | 38 ++-- .../exec/api/command/ExeCCommandManager.kt | 203 +++++++++--------- .../llvg/exec/api/command/ExeCCommandUtils.kt | 53 +++-- .../net/llvg/exec/api/feature/ExeCFeature.kt | 13 +- .../net/llvg/exec/feature/freecam/FreeCam.kt | 28 +-- .../vanilla/utils/chat_component/ChatColor.kt | 45 ++++ .../chat_component/ChatComponentBuildScope.kt | 71 ++++-- .../chat_component/ChatComponentUtils.kt | 12 +- 9 files changed, 300 insertions(+), 197 deletions(-) create mode 100644 src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatColor.kt diff --git a/src/main/kotlin/net/llvg/exec/ExeClient.kt b/src/main/kotlin/net/llvg/exec/ExeClient.kt index e995258..927aa16 100644 --- a/src/main/kotlin/net/llvg/exec/ExeClient.kt +++ b/src/main/kotlin/net/llvg/exec/ExeClient.kt @@ -24,10 +24,10 @@ import net.llvg.exec.api.config.ExeClientConfig import net.llvg.exec.api.event.ExeCEventManager import net.llvg.exec.api.feature.ExeCFeatureManager import net.llvg.exec.utils.classNameLogger +import net.llvg.exec.vanilla.utils.chat_component.ChatColor import net.llvg.exec.vanilla.utils.chat_component.ChatComponentBuildScope import net.llvg.exec.vanilla.utils.chat_component.buildChat import net.llvg.exec.vanilla.utils.player -import net.minecraft.util.EnumChatFormatting import net.minecraft.util.IChatComponent object ExeClient { @@ -46,9 +46,11 @@ object ExeClient { message: IChatComponent ) { buildChat { - empty + - execPrefix + - message + of( + empty() + ..execPrefix + ..message + ) }.run(player::addChatMessage) } @@ -60,16 +62,16 @@ object ExeClient { } private val execPrefix = buildChat { - empty + - buildChat { - empty { - bold = true - color = EnumChatFormatting.WHITE - } + - "[" + - "Exe Client" { - color = EnumChatFormatting.AQUA - } + - "] " - } + of( + empty() + ..of( + empty() + .`--color`(ChatColor.WHITE) + .`--bold`(true) + .."[" + .."Exe Client"() + .`--color`(ChatColor.AQUA) + .."] " + ) + ) } \ No newline at end of file diff --git a/src/main/kotlin/net/llvg/exec/api/command/ExeCCommandChatComponentScope.kt b/src/main/kotlin/net/llvg/exec/api/command/ExeCCommandChatComponentScope.kt index ec8535d..c5ee954 100644 --- a/src/main/kotlin/net/llvg/exec/api/command/ExeCCommandChatComponentScope.kt +++ b/src/main/kotlin/net/llvg/exec/api/command/ExeCCommandChatComponentScope.kt @@ -19,34 +19,36 @@ package net.llvg.exec.api.command -import net.minecraft.util.ChatStyle -import net.minecraft.util.EnumChatFormatting +import net.llvg.exec.vanilla.utils.chat_component.ChatColor +import net.llvg.exec.vanilla.utils.chat_component.ChatComponentBuildScope.`--color` +import net.llvg.exec.vanilla.utils.chat_component.style +import net.minecraft.util.IChatComponent +@Suppress("ObjectPropertyName") object ExeCCommandChatComponentScope { - val ChatStyle.styleCommandName: Unit - get() { - color = EnumChatFormatting.GOLD + val IChatComponent.`--style command-name`: IChatComponent + get() = style { + `--color`(ChatColor.GOLD) bold = true } - val ChatStyle.styleCommandText: Unit - get() { - color = EnumChatFormatting.YELLOW + val IChatComponent.`--style command-text`: IChatComponent + get() = style { + `--color`(ChatColor.YELLOW) } - val ChatStyle.styleParameter: Unit - get() { - color = EnumChatFormatting.LIGHT_PURPLE + val IChatComponent.`--style parameter`: IChatComponent + get() = style { + `--color`(ChatColor.LIGHT_PURPLE) } - val ChatStyle.styleSplitMark: Unit - get() { - color = EnumChatFormatting.GRAY + val IChatComponent.`--style split-mark`: IChatComponent + get() = style { + `--color`(ChatColor.GRAY) } - val ChatStyle.styleWarn: Unit - get() { - color = EnumChatFormatting.RED + val IChatComponent.`--style warn`: IChatComponent + get() = style { + `--color`(ChatColor.RED) } - } diff --git a/src/main/kotlin/net/llvg/exec/api/command/ExeCCommandManager.kt b/src/main/kotlin/net/llvg/exec/api/command/ExeCCommandManager.kt index dc3704e..080c481 100644 --- a/src/main/kotlin/net/llvg/exec/api/command/ExeCCommandManager.kt +++ b/src/main/kotlin/net/llvg/exec/api/command/ExeCCommandManager.kt @@ -25,13 +25,13 @@ import net.llvg.exec.ExeClient import net.llvg.exec.utils.classNameLogger import net.llvg.exec.utils.registry.RegisterEvent import net.llvg.exec.utils.registry.Registry +import net.llvg.exec.vanilla.utils.chat_component.ChatColor import net.llvg.exec.vanilla.utils.chat_component.buildChat import net.llvg.exec.vanilla.utils.player import net.llvg.loliutils.iterator.subArray import net.minecraft.command.ICommand import net.minecraft.command.ICommandSender import net.minecraft.util.BlockPos -import net.minecraft.util.EnumChatFormatting import net.minecraft.util.IChatComponent import net.minecraftforge.client.ClientCommandHandler @@ -69,21 +69,19 @@ object ExeCCommandManager : Registry( .also { if (it === null) ExeClient.send { with(ExeCCommandChatComponentScope) { - empty { - styleWarn - } + - "No such sub-command starts with " + - name { - styleCommandName - } + - ", use " + - "/exec" { - styleCommandText - } + - " " + - "help" { - styleCommandName - } + of( + empty() + .`--style warn` + .."No such sub-command starts with " + ..name() + .`--style command-name` + ..", use " + .."/exec"() + .`--style command-text` + .." " + .."help"() + .`--style command-name` + ) } } } @@ -100,15 +98,15 @@ object ExeCCommandManager : Registry( private fun sendUsage() { ExeClient.send { with(ExeCCommandChatComponentScope) { - empty + - "Use " + - "/exec" { - styleCommandText - } + - " " + - "help" { - styleCommandName - } + of( + empty() + .."Use " + .."/exec"() + .`--style command-text` + .." " + .."help"() + .`--style command-name` + ) } } } @@ -144,14 +142,14 @@ object ExeCCommandManager : Registry( logger.info("An error occur during processing command {}", args[0], e) ExeClient.send { with(ExeCCommandChatComponentScope) { - empty { - color = EnumChatFormatting.DARK_RED - } + - "An error occur during processing command " + - args[0] { - styleCommandName - } + - ", check log for more information" + of( + empty() + .`--color`(ChatColor.DARK_RED) + .."An error occur during processing command " + ..args[0]() + .`--style command-name` + ..", check log for more information" + ) } } } @@ -173,14 +171,14 @@ object ExeCCommandManager : Registry( logger.info("An error occur during completing tab by command {}", args[0], e) ExeClient.send { with(ExeCCommandChatComponentScope) { - empty { - color = EnumChatFormatting.DARK_RED - } + - "An error occur during completing tab by command " + - args[0] { - styleCommandName - } + - ", check log for more information" + of( + empty() + .`--color`(ChatColor.DARK_RED) + .."An error occur during completing tab by command " + ..args[0]() + .`--style command-name` + ..", check log for more information" + ) } } emptyList() @@ -203,44 +201,38 @@ object ExeCCommandManager : Registry( override val usage: IChatComponent = buildChat { with(ExeCCommandChatComponentScope) { - empty + // " - help | Display all known commands" - " " + - "-" { - styleSplitMark - } + - " " + - "help" { - styleCommandName - } + - " " + - "|" { - styleSplitMark - } + - " " + - "Display all known commands" + - "\n" + // " - help | Display usage of command " - " " + - "-" { - styleSplitMark - } + - " " + - "help" { - styleCommandName - } + - " " + - "" { - styleParameter - } + - " " + - "|" { - styleSplitMark - } + - " " + - "Display usage of command" + - " " + - "" { - styleParameter - } + of( + empty() // " - help | Display all known commands" + .." " + .."-"() + .`--style split-mark` + .." " + .."help"() + .`--style command-name` + .." " + .."|"() + .`--style split-mark` + .." " + .."Display all known commands" + .."\n" // " - help | Display usage of command " + .." " + .."-"() + .`--style split-mark` + .." " + .."help"() + .`--style command-name` + .." " + ..""() + .`--style parameter` + .." " + .."|"() + .`--style split-mark` + .." " + .."Display usage of command" + .." " + ..""() + .`--style parameter` + ) } } @@ -249,19 +241,19 @@ object ExeCCommandManager : Registry( ) { if (args.isEmpty()) ExeClient.send { with(ExeCCommandChatComponentScope) { - empty + - "Known commands:" + - { - for (name in commands.keys) { - it + - "\n - " { - styleSplitMark - } + - name { - styleCommandName - } + of( + empty() + .."Known commands:" + ..{ + for (name in commands.keys) of( + it + .."\n - "() + .`--style split-mark` + ..name() + .`--style command-name` + ) } - } + ) } } else { command(args[0])?.sendUsage() @@ -282,21 +274,20 @@ object ExeCCommandManager : Registry( override val usage: IChatComponent = buildChat { with(ExeCCommandChatComponentScope) { - empty + // " - throw | Throw a runtime-exception" - " " + - "-" { - styleSplitMark - } + - " " + - "throw" { - styleCommandName - } + - " " + - "|" { - styleSplitMark - } + - " " + - "Throw a runtime-exception" + of( + empty() // " - throw | Throw a runtime-exception" + .." " + .."-"() + .`--style split-mark` + .." " + .."throw"() + .`--style command-name` + .." " + .."|"() + .`--style split-mark` + .." " + .."Throw a runtime-exception" + ) } } diff --git a/src/main/kotlin/net/llvg/exec/api/command/ExeCCommandUtils.kt b/src/main/kotlin/net/llvg/exec/api/command/ExeCCommandUtils.kt index 165c9bb..640a41a 100644 --- a/src/main/kotlin/net/llvg/exec/api/command/ExeCCommandUtils.kt +++ b/src/main/kotlin/net/llvg/exec/api/command/ExeCCommandUtils.kt @@ -28,13 +28,14 @@ import net.minecraft.util.IChatComponent fun ExeCCommand.sendUsage() { ExeClient.send { with(ExeCCommandChatComponentScope) { - empty + - "Following are the usage of command " + - name { - styleCommandName - } + - ": \n" + - usage + of( + empty() + .."Following are the usage of command " + ..name() + .`--style command-name` + ..": \n" + ..usage + ) } } } @@ -43,13 +44,13 @@ fun ExeCCommand.sendUsage() { fun ExeCCommand.sendWrongUsage() { ExeClient.send { with(ExeCCommandChatComponentScope) { - empty + - "Incorrect use of command " { - styleWarn - } + - name { - styleCommandName - } + of( + empty() + .."Incorrect use of command "() + .`--style warn` + ..name() + .`--style command-name` + ) } } sendUsage() @@ -58,15 +59,21 @@ fun ExeCCommand.sendWrongUsage() { @Suppress("UNUSED") val Map.combineUsages: IChatComponent get() = buildChat { - empty + { - if (isNotEmpty()) { + of( + empty() + ..{ + if (isEmpty()) return it + val i = values.iterator() - it + i.next().usage - while (i.hasNext()) { - it + - "\n" + - i.next().usage - } + of( + it + ..i.next().usage + ) + while (i.hasNext()) of( + it + .."\n" + ..i.next().usage + ) } - } + ) } \ No newline at end of file diff --git a/src/main/kotlin/net/llvg/exec/api/feature/ExeCFeature.kt b/src/main/kotlin/net/llvg/exec/api/feature/ExeCFeature.kt index 13ad172..5e21076 100644 --- a/src/main/kotlin/net/llvg/exec/api/feature/ExeCFeature.kt +++ b/src/main/kotlin/net/llvg/exec/api/feature/ExeCFeature.kt @@ -21,9 +21,10 @@ package net.llvg.exec.api.feature import net.llvg.exec.api.config.ExeCFeatureConfig import net.llvg.exec.api.event.ExeCEventListenable -import net.llvg.exec.vanilla.utils.chat_component.withChatStyle +import net.llvg.exec.vanilla.utils.chat_component.ChatColor +import net.llvg.exec.vanilla.utils.chat_component.color +import net.llvg.exec.vanilla.utils.chat_component.style import net.minecraft.util.ChatComponentText -import net.minecraft.util.EnumChatFormatting import net.minecraft.util.IChatComponent interface ExeCFeature> : ExeCEventListenable { @@ -36,14 +37,14 @@ interface ExeCFeature> : ExeCEventListenable { companion object { val MESSAGE_ENABLED: IChatComponent = ChatComponentText("Enabled") - .withChatStyle { - color = EnumChatFormatting.GREEN + .style { + color(ChatColor.GREEN) } val MESSAGE_DISABLED: IChatComponent = ChatComponentText("Disabled") - .withChatStyle { - color = EnumChatFormatting.RED + .style { + color(ChatColor.RED) } } } \ No newline at end of file diff --git a/src/main/kotlin/net/llvg/exec/feature/freecam/FreeCam.kt b/src/main/kotlin/net/llvg/exec/feature/freecam/FreeCam.kt index 832321d..74fa555 100644 --- a/src/main/kotlin/net/llvg/exec/feature/freecam/FreeCam.kt +++ b/src/main/kotlin/net/llvg/exec/feature/freecam/FreeCam.kt @@ -29,13 +29,13 @@ import net.llvg.exec.mixin.inject.InjectNetHandlerPlayClient import net.llvg.exec.vanilla.event.EntityLivingBaseEvent import net.llvg.exec.vanilla.event.PacketEvent import net.llvg.exec.vanilla.event.WorldClientEvent +import net.llvg.exec.vanilla.utils.chat_component.ChatColor import net.llvg.exec.vanilla.utils.chat_component.buildChat import net.llvg.exec.vanilla.utils.mc import net.llvg.exec.vanilla.utils.player import net.llvg.exec.vanilla.utils.world import net.llvg.loliutils.exception.cast import net.minecraft.entity.Entity -import net.minecraft.util.EnumChatFormatting import net.minecraft.util.MovementInput import net.minecraft.util.MovementInputFromOptions @@ -46,9 +46,8 @@ object FreeCam : ExeCFeature { if (FreeCamConfig.disableOnDamage && e.entity.health > e.health) { if (FreeCamConfig.sendMessage) ExeClient.send { - "You took damage!" { - color = EnumChatFormatting.YELLOW - } + "You took damage!"() + .`--color`(ChatColor.YELLOW) } disable() @@ -58,9 +57,8 @@ object FreeCam : ExeCFeature { onEvent(Dispatchers.Default) { e: PacketEvent.Server.S43.Pre -> if (FreeCamConfig.disableOnSeverCameraChange) { if (FreeCamConfig.sendMessage) ExeClient.send { - "Server is trying to change your camera entity!" { - color = EnumChatFormatting.YELLOW - } + "Server is trying to change your camera entity!"() + .`--color`(ChatColor.YELLOW) } disable() @@ -168,15 +166,19 @@ object FreeCam : ExeCFeature { private val toggleLock = Any() private val MESSAGE_ENABLED = buildChat { - empty + - "Free Camera " + - ExeCFeature.MESSAGE_ENABLED + of( + empty() + .."Free Camera " + ..ExeCFeature.MESSAGE_ENABLED + ) } private val MESSAGE_DISABLED = buildChat { - empty + - "Free Camera " + - ExeCFeature.MESSAGE_DISABLED + of( + empty() + .."Free Camera " + ..ExeCFeature.MESSAGE_DISABLED + ) } @Synchronized diff --git a/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatColor.kt b/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatColor.kt new file mode 100644 index 0000000..216beac --- /dev/null +++ b/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatColor.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2025 Water-OR + * + * This file is part of ExeClient + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.llvg.exec.vanilla.utils.chat_component + +import net.minecraft.util.EnumChatFormatting + +@Suppress("UNUSED") +enum class ChatColor( + val format: EnumChatFormatting +) { + BLACK(EnumChatFormatting.BLACK), + DARK_BLUE(EnumChatFormatting.DARK_BLUE), + DARK_GREEN(EnumChatFormatting.DARK_GREEN), + DARK_AQUA(EnumChatFormatting.DARK_AQUA), + DARK_RED(EnumChatFormatting.DARK_RED), + DARK_PURPLE(EnumChatFormatting.DARK_PURPLE), + GOLD(EnumChatFormatting.GOLD), + GRAY(EnumChatFormatting.GRAY), + DARK_GRAY(EnumChatFormatting.DARK_GRAY), + BLUE(EnumChatFormatting.BLUE), + GREEN(EnumChatFormatting.GREEN), + AQUA(EnumChatFormatting.AQUA), + RED(EnumChatFormatting.RED), + LIGHT_PURPLE(EnumChatFormatting.LIGHT_PURPLE), + YELLOW(EnumChatFormatting.YELLOW), + WHITE(EnumChatFormatting.WHITE), + RESET(EnumChatFormatting.RESET) +} \ No newline at end of file diff --git a/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatComponentBuildScope.kt b/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatComponentBuildScope.kt index dd432b8..5ea7132 100644 --- a/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatComponentBuildScope.kt +++ b/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatComponentBuildScope.kt @@ -23,27 +23,27 @@ import net.minecraft.util.ChatComponentText import net.minecraft.util.ChatStyle import net.minecraft.util.IChatComponent +@Suppress("UNUSED", "FunctionName") object ChatComponentBuildScope { - val empty: IChatComponent - get() = ChatComponentEmpty() + fun empty(): IChatComponent = + ChatComponentEmpty() - @Suppress("UNUSED") - fun empty( - configure: ChatStyle.() -> Unit = {} + fun of( + component: IChatComponent ): IChatComponent = - empty withChatStyle configure + component - infix operator fun IChatComponent.plus( + infix operator fun IChatComponent.rangeTo( o: String ): IChatComponent = - plus { it.appendSibling(o()) } + rangeTo { it.appendSibling(ChatComponentText(o)) } - infix operator fun IChatComponent.plus( + infix operator fun IChatComponent.rangeTo( o: IChatComponent ): IChatComponent = - plus { it.appendSibling(o) } + rangeTo { it.appendSibling(o) } - inline infix operator fun IChatComponent.plus( + inline infix operator fun IChatComponent.rangeTo( configure: (IChatComponent) -> Unit ): IChatComponent = apply(configure) @@ -54,5 +54,52 @@ object ChatComponentBuildScope { inline infix operator fun String.invoke( configure: ChatStyle.() -> Unit ): IChatComponent = - ChatComponentText(this) withChatStyle configure + ChatComponentText(this) style configure + + inline infix operator fun IChatComponent.invoke( + configure: ChatStyle.() -> Unit + ): IChatComponent = + style(configure) + + infix fun IChatComponent.`--color`( + color: ChatColor + ): IChatComponent = + style { + color(color) + } + + infix fun IChatComponent.`--bold`( + bold: Boolean + ): IChatComponent = + style { + this.bold = bold + } + + infix fun IChatComponent.`--italic`( + italic: Boolean + ): IChatComponent = + style { + this.italic = italic + } + + infix fun IChatComponent.`--strikethrough`( + strikethrough: Boolean + ): IChatComponent = + style { + this.strikethrough = strikethrough + } + + infix fun IChatComponent.`--underlined`( + underlined: Boolean + ): IChatComponent = + style { + this.underlined = underlined + } + + infix fun IChatComponent.`--obfuscated`( + obfuscated: Boolean + ): IChatComponent = + style { + this.obfuscated = obfuscated + } } \ No newline at end of file diff --git a/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatComponentUtils.kt b/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatComponentUtils.kt index a939587..98f5835 100644 --- a/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatComponentUtils.kt +++ b/src/main/kotlin/net/llvg/exec/vanilla/utils/chat_component/ChatComponentUtils.kt @@ -30,13 +30,19 @@ inline fun buildChat( ): IChatComponent = ChatComponentBuildScope.builder() -inline fun IChatComponent.withChatStyle( +inline fun IChatComponent.style( chatStyle: ChatStyle, configure: ChatStyle.() -> Unit ): IChatComponent = apply { this.chatStyle = chatStyle.apply(configure) } -inline infix fun IChatComponent.withChatStyle( +inline infix fun IChatComponent.style( configure: ChatStyle.() -> Unit ): IChatComponent = - withChatStyle(chatStyle, configure) \ No newline at end of file + style(chatStyle, configure) + +infix fun ChatStyle.color( + color: ChatColor +) { + this.color = color.format +} \ No newline at end of file From fa0b98a0f264ba53ad7b7aebebfb014cf218b685 Mon Sep 17 00:00:00 2001 From: Water-OR Date: Mon, 14 Apr 2025 22:19:54 +0800 Subject: [PATCH 5/6] feat: improve compatibility with other mixins --- .../llvg/exec/mixin/mixin/MixinEntityPlayerSP.java | 7 +++++++ .../net/llvg/exec/mixin/mixin/MixinRenderPlayer.java | 11 ----------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/llvg/exec/mixin/mixin/MixinEntityPlayerSP.java b/src/main/java/net/llvg/exec/mixin/mixin/MixinEntityPlayerSP.java index 3e5b6b0..0b61e6a 100644 --- a/src/main/java/net/llvg/exec/mixin/mixin/MixinEntityPlayerSP.java +++ b/src/main/java/net/llvg/exec/mixin/mixin/MixinEntityPlayerSP.java @@ -50,4 +50,11 @@ private void isCurrentViewEntityInject(CallbackInfoReturnable cir) { cir.setReturnValue(true); } } + + @Inject(method = "isUser", at = @At("HEAD"), cancellable = true) + private void isUserInject(CallbackInfoReturnable cir) { + if (FreeCam.isEnabled()) { + cir.setReturnValue(isEntityEqual(FreeCam.getCamera())); + } + } } diff --git a/src/main/java/net/llvg/exec/mixin/mixin/MixinRenderPlayer.java b/src/main/java/net/llvg/exec/mixin/mixin/MixinRenderPlayer.java index 2317d95..7057ea3 100644 --- a/src/main/java/net/llvg/exec/mixin/mixin/MixinRenderPlayer.java +++ b/src/main/java/net/llvg/exec/mixin/mixin/MixinRenderPlayer.java @@ -19,25 +19,14 @@ package net.llvg.exec.mixin.mixin; -import net.llvg.exec.feature.freecam.FreeCam; import net.minecraft.client.entity.AbstractClientPlayer; import net.minecraft.client.renderer.entity.RenderPlayer; import net.minecraft.client.renderer.entity.RendererLivingEntity; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; @Mixin (RenderPlayer.class) public abstract class MixinRenderPlayer extends RendererLivingEntity { private MixinRenderPlayer() { super(null, null, 0); } - - @Redirect (method = "doRender(Lnet/minecraft/client/entity/AbstractClientPlayer;DDDFF)V", at = @At (value = "INVOKE", target = "Lnet/minecraft/client/entity/AbstractClientPlayer;isUser()Z")) - private boolean doRenderRedirect(AbstractClientPlayer instance) { - if (FreeCam.isEnabled()) { - return instance == FreeCam.getCamera(); - } - return instance.isUser(); - } } From 9b603347496941bee9bb82996d71109afa416b8f Mon Sep 17 00:00:00 2001 From: Water-OR Date: Mon, 14 Apr 2025 23:17:46 +0800 Subject: [PATCH 6/6] fix: fix a problem of ExeCEventListener::always --- .../kotlin/net/llvg/exec/api/event/ExeCEventListener.kt | 5 +---- .../kotlin/net/llvg/exec/api/event/ExeCEventManager.kt | 8 +++++--- src/main/kotlin/net/llvg/exec/api/event/ExeCEventUtils.kt | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/net/llvg/exec/api/event/ExeCEventListener.kt b/src/main/kotlin/net/llvg/exec/api/event/ExeCEventListener.kt index 8285976..84b882a 100644 --- a/src/main/kotlin/net/llvg/exec/api/event/ExeCEventListener.kt +++ b/src/main/kotlin/net/llvg/exec/api/event/ExeCEventListener.kt @@ -24,12 +24,9 @@ import kotlinx.coroutines.CoroutineScope sealed class ExeCEventListener( val owner: ExeCEventListenable, - private val always: Boolean, + val always: Boolean, private val priority: Int ) : Comparable> { - val active: Boolean - get() = always || owner.active - private val order = Companion.order++ override fun compareTo( diff --git a/src/main/kotlin/net/llvg/exec/api/event/ExeCEventManager.kt b/src/main/kotlin/net/llvg/exec/api/event/ExeCEventManager.kt index 129b879..1aa97b2 100644 --- a/src/main/kotlin/net/llvg/exec/api/event/ExeCEventManager.kt +++ b/src/main/kotlin/net/llvg/exec/api/event/ExeCEventManager.kt @@ -87,13 +87,15 @@ object ExeCEventManager { val jobs: MutableList = ArrayList() asyncListeners.traverse { - jobs += launch { - trigger(it, event) + if (it.always || it.owner.active) { + jobs += trigger(it, event) } } blockListeners.traverse { - trigger(it, event) + if (it.always || it.owner.active) { + trigger(it, event) + } } jobs.joinAll() diff --git a/src/main/kotlin/net/llvg/exec/api/event/ExeCEventUtils.kt b/src/main/kotlin/net/llvg/exec/api/event/ExeCEventUtils.kt index bb0e876..afca900 100644 --- a/src/main/kotlin/net/llvg/exec/api/event/ExeCEventUtils.kt +++ b/src/main/kotlin/net/llvg/exec/api/event/ExeCEventUtils.kt @@ -27,8 +27,8 @@ import kotlinx.coroutines.CoroutineScope inline fun ExeCEventListenable.onEvent( dispatcher: CoroutineDispatcher, forced: Boolean = false, - always: Boolean = true, priority: Int = 0, + always: Boolean = false, noinline action: suspend CoroutineScope.(E) -> Unit ) { ExeCEventManager.register( @@ -47,8 +47,8 @@ inline fun ExeCEventListenable.onEvent( @Suppress("UNUSED") inline fun ExeCEventListenable.onEvent( forced: Boolean = false, - always: Boolean = true, priority: Int = 0, + always: Boolean = false, noinline action: (E) -> Unit ) { ExeCEventManager.register(