aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java67
-rw-r--r--src/main/java/moe/nea/firmament/mixins/accessor/AccessorChatHud.java14
-rw-r--r--src/main/kotlin/commands/rome.kt11
-rw-r--r--src/main/kotlin/events/ChestInventoryUpdateEvent.kt11
-rw-r--r--src/main/kotlin/features/events/carnival/MinesweeperHelper.kt2
-rw-r--r--src/main/kotlin/features/inventory/ItemRarityCosmetics.kt2
-rw-r--r--src/main/kotlin/features/inventory/SlotLocking.kt4
-rw-r--r--src/main/kotlin/features/mining/PickaxeAbility.kt4
-rw-r--r--src/main/kotlin/features/texturepack/CustomSkyBlockTextures.kt2
-rw-r--r--src/main/kotlin/features/texturepack/DisplayNamePredicate.kt4
-rw-r--r--src/main/kotlin/features/texturepack/LorePredicate.kt2
-rw-r--r--src/main/kotlin/gui/entity/ModifyEquipment.kt4
-rw-r--r--src/main/kotlin/rei/SBItemEntryDefinition.kt4
-rw-r--r--src/main/kotlin/repo/ItemCache.kt8
-rw-r--r--src/main/kotlin/util/accessors/chathud.kt8
-rw-r--r--src/main/kotlin/util/mc/InventoryUtil.kt28
-rw-r--r--src/main/kotlin/util/mc/ItemUtil.kt (renamed from src/main/kotlin/util/ItemUtil.kt)8
-rw-r--r--src/main/kotlin/util/mc/NbtItemData.kt (renamed from src/main/kotlin/util/item/NbtItemData.kt)4
-rw-r--r--src/main/kotlin/util/mc/SkullItemData.kt (renamed from src/main/kotlin/util/item/SkullItemData.kt)4
-rw-r--r--src/main/kotlin/util/regex.kt54
-rw-r--r--src/main/kotlin/util/skyblock/SackUtil.kt110
-rw-r--r--src/main/kotlin/util/textutil.kt184
22 files changed, 356 insertions, 183 deletions
diff --git a/src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java b/src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java
index f1d4a80..6c854d4 100644
--- a/src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java
+++ b/src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java
@@ -1,13 +1,12 @@
package moe.nea.firmament.mixins;
-import com.llamalad7.mixinextras.sugar.Local;
+import moe.nea.firmament.events.ChestInventoryUpdateEvent;
import moe.nea.firmament.events.PlayerInventoryUpdate;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientCommonNetworkHandler;
import net.minecraft.client.network.ClientConnectionState;
import net.minecraft.client.network.ClientPlayNetworkHandler;
-import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.network.ClientConnection;
import net.minecraft.network.packet.s2c.play.InventoryS2CPacket;
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
@@ -18,36 +17,40 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ClientPlayNetworkHandler.class)
public abstract class SlotUpdateListener extends ClientCommonNetworkHandler {
- protected SlotUpdateListener(MinecraftClient client, ClientConnection connection, ClientConnectionState connectionState) {
- super(client, connection, connectionState);
- }
+ protected SlotUpdateListener(MinecraftClient client, ClientConnection connection, ClientConnectionState connectionState) {
+ super(client, connection, connectionState);
+ }
- @Inject(
- method = "onScreenHandlerSlotUpdate",
- at = @At(value = "INVOKE", target = "Lnet/minecraft/client/tutorial/TutorialManager;onSlotUpdate(Lnet/minecraft/item/ItemStack;)V"))
- private void onSingleSlotUpdate(
- ScreenHandlerSlotUpdateS2CPacket packet,
- CallbackInfo ci) {
- var player = this.client.player;
- assert player != null;
- if (packet.getSyncId() == ScreenHandlerSlotUpdateS2CPacket.UPDATE_PLAYER_INVENTORY_SYNC_ID
- || packet.getSyncId() == 0) {
- PlayerInventoryUpdate.Companion.publish(new PlayerInventoryUpdate.Single(packet.getSlot(), packet.getStack()));
- } else if (packet.getSyncId() == player.currentScreenHandler.syncId) {
- // TODO: dispatch single chest slot
- }
- }
+ @Inject(
+ method = "onScreenHandlerSlotUpdate",
+ at = @At(value = "INVOKE", target = "Lnet/minecraft/client/tutorial/TutorialManager;onSlotUpdate(Lnet/minecraft/item/ItemStack;)V"))
+ private void onSingleSlotUpdate(
+ ScreenHandlerSlotUpdateS2CPacket packet,
+ CallbackInfo ci) {
+ var player = this.client.player;
+ assert player != null;
+ if (packet.getSyncId() == ScreenHandlerSlotUpdateS2CPacket.UPDATE_PLAYER_INVENTORY_SYNC_ID
+ || packet.getSyncId() == 0) {
+ PlayerInventoryUpdate.Companion.publish(new PlayerInventoryUpdate.Single(packet.getSlot(), packet.getStack()));
+ } else if (packet.getSyncId() == player.currentScreenHandler.syncId) {
+ ChestInventoryUpdateEvent.Companion.publish(
+ new ChestInventoryUpdateEvent.Single(packet.getSlot(), packet.getStack())
+ );
+ }
+ }
- @Inject(method = "onInventory",
- at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkThreadUtils;forceMainThread(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/listener/PacketListener;Lnet/minecraft/util/thread/ThreadExecutor;)V",
- shift = At.Shift.AFTER))
- private void onMultiSlotUpdate(InventoryS2CPacket packet, CallbackInfo ci) {
- var player = this.client.player;
- assert player != null;
- if (packet.getSyncId() == 0) {
- PlayerInventoryUpdate.Companion.publish(new PlayerInventoryUpdate.Multi(packet.getContents()));
- } else if (packet.getSyncId() == player.currentScreenHandler.syncId) {
- // TODO: dispatch multi chest
- }
- }
+ @Inject(method = "onInventory",
+ at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkThreadUtils;forceMainThread(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/listener/PacketListener;Lnet/minecraft/util/thread/ThreadExecutor;)V",
+ shift = At.Shift.AFTER))
+ private void onMultiSlotUpdate(InventoryS2CPacket packet, CallbackInfo ci) {
+ var player = this.client.player;
+ assert player != null;
+ if (packet.getSyncId() == 0) {
+ PlayerInventoryUpdate.Companion.publish(new PlayerInventoryUpdate.Multi(packet.getContents()));
+ } else if (packet.getSyncId() == player.currentScreenHandler.syncId) {
+ ChestInventoryUpdateEvent.Companion.publish(
+ new ChestInventoryUpdateEvent.Multi(packet.getContents())
+ );
+ }
+ }
}
diff --git a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorChatHud.java b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorChatHud.java
new file mode 100644
index 0000000..72a72f0
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorChatHud.java
@@ -0,0 +1,14 @@
+package moe.nea.firmament.mixins.accessor;
+
+import net.minecraft.client.gui.hud.ChatHud;
+import net.minecraft.client.gui.hud.ChatHudLine;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+import java.util.List;
+
+@Mixin(ChatHud.class)
+public interface AccessorChatHud {
+ @Accessor("messages")
+ List<ChatHudLine> getMessages_firmament();
+}
diff --git a/src/main/kotlin/commands/rome.kt b/src/main/kotlin/commands/rome.kt
index afb3cae..cc4f4ba 100644
--- a/src/main/kotlin/commands/rome.kt
+++ b/src/main/kotlin/commands/rome.kt
@@ -4,7 +4,9 @@ import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.StringArgumentType.string
import io.ktor.client.statement.bodyAsText
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
+import net.minecraft.nbt.NbtOps
import net.minecraft.text.Text
+import net.minecraft.text.TextCodecs
import moe.nea.firmament.apis.UrsaManager
import moe.nea.firmament.events.CommandEvent
import moe.nea.firmament.events.FirmamentEventBus
@@ -24,6 +26,7 @@ import moe.nea.firmament.util.MC
import moe.nea.firmament.util.SBData
import moe.nea.firmament.util.ScreenUtil
import moe.nea.firmament.util.SkyblockId
+import moe.nea.firmament.util.accessors.messages
import moe.nea.firmament.util.collections.InstanceList
import moe.nea.firmament.util.collections.WeakCache
@@ -205,6 +208,14 @@ fun firmamentCommand() = literal("firmament") {
}
}
}
+ thenLiteral("dumpchat") {
+ thenExecute {
+ MC.inGameHud.chatHud.messages.forEach {
+ val nbt = TextCodecs.CODEC.encodeStart(NbtOps.INSTANCE, it.content).orThrow
+ println(nbt)
+ }
+ }
+ }
thenLiteral("sbdata") {
thenExecute {
source.sendFeedback(Text.stringifiedTranslatable("firmament.sbinfo.profile", SBData.profileId))
diff --git a/src/main/kotlin/events/ChestInventoryUpdateEvent.kt b/src/main/kotlin/events/ChestInventoryUpdateEvent.kt
new file mode 100644
index 0000000..ddf54fc
--- /dev/null
+++ b/src/main/kotlin/events/ChestInventoryUpdateEvent.kt
@@ -0,0 +1,11 @@
+package moe.nea.firmament.events
+
+import net.minecraft.item.ItemStack
+import moe.nea.firmament.util.MC
+
+sealed class ChestInventoryUpdateEvent : FirmamentEvent() {
+ companion object : FirmamentEventBus<ChestInventoryUpdateEvent>()
+ data class Single(val slot: Int, val stack: ItemStack) : ChestInventoryUpdateEvent()
+ data class Multi(val contents: List<ItemStack>) : ChestInventoryUpdateEvent()
+ val inventory = MC.screen
+}
diff --git a/src/main/kotlin/features/events/carnival/MinesweeperHelper.kt b/src/main/kotlin/features/events/carnival/MinesweeperHelper.kt
index 06caf86..1824225 100644
--- a/src/main/kotlin/features/events/carnival/MinesweeperHelper.kt
+++ b/src/main/kotlin/features/events/carnival/MinesweeperHelper.kt
@@ -27,7 +27,7 @@ import moe.nea.firmament.util.MC
import moe.nea.firmament.util.MoulConfigUtils
import moe.nea.firmament.util.ScreenUtil
import moe.nea.firmament.util.SkyblockId
-import moe.nea.firmament.util.item.createSkullItem
+import moe.nea.firmament.util.mc.createSkullItem
import moe.nea.firmament.util.render.RenderInWorldContext
import moe.nea.firmament.util.setSkyBlockFirmamentUiId
import moe.nea.firmament.util.skyBlockId
diff --git a/src/main/kotlin/features/inventory/ItemRarityCosmetics.kt b/src/main/kotlin/features/inventory/ItemRarityCosmetics.kt
index 55509f5..424f13b 100644
--- a/src/main/kotlin/features/inventory/ItemRarityCosmetics.kt
+++ b/src/main/kotlin/features/inventory/ItemRarityCosmetics.kt
@@ -13,7 +13,7 @@ import moe.nea.firmament.events.SlotRenderEvents
import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.util.MC
-import moe.nea.firmament.util.item.loreAccordingToNbt
+import moe.nea.firmament.util.mc.loreAccordingToNbt
import moe.nea.firmament.util.collections.lastNotNullOfOrNull
import moe.nea.firmament.util.collections.memoizeIdentity
import moe.nea.firmament.util.unformattedString
diff --git a/src/main/kotlin/features/inventory/SlotLocking.kt b/src/main/kotlin/features/inventory/SlotLocking.kt
index a50d8fb..9f13af8 100644
--- a/src/main/kotlin/features/inventory/SlotLocking.kt
+++ b/src/main/kotlin/features/inventory/SlotLocking.kt
@@ -28,8 +28,8 @@ import moe.nea.firmament.util.MC
import moe.nea.firmament.util.SBData
import moe.nea.firmament.util.SkyBlockIsland
import moe.nea.firmament.util.data.ProfileSpecificDataHolder
-import moe.nea.firmament.util.item.displayNameAccordingToNbt
-import moe.nea.firmament.util.item.loreAccordingToNbt
+import moe.nea.firmament.util.mc.displayNameAccordingToNbt
+import moe.nea.firmament.util.mc.loreAccordingToNbt
import moe.nea.firmament.util.json.DashlessUUIDSerializer
import moe.nea.firmament.util.skyblockUUID
import moe.nea.firmament.util.unformattedString
diff --git a/src/main/kotlin/features/mining/PickaxeAbility.kt b/src/main/kotlin/features/mining/PickaxeAbility.kt
index 7879f2d..ca8ae6b 100644
--- a/src/main/kotlin/features/mining/PickaxeAbility.kt
+++ b/src/main/kotlin/features/mining/PickaxeAbility.kt
@@ -21,8 +21,8 @@ import moe.nea.firmament.util.SHORT_NUMBER_FORMAT
import moe.nea.firmament.util.TIME_PATTERN
import moe.nea.firmament.util.TimeMark
import moe.nea.firmament.util.extraAttributes
-import moe.nea.firmament.util.item.displayNameAccordingToNbt
-import moe.nea.firmament.util.item.loreAccordingToNbt
+import moe.nea.firmament.util.mc.displayNameAccordingToNbt
+import moe.nea.firmament.util.mc.loreAccordingToNbt
import moe.nea.firmament.util.parseShortNumber
import moe.nea.firmament.util.parseTimePattern
import moe.nea.firmament.util.render.RenderCircleProgress
diff --git a/src/main/kotlin/features/texturepack/CustomSkyBlockTextures.kt b/src/main/kotlin/features/texturepack/CustomSkyBlockTextures.kt
index 3b7cb96..2aca8e8 100644
--- a/src/main/kotlin/features/texturepack/CustomSkyBlockTextures.kt
+++ b/src/main/kotlin/features/texturepack/CustomSkyBlockTextures.kt
@@ -19,7 +19,7 @@ import moe.nea.firmament.events.TickEvent
import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.util.collections.WeakCache
-import moe.nea.firmament.util.item.decodeProfileTextureProperty
+import moe.nea.firmament.util.mc.decodeProfileTextureProperty
import moe.nea.firmament.util.skyBlockId
object CustomSkyBlockTextures : FirmamentFeature {
diff --git a/src/main/kotlin/features/texturepack/DisplayNamePredicate.kt b/src/main/kotlin/features/texturepack/DisplayNamePredicate.kt
index c89931e..100aaf4 100644
--- a/src/main/kotlin/features/texturepack/DisplayNamePredicate.kt
+++ b/src/main/kotlin/features/texturepack/DisplayNamePredicate.kt
@@ -5,8 +5,8 @@ import com.google.gson.JsonElement
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NbtElement
import net.minecraft.nbt.NbtString
-import moe.nea.firmament.util.item.displayNameAccordingToNbt
-import moe.nea.firmament.util.item.loreAccordingToNbt
+import moe.nea.firmament.util.mc.displayNameAccordingToNbt
+import moe.nea.firmament.util.mc.loreAccordingToNbt
data class DisplayNamePredicate(val stringMatcher: StringMatcher) : FirmamentModelPredicate {
override fun test(stack: ItemStack): Boolean {
diff --git a/src/main/kotlin/features/texturepack/LorePredicate.kt b/src/main/kotlin/features/texturepack/LorePredicate.kt
index 13e3974..f2b7e76 100644
--- a/src/main/kotlin/features/texturepack/LorePredicate.kt
+++ b/src/main/kotlin/features/texturepack/LorePredicate.kt
@@ -3,7 +3,7 @@ package moe.nea.firmament.features.texturepack
import com.google.gson.JsonElement
import net.minecraft.item.ItemStack
-import moe.nea.firmament.util.item.loreAccordingToNbt
+import moe.nea.firmament.util.mc.loreAccordingToNbt
class LorePredicate(val matcher: StringMatcher) : FirmamentModelPredicate {
object Parser : FirmamentModelPredicateParser {
diff --git a/src/main/kotlin/gui/entity/ModifyEquipment.kt b/src/main/kotlin/gui/entity/ModifyEquipment.kt
index 73e450e..11dfb52 100644
--- a/src/main/kotlin/gui/entity/ModifyEquipment.kt
+++ b/src/main/kotlin/gui/entity/ModifyEquipment.kt
@@ -12,8 +12,8 @@ import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import moe.nea.firmament.rei.SBItemStack
import moe.nea.firmament.util.SkyblockId
-import moe.nea.firmament.util.item.setEncodedSkullOwner
-import moe.nea.firmament.util.item.zeroUUID
+import moe.nea.firmament.util.mc.setEncodedSkullOwner
+import moe.nea.firmament.util.mc.zeroUUID
object ModifyEquipment : EntityModifier {
val names = mapOf(
diff --git a/src/main/kotlin/rei/SBItemEntryDefinition.kt b/src/main/kotlin/rei/SBItemEntryDefinition.kt
index 077eeb1..3df8fa3 100644
--- a/src/main/kotlin/rei/SBItemEntryDefinition.kt
+++ b/src/main/kotlin/rei/SBItemEntryDefinition.kt
@@ -28,8 +28,8 @@ import moe.nea.firmament.util.FirmFormatters
import moe.nea.firmament.util.HypixelPetInfo
import moe.nea.firmament.util.LegacyFormattingCode
import moe.nea.firmament.util.SkyblockId
-import moe.nea.firmament.util.appendLore
-import moe.nea.firmament.util.item.displayNameAccordingToNbt
+import moe.nea.firmament.util.mc.appendLore
+import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.petData
import moe.nea.firmament.util.skyBlockId
import moe.nea.firmament.util.withColor
diff --git a/src/main/kotlin/repo/ItemCache.kt b/src/main/kotlin/repo/ItemCache.kt
index 08143be..c86934c 100644
--- a/src/main/kotlin/repo/ItemCache.kt
+++ b/src/main/kotlin/repo/ItemCache.kt
@@ -33,10 +33,10 @@ import moe.nea.firmament.gui.hud.MoulConfigHud
import moe.nea.firmament.util.LegacyTagParser
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.SkyblockId
-import moe.nea.firmament.util.appendLore
-import moe.nea.firmament.util.item.setCustomName
-import moe.nea.firmament.util.item.setSkullOwner
-import moe.nea.firmament.util.modifyLore
+import moe.nea.firmament.util.mc.appendLore
+import moe.nea.firmament.util.mc.setCustomName
+import moe.nea.firmament.util.mc.setSkullOwner
+import moe.nea.firmament.util.mc.modifyLore
import moe.nea.firmament.util.skyblockId
object ItemCache : IReloadable {
diff --git a/src/main/kotlin/util/accessors/chathud.kt b/src/main/kotlin/util/accessors/chathud.kt
new file mode 100644
index 0000000..effac7d
--- /dev/null
+++ b/src/main/kotlin/util/accessors/chathud.kt
@@ -0,0 +1,8 @@
+package moe.nea.firmament.util.accessors
+
+import net.minecraft.client.gui.hud.ChatHud
+import net.minecraft.client.gui.hud.ChatHudLine
+import moe.nea.firmament.mixins.accessor.AccessorChatHud
+
+val ChatHud.messages: MutableList<ChatHudLine>
+ get() = (this as AccessorChatHud).messages_firmament
diff --git a/src/main/kotlin/util/mc/InventoryUtil.kt b/src/main/kotlin/util/mc/InventoryUtil.kt
new file mode 100644
index 0000000..74f7b9f
--- /dev/null
+++ b/src/main/kotlin/util/mc/InventoryUtil.kt
@@ -0,0 +1,28 @@
+package moe.nea.firmament.util.mc
+
+import java.util.Spliterator
+import java.util.Spliterators
+import net.minecraft.inventory.Inventory
+import net.minecraft.item.ItemStack
+
+val Inventory.indices get() = 0 until size()
+val Inventory.iterableView
+ get() = object : Iterable<ItemStack> {
+ override fun spliterator(): Spliterator<ItemStack> {
+ return Spliterators.spliterator(iterator(), size().toLong(), 0)
+ }
+
+ override fun iterator(): Iterator<ItemStack> {
+ return object : Iterator<ItemStack> {
+ var i = 0
+ override fun hasNext(): Boolean {
+ return i < size()
+ }
+
+ override fun next(): ItemStack {
+ if (!hasNext()) throw NoSuchElementException()
+ return getStack(i++)
+ }
+ }
+ }
+ }
diff --git a/src/main/kotlin/util/ItemUtil.kt b/src/main/kotlin/util/mc/ItemUtil.kt
index 40d6198..13519cf 100644
--- a/src/main/kotlin/util/ItemUtil.kt
+++ b/src/main/kotlin/util/mc/ItemUtil.kt
@@ -1,13 +1,7 @@
-
-
-package moe.nea.firmament.util
+package moe.nea.firmament.util.mc
import net.minecraft.item.ItemStack
-import net.minecraft.nbt.NbtCompound
-import net.minecraft.nbt.NbtList
import net.minecraft.text.Text
-import moe.nea.firmament.util.item.loreAccordingToNbt
-
fun ItemStack.appendLore(args: List<Text>) {
if (args.isEmpty()) return
diff --git a/src/main/kotlin/util/item/NbtItemData.kt b/src/main/kotlin/util/mc/NbtItemData.kt
index f7f259d..e8a908f 100644
--- a/src/main/kotlin/util/item/NbtItemData.kt
+++ b/src/main/kotlin/util/mc/NbtItemData.kt
@@ -1,6 +1,4 @@
-
-
-package moe.nea.firmament.util.item
+package moe.nea.firmament.util.mc
import net.minecraft.component.DataComponentTypes
import net.minecraft.component.type.LoreComponent
diff --git a/src/main/kotlin/util/item/SkullItemData.kt b/src/main/kotlin/util/mc/SkullItemData.kt
index ddab88e..0405b65 100644
--- a/src/main/kotlin/util/item/SkullItemData.kt
+++ b/src/main/kotlin/util/mc/SkullItemData.kt
@@ -1,8 +1,6 @@
-
-
@file:UseSerializers(DashlessUUIDSerializer::class, InstantAsLongSerializer::class)
-package moe.nea.firmament.util.item
+package moe.nea.firmament.util.mc
import com.mojang.authlib.GameProfile
import com.mojang.authlib.minecraft.MinecraftProfileTexture
diff --git a/src/main/kotlin/util/regex.kt b/src/main/kotlin/util/regex.kt
index 3ce5bd8..78c90e8 100644
--- a/src/main/kotlin/util/regex.kt
+++ b/src/main/kotlin/util/regex.kt
@@ -1,5 +1,3 @@
-
-
package moe.nea.firmament.util
import java.util.regex.Matcher
@@ -10,12 +8,12 @@ import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
inline fun <T> String.ifMatches(regex: Regex, block: (MatchResult) -> T): T? =
- regex.matchEntire(this)?.let(block)
+ regex.matchEntire(this)?.let(block)
inline fun <T> Pattern.useMatch(string: String, block: Matcher.() -> T): T? =
- matcher(string)
- .takeIf(Matcher::matches)
- ?.let(block)
+ matcher(string)
+ .takeIf(Matcher::matches)
+ ?.let(block)
@Language("RegExp")
val TIME_PATTERN = "[0-9]+[ms]"
@@ -25,31 +23,33 @@ val SHORT_NUMBER_FORMAT = "[0-9]+(?:,[0-9]+)*(?:\\.[0-9]+)?[kKmMbB]?"
val siScalars = mapOf(
- 'k' to 1_000.0,
- 'K' to 1_000.0,
- 'm' to 1_000_000.0,
- 'M' to 1_000_000.0,
- 'b' to 1_000_000_000.0,
- 'B' to 1_000_000_000.0,
+ 'k' to 1_000.0,
+ 'K' to 1_000.0,
+ 'm' to 1_000_000.0,
+ 'M' to 1_000_000.0,
+ 'b' to 1_000_000_000.0,
+ 'B' to 1_000_000_000.0,
)
fun parseTimePattern(text: String): Duration {
- val length = text.dropLast(1).toInt()
- return when (text.last()) {
- 'm' -> length.minutes
- 's' -> length.seconds
- else -> error("Invalid pattern for time $text")
- }
+ val length = text.dropLast(1).toInt()
+ return when (text.last()) {
+ 'm' -> length.minutes
+ 's' -> length.seconds
+ else -> error("Invalid pattern for time $text")
+ }
}
fun parseShortNumber(string: String): Double {
- var k = string.replace(",", "")
- val scalar = k.last()
- var scalarMultiplier = siScalars[scalar]
- if (scalarMultiplier == null) {
- scalarMultiplier = 1.0
- } else {
- k = k.dropLast(1)
- }
- return k.toDouble() * scalarMultiplier
+ if (string.startsWith("-")) return -parseShortNumber(string.substring(1))
+ if (string.startsWith("+")) return parseShortNumber(string.substring(1))
+ var k = string.replace(",", "")
+ val scalar = k.last()
+ var scalarMultiplier = siScalars[scalar]
+ if (scalarMultiplier == null) {
+ scalarMultiplier = 1.0
+ } else {
+ k = k.dropLast(1)
+ }
+ return k.toDouble() * scalarMultiplier
}
diff --git a/src/main/kotlin/util/skyblock/SackUtil.kt b/src/main/kotlin/util/skyblock/SackUtil.kt
new file mode 100644
index 0000000..2679949
--- /dev/null
+++ b/src/main/kotlin/util/skyblock/SackUtil.kt
@@ -0,0 +1,110 @@
+package moe.nea.firmament.util.skyblock
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.serializer
+import net.minecraft.client.gui.screen.ingame.GenericContainerScreen
+import net.minecraft.text.HoverEvent
+import net.minecraft.text.Text
+import moe.nea.firmament.annotations.Subscribe
+import moe.nea.firmament.events.ChestInventoryUpdateEvent
+import moe.nea.firmament.events.ProcessChatEvent
+import moe.nea.firmament.repo.ItemNameLookup
+import moe.nea.firmament.util.SHORT_NUMBER_FORMAT
+import moe.nea.firmament.util.SkyblockId
+import moe.nea.firmament.util.data.ProfileSpecificDataHolder
+import moe.nea.firmament.util.mc.displayNameAccordingToNbt
+import moe.nea.firmament.util.mc.iterableView
+import moe.nea.firmament.util.mc.loreAccordingToNbt
+import moe.nea.firmament.util.parseShortNumber
+import moe.nea.firmament.util.skyBlockId
+import moe.nea.firmament.util.unformattedString
+import moe.nea.firmament.util.useMatch
+
+object SackUtil {
+ @Serializable
+ data class SackContents(
+ // TODO: store the certainty of knowledge for each item.
+ val contents: MutableMap<SkyblockId, Long> = mutableMapOf(),
+// val sackTypes:
+ )
+
+ object Store : ProfileSpecificDataHolder<SackContents>(serializer(), "Sacks", ::SackContents)
+
+ val items get() = Store.data?.contents ?: mutableMapOf()
+ val storedRegex = "^Stored: (?<stored>$SHORT_NUMBER_FORMAT)/(?<max>$SHORT_NUMBER_FORMAT)$".toPattern()
+
+ @Subscribe
+ fun storeDataFromInventory(event: ChestInventoryUpdateEvent) {
+ val screen = event.inventory as? GenericContainerScreen ?: return
+ if (!screen.title.unformattedString.endsWith(" Sack")) return
+ val inv = screen.screenHandler?.inventory ?: return
+ if (inv.size() < 18) return
+ val backSlot = inv.getStack(inv.size() - 5)
+ if (backSlot.displayNameAccordingToNbt.unformattedString != "Go Back") return
+ if (backSlot.loreAccordingToNbt.map { it.unformattedString } != listOf("To Sack of Sacks")) return
+ for (itemStack in inv.iterableView) {
+ // TODO: handle runes and gemstones
+ val stored = itemStack.loreAccordingToNbt.firstNotNullOfOrNull {
+ storedRegex.useMatch(it.unformattedString) {
+ val stored = parseShortNumber(group("stored")).toLong()
+ val max = parseShortNumber(group("max")).toLong()
+ stored
+ }
+ } ?: continue
+ val itemId = itemStack.skyBlockId ?: continue
+ items[itemId] = stored
+ }
+ Store.markDirty()
+ }
+
+ @Subscribe
+ fun updateFromChat(event: ProcessChatEvent) {
+ if (!event.unformattedString.startsWith("[Sacks]")) return
+ val update = ChatUpdate()
+ event.text.siblings.forEach(update::updateFromHoverText)
+ }
+
+ data class SackUpdate(
+ val itemId: SkyblockId?,
+ val itemName: String,
+ val changeAmount: Long,
+ )
+
+ private class ChatUpdate {
+ val updates = mutableListOf<SackUpdate>()
+ var foundAdded = false
+ var foundRemoved = false
+
+ fun updateFromCleanText(cleanedText: String) {
+ cleanedText.split("\n").forEach { line ->
+ changePattern.useMatch(line) {
+ val amount = parseShortNumber(group("amount")).toLong()
+ val itemName = group("itemName")
+ val itemId = ItemNameLookup.guessItemByName(itemName, false)
+ updates.add(SackUpdate(itemId, itemName, amount))
+ }
+ }
+ }
+
+ fun updateFromHoverText(text: Text) {
+ text.siblings.forEach(::updateFromHoverText)
+ val hoverText = text.style.hoverEvent?.getValue(HoverEvent.Action.SHOW_TEXT) ?: return
+ val cleanedText = hoverText.unformattedString
+ if (cleanedText.startsWith("Added items:\n")) {
+ if (!foundAdded) {
+ updateFromCleanText(cleanedText)
+ foundAdded = true
+ }
+ }
+ if (cleanedText.startsWith("Removed items:\n")) {
+ if (!foundRemoved) {
+ updateFromCleanText(cleanedText)
+ foundRemoved = true
+ }
+ }
+ }
+
+ }
+
+ val changePattern = " (?<amount>[+\\-]$SHORT_NUMBER_FORMAT) (?<itemName>[^(]+) \\(.*\\)".toPattern()
+}
diff --git a/src/main/kotlin/util/textutil.kt b/src/main/kotlin/util/textutil.kt
index a05733c..36924a6 100644
--- a/src/main/kotlin/util/textutil.kt
+++ b/src/main/kotlin/util/textutil.kt
@@ -1,10 +1,7 @@
-
-
package moe.nea.firmament.util
import net.minecraft.text.MutableText
import net.minecraft.text.PlainTextContent
-import net.minecraft.text.Style
import net.minecraft.text.Text
import net.minecraft.text.TranslatableTextContent
import net.minecraft.util.Formatting
@@ -12,106 +9,107 @@ import moe.nea.firmament.Firmament
class TextMatcher(text: Text) {
- data class State(
- var iterator: MutableList<Text>,
- var currentText: Text?,
- var offset: Int,
- var textContent: String,
- )
-
- var state = State(
- mutableListOf(text),
- null,
- 0,
- ""
- )
-
- fun pollChunk(): Boolean {
- val firstOrNull = state.iterator.removeFirstOrNull() ?: return false
- state.offset = 0
- state.currentText = firstOrNull
- state.textContent = when (val content = firstOrNull.content) {
- is PlainTextContent.Literal -> content.string
- else -> {
- Firmament.logger.warn("TextContent of type ${content.javaClass} not understood.")
- return false
- }
- }
- state.iterator.addAll(0, firstOrNull.siblings)
- return true
- }
-
- fun pollChunks(): Boolean {
- while (state.offset !in state.textContent.indices) {
- if (!pollChunk()) {
- return false
- }
- }
- return true
- }
-
- fun pollChar(): Char? {
- if (!pollChunks()) return null
- return state.textContent[state.offset++]
- }
-
-
- fun expectString(string: String): Boolean {
- var found = ""
- while (found.length < string.length) {
- if (!pollChunks()) return false
- val takeable = state.textContent.drop(state.offset).take(string.length - found.length)
- state.offset += takeable.length
- found += takeable
- }
- return found == string
- }
+ data class State(
+ var iterator: MutableList<Text>,
+ var currentText: Text?,
+ var offset: Int,
+ var textContent: String,
+ )
+
+ var state = State(
+ mutableListOf(text),
+ null,
+ 0,
+ ""
+ )
+
+ fun pollChunk(): Boolean {
+ val firstOrNull = state.iterator.removeFirstOrNull() ?: return false
+ state.offset = 0
+ state.currentText = firstOrNull
+ state.textContent = when (val content = firstOrNull.content) {
+ is PlainTextContent.Literal -> content.string
+ else -> {
+ Firmament.logger.warn("TextContent of type ${content.javaClass} not understood.")
+ return false
+ }
+ }
+ state.iterator.addAll(0, firstOrNull.siblings)
+ return true
+ }
+
+ fun pollChunks(): Boolean {
+ while (state.offset !in state.textContent.indices) {
+ if (!pollChunk()) {
+ return false
+ }
+ }
+ return true
+ }
+
+ fun pollChar(): Char? {
+ if (!pollChunks()) return null
+ return state.textContent[state.offset++]
+ }
+
+
+ fun expectString(string: String): Boolean {
+ var found = ""
+ while (found.length < string.length) {
+ if (!pollChunks()) return false
+ val takeable = state.textContent.drop(state.offset).take(string.length - found.length)
+ state.offset += takeable.length
+ found += takeable
+ }
+ return found == string
+ }
}
val formattingChars = "kmolnrKMOLNR".toSet()
fun CharSequence.removeColorCodes(keepNonColorCodes: Boolean = false): String {
- var nextParagraph = indexOf('§')
- if (nextParagraph < 0) return this.toString()
- val stringBuffer = StringBuilder(this.length)
- var readIndex = 0
- while (nextParagraph >= 0) {
- stringBuffer.append(this, readIndex, nextParagraph)
- if (keepNonColorCodes && nextParagraph + 1 < length && this[nextParagraph + 1] in formattingChars) {
- readIndex = nextParagraph
- nextParagraph = indexOf('§', startIndex = readIndex + 1)
- } else {
- readIndex = nextParagraph + 2
- nextParagraph = indexOf('§', startIndex = readIndex)
- }
- if (readIndex > this.length)
- readIndex = this.length
- }
- stringBuffer.append(this, readIndex, this.length)
- return stringBuffer.toString()
+ var nextParagraph = indexOf('§')
+ if (nextParagraph < 0) return this.toString()
+ val stringBuffer = StringBuilder(this.length)
+ var readIndex = 0
+ while (nextParagraph >= 0) {
+ stringBuffer.append(this, readIndex, nextParagraph)
+ if (keepNonColorCodes && nextParagraph + 1 < length && this[nextParagraph + 1] in formattingChars) {
+ readIndex = nextParagraph
+ nextParagraph = indexOf('§', startIndex = readIndex + 1)
+ } else {
+ readIndex = nextParagraph + 2
+ nextParagraph = indexOf('§', startIndex = readIndex)
+ }
+ if (readIndex > this.length)
+ readIndex = this.length
+ }
+ stringBuffer.append(this, readIndex, this.length)
+ return stringBuffer.toString()
}
val Text.unformattedString: String
- get() = string.removeColorCodes()
+ get() = string.removeColorCodes()
+fun Text.allSiblings(): List<Text> = listOf(this) + siblings.flatMap { it.allSiblings() }
fun MutableText.withColor(formatting: Formatting) = this.styled { it.withColor(formatting).withItalic(false) }
fun Text.transformEachRecursively(function: (Text) -> Text): Text {
- val c = this.content
- if (c is TranslatableTextContent) {
- return Text.translatableWithFallback(c.key, c.fallback, *c.args.map {
- (if (it is Text) it else Text.literal(it.toString())).transformEachRecursively(function)
- }.toTypedArray()).also { new ->
- new.style = this.style
- new.siblings.clear()
- this.siblings.forEach { child ->
- new.siblings.add(child.transformEachRecursively(function))
- }
- }
- }
- return function(this.copy().also { it.siblings.clear() }).also { tt ->
- this.siblings.forEach {
- tt.siblings.add(it.transformEachRecursively(function))
- }
- }
+ val c = this.content
+ if (c is TranslatableTextContent) {
+ return Text.translatableWithFallback(c.key, c.fallback, *c.args.map {
+ (if (it is Text) it else Text.literal(it.toString())).transformEachRecursively(function)
+ }.toTypedArray()).also { new ->
+ new.style = this.style
+ new.siblings.clear()
+ this.siblings.forEach { child ->
+ new.siblings.add(child.transformEachRecursively(function))
+ }
+ }
+ }
+ return function(this.copy().also { it.siblings.clear() }).also { tt ->
+ this.siblings.forEach {
+ tt.siblings.add(it.transformEachRecursively(function))
+ }
+ }
}