From d267913e206f5f7bfc16607c0dc058290e6b556f Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Wed, 13 Nov 2024 13:40:50 +0100 Subject: test: Add sack util test --- build.gradle.kts | 33 +- buildSrc/src/RepoDownload.kt | 41 +++ gradle.properties | 3 +- .../kotlin/features/diana/AncestralSpadeSolver.kt | 4 +- src/main/kotlin/repo/ItemCache.kt | 342 +++++++++++---------- src/main/kotlin/repo/ItemNameLookup.kt | 155 +++++----- src/main/kotlin/repo/RepoManager.kt | 61 ++-- src/main/kotlin/util/ErrorUtil.kt | 2 +- src/main/kotlin/util/MC.kt | 2 +- src/main/kotlin/util/TestUtil.kt | 5 + src/main/kotlin/util/skyblock/SackUtil.kt | 7 +- src/main/kotlin/util/skyblock/SkyBlockItems.kt | 10 + src/test/kotlin/util/skyblock/SackUtilTest.kt | 29 ++ 13 files changed, 425 insertions(+), 269 deletions(-) create mode 100644 buildSrc/src/RepoDownload.kt create mode 100644 src/main/kotlin/util/TestUtil.kt create mode 100644 src/main/kotlin/util/skyblock/SkyBlockItems.kt create mode 100644 src/test/kotlin/util/skyblock/SackUtilTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 7887641..53b0b87 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,6 +7,8 @@ */ import com.google.devtools.ksp.gradle.KspTaskJvm +import com.google.gson.Gson +import com.google.gson.JsonObject import moe.nea.licenseextractificator.LicenseDiscoveryTask import moe.nea.mcautotranslations.gradle.CollectTranslations import net.fabricmc.loom.LoomGradleExtension @@ -219,7 +221,8 @@ val configuredSourceSet = createIsolatedSourceSet("configured", val sodiumSourceSet = createIsolatedSourceSet("sodium") val citResewnSourceSet = createIsolatedSourceSet("citresewn", isEnabled = false) // TODO: Wait for update val yaclSourceSet = createIsolatedSourceSet("yacl") -val explosiveEnhancementSourceSet = createIsolatedSourceSet("explosiveEnhancement", isEnabled = false) // TODO: wait for their port +val explosiveEnhancementSourceSet = + createIsolatedSourceSet("explosiveEnhancement", isEnabled = false) // TODO: wait for their port val wildfireGenderSourceSet = createIsolatedSourceSet("wildfireGender", isEnabled = false) // TODO: wait on their port val modmenuSourceSet = createIsolatedSourceSet("modmenu") val reiSourceSet = createIsolatedSourceSet("rei") @@ -344,7 +347,35 @@ mcAutoTranslations { translationFunctionResolved.set("moe.nea.firmament.util.trResolved") } +val downloadTestRepo by tasks.registering(RepoDownload::class) { + this.hash.set(project.property("firmament.compiletimerepohash") as String) +} + +val updateTestRepo by tasks.registering { + outputs.upToDateWhen { false } + doLast { + val propertiesFile = rootProject.file("gradle.properties") + val json = + Gson().fromJson(uri("https://api.github.com/repos/NotEnoughUpdates/NotEnoughUpdates-REPO/branches/master") + .toURL().readText(), JsonObject::class.java) + val latestSha = json["commit"].asJsonObject["sha"].asString + var text = propertiesFile.readText() + text = text.replace("firmament\\.compiletimerepohash=[^\n]*".toRegex(), + "firmament.compiletimerepohash=$latestSha") + propertiesFile.writeText(text) + } +} + + tasks.test { + val wd =file("build/testWorkDir") + workingDir(wd) + dependsOn(downloadTestRepo) + doFirst { + wd.mkdirs() + wd.resolve("config").deleteRecursively() + systemProperty("firmament.testrepo", downloadTestRepo.flatMap { it.outputDirectory.asFile }.map { it.absolutePath }.get()) + } useJUnitPlatform() } diff --git a/buildSrc/src/RepoDownload.kt b/buildSrc/src/RepoDownload.kt new file mode 100644 index 0000000..42a09b3 --- /dev/null +++ b/buildSrc/src/RepoDownload.kt @@ -0,0 +1,41 @@ +import java.net.URI +import java.util.zip.ZipInputStream +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction + +abstract class RepoDownload : DefaultTask() { + @get:Input + abstract val hash: Property + + @get:OutputDirectory + abstract val outputDirectory: DirectoryProperty + + init { + outputDirectory.convention(project.layout.buildDirectory.dir("extracted-test-repo")) + } + + @TaskAction + fun performDownload() { + val outputDir = outputDirectory.asFile.get().absoluteFile + outputDir.mkdirs() + URI("https://github.com/notEnoughUpdates/notEnoughUpdates-rEPO/archive/${hash.get()}.zip").toURL().openStream() + .let(::ZipInputStream) + .use { zipInput -> + while (true) { + val entry = zipInput.nextEntry ?: break + val destination = outputDir.resolve( + entry.name.substringAfter('/')).absoluteFile + require(outputDir in generateSequence(destination) { it.parentFile }) + if (entry.isDirectory) continue + destination.parentFile.mkdirs() + destination.outputStream().use { output -> + zipInput.copyTo(output) + } + } + } + } +} diff --git a/gradle.properties b/gradle.properties index abca2cd..ec779d5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2023 Linnea Gräf +# SPDX-FileCopyrightText: 2023 Linnea Gräf # # SPDX-License-Identifier: CC0-1.0 # suppress inspection "UnusedProperty" for whole file @@ -9,3 +9,4 @@ loom.platform=fabric archives_base_name=Firmament maven_group=moe.nea.firmament +firmament.compiletimerepohash=a6116d945491d7c57c93d43f51250f93d62d8434 diff --git a/src/main/kotlin/features/diana/AncestralSpadeSolver.kt b/src/main/kotlin/features/diana/AncestralSpadeSolver.kt index 8918e66..ff85c00 100644 --- a/src/main/kotlin/features/diana/AncestralSpadeSolver.kt +++ b/src/main/kotlin/features/diana/AncestralSpadeSolver.kt @@ -20,6 +20,7 @@ import moe.nea.firmament.util.TimeMark import moe.nea.firmament.util.WarpUtil import moe.nea.firmament.util.render.RenderInWorldContext import moe.nea.firmament.util.skyBlockId +import moe.nea.firmament.util.skyblock.SkyBlockItems object AncestralSpadeSolver : SubscriptionOwner { var lastDing = TimeMark.farPast() @@ -29,13 +30,12 @@ object AncestralSpadeSolver : SubscriptionOwner { var nextGuess: Vec3d? = null private set - val ancestralSpadeId = SkyblockId("ANCESTRAL_SPADE") private var lastTeleportAttempt = TimeMark.farPast() fun isEnabled() = DianaWaypoints.TConfig.ancestralSpadeSolver && SBData.skyblockLocation == SkyBlockIsland.HUB - && MC.player?.inventory?.containsAny { it.skyBlockId == ancestralSpadeId } == true // TODO: add a reactive property here + && MC.player?.inventory?.containsAny { it.skyBlockId == SkyBlockItems.ANCESTRAL_SPADE } == true // TODO: add a reactive property here @Subscribe fun onKeyBind(event: WorldKeyboardEvent) { diff --git a/src/main/kotlin/repo/ItemCache.kt b/src/main/kotlin/repo/ItemCache.kt index c86934c..6fb2ab8 100644 --- a/src/main/kotlin/repo/ItemCache.kt +++ b/src/main/kotlin/repo/ItemCache.kt @@ -1,5 +1,3 @@ - - package moe.nea.firmament.repo import com.mojang.serialization.Dynamic @@ -30,186 +28,194 @@ 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.repo.RepoManager.initialize import moe.nea.firmament.util.LegacyTagParser import moe.nea.firmament.util.MC import moe.nea.firmament.util.SkyblockId +import moe.nea.firmament.util.TestUtil import moe.nea.firmament.util.mc.appendLore +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.mc.modifyLore import moe.nea.firmament.util.skyblockId object ItemCache : IReloadable { - private val cache: MutableMap = ConcurrentHashMap() - private val df = Schemas.getFixer() - val logger = LogManager.getLogger("${Firmament.logger.name}.ItemCache") - var isFlawless = true - private set - - private fun NEUItem.get10809CompoundTag(): NbtCompound = NbtCompound().apply { - put("tag", LegacyTagParser.parse(nbttag)) - putString("id", minecraftItemId) - putByte("Count", 1) - putShort("Damage", damage.toShort()) - } - - private fun NbtCompound.transformFrom10809ToModern(): NbtCompound? = - try { - df.update( - TypeReferences.ITEM_STACK, - Dynamic(NbtOps.INSTANCE, this), - -1, - SharedConstants.getGameVersion().saveVersion.id - ).value as NbtCompound - } catch (e: Exception) { - isFlawless = false - logger.error("Could not data fix up $this", e) - null - } - - fun brokenItemStack(neuItem: NEUItem?, idHint: SkyblockId? = null): ItemStack { - return ItemStack(Items.PAINTING).apply { - setCustomName(Text.literal(neuItem?.displayName ?: idHint?.neuItem ?: "null")) - appendLore( - listOf( - Text.stringifiedTranslatable( - "firmament.repo.brokenitem", - (neuItem?.skyblockItemId ?: idHint) - ) - ) - ) - } - } - - private fun NEUItem.asItemStackNow(): ItemStack { - try { - val oldItemTag = get10809CompoundTag() - val modernItemTag = oldItemTag.transformFrom10809ToModern() - ?: return brokenItemStack(this) - val itemInstance = - ItemStack.fromNbt(MC.defaultRegistries, modernItemTag).getOrNull() ?: return brokenItemStack(this) - 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() - return brokenItemStack(this) - } - } - - fun NEUItem?.asItemStack(idHint: SkyblockId? = null, loreReplacements: Map? = null): ItemStack { - if (this == null) return brokenItemStack(null, idHint) - var s = cache[this.skyblockItemId] - if (s == null) { - s = asItemStackNow() - cache[this.skyblockItemId] = s - } - if (!loreReplacements.isNullOrEmpty()) { - s = s.copy()!! - s.applyLoreReplacements(loreReplacements) - s.setCustomName(s.name.applyLoreReplacements(loreReplacements)) - } - return s - } - - fun ItemStack.applyLoreReplacements(loreReplacements: Map) { - modifyLore { lore -> - lore.map { - it.applyLoreReplacements(loreReplacements) - } - } - } - - fun Text.applyLoreReplacements(loreReplacements: Map): Text { - assert(this.siblings.isEmpty()) - var string = this.string - loreReplacements.forEach { (find, replace) -> - string = string.replace("{$find}", replace) - } - return Text.literal(string).styled { this.style } - } - - fun NEUItem.getIdentifier() = skyblockId.identifier - - 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) { - j.cancel() - } - cache.clear() - isFlawless = true - - 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 - } - } - - fun coinItem(coinAmount: Int): ItemStack { - var uuid = UUID.fromString("2070f6cb-f5db-367a-acd0-64d39a7e5d1b") - var texture = - "http://textures.minecraft.net/texture/538071721cc5b4cd406ce431a13f86083a8973e1064d2f8897869930ee6e5237" - if (coinAmount >= 100000) { - uuid = UUID.fromString("94fa2455-2881-31fe-bb4e-e3e24d58dbe3") - texture = - "http://textures.minecraft.net/texture/c9b77999fed3a2758bfeaf0793e52283817bea64044bf43ef29433f954bb52f6" - } - if (coinAmount >= 10000000) { - uuid = UUID.fromString("0af8df1f-098c-3b72-ac6b-65d65fd0b668") - texture = - "http://textures.minecraft.net/texture/7b951fed6a7b2cbc2036916dec7a46c4a56481564d14f945b6ebc03382766d3b" - } - val itemStack = ItemStack(Items.PLAYER_HEAD) - itemStack.setCustomName(Text.literal("§r§6" + NumberFormat.getInstance().format(coinAmount) + " Coins")) - itemStack.setSkullOwner(uuid, texture) - return itemStack - } + private val cache: MutableMap = ConcurrentHashMap() + private val df = Schemas.getFixer() + val logger = LogManager.getLogger("${Firmament.logger.name}.ItemCache") + var isFlawless = true + private set + + private fun NEUItem.get10809CompoundTag(): NbtCompound = NbtCompound().apply { + put("tag", LegacyTagParser.parse(nbttag)) + putString("id", minecraftItemId) + putByte("Count", 1) + putShort("Damage", damage.toShort()) + } + + private fun NbtCompound.transformFrom10809ToModern(): NbtCompound? = + try { + df.update( + TypeReferences.ITEM_STACK, + Dynamic(NbtOps.INSTANCE, this), + -1, + SharedConstants.getGameVersion().saveVersion.id + ).value as NbtCompound + } catch (e: Exception) { + isFlawless = false + logger.error("Could not data fix up $this", e) + null + } + + fun brokenItemStack(neuItem: NEUItem?, idHint: SkyblockId? = null): ItemStack { + return ItemStack(Items.PAINTING).apply { + setCustomName(Text.literal(neuItem?.displayName ?: idHint?.neuItem ?: "null")) + appendLore( + listOf( + Text.stringifiedTranslatable( + "firmament.repo.brokenitem", + (neuItem?.skyblockItemId ?: idHint) + ) + ) + ) + } + } + + private fun NEUItem.asItemStackNow(): ItemStack { + try { + val oldItemTag = get10809CompoundTag() + val modernItemTag = oldItemTag.transformFrom10809ToModern() + ?: return brokenItemStack(this) + val itemInstance = + ItemStack.fromNbt(MC.defaultRegistries, modernItemTag).getOrNull() ?: return brokenItemStack(this) + 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() + return brokenItemStack(this) + } + } + + fun NEUItem?.asItemStack(idHint: SkyblockId? = null, loreReplacements: Map? = null): ItemStack { + if (this == null) return brokenItemStack(null, idHint) + var s = cache[this.skyblockItemId] + if (s == null) { + s = asItemStackNow() + cache[this.skyblockItemId] = s + } + if (!loreReplacements.isNullOrEmpty()) { + s = s.copy()!! + s.applyLoreReplacements(loreReplacements) + s.setCustomName(s.name.applyLoreReplacements(loreReplacements)) + } + return s + } + + fun ItemStack.applyLoreReplacements(loreReplacements: Map) { + modifyLore { lore -> + lore.map { + it.applyLoreReplacements(loreReplacements) + } + } + } + + fun Text.applyLoreReplacements(loreReplacements: Map): Text { + assert(this.siblings.isEmpty()) + var string = this.string + loreReplacements.forEach { (find, replace) -> + string = string.replace("{$find}", replace) + } + return Text.literal(string).styled { this.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 + } + + @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) { + j.cancel() + } + 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 + } + } + + fun coinItem(coinAmount: Int): ItemStack { + var uuid = UUID.fromString("2070f6cb-f5db-367a-acd0-64d39a7e5d1b") + var texture = + "http://textures.minecraft.net/texture/538071721cc5b4cd406ce431a13f86083a8973e1064d2f8897869930ee6e5237" + if (coinAmount >= 100000) { + uuid = UUID.fromString("94fa2455-2881-31fe-bb4e-e3e24d58dbe3") + texture = + "http://textures.minecraft.net/texture/c9b77999fed3a2758bfeaf0793e52283817bea64044bf43ef29433f954bb52f6" + } + if (coinAmount >= 10000000) { + uuid = UUID.fromString("0af8df1f-098c-3b72-ac6b-65d65fd0b668") + texture = + "http://textures.minecraft.net/texture/7b951fed6a7b2cbc2036916dec7a46c4a56481564d14f945b6ebc03382766d3b" + } + val itemStack = ItemStack(Items.PLAYER_HEAD) + itemStack.setCustomName(Text.literal("§r§6" + NumberFormat.getInstance().format(coinAmount) + " Coins")) + itemStack.setSkullOwner(uuid, texture) + return itemStack + } + + init { + if (TestUtil.isInTest) { + initialize() + } + } + } operator fun NbtCompound.set(key: String, value: String) { - putString(key, value) + putString(key, value) } operator fun NbtCompound.set(key: String, value: NbtElement) { - put(key, value) + put(key, value) } diff --git a/src/main/kotlin/repo/ItemNameLookup.kt b/src/main/kotlin/repo/ItemNameLookup.kt index 770de85..1250730 100644 --- a/src/main/kotlin/repo/ItemNameLookup.kt +++ b/src/main/kotlin/repo/ItemNameLookup.kt @@ -1,4 +1,3 @@ - package moe.nea.firmament.repo import io.github.moulberry.repo.IReloadable @@ -12,87 +11,91 @@ import moe.nea.firmament.util.skyblockId object ItemNameLookup : IReloadable { - fun getItemNameChunks(name: String): Set { - return name.removeColorCodes().split(" ").filterTo(mutableSetOf()) { it.isNotBlank() } - } + fun getItemNameChunks(name: String): Set { + return name.removeColorCodes().split(" ").filterTo(mutableSetOf()) { it.isNotBlank() } + } + + var nameMap: NavigableMap> = TreeMap() - var nameMap: NavigableMap> = TreeMap() + override fun reload(repository: NEURepository) { + val nameMap = TreeMap>() + repository.items.items.values.forEach { item -> + getAllNamesForItem(item).forEach { name -> + val chunks = getItemNameChunks(name) + chunks.forEach { chunk -> + val set = nameMap.getOrPut(chunk, ::mutableSetOf) + set.add(item.skyblockId) + } + } + } + this.nameMap = nameMap + } - override fun reload(repository: NEURepository) { - val nameMap = TreeMap>() - repository.items.items.values.forEach { item -> - getAllNamesForItem(item).forEach { name -> - val chunks = getItemNameChunks(name) - chunks.forEach { chunk -> - val set = nameMap.getOrPut(chunk, ::mutableSetOf) - set.add(item.skyblockId) - } - } - } - this.nameMap = nameMap - } + fun getAllNamesForItem(item: NEUItem): Set { + val names = mutableSetOf() + names.add(item.displayName) + if (item.displayName.contains("Enchanted Book")) { + val enchantName = item.lore.firstOrNull() + if (enchantName != null) { + names.add(enchantName) + } + } + return names + } - fun getAllNamesForItem(item: NEUItem): Set { - val names = mutableSetOf() - names.add(item.displayName) - if (item.displayName.contains("Enchanted Book")) { - val enchantName = item.lore.firstOrNull() - if (enchantName != null) { - names.add(enchantName) - } - } - return names - } + fun findItemCandidatesByName(name: String): MutableSet { + val candidates = mutableSetOf() + for (chunk in getItemNameChunks(name)) { + val set = nameMap[chunk] ?: emptySet() + candidates.addAll(set) + } + return candidates + } - fun findItemCandidatesByName(name: String): MutableSet { - val candidates = mutableSetOf() - for (chunk in getItemNameChunks(name)) { - val set = nameMap[chunk] ?: emptySet() - candidates.addAll(set) - } - return candidates - } + fun guessItemByName( + /** + * The display name of the item. Color codes will be ignored. + */ + name: String, + /** + * Whether the [name] may contain other text, such as reforges, master stars and such. + */ + mayBeMangled: Boolean + ): SkyblockId? { + val cleanName = name.removeColorCodes() + return findBestItemFromCandidates( + findItemCandidatesByName(cleanName), + cleanName, + true + ) + } - fun guessItemByName( - /** - * The display name of the item. Color codes will be ignored. - */ - name: String, - /** - * Whether the [name] may contain other text, such as reforges, master stars and such. - */ - mayBeMangled: Boolean - ): SkyblockId? { - val cleanName = name.removeColorCodes() - return findBestItemFromCandidates( - findItemCandidatesByName(cleanName), - cleanName, - true - ) - } + fun findBestItemFromCandidates( + candidates: Iterable, + name: String, mayBeMangled: Boolean + ): SkyblockId? { + val expectedClean = name.removeColorCodes() + var bestMatch: SkyblockId? = null + var bestMatchLength = -1 + for (candidate in candidates) { + val item = RepoManager.getNEUItem(candidate) ?: continue + for (name in getAllNamesForItem(item)) { + val actualClean = name.removeColorCodes() + val matches = if (mayBeMangled) expectedClean == actualClean + else expectedClean.contains(actualClean) + if (!matches) continue + if (actualClean.length > bestMatchLength) { + bestMatch = candidate + bestMatchLength = actualClean.length + } + } + } + return bestMatch + } - fun findBestItemFromCandidates( - candidates: Iterable, - name: String, mayBeMangled: Boolean - ): SkyblockId? { - val expectedClean = name.removeColorCodes() - var bestMatch: SkyblockId? = null - var bestMatchLength = -1 - for (candidate in candidates) { - val item = RepoManager.getNEUItem(candidate) ?: continue - for (name in getAllNamesForItem(item)) { - val actualClean = name.removeColorCodes() - val matches = if (mayBeMangled) expectedClean == actualClean - else expectedClean.contains(actualClean) - if (!matches) continue - if (actualClean.length > bestMatchLength) { - bestMatch = candidate - bestMatchLength = actualClean.length - } - } - } - return bestMatch - } + init { + RepoManager.initialize() + } } diff --git a/src/main/kotlin/repo/RepoManager.kt b/src/main/kotlin/repo/RepoManager.kt index 725642e..dc39511 100644 --- a/src/main/kotlin/repo/RepoManager.kt +++ b/src/main/kotlin/repo/RepoManager.kt @@ -5,6 +5,7 @@ import io.github.moulberry.repo.NEURepositoryException import io.github.moulberry.repo.data.NEUItem import io.github.moulberry.repo.data.NEURecipe import io.github.moulberry.repo.data.Rarity +import java.nio.file.Path import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents import kotlinx.coroutines.launch import net.minecraft.client.MinecraftClient @@ -14,9 +15,11 @@ import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament.logger import moe.nea.firmament.events.ReloadRegistrationEvent import moe.nea.firmament.gui.config.ManagedConfig +import moe.nea.firmament.util.ErrorUtil 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.tr object RepoManager { @@ -49,28 +52,31 @@ object RepoManager { var recentlyFailedToUpdateItemList = false - val neuRepo: NEURepository = NEURepository.of(RepoDownloadManager.repoSavedLocation).apply { - registerReloadListener(ItemCache) - registerReloadListener(ExpLadders) - registerReloadListener(ItemNameLookup) - ReloadRegistrationEvent.publish(ReloadRegistrationEvent(this)) - registerReloadListener { - Firmament.coroutineScope.launch(MinecraftDispatcher) { - if (!trySendClientboundUpdateRecipesPacket()) { - logger.warn("Failed to issue a ClientboundUpdateRecipesPacket (to reload REI). This may lead to an outdated item list.") - recentlyFailedToUpdateItemList = true + val essenceRecipeProvider = EssenceRecipeProvider() + val recipeCache = BetterRepoRecipeCache(essenceRecipeProvider) + + fun makeNEURepository(path: Path): NEURepository { + return NEURepository.of(path).apply { + registerReloadListener(ItemCache) + registerReloadListener(ExpLadders) + registerReloadListener(ItemNameLookup) + ReloadRegistrationEvent.publish(ReloadRegistrationEvent(this)) + registerReloadListener { + if (TestUtil.isInTest) return@registerReloadListener + Firmament.coroutineScope.launch(MinecraftDispatcher) { + if (!trySendClientboundUpdateRecipesPacket()) { + logger.warn("Failed to issue a ClientboundUpdateRecipesPacket (to reload REI). This may lead to an outdated item list.") + recentlyFailedToUpdateItemList = true + } } } + registerReloadListener(essenceRecipeProvider) + registerReloadListener(recipeCache) } } - val essenceRecipeProvider = EssenceRecipeProvider() - val recipeCache = BetterRepoRecipeCache(essenceRecipeProvider) - - init { - neuRepo.registerReloadListener(essenceRecipeProvider) - neuRepo.registerReloadListener(recipeCache) - } + lateinit var neuRepo: NEURepository + private set fun getAllRecipes() = neuRepo.items.items.values.asSequence().flatMap { it.recipes } @@ -106,6 +112,11 @@ object RepoManager { } } + fun reloadForTest(from: Path) { + neuRepo = makeNEURepository(from) + reload() + } + fun reload() { try { ItemCache.ReloadProgressHud.reportProgress("Reloading from Disk", @@ -114,16 +125,24 @@ object RepoManager { ItemCache.ReloadProgressHud.isEnabled = true neuRepo.reload() } 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.") ) ItemCache.ReloadProgressHud.isEnabled = false - exc.printStackTrace() } } + private var wasInitialized = false fun initialize() { + if (wasInitialized) return + wasInitialized = true + System.getProperty("firmament.testrepo")?.let { compTimeRepo -> + reloadForTest(Path.of(compTimeRepo)) + return + } + neuRepo = makeNEURepository(RepoDownloadManager.repoSavedLocation) if (Config.autoUpdate) { launchAsyncUpdate() } else { @@ -131,6 +150,12 @@ object RepoManager { } } + init { + if (TestUtil.isInTest) { + initialize() + } + } + fun getPotentialStubPetData(skyblockId: SkyblockId): PetData? { val parts = skyblockId.neuItem.split(";") if (parts.size != 2) { diff --git a/src/main/kotlin/util/ErrorUtil.kt b/src/main/kotlin/util/ErrorUtil.kt index b06093b..5dc44d3 100644 --- a/src/main/kotlin/util/ErrorUtil.kt +++ b/src/main/kotlin/util/ErrorUtil.kt @@ -10,7 +10,7 @@ import moe.nea.firmament.Firmament @Suppress("NOTHING_TO_INLINE") // Suppressed since i want the logger to not pick up the ErrorUtil stack-frame object ErrorUtil { var aggressiveErrors = run { - Thread.currentThread().stackTrace.any { it.className.startsWith("org.junit.") } || Firmament.DEBUG + TestUtil.isInTest || Firmament.DEBUG || ErrorUtil::class.java.desiredAssertionStatus() } diff --git a/src/main/kotlin/util/MC.kt b/src/main/kotlin/util/MC.kt index cbcd8ae..3f5d633 100644 --- a/src/main/kotlin/util/MC.kt +++ b/src/main/kotlin/util/MC.kt @@ -102,7 +102,7 @@ object MC { inline val handledScreen: HandledScreen<*>? get() = instance.currentScreen as? HandledScreen<*> inline val window get() = instance.window inline val currentRegistries: RegistryWrapper.WrapperLookup? get() = world?.registryManager - val defaultRegistries: RegistryWrapper.WrapperLookup = BuiltinRegistries.createWrapperLookup() + val defaultRegistries: RegistryWrapper.WrapperLookup by lazy { BuiltinRegistries.createWrapperLookup() } inline val currentOrDefaultRegistries get() = currentRegistries ?: defaultRegistries val defaultItems: RegistryWrapper.Impl = defaultRegistries.getOrThrow(RegistryKeys.ITEM) var lastWorld: World? = null diff --git a/src/main/kotlin/util/TestUtil.kt b/src/main/kotlin/util/TestUtil.kt new file mode 100644 index 0000000..68a406f --- /dev/null +++ b/src/main/kotlin/util/TestUtil.kt @@ -0,0 +1,5 @@ +package moe.nea.firmament.util + +object TestUtil { + val isInTest = Thread.currentThread().stackTrace.any { it.className.startsWith("org.junit.") } +} diff --git a/src/main/kotlin/util/skyblock/SackUtil.kt b/src/main/kotlin/util/skyblock/SackUtil.kt index 2679949..fd67c44 100644 --- a/src/main/kotlin/util/skyblock/SackUtil.kt +++ b/src/main/kotlin/util/skyblock/SackUtil.kt @@ -60,8 +60,13 @@ object SackUtil { @Subscribe fun updateFromChat(event: ProcessChatEvent) { if (!event.unformattedString.startsWith("[Sacks]")) return + getUpdatesFromMessage(event.text) + } + + fun getUpdatesFromMessage(text: Text): List { val update = ChatUpdate() - event.text.siblings.forEach(update::updateFromHoverText) + text.siblings.forEach(update::updateFromHoverText) + return update.updates } data class SackUpdate( diff --git a/src/main/kotlin/util/skyblock/SkyBlockItems.kt b/src/main/kotlin/util/skyblock/SkyBlockItems.kt new file mode 100644 index 0000000..c94ebfe --- /dev/null +++ b/src/main/kotlin/util/skyblock/SkyBlockItems.kt @@ -0,0 +1,10 @@ +package moe.nea.firmament.util.skyblock + +import moe.nea.firmament.util.SkyblockId + +object SkyBlockItems { + val ROTTEN_FLESH = SkyblockId("ROTTEN_FLESH") + val ENCHANTED_DIAMOND = SkyblockId("ENCHANTED_DIAMOND") + val DIAMOND = SkyblockId("DIAMOND") + val ANCESTRAL_SPADE = SkyblockId("ANCESTRAL_SPADE") +} diff --git a/src/test/kotlin/util/skyblock/SackUtilTest.kt b/src/test/kotlin/util/skyblock/SackUtilTest.kt new file mode 100644 index 0000000..e0e3e63 --- /dev/null +++ b/src/test/kotlin/util/skyblock/SackUtilTest.kt @@ -0,0 +1,29 @@ +package moe.nea.firmament.test.util.skyblock + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import moe.nea.firmament.test.testutil.ItemResources +import moe.nea.firmament.util.skyblock.SackUtil +import moe.nea.firmament.util.skyblock.SkyBlockItems + +class SackUtilTest { + @Test + fun testOneRottenFlesh() { + Assertions.assertEquals( + listOf( + SackUtil.SackUpdate(SkyBlockItems.ROTTEN_FLESH, "Rotten Flesh", 1) + ), + SackUtil.getUpdatesFromMessage(ItemResources.loadText("sacks/gain-rotten-flesh")) + ) + } + + @Test + fun testAFewRegularItems() { + Assertions.assertEquals( + listOf( + SackUtil.SackUpdate(SkyBlockItems.ROTTEN_FLESH, "Rotten Flesh", 1) + ), + SackUtil.getUpdatesFromMessage(ItemResources.loadText("sacks/gain-and-lose-regular")) + ) + } +} -- cgit