diff options
Diffstat (limited to 'src/main/kotlin/repo')
-rw-r--r-- | src/main/kotlin/repo/ItemCache.kt | 42 | ||||
-rw-r--r-- | src/main/kotlin/repo/MiningRepoData.kt | 131 | ||||
-rw-r--r-- | src/main/kotlin/repo/RepoDownloadManager.kt | 193 | ||||
-rw-r--r-- | src/main/kotlin/repo/RepoManager.kt | 25 | ||||
-rw-r--r-- | src/main/kotlin/repo/RepoModResourcePack.kt | 5 | ||||
-rw-r--r-- | src/main/kotlin/repo/SBItemStack.kt | 8 |
6 files changed, 253 insertions, 151 deletions
diff --git a/src/main/kotlin/repo/ItemCache.kt b/src/main/kotlin/repo/ItemCache.kt index 9fa0083..0967ad1 100644 --- a/src/main/kotlin/repo/ItemCache.kt +++ b/src/main/kotlin/repo/ItemCache.kt @@ -61,11 +61,12 @@ object ItemCache : IReloadable { putShort("Damage", damage.toShort()) } - private fun NbtCompound.transformFrom10809ToModern(): NbtCompound? = + private fun NbtCompound.transformFrom10809ToModern() = convert189ToModern(this@transformFrom10809ToModern) + fun convert189ToModern(nbtComponent: NbtCompound): NbtCompound? = try { df.update( TypeReferences.ITEM_STACK, - Dynamic(NbtOps.INSTANCE, this), + Dynamic(NbtOps.INSTANCE, nbtComponent), -1, SharedConstants.getGameVersion().saveVersion.id ).value as NbtCompound @@ -184,31 +185,6 @@ object ItemCache : IReloadable { 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 - } - - @get:Bind("current") - var current: Double = 0.0 - - @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() - } - } - override fun reload(repository: NEURepository) { val j = job if (j != null && j.isActive) { @@ -218,20 +194,10 @@ object ItemCache : IReloadable { 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 + val items = repository.items?.items ?: return@launch items.values.forEach { it.asItemStack() // Rebuild cache - ReloadProgressHud.reportProgress(recacheItems, i++, items.size) } - ReloadProgressHud.isEnabled = false } } diff --git a/src/main/kotlin/repo/MiningRepoData.kt b/src/main/kotlin/repo/MiningRepoData.kt new file mode 100644 index 0000000..e40292d --- /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 io.github.moulberry.repo.data.NEUItem +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.jvm.optionals.getOrNull +import kotlin.streams.asSequence +import net.minecraft.block.Block +import net.minecraft.item.BlockItem +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NbtCompound +import net.minecraft.text.Text +import moe.nea.firmament.repo.ReforgeStore.kJson +import moe.nea.firmament.util.MC +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.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) + 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 = Text.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 + + private fun convertToModernBlock(): Block? { + // TODO: this should be in a shared util, really + val newCompound = ItemCache.convert189ToModern(NbtCompound().apply { + putString("id", itemId) + putShort("Damage", damage) + }) ?: return null + val itemStack = ItemStack.fromNbt(MC.defaultRegistries, newCompound).getOrNull() ?: 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/RepoDownloadManager.kt b/src/main/kotlin/repo/RepoDownloadManager.kt index 3efd83b..888248d 100644 --- a/src/main/kotlin/repo/RepoDownloadManager.kt +++ b/src/main/kotlin/repo/RepoDownloadManager.kt @@ -1,5 +1,3 @@ - - package moe.nea.firmament.repo import io.ktor.client.call.body @@ -28,101 +26,102 @@ import moe.nea.firmament.util.iterate 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") { - RepoManager.Config.branch = "master" - } - val response = - Firmament.httpClient.get("https://api.github.com/repos/${RepoManager.Config.username}/${RepoManager.Config.reponame}/commits/${RepoManager.Config.branch}") - if (response.status.value != 200) { - return null - } - return response.body<GithubCommitsResponse>().sha - } - - private suspend fun downloadGithubArchive(url: String): Path = withContext(IO) { - val response = Firmament.httpClient.get(url) - val targetFile = Files.createTempFile("firmament-repo", ".zip") - val outputChannel = Files.newByteChannel(targetFile, StandardOpenOption.CREATE, StandardOpenOption.WRITE) - response.bodyAsChannel().copyTo(outputChannel) - targetFile - } - - /** - * Downloads the latest repository from github, setting [latestSavedVersionHash]. - * @return true, if an update was performed, false, otherwise (no update needed, or wasn't able to complete update) - */ - suspend fun downloadUpdate(force: Boolean): Boolean = withContext(CoroutineName("Repo Update Check")) { - val latestSha = requestLatestGithubSha() - if (latestSha == null) { - logger.warn("Could not request github API to retrieve latest REPO sha.") - return@withContext false - } - val currentSha = loadSavedVersionHash() - if (latestSha != currentSha || force) { - val requestUrl = - "https://github.com/${RepoManager.Config.username}/${RepoManager.Config.reponame}/archive/$latestSha.zip" - logger.info("Planning to upgrade repository from $currentSha to $latestSha from $requestUrl") - val zipFile = downloadGithubArchive(requestUrl) - logger.info("Download repository zip file to $zipFile. Deleting old repository") - withContext(IO) { repoSavedLocation.toFile().deleteRecursively() } - logger.info("Extracting new repository") - withContext(IO) { extractNewRepository(zipFile) } - logger.info("Repository loaded on disk.") - saveVersionHash(latestSha) - return@withContext true - } else { - logger.debug("Repository on latest sha $currentSha. Not performing update") - return@withContext false - } - } - - private fun extractNewRepository(zipFile: Path) { - repoSavedLocation.createDirectories() - ZipInputStream(zipFile.inputStream()).use { cis -> - while (true) { - val entry = cis.nextEntry ?: break - if (entry.isDirectory) continue - val extractedLocation = - repoSavedLocation.resolve( - entry.name.substringAfter('/', missingDelimiterValue = "") - ) - if (repoSavedLocation !in extractedLocation.iterate { it.parent }) { - logger.error("Firmament detected an invalid zip file. This is a potential security risk, please report this in the Firmament discord.") - throw RuntimeException("Firmament detected an invalid zip file. This is a potential security risk, please report this in the Firmament discord.") - } - extractedLocation.parent.createDirectories() - extractedLocation.outputStream().use { cis.copyTo(it) } - } - } - } + 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(branchOverride: String?): String? { + if (RepoManager.Config.branch == "prerelease") { + RepoManager.Config.branch = "master" + } + val response = + Firmament.httpClient.get("https://api.github.com/repos/${RepoManager.Config.username}/${RepoManager.Config.reponame}/commits/${branchOverride ?: RepoManager.Config.branch}") + if (response.status.value != 200) { + return null + } + return response.body<GithubCommitsResponse>().sha + } + + private suspend fun downloadGithubArchive(url: String): Path = withContext(IO) { + val response = Firmament.httpClient.get(url) + val targetFile = Files.createTempFile("firmament-repo", ".zip") + val outputChannel = Files.newByteChannel(targetFile, StandardOpenOption.CREATE, StandardOpenOption.WRITE) + response.bodyAsChannel().copyTo(outputChannel) + targetFile + } + + /** + * Downloads the latest repository from github, setting [latestSavedVersionHash]. + * @return true, if an update was performed, false, otherwise (no update needed, or wasn't able to complete update) + */ + suspend fun downloadUpdate(force: Boolean, branch: String? = null): Boolean = + withContext(CoroutineName("Repo Update Check")) { + val latestSha = requestLatestGithubSha(branch) + if (latestSha == null) { + logger.warn("Could not request github API to retrieve latest REPO sha.") + return@withContext false + } + val currentSha = loadSavedVersionHash() + if (latestSha != currentSha || force) { + val requestUrl = + "https://github.com/${RepoManager.Config.username}/${RepoManager.Config.reponame}/archive/$latestSha.zip" + logger.info("Planning to upgrade repository from $currentSha to $latestSha from $requestUrl") + val zipFile = downloadGithubArchive(requestUrl) + logger.info("Download repository zip file to $zipFile. Deleting old repository") + withContext(IO) { repoSavedLocation.toFile().deleteRecursively() } + logger.info("Extracting new repository") + withContext(IO) { extractNewRepository(zipFile) } + logger.info("Repository loaded on disk.") + saveVersionHash(latestSha) + return@withContext true + } else { + logger.debug("Repository on latest sha $currentSha. Not performing update") + return@withContext false + } + } + + private fun extractNewRepository(zipFile: Path) { + repoSavedLocation.createDirectories() + ZipInputStream(zipFile.inputStream()).use { cis -> + while (true) { + val entry = cis.nextEntry ?: break + if (entry.isDirectory) continue + val extractedLocation = + repoSavedLocation.resolve( + entry.name.substringAfter('/', missingDelimiterValue = "") + ) + if (repoSavedLocation !in extractedLocation.iterate { it.parent }) { + logger.error("Firmament detected an invalid zip file. This is a potential security risk, please report this in the Firmament discord.") + throw RuntimeException("Firmament detected an invalid zip file. This is a potential security risk, please report this in the Firmament discord.") + } + extractedLocation.parent.createDirectories() + extractedLocation.outputStream().use { cis.copyTo(it) } + } + } + } } diff --git a/src/main/kotlin/repo/RepoManager.kt b/src/main/kotlin/repo/RepoManager.kt index 6d9ba14..cc36fba 100644 --- a/src/main/kotlin/repo/RepoManager.kt +++ b/src/main/kotlin/repo/RepoManager.kt @@ -54,6 +54,7 @@ object RepoManager { val essenceRecipeProvider = EssenceRecipeProvider() val recipeCache = BetterRepoRecipeCache(essenceRecipeProvider, ReforgeStore) + val miningData = MiningRepoData() fun makeNEURepository(path: Path): NEURepository { return NEURepository.of(path).apply { @@ -63,6 +64,8 @@ object RepoManager { registerReloadListener(ItemNameLookup) registerReloadListener(ReforgeStore) registerReloadListener(essenceRecipeProvider) + registerReloadListener(recipeCache) + registerReloadListener(miningData) ReloadRegistrationEvent.publish(ReloadRegistrationEvent(this)) registerReloadListener { if (TestUtil.isInTest) return@registerReloadListener @@ -73,7 +76,6 @@ object RepoManager { } } } - registerReloadListener(recipeCache) } } @@ -100,16 +102,16 @@ object RepoManager { fun getNEUItem(skyblockId: SkyblockId): NEUItem? = neuRepo.items.getItemBySkyblockId(skyblockId.neuItem) + fun downloadOverridenBranch(branch: String) { + Firmament.coroutineScope.launch { + RepoDownloadManager.downloadUpdate(true, branch) + reload() + } + } + fun launchAsyncUpdate(force: Boolean = false) { Firmament.coroutineScope.launch { - ItemCache.ReloadProgressHud.reportProgress("Downloading", 0, -1) // TODO: replace with a proper bouncy bar - ItemCache.ReloadProgressHud.isEnabled = true - try { - RepoDownloadManager.downloadUpdate(force) - ItemCache.ReloadProgressHud.reportProgress("Download complete", 1, 1) - } finally { - ItemCache.ReloadProgressHud.isEnabled = false - } + RepoDownloadManager.downloadUpdate(force) reload() } } @@ -127,10 +129,6 @@ object RepoManager { return } try { - ItemCache.ReloadProgressHud.reportProgress("Reloading from Disk", - 0, - -1) // TODO: replace with a proper bouncy bar - ItemCache.ReloadProgressHud.isEnabled = true logger.info("Repo reload started.") neuRepo.reload() logger.info("Repo reload completed.") @@ -140,7 +138,6 @@ object RepoManager { tr("firmament.repo.reloadfail", "Failed to reload repository. This will result in some mod features not working.") ) - ItemCache.ReloadProgressHud.isEnabled = false } } diff --git a/src/main/kotlin/repo/RepoModResourcePack.kt b/src/main/kotlin/repo/RepoModResourcePack.kt index 617efec..2fdf710 100644 --- a/src/main/kotlin/repo/RepoModResourcePack.kt +++ b/src/main/kotlin/repo/RepoModResourcePack.kt @@ -5,6 +5,7 @@ import java.nio.file.Files import java.nio.file.Path import java.util.* import net.fabricmc.fabric.api.resource.ModResourcePack +import net.fabricmc.fabric.impl.resource.loader.ModResourcePackSorter import net.fabricmc.loader.api.FabricLoader import net.fabricmc.loader.api.metadata.ModMetadata import kotlin.io.path.exists @@ -28,9 +29,9 @@ import moe.nea.firmament.Firmament class RepoModResourcePack(val basePath: Path) : ModResourcePack { companion object { - fun append(packs: MutableList<in ModResourcePack>) { + fun append(packs: ModResourcePackSorter) { Firmament.logger.info("Registering mod resource pack") - packs.add(RepoModResourcePack(RepoDownloadManager.repoSavedLocation)) + packs.addPack(RepoModResourcePack(RepoDownloadManager.repoSavedLocation)) } fun createResourceDirectly(identifier: Identifier): Optional<Resource> { diff --git a/src/main/kotlin/repo/SBItemStack.kt b/src/main/kotlin/repo/SBItemStack.kt index da34707..3690866 100644 --- a/src/main/kotlin/repo/SBItemStack.kt +++ b/src/main/kotlin/repo/SBItemStack.kt @@ -34,6 +34,7 @@ import moe.nea.firmament.util.modifyExtraAttributes import moe.nea.firmament.util.petData import moe.nea.firmament.util.prepend import moe.nea.firmament.util.reconstitute +import moe.nea.firmament.util.removeColorCodes import moe.nea.firmament.util.skyBlockId import moe.nea.firmament.util.skyblock.ItemType import moe.nea.firmament.util.skyblock.Rarity @@ -81,6 +82,7 @@ data class SBItemStack constructor( } val EMPTY = SBItemStack(SkyblockId.NULL, 0) + private val BREAKING_POWER_REGEX = "Breaking Power (?<power>[0-9]+)".toPattern() operator fun invoke(itemStack: ItemStack): SBItemStack { val skyblockId = itemStack.skyBlockId ?: SkyblockId.NULL return SBItemStack( @@ -349,6 +351,12 @@ data class SBItemStack constructor( private var itemStack_: ItemStack? = null + val breakingPower: Int + get() = + BREAKING_POWER_REGEX.useMatch(neuItem?.lore?.firstOrNull()?.removeColorCodes()) { + group("power").toInt() + } ?: 0 + private val itemStack: ItemStack get() { val itemStack = itemStack_ ?: run { |