Skip to content
This repository has been archived by the owner on Sep 2, 2024. It is now read-only.

Commit

Permalink
Merge remote-tracking branch 'origin/main' into recode
Browse files Browse the repository at this point in the history
  • Loading branch information
mudkip989 committed Aug 17, 2024
2 parents 9972c6c + 585705b commit 6981e5a
Show file tree
Hide file tree
Showing 41 changed files with 699 additions and 278 deletions.
18 changes: 7 additions & 11 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ConfigureShadowRelocation
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
kotlin("jvm") version "1.9.23"
kotlin("jvm") version "2.0.0"
id("fabric-loom") version "1.5-SNAPSHOT"
id("com.modrinth.minotaur") version "2.+"
id("com.github.johnrengelman.shadow") version "7.1.2"
id("com.github.johnrengelman.shadow") version "8.1.1"
}

val modName: String by project
Expand Down Expand Up @@ -144,20 +143,17 @@ tasks {
enabled = false
}

val relocate by registering(ConfigureShadowRelocation::class) {
// repackage shaded dependencies
target = shadowJar.get()
prefix = "$mavenGroup.recode.shaded"
}

shadowJar {
dependsOn(relocate.get())
configurations = listOf(shade)
from("LICENSE")

// output shaded jar in the correct destination to be used by remapJar
destinationDirectory.set(file("build/devlibs"))
archiveClassifier.set("dev")

from("LICENSE")
// relocate
isEnableRelocation = true
relocationPrefix = "$mavenGroup.$modName.shaded"
}

remapJar {
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
org.gradle.jvmargs = -Xmx1G
org.gradle.jvmargs = -Xmx1G

# https://fabricmc.net/develop/

Expand All @@ -15,7 +15,7 @@ loaderVersion = 0.15.2
# ideally, pick versions without bugs but not versions with unused features (for compatibility)

fabricVersion = 0.96.1+1.20.4
flkVersion = 1.10.19+kotlin.1.9.23
flkVersion = 1.11.0+kotlin.2.0.0

# required dependency mods
required.adventure-platform-fabric.artifact = net.kyori:adventure-platform-fabric
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
3 changes: 2 additions & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
8 changes: 6 additions & 2 deletions gradlew
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
Expand Down Expand Up @@ -130,10 +131,13 @@ location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi

# Increase the maximum file descriptors if we can.
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/io/github/homchom/recode/Recode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import io.github.homchom.recode.event.listenEach
import io.github.homchom.recode.feature.automation.AutoCommands
import io.github.homchom.recode.feature.meta.FDebugMode
import io.github.homchom.recode.feature.visual.*
import io.github.homchom.recode.game.QuitGameEvent
import io.github.homchom.recode.game.GameStopEvent
import io.github.homchom.recode.hypercube.JoinDFDetector
import io.github.homchom.recode.mod.commands.CommandHandler
import io.github.homchom.recode.mod.config.LegacyConfig
Expand Down Expand Up @@ -48,7 +48,7 @@ object Recode : ClientModInitializer, ModContainer {
val raw = metadata.version.friendlyString
val preReleaseMatch = regex {
str('-')
val phase by group { str("alpha"); or; str("beta") }
val phase by anyStr("alpha", "beta")
str('.')
}.find(raw)
if (preReleaseMatch == null) raw else {
Expand Down Expand Up @@ -99,7 +99,7 @@ object Recode : ClientModInitializer, ModContainer {
// register globally active listeners that aren't feature-related
private fun Power.registerTopLevelListeners() {
// handle close
listenEach(QuitGameEvent) { close() }
listenEach(GameStopEvent) { close() }

// show mod usage messages
listenEach(JoinDFDetector) {
Expand Down
48 changes: 28 additions & 20 deletions src/main/java/io/github/homchom/recode/RecodeDispatcher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,37 @@ package io.github.homchom.recode

import io.github.homchom.recode.ui.sendSystemToast
import io.github.homchom.recode.ui.text.translatedText
import io.github.homchom.recode.util.coroutines.YieldingExecutorDispatcher
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Runnable
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.runBlocking
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.Executor
import kotlin.coroutines.CoroutineContext

/**
* Runs [block] on Minecraft's main thread. If this is called from another thread, the calling thread is blocked
* until completion.
* Runs [block] on Minecraft's main thread. If this is called from another thread, the calling thread is
* blocked until completion.
*/
inline fun <R> runOnMinecraftThread(crossinline block: () -> R) =
if (mc.isSameThread) block() else runBlocking(RecodeDispatcher) { block() }

/**
* A [CoroutineDispatcher] and [CoroutineExceptionHandler] that confines execution to Minecraft's main
* thread. Use this instead of Minecraft's default [Executor] because this, runs first and supports the
* [kotlinx.coroutines.yield] function.
* A [kotlinx.coroutines.CoroutineDispatcher] and [CoroutineExceptionHandler] that confines execution to
* Minecraft's main thread. This will always dispatch before Minecraft's default executor, and it supports
* the [kotlinx.coroutines.yield] function.
*/
object RecodeDispatcher : CoroutineContext {
private val pending = ConcurrentLinkedQueue<Runnable>()
private val dispatcher = YieldingExecutorDispatcher(pending::add, mc::isSameThread)

private val delegate = dispatcher + CoroutineExceptionHandler { _, exception ->
mc.sendSystemToast(
translatedText("recode.uncaught_exception.toast.title"),
translatedText("recode.uncaught_exception.toast")
)
runOnMinecraftThread {
val thread = Thread.currentThread()
thread.uncaughtExceptionHandler.uncaughtException(thread, exception)
private val executor = Executor { task ->
if (mc.isSameThread && mc.pendingTasksCount == 0) {
task.run()
} else synchronized(pending) {
pending.add(task)
}
}

private val delegate = executor.asCoroutineDispatcher() + RecodeExceptionHandler

override fun <E : CoroutineContext.Element> get(key: CoroutineContext.Key<E>) = delegate[key]
override fun <R> fold(initial: R, operation: (R, CoroutineContext.Element) -> R) =
delegate.fold(initial, operation)
Expand All @@ -47,8 +42,21 @@ object RecodeDispatcher : CoroutineContext {
* Tells the dispatcher to expedite previously dispatched blocks. If this is called from Minecraft's main
* thread, it is guaranteed that all blocks will complete before the function returns.
*/
fun expedite() = with(pending) {
fun expedite() {
if (!mc.isSameThread) return
while (isNotEmpty()) remove().run()
val tasks = generateSequence(pending::poll).toList()
for (task in tasks) task.run()
}
}

/**
* The [CoroutineExceptionHandler] used by [RecodeDispatcher].
*/
val RecodeExceptionHandler = CoroutineExceptionHandler { _, exception ->
mc.sendSystemToast(
translatedText("recode.uncaught_exception.toast.title"),
translatedText("recode.uncaught_exception.toast")
)
val thread = Thread.currentThread()
thread.uncaughtExceptionHandler.uncaughtException(thread, exception)
}
5 changes: 5 additions & 0 deletions src/main/java/io/github/homchom/recode/event/EventTypes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ interface Requester<T : Any, out R : Any> : Sender<T, R?>, Detector<T, R> {
suspend fun request(input: T, hidden: Boolean = false): R
}

/**
* @see CustomEvent.run
*/
fun <R : Any> CustomEvent<Unit, R>.run() = run(Unit)

/**
* @throws kotlinx.coroutines.TimeoutCancellationException
*
Expand Down
9 changes: 3 additions & 6 deletions src/main/java/io/github/homchom/recode/event/EventWrapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,16 @@ fun <T, L> wrapFabricEvent(
event: Event<L>,
transform: (EventInvoker<T>) -> L
): WrappedEvent<T, L> {
return EventWrapper(event, transform, createEvent())
val async = createEvent<T>()
event.register(transform(async::run))
return EventWrapper(event, async)
}

private class EventWrapper<T, L>(
private val fabricEvent: Event<L>,
transform: ((T) -> Unit) -> L,
private val async: CustomEvent<T, Unit>
) : WrappedEvent<T, L>, PowerSink by async {
override val notifications by async::notifications

override val invoker: L get() = fabricEvent.invoker()

init {
fabricEvent.register(transform(async::run))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.flow.*
import java.util.concurrent.ConcurrentLinkedDeque
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.time.Duration

Expand Down Expand Up @@ -60,7 +60,7 @@ private open class TrialDetector<T, R : Any>(
}
)

private val entries by lazy { ConcurrentLinkedDeque<DetectEntry<T, R>>() }
private val entries by lazy { ConcurrentLinkedQueue<DetectEntry<T, R>>() }

override val notifications by event::notifications
override val previous by event::previous
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ private class TrialCoroutineScope(
coroutineScope.launch(coroutineContext, CoroutineStart.UNDISPATCHED) {
channel.consumeEach { test(it) ?: throw TrialScopeException() }
}
yield() // fast-fail
yield() // fast fail
}

override fun <R : Any> instant(value: R?) = TrialResult(value)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,25 @@
package io.github.homchom.recode.feature.visual

import io.github.homchom.recode.hypercube.CommandAliasGroup
import io.github.homchom.recode.hypercube.HypercubeCommandAliases
import io.github.homchom.recode.render.text.formattedCharSequence
import io.github.homchom.recode.render.text.subSequence
import io.github.homchom.recode.util.regex.regex
import net.minecraft.util.FormattedCharSequence
import kotlin.math.max
import kotlin.math.min

private val highlightedCommands = buildList {
fun addGroup(
group: CommandAliasGroup,
highlightedArgumentIndex: Int = 0,
hasCount: Boolean = false,
parseMiniMessage: Boolean = true
) {
addAll(group.map { prefix ->
CommandInfo(prefix, highlightedArgumentIndex, hasCount, parseMiniMessage)
})
}

addGroup(CommandAliasGroup.NUMBER, hasCount = true, parseMiniMessage = false)
addGroup(CommandAliasGroup.STRING, hasCount = true, parseMiniMessage = false)
addGroup(CommandAliasGroup.TEXT, hasCount = true)
addGroup(CommandAliasGroup.VARIABLE, hasCount = true, parseMiniMessage = false)
addGroup(CommandAliasGroup.ITEM_NAME)
addGroup(CommandAliasGroup.ITEM_LORE_ADD)
addGroup(CommandAliasGroup.ITEM_LORE_SET, highlightedArgumentIndex = 1)
addGroup(CommandAliasGroup.PLOT_NAME)
addGroup(CommandAliasGroup.RELORE)
}
private val highlightedCommands = listOf(
CommandInfo(HypercubeCommandAliases.NUMBER, hasCount = true, parseMiniMessage = false),
CommandInfo(HypercubeCommandAliases.STRING, hasCount = true, parseMiniMessage = false),
CommandInfo(HypercubeCommandAliases.TEXT, hasCount = true),
CommandInfo(HypercubeCommandAliases.VARIABLE, hasCount = true, parseMiniMessage = false),
CommandInfo(HypercubeCommandAliases.ITEM_NAME),
CommandInfo(HypercubeCommandAliases.ITEM_LORE_ADD),
CommandInfo(HypercubeCommandAliases.ITEM_LORE_SET, highlightedArgumentIndex = 1),
CommandInfo(HypercubeCommandAliases.ITEM_LORE_INSERT, highlightedArgumentIndex = 1),
CommandInfo(HypercubeCommandAliases.PLOT_NAME),
CommandInfo(HypercubeCommandAliases.RELORE)
)

/**
* An object that retrofits [net.minecraft.client.gui.components.EditBox] for expression highlighting.
Expand Down Expand Up @@ -62,10 +52,11 @@ class EditBoxExpressionFormatter(
): HighlightedExpression? {
// highlight commands
if (formatCommands && chatInput.startsWith('/')) {
val command = highlightedCommands.firstNotNullOfOrNull { info ->
info.takeIf { chatInput.startsWith(info.prefix, 1) }
val (command, startIndex) = highlightedCommands.firstNotNullOfOrNull { info ->
val match = info.prefix.matchAt(chatInput, 1)
match?.let { info to it.value.length + 1 }
} ?: return null
return formatCommand(chatInput, partialParentFormat, partialRange, command)
return formatCommand(chatInput, startIndex, partialParentFormat, partialRange, command)
}

// highlight values
Expand All @@ -83,11 +74,12 @@ class EditBoxExpressionFormatter(

private fun formatCommand(
chatInput: String,
index: Int,
partialParentFormat: FormattedCharSequence,
partialRange: IntRange,
info: CommandInfo
): HighlightedExpression? {
var startIndex = info.prefix.length + 2
var startIndex = index
var endIndex = chatInput.length
if (startIndex > chatInput.lastIndex) return null

Expand Down Expand Up @@ -137,10 +129,10 @@ class EditBoxExpressionFormatter(
}

private data class CommandInfo(
val prefix: String,
val highlightedArgumentIndex: Int,
val hasCount: Boolean,
val parseMiniMessage: Boolean
val prefix: Regex,
val highlightedArgumentIndex: Int = 0,
val hasCount: Boolean = false,
val parseMiniMessage: Boolean = true
)

private val countRegex = regex {
Expand Down
10 changes: 8 additions & 2 deletions src/main/java/io/github/homchom/recode/game/GameEvents.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

package io.github.homchom.recode.game

import io.github.homchom.recode.event.createEvent
import io.github.homchom.recode.event.createValidatedEvent
import io.github.homchom.recode.event.run
import io.github.homchom.recode.event.wrapFabricEvent
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents.ClientStopping
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents.EndTick
import net.minecraft.network.protocol.game.ClientboundSoundPacket
Expand All @@ -14,4 +15,9 @@ val AfterClientTickEvent = wrapFabricEvent(ClientTickEvents.END_CLIENT_TICK) { E

val PlaySoundEvent = createValidatedEvent<ClientboundSoundPacket>()

val QuitGameEvent = wrapFabricEvent(ClientLifecycleEvents.CLIENT_STOPPING) { ClientStopping(it) }
/**
* An event that runs when the client stops, including crashes.
*/
val GameStopEvent = createEvent<Unit>().also { event ->
ClientLifecycleEvents.CLIENT_STOPPING.register { event.run() }
}
Loading

0 comments on commit 6981e5a

Please sign in to comment.