diff options
Diffstat (limited to 'src/main/kotlin/features/chat')
| -rw-r--r-- | src/main/kotlin/features/chat/AutoCompletions.kt | 33 | ||||
| -rw-r--r-- | src/main/kotlin/features/chat/ChatLinks.kt | 101 | ||||
| -rw-r--r-- | src/main/kotlin/features/chat/CopyChat.kt | 21 | ||||
| -rw-r--r-- | src/main/kotlin/features/chat/PartyCommands.kt | 8 | ||||
| -rw-r--r-- | src/main/kotlin/features/chat/QuickCommands.kt | 55 |
5 files changed, 137 insertions, 81 deletions
diff --git a/src/main/kotlin/features/chat/AutoCompletions.kt b/src/main/kotlin/features/chat/AutoCompletions.kt index 9e0de40..f13fe7e 100644 --- a/src/main/kotlin/features/chat/AutoCompletions.kt +++ b/src/main/kotlin/features/chat/AutoCompletions.kt @@ -1,6 +1,15 @@ package moe.nea.firmament.features.chat +import com.mojang.brigadier.Message import com.mojang.brigadier.arguments.StringArgumentType.string +import com.mojang.brigadier.context.CommandContext +import com.mojang.brigadier.exceptions.BuiltInExceptions +import com.mojang.brigadier.exceptions.CommandExceptionType +import com.mojang.brigadier.exceptions.CommandSyntaxException +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType +import kotlin.concurrent.thread +import net.minecraft.SharedConstants +import net.minecraft.commands.BrigadierExceptions import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.commands.get import moe.nea.firmament.commands.suggestsList @@ -8,21 +17,21 @@ import moe.nea.firmament.commands.thenArgument import moe.nea.firmament.commands.thenExecute import moe.nea.firmament.events.CommandEvent import moe.nea.firmament.events.MaskCommands -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.util.MC +import moe.nea.firmament.util.data.Config +import moe.nea.firmament.util.data.ManagedConfig +import moe.nea.firmament.util.tr -object AutoCompletions : FirmamentFeature { +object AutoCompletions { + @Config object TConfig : ManagedConfig(identifier, Category.CHAT) { val provideWarpTabCompletion by toggle("warp-complete") { true } val replaceWarpIsByWarpIsland by toggle("warp-is") { true } } - override val config: ManagedConfig? - get() = TConfig - override val identifier: String + val identifier: String get() = "auto-completions" @Subscribe @@ -44,12 +53,20 @@ object AutoCompletions : FirmamentFeature { thenExecute { val warpName = get(toArg) if (warpName == "is" && TConfig.replaceWarpIsByWarpIsland) { - MC.sendServerCommand("warp island") + MC.sendCommand("warp island") } else { - MC.sendServerCommand("warp $warpName") + redirectToServer() } } } } } + + fun CommandContext<*>.redirectToServer() { + val message = tr( + "firmament.warp.auto-complete.internal-throw", + "This is an internal syntax exception that should not show up in gameplay, used to pass on a command to the server" + ) + throw CommandSyntaxException(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand(), message) + } } diff --git a/src/main/kotlin/features/chat/ChatLinks.kt b/src/main/kotlin/features/chat/ChatLinks.kt index f85825b..aca7af8 100644 --- a/src/main/kotlin/features/chat/ChatLinks.kt +++ b/src/main/kotlin/features/chat/ChatLinks.kt @@ -1,47 +1,48 @@ package moe.nea.firmament.features.chat -import io.ktor.client.request.get -import io.ktor.client.statement.bodyAsChannel -import io.ktor.utils.io.jvm.javaio.toInputStream -import java.net.URL +import java.net.URI import java.util.Collections import java.util.concurrent.atomic.AtomicInteger -import moe.nea.jarvis.api.Point +import org.joml.Vector2i import kotlinx.coroutines.Deferred import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.async +import kotlinx.coroutines.future.await import kotlin.math.min -import net.minecraft.client.gui.screen.ChatScreen -import net.minecraft.client.texture.NativeImage -import net.minecraft.client.texture.NativeImageBackedTexture -import net.minecraft.text.ClickEvent -import net.minecraft.text.HoverEvent -import net.minecraft.text.Style -import net.minecraft.text.Text -import net.minecraft.util.Formatting -import net.minecraft.util.Identifier +import net.minecraft.client.gui.screens.ChatScreen +import com.mojang.blaze3d.platform.NativeImage +import net.minecraft.client.renderer.texture.DynamicTexture +import net.minecraft.network.chat.ClickEvent +import net.minecraft.network.chat.HoverEvent +import net.minecraft.network.chat.Style +import net.minecraft.network.chat.Component +import net.minecraft.ChatFormatting +import net.minecraft.resources.ResourceLocation import moe.nea.firmament.Firmament import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.events.ModifyChatEvent import moe.nea.firmament.events.ScreenRenderPostEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig +import moe.nea.firmament.jarvis.JarvisIntegration import moe.nea.firmament.util.MC +import moe.nea.firmament.util.data.Config +import moe.nea.firmament.util.data.ManagedConfig +import moe.nea.firmament.util.net.HttpUtil import moe.nea.firmament.util.render.drawTexture import moe.nea.firmament.util.transformEachRecursively import moe.nea.firmament.util.unformattedString -object ChatLinks : FirmamentFeature { - override val identifier: String +object ChatLinks { + val identifier: String get() = "chat-links" + @Config object TConfig : ManagedConfig(identifier, Category.CHAT) { val enableLinks by toggle("links-enabled") { true } val imageEnabled by toggle("image-enabled") { true } val allowAllHosts by toggle("allow-all-hosts") { false } val allowedHosts by string("allowed-hosts") { "cdn.discordapp.com,media.discordapp.com,media.discordapp.net,i.imgur.com" } val actualAllowedHosts get() = allowedHosts.split(",").map { it.trim() } - val position by position("position", 16 * 20, 9 * 20) { Point(0.0, 0.0) } + val position by position("position", 16 * 20, 9 * 20) { Vector2i(0, 0) } } private fun isHostAllowed(host: String) = @@ -49,12 +50,11 @@ object ChatLinks : FirmamentFeature { private fun isUrlAllowed(url: String) = isHostAllowed(url.removePrefix("https://").substringBefore("/")) - override val config get() = TConfig - val urlRegex = "https://[^. ]+\\.[^ ]+(\\.?( |$))".toRegex() + val urlRegex = "https://[^. ]+\\.[^ ]+(\\.?(\\s|$))".toRegex() val nextTexId = AtomicInteger(0) data class Image( - val texture: Identifier, + val texture: ResourceLocation, val width: Int, val height: Int, ) @@ -71,18 +71,16 @@ object ChatLinks : FirmamentFeature { } imageCache[url] = Firmament.coroutineScope.async { try { - val response = Firmament.httpClient.get(URL(url)) - if (response.status.value == 200) { - val inputStream = response.bodyAsChannel().toInputStream(Firmament.globalJob) - val image = NativeImage.read(inputStream) - val texId = Firmament.identifier("dynamic_image_preview${nextTexId.getAndIncrement()}") - MC.textureManager.registerTexture( - texId, - NativeImageBackedTexture(image) - ) - Image(texId, image.width, image.height) - } else - null + val inputStream = HttpUtil.request(url) + .forInputStream() + .await() + val image = NativeImage.read(inputStream) + val texId = Firmament.identifier("dynamic_image_preview${nextTexId.getAndIncrement()}") + MC.textureManager.register( + texId, + DynamicTexture({ texId.path }, image) + ) + Image(texId, image.width, image.height) } catch (exc: Exception) { exc.printStackTrace() null @@ -101,19 +99,19 @@ object ChatLinks : FirmamentFeature { if (!TConfig.imageEnabled) return if (it.screen !is ChatScreen) return val hoveredComponent = - MC.inGameHud.chatHud.getTextStyleAt(it.mouseX.toDouble(), it.mouseY.toDouble()) ?: return - val hoverEvent = hoveredComponent.hoverEvent ?: return - val value = hoverEvent.getValue(HoverEvent.Action.SHOW_TEXT) ?: return + MC.inGameHud.chat.getClickedComponentStyleAt(it.mouseX.toDouble(), it.mouseY.toDouble()) ?: return + val hoverEvent = hoveredComponent.hoverEvent as? HoverEvent.ShowText ?: return + val value = hoverEvent.value val url = urlRegex.matchEntire(value.unformattedString)?.groupValues?.get(0) ?: return if (!isImageUrl(url)) return val imageFuture = imageCache[url] ?: return if (!imageFuture.isCompleted) return val image = imageFuture.getCompleted() ?: return - it.drawContext.matrices.push() + it.drawContext.pose().pushMatrix() val pos = TConfig.position - pos.applyTransformations(it.drawContext.matrices) + pos.applyTransformations(JarvisIntegration.jarvis, it.drawContext.pose()) val scale = min(1F, min((9 * 20F) / image.height, (16 * 20F) / image.width)) - it.drawContext.matrices.scale(scale, scale, 1F) + it.drawContext.pose().scale(scale, scale) it.drawContext.drawTexture( image.texture, 0, @@ -125,7 +123,7 @@ object ChatLinks : FirmamentFeature { image.width, image.height, ) - it.drawContext.matrices.pop() + it.drawContext.pose().popMatrix() } @Subscribe @@ -134,23 +132,24 @@ object ChatLinks : FirmamentFeature { it.replaceWith = it.replaceWith.transformEachRecursively { child -> val text = child.string if ("://" !in text) return@transformEachRecursively child - val s = Text.empty().setStyle(child.style) + val s = Component.empty().setStyle(child.style) var index = 0 while (index < text.length) { val nextMatch = urlRegex.find(text, index) - if (nextMatch == null) { - s.append(Text.literal(text.substring(index, text.length))) + val url = nextMatch?.groupValues[0] + val uri = runCatching { url?.let(::URI) }.getOrNull() + if (nextMatch == null || url == null || uri == null) { + s.append(Component.literal(text.substring(index, text.length))) break } val range = nextMatch.groups[0]!!.range - val url = nextMatch.groupValues[0] - s.append(Text.literal(text.substring(index, range.first))) + s.append(Component.literal(text.substring(index, range.first))) s.append( - Text.literal(url).setStyle( - Style.EMPTY.withUnderline(true).withColor( - Formatting.AQUA - ).withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(url))) - .withClickEvent(ClickEvent(ClickEvent.Action.OPEN_URL, url)) + Component.literal(url).setStyle( + Style.EMPTY.withUnderlined(true).withColor( + ChatFormatting.AQUA + ).withHoverEvent(HoverEvent.ShowText(Component.literal(url))) + .withClickEvent(ClickEvent.OpenUrl(uri)) ) ) if (isImageUrl(url)) diff --git a/src/main/kotlin/features/chat/CopyChat.kt b/src/main/kotlin/features/chat/CopyChat.kt new file mode 100644 index 0000000..6bef99f --- /dev/null +++ b/src/main/kotlin/features/chat/CopyChat.kt @@ -0,0 +1,21 @@ +package moe.nea.firmament.features.chat + +import net.minecraft.util.FormattedCharSequence +import moe.nea.firmament.util.data.Config +import moe.nea.firmament.util.data.ManagedConfig +import moe.nea.firmament.util.reconstitute + + +object CopyChat { + val identifier: String + get() = "copy-chat" + + @Config + object TConfig : ManagedConfig(identifier, Category.CHAT) { + val copyChat by toggle("copy-chat") { false } + } + + fun orderedTextToString(orderedText: FormattedCharSequence): String { + return orderedText.reconstitute().string + } +} diff --git a/src/main/kotlin/features/chat/PartyCommands.kt b/src/main/kotlin/features/chat/PartyCommands.kt index de3a0d9..85daf51 100644 --- a/src/main/kotlin/features/chat/PartyCommands.kt +++ b/src/main/kotlin/features/chat/PartyCommands.kt @@ -5,17 +5,18 @@ import com.mojang.brigadier.StringReader import com.mojang.brigadier.exceptions.CommandSyntaxException import com.mojang.brigadier.tree.LiteralCommandNode import kotlin.time.Duration.Companion.seconds -import net.minecraft.util.math.BlockPos +import net.minecraft.core.BlockPos import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.commands.CaseInsensitiveLiteralCommandNode import moe.nea.firmament.commands.thenExecute import moe.nea.firmament.events.CommandEvent import moe.nea.firmament.events.PartyMessageReceivedEvent import moe.nea.firmament.events.ProcessChatEvent -import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.util.ErrorUtil import moe.nea.firmament.util.MC import moe.nea.firmament.util.TimeMark +import moe.nea.firmament.util.data.Config +import moe.nea.firmament.util.data.ManagedConfig import moe.nea.firmament.util.tr import moe.nea.firmament.util.useMatch @@ -79,7 +80,7 @@ object PartyCommands { register("coords") { executes { - val p = MC.player?.blockPos ?: BlockPos.ORIGIN + val p = MC.player?.blockPosition() ?: BlockPos.ZERO MC.sendCommand("pc x: ${p.x}, y: ${p.y}, z: ${p.z}") 0 } @@ -89,6 +90,7 @@ object PartyCommands { // TODO: at TPS command } + @Config object TConfig : ManagedConfig("party-commands", Category.CHAT) { val enable by toggle("enable") { false } val cooldown by duration("cooldown", 0.seconds, 20.seconds) { 2.seconds } diff --git a/src/main/kotlin/features/chat/QuickCommands.kt b/src/main/kotlin/features/chat/QuickCommands.kt index 7963171..b857f8a 100644 --- a/src/main/kotlin/features/chat/QuickCommands.kt +++ b/src/main/kotlin/features/chat/QuickCommands.kt @@ -5,9 +5,9 @@ import com.mojang.brigadier.context.CommandContext import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource import net.fabricmc.fabric.impl.command.client.ClientCommandInternals -import net.minecraft.command.CommandRegistryAccess -import net.minecraft.network.packet.s2c.play.CommandTreeS2CPacket -import net.minecraft.text.Text +import net.minecraft.commands.CommandBuildContext +import net.minecraft.network.protocol.game.ClientboundCommandsPacket +import net.minecraft.network.chat.Component import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.commands.DefaultSource import moe.nea.firmament.commands.RestArgumentType @@ -15,18 +15,19 @@ import moe.nea.firmament.commands.get import moe.nea.firmament.commands.thenArgument import moe.nea.firmament.commands.thenExecute import moe.nea.firmament.events.CommandEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedOption import moe.nea.firmament.util.MC import moe.nea.firmament.util.SBData +import moe.nea.firmament.util.data.Config +import moe.nea.firmament.util.data.ManagedConfig import moe.nea.firmament.util.grey import moe.nea.firmament.util.tr -object QuickCommands : FirmamentFeature { - override val identifier: String +object QuickCommands { + val identifier: String get() = "quick-commands" + @Config object TConfig : ManagedConfig("quick-commands", Category.CHAT) { val enableJoin by toggle("join") { true } val enableDh by toggle("dh") { true } @@ -43,10 +44,14 @@ object QuickCommands : FirmamentFeature { val dispatcher = CommandDispatcher<FabricClientCommandSource>() ClientCommandInternals.setActiveDispatcher(dispatcher) ClientCommandRegistrationCallback.EVENT.invoker() - .register(dispatcher, CommandRegistryAccess.of(network.combinedDynamicRegistries, - network.enabledFeatures)) + .register( + dispatcher, CommandBuildContext.simple( + network.registryAccess, + network.enabledFeatures() + ) + ) ClientCommandInternals.finalizeInit() - network.onCommandTree(lastPacket) + network.handleCommands(lastPacket) } catch (ex: Exception) { ClientCommandInternals.setActiveDispatcher(fallback) throw ex @@ -64,7 +69,7 @@ object QuickCommands : FirmamentFeature { return lf } - var lastReceivedTreePacket: CommandTreeS2CPacket? = null + var lastReceivedTreePacket: ClientboundCommandsPacket? = null val kuudraLevelNames = listOf("NORMAL", "HOT", "BURNING", "FIERY", "INFERNAL") val dungeonLevelNames = listOf("ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN") @@ -98,16 +103,20 @@ object QuickCommands : FirmamentFeature { } val joinName = getNameForFloor(what.replace(" ", "").lowercase()) if (joinName == null) { - source.sendFeedback(Text.stringifiedTranslatable("firmament.quick-commands.join.unknown", what)) + source.sendFeedback(Component.translatableEscape("firmament.quick-commands.join.unknown", what)) } else { - source.sendFeedback(Text.stringifiedTranslatable("firmament.quick-commands.join.success", - joinName)) + source.sendFeedback( + Component.translatableEscape( + "firmament.quick-commands.join.success", + joinName + ) + ) MC.sendCommand("joininstance $joinName") } } } thenExecute { - source.sendFeedback(Text.translatable("firmament.quick-commands.join.explain")) + source.sendFeedback(Component.translatable("firmament.quick-commands.join.explain")) } } } @@ -122,8 +131,12 @@ object QuickCommands : FirmamentFeature { ) } if (l !in kuudraLevelNames.indices) { - source.sendFeedback(Text.stringifiedTranslatable("firmament.quick-commands.join.unknown-kuudra", - kuudraLevel)) + source.sendFeedback( + Component.translatableEscape( + "firmament.quick-commands.join.unknown-kuudra", + kuudraLevel + ) + ) return null } return "KUUDRA_${kuudraLevelNames[l]}" @@ -143,8 +156,12 @@ object QuickCommands : FirmamentFeature { return "CATACOMBS_ENTRANCE" } if (l !in dungeonLevelNames.indices) { - source.sendFeedback(Text.stringifiedTranslatable("firmament.quick-commands.join.unknown-catacombs", - kuudraLevel)) + source.sendFeedback( + Component.translatableEscape( + "firmament.quick-commands.join.unknown-catacombs", + kuudraLevel + ) + ) return null } return "${if (masterLevel != null) "MASTER_" else ""}CATACOMBS_FLOOR_${dungeonLevelNames[l]}" |
