aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/features
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-08-28 19:04:24 +0200
committerLinnea Gräf <nea@nea.moe>2024-08-28 19:04:24 +0200
commitd2f240ff0ca0d27f417f837e706c781a98c31311 (patch)
tree0db7aff6cc14deaf36eed83889d59fd6b3a6f599 /src/main/kotlin/features
parenta6906308163aa3b2d18fa1dc1aa71ac9bbcc83ab (diff)
downloadFirmament-d2f240ff0ca0d27f417f837e706c781a98c31311.tar.gz
Firmament-d2f240ff0ca0d27f417f837e706c781a98c31311.tar.bz2
Firmament-d2f240ff0ca0d27f417f837e706c781a98c31311.zip
Refactor source layout
Introduce compat source sets and move all kotlin sources to the main directory [no changelog]
Diffstat (limited to 'src/main/kotlin/features')
-rw-r--r--src/main/kotlin/features/FeatureManager.kt120
-rw-r--r--src/main/kotlin/features/FirmamentFeature.kt23
-rw-r--r--src/main/kotlin/features/chat/AutoCompletions.kt57
-rw-r--r--src/main/kotlin/features/chat/ChatLinks.kt161
-rw-r--r--src/main/kotlin/features/chat/QuickCommands.kt100
-rw-r--r--src/main/kotlin/features/debug/DebugLogger.kt13
-rw-r--r--src/main/kotlin/features/debug/DebugView.kt38
-rw-r--r--src/main/kotlin/features/debug/DeveloperFeatures.kt55
-rw-r--r--src/main/kotlin/features/debug/MinorTrolling.kt27
-rw-r--r--src/main/kotlin/features/debug/PowerUserTools.kt193
-rw-r--r--src/main/kotlin/features/diana/AncestralSpadeSolver.kt131
-rw-r--r--src/main/kotlin/features/diana/DianaWaypoints.kt35
-rw-r--r--src/main/kotlin/features/diana/NearbyBurrowsSolver.kt144
-rw-r--r--src/main/kotlin/features/events/anniversity/AnniversaryFeatures.kt224
-rw-r--r--src/main/kotlin/features/events/carnival/CarnivalFeatures.kt17
-rw-r--r--src/main/kotlin/features/events/carnival/MinesweeperHelper.kt276
-rw-r--r--src/main/kotlin/features/fixes/CompatibliltyFeatures.kt51
-rw-r--r--src/main/kotlin/features/fixes/Fixes.kt71
-rw-r--r--src/main/kotlin/features/inventory/CraftingOverlay.kt66
-rw-r--r--src/main/kotlin/features/inventory/ItemRarityCosmetics.kt85
-rw-r--r--src/main/kotlin/features/inventory/PriceData.kt51
-rw-r--r--src/main/kotlin/features/inventory/SaveCursorPosition.kt66
-rw-r--r--src/main/kotlin/features/inventory/SlotLocking.kt203
-rw-r--r--src/main/kotlin/features/inventory/buttons/InventoryButton.kt85
-rw-r--r--src/main/kotlin/features/inventory/buttons/InventoryButtonEditor.kt184
-rw-r--r--src/main/kotlin/features/inventory/buttons/InventoryButtonTemplates.kt35
-rw-r--r--src/main/kotlin/features/inventory/buttons/InventoryButtons.kt88
-rw-r--r--src/main/kotlin/features/inventory/storageoverlay/StorageBackingHandle.kt53
-rw-r--r--src/main/kotlin/features/inventory/storageoverlay/StorageData.kt21
-rw-r--r--src/main/kotlin/features/inventory/storageoverlay/StorageOverlay.kt154
-rw-r--r--src/main/kotlin/features/inventory/storageoverlay/StorageOverlayCustom.kt98
-rw-r--r--src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt296
-rw-r--r--src/main/kotlin/features/inventory/storageoverlay/StorageOverviewScreen.kt123
-rw-r--r--src/main/kotlin/features/inventory/storageoverlay/StoragePageSlot.kt66
-rw-r--r--src/main/kotlin/features/inventory/storageoverlay/VirtualInventory.kt65
-rw-r--r--src/main/kotlin/features/mining/Histogram.kt81
-rw-r--r--src/main/kotlin/features/mining/PickaxeAbility.kt176
-rw-r--r--src/main/kotlin/features/mining/PristineProfitTracker.kt133
-rw-r--r--src/main/kotlin/features/notifications/Notifications.kt7
-rw-r--r--src/main/kotlin/features/texturepack/AlwaysPredicate.kt17
-rw-r--r--src/main/kotlin/features/texturepack/AndPredicate.kt26
-rw-r--r--src/main/kotlin/features/texturepack/BakedModelExtra.kt9
-rw-r--r--src/main/kotlin/features/texturepack/BakedOverrideData.kt8
-rw-r--r--src/main/kotlin/features/texturepack/CustomBlockTextures.kt295
-rw-r--r--src/main/kotlin/features/texturepack/CustomGlobalArmorOverrides.kt106
-rw-r--r--src/main/kotlin/features/texturepack/CustomGlobalTextures.kt167
-rw-r--r--src/main/kotlin/features/texturepack/CustomModelOverrideParser.kt74
-rw-r--r--src/main/kotlin/features/texturepack/CustomSkyBlockTextures.kt114
-rw-r--r--src/main/kotlin/features/texturepack/DisplayNamePredicate.kt22
-rw-r--r--src/main/kotlin/features/texturepack/ExtraAttributesPredicate.kt268
-rw-r--r--src/main/kotlin/features/texturepack/FirmamentModelPredicate.kt8
-rw-r--r--src/main/kotlin/features/texturepack/FirmamentModelPredicateParser.kt8
-rw-r--r--src/main/kotlin/features/texturepack/ItemPredicate.kt32
-rw-r--r--src/main/kotlin/features/texturepack/JsonUnbakedModelFirmExtra.kt10
-rw-r--r--src/main/kotlin/features/texturepack/LorePredicate.kt19
-rw-r--r--src/main/kotlin/features/texturepack/ModelOverrideData.kt7
-rw-r--r--src/main/kotlin/features/texturepack/ModelOverrideFilterSet.kt19
-rw-r--r--src/main/kotlin/features/texturepack/NotPredicate.kt18
-rw-r--r--src/main/kotlin/features/texturepack/NumberMatcher.kt125
-rw-r--r--src/main/kotlin/features/texturepack/OrPredicate.kt26
-rw-r--r--src/main/kotlin/features/texturepack/PetPredicate.kt66
-rw-r--r--src/main/kotlin/features/texturepack/RarityMatcher.kt69
-rw-r--r--src/main/kotlin/features/texturepack/StringMatcher.kt159
-rw-r--r--src/main/kotlin/features/world/FairySouls.kt131
-rw-r--r--src/main/kotlin/features/world/NPCWaypoints.kt40
-rw-r--r--src/main/kotlin/features/world/NavigableWaypoint.kt22
-rw-r--r--src/main/kotlin/features/world/NavigationHelper.kt121
-rw-r--r--src/main/kotlin/features/world/NpcWaypointGui.kt68
-rw-r--r--src/main/kotlin/features/world/Waypoints.kt297
69 files changed, 6223 insertions, 0 deletions
diff --git a/src/main/kotlin/features/FeatureManager.kt b/src/main/kotlin/features/FeatureManager.kt
new file mode 100644
index 0000000..19b91de
--- /dev/null
+++ b/src/main/kotlin/features/FeatureManager.kt
@@ -0,0 +1,120 @@
+
+
+package moe.nea.firmament.features
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.serializer
+import moe.nea.firmament.Firmament
+import moe.nea.firmament.annotations.generated.AllSubscriptions
+import moe.nea.firmament.events.FeaturesInitializedEvent
+import moe.nea.firmament.events.FirmamentEvent
+import moe.nea.firmament.events.subscription.Subscription
+import moe.nea.firmament.features.chat.AutoCompletions
+import moe.nea.firmament.features.chat.ChatLinks
+import moe.nea.firmament.features.chat.QuickCommands
+import moe.nea.firmament.features.debug.DebugView
+import moe.nea.firmament.features.debug.DeveloperFeatures
+import moe.nea.firmament.features.debug.MinorTrolling
+import moe.nea.firmament.features.debug.PowerUserTools
+import moe.nea.firmament.features.diana.DianaWaypoints
+import moe.nea.firmament.features.events.anniversity.AnniversaryFeatures
+import moe.nea.firmament.features.events.carnival.CarnivalFeatures
+import moe.nea.firmament.features.fixes.CompatibliltyFeatures
+import moe.nea.firmament.features.fixes.Fixes
+import moe.nea.firmament.features.inventory.CraftingOverlay
+import moe.nea.firmament.features.inventory.ItemRarityCosmetics
+import moe.nea.firmament.features.inventory.PriceData
+import moe.nea.firmament.features.inventory.SaveCursorPosition
+import moe.nea.firmament.features.inventory.SlotLocking
+import moe.nea.firmament.features.inventory.buttons.InventoryButtons
+import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlay
+import moe.nea.firmament.features.mining.PickaxeAbility
+import moe.nea.firmament.features.mining.PristineProfitTracker
+import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures
+import moe.nea.firmament.features.world.FairySouls
+import moe.nea.firmament.features.world.Waypoints
+import moe.nea.firmament.util.data.DataHolder
+
+object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "features", ::Config) {
+ @Serializable
+ data class Config(
+ val enabledFeatures: MutableMap<String, Boolean> = mutableMapOf()
+ )
+
+ private val features = mutableMapOf<String, FirmamentFeature>()
+
+ val allFeatures: Collection<FirmamentFeature> get() = features.values
+
+ private var hasAutoloaded = false
+
+ init {
+ autoload()
+ }
+
+ fun autoload() {
+ synchronized(this) {
+ if (hasAutoloaded) return
+ loadFeature(MinorTrolling)
+ loadFeature(FairySouls)
+ loadFeature(AutoCompletions)
+ // TODO: loadFeature(FishingWarning)
+ loadFeature(SlotLocking)
+ loadFeature(StorageOverlay)
+ loadFeature(PristineProfitTracker)
+ loadFeature(CraftingOverlay)
+ loadFeature(PowerUserTools)
+ loadFeature(Waypoints)
+ loadFeature(ChatLinks)
+ loadFeature(InventoryButtons)
+ loadFeature(CompatibliltyFeatures)
+ loadFeature(AnniversaryFeatures)
+ loadFeature(QuickCommands)
+ loadFeature(SaveCursorPosition)
+ loadFeature(CustomSkyBlockTextures)
+ loadFeature(PriceData)
+ loadFeature(Fixes)
+ loadFeature(DianaWaypoints)
+ loadFeature(ItemRarityCosmetics)
+ loadFeature(PickaxeAbility)
+ loadFeature(CarnivalFeatures)
+ if (Firmament.DEBUG) {
+ loadFeature(DeveloperFeatures)
+ loadFeature(DebugView)
+ }
+ allFeatures.forEach { it.config }
+ FeaturesInitializedEvent.publish(FeaturesInitializedEvent(allFeatures.toList()))
+ hasAutoloaded = true
+ }
+ }
+
+ fun subscribeEvents() {
+ AllSubscriptions.provideSubscriptions {
+ subscribeSingleEvent(it)
+ }
+ }
+
+ private fun <T : FirmamentEvent> subscribeSingleEvent(it: Subscription<T>) {
+ it.eventBus.subscribe(false, it.invoke)
+ }
+
+ fun loadFeature(feature: FirmamentFeature) {
+ synchronized(features) {
+ if (feature.identifier in features) {
+ Firmament.logger.error("Double registering feature ${feature.identifier}. Ignoring second instance $feature")
+ return
+ }
+ features[feature.identifier] = feature
+ feature.onLoad()
+ }
+ }
+
+ fun isEnabled(identifier: String): Boolean? =
+ data.enabledFeatures[identifier]
+
+
+ fun setEnabled(identifier: String, value: Boolean) {
+ data.enabledFeatures[identifier] = value
+ markDirty()
+ }
+
+}
diff --git a/src/main/kotlin/features/FirmamentFeature.kt b/src/main/kotlin/features/FirmamentFeature.kt
new file mode 100644
index 0000000..2cfc4fd
--- /dev/null
+++ b/src/main/kotlin/features/FirmamentFeature.kt
@@ -0,0 +1,23 @@
+
+
+package moe.nea.firmament.features
+
+import moe.nea.firmament.events.subscription.SubscriptionOwner
+import moe.nea.firmament.gui.config.ManagedConfig
+
+// TODO: remove this entire feature system and revamp config
+interface FirmamentFeature : SubscriptionOwner {
+ val identifier: String
+ val defaultEnabled: Boolean
+ get() = true
+ var isEnabled: Boolean
+ get() = FeatureManager.isEnabled(identifier) ?: defaultEnabled
+ set(value) {
+ FeatureManager.setEnabled(identifier, value)
+ }
+ override val delegateFeature: FirmamentFeature
+ get() = this
+ val config: ManagedConfig? get() = null
+ fun onLoad() {}
+
+}
diff --git a/src/main/kotlin/features/chat/AutoCompletions.kt b/src/main/kotlin/features/chat/AutoCompletions.kt
new file mode 100644
index 0000000..9144898
--- /dev/null
+++ b/src/main/kotlin/features/chat/AutoCompletions.kt
@@ -0,0 +1,57 @@
+
+
+package moe.nea.firmament.features.chat
+
+import com.mojang.brigadier.arguments.StringArgumentType.string
+import moe.nea.firmament.annotations.Subscribe
+import moe.nea.firmament.commands.get
+import moe.nea.firmament.commands.suggestsList
+import moe.nea.firmament.commands.thenArgument
+import moe.nea.firmament.commands.thenExecute
+import moe.nea.firmament.events.CommandEvent
+import moe.nea.firmament.events.MaskCommands
+import moe.nea.firmament.features.FirmamentFeature
+import moe.nea.firmament.gui.config.ManagedConfig
+import moe.nea.firmament.repo.RepoManager
+import moe.nea.firmament.util.MC
+
+object AutoCompletions : FirmamentFeature {
+
+ object TConfig : ManagedConfig(identifier) {
+ val provideWarpTabCompletion by toggle("warp-complete") { true }
+ val replaceWarpIsByWarpIsland by toggle("warp-is") { true }
+ }
+
+ override val config: ManagedConfig?
+ get() = TConfig
+ override val identifier: String
+ get() = "auto-completions"
+
+ @Subscribe
+ fun onMaskCommands(event: MaskCommands) {
+ if (TConfig.provideWarpTabCompletion) {
+ event.mask("warp")
+ }
+ }
+
+ @Subscribe
+ fun onCommandEvent(event: CommandEvent) {
+ if (!TConfig.provideWarpTabCompletion) return
+ event.deleteCommand("warp")
+ event.register("warp") {
+ thenArgument("to", string()) { toArg ->
+ suggestsList {
+ RepoManager.neuRepo.constants?.islands?.warps?.flatMap { listOf(it.warp) + it.aliases } ?: listOf()
+ }
+ thenExecute {
+ val warpName = get(toArg)
+ if (warpName == "is" && TConfig.replaceWarpIsByWarpIsland) {
+ MC.sendServerCommand("warp island")
+ } else {
+ MC.sendServerCommand("warp $warpName")
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/kotlin/features/chat/ChatLinks.kt b/src/main/kotlin/features/chat/ChatLinks.kt
new file mode 100644
index 0000000..f2cb78a
--- /dev/null
+++ b/src/main/kotlin/features/chat/ChatLinks.kt
@@ -0,0 +1,161 @@
+
+
+package moe.nea.firmament.features.chat
+
+import io.ktor.client.request.get
+import io.ktor.client.statement.bodyAsChannel
+import io.ktor.utils.io.jvm.javaio.toInputStream
+import java.net.URL
+import java.util.Collections
+import moe.nea.jarvis.api.Point
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.async
+import kotlin.math.min
+import net.minecraft.client.gui.screen.ChatScreen
+import net.minecraft.client.texture.NativeImage
+import net.minecraft.client.texture.NativeImageBackedTexture
+import net.minecraft.text.ClickEvent
+import net.minecraft.text.HoverEvent
+import net.minecraft.text.Style
+import net.minecraft.text.Text
+import net.minecraft.util.Formatting
+import net.minecraft.util.Identifier
+import moe.nea.firmament.Firmament
+import moe.nea.firmament.annotations.Subscribe
+import moe.nea.firmament.events.ModifyChatEvent
+import moe.nea.firmament.events.ScreenRenderPostEvent
+import moe.nea.firmament.features.FirmamentFeature
+import moe.nea.firmament.gui.config.ManagedConfig
+import moe.nea.firmament.util.MC
+import moe.nea.firmament.util.transformEachRecursively
+import moe.nea.firmament.util.unformattedString
+
+object ChatLinks : FirmamentFeature {
+ override val identifier: String
+ get() = "chat-links"
+
+ object TConfig : ManagedConfig(identifier) {
+ val enableLinks by toggle("links-enabled") { true }
+ val imageEnabled by toggle("image-enabled") { true }
+ val allowAllHosts by toggle("allow-all-hosts") { false }
+ val allowedHosts by string("allowed-hosts") { "cdn.discordapp.com,media.discordapp.com,media.discordapp.net,i.imgur.com" }
+ val actualAllowedHosts get() = allowedHosts.split(",").map { it.trim() }
+ val position by position("position", 16 * 20, 9 * 20) { Point(0.0, 0.0) }
+ }
+
+ private fun isHostAllowed(host: String) =
+ TConfig.allowAllHosts || TConfig.actualAllowedHosts.any { it.equals(host, ignoreCase = true) }
+
+ private fun isUrlAllowed(url: String) = isHostAllowed(url.removePrefix("https://").substringBefore("/"))
+
+ override val config get() = TConfig
+ val urlRegex = "https://[^. ]+\\.[^ ]+(\\.?( |$))".toRegex()
+
+ data class Image(
+ val texture: Identifier,
+ val width: Int,
+ val height: Int,
+ )
+
+ val imageCache: MutableMap<String, Deferred<Image?>> =
+ Collections.synchronizedMap(mutableMapOf<String, Deferred<Image?>>())
+
+ private fun tryCacheUrl(url: String) {
+ if (!isUrlAllowed(url)) {
+ return
+ }
+ if (url in imageCache) {
+ return
+ }
+ imageCache[url] = Firmament.coroutineScope.async {
+ try {
+ val response = Firmament.httpClient.get(URL(url))
+ if (response.status.value == 200) {
+ val inputStream = response.bodyAsChannel().toInputStream(Firmament.globalJob)
+ val image = NativeImage.read(inputStream)
+ val texture = MC.textureManager.registerDynamicTexture(
+ "dynamic_image_preview",
+ NativeImageBackedTexture(image)
+ )
+ Image(texture, image.width, image.height)
+ } else
+ null
+ } catch (exc: Exception) {
+ exc.printStackTrace()
+ null
+ }
+ }
+ }
+
+ val imageExtensions = listOf("jpg", "png", "gif", "jpeg")
+ fun isImageUrl(url: String): Boolean {
+ return (url.substringAfterLast('.').lowercase() in imageExtensions)
+ }
+
+ @Subscribe
+ @OptIn(ExperimentalCoroutinesApi::class)
+ fun onRender(it: ScreenRenderPostEvent) {
+ if (!TConfig.imageEnabled) return
+ if (it.screen !is ChatScreen) return
+ val hoveredComponent =
+ MC.inGameHud.chatHud.getTextStyleAt(it.mouseX.toDouble(), it.mouseY.toDouble()) ?: return
+ val hoverEvent = hoveredComponent.hoverEvent ?: return
+ val value = hoverEvent.getValue(HoverEvent.Action.SHOW_TEXT) ?: return
+ val url = urlRegex.matchEntire(value.unformattedString)?.groupValues?.get(0) ?: return
+ if (!isImageUrl(url)) return
+ val imageFuture = imageCache[url] ?: return
+ if (!imageFuture.isCompleted) return
+ val image = imageFuture.getCompleted() ?: return
+ it.drawContext.matrices.push()
+ val pos = TConfig.position
+ pos.applyTransformations(it.drawContext.matrices)
+ val scale = min(1F, min((9 * 20F) / image.height, (16 * 20F) / image.width))
+ it.drawContext.matrices.scale(scale, scale, 1F)
+ it.drawContext.drawTexture(
+ image.texture,
+ 0,
+ 0,
+ 1F,
+ 1F,
+ image.width,
+ image.height,
+ image.width,
+ image.height,
+ )
+ it.drawContext.matrices.pop()
+ }
+
+ @Subscribe
+ fun onModifyChat(it: ModifyChatEvent) {
+ if (!TConfig.enableLinks) return
+ it.replaceWith = it.replaceWith.transformEachRecursively { child ->
+ val text = child.string
+ if ("://" !in text) return@transformEachRecursively child
+ val s = Text.empty().setStyle(child.style)
+ var index = 0
+ while (index < text.length) {
+ val nextMatch = urlRegex.find(text, index)
+ if (nextMatch == null) {
+ s.append(Text.literal(text.substring(index, text.length)))
+ break
+ }
+ val range = nextMatch.groups[0]!!.range
+ val url = nextMatch.groupValues[0]
+ s.append(Text.literal(text.substring(index, range.first)))
+ s.append(
+ Text.literal(url).setStyle(
+ Style.EMPTY.withUnderline(true).withColor(
+ Formatting.AQUA
+ ).withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(url)))
+ .withClickEvent(ClickEvent(ClickEvent.Action.OPEN_URL, url))
+ )
+ )
+ if (isImageUrl(url))
+ tryCacheUrl(url)
+ index = range.last + 1
+ }
+ s
+ }
+ }
+}
diff --git a/src/main/kotlin/features/chat/QuickCommands.kt b/src/main/kotlin/features/chat/QuickCommands.kt
new file mode 100644
index 0000000..5944b92
--- /dev/null
+++ b/src/main/kotlin/features/chat/QuickCommands.kt
@@ -0,0 +1,100 @@
+
+
+package moe.nea.firmament.features.chat
+
+import com.mojang.brigadier.context.CommandContext
+import net.minecraft.text.Text
+import moe.nea.firmament.annotations.Subscribe
+import moe.nea.firmament.commands.DefaultSource
+import moe