Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ class HUDSetting(
val module: Module,
) : RenderableSetting<HudElement>(name, description), Saving {

constructor(name: String, x: Float, y: Float, scale: Float, toggleable: Boolean, description: String, module: Module, draw: (Boolean) -> Pair<Number, Number>)
Copy link
Owner

Choose a reason for hiding this comment

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

Revert

Copy link
Contributor Author

@FlyModeZ FlyModeZ Aug 8, 2025

Choose a reason for hiding this comment

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

(old) if you have a HUD, toggleable=false, it's also not enabled when first launch
i'm fixing this :<

: this(name, HudElement(x, y, scale, toggleable, draw), toggleable, description, module)
constructor(name: String, x: Float, y: Float, scale: Float, default: Boolean, toggleable: Boolean, description: String, module: Module, draw: (Boolean) -> Pair<Number, Number>)
: this(name, HudElement(x, y, scale, default, draw), toggleable, description, module)

override val default: HudElement = hud
override var value: HudElement = default
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,25 @@ import java.lang.reflect.Type
class ListSetting<E, T : MutableCollection<E>>(
Copy link
Owner

Choose a reason for hiding this comment

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

Do not modify this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i couldn't find any other way to define realRegex
like idk but when you read from config this var realRegex code block doesn't run

    data class CountdownTrigger(val prefix: String, val time: Int, val regex: Boolean, val message: String) {
        @Transient
        var realRegex: Regex? = if (regex) {
            runCatching { Regex(message) }.getOrNull()
        } else {
            null
        }
    }

init {} block runs before config load btw

Copy link
Contributor Author

Choose a reason for hiding this comment

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

uh ok

name: String,
override val default: T,
private val type: Type
private val type: Type,
private val reloader: ((E) -> E)? = null
) : Setting<T>(name, description = ""), Saving {

override var value: T = default

override fun write(): JsonElement = gson.toJsonTree(value)

override fun read(element: JsonElement?) {
element?.asJsonArray?.let {
val temp = gson.fromJson<T>(it, type)
element?.asJsonArray?.let { ja ->
val temp = gson.fromJson<T>(ja, type)
value.clear()
value.addAll(temp)
value.addAll(reloader?.let{ temp.map(it) } ?: temp)
}
}
}

inline fun <reified E : Any, reified T : MutableCollection<E>> ListSetting(
name: String,
default: T,
): ListSetting<E, T> = ListSetting(name, default, object : TypeToken<T>() {}.type)
noinline reloader: ((E) -> E)? = null
): ListSetting<E, T> = ListSetting(name, default, object : TypeToken<T>() {}.type, reloader)
3 changes: 2 additions & 1 deletion src/main/kotlin/me/odinmain/commands/CommandRegistry.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ object CommandRegistry {
termSimCommand, chatCommandsCommand,
devCommand, highlightCommand,
waypointCommand, dungeonWaypointsCommand,
petCommand, visualWordsCommand, PosMsgCommand
petCommand, visualWordsCommand, PosMsgCommand,
CountdownsCommand
)

fun add(vararg commands: Commodore) {
Expand Down
58 changes: 58 additions & 0 deletions src/main/kotlin/me/odinmain/commands/impl/CountdownsCommand.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package me.odinmain.commands.impl

import com.github.stivais.commodore.Commodore
import com.github.stivais.commodore.utils.GreedyString
import me.odinmain.config.Config
import me.odinmain.features.impl.skyblock.Countdowns.countdownTriggers
import me.odinmain.features.impl.skyblock.Countdowns.CountdownTrigger
import me.odinmain.utils.addOrNull
import me.odinmain.utils.skyblock.modMessage
import me.odinmain.utils.toFixed

val CountdownsCommand = Commodore("countdowns") {
literal("add").runs { prefix: String, time: Int, message: GreedyString ->
val prefix = prefix.replace("&", "§")
countdownTriggers.addOrNull(CountdownTrigger(prefix.replace("&", "§"), time, false, message.string))
?: return@runs modMessage("This thing already exists!")
modMessage("$prefix${time.toFixed(divisor = 20)}, Triggers by \"$message\"")
Config.save()
}

literal("addregex").runs { prefix: String, time: Int, message: GreedyString ->
Copy link
Owner

Choose a reason for hiding this comment

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

Don't use runCatching here and add a string to the list and not a regex class

val prefix = prefix.replace("&", "§")
runCatching {
Regex(message.string)
}.onSuccess {
countdownTriggers.addOrNull(CountdownTrigger(prefix, time, true, message.string))
?: return@runs modMessage("This thing already exists!")
modMessage("$prefix${time.toFixed(divisor = 20)}, Triggers by regex \"$message\"")
Config.save()
}.onFailure {
modMessage("Bad regex!")
}
}

literal("remove").runs { index: Int ->
Copy link
Owner

Choose a reason for hiding this comment

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

Don't use runCatching here

runCatching {
countdownTriggers.removeAt(index)
}.onSuccess {
modMessage("Removed Countdown Trigger #$index")
Config.save()
}.onFailure {
modMessage("Theres no countdown trigger in position #$index")
}
}

literal("clear").runs {
countdownTriggers.clear()
modMessage("Cleared List")
Config.save()
}

literal("list").runs {
val output = countdownTriggers.withIndex().joinToString("\n") { (i, it) ->
"$i: ${it.prefix}${it.time.toFixed(divisor = 20)}&r, ${if (it.regex) "regex" else "normal"} \"${it.message}\""
}
modMessage(if (countdownTriggers.isEmpty()) "The list is empty!" else "Countdown Trigger list:\n$output")
}
}
16 changes: 14 additions & 2 deletions src/main/kotlin/me/odinmain/commands/impl/DevCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,20 @@ val devCommand = Commodore("oddev") {
""".trimIndent(), "")
}

literal("party").runs {
modMessage("${PartyUtils.isInParty}, ${PartyUtils.partyLeader}, ${PartyUtils.partyMembers}")
Copy link
Owner

Choose a reason for hiding this comment

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

Why is that needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

idk i didn't make these 2 lines

Copy link
Owner

Choose a reason for hiding this comment

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

I more like meant the forceInParty and forceIsLeader the entire changes here...

literal("party"){
runs {
modMessage("${PartyUtils.isInParty}, ${PartyUtils.partyLeader}, ${PartyUtils.partyMembers}")
}

literal("forceInParty").runs {
PartyUtils.forceInParty = !PartyUtils.forceInParty
modMessage("forceInParty is now: $PartyUtils.forceInParty")
}

literal("forceIsLeader").runs {
PartyUtils.forceIsLeader = !PartyUtils.forceIsLeader
modMessage("forceIsLeader is now: $PartyUtils.forceIsLeader")
}
}

literal("simulate").runs { str: GreedyString ->
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/me/odinmain/commands/impl/OdinCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ val mainCommand = Commodore("od", "odin") {
§3- /posmsg §7» §8Position message command.
§3- /chatclist §7» §8Used to configure your blacklist/whitelist.
§3- /highlight §7» §8Used to configure Highlight list.
§3- /countdowns §7» §8Make custom countdowns by chat triggers.
§3- /waypoint §7» §8Configure waypoints.
§3- /termsim <ping>? §7» §8Simulates terminals so you can practice them.
§3- /od rq §7» §8Requeues dungeon run.
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/me/odinmain/events/EventDispatcher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ object EventDispatcher {

if (event.packet is S29PacketSoundEffect && inDungeons && !inBoss && (event.packet.soundName.equalsOneOf("mob.bat.hurt", "mob.bat.death") && event.packet.volume == 0.1f)) SecretPickupEvent.Bat(event.packet).postAndCatch()

if (event.packet is S02PacketChat && ChatPacketEvent(event.packet.chatComponent.unformattedText.noControlCodes).postAndCatch())
if (event.packet is S02PacketChat && event.packet.type != 2.toByte() && ChatPacketEvent(event.packet.chatComponent.unformattedText.noControlCodes).postAndCatch())
event.isCanceled = true

}
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/me/odinmain/features/Module.kt
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,13 @@ abstract class Module(
fun HUD(
Copy link
Owner

Choose a reason for hiding this comment

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

Revert

name: String,
desc: String,
default: Boolean = true,
toggleable: Boolean = true,
x: Float = 10f,
y: Float = 10f,
scale: Float = 2f,
block: (example: Boolean) -> Pair<Number, Number>
): HUDSetting = HUDSetting(name, x, y, scale, toggleable, desc, this, block)
): HUDSetting = HUDSetting(name, x, y, scale, default, toggleable, desc, this, block)

/**
* Helper function to make cleaner code, and better performance, since we don't need multiple registers for packet received events.
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/me/odinmain/features/ModuleManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ object ModuleManager {
//skyblock
NoCursorReset, AutoSprint, BlazeAttunement, ChatCommands, DeployableTimer, DianaHelper, ArrowHit,
Ragnarock, MobSpawn, Splits, WardrobeKeybinds, InvincibilityTimer, ItemsHighlight, PlayerDisplay,
FarmKeys, PetKeybinds, CommandKeybinds, SpringBoots, AbilityTimers, SlotBinds,
FarmKeys, PetKeybinds, CommandKeybinds, SpringBoots, AbilityTimers, SlotBinds, Countdowns,

// kuudra
BuildHelper, FreshTools, KuudraDisplay, NoPre, PearlWaypoints, RemovePerks, SupplyHelper, TeamHighlight,
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/me/odinmain/features/impl/dungeon/MapInfo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ object MapInfo : Module(
return when {
score < 270 -> "§c${score}"
score < 300 -> "§e${score}"
else -> "§a${score}"
score < 305 -> "§a${score}"
else -> "§b${score}"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ object SpiritBear : Module(
name = "Spirit Bear",
description = "Displays the current state of Spirit Bear."
) {
Copy link
Owner

Choose a reason for hiding this comment

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

Revert

private val hud by HUD("Hud", "Displays the current state of Spirit Bear in the HUD.", false) { example ->
private val hud by HUD("Hud", "Displays the current state of Spirit Bear in the HUD.", toggleable = false) { example ->
when {
example -> "§e1.45s"
!DungeonUtils.isFloor(4) || !DungeonUtils.inBoss -> null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import me.odinmain.clickgui.settings.impl.BooleanSetting
import me.odinmain.clickgui.settings.impl.DropdownSetting
import me.odinmain.clickgui.settings.impl.ListSetting
import me.odinmain.events.impl.MessageSentEvent
import me.odinmain.events.impl.PacketEvent
import me.odinmain.features.Module
import me.odinmain.features.impl.dungeon.DungeonRequeue.disableRequeue
import me.odinmain.utils.*
import me.odinmain.utils.skyblock.*
import net.minecraft.event.ClickEvent
import net.minecraft.network.play.client.C01PacketChatMessage
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
Expand Down Expand Up @@ -52,6 +54,10 @@ object ChatCommands : Module(
private val location by BooleanSetting("Location", false, desc = "Sends your current location.").withDependency { showSettings }
private val holding by BooleanSetting("Holding", false, desc = "Sends the item you are holding.").withDependency { showSettings }

private val showExtraStuff by DropdownSetting("Show Extra Stuff", false)
private val noMoreLocraw by BooleanSetting("No More Locraw", false, desc = "Cancel repetitive /locraw commands.").withDependency { showExtraStuff }

private var locrawSent = false
private val dtReason = mutableListOf<Pair<String, String>>()
val blacklist: MutableList<String> by ListSetting("Blacklist", mutableListOf())

Expand Down Expand Up @@ -85,7 +91,10 @@ object ChatCommands : Module(
runIn(5) { handleChatCommands(msg, ign, channel) }
}

onWorldLoad { dtReason.clear() }
onWorldLoad {
locrawSent = false
dtReason.clear()
}
}

private fun handleChatCommands(message: String, name: String, channel: ChatChannel) {
Expand Down Expand Up @@ -187,6 +196,18 @@ object ChatCommands : Module(
sendChatMessage(words.joinToString(" "))
}

@SubscribeEvent
Copy link
Owner

Choose a reason for hiding this comment

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

Odin doesn't use locraw

Copy link
Contributor Author

Choose a reason for hiding this comment

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

skyhanni and oneconfig sending them at the same time for me

Copy link
Owner

Choose a reason for hiding this comment

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

Yea well this isn't skyhanni nor oneconfig

fun onPacket(event: PacketEvent.Send) {
(event.packet as? C01PacketChatMessage)?.let {
if (!noMoreLocraw || it.message != "/locraw") return
if (locrawSent) {
event.isCanceled = true
} else {
locrawSent = true
}
}
}

private val replacements = mapOf(
"<3" to "❤",
"o/" to "( ゚◡゚)/",
Expand Down
87 changes: 87 additions & 0 deletions src/main/kotlin/me/odinmain/features/impl/skyblock/Countdowns.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package me.odinmain.features.impl.skyblock


import me.odinmain.clickgui.settings.Setting.Companion.withDependency
import me.odinmain.clickgui.settings.impl.ActionSetting
import me.odinmain.clickgui.settings.impl.DropdownSetting
import me.odinmain.clickgui.settings.impl.ListSetting
import me.odinmain.events.impl.ServerTickEvent
import me.odinmain.features.Module
import me.odinmain.utils.addOrNull
import me.odinmain.utils.render.Colors
import me.odinmain.utils.toFixed
import me.odinmain.utils.ui.drawStringWidth
import me.odinmain.utils.ui.getMCTextHeight
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import java.util.concurrent.CopyOnWriteArrayList

object Countdowns : Module(
name = "Countdowns",
description = "Starts a countdown in HUD when you trigger certain chat message, or regex expressions."
) {
private val hud by HUD("Hud", "Displays something i can't tell.", toggleable = false) { example ->
if (example) return@HUD drawStringWidth("Some Countdown: 3.50s", 1f, 1f, Colors.WHITE) to 10f
if (countdowns.isEmpty()) return@HUD 0f to 0f

var w = 1f
var h = 1f
val lineHeight = getMCTextHeight()

countdowns.forEach {
w = maxOf(w, drawStringWidth(
"§r${it.prefix}${it.time.toFixed(divisor = 20)}",
1f, h, Colors.WHITE
))
h += lineHeight
}

w to h
}

data class CountdownTrigger(val prefix: String, val time: Int, val regex: Boolean, val message: String) {
Copy link
Owner

Choose a reason for hiding this comment

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

Parse Regex seperatly and refrain from changing ListSetting stracture

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh okay

@Transient
var realRegex: Regex? = if (regex) {
runCatching { Regex(message) }.getOrNull()
} else {
null
}
}
val countdownTriggers by ListSetting("Countdowns", mutableListOf<CountdownTrigger>()) { it.copy() }

Copy link
Owner

Choose a reason for hiding this comment

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

Don't have presets inside the mod

private val presetsDropdown by DropdownSetting("Add Presets")
private val presetQuiz by ActionSetting("Quiz", desc = "Quiz puzzle in dungeons. (2)") {
countdownTriggers.addOrNull(CountdownTrigger("§eQuiz: §f", 220, false, "[STATUE] Oruo the Omniscient: I am Oruo the Omniscient. I have lived many lives. I have learned all there is to know."))
countdownTriggers.addOrNull(CountdownTrigger("§eQuiz: §f", 140, true, "\\[STATUE\\] Oruo the Omniscient: \\w{1,16} answered Question #[12] correctly!"))
}.withDependency { presetsDropdown }
private val presetEndIsland by ActionSetting("End Island", desc = "Endstone & Dragon Protector spawn. (2)") {
countdownTriggers.addOrNull(CountdownTrigger("§eEndstone Protector: §f", 400, false, "The ground begins to shake as an Endstone Protector rises from below!"))
countdownTriggers.addOrNull(CountdownTrigger("§eDragon: §f", 174, true, "☬ \\w{1,16} placed a Summoning Eye! Brace yourselves! \\(8/8\\)"))
}.withDependency { presetsDropdown }
private val presetM3FireFreeze by ActionSetting("M3 Fire Freeze", desc = "The Professor. (1)") {
countdownTriggers.addOrNull(CountdownTrigger("§eFire Freeze: §f", 106, false, "[BOSS] The Professor: Oh? You found my Guardians' one weakness?"))
}.withDependency { presetsDropdown }

private data class Countdown(val prefix: String, var time: Int)
private val countdowns = CopyOnWriteArrayList<Countdown>()

init {
onMessage(Regex(".*")) { result ->
countdownTriggers.firstOrNull {
if (it.regex) (it.realRegex?.let { regex -> result.value.matches(regex) } ?: false) else (it.message == result.value)
}?.let {
countdowns.add(Countdown(it.prefix, it.time))
}
}

onWorldLoad {
countdowns.clear()
}
}

@SubscribeEvent
fun onServerTick(event: ServerTickEvent) {
countdowns.removeIf {
--it.time <= 0
}
}
}
17 changes: 7 additions & 10 deletions src/main/kotlin/me/odinmain/utils/ServerUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import me.odinmain.OdinMain.mc
import me.odinmain.events.impl.PacketEvent
import me.odinmain.utils.clock.Executor
import me.odinmain.utils.clock.Executor.Companion.register
import me.odinmain.utils.skyblock.sendCommand
import net.minecraft.network.play.server.S02PacketChat
import me.odinmain.utils.skyblock.LocationUtils.isOnHypixel
import net.minecraft.network.play.client.C16PacketClientStatus
import net.minecraft.network.play.server.S03PacketTimeUpdate
import net.minecraft.network.play.server.S37PacketStatistics
import net.minecraftforge.event.world.WorldEvent
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent

Expand All @@ -17,28 +18,24 @@ object ServerUtils {
var averagePing = 0f
private set

Copy link
Owner

Choose a reason for hiding this comment

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

Revert this

private val unknownCommandRegex = Regex("^/?Unknown command\\. Type \"/?help\" for help\\. \\('odingetpingcommand-----'\\)$")
private var pingStartTime = 0L
private var isPinging = false
private var prevTime = 0L

init {
Executor(2000, "ServerUtils") {
if (mc.isSingleplayer) return@Executor
if (!isOnHypixel) return@Executor
pingStartTime = System.nanoTime()
isPinging = true

sendCommand("odingetpingcommand-----")
mc.netHandler?.addToSendQueue(C16PacketClientStatus(C16PacketClientStatus.EnumState.REQUEST_STATS))
}.register()
}

@SubscribeEvent
fun onPacket(event: PacketEvent.Receive) {
when (event.packet) {
is S02PacketChat -> {
if (event.packet.chatComponent?.unformattedText?.noControlCodes?.let { unknownCommandRegex.matches(it) } == false) return
is S37PacketStatistics -> {
averagePing = (System.nanoTime() - pingStartTime) / 1e6f
event.isCanceled = true
isPinging = false
}

Expand All @@ -58,4 +55,4 @@ object ServerUtils {
averageTps = 20f
prevTime = 0L
}
}
}
Loading
Loading