diff options
Diffstat (limited to 'src/main/kotlin/repo')
18 files changed, 1105 insertions, 412 deletions
diff --git a/src/main/kotlin/repo/ExpLadder.kt b/src/main/kotlin/repo/ExpLadder.kt index fbc9eb8..25a74de 100644 --- a/src/main/kotlin/repo/ExpLadder.kt +++ b/src/main/kotlin/repo/ExpLadder.kt @@ -19,7 +19,8 @@ object ExpLadders : IReloadable { val expInCurrentLevel: Float, var expTotal: Float, ) { - val percentageToNextLevel: Float = expInCurrentLevel / expRequiredForNextLevel + val percentageToNextLevel: Float = expInCurrentLevel / expRequiredForNextLevel + val percentageToMaxLevel: Float = expTotal / expRequiredForMaxLevel } data class ExpLadder( diff --git a/src/main/kotlin/repo/ExpensiveItemCacheApi.kt b/src/main/kotlin/repo/ExpensiveItemCacheApi.kt new file mode 100644 index 0000000..eef95a6 --- /dev/null +++ b/src/main/kotlin/repo/ExpensiveItemCacheApi.kt @@ -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 diff --git a/src/main/kotlin/repo/HypixelStaticData.kt b/src/main/kotlin/repo/HypixelStaticData.kt index 181aa70..d6be96f 100644 --- a/src/main/kotlin/repo/HypixelStaticData.kt +++ b/src/main/kotlin/repo/HypixelStaticData.kt @@ -1,23 +1,19 @@ package moe.nea.firmament.repo -import io.ktor.client.call.body -import io.ktor.client.request.get import org.apache.logging.log4j.LogManager -import org.lwjgl.glfw.GLFW import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.delay +import kotlinx.coroutines.future.await import kotlinx.coroutines.launch -import kotlinx.coroutines.withTimeoutOrNull import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlin.time.Duration.Companion.minutes import moe.nea.firmament.Firmament import moe.nea.firmament.apis.CollectionResponse import moe.nea.firmament.apis.CollectionSkillData -import moe.nea.firmament.keybindings.IKeyBinding import moe.nea.firmament.util.SkyblockId -import moe.nea.firmament.util.async.waitForInput +import moe.nea.firmament.util.net.HttpUtil object HypixelStaticData { private val logger = LogManager.getLogger("Firmament.HypixelStaticData") @@ -25,7 +21,13 @@ object HypixelStaticData { private val hypixelApiBaseUrl = "https://api.hypixel.net" var lowestBin: Map<SkyblockId, Double> = mapOf() private set - var bazaarData: Map<SkyblockId, BazaarData> = mapOf() + var avg1dlowestBin: Map<SkyblockId, Double> = mapOf() + private set + var avg3dlowestBin: Map<SkyblockId, Double> = mapOf() + private set + var avg7dlowestBin: Map<SkyblockId, Double> = mapOf() + private set + var bazaarData: Map<SkyblockId.BazaarStock, BazaarData> = mapOf() private set var collectionData: Map<String, CollectionSkillData> = mapOf() private set @@ -56,9 +58,11 @@ object HypixelStaticData { val products: Map<SkyblockId.BazaarStock, BazaarData> = mapOf(), ) - fun getPriceOfItem(item: SkyblockId): Double? = bazaarData[item]?.quickStatus?.buyPrice ?: lowestBin[item] - fun hasBazaarStock(item: SkyblockId): Boolean { + fun getPriceOfItem(item: SkyblockId): Double? = + bazaarData[SkyblockId.BazaarStock.fromSkyBlockId(item)]?.quickStatus?.buyPrice ?: lowestBin[item] + + fun hasBazaarStock(item: SkyblockId.BazaarStock): Boolean { return item in bazaarData } @@ -74,35 +78,43 @@ object HypixelStaticData { Firmament.coroutineScope.launch { while (true) { logger.info("Updating NEU prices") - updatePrices() - delay(10.minutes) + fetchPricesFromMoulberry() + delay(5.minutes) + } + } + Firmament.coroutineScope.launch { + while (true) { + logger.info("Updating bazaar prices") + fetchBazaarPrices() + delay(2.minutes) } } - } - - private suspend fun updatePrices() { - awaitAll( - Firmament.coroutineScope.async { fetchBazaarPrices() }, - Firmament.coroutineScope.async { fetchPricesFromMoulberry() }, - ) } private suspend fun fetchPricesFromMoulberry() { - lowestBin = Firmament.httpClient.get("$moulberryBaseUrl/lowestbin.json") - .body<Map<SkyblockId, Double>>() + lowestBin = HttpUtil.request("$moulberryBaseUrl/lowestbin.json") + .forJson<Map<SkyblockId, Double>>().await() + avg1dlowestBin = HttpUtil.request("$moulberryBaseUrl/auction_averages_lbin/1day.json") + .forJson<Map<SkyblockId, Double>>().await() + avg3dlowestBin = HttpUtil.request("$moulberryBaseUrl/auction_averages_lbin/3day.json") + .forJson<Map<SkyblockId, Double>>().await() + avg7dlowestBin = HttpUtil.request("$moulberryBaseUrl/auction_averages_lbin/7day.json") + .forJson<Map<SkyblockId, Double>>().await() } private suspend fun fetchBazaarPrices() { - val response = Firmament.httpClient.get("$hypixelApiBaseUrl/skyblock/bazaar").body<BazaarResponse>() + val response = HttpUtil.request("$hypixelApiBaseUrl/skyblock/bazaar").forJson<BazaarResponse>() + .await() if (!response.success) { logger.warn("Retrieved unsuccessful bazaar data") } - bazaarData = response.products.mapKeys { it.key.toRepoId() } + bazaarData = response.products } private suspend fun updateCollectionData() { val response = - Firmament.httpClient.get("$hypixelApiBaseUrl/resources/skyblock/collections").body<CollectionResponse>() + HttpUtil.request("$hypixelApiBaseUrl/resources/skyblock/collections").forJson<CollectionResponse>() + .await() if (!response.success) { logger.warn("Retrieved unsuccessful collection data") } diff --git a/src/main/kotlin/repo/ItemCache.kt b/src/main/kotlin/repo/ItemCache.kt index 9fa0083..be07042 100644 --- a/src/main/kotlin/repo/ItemCache.kt +++ b/src/main/kotlin/repo/ItemCache.kt @@ -4,71 +4,83 @@ import com.mojang.serialization.Dynamic import io.github.moulberry.repo.IReloadable import io.github.moulberry.repo.NEURepository import io.github.moulberry.repo.data.NEUItem -import io.github.notenoughupdates.moulconfig.xml.Bind 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.io.path.readText import kotlin.jvm.optionals.getOrNull import net.minecraft.SharedConstants -import net.minecraft.client.resource.language.I18n -import net.minecraft.component.DataComponentTypes -import net.minecraft.component.type.NbtComponent -import net.minecraft.datafixer.Schemas -import net.minecraft.datafixer.TypeReferences -import net.minecraft.item.ItemStack -import net.minecraft.item.Items -import net.minecraft.nbt.NbtCompound -import net.minecraft.nbt.NbtElement +import net.minecraft.core.component.DataComponents +import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.NbtOps -import net.minecraft.nbt.NbtString -import net.minecraft.text.MutableText -import net.minecraft.text.Style -import net.minecraft.text.Text +import net.minecraft.nbt.StringTag +import net.minecraft.nbt.Tag +import net.minecraft.nbt.TagParser +import net.minecraft.network.chat.Component +import net.minecraft.network.chat.MutableComponent +import net.minecraft.network.chat.Style +import net.minecraft.resources.ResourceLocation +import net.minecraft.util.datafix.DataFixers +import net.minecraft.util.datafix.fixes.References +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import net.minecraft.world.item.component.CustomData import moe.nea.firmament.Firmament -import moe.nea.firmament.gui.config.HudMeta -import moe.nea.firmament.gui.config.HudPosition -import moe.nea.firmament.gui.hud.MoulConfigHud +import moe.nea.firmament.features.debug.ExportedTestConstantMeta 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 import moe.nea.firmament.util.mc.FirmamentDataComponentTypes import moe.nea.firmament.util.mc.appendLore import moe.nea.firmament.util.mc.displayNameAccordingToNbt +import moe.nea.firmament.util.mc.loadItemFromNbt 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 { private val cache: MutableMap<String, ItemStack> = ConcurrentHashMap() - private val df = Schemas.getFixer() + private val df = DataFixers.getDataFixer() val logger = LogManager.getLogger("${Firmament.logger.name}.ItemCache") var isFlawless = true private set - private fun NEUItem.get10809CompoundTag(): NbtCompound = NbtCompound().apply { + private fun NEUItem.get10809CompoundTag(): CompoundTag = CompoundTag().apply { put("tag", LegacyTagParser.parse(nbttag)) putString("id", minecraftItemId) putByte("Count", 1) putShort("Damage", damage.toShort()) } - private fun NbtCompound.transformFrom10809ToModern(): NbtCompound? = + @ExpensiveItemCacheApi + private fun CompoundTag.transformFrom10809ToModern() = convert189ToModern(this@transformFrom10809ToModern) + val currentSaveVersion = SharedConstants.getCurrentVersion().dataVersion().version + + @ExpensiveItemCacheApi + fun convert189ToModern(nbtComponent: CompoundTag): CompoundTag? = try { df.update( - TypeReferences.ITEM_STACK, - Dynamic(NbtOps.INSTANCE, this), + References.ITEM_STACK, + Dynamic(NbtOps.INSTANCE, nbtComponent), -1, - SharedConstants.getGameVersion().saveVersion.id - ).value as NbtCompound + currentSaveVersion + ).value as CompoundTag } catch (e: Exception) { isFlawless = false logger.error("Could not data fix up $this", e) @@ -85,24 +97,24 @@ object ItemCache : IReloadable { fun brokenItemStack(neuItem: NEUItem?, idHint: SkyblockId? = null): ItemStack { return ItemStack(Items.PAINTING).apply { - setCustomName(Text.literal(neuItem?.displayName ?: idHint?.neuItem ?: "null")) + setCustomName(Component.literal(neuItem?.displayName ?: idHint?.neuItem ?: "null")) appendLore( listOf( - Text.stringifiedTranslatable( + Component.translatableEscape( "firmament.repo.brokenitem", (neuItem?.skyblockItemId ?: idHint) ) ) ) - set(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(NbtCompound().apply { - put("ID", NbtString.of(neuItem?.skyblockItemId ?: idHint?.neuItem ?: "null")) + set(DataComponents.CUSTOM_DATA, CustomData.of(CompoundTag().apply { + put("ID", StringTag.valueOf(neuItem?.skyblockItemId ?: idHint?.neuItem ?: "null")) })) set(FirmamentDataComponentTypes.IS_BROKEN, true) } } - fun un189Lore(lore: String): MutableText { - val base = Text.literal("") + fun un189Lore(lore: String): MutableComponent { + val base = Component.literal("") base.setStyle(Style.EMPTY.withItalic(false)) var lastColorCode = Style.EMPTY var readOffset = 0 @@ -113,7 +125,7 @@ object ItemCache : IReloadable { } val text = lore.substring(readOffset, nextCode) if (text.isNotEmpty()) { - base.append(Text.literal(text).setStyle(lastColorCode)) + base.append(Component.literal(text).setStyle(lastColorCode)) } readOffset = nextCode + 2 if (nextCode + 1 < lore.length) { @@ -123,25 +135,55 @@ object ItemCache : IReloadable { if (modernFormatting.isColor) { lastColorCode = Style.EMPTY.withColor(modernFormatting) } else { - lastColorCode = lastColorCode.withFormatting(modernFormatting) + lastColorCode = lastColorCode.applyFormat(modernFormatting) } } } return base } + fun tryFindFromModernFormat(skyblockId: SkyblockId): CompoundTag? { + val overlayFile = + RepoManager.overlayData.getMostModernReadableOverlay(skyblockId, currentSaveVersion) ?: return null + val overlay = TagParser.parseCompoundFully(overlayFile.path.readText()) + val result = ExportedTestConstantMeta.SOURCE_CODEC.decode( + NbtOps.INSTANCE, overlay + ).result().getOrNull() ?: return null + val meta = result.first + return df.update( + References.ITEM_STACK, + Dynamic(NbtOps.INSTANCE, result.second), + meta.dataVersion, + currentSaveVersion + ).value as CompoundTag + } + + @ExpensiveItemCacheApi private fun NEUItem.asItemStackNow(): ItemStack { + try { + var modernItemTag = tryFindFromModernFormat(this.skyblockId) val oldItemTag = get10809CompoundTag() - val modernItemTag = oldItemTag.transformFrom10809ToModern() - ?: return brokenItemStack(this) + var usedOldNbt = false + if (modernItemTag == null) { + usedOldNbt = true + modernItemTag = oldItemTag.transformFrom10809ToModern() + ?: return brokenItemStack(this) + } val itemInstance = - ItemStack.fromNbt(MC.defaultRegistries, modernItemTag).getOrNull() ?: return brokenItemStack(this) + loadItemFromNbt( modernItemTag) ?: return brokenItemStack(this) + if (usedOldNbt) { + val tag = oldItemTag.getCompound("tag") + val extraAttributes = tag.flatMap { it.getCompound("ExtraAttributes") } + .getOrNull() + if (extraAttributes != null) + itemInstance.set(DataComponents.CUSTOM_DATA, CustomData.of(extraAttributes)) + val itemModel = tag.flatMap { it.getString("ItemModel") }.getOrNull() + if (itemModel != null) + itemInstance.set(DataComponents.ITEM_MODEL, ResourceLocation.parse(itemModel)) + } itemInstance.loreAccordingToNbt = lore.map { un189Lore(it) } itemInstance.displayNameAccordingToNbt = un189Lore(displayName) - val extraAttributes = oldItemTag.getCompound("tag").getCompound("ExtraAttributes") - if (extraAttributes != null) - itemInstance.set(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(extraAttributes)) return itemInstance } catch (e: Exception) { e.printStackTrace() @@ -149,6 +191,11 @@ object ItemCache : IReloadable { } } + fun hasCacheFor(skyblockId: SkyblockId): Boolean { + 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] @@ -159,7 +206,7 @@ object ItemCache : IReloadable { if (!loreReplacements.isNullOrEmpty()) { s = s.copy()!! s.applyLoreReplacements(loreReplacements) - s.setCustomName(s.name.applyLoreReplacements(loreReplacements)) + s.setCustomName(s.hoverName.applyLoreReplacements(loreReplacements)) } return s } @@ -172,67 +219,59 @@ object ItemCache : IReloadable { } } - fun Text.applyLoreReplacements(loreReplacements: Map<String, String>): Text { + fun Component.applyLoreReplacements(loreReplacements: Map<String, String>): Component { return this.transformEachRecursively { var string = it.directLiteralStringContent ?: return@transformEachRecursively it loreReplacements.forEach { (find, replace) -> string = string.replace("{$find}", replace) } - Text.literal(string).setStyle(it.style) + Component.literal(string).setStyle(it.style) } } - var job: Job? = null - - object ReloadProgressHud : MoulConfigHud( - "repo_reload", HudMeta(HudPosition(0.0, 0.0, 1F), Text.literal("Repo Reload"), 180, 18)) { - - - var isEnabled = false - override fun shouldRender(): Boolean { - return isEnabled - } + var itemRecacheScope: CoroutineScope? = null - @get:Bind("current") - var current: Double = 0.0 + private var recacheSoonSubmitted = mutableSetOf<SkyblockId>() - @get:Bind("label") - var label: String = "" - - @get:Bind("max") - var max: Double = 0.0 - - fun reportProgress(label: String, current: Int, max: Int) { - this.label = label - this.current = current.toDouble() - this.max = max.toDouble() + @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 = job - if (j != null && j.isActive) { - j.cancel() - } + 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 - if (items == null) { - ReloadProgressHud.isEnabled = false - return@launch - } - val recacheItems = I18n.translate("firmament.repo.cache") - ReloadProgressHud.reportProgress(recacheItems, 0, items.size) - ReloadProgressHud.isEnabled = true - var i = 0 - items.values.forEach { - it.asItemStack() // Rebuild cache - ReloadProgressHud.reportProgress(recacheItems, i++, items.size) - } - ReloadProgressHud.isEnabled = false + val newScope = + CoroutineScope( + Firmament.coroutineScope.coroutineContext + + SupervisorJob(Firmament.globalJob) + + Dispatchers.Default.limitedParallelism( + (Runtime.getRuntime().availableProcessors() / 4).coerceAtLeast(1) + ) + ) + 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 { @@ -250,7 +289,7 @@ object ItemCache : IReloadable { "http://textures.minecraft.net/texture/7b951fed6a7b2cbc2036916dec7a46c4a56481564d14f945b6ebc03382766d3b" } val itemStack = ItemStack(Items.PLAYER_HEAD) - itemStack.setCustomName(Text.literal("§r§6" + NumberFormat.getInstance().format(coinAmount) + " Coins")) + itemStack.setCustomName(Component.literal("§r§6" + NumberFormat.getInstance().format(coinAmount) + " Coins")) itemStack.setSkullOwner(uuid, texture) return itemStack } @@ -264,10 +303,10 @@ object ItemCache : IReloadable { } -operator fun NbtCompound.set(key: String, value: String) { +operator fun CompoundTag.set(key: String, value: String) { putString(key, value) } -operator fun NbtCompound.set(key: String, value: NbtElement) { +operator fun CompoundTag.set(key: String, value: Tag) { put(key, value) } diff --git a/src/main/kotlin/repo/MiningRepoData.kt b/src/main/kotlin/repo/MiningRepoData.kt new file mode 100644 index 0000000..5b5b016 --- /dev/null +++ b/src/main/kotlin/repo/MiningRepoData.kt @@ -0,0 +1,131 @@ +package moe.nea.firmament.repo + +import io.github.moulberry.repo.IReloadable +import io.github.moulberry.repo.NEURepository +import java.util.Collections +import java.util.NavigableMap +import java.util.TreeMap +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient +import kotlinx.serialization.serializer +import kotlin.streams.asSequence +import net.minecraft.world.level.block.Block +import net.minecraft.world.item.BlockItem +import net.minecraft.world.item.ItemStack +import net.minecraft.nbt.CompoundTag +import net.minecraft.network.chat.Component +import moe.nea.firmament.repo.ReforgeStore.kJson +import moe.nea.firmament.util.SBData +import moe.nea.firmament.util.SkyBlockIsland +import moe.nea.firmament.util.SkyblockId +import moe.nea.firmament.util.mc.FirmamentDataComponentTypes +import moe.nea.firmament.util.mc.displayNameAccordingToNbt +import moe.nea.firmament.util.mc.loadItemFromNbt +import moe.nea.firmament.util.skyblockId + +class MiningRepoData : IReloadable { + var customMiningAreas: Map<SkyBlockIsland, CustomMiningArea> = mapOf() + private set + var customMiningBlocks: List<CustomMiningBlock> = listOf() + private set + var toolsByBreakingPower: NavigableMap<BreakingPowerKey, SBItemStack> = Collections.emptyNavigableMap() + private set + + + data class BreakingPowerKey( + val breakingPower: Int, + val itemId: SkyblockId? = null + ) { + companion object { + val COMPARATOR: Comparator<BreakingPowerKey> = + Comparator + .comparingInt<BreakingPowerKey> { it.breakingPower } + .thenComparing(Comparator.comparing( + { it.itemId }, + nullsFirst(Comparator.comparing<SkyblockId, Boolean> { "PICK" in it.neuItem || "BING" in it.neuItem }.thenComparing(Comparator.naturalOrder<SkyblockId>())))) + } + } + + override fun reload(repo: NEURepository) { + customMiningAreas = repo.file("mining/custom_mining_areas.json") + ?.kJson(serializer()) ?: mapOf() + customMiningBlocks = repo.tree("mining/blocks") + .asSequence() + .filter { it.path.endsWith(".json") } + .map { it.kJson(serializer<CustomMiningBlock>()) } + .toList() + toolsByBreakingPower = Collections.unmodifiableNavigableMap( + repo.items.items + .values + .asSequence() + .map { SBItemStack(it.skyblockId) } + .filter { it.breakingPower > 0 } + .associateTo(TreeMap<BreakingPowerKey, SBItemStack>(BreakingPowerKey.COMPARATOR)) { + BreakingPowerKey(it.breakingPower, it.skyblockId) to it + }) + } + + fun getToolsThatCanBreak(breakingPower: Int): Collection<SBItemStack> { + return toolsByBreakingPower.tailMap(BreakingPowerKey(breakingPower, null), true).values + } + + @Serializable + data class CustomMiningBlock( + val breakingPower: Int = 0, + val blockStrength: Int = 0, + val name: String? = null, + val baseDrop: SkyblockId? = null, + val blocks189: List<Block189> = emptyList() + ) { + @Transient + val dropItem = baseDrop?.let(::SBItemStack) + @OptIn(ExpensiveItemCacheApi::class) + private val labeledStack by lazy { + dropItem?.asCopiedItemStack()?.also(::markItemStack) + } + + private fun markItemStack(itemStack: ItemStack) { + itemStack.set(FirmamentDataComponentTypes.CUSTOM_MINING_BLOCK_DATA, this) + if (name != null) + itemStack.displayNameAccordingToNbt = Component.literal(name) + } + + fun getDisplayItem(block: Block): ItemStack { + return labeledStack ?: ItemStack(block).also(::markItemStack) + } + } + + @Serializable + data class Block189( + val itemId: String, + val damage: Short = 0, + val onlyIn: List<SkyBlockIsland>? = null, + ) { + @Transient + val block = convertToModernBlock() + + val isCurrentlyActive: Boolean + get() = isActiveIn(SBData.skyblockLocation ?: SkyBlockIsland.NIL) + + 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(CompoundTag().apply { + putString("id", itemId) + putShort("Damage", damage) + }) ?: return null + val itemStack = loadItemFromNbt(newCompound) ?: return null + val blockItem = itemStack.item as? BlockItem ?: return null + return blockItem.block + } + } + + @Serializable + data class CustomMiningArea( + val isSpecialMining: Boolean = true + ) + + +} diff --git a/src/main/kotlin/repo/ModernOverlaysData.kt b/src/main/kotlin/repo/ModernOverlaysData.kt new file mode 100644 index 0000000..543b800 --- /dev/null +++ b/src/main/kotlin/repo/ModernOverlaysData.kt @@ -0,0 +1,41 @@ +package moe.nea.firmament.repo + +import io.github.moulberry.repo.IReloadable +import io.github.moulberry.repo.NEURepository +import java.nio.file.Path +import kotlin.io.path.extension +import kotlin.io.path.isDirectory +import kotlin.io.path.listDirectoryEntries +import kotlin.io.path.nameWithoutExtension +import moe.nea.firmament.util.SkyblockId + +// TODO: move this over to the repo parser +class ModernOverlaysData : IReloadable { + data class OverlayFile( + val version: Int, + val path: Path, + ) + + var overlays: Map<SkyblockId, List<OverlayFile>> = mapOf() + override fun reload(repo: NEURepository) { + val items = mutableMapOf<SkyblockId, MutableList<OverlayFile>>() + repo.baseFolder.resolve("itemsOverlay") + .takeIf { it.isDirectory() } + ?.listDirectoryEntries() + ?.forEach { versionFolder -> + val version = versionFolder.fileName.toString().toIntOrNull() ?: return@forEach + versionFolder.listDirectoryEntries() + .forEach { item -> + if (item.extension != "snbt") return@forEach + val itemId = item.nameWithoutExtension + items.getOrPut(SkyblockId(itemId)) { mutableListOf() }.add(OverlayFile(version, item)) + } + } + this.overlays = items + } + + fun getOverlayFiles(skyblockId: SkyblockId) = overlays[skyblockId] ?: listOf() + fun getMostModernReadableOverlay(skyblockId: SkyblockId, version: Int) = getOverlayFiles(skyblockId) + .filter { it.version <= version } + .maxByOrNull { it.version } +} diff --git a/src/main/kotlin/repo/Reforge.kt b/src/main/kotlin/repo/Reforge.kt index dc0d93d..5f6506f 100644 --- a/src/main/kotlin/repo/Reforge.kt +++ b/src/main/kotlin/repo/Reforge.kt @@ -13,10 +13,10 @@ import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.serializer -import net.minecraft.item.Item -import net.minecraft.registry.RegistryKey -import net.minecraft.registry.RegistryKeys -import net.minecraft.util.Identifier +import net.minecraft.world.item.Item +import net.minecraft.resources.ResourceKey +import net.minecraft.core.registries.Registries +import net.minecraft.resources.ResourceLocation import moe.nea.firmament.util.ReforgeId import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.skyblock.ItemType @@ -64,9 +64,9 @@ data class Reforge( } jsonElement["itemId"]?.let { decoder.json.decodeFromJsonElement(serializer<List<String>>(), it).forEach { - val ident = Identifier.tryParse(it) + val ident = ResourceLocation.tryParse(it) if (ident != null) - filters.add(AllowsVanillaItemType(RegistryKey.of(RegistryKeys.ITEM, ident))) + filters.add(AllowsVanillaItemType(ResourceKey.create(Registries.ITEM, ident))) } } return filters @@ -90,8 +90,8 @@ data class Reforge( return AllowsItemType(ItemType.ofName((it as JsonPrimitive).content)) } jsonObject["minecraftId"]?.let { - return AllowsVanillaItemType(RegistryKey.of(RegistryKeys.ITEM, - Identifier.of((it as JsonPrimitive).content))) + return AllowsVanillaItemType(ResourceKey.create(Registries.ITEM, + ResourceLocation.parse((it as JsonPrimitive).content))) } error("Unknown item type") } @@ -104,7 +104,7 @@ data class Reforge( data class AllowsItemType(val itemType: ItemType) : ReforgeEligibilityFilter data class AllowsInternalName(val internalName: SkyblockId) : ReforgeEligibilityFilter - data class AllowsVanillaItemType(val minecraftId: RegistryKey<Item>) : ReforgeEligibilityFilter + data class AllowsVanillaItemType(val minecraftId: ResourceKey<Item>) : ReforgeEligibilityFilter } diff --git a/src/main/kotlin/repo/ReforgeStore.kt b/src/main/kotlin/repo/ReforgeStore.kt index 4c01974..cf8b434 100644 --- a/src/main/kotlin/repo/ReforgeStore.kt +++ b/src/main/kotlin/repo/ReforgeStore.kt @@ -9,8 +9,8 @@ import io.github.moulberry.repo.NEURepositoryException import io.github.moulberry.repo.data.NEURecipe import kotlinx.serialization.KSerializer import kotlinx.serialization.serializer -import net.minecraft.item.Item -import net.minecraft.registry.RegistryKey +import net.minecraft.world.item.Item +import net.minecraft.resources.ResourceKey import moe.nea.firmament.Firmament import moe.nea.firmament.util.ReforgeId import moe.nea.firmament.util.SkyblockId @@ -23,7 +23,7 @@ object ReforgeStore : ExtraRecipeProvider, IReloadable { } var byType: Map<ItemType, List<Reforge>> = mapOf() - var byVanilla: Map<RegistryKey<Item>, List<Reforge>> = mapOf() + var byVanilla: Map<ResourceKey<Item>, List<Reforge>> = mapOf() var byInternalName: Map<SkyblockId, List<Reforge>> = mapOf() var modifierLut = mapOf<ReforgeId, Reforge>() var byReforgeStone = mapOf<SkyblockId, Reforge>() @@ -52,7 +52,7 @@ object ReforgeStore : ExtraRecipeProvider, IReloadable { byReforgeStone = allReforges.filter { it.reforgeStone != null } .associateBy { it.reforgeStone!! } val byType = mutableMapOf<ItemType, MutableList<Reforge>>() - val byVanilla = mutableMapOf<RegistryKey<Item>, MutableList<Reforge>>() + val byVanilla = mutableMapOf<ResourceKey<Item>, MutableList<Reforge>>() val byInternalName = mutableMapOf<SkyblockId, MutableList<Reforge>>() this.byType = byType this.byVanilla = byVanilla diff --git a/src/main/kotlin/repo/RepoDownloadManager.kt b/src/main/kotlin/repo/RepoDownloadManager.kt index 3efd83b..adbe36e 100644 --- a/src/main/kotlin/repo/RepoDownloadManager.kt +++ b/src/main/kotlin/repo/RepoDownloadManager.kt @@ -1,11 +1,5 @@ - - package moe.nea.firmament.repo -import io.ktor.client.call.body -import io.ktor.client.request.get -import io.ktor.client.statement.bodyAsChannel -import io.ktor.utils.io.copyTo import java.io.IOException import java.nio.file.Files import java.nio.file.Path @@ -13,6 +7,7 @@ import java.nio.file.StandardOpenOption import java.util.zip.ZipInputStream import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.future.await import kotlinx.coroutines.withContext import kotlinx.serialization.Serializable import kotlin.io.path.createDirectories @@ -23,106 +18,112 @@ import kotlin.io.path.readText import kotlin.io.path.writeText import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament.logger +import moe.nea.firmament.repo.RepoDownloadManager.latestSavedVersionHash import moe.nea.firmament.util.iterate +import moe.nea.firmament.util.net.HttpUtil object RepoDownloadManager { - val repoSavedLocation = Firmament.DATA_DIR.resolve("repo-extracted") - val repoMetadataLocation = Firmament.DATA_DIR.resolve("loaded-repo-sha.txt") - - private fun loadSavedVersionHash(): String? = - if (repoSavedLocation.exists()) { - if (repoMetadataLocation.exists()) { - try { - repoMetadataLocation.readText().trim() - } catch (e: IOException) { - null - } - } else { - null - } - } else null - - private fun saveVersionHash(versionHash: String) { - latestSavedVersionHash = versionHash - repoMetadataLocation.writeText(versionHash) - } - - var latestSavedVersionHash: String? = loadSavedVersionHash() - private set - - @Serializable - private class GithubCommitsResponse(val sha: String) - - private suspend fun requestLatestGithubSha(): String? { - if (RepoManager.Config.branch == "prerelease") { - Repo |
