Add basic sack util
[no changelog]
This commit is contained in:
@@ -1,13 +1,12 @@
|
|||||||
|
|
||||||
package moe.nea.firmament.mixins;
|
package moe.nea.firmament.mixins;
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.sugar.Local;
|
import moe.nea.firmament.events.ChestInventoryUpdateEvent;
|
||||||
import moe.nea.firmament.events.PlayerInventoryUpdate;
|
import moe.nea.firmament.events.PlayerInventoryUpdate;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.network.ClientCommonNetworkHandler;
|
import net.minecraft.client.network.ClientCommonNetworkHandler;
|
||||||
import net.minecraft.client.network.ClientConnectionState;
|
import net.minecraft.client.network.ClientConnectionState;
|
||||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||||
import net.minecraft.client.network.ClientPlayerEntity;
|
|
||||||
import net.minecraft.network.ClientConnection;
|
import net.minecraft.network.ClientConnection;
|
||||||
import net.minecraft.network.packet.s2c.play.InventoryS2CPacket;
|
import net.minecraft.network.packet.s2c.play.InventoryS2CPacket;
|
||||||
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
|
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
|
||||||
@@ -18,36 +17,40 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
|
|
||||||
@Mixin(ClientPlayNetworkHandler.class)
|
@Mixin(ClientPlayNetworkHandler.class)
|
||||||
public abstract class SlotUpdateListener extends ClientCommonNetworkHandler {
|
public abstract class SlotUpdateListener extends ClientCommonNetworkHandler {
|
||||||
protected SlotUpdateListener(MinecraftClient client, ClientConnection connection, ClientConnectionState connectionState) {
|
protected SlotUpdateListener(MinecraftClient client, ClientConnection connection, ClientConnectionState connectionState) {
|
||||||
super(client, connection, connectionState);
|
super(client, connection, connectionState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(
|
@Inject(
|
||||||
method = "onScreenHandlerSlotUpdate",
|
method = "onScreenHandlerSlotUpdate",
|
||||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/tutorial/TutorialManager;onSlotUpdate(Lnet/minecraft/item/ItemStack;)V"))
|
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/tutorial/TutorialManager;onSlotUpdate(Lnet/minecraft/item/ItemStack;)V"))
|
||||||
private void onSingleSlotUpdate(
|
private void onSingleSlotUpdate(
|
||||||
ScreenHandlerSlotUpdateS2CPacket packet,
|
ScreenHandlerSlotUpdateS2CPacket packet,
|
||||||
CallbackInfo ci) {
|
CallbackInfo ci) {
|
||||||
var player = this.client.player;
|
var player = this.client.player;
|
||||||
assert player != null;
|
assert player != null;
|
||||||
if (packet.getSyncId() == ScreenHandlerSlotUpdateS2CPacket.UPDATE_PLAYER_INVENTORY_SYNC_ID
|
if (packet.getSyncId() == ScreenHandlerSlotUpdateS2CPacket.UPDATE_PLAYER_INVENTORY_SYNC_ID
|
||||||
|| packet.getSyncId() == 0) {
|
|| packet.getSyncId() == 0) {
|
||||||
PlayerInventoryUpdate.Companion.publish(new PlayerInventoryUpdate.Single(packet.getSlot(), packet.getStack()));
|
PlayerInventoryUpdate.Companion.publish(new PlayerInventoryUpdate.Single(packet.getSlot(), packet.getStack()));
|
||||||
} else if (packet.getSyncId() == player.currentScreenHandler.syncId) {
|
} else if (packet.getSyncId() == player.currentScreenHandler.syncId) {
|
||||||
// TODO: dispatch single chest slot
|
ChestInventoryUpdateEvent.Companion.publish(
|
||||||
}
|
new ChestInventoryUpdateEvent.Single(packet.getSlot(), packet.getStack())
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Inject(method = "onInventory",
|
@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",
|
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))
|
shift = At.Shift.AFTER))
|
||||||
private void onMultiSlotUpdate(InventoryS2CPacket packet, CallbackInfo ci) {
|
private void onMultiSlotUpdate(InventoryS2CPacket packet, CallbackInfo ci) {
|
||||||
var player = this.client.player;
|
var player = this.client.player;
|
||||||
assert player != null;
|
assert player != null;
|
||||||
if (packet.getSyncId() == 0) {
|
if (packet.getSyncId() == 0) {
|
||||||
PlayerInventoryUpdate.Companion.publish(new PlayerInventoryUpdate.Multi(packet.getContents()));
|
PlayerInventoryUpdate.Companion.publish(new PlayerInventoryUpdate.Multi(packet.getContents()));
|
||||||
} else if (packet.getSyncId() == player.currentScreenHandler.syncId) {
|
} else if (packet.getSyncId() == player.currentScreenHandler.syncId) {
|
||||||
// TODO: dispatch multi chest
|
ChestInventoryUpdateEvent.Companion.publish(
|
||||||
}
|
new ChestInventoryUpdateEvent.Multi(packet.getContents())
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -4,7 +4,9 @@ import com.mojang.brigadier.CommandDispatcher
|
|||||||
import com.mojang.brigadier.arguments.StringArgumentType.string
|
import com.mojang.brigadier.arguments.StringArgumentType.string
|
||||||
import io.ktor.client.statement.bodyAsText
|
import io.ktor.client.statement.bodyAsText
|
||||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
|
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
|
||||||
|
import net.minecraft.nbt.NbtOps
|
||||||
import net.minecraft.text.Text
|
import net.minecraft.text.Text
|
||||||
|
import net.minecraft.text.TextCodecs
|
||||||
import moe.nea.firmament.apis.UrsaManager
|
import moe.nea.firmament.apis.UrsaManager
|
||||||
import moe.nea.firmament.events.CommandEvent
|
import moe.nea.firmament.events.CommandEvent
|
||||||
import moe.nea.firmament.events.FirmamentEventBus
|
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.SBData
|
||||||
import moe.nea.firmament.util.ScreenUtil
|
import moe.nea.firmament.util.ScreenUtil
|
||||||
import moe.nea.firmament.util.SkyblockId
|
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.InstanceList
|
||||||
import moe.nea.firmament.util.collections.WeakCache
|
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") {
|
thenLiteral("sbdata") {
|
||||||
thenExecute {
|
thenExecute {
|
||||||
source.sendFeedback(Text.stringifiedTranslatable("firmament.sbinfo.profile", SBData.profileId))
|
source.sendFeedback(Text.stringifiedTranslatable("firmament.sbinfo.profile", SBData.profileId))
|
||||||
|
|||||||
11
src/main/kotlin/events/ChestInventoryUpdateEvent.kt
Normal file
11
src/main/kotlin/events/ChestInventoryUpdateEvent.kt
Normal file
@@ -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
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ import moe.nea.firmament.util.MC
|
|||||||
import moe.nea.firmament.util.MoulConfigUtils
|
import moe.nea.firmament.util.MoulConfigUtils
|
||||||
import moe.nea.firmament.util.ScreenUtil
|
import moe.nea.firmament.util.ScreenUtil
|
||||||
import moe.nea.firmament.util.SkyblockId
|
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.render.RenderInWorldContext
|
||||||
import moe.nea.firmament.util.setSkyBlockFirmamentUiId
|
import moe.nea.firmament.util.setSkyBlockFirmamentUiId
|
||||||
import moe.nea.firmament.util.skyBlockId
|
import moe.nea.firmament.util.skyBlockId
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import moe.nea.firmament.events.SlotRenderEvents
|
|||||||
import moe.nea.firmament.features.FirmamentFeature
|
import moe.nea.firmament.features.FirmamentFeature
|
||||||
import moe.nea.firmament.gui.config.ManagedConfig
|
import moe.nea.firmament.gui.config.ManagedConfig
|
||||||
import moe.nea.firmament.util.MC
|
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.lastNotNullOfOrNull
|
||||||
import moe.nea.firmament.util.collections.memoizeIdentity
|
import moe.nea.firmament.util.collections.memoizeIdentity
|
||||||
import moe.nea.firmament.util.unformattedString
|
import moe.nea.firmament.util.unformattedString
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ import moe.nea.firmament.util.MC
|
|||||||
import moe.nea.firmament.util.SBData
|
import moe.nea.firmament.util.SBData
|
||||||
import moe.nea.firmament.util.SkyBlockIsland
|
import moe.nea.firmament.util.SkyBlockIsland
|
||||||
import moe.nea.firmament.util.data.ProfileSpecificDataHolder
|
import moe.nea.firmament.util.data.ProfileSpecificDataHolder
|
||||||
import moe.nea.firmament.util.item.displayNameAccordingToNbt
|
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
||||||
import moe.nea.firmament.util.item.loreAccordingToNbt
|
import moe.nea.firmament.util.mc.loreAccordingToNbt
|
||||||
import moe.nea.firmament.util.json.DashlessUUIDSerializer
|
import moe.nea.firmament.util.json.DashlessUUIDSerializer
|
||||||
import moe.nea.firmament.util.skyblockUUID
|
import moe.nea.firmament.util.skyblockUUID
|
||||||
import moe.nea.firmament.util.unformattedString
|
import moe.nea.firmament.util.unformattedString
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import moe.nea.firmament.util.SHORT_NUMBER_FORMAT
|
|||||||
import moe.nea.firmament.util.TIME_PATTERN
|
import moe.nea.firmament.util.TIME_PATTERN
|
||||||
import moe.nea.firmament.util.TimeMark
|
import moe.nea.firmament.util.TimeMark
|
||||||
import moe.nea.firmament.util.extraAttributes
|
import moe.nea.firmament.util.extraAttributes
|
||||||
import moe.nea.firmament.util.item.displayNameAccordingToNbt
|
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
||||||
import moe.nea.firmament.util.item.loreAccordingToNbt
|
import moe.nea.firmament.util.mc.loreAccordingToNbt
|
||||||
import moe.nea.firmament.util.parseShortNumber
|
import moe.nea.firmament.util.parseShortNumber
|
||||||
import moe.nea.firmament.util.parseTimePattern
|
import moe.nea.firmament.util.parseTimePattern
|
||||||
import moe.nea.firmament.util.render.RenderCircleProgress
|
import moe.nea.firmament.util.render.RenderCircleProgress
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import moe.nea.firmament.events.TickEvent
|
|||||||
import moe.nea.firmament.features.FirmamentFeature
|
import moe.nea.firmament.features.FirmamentFeature
|
||||||
import moe.nea.firmament.gui.config.ManagedConfig
|
import moe.nea.firmament.gui.config.ManagedConfig
|
||||||
import moe.nea.firmament.util.collections.WeakCache
|
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
|
import moe.nea.firmament.util.skyBlockId
|
||||||
|
|
||||||
object CustomSkyBlockTextures : FirmamentFeature {
|
object CustomSkyBlockTextures : FirmamentFeature {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import com.google.gson.JsonElement
|
|||||||
import net.minecraft.item.ItemStack
|
import net.minecraft.item.ItemStack
|
||||||
import net.minecraft.nbt.NbtElement
|
import net.minecraft.nbt.NbtElement
|
||||||
import net.minecraft.nbt.NbtString
|
import net.minecraft.nbt.NbtString
|
||||||
import moe.nea.firmament.util.item.displayNameAccordingToNbt
|
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
||||||
import moe.nea.firmament.util.item.loreAccordingToNbt
|
import moe.nea.firmament.util.mc.loreAccordingToNbt
|
||||||
|
|
||||||
data class DisplayNamePredicate(val stringMatcher: StringMatcher) : FirmamentModelPredicate {
|
data class DisplayNamePredicate(val stringMatcher: StringMatcher) : FirmamentModelPredicate {
|
||||||
override fun test(stack: ItemStack): Boolean {
|
override fun test(stack: ItemStack): Boolean {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package moe.nea.firmament.features.texturepack
|
|||||||
|
|
||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
import net.minecraft.item.ItemStack
|
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 {
|
class LorePredicate(val matcher: StringMatcher) : FirmamentModelPredicate {
|
||||||
object Parser : FirmamentModelPredicateParser {
|
object Parser : FirmamentModelPredicateParser {
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import net.minecraft.item.ItemStack
|
|||||||
import net.minecraft.item.Items
|
import net.minecraft.item.Items
|
||||||
import moe.nea.firmament.rei.SBItemStack
|
import moe.nea.firmament.rei.SBItemStack
|
||||||
import moe.nea.firmament.util.SkyblockId
|
import moe.nea.firmament.util.SkyblockId
|
||||||
import moe.nea.firmament.util.item.setEncodedSkullOwner
|
import moe.nea.firmament.util.mc.setEncodedSkullOwner
|
||||||
import moe.nea.firmament.util.item.zeroUUID
|
import moe.nea.firmament.util.mc.zeroUUID
|
||||||
|
|
||||||
object ModifyEquipment : EntityModifier {
|
object ModifyEquipment : EntityModifier {
|
||||||
val names = mapOf(
|
val names = mapOf(
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ import moe.nea.firmament.util.FirmFormatters
|
|||||||
import moe.nea.firmament.util.HypixelPetInfo
|
import moe.nea.firmament.util.HypixelPetInfo
|
||||||
import moe.nea.firmament.util.LegacyFormattingCode
|
import moe.nea.firmament.util.LegacyFormattingCode
|
||||||
import moe.nea.firmament.util.SkyblockId
|
import moe.nea.firmament.util.SkyblockId
|
||||||
import moe.nea.firmament.util.appendLore
|
import moe.nea.firmament.util.mc.appendLore
|
||||||
import moe.nea.firmament.util.item.displayNameAccordingToNbt
|
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
||||||
import moe.nea.firmament.util.petData
|
import moe.nea.firmament.util.petData
|
||||||
import moe.nea.firmament.util.skyBlockId
|
import moe.nea.firmament.util.skyBlockId
|
||||||
import moe.nea.firmament.util.withColor
|
import moe.nea.firmament.util.withColor
|
||||||
|
|||||||
@@ -33,10 +33,10 @@ import moe.nea.firmament.gui.hud.MoulConfigHud
|
|||||||
import moe.nea.firmament.util.LegacyTagParser
|
import moe.nea.firmament.util.LegacyTagParser
|
||||||
import moe.nea.firmament.util.MC
|
import moe.nea.firmament.util.MC
|
||||||
import moe.nea.firmament.util.SkyblockId
|
import moe.nea.firmament.util.SkyblockId
|
||||||
import moe.nea.firmament.util.appendLore
|
import moe.nea.firmament.util.mc.appendLore
|
||||||
import moe.nea.firmament.util.item.setCustomName
|
import moe.nea.firmament.util.mc.setCustomName
|
||||||
import moe.nea.firmament.util.item.setSkullOwner
|
import moe.nea.firmament.util.mc.setSkullOwner
|
||||||
import moe.nea.firmament.util.modifyLore
|
import moe.nea.firmament.util.mc.modifyLore
|
||||||
import moe.nea.firmament.util.skyblockId
|
import moe.nea.firmament.util.skyblockId
|
||||||
|
|
||||||
object ItemCache : IReloadable {
|
object ItemCache : IReloadable {
|
||||||
|
|||||||
8
src/main/kotlin/util/accessors/chathud.kt
Normal file
8
src/main/kotlin/util/accessors/chathud.kt
Normal file
@@ -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
|
||||||
28
src/main/kotlin/util/mc/InventoryUtil.kt
Normal file
28
src/main/kotlin/util/mc/InventoryUtil.kt
Normal file
@@ -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++)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,7 @@
|
|||||||
|
package moe.nea.firmament.util.mc
|
||||||
|
|
||||||
package moe.nea.firmament.util
|
|
||||||
|
|
||||||
import net.minecraft.item.ItemStack
|
import net.minecraft.item.ItemStack
|
||||||
import net.minecraft.nbt.NbtCompound
|
|
||||||
import net.minecraft.nbt.NbtList
|
|
||||||
import net.minecraft.text.Text
|
import net.minecraft.text.Text
|
||||||
import moe.nea.firmament.util.item.loreAccordingToNbt
|
|
||||||
|
|
||||||
|
|
||||||
fun ItemStack.appendLore(args: List<Text>) {
|
fun ItemStack.appendLore(args: List<Text>) {
|
||||||
if (args.isEmpty()) return
|
if (args.isEmpty()) return
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
|
package moe.nea.firmament.util.mc
|
||||||
|
|
||||||
package moe.nea.firmament.util.item
|
|
||||||
|
|
||||||
import net.minecraft.component.DataComponentTypes
|
import net.minecraft.component.DataComponentTypes
|
||||||
import net.minecraft.component.type.LoreComponent
|
import net.minecraft.component.type.LoreComponent
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
@file:UseSerializers(DashlessUUIDSerializer::class, InstantAsLongSerializer::class)
|
@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.GameProfile
|
||||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture
|
import com.mojang.authlib.minecraft.MinecraftProfileTexture
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
package moe.nea.firmament.util
|
package moe.nea.firmament.util
|
||||||
|
|
||||||
import java.util.regex.Matcher
|
import java.util.regex.Matcher
|
||||||
@@ -10,12 +8,12 @@ import kotlin.time.Duration.Companion.minutes
|
|||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
inline fun <T> String.ifMatches(regex: Regex, block: (MatchResult) -> T): T? =
|
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? =
|
inline fun <T> Pattern.useMatch(string: String, block: Matcher.() -> T): T? =
|
||||||
matcher(string)
|
matcher(string)
|
||||||
.takeIf(Matcher::matches)
|
.takeIf(Matcher::matches)
|
||||||
?.let(block)
|
?.let(block)
|
||||||
|
|
||||||
@Language("RegExp")
|
@Language("RegExp")
|
||||||
val TIME_PATTERN = "[0-9]+[ms]"
|
val TIME_PATTERN = "[0-9]+[ms]"
|
||||||
@@ -25,31 +23,33 @@ val SHORT_NUMBER_FORMAT = "[0-9]+(?:,[0-9]+)*(?:\\.[0-9]+)?[kKmMbB]?"
|
|||||||
|
|
||||||
|
|
||||||
val siScalars = mapOf(
|
val siScalars = mapOf(
|
||||||
'k' to 1_000.0,
|
'k' to 1_000.0,
|
||||||
'K' to 1_000.0,
|
'K' to 1_000.0,
|
||||||
'm' to 1_000_000.0,
|
'm' to 1_000_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,
|
||||||
'B' to 1_000_000_000.0,
|
'B' to 1_000_000_000.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun parseTimePattern(text: String): Duration {
|
fun parseTimePattern(text: String): Duration {
|
||||||
val length = text.dropLast(1).toInt()
|
val length = text.dropLast(1).toInt()
|
||||||
return when (text.last()) {
|
return when (text.last()) {
|
||||||
'm' -> length.minutes
|
'm' -> length.minutes
|
||||||
's' -> length.seconds
|
's' -> length.seconds
|
||||||
else -> error("Invalid pattern for time $text")
|
else -> error("Invalid pattern for time $text")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parseShortNumber(string: String): Double {
|
fun parseShortNumber(string: String): Double {
|
||||||
var k = string.replace(",", "")
|
if (string.startsWith("-")) return -parseShortNumber(string.substring(1))
|
||||||
val scalar = k.last()
|
if (string.startsWith("+")) return parseShortNumber(string.substring(1))
|
||||||
var scalarMultiplier = siScalars[scalar]
|
var k = string.replace(",", "")
|
||||||
if (scalarMultiplier == null) {
|
val scalar = k.last()
|
||||||
scalarMultiplier = 1.0
|
var scalarMultiplier = siScalars[scalar]
|
||||||
} else {
|
if (scalarMultiplier == null) {
|
||||||
k = k.dropLast(1)
|
scalarMultiplier = 1.0
|
||||||
}
|
} else {
|
||||||
return k.toDouble() * scalarMultiplier
|
k = k.dropLast(1)
|
||||||
|
}
|
||||||
|
return k.toDouble() * scalarMultiplier
|
||||||
}
|
}
|
||||||
|
|||||||
110
src/main/kotlin/util/skyblock/SackUtil.kt
Normal file
110
src/main/kotlin/util/skyblock/SackUtil.kt
Normal file
@@ -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()
|
||||||
|
}
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
package moe.nea.firmament.util
|
package moe.nea.firmament.util
|
||||||
|
|
||||||
import net.minecraft.text.MutableText
|
import net.minecraft.text.MutableText
|
||||||
import net.minecraft.text.PlainTextContent
|
import net.minecraft.text.PlainTextContent
|
||||||
import net.minecraft.text.Style
|
|
||||||
import net.minecraft.text.Text
|
import net.minecraft.text.Text
|
||||||
import net.minecraft.text.TranslatableTextContent
|
import net.minecraft.text.TranslatableTextContent
|
||||||
import net.minecraft.util.Formatting
|
import net.minecraft.util.Formatting
|
||||||
@@ -12,106 +9,107 @@ import moe.nea.firmament.Firmament
|
|||||||
|
|
||||||
|
|
||||||
class TextMatcher(text: Text) {
|
class TextMatcher(text: Text) {
|
||||||
data class State(
|
data class State(
|
||||||
var iterator: MutableList<Text>,
|
var iterator: MutableList<Text>,
|
||||||
var currentText: Text?,
|
var currentText: Text?,
|
||||||
var offset: Int,
|
var offset: Int,
|
||||||
var textContent: String,
|
var textContent: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
var state = State(
|
var state = State(
|
||||||
mutableListOf(text),
|
mutableListOf(text),
|
||||||
null,
|
null,
|
||||||
0,
|
0,
|
||||||
""
|
""
|
||||||
)
|
)
|
||||||
|
|
||||||
fun pollChunk(): Boolean {
|
fun pollChunk(): Boolean {
|
||||||
val firstOrNull = state.iterator.removeFirstOrNull() ?: return false
|
val firstOrNull = state.iterator.removeFirstOrNull() ?: return false
|
||||||
state.offset = 0
|
state.offset = 0
|
||||||
state.currentText = firstOrNull
|
state.currentText = firstOrNull
|
||||||
state.textContent = when (val content = firstOrNull.content) {
|
state.textContent = when (val content = firstOrNull.content) {
|
||||||
is PlainTextContent.Literal -> content.string
|
is PlainTextContent.Literal -> content.string
|
||||||
else -> {
|
else -> {
|
||||||
Firmament.logger.warn("TextContent of type ${content.javaClass} not understood.")
|
Firmament.logger.warn("TextContent of type ${content.javaClass} not understood.")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.iterator.addAll(0, firstOrNull.siblings)
|
state.iterator.addAll(0, firstOrNull.siblings)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pollChunks(): Boolean {
|
fun pollChunks(): Boolean {
|
||||||
while (state.offset !in state.textContent.indices) {
|
while (state.offset !in state.textContent.indices) {
|
||||||
if (!pollChunk()) {
|
if (!pollChunk()) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pollChar(): Char? {
|
fun pollChar(): Char? {
|
||||||
if (!pollChunks()) return null
|
if (!pollChunks()) return null
|
||||||
return state.textContent[state.offset++]
|
return state.textContent[state.offset++]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun expectString(string: String): Boolean {
|
fun expectString(string: String): Boolean {
|
||||||
var found = ""
|
var found = ""
|
||||||
while (found.length < string.length) {
|
while (found.length < string.length) {
|
||||||
if (!pollChunks()) return false
|
if (!pollChunks()) return false
|
||||||
val takeable = state.textContent.drop(state.offset).take(string.length - found.length)
|
val takeable = state.textContent.drop(state.offset).take(string.length - found.length)
|
||||||
state.offset += takeable.length
|
state.offset += takeable.length
|
||||||
found += takeable
|
found += takeable
|
||||||
}
|
}
|
||||||
return found == string
|
return found == string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val formattingChars = "kmolnrKMOLNR".toSet()
|
val formattingChars = "kmolnrKMOLNR".toSet()
|
||||||
fun CharSequence.removeColorCodes(keepNonColorCodes: Boolean = false): String {
|
fun CharSequence.removeColorCodes(keepNonColorCodes: Boolean = false): String {
|
||||||
var nextParagraph = indexOf('§')
|
var nextParagraph = indexOf('§')
|
||||||
if (nextParagraph < 0) return this.toString()
|
if (nextParagraph < 0) return this.toString()
|
||||||
val stringBuffer = StringBuilder(this.length)
|
val stringBuffer = StringBuilder(this.length)
|
||||||
var readIndex = 0
|
var readIndex = 0
|
||||||
while (nextParagraph >= 0) {
|
while (nextParagraph >= 0) {
|
||||||
stringBuffer.append(this, readIndex, nextParagraph)
|
stringBuffer.append(this, readIndex, nextParagraph)
|
||||||
if (keepNonColorCodes && nextParagraph + 1 < length && this[nextParagraph + 1] in formattingChars) {
|
if (keepNonColorCodes && nextParagraph + 1 < length && this[nextParagraph + 1] in formattingChars) {
|
||||||
readIndex = nextParagraph
|
readIndex = nextParagraph
|
||||||
nextParagraph = indexOf('§', startIndex = readIndex + 1)
|
nextParagraph = indexOf('§', startIndex = readIndex + 1)
|
||||||
} else {
|
} else {
|
||||||
readIndex = nextParagraph + 2
|
readIndex = nextParagraph + 2
|
||||||
nextParagraph = indexOf('§', startIndex = readIndex)
|
nextParagraph = indexOf('§', startIndex = readIndex)
|
||||||
}
|
}
|
||||||
if (readIndex > this.length)
|
if (readIndex > this.length)
|
||||||
readIndex = this.length
|
readIndex = this.length
|
||||||
}
|
}
|
||||||
stringBuffer.append(this, readIndex, this.length)
|
stringBuffer.append(this, readIndex, this.length)
|
||||||
return stringBuffer.toString()
|
return stringBuffer.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
val Text.unformattedString: String
|
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 MutableText.withColor(formatting: Formatting) = this.styled { it.withColor(formatting).withItalic(false) }
|
||||||
|
|
||||||
fun Text.transformEachRecursively(function: (Text) -> Text): Text {
|
fun Text.transformEachRecursively(function: (Text) -> Text): Text {
|
||||||
val c = this.content
|
val c = this.content
|
||||||
if (c is TranslatableTextContent) {
|
if (c is TranslatableTextContent) {
|
||||||
return Text.translatableWithFallback(c.key, c.fallback, *c.args.map {
|
return Text.translatableWithFallback(c.key, c.fallback, *c.args.map {
|
||||||
(if (it is Text) it else Text.literal(it.toString())).transformEachRecursively(function)
|
(if (it is Text) it else Text.literal(it.toString())).transformEachRecursively(function)
|
||||||
}.toTypedArray()).also { new ->
|
}.toTypedArray()).also { new ->
|
||||||
new.style = this.style
|
new.style = this.style
|
||||||
new.siblings.clear()
|
new.siblings.clear()
|
||||||
this.siblings.forEach { child ->
|
this.siblings.forEach { child ->
|
||||||
new.siblings.add(child.transformEachRecursively(function))
|
new.siblings.add(child.transformEachRecursively(function))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return function(this.copy().also { it.siblings.clear() }).also { tt ->
|
return function(this.copy().also { it.siblings.clear() }).also { tt ->
|
||||||
this.siblings.forEach {
|
this.siblings.forEach {
|
||||||
tt.siblings.add(it.transformEachRecursively(function))
|
tt.siblings.add(it.transformEachRecursively(function))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user