Skip to content
3 changes: 3 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ allprojects {
implementation(kotlin("reflect"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC")

implementation("com.squareup.okhttp3:okhttp:4.9.3")
implementation("com.squareup.okio:okio:3.4.0")
Comment on lines +43 to +44
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The versions of okhttp (4.9.3) and okio (3.4.0) are outdated. The latest stable version for OkHttp 4.x is 4.12.0. Using old dependencies can expose the project to known vulnerabilities and bugs that have been fixed in newer versions. It's recommended to update these dependencies.

        implementation("com.squareup.okhttp3:okhttp:4.12.0")
        implementation("com.squareup.okio:okio:3.9.0")


compileOnly("com.github.NotEnoughUpdates:NotEnoughUpdates:2.4.0:all")

annotationProcessor("org.spongepowered:mixin:0.8.5-SNAPSHOT")
Expand Down
10 changes: 8 additions & 2 deletions src/main/kotlin/me/odinmain/OdinMain.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package me.odinmain

import com.google.gson.Gson
import com.google.gson.GsonBuilder
import kotlinx.coroutines.*
import me.odinmain.commands.CommandRegistry
import me.odinmain.config.Config
Expand All @@ -12,11 +14,12 @@ import me.odinmain.features.impl.render.WaypointManager
import me.odinmain.utils.ServerUtils
import me.odinmain.utils.SplitsManager
import me.odinmain.utils.clock.Executor
import me.odinmain.utils.network.WebUtils.createClient
import me.odinmain.utils.network.WebUtils.postData
import me.odinmain.utils.render.HighlightRenderer
import me.odinmain.utils.render.RenderUtils
import me.odinmain.utils.render.RenderUtils2D
import me.odinmain.utils.render.Renderer
import me.odinmain.utils.sendDataToServer
import me.odinmain.utils.skyblock.*
import me.odinmain.utils.skyblock.dungeon.DungeonListener
import me.odinmain.utils.skyblock.dungeon.DungeonUtils
Expand All @@ -37,6 +40,9 @@ object OdinMain {
val scope = CoroutineScope(SupervisorJob() + EmptyCoroutineContext)
val logger: Logger = LogManager.getLogger("Odin")

val okClient = createClient()
val gson: Gson = GsonBuilder().setPrettyPrinting().create()

var display: GuiScreen? = null
inline val isLegitVersion: Boolean
get() = Loader.instance().activeModList.none { it.modId == "odclient" }
Expand Down Expand Up @@ -70,7 +76,7 @@ object OdinMain {
scope.launch(Dispatchers.IO) {
DungeonWaypointConfig.loadConfig()
ClickGUIModule.latestVersionNumber = ClickGUIModule.checkNewerVersion(VERSION)
sendDataToServer(body = """{"username": "$name", "version": "${if (isLegitVersion) "legit" else "cheater"} $VERSION"}""")
postData("https://api.odtheking.com/tele/", """{"username": "$name", "version": "${if (isLegitVersion) "legit" else "cheater"} $VERSION"}""")
}
}

Expand Down
43 changes: 26 additions & 17 deletions src/main/kotlin/me/odinmain/commands/impl/DevCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import kotlinx.coroutines.launch
import me.odinmain.OdinMain.VERSION
import me.odinmain.OdinMain.mc
import me.odinmain.OdinMain.scope
import me.odinmain.config.Config
import me.odinmain.events.impl.PacketEvent
import me.odinmain.features.ModuleManager.generateFeatureList
import me.odinmain.features.impl.dungeon.MapInfo
Expand All @@ -14,11 +15,17 @@ import me.odinmain.features.impl.floor7.DragonPriority.findPriority
import me.odinmain.features.impl.floor7.WitherDragonState
import me.odinmain.features.impl.floor7.WitherDragons.priorityDragon
import me.odinmain.features.impl.floor7.WitherDragonsEnum
import me.odinmain.features.impl.floor7.p3.MelodyMessage.webSocket
import me.odinmain.features.impl.floor7.p3.TerminalSolver.firstClickProt
import me.odinmain.features.impl.nether.NoPre
import me.odinmain.features.impl.render.ClickGUIModule.wsServer
import me.odinmain.features.impl.render.PlayerSize
import me.odinmain.features.impl.render.PlayerSize.DEV_SERVER
import me.odinmain.features.impl.render.PlayerSize.buildDevBody
import me.odinmain.utils.isOtherPlayer
import me.odinmain.utils.network.WebUtils.postData
import me.odinmain.utils.postAndCatch
import me.odinmain.utils.sendDataToServer
import me.odinmain.utils.render.Colors
import me.odinmain.utils.skyblock.*
import me.odinmain.utils.skyblock.PlayerUtils.posX
import me.odinmain.utils.skyblock.PlayerUtils.posZ
Expand All @@ -33,6 +40,17 @@ import net.minecraft.util.ChatComponentText

val devCommand = Commodore("oddev") {

literal("firstclickprot").runs { time: Long ->
firstClickProt = time
Config.save()
}

literal("ws") {
literal("connect").runs { lobby: String ->
webSocket.connect("${wsServer}$lobby")
}
}

literal("drags") {
runs { text: GreedyString ->
val drags = WitherDragonsEnum.entries.mapNotNull {
Expand Down Expand Up @@ -86,27 +104,18 @@ val devCommand = Commodore("oddev") {
}

literal("updatedevs").runs {
PlayerSize.updateCustomProperties()
}

literal("adddev").runs { name: String, password: String, xSize: Float?, ySize: Float?, zSize: Float? ->
val x = xSize ?: 0.6
val y = ySize ?: 0.6
val z = zSize ?: 0.6
modMessage("Sending data... name: $name, password: $password")
scope.launch {
modMessage(sendDataToServer("$name, [1,2,3], [$x,$y,$z], false, , $password", "https://tj4yzotqjuanubvfcrfo7h5qlq0opcyk.lambda-url.eu-north-1.on.aws/"))
PlayerSize.updateCustomProperties()
}
}

literal("customSize").runs { password: String, xSize: Float?, ySize: Float?, zSize: Float?, customName: String? ->
val x = xSize ?: 0.6
val y = ySize ?: 0.6
val z = zSize ?: 0.6
val name = customName ?: ""
literal("adddev").runs { name: String, password: String, xSize: Float?, ySize: Float?, zSize: Float? ->
val x = xSize ?: 0.6f
val y = ySize ?: 0.6f
val z = zSize ?: 0.6f
modMessage("Sending data... name: $name, x: $x, y: $y, z: $z")
scope.launch {
modMessage(sendDataToServer(body = "${mc.thePlayer.name}, [1,2,3], [$x,$y,$z], false, $name, $password", "https://tj4yzotqjuanubvfcrfo7h5qlq0opcyk.lambda-url.eu-north-1.on.aws/"))
PlayerSize.updateCustomProperties()
modMessage(postData(DEV_SERVER, buildDevBody(name, Colors.WHITE, x, y, z, false, " ", password)).getOrNull())
}
}

Expand Down
17 changes: 5 additions & 12 deletions src/main/kotlin/me/odinmain/commands/impl/SoopyCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ package me.odinmain.commands.impl

import com.github.stivais.commodore.Commodore
import com.github.stivais.commodore.utils.SyntaxException
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeout
import me.odinmain.OdinMain.mc
import me.odinmain.OdinMain.scope
import me.odinmain.utils.fetchURLData
import me.odinmain.utils.network.WebUtils.fetchString
import me.odinmain.utils.skyblock.modMessage

val soopyCommand = Commodore("soopycmd", "spcmd", "spc") {
Expand All @@ -33,15 +31,10 @@ val soopyCommand = Commodore("soopycmd", "spcmd", "spc") {
val player = user ?: mc.thePlayer.name
modMessage("Running command...")
scope.launch {
try {
modMessage(withTimeout(5000) {
fetchURLData("https://soopy.dev/api/soopyv2/botcommand?m=$command&u=$player") }
)
} catch (_: TimeoutCancellationException) {
modMessage("Request timed out")
} catch (e: Exception) {
modMessage("Failed to fetch data: ${e.message}")
}
fetchString("https://soopy.dev/api/soopyv2/botcommand?m=$command&u=$player").fold(
{ modMessage(it) },
{ e -> modMessage("Failed to fetch data: ${e.message}") }
)
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/me/odinmain/features/impl/dungeon/Mimic.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ object Mimic : Module(
private val color by ColorSetting("Color", Colors.MINECRAFT_RED.withAlpha(0.5f), allowAlpha = true, desc = "The color of the box.").withDependency { mimicBox }
private val lineWidth by NumberSetting("Line Width", 2f, 0.1f, 10f, 0.1f, desc = "The width of the box's lines.").withDependency { mimicBox }

private val princeMessageToggle by BooleanSetting("Toggle Prince Message", false, desc = "Toggles the prince killed message.")
val princeMessage by StringSetting("Prince Message", "Prince Killed!", 128, desc = "Message sent when prince is detected as killed.").withDependency { princeMessageToggle }
private val princeMessageToggle by BooleanSetting("Prince Message", true, desc = "Toggles the prince killed message.")
val princeMessage by StringSetting("Prince Message Text", "Prince Killed!", 128, desc = "Message sent when prince is detected as killed.").withDependency { princeMessageToggle }
private val princeReset by ActionSetting("Prince Killed", desc = "Sends Prince killed message in party chat.") { princeKilled() }

private const val MIMIC_TEXTURE = "ewogICJ0aW1lc3RhbXAiIDogMTY3Mjc2NTM1NTU0MCwKICAicHJvZmlsZUlkIiA6ICJhNWVmNzE3YWI0MjA0MTQ4ODlhOTI5ZDA5OTA0MzcwMyIsCiAgInByb2ZpbGVOYW1lIiA6ICJXaW5zdHJlYWtlcnoiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTE5YzEyNTQzYmM3NzkyNjA1ZWY2OGUxZjg3NDlhZThmMmEzODFkOTA4NWQ0ZDRiNzgwYmExMjgyZDM1OTdhMCIsCiAgICAgICJtZXRhZGF0YSIgOiB7CiAgICAgICAgIm1vZGVsIiA6ICJzbGltIgogICAgICB9CiAgICB9CiAgfQp9"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@ enum class WitherDragonsEnum (
}
}

fun setDead() {
fun setDead(deathless: Boolean = false) {
state = WitherDragonState.DEAD
dragonEntityList.remove(entity)
entityId = null
entity = null
lastDragonDeath = this
if (!deathless) lastDragonDeath = this
if (sendArrowHit && WitherDragons.enabled && currentTick - spawnedTime < skipKillTime)
modMessage("§fArrows Hit on §${colorCode}${name}§7: ${arrowsHit.entries.joinToString(", ") { "§f${it.key}§7: §6${it.value.good}${it.value.late.let { if (it > 0) " §8(§7${it}§8)" else "" }}§7" }}.")
if (priorityDragon == this) priorityDragon = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,8 @@ object WitherDragons : Module(
onMessage(Regex("^\\[BOSS] Wither King: (Oh, this one hurts!|I have more of those\\.|My soul is disposable\\.)$"), { enabled && DungeonUtils.getF7Phase() == M7Phases.P5 } ) {
WitherDragonsEnum.entries.find { lastDragonDeath == it && lastDragonDeath != WitherDragonsEnum.None }?.let {
if (sendNotification) modMessage("§${it.colorCode}${it.name} dragon counts.")
it.state = WitherDragonState.DEAD
}
WitherDragonsEnum.entries.find { it.state == WitherDragonState.ALIVE }?.let { it.state = WitherDragonState.DEAD }
lastDragonDeath = WitherDragonsEnum.None
} ?: WitherDragonsEnum.entries.find { it.state == WitherDragonState.ALIVE }?.setDead(true)
}
}

Expand Down
130 changes: 122 additions & 8 deletions src/main/kotlin/me/odinmain/features/impl/floor7/p3/MelodyMessage.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
package me.odinmain.features.impl.floor7.p3

import me.odinmain.OdinMain.gson
import me.odinmain.clickgui.settings.Setting.Companion.withDependency
import me.odinmain.clickgui.settings.impl.BooleanSetting
import me.odinmain.clickgui.settings.impl.StringSetting
import me.odinmain.events.impl.TerminalEvent
import me.odinmain.features.Module
import me.odinmain.features.impl.floor7.p3.termsim.TermSimGUI
import me.odinmain.features.impl.render.ClickGUIModule.wsServer
import me.odinmain.utils.network.webSocket
import me.odinmain.utils.render.RenderUtils
import me.odinmain.utils.skyblock.LocationUtils
import me.odinmain.utils.skyblock.dungeon.DungeonUtils
import me.odinmain.utils.skyblock.dungeon.M7Phases
import me.odinmain.utils.skyblock.partyMessage
import me.odinmain.utils.skyblock.sendCommand
import me.odinmain.utils.ui.getTextWidth
import net.minecraft.item.Item
import net.minecraft.network.play.server.S2FPacketSetSlot
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import java.util.concurrent.ConcurrentHashMap

object MelodyMessage : Module(
name = "Melody Message",
Expand All @@ -20,26 +29,131 @@ object MelodyMessage : Module(
private val melodyMessage by StringSetting("Melody Message", "Melody Terminal start!", 128, desc = "Message sent when the melody terminal opens.").withDependency { sendMelodyMessage }
private val melodyProgress by BooleanSetting("Melody Progress", false, desc = "Tells the party about melody terminal progress.")
private val melodySendCoords by BooleanSetting("Melody Send Coords", false, desc = "Sends the coordinates of the melody terminal.").withDependency { melodyProgress }
private val broadcast by BooleanSetting("Broadcast Progress", true, desc = "Broadcasts melody progress to all other odin users in your run using a websocket.")
private val melodyGui by HUD("Progress GUI", "Shows a GUI with the progress of broadcasting odin users in the melody terminal.", true) {
if (it) {
drawMelody(MelodyData(3, 1, 2), 0)
return@HUD 45f to 25f
}

if (!broadcast || !webSocket.connected) return@HUD 0f to 0f
melodies.entries.forEachIndexed { i, (name, data) ->
if (!showOwn && name == mc.session.username) return@forEachIndexed
drawMelody(data, i)
}
45f to 25f
}.withDependency { broadcast }

// explicit boolean because showOwn is broken or something
private val showOwn: Boolean by BooleanSetting("Show Own", false, desc = "Shows your own melody progress in the GUI.").withDependency { melodyGui.enabled && broadcast }

val webSocket = webSocket {
onMessage {
val (user, type, slot) = try { gson.fromJson(it, UpdateMessage::class.java) } catch (_: Exception) { return@onMessage }
val entry = melodies.getOrPut(user) { MelodyData(null, null, null) }
when (type) {
0 -> melodies.remove(user)
1 -> entry.clay = slot
2 -> entry.purple = slot
5 -> entry.pane = slot
}
}
}

private val melodies = ConcurrentHashMap<String, MelodyData>()
private val lastSent = MelodyData(null, null, null)

init {
onMessage(Regex("^\\[BOSS] Goldor: Who dares trespass into my domain\\?$"), { enabled && broadcast }) {
webSocket.connect("${wsServer}${LocationUtils.lobbyId}")
}

onMessage(Regex("^The Core entrance is opening!$"), { enabled && broadcast }) {
webSocket.shutdown()
melodies.clear()
}

onWorldLoad {
webSocket.shutdown()
melodies.clear()
}

onPacket<S2FPacketSetSlot>({ enabled && broadcast }) {
val term = TerminalSolver.currentTerm ?: return@onPacket
if (DungeonUtils.getF7Phase() != M7Phases.P3 || term.type != TerminalTypes.MELODY || it.func_149173_d() !in 0 until term.type.windowSize || mc.currentScreen is TermSimGUI) return@onPacket

val meta = it.func_149174_e()?.metadata ?: return@onPacket

private var claySlots = hashMapOf(25 to "Melody 25%", 34 to "Melody 50%", 43 to "Melody 75%")
val clay = Item.getIdFromItem(it.func_149174_e().item) == 159
if (clay && meta != 5) return@onPacket

if (clay) {
val position = it.func_149173_d() / 9
if (lastSent.clay == position) return@onPacket
webSocket.send(update(1, position))
lastSent.clay = position
if (melodyProgress) clayProgress[position]?.let { partyMessage(it) }
return@onPacket
}

if (meta != 5 && meta != 2) return@onPacket

val index = mapToRange(it.func_149173_d()) ?: return@onPacket

val shouldSend = when (meta) {
2 -> lastSent.purple != index
5 -> lastSent.pane != index
else -> false
}

if (!shouldSend) return@onPacket
webSocket.send(update(meta, index))
when (meta) {
2 -> lastSent.purple = index
5 -> lastSent.pane = index
}
}
}

@SubscribeEvent
fun onClose(event: TerminalEvent.Closed) {
if (event.terminal.type != TerminalTypes.MELODY) return
webSocket.send(update(0, 0))
}

private val clayProgress = hashMapOf(1 to "Melody 25%", 2 to "Melody 50%", 3 to "Melody 75%")

@SubscribeEvent
fun onTermLoad(event: TerminalEvent.Opened) {
if (DungeonUtils.getF7Phase() != M7Phases.P3 || event.terminal.type != TerminalTypes.MELODY || mc.currentScreen is TermSimGUI) return
if (sendMelodyMessage) partyMessage(melodyMessage)
if (melodySendCoords) sendCommand("od sendcoords", true)
}

fun update(type: Int, slot: Int): String = gson.toJson(UpdateMessage(mc.session.username, type, slot))

claySlots = hashMapOf(25 to "Melody 25%", 34 to "Melody 50%", 43 to "Melody 75%")
val ranges = listOf(1..5, 10..14, 19..23, 28..32, 37..41)

fun mapToRange(value: Int): Int? {
for (r in ranges) {
if (value in r) return (value - r.first) % 5
}
return null
}

init {
execute(250) {
if (DungeonUtils.getF7Phase() != M7Phases.P3 || TerminalSolver.currentTerm?.type != TerminalTypes.MELODY || mc.currentScreen is TermSimGUI || !melodyProgress) return@execute
private val width = getTextWidth("§d■").toFloat()

val greenClayIndices = claySlots.keys.filter { index -> TerminalSolver.currentTerm?.items?.get(index)?.metadata == 5 }.ifEmpty { return@execute }
fun drawMelody(data: MelodyData, index: Int) {
val y = width * 2 * index

partyMessage(claySlots[greenClayIndices.last()] ?: return@execute)
greenClayIndices.forEach { claySlots.remove(it) }
repeat(5) {
if (data.purple == it) RenderUtils.drawText("§d■", width * it, y)
val color = if (data.pane == it) "§a" else "§f"
RenderUtils.drawText("${color}■", width * it, y + width)
}
data.clay?.let { RenderUtils.drawText(it.toString(), 40f, y + width / 2) }
}

data class UpdateMessage(val user: String, val type: Int, val slot: Int)
data class MelodyData(var purple: Int?, var pane: Int?, var clay: Int?)
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ object TerminalSolver : Module(
val melodyRowColor by ColorSetting("Melody Row", Colors.MINECRAFT_RED, true, desc = "Color of the row indicator for melody.").withDependency { showColors && !cancelMelodySolver }
val melodyPointerColor by ColorSetting("Melody Pointer", Colors.MINECRAFT_GREEN, true, desc = "Color of the location for pressing for melody.").withDependency { showColors && !cancelMelodySolver }

var firstClickProt by NumberSetting("First Click Protection", 350L, 0, 500, 10, unit = "ms", desc = "The amount of time before you can click in a terminal.").hide()

var currentTerm: TerminalHandler? = null
private set
var lastTermOpened: TerminalHandler? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package me.odinmain.features.impl.floor7.p3.termGUI
import me.odinmain.OdinMain.mc
import me.odinmain.events.impl.GuiEvent
import me.odinmain.features.impl.floor7.p3.TerminalSolver
import me.odinmain.features.impl.floor7.p3.TerminalSolver.firstClickProt
import me.odinmain.features.impl.floor7.p3.TerminalSolver.hideClicked
import me.odinmain.utils.postAndCatch
import me.odinmain.utils.render.Color
Expand Down Expand Up @@ -51,7 +52,7 @@ abstract class TermGui {
fun mouseClicked(button: Int) {
getHoveredItem()?.let { slot ->
TerminalSolver.currentTerm?.let {
if (System.currentTimeMillis() - it.timeOpened >= 350 && !GuiEvent.CustomTermGuiClick(slot, button).postAndCatch() && it.canClick(slot, button)) {
if (System.currentTimeMillis() - it.timeOpened >= firstClickProt && !GuiEvent.CustomTermGuiClick(slot, button).postAndCatch() && it.canClick(slot, button)) {
it.click(slot, if (button == 0) ClickType.Middle else ClickType.Right, hideClicked && !it.isClicked)
if (TerminalSolver.customAnimations) colorAnimations[slot]?.start()
}
Expand Down
Loading
Loading