aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorThunderblade73 <85900443+Thunderblade73@users.noreply.github.com>2024-06-03 19:38:55 +0200
committerGitHub <noreply@github.com>2024-06-03 19:38:55 +0200
commit0d288498a60718e2a165666ebd69b10aef658833 (patch)
tree7898767bcf970e77321a806afd9337e971d186e5 /src/main
parent960009ef6883b49fa13293f05567bd3d7e4977a3 (diff)
downloadskyhanni-0d288498a60718e2a165666ebd69b10aef658833.tar.gz
skyhanni-0d288498a60718e2a165666ebd69b10aef658833.tar.bz2
skyhanni-0d288498a60718e2a165666ebd69b10aef658833.zip
Backend: Widget abstractions (#1150)
Co-authored-by: Cal <cwolfson58@gmail.com>
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt90
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/model/TabWidget.kt438
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/WidgetUpdateEvent.kt14
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/TabListData.kt6
5 files changed, 503 insertions, 47 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
index b7e6d7417..a3143ee36 100644
--- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
+++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
@@ -64,6 +64,7 @@ import at.hannibal2.skyhanni.data.jsonobjects.local.VisualWordsJson
import at.hannibal2.skyhanni.data.mob.MobData
import at.hannibal2.skyhanni.data.mob.MobDebug
import at.hannibal2.skyhanni.data.mob.MobDetection
+import at.hannibal2.skyhanni.data.model.TabWidget
import at.hannibal2.skyhanni.data.repo.RepoManager
import at.hannibal2.skyhanni.events.LorenzTickEvent
import at.hannibal2.skyhanni.events.PreInitFinishedEvent
@@ -577,6 +578,7 @@ class SkyHanniMod {
loadModule(ChatUtils)
loadModule(FixedRateTimerManager())
loadModule(ChromaManager)
+ loadModule(TabWidget)
loadModule(HotmData)
loadModule(ContributorManager)
loadModule(TabComplete)
diff --git a/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt b/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt
index 166282582..6f9fdd138 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt
@@ -2,6 +2,7 @@ package at.hannibal2.skyhanni.data
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.config.ConfigManager.Companion.gson
+import at.hannibal2.skyhanni.data.model.TabWidget
import at.hannibal2.skyhanni.events.HypixelJoinEvent
import at.hannibal2.skyhanni.events.IslandChangeEvent
import at.hannibal2.skyhanni.events.LorenzChatEvent
@@ -9,6 +10,7 @@ import at.hannibal2.skyhanni.events.LorenzTickEvent
import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
import at.hannibal2.skyhanni.events.ProfileJoinEvent
import at.hannibal2.skyhanni.events.TabListUpdateEvent
+import at.hannibal2.skyhanni.events.WidgetUpdateEvent
import at.hannibal2.skyhanni.features.bingo.BingoAPI
import at.hannibal2.skyhanni.features.dungeon.DungeonAPI
import at.hannibal2.skyhanni.features.rift.RiftAPI
@@ -36,8 +38,7 @@ class HypixelData {
private val patternGroup = RepoPattern.group("data.hypixeldata")
private val islandNamePattern by patternGroup.pattern(
- "islandname",
- "(?:§.)*(Area|Dungeon): (?:§.)*(?<island>.*)"
+ "islandname", "(?:§.)*(Area|Dungeon): (?:§.)*(?<island>.*)"
)
private var lastLocRaw = SimpleTimeMark.farPast()
@@ -45,51 +46,41 @@ class HypixelData {
companion object {
private val patternGroup = RepoPattern.group("data.hypixeldata")
private val serverIdScoreboardPattern by patternGroup.pattern(
- "serverid.scoreboard",
- "§7\\d+/\\d+/\\d+ §8(?<servertype>[mM])(?<serverid>\\S+).*"
+ "serverid.scoreboard", "§7\\d+/\\d+/\\d+ §8(?<servertype>[mM])(?<serverid>\\S+).*"
)
private val serverIdTablistPattern by patternGroup.pattern(
- "serverid.tablist",
- " Server: §r§8(?<serverid>\\S+)"
+ "serverid.tablist", " Server: §r§8(?<serverid>\\S+)"
)
private val lobbyTypePattern by patternGroup.pattern(
- "lobbytype",
- "(?<lobbyType>.*lobby)\\d+"
+ "lobbytype", "(?<lobbyType>.*lobby)\\d+"
)
private val playerAmountPattern by patternGroup.pattern(
- "playeramount",
- "^\\s*(?:§.)+Players (?:§.)+\\((?<amount>\\d+)\\)\\s*$"
+ "playeramount", "^\\s*(?:§.)+Players (?:§.)+\\((?<amount>\\d+)\\)\\s*$"
)
private val playerAmountCoopPattern by patternGroup.pattern(
- "playeramount.coop",
- "^\\s*(?:§.)*Coop (?:§.)*\\((?<amount>\\d+)\\)\\s*$"
+ "playeramount.coop", "^\\s*(?:§.)*Coop (?:§.)*\\((?<amount>\\d+)\\)\\s*$"
)
private val playerAmountGuestingPattern by patternGroup.pattern(
- "playeramount.guesting",
- "^\\s*(?:§.)*Guests (?:§.)*\\((?<amount>\\d+)\\)\\s*$"
+ "playeramount.guesting", "^\\s*(?:§.)*Guests (?:§.)*\\((?<amount>\\d+)\\)\\s*$"
)
+
/**
* REGEX-TEST: §r§b§lParty §r§f(4)
*/
private val dungeonPartyAmountPattern by patternGroup.pattern(
- "playeramount.dungeonparty",
- "^\\s*(?:§.)+Party (?:§.)+\\((?<amount>\\d+)\\)\\s*$"
+ "playeramount.dungeonparty", "^\\s*(?:§.)+Party (?:§.)+\\((?<amount>\\d+)\\)\\s*$"
)
private val soloProfileAmountPattern by patternGroup.pattern(
- "solo.profile.amount",
- "^\\s*(?:§.)*Island\\s*$"
+ "solo.profile.amount", "^\\s*(?:§.)*Island\\s*$"
)
private val scoreboardVisitingAmoutPattern by patternGroup.pattern(
- "scoreboard.visiting.amount",
- "\\s+§.✌ §.\\(§.(?<currentamount>\\d+)§./(?<maxamount>\\d+)\\)"
+ "scoreboard.visiting.amount", "\\s+§.✌ §.\\(§.(?<currentamount>\\d+)§./(?<maxamount>\\d+)\\)"
)
private val guestPattern by patternGroup.pattern(
- "guesting.scoreboard",
- "SKYBLOCK GUEST"
+ "guesting.scoreboard", "SKYBLOCK GUEST"
)
private val scoreboardTitlePattern by patternGroup.pattern(
- "scoreboard.title",
- "SK[YI]BLOCK(?: CO-OP| GUEST)?"
+ "scoreboard.title", "SK[YI]BLOCK(?: CO-OP| GUEST)?"
)
/**
@@ -97,8 +88,7 @@ class HypixelData {
* REGEX-TEST: §5ф §dWizard Tower
*/
private val skyblockAreaPattern by patternGroup.pattern(
- "skyblock.area",
- "\\s*§(?<symbol>7⏣|5ф) §(?<color>.)(?<area>.*)"
+ "skyblock.area", "\\s*§(?<symbol>7⏣|5ф) §(?<color>.)(?<area>.*)"
)
var hypixelLive = false
@@ -125,12 +115,7 @@ class HypixelData {
// Data from locraw
var locrawData: JsonObject? = null
private var locraw: MutableMap<String, String> = mutableMapOf(
- "server" to "",
- "gametype" to "",
- "lobbyname" to "",
- "lobbytype" to "",
- "mode" to "",
- "map" to ""
+ "server" to "", "gametype" to "", "lobbyname" to "", "lobbytype" to "", "mode" to "", "map" to ""
)
val server get() = locraw["server"] ?: ""
@@ -158,7 +143,8 @@ class HypixelData {
}
ErrorManager.logErrorWithData(
- Exception("NoServerId"), "Could not find server id",
+ Exception("NoServerId"),
+ "Could not find server id",
"islandType" to LorenzUtils.skyBlockIsland,
"tablist" to TabListData.getTabList(),
"scoreboard" to ScoreboardData.sidebarLinesFormatted
@@ -302,10 +288,7 @@ class HypixelData {
// So, as requested by Hannibal, use locraw from
// NEU and have NEU send it.
// Remove this when NEU dependency is removed
- if (LorenzUtils.onHypixel &&
- locrawData == null &&
- lastLocRaw.passedSince() > 15.seconds
- ) {
+ if (LorenzUtils.onHypixel && locrawData == null && lastLocRaw.passedSince() > 15.seconds) {
lastLocRaw = SimpleTimeMark.now()
thread(start = true) {
Thread.sleep(1000)
@@ -342,7 +325,6 @@ class HypixelData {
val inSkyBlock = checkScoreboard()
if (inSkyBlock) {
- checkIsland()
checkSidebar()
checkCurrentServerId()
}
@@ -351,6 +333,14 @@ class HypixelData {
skyBlock = inSkyBlock
}
+ @SubscribeEvent
+ fun onTabListUpdate(event: WidgetUpdateEvent) {
+ when (event.widget) {
+ TabWidget.AREA -> checkIsland(event)
+ else -> Unit
+ }
+ }
+
private fun checkProfileName() {
if (profileName.isNotEmpty()) return
@@ -392,18 +382,23 @@ class HypixelData {
noTrade = ironman || stranded || bingo
}
- private fun checkIsland() {
- var foundIsland = ""
- TabListData.fullyLoaded = false
+ private fun checkIsland(event: WidgetUpdateEvent) {
+ val islandType: IslandType
+ val foundIsland: String
+ if (event.isClear()) {
+
+ TabListData.fullyLoaded = false
+ islandType = IslandType.NONE
+ foundIsland = ""
- TabListData.getTabList().matchFirst(islandNamePattern) {
- foundIsland = group("island").removeColor()
+ } else {
TabListData.fullyLoaded = true
+ // Can not use color coding, because of the color effect (§f§lSKYB§6§lL§e§lOCK§A§L GUEST)
+ val guesting = guestPattern.matches(ScoreboardData.objectiveTitle.removeColor())
+ foundIsland = TabWidget.AREA.matchMatcherFirstLine { group("island").removeColor() } ?: ""
+ islandType = getIslandType(foundIsland, guesting)
}
- // Can not use color coding, because of the color effect (§f§lSKYB§6§lL§e§lOCK§A§L GUEST)
- val guesting = guestPattern.matches(ScoreboardData.objectiveTitle.removeColor())
- val islandType = getIslandType(foundIsland, guesting)
if (skyBlockIsland != islandType) {
IslandChangeEvent(islandType, skyBlockIsland).postAndCatch()
if (islandType == IslandType.UNKNOWN) {
@@ -413,6 +408,9 @@ class HypixelData {
loggerIslandChange.log(islandType.name)
}
skyBlockIsland = islandType
+ if (TabListData.fullyLoaded) {
+ TabWidget.reSendEvents()
+ }
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/TabWidget.kt b/src/main/java/at/hannibal2/skyhanni/data/model/TabWidget.kt
new file mode 100644
index 000000000..d459e2a9e
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/model/TabWidget.kt
@@ -0,0 +1,438 @@
+package at.hannibal2.skyhanni.data.model
+
+import at.hannibal2.skyhanni.events.RepositoryReloadEvent
+import at.hannibal2.skyhanni.events.TabListUpdateEvent
+import at.hannibal2.skyhanni.events.WidgetUpdateEvent
+import at.hannibal2.skyhanni.utils.CollectionUtils.editCopy
+import at.hannibal2.skyhanni.utils.CollectionUtils.getOrNull
+import at.hannibal2.skyhanni.utils.ConditionalUtils.transformIf
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
+import at.hannibal2.skyhanni.utils.RegexUtils.matches
+import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
+import net.minecraftforge.fml.common.eventhandler.EventPriority
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.util.regex.Matcher
+import java.util.regex.Pattern
+
+private val repoGroup by RepoPattern.exclusiveGroup("tab.widget.enum")
+
+/**
+ * This class defines various widgets within the tab list, specifically focusing on the reading of the values.
+ * Each enum value corresponds to a distinct widget in the tab list, ensuring no overlap between them.
+ * The general info widget is broken up into multiple smaller ones.
+ * The class facilitates access to the lines associated with each widget and triggers events when a widget undergoes changes or becomes invisible.
+ */
+enum class TabWidget(
+ pattern0: String,
+) {
+ PLAYER_LIST(
+ // language=RegExp
+ "(?:§.)*Players (?:§.)*\\(\\d+\\)"
+ ),
+
+ /** This line holds no information, only here because every widget must be present */
+ INFO(
+ // language=RegExp
+ "(?:§.)*Info"
+ ),
+ AREA(
+ // language=RegExp
+ "(?:§.)*(Area|Dungeon): (?:§.)*(?<island>.*)"
+ ),
+ SERVER(
+ // language=RegExp
+ "Server: (?:§.)*(?<serverid>.*)"
+ ),
+ GEMS(
+ // language=RegExp
+ "Gems: (?:§.)*(?<gems>.*)"
+ ),
+ FAIRY_SOULS(
+ // language=RegExp
+ "Fairy Souls: (?:§.)*(?<got>\\d+)(?:§.)*\\/(?:§.)*(?<max>\\d+)"
+ ),
+ PROFILE(
+ // language=RegExp
+ "(?:§.)*Profile: (?:§.)*(?<profile>\\S+).*"
+ ),
+ SB_LEVEL(
+ // language=RegExp
+ "SB Level(?:§.)*: (?:§.)*\\[(?:§.)*(?<level>\\d+)(?:§.)*\\] (?:§.)*(?<xp>\\d+).*"
+ ),
+ BANK(
+ // language=RegExp
+ "Bank: (?:§.)*(?<amount>[^§]+)(?:(?:§.)* \\/ (?:§.)*(?<personal>.*))?"
+ ),
+ INTEREST(
+ // language=RegExp
+ "Interest: (?:§.)*(?<time>[^§]+)(?:§.)* \\((?<amount>[^)]+)\\)"
+ ),
+ SOULFLOW(
+ // language=RegExp
+ "Soulflow: (?:§.)*(?<amount>.*)"
+ ),
+ PET(
+ // language=RegExp
+ "(?:§.)*Pet:"
+ ),
+ PET_TRANING(
+ // language=RegExp
+ "(?:§.)*Pet Training:"
+ ),
+ PET_SITTER(
+ // language=RegExp
+ "Kat: .*"
+ ),
+ FIRE_SALE(
+ // language=RegExp
+ "(?:§.)*Fire Sales: .*"
+ ),
+ ELECTION(
+ // language=RegExp
+ "(?:§.)*Election: (?:§.)*(?<time>.*)"
+ ),
+ EVENT(
+ // language=RegExp
+ "(?:§.)*Event: (?:§.)*(?<event>.*)"
+ ),
+ SKILLS(
+ // language=RegExp
+ "(?:§.)*Skills: ?(?:§.)*(?<avg>[\\d.]*)"
+ ),
+ STATS(
+ // language=RegExp
+ "(?:§.)*Stats:"
+ ),
+ GUESTS(
+ // language=RegExp
+ "(?:§.)*Guests (?:§.)*.*"
+ ),
+ COOP(
+ // language=RegExp
+ "(?:§.)*Coop (?:§.)*.*"
+ ),
+ MINION(
+ // language=RegExp
+ "(?:§.)*Minions: (?:§.)*(?<used>\\d+)(?:§.)*/(?:§.)*(?<max>\\d+)"
+ ),
+ JERRY_ISLAND_CLOSING(
+ // language=RegExp
+ "Island closes in: (?:§.)*(?<time>.*)"
+ ),
+ NORTH_STARS(
+ // language=RegExp
+ "North Stars: (?:§.)*(?<amount>\\d+)"
+ ),
+ COLLECTION(
+ // language=RegExp
+ "(?:§.)*Collection:"
+ ),
+ JACOB_CONTEST(
+ // language=RegExp
+ "(?:§.)*Jacob's Contest:.*"
+ ),
+ SLAYER(
+ // language=RegExp
+ "(?:§.)*Slayer:"
+ ),
+ DAILY_QUESTS(
+ // language=RegExp
+ "(?:§.)*Daily Quests:"
+ ),
+ ACTIVE_EFFECTS(
+ // language=RegExp
+ "(?:§.)*Active Effects: (?:§.)*\\((?<amount>\\d+)\\)"
+ ),
+ BESTIARY(
+ // language=RegExp
+ "(?:§.)*Bestiary:"
+ ),
+ ESSENCE(
+ // language=RegExp
+ "(?:§.)*Essence:.*"
+ ),
+ FORGE(
+ // language=RegExp
+ "(?:§.)*Forges:"
+ ),
+ TIMERS(
+ // language=RegExp
+ "(?:§.)*Timers:"
+ ),
+ DUNGEON_STATS(
+ // language=RegExp
+ "Opened Rooms: (?:§.)*(?<opend>\\d+)"
+ ),
+ PARTY(
+ // language=RegExp
+ "(?:§.)*Party:.*"
+ ),
+ TRAPPER(
+ // language=RegExp
+ "(?:§.)*Trapper:"
+ ),
+ COMMISSIONS(
+ // language=RegExp
+ "(?:§.)*Commissions:"
+ ),
+ POWDER(
+ // language=RegExp
+ "(?:§.)*Powders:"
+ ),
+ CRYSTAL(
+ // language=RegExp
+ "(?:§.)*Crystals:"
+ ),
+ UNCLAIMED_CHESTS(
+ // language=RegExp
+ "Unclaimed chests: (?:§.)*(?<amount>\\d+)"
+ ),
+ RAIN(
+ // language=RegExp
+ "(?<type>Thunder|Rain): (?:§.)*(?<time>.*)"
+ ),
+ BROODMOTHER(
+ // language=RegExp
+ "Broodmother: (?:§.)*(?<time>.*)"
+ ),
+ EYES_PLACED(
+ // language=RegExp
+ "Eyes placed: (?:§.)*(?<amount>\\d).*|(?:§.)*Dragon spawned!|(?:§.)*Egg respawning!"
+ ),
+ PROTECTOR(
+ // language=RegExp
+ "Protector: (?:§.)*(?<time>.*)"
+ ),
+ DRAGON(
+ // language=RegExp
+ "(?:§.)*Dragon: (?:§.)*\\((?<type>[^)])\\)"
+ ),
+ VOLCANO(
+ // language=RegExp
+ "Volcano: (?:§.)*(?<time>.*)"
+ ),
+ REPUTATION(
+ // language=RegExp
+ "(?:§.)*(Barbarian|Mage) Reputation:"
+ ),
+ FACTION_QUESTS(
+ // language=RegExp
+ "(?:§.)*Faction Quests:"
+ ),
+ TROPHY_FISH(
+ // language=RegExp
+ "(?:§.)*Trophy Fish:"
+ ),
+ RIFT_INFO(
+ // language=RegExp
+ "(?:§.)*Good to know:"
+ ),
+ RIFT_SHEN(
+ // language=RegExp
+ "(?:§.)*Shen: (?:§.)*\\((?<time>[^)])\\)"
+ ),
+ RIFT_BARRY(
+ // language=RegExp
+ "(?:§.)*Advertisement:"
+ ),
+ COMPOSTER(
+ // language=RegExp
+ "(?:§.)*Composter:"
+ ),
+ GARDEN_LEVEL(
+ // language=RegExp
+ "Garden Level: (?:§.)*(?<level>.*)"
+ ),
+ COPPER(
+ // language=RegExp
+ "Copper: (?:§.)*(?<amount>\\d+)"
+ ),
+ PESTS(
+ // language=RegExp
+ "(?:§.)*Pests:"
+ ),
+ VISITORS(
+ // language=RegExp
+ "(?:§.)*Visitors: (?:§.)*\\((?<count>\\d+)\\)"
+ ),
+ CROP_MILESTONE(
+ // language=RegExp
+ "(?:§.)*Crop Milestones:"
+ ),
+ PRIVATE_ISLAND_CRYSTALS(
+ // language=RegExp
+ "Crystals: (?:§.)*(?<count>\\d+)"
+ ),
+ OLD_PET_SITTER(
+ // language=RegExp
+ "Pet Sitter:.*"
+ ),
+ DUNGEON_HUB_PROGRESS(
+ // language=RegExp
+ "(?:§.)*Dungeons:"
+ ),
+ DUNGEON_PUZZLE(
+ // language=RegExp
+ "(?:§.)*Puzzles: (?:§.)*\\((?<amount>\\d+)\\)"
+ ),
+ DUNGEON_PARTY(
+ // language=RegExp
+ "(?:§.)*Party (?:§.)*\\(\\d+\\)"
+ ),
+ DUNGEON_PLAYER_STATS(
+ // language=RegExp
+ "(?:§.)*Player Stats"
+ ),
+ DUNGEON_SKILLS_AND_STATS(
+ // language=RegExp
+ "(?:§.)*Skills: (?:§.)*\\w+ \\d+: (?:§.)*[\\d.]+%"
+ ),
+
+ /** This line holds no information, only here because every widget must be present */
+ DUNGEON_ACCOUNT_INFO_LINE(
+ // language=RegExp
+ "(?:§.)*Account Info"
+ ),
+ DUNGEON_STATS_LINE(
+ // language=RegExp
+ "(?:§.)*Dungeon Stats"
+ ),
+ FROZEN_CORPSES(
+ // language=RegExp
+ "§b§lFrozen Corpses:"
+ ),
+
+ ;
+
+ /** The pattern for the first line of the widget*/
+ val pattern by repoGroup.pattern(name.replace("_", ".").lowercase(), "\\s*$pattern0")
+
+ /** The current active information from tab list.
+ *
+ * When the widget isn't visible it will be empty
+ * */
+ var lines: List<String> = emptyList()
+ private set
+
+ /** Both are inclusive */
+ var boundary = -1 to -1
+
+ /** Is this widget currently visible in the tab list */
+ var isActive: Boolean = false
+ private set
+
+ /** Internal value for the checking to set [isActive] */
+ private var gotChecked = false
+
+ /** A [matchMatcher] for the first line using the pattern from the widget*/
+ inline fun <T> matchMatcherFirstLine(consumer: Matcher.() -> T) =
+ if (isActive)
+ pattern.matchMatcher(lines.first(), consumer)
+ else null
+
+ private fun postNewEvent(lines: List<String>) {
+ // Prevent Post if lines are equal
+ if (lines == this.lines) return
+ this.lines = lines
+ isActive = true
+ WidgetUpdateEvent(this, lines).postAndCatch()
+ }
+
+ private fun postClearEvent() {
+ lines = emptyList()
+ WidgetUpdateEvent(this, lines).postAndCatch()
+ }
+
+ /** Update the state of the widget, posts the clear if [isActive] == true && [gotChecked] == false */
+ private fun updateIsActive() {
+ if (isActive == gotChecked) return
+ isActive = gotChecked
+ if (!gotChecked) {
+ postClearEvent()
+ }
+ }
+
+ companion object {
+
+ /** The index for the start of each Widget (inclusive) */
+ private val separatorIndexes = mutableListOf<Pair<Int, TabWidget?>>()
+
+ /** Patterns that where loaded from a future version*/
+ private var extraPatterns: List<Pattern> = emptyList()
+
+ init {
+ entries.forEach { it.pattern }
+ }
+
+ @SubscribeEvent(priority = EventPriority.HIGH)
+ fun onTabListUpdate(event: TabListUpdateEvent) {
+ if (!LorenzUtils.inSkyBlock) {
+ if (separatorIndexes.isNotEmpty()) {
+ separatorIndexes.forEach { it.second?.updateIsActive() }
+ separatorIndexes.clear()
+ }
+ return
+ }
+
+ val tabList = filterTabList(event.tabList)
+
+ separatorIndexes.clear()
+
+ for ((index, line) in tabList.withIndex()) {
+ val match = entries.firstOrNull { it.pattern.matches(line) }
+ ?: if (extraPatterns.any { it.matches(line) }) null else continue
+ separatorIndexes.add(index to match)
+ }
+ separatorIndexes.add(tabList.size to null)
+
+ separatorIndexes.zipWithNext { (firstIndex, widget), (secondIndex, _) ->
+ widget?.boundary = firstIndex to secondIndex - 1
+ widget?.gotChecked = true
+ widget?.postNewEvent(tabList.subList(firstIndex, secondIndex).filter { it.isNotEmpty() })
+ }
+
+ entries.forEach { it.updateIsActive() }
+
+ separatorIndexes.forEach {
+ it.second?.gotChecked = false
+ }
+ }
+
+ @SubscribeEvent(priority = EventPriority.LOW)
+ fun onRepoReload(event: RepositoryReloadEvent) {
+ extraPatterns = repoGroup.getUnusedPatterns()
+ }
+
+ private fun filterTabList(tabList: List<String>): List<String> {
+ var playerListFound = false
+ var infoFound = false
+
+ val headers = generateSequence(0) { it + 20 }.take(4).map { it to tabList.getOrNull(it) }
+
+ val removeIndexes = mutableListOf<Int>()
+
+ for ((index, header) in headers) when {
+ PLAYER_LIST.pattern.matches(header) -> if (playerListFound) removeIndexes.add(index - removeIndexes.size) else playerListFound =
+ true
+
+ INFO.pattern.matches(header) -> if (infoFound) removeIndexes.add(index - removeIndexes.size) else infoFound =
+ true
+ }
+
+ return tabList.transformIf({ size > 81 }, { dropLast(size - 80) }).editCopy {
+ removeIndexes.forEach {
+ removeAt(it)
+ }
+ }
+ }
+
+ fun reSendEvents() = entries.forEach {
+ if (it.isActive) {
+ it.postNewEvent(it.lines)
+ } else {
+ it.postClearEvent()
+ }
+ }
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/events/WidgetUpdateEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/WidgetUpdateEvent.kt
new file mode 100644
index 000000000..60300628f
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/events/WidgetUpdateEvent.kt
@@ -0,0 +1,14 @@
+package at.hannibal2.skyhanni.events
+
+import at.hannibal2.skyhanni.data.model.TabWidget
+
+/** The events get send on change of the widget and on island switch */
+open class WidgetUpdateEvent(
+ val widget: TabWidget,
+ val lines: List<String>,
+) : LorenzEvent() {
+
+ fun isWidget(widgetType: TabWidget) = widget == widgetType
+
+ fun isClear() = lines.isEmpty()
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/TabListData.kt b/src/main/java/at/hannibal2/skyhanni/utils/TabListData.kt
index 22032ff84..e3df7086c 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/TabListData.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/TabListData.kt
@@ -1,6 +1,7 @@
package at.hannibal2.skyhanni.utils
import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.model.TabWidget
import at.hannibal2.skyhanni.events.LorenzTickEvent
import at.hannibal2.skyhanni.events.PacketEvent
import at.hannibal2.skyhanni.events.TabListUpdateEvent
@@ -70,7 +71,10 @@ object TabListData {
val tabHeader = header.conditionalTransform(noColor, { this.removeColor() }, { this })
val tabFooter = footer.conditionalTransform(noColor, { this.removeColor() }, { this })
- val string = "Header:\n\n$tabHeader\n\nBody:\n\n${resultList.joinToString("\n")}\n\nFooter:\n\n$tabFooter"
+ val widgets = TabWidget.entries.filter { it.isActive }
+ .joinToString("\n") { "\n${it.name} : \n${it.lines.joinToString("\n")}" }
+ val string =
+ "Header:\n\n$tabHeader\n\nBody:\n\n${resultList.joinToString("\n")}\n\nFooter:\n\n$tabFooter\n\nWidgets:$widgets"
OSUtils.copyToClipboard(string)
ChatUtils.chat("Tab list copied into the clipboard!")