diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index dfaac0e6d101..55b2f94c3645 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -237,6 +237,7 @@ import at.hannibal2.skyhanni.features.mining.DeepCavernsParkour import at.hannibal2.skyhanni.features.mining.HighlightMiningCommissionMobs import at.hannibal2.skyhanni.features.mining.KingTalismanHelper import at.hannibal2.skyhanni.features.mining.crystalhollows.CrystalHollowsNamesInCore +import at.hannibal2.skyhanni.features.mining.eventtracker.MiningEventDisplay import at.hannibal2.skyhanni.features.mining.eventtracker.MiningEventTracker import at.hannibal2.skyhanni.features.mining.powdertracker.PowderTracker import at.hannibal2.skyhanni.features.minion.InfernoMinionFeatures @@ -676,6 +677,7 @@ class SkyHanniMod { loadModule(ArachneChatMessageHider()) loadModule(ShowItemUuid()) loadModule(FrozenTreasureTracker) + loadModule(MiningEventDisplay) loadModule(SlayerRngMeterDisplay()) loadModule(GhostCounter) loadModule(RiftTimer()) diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningEventConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningEventConfig.java index 0c72e2b32717..4e8eee363d5f 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningEventConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningEventConfig.java @@ -1,50 +1,47 @@ package at.hannibal2.skyhanni.config.features.mining; import at.hannibal2.skyhanni.config.FeatureToggle; +import at.hannibal2.skyhanni.config.core.config.Position; import com.google.gson.annotations.Expose; import io.github.moulberry.moulconfig.annotations.ConfigEditorBoolean; +import io.github.moulberry.moulconfig.annotations.ConfigEditorDropdown; import io.github.moulberry.moulconfig.annotations.ConfigOption; public class MiningEventConfig { -// @Expose -// @ConfigOption(name = "Enabled", desc = "Show information about upcoming Dwarven Mines and Crystal Hollows mining events, also enables you sending data.") -// @ConfigEditorBoolean -// @FeatureToggle -// public boolean enabled = true; -// -// @Expose -// @ConfigOption(name = "Show Outside Mining Islands", desc = "Shows the event tracker when you are not inside of the Dwarven Mines or Crystal Hollows.") -// @ConfigEditorBoolean -// public boolean outsideMining = true; -// -// @Expose -// @ConfigOption(name = "What to Show", desc = "Choose which island's events are shown in the gui.") -// @ConfigEditorDropdown -// public ShowType showType = ShowType.BOTH; -// -// @Expose -// @ConfigOption(name = "Show Warnings For Events", desc = "Shows the warnings when select mining events are about to start.") -// @ConfigEditorBoolean -// @FeatureToggle -// public boolean showWarnings = false; - - //todo remove when released @Expose - @ConfigOption(name = "Send Test data", desc = "Sends test data to make sure the api works.") + @ConfigOption(name = "Enabled", desc = "Show information about upcoming Dwarven Mines and Crystal Hollows mining events, " + + "also enables you sending data. §eTakes up to a minute to sync new events.") @ConfigEditorBoolean @FeatureToggle - public boolean sendData = true; + public boolean enabled = true; -// @Expose -// @ConfigOption(name = "Events to Warn for", desc = "Choose which mining events you get warned about.") -// @ConfigEditorDraggableList -// public List eventsToWarn = new ArrayList<>(Collections.singletonList(MiningEvent.DOUBLE_POWDER)); + @Expose + @ConfigOption(name = "Show Outside Mining Islands", desc = "Shows the event tracker when you are not inside of the Dwarven Mines or Crystal Hollows.") + @ConfigEditorBoolean + public boolean outsideMining = false; + + @Expose + @ConfigOption(name = "What to Show", desc = "Choose which island's events are shown in the gui.") + @ConfigEditorDropdown + public ShowType showType = ShowType.BOTH; + + @Expose + @ConfigOption(name = "Compressed Format", desc = "Compresses the event names so that they are shorter.") + @ConfigEditorBoolean + public boolean compressedFormat = false; + + @Expose + @ConfigOption(name = "Show Passed Events", desc = "Shows the most recent passed event at the start greyed out. " + + "§eTakes a little while to save last event.") + @ConfigEditorBoolean + public boolean passedEvents = false; public enum ShowType { BOTH("Both Mining Islands"), CRYSTAL("Crystal Hollows Only"), DWARVEN("Dwarven Mines Only"), + CURRENT("Current Island Only"), ; private final String str; @@ -58,4 +55,7 @@ public String toString() { return str; } } + + @Expose + public Position position = new Position(15, 70, false, true); } diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEvent.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEvent.kt deleted file mode 100644 index f5d8e26d8066..000000000000 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEvent.kt +++ /dev/null @@ -1,26 +0,0 @@ -package at.hannibal2.skyhanni.features.mining.eventtracker - -import at.hannibal2.skyhanni.utils.StringUtils.removeColor -import kotlin.time.Duration -import kotlin.time.Duration.Companion.minutes -import kotlin.time.Duration.Companion.seconds - -enum class MiningEvent(val eventName: String, val defaultLength: Duration, private val colourCode: Char) { - GONE_WITH_THE_WIND("GONE WITH THE WIND", 18.minutes, '9'), - DOUBLE_POWDER("2X POWDER", 15.minutes, 'b'), - GOBLIN_RAID("GOBLIN RAID", 5.minutes, 'c'), - BETTER_TOGETHER("BETTER TOGETHER", 18.minutes, 'd'), - RAFFLE("RAFFLE", 160.seconds, '6'), - MITHRIL_GOURMAND("MITHRIL GOURMAND", 10.minutes, 'b'), - ; - - override fun toString(): String { - return "§$colourCode$eventName" - } - - companion object { - fun fromBossbarName(bossbarName: String): MiningEvent? { - return MiningEvent.entries.find { it.eventName == bossbarName.removeColor() } - } - } -} diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventData.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventData.kt index ee2033ed444e..9ce3d3c4914a 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventData.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventData.kt @@ -4,10 +4,39 @@ import at.hannibal2.skyhanni.data.IslandType import com.google.gson.annotations.Expose import com.google.gson.annotations.SerializedName -data class MiningEventData( +data class MiningEventDataSend( @Expose @SerializedName("server_type") val serverType: IslandType, @Expose @SerializedName("server_id") val serverId: String, - @Expose val event: MiningEvent, + @Expose val event: MiningEventType, @Expose @SerializedName("time_left") val timeRemaining: Long, @Expose @SerializedName("reporter_uuid") val uuid: String ) + +data class MiningEventDataReceive( + @Expose val success: Boolean, + @Expose val data: MiningEventData, + @Expose val cause: String +) + +data class MiningEventData( + @Expose @SerializedName("event_datas") val eventData: Map>, + @Expose @SerializedName("running_events") val runningEvents: Map>, + @Expose @SerializedName("total_lobbys") val totalLobbies: Map, + @Expose @SerializedName("update_in") val updateIn: Long, + @Expose @SerializedName("curr_time") val currentTime: Long +) + +data class EventData( + @Expose @SerializedName("starts_at_min") val startMin: Long, + @Expose @SerializedName("starts_at_max") val startMax: Long, + @Expose @SerializedName("ends_at_min") val endMin: Long, + @Expose @SerializedName("ends_at_max") val endMax: Long, + @Expose @SerializedName("lobby_count") val lobbyCount: Int +) + +data class RunningEventType( + @Expose val event: MiningEventType, + @Expose @SerializedName("ends_at") val endsAt: Long, + @Expose @SerializedName("lobby_count") val lobbyCount: Int, + @Expose @SerializedName("is_double") val isDoubleEvent: Boolean +) diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventDisplay.kt new file mode 100644 index 000000000000..7281da5a3870 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventDisplay.kt @@ -0,0 +1,91 @@ +package at.hannibal2.skyhanni.features.mining.eventtracker + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.features.mining.MiningEventConfig +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.LorenzTickEvent +import at.hannibal2.skyhanni.features.fame.ReminderUtils +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland +import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings +import at.hannibal2.skyhanni.utils.SimpleTimeMark.Companion.asTimeMark +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +object MiningEventDisplay { + private val config get() = SkyHanniMod.feature.mining.miningEvent + private var display = mutableListOf() + + private var dwarvenEvents = listOf() + private var crystalEvents = listOf() + private var lastDwarvenEvent: MiningEventType? = null + private var lastCrystalEvent: MiningEventType? = null + + @SubscribeEvent + fun onTick(event: LorenzTickEvent) { + if (!event.repeatSeconds(1)) return + updateDisplay() + } + + @SubscribeEvent + fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) { + if (!shouldDisplay()) return + config.position.renderStrings(display, posLabel = "Upcoming Events Display") + } + + private fun updateDisplay() { + display.clear() + updateEvents(IslandType.DWARVEN_MINES, dwarvenEvents, lastDwarvenEvent) + updateEvents(IslandType.CRYSTAL_HOLLOWS, crystalEvents, lastCrystalEvent) + } + + private fun updateEvents(islandType: IslandType, events: List, lastEvent: MiningEventType?) { + val shouldShow = when (config.showType) { + MiningEventConfig.ShowType.DWARVEN -> islandType == IslandType.DWARVEN_MINES + MiningEventConfig.ShowType.CRYSTAL -> islandType == IslandType.CRYSTAL_HOLLOWS + MiningEventConfig.ShowType.CURRENT -> islandType.isInIsland() + else -> true + } + + events.firstOrNull()?.let { firstEvent -> + if (firstEvent.endsAt.asTimeMark().isInPast()) { + when (islandType) { + IslandType.DWARVEN_MINES -> lastDwarvenEvent = firstEvent.event + IslandType.CRYSTAL_HOLLOWS -> lastCrystalEvent = firstEvent.event + else -> Unit + } + } + } + + if (shouldShow) { + val upcomingEvents = formatUpcomingEvents(events, lastEvent) + display.add("§a${islandType.displayName}§8: $upcomingEvents") + } + } + + private fun formatUpcomingEvents(events: List, lastEvent: MiningEventType?): String { + val upcoming = events.filter { !it.endsAt.asTimeMark().isInPast() } + .map { if (it.isDoubleEvent) "${it.event} §8-> ${it.event}" else it.event.toString() } + .toMutableList() + + if (upcoming.isEmpty()) upcoming.add("§7???") + if (config.passedEvents && upcoming.size < 4) lastEvent?.let { upcoming.add(0, it.toPastString()) } + return upcoming.joinToString(" §8-> ") + } + + fun updateData(eventData: MiningEventData) { + eventData.runningEvents.forEach { (islandType, events) -> + when (islandType) { + IslandType.DWARVEN_MINES -> dwarvenEvents = + (events.sortedBy { it.endsAt - it.event.defaultLength.inWholeMilliseconds }) + + IslandType.CRYSTAL_HOLLOWS -> crystalEvents = + (events.sortedBy { it.endsAt - it.event.defaultLength.inWholeMilliseconds }) + else -> Unit + } + } + } + + private fun shouldDisplay() = LorenzUtils.inSkyBlock && config.enabled && !ReminderUtils.isBusy() && + !(!config.outsideMining && !LorenzUtils.inAdvancedMiningIsland()) +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventTracker.kt index 010c62615f13..137aecec22ea 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventTracker.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventTracker.kt @@ -4,24 +4,24 @@ import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.config.ConfigManager import at.hannibal2.skyhanni.data.BossbarData import at.hannibal2.skyhanni.data.HypixelData -import at.hannibal2.skyhanni.data.IslandType import at.hannibal2.skyhanni.data.ScoreboardData import at.hannibal2.skyhanni.events.BossbarUpdateEvent import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.APIUtil import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland import at.hannibal2.skyhanni.utils.SimpleTimeMark import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher import at.hannibal2.skyhanni.utils.TabListData import at.hannibal2.skyhanni.utils.TimeUtils -import at.hannibal2.skyhanni.utils.getBoolean -import at.hannibal2.skyhanni.utils.getStringOrValue +import at.hannibal2.skyhanni.utils.fromJson import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import kotlinx.coroutines.launch import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds class MiningEventTracker { @@ -45,24 +45,24 @@ class MiningEventTracker { "(?:§.)*\\s+(?:§.)+§l(?.+) ENDED!" ) - private var lastRequestSent = SimpleTimeMark.farPast() + private val defaultCooldown = 1.minutes + private var lastWorldSwitch = SimpleTimeMark.farPast() private var eventEndTime = SimpleTimeMark.farPast() + private var lastSentEvent: MiningEventType? = null - private var lastSentEvent: MiningEvent? = null + private var canRequestAt = SimpleTimeMark.farPast() @SubscribeEvent fun onWorldChange(event: LorenzWorldChangeEvent) { - lastRequestSent = SimpleTimeMark.now() lastWorldSwitch = SimpleTimeMark.farPast() eventEndTime = SimpleTimeMark.farPast() - lastSentEvent = null } @SubscribeEvent fun onBossbarChange(event: BossbarUpdateEvent) { - if (!isEnabled()) return + if (!LorenzUtils.inAdvancedMiningIsland()) return if (lastWorldSwitch.passedSince() < 2.seconds) return if (!eventEndTime.isInPast()) { return @@ -78,7 +78,7 @@ class MiningEventTracker { @SubscribeEvent fun onChat(event: LorenzChatEvent) { - if (!isEnabled()) return + if (!LorenzUtils.inAdvancedMiningIsland()) return eventStartedPattern.matchMatcher(event.message) { sendData(group("event"), null) @@ -88,10 +88,21 @@ class MiningEventTracker { } } + @SubscribeEvent + fun onTick(event: LorenzTickEvent) { + if (!event.repeatSeconds(1)) return + if (!config.enabled) return + if (!LorenzUtils.inSkyBlock || (!config.outsideMining && !LorenzUtils.inAdvancedMiningIsland())) return + if (!canRequestAt.isInPast()) return + + fetchData() + } + private fun sendData(eventName: String, time: String?) { - val eventType = MiningEvent.fromBossbarName(eventName) + val eventType = MiningEventType.fromBossbarName(eventName) if (lastSentEvent == eventType) return if (eventType == null) { + if (!config.enabled) return ErrorManager.logErrorWithData( Exception("UnknownMiningEvent"), "Unknown mining event detected from string $eventName", "eventName" to eventName, @@ -121,7 +132,7 @@ class MiningEventTracker { return } - val miningEventData = MiningEventData( + val miningEventData = MiningEventDataSend( LorenzUtils.skyBlockIsland, serverId, eventType, @@ -129,28 +140,44 @@ class MiningEventTracker { LorenzUtils.getPlayerUuid() ) val miningEventJson = ConfigManager.gson.toJson(miningEventData) -// //todo remove -// println("\n```json$miningEventJson```") SkyHanniMod.coroutineScope.launch { sendData(miningEventJson) } } - private fun isEnabled() = (IslandType.DWARVEN_MINES.isInIsland() || IslandType.CRYSTAL_HOLLOWS.isInIsland()) - && config.sendData -// && config.enabled - private fun sendData(json: String) { val response = APIUtil.postJSON("https://api.soopy.dev/skyblock/chevents/set", json) if (!response.success) return - val success = response.data.getBoolean("success") - if (!success) { - val cause = response.data.getStringOrValue("cause", "unknown") + + val formattedResponse = ConfigManager.gson.fromJson(response.data) + if (!formattedResponse.success) { + if (!config.enabled) return ErrorManager.logErrorWithData( Exception("PostFailure"), "Sending mining event data was unsuccessful", - "cause" to cause, + "cause" to formattedResponse.cause, "sentData" to json ) } } + + private fun fetchData() { + canRequestAt = SimpleTimeMark.now() + defaultCooldown + SkyHanniMod.coroutineScope.launch { + val data = APIUtil.getJSONResponse("https://api.soopy.dev/skyblock/chevents/get") + val miningEventData = ConfigManager.gson.fromJson(data, MiningEventDataReceive::class.java) + + if (!miningEventData.success) { + ErrorManager.logErrorWithData( + Exception("PostFailure"), "Sending mining event data was unsuccessful", + "cause" to miningEventData.cause, + "recievedData" to data + ) + return@launch + } + + canRequestAt = SimpleTimeMark.now() + miningEventData.data.updateIn.milliseconds + + MiningEventDisplay.updateData(miningEventData.data) + } + } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventType.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventType.kt new file mode 100644 index 000000000000..b8fb1d35a0ac --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventType.kt @@ -0,0 +1,36 @@ +package at.hannibal2.skyhanni.features.mining.eventtracker + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import kotlin.time.Duration +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds + +enum class MiningEventType( + val eventName: String, + private val shortName: String, + val defaultLength: Duration, + private val colourCode: Char +) { + GONE_WITH_THE_WIND("GONE WITH THE WIND", "Wind", 18.minutes, '9'), + DOUBLE_POWDER("2X POWDER", "2x", 15.minutes, 'b'), + GOBLIN_RAID("GOBLIN RAID", "Raid", 5.minutes, 'c'), + BETTER_TOGETHER("BETTER TOGETHER", "Better", 18.minutes, 'd'), + RAFFLE("RAFFLE", "Raffle", 160.seconds, '6'), + MITHRIL_GOURMAND("MITHRIL GOURMAND", "Gourmand", 10.minutes, 'b'), + ; + + override fun toString() = + if (config.compressedFormat) "§$colourCode$shortName" else "§$colourCode$eventName" + + fun toPastString() = + if (config.compressedFormat) "§7$shortName" else "§7$eventName" + + companion object { + private val config get() = SkyHanniMod.feature.mining.miningEvent + + fun fromBossbarName(bossbarName: String): MiningEventType? { + return MiningEventType.entries.find { it.eventName == bossbarName.removeColor() } + } + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/JsonUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/JsonUtils.kt index e9d755fab2a2..81f6e82fd6eb 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/JsonUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/JsonUtils.kt @@ -13,27 +13,3 @@ inline fun Gson.fromJson(jsonElement: JsonElement): T = this.fromJson(jsonElement, typeOf().javaType) inline fun Gson.fromJson(reader: Reader): T = this.fromJson(reader, typeOf().javaType) - -fun JsonObject.getBoolean(key: String): Boolean { - return if (has(key)) { - try { - get(key).asBoolean - } catch (_: Exception) { - false - } - } else { - false - } -} - -fun JsonObject.getStringOrValue(key: String, alternative: String): String { - return if (has(key)) { - try { - get(key).asString - } catch (_: Exception) { - alternative - } - } else { - alternative - } -} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt index 80fca61789b4..7df997f0dbde 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt @@ -366,6 +366,8 @@ object LorenzUtils { @Deprecated("moved", ReplaceWith("ChatUtils.sendMessageToServer(message)")) fun sendMessageToServer(message: String) = ChatUtils.sendMessageToServer(message) + fun inAdvancedMiningIsland() = IslandType.DWARVEN_MINES.isInIsland() || IslandType.CRYSTAL_HOLLOWS.isInIsland() + fun inMiningIsland() = IslandType.GOLD_MINES.isInIsland() || IslandType.DEEP_CAVERNS.isInIsland() - || IslandType.DWARVEN_MINES.isInIsland() || IslandType.CRYSTAL_HOLLOWS.isInIsland() + || inAdvancedMiningIsland() }