diff options
13 files changed, 251 insertions, 50 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 8abfa321e..3ec268ef3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ + Added **Show Price** - Show the bazaar price of the items required for the visitors. + Added **Crop Milestone** Number - Show the number of the crop milestone in the inventory. + Added **Crop Upgrades** Number - Show the number of upgrades in the crop upgrades inventory. ++ Added **Visitor Timer** - Timer when the next visitor will appear, and a number how many visitors are already waiting. ### Features from other Mods > *The following features are only there because I want them when testing SkyHanni features without other mods present.* diff --git a/FEATURES.md b/FEATURES.md index 8cb6670df..7a8c1c211 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -165,6 +165,7 @@ + **Show Price** - Show the bazaar price of the items required for the visitors. + **Crop Milestone** Number - Show the number of the crop milestone in the inventory. + **Crop Upgrades** Number - Show the number of upgrades in the crop upgrades inventory. ++ **Visitor Timer** - Timer when the next visitor will appear, and a number how many visitors are already waiting. ## Commands - /wiki (using hypixel-skyblock.fandom.com instead of Hypixel wiki) diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java index a848ab072..5fde02e86 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java @@ -25,6 +25,7 @@ import at.hannibal2.skyhanni.features.event.diana.SoopyGuessBurrow; import at.hannibal2.skyhanni.features.fishing.*; import at.hannibal2.skyhanni.features.garden.GardenInventoryNumbers; import at.hannibal2.skyhanni.features.garden.GardenVisitorFeatures; +import at.hannibal2.skyhanni.features.garden.GardenVisitorTimer; import at.hannibal2.skyhanni.features.garden.SkyMartBestProfit; import at.hannibal2.skyhanni.features.inventory.*; import at.hannibal2.skyhanni.features.itemabilities.FireVeilWandParticles; @@ -52,6 +53,7 @@ import at.hannibal2.skyhanni.mixins.hooks.RenderLivingEntityHelper; import at.hannibal2.skyhanni.test.LorenzTest; import at.hannibal2.skyhanni.test.PacketTest; import at.hannibal2.skyhanni.utils.MinecraftConsoleFilter; +import at.hannibal2.skyhanni.utils.TabListData; import kotlin.coroutines.EmptyCoroutineContext; import kotlinx.coroutines.*; import net.minecraft.client.Minecraft; @@ -112,6 +114,7 @@ public class SkyHanniMod { loadModule(new RenderLivingEntityHelper()); loadModule(new SkillExperience()); loadModule(new InventoryData()); + loadModule(new TabListData()); //features loadModule(new BazaarOrderHelper()); @@ -201,6 +204,7 @@ public class SkyHanniMod { loadModule(new SkyMartBestProfit()); loadModule(new GardenVisitorFeatures()); loadModule(new GardenInventoryNumbers()); + loadModule(new GardenVisitorTimer()); Commands.INSTANCE.init(); diff --git a/src/main/java/at/hannibal2/skyhanni/config/Features.java b/src/main/java/at/hannibal2/skyhanni/config/Features.java index 13ebf5106..42c64290d 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/Features.java +++ b/src/main/java/at/hannibal2/skyhanni/config/Features.java @@ -146,6 +146,11 @@ public class Features extends Config { editOverlay(activeConfigCategory, 200, 16, garden.visitorHelperPos); return; } + + if (runnableId.equals("visitorHelperTimer")) { + editOverlay(activeConfigCategory, 200, 16, garden.visitorHelperTimerPos); + return; + } } @Expose diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/Garden.java b/src/main/java/at/hannibal2/skyhanni/config/features/Garden.java index 139567c97..45624e7f4 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/Garden.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/Garden.java @@ -29,6 +29,25 @@ public class Garden { public boolean visitorHelper = false; @Expose + @ConfigOption(name = "Visitor Timer", desc = "") + @ConfigAccordionId(id = 1) + @ConfigEditorAccordion(id = 2) + public boolean visitorHelperTimer = false; + + @Expose + @ConfigOption(name = "Visitor Timer", desc = "Timer when the next visitor will appear," + + "and a number how many visitors are already waiting.") + @ConfigEditorBoolean + @ConfigAccordionId(id = 2) + public boolean visitorHelperTimerEnabled = true; + + @Expose + @ConfigOption(name = "Visitor Timer Position", desc = "") + @ConfigEditorButton(runnableId = "visitorHelperTimer", buttonText = "Edit") + @ConfigAccordionId(id = 2) + public Position visitorHelperTimerPos = new Position(0, 0, false, true); + + @Expose @ConfigOption(name = "Visitor Display", desc = "Show all items needed for the visitors.") @ConfigEditorBoolean @ConfigAccordionId(id = 1) @@ -54,18 +73,18 @@ public class Garden { @Expose @ConfigOption(name = "Numbers", desc = "") - @ConfigEditorAccordion(id = 2) + @ConfigEditorAccordion(id = 3) public boolean numbers = false; @Expose @ConfigOption(name = "Crop Milestone", desc = "Show the number of the crop milestone in the inventory.") @ConfigEditorBoolean - @ConfigAccordionId(id = 2) + @ConfigAccordionId(id = 3) public boolean cropMilestoneNumber = true; @Expose @ConfigOption(name = "Crop Upgrades", desc = "Show the number of upgrades in the crop upgrades inventory.") @ConfigEditorBoolean - @ConfigAccordionId(id = 2) + @ConfigAccordionId(id = 3) public boolean cropUpgradesNumber = true; } diff --git a/src/main/java/at/hannibal2/skyhanni/data/HyPixelData.kt b/src/main/java/at/hannibal2/skyhanni/data/HyPixelData.kt index 174c5d3d0..458d94260 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/HyPixelData.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/HyPixelData.kt @@ -5,7 +5,7 @@ import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.events.ProfileJoinEvent import at.hannibal2.skyhanni.utils.LorenzLogger import at.hannibal2.skyhanni.utils.StringUtils.removeColor -import at.hannibal2.skyhanni.utils.TabListUtils +import at.hannibal2.skyhanni.utils.TabListData import net.minecraft.client.Minecraft import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -125,7 +125,7 @@ class HyPixelData { private fun checkIsland() { var newIsland = "" var guesting = false - for (line in TabListUtils.getTabList()) { + for (line in TabListData.getTabList()) { if (line.startsWith("§b§lArea: ")) { newIsland = line.split(": ")[1].removeColor() } diff --git a/src/main/java/at/hannibal2/skyhanni/events/TabListUpdateEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/TabListUpdateEvent.kt new file mode 100644 index 000000000..4524cbd05 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/events/TabListUpdateEvent.kt @@ -0,0 +1,3 @@ +package at.hannibal2.skyhanni.events + +class TabListUpdateEvent(val tabList: List<String>): LorenzEvent()
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenVisitorTimer.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenVisitorTimer.kt new file mode 100644 index 000000000..3125cfc36 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenVisitorTimer.kt @@ -0,0 +1,64 @@ +package at.hannibal2.skyhanni.features.garden + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.events.TabListUpdateEvent +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.RenderUtils.renderString +import at.hannibal2.skyhanni.utils.TimeUtils +import net.minecraftforge.client.event.RenderGameOverlayEvent +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Pattern + +class GardenVisitorTimer { + private val patternNextVisitor = Pattern.compile(" Next Visitor: §r§b(.*)") + private val patternVisitors = Pattern.compile("§b§lVisitors: §r§f\\((\\d)\\)") + private var render = "" + private var lastMillis = 0L + + @SubscribeEvent + fun onTick(event: TabListUpdateEvent) { + if (!isEnabled()) return + + var visitorsAmount = 0 + var millis = 15 * 60_000L + for (line in event.tabList) { + var matcher = patternNextVisitor.matcher(line) + if (matcher.matches()) { + val rawTime = matcher.group(1) + millis = TimeUtils.getMillis(rawTime) + } + + matcher = patternVisitors.matcher(line) + if (matcher.matches()) { + visitorsAmount = matcher.group(1).toInt() + } + } + + val diff = lastMillis - millis + if (diff == 0L) return + lastMillis = millis + + val extraSpeed = if (diff in 1001..10_000) { + val factor = diff / 1000 + "§f/§e" + TimeUtils.formatDuration(millis / factor) + } else "" + + val visitorLabel = if (visitorsAmount == 1) "Visitor" else "Visitors" + val formatDuration = TimeUtils.formatDuration(millis) + render = "§b$visitorsAmount $visitorLabel §f(Next in §e$formatDuration$extraSpeed§f)" + } + + @SubscribeEvent + fun onRenderOverlay(event: RenderGameOverlayEvent.Post) { + if (event.type != RenderGameOverlayEvent.ElementType.ALL) return + if (!isEnabled()) return + + SkyHanniMod.feature.garden.visitorHelperTimerPos.renderString(render) + } + + private fun isEnabled() = + LorenzUtils.inSkyBlock && + SkyHanniMod.feature.garden.visitorHelperTimerEnabled && + LorenzUtils.skyBlockIsland == IslandType.GARDEN +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/nether/reputationhelper/CrimsonIsleReputationHelper.kt b/src/main/java/at/hannibal2/skyhanni/features/nether/reputationhelper/CrimsonIsleReputationHelper.kt index c4d5d311e..887df0617 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/nether/reputationhelper/CrimsonIsleReputationHelper.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/nether/reputationhelper/CrimsonIsleReputationHelper.kt @@ -10,7 +10,7 @@ import at.hannibal2.skyhanni.features.nether.reputationhelper.miniboss.DailyMini import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzVec import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems -import at.hannibal2.skyhanni.utils.TabListUtils +import at.hannibal2.skyhanni.utils.TabListData import com.google.gson.JsonObject import net.minecraftforge.client.event.RenderGameOverlayEvent import net.minecraftforge.fml.common.eventhandler.EventPriority @@ -59,7 +59,7 @@ class CrimsonIsleReputationHelper(skyHanniMod: SkyHanniMod) { tick++ if (tick % 60 == 0) { - TabListUtils.getTabList() + TabListData.getTabList() .filter { it.contains("Reputation:") } .forEach { factionType = if (it.contains("Mage")) { diff --git a/src/main/java/at/hannibal2/skyhanni/features/nether/reputationhelper/dailyquest/QuestLoader.kt b/src/main/java/at/hannibal2/skyhanni/features/nether/reputationhelper/dailyquest/QuestLoader.kt index e4bba6494..2444cb0be 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/nether/reputationhelper/dailyquest/QuestLoader.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/nether/reputationhelper/dailyquest/QuestLoader.kt @@ -5,7 +5,7 @@ import at.hannibal2.skyhanni.features.nether.reputationhelper.dailyquest.quest.* import at.hannibal2.skyhanni.utils.InventoryUtils.getInventoryName import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.TabListUtils +import at.hannibal2.skyhanni.utils.TabListData import net.minecraft.client.Minecraft import net.minecraft.client.gui.inventory.GuiChest import net.minecraft.inventory.ContainerChest @@ -14,7 +14,7 @@ class QuestLoader(private val dailyQuestHelper: DailyQuestHelper) { fun loadFromTabList() { var i = -1 - for (line in TabListUtils.getTabList()) { + for (line in TabListData.getTabList()) { if (line.contains("Faction Quests:")) { i = 0 continue diff --git a/src/main/java/at/hannibal2/skyhanni/utils/TabListData.kt b/src/main/java/at/hannibal2/skyhanni/utils/TabListData.kt new file mode 100644 index 000000000..760e40450 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/TabListData.kt @@ -0,0 +1,117 @@ +package at.hannibal2.skyhanni.utils + +import at.hannibal2.skyhanni.events.PacketEvent +import at.hannibal2.skyhanni.events.TabListUpdateEvent +import at.hannibal2.skyhanni.utils.LorenzUtils.sorted +import net.minecraft.network.play.server.S38PacketPlayerListItem +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.* + +class TabListData { + + private val uuidMap = mutableMapOf<UUID, TabListPlayer>() + private val tabListMap = mutableMapOf<TabListPlayer, String>() + + class TabListPlayer(var displayName: String, var internalName: String) + + @SubscribeEvent + fun onChatPacket(event: PacketEvent.ReceiveEvent) { + val packet = event.packet + if (packet is S38PacketPlayerListItem) { + val action = packet.action + if (action == S38PacketPlayerListItem.Action.UPDATE_LATENCY) return + + val entries = packet.entries + if (action == S38PacketPlayerListItem.Action.REMOVE_PLAYER) { +// println("REMOVE_PLAYER") +// println("old: " + uuidMap.size) + for (entry in entries) { + val profile = entry.profile + val id = profile.id + val key = uuidMap.remove(id) + tabListMap.remove(key) + } +// println("new: " + uuidMap.size) + update() + return + } + + val size = entries.size + if (size != 1) { + println("wrong size: $size") + return + } + val entry = entries[0] + val profile = entry.profile + val id = profile.id + val name = profile.name + if (name != null) { + if (!name.contains("-")) { + return + } + } + + val text = entry?.displayName?.formattedText ?: "" + val formattedName = LorenzUtils.stripVanillaMessage(text) + if (action == S38PacketPlayerListItem.Action.ADD_PLAYER) { +// println("ADD_PLAYER") + val tabList = TabListPlayer(formattedName, name) + + if (uuidMap.contains(id)) { + val key = uuidMap.remove(id) + val internalName = key!!.internalName + val displayName = key.displayName +// println("") +// println("internalName: $internalName") +// println("displayName: $displayName") + tabListMap.remove(key) + } + +// println("new name: $name") +// println("new formattedName: $formattedName") + + uuidMap[id] = tabList + tabListMap[tabList] = name + update() + return + } + + if (action == S38PacketPlayerListItem.Action.UPDATE_DISPLAY_NAME) { +// println("UPDATE_DISPLAY_NAME") + val listPlayer = uuidMap[id]!! +// println("old: '" + listPlayer.displayName + "'") +// println("new: '$formattedName'") + + listPlayer.displayName = formattedName + update() + return + } + } + } + + private fun update() { + val result = mutableListOf<String>() + if (uuidMap.size == 80) { + var i = 0 + for (tabList in tabListMap.sorted().keys) { + val contains = uuidMap.values.contains(tabList) + if (contains) { + val displayName = tabList.displayName + result.add(displayName) + i++ + } + } + } else if (uuidMap.isNotEmpty()) return + val list = result.toList() + cache = list + TabListUpdateEvent(list).postAndCatch() + + } + + companion object { + private var cache = listOf<String>() + + // TODO replace with TabListUpdateEvent + fun getTabList() = cache + } +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/TabListUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/TabListUtils.kt deleted file mode 100644 index a81675f74..000000000 --- a/src/main/java/at/hannibal2/skyhanni/utils/TabListUtils.kt +++ /dev/null @@ -1,41 +0,0 @@ -package at.hannibal2.skyhanni.utils - -import com.google.common.collect.ComparisonChain -import com.google.common.collect.Ordering -import net.minecraft.client.Minecraft -import net.minecraft.client.network.NetworkPlayerInfo -import net.minecraft.world.WorldSettings -import net.minecraftforge.fml.relauncher.Side -import net.minecraftforge.fml.relauncher.SideOnly - -object TabListUtils { - - private val playerOrdering = Ordering.from(PlayerComparator()) - - @SideOnly(Side.CLIENT) - internal class PlayerComparator : Comparator<NetworkPlayerInfo> { - override fun compare(o1: NetworkPlayerInfo, o2: NetworkPlayerInfo): Int { - val team1 = o1.playerTeam - val team2 = o2.playerTeam - return ComparisonChain.start().compareTrueFirst( - o1.gameType != WorldSettings.GameType.SPECTATOR, - o2.gameType != WorldSettings.GameType.SPECTATOR - ) - .compare( - if (team1 != null) team1.registeredName else "", - if (team2 != null) team2.registeredName else "" - ) - .compare(o1.gameProfile.name, o2.gameProfile.name).result() - } - } - - fun getTabList(): List<String> { - val players = playerOrdering.sortedCopy(Minecraft.getMinecraft().thePlayer.sendQueue.playerInfoMap) - val result: MutableList<String> = ArrayList() - for (info in players) { - val name = Minecraft.getMinecraft().ingameGUI.tabList.getPlayerName(info) - result.add(LorenzUtils.stripVanillaMessage(name)) - } - return result - } -}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt index 107395886..f7a684630 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt @@ -1,6 +1,12 @@ package at.hannibal2.skyhanni.utils +import java.util.regex.Pattern + object TimeUtils { + + private val pattern = + Pattern.compile("(?:(?<y>\\d+)y(?:\\w* ?)?)?(?:(?<d>\\d+)d(?:\\w* ?)?)?(?:(?<h>\\d+)h(?:\\w* ?)?)?(?:(?<m>\\d+)m(?:\\w* ?)?)?(?:(?<s>\\d+)s(?:\\w* ?)?)?") + fun formatDuration( millis: Long, biggestUnit: TimeUnit = TimeUnit.YEAR, @@ -41,6 +47,28 @@ object TimeUtils { } return builder.toString() } + + fun getMillis(string: String): Long { + val matcher = pattern.matcher(string.lowercase().trim()) + if (!matcher.matches()) { + throw RuntimeException("Matcher is null for '$string'") + } + + val years = matcher.group("y")?.toLong() ?: 0L + val days = matcher.group("d")?.toLong() ?: 0L + val hours = matcher.group("h")?.toLong() ?: 0L + val minutes = matcher.group("m")?.toLong() ?: 0L + val seconds = matcher.group("s")?.toLong() ?: 0L + + var millis = 0L + millis += seconds * 1000 + millis += minutes * 60 * 1000 + millis += hours * 60 * 60 * 1000 + millis += days * 24 * 60 * 60 * 1000 + millis += (years * 365.25 * 24 * 60 * 60 * 1000).toLong() + + return millis + } } private const val FACTOR_SECONDS = 1000L |