feat: Allow breaking itemstacks even further for faster repo reloads

This commit is contained in:
Linnea Gräf
2025-06-22 16:09:43 +02:00
parent 7c45e48050
commit cbc8eff63a
19 changed files with 128 additions and 25 deletions

View File

@@ -6,6 +6,7 @@ import net.minecraft.item.ItemStack
import net.minecraft.nbt.NbtCompound
import net.minecraft.util.Identifier
import moe.nea.firmament.Firmament
import moe.nea.firmament.repo.ExpensiveItemCacheApi
import moe.nea.firmament.repo.ItemCache
import moe.nea.firmament.util.MC
@@ -57,6 +58,7 @@ object LegacyItemData {
val enchantmentLut = enchantmentData.associateBy { Identifier.ofVanilla(it.name) }
val itemDat = getLegacyData<List<ItemData>>("items")
@OptIn(ExpensiveItemCacheApi::class) // This is fine, we get loaded in a thread.
val itemLut = itemDat.flatMap { item ->
item.allVariants().map { legacyItemType ->
val nbt = ItemCache.convert189ToModern(NbtCompound().apply {

View File

@@ -15,6 +15,7 @@ import moe.nea.firmament.events.WorldReadyEvent
import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.gui.hud.MoulConfigHud
import moe.nea.firmament.repo.ExpensiveItemCacheApi
import moe.nea.firmament.repo.ItemNameLookup
import moe.nea.firmament.repo.SBItemStack
import moe.nea.firmament.util.MC
@@ -202,7 +203,8 @@ object AnniversaryFeatures : FirmamentFeature {
SBItemStack(SkyblockId.NULL)
}
@Bind
@OptIn(ExpensiveItemCacheApi::class)
@Bind
fun name(): String {
return when (backedBy) {
is Reward.Coins -> "Coins"

View File

@@ -8,6 +8,7 @@ import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.ScreenChangeEvent
import moe.nea.firmament.events.SlotRenderEvents
import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.repo.ExpensiveItemCacheApi
import moe.nea.firmament.repo.SBItemStack
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.skyblockId
@@ -45,6 +46,7 @@ object CraftingOverlay : FirmamentFeature {
override val identifier: String
get() = "crafting-overlay"
@OptIn(ExpensiveItemCacheApi::class)
@Subscribe
fun onSlotRender(event: SlotRenderEvents.After) {
val slot = event.slot

View File

@@ -3,6 +3,7 @@ package moe.nea.firmament.features.inventory
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.repo.ExpensiveItemCacheApi
import moe.nea.firmament.repo.HypixelStaticData
import moe.nea.firmament.repo.ItemCache
import moe.nea.firmament.repo.ItemCache.asItemStack
@@ -18,6 +19,7 @@ object ItemHotkeys {
val openGlobalTradeInterface by keyBindingWithDefaultUnbound("global-trade-interface")
}
@OptIn(ExpensiveItemCacheApi::class)
@Subscribe
fun onHandledInventoryPress(event: HandledScreenKeyPressedEvent) {
if (!event.matches(TConfig.openGlobalTradeInterface)) {

View File

@@ -11,6 +11,7 @@ import net.minecraft.command.argument.ItemStackArgumentType
import net.minecraft.item.ItemStack
import net.minecraft.resource.featuretoggle.FeatureFlags
import net.minecraft.util.Identifier
import moe.nea.firmament.repo.ExpensiveItemCacheApi
import moe.nea.firmament.repo.ItemCache.asItemStack
import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.MC
@@ -40,6 +41,7 @@ data class InventoryButton(
}
val dimensions = Dimension(18, 18)
val getItemForName = ::getItemForName0.memoize(1024)
@OptIn(ExpensiveItemCacheApi::class)
fun getItemForName0(icon: String): ItemStack {
val repoItem = RepoManager.getNEUItem(SkyblockId(icon))
var itemStack = repoItem.asItemStack(idHint = SkyblockId(icon))

View File

@@ -8,6 +8,7 @@ import net.minecraft.entity.LivingEntity
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import moe.nea.firmament.repo.ExpensiveItemCacheApi
import moe.nea.firmament.repo.SBItemStack
import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.mc.setEncodedSkullOwner
@@ -31,6 +32,7 @@ object ModifyEquipment : EntityModifier {
return entity
}
@OptIn(ExpensiveItemCacheApi::class)
private fun createItem(item: String): ItemStack {
val split = item.split("#")
if (split.size != 2) return SBItemStack(SkyblockId(item)).asImmutableItemStack()

View File

@@ -0,0 +1,8 @@
package moe.nea.firmament.repo
/**
* Marker for functions that could potentially invoke DFU. Please do not call on a lot of objects at once, or try to make sure the item is cached and fall back to a more gentle function call using [SBItemStack.isWarm] and similar functions.
*/
@RequiresOptIn
@Retention(AnnotationRetention.BINARY)
annotation class ExpensiveItemCacheApi

View File

@@ -8,8 +8,14 @@ import java.text.NumberFormat
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import org.apache.logging.log4j.LogManager
import kotlinx.coroutines.Job
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.jvm.optionals.getOrNull
import net.minecraft.SharedConstants
import net.minecraft.component.DataComponentTypes
@@ -30,6 +36,7 @@ import moe.nea.firmament.repo.RepoManager.initialize
import moe.nea.firmament.util.LegacyFormattingCode
import moe.nea.firmament.util.LegacyTagParser
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.MinecraftDispatcher
import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.TestUtil
import moe.nea.firmament.util.directLiteralStringContent
@@ -40,6 +47,7 @@ import moe.nea.firmament.util.mc.loreAccordingToNbt
import moe.nea.firmament.util.mc.modifyLore
import moe.nea.firmament.util.mc.setCustomName
import moe.nea.firmament.util.mc.setSkullOwner
import moe.nea.firmament.util.skyblockId
import moe.nea.firmament.util.transformEachRecursively
object ItemCache : IReloadable {
@@ -56,7 +64,10 @@ object ItemCache : IReloadable {
putShort("Damage", damage.toShort())
}
@ExpensiveItemCacheApi
private fun NbtCompound.transformFrom10809ToModern() = convert189ToModern(this@transformFrom10809ToModern)
@ExpensiveItemCacheApi
fun convert189ToModern(nbtComponent: NbtCompound): NbtCompound? =
try {
df.update(
@@ -126,6 +137,7 @@ object ItemCache : IReloadable {
return base
}
@ExpensiveItemCacheApi
private fun NEUItem.asItemStackNow(): ItemStack {
try {
val oldItemTag = get10809CompoundTag()
@@ -150,6 +162,7 @@ object ItemCache : IReloadable {
return skyblockId.neuItem in cache
}
@ExpensiveItemCacheApi
fun NEUItem?.asItemStack(idHint: SkyblockId? = null, loreReplacements: Map<String, String>? = null): ItemStack {
if (this == null) return brokenItemStack(null, idHint)
var s = cache[this.skyblockItemId]
@@ -183,22 +196,43 @@ object ItemCache : IReloadable {
}
}
var job: Job? = null
var itemRecacheScope: CoroutineScope? = null
override fun reload(repository: NEURepository) {
val j = job
if (j != null && j.isActive) {
j.cancel()
private var recacheSoonSubmitted = mutableSetOf<SkyblockId>()
@OptIn(ExpensiveItemCacheApi::class)
fun recacheSoon(neuItem: NEUItem) {
itemRecacheScope?.launch {
if (!withContext(MinecraftDispatcher) {
recacheSoonSubmitted.add(neuItem.skyblockId)
}) {
return@launch
}
neuItem.asItemStack()
}
}
@OptIn(ExpensiveItemCacheApi::class)
override fun reload(repository: NEURepository) {
val j = itemRecacheScope
j?.cancel("New reload invoked")
cache.clear()
isFlawless = true
if (TestUtil.isInTest) return
job = Firmament.coroutineScope.launch {
val items = repository.items?.items ?: return@launch
items.values.forEach {
it.asItemStack() // Rebuild cache
}
val newScope =
CoroutineScope(Firmament.coroutineScope.coroutineContext + SupervisorJob(Firmament.globalJob) + Dispatchers.Default)
val items = repository.items?.items
newScope.launch {
val items = items ?: return@launch
items.values.chunked(500).map { chunk ->
async {
chunk.forEach {
it.asItemStack() // Rebuild cache
}
}
}.awaitAll()
}
itemRecacheScope = newScope
}
fun coinItem(coinAmount: Int): ItemStack {

View File

@@ -81,6 +81,7 @@ class MiningRepoData : IReloadable {
) {
@Transient
val dropItem = baseDrop?.let(::SBItemStack)
@OptIn(ExpensiveItemCacheApi::class)
private val labeledStack by lazy {
dropItem?.asCopiedItemStack()?.also(::markItemStack)
}
@@ -110,6 +111,7 @@ class MiningRepoData : IReloadable {
fun isActiveIn(location: SkyBlockIsland) = onlyIn == null || location in onlyIn
@OptIn(ExpensiveItemCacheApi::class)
private fun convertToModernBlock(): Block? {
// TODO: this should be in a shared util, really
val newCompound = ItemCache.convert189ToModern(NbtCompound().apply {

View File

@@ -11,6 +11,7 @@ import kotlinx.coroutines.launch
import net.minecraft.client.MinecraftClient
import net.minecraft.network.packet.s2c.play.SynchronizeRecipesS2CPacket
import net.minecraft.recipe.display.CuttingRecipeDisplay
import net.minecraft.util.StringIdentifiable
import moe.nea.firmament.Firmament
import moe.nea.firmament.Firmament.logger
import moe.nea.firmament.events.ReloadRegistrationEvent
@@ -46,7 +47,16 @@ object RepoManager {
}
val alwaysSuperCraft by toggle("enable-super-craft") { true }
var warnForMissingItemListMod by toggle("warn-for-missing-item-list-mod") { true }
val perfectTooltips by toggle("perfect-tooltips") { false }
val perfectRenders by choice("perfect-renders") { PerfectRender.RENDER }
}
enum class PerfectRender(val label: String) : StringIdentifiable {
NOTHING("nothing"),
RENDER("render"),
RENDER_AND_TEXT("text"),
;
override fun asString(): String? = label
}
val currentDownloadedSha by RepoDownloadManager::latestSavedVersionHash
@@ -136,8 +146,10 @@ object RepoManager {
} catch (exc: NEURepositoryException) {
ErrorUtil.softError("Failed to reload repository", exc)
MC.sendChat(
tr("firmament.repo.reloadfail",
"Failed to reload repository. This will result in some mod features not working.")
tr(
"firmament.repo.reloadfail",
"Failed to reload repository. This will result in some mod features not working."
)
)
}
}

View File

@@ -353,7 +353,9 @@ data class SBItemStack constructor(
}
// TODO: avoid instantiating the item stack here
@ExpensiveItemCacheApi
val itemType: ItemType? get() = ItemType.fromItemStack(asImmutableItemStack())
@ExpensiveItemCacheApi
val rarity: Rarity? get() = Rarity.fromItem(asImmutableItemStack())
private var itemStack_: ItemStack? = null
@@ -364,6 +366,7 @@ data class SBItemStack constructor(
group("power").toInt()
} ?: 0
@ExpensiveItemCacheApi
private val itemStack: ItemStack
get() {
val itemStack = itemStack_ ?: run {
@@ -437,15 +440,18 @@ data class SBItemStack constructor(
return false
}
@OptIn(ExpensiveItemCacheApi::class)
fun asLazyImmutableItemStack(): ItemStack? {
if (isWarm()) return asImmutableItemStack()
return null
}
fun asImmutableItemStack(): ItemStack {
@ExpensiveItemCacheApi
fun asImmutableItemStack(): ItemStack { // TODO: add a "or fallback to painting" option to asLazyImmutableItemStack to be used in more places.
return itemStack
}
@ExpensiveItemCacheApi
fun asCopiedItemStack(): ItemStack {
return itemStack.copy()
}

View File

@@ -9,6 +9,7 @@ import net.minecraft.text.Text
import net.minecraft.util.Identifier
import moe.nea.firmament.Firmament
import moe.nea.firmament.repo.EssenceRecipeProvider
import moe.nea.firmament.repo.ExpensiveItemCacheApi
import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.repo.SBItemStack
import moe.nea.firmament.util.SkyblockId
@@ -62,6 +63,7 @@ object SBEssenceUpgradeRecipeRenderer : GenericRecipeRenderer<EssenceRecipeProvi
return listOfNotNull(SBItemStack(recipe.itemId))
}
@OptIn(ExpensiveItemCacheApi::class)
override val icon: ItemStack get() = SBItemStack(SkyblockId("ESSENCE_WITHER")).asImmutableItemStack()
override val title: Text = tr("firmament.category.essence", "Essence Upgrades")
override val identifier: Identifier = Firmament.identifier("essence_upgrade")

View File

@@ -22,6 +22,7 @@ import net.minecraft.network.codec.PacketCodec
import net.minecraft.network.codec.PacketCodecs
import net.minecraft.util.Identifier
import moe.nea.firmament.repo.ExpLadders
import moe.nea.firmament.repo.ExpensiveItemCacheApi
import moe.nea.firmament.repo.ItemCache.asItemStack
import moe.nea.firmament.repo.set
import moe.nea.firmament.util.collections.WeakCache
@@ -86,6 +87,7 @@ value class SkyblockId(val neuItem: String) : Comparable<SkyblockId> {
val NEUItem.skyblockId get() = SkyblockId(skyblockItemId)
val NEUIngredient.skyblockId get() = SkyblockId(itemId)
@ExpensiveItemCacheApi
fun NEUItem.guessRecipeId(): String? {
if (!skyblockItemId.contains(";")) return skyblockItemId
val item = this.asItemStack()