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
+ }
}
}
}