aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2
diff options
context:
space:
mode:
authorThatGravyBoat <thatgravyboat@gmail.com>2024-10-09 08:14:16 -0230
committerGitHub <noreply@github.com>2024-10-09 12:44:16 +0200
commit2ebce56d4fa44fb12ec15a436feea6c43f7ecd8b (patch)
tree33781b931d7f453399cab746c8c78bac18a4f9a4 /src/main/java/at/hannibal2
parenta0229f243df1a158543e8b33923c839e7a017746 (diff)
downloadskyhanni-2ebce56d4fa44fb12ec15a436feea6c43f7ecd8b.tar.gz
skyhanni-2ebce56d4fa44fb12ec15a436feea6c43f7ecd8b.tar.bz2
skyhanni-2ebce56d4fa44fb12ec15a436feea6c43f7ecd8b.zip
Improvement: Add Suggestion Provider and guild support in tab complete (#2637)
Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
Diffstat (limited to 'src/main/java/at/hannibal2')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/commands/TabCompleteConfig.java12
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/GuildAPI.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/commands/PartyCommands.kt29
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/commands/suggestions/SuggestionEntry.kt31
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/commands/suggestions/SuggestionProvider.kt83
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/PlayerTabComplete.kt122
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/TabComplete.kt18
7 files changed, 208 insertions, 93 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/commands/TabCompleteConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/commands/TabCompleteConfig.java
index 790224761..388cec87d 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/commands/TabCompleteConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/commands/TabCompleteConfig.java
@@ -36,22 +36,22 @@ public class TabCompleteConfig {
public boolean party = true;
@Expose
- @ConfigOption(name = "VIP Visits", desc = "Tab-complete the visit to special users with cake souls on it.")
+ @ConfigOption(name = "Guild", desc = "Tab-complete Guild Members.")
@ConfigEditorBoolean
@FeatureToggle
- public boolean vipVisits = true;
+ public boolean guild = false;
@Expose
- @ConfigOption(name = "/gfs Sack", desc = "Tab-complete §e/gfs §7sack items.")
+ @ConfigOption(name = "VIP Visits", desc = "Tab-complete the visit to special users with cake souls on it.")
@ConfigEditorBoolean
@FeatureToggle
- public boolean gfsSack = true;
+ public boolean vipVisits = true;
@Expose
- @ConfigOption(name = "Party Commands", desc = "Tab-complete commonly used party commands.")
+ @ConfigOption(name = "/gfs Sack", desc = "Tab-complete §e/gfs §7sack items.")
@ConfigEditorBoolean
@FeatureToggle
- public boolean partyCommands = true;
+ public boolean gfsSack = true;
@Expose
@ConfigOption(name = "View Recipe", desc = "Tab-complete item IDs in the the Hypixel command §e/viewrecipe§7. Only items with recipes are tab completed.")
diff --git a/src/main/java/at/hannibal2/skyhanni/data/GuildAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/GuildAPI.kt
index 541eb323c..1e98b61e4 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/GuildAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/GuildAPI.kt
@@ -38,7 +38,7 @@ object GuildAPI {
}
}
- fun isInGuild(name: String) = ProfileStorageData.playerSpecific?.guildMembers?.let {
- name in it
- } ?: false
+ fun isInGuild(name: String) = name in getAllMembers()
+
+ fun getAllMembers() = ProfileStorageData.playerSpecific?.guildMembers ?: emptyList()
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/commands/PartyCommands.kt b/src/main/java/at/hannibal2/skyhanni/features/commands/PartyCommands.kt
index e7b3333dd..3cbd43a78 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/commands/PartyCommands.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/commands/PartyCommands.kt
@@ -2,7 +2,6 @@ package at.hannibal2.skyhanni.features.commands
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator
-import at.hannibal2.skyhanni.data.FriendAPI
import at.hannibal2.skyhanni.data.PartyAPI
import at.hannibal2.skyhanni.data.PartyAPI.partyLeader
import at.hannibal2.skyhanni.data.PartyAPI.transferVoluntaryPattern
@@ -11,7 +10,6 @@ import at.hannibal2.skyhanni.events.MessageSendToServerEvent
import at.hannibal2.skyhanni.features.misc.limbo.LimboTimeTracker
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
-import at.hannibal2.skyhanni.utils.EntityUtils
import at.hannibal2.skyhanni.utils.HypixelCommands
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.RegexUtils.matches
@@ -115,25 +113,9 @@ object PartyCommands {
if (command == "pk" || command == "pt" || command == "pp" && config.shortCommands) {
return PartyAPI.partyMembers
}
-
- if (command == "p" || command == "party") {
- val friends = if (config.tabComplete.friends) {
- FriendAPI.getAllFriends().filter { it.bestFriend || !config.tabComplete.onlyBestFriends }.map { it.name }
- } else {
- emptyList<String>()
- }
- val allOnLobby = EntityUtils.getPlayerEntities().map { it.name }
- return friends + getPartyCommands() + allOnLobby
- }
return null
}
- private fun getPartyCommands(): List<String> {
- return if (config.tabComplete.partyCommands && PartyAPI.partyMembers.isNotEmpty()) {
- otherPartyCommands
- } else emptyList()
- }
-
@SubscribeEvent
fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) {
event.move(5, "commands.usePartyTransferAlias", "commands.shortCommands")
@@ -157,14 +139,3 @@ object PartyCommands {
)
}
}
-
-private val otherPartyCommands = listOf(
- "Disband",
- "KickOffline",
- "Leave",
- "List",
- "Mute",
- "Private",
- "Warp",
- "Settings",
-)
diff --git a/src/main/java/at/hannibal2/skyhanni/features/commands/suggestions/SuggestionEntry.kt b/src/main/java/at/hannibal2/skyhanni/features/commands/suggestions/SuggestionEntry.kt
new file mode 100644
index 000000000..74fcb0ef0
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/commands/suggestions/SuggestionEntry.kt
@@ -0,0 +1,31 @@
+package at.hannibal2.skyhanni.features.commands.suggestions
+
+interface SuggestionEntry {
+
+ val suggestions: List<String>
+
+ fun isEntryFor(argument: String): Boolean {
+ return this.suggestions.any { it.equals(argument, ignoreCase = true) }
+ }
+}
+
+data class LiteralSuggestionEntry(override val suggestions: List<String>) : SuggestionEntry
+
+data class CompositeSuggestionEntry(val entries: List<SuggestionEntry>) : SuggestionEntry {
+ override val suggestions: List<String> get() = entries.flatMap { it.suggestions }
+ override fun isEntryFor(argument: String): Boolean = entries.any { it.isEntryFor(argument) }
+}
+
+data class ParentSuggestionEntry(val parent: SuggestionEntry, val children: List<SuggestionEntry>) : SuggestionEntry {
+ override val suggestions: List<String> get() = parent.suggestions
+ override fun isEntryFor(argument: String): Boolean = parent.isEntryFor(argument)
+}
+
+data class LazySuggestionEntry(val supplier: MutableList<String>.() -> Unit) : SuggestionEntry {
+ override val suggestions: List<String> get() = mutableListOf<String>().apply { supplier() }
+}
+
+data class ConditionalSuggestionEntry(val condition: () -> Boolean, val entry: SuggestionEntry) : SuggestionEntry {
+ override val suggestions: List<String> get() = if (condition()) entry.suggestions else emptyList()
+ override fun isEntryFor(argument: String): Boolean = entry.isEntryFor(argument)
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/commands/suggestions/SuggestionProvider.kt b/src/main/java/at/hannibal2/skyhanni/features/commands/suggestions/SuggestionProvider.kt
new file mode 100644
index 000000000..eda4c704c
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/commands/suggestions/SuggestionProvider.kt
@@ -0,0 +1,83 @@
+package at.hannibal2.skyhanni.features.commands.suggestions
+
+class SuggestionProvider {
+
+ private val entry = mutableListOf<SuggestionEntry>()
+
+ fun add(entry: SuggestionEntry) {
+ this.entry.add(entry)
+ }
+
+ private fun List<SuggestionEntry>.getEntryForPath(path: List<String>): SuggestionEntry? {
+ val entry = this.firstOrNull { it.isEntryFor(path.first()) }
+ val remainingPath = path.drop(1)
+ if (remainingPath.isNotEmpty()) {
+ if (entry is ParentSuggestionEntry) {
+ return entry.children.getEntryForPath(remainingPath)
+ }
+ return null
+ } else if (entry is ParentSuggestionEntry) {
+ return CompositeSuggestionEntry(entry.children)
+ }
+ return null
+ }
+
+ fun getSuggestions(command: String): List<String> {
+ val arguments = command.lowercase().split(" ")
+ val last = arguments.lastOrNull() ?: ""
+ val suggestions = mutableListOf<String>()
+ if (arguments.size != 1) {
+ entry.getEntryForPath(arguments.dropLast(1))?.suggestions?.let { suggestions.addAll(it) }
+ } else {
+ entry.forEach { suggestions.addAll(it.suggestions) }
+ }
+ return suggestions.filter { it.startsWith(last, ignoreCase = true) }
+ }
+
+ companion object {
+
+ fun build(builder: Builder.() -> Unit): SuggestionProvider {
+ val b = Builder()
+ b.builder()
+ return b.build()
+ }
+ }
+}
+
+class Builder {
+ private val entries = mutableListOf<SuggestionEntry>()
+
+ fun add(entry: SuggestionEntry) {
+ entries.add(entry)
+ }
+
+ fun conditional(condition: () -> Boolean, builder: Builder.() -> Unit) {
+ val childBuilder = Builder()
+ childBuilder.builder()
+ add(ConditionalSuggestionEntry(condition, CompositeSuggestionEntry(childBuilder.entries)))
+ }
+
+ fun literal(vararg literals: String) {
+ add(LiteralSuggestionEntry(literals.toList()))
+ }
+
+ fun lazy(supplier: () -> List<String>) {
+ add(LazySuggestionEntry { addAll(supplier()) })
+ }
+
+ fun group(vararg children: SuggestionEntry) {
+ add(CompositeSuggestionEntry(children.toList()))
+ }
+
+ fun parent(vararg literals: String, children: Builder.() -> Unit) {
+ val childBuilder = Builder()
+ childBuilder.children()
+ add(ParentSuggestionEntry(LiteralSuggestionEntry(literals.toList()), childBuilder.entries))
+ }
+
+ fun build(): SuggestionProvider {
+ val provider = SuggestionProvider()
+ entries.forEach { provider.add(it) }
+ return provider
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/PlayerTabComplete.kt b/src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/PlayerTabComplete.kt
index 070ab478e..95acec9a6 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/PlayerTabComplete.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/PlayerTabComplete.kt
@@ -3,9 +3,12 @@ package at.hannibal2.skyhanni.features.commands.tabcomplete
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator
import at.hannibal2.skyhanni.data.FriendAPI
+import at.hannibal2.skyhanni.data.GuildAPI
import at.hannibal2.skyhanni.data.PartyAPI
import at.hannibal2.skyhanni.data.jsonobjects.repo.VipVisitsJson
import at.hannibal2.skyhanni.events.RepositoryReloadEvent
+import at.hannibal2.skyhanni.features.commands.suggestions.LazySuggestionEntry
+import at.hannibal2.skyhanni.features.commands.suggestions.SuggestionProvider
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.EntityUtils
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
@@ -15,68 +18,97 @@ object PlayerTabComplete {
private val config get() = SkyHanniMod.feature.misc.commands.tabComplete
private var vipVisits = listOf<String>()
- private val ignoredCommandCategories = mapOf(
- "f" to listOf(PlayerCategory.FRIENDS),
- "friend" to listOf(PlayerCategory.FRIENDS),
- "msg" to listOf(),
- "w" to listOf(),
- "tell" to listOf(),
- "boop" to listOf(),
+ private val friendsEntry = lazyEntry { FriendAPI.getAllFriends().map { it.name } }
+ private val partyMembersEntry = lazyEntry { PartyAPI.partyMembers }
+ private val guildMembersEntry = lazyEntry { GuildAPI.getAllMembers() }
+ private val vipVisitsEntry = lazyEntry { vipVisits }
+ private val islandPlayersEntry = lazyEntry { EntityUtils.getPlayerEntities().map { it.name } }
+
+ private val suggestions = SuggestionProvider.build {
+ parent("f", "friend") {
+ parent("accept", "add", "deny") { add(getExcluding(PlayerCategory.FRIENDS)) }
+ parent("best") { add(friendsEntry) }
+ parent("remove", "nickname") { add(friendsEntry) }
+ parent("list") { literal("best") }
+ literal("help", "notifications", "removeall", "requests")
+ }
- "visit" to listOf(),
- "invite" to listOf(),
- "ah" to listOf(),
+ parent("g", "guild") {
+ parent("invite") { add(getExcluding(PlayerCategory.GUILD)) }
+ parent("kick", "transfer", "setrank", "promote", "demote") { add(guildMembersEntry) }
+ parent("mute", "unmute") {
+ add(guildMembersEntry)
+ literal("everyone")
+ }
+ parent("member") { add(guildMembersEntry) }
+ literal(
+ "top", "toggle", "tagcolor", "tag", "slow", "settings", "rename", "quest", "permissions", "party", "onlinemode",
+ "online", "officerchat", "notifications", "mypermissions", "motd", "menu", "members", "log", "leave", "info", "history",
+ "help", "discord", "disband", "create", "chat", "accept",
+ )
+ }
- "pv" to listOf(), // NEU's Profile Viewer
- "shmarkplayer" to listOf(), // SkyHanni's Mark Player
+ parent("p", "party") {
+ parent("accept", "invite") { add(getExcluding(PlayerCategory.PARTY)) }
+ conditional({ PartyAPI.partyMembers.isNotEmpty() }) {
+ parent("kick", "demote", "promote", "transfer") { add(partyMembersEntry) }
+ literal("chat", "disband", "kickoffline", "leave", "list", "mute", "poll", "private", "settings", "warp")
+ }
+ }
- "trade" to listOf(PlayerCategory.FRIENDS, PlayerCategory.PARTY),
- )
+ parent("w", "msg", "tell", "boop") { add(getExcluding()) }
- @SubscribeEvent
- fun onRepoReload(event: RepositoryReloadEvent) {
- val data = event.getConstant<VipVisitsJson>("VipVisits")
- vipVisits = data.vipVisits
- }
+ parent("visit") {
+ add(getExcluding())
+ conditional({ config.vipVisits }) {
+ add(vipVisitsEntry)
+ }
+ }
- @SubscribeEvent
- fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) {
- event.move(2, "misc.tabCompleteCommands", "commands.tabComplete")
+ parent("invite") { add(getExcluding()) }
+ parent("ah") { add(getExcluding()) }
+
+ parent("pv") { add(getExcluding()) }
+ parent("shmarkplayer") { add(getExcluding()) }
+
+ parent("trade") { add(islandPlayersEntry) }
}
enum class PlayerCategory {
FRIENDS,
ISLAND_PLAYERS,
PARTY,
+ GUILD,
}
- fun handleTabComplete(command: String): List<String>? {
- val ignoredCategories = ignoredCommandCategories[command] ?: return null
-
- return buildList {
+ private fun getExcluding(vararg categories: PlayerCategory) = LazySuggestionEntry {
+ if (config.friends && PlayerCategory.FRIENDS !in categories) {
+ addAll(FriendAPI.getAllFriends().filter { it.bestFriend || !config.onlyBestFriends }.map { it.name })
+ }
+ if (config.islandPlayers && PlayerCategory.ISLAND_PLAYERS !in categories) {
+ addAll(EntityUtils.getPlayerEntities().map { it.name })
+ }
+ if (config.party && PlayerCategory.PARTY !in categories) {
+ addAll(PartyAPI.partyMembers)
+ }
+ if (config.guild && PlayerCategory.GUILD !in categories) {
+ addAll(GuildAPI.getAllMembers())
+ }
+ }
- if (config.friends && PlayerCategory.FRIENDS !in ignoredCategories) {
- FriendAPI.getAllFriends().filter { it.bestFriend || !config.onlyBestFriends }.forEach { add(it.name) }
- }
+ private fun lazyEntry(getter: () -> List<String>) = LazySuggestionEntry { addAll(getter()) }
- if (config.islandPlayers && PlayerCategory.ISLAND_PLAYERS !in ignoredCategories) {
- for (entity in EntityUtils.getPlayerEntities()) {
- add(entity.name)
- }
- }
+ fun handleTabComplete(command: String): List<String>? = suggestions.getSuggestions(command).takeIf { it.isNotEmpty() }?.distinct()
- if (config.party && PlayerCategory.PARTY !in ignoredCategories) {
- for (member in PartyAPI.partyMembers) {
- add(member)
- }
- }
+ @SubscribeEvent
+ fun onRepoReload(event: RepositoryReloadEvent) {
+ val data = event.getConstant<VipVisitsJson>("VipVisits")
+ vipVisits = data.vipVisits
+ }
- if (config.vipVisits && command == "visit") {
- for (visit in vipVisits) {
- add(visit)
- }
- }
- }
+ @SubscribeEvent
+ fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) {
+ event.move(2, "misc.tabCompleteCommands", "commands.tabComplete")
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/TabComplete.kt b/src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/TabComplete.kt
index 602c4a3b9..aa66b6937 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/TabComplete.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/TabComplete.kt
@@ -14,20 +14,18 @@ object TabComplete {
@SubscribeEvent
fun handleTabComplete(event: TabCompletionEvent) {
val splits = event.leftOfCursor.split(" ")
- if (splits.size > 1) {
- var command = splits.first().lowercase()
- if (command.startsWith("/")) {
- command = command.substring(1)
- customTabComplete(command)?.let {
- event.addSuggestions(it)
- }
- }
+ if (splits.size <= 1) return
+ var command = splits.first().lowercase()
+ if (!command.startsWith("/")) return
+ command = command.substring(1)
+ customTabComplete(event.leftOfCursor.substring(1), command)?.let {
+ event.addSuggestions(it)
}
}
- private fun customTabComplete(command: String): List<String>? {
+ private fun customTabComplete(fullCommand: String, command: String): List<String>? {
GetFromSacksTabComplete.handleTabComplete(command)?.let { return it }
- PlayerTabComplete.handleTabComplete(command)?.let { return it }
+ PlayerTabComplete.handleTabComplete(fullCommand)?.let { return it }
CollectionTracker.handleTabComplete(command)?.let { return it }
PartyCommands.customTabComplete(command)?.let { return it }
ViewRecipeCommand.customTabComplete(command)?.let { return it }