From 269416f70cb85a393596363200073877fe7b3120 Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Thu, 11 Apr 2024 16:08:46 -0400 Subject: [PATCH 01/25] feat: add hypixel payload api chore: bump hypixel mod-api to 0.1.7 chore: upgrade mod-api to 0.2.1 --- build.gradle.kts | 4 ++ .../kotlin/gg/skytils/skytilsmod/Skytils.kt | 32 ++++++++- .../events/impl/HypixelPacketEvent.kt | 45 +++++++++++++ .../listeners/ServerPayloadInterceptor.kt | 66 +++++++++++++++++++ 4 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/gg/skytils/skytilsmod/events/impl/HypixelPacketEvent.kt create mode 100644 src/main/kotlin/gg/skytils/skytilsmod/listeners/ServerPayloadInterceptor.kt diff --git a/build.gradle.kts b/build.gradle.kts index 64e708aeb..0de006008 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,6 +42,7 @@ repositories { mavenCentral() maven("https://repo.sk1er.club/repository/maven-public/") maven("https://repo.sk1er.club/repository/maven-releases/") + maven("https://repo.hypixel.net/repository/Hypixel/") maven("https://jitpack.io") { mavenContent { includeGroupAndSubgroups("com.github") @@ -151,6 +152,8 @@ dependencies { exclude(module = "bcprov-jdk18on") } compileOnly("org.bouncycastle:bcprov-jdk18on:1.78.1") + shadowMe("net.hypixel:mod-api:0.2.1") + shadowMe(annotationProcessor("io.github.llamalad7:mixinextras-common:0.3.5")!!) annotationProcessor("org.spongepowered:mixin:0.8.5:processor") @@ -217,6 +220,7 @@ tasks { relocate("kotlinx.serialization", "gg.skytils.ktx-serialization") relocate("kotlinx.coroutines", "gg.skytils.ktx-coroutines") relocate("gg.essential.vigilance", "gg.skytils.vigilance") + relocate("net.hypixel", "gg.skytils.hypixel-net") exclude( "**/LICENSE_MixinExtras", diff --git a/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt b/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt index 803584951..74e49122f 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt @@ -26,6 +26,7 @@ import gg.skytils.skytilsmod.commands.stats.impl.CataCommand import gg.skytils.skytilsmod.commands.stats.impl.SlayerCommand import gg.skytils.skytilsmod.core.* import gg.skytils.skytilsmod.tweaker.DependencyLoader +import gg.skytils.skytilsmod.events.impl.HypixelPacketEvent import gg.skytils.skytilsmod.events.impl.MainReceivePacketEvent import gg.skytils.skytilsmod.events.impl.PacketEvent import gg.skytils.skytilsmod.features.impl.crimson.KuudraChestProfit @@ -62,6 +63,8 @@ import gg.skytils.skytilsmod.gui.OptionsGui import gg.skytils.skytilsmod.gui.ReopenableGUI import gg.skytils.skytilsmod.listeners.ChatListener import gg.skytils.skytilsmod.listeners.DungeonListener +import gg.skytils.skytilsmod.listeners.ServerPayloadInterceptor +import gg.skytils.skytilsmod.listeners.ServerPayloadInterceptor.toCustomPayload import gg.skytils.skytilsmod.localapi.LocalAPI import gg.skytils.skytilsmod.mixins.extensions.ExtensionEntityLivingBase import gg.skytils.skytilsmod.mixins.hooks.entity.EntityPlayerSPHook @@ -83,11 +86,14 @@ import io.ktor.serialization.kotlinx.json.* import kotlinx.coroutines.* import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule +import net.hypixel.modapi.packet.impl.clientbound.ClientboundPingPacket +import net.hypixel.modapi.packet.impl.serverbound.ServerboundPingPacket import net.minecraft.client.Minecraft import net.minecraft.client.gui.GuiButton import net.minecraft.client.gui.GuiGameOver import net.minecraft.client.gui.GuiIngameMenu import net.minecraft.client.gui.GuiScreen +import net.minecraft.client.network.NetHandlerPlayClient import net.minecraft.client.settings.KeyBinding import net.minecraft.inventory.ContainerChest import net.minecraft.launchwrapper.Launch @@ -289,6 +295,7 @@ class Skytils { LocalAPI, MayorInfo, SBInfo, + ServerPayloadInterceptor, SoundQueue, UpdateChecker, @@ -531,11 +538,27 @@ class Skytils { ?: currentServerData?.serverIP?.lowercase()?.contains("hypixel") ?: false) }.onFailure { it.printStackTrace() }.getOrDefault(false) + if (Utils.isOnHypixel) { + onJoinHypixel(event.handler as NetHandlerPlayClient) + } + IO.launch { TrophyFish.loadFromApi() } } + @SubscribeEvent + fun onHypixelPacket(event: HypixelPacketEvent.ReceiveEvent) { + if (event.packet is ClientboundPingPacket) { + println("${event.packet.response} ${event.packet.version}") + } + } + + @SubscribeEvent + fun onHypixelPacketFail(event: HypixelPacketEvent.FailedEvent) { + UChat.chat("$failPrefix Mod API request failed: ${event.reason}") + } + @SubscribeEvent(priority = EventPriority.HIGHEST) fun onPacket(event: MainReceivePacketEvent<*, *>) { if (event.packet is S01PacketJoinGame) { @@ -556,8 +579,11 @@ class Skytils { } } if (!Utils.isOnHypixel && event.packet is S3FPacketCustomPayload && event.packet.channelName == "MC|Brand") { - if (event.packet.bufferData.readStringFromBuffer(Short.MAX_VALUE.toInt()).lowercase().contains("hypixel")) + val brand = event.packet.bufferData.readStringFromBuffer(Short.MAX_VALUE.toInt()) + if (brand.lowercase().contains("hypixel")) { Utils.isOnHypixel = true + onJoinHypixel(event.handler as NetHandlerPlayClient) + } } if (Utils.inDungeons || !Utils.isOnHypixel || event.packet !is S38PacketPlayerListItem || (event.packet.action != S38PacketPlayerListItem.Action.UPDATE_DISPLAY_NAME && @@ -575,6 +601,10 @@ class Skytils { } } + fun onJoinHypixel(handler: NetHandlerPlayClient) { + handler.addToSendQueue(ServerboundPingPacket().toCustomPayload()) + } + @SubscribeEvent fun onDisconnect(event: FMLNetworkEvent.ClientDisconnectionFromServerEvent) { Utils.isOnHypixel = false diff --git a/src/main/kotlin/gg/skytils/skytilsmod/events/impl/HypixelPacketEvent.kt b/src/main/kotlin/gg/skytils/skytilsmod/events/impl/HypixelPacketEvent.kt new file mode 100644 index 000000000..e64a7b125 --- /dev/null +++ b/src/main/kotlin/gg/skytils/skytilsmod/events/impl/HypixelPacketEvent.kt @@ -0,0 +1,45 @@ +/* + * Skytils - Hypixel Skyblock Quality of Life Mod + * Copyright (C) 2020-2023 Skytils + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package gg.skytils.skytilsmod.events.impl + +import gg.skytils.skytilsmod.events.SkytilsEvent +import net.hypixel.modapi.error.ErrorReason +import net.hypixel.modapi.packet.HypixelPacket +import net.hypixel.modapi.packet.HypixelPacketType +import net.minecraftforge.fml.common.eventhandler.Cancelable + +@Cancelable +abstract class HypixelPacketEvent : SkytilsEvent() { + abstract val direction: Direction + + class ReceiveEvent(val packet: HypixelPacket) : HypixelPacketEvent() { + override val direction: Direction = Direction.INBOUND + } + + class SendEvent(val type: HypixelPacketType) : HypixelPacketEvent() { + override val direction: Direction = Direction.OUTBOUND + } + + class FailedEvent(val type: HypixelPacketType, val reason: ErrorReason) : HypixelPacketEvent() { + override val direction: Direction = Direction.OUTBOUND + } + + enum class Direction { + INBOUND, OUTBOUND + } +} \ No newline at end of file diff --git a/src/main/kotlin/gg/skytils/skytilsmod/listeners/ServerPayloadInterceptor.kt b/src/main/kotlin/gg/skytils/skytilsmod/listeners/ServerPayloadInterceptor.kt new file mode 100644 index 000000000..401e77772 --- /dev/null +++ b/src/main/kotlin/gg/skytils/skytilsmod/listeners/ServerPayloadInterceptor.kt @@ -0,0 +1,66 @@ +/* + * Skytils - Hypixel Skyblock Quality of Life Mod + * Copyright (C) 2020-2024 Skytils + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package gg.skytils.skytilsmod.listeners + +import gg.skytils.skytilsmod.events.impl.HypixelPacketEvent +import gg.skytils.skytilsmod.events.impl.PacketEvent +import io.netty.buffer.Unpooled +import net.hypixel.modapi.error.ErrorReason +import net.hypixel.modapi.packet.HypixelPacket +import net.hypixel.modapi.packet.HypixelPacketType +import net.hypixel.modapi.serializer.PacketSerializer +import net.minecraft.network.PacketBuffer +import net.minecraft.network.play.client.C17PacketCustomPayload +import net.minecraft.network.play.server.S3FPacketCustomPayload +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +object ServerPayloadInterceptor { + @SubscribeEvent(priority = EventPriority.HIGHEST) + fun onReceivePacket(event: PacketEvent.ReceiveEvent) { + if (event.packet is S3FPacketCustomPayload) { + HypixelPacketType.getByIdentifier(event.packet.channelName)?.let { pt -> + val packetSerializer = PacketSerializer(event.packet.bufferData.duplicate()) + if (!packetSerializer.readBoolean()) { + val reason = ErrorReason.getById(packetSerializer.readVarInt()) + HypixelPacketEvent.FailedEvent(pt, reason).postAndCatch() + } else { + val packet = pt.packetFactory.apply(packetSerializer) + HypixelPacketEvent.ReceiveEvent(packet).postAndCatch() + } + } + } + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + fun onSendPacket(event: PacketEvent.SendEvent) { + if (event.packet is C17PacketCustomPayload) { + HypixelPacketType.getByIdentifier(event.packet.channelName)?.let { pt -> + HypixelPacketEvent.SendEvent(pt).postAndCatch() + } + } + } + + fun HypixelPacket.toCustomPayload(): C17PacketCustomPayload { + val buffer = PacketBuffer(Unpooled.buffer()) + val serializer = PacketSerializer(buffer) + this.write(serializer) + return C17PacketCustomPayload(type.identifier, buffer) + } +} \ No newline at end of file From d78a4803375c5ebf978cf74cd97293fc106cbacc Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Thu, 11 Apr 2024 16:33:27 -0400 Subject: [PATCH 02/25] feat: add coroutine awaiting for Hypixel packets --- .../kotlin/gg/skytils/skytilsmod/Skytils.kt | 15 +++----- .../listeners/ServerPayloadInterceptor.kt | 35 ++++++++++++++----- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt b/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt index 74e49122f..aa714ae92 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt @@ -64,7 +64,7 @@ import gg.skytils.skytilsmod.gui.ReopenableGUI import gg.skytils.skytilsmod.listeners.ChatListener import gg.skytils.skytilsmod.listeners.DungeonListener import gg.skytils.skytilsmod.listeners.ServerPayloadInterceptor -import gg.skytils.skytilsmod.listeners.ServerPayloadInterceptor.toCustomPayload +import gg.skytils.skytilsmod.listeners.ServerPayloadInterceptor.getResponse import gg.skytils.skytilsmod.localapi.LocalAPI import gg.skytils.skytilsmod.mixins.extensions.ExtensionEntityLivingBase import gg.skytils.skytilsmod.mixins.hooks.entity.EntityPlayerSPHook @@ -547,13 +547,6 @@ class Skytils { } } - @SubscribeEvent - fun onHypixelPacket(event: HypixelPacketEvent.ReceiveEvent) { - if (event.packet is ClientboundPingPacket) { - println("${event.packet.response} ${event.packet.version}") - } - } - @SubscribeEvent fun onHypixelPacketFail(event: HypixelPacketEvent.FailedEvent) { UChat.chat("$failPrefix Mod API request failed: ${event.reason}") @@ -601,8 +594,10 @@ class Skytils { } } - fun onJoinHypixel(handler: NetHandlerPlayClient) { - handler.addToSendQueue(ServerboundPingPacket().toCustomPayload()) + fun onJoinHypixel(handler: NetHandlerPlayClient) = IO.launch { + ServerboundPingPacket().getResponse(handler).let { packet -> + println("Hypixel Pong: ${packet.response}, version ${packet.version}") + } } @SubscribeEvent diff --git a/src/main/kotlin/gg/skytils/skytilsmod/listeners/ServerPayloadInterceptor.kt b/src/main/kotlin/gg/skytils/skytilsmod/listeners/ServerPayloadInterceptor.kt index 401e77772..d13a26054 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/listeners/ServerPayloadInterceptor.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/listeners/ServerPayloadInterceptor.kt @@ -18,31 +18,44 @@ package gg.skytils.skytilsmod.listeners +import gg.skytils.skytilsmod.Skytils.Companion.IO import gg.skytils.skytilsmod.events.impl.HypixelPacketEvent import gg.skytils.skytilsmod.events.impl.PacketEvent import io.netty.buffer.Unpooled +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import kotlinx.coroutines.withTimeout import net.hypixel.modapi.error.ErrorReason import net.hypixel.modapi.packet.HypixelPacket import net.hypixel.modapi.packet.HypixelPacketType import net.hypixel.modapi.serializer.PacketSerializer +import net.minecraft.client.network.NetHandlerPlayClient import net.minecraft.network.PacketBuffer import net.minecraft.network.play.client.C17PacketCustomPayload import net.minecraft.network.play.server.S3FPacketCustomPayload import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import kotlin.time.Duration.Companion.minutes object ServerPayloadInterceptor { + private val receivedPackets = MutableSharedFlow() + @SubscribeEvent(priority = EventPriority.HIGHEST) fun onReceivePacket(event: PacketEvent.ReceiveEvent) { if (event.packet is S3FPacketCustomPayload) { - HypixelPacketType.getByIdentifier(event.packet.channelName)?.let { pt -> - val packetSerializer = PacketSerializer(event.packet.bufferData.duplicate()) - if (!packetSerializer.readBoolean()) { - val reason = ErrorReason.getById(packetSerializer.readVarInt()) - HypixelPacketEvent.FailedEvent(pt, reason).postAndCatch() - } else { - val packet = pt.packetFactory.apply(packetSerializer) - HypixelPacketEvent.ReceiveEvent(packet).postAndCatch() + IO.launch { + HypixelPacketType.getByIdentifier(event.packet.channelName)?.let { pt -> + val packetSerializer = PacketSerializer(event.packet.bufferData.duplicate()) + if (!packetSerializer.readBoolean()) { + val reason = ErrorReason.getById(packetSerializer.readVarInt()) + HypixelPacketEvent.FailedEvent(pt, reason).postAndCatch() + } else { + val packet = pt.packetFactory.apply(packetSerializer) + receivedPackets.emit(packet) + HypixelPacketEvent.ReceiveEvent(packet).postAndCatch() + } } } } @@ -63,4 +76,10 @@ object ServerPayloadInterceptor { this.write(serializer) return C17PacketCustomPayload(type.identifier, buffer) } + + suspend fun HypixelPacket.getResponse(handler: NetHandlerPlayClient): T = withTimeout(1.minutes) { + val packet: C17PacketCustomPayload = this@getResponse.toCustomPayload() + handler.addToSendQueue(packet) + return@withTimeout receivedPackets.filter { it.type == this@getResponse.type }.first() as T + } } \ No newline at end of file From ee118aba607c952959b84b795e3490f992119a2a Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Thu, 11 Apr 2024 16:47:50 -0400 Subject: [PATCH 03/25] feat: add command to send hypixel packets --- .../commands/impl/SkytilsCommand.kt | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/gg/skytils/skytilsmod/commands/impl/SkytilsCommand.kt b/src/main/kotlin/gg/skytils/skytilsmod/commands/impl/SkytilsCommand.kt index c4e527b97..b6a02ec1f 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/commands/impl/SkytilsCommand.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/commands/impl/SkytilsCommand.kt @@ -45,18 +45,24 @@ import gg.skytils.skytilsmod.features.impl.misc.Ping import gg.skytils.skytilsmod.features.impl.misc.PricePaid import gg.skytils.skytilsmod.features.impl.slayer.SlayerFeatures import gg.skytils.skytilsmod.features.impl.trackers.Tracker -import gg.skytils.skytilsmod.gui.* +import gg.skytils.skytilsmod.gui.OptionsGui import gg.skytils.skytilsmod.gui.editing.ElementaEditingGui import gg.skytils.skytilsmod.gui.editing.VanillaEditingGui import gg.skytils.skytilsmod.gui.features.* import gg.skytils.skytilsmod.gui.profile.ProfileGui import gg.skytils.skytilsmod.gui.updater.UpdateGui import gg.skytils.skytilsmod.gui.waypoints.WaypointsGui +import gg.skytils.skytilsmod.listeners.ServerPayloadInterceptor.getResponse import gg.skytils.skytilsmod.localapi.LocalAPI import gg.skytils.skytilsmod.utils.* import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import net.hypixel.modapi.packet.HypixelPacket +import net.hypixel.modapi.packet.impl.serverbound.ServerboundLocationPacket +import net.hypixel.modapi.packet.impl.serverbound.ServerboundPartyInfoPacket +import net.hypixel.modapi.packet.impl.serverbound.ServerboundPingPacket +import net.hypixel.modapi.packet.impl.serverbound.ServerboundPlayerInfoPacket import net.minecraft.client.entity.EntityPlayerSP import net.minecraft.client.gui.GuiScreen import net.minecraft.command.WrongUsageException @@ -376,6 +382,27 @@ object SkytilsCommand : BaseCommand("skytils", listOf("st")) { } } + "hypixelpacket" -> { + val packet = when (args.getOrNull(1)) { + "ping" -> ServerboundPingPacket() + "location" -> ServerboundLocationPacket() + "party_info" -> ServerboundPartyInfoPacket() + "player_info" -> ServerboundPlayerInfoPacket() + else -> return UChat.chat("$failPrefix §cPacket not found!") + } + + UChat.chat("$successPrefix §aPacket created: $packet") + Skytils.IO.launch { + runCatching { + packet.getResponse(mc.netHandler) + }.onFailure { + UChat.chat("$failPrefix §cFailed to get packet response: ${it.message}") + }.onSuccess { response -> + UChat.chat("$successPrefix §aPacket response: $response") + } + } + } + else -> UChat.chat("$failPrefix §cThis command doesn't exist!\n §cUse §f/skytils help§c for a full list of commands") } } From 5dace83f62beddd463abf35a9050f26ffa30708f Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:15:03 -0400 Subject: [PATCH 04/25] chore: update hypixel mod-api to 0.3.1 chore: update to mod-api 0.3.2 --- build.gradle.kts | 2 +- .../AccessorHypixelPacketRegistry.java | 32 +++++++++++++ .../commands/impl/SkytilsCommand.kt | 45 ++++++++++--------- .../events/impl/HypixelPacketEvent.kt | 9 ++-- .../listeners/ServerPayloadInterceptor.kt | 28 +++++++----- src/main/resources/mixins.skytils.json | 1 + 6 files changed, 79 insertions(+), 38 deletions(-) create mode 100644 src/main/java/gg/skytils/skytilsmod/mixins/transformers/accessors/AccessorHypixelPacketRegistry.java diff --git a/build.gradle.kts b/build.gradle.kts index 0de006008..cd4949a81 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -152,7 +152,7 @@ dependencies { exclude(module = "bcprov-jdk18on") } compileOnly("org.bouncycastle:bcprov-jdk18on:1.78.1") - shadowMe("net.hypixel:mod-api:0.2.1") + shadowMe("net.hypixel:mod-api:0.3.2") shadowMe(annotationProcessor("io.github.llamalad7:mixinextras-common:0.3.5")!!) diff --git a/src/main/java/gg/skytils/skytilsmod/mixins/transformers/accessors/AccessorHypixelPacketRegistry.java b/src/main/java/gg/skytils/skytilsmod/mixins/transformers/accessors/AccessorHypixelPacketRegistry.java new file mode 100644 index 000000000..4e30a711b --- /dev/null +++ b/src/main/java/gg/skytils/skytilsmod/mixins/transformers/accessors/AccessorHypixelPacketRegistry.java @@ -0,0 +1,32 @@ +/* + * Skytils - Hypixel Skyblock Quality of Life Mod + * Copyright (C) 2020-2024 Skytils + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package gg.skytils.skytilsmod.mixins.transformers.accessors; + +import net.hypixel.modapi.packet.HypixelPacket; +import net.hypixel.modapi.packet.PacketRegistry; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(PacketRegistry.class) +public interface AccessorHypixelPacketRegistry { + @Accessor + Map, String> getClassToIdentifier(); +} diff --git a/src/main/kotlin/gg/skytils/skytilsmod/commands/impl/SkytilsCommand.kt b/src/main/kotlin/gg/skytils/skytilsmod/commands/impl/SkytilsCommand.kt index b6a02ec1f..15f3a8e0a 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/commands/impl/SkytilsCommand.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/commands/impl/SkytilsCommand.kt @@ -54,15 +54,14 @@ import gg.skytils.skytilsmod.gui.updater.UpdateGui import gg.skytils.skytilsmod.gui.waypoints.WaypointsGui import gg.skytils.skytilsmod.listeners.ServerPayloadInterceptor.getResponse import gg.skytils.skytilsmod.localapi.LocalAPI +import gg.skytils.skytilsmod.mixins.transformers.accessors.AccessorHypixelPacketRegistry import gg.skytils.skytilsmod.utils.* import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import net.hypixel.modapi.packet.HypixelPacket -import net.hypixel.modapi.packet.impl.serverbound.ServerboundLocationPacket -import net.hypixel.modapi.packet.impl.serverbound.ServerboundPartyInfoPacket -import net.hypixel.modapi.packet.impl.serverbound.ServerboundPingPacket -import net.hypixel.modapi.packet.impl.serverbound.ServerboundPlayerInfoPacket +import net.hypixel.modapi.HypixelModAPI +import net.hypixel.modapi.packet.ClientboundHypixelPacket +import net.hypixel.modapi.packet.impl.serverbound.ServerboundVersionedPacket import net.minecraft.client.entity.EntityPlayerSP import net.minecraft.client.gui.GuiScreen import net.minecraft.command.WrongUsageException @@ -383,22 +382,26 @@ object SkytilsCommand : BaseCommand("skytils", listOf("st")) { } "hypixelpacket" -> { - val packet = when (args.getOrNull(1)) { - "ping" -> ServerboundPingPacket() - "location" -> ServerboundLocationPacket() - "party_info" -> ServerboundPartyInfoPacket() - "player_info" -> ServerboundPlayerInfoPacket() - else -> return UChat.chat("$failPrefix §cPacket not found!") - } - - UChat.chat("$successPrefix §aPacket created: $packet") - Skytils.IO.launch { - runCatching { - packet.getResponse(mc.netHandler) - }.onFailure { - UChat.chat("$failPrefix §cFailed to get packet response: ${it.message}") - }.onSuccess { response -> - UChat.chat("$successPrefix §aPacket response: $response") + val registry = HypixelModAPI.getInstance().registry + val id = args.getOrNull(1) ?: return UChat.chat("$failPrefix §cInput a packet type!") + if (id == "list") { + UChat.chat("$successPrefix §eAvailable types: ${registry.identifiers.joinToString(", ")}") + } else if (!registry.isRegistered(id)) { + UChat.chat("$failPrefix §cPacket not found!") + } else { + registry as AccessorHypixelPacketRegistry + val packetClass = registry.classToIdentifier.entries.find { it.value == id && ServerboundVersionedPacket::class.java.isAssignableFrom(it.key) } + ?: return UChat.chat("$failPrefix §cPacket not found!") + val packet = packetClass.key.newInstance() as ServerboundVersionedPacket + UChat.chat("$successPrefix §aPacket created: $packet") + Skytils.IO.launch { + runCatching { + packet.getResponse(mc.netHandler) + }.onFailure { + UChat.chat("$failPrefix §cFailed to get packet response: ${it.message}") + }.onSuccess { response -> + UChat.chat("$successPrefix §aPacket response: $response") + } } } } diff --git a/src/main/kotlin/gg/skytils/skytilsmod/events/impl/HypixelPacketEvent.kt b/src/main/kotlin/gg/skytils/skytilsmod/events/impl/HypixelPacketEvent.kt index e64a7b125..e25f707c7 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/events/impl/HypixelPacketEvent.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/events/impl/HypixelPacketEvent.kt @@ -19,23 +19,22 @@ package gg.skytils.skytilsmod.events.impl import gg.skytils.skytilsmod.events.SkytilsEvent import net.hypixel.modapi.error.ErrorReason -import net.hypixel.modapi.packet.HypixelPacket -import net.hypixel.modapi.packet.HypixelPacketType +import net.hypixel.modapi.packet.ClientboundHypixelPacket import net.minecraftforge.fml.common.eventhandler.Cancelable @Cancelable abstract class HypixelPacketEvent : SkytilsEvent() { abstract val direction: Direction - class ReceiveEvent(val packet: HypixelPacket) : HypixelPacketEvent() { + class ReceiveEvent(val packet: ClientboundHypixelPacket) : HypixelPacketEvent() { override val direction: Direction = Direction.INBOUND } - class SendEvent(val type: HypixelPacketType) : HypixelPacketEvent() { + class SendEvent(val type: String) : HypixelPacketEvent() { override val direction: Direction = Direction.OUTBOUND } - class FailedEvent(val type: HypixelPacketType, val reason: ErrorReason) : HypixelPacketEvent() { + class FailedEvent(val type: String, val reason: ErrorReason) : HypixelPacketEvent() { override val direction: Direction = Direction.OUTBOUND } diff --git a/src/main/kotlin/gg/skytils/skytilsmod/listeners/ServerPayloadInterceptor.kt b/src/main/kotlin/gg/skytils/skytilsmod/listeners/ServerPayloadInterceptor.kt index d13a26054..4dcfe6117 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/listeners/ServerPayloadInterceptor.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/listeners/ServerPayloadInterceptor.kt @@ -27,9 +27,11 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout +import net.hypixel.modapi.HypixelModAPI import net.hypixel.modapi.error.ErrorReason +import net.hypixel.modapi.packet.ClientboundHypixelPacket import net.hypixel.modapi.packet.HypixelPacket -import net.hypixel.modapi.packet.HypixelPacketType +import net.hypixel.modapi.packet.impl.serverbound.ServerboundVersionedPacket import net.hypixel.modapi.serializer.PacketSerializer import net.minecraft.client.network.NetHandlerPlayClient import net.minecraft.network.PacketBuffer @@ -40,19 +42,21 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import kotlin.time.Duration.Companion.minutes object ServerPayloadInterceptor { - private val receivedPackets = MutableSharedFlow() + private val receivedPackets = MutableSharedFlow() @SubscribeEvent(priority = EventPriority.HIGHEST) fun onReceivePacket(event: PacketEvent.ReceiveEvent) { if (event.packet is S3FPacketCustomPayload) { IO.launch { - HypixelPacketType.getByIdentifier(event.packet.channelName)?.let { pt -> + val registry = HypixelModAPI.getInstance().registry + val id = event.packet.channelName + if (registry.isRegistered(id)) { val packetSerializer = PacketSerializer(event.packet.bufferData.duplicate()) if (!packetSerializer.readBoolean()) { val reason = ErrorReason.getById(packetSerializer.readVarInt()) - HypixelPacketEvent.FailedEvent(pt, reason).postAndCatch() + HypixelPacketEvent.FailedEvent(id, reason).postAndCatch() } else { - val packet = pt.packetFactory.apply(packetSerializer) + val packet = registry.createClientboundPacket(id, packetSerializer) receivedPackets.emit(packet) HypixelPacketEvent.ReceiveEvent(packet).postAndCatch() } @@ -64,22 +68,24 @@ object ServerPayloadInterceptor { @SubscribeEvent(priority = EventPriority.HIGHEST) fun onSendPacket(event: PacketEvent.SendEvent) { if (event.packet is C17PacketCustomPayload) { - HypixelPacketType.getByIdentifier(event.packet.channelName)?.let { pt -> - HypixelPacketEvent.SendEvent(pt).postAndCatch() + val registry = HypixelModAPI.getInstance().registry + val id = event.packet.channelName + if (registry.isRegistered(id)) { + HypixelPacketEvent.SendEvent(id).postAndCatch() } } } - fun HypixelPacket.toCustomPayload(): C17PacketCustomPayload { + fun ServerboundVersionedPacket.toCustomPayload(): C17PacketCustomPayload { val buffer = PacketBuffer(Unpooled.buffer()) val serializer = PacketSerializer(buffer) this.write(serializer) - return C17PacketCustomPayload(type.identifier, buffer) + return C17PacketCustomPayload(this.identifier, buffer) } - suspend fun HypixelPacket.getResponse(handler: NetHandlerPlayClient): T = withTimeout(1.minutes) { + suspend fun ServerboundVersionedPacket.getResponse(handler: NetHandlerPlayClient): T = withTimeout(1.minutes) { val packet: C17PacketCustomPayload = this@getResponse.toCustomPayload() handler.addToSendQueue(packet) - return@withTimeout receivedPackets.filter { it.type == this@getResponse.type }.first() as T + return@withTimeout receivedPackets.filter { it.identifier == this@getResponse.identifier }.first() as T } } \ No newline at end of file diff --git a/src/main/resources/mixins.skytils.json b/src/main/resources/mixins.skytils.json index 02f0872fc..7b52269bd 100644 --- a/src/main/resources/mixins.skytils.json +++ b/src/main/resources/mixins.skytils.json @@ -20,6 +20,7 @@ "accessors.AccessorGuiMainMenu", "accessors.AccessorGuiNewChat", "accessors.AccessorGuiStreamUnavailable", + "accessors.AccessorHypixelPacketRegistry", "accessors.AccessorMinecraft", "accessors.AccessorModelDragon", "accessors.AccessorRenderItem", From ab0088b214fa8c17a7a9bce8d312620a6257de6c Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Sat, 18 May 2024 18:08:53 -0400 Subject: [PATCH 05/25] feat: add dungeon syncing through ws chore: update ws URL fix: rebase errors fix: rebase error fix: queue room packets from before dungeon start fix: rebase error didn't close SkytilsWS connection --- .gitmodules | 4 + build.gradle.kts | 3 +- settings.gradle.kts | 1 + .../kotlin/gg/skytils/skytilsmod/Skytils.kt | 29 ++++++- .../dungeons/catlas/core/map/UniqueRoom.kt | 3 +- .../catlas/handlers/DungeonScanner.kt | 15 ++++ .../features/impl/handlers/Waypoints.kt | 2 - .../skytilsmod/listeners/DungeonListener.kt | 80 ++++++++++++++++--- .../gg/skytils/skytilsmod/utils/SBInfo.kt | 2 +- .../skytils/skytilsws/client/PacketHandler.kt | 76 ++++++++++++++++++ .../gg/skytils/skytilsws/client/WSClient.kt | 63 +++++++++++++++ ws-shared | 1 + 12 files changed, 261 insertions(+), 18 deletions(-) create mode 100644 src/main/kotlin/gg/skytils/skytilsws/client/PacketHandler.kt create mode 100644 src/main/kotlin/gg/skytils/skytilsws/client/WSClient.kt create mode 160000 ws-shared diff --git a/.gitmodules b/.gitmodules index ff5e49108..0f76f3979 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,7 @@ [submodule "hypixel-api"] path = hypixel-api url = https://github.com/Skytils/hypixel-api + +[submodule "ws-shared"] + path = ws-shared + url = https://github.com/Skytils/ws-shared \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index cd4949a81..53d28d6e2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -115,7 +115,7 @@ dependencies { } shadowMe(platform(kotlin("bom"))) - shadowMe(platform(ktor("bom", "2.3.9", addSuffix = false))) + shadowMe(platform(ktor("bom", "2.3.11", addSuffix = false))) shadowMe(ktor("serialization-kotlinx-json")) @@ -147,6 +147,7 @@ dependencies { shadowMe(project(":events")) shadowMe(project(":hypixel-api:types")) + shadowMe(project(":ws-shared")) shadowMe("org.bouncycastle:bcpg-jdk18on:1.78.1") { exclude(module = "bcprov-jdk18on") diff --git a/settings.gradle.kts b/settings.gradle.kts index 19334a791..427b3e7b8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -37,3 +37,4 @@ pluginManagement { rootProject.name = "SkytilsMod" include("events") include("hypixel-api:types") +include("ws-shared") diff --git a/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt b/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt index aa714ae92..5fa5e8d2c 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt @@ -25,7 +25,6 @@ import gg.skytils.skytilsmod.commands.impl.* import gg.skytils.skytilsmod.commands.stats.impl.CataCommand import gg.skytils.skytilsmod.commands.stats.impl.SlayerCommand import gg.skytils.skytilsmod.core.* -import gg.skytils.skytilsmod.tweaker.DependencyLoader import gg.skytils.skytilsmod.events.impl.HypixelPacketEvent import gg.skytils.skytilsmod.events.impl.MainReceivePacketEvent import gg.skytils.skytilsmod.events.impl.PacketEvent @@ -72,18 +71,25 @@ import gg.skytils.skytilsmod.mixins.hooks.util.MouseHelperHook import gg.skytils.skytilsmod.mixins.transformers.accessors.AccessorCommandHandler import gg.skytils.skytilsmod.mixins.transformers.accessors.AccessorGuiStreamUnavailable import gg.skytils.skytilsmod.mixins.transformers.accessors.AccessorSettingsGui +import gg.skytils.skytilsmod.tweaker.DependencyLoader import gg.skytils.skytilsmod.utils.* import gg.skytils.skytilsmod.utils.graphics.ScreenRenderer import gg.skytils.skytilsmod.utils.graphics.colors.CustomColor +import gg.skytils.skytilsws.client.WSClient +import gg.skytils.skytilsws.shared.SkytilsWS import io.ktor.client.* import io.ktor.client.engine.cio.* import io.ktor.client.plugins.* import io.ktor.client.plugins.cache.* import io.ktor.client.plugins.compression.* import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.plugins.websocket.* import io.ktor.http.* +import io.ktor.serialization.kotlinx.* import io.ktor.serialization.kotlinx.json.* +import io.ktor.websocket.* import kotlinx.coroutines.* +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule import net.hypixel.modapi.packet.impl.clientbound.ClientboundPingPacket @@ -123,6 +129,7 @@ import java.security.KeyStore import java.util.* import java.util.concurrent.Executors import java.util.concurrent.ThreadPoolExecutor +import java.util.zip.Deflater import javax.net.ssl.TrustManagerFactory import javax.net.ssl.X509TrustManager import kotlin.coroutines.CoroutineContext @@ -260,6 +267,18 @@ class Skytils { trustManager = UnionX509TrustManager(backingManager, ourManager) } } + + install(WebSockets) { + pingInterval = 5_000L + @OptIn(ExperimentalSerializationApi::class) + contentConverter = KotlinxWebsocketSerializationConverter(SkytilsWS.packetSerializer) + extensions { + install(WebSocketDeflateExtension) { + compressionLevel = Deflater.DEFAULT_COMPRESSION + compressIfBiggerThan(bytes = 4 * 1024) + } + } + } } val areaRegex = Regex("§r§b§l(?[\\w]+): §r§7(?[\\w ]+)§r") @@ -545,6 +564,10 @@ class Skytils { IO.launch { TrophyFish.loadFromApi() } + + IO.launch { + WSClient.openConnection() + } } @SubscribeEvent @@ -605,6 +628,10 @@ class Skytils { Utils.isOnHypixel = false Utils.skyblock = false Utils.dungeons = false + + IO.launch { + WSClient.closeConnection() + } } @SubscribeEvent diff --git a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/core/map/UniqueRoom.kt b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/core/map/UniqueRoom.kt index c3c2ceb51..082dcec70 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/core/map/UniqueRoom.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/core/map/UniqueRoom.kt @@ -10,7 +10,8 @@ class UniqueRoom(arrX: Int, arrY: Int, room: Room) { private var topLeft = Pair(arrX, arrY) private var center = Pair(arrX, arrY) var mainRoom = room - private val tiles = mutableListOf(room) + val tiles = mutableListOf(room) + var foundSecrets: Int? = null init { DungeonInfo.cryptCount += room.data.crypts diff --git a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/handlers/DungeonScanner.kt b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/handlers/DungeonScanner.kt index f1699f04d..57154b242 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/handlers/DungeonScanner.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/handlers/DungeonScanner.kt @@ -18,11 +18,21 @@ package gg.skytils.skytilsmod.features.impl.dungeons.catlas.handlers +import gg.skytils.skytilsmod.Skytils.Companion.IO import gg.skytils.skytilsmod.Skytils.Companion.mc import gg.skytils.skytilsmod.features.impl.dungeons.DungeonFeatures.dungeonFloorNumber import gg.skytils.skytilsmod.features.impl.dungeons.catlas.core.map.* import gg.skytils.skytilsmod.features.impl.dungeons.catlas.handlers.DungeonScanner.scan import gg.skytils.skytilsmod.features.impl.dungeons.catlas.utils.ScanUtils +import gg.skytils.skytilsmod.listeners.DungeonListener +import gg.skytils.skytilsmod.listeners.ServerPayloadInterceptor.getResponse +import gg.skytils.skytilsmod.utils.SBInfo +import gg.skytils.skytilsws.client.WSClient +import gg.skytils.skytilsws.shared.packet.C2SPacketDungeonRoom +import gg.skytils.skytilsws.shared.packet.C2SPacketDungeonRoomSecret +import kotlinx.coroutines.launch +import net.hypixel.modapi.packet.impl.clientbound.ClientboundLocationPacket +import net.hypixel.modapi.packet.impl.serverbound.ServerboundLocationPacket import net.minecraft.init.Blocks import net.minecraft.util.BlockPos @@ -73,6 +83,11 @@ object DungeonScanner { scanRoom(xPos, zPos, z, x)?.let { DungeonInfo.dungeonList[z * 11 + x] = it + if (it is Room && it.data.name != "Unknown") { + IO.launch { + DungeonListener.outboundRoomQueue.add(C2SPacketDungeonRoom(SBInfo.server ?: return@launch, it.data.name, xPos, zPos, x, z, it.core, it.isSeparator)) + } + } } } } diff --git a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/handlers/Waypoints.kt b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/handlers/Waypoints.kt index 8acd1f692..95d9674e9 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/handlers/Waypoints.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/handlers/Waypoints.kt @@ -25,8 +25,6 @@ import gg.essential.universal.UMatrixStack import gg.skytils.skytilsmod.Skytils import gg.skytils.skytilsmod.commands.impl.OrderedWaypointCommand import gg.skytils.skytilsmod.core.PersistentSave -import gg.skytils.skytilsmod.core.tickTimer -import gg.skytils.skytilsmod.events.impl.skyblock.LocrawReceivedEvent import gg.skytils.skytilsmod.tweaker.DependencyLoader import gg.skytils.skytilsmod.utils.* import kotlinx.serialization.EncodeDefault diff --git a/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt b/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt index a0bced94d..77a965040 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt @@ -24,6 +24,7 @@ import gg.essential.lib.caffeine.cache.Expiry import gg.essential.universal.UChat import gg.skytils.hypixel.types.skyblock.Pet import gg.skytils.skytilsmod.Skytils +import gg.skytils.skytilsmod.Skytils.Companion.IO import gg.skytils.skytilsmod.Skytils.Companion.failPrefix import gg.skytils.skytilsmod.Skytils.Companion.mc import gg.skytils.skytilsmod.commands.impl.RepartyCommand @@ -35,14 +36,28 @@ import gg.skytils.skytilsmod.features.impl.dungeons.DungeonFeatures import gg.skytils.skytilsmod.features.impl.dungeons.DungeonTimer import gg.skytils.skytilsmod.features.impl.dungeons.ScoreCalculation import gg.skytils.skytilsmod.features.impl.dungeons.catlas.core.DungeonMapPlayer +import gg.skytils.skytilsmod.features.impl.dungeons.catlas.core.map.RoomType import gg.skytils.skytilsmod.features.impl.dungeons.catlas.handlers.DungeonInfo +import gg.skytils.skytilsmod.features.impl.dungeons.catlas.handlers.DungeonScanner +import gg.skytils.skytilsmod.features.impl.dungeons.catlas.utils.MapUtils import gg.skytils.skytilsmod.features.impl.handlers.CooldownTracker import gg.skytils.skytilsmod.features.impl.handlers.SpiritLeap +import gg.skytils.skytilsmod.listeners.ServerPayloadInterceptor.getResponse import gg.skytils.skytilsmod.mixins.transformers.accessors.AccessorChatComponentText import gg.skytils.skytilsmod.utils.* import gg.skytils.skytilsmod.utils.NumberUtil.addSuffix import gg.skytils.skytilsmod.utils.NumberUtil.romanToDecimal +import gg.skytils.skytilsws.client.WSClient +import gg.skytils.skytilsws.shared.packet.C2SPacketDungeonRoom +import gg.skytils.skytilsws.shared.packet.C2SPacketDungeonRoomSecret +import gg.skytils.skytilsws.shared.packet.C2SPacketStartDungeon +import kotlinx.coroutines.async +import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import net.hypixel.modapi.packet.impl.clientbound.ClientboundLocationPacket +import net.hypixel.modapi.packet.impl.clientbound.ClientboundPartyInfoPacket +import net.hypixel.modapi.packet.impl.serverbound.ServerboundLocationPacket +import net.hypixel.modapi.packet.impl.serverbound.ServerboundPartyInfoPacket import net.minecraft.entity.player.EntityPlayer import net.minecraft.network.play.server.S02PacketChat import net.minecraft.util.ResourceLocation @@ -50,6 +65,7 @@ import net.minecraftforge.client.event.ClientChatReceivedEvent import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.concurrent.CopyOnWriteArrayList object DungeonListener { val team = hashMapOf() @@ -99,6 +115,7 @@ object DungeonListener { private val keyPickupRegex = Regex("§r§e§lRIGHT CLICK §r§7on §r§7.+?§r§7 to open it\\. This key can only be used to open §r§a(?\\d+)§r§7 door!§r") private val witherDoorOpenedRegex = Regex("^(?:\\[.+?] )?(?\\w+) opened a WITHER door!$") private const val bloodOpenedString = "§r§cThe §r§c§lBLOOD DOOR§r§c has been opened!§r" + val outboundRoomQueue = arrayListOf() @SubscribeEvent fun onWorldLoad(event: WorldEvent.Unload) { @@ -108,6 +125,7 @@ object DungeonListener { missingPuzzles.clear() completedPuzzles.clear() teamCached.clear() + outboundRoomQueue.clear() } @SubscribeEvent @@ -117,19 +135,28 @@ object DungeonListener { val text = event.packet.chatComponent.formattedText val unformatted = text.stripControlCodes() if (event.packet.type == 2.toByte()) { - if (Skytils.config.dungeonSecretDisplay) { - secretsRegex.find(text)?.destructured?.also { (secrets, maxSecrets) -> - val sec = secrets.toInt() - val max = maxSecrets.toInt().coerceAtLeast(sec) - - DungeonFeatures.DungeonSecretDisplay.secrets = sec - DungeonFeatures.DungeonSecretDisplay.maxSecrets = max - }.ifNull { - DungeonFeatures.DungeonSecretDisplay.secrets = -1 - DungeonFeatures.DungeonSecretDisplay.maxSecrets = -1 + secretsRegex.find(text)?.destructured?.also { (secrets, maxSecrets) -> + val sec = secrets.toInt() + val max = maxSecrets.toInt().coerceAtLeast(sec) + + DungeonFeatures.DungeonSecretDisplay.secrets = sec + DungeonFeatures.DungeonSecretDisplay.maxSecrets = max + + IO.launch { + val x = ((mc.thePlayer.posX - DungeonScanner.startX + 15) * MapUtils.coordMultiplier / (MapUtils.mapRoomSize + 4) * 2).toInt() + val z = ((mc.thePlayer.posZ - DungeonScanner.startZ + 15) * MapUtils.coordMultiplier / (MapUtils.mapRoomSize + 4) * 2).toInt() + + val room = DungeonInfo.uniqueRooms.find { it.tiles.any { it.x == x && it.z == z } } + + if (room != null && room.foundSecrets != sec) { + room.foundSecrets = sec + WSClient.sendPacket(C2SPacketDungeonRoomSecret(SBInfo.locraw?.server ?: ServerboundLocationPacket().getResponse(mc.netHandler).serverName, room.mainRoom.data.name, sec)) + } } + }.ifNull { + DungeonFeatures.DungeonSecretDisplay.secrets = -1 + DungeonFeatures.DungeonSecretDisplay.maxSecrets = -1 } - } else { if (text.stripControlCodes() .trim() == "> EXTRA STATS <" @@ -170,6 +197,35 @@ object DungeonListener { } else if (text == bloodOpenedString) { SpiritLeap.doorOpener = null DungeonInfo.keys-- + } else if (text == "§r§aStarting in 1 second.§r") { + IO.launch { + delay(2000) + if (DungeonTimer.dungeonStartTime != -1L) { + val location = async { + ServerboundLocationPacket().getResponse(mc.netHandler) + } + val party = async { + ServerboundPartyInfoPacket().getResponse(mc.netHandler) + } + val partyMembers = party.await().members.ifEmpty { setOf(mc.thePlayer.uniqueID) }.mapTo(hashSetOf()) { it.toString() } + val entrance = DungeonInfo.uniqueRooms.first { it.mainRoom.data.type == RoomType.ENTRANCE } + WSClient.sendPacket(C2SPacketStartDungeon( + serverId = location.await().serverName, + floor = DungeonFeatures.dungeonFloor!!, + members = partyMembers, + startTime = DungeonTimer.dungeonStartTime, + entranceLoc = entrance.mainRoom.z * entrance.mainRoom.x + )) + while (DungeonTimer.dungeonStartTime != -1L) { + val itr = outboundRoomQueue.iterator() + while (itr.hasNext()) { + val packet = itr.next() + itr.remove() + WSClient.sendPacket(packet) + } + } + } + } } else { witherDoorOpenedRegex.find(unformatted)?.destructured?.let { (name) -> SpiritLeap.doorOpener = name @@ -417,7 +473,7 @@ object DungeonListener { fun checkSpiritPet() { val teamCopy = team.values.toList() - Skytils.IO.launch { + IO.launch { runCatching { for (teammate in teamCopy) { val name = teammate.playerName diff --git a/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt b/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt index 31155b573..4c7d01c8d 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt @@ -64,7 +64,7 @@ object SBInfo { private var lastManualLocRaw: Long = -1 private var lastLocRaw: Long = -1 private var joinedWorld: Long = -1 - private var locraw: LocrawObject? = null + var locraw: LocrawObject? = null private val junkRegex = Regex("[^\u0020-\u0127û]") @SubscribeEvent diff --git a/src/main/kotlin/gg/skytils/skytilsws/client/PacketHandler.kt b/src/main/kotlin/gg/skytils/skytilsws/client/PacketHandler.kt new file mode 100644 index 000000000..4a5d4323b --- /dev/null +++ b/src/main/kotlin/gg/skytils/skytilsws/client/PacketHandler.kt @@ -0,0 +1,76 @@ +/* + * Skytils - Hypixel Skyblock Quality of Life Mod + * Copyright (C) 2020-2024 Skytils + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package gg.skytils.skytilsws.client + +import gg.skytils.skytilsmod.Reference +import gg.skytils.skytilsmod.Skytils.Companion.mc +import gg.skytils.skytilsmod.features.impl.dungeons.catlas.core.map.Room +import gg.skytils.skytilsmod.features.impl.dungeons.catlas.core.map.Unknown +import gg.skytils.skytilsmod.features.impl.dungeons.catlas.handlers.DungeonInfo +import gg.skytils.skytilsmod.features.impl.dungeons.catlas.utils.ScanUtils +import gg.skytils.skytilsws.shared.IPacketHandler +import gg.skytils.skytilsws.shared.SkytilsWS +import gg.skytils.skytilsws.shared.packet.* +import io.ktor.websocket.* +import kotlinx.coroutines.coroutineScope +import java.util.* + +object PacketHandler : IPacketHandler { + suspend fun handleLogin(session: WebSocketSession, packet: S2CPacketAcknowledge) { + val serverId = UUID.randomUUID().toString().replace("-".toRegex(), "") + mc.sessionService.joinServer(mc.session.profile, mc.session.token, serverId) + WSClient.sendPacket(C2SPacketLogin(mc.session.username, mc.session.profile.id.toString(), Reference.VERSION, SkytilsWS.version, serverId)) + } + + override suspend fun processPacket(session: WebSocketSession, packet: Packet) { + println("Received packet: $packet") + when (packet) { + is S2CPacketAcknowledge -> { + if (packet.wsVersion != SkytilsWS.version) { + session.close(CloseReason(CloseReason.Codes.CANNOT_ACCEPT, "Incompatible WS version")) + } else { + coroutineScope { + handleLogin(session, packet) + } + } + } + is S2CPacketDungeonRoomSecret -> { + DungeonInfo.uniqueRooms.find { it.mainRoom.data.name == packet.roomId }?.let { + if (packet.secretCount > (it.foundSecrets ?: -1)) { + it.foundSecrets = packet.secretCount + } + } + } + is S2CPacketDungeonRoom -> { + val room = DungeonInfo.dungeonList[packet.row * 11 + packet.col] + if (room is Unknown || (room as? Room)?.data?.name == "Unknown") { + val data = ScanUtils.roomList.find { it.name == packet.roomId } + DungeonInfo.dungeonList[packet.row * 11 + packet.col] = Room(packet.x, packet.z, data ?: return).apply { + isSeparator = packet.isSeparator + core = packet.core + addToUnique(packet.row, packet.col) + } + } + } + else -> { + session.close(CloseReason(CloseReason.Codes.CANNOT_ACCEPT, "Unknown packet type")) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/gg/skytils/skytilsws/client/WSClient.kt b/src/main/kotlin/gg/skytils/skytilsws/client/WSClient.kt new file mode 100644 index 000000000..c7fb57257 --- /dev/null +++ b/src/main/kotlin/gg/skytils/skytilsws/client/WSClient.kt @@ -0,0 +1,63 @@ +/* + * Skytils - Hypixel Skyblock Quality of Life Mod + * Copyright (C) 2020-2024 Skytils + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package gg.skytils.skytilsws.client + +import gg.skytils.skytilsmod.Skytils +import gg.skytils.skytilsmod.Skytils.Companion.client +import gg.skytils.skytilsws.shared.SkytilsWS +import gg.skytils.skytilsws.shared.packet.C2SPacketConnect +import gg.skytils.skytilsws.shared.packet.Packet +import io.ktor.client.plugins.websocket.* +import io.ktor.websocket.* +import kotlinx.coroutines.channels.ClosedReceiveChannelException + +object WSClient { + var session: DefaultClientWebSocketSession? = null + + suspend fun openConnection() { + if (session != null) error("Session already open") + + client.webSocketSession("wss://ws.skytils.gg/ws").apply { + session = this + try { + sendSerialized(C2SPacketConnect(SkytilsWS.version, Skytils.VERSION)) + while (true) { + val packet = receiveDeserialized() + PacketHandler.processPacket(this@apply, packet) + } + } catch(e: ClosedReceiveChannelException) { + e.printStackTrace() + closeExceptionally(e) + } catch (e: Throwable) { + e.printStackTrace() + closeExceptionally(e) + } finally { + session = null + } + } + } + + suspend fun closeConnection() { + session?.close(CloseReason(CloseReason.Codes.NORMAL, "Client closed connection")) + } + + suspend fun sendPacket(packet: Packet) { + session?.sendSerialized(packet) ?: error("Tried to send packet but session was null") + } +} \ No newline at end of file diff --git a/ws-shared b/ws-shared new file mode 160000 index 000000000..51e490538 --- /dev/null +++ b/ws-shared @@ -0,0 +1 @@ +Subproject commit 51e4905381c9875a9283091d4ff628c904b42e66 From 6145a5d65cc5ed6c5aceec28fff2302954f10873 Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Sat, 18 May 2024 18:09:16 -0400 Subject: [PATCH 06/25] feat: remove usage of locraw for hypixel mod api fix: SBInfo fields are always null --- .../impl/skyblock/LocrawReceivedEvent.kt | 24 ------ .../features/impl/handlers/Waypoints.kt | 15 ++-- .../skytilsmod/listeners/DungeonListener.kt | 2 +- .../gg/skytils/skytilsmod/utils/SBInfo.kt | 86 +++++-------------- 4 files changed, 32 insertions(+), 95 deletions(-) delete mode 100644 src/main/kotlin/gg/skytils/skytilsmod/events/impl/skyblock/LocrawReceivedEvent.kt diff --git a/src/main/kotlin/gg/skytils/skytilsmod/events/impl/skyblock/LocrawReceivedEvent.kt b/src/main/kotlin/gg/skytils/skytilsmod/events/impl/skyblock/LocrawReceivedEvent.kt deleted file mode 100644 index 10282ea87..000000000 --- a/src/main/kotlin/gg/skytils/skytilsmod/events/impl/skyblock/LocrawReceivedEvent.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Skytils - Hypixel Skyblock Quality of Life Mod - * Copyright (C) 2020-2023 Skytils - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package gg.skytils.skytilsmod.events.impl.skyblock - -import gg.skytils.skytilsmod.events.SkytilsEvent -import gg.skytils.skytilsmod.utils.LocrawObject - -data class LocrawReceivedEvent(val loc: LocrawObject) : SkytilsEvent() \ No newline at end of file diff --git a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/handlers/Waypoints.kt b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/handlers/Waypoints.kt index 95d9674e9..424b98aac 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/handlers/Waypoints.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/handlers/Waypoints.kt @@ -60,6 +60,7 @@ object Waypoints : PersistentSave(File(Skytils.modDir, "waypoints.json")) { private val sbeWaypointFormat = Regex("(?:\\.?\\/?crystalwaypoint parse )?(?[a-zA-Z\\d]+)@-(?[-\\d]+),(?[-\\d]+),(?[-\\d]+)\\\\?n?") private var visibleWaypoints = emptyList() + var needsRefresh = false @OptIn(ExperimentalSerializationApi::class) fun getWaypointsFromString(str: String): Set { @@ -231,17 +232,17 @@ object Waypoints : PersistentSave(File(Skytils.modDir, "waypoints.json")) { @SubscribeEvent fun onWorldChange(event: WorldEvent.Unload) { visibleWaypoints = emptyList() - } - - @SubscribeEvent - fun onLocraw(event: LocrawReceivedEvent) { - tickTimer(20, task = ::computeVisibleWaypoints) + needsRefresh = true } @SubscribeEvent fun onPlayerMove(event: ClientTickEvent) { - if (event.phase == TickEvent.Phase.END || mc.thePlayer?.hasMoved != true) return - if (SBInfo.mode != null && OrderedWaypointCommand.trackedIsland?.mode == SBInfo.mode) { + if (event.phase == TickEvent.Phase.END) return + if (needsRefresh && SBInfo.mode != null) { + computeVisibleWaypoints() + needsRefresh = false + } + if (mc.thePlayer?.hasMoved == true && SBInfo.mode != null && OrderedWaypointCommand.trackedIsland?.mode == SBInfo.mode) { val tracked = OrderedWaypointCommand.trackedSet?.firstOrNull() if (tracked == null) { OrderedWaypointCommand.doneTracking() diff --git a/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt b/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt index 77a965040..2ae81684a 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt @@ -150,7 +150,7 @@ object DungeonListener { if (room != null && room.foundSecrets != sec) { room.foundSecrets = sec - WSClient.sendPacket(C2SPacketDungeonRoomSecret(SBInfo.locraw?.server ?: ServerboundLocationPacket().getResponse(mc.netHandler).serverName, room.mainRoom.data.name, sec)) + WSClient.sendPacket(C2SPacketDungeonRoomSecret(SBInfo.server ?: return@launch, room.mainRoom.data.name, sec)) } } }.ifNull { diff --git a/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt b/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt index 4c7d01c8d..320596b72 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt @@ -17,27 +17,24 @@ */ package gg.skytils.skytilsmod.utils -import gg.skytils.skytilsmod.Skytils -import gg.skytils.skytilsmod.Skytils.Companion.json import gg.skytils.skytilsmod.Skytils.Companion.mc -import gg.skytils.skytilsmod.events.impl.skyblock.LocrawReceivedEvent -import gg.skytils.skytilsmod.events.impl.PacketEvent -import gg.skytils.skytilsmod.events.impl.SendChatMessageEvent +import gg.skytils.skytilsmod.events.impl.HypixelPacketEvent +import gg.skytils.skytilsmod.listeners.ServerPayloadInterceptor.toCustomPayload import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable -import kotlinx.serialization.SerializationException import kotlinx.serialization.descriptors.* import kotlinx.serialization.encoding.* +import net.hypixel.modapi.HypixelModAPI +import net.hypixel.modapi.packet.impl.clientbound.event.ClientboundLocationPacket import net.minecraft.client.gui.inventory.GuiChest import net.minecraft.inventory.ContainerChest -import net.minecraft.network.play.client.C01PacketChatMessage -import net.minecraft.network.play.server.S02PacketChat import net.minecraftforge.client.event.GuiOpenEvent import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.gameevent.TickEvent import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent +import net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientDisconnectionFromServerEvent import java.text.ParseException import java.text.SimpleDateFormat import java.util.* @@ -55,19 +52,17 @@ object SBInfo { var location = "" var date = "" var time = "" - var objective: String? = "" - var mode: String? = "" + var objective: String? = null + var mode: String? = null + var server: String? = null var currentTimeDate: Date? = null + var lastLocationPacket: ClientboundLocationPacket? = null @JvmField var lastOpenContainerName: String? = null - private var lastManualLocRaw: Long = -1 - private var lastLocRaw: Long = -1 - private var joinedWorld: Long = -1 - var locraw: LocrawObject? = null private val junkRegex = Regex("[^\u0020-\u0127û]") - @SubscribeEvent + @SubscribeEvent(priority = EventPriority.HIGHEST) fun onGuiOpen(event: GuiOpenEvent) { if (!Utils.inSkyblock) return if (event.gui is GuiChest) { @@ -80,46 +75,25 @@ object SBInfo { @SubscribeEvent fun onWorldChange(event: WorldEvent.Unload) { - lastLocRaw = -1 - locraw = null - mode = null - joinedWorld = System.currentTimeMillis() lastOpenContainerName = null + lastLocationRequest = -1 } @SubscribeEvent - fun onSendChatMessage(event: SendChatMessageEvent) { - val msg = event.message - if (msg.trim().startsWith("/locraw")) { - lastManualLocRaw = System.currentTimeMillis() - } - } - - @SubscribeEvent(priority = EventPriority.LOW, receiveCanceled = true) - fun onChatMessage(event: PacketEvent.ReceiveEvent) { - if (event.packet is S02PacketChat) { - val unformatted = event.packet.chatComponent.unformattedText - if (unformatted.startsWith("{") && unformatted.endsWith("}")) { - try { - val obj = json.decodeFromString(unformatted) - if (System.currentTimeMillis() - lastManualLocRaw > 5000) { - Utils.cancelChatPacket(event) - } - locraw = obj - mode = obj.mode - LocrawReceivedEvent(obj).postAndCatch() - } catch (e: SerializationException) { - e.printStackTrace() - } - } - } + fun onDisconnect(event: ClientDisconnectionFromServerEvent) { + mode = null + server = null + lastLocationPacket = null } @SubscribeEvent - fun onPacket(event: PacketEvent.SendEvent) { - if (Utils.isOnHypixel && event.packet is C01PacketChatMessage) { - if (event.packet.message.startsWith("/locraw")) { - lastLocRaw = System.currentTimeMillis() + fun onHypixelPacket(event: HypixelPacketEvent.ReceiveEvent) { + if (event.packet is ClientboundLocationPacket) { + Utils.checkThreadAndQueue { + mode = event.packet.mode.orElse(null) + server = event.packet.serverName + lastLocationPacket = event.packet + println(event.packet) } } } @@ -127,11 +101,6 @@ object SBInfo { @SubscribeEvent fun onTick(event: ClientTickEvent) { if (event.phase != TickEvent.Phase.START || mc.thePlayer == null || mc.theWorld == null || !Utils.inSkyblock) return - val currentTime = System.currentTimeMillis() - if (locraw == null && currentTime - joinedWorld > 1300 && currentTime - lastLocRaw > 15000) { - lastLocRaw = System.currentTimeMillis() - Skytils.sendMessageQueue.add("/locraw") - } try { val lines = ScoreboardUtil.fetchScoreboardLines().map { it.stripControlCodes() } if (lines.size >= 5) { @@ -222,13 +191,4 @@ enum class SkyblockIsland(val displayName: String, val mode: String) { encodeStringElement(descriptor, 1, value.mode) } } -} - - -@Serializable -data class LocrawObject( - val server: String, - val gametype: String = "unknown", - val mode: String = "unknown", - val map: String = "unknown" -) \ No newline at end of file +} \ No newline at end of file From d20d8d1c27697d9fbc02aba3aea0df7ccc8dc725 Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Sun, 21 Apr 2024 12:43:47 -0400 Subject: [PATCH 07/25] fix: tell the server when dungeon ends --- .../kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt b/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt index 2ae81684a..049fefad1 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt @@ -48,6 +48,7 @@ import gg.skytils.skytilsmod.utils.* import gg.skytils.skytilsmod.utils.NumberUtil.addSuffix import gg.skytils.skytilsmod.utils.NumberUtil.romanToDecimal import gg.skytils.skytilsws.client.WSClient +import gg.skytils.skytilsws.shared.packet.C2SPacketDungeonEnd import gg.skytils.skytilsws.shared.packet.C2SPacketDungeonRoom import gg.skytils.skytilsws.shared.packet.C2SPacketDungeonRoomSecret import gg.skytils.skytilsws.shared.packet.C2SPacketStartDungeon @@ -161,6 +162,9 @@ object DungeonListener { if (text.stripControlCodes() .trim() == "> EXTRA STATS <" ) { + IO.launch { + WSClient.sendPacket(C2SPacketDungeonEnd(SBInfo.server ?: return@launch)) + } if (Skytils.config.dungeonDeathCounter) { tickTimer(6) { UChat.chat("§c☠ §lDeaths:§r ${team.values.sumOf { it.deaths }}\n${ From 21259ac10cbc31bbbf62317578ba21dddc5a89fa Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Sun, 19 May 2024 14:51:48 -0400 Subject: [PATCH 08/25] chore: update mod-api to 0.4.0 fix: various issues from hypixel mod api event registration fix: serverpayloadinterceptor uses incorrect duplication method fix: register packet fails to send in prod fix: hyevent registration on subsequent logins --- build.gradle.kts | 2 +- .../accessors/AccessorHypixelModAPI.java | 36 +++++++++ .../network/MixinNetworkManager.java | 1 + .../kotlin/gg/skytils/skytilsmod/Skytils.kt | 16 +--- .../commands/impl/SkytilsCommand.kt | 2 +- .../catlas/handlers/DungeonScanner.kt | 5 -- .../skytilsmod/listeners/DungeonListener.kt | 38 ++++----- .../listeners/ServerPayloadInterceptor.kt | 80 ++++++++++++++----- .../gg/skytils/skytilsmod/utils/SBInfo.kt | 3 - .../gg/skytils/skytilsmod/utils/Utils.kt | 3 + src/main/resources/mixins.skytils.json | 2 + 11 files changed, 123 insertions(+), 65 deletions(-) create mode 100644 src/main/java/gg/skytils/skytilsmod/mixins/transformers/accessors/AccessorHypixelModAPI.java diff --git a/build.gradle.kts b/build.gradle.kts index 53d28d6e2..c995a8c04 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -153,7 +153,7 @@ dependencies { exclude(module = "bcprov-jdk18on") } compileOnly("org.bouncycastle:bcprov-jdk18on:1.78.1") - shadowMe("net.hypixel:mod-api:0.3.2") + shadowMe("net.hypixel:mod-api:0.4.0") shadowMe(annotationProcessor("io.github.llamalad7:mixinextras-common:0.3.5")!!) diff --git a/src/main/java/gg/skytils/skytilsmod/mixins/transformers/accessors/AccessorHypixelModAPI.java b/src/main/java/gg/skytils/skytilsmod/mixins/transformers/accessors/AccessorHypixelModAPI.java new file mode 100644 index 000000000..f8315fb15 --- /dev/null +++ b/src/main/java/gg/skytils/skytilsmod/mixins/transformers/accessors/AccessorHypixelModAPI.java @@ -0,0 +1,36 @@ +/* + * Skytils - Hypixel Skyblock Quality of Life Mod + * Copyright (C) 2020-2024 Skytils + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package gg.skytils.skytilsmod.mixins.transformers.accessors; + +import net.hypixel.modapi.HypixelModAPI; +import net.hypixel.modapi.packet.HypixelPacket; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.util.function.Predicate; + +@Mixin(HypixelModAPI.class) +public interface AccessorHypixelModAPI { + @Accessor + Predicate getPacketSender(); + + @Invoker + void invokeSendRegisterPacket(boolean alwaysSendIfNotEmpty); +} diff --git a/src/main/java/gg/skytils/skytilsmod/mixins/transformers/network/MixinNetworkManager.java b/src/main/java/gg/skytils/skytilsmod/mixins/transformers/network/MixinNetworkManager.java index 0ec81b92c..e7bd3fb28 100644 --- a/src/main/java/gg/skytils/skytilsmod/mixins/transformers/network/MixinNetworkManager.java +++ b/src/main/java/gg/skytils/skytilsmod/mixins/transformers/network/MixinNetworkManager.java @@ -19,6 +19,7 @@ package gg.skytils.skytilsmod.mixins.transformers.network; import gg.skytils.skytilsmod.mixins.hooks.network.NetworkManagerHookKt; +import gg.skytils.skytilsmod.utils.Utils; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import net.minecraft.network.EnumPacketDirection; diff --git a/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt b/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt index 5fa5e8d2c..50701e5e8 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt @@ -63,7 +63,6 @@ import gg.skytils.skytilsmod.gui.ReopenableGUI import gg.skytils.skytilsmod.listeners.ChatListener import gg.skytils.skytilsmod.listeners.DungeonListener import gg.skytils.skytilsmod.listeners.ServerPayloadInterceptor -import gg.skytils.skytilsmod.listeners.ServerPayloadInterceptor.getResponse import gg.skytils.skytilsmod.localapi.LocalAPI import gg.skytils.skytilsmod.mixins.extensions.ExtensionEntityLivingBase import gg.skytils.skytilsmod.mixins.hooks.entity.EntityPlayerSPHook @@ -92,8 +91,6 @@ import kotlinx.coroutines.* import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule -import net.hypixel.modapi.packet.impl.clientbound.ClientboundPingPacket -import net.hypixel.modapi.packet.impl.serverbound.ServerboundPingPacket import net.minecraft.client.Minecraft import net.minecraft.client.gui.GuiButton import net.minecraft.client.gui.GuiGameOver @@ -552,15 +549,12 @@ class Skytils { @SubscribeEvent fun onConnect(event: FMLNetworkEvent.ClientConnectedToServerEvent) { + Utils.lastNHPC = event.handler as? NetHandlerPlayClient Utils.isOnHypixel = mc.runCatching { !event.isLocal && (thePlayer?.clientBrand?.lowercase()?.contains("hypixel") ?: currentServerData?.serverIP?.lowercase()?.contains("hypixel") ?: false) }.onFailure { it.printStackTrace() }.getOrDefault(false) - if (Utils.isOnHypixel) { - onJoinHypixel(event.handler as NetHandlerPlayClient) - } - IO.launch { TrophyFish.loadFromApi() } @@ -598,7 +592,6 @@ class Skytils { val brand = event.packet.bufferData.readStringFromBuffer(Short.MAX_VALUE.toInt()) if (brand.lowercase().contains("hypixel")) { Utils.isOnHypixel = true - onJoinHypixel(event.handler as NetHandlerPlayClient) } } if (Utils.inDungeons || !Utils.isOnHypixel || event.packet !is S38PacketPlayerListItem || @@ -617,14 +610,9 @@ class Skytils { } } - fun onJoinHypixel(handler: NetHandlerPlayClient) = IO.launch { - ServerboundPingPacket().getResponse(handler).let { packet -> - println("Hypixel Pong: ${packet.response}, version ${packet.version}") - } - } - @SubscribeEvent fun onDisconnect(event: FMLNetworkEvent.ClientDisconnectionFromServerEvent) { + Utils.lastNHPC = null Utils.isOnHypixel = false Utils.skyblock = false Utils.dungeons = false diff --git a/src/main/kotlin/gg/skytils/skytilsmod/commands/impl/SkytilsCommand.kt b/src/main/kotlin/gg/skytils/skytilsmod/commands/impl/SkytilsCommand.kt index 15f3a8e0a..20fa0b11e 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/commands/impl/SkytilsCommand.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/commands/impl/SkytilsCommand.kt @@ -396,7 +396,7 @@ object SkytilsCommand : BaseCommand("skytils", listOf("st")) { UChat.chat("$successPrefix §aPacket created: $packet") Skytils.IO.launch { runCatching { - packet.getResponse(mc.netHandler) + packet.getResponse() }.onFailure { UChat.chat("$failPrefix §cFailed to get packet response: ${it.message}") }.onSuccess { response -> diff --git a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/handlers/DungeonScanner.kt b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/handlers/DungeonScanner.kt index 57154b242..6bd51058c 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/handlers/DungeonScanner.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/handlers/DungeonScanner.kt @@ -25,14 +25,9 @@ import gg.skytils.skytilsmod.features.impl.dungeons.catlas.core.map.* import gg.skytils.skytilsmod.features.impl.dungeons.catlas.handlers.DungeonScanner.scan import gg.skytils.skytilsmod.features.impl.dungeons.catlas.utils.ScanUtils import gg.skytils.skytilsmod.listeners.DungeonListener -import gg.skytils.skytilsmod.listeners.ServerPayloadInterceptor.getResponse import gg.skytils.skytilsmod.utils.SBInfo -import gg.skytils.skytilsws.client.WSClient import gg.skytils.skytilsws.shared.packet.C2SPacketDungeonRoom -import gg.skytils.skytilsws.shared.packet.C2SPacketDungeonRoomSecret import kotlinx.coroutines.launch -import net.hypixel.modapi.packet.impl.clientbound.ClientboundLocationPacket -import net.hypixel.modapi.packet.impl.serverbound.ServerboundLocationPacket import net.minecraft.init.Blocks import net.minecraft.util.BlockPos diff --git a/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt b/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt index 049fefad1..626aef210 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt @@ -36,10 +36,10 @@ import gg.skytils.skytilsmod.features.impl.dungeons.DungeonFeatures import gg.skytils.skytilsmod.features.impl.dungeons.DungeonTimer import gg.skytils.skytilsmod.features.impl.dungeons.ScoreCalculation import gg.skytils.skytilsmod.features.impl.dungeons.catlas.core.DungeonMapPlayer +import gg.skytils.skytilsmod.features.impl.dungeons.catlas.core.map.Room import gg.skytils.skytilsmod.features.impl.dungeons.catlas.core.map.RoomType import gg.skytils.skytilsmod.features.impl.dungeons.catlas.handlers.DungeonInfo -import gg.skytils.skytilsmod.features.impl.dungeons.catlas.handlers.DungeonScanner -import gg.skytils.skytilsmod.features.impl.dungeons.catlas.utils.MapUtils +import gg.skytils.skytilsmod.features.impl.dungeons.catlas.utils.ScanUtils import gg.skytils.skytilsmod.features.impl.handlers.CooldownTracker import gg.skytils.skytilsmod.features.impl.handlers.SpiritLeap import gg.skytils.skytilsmod.listeners.ServerPayloadInterceptor.getResponse @@ -55,9 +55,7 @@ import gg.skytils.skytilsws.shared.packet.C2SPacketStartDungeon import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import net.hypixel.modapi.packet.impl.clientbound.ClientboundLocationPacket import net.hypixel.modapi.packet.impl.clientbound.ClientboundPartyInfoPacket -import net.hypixel.modapi.packet.impl.serverbound.ServerboundLocationPacket import net.hypixel.modapi.packet.impl.serverbound.ServerboundPartyInfoPacket import net.minecraft.entity.player.EntityPlayer import net.minecraft.network.play.server.S02PacketChat @@ -66,7 +64,7 @@ import net.minecraftforge.client.event.ClientChatReceivedEvent import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.util.concurrent.CopyOnWriteArrayList +import java.util.concurrent.ConcurrentLinkedQueue object DungeonListener { val team = hashMapOf() @@ -116,7 +114,7 @@ object DungeonListener { private val keyPickupRegex = Regex("§r§e§lRIGHT CLICK §r§7on §r§7.+?§r§7 to open it\\. This key can only be used to open §r§a(?\\d+)§r§7 door!§r") private val witherDoorOpenedRegex = Regex("^(?:\\[.+?] )?(?\\w+) opened a WITHER door!$") private const val bloodOpenedString = "§r§cThe §r§c§lBLOOD DOOR§r§c has been opened!§r" - val outboundRoomQueue = arrayListOf() + val outboundRoomQueue = ConcurrentLinkedQueue() @SubscribeEvent fun onWorldLoad(event: WorldEvent.Unload) { @@ -144,14 +142,13 @@ object DungeonListener { DungeonFeatures.DungeonSecretDisplay.maxSecrets = max IO.launch { - val x = ((mc.thePlayer.posX - DungeonScanner.startX + 15) * MapUtils.coordMultiplier / (MapUtils.mapRoomSize + 4) * 2).toInt() - val z = ((mc.thePlayer.posZ - DungeonScanner.startZ + 15) * MapUtils.coordMultiplier / (MapUtils.mapRoomSize + 4) * 2).toInt() - - val room = DungeonInfo.uniqueRooms.find { it.tiles.any { it.x == x && it.z == z } } - - if (room != null && room.foundSecrets != sec) { - room.foundSecrets = sec - WSClient.sendPacket(C2SPacketDungeonRoomSecret(SBInfo.server ?: return@launch, room.mainRoom.data.name, sec)) + val tile = ScanUtils.getRoomFromPos(mc.thePlayer.position) + if (tile is Room && tile.data.name != "Unknown") { + val room = DungeonInfo.uniqueRooms.find { tile in it.tiles } ?: return@launch + if (room.foundSecrets != sec) { + room.foundSecrets = sec + WSClient.sendPacket(C2SPacketDungeonRoomSecret(SBInfo.server ?: return@launch, room.mainRoom.data.name, sec)) + } } } }.ifNull { @@ -205,26 +202,21 @@ object DungeonListener { IO.launch { delay(2000) if (DungeonTimer.dungeonStartTime != -1L) { - val location = async { - ServerboundLocationPacket().getResponse(mc.netHandler) - } val party = async { - ServerboundPartyInfoPacket().getResponse(mc.netHandler) + ServerboundPartyInfoPacket().getResponse() } val partyMembers = party.await().members.ifEmpty { setOf(mc.thePlayer.uniqueID) }.mapTo(hashSetOf()) { it.toString() } val entrance = DungeonInfo.uniqueRooms.first { it.mainRoom.data.type == RoomType.ENTRANCE } WSClient.sendPacket(C2SPacketStartDungeon( - serverId = location.await().serverName, + serverId = SBInfo.server ?: return@launch, floor = DungeonFeatures.dungeonFloor!!, members = partyMembers, startTime = DungeonTimer.dungeonStartTime, entranceLoc = entrance.mainRoom.z * entrance.mainRoom.x )) while (DungeonTimer.dungeonStartTime != -1L) { - val itr = outboundRoomQueue.iterator() - while (itr.hasNext()) { - val packet = itr.next() - itr.remove() + while (outboundRoomQueue.isNotEmpty()) { + val packet = outboundRoomQueue.poll() ?: continue WSClient.sendPacket(packet) } } diff --git a/src/main/kotlin/gg/skytils/skytilsmod/listeners/ServerPayloadInterceptor.kt b/src/main/kotlin/gg/skytils/skytilsmod/listeners/ServerPayloadInterceptor.kt index 4dcfe6117..5b3281660 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/listeners/ServerPayloadInterceptor.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/listeners/ServerPayloadInterceptor.kt @@ -18,22 +18,27 @@ package gg.skytils.skytilsmod.listeners +import gg.skytils.skytilsmod.Skytils import gg.skytils.skytilsmod.Skytils.Companion.IO +import gg.skytils.skytilsmod.Skytils.Companion.mc +import gg.skytils.skytilsmod.core.MC import gg.skytils.skytilsmod.events.impl.HypixelPacketEvent import gg.skytils.skytilsmod.events.impl.PacketEvent +import gg.skytils.skytilsmod.mixins.transformers.accessors.AccessorHypixelModAPI +import gg.skytils.skytilsmod.utils.Utils +import gg.skytils.skytilsmod.utils.ifNull import io.netty.buffer.Unpooled +import kotlinx.coroutines.* import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch -import kotlinx.coroutines.withTimeout import net.hypixel.modapi.HypixelModAPI import net.hypixel.modapi.error.ErrorReason import net.hypixel.modapi.packet.ClientboundHypixelPacket -import net.hypixel.modapi.packet.HypixelPacket +import net.hypixel.modapi.packet.impl.clientbound.ClientboundHelloPacket +import net.hypixel.modapi.packet.impl.clientbound.event.ClientboundLocationPacket import net.hypixel.modapi.packet.impl.serverbound.ServerboundVersionedPacket import net.hypixel.modapi.serializer.PacketSerializer -import net.minecraft.client.network.NetHandlerPlayClient import net.minecraft.network.PacketBuffer import net.minecraft.network.play.client.C17PacketCustomPayload import net.minecraft.network.play.server.S3FPacketCustomPayload @@ -47,19 +52,29 @@ object ServerPayloadInterceptor { @SubscribeEvent(priority = EventPriority.HIGHEST) fun onReceivePacket(event: PacketEvent.ReceiveEvent) { if (event.packet is S3FPacketCustomPayload) { - IO.launch { - val registry = HypixelModAPI.getInstance().registry - val id = event.packet.channelName - if (registry.isRegistered(id)) { - val packetSerializer = PacketSerializer(event.packet.bufferData.duplicate()) - if (!packetSerializer.readBoolean()) { - val reason = ErrorReason.getById(packetSerializer.readVarInt()) - HypixelPacketEvent.FailedEvent(id, reason).postAndCatch() - } else { - val packet = registry.createClientboundPacket(id, packetSerializer) - receivedPackets.emit(packet) - HypixelPacketEvent.ReceiveEvent(packet).postAndCatch() + val registry = HypixelModAPI.getInstance().registry + val id = event.packet.channelName + if (registry.isRegistered(id)) { + println("Received Hypixel packet $id") + val data = event.packet.bufferData + synchronized(data) { + data.retain() + runCatching { + val packetSerializer = PacketSerializer(data.duplicate()) + if (!packetSerializer.readBoolean()) { + val reason = ErrorReason.getById(packetSerializer.readVarInt()) + HypixelPacketEvent.FailedEvent(id, reason).postAndCatch() + } else { + val packet = registry.createClientboundPacket(id, packetSerializer) + IO.launch { + receivedPackets.emit(packet) + } + HypixelPacketEvent.ReceiveEvent(packet).postAndCatch() + } + }.onFailure { + it.printStackTrace() } + data.release() } } } @@ -71,11 +86,38 @@ object ServerPayloadInterceptor { val registry = HypixelModAPI.getInstance().registry val id = event.packet.channelName if (registry.isRegistered(id)) { + println("Sent Hypixel packet $id") HypixelPacketEvent.SendEvent(id).postAndCatch() } } } + @SubscribeEvent + fun onHypixelPacket(event: HypixelPacketEvent.ReceiveEvent) { + if (event.packet is ClientboundHelloPacket) { + val modAPI = HypixelModAPI.getInstance() + modAPI as AccessorHypixelModAPI + if (modAPI.packetSender == null) { + println("Hypixel Mod API packet sender is not set, Skytils will set the packet sender.") + modAPI.setPacketSender { + return@setPacketSender getNetClientHandler()?.addToSendQueue((it as ServerboundVersionedPacket).toCustomPayload()).ifNull { + println("Failed to send packet ${it.identifier}") + } != null + } + } + Skytils.launch { + while (getNetClientHandler() == null) { + println("Waiting for client handler to be set.") + delay(50L) + } + withContext(Dispatchers.MC) { + modAPI.subscribeToEventPacket(ClientboundLocationPacket::class.java) + modAPI.invokeSendRegisterPacket(true) + } + } + } + } + fun ServerboundVersionedPacket.toCustomPayload(): C17PacketCustomPayload { val buffer = PacketBuffer(Unpooled.buffer()) val serializer = PacketSerializer(buffer) @@ -83,9 +125,11 @@ object ServerPayloadInterceptor { return C17PacketCustomPayload(this.identifier, buffer) } - suspend fun ServerboundVersionedPacket.getResponse(handler: NetHandlerPlayClient): T = withTimeout(1.minutes) { + suspend fun ServerboundVersionedPacket.getResponse(): T = withTimeout(1.minutes) { val packet: C17PacketCustomPayload = this@getResponse.toCustomPayload() - handler.addToSendQueue(packet) + getNetClientHandler()?.addToSendQueue(packet) return@withTimeout receivedPackets.filter { it.identifier == this@getResponse.identifier }.first() as T } + + private fun getNetClientHandler() = mc.netHandler ?: Utils.lastNHPC } \ No newline at end of file diff --git a/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt b/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt index 320596b72..794914468 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt @@ -19,12 +19,10 @@ package gg.skytils.skytilsmod.utils import gg.skytils.skytilsmod.Skytils.Companion.mc import gg.skytils.skytilsmod.events.impl.HypixelPacketEvent -import gg.skytils.skytilsmod.listeners.ServerPayloadInterceptor.toCustomPayload import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.* import kotlinx.serialization.encoding.* -import net.hypixel.modapi.HypixelModAPI import net.hypixel.modapi.packet.impl.clientbound.event.ClientboundLocationPacket import net.minecraft.client.gui.inventory.GuiChest import net.minecraft.inventory.ContainerChest @@ -76,7 +74,6 @@ object SBInfo { @SubscribeEvent fun onWorldChange(event: WorldEvent.Unload) { lastOpenContainerName = null - lastLocationRequest = -1 } @SubscribeEvent diff --git a/src/main/kotlin/gg/skytils/skytilsmod/utils/Utils.kt b/src/main/kotlin/gg/skytils/skytilsmod/utils/Utils.kt index f5f2f458c..b1b867963 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/utils/Utils.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/utils/Utils.kt @@ -42,6 +42,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.launch import net.minecraft.client.gui.ChatLine import net.minecraft.client.gui.GuiNewChat +import net.minecraft.client.network.NetHandlerPlayClient import net.minecraft.client.settings.GameSettings import net.minecraft.entity.Entity import net.minecraft.entity.EntityLivingBase @@ -89,6 +90,8 @@ object Utils { @JvmField var lastRenderedSkullEntity: EntityLivingBase? = null + var lastNHPC: NetHandlerPlayClient? = null + @JvmStatic var random = Random() diff --git a/src/main/resources/mixins.skytils.json b/src/main/resources/mixins.skytils.json index 7b52269bd..571f6211c 100644 --- a/src/main/resources/mixins.skytils.json +++ b/src/main/resources/mixins.skytils.json @@ -20,6 +20,7 @@ "accessors.AccessorGuiMainMenu", "accessors.AccessorGuiNewChat", "accessors.AccessorGuiStreamUnavailable", + "accessors.AccessorHypixelModAPI", "accessors.AccessorHypixelPacketRegistry", "accessors.AccessorMinecraft", "accessors.AccessorModelDragon", @@ -96,6 +97,7 @@ ], "verbose": true, "client": [ + "accessors.AccessorPlayerControllerMP", "gui.MixinGuiEditSign", "util.MixinMouseHelper" ] From ebceef89205a8a098192694d2f3dc86a710cfce9 Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Tue, 21 May 2024 18:08:40 -0400 Subject: [PATCH 09/25] perf: only send dungeon data if there are teammates --- .../skytilsmod/listeners/DungeonListener.kt | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt b/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt index 626aef210..de2472857 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt @@ -115,6 +115,7 @@ object DungeonListener { private val witherDoorOpenedRegex = Regex("^(?:\\[.+?] )?(?\\w+) opened a WITHER door!$") private const val bloodOpenedString = "§r§cThe §r§c§lBLOOD DOOR§r§c has been opened!§r" val outboundRoomQueue = ConcurrentLinkedQueue() + var isSoloDungeon = false @SubscribeEvent fun onWorldLoad(event: WorldEvent.Unload) { @@ -125,6 +126,7 @@ object DungeonListener { completedPuzzles.clear() teamCached.clear() outboundRoomQueue.clear() + isSoloDungeon = false } @SubscribeEvent @@ -141,13 +143,15 @@ object DungeonListener { DungeonFeatures.DungeonSecretDisplay.secrets = sec DungeonFeatures.DungeonSecretDisplay.maxSecrets = max - IO.launch { - val tile = ScanUtils.getRoomFromPos(mc.thePlayer.position) - if (tile is Room && tile.data.name != "Unknown") { - val room = DungeonInfo.uniqueRooms.find { tile in it.tiles } ?: return@launch - if (room.foundSecrets != sec) { - room.foundSecrets = sec - WSClient.sendPacket(C2SPacketDungeonRoomSecret(SBInfo.server ?: return@launch, room.mainRoom.data.name, sec)) + if (team.size > 1) { + IO.launch { + val tile = ScanUtils.getRoomFromPos(mc.thePlayer.position) + if (tile is Room && tile.data.name != "Unknown") { + val room = DungeonInfo.uniqueRooms.find { tile in it.tiles } ?: return@launch + if (room.foundSecrets != sec) { + room.foundSecrets = sec + WSClient.sendPacket(C2SPacketDungeonRoomSecret(SBInfo.server ?: return@launch, room.mainRoom.data.name, sec)) + } } } } @@ -159,8 +163,10 @@ object DungeonListener { if (text.stripControlCodes() .trim() == "> EXTRA STATS <" ) { - IO.launch { - WSClient.sendPacket(C2SPacketDungeonEnd(SBInfo.server ?: return@launch)) + if (team.size > 1) { + IO.launch { + WSClient.sendPacket(C2SPacketDungeonEnd(SBInfo.server ?: return@launch)) + } } if (Skytils.config.dungeonDeathCounter) { tickTimer(6) { @@ -201,7 +207,7 @@ object DungeonListener { } else if (text == "§r§aStarting in 1 second.§r") { IO.launch { delay(2000) - if (DungeonTimer.dungeonStartTime != -1L) { + if (DungeonTimer.dungeonStartTime != -1L && team.size > 1) { val party = async { ServerboundPartyInfoPacket().getResponse() } From b019d62562e02068b244f859635c0b9d8f7c032f Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Wed, 22 May 2024 00:05:48 -0400 Subject: [PATCH 10/25] feat: add found room secrets to Catlas --- .../impl/dungeons/catlas/core/CatlasConfig.kt | 11 +++++++++++ .../impl/dungeons/catlas/core/CatlasElement.kt | 13 +++++++++---- .../skytilsmod/listeners/DungeonListener.kt | 15 +++++++-------- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/core/CatlasConfig.kt b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/core/CatlasConfig.kt index 50bf6cc0e..e69b81d5e 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/core/CatlasConfig.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/core/CatlasConfig.kt @@ -226,6 +226,17 @@ object CatlasConfig : Vigilant( ) var mapRoomSecrets = 0 + // TODO: Add translation + @Property( + name = "Found Room Secrets", + type = PropertyType.SELECTOR, + description = "Shows found secrets of rooms on map.", + category = "Rooms", + options = ["Off", "On", "Replace Total"], + i18nCategory = "catlas.config.rooms" + ) + var foundRoomSecrets = 0 + @Property( name = "Color Text", type = PropertyType.SWITCH, diff --git a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/core/CatlasElement.kt b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/core/CatlasElement.kt index 293aa2498..3877ee4af 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/core/CatlasElement.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/core/CatlasElement.kt @@ -142,7 +142,7 @@ object CatlasElement : GuiElement(name = "Dungeon Map", x = 0, y = 0) { DungeonInfo.uniqueRooms.forEach { unq -> val room = unq.mainRoom - if (room.state == RoomState.UNDISCOVERED) return@forEach + if (room.state == RoomState.UNDISCOVERED || room.state == RoomState.UNOPENED) return@forEach val size = MapUtils.mapRoomSize + DungeonMapColorParser.quarterRoom val checkPos = unq.getCheckmarkPosition() val namePos = unq.getNamePosition() @@ -161,7 +161,12 @@ object CatlasElement : GuiElement(name = "Dungeon Map", x = 0, y = 0) { val roomType = room.data.type val hasSecrets = secretCount > 0 - if (room.state == RoomState.UNOPENED) return@forEach + val secretText = when (CatlasConfig.foundRoomSecrets) { + 0 -> secretCount.toString() + 1 -> "${unq.foundSecrets ?: "?"}/${secretCount}" + 2 -> unq.foundSecrets?.toString() ?: "?" + else -> error("Invalid foundRoomSecrets value") + } if (CatlasConfig.mapRoomSecrets == 2 && hasSecrets) { GlStateManager.pushMatrix() @@ -171,7 +176,7 @@ object CatlasElement : GuiElement(name = "Dungeon Map", x = 0, y = 0) { 0f ) GlStateManager.scale(2f, 2f, 1f) - RenderUtils.renderCenteredText(listOf(secretCount.toString()), 0, 0, color) + RenderUtils.renderCenteredText(listOf(secretText), 0, 0, color) GlStateManager.popMatrix() } else if (CatlasConfig.mapCheckmark != 0) { drawCheckmark(room, xOffsetCheck, yOffsetCheck, checkmarkSize) @@ -191,7 +196,7 @@ object CatlasElement : GuiElement(name = "Dungeon Map", x = 0, y = 0) { name.addAll(room.data.name.split(" ")) } if (room.data.type == RoomType.NORMAL && CatlasConfig.mapRoomSecrets == 1) { - name.add(secretCount.toString()) + name.add(secretText) } // Offset + half of roomsize RenderUtils.renderCenteredText( diff --git a/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt b/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt index de2472857..9e0bfedce 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt @@ -143,15 +143,14 @@ object DungeonListener { DungeonFeatures.DungeonSecretDisplay.secrets = sec DungeonFeatures.DungeonSecretDisplay.maxSecrets = max - if (team.size > 1) { - IO.launch { - val tile = ScanUtils.getRoomFromPos(mc.thePlayer.position) - if (tile is Room && tile.data.name != "Unknown") { - val room = DungeonInfo.uniqueRooms.find { tile in it.tiles } ?: return@launch - if (room.foundSecrets != sec) { - room.foundSecrets = sec + IO.launch { + val tile = ScanUtils.getRoomFromPos(mc.thePlayer.position) + if (tile is Room && tile.data.name != "Unknown") { + val room = DungeonInfo.uniqueRooms.find { tile in it.tiles } ?: return@launch + if (room.foundSecrets != sec) { + room.foundSecrets = sec + if (team.size > 1) WSClient.sendPacket(C2SPacketDungeonRoomSecret(SBInfo.server ?: return@launch, room.mainRoom.data.name, sec)) - } } } } From a6c044db683257b2c0ae258e19509e4bdab137b1 Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Wed, 22 May 2024 18:01:53 -0400 Subject: [PATCH 11/25] refactor: move CH Waypoints to their own class --- .../kotlin/gg/skytils/skytilsmod/Skytils.kt | 2 + .../commands/impl/HollowWaypointCommand.kt | 18 +- .../features/impl/mining/CHWaypoints.kt | 355 ++++++++++++++++++ .../features/impl/mining/MiningFeatures.kt | 299 --------------- 4 files changed, 366 insertions(+), 308 deletions(-) create mode 100644 src/main/kotlin/gg/skytils/skytilsmod/features/impl/mining/CHWaypoints.kt diff --git a/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt b/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt index 50701e5e8..fed9022f9 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt @@ -46,6 +46,7 @@ import gg.skytils.skytilsmod.features.impl.farming.TreasureHunter import gg.skytils.skytilsmod.features.impl.farming.VisitorHelper import gg.skytils.skytilsmod.features.impl.funny.Funny import gg.skytils.skytilsmod.features.impl.handlers.* +import gg.skytils.skytilsmod.features.impl.mining.CHWaypoints import gg.skytils.skytilsmod.features.impl.mining.MiningFeatures import gg.skytils.skytilsmod.features.impl.mining.StupidTreasureChestOpeningThing import gg.skytils.skytilsmod.features.impl.misc.* @@ -328,6 +329,7 @@ class Skytils { BoulderSolver, ChatTabs, ChangeAllToSameColorSolver, + CHWaypoints, DungeonChestProfit, ClickInOrderSolver, NamespacedCommands, diff --git a/src/main/kotlin/gg/skytils/skytilsmod/commands/impl/HollowWaypointCommand.kt b/src/main/kotlin/gg/skytils/skytilsmod/commands/impl/HollowWaypointCommand.kt index 55c403356..8841206e7 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/commands/impl/HollowWaypointCommand.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/commands/impl/HollowWaypointCommand.kt @@ -27,7 +27,7 @@ import gg.skytils.skytilsmod.Skytils.Companion.mc import gg.skytils.skytilsmod.Skytils.Companion.prefix import gg.skytils.skytilsmod.Skytils.Companion.successPrefix import gg.skytils.skytilsmod.commands.BaseCommand -import gg.skytils.skytilsmod.features.impl.mining.MiningFeatures +import gg.skytils.skytilsmod.features.impl.mining.CHWaypoints import gg.skytils.skytilsmod.utils.append import gg.skytils.skytilsmod.utils.setHoverText import net.minecraft.client.entity.EntityPlayerSP @@ -47,13 +47,13 @@ object HollowWaypointCommand : BaseCommand("skytilshollowwaypoint", listOf("sthw } if (args.isEmpty()) { val message = UMessage("$prefix §eWaypoints:\n") - for (loc in MiningFeatures.CrystalHollowsMap.Locations.entries) { + for (loc in CHWaypoints.CrystalHollowsMap.Locations.entries) { if (!loc.loc.exists()) continue message.append("${loc.displayName} ") message.append(copyMessage("${loc.cleanName}: ${loc.loc}")) message.append(removeMessage(loc.id)) } - for ((key, value) in MiningFeatures.waypoints) { + for ((key, value) in CHWaypoints.waypoints) { message.append("§e$key ") message.append(copyMessage("$key: ${value.x} ${value.y} ${value.z}")) message.append(removeMessage(key)) @@ -81,13 +81,13 @@ object HollowWaypointCommand : BaseCommand("skytilshollowwaypoint", listOf("sthw y = match.groups["y"]!!.value.toDouble() z = match.groups["z"]!!.value.toDouble() } - val internalLoc = MiningFeatures.CrystalHollowsMap.Locations.entries.find { it.id == loc }?.loc + val internalLoc = CHWaypoints.CrystalHollowsMap.Locations.entries.find { it.id == loc }?.loc if (internalLoc != null) { internalLoc.locX = (x - 200).coerceIn(0.0, 624.0) internalLoc.locY = y internalLoc.locZ = (z - 200).coerceIn(0.0, 624.0) } else { - MiningFeatures.waypoints[loc] = BlockPos(x, y, z) + CHWaypoints.waypoints[loc] = BlockPos(x, y, z) } UChat.chat("$successPrefix §aSuccessfully created waypoint $loc") } @@ -95,11 +95,11 @@ object HollowWaypointCommand : BaseCommand("skytilshollowwaypoint", listOf("sthw "remove", "delete" -> { if (args.size >= 2) { val name = args.drop(1).joinToString(" ") - if (MiningFeatures.CrystalHollowsMap.Locations.entries + if (CHWaypoints.CrystalHollowsMap.Locations.entries .find { it.id == name }?.loc?.reset() != null ) UChat.chat("$successPrefix §aSuccessfully removed waypoint ${name}!") - else if (MiningFeatures.waypoints.remove(name) != null) + else if (CHWaypoints.waypoints.remove(name) != null) UChat.chat("$successPrefix §aSuccessfully removed waypoint $name") else UChat.chat("$failPrefix §cWaypoint $name does not exist") @@ -108,8 +108,8 @@ object HollowWaypointCommand : BaseCommand("skytilshollowwaypoint", listOf("sthw } "clear" -> { - MiningFeatures.CrystalHollowsMap.Locations.entries.forEach { it.loc.reset() } - MiningFeatures.waypoints.clear() + CHWaypoints.CrystalHollowsMap.Locations.entries.forEach { it.loc.reset() } + CHWaypoints.waypoints.clear() UChat.chat("$successPrefix §aSuccessfully cleared all waypoints.") } diff --git a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/mining/CHWaypoints.kt b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/mining/CHWaypoints.kt new file mode 100644 index 000000000..b7020a162 --- /dev/null +++ b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/mining/CHWaypoints.kt @@ -0,0 +1,355 @@ +/* + * Skytils - Hypixel Skyblock Quality of Life Mod + * Copyright (C) 2020-2024 Skytils + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package gg.skytils.skytilsmod.features.impl.mining + +import gg.essential.universal.UChat +import gg.essential.universal.UGraphics +import gg.essential.universal.UMatrixStack +import gg.essential.universal.utils.MCClickEventAction +import gg.essential.universal.wrappers.message.UMessage +import gg.essential.universal.wrappers.message.UTextComponent +import gg.skytils.skytilsmod.Skytils +import gg.skytils.skytilsmod.Skytils.Companion.mc +import gg.skytils.skytilsmod.Skytils.Companion.prefix +import gg.skytils.skytilsmod.core.structure.GuiElement +import gg.skytils.skytilsmod.events.impl.PacketEvent +import gg.skytils.skytilsmod.features.impl.handlers.MayorInfo +import gg.skytils.skytilsmod.utils.* +import gg.skytils.skytilsmod.utils.graphics.colors.ColorFactory +import net.minecraft.client.entity.EntityOtherPlayerMP +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.vertex.DefaultVertexFormats +import net.minecraft.entity.EntityLivingBase +import net.minecraft.network.play.server.S08PacketPlayerPosLook +import net.minecraft.util.BlockPos +import net.minecraft.util.ResourceLocation +import net.minecraftforge.client.event.ClientChatReceivedEvent +import net.minecraftforge.client.event.RenderLivingEvent +import net.minecraftforge.client.event.RenderWorldLastEvent +import net.minecraftforge.event.world.WorldEvent +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.common.gameevent.TickEvent +import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent + +object CHWaypoints { + var lastTPLoc: BlockPos? = null + var waypoints = hashMapOf() + var waypointDelayTicks = 0 + private val SBE_DSM_PATTERN = + Regex("\\\$(?:SBECHWP\\b|DSMCHWP):(?.*?)@-(?-?\\d+),(?-?\\d+),(?-?\\d+)") + private val xyzPattern = + Regex(".*?(?[a-zA-Z0-9_]{3,16}):.*?(?[0-9]{1,3}),? (?:y: )?(?[0-9]{1,3}),? (?:z: )?(?[0-9]{1,3}).*?") + private val xzPattern = + Regex(".*(?[a-zA-Z0-9_]{3,16}):.* (?[0-9]{1,3}),? (?[0-9]{1,3}).*") + + @SubscribeEvent + fun onReceivePacket(event: PacketEvent.ReceiveEvent) { + if (!Utils.inSkyblock) return + if (Skytils.config.crystalHollowDeathWaypoint && event.packet is S08PacketPlayerPosLook && mc.thePlayer != null) { + lastTPLoc = mc.thePlayer.position + } + } + + @SubscribeEvent(priority = EventPriority.HIGHEST, receiveCanceled = true) + fun onChat(event: ClientChatReceivedEvent) { + if (!Utils.inSkyblock || event.type == 2.toByte()) return + val unformatted = event.message.unformattedText.stripControlCodes() + if (Skytils.config.hollowChatCoords && SBInfo.mode == SkyblockIsland.CrystalHollows.mode) { + xyzPattern.find(unformatted)?.groups?.let { + waypointChatMessage(it["x"]!!.value, it["y"]!!.value, it["z"]!!.value) + return + } + xzPattern.find(unformatted)?.groups?.let { + waypointChatMessage(it["x"]!!.value, "100", it["z"]!!.value) + return + } + + /** + * Checks for the format used in DSM and SBE + * $DSMCHWP:Mines of Divan@-673,117,426 ✔ + * $SBECHWP:Khazad-dûm@-292,63,281 ✔ + * $asdf:Khazad-dûm@-292,63,281 ❌ + * $SBECHWP:Khazad-dûm@asdf,asdf,asdf ❌ + */ + val cleaned = SBE_DSM_PATTERN.find(unformatted) + if (cleaned != null) { + val stringLocation = cleaned.groups["stringLocation"]!!.value + val x = cleaned.groups["x"]!!.value + val y = cleaned.groups["y"]!!.value + val z = cleaned.groups["z"]!!.value + CrystalHollowsMap.Locations.entries.find { it.cleanName == stringLocation } + ?.takeIf { !it.loc.exists() }?.let { loc -> + /** + * Sends the waypoints message except it suggests which one should be used based on + * the name contained in the message and converts it to the internally used names for the waypoints. + */ + UMessage("§3Skytils > §eFound coordinates in a chat message, click a button to set a waypoint.\n") + .append( + UTextComponent("§f${loc.displayName} ") + .setClick( + MCClickEventAction.RUN_COMMAND, + "/skytilshollowwaypoint set $x $y $z ${loc.id}" + ) + .setHoverText("§eSet waypoint for ${loc.displayName}") + ) + .append( + UTextComponent("§e[Custom]") + .setClick( + MCClickEventAction.SUGGEST_COMMAND, + "/skytilshollowwaypoint set $x $y $z name_here" + ) + .setHoverText("§eSet custom waypoint") + ).chat() + } + } + } + if ((Skytils.config.crystalHollowWaypoints || Skytils.config.crystalHollowMapPlaces) && Skytils.config.kingYolkarWaypoint && SBInfo.mode == SkyblockIsland.CrystalHollows.mode + && mc.thePlayer != null && unformatted.startsWith("[NPC] King Yolkar:") + ) { + CrystalHollowsMap.Locations.KingYolkar.loc.set() + } + if (unformatted.startsWith("You died") || unformatted.startsWith("☠ You were killed")) { + waypointDelayTicks = + 50 //this is to make sure the scoreboard has time to update and nothing moves halfway across the map + if (Skytils.config.crystalHollowDeathWaypoint && SBInfo.mode == SkyblockIsland.CrystalHollows.mode && lastTPLoc != null) { + UChat.chat( + UTextComponent("$prefix §eClick to set a death waypoint at ${lastTPLoc!!.x} ${lastTPLoc!!.y} ${lastTPLoc!!.z}").setClick( + MCClickEventAction.RUN_COMMAND, + "/sthw set ${lastTPLoc!!.x} ${lastTPLoc!!.y} ${lastTPLoc!!.z} Last Death" + ) + ) + } + } else if (unformatted.startsWith("Warp")) { + waypointDelayTicks = 50 + } + } + + private fun waypointChatMessage(x: String, y: String, z: String) { + val message = UMessage( + "$prefix §eFound coordinates in a chat message, click a button to set a waypoint.\n" + ) + for (loc in CrystalHollowsMap.Locations.entries) { + if (loc.loc.exists()) continue + message.append( + UTextComponent("${loc.displayName.substring(0, 2)}[${loc.displayName}] ") + .setClick(MCClickEventAction.SUGGEST_COMMAND, "/sthw set $x $y $z ${loc.id}") + .setHoverText("§eSet waypoint for ${loc.cleanName}") + ) + } + message.append( + UTextComponent("§e[Custom]").setClick( + MCClickEventAction.SUGGEST_COMMAND, + "/sthw set $x $y $z Name" + ).setHoverText("§eSet waypoint for custom location") + ) + message.chat() + } + + @SubscribeEvent + fun onRenderWorld(event: RenderWorldLastEvent) { + if (!Utils.inSkyblock) return + val matrixStack = UMatrixStack() + if (Skytils.config.crystalHollowWaypoints && SBInfo.mode == SkyblockIsland.CrystalHollows.mode) { + GlStateManager.disableDepth() + for (loc in CrystalHollowsMap.Locations.entries) { + loc.loc.drawWaypoint(loc.cleanName, event.partialTicks, matrixStack) + } + RenderUtil.renderWaypointText("Crystal Nucleus", 513.5, 107.0, 513.5, event.partialTicks, matrixStack) + for ((key, value) in waypoints) + RenderUtil.renderWaypointText(key, value, event.partialTicks, matrixStack) + GlStateManager.enableDepth() + } + } + + @SubscribeEvent + fun onRenderLivingPre(event: RenderLivingEvent.Pre) { + if (!Utils.inSkyblock) return + if (Skytils.config.crystalHollowWaypoints && + event.entity is EntityOtherPlayerMP && + event.entity.name == "Team Treasurite" && + mc.thePlayer.canEntityBeSeen(event.entity) && + event.entity.baseMaxHealth == if (MayorInfo.mayorPerks.contains("DOUBLE MOBS HP!!!")) 2_000_000.0 else 1_000_000.0 + ) { + waypoints["Corleone"] = event.entity.position + } + } + + @SubscribeEvent(priority = EventPriority.LOW) // priority low so it always runs after sbinfo is updated + fun onTick(event: ClientTickEvent) { + if (!Utils.inSkyblock || event.phase != TickEvent.Phase.START) return + if ((Skytils.config.crystalHollowWaypoints || Skytils.config.crystalHollowMapPlaces) && SBInfo.mode == SkyblockIsland.CrystalHollows.mode + && waypointDelayTicks == 0 && mc.thePlayer != null + ) { + CrystalHollowsMap.Locations.cleanNameToLocation[SBInfo.location]?.loc?.set() + } else if (waypointDelayTicks > 0) + waypointDelayTicks-- + } + + @SubscribeEvent + fun onWorldChange(event: WorldEvent.Unload) { + CrystalHollowsMap.Locations.entries.forEach { it.loc.reset() } + waypoints.clear() + } + + + class CrystalHollowsMap : GuiElement(name = "Crystal Hollows Map", x = 0, y = 0) { + val mapLocation = ResourceLocation("skytils", "crystalhollowsmap.png") + + enum class Locations(val displayName: String, val id: String, val color: Int, val size: Int = 50) { + LostPrecursorCity("§fLost Precursor City", "internal_city", ColorFactory.WHITE.rgb), + JungleTemple("§aJungle Temple", "internal_temple", ColorFactory.GREEN.rgb), + GoblinQueensDen("§eGoblin Queen's Den", "internal_den", ColorFactory.YELLOW.rgb), + MinesOfDivan("§9Mines of Divan", "internal_mines", ColorFactory.BLUE.rgb), + KingYolkar("§6King Yolkar", "internal_king", ColorFactory.ORANGE.rgb, 25), + KhazadDum("§cKhazad-dûm", "internal_bal", ColorFactory.RED.rgb), + FairyGrotto("§dFairy Grotto", "internal_fairy", ColorFactory.PINK.rgb, 26), + Corleone("§bCorleone", "internal_corleone", ColorFactory.AQUA.rgb, 26); + + val loc = LocationObject() + val cleanName = displayName.stripControlCodes() + + companion object { + val cleanNameToLocation = entries.associateBy { it.cleanName } + } + } + + override fun render() { + if (!toggled || SBInfo.mode != SkyblockIsland.CrystalHollows.mode || mc.thePlayer == null) return + val stack = UMatrixStack() + UMatrixStack.Compat.runLegacyMethod(stack) { + stack.scale(0.1, 0.1, 1.0) + UGraphics.disableLighting() + stack.runWithGlobalState { + RenderUtil.renderTexture(mapLocation, 0, 0, 624, 624, false) + if (Skytils.config.crystalHollowMapPlaces) { + Locations.entries.forEach { + it.loc.drawOnMap(it.size, it.color) + } + } + } + val x = (mc.thePlayer.posX - 202).coerceIn(0.0, 624.0) + val y = (mc.thePlayer.posZ - 202).coerceIn(0.0, 624.0) + + // player marker code + val wr = UGraphics.getFromTessellator() + mc.textureManager.bindTexture(ResourceLocation("textures/map/map_icons.png")) + + stack.push() + stack.translate(x, y, 0.0) + + // Rotate about the center to match the player's yaw + stack.rotate((mc.thePlayer.rotationYawHead + 180f) % 360f, 0f, 0f, 1f) + stack.scale(1.5f, 1.5f, 1.5f) + stack.translate(-0.125f, 0.125f, 0.0f) + UGraphics.color4f(1f, 1f, 1f, 1f) + UGraphics.enableAlpha() + val d1 = 0.0 + val d2 = 0.25 + wr.beginWithActiveShader(UGraphics.DrawMode.QUADS, DefaultVertexFormats.POSITION_TEX) + wr.pos(stack, -8.0, -8.0, 100.0).tex(d1, d1).endVertex() + wr.pos(stack, -8.0, 8.0, 100.0).tex(d1, d2).endVertex() + wr.pos(stack, 8.0, 8.0, 100.0).tex(d2, d2).endVertex() + wr.pos(stack, 8.0, -8.0, 100.0).tex(d2, d1).endVertex() + wr.drawDirect() + stack.pop() + } + } + + override fun demoRender() { + UGraphics.disableLighting() + RenderUtil.renderTexture(mapLocation, 0, 0, 62, 62, false) + } + + override val toggled: Boolean + get() = Skytils.config.crystalHollowMap + override val height: Int + get() = 62 // should be 62.4 but oh well + override val width: Int + get() = 62 + + init { + Skytils.guiManager.registerElement(this) + } + } + + init { + CrystalHollowsMap() + } + + class LocationObject { + var locX: Double? = null + var locY: Double? = null + var locZ: Double? = null + private var locMinX: Double = 1100.0 + private var locMinY: Double = 1100.0 + private var locMinZ: Double = 1100.0 + private var locMaxX: Double = -100.0 + private var locMaxY: Double = -100.0 + private var locMaxZ: Double = -100.0 + + fun reset() { + locX = null + locY = null + locZ = null + locMinX = 1100.0 + locMinY = 1100.0 + locMinZ = 1100.0 + locMaxX = -100.0 + locMaxY = -100.0 + locMaxZ = -100.0 + } + + fun set() { + locMinX = (mc.thePlayer.posX - 200).coerceIn(0.0, 624.0).coerceAtMost(locMinX) + locMinY = mc.thePlayer.posY.coerceIn(0.0, 256.0).coerceAtMost(locMinY) + locMinZ = (mc.thePlayer.posZ - 200).coerceIn(0.0, 624.0).coerceAtMost(locMinZ) + locMaxX = (mc.thePlayer.posX - 200).coerceIn(0.0, 624.0).coerceAtLeast(locMaxX) + locMaxY = mc.thePlayer.posY.coerceIn(0.0, 256.0).coerceAtLeast(locMaxY) + locMaxZ = (mc.thePlayer.posZ - 200).coerceIn(0.0, 624.0).coerceAtLeast(locMaxZ) + locX = (locMinX + locMaxX) / 2 + locY = (locMinY + locMaxY) / 2 + locZ = (locMinZ + locMaxZ) / 2 + } + + fun exists(): Boolean { + return locX != null && locY != null && locZ != null + } + + fun drawWaypoint(text: String, partialTicks: Float, matrixStack: UMatrixStack) { + if (exists()) + RenderUtil.renderWaypointText(text, locX!! + 200, locY!!, locZ!! + 200, partialTicks, matrixStack) + } + + fun drawOnMap(size: Int, color: Int) { + if (exists()) + RenderUtil.drawRect(locX!! - size, locZ!! - size, locX!! + size, locZ!! + size, color) + } + + override fun toString(): String { + return String.format("%.0f", locX?.plus(200)) + " " + String.format( + "%.0f", + locY + ) + " " + String.format( + "%.0f", + locZ?.plus(200) + ) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/mining/MiningFeatures.kt b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/mining/MiningFeatures.kt index 35bca0c4c..aede86fa9 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/mining/MiningFeatures.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/mining/MiningFeatures.kt @@ -18,7 +18,6 @@ package gg.skytils.skytilsmod.features.impl.mining import gg.essential.universal.UChat -import gg.essential.universal.UGraphics import gg.essential.universal.UMatrixStack import gg.essential.universal.utils.MCClickEventAction import gg.essential.universal.wrappers.message.UMessage @@ -26,41 +25,29 @@ import gg.essential.universal.wrappers.message.UTextComponent import gg.skytils.skytilsmod.Skytils import gg.skytils.skytilsmod.Skytils.Companion.failPrefix import gg.skytils.skytilsmod.Skytils.Companion.mc -import gg.skytils.skytilsmod.Skytils.Companion.prefix import gg.skytils.skytilsmod.Skytils.Companion.successPrefix import gg.skytils.skytilsmod.core.DataFetcher import gg.skytils.skytilsmod.core.GuiManager import gg.skytils.skytilsmod.core.GuiManager.createTitle -import gg.skytils.skytilsmod.core.structure.GuiElement import gg.skytils.skytilsmod.core.tickTimer import gg.skytils.skytilsmod.events.impl.BossBarEvent import gg.skytils.skytilsmod.events.impl.GuiContainerEvent import gg.skytils.skytilsmod.events.impl.PacketEvent -import gg.skytils.skytilsmod.features.impl.handlers.MayorInfo import gg.skytils.skytilsmod.utils.* import gg.skytils.skytilsmod.utils.RenderUtil.highlight -import gg.skytils.skytilsmod.utils.graphics.colors.ColorFactory -import net.minecraft.client.entity.EntityOtherPlayerMP import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import net.minecraft.entity.EntityLivingBase import net.minecraft.init.Blocks import net.minecraft.init.Items import net.minecraft.inventory.ContainerChest -import net.minecraft.network.play.server.S08PacketPlayerPosLook import net.minecraft.network.play.server.S3EPacketTeams import net.minecraft.util.AxisAlignedBB import net.minecraft.util.BlockPos -import net.minecraft.util.ResourceLocation import net.minecraftforge.client.event.ClientChatReceivedEvent -import net.minecraftforge.client.event.RenderLivingEvent import net.minecraftforge.client.event.RenderWorldLastEvent import net.minecraftforge.event.entity.player.PlayerInteractEvent import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.common.gameevent.TickEvent -import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent import java.awt.Color import java.util.regex.Pattern @@ -74,15 +61,6 @@ object MiningFeatures { private var puzzlerSolution: BlockPos? = null private var raffleBox: BlockPos? = null private var inRaffle = false - var lastTPLoc: BlockPos? = null - var waypoints = hashMapOf() - var waypointDelayTicks = 0 - private val SBE_DSM_PATTERN = - Regex("\\\$(?:SBECHWP\\b|DSMCHWP):(?.*?)@-(?-?\\d+),(?-?\\d+),(?-?\\d+)") - private val xyzPattern = - Regex(".*?(?[a-zA-Z0-9_]{3,16}):.*?(?[0-9]{1,3}),? (?:y: )?(?[0-9]{1,3}),? (?:z: )?(?[0-9]{1,3}).*?") - private val xzPattern = - Regex(".*(?[a-zA-Z0-9_]{3,16}):.* (?[0-9]{1,3}),? (?[0-9]{1,3}).*") @SubscribeEvent fun onBossBar(event: BossBarEvent.Set) { @@ -106,14 +84,6 @@ object MiningFeatures { } } - @SubscribeEvent - fun onReceivePacket(event: PacketEvent.ReceiveEvent) { - if (!Utils.inSkyblock) return - if (Skytils.config.crystalHollowDeathWaypoint && event.packet is S08PacketPlayerPosLook && mc.thePlayer != null) { - lastTPLoc = mc.thePlayer.position - } - } - @SubscribeEvent(priority = EventPriority.HIGHEST, receiveCanceled = true) fun onChat(event: ClientChatReceivedEvent) { if (!Utils.inSkyblock || event.type == 2.toByte()) return @@ -186,95 +156,6 @@ object MiningFeatures { } } } - if (Skytils.config.hollowChatCoords && SBInfo.mode == SkyblockIsland.CrystalHollows.mode) { - xyzPattern.find(unformatted)?.groups?.let { - waypointChatMessage(it["x"]!!.value, it["y"]!!.value, it["z"]!!.value) - return - } - xzPattern.find(unformatted)?.groups?.let { - waypointChatMessage(it["x"]!!.value, "100", it["z"]!!.value) - return - } - - /** - * Checks for the format used in DSM and SBE - * $DSMCHWP:Mines of Divan@-673,117,426 ✔ - * $SBECHWP:Khazad-dûm@-292,63,281 ✔ - * $asdf:Khazad-dûm@-292,63,281 ❌ - * $SBECHWP:Khazad-dûm@asdf,asdf,asdf ❌ - */ - val cleaned = SBE_DSM_PATTERN.find(unformatted) - if (cleaned != null) { - val stringLocation = cleaned.groups["stringLocation"]!!.value - val x = cleaned.groups["x"]!!.value - val y = cleaned.groups["y"]!!.value - val z = cleaned.groups["z"]!!.value - CrystalHollowsMap.Locations.entries.find { it.cleanName == stringLocation } - ?.takeIf { !it.loc.exists() }?.let { loc -> - /** - * Sends the waypoints message except it suggests which one should be used based on - * the name contained in the message and converts it to the internally used names for the waypoints. - */ - UMessage("§3Skytils > §eFound coordinates in a chat message, click a button to set a waypoint.\n") - .append( - UTextComponent("§f${loc.displayName} ") - .setClick( - MCClickEventAction.RUN_COMMAND, - "/skytilshollowwaypoint set $x $y $z ${loc.id}" - ) - .setHoverText("§eSet waypoint for ${loc.displayName}") - ) - .append( - UTextComponent("§e[Custom]") - .setClick( - MCClickEventAction.SUGGEST_COMMAND, - "/skytilshollowwaypoint set $x $y $z name_here" - ) - .setHoverText("§eSet custom waypoint") - ).chat() - } - } - } - if ((Skytils.config.crystalHollowWaypoints || Skytils.config.crystalHollowMapPlaces) && Skytils.config.kingYolkarWaypoint && SBInfo.mode == SkyblockIsland.CrystalHollows.mode - && mc.thePlayer != null && unformatted.startsWith("[NPC] King Yolkar:") - ) { - CrystalHollowsMap.Locations.KingYolkar.loc.set() - } - if (unformatted.startsWith("You died") || unformatted.startsWith("☠ You were killed")) { - waypointDelayTicks = - 50 //this is to make sure the scoreboard has time to update and nothing moves halfway across the map - if (Skytils.config.crystalHollowDeathWaypoint && SBInfo.mode == SkyblockIsland.CrystalHollows.mode && lastTPLoc != null) { - UChat.chat( - UTextComponent("$prefix §eClick to set a death waypoint at ${lastTPLoc!!.x} ${lastTPLoc!!.y} ${lastTPLoc!!.z}").setClick( - MCClickEventAction.RUN_COMMAND, - "/sthw set ${lastTPLoc!!.x} ${lastTPLoc!!.y} ${lastTPLoc!!.z} Last Death" - ) - ) - } - } else if (unformatted.startsWith("Warp")) { - waypointDelayTicks = 50 - } - } - - private fun waypointChatMessage(x: String, y: String, z: String) { - val message = UMessage( - "$prefix §eFound coordinates in a chat message, click a button to set a waypoint.\n" - ) - for (loc in CrystalHollowsMap.Locations.entries) { - if (loc.loc.exists()) continue - message.append( - UTextComponent("${loc.displayName.substring(0, 2)}[${loc.displayName}] ") - .setClick(MCClickEventAction.SUGGEST_COMMAND, "/sthw set $x $y $z ${loc.id}") - .setHoverText("§eSet waypoint for ${loc.cleanName}") - ) - } - message.append( - UTextComponent("§e[Custom]").setClick( - MCClickEventAction.SUGGEST_COMMAND, - "/sthw set $x $y $z Name" - ).setHoverText("§eSet waypoint for custom location") - ) - message.chat() } @SubscribeEvent @@ -340,29 +221,6 @@ object MiningFeatures { GlStateManager.enableDepth() GlStateManager.enableCull() } - if (Skytils.config.crystalHollowWaypoints && SBInfo.mode == SkyblockIsland.CrystalHollows.mode) { - GlStateManager.disableDepth() - for (loc in CrystalHollowsMap.Locations.entries) { - loc.loc.drawWaypoint(loc.cleanName, event.partialTicks, matrixStack) - } - RenderUtil.renderWaypointText("Crystal Nucleus", 513.5, 107.0, 513.5, event.partialTicks, matrixStack) - for ((key, value) in waypoints) - RenderUtil.renderWaypointText(key, value, event.partialTicks, matrixStack) - GlStateManager.enableDepth() - } - } - - @SubscribeEvent - fun onRenderLivingPre(event: RenderLivingEvent.Pre) { - if (!Utils.inSkyblock) return - if (Skytils.config.crystalHollowWaypoints && - event.entity is EntityOtherPlayerMP && - event.entity.name == "Team Treasurite" && - mc.thePlayer.canEntityBeSeen(event.entity) && - event.entity.baseMaxHealth == if (MayorInfo.mayorPerks.contains("DOUBLE MOBS HP!!!")) 2_000_000.0 else 1_000_000.0 - ) { - waypoints["Corleone"] = event.entity.position - } } @SubscribeEvent @@ -386,168 +244,11 @@ object MiningFeatures { } } - @SubscribeEvent(priority = EventPriority.LOW) // priority low so it always runs after sbinfo is updated - fun onTick(event: ClientTickEvent) { - if (!Utils.inSkyblock || event.phase != TickEvent.Phase.START) return - if ((Skytils.config.crystalHollowWaypoints || Skytils.config.crystalHollowMapPlaces) && SBInfo.mode == SkyblockIsland.CrystalHollows.mode - && waypointDelayTicks == 0 && mc.thePlayer != null - ) { - CrystalHollowsMap.Locations.cleanNameToLocation[SBInfo.location]?.loc?.set() - } else if (waypointDelayTicks > 0) - waypointDelayTicks-- - } - @SubscribeEvent fun onWorldChange(event: WorldEvent.Unload) { puzzlerSolution = null lastJukebox = null raffleBox = null inRaffle = false - CrystalHollowsMap.Locations.entries.forEach { it.loc.reset() } - waypoints.clear() - } - - class CrystalHollowsMap : GuiElement(name = "Crystal Hollows Map", x = 0, y = 0) { - val mapLocation = ResourceLocation("skytils", "crystalhollowsmap.png") - - enum class Locations(val displayName: String, val id: String, val color: Int, val size: Int = 50) { - LostPrecursorCity("§fLost Precursor City", "internal_city", ColorFactory.WHITE.rgb), - JungleTemple("§aJungle Temple", "internal_temple", ColorFactory.GREEN.rgb), - GoblinQueensDen("§eGoblin Queen's Den", "internal_den", ColorFactory.YELLOW.rgb), - MinesOfDivan("§9Mines of Divan", "internal_mines", ColorFactory.BLUE.rgb), - KingYolkar("§6King Yolkar", "internal_king", ColorFactory.ORANGE.rgb, 25), - KhazadDum("§cKhazad-dûm", "internal_bal", ColorFactory.RED.rgb), - FairyGrotto("§dFairy Grotto", "internal_fairy", ColorFactory.PINK.rgb, 26), - Corleone("§bCorleone", "internal_corleone", ColorFactory.AQUA.rgb, 26); - - val loc = LocationObject() - val cleanName = displayName.stripControlCodes() - - companion object { - val cleanNameToLocation = entries.associateBy { it.cleanName } - } - } - - override fun render() { - if (!toggled || SBInfo.mode != SkyblockIsland.CrystalHollows.mode || mc.thePlayer == null) return - val stack = UMatrixStack() - UMatrixStack.Compat.runLegacyMethod(stack) { - stack.scale(0.1, 0.1, 1.0) - UGraphics.disableLighting() - stack.runWithGlobalState { - RenderUtil.renderTexture(mapLocation, 0, 0, 624, 624, false) - if (Skytils.config.crystalHollowMapPlaces) { - Locations.entries.forEach { - it.loc.drawOnMap(it.size, it.color) - } - } - } - val x = (mc.thePlayer.posX - 202).coerceIn(0.0, 624.0) - val y = (mc.thePlayer.posZ - 202).coerceIn(0.0, 624.0) - - // player marker code - val wr = UGraphics.getFromTessellator() - mc.textureManager.bindTexture(ResourceLocation("textures/map/map_icons.png")) - - stack.push() - stack.translate(x, y, 0.0) - - // Rotate about the center to match the player's yaw - stack.rotate((mc.thePlayer.rotationYawHead + 180f) % 360f, 0f, 0f, 1f) - stack.scale(1.5f, 1.5f, 1.5f) - stack.translate(-0.125f, 0.125f, 0.0f) - UGraphics.color4f(1f, 1f, 1f, 1f) - UGraphics.enableAlpha() - val d1 = 0.0 - val d2 = 0.25 - wr.beginWithActiveShader(UGraphics.DrawMode.QUADS, DefaultVertexFormats.POSITION_TEX) - wr.pos(stack, -8.0, -8.0, 100.0).tex(d1, d1).endVertex() - wr.pos(stack, -8.0, 8.0, 100.0).tex(d1, d2).endVertex() - wr.pos(stack, 8.0, 8.0, 100.0).tex(d2, d2).endVertex() - wr.pos(stack, 8.0, -8.0, 100.0).tex(d2, d1).endVertex() - wr.drawDirect() - stack.pop() - } - } - - override fun demoRender() { - UGraphics.disableLighting() - RenderUtil.renderTexture(mapLocation, 0, 0, 62, 62, false) - } - - override val toggled: Boolean - get() = Skytils.config.crystalHollowMap - override val height: Int - get() = 62 // should be 62.4 but oh well - override val width: Int - get() = 62 - - init { - Skytils.guiManager.registerElement(this) - } - } - - init { - CrystalHollowsMap() - } - - class LocationObject { - var locX: Double? = null - var locY: Double? = null - var locZ: Double? = null - private var locMinX: Double = 1100.0 - private var locMinY: Double = 1100.0 - private var locMinZ: Double = 1100.0 - private var locMaxX: Double = -100.0 - private var locMaxY: Double = -100.0 - private var locMaxZ: Double = -100.0 - - fun reset() { - locX = null - locY = null - locZ = null - locMinX = 1100.0 - locMinY = 1100.0 - locMinZ = 1100.0 - locMaxX = -100.0 - locMaxY = -100.0 - locMaxZ = -100.0 - } - - fun set() { - locMinX = (mc.thePlayer.posX - 200).coerceIn(0.0, 624.0).coerceAtMost(locMinX) - locMinY = mc.thePlayer.posY.coerceIn(0.0, 256.0).coerceAtMost(locMinY) - locMinZ = (mc.thePlayer.posZ - 200).coerceIn(0.0, 624.0).coerceAtMost(locMinZ) - locMaxX = (mc.thePlayer.posX - 200).coerceIn(0.0, 624.0).coerceAtLeast(locMaxX) - locMaxY = mc.thePlayer.posY.coerceIn(0.0, 256.0).coerceAtLeast(locMaxY) - locMaxZ = (mc.thePlayer.posZ - 200).coerceIn(0.0, 624.0).coerceAtLeast(locMaxZ) - locX = (locMinX + locMaxX) / 2 - locY = (locMinY + locMaxY) / 2 - locZ = (locMinZ + locMaxZ) / 2 - } - - fun exists(): Boolean { - return locX != null && locY != null && locZ != null - } - - fun drawWaypoint(text: String, partialTicks: Float, matrixStack: UMatrixStack) { - if (exists()) - RenderUtil.renderWaypointText(text, locX!! + 200, locY!!, locZ!! + 200, partialTicks, matrixStack) - } - - fun drawOnMap(size: Int, color: Int) { - if (exists()) - RenderUtil.drawRect(locX!! - size, locZ!! - size, locX!! + size, locZ!! + size, color) - } - - override fun toString(): String { - return String.format("%.0f", locX?.plus(200)) + " " + String.format( - "%.0f", - locY - ) + " " + String.format( - "%.0f", - locZ?.plus(200) - ) - } } } From f7b9416ad6069034d46068721215578f6aa47158 Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Thu, 23 May 2024 18:10:35 -0400 Subject: [PATCH 12/25] chore: split the ktor client used for http and ws --- .../kotlin/gg/skytils/skytilsmod/Skytils.kt | 50 +++++++------------ .../gg/skytils/skytilsws/client/WSClient.kt | 36 ++++++++++++- 2 files changed, 53 insertions(+), 33 deletions(-) diff --git a/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt b/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt index fed9022f9..e137637b8 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/Skytils.kt @@ -76,20 +76,15 @@ import gg.skytils.skytilsmod.utils.* import gg.skytils.skytilsmod.utils.graphics.ScreenRenderer import gg.skytils.skytilsmod.utils.graphics.colors.CustomColor import gg.skytils.skytilsws.client.WSClient -import gg.skytils.skytilsws.shared.SkytilsWS import io.ktor.client.* import io.ktor.client.engine.cio.* import io.ktor.client.plugins.* import io.ktor.client.plugins.cache.* import io.ktor.client.plugins.compression.* import io.ktor.client.plugins.contentnegotiation.* -import io.ktor.client.plugins.websocket.* import io.ktor.http.* -import io.ktor.serialization.kotlinx.* import io.ktor.serialization.kotlinx.json.* -import io.ktor.websocket.* import kotlinx.coroutines.* -import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule import net.minecraft.client.Minecraft @@ -127,7 +122,6 @@ import java.security.KeyStore import java.util.* import java.util.concurrent.Executors import java.util.concurrent.ThreadPoolExecutor -import java.util.zip.Deflater import javax.net.ssl.TrustManagerFactory import javax.net.ssl.X509TrustManager import kotlin.coroutines.CoroutineContext @@ -220,6 +214,23 @@ class Skytils { } } + val trustManager by lazy { + val backingManager = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).apply { + init(null as KeyStore?) + }.trustManagers.first { it is X509TrustManager } as X509TrustManager + + val ourManager = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).apply { + Skytils::class.java.getResourceAsStream("/skytilscacerts.jks").use { + val ourKs = KeyStore.getInstance(KeyStore.getDefaultType()).apply { + load(it, "skytilsontop".toCharArray()) + } + init(ourKs) + } + }.trustManagers.first { it is X509TrustManager } as X509TrustManager + + UnionX509TrustManager(backingManager, ourManager) + } + val client = HttpClient(CIO) { install(ContentEncoding) { customEncoder(BrotliEncoder, 1.0F) @@ -249,32 +260,7 @@ class Skytils { socketTimeout = 10000 } https { - val backingManager = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).apply { - init(null as KeyStore?) - }.trustManagers.first { it is X509TrustManager } as X509TrustManager - - val ourManager = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).apply { - Skytils::class.java.getResourceAsStream("/skytilscacerts.jks").use { - val ourKs = KeyStore.getInstance(KeyStore.getDefaultType()).apply { - load(it, "skytilsontop".toCharArray()) - } - init(ourKs) - } - }.trustManagers.first { it is X509TrustManager } as X509TrustManager - - trustManager = UnionX509TrustManager(backingManager, ourManager) - } - } - - install(WebSockets) { - pingInterval = 5_000L - @OptIn(ExperimentalSerializationApi::class) - contentConverter = KotlinxWebsocketSerializationConverter(SkytilsWS.packetSerializer) - extensions { - install(WebSocketDeflateExtension) { - compressionLevel = Deflater.DEFAULT_COMPRESSION - compressIfBiggerThan(bytes = 4 * 1024) - } + trustManager = Skytils.trustManager } } } diff --git a/src/main/kotlin/gg/skytils/skytilsws/client/WSClient.kt b/src/main/kotlin/gg/skytils/skytilsws/client/WSClient.kt index c7fb57257..22a027524 100644 --- a/src/main/kotlin/gg/skytils/skytilsws/client/WSClient.kt +++ b/src/main/kotlin/gg/skytils/skytilsws/client/WSClient.kt @@ -19,16 +19,50 @@ package gg.skytils.skytilsws.client import gg.skytils.skytilsmod.Skytils -import gg.skytils.skytilsmod.Skytils.Companion.client import gg.skytils.skytilsws.shared.SkytilsWS import gg.skytils.skytilsws.shared.packet.C2SPacketConnect import gg.skytils.skytilsws.shared.packet.Packet +import io.ktor.client.* +import io.ktor.client.engine.cio.* +import io.ktor.client.plugins.* import io.ktor.client.plugins.websocket.* +import io.ktor.serialization.kotlinx.* import io.ktor.websocket.* import kotlinx.coroutines.channels.ClosedReceiveChannelException +import kotlinx.serialization.ExperimentalSerializationApi +import java.util.zip.Deflater object WSClient { var session: DefaultClientWebSocketSession? = null + val wsClient by lazy { + HttpClient(CIO) { + install(UserAgent) { + agent = "Skytils/${Skytils.VERSION} SkytilsWS/${SkytilsWS.version}" + } + + install(WebSockets) { + pingInterval = 59_000L + @OptIn(ExperimentalSerializationApi::class) + contentConverter = KotlinxWebsocketSerializationConverter(SkytilsWS.packetSerializer) + extensions { + install(WebSocketDeflateExtension) { + compressionLevel = Deflater.DEFAULT_COMPRESSION + compressIfBiggerThan(bytes = 4 * 1024) + } + } + } + + engine { + endpoint { + connectTimeout = 10000 + keepAliveTime = 60000 + } + https { + trustManager = Skytils.trustManager + } + } + } + } suspend fun openConnection() { if (session != null) error("Session already open") From b685d61a87a6afd3d46dbecb351eeb0b94df54e2 Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Fri, 24 May 2024 15:09:39 -0400 Subject: [PATCH 13/25] feat!: add crystal hollows waypoint sharing fix: ws client connects to localhost fix: client sends CH location as serverid --- .../features/impl/mining/CHWaypoints.kt | 70 +++++++++++++++---- .../skytilsmod/listeners/DungeonListener.kt | 4 +- .../gg/skytils/skytilsmod/utils/SBInfo.kt | 2 +- .../skytils/skytilsws/client/PacketHandler.kt | 26 +++++++ .../gg/skytils/skytilsws/client/WSClient.kt | 2 +- ws-shared | 2 +- 6 files changed, 88 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/mining/CHWaypoints.kt b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/mining/CHWaypoints.kt index b7020a162..e44c4ff15 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/mining/CHWaypoints.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/mining/CHWaypoints.kt @@ -28,10 +28,17 @@ import gg.skytils.skytilsmod.Skytils import gg.skytils.skytilsmod.Skytils.Companion.mc import gg.skytils.skytilsmod.Skytils.Companion.prefix import gg.skytils.skytilsmod.core.structure.GuiElement +import gg.skytils.skytilsmod.events.impl.HypixelPacketEvent import gg.skytils.skytilsmod.events.impl.PacketEvent import gg.skytils.skytilsmod.features.impl.handlers.MayorInfo import gg.skytils.skytilsmod.utils.* import gg.skytils.skytilsmod.utils.graphics.colors.ColorFactory +import gg.skytils.skytilsws.client.WSClient +import gg.skytils.skytilsws.shared.packet.C2SPacketCHWaypoint +import gg.skytils.skytilsws.shared.packet.C2SPacketCHWaypointsSubscribe +import gg.skytils.skytilsws.shared.structs.CHWaypointType +import kotlinx.coroutines.launch +import net.hypixel.modapi.packet.impl.clientbound.event.ClientboundLocationPacket import net.minecraft.client.entity.EntityOtherPlayerMP import net.minecraft.client.renderer.GlStateManager import net.minecraft.client.renderer.vertex.DefaultVertexFormats @@ -47,6 +54,7 @@ import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.gameevent.TickEvent import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent +import kotlin.jvm.optionals.getOrNull object CHWaypoints { var lastTPLoc: BlockPos? = null @@ -58,6 +66,22 @@ object CHWaypoints { Regex(".*?(?[a-zA-Z0-9_]{3,16}):.*?(?[0-9]{1,3}),? (?:y: )?(?[0-9]{1,3}),? (?:z: )?(?[0-9]{1,3}).*?") private val xzPattern = Regex(".*(?[a-zA-Z0-9_]{3,16}):.* (?[0-9]{1,3}),? (?[0-9]{1,3}).*") + val chWaypointsList = hashMapOf() + class CHInstance { + val waypoints = hashMapOf() + } + + + @SubscribeEvent + fun onHypixelPacket(event: HypixelPacketEvent.ReceiveEvent) { + if (event.packet is ClientboundLocationPacket) { + if (event.packet.mode.getOrNull() == SkyblockIsland.CrystalHollows.mode) { + Skytils.IO.launch { + WSClient.sendPacket(C2SPacketCHWaypointsSubscribe(event.packet.serverName)) + } + } + } + } @SubscribeEvent fun onReceivePacket(event: PacketEvent.ReceiveEvent) { @@ -187,7 +211,14 @@ object CHWaypoints { mc.thePlayer.canEntityBeSeen(event.entity) && event.entity.baseMaxHealth == if (MayorInfo.mayorPerks.contains("DOUBLE MOBS HP!!!")) 2_000_000.0 else 1_000_000.0 ) { - waypoints["Corleone"] = event.entity.position + if (!CrystalHollowsMap.Locations.Corleone.loc.exists()) { + CrystalHollowsMap.Locations.Corleone.apply { + loc.set() + Skytils.IO.launch { + WSClient.sendPacket(C2SPacketCHWaypoint(serverId = SBInfo.server ?: "", serverTime = mc.theWorld.worldTime, packetType, loc.locX!!.toInt(), loc.locY!!.toInt(), loc.locZ!!.toInt())) + } + } + } else CrystalHollowsMap.Locations.Corleone.loc.set() } } @@ -197,14 +228,27 @@ object CHWaypoints { if ((Skytils.config.crystalHollowWaypoints || Skytils.config.crystalHollowMapPlaces) && SBInfo.mode == SkyblockIsland.CrystalHollows.mode && waypointDelayTicks == 0 && mc.thePlayer != null ) { - CrystalHollowsMap.Locations.cleanNameToLocation[SBInfo.location]?.loc?.set() + CrystalHollowsMap.Locations.cleanNameToLocation[SBInfo.location]?.let { + if (!it.loc.exists()) { + it.loc.set() + Skytils.IO.launch { + WSClient.sendPacket(C2SPacketCHWaypoint(serverId = SBInfo.server ?: "", serverTime = mc.theWorld.worldTime, it.packetType, it.loc.locX!!.toInt(), it.loc.locY!!.toInt(), it.loc.locZ!!.toInt())) + } + } else it.loc.set() + } } else if (waypointDelayTicks > 0) waypointDelayTicks-- } - @SubscribeEvent + @SubscribeEvent(priority = EventPriority.HIGHEST) fun onWorldChange(event: WorldEvent.Unload) { - CrystalHollowsMap.Locations.entries.forEach { it.loc.reset() } + val instance = chWaypointsList.getOrPut(SBInfo.server ?: "") { CHInstance() } + CrystalHollowsMap.Locations.entries.forEach { + if (it.loc.exists()) { + instance.waypoints[it.packetType] = BlockPos(it.loc.locX!!, it.loc.locY!!, it.loc.locZ!!) + } + it.loc.reset() + } waypoints.clear() } @@ -212,15 +256,15 @@ object CHWaypoints { class CrystalHollowsMap : GuiElement(name = "Crystal Hollows Map", x = 0, y = 0) { val mapLocation = ResourceLocation("skytils", "crystalhollowsmap.png") - enum class Locations(val displayName: String, val id: String, val color: Int, val size: Int = 50) { - LostPrecursorCity("§fLost Precursor City", "internal_city", ColorFactory.WHITE.rgb), - JungleTemple("§aJungle Temple", "internal_temple", ColorFactory.GREEN.rgb), - GoblinQueensDen("§eGoblin Queen's Den", "internal_den", ColorFactory.YELLOW.rgb), - MinesOfDivan("§9Mines of Divan", "internal_mines", ColorFactory.BLUE.rgb), - KingYolkar("§6King Yolkar", "internal_king", ColorFactory.ORANGE.rgb, 25), - KhazadDum("§cKhazad-dûm", "internal_bal", ColorFactory.RED.rgb), - FairyGrotto("§dFairy Grotto", "internal_fairy", ColorFactory.PINK.rgb, 26), - Corleone("§bCorleone", "internal_corleone", ColorFactory.AQUA.rgb, 26); + enum class Locations(val displayName: String, val id: String, val color: Int, val packetType: CHWaypointType, val size: Int = 50) { + LostPrecursorCity("§fLost Precursor City", "internal_city", ColorFactory.WHITE.rgb, CHWaypointType.LostPrecursorCity), + JungleTemple("§aJungle Temple", "internal_temple", ColorFactory.GREEN.rgb, CHWaypointType.JungleTemple), + GoblinQueensDen("§eGoblin Queen's Den", "internal_den", ColorFactory.YELLOW.rgb, CHWaypointType.GoblinQueensDen), + MinesOfDivan("§9Mines of Divan", "internal_mines", ColorFactory.BLUE.rgb, CHWaypointType.MinesOfDivan), + KingYolkar("§6King Yolkar", "internal_king", ColorFactory.ORANGE.rgb, CHWaypointType.KingYolkar,25), + KhazadDum("§cKhazad-dûm", "internal_bal", ColorFactory.RED.rgb, CHWaypointType.KhazadDum), + FairyGrotto("§dFairy Grotto", "internal_fairy", ColorFactory.PINK.rgb, CHWaypointType.FairyGrotto, 26), + Corleone("§bCorleone", "internal_corleone", ColorFactory.AQUA.rgb, CHWaypointType.Corleone, 26); val loc = LocationObject() val cleanName = displayName.stripControlCodes() diff --git a/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt b/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt index 9e0bfedce..64d32f96d 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt @@ -51,7 +51,7 @@ import gg.skytils.skytilsws.client.WSClient import gg.skytils.skytilsws.shared.packet.C2SPacketDungeonEnd import gg.skytils.skytilsws.shared.packet.C2SPacketDungeonRoom import gg.skytils.skytilsws.shared.packet.C2SPacketDungeonRoomSecret -import gg.skytils.skytilsws.shared.packet.C2SPacketStartDungeon +import gg.skytils.skytilsws.shared.packet.C2SPacketDungeonStart import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -212,7 +212,7 @@ object DungeonListener { } val partyMembers = party.await().members.ifEmpty { setOf(mc.thePlayer.uniqueID) }.mapTo(hashSetOf()) { it.toString() } val entrance = DungeonInfo.uniqueRooms.first { it.mainRoom.data.type == RoomType.ENTRANCE } - WSClient.sendPacket(C2SPacketStartDungeon( + WSClient.sendPacket(C2SPacketDungeonStart( serverId = SBInfo.server ?: return@launch, floor = DungeonFeatures.dungeonFloor!!, members = partyMembers, diff --git a/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt b/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt index 794914468..80cf6ade6 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt @@ -83,7 +83,7 @@ object SBInfo { lastLocationPacket = null } - @SubscribeEvent + @SubscribeEvent(priority = EventPriority.HIGH) fun onHypixelPacket(event: HypixelPacketEvent.ReceiveEvent) { if (event.packet is ClientboundLocationPacket) { Utils.checkThreadAndQueue { diff --git a/src/main/kotlin/gg/skytils/skytilsws/client/PacketHandler.kt b/src/main/kotlin/gg/skytils/skytilsws/client/PacketHandler.kt index 4a5d4323b..f4b0bb0ed 100644 --- a/src/main/kotlin/gg/skytils/skytilsws/client/PacketHandler.kt +++ b/src/main/kotlin/gg/skytils/skytilsws/client/PacketHandler.kt @@ -24,11 +24,16 @@ import gg.skytils.skytilsmod.features.impl.dungeons.catlas.core.map.Room import gg.skytils.skytilsmod.features.impl.dungeons.catlas.core.map.Unknown import gg.skytils.skytilsmod.features.impl.dungeons.catlas.handlers.DungeonInfo import gg.skytils.skytilsmod.features.impl.dungeons.catlas.utils.ScanUtils +import gg.skytils.skytilsmod.features.impl.mining.CHWaypoints +import gg.skytils.skytilsmod.features.impl.mining.CHWaypoints.CHInstance +import gg.skytils.skytilsmod.features.impl.mining.CHWaypoints.chWaypointsList +import gg.skytils.skytilsmod.utils.SBInfo import gg.skytils.skytilsws.shared.IPacketHandler import gg.skytils.skytilsws.shared.SkytilsWS import gg.skytils.skytilsws.shared.packet.* import io.ktor.websocket.* import kotlinx.coroutines.coroutineScope +import net.minecraft.util.BlockPos import java.util.* object PacketHandler : IPacketHandler { @@ -68,6 +73,27 @@ object PacketHandler : IPacketHandler { } } } + is S2CPacketCHReset -> { + CHWaypoints.waypoints.remove(packet.serverId) + } + is S2CPacketCHWaypoint -> { + if (SBInfo.server == packet.serverId) { + if (mc.theWorld.worldTime < packet.serverTime) { + WSClient.sendPacket(C2SPacketCHReset(packet.serverId)) + } else { + CHWaypoints.CrystalHollowsMap.Locations.entries.find { it.packetType == packet.type }?.let { + if (!it.loc.exists()) { + it.loc.locX = packet.x.toDouble() + it.loc.locY = packet.y.toDouble() + it.loc.locZ = packet.z.toDouble() + } + } + } + } else { + val instance = chWaypointsList.getOrPut(SBInfo.server ?: "") { CHInstance() } + instance.waypoints[packet.type] = BlockPos(packet.x, packet.y, packet.z) + } + } else -> { session.close(CloseReason(CloseReason.Codes.CANNOT_ACCEPT, "Unknown packet type")) } diff --git a/src/main/kotlin/gg/skytils/skytilsws/client/WSClient.kt b/src/main/kotlin/gg/skytils/skytilsws/client/WSClient.kt index 22a027524..76e176418 100644 --- a/src/main/kotlin/gg/skytils/skytilsws/client/WSClient.kt +++ b/src/main/kotlin/gg/skytils/skytilsws/client/WSClient.kt @@ -67,7 +67,7 @@ object WSClient { suspend fun openConnection() { if (session != null) error("Session already open") - client.webSocketSession("wss://ws.skytils.gg/ws").apply { + wsClient.webSocketSession(System.getProperty("skytils.websocketURL", "wss://ws.skytils.gg/ws")).apply { session = this try { sendSerialized(C2SPacketConnect(SkytilsWS.version, Skytils.VERSION)) diff --git a/ws-shared b/ws-shared index 51e490538..826fc2b77 160000 --- a/ws-shared +++ b/ws-shared @@ -1 +1 @@ -Subproject commit 51e4905381c9875a9283091d4ff628c904b42e66 +Subproject commit 826fc2b779144272195eb055c0e49eebffe52892 From 12da2df10d2bcdd10e28e98b510833332ae124c7 Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Mon, 27 May 2024 20:47:08 -0400 Subject: [PATCH 14/25] fix: small player model draws hat layer in incorrect location --- .../renderer/MixinItemRenderer.java | 2 +- .../transformers/renderer/MixinLayerCape.java | 2 +- .../renderer/MixinLayerCustomHead.java | 8 +++ .../renderer/MixinModelBiped.java | 49 +++++++++++++++++++ .../renderer/MixinRenderManager.java | 2 +- src/main/resources/mixins.skytils.json | 1 + 6 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 src/main/java/gg/skytils/skytilsmod/mixins/transformers/renderer/MixinModelBiped.java diff --git a/src/main/java/gg/skytils/skytilsmod/mixins/transformers/renderer/MixinItemRenderer.java b/src/main/java/gg/skytils/skytilsmod/mixins/transformers/renderer/MixinItemRenderer.java index 5189e2e0d..391ee1b8e 100644 --- a/src/main/java/gg/skytils/skytilsmod/mixins/transformers/renderer/MixinItemRenderer.java +++ b/src/main/java/gg/skytils/skytilsmod/mixins/transformers/renderer/MixinItemRenderer.java @@ -31,7 +31,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ItemRenderer.class) -public class MixinItemRenderer { +public abstract class MixinItemRenderer { @Shadow private ItemStack itemToRender; diff --git a/src/main/java/gg/skytils/skytilsmod/mixins/transformers/renderer/MixinLayerCape.java b/src/main/java/gg/skytils/skytilsmod/mixins/transformers/renderer/MixinLayerCape.java index 7681d6e02..995a8666c 100644 --- a/src/main/java/gg/skytils/skytilsmod/mixins/transformers/renderer/MixinLayerCape.java +++ b/src/main/java/gg/skytils/skytilsmod/mixins/transformers/renderer/MixinLayerCape.java @@ -38,7 +38,7 @@ public abstract class MixinLayerCape implements LayerRenderer { + @WrapOperation(method = "doRenderLayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/EntityLivingBase;isSneaking()Z")) + private boolean disableSneakOffset(EntityLivingBase instance, Operation original) { + return (!(instance instanceof EntityPlayer) || !instance.isChild()) && original.call(instance); + } + @Inject(method = "doRenderLayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;color(FFFF)V", shift = At.Shift.AFTER), cancellable = true) private void renderCustomHeadLayer(EntityLivingBase entity, float p_177141_2_, float p_177141_3_, float partialTicks, float p_177141_5_, float p_177141_6_, float p_177141_7_, float scale, CallbackInfo ci) { LayerCustomHeadHookKt.renderCustomHeadLayer(entity, p_177141_2_, p_177141_3_, partialTicks, p_177141_5_, p_177141_6_, p_177141_7_, scale, ci); diff --git a/src/main/java/gg/skytils/skytilsmod/mixins/transformers/renderer/MixinModelBiped.java b/src/main/java/gg/skytils/skytilsmod/mixins/transformers/renderer/MixinModelBiped.java new file mode 100644 index 000000000..3b7fd7661 --- /dev/null +++ b/src/main/java/gg/skytils/skytilsmod/mixins/transformers/renderer/MixinModelBiped.java @@ -0,0 +1,49 @@ +/* + * Skytils - Hypixel Skyblock Quality of Life Mod + * Copyright (C) 2020-2024 Skytils + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package gg.skytils.skytilsmod.mixins.transformers.renderer; + +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.client.model.ModelBase; +import net.minecraft.client.model.ModelBiped; +import net.minecraft.client.model.ModelRenderer; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ModelBiped.class) +public abstract class MixinModelBiped extends ModelBase { + @Shadow public ModelRenderer bipedHeadwear; + + @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;popMatrix()V", ordinal = 0)) + private void renderChildHeadPost(Entity entityIn, float f, float g, float h, float i, float j, float scale, CallbackInfo ci) { + if (this.isChild && entityIn instanceof EntityPlayer) { + this.bipedHeadwear.render(scale); + } + } + + @WrapWithCondition(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/ModelRenderer;render(F)V", ordinal = 6)) + private boolean renderChildHeadwear(ModelRenderer instance, float j, @Local(argsOnly = true) Entity entityIn) { + return !this.isChild || !(entityIn instanceof EntityPlayer); + } +} diff --git a/src/main/java/gg/skytils/skytilsmod/mixins/transformers/renderer/MixinRenderManager.java b/src/main/java/gg/skytils/skytilsmod/mixins/transformers/renderer/MixinRenderManager.java index 2e76c5972..75709aee4 100644 --- a/src/main/java/gg/skytils/skytilsmod/mixins/transformers/renderer/MixinRenderManager.java +++ b/src/main/java/gg/skytils/skytilsmod/mixins/transformers/renderer/MixinRenderManager.java @@ -28,7 +28,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(RenderManager.class) -public class MixinRenderManager { +public abstract class MixinRenderManager { @Inject(method = "shouldRender", at = @At("HEAD"), cancellable = true) private void shouldRender(Entity entityIn, ICamera camera, double camX, double camY, double camZ, CallbackInfoReturnable cir) { RenderManagerHookKt.shouldRender(entityIn, camera, camX, camY, camZ, cir); diff --git a/src/main/resources/mixins.skytils.json b/src/main/resources/mixins.skytils.json index 571f6211c..30f8fae30 100644 --- a/src/main/resources/mixins.skytils.json +++ b/src/main/resources/mixins.skytils.json @@ -99,6 +99,7 @@ "client": [ "accessors.AccessorPlayerControllerMP", "gui.MixinGuiEditSign", + "renderer.MixinModelBiped", "util.MixinMouseHelper" ] } \ No newline at end of file From dc6268f3adc7fbb4d1489e0ca0d9fe1d0c593a78 Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Thu, 30 May 2024 16:28:49 -0400 Subject: [PATCH 15/25] fix: waypoints not loading regression --- .../impl/skyblock/LocationChangeEvent.kt | 24 +++++++++++++++++++ .../features/impl/handlers/Waypoints.kt | 13 +++++----- .../gg/skytils/skytilsmod/utils/SBInfo.kt | 2 ++ 3 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 src/main/kotlin/gg/skytils/skytilsmod/events/impl/skyblock/LocationChangeEvent.kt diff --git a/src/main/kotlin/gg/skytils/skytilsmod/events/impl/skyblock/LocationChangeEvent.kt b/src/main/kotlin/gg/skytils/skytilsmod/events/impl/skyblock/LocationChangeEvent.kt new file mode 100644 index 000000000..3fa40d124 --- /dev/null +++ b/src/main/kotlin/gg/skytils/skytilsmod/events/impl/skyblock/LocationChangeEvent.kt @@ -0,0 +1,24 @@ +/* + * Skytils - Hypixel Skyblock Quality of Life Mod + * Copyright (C) 2020-2024 Skytils + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package gg.skytils.skytilsmod.events.impl.skyblock + +import gg.skytils.skytilsmod.events.SkytilsEvent +import net.hypixel.modapi.packet.impl.clientbound.event.ClientboundLocationPacket + +data class LocationChangeEvent(val packet: ClientboundLocationPacket) : SkytilsEvent() diff --git a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/handlers/Waypoints.kt b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/handlers/Waypoints.kt index 424b98aac..ef8af9bc7 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/handlers/Waypoints.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/handlers/Waypoints.kt @@ -25,6 +25,8 @@ import gg.essential.universal.UMatrixStack import gg.skytils.skytilsmod.Skytils import gg.skytils.skytilsmod.commands.impl.OrderedWaypointCommand import gg.skytils.skytilsmod.core.PersistentSave +import gg.skytils.skytilsmod.core.tickTimer +import gg.skytils.skytilsmod.events.impl.skyblock.LocationChangeEvent import gg.skytils.skytilsmod.tweaker.DependencyLoader import gg.skytils.skytilsmod.utils.* import kotlinx.serialization.EncodeDefault @@ -60,7 +62,6 @@ object Waypoints : PersistentSave(File(Skytils.modDir, "waypoints.json")) { private val sbeWaypointFormat = Regex("(?:\\.?\\/?crystalwaypoint parse )?(?[a-zA-Z\\d]+)@-(?[-\\d]+),(?[-\\d]+),(?[-\\d]+)\\\\?n?") private var visibleWaypoints = emptyList() - var needsRefresh = false @OptIn(ExperimentalSerializationApi::class) fun getWaypointsFromString(str: String): Set { @@ -232,16 +233,16 @@ object Waypoints : PersistentSave(File(Skytils.modDir, "waypoints.json")) { @SubscribeEvent fun onWorldChange(event: WorldEvent.Unload) { visibleWaypoints = emptyList() - needsRefresh = true + } + + @SubscribeEvent + fun onLocationChange(event: LocationChangeEvent) { + tickTimer(20, task = ::computeVisibleWaypoints) } @SubscribeEvent fun onPlayerMove(event: ClientTickEvent) { if (event.phase == TickEvent.Phase.END) return - if (needsRefresh && SBInfo.mode != null) { - computeVisibleWaypoints() - needsRefresh = false - } if (mc.thePlayer?.hasMoved == true && SBInfo.mode != null && OrderedWaypointCommand.trackedIsland?.mode == SBInfo.mode) { val tracked = OrderedWaypointCommand.trackedSet?.firstOrNull() if (tracked == null) { diff --git a/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt b/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt index 80cf6ade6..239546d6c 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/utils/SBInfo.kt @@ -19,6 +19,7 @@ package gg.skytils.skytilsmod.utils import gg.skytils.skytilsmod.Skytils.Companion.mc import gg.skytils.skytilsmod.events.impl.HypixelPacketEvent +import gg.skytils.skytilsmod.events.impl.skyblock.LocationChangeEvent import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.* @@ -91,6 +92,7 @@ object SBInfo { server = event.packet.serverName lastLocationPacket = event.packet println(event.packet) + LocationChangeEvent(event.packet).postAndCatch() } } } From 551b1d7bd5b05f4e0586ec1e384528878e6ac349 Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Sat, 1 Jun 2024 15:59:42 -0400 Subject: [PATCH 16/25] fix: config typo Trophy Fish Tracker --- src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt | 4 ++-- src/main/resources/assets/skytils/lang/en_US.lang | 2 +- src/main/resources/assets/skytils/lang/zh_CN.lang | 2 +- src/main/resources/assets/skytils/lang/zh_TW.lang | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt b/src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt index f7bd0699b..a0016c846 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt @@ -3287,10 +3287,10 @@ object Config : Vigilant( var fishingHookAge = false @Property( - type = PropertyType.SWITCH, name = "Tropy Fish Tracker", + type = PropertyType.SWITCH, name = "Trophy Fish Tracker", description = "Tracks trophy fish caught.", category = "Miscellaneous", subcategory = "Quality of Life", - i18nName = "skytils.config.miscellaneous.quality_of_life.tropy_fish_tracker", + i18nName = "skytils.config.miscellaneous.quality_of_life.trophy_fish_tracker", i18nCategory = "skytils.config.miscellaneous", i18nSubcategory = "skytils.config.miscellaneous.quality_of_life" ) diff --git a/src/main/resources/assets/skytils/lang/en_US.lang b/src/main/resources/assets/skytils/lang/en_US.lang index a992c140c..7a1ac39bd 100644 --- a/src/main/resources/assets/skytils/lang/en_US.lang +++ b/src/main/resources/assets/skytils/lang/en_US.lang @@ -311,7 +311,7 @@ skytils.config.miscellaneous.quality_of_life.reset_found_relic_waypoints=Reset F skytils.config.miscellaneous.quality_of_life.potion_duration_notifications=Potion Duration Notifications skytils.config.miscellaneous.quality_of_life.stop_hook_sinking_in_lava=Stop Hook Sinking in Lava skytils.config.miscellaneous.quality_of_life.fishing_hook_age=Fishing Hook Age -skytils.config.miscellaneous.quality_of_life.tropy_fish_tracker=Tropy Fish Tracker +skytils.config.miscellaneous.quality_of_life.trophy_fish_tracker=Tropy Fish Tracker skytils.config.miscellaneous.quality_of_life.show_trophy_fish_totals=Show Trophy Fish Totals skytils.config.miscellaneous.quality_of_life.show_total_trophy_fish=Show Total Trophy Fish skytils.config.pets.quality_of_life.autopet_message_hider=Autopet Message Hider diff --git a/src/main/resources/assets/skytils/lang/zh_CN.lang b/src/main/resources/assets/skytils/lang/zh_CN.lang index e1f2be2bb..8a96c2ae5 100644 --- a/src/main/resources/assets/skytils/lang/zh_CN.lang +++ b/src/main/resources/assets/skytils/lang/zh_CN.lang @@ -311,7 +311,7 @@ skytils.config.miscellaneous.quality_of_life.reset_found_relic_waypoints=重置 skytils.config.miscellaneous.quality_of_life.potion_duration_notifications=药水效果即将消失警告 skytils.config.miscellaneous.quality_of_life.stop_hook_sinking_in_lava=防止鱼钩在岩浆中下沉 skytils.config.miscellaneous.quality_of_life.fishing_hook_age=鱼钩计时器 -skytils.config.miscellaneous.quality_of_life.tropy_fish_tracker=奖杯鱼数据跟踪器 +skytils.config.miscellaneous.quality_of_life.trophy_fish_tracker=奖杯鱼数据跟踪器 skytils.config.miscellaneous.quality_of_life.show_trophy_fish_totals=显示每个奖杯鱼总数 skytils.config.miscellaneous.quality_of_life.show_total_trophy_fish=显示总奖杯鱼数 skytils.config.pets.quality_of_life.autopet_message_hider=自动换宠物消息隐藏 diff --git a/src/main/resources/assets/skytils/lang/zh_TW.lang b/src/main/resources/assets/skytils/lang/zh_TW.lang index 37515d7c1..65e3ac57d 100644 --- a/src/main/resources/assets/skytils/lang/zh_TW.lang +++ b/src/main/resources/assets/skytils/lang/zh_TW.lang @@ -311,7 +311,7 @@ skytils.config.miscellaneous.quality_of_life.reset_found_relic_waypoints=重置 skytils.config.miscellaneous.quality_of_life.potion_duration_notifications=藥水效果即將消失警告 skytils.config.miscellaneous.quality_of_life.stop_hook_sinking_in_lava=防止魚鉤在岩漿中下沉 skytils.config.miscellaneous.quality_of_life.fishing_hook_age=魚鉤計時器 -skytils.config.miscellaneous.quality_of_life.tropy_fish_tracker=獎盃魚數據跟蹤器 +skytils.config.miscellaneous.quality_of_life.trophy_fish_tracker=獎盃魚數據跟蹤器 skytils.config.miscellaneous.quality_of_life.show_trophy_fish_totals=顯示每個獎盃魚總數 skytils.config.miscellaneous.quality_of_life.show_total_trophy_fish=顯示總獎盃魚數 skytils.config.pets.quality_of_life.autopet_message_hider=自動換寵物消息隱藏 From 21b74196342f23feaadfe2651269366108f78b14 Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Fri, 24 May 2024 15:48:58 -0400 Subject: [PATCH 17/25] version: 1.10.0-pre8 version: 1.10.0-pre1 version: 1.10.0-pre2 version: 1.10.0-pre3 version: 1.10.0-pre4 version: 1.10.0-pre5 version: 1.10.0-pre6 version: 1.10.0-pre7 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index c995a8c04..9b92c55de 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,7 +34,7 @@ plugins { signing } -version = "1.9.7" +version = "1.10.0-pre8" group = "gg.skytils" repositories { From 14c7acb802ec7e85d37f50381813981052c58ed1 Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Thu, 13 Jun 2024 13:13:15 -0400 Subject: [PATCH 18/25] fix: add a preinit mixin config because vendor lock in --- build.gradle.kts | 5 ++-- .../mixins/SkytilsMixinInitPlugin.kt | 30 +++++++++++++++++++ .../skytilsmod/mixins/SkytilsMixinPlugin.kt | 6 ++-- src/main/resources/mixins.skytils-init.json | 11 +++++++ src/main/resources/mixins.skytils.json | 1 - 5 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 src/main/kotlin/gg/skytils/skytilsmod/mixins/SkytilsMixinInitPlugin.kt create mode 100644 src/main/resources/mixins.skytils-init.json diff --git a/build.gradle.kts b/build.gradle.kts index 9b92c55de..3dcb502b4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -74,11 +74,12 @@ loom { programArgs("--tweakClass", "gg.skytils.skytilsmod.tweaker.SkytilsTweaker") programArgs("--mixin", "mixins.skytils.json") programArgs("--mixin", "mixins.skytils-events.json") + programArgs("--mixin", "mixins.skytils-init.json") } remove(getByName("server")) } forge { - mixinConfig("mixins.skytils.json", "mixins.skytils-events.json") + mixinConfig("mixins.skytils.json", "mixins.skytils-events.json", "mixins.skytils-init.json") } mixin { defaultRefmapName = "mixins.skytils.refmap.json" @@ -188,7 +189,7 @@ tasks { "FMLCorePlugin" to "gg.skytils.skytilsmod.tweaker.SkytilsLoadingPlugin", "FMLCorePluginContainsFMLMod" to true, "ForceLoadAsMod" to true, - "MixinConfigs" to "mixins.skytils.json,mixins.skytils-events.json", + "MixinConfigs" to "mixins.skytils.json,mixins.skytils-events.json,mixins.skytils-init.json", "ModSide" to "CLIENT", "ModType" to "FML", "TweakClass" to "gg.skytils.skytilsmod.tweaker.SkytilsTweaker", diff --git a/src/main/kotlin/gg/skytils/skytilsmod/mixins/SkytilsMixinInitPlugin.kt b/src/main/kotlin/gg/skytils/skytilsmod/mixins/SkytilsMixinInitPlugin.kt new file mode 100644 index 000000000..c9269ecc7 --- /dev/null +++ b/src/main/kotlin/gg/skytils/skytilsmod/mixins/SkytilsMixinInitPlugin.kt @@ -0,0 +1,30 @@ +/* + * Skytils - Hypixel Skyblock Quality of Life Mod + * Copyright (C) 2020-2024 Skytils + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package gg.skytils.skytilsmod.mixins + +import com.llamalad7.mixinextras.MixinExtrasBootstrap + +class SkytilsMixinInitPlugin : SkytilsMixinPlugin() { + override val mixinPackage = "gg.skytils.skytilsmod.mixins.transformers.init" + + override fun onLoad(mixinPackage: String) { + MixinExtrasBootstrap.init() + super.onLoad(mixinPackage) + } +} \ No newline at end of file diff --git a/src/main/kotlin/gg/skytils/skytilsmod/mixins/SkytilsMixinPlugin.kt b/src/main/kotlin/gg/skytils/skytilsmod/mixins/SkytilsMixinPlugin.kt index 1f86585fe..20299f256 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/mixins/SkytilsMixinPlugin.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/mixins/SkytilsMixinPlugin.kt @@ -18,14 +18,13 @@ package gg.skytils.skytilsmod.mixins -import com.llamalad7.mixinextras.MixinExtrasBootstrap import net.minecraft.launchwrapper.Launch import org.objectweb.asm.tree.ClassNode import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin import org.spongepowered.asm.mixin.extensibility.IMixinInfo -class SkytilsMixinPlugin : IMixinConfigPlugin { - val mixinPackage = "gg.skytils.skytilsmod.mixins.transformers" +open class SkytilsMixinPlugin : IMixinConfigPlugin { + open val mixinPackage = "gg.skytils.skytilsmod.mixins.transformers" val eventsPackage = "gg.skytils.events.mixins" var deobfEnvironment = false @@ -34,7 +33,6 @@ class SkytilsMixinPlugin : IMixinConfigPlugin { if (deobfEnvironment) { println("We are in a deobfuscated environment, loading compatibility mixins.") } - MixinExtrasBootstrap.init() } override fun getRefMapperConfig(): String? = null diff --git a/src/main/resources/mixins.skytils-init.json b/src/main/resources/mixins.skytils-init.json new file mode 100644 index 000000000..894598378 --- /dev/null +++ b/src/main/resources/mixins.skytils-init.json @@ -0,0 +1,11 @@ +{ + "compatibilityLevel": "JAVA_8", + "minVersion": "0.8", + "plugin": "gg.skytils.skytilsmod.mixins.SkytilsMixinInitPlugin", + "package": "gg.skytils.skytilsmod.mixins.transformers.init", + "refmap": "mixins.skytils-init.refmap.json", + "target": "@env(PREINIT)", + "priority": 999, + "mixins": [], + "verbose": true +} \ No newline at end of file diff --git a/src/main/resources/mixins.skytils.json b/src/main/resources/mixins.skytils.json index 30f8fae30..7ea9201b0 100644 --- a/src/main/resources/mixins.skytils.json +++ b/src/main/resources/mixins.skytils.json @@ -97,7 +97,6 @@ ], "verbose": true, "client": [ - "accessors.AccessorPlayerControllerMP", "gui.MixinGuiEditSign", "renderer.MixinModelBiped", "util.MixinMouseHelper" From 3faf305b45b5e98da9e1a6cda0e366d3c9646f8c Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal002@users.noreply.github.com> Date: Thu, 13 Jun 2024 19:15:26 +0200 Subject: [PATCH 19/25] Fixed item stack copy (#484) * fixed too many item copy calls * removed comment * inline item --------- Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../features/impl/mining/MiningFeatures.kt | 23 +++++++++---------- .../mixins/hooks/inventory/SlotHook.kt | 8 +++---- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/mining/MiningFeatures.kt b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/mining/MiningFeatures.kt index aede86fa9..fcdd1d8fb 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/mining/MiningFeatures.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/mining/MiningFeatures.kt @@ -161,22 +161,21 @@ object MiningFeatures { @SubscribeEvent fun onDrawSlot(event: GuiContainerEvent.DrawSlotEvent.Pre) { if (!Utils.inSkyblock || event.container !is ContainerChest) return - if (event.slot.hasStack) { + if (!event.slot.hasStack) return + if (Skytils.config.highlightDisabledHOTMPerks && SBInfo.lastOpenContainerName == "Heart of the Mountain") { + if (ItemUtil.getItemLore(event.slot.stack).any { it == "§c§lDISABLED" }) { + event.slot highlight Color(255, 0, 0) + } + } + if (Skytils.config.highlightCompletedCommissions && SBInfo.lastOpenContainerName.equals("Commissions")) { val item = event.slot.stack - if (Skytils.config.highlightDisabledHOTMPerks && SBInfo.lastOpenContainerName == "Heart of the Mountain") { - if (ItemUtil.getItemLore(item).any { it == "§c§lDISABLED" }) { + if (item.displayName.startsWith("§6Commission #") && item.item == Items.writable_book) { + if (ItemUtil.getItemLore(item).any { + it == "§eClick to claim rewards!" + }) { event.slot highlight Color(255, 0, 0) } } - if (Skytils.config.highlightCompletedCommissions && SBInfo.lastOpenContainerName.equals("Commissions")) { - if (item.displayName.startsWith("§6Commission #") && item.item == Items.writable_book) { - if (ItemUtil.getItemLore(item).any { - it == "§eClick to claim rewards!" - }) { - event.slot highlight Color(255, 0, 0) - } - } - } } } diff --git a/src/main/kotlin/gg/skytils/skytilsmod/mixins/hooks/inventory/SlotHook.kt b/src/main/kotlin/gg/skytils/skytilsmod/mixins/hooks/inventory/SlotHook.kt index b31f725b5..9b8dbcf36 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/mixins/hooks/inventory/SlotHook.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/mixins/hooks/inventory/SlotHook.kt @@ -27,11 +27,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable fun markTerminalItems(slot: Slot, cir: CallbackInfoReturnable) { if (!Utils.inSkyblock) return - val item: ItemStack = (slot.inventory.getStackInSlot(slot.slotIndex) ?: return).copy() - if (!item.isItemEnchanted && (SelectAllColorSolver.shouldClick.contains(slot.slotNumber) || StartsWithSequenceSolver.shouldClick.contains( - slot.slotNumber - )) + val original = slot.inventory.getStackInSlot(slot.slotIndex) ?: return + if (!original.isItemEnchanted && (SelectAllColorSolver.shouldClick.contains(slot.slotNumber) || + StartsWithSequenceSolver.shouldClick.contains(slot.slotNumber)) ) { + val item = original.copy() if (item.tagCompound == null) { item.tagCompound = NBTTagCompound() } From 47a9530068daed027e075977878e6be5dec79433 Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Thu, 13 Jun 2024 13:22:56 -0400 Subject: [PATCH 20/25] Major fixes of zh-tw localization (#482) * Update zh_TW.lang Fixed mistranslations Signed-off-by: wbxshiori * Update zh_TW.lang Signed-off-by: wbxshiori * Update zh_TW.lang Signed-off-by: wbxshiori * Update zh_TW.lang Signed-off-by: wbxshiori * Update zh_TW.lang Signed-off-by: wbxshiori * Update zh_TW.lang Replaced fullwidth parentheses (not displayable by Minecraft) with halfwidth ones and a space. (In previous changes) Fixed a lot of mistranslations, also replaced certain unofficial translations to official, original English names/terms for readability. Signed-off-by: wbxshiori * Update zh_TW.lang Added some parentheses to mark original English names/terms for readability. Other fixes. Signed-off-by: wbxshiori * Update zh_TW.lang Signed-off-by: wbxshiori * Update zh_TW.lang Signed-off-by: wbxshiori * Update zh_TW.lang Signed-off-by: wbxshiori * Update zh_TW.lang Signed-off-by: wbxshiori --------- Signed-off-by: wbxshiori --- .../resources/assets/catlas/lang/zh_TW.lang | 48 +- .../resources/assets/skytils/lang/zh_TW.lang | 718 +++++++++--------- 2 files changed, 383 insertions(+), 383 deletions(-) diff --git a/src/main/resources/assets/catlas/lang/zh_TW.lang b/src/main/resources/assets/catlas/lang/zh_TW.lang index c4d13de41..78bbd7e30 100644 --- a/src/main/resources/assets/catlas/lang/zh_TW.lang +++ b/src/main/resources/assets/catlas/lang/zh_TW.lang @@ -1,43 +1,43 @@ -catlas.config.map.toggle.map_enabled=地圖啓用 +catlas.config.map.toggle.map_enabled=啟用地圖 catlas.config.map.toggle.rotate_map=旋轉地圖 -catlas.config.map.toggle.center_map=居中地圖 +catlas.config.map.toggle.center_map=玩家置中 catlas.config.map.toggle.dynamic_rotate=動態旋轉 -catlas.config.map.toggle.hide_in_boss=Boss戰隱藏 +catlas.config.map.toggle.hide_in_boss=Boss戰時隱藏 catlas.config.map.toggle.show_player_names=顯示玩家名稱 -catlas.config.map.toggle.vanilla_head_marker=原版頭顱標記 -catlas.config.map.size.map_text_scale=地圖文本縮放 +catlas.config.map.toggle.vanilla_head_marker=原版Minecraft地圖標記 +catlas.config.map.size.map_text_scale=地圖文字縮放 catlas.config.map.size.player_heads_scale=玩家頭顱縮放 catlas.config.map.size.player_name_scale=玩家名稱縮放 catlas.config.map.render.map_background_color=地圖背景顏色 catlas.config.map.render.map_border_color=地圖邊框顏色 -catlas.config.map.render.border_thickness=邊框厚度 +catlas.config.map.render.border_thickness=邊框粗細 catlas.config.rooms..room_names=房間名稱 -catlas.config.rooms.text.center_room_names=居中房間名稱 +catlas.config.rooms.text.center_room_names=房間名稱置中 catlas.config.rooms..room_secrets=房間祕密 -catlas.config.rooms..color_text=文本着色 -catlas.config.rooms..room_checkmarks=房間複選標記 -catlas.config.rooms.checkmarks.center_room_checkmarks=居中房間複選標記 +catlas.config.rooms..color_text=文字上色 +catlas.config.rooms..room_checkmarks=房間打勾標記 +catlas.config.rooms.checkmarks.center_room_checkmarks=房間打勾標記置中 catlas.config.colors.doors.blood_door=血之門 catlas.config.colors.doors.entrance_door=入口門 catlas.config.colors.doors.normal_door=普通門 catlas.config.colors.doors.wither_door=凋零之門 -catlas.config.colors.doors.opened_wither_door=已開啓凋零之門 -catlas.config.colors.doors.unopened_door=未開啓門 +catlas.config.colors.doors.opened_wither_door=已開啟的凋零之門 +catlas.config.colors.doors.unopened_door=未開啟的門 catlas.config.colors.rooms.blood_room=血之房間 catlas.config.colors.rooms.entrance_room=入口房間 -catlas.config.colors.rooms.fairy_room=精靈房間 +catlas.config.colors.rooms.fairy_room=小精靈房間 catlas.config.colors.rooms.miniboss_room=小Boss房間 catlas.config.colors.rooms.normal_room=普通房間 -catlas.config.colors.rooms.puzzle_room=拼圖房間 +catlas.config.colors.rooms.puzzle_room=謎題房間 catlas.config.colors.rooms.rare_room=稀有房間 catlas.config.colors.rooms.trap_room=陷阱房間 -catlas.config.colors.rooms.unopened_room=未開啓房間 -catlas.config.other_features.wither_door.box_wither_doors=標框凋零之門 -catlas.config.other_features.wither_door.no_key_color=無鑰匙顏色 -catlas.config.other_features.wither_door.has_key_color=有鑰匙顏色 -catlas.config.other_features.wither_door.door_outline_width=門框輪廓寬度 -catlas.config.other_features.wither_door.door_outline_opacity=門框輪廓不透明度 -catlas.config.other_features.wither_door.door_fill_opacity=門填充不透明度 +catlas.config.colors.rooms.unopened_room=未開啟的房間 +catlas.config.other_features.wither_door.box_wither_doors=標記凋零之門外框 +catlas.config.other_features.wither_door.no_key_color=無鑰匙時顏色 +catlas.config.other_features.wither_door.has_key_color=有鑰匙時顏色 +catlas.config.other_features.wither_door.door_outline_width=門框線寬度 +catlas.config.other_features.wither_door.door_outline_opacity=門框線不透明度 +catlas.config.other_features.wither_door.door_fill_opacity=門填色不透明度 catlas.config.map=地圖 catlas.config.rooms=房間 catlas.config.colors=顏色 @@ -45,8 +45,8 @@ catlas.config.other_features=其他功能 catlas.config.map.toggle=切換 catlas.config.map.size=大小 catlas.config.map.render=渲染 -catlas.config.rooms.text=文本 -catlas.config.rooms.checkmarks=複選標記 +catlas.config.rooms.text=文字 +catlas.config.rooms.checkmarks=打勾標記 catlas.config.colors.doors=門 catlas.config.colors.rooms=房間 -catlas.config.other_features.wither_door=凋零之門 \ No newline at end of file +catlas.config.other_features.wither_door=凋零之門 diff --git a/src/main/resources/assets/skytils/lang/zh_TW.lang b/src/main/resources/assets/skytils/lang/zh_TW.lang index 65e3ac57d..ed7e93dc8 100644 --- a/src/main/resources/assets/skytils/lang/zh_TW.lang +++ b/src/main/resources/assets/skytils/lang/zh_TW.lang @@ -1,221 +1,221 @@ -skytils.config.general.api.fetch_kuudra_prices=獲取最低的 Kuudra 一口價的數據 -skytils.config.general.api.fetch_lowest_bin_prices=獲取最低一口價的數據 +skytils.config.general.api.fetch_kuudra_prices=取得 Kuudra 最低直購價資訊 +skytils.config.general.api.fetch_lowest_bin_prices=取得最低直購價資訊 skytils.config.general.command_aliases.command_alias_mode=指令別名模式 -skytils.config.general.local_api.auto_start_local_api=自動啓動本地API +skytils.config.general.local_api.auto_start_local_api=自動啟動本地API skytils.config.general.local_api.local_api_password=本地API密碼 skytils.config.general.other.join_the_skytils_discord=加入Skytils Discord -skytils.config.general.other.first_launch=首次啓動 -skytils.config.general.other.last_launched_skytils_version=上次啓動的Skytils版本 -skytils.config.general.other.always_sprint_in_skyblock=在空島生存中保持疾跑 -skytils.config.general.other.config_button_on_pause=暫停時顯示配置按鈕 -skytils.config.general.other.reopen_options_menu=重新打開Skytils選項菜單 -skytils.config.general.reparty.override_other_reparty_commands=覆蓋其他模組的重新組隊指令 -skytils.config.general.hypixel.coop_add_confirmation=邀請其他人加入存檔確認 -skytils.config.general.hypixel.guild_leave_confirmation=離開公會確認 -skytils.config.general.hypixel.multiple_party_invites_fix=邀請多人導致幽靈隊伍的修復 +skytils.config.general.other.first_launch=首次啟動 +skytils.config.general.other.last_launched_skytils_version=上次啟動的Skytils版本 +skytils.config.general.other.always_sprint_in_skyblock=在空島生存中維持疾走 +skytils.config.general.other.config_button_on_pause=暫停畫面中顯示設定按鈕 +skytils.config.general.other.reopen_options_menu=重新開啟Skytils選項選單 +skytils.config.general.reparty.override_other_reparty_commands=覆寫其他模組的重新組隊指令 +skytils.config.general.hypixel.coop_add_confirmation=確認邀請其他人加入Coop +skytils.config.general.hypixel.guild_leave_confirmation=確認離開公會 +skytils.config.general.hypixel.multiple_party_invites_fix=修正邀請多人時產生的空白隊伍 skytils.config.general.reparty.autoaccept_reparty=自動接受重新組隊邀請 -skytils.config.general.reparty.autoaccept_reparty_timeout=自動接受重新組隊邀請延時 +skytils.config.general.reparty.autoaccept_reparty_timeout=自動接受重新組隊邀請逾時 skytils.config.general.updates.update_channel=Skytils更新提醒 -skytils.config.dungeons.fixes.inject_fake_dungeon_map=添加假地牢地圖 -skytils.config.dungeons.hud.dungeon_crypts_counter=地牢墳墓計數器 -skytils.config.dungeons.miscellaneous.auto_copy_fails_to_clipboard=自動複製失敗信息到剪貼板 -skytils.config.dungeons.quality_of_life.autoreparty_on_dungeon_ending=地牢結束時自動重新組隊 +skytils.config.dungeons.fixes.inject_fake_dungeon_map=快捷列中插入虛擬的Dungeon地圖 +skytils.config.dungeons.hud.dungeon_crypts_counter=Dungeon Crypt計數器 +skytils.config.dungeons.miscellaneous.auto_copy_fails_to_clipboard=自動複製失敗訊息至剪貼簿 +skytils.config.dungeons.quality_of_life.autoreparty_on_dungeon_ending=Dungeon結束時自動重新組隊 skytils.config.dungeons.miscellaneous.death_counter=死亡計數器 -skytils.config.dungeons.party_finder.party_finder_stats=隊伍尋找加入時查詢數據 -skytils.config.dungeons.miscellaneous.dungeon_chest_profit=地牢寶箱利潤 -skytils.config.dungeons.miscellaneous.dungeon_chest_profit_includes_essence=地牢寶箱利潤將包括精華 -skytils.config.dungeons.miscellaneous.highlight_unopened_croesus_chests=在Croesus高亮未開啓的寶箱 -skytils.config.dungeons.miscellaneous.hide_opened_croesus_chests=在Croesus隱藏已開啓的寶箱 -skytils.config.dungeons.miscellaneous.catlas=地牢地圖 -skytils.config.dungeons.miscellaneous.dungeon_start_confirmation=進入地牢確認 -skytils.config.dungeons.miscellaneous.dungeon_sweat=地牢彩蛋 -skytils.config.dungeons.miscellaneous.dungeon_timer=地牢計時器 +skytils.config.dungeons.party_finder.party_finder_stats=隊伍搜尋器顯示加入者資訊 +skytils.config.dungeons.miscellaneous.dungeon_chest_profit=Dungeon寶箱利潤 +skytils.config.dungeons.miscellaneous.dungeon_chest_profit_includes_essence=Dungeon寶箱利潤包含精華 +skytils.config.dungeons.miscellaneous.highlight_unopened_croesus_chests=在Croesus介面中醒目提示未開啟的寶箱 +skytils.config.dungeons.miscellaneous.hide_opened_croesus_chests=在Croesus介面中隱藏已開啟的寶箱 +skytils.config.dungeons.miscellaneous.catlas=Catlas (Dungeon小地圖) +skytils.config.dungeons.miscellaneous.dungeon_start_confirmation=確認進入Dungeon +skytils.config.dungeons.miscellaneous.dungeon_sweat=Dungeon彩蛋 +skytils.config.dungeons.miscellaneous.dungeon_timer=Dungeon計時器 skytils.config.dungeons.miscellaneous.necron_phase_timer=F7/M7 Boss階段計時器 -skytils.config.dungeons.miscellaneous.red_screen_fix=邊界外紅屏修復 -skytils.config.dungeons.miscellaneous.show_decimal_seconds_on_timers=計時器顯示小數秒數 +skytils.config.dungeons.miscellaneous.red_screen_fix=修正畫面泛紅 +skytils.config.dungeons.miscellaneous.show_decimal_seconds_on_timers=計時器秒數顯示小數 skytils.config.dungeons.miscellaneous.sadan_phase_timer=F6/M6 Boss階段計時器 -skytils.config.dungeons.score_calculation.show_dungeon_score_estimate=顯示地牢分數估算 -skytils.config.dungeons.score_calculation.minimized_dungeon_score_estimate=簡化顯示地牢分數估算 +skytils.config.dungeons.score_calculation.show_dungeon_score_estimate=顯示Dungeon分數估算 +skytils.config.dungeons.score_calculation.minimized_dungeon_score_estimate=簡化顯示Dungeon分數估算 skytils.config.dungeons.score_calculation.score_calculation_party_assist=協助隊伍計算Mimic擊殺分數 -skytils.config.dungeons.score_calculation.receive_score_calculation_party_assist=接收隊員的Mimic擊殺信息協助分數計算 -skytils.config.dungeons.score_calculation.allow_mimic_dead_from_other_mods=允許接收其他模組的Mimic擊殺消息 -skytils.config.dungeons.score_calculation.send_message_on_270_score=達到270分時發送消息 -skytils.config.dungeons.score_calculation.message_for_270_score=270分時的消息 +skytils.config.dungeons.score_calculation.receive_score_calculation_party_assist=接收隊員的Mimic擊殺訊息以計算分數 +skytils.config.dungeons.score_calculation.allow_mimic_dead_from_other_mods=允許接收其他模組的Mimic擊殺訊息 +skytils.config.dungeons.score_calculation.send_message_on_270_score=達到270分時發送訊息 +skytils.config.dungeons.score_calculation.message_for_270_score=270分訊息 skytils.config.dungeons.score_calculation.create_title_on_270_score=達到270分時顯示標題 -skytils.config.dungeons.score_calculation.270_title_message=270分標題消息 -skytils.config.dungeons.score_calculation.send_message_on_300_score=達到300分時發送消息 -skytils.config.dungeons.score_calculation.message_for_300_score=300分時的消息 +skytils.config.dungeons.score_calculation.270_title_message=270分標題訊息 +skytils.config.dungeons.score_calculation.send_message_on_300_score=達到300分時發送訊息 +skytils.config.dungeons.score_calculation.message_for_300_score=300分訊息 skytils.config.dungeons.score_calculation.create_title_on_300_score=達到300分時顯示標題 -skytils.config.dungeons.score_calculation.300_title_message=300分標題消息 -skytils.config.dungeons.quality_of_life.blood_camp_helper=血房擊殺助手 -skytils.config.dungeons.quality_of_life.blood_camp_helper_color=血房擊殺助手的顏色 -skytils.config.dungeons.quality_of_life.box_starred_mobs=標記星標怪物 -skytils.config.dungeons.quality_of_life.box_starred_mobs_color=標記星標怪物的顏色 -skytils.config.dungeons.quality_of_life.box_skeleton_masters=標記骷髏大師 -skytils.config.dungeons.quality_of_life.box_spirit_bear=標記靈魂熊 -skytils.config.dungeons.quality_of_life.box_spirit_bow=標記靈魂弓 -skytils.config.dungeons.quality_of_life.dungeon_chest_reroll_confirmation=地牢寶箱刷新確認 -skytils.config.dungeons.quality_of_life.dungeon_chest_reroll_protection_threshold=地牢寶箱刷新需要的點擊次數 -skytils.config.dungeons.quality_of_life.dungeon_secret_display=地牢房間祕密數量顯示 -skytils.config.dungeons.quality_of_life.ghost_leap_names=鬼魂傳送顯示名字 -skytils.config.dungeons.quality_of_life.hide_archer_bone_passive=隱藏弓箭手被動的骨粉 -skytils.config.dungeons.quality_of_life.hide_damage_in_boss=在Boss階段隱藏傷害文本 -skytils.config.dungeons.quality_of_life.hide_wither_king_dragon_death=隱藏凋靈王的龍的死亡動畫 -skytils.config.dungeons.quality_of_life.hide_fairies=隱藏仙女 -skytils.config.dungeons.quality_of_life.hide_floor_4_crowd_messages=隱藏F4/M4的看臺觀衆消息 -skytils.config.dungeons.quality_of_life.hide_oruo_messages=隱藏問答解密房Oruo的消息 +skytils.config.dungeons.score_calculation.300_title_message=300分標題訊息 +skytils.config.dungeons.quality_of_life.blood_camp_helper=血之房間輔助顯示 +skytils.config.dungeons.quality_of_life.blood_camp_helper_color=血之房間輔助顯示的顏色 +skytils.config.dungeons.quality_of_life.box_starred_mobs=標記星星怪物 +skytils.config.dungeons.quality_of_life.box_starred_mobs_color=標記星星怪物的顏色 +skytils.config.dungeons.quality_of_life.box_skeleton_masters=標記Skeleton Master +skytils.config.dungeons.quality_of_life.box_spirit_bear=標記Spirit Bear +skytils.config.dungeons.quality_of_life.box_spirit_bow=標記Spirit Bow +skytils.config.dungeons.quality_of_life.dungeon_chest_reroll_confirmation=Dungeon寶箱重骰確認 +skytils.config.dungeons.quality_of_life.dungeon_chest_reroll_protection_threshold=Dungeon寶箱重骰需要的點選次數 +skytils.config.dungeons.quality_of_life.dungeon_secret_display=顯示Dungeon房間祕密數量 +skytils.config.dungeons.quality_of_life.ghost_leap_names=鬼魂傳送選單顯示名字 +skytils.config.dungeons.quality_of_life.hide_archer_bone_passive=隱藏Archer被動技能的骨粉 +skytils.config.dungeons.quality_of_life.hide_damage_in_boss=在Boss階段時隱藏傷害文字 +skytils.config.dungeons.quality_of_life.hide_wither_king_dragon_death=隱藏Wither King的龍死亡動畫 +skytils.config.dungeons.quality_of_life.hide_fairies=隱藏Fairy +skytils.config.dungeons.quality_of_life.hide_floor_4_crowd_messages=隱藏F4/M4的觀眾台詞 +skytils.config.dungeons.quality_of_life.hide_oruo_messages=隱藏Oruo的台詞 skytils.config.dungeons.quality_of_life.hide_spirit_animal_nametags=隱藏靈魂動物名稱 -skytils.config.dungeons.quality_of_life.hide_terminal_completion_titles=隱藏終端完成的標題 -skytils.config.dungeons.quality_of_life.hide_wither_miner_nametags=隱藏凋靈礦工頭頂名稱 -skytils.config.dungeons.quality_of_life.hide_terracotta_nametags=隱藏陶土人名稱 -skytils.config.dungeons.quality_of_life.hide_nonstarred_mobs_nametags=隱藏非星標怪物頭頂名稱 -skytils.config.dungeons.quality_of_life.larger_bat_models=更大的蝙蝠模型 -skytils.config.dungeons.quality_of_life.change_hurt_color_on_the_wither_kings_dragons=改變凋靈王的龍受傷顏色 -skytils.config.dungeons.quality_of_life.retexture_wither_kings_dragons=重新給凋靈王的龍上材質 +skytils.config.dungeons.quality_of_life.hide_terminal_completion_titles=隱藏終端機完成的浮動文字 +skytils.config.dungeons.quality_of_life.hide_wither_miner_nametags=隱藏Wither Miner頭頂名稱 +skytils.config.dungeons.quality_of_life.hide_terracotta_nametags=隱藏Terracotta名稱 +skytils.config.dungeons.quality_of_life.hide_nonstarred_mobs_nametags=隱藏非星星怪物頭頂名稱 +skytils.config.dungeons.quality_of_life.larger_bat_models=蝙蝠模型加大 +skytils.config.dungeons.quality_of_life.change_hurt_color_on_the_wither_kings_dragons=改變Wither King的龍受傷顏色 +skytils.config.dungeons.quality_of_life.retexture_wither_kings_dragons=替Wither King的龍重新上色 skytils.config.dungeons.quality_of_life.revive_stone_names=復活石顯示名字 -skytils.config.dungeons.quality_of_life.say_blaze_done=發送烈焰人解密完成消息 +skytils.config.dungeons.quality_of_life.say_blaze_done=發送烈焰使者解謎完成訊息 skytils.config.dungeons.quality_of_life.show_bat_hitboxes=顯示蝙蝠碰撞箱 -skytils.config.miscellaneous.brewing.color_brewing_stands=根據釀造臺狀態改變顏色 -skytils.config.miscellaneous.items.show_dungeon_floor_as_stack_size=在地牢樓層選項上顯示層數 +skytils.config.miscellaneous.brewing.color_brewing_stands=根據釀造台狀態改變顏色 +skytils.config.miscellaneous.items.show_dungeon_floor_as_stack_size=在Dungeon樓層選項上顯示層數 skytils.config.miscellaneous.items.held_item_scale=手持物品縮放 skytils.config.dungeons.quality_of_life.show_giant_hp=顯示巨人生命值 skytils.config.dungeons.quality_of_life.show_giant_hp_at_feet=在巨人腳下顯示生命值 -skytils.config.dungeons.quality_of_life.show_guardian_respawn_timer=顯示守衛者重生計時器 -skytils.config.dungeons.quality_of_life.show_wither_king_statue_box=顯示凋靈王雕像邊界框 -skytils.config.dungeons.quality_of_life.show_sadans_interest=顯示Sadan的陶土人階段倒計時 -skytils.config.dungeons.quality_of_life.show_terracotta_respawn_time=顯示陶土人重生時間 +skytils.config.dungeons.quality_of_life.show_guardian_respawn_timer=顯示深海守衛重生計時器 +skytils.config.dungeons.quality_of_life.show_wither_king_statue_box=顯示Wither King雕像邊界框 +skytils.config.dungeons.quality_of_life.show_sadans_interest=顯示Sadan的Terracota階段倒數計時 +skytils.config.dungeons.quality_of_life.show_terracotta_respawn_time=顯示Terracota重生時間 skytils.config.dungeons.quality_of_life.show_necrons_hp=顯示F7/M7 Boss的生命值 -skytils.config.dungeons.quality_of_life.show_wither_kings_dragons_color_as_text=凋靈王的龍的顏色顯示爲文本 -skytils.config.dungeons.quality_of_life.show_wither_kings_dragons_hp=顯示凋靈王的龍的生命值 -skytils.config.dungeons.quality_of_life.show_wither_kings_dragons_spawn_timer=顯示凋靈王的龍的生成倒計時 -skytils.config.dungeons.quality_of_life.spirit_bear_timer=靈魂熊生成倒計時 -skytils.config.dungeons.quality_of_life.spirit_leap_names=傳送珍珠傳送時顯示名字 -skytils.config.dungeons.quality_of_life.highlight_door_opener=高亮門的開啓者 -skytils.config.dungeons.quality_of_life.spirit_leap_highlights=傳送珍珠傳送高亮 -skytils.config.dungeons.quality_of_life.spirit_pet_warning=靈魂寵物警告 -skytils.config.dungeons.quality_of_life.wither_king_dragon_dimensional_slash_alert=站在凋靈王的閃電技能上的警告 -skytils.config.dungeons.quality_of_life.wither_king_dragon_spawn_alert=凋靈王的龍的生成警告 -skytils.config.dungeons.solvers.blaze_solver=烈焰人射箭解密房的解密器 -skytils.config.dungeons.solvers.show_next_blaze=顯示下一個烈焰人 -skytils.config.dungeons.solvers.line_to_next_blaze=到下一個烈焰人的線條指引 -skytils.config.dungeons.solvers.lowest_blaze_color=最低血量烈焰人的顏色 -skytils.config.dungeons.solvers.highest_blaze_color=最高血量烈焰人的顏色 -skytils.config.dungeons.solvers.next_blaze_color=下一個烈焰人的顏色 -skytils.config.dungeons.solvers.line_to_next_blaze_color=到下一個烈焰人的線條指引的顏色 -skytils.config.dungeons.solvers.boulder_solver=推箱子解密房的解密器 -skytils.config.dungeons.solvers.creeper_beams_solver=爬行者激光連線解密房的解密器 -skytils.config.dungeons.solvers.ice_fill_solver=冰塊填充解密房的解密器 -skytils.config.dungeons.solvers.ice_path_solver=冰上推蠹蟲解密房的解密器 -skytils.config.dungeons.solvers.teleport_maze_solver=3x3傳送迷宮解密房的解密器 -skytils.config.dungeons.solvers.teleport_maze_solver_color=傳送迷宮解密器顏色 -skytils.config.dungeons.solvers.three_weirdos_solver=三箱推理解密房的解密器 -skytils.config.dungeons.solvers.tic_tac_toe_solver=井字棋遊戲解密房的解密器 -skytils.config.dungeons.solvers.tic_tac_toe_solver_color=井字棋遊戲解密器的顏色 -skytils.config.dungeons.solvers.trivia_solver=問答解密房的解密器 -skytils.config.dungeons.solvers.water_board_solver=流水解密房的解密器 +skytils.config.dungeons.quality_of_life.show_wither_kings_dragons_color_as_text=以文字類示Wither King的龍顏色 +skytils.config.dungeons.quality_of_life.show_wither_kings_dragons_hp=顯示Wither King的龍生命值 +skytils.config.dungeons.quality_of_life.show_wither_kings_dragons_spawn_timer=顯示Wither King的龍的生成倒數計時 +skytils.config.dungeons.quality_of_life.spirit_bear_timer=Spirit Bear生成倒數計時 +skytils.config.dungeons.quality_of_life.spirit_leap_names=Spirit Leap傳送時顯示名字 +skytils.config.dungeons.quality_of_life.highlight_door_opener=醒目顯示開門者 +skytils.config.dungeons.quality_of_life.spirit_leap_highlights=醒目顯示Spirit Leap介面玩家資訊 +skytils.config.dungeons.quality_of_life.spirit_pet_warning=Spirit寵物警告 +skytils.config.dungeons.quality_of_life.wither_king_dragon_dimensional_slash_alert=Wither King的Dimensional Slash範圍警告 +skytils.config.dungeons.quality_of_life.wither_king_dragon_spawn_alert=Wither King的龍生成警告 +skytils.config.dungeons.solvers.blaze_solver=烈焰使者謎題 (Higher or Lower) 解答器 +skytils.config.dungeons.solvers.show_next_blaze=顯示下一個烈焰使者 +skytils.config.dungeons.solvers.line_to_next_blaze=以直線連接下一個烈焰使者 +skytils.config.dungeons.solvers.lowest_blaze_color=最低血量烈焰使者的顏色 +skytils.config.dungeons.solvers.highest_blaze_color=最高血量烈焰使者的顏色 +skytils.config.dungeons.solvers.next_blaze_color=下一個烈焰使者的顏色 +skytils.config.dungeons.solvers.line_to_next_blaze_color=到下一個烈焰使者的連接線顏色 +skytils.config.dungeons.solvers.boulder_solver=推箱子 (Boudler) 解答器 +skytils.config.dungeons.solvers.creeper_beams_solver=苦力怕光線 (Creeper Beams) 解答器 +skytils.config.dungeons.solvers.ice_fill_solver=冰塊一筆劃 (Ice Fill) 解答器 +skytils.config.dungeons.solvers.ice_path_solver=滑冰蠹魚 (Ice Path) 解答器 +skytils.config.dungeons.solvers.teleport_maze_solver=3x3傳送迷宮 (Teleport Maze) 解答器 +skytils.config.dungeons.solvers.teleport_maze_solver_color=傳送迷宮解答器顏色 +skytils.config.dungeons.solvers.three_weirdos_solver=三怪咖 (Three Weirdos) 推理解答器 +skytils.config.dungeons.solvers.tic_tac_toe_solver=井字棋 (Tic Tac Toe) 解答器 +skytils.config.dungeons.solvers.tic_tac_toe_solver_color=井字棋解答器的顏色 +skytils.config.dungeons.solvers.trivia_solver=機智問答 (Trivia) 解答器 +skytils.config.dungeons.solvers.water_board_solver=水流圖版 (Water Board) 解答器 skytils.config.dungeons.solvers.find_correct_livid=尋找正確的Livid skytils.config.dungeons.solvers.type_of_livid_finder=Livid標記的類型 -skytils.config.dungeons.tank_helper_tools.boxed_tanks=標記坦克 -skytils.config.dungeons.tank_helper_tools.boxed_tank_color=標記坦克的顏色 +skytils.config.dungeons.tank_helper_tools.boxed_tanks=標記Tank +skytils.config.dungeons.tank_helper_tools.boxed_tank_color=標記Tank的顏色 skytils.config.dungeons.tank_helper_tools.box_protected_teammates=標記被保護的隊友 skytils.config.dungeons.tank_helper_tools.protected_teammate_box_color=被保護的隊友框顏色 -skytils.config.dungeons.tank_helper_tools.tank_protection_range_display=坦克保護範圍顯示 -skytils.config.dungeons.tank_helper_tools.tank_range_wall=坦克保護範圍顯示的圓柱形邊框 -skytils.config.dungeons.tank_helper_tools.tank_range_wall_color=坦克保護範圍顯示的圓柱形邊框顏色 -skytils.config.dungeons.terminal_solvers.block_incorrect_terminal_clicks=阻止終端內不正確的點擊 -skytils.config.dungeons.terminal_solvers.middle_click_on_terminals=終端左鍵點擊替換爲中鍵 -skytils.config.dungeons.terminal_solvers.change_all_to_same_color_solver=變同色終端的解密器 -skytils.config.dungeons.terminal_solvers.change_all_to_same_color_solver_mode=變同色解密器模式 -skytils.config.dungeons.terminal_solvers.click_in_order_solver=按順序點擊終端的解密器 -skytils.config.dungeons.terminal_solvers.click_in_order_first_color=按順序點擊第一顏色 -skytils.config.dungeons.terminal_solvers.click_in_order_second_color=按順序點擊第二顏色 -skytils.config.dungeons.terminal_solvers.click_in_order_third_color=按順序點擊第三顏色 -skytils.config.dungeons.terminal_solvers.select_all_colors_solver=選擇指定顏色的全部物品終端的解密器 -skytils.config.dungeons.terminal_solvers.starts_with_sequence_solver=選擇某字母開頭的全部物品終端的解密器 -skytils.config.dungeons.terminal_solvers.item_frame_alignment_solver=物品展示框轉箭設備的解密器 -skytils.config.dungeons.terminal_solvers.predict_clicks_for_alignment_solver=預測轉箭設備的點擊次數 -skytils.config.dungeons.terminal_solvers.shoot_the_target_solver=射擊指定目標設備的解密器 -skytils.config.dungeons.terminal_solvers.simon_says_solver=按順序點按鈕設備(Simon Says)的解密器 -skytils.config.dungeons.terminal_solvers.predict_clicks_for_simon_says_solver=預測按順序點按鈕設備(Simon Says)的點擊 -skytils.config.events.mayor_jerry.display_jerry_perks=顯示Jerry特權 -skytils.config.events.mayor_jerry.hidden_jerry_alert=特殊Jerry的警報 -skytils.config.events.mayor_jerry.hidden_jerry_timer=特殊Jerry的計時器 -skytils.config.events.mayor_jerry.track_mayor_jerry_items=跟蹤Jerry市長物品 -skytils.config.events.mythological.show_griffin_burrows=顯示獅鷲巢穴 +skytils.config.dungeons.tank_helper_tools.tank_protection_range_display=顯示Tank保護範圍 +skytils.config.dungeons.tank_helper_tools.tank_range_wall=以圓柱形邊框顯示的Tank保護範圍 +skytils.config.dungeons.tank_helper_tools.tank_range_wall_color=Tank保護範圍圓柱形邊框顏色 +skytils.config.dungeons.terminal_solvers.block_incorrect_terminal_clicks=阻止終端機內的錯誤點選 +skytils.config.dungeons.terminal_solvers.middle_click_on_terminals=終端機左鍵替換為中鍵 +skytils.config.dungeons.terminal_solvers.change_all_to_same_color_solver=「九宮格同色」終端機的解答器 +skytils.config.dungeons.terminal_solvers.change_all_to_same_color_solver_mode=「九宮格同色」解答器模式 +skytils.config.dungeons.terminal_solvers.click_in_order_solver=「14格依序點選」終端機的解答器 +skytils.config.dungeons.terminal_solvers.click_in_order_first_color=「14格依序點選」第一顏色 +skytils.config.dungeons.terminal_solvers.click_in_order_second_color=「14格依序點選」第二顏色 +skytils.config.dungeons.terminal_solvers.click_in_order_third_color=「14格依序點選」第三顏色 +skytils.config.dungeons.terminal_solvers.select_all_colors_solver=「選擇特定顏色」終端機的解答器 +skytils.config.dungeons.terminal_solvers.starts_with_sequence_solver=「選擇特定字母開頭」終端機的解答器 +skytils.config.dungeons.terminal_solvers.item_frame_alignment_solver=「旋轉箭矢」裝置的解答器 +skytils.config.dungeons.terminal_solvers.predict_clicks_for_alignment_solver=預測「旋轉箭矢」的點選次數 +skytils.config.dungeons.terminal_solvers.shoot_the_target_solver=「射靶」裝置的解答器 +skytils.config.dungeons.terminal_solvers.simon_says_solver=「依序按鈕」 (Simon Says) 裝置的解答器 +skytils.config.dungeons.terminal_solvers.predict_clicks_for_simon_says_solver=判斷「依序按鈕」的下一個時納入隊友動作 +skytils.config.events.mayor_jerry.display_jerry_perks=顯示Jerry的Perk +skytils.config.events.mayor_jerry.hidden_jerry_alert=警示特殊Jerry現身 +skytils.config.events.mayor_jerry.hidden_jerry_timer=特殊Jerry計時器 +skytils.config.events.mayor_jerry.track_mayor_jerry_items=追蹤Jerry市長物品掉落紀錄 +skytils.config.events.mythological.show_griffin_burrows=顯示獅鷲巢穴 (Griffin Burrow) skytils.config.events.mythological.emptystart_burrow_color=空白/起始巢穴顏色 skytils.config.events.mythological.mob_burrow_color=怪物巢穴顏色 skytils.config.events.mythological.treasure_burrow_color=寶藏巢穴顏色 skytils.config.events.mythological.broadcast_rare_drop_notifications=稀有掉落通知 skytils.config.events.mythological.display_gaia_construct_hits=顯示Gaia Construct的護盾擊打次數 -skytils.config.events.mythological.track_mythological_creatures=跟蹤神話生物數據 -skytils.config.events.spooky.trick_or_treat_chest_alert=不給糖就搗蛋寶箱警報 -skytils.config.events.technoblade.show_shiny_orb_waypoints=顯示閃耀寶珠路徑點 -skytils.config.events.technoblade.show_shiny_pig_locations=顯示閃耀豬位置 +skytils.config.events.mythological.track_mythological_creatures=追蹤神話生物統計 +skytils.config.events.spooky.trick_or_treat_chest_alert=Trick or Treat寶箱警報 +skytils.config.events.technoblade.show_shiny_orb_waypoints=顯示Shiny Orb路徑點 +skytils.config.events.technoblade.show_shiny_pig_locations=顯示Shiny Pig位置 skytils.config.farming.garden.plot_cleanup_helper=農場清理助手 -skytils.config.farming.garden.show_sams_scythe_blocks=Sam的鐮刀破壞方塊預覽 -skytils.config.farming.garden.color_of_sams_scythe_marked_blocks=Sam的鐮刀破壞方塊預覽的顏色 +skytils.config.farming.garden.show_sams_scythe_blocks=預覽Sam的鐮刀破壞方塊範圍 +skytils.config.farming.garden.color_of_sams_scythe_marked_blocks=Sam的鐮刀破壞方塊的顏色 skytils.config.farming.garden.visitor_offer_helper=訪客需求助手 skytils.config.farming.garden.visitor_notifications=訪客通知 -skytils.config.farming.quality_of_life.hide_farming_rng_titles=隱藏農業RNG標題 -skytils.config.farming.solvers.hungry_hiker_solver=Hungry Hiker解密器 -skytils.config.farming.solvers.treasure_hunter_solver=Treasure Hunter解密器 -skytils.config.farming.quality_of_life.click_to_accept_trapper_task=點擊接受Trapper的任務 -skytils.config.farming.quality_of_life.trapper_cooldown_alarm=Trapper的冷卻警報 +skytils.config.farming.quality_of_life.hide_farming_rng_titles=隱藏農業RNG標題文字 +skytils.config.farming.solvers.hungry_hiker_solver=Hungry Hiker解答器 +skytils.config.farming.solvers.treasure_hunter_solver=Treasure Hunter解答器 +skytils.config.farming.quality_of_life.click_to_accept_trapper_task=點選任意處接受Trapper的任務 +skytils.config.farming.quality_of_life.trapper_cooldown_alarm=Trapper冷卻警示 skytils.config.farming.quality_of_life.talbots_theodolite_helper=Talbots Theodolite定位助手 -skytils.config.kuudra.performance.hide_nonnametag_armor_stands_on_kuudra=在Kuudra內隱藏無名稱的盔甲架 +skytils.config.kuudra.performance.hide_nonnametag_armor_stands_on_kuudra=在Kuudra內隱藏無名稱的盔甲座 skytils.config.kuudra.price_checking.kuudra_chest_profit=Kuudra寶箱利潤 -skytils.config.kuudra.price_checking.kuudra_chest_profit_includes_essence=Kuudra寶箱利潤將包括精華 -skytils.config.kuudra.price_checking.kuudra_chest_profit_subtracts_key=Kuudra寶箱利潤將扣除鑰匙 -skytils.config.kuudra.price_checking.show_kuudra_lowest_bin_price=顯示Kuudra最低一口價 -skytils.config.mining.quality_of_life.dark_mode_mist=深色模式的Mist區域 -skytils.config.mining.quality_of_life.highlight_completed_commissions=高亮已完成的委託 -skytils.config.mining.quality_of_life.highlight_disabled_hotm_perks=高亮已禁用的HOTM特權 -skytils.config.mining.quality_of_life.more_visible_ghosts=更清晰可見的幽靈 -skytils.config.mining.quality_of_life.powder_ghast_ping=粉塵惡魂警報 -skytils.config.mining.quality_of_life.raffle_warning=抽獎事件警告 -skytils.config.mining.quality_of_life.raffle_waypoint=抽獎事件路徑點 -skytils.config.mining.quality_of_life.recolor_carpets=給Dwarven Mines的地毯重新上色 -skytils.config.mining.quality_of_life.skymall_reminder=HOTM的Skymall特權刷新提醒 -skytils.config.mining.solvers.fetchur_solver=Fetchur的解密器 -skytils.config.mining.solvers.puzzler_solver=Puzzler的解密器 -skytils.config.mining.crystal_hollows.crystal_hollows_death_waypoints=水晶礦洞死亡路徑點 -skytils.config.mining.crystal_hollows.crystal_hollows_map=水晶礦洞地圖 -skytils.config.mining.crystal_hollows.crystal_hollows_map_special_places=水晶礦洞地圖標記特殊地點 -skytils.config.mining.crystal_hollows.crystal_hollows_waypoints=水晶礦洞路徑點 +skytils.config.kuudra.price_checking.kuudra_chest_profit_includes_essence=Kuudra寶箱利潤包含精華 +skytils.config.kuudra.price_checking.kuudra_chest_profit_subtracts_key=Kuudra寶箱利潤扣除鑰匙 +skytils.config.kuudra.price_checking.show_kuudra_lowest_bin_price=顯示Kuudra最低直購價 +skytils.config.mining.quality_of_life.dark_mode_mist=Mist區域深色模式 +skytils.config.mining.quality_of_life.highlight_completed_commissions=醒目顯示已完成的委託 +skytils.config.mining.quality_of_life.highlight_disabled_hotm_perks=醒目顯示已停用的HOTM天賦 +skytils.config.mining.quality_of_life.more_visible_ghosts=使Ghost更更清晰可見 +skytils.config.mining.quality_of_life.powder_ghast_ping=Powder Ghast警示 +skytils.config.mining.quality_of_life.raffle_warning=Raffle活動警示 +skytils.config.mining.quality_of_life.raffle_waypoint=Raffle活動路徑點 +skytils.config.mining.quality_of_life.recolor_carpets=替Dwarven Mines的地毯重新上色 +skytils.config.mining.quality_of_life.skymall_reminder=Skymall加成更新提醒 +skytils.config.mining.solvers.fetchur_solver=Fetchur的解答器 +skytils.config.mining.solvers.puzzler_solver=Puzzler的解答器 +skytils.config.mining.crystal_hollows.crystal_hollows_death_waypoints=Crystal Hollows死亡路徑點 +skytils.config.mining.crystal_hollows.crystal_hollows_map=Crystal Hollows地圖 +skytils.config.mining.crystal_hollows.crystal_hollows_map_special_places=Crystal Hollows地圖標記特殊地點 +skytils.config.mining.crystal_hollows.crystal_hollows_waypoints=Crystal Hollows路徑點 skytils.config.mining.crystal_hollows.king_yolkar_waypoint=哥布林王路徑點 -skytils.config.mining.crystal_hollows.crystal_hollows_chat_coordinates_grabber=水晶礦洞聊天座標抓取器 -skytils.config.mining.crystal_hollows.crystal_hollows_treasure_helper=水晶礦洞寶藏寶箱助手 -skytils.config.miscellaneous.chat_tabs.chat_tabs=聊天頻道 -skytils.config.miscellaneous.chat_tabs.prefill_chat_commands=預填充聊天指令 +skytils.config.mining.crystal_hollows.crystal_hollows_chat_coordinates_grabber=Crystal Hollows聊天座標擷取器 +skytils.config.mining.crystal_hollows.crystal_hollows_treasure_helper=Crystal Hollows寶箱助手 +skytils.config.miscellaneous.chat_tabs.chat_tabs=聊天頻道頁籤 +skytils.config.miscellaneous.chat_tabs.prefill_chat_commands=預先填入聊天指令 skytils.config.miscellaneous.chat_tabs.auto_switch_chat_channel=自動切換聊天頻道 -skytils.config.miscellaneous.chat_tabs.copy_chat_messages=複製聊天消息 -skytils.config.miscellaneous.fixes.boss_bar_fix=Boss血條修復 -skytils.config.miscellaneous.fixes.fix_falling_sand_rendering=修復下落沙渲染 -skytils.config.miscellaneous.fixes.fix_world_time=修復世界時間 +skytils.config.miscellaneous.chat_tabs.copy_chat_messages=複製聊天訊息 +skytils.config.miscellaneous.fixes.boss_bar_fix=修正Boss血條 +skytils.config.miscellaneous.fixes.fix_falling_sand_rendering=修正掉落中的沙子渲染 +skytils.config.miscellaneous.fixes.fix_world_time=修正世界時間 skytils.config.miscellaneous.fixes.prevent_log_spam=防止垃圾日誌 -skytils.config.miscellaneous.fixes.twitch_fix=Twitch修復 +skytils.config.miscellaneous.fixes.twitch_fix=Twitch修正 skytils.config.miscellaneous.items.price_paid=記錄支付的價格 -skytils.config.miscellaneous.items.disable_block_animation=禁用格擋動畫 +skytils.config.miscellaneous.items.disable_block_animation=停用格擋動畫 skytils.config.miscellaneous.items.dropped_item_size=掉落物品大小 -skytils.config.miscellaneous.items.hide_implosion_particles=隱藏爆炸粒子 +skytils.config.miscellaneous.items.hide_implosion_particles=隱藏Implosion技能粒子 skytils.config.miscellaneous.items.hide_midas_staff_gold=隱藏Midas Staff的黃金方塊 -skytils.config.miscellaneous.items.highlight_filled_bazaar_orders=高亮已填滿的集市訂單 -skytils.config.miscellaneous.items.item_cooldown_display=物品冷卻顯示 +skytils.config.miscellaneous.items.highlight_filled_bazaar_orders=醒目顯示已到貨的集市訂單 +skytils.config.miscellaneous.items.item_cooldown_display=顯示物品冷卻 skytils.config.miscellaneous.items.item_stars_display=物品星星顯示模式 -skytils.config.miscellaneous.items.show_item_quality=顯示物品質量 -skytils.config.miscellaneous.items.head_display_size=揹包內頭顱顯示大小 +skytils.config.miscellaneous.items.show_item_quality=顯示物品之品質 +skytils.config.miscellaneous.items.head_display_size=物品欄內頭顱顯示大小 skytils.config.miscellaneous.items.prevent_placing_weapons=防止放置武器 -skytils.config.miscellaneous.items.wither_shield_cooldown_tracker=Wither Shield冷卻跟蹤器 -skytils.config.miscellaneous.items.wither_shield_has_wither_impact=改爲Wither Impact的護盾冷卻時長 -skytils.config.miscellaneous.items.show_enchanted_book_abbreviation=顯示附魔書名字縮寫 -skytils.config.miscellaneous.items.show_attribute_shard_abbreviation=顯示屬性碎片名字縮寫 -skytils.config.miscellaneous.items.show_attribute_shard_level=顯示屬性碎片等級 +skytils.config.miscellaneous.items.wither_shield_cooldown_tracker=Wither Shield冷卻追蹤 +skytils.config.miscellaneous.items.wither_shield_has_wither_impact=改以Wither Impact的冷卻時間顯示Wither Shield +skytils.config.miscellaneous.items.show_enchanted_book_abbreviation=顯示附魔書名稱縮寫 +skytils.config.miscellaneous.items.show_attribute_shard_abbreviation=顯示Attribute Shard名稱縮寫 +skytils.config.miscellaneous.items.show_attribute_shard_level=顯示Attribute Shard等級 skytils.config.miscellaneous.items.show_enchanted_book_tier=顯示附魔書等級 skytils.config.miscellaneous.items.combine_helper=附魔書合併助手 skytils.config.miscellaneous.items.show_etherwarp_teleport_position=顯示Etherwarp傳送位置 skytils.config.miscellaneous.items.etherwarp_teleport_position_color=Etherwarp傳送位置顏色 skytils.config.miscellaneous.items.show_gemstones=顯示寶石 -skytils.config.miscellaneous.items.show_head_floor_number=顯示金頭/鑽頭所屬地牢層數 +skytils.config.miscellaneous.items.show_head_floor_number=顯示Golden Hean與Diamond Head所屬Dungeon層數 skytils.config.miscellaneous.items.show_item_origin=顯示物品來源 skytils.config.miscellaneous.items.show_new_year_cake_year=顯示新年蛋糕年份 skytils.config.miscellaneous.items.show_npc_sell_price=顯示NPC出售價格 @@ -223,203 +223,203 @@ skytils.config.miscellaneous.items.show_potion_tier=顯示藥水等級 skytils.config.miscellaneous.items.show_pet_candies=顯示寵物使用糖果數 skytils.config.miscellaneous.items.show_item_star_count=顯示物品星星數量 skytils.config.miscellaneous.items.stacking_enchant_progress_display=成長型附魔進度顯示 -skytils.config.miscellaneous.items.radioactive_bonus=狼蛛頭技能的加成顯示 +skytils.config.miscellaneous.items.radioactive_bonus=顯示Radioactive技能加成 skytils.config.miscellaneous.item_rarity.show_item_rarity=顯示物品稀有度 -skytils.config.miscellaneous.item_rarity.item_rarity_shape=物品稀有度顯示形狀 +skytils.config.miscellaneous.item_rarity.item_rarity_shape=以形狀顯示物品稀有度 skytils.config.miscellaneous.item_rarity.show_pet_rarity=顯示寵物稀有度 skytils.config.miscellaneous.item_rarity.item_rarity_transparency=物品稀有度透明度 -skytils.config.miscellaneous.minions.only_collect_enchanted_items=僅收集僕從附魔物品 -skytils.config.miscellaneous.minions.show_minion_tier=顯示僕從等級 -skytils.config.miscellaneous.quality_of_life.always_show_item_name_highlight=總是顯示物品名稱高亮 +skytils.config.miscellaneous.minions.only_collect_enchanted_items=收取Minion產品時僅收取附魔物品 +skytils.config.miscellaneous.minions.show_minion_tier=顯示Minion等級 +skytils.config.miscellaneous.quality_of_life.always_show_item_name_highlight=持續顯示手持物品名稱 skytils.config.miscellaneous.quality_of_life.low_health_vignette_threshold=低生命值警告的血線 skytils.config.miscellaneous.quality_of_life.low_health_vignette_color=低生命值警告顏色 -skytils.config.miscellaneous.other.hide_tooltips_while_on_storage=揹包界面隱藏物品描述 -skytils.config.miscellaneous.other.copy_deaths_to_clipboard=複製死亡消息到剪貼板 -skytils.config.miscellaneous.other.auto_copy_rng_drops_to_clipboard=自動複製RNG掉落消息到剪貼板 -skytils.config.miscellaneous.other.also_copy_very_rare_drops_to_clipboard=複製極其稀有掉落消息到剪貼板 -skytils.config.miscellaneous.other.dupe_tracker=違禁的複製的物品追蹤器 -skytils.config.miscellaneous.other.dupe_tracker_overlay_color=違禁的複製的物品追蹤器覆蓋顏色 -skytils.config.miscellaneous.other.endstone_protector_spawn_timer=末地石守護者生成計時器 -skytils.config.miscellaneous.other.players_in_range_display=範圍內玩家數量顯示 -skytils.config.miscellaneous.other.placed_summoning_eye_display=已放置召喚之眼顯示 -skytils.config.miscellaneous.other.ping_display=Ping顯示 -skytils.config.miscellaneous.other.random_stuff=隨機東西 +skytils.config.miscellaneous.other.hide_tooltips_while_on_storage=隱藏背包選單中的物品描述 +skytils.config.miscellaneous.other.copy_deaths_to_clipboard=複製死亡訊息到剪貼簿 +skytils.config.miscellaneous.other.auto_copy_rng_drops_to_clipboard=自動複製RNG掉落訊息到剪貼簿 +skytils.config.miscellaneous.other.also_copy_very_rare_drops_to_clipboard=自動複製VERY RARE掉落訊息到剪貼簿 +skytils.config.miscellaneous.other.dupe_tracker=追蹤違規複製的物品 +skytils.config.miscellaneous.other.dupe_tracker_overlay_color=違規複製物品的覆蓋顏色 +skytils.config.miscellaneous.other.endstone_protector_spawn_timer=Endstone Protector生成計時器 +skytils.config.miscellaneous.other.players_in_range_display=顯示範圍內玩家數量 +skytils.config.miscellaneous.other.placed_summoning_eye_display=顯示已放置Summonine Eye +skytils.config.miscellaneous.other.ping_display=顯示Ping +skytils.config.miscellaneous.other.random_stuff=隨機功能 skytils.config.miscellaneous.other.scam_check=交易詐騙檢查 skytils.config.miscellaneous.other.show_bestiary_level=顯示圖鑑等級 -skytils.config.miscellaneous.other.show_selected_arrow=顯示使用的箭 +skytils.config.miscellaneous.other.show_selected_arrow=顯示選擇中箭矢 skytils.config.miscellaneous.other.show_world_age=顯示世界年齡 -skytils.config.miscellaneous.other.transparent_armor_layer=透明盔甲層 -skytils.config.miscellaneous.other.head_layer_transparency=頭顱層透明度 -skytils.config.miscellaneous.other.fix_summon_skin=修復召喚物皮膚 -skytils.config.miscellaneous.other.use_player_skin=使用玩家皮膚 -skytils.config.miscellaneous.quality_of_life.custom_auction_price_input=自定義拍賣價格輸入界面 -skytils.config.miscellaneous.quality_of_life.better_stash=更好的儲物箱 -skytils.config.miscellaneous.quality_of_life.container_sell_value=容器界面顯示物品價值 -skytils.config.miscellaneous.quality_of_life.include_item_modifiers=包括物品修正 +skytils.config.miscellaneous.other.transparent_armor_layer=盔甲透明度 +skytils.config.miscellaneous.other.head_layer_transparency=頭顱透明度 +skytils.config.miscellaneous.other.fix_summon_skin=修正召喚物外觀 +skytils.config.miscellaneous.other.use_player_skin=使用玩家外觀 +skytils.config.miscellaneous.quality_of_life.custom_auction_price_input=自訂拍賣價格輸入界面 +skytils.config.miscellaneous.quality_of_life.better_stash=更好的Stash +skytils.config.miscellaneous.quality_of_life.container_sell_value=容器界面顯示物品價格 +skytils.config.miscellaneous.quality_of_life.include_item_modifiers=計算時包含物品附加數值 skytils.config.miscellaneous.quality_of_life.max_displayed_items=最大顯示物品數 -skytils.config.miscellaneous.quality_of_life.custom_damage_splash_style=自定義傷害顯示樣式 -skytils.config.miscellaneous.quality_of_life.disable_enderman_teleportation=禁用末影人傳送動畫 -skytils.config.miscellaneous.quality_of_life.disable_night_vision=禁用夜視 -skytils.config.miscellaneous.quality_of_life.dungeon_pot_lock=地牢藥水鎖定 -skytils.config.miscellaneous.quality_of_life.enchant_glint_fix=修復附魔發光 -skytils.config.miscellaneous.quality_of_life.hide_absorption_hearts=隱藏傷害吸收的心形血條 -skytils.config.miscellaneous.quality_of_life.hide_air_display=隱藏水下氣泡屏幕效果 +skytils.config.miscellaneous.quality_of_life.custom_damage_splash_style=自訂傷害顯示樣式 +skytils.config.miscellaneous.quality_of_life.disable_enderman_teleportation=停用終界使者傳送動畫 +skytils.config.miscellaneous.quality_of_life.disable_night_vision=停用夜視 +skytils.config.miscellaneous.quality_of_life.dungeon_pot_lock=鎖定可購買的Dungeon藥水 +skytils.config.miscellaneous.quality_of_life.enchant_glint_fix=修正附魔發光 +skytils.config.miscellaneous.quality_of_life.hide_absorption_hearts=隱藏吸收效果的心形血條 +skytils.config.miscellaneous.quality_of_life.hide_air_display=隱藏水下氣泡特效 skytils.config.miscellaneous.quality_of_life.hide_armor_display=隱藏護甲值顯示 -skytils.config.miscellaneous.quality_of_life.hide_cheap_coins=隱藏便宜硬幣 +skytils.config.miscellaneous.quality_of_life.hide_cheap_coins=隱藏低價值硬幣 skytils.config.dungeons.quality_of_life.hide_dying_mobs=隱藏死亡的怪物 skytils.config.miscellaneous.quality_of_life.hide_fire_on_entities=隱藏實體上的火焰 -skytils.config.miscellaneous.quality_of_life.hide_fishing_hooks=隱藏其他人的釣魚鉤 +skytils.config.miscellaneous.quality_of_life.hide_fishing_hooks=隱藏其他人的釣鉤 skytils.config.miscellaneous.quality_of_life.hide_hunger_display=隱藏飢餓值顯示 skytils.config.miscellaneous.quality_of_life.hide_jerry_rune=隱藏Jerry符文效果 skytils.config.miscellaneous.quality_of_life.hide_lightning=隱藏閃電 skytils.config.miscellaneous.quality_of_life.hide_mob_death_particles=隱藏怪物死亡粒子 -skytils.config.miscellaneous.quality_of_life.hide_pet_health_display=隱藏寵物生命值顯示 +skytils.config.miscellaneous.quality_of_life.hide_pet_health_display=隱藏原版Minecraft座騎生命值 skytils.config.miscellaneous.quality_of_life.hide_players_in_spawn=隱藏出生點玩家 skytils.config.miscellaneous.quality_of_life.hide_potion_effects_in_inventory=隱藏物品欄藥水效果 -skytils.config.miscellaneous.quality_of_life.hide_scoreboard_score=隱藏記分板分數 -skytils.config.miscellaneous.quality_of_life.hide_vanilla_health_display=隱藏原版生命值顯示 -skytils.config.miscellaneous.quality_of_life.highlight_disabled_potion_effects=高亮禁用的藥水效果 -skytils.config.miscellaneous.quality_of_life.highlight_salvageable_items=高亮可分解物品 -skytils.config.miscellaneous.quality_of_life.highlight_dungeonsellable_items=高亮可出售的地牢物品 -skytils.config.miscellaneous.quality_of_life.lower_enderman_nametags=降低末影人名稱牌 -skytils.config.miscellaneous.quality_of_life.middle_click_gui_items=中鍵點擊GUI物品 -skytils.config.miscellaneous.quality_of_life.moveable_action_bar=可移動快捷欄 -skytils.config.miscellaneous.quality_of_life.moveable_item_name_highlight=可移動的物品名稱高亮 +skytils.config.miscellaneous.quality_of_life.hide_scoreboard_score=隱藏計分板分數 +skytils.config.miscellaneous.quality_of_life.hide_vanilla_health_display=隱藏原版Minecraft生命值 +skytils.config.miscellaneous.quality_of_life.highlight_disabled_potion_effects=醒目顯示停用的藥水效果 +skytils.config.miscellaneous.quality_of_life.highlight_salvageable_items=醒目顯示可分解物品 +skytils.config.miscellaneous.quality_of_life.highlight_dungeonsellable_items=醒目顯示可出售的Dungeon物品 +skytils.config.miscellaneous.quality_of_life.lower_enderman_nametags=降低終界使者名稱浮動文字位置 +skytils.config.miscellaneous.quality_of_life.middle_click_gui_items=中鍵點選GUI物品 +skytils.config.miscellaneous.quality_of_life.moveable_action_bar=可動式快捷列 +skytils.config.miscellaneous.quality_of_life.moveable_item_name_highlight=可動式手持物品名稱 skytils.config.miscellaneous.quality_of_life.no_fire=無火焰效果 skytils.config.miscellaneous.quality_of_life.no_hurtcam=無受傷抖動 -skytils.config.miscellaneous.quality_of_life.party_addons=隊伍插件 -skytils.config.miscellaneous.quality_of_life.prevent_cursor_reset=防止鼠標光標重置 +skytils.config.miscellaneous.quality_of_life.party_addons=組隊附加功能 (Party Addons) +skytils.config.miscellaneous.quality_of_life.prevent_cursor_reset=防止滑鼠游標位置重設 skytils.config.miscellaneous.quality_of_life.prevent_moving_on_death=死亡時防止移動 -skytils.config.miscellaneous.quality_of_life.power_orb_lock=防止重複放置同級或更好的狼獵手球 -skytils.config.miscellaneous.quality_of_life.power_orb_lock_duration=允許重複放置狼獵手球的剩餘時間 -skytils.config.miscellaneous.quality_of_life.press_enter_to_confirm_sign_popups=按回車確認告示牌窗口 +skytils.config.miscellaneous.quality_of_life.power_orb_lock=防止重複放置Power Orb +skytils.config.miscellaneous.quality_of_life.power_orb_lock_duration=允許重複放置Power Orb的間隔 +skytils.config.miscellaneous.quality_of_life.press_enter_to_confirm_sign_popups=按Enter鍵以確定告示牌數值並關閉彈出介面 skytils.config.miscellaneous.quality_of_life.protect_items=保護物品 skytils.config.miscellaneous.quality_of_life.protect_items_above_value=保護價值高於某值的物品 -skytils.config.miscellaneous.quality_of_life.protect_starred_items=保護已打星物品 -skytils.config.miscellaneous.quality_of_life.quiver_display=箭袋顯示 +skytils.config.miscellaneous.quality_of_life.protect_starred_items=保護已升星物品 +skytils.config.miscellaneous.quality_of_life.quiver_display=顯示箭袋內箭矢 skytils.config.miscellaneous.quality_of_life.restock_arrows_warning=箭矢不足警告 -skytils.config.miscellaneous.quality_of_life.spiders_den_rain_timer=蜘蛛島嶼下雨計時器 +skytils.config.miscellaneous.quality_of_life.spiders_den_rain_timer=Spider's Den下雨計時器 skytils.config.miscellaneous.quality_of_life.show_arachne_spawn=顯示Arachne生成 skytils.config.miscellaneous.quality_of_life.show_arachne_hp=顯示Arachne生命值 skytils.config.miscellaneous.quality_of_life.show_coins_per_bit=顯示每Bits價值的硬幣數 skytils.config.miscellaneous.quality_of_life.show_coins_per_copper=顯示每個銅幣價值的硬幣數 -skytils.config.miscellaneous.quality_of_life.show_lowest_bin_price=顯示最低一口價 -skytils.config.miscellaneous.quality_of_life.stop_clicking_nonsalvageable_items=阻止點擊不可分解物品 -skytils.config.miscellaneous.quality_of_life.view_relic_waypoints=查看遺物路徑點 -skytils.config.miscellaneous.quality_of_life.find_rare_relics=查找稀有遺物 -skytils.config.miscellaneous.quality_of_life.reset_found_relic_waypoints=重置已找到的遺物路徑點 -skytils.config.miscellaneous.quality_of_life.potion_duration_notifications=藥水效果即將消失警告 -skytils.config.miscellaneous.quality_of_life.stop_hook_sinking_in_lava=防止魚鉤在岩漿中下沉 -skytils.config.miscellaneous.quality_of_life.fishing_hook_age=魚鉤計時器 -skytils.config.miscellaneous.quality_of_life.trophy_fish_tracker=獎盃魚數據跟蹤器 -skytils.config.miscellaneous.quality_of_life.show_trophy_fish_totals=顯示每個獎盃魚總數 -skytils.config.miscellaneous.quality_of_life.show_total_trophy_fish=顯示總獎盃魚數 -skytils.config.pets.quality_of_life.autopet_message_hider=自動換寵物消息隱藏 -skytils.config.pets.quality_of_life.highlight_active_pet=高亮選中寵物 -skytils.config.pets.quality_of_life.active_pet_highlight_color=選中寵物高亮的顏色 -skytils.config.pets.quality_of_life.highlight_favorite_pets=高亮收藏寵物 -skytils.config.pets.quality_of_life.favorite_pet_highlight_color=收藏寵物高亮的顏色 +skytils.config.miscellaneous.quality_of_life.show_lowest_bin_price=顯示最低直購價 +skytils.config.miscellaneous.quality_of_life.stop_clicking_nonsalvageable_items=防止點選不可分解之物品 +skytils.config.miscellaneous.quality_of_life.view_relic_waypoints=檢視Relic路徑點 +skytils.config.miscellaneous.quality_of_life.find_rare_relics=尋找稀有Relic +skytils.config.miscellaneous.quality_of_life.reset_found_relic_waypoints=重設已找到的Relic路徑點 +skytils.config.miscellaneous.quality_of_life.potion_duration_notifications=藥水效果即將消失時通知 +skytils.config.miscellaneous.quality_of_life.stop_hook_sinking_in_lava=防止釣鉤在岩漿中下沉 +skytils.config.miscellaneous.quality_of_life.fishing_hook_age=釣鉤計時器 +skytils.config.miscellaneous.quality_of_life.tropy_fish_tracker=追蹤Trophy Fish統計 +skytils.config.miscellaneous.quality_of_life.show_trophy_fish_totals=顯示每種Trophy Fish數量 +skytils.config.miscellaneous.quality_of_life.show_total_trophy_fish=顯示Trophy Fish總數 +skytils.config.pets.quality_of_life.autopet_message_hider=隱藏Autopet訊息 +skytils.config.pets.quality_of_life.highlight_active_pet=醒目標示使用中寵物 +skytils.config.pets.quality_of_life.active_pet_highlight_color=使用中寵物的標示顏色 +skytils.config.pets.quality_of_life.highlight_favorite_pets=醒目標示最愛寵物 +skytils.config.pets.quality_of_life.favorite_pet_highlight_color=最愛寵物的標示顏色 skytils.config.pets.quality_of_life.pet_item_confirmation=寵物物品確認 -skytils.config.slayer..current_revenant_rng_meter=當前殭屍獵手RNG里程條 -skytils.config.slayer..current_tarantula_rng_meter=當前蜘蛛獵手RNG里程條 -skytils.config.slayer..current_sven_rng_meter=當前狼獵手RNG里程條 -skytils.config.slayer..current_voidgloom_rng_meter=當前末影人獵手RNG里程條 -skytils.config.slayer..current_inferno_rng_meter=當前烈焰人獵手RNG里程條 -skytils.config.slayer..current_bloodfiend_rng_meter=當前吸血鬼獵手RNG里程條 -skytils.config.slayer.quality_of_life.click_to_open_maddox_menu=點擊打開Maddox菜單 +skytils.config.slayer..current_revenant_rng_meter=當前殭屍Slayer的RNG量表 +skytils.config.slayer..current_tarantula_rng_meter=當前蜘蛛Slayer的RNG量表 +skytils.config.slayer..current_sven_rng_meter=當前狼Slayer的RNG量表 +skytils.config.slayer..current_voidgloom_rng_meter=當前終界使者Slayer的RNG量表 +skytils.config.slayer..current_inferno_rng_meter=當前烈焰使者Slayer的RNG量表 +skytils.config.slayer..current_bloodfiend_rng_meter=當前吸血鬼Slayer的RNG量表 +skytils.config.slayer.quality_of_life.click_to_open_maddox_menu=點選任意處開啟Maddox選單 skytils.config.slayer.general.carry_mode=帶人模式 -skytils.config.slayer.general.use_hits_to_detect_slayer=使用擊中檢測獵手 -skytils.config.slayer.quality_of_life.ping_when_in_atoned_horror_danger_zone=進入殭屍獵手TNT技能危險區時警報 -skytils.config.slayer.quality_of_life.slayer_boss_hitbox=獵手boss碰撞箱 -skytils.config.slayer.quality_of_life.slayer_miniboss_spawn_alert=獵手的Miniboss刷新警報 -skytils.config.slayer.quality_of_life.show_rngesus_meter=顯示RNG里程條 -skytils.config.slayer.quality_of_life.show_slayer_armor_kills=顯示獵手護甲的擊殺數 -skytils.config.slayer.quality_of_life.show_slayer_display=顯示獵手信息 -skytils.config.slayer.quality_of_life.show_slayer_time_to_kill=顯示獵手擊殺時間 -skytils.config.slayer.voidgloom_seraph.hide_others_broken_heart_radiation=隱藏他人末影人獵手的Broken Heart Radiation激光技能 -skytils.config.slayer.voidgloom_seraph.recolor_seraph_boss=給末影人獵手boss上色 -skytils.config.slayer.voidgloom_seraph.seraph_beacon_phase_color=末影人獵手信標存在時顏色 -skytils.config.slayer.voidgloom_seraph.seraph_hits_phase_color=末影人獵手護盾階段顏色 -skytils.config.slayer.voidgloom_seraph.seraph_normal_phase_color=末影人獵手常規階段顏色 -skytils.config.slayer.voidgloom_seraph.show_seraph_display=顯示末影人獵手信息 -skytils.config.slayer.voidgloom_seraph.experimental_yang_glyph_detection=實驗性Yang Glyph信標技能檢測 -skytils.config.slayer.voidgloom_seraph.yang_glyph_ping=Yang Glyph信標技能警報 -skytils.config.slayer.voidgloom_seraph.yang_glyph_ping_on_land=Yang Glyph信標落地警報 -skytils.config.slayer.voidgloom_seraph.highlight_yang_glyph=高亮Yang Glyph信標 -skytils.config.slayer.voidgloom_seraph.point_to_yang_glyph=指向Yang Glyph信標 -skytils.config.slayer.voidgloom_seraph.yang_glyph_highlight_color=Yang Glyph信標高亮的顏色 -skytils.config.slayer.voidgloom_seraph.highlight_nukekebi_fixation_heads=高亮Nukekebi Fixation技能的頭顱 +skytils.config.slayer.general.use_hits_to_detect_slayer=以擊中的攻擊偵測Slayer +skytils.config.slayer.quality_of_life.ping_when_in_atoned_horror_danger_zone=殭屍Slayer的TNT技能危險區警告 +skytils.config.slayer.quality_of_life.slayer_boss_hitbox=Slayer boss碰撞箱 +skytils.config.slayer.quality_of_life.slayer_miniboss_spawn_alert=Slayer Miniboss生成警報 +skytils.config.slayer.quality_of_life.show_rngesus_meter=顯示RNG量表 +skytils.config.slayer.quality_of_life.show_slayer_armor_kills=顯示Slayer盔甲的擊殺數 +skytils.config.slayer.quality_of_life.show_slayer_display=顯示Slayer資訊 +skytils.config.slayer.quality_of_life.show_slayer_time_to_kill=顯示Slayer擊殺時間 +skytils.config.slayer.voidgloom_seraph.hide_others_broken_heart_radiation=隱藏他人終界使者Slayer的Broken Heart Radiation技能光線 +skytils.config.slayer.voidgloom_seraph.recolor_seraph_boss=替終界使者Slayer的boss上色 +skytils.config.slayer.voidgloom_seraph.seraph_beacon_phase_color=終界使者Slayer烽火台存在時顏色 +skytils.config.slayer.voidgloom_seraph.seraph_hits_phase_color=終界使者Slayer護盾階段顏色 +skytils.config.slayer.voidgloom_seraph.seraph_normal_phase_color=終界使者Slayer一般階段顏色 +skytils.config.slayer.voidgloom_seraph.show_seraph_display=顯示終界使者Slayer資訊 +skytils.config.slayer.voidgloom_seraph.experimental_yang_glyph_detection=實驗性Yang Glyph技能偵測 +skytils.config.slayer.voidgloom_seraph.yang_glyph_ping=Yang Glyph烽火台技能警報 +skytils.config.slayer.voidgloom_seraph.yang_glyph_ping_on_land=Yang Glyph烽火台落地警報 +skytils.config.slayer.voidgloom_seraph.highlight_yang_glyph=醒目顯示Yang Glyph烽火台 +skytils.config.slayer.voidgloom_seraph.point_to_yang_glyph=箭頭指向Yang Glyph烽火台 +skytils.config.slayer.voidgloom_seraph.yang_glyph_highlight_color=Yang Glyph烽火台的顏色 +skytils.config.slayer.voidgloom_seraph.highlight_nukekebi_fixation_heads=醒目顯示Nukekebi Fixation技能的頭顱 skytils.config.slayer.voidgloom_seraph.nukekebi_fixation_head_color=Nukekebi Fixation技能的頭顱顏色 -skytils.config.slayer.voidgloom_seraph.show_soulflow_display=顯示靈魂流 -skytils.config.slayer.voidgloom_seraph.low_soulflow_ping=靈魂流不足警報 -skytils.config.slayer.inferno_demonlord.show_totem_display=顯示圖騰技能信息 -skytils.config.slayer.inferno_demonlord.totem_ping=圖騰倒計時警報 -skytils.config.slayer.inferno_demonlord.hide_pacified_blazes=隱藏已平息的烈焰人 -skytils.config.slayer.inferno_demonlord.ping_when_in_inferno_demonlord_fire=進入烈焰人獵手的十字火時警報 -skytils.config.slayer.inferno_demonlord.recolor_demonlord_boss_by_attunement=根據Attunement護盾給烈焰人獵手上色 -skytils.config.slayer..vampire_slayer_one_shot_alert=吸血鬼獵手斬殺提醒 -skytils.config.slayer..twinclaw_alert=雙向爪(Twinclaw)技能警報 -skytils.config.sounds.abilities.disable_cooldown_sounds=禁用冷卻音效 -skytils.config.sounds.abilities.disable_jerrychine_gun_sounds=禁用Jerry槍音效 -skytils.config.sounds.abilities.disable_flower_of_truth_sounds=禁用Flower of Truth音效 -skytils.config.sounds.dungeons.disable_terracotta_sounds=禁用陶土人音效 +skytils.config.slayer.voidgloom_seraph.show_soulflow_display=顯示Soulflow +skytils.config.slayer.voidgloom_seraph.low_soulflow_ping=Soulflow不足警報 +skytils.config.slayer.inferno_demonlord.show_totem_display=顯示Totem技能訊息 +skytils.config.slayer.inferno_demonlord.totem_ping=Totem倒數計時警報 +skytils.config.slayer.inferno_demonlord.hide_pacified_blazes=隱藏不具攻擊性的烈焰使者 +skytils.config.slayer.inferno_demonlord.ping_when_in_inferno_demonlord_fire=烈焰使者Slayer的十字火範圍警報 +skytils.config.slayer.inferno_demonlord.recolor_demonlord_boss_by_attunement=根據Attunement護盾替boss上色 +skytils.config.slayer..vampire_slayer_one_shot_alert=吸血鬼Slayer終結提醒 +skytils.config.slayer..twinclaw_alert=Twinclaw技能警報 +skytils.config.sounds.abilities.disable_cooldown_sounds=停用冷卻音效 +skytils.config.sounds.abilities.disable_jerrychine_gun_sounds=停用Jerry-chine Gun音效 +skytils.config.sounds.abilities.disable_flower_of_truth_sounds=停用Flower of Truth音效 +skytils.config.sounds.dungeons.disable_terracotta_sounds=停用Terracota音效 skytils.config.spam.display.text_shadow=文字陰影 -skytils.config.spam.abilities.implosion_hider=爆炸消息隱藏 -skytils.config.spam.abilities.midas_staff_hider=Midas Staff消息隱藏 -skytils.config.spam.abilities.spirit_sceptre_hider=Spirit Sceptre消息隱藏 -skytils.config.spam.abilities.giant_sword_hider=Giant's Sword消息隱藏 -skytils.config.spam.abilities.livid_dagger_hider=Livid Dagger消息隱藏 -skytils.config.spam.abilities.ray_of_hope_hider=Staff of the Rising Sun消息隱藏 -skytils.config.spam.abilities.mining_ability_hider=挖礦能力消息隱藏 -skytils.config.spam.abilities.mana_use_hider=魔法值使用消息隱藏 -skytils.config.spam.abilities.healing_message_hider=治療消息隱藏 -skytils.config.spam.dungeons.blessing_hider=祝福消息隱藏 -skytils.config.spam.dungeons.blood_key_hider=血房鑰匙消息隱藏 -skytils.config.spam.dungeons.boss_messages_hider=Boss消息隱藏 -skytils.config.spam.dungeons.wither_essence_hider=凋靈精華消息隱藏 -skytils.config.spam.dungeons.undead_essence_hider=亡靈精華消息隱藏 -skytils.config.spam.dungeons.countdown_and_ready_messages_hider=倒計時和準備就緒消息隱藏 -skytils.config.spam.dungeons.dungeon_abilities_messages_hider=地牢能力消息隱藏 -skytils.config.spam.dungeons.mort_messages_hider=Mort消息隱藏 -skytils.config.spam.dungeons.superboom_pickup_hider=Superboom物品拾取消息隱藏 -skytils.config.spam.dungeons.revive_stone_pickup_hider=復活石拾取消息隱藏 -skytils.config.spam.dungeons.wither_key_hider=凋靈鑰匙消息隱藏 -skytils.config.spam.dungeons.tether_hider=奶媽技能綁定消息隱藏 -skytils.config.spam.dungeons.self_orb_pickup_hider=自己拾取治療球消息隱藏 -skytils.config.spam.dungeons.other_orb_pickup_hider=隊員拾取治療球消息隱藏 -skytils.config.spam.dungeons.trap_damage_hider=陷阱傷害消息隱藏 -skytils.config.spam.dungeons.toast_time=懸浮消息時間 -skytils.config.spam.miscellaneous.blocks_in_the_way_hider=被方塊阻擋的消息隱藏 -skytils.config.spam.miscellaneous.cant_use_ability_hider=無法使用能力消息隱藏 -skytils.config.spam.miscellaneous.combo_hider=連擊消息隱藏 -skytils.config.spam.miscellaneous.autorecombobulator_hider=Auto Recombobulator消息隱藏 -skytils.config.spam.miscellaneous.compact_building_tools=合併建築工具消息 -skytils.config.spam.miscellaneous.compact_mining_powder_gain=合併挖礦粉塵獲取消息 -skytils.config.spam.miscellaneous.cooldown_hider=冷卻消息隱藏 -skytils.config.spam.miscellaneous.no_enemies_nearby_hider=附近無敵人消息隱藏 -skytils.config.spam.miscellaneous.out_of_mana_hider=魔法值耗盡消息隱藏 -skytils.config.spam.miscellaneous.profile_message_hider=存檔消息隱藏 -skytils.config.spam.miscellaneous.spook_message_hider=萬聖節消息隱藏 -skytils.config.spam.fishing.blessing_enchant_hider=祝福附魔消息隱藏 -skytils.config.spam.fishing.blessed_bait_hider=祝福誘餌消息隱藏 -skytils.config.spam.fishing.sea_creature_catch_hider=海洋生物捕獲消息隱藏 -skytils.config.spam.fishing.legendary_sea_creature_catch_hider=傳奇海洋生物捕獲消息隱藏 -skytils.config.spam.fishing.good_fishing_treasure_hider=較好釣魚寶藏消息隱藏 -skytils.config.spam.fishing.great_fishing_treasure_hider=極好釣魚寶藏消息隱藏 -skytils.config.spam.miscellaneous.compact_hider=合成附魔消息隱藏 -skytils.config.spam.miscellaneous.pristine_hider=Pristine消息隱藏 -skytils.config.spam.miscellaneous.wind_direction_hider=風向消息隱藏 -skytils.config.general=通用 -skytils.config.dungeons=地牢 +skytils.config.spam.abilities.implosion_hider=隱藏Implosion技能訊息 +skytils.config.spam.abilities.midas_staff_hider=隱藏Midas Staff訊息 +skytils.config.spam.abilities.spirit_sceptre_hider=隱藏Spirit Sceptre訊息 +skytils.config.spam.abilities.giant_sword_hider=隱藏Giant's Sword訊息 +skytils.config.spam.abilities.livid_dagger_hider=隱藏Livid Dagger訊息 +skytils.config.spam.abilities.ray_of_hope_hider=隱藏Staff of the Rising Sun訊息 +skytils.config.spam.abilities.mining_ability_hider=隱藏挖礦技能訊息 +skytils.config.spam.abilities.mana_use_hider=隱藏魔力使用訊息 +skytils.config.spam.abilities.healing_message_hider=隱藏治療訊息 +skytils.config.spam.dungeons.blessing_hider=隱藏祝福訊息 +skytils.config.spam.dungeons.blood_key_hider=隱藏血之房間鑰匙訊息 +skytils.config.spam.dungeons.boss_messages_hider=隱藏Boss台詞 +skytils.config.spam.dungeons.wither_essence_hider=隱藏凋零精華訊息 +skytils.config.spam.dungeons.undead_essence_hider=隱藏亡靈精華訊息 +skytils.config.spam.dungeons.countdown_and_ready_messages_hider=隱藏倒數計時和準備就緒訊息 +skytils.config.spam.dungeons.dungeon_abilities_messages_hider=隱藏Dungeon能力訊息 +skytils.config.spam.dungeons.mort_messages_hider=隱藏Mort訊息 +skytils.config.spam.dungeons.superboom_pickup_hider=隱藏Superboom物品拾取訊息 +skytils.config.spam.dungeons.revive_stone_pickup_hider=隱藏復活石拾取訊息 +skytils.config.spam.dungeons.wither_key_hider=隱藏凋零鑰匙訊息 +skytils.config.spam.dungeons.tether_hider=隱藏Healer技能綁定訊息 +skytils.config.spam.dungeons.self_orb_pickup_hider=隱藏自己拾取治療球訊息 +skytils.config.spam.dungeons.other_orb_pickup_hider=隱藏隊員拾取治療球訊息 +skytils.config.spam.dungeons.trap_damage_hider=隱藏陷阱傷害訊息 +skytils.config.spam.dungeons.toast_time=彈出式通知(Toast)持續時間 +skytils.config.spam.miscellaneous.blocks_in_the_way_hider=隱藏被方塊阻擋的訊息 +skytils.config.spam.miscellaneous.cant_use_ability_hider=隱藏無法使用技能訊息 +skytils.config.spam.miscellaneous.combo_hider=隱藏連擊訊息 +skytils.config.spam.miscellaneous.autorecombobulator_hider=隱藏Auto Recombobulator訊息 +skytils.config.spam.miscellaneous.compact_building_tools=合併建築工具訊息 +skytils.config.spam.miscellaneous.compact_mining_powder_gain=合併挖礦粉塵 (Powder) 取得訊息 +skytils.config.spam.miscellaneous.cooldown_hider=隱藏冷卻訊息 +skytils.config.spam.miscellaneous.no_enemies_nearby_hider=隱藏附近沒有敵人之訊息 +skytils.config.spam.miscellaneous.out_of_mana_hider=隱藏魔力耗盡訊息 +skytils.config.spam.miscellaneous.profile_message_hider=隱藏存檔 (Profile) 訊息 +skytils.config.spam.miscellaneous.spook_message_hider=隱藏Spooky節訊息 +skytils.config.spam.fishing.blessing_enchant_hider=隱藏Blessing附魔訊息 +skytils.config.spam.fishing.blessed_bait_hider=隱藏Blessed Bait訊息 +skytils.config.spam.fishing.sea_creature_catch_hider=隱藏釣起海洋生物訊息 +skytils.config.spam.fishing.legendary_sea_creature_catch_hider=隱藏傳說一釣訊息 +skytils.config.spam.fishing.good_fishing_treasure_hider=隱藏GOOD CATCH訊息 +skytils.config.spam.fishing.great_fishing_treasure_hider=隱藏GREAT CATCH訊息 +skytils.config.spam.miscellaneous.compact_hider=隱藏Compact附魔訊息 +skytils.config.spam.miscellaneous.pristine_hider=隱藏Pristine訊息 +skytils.config.spam.miscellaneous.wind_direction_hider=隱藏Gone with the Wind風向訊息 +skytils.config.general=一般 +skytils.config.dungeons=Dungeon skytils.config.miscellaneous=雜項 -skytils.config.events=事件 +skytils.config.events=活動 skytils.config.farming=農業 skytils.config.kuudra=Kuudra skytils.config.mining=挖礦 skytils.config.pets=寵物 -skytils.config.slayer=獵手 +skytils.config.slayer=Slayer skytils.config.sounds=音效 -skytils.config.spam=消息 +skytils.config.spam=訊息過濾 skytils.config.general.api=API skytils.config.general.command_aliases=指令別名 skytils.config.general.local_api=本地API @@ -427,44 +427,44 @@ skytils.config.general.other=其他 skytils.config.general.reparty=重新組隊 skytils.config.general.hypixel=Hypixel skytils.config.general.updates=更新 -skytils.config.dungeons.fixes=修復 +skytils.config.dungeons.fixes=修正 skytils.config.dungeons.hud=HUD skytils.config.dungeons.miscellaneous=雜項 -skytils.config.dungeons.quality_of_life=生活質量 -skytils.config.dungeons.party_finder=隊伍尋找 +skytils.config.dungeons.quality_of_life=改善遊戲體驗 +skytils.config.dungeons.party_finder=隊伍搜尋器 skytils.config.dungeons.score_calculation=分數計算 skytils.config.miscellaneous.brewing=釀造 skytils.config.miscellaneous.items=物品 -skytils.config.dungeons.solvers=解密器 -skytils.config.dungeons.tank_helper_tools=坦克助手工具 -skytils.config.dungeons.terminal_solvers=終端解密器 +skytils.config.dungeons.solvers=解答器 +skytils.config.dungeons.tank_helper_tools=Tank輔助工具 +skytils.config.dungeons.terminal_solvers=終端機 (Terminal) 解答器 skytils.config.events.mayor_jerry=市長Jerry -skytils.config.events.mythological=神話 -skytils.config.events.spooky=萬聖節 +skytils.config.events.mythological=神話儀式 (Mythological Ritual) +skytils.config.events.spooky=Spooky skytils.config.events.technoblade=Technoblade -skytils.config.farming.garden=花園 -skytils.config.farming.quality_of_life=生活質量 -skytils.config.farming.solvers=解密器 +skytils.config.farming.garden=農園 +skytils.config.farming.quality_of_life=改善遊戲體驗 +skytils.config.farming.solvers=解答器 skytils.config.kuudra.performance=性能 skytils.config.kuudra.price_checking=價格檢查 -skytils.config.mining.quality_of_life=生活質量 -skytils.config.mining.solvers=解密器 -skytils.config.mining.crystal_hollows=水晶礦洞 +skytils.config.mining.quality_of_life=改善遊戲體驗 +skytils.config.mining.solvers=解答器 +skytils.config.mining.crystal_hollows=Crystal Hollows skytils.config.miscellaneous.chat_tabs=聊天頻道 -skytils.config.miscellaneous.fixes=修復 +skytils.config.miscellaneous.fixes=修正 skytils.config.miscellaneous.item_rarity=物品稀有度 -skytils.config.miscellaneous.minions=僕從 -skytils.config.miscellaneous.quality_of_life=生活質量 +skytils.config.miscellaneous.minions=Minion +skytils.config.miscellaneous.quality_of_life=改善遊戲體驗 skytils.config.miscellaneous.other=其他 -skytils.config.pets.quality_of_life=生活質量 -skytils.config.slayer.quality_of_life=生活質量 -skytils.config.slayer.general=通用 -skytils.config.slayer.voidgloom_seraph=末影人獵手 -skytils.config.slayer.inferno_demonlord=烈焰人獵手 -skytils.config.sounds.abilities=能力 -skytils.config.sounds.dungeons=地牢 +skytils.config.pets.quality_of_life=改善遊戲體驗 +skytils.config.slayer.quality_of_life=改善遊戲體驗 +skytils.config.slayer.general=一般 +skytils.config.slayer.voidgloom_seraph=終界使者Slayer +skytils.config.slayer.inferno_demonlord=烈焰使者Slayer +skytils.config.sounds.abilities=技能 +skytils.config.sounds.dungeons=Dungeon skytils.config.spam.display=顯示 -skytils.config.spam.abilities=能力 -skytils.config.spam.dungeons=地牢 +skytils.config.spam.abilities=技能 +skytils.config.spam.dungeons=Dungeon skytils.config.spam.miscellaneous=雜項 -skytils.config.spam.fishing=釣魚 \ No newline at end of file +skytils.config.spam.fishing=釣魚 From 0b188765480b5b174b18d52ffd319a4c879f260a Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Thu, 13 Jun 2024 13:23:26 -0400 Subject: [PATCH 21/25] version: 1.10.0-pre9 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3dcb502b4..897fd582b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,7 +34,7 @@ plugins { signing } -version = "1.10.0-pre8" +version = "1.10.0-pre9" group = "gg.skytils" repositories { From a108b41847cabadbc304a9be733e7f23ea909eed Mon Sep 17 00:00:00 2001 From: "thatonecoder (formerly Coccocoa's Helper)" <157546848+Coccocoahelper@users.noreply.github.com> Date: Sat, 6 Jul 2024 23:30:13 +0100 Subject: [PATCH 22/25] feat: make all dungeon solver colors customizable (#487) * feat: Make all Dungeon solver colors customizable Not ready, but likely not going to break thing Signed-off-by: thatonecoder (formerly Coccocoa's Helper) <157546848+Coccocoahelper@users.noreply.github.com> * Maybe done * maybe a proper fix * Update TileEntityChestRendererHook.kt Signed-off-by: thatonecoder (formerly Coccocoa's Helper) <157546848+Coccocoahelper@users.noreply.github.com> * revert * fix import * Fix missing package ??????????????????????????????????????????????????????????? --------- Signed-off-by: thatonecoder (formerly Coccocoa's Helper) <157546848+Coccocoahelper@users.noreply.github.com> Co-authored-by: sychic <47618543+Sychic@users.noreply.github.com> --- .../gg/skytils/skytilsmod/core/Config.kt | 28 ++++++++++++++++--- .../impl/dungeons/solvers/BoulderSolver.kt | 4 +-- .../renderer/TileEntityChestRendererHook.kt | 23 ++------------- .../resources/assets/skytils/lang/en_US.lang | 2 ++ 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt b/src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt index a0016c846..ec80224f7 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt @@ -1132,7 +1132,7 @@ object Config : Vigilant( @Property( type = PropertyType.SWITCH, name = "Boulder Solver", - description = "§b[WIP] §rShow which boxes to move on the Boulder puzzle.", + description = "Show which boxes to move on the Boulder puzzle.", category = "Dungeons", subcategory = "Solvers", i18nName = "skytils.config.dungeons.solvers.boulder_solver", i18nCategory = "skytils.config.dungeons", @@ -1140,6 +1140,16 @@ object Config : Vigilant( ) var boulderSolver = false + @Property( + type = PropertyType.COLOR, name = "Boulder Solver Color", + description = "Color of the box that shows which button to click in the Boulder puzzle.", + category = "Dungeons", subcategory = "Solvers", + i18nName = "skytils.config.dungeons.solvers.boulder_solver_color", + i18nCategory = "skytils.config.dungeons", + i18nSubcategory = "skytils.config.dungeons.solvers" + ) + var boulderSolverColor = Color(255, 0, 0, 255) + @Property( type = PropertyType.SWITCH, name = "Creeper Beams Solver", description = "Shows pairs on the Creeper Beams puzzle.", @@ -1182,7 +1192,7 @@ object Config : Vigilant( @Property( type = PropertyType.COLOR, name = "Teleport Maze Solver Color", - description = "Color of the thing that shows which pads you've stepped on in the Teleport Maze puzzle.", + description = "Color of the box that shows which pads you've stepped on in the Teleport Maze puzzle.", category = "Dungeons", subcategory = "Solvers", i18nName = "skytils.config.dungeons.solvers.teleport_maze_solver_color", i18nCategory = "skytils.config.dungeons", @@ -1200,9 +1210,19 @@ object Config : Vigilant( ) var threeWeirdosSolver = false + @Property( + type = PropertyType.COLOR, name = "Three Weirdos Solver Color", + description = "Color of the chest to click on the Three Weirdos puzzle.", + category = "Dungeons", subcategory = "Solvers", + i18nName = "skytils.config.dungeons.solvers.three_weirdos_solver_color", + i18nCategory = "skytils.config.dungeons", + i18nSubcategory = "skytils.config.dungeons.solvers" + ) + var threeWeirdosSolverColor = Color(255, 0, 0, 255) + @Property( type = PropertyType.SWITCH, name = "Tic Tac Toe Solver", - description = "§b[WIP] §rDisplays the best move on the Tic Tac Toe puzzle.", + description = "Displays the best move on the Tic Tac Toe puzzle.", category = "Dungeons", subcategory = "Solvers", i18nName = "skytils.config.dungeons.solvers.tic_tac_toe_solver", i18nCategory = "skytils.config.dungeons", @@ -1212,7 +1232,7 @@ object Config : Vigilant( @Property( type = PropertyType.COLOR, name = "Tic Tac Toe Solver Color", - description = "Color of the thing that displays the best move on the Tic Tac Toe puzzle.", + description = "Color of the outline that displays the best move on the Tic Tac Toe puzzle.", category = "Dungeons", subcategory = "Solvers", i18nName = "skytils.config.dungeons.solvers.tic_tac_toe_solver_color", i18nCategory = "skytils.config.dungeons", diff --git a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/solvers/BoulderSolver.kt b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/solvers/BoulderSolver.kt index 2da983ddd..284e637e7 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/solvers/BoulderSolver.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/solvers/BoulderSolver.kt @@ -93,7 +93,7 @@ object BoulderSolver { RenderUtil.drawFilledBoundingBox( matrixStack, AxisAlignedBB(x, y, z, x + 1, y + 1, z + 1), - Color(255, 0, 0, 255), + Skytils.config.boulderSolverColor, 0.7f ) GlStateManager.enableCull() @@ -379,4 +379,4 @@ object BoulderSolver { ) variantSteps.add(arrayListOf(BoulderPush(0, 1, Direction.FORWARD))) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/gg/skytils/skytilsmod/mixins/hooks/renderer/TileEntityChestRendererHook.kt b/src/main/kotlin/gg/skytils/skytilsmod/mixins/hooks/renderer/TileEntityChestRendererHook.kt index e44037e34..6ece0a090 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/mixins/hooks/renderer/TileEntityChestRendererHook.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/mixins/hooks/renderer/TileEntityChestRendererHook.kt @@ -1,28 +1,11 @@ -/* - * Skytils - Hypixel Skyblock Quality of Life Mod - * Copyright (C) 2020-2023 Skytils - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ package gg.skytils.skytilsmod.mixins.hooks.renderer +import gg.skytils.skytilsmod.core.Config.threeWeirdosSolverColor import gg.skytils.skytilsmod.features.impl.dungeons.solvers.ThreeWeirdosSolver import gg.skytils.skytilsmod.utils.bindColor import net.minecraft.client.renderer.GlStateManager import net.minecraft.tileentity.TileEntityChest import org.spongepowered.asm.mixin.injection.callback.CallbackInfo -import java.awt.Color fun setChestColor( te: TileEntityChest, @@ -34,7 +17,7 @@ fun setChestColor( ci: CallbackInfo ) { if (te.pos == ThreeWeirdosSolver.riddleChest) { - Color.RED.bindColor() + threeWeirdosSolverColor.bindColor() GlStateManager.disableTexture2D() } } @@ -49,4 +32,4 @@ fun setChestColorPost( ci: CallbackInfo ) { GlStateManager.enableTexture2D() -} +} \ No newline at end of file diff --git a/src/main/resources/assets/skytils/lang/en_US.lang b/src/main/resources/assets/skytils/lang/en_US.lang index 7a1ac39bd..c9f98c2e5 100644 --- a/src/main/resources/assets/skytils/lang/en_US.lang +++ b/src/main/resources/assets/skytils/lang/en_US.lang @@ -103,12 +103,14 @@ skytils.config.dungeons.solvers.highest_blaze_color=Highest Blaze Color skytils.config.dungeons.solvers.next_blaze_color=Next Blaze Color skytils.config.dungeons.solvers.line_to_next_blaze_color=Line to Next Blaze Color skytils.config.dungeons.solvers.boulder_solver=Boulder Solver +skytils.config.dungeons.solvers.boulder_solver_color=Boulder Solver Color skytils.config.dungeons.solvers.creeper_beams_solver=Creeper Beams Solver skytils.config.dungeons.solvers.ice_fill_solver=Ice Fill Solver skytils.config.dungeons.solvers.ice_path_solver=Ice Path Solver skytils.config.dungeons.solvers.teleport_maze_solver=Teleport Maze Solver skytils.config.dungeons.solvers.teleport_maze_solver_color=Teleport Maze Solver Color skytils.config.dungeons.solvers.three_weirdos_solver=Three Weirdos Solver +skytils.config.dungeons.solvers.three_weirdos_solver_color=Three Weirdos Solver Color skytils.config.dungeons.solvers.tic_tac_toe_solver=Tic Tac Toe Solver skytils.config.dungeons.solvers.tic_tac_toe_solver_color=Tic Tac Toe Solver Color skytils.config.dungeons.solvers.trivia_solver=Trivia Solver From f4cef74ab17ecfa22fc7eeb38327b684e888b057 Mon Sep 17 00:00:00 2001 From: The Holy Chicken <167555771+TheHolyChickn@users.noreply.github.com> Date: Thu, 8 Aug 2024 14:10:58 -0400 Subject: [PATCH 23/25] Fixes profit calc after changes to Croesus GUI name (#494) Signed-off-by: The Holy Chicken <167555771+TheHolyChickn@users.noreply.github.com> --- .../skytilsmod/features/impl/dungeons/DungeonChestProfit.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/DungeonChestProfit.kt b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/DungeonChestProfit.kt index ac73bb5f2..3884a2879 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/DungeonChestProfit.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/DungeonChestProfit.kt @@ -53,7 +53,7 @@ object DungeonChestProfit { private val element = DungeonChestProfitElement() private var rerollBypass = false private val essenceRegex = Regex("§d(?\\w+) Essence §8x(?\\d+)") - private val croesusChestRegex = Regex("^(Master Mode|The)? Catacombs - Floor (IV|V?I{0,3})$") + private val croesusChestRegex = Regex("^(Master Mode )?The Catacombs - Flo(or (IV|V?I{0,3}))?$") @SubscribeEvent fun onGUIDrawnEvent(event: GuiContainerEvent.ForegroundDrawnEvent) { @@ -126,7 +126,7 @@ object DungeonChestProfit { val stack = event.slot.stack ?: return if (stack.item == Items.skull) { val name = stack.displayName - if (!(name == "§cThe Catacombs" || name == "§cMaster Mode Catacombs")) return + if (!(name == "§cThe Catacombs" || name == "§cMaster Mode The Catacombs")) return val lore = ItemUtil.getItemLore(stack) event.slot highlight when { lore.any { line -> line == "§aNo more Chests to open!" } -> { @@ -325,4 +325,4 @@ object DungeonChestProfit { Skytils.guiManager.registerElement(this) } } -} \ No newline at end of file +} From cbff917d51f69c6b941fd97b4951544e7a1c0f9c Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Thu, 8 Aug 2024 22:41:08 -0400 Subject: [PATCH 24/25] Update Ice Fill cores from upstream --- src/main/resources/assets/catlas/rooms.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/resources/assets/catlas/rooms.json b/src/main/resources/assets/catlas/rooms.json index fc494dfe7..49abad070 100644 --- a/src/main/resources/assets/catlas/rooms.json +++ b/src/main/resources/assets/catlas/rooms.json @@ -518,8 +518,11 @@ "type": "PUZZLE", "cores": [ 2051424561, + 1262122263, + 1673994041, 884728242, - 1262122263 + 1328525306, + 161828987 ] }, { From db6a08484fa836b78963f17cf30f574ea7bacedc Mon Sep 17 00:00:00 2001 From: My-Name-Is-Jeff <37018278+My-Name-Is-Jeff@users.noreply.github.com> Date: Thu, 8 Aug 2024 23:04:08 -0400 Subject: [PATCH 25/25] fix: Ice Fill Solver doesn't account for 2 chests --- .../impl/dungeons/solvers/IceFillSolver.kt | 54 ++++++++++++------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/solvers/IceFillSolver.kt b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/solvers/IceFillSolver.kt index 11215c895..a7ad8bf1f 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/solvers/IceFillSolver.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/solvers/IceFillSolver.kt @@ -59,31 +59,45 @@ object IceFillSolver { val pos = te.pos if (world.getBlockState(pos.down()).block == Blocks.stone) { for (direction in EnumFacing.HORIZONTALS) { - if (world.getBlockState(pos.offset(direction)).block == Blocks.cobblestone && world.getBlockState( - pos.offset(direction.opposite, 2) - ).block == Blocks.iron_bars && world.getBlockState( + fun checkChestTorches(dir: EnumFacing): Boolean { + return world.getBlockState( pos.offset( - direction.rotateY(), - 2 + dir, + 1 ) ).block == Blocks.torch && world.getBlockState( pos.offset( - direction.rotateYCCW(), - 2 + dir.opposite, + 3 ) - ).block == Blocks.torch && world.getBlockState( - pos.offset(direction.opposite).down(2) - ).block == Blocks.stone_brick_stairs - ) { - puzzles = Triple( - IceFillPuzzle(world, 70, pos, direction), - IceFillPuzzle(world, 71, pos, direction), - IceFillPuzzle(world, 72, pos, direction) - ) - println( - "Ice fill chest is at $pos and is facing $direction" - ) - break@findChest + ).block == Blocks.torch + } + + if (world.getBlockState(pos.offset(direction)).block == Blocks.cobblestone && world.getBlockState( + pos.offset(direction.opposite, 2) + ).block == Blocks.iron_bars) { + + val offsetDir: EnumFacing? = if (checkChestTorches(direction.rotateY())) { + direction.rotateYCCW() + } else if (checkChestTorches(direction.rotateYCCW())) { + direction.rotateY() + } else continue + + if (world.getBlockState( + pos.offset(direction.opposite) + .offset(offsetDir) + .down(2) + ).block == Blocks.stone_brick_stairs) { + puzzles = Triple( + IceFillPuzzle(world, 70, pos, direction), + IceFillPuzzle(world, 71, pos, direction), + IceFillPuzzle(world, 72, pos, direction) + ) + println( + "An Ice Fill chest is at $pos and is facing $direction. Offset direction is $offsetDir." + ) + break@findChest + } } } }