diff options
Diffstat (limited to 'src')
17 files changed, 279 insertions, 49 deletions
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiCommonPlugin.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiCommonPlugin.kt index 98ac276..71e867a 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiCommonPlugin.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiCommonPlugin.kt @@ -2,9 +2,11 @@ package moe.nea.firmament.compat.rei import me.shedaniel.rei.api.common.entry.type.EntryTypeRegistry import me.shedaniel.rei.api.common.plugins.REICommonPlugin +import moe.nea.firmament.repo.RepoManager class FirmamentReiCommonPlugin : REICommonPlugin { override fun registerEntryTypes(registry: EntryTypeRegistry) { + if (!RepoManager.shouldLoadREI()) return registry.register(FirmamentReiPlugin.SKYBLOCK_ITEM_TYPE_ID, SBItemEntryDefinition) } } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt index 89c3e19..3a494b9 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt @@ -55,6 +55,7 @@ class FirmamentReiPlugin : REIClientPlugin { @OptIn(ExpensiveItemCacheApi::class) override fun registerTransferHandlers(registry: TransferHandlerRegistry) { + if (!RepoManager.shouldLoadREI()) return registry.register(TransferHandler { context -> val screen = context.containerScreen val display = context.display @@ -64,8 +65,11 @@ class FirmamentReiPlugin : REIClientPlugin { val neuItem = RepoManager.getNEUItem(SkyblockId(recipe.output.itemId)) ?: error("Could not find neu item ${recipe.output.itemId} which is used in a recipe output") val useSuperCraft = context.isStackedCrafting || RepoManager.Config.alwaysSuperCraft - if (neuItem.isVanilla && useSuperCraft) return@TransferHandler TransferHandler.Result.createFailed(Text.translatable( - "firmament.recipe.novanilla")) + if (neuItem.isVanilla && useSuperCraft) return@TransferHandler TransferHandler.Result.createFailed( + Text.translatable( + "firmament.recipe.novanilla" + ) + ) var shouldReturn = true if (context.isActuallyCrafting && !useSuperCraft) { val craftingScreen = (screen as? GenericContainerScreen) @@ -85,13 +89,16 @@ class FirmamentReiPlugin : REIClientPlugin { } - val generics = listOf<GenericREIRecipeCategory<*>>( // Order matters: The order in here is the order in which they show up in REI + val generics = listOf<GenericREIRecipeCategory<*>>( + // Order matters: The order in here is the order in which they show up in REI GenericREIRecipeCategory(SBCraftingRecipeRenderer), GenericREIRecipeCategory(SBForgeRecipeRenderer), GenericREIRecipeCategory(SBEssenceUpgradeRecipeRenderer), ) override fun registerCategories(registry: CategoryRegistry) { + if (!RepoManager.shouldLoadREI()) return + registry.add(generics) registry.add(SBMobDropRecipe.Category) registry.add(SBKatRecipe.Category) @@ -105,6 +112,8 @@ class FirmamentReiPlugin : REIClientPlugin { } override fun registerDisplays(registry: DisplayRegistry) { + if (!RepoManager.shouldLoadREI()) return + generics.forEach { it.registerDynamicGenerator(registry) } @@ -114,16 +123,21 @@ class FirmamentReiPlugin : REIClientPlugin { ) registry.registerDisplayGenerator( SBMobDropRecipe.Category.categoryIdentifier, - SkyblockMobDropRecipeDynamicGenerator) + SkyblockMobDropRecipeDynamicGenerator + ) registry.registerDisplayGenerator( SBShopRecipe.Category.categoryIdentifier, - SkyblockShopRecipeDynamicGenerator) + SkyblockShopRecipeDynamicGenerator + ) registry.registerDisplayGenerator( SBKatRecipe.Category.categoryIdentifier, - SkyblockKatRecipeDynamicGenerator) + SkyblockKatRecipeDynamicGenerator + ) } override fun registerCollapsibleEntries(registry: CollapsibleEntryRegistry) { + if (!RepoManager.shouldLoadREI()) return + if (!RepoManager.Config.disableItemGroups) RepoManager.neuRepo.constants.parents.parents .forEach { (parent, children) -> @@ -148,6 +162,8 @@ class FirmamentReiPlugin : REIClientPlugin { } override fun registerEntries(registry: EntryRegistry) { + if (!RepoManager.shouldLoadREI()) return + registry.removeEntryIf { true } RepoManager.neuRepo.items?.items?.values?.forEach { neuItem -> registry.addEntry(SBItemEntryDefinition.getEntry(neuItem.skyblockId)) diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt index d73500a..5e4eee3 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt @@ -45,7 +45,7 @@ object NEUItemEntryRenderer : EntryRenderer<SBItemStack> { delta: Float ) { val neuItem = entry.value.neuItem - val itemToRender = if(RepoManager.Config.perfectRenders < RepoManager.PerfectRender.RENDER && !entry.value.isWarm() && neuItem != null) { + val itemToRender = if(!RepoManager.Config.perfectRenders.rendersPerfectVisuals() && !entry.value.isWarm() && neuItem != null) { ItemCache.recacheSoon(neuItem) ItemStack(Items.PAINTING) } else { @@ -72,7 +72,7 @@ object NEUItemEntryRenderer : EntryRenderer<SBItemStack> { @OptIn(ExpensiveItemCacheApi::class) override fun getTooltip(entry: EntryStack<SBItemStack>, tooltipContext: TooltipContext): Tooltip? { - if (!entry.value.isWarm() && RepoManager.Config.perfectRenders < RepoManager.PerfectRender.RENDER_AND_TEXT) { + if (!entry.value.isWarm() && !RepoManager.Config.perfectRenders.rendersPerfectText()) { val neuItem = entry.value.neuItem if (neuItem != null) { val lore = mutableListOf<Text>() diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/SBItemEntryDefinition.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/SBItemEntryDefinition.kt index 1d0a611..740eeeb 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/SBItemEntryDefinition.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/SBItemEntryDefinition.kt @@ -46,7 +46,7 @@ object SBItemEntryDefinition : EntryDefinition<SBItemStack> { @OptIn(ExpensiveItemCacheApi::class) override fun asFormattedText(entry: EntryStack<SBItemStack>, value: SBItemStack): Text { val neuItem = entry.value.neuItem - return if (RepoManager.Config.perfectRenders < RepoManager.PerfectRender.RENDER_AND_TEXT || entry.value.isWarm() || neuItem == null) { + return if (!RepoManager.Config.perfectRenders.rendersPerfectText() || entry.value.isWarm() || neuItem == null) { VanillaEntryTypes.ITEM.definition.asFormattedText(entry.asItemEntry(), value.asImmutableItemStack()) } else { Text.literal(neuItem.displayName) diff --git a/src/main/kotlin/features/debug/PowerUserTools.kt b/src/main/kotlin/features/debug/PowerUserTools.kt index 1a7b2cf..0800a4f 100644 --- a/src/main/kotlin/features/debug/PowerUserTools.kt +++ b/src/main/kotlin/features/debug/PowerUserTools.kt @@ -41,6 +41,7 @@ import moe.nea.firmament.util.mc.iterableArmorItems import moe.nea.firmament.util.mc.loreAccordingToNbt import moe.nea.firmament.util.skyBlockId import moe.nea.firmament.util.tr +import moe.nea.firmament.util.grey object PowerUserTools : FirmamentFeature { override val identifier: String @@ -60,6 +61,7 @@ object PowerUserTools : FirmamentFeature { val exportUIRecipes by keyBindingWithDefaultUnbound("export-recipe") val exportNpcLocation by keyBindingWithDefaultUnbound("export-npc-location") val highlightNonOverlayItems by toggle("highlight-non-overlay") { false } + val dontHighlightSemicolonItems by toggle("dont-highlight-semicolon-items") { false } } override val config @@ -228,7 +230,7 @@ object PowerUserTools : FirmamentFeature { fun addItemId(it: ItemTooltipEvent) { if (TConfig.showItemIds) { val id = it.stack.skyBlockId ?: return - it.lines.add(Text.stringifiedTranslatable("firmament.tooltip.skyblockid", id.neuItem)) + it.lines.add(Text.stringifiedTranslatable("firmament.tooltip.skyblockid", id.neuItem).grey()) } val (item, text) = lastCopiedStack ?: return if (!ItemStack.areEqual(item, it.stack)) { diff --git a/src/main/kotlin/features/debug/itemeditor/ItemExporter.kt b/src/main/kotlin/features/debug/itemeditor/ItemExporter.kt index c521b5a..2a56204 100644 --- a/src/main/kotlin/features/debug/itemeditor/ItemExporter.kt +++ b/src/main/kotlin/features/debug/itemeditor/ItemExporter.kt @@ -215,6 +215,8 @@ object ItemExporter { return } val stack = event.slot.stack ?: return + val id = event.slot.stack.skyBlockId?.neuItem + if (PowerUserTools.TConfig.dontHighlightSemicolonItems && id != null && id.contains(";")) return val isExported = nonOverlayCache.getOrPut(stack.skyBlockId ?: return) { RepoDownloadManager.repoSavedLocation.resolve("itemsOverlay") .resolve(ExportedTestConstantMeta.current.dataVersion.toString()) diff --git a/src/main/kotlin/features/debug/itemeditor/LegacyItemExporter.kt b/src/main/kotlin/features/debug/itemeditor/LegacyItemExporter.kt index ad03b16..ecf3d2c 100644 --- a/src/main/kotlin/features/debug/itemeditor/LegacyItemExporter.kt +++ b/src/main/kotlin/features/debug/itemeditor/LegacyItemExporter.kt @@ -5,6 +5,7 @@ import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.put import kotlin.concurrent.thread +import kotlin.jvm.optionals.getOrNull import net.minecraft.component.DataComponentTypes import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtByte @@ -14,6 +15,7 @@ import net.minecraft.nbt.NbtInt import net.minecraft.nbt.NbtList import net.minecraft.nbt.NbtOps import net.minecraft.nbt.NbtString +import net.minecraft.registry.tag.ItemTags import net.minecraft.text.Text import net.minecraft.util.Unit import moe.nea.firmament.Firmament @@ -39,6 +41,7 @@ import moe.nea.firmament.util.unformattedString class LegacyItemExporter private constructor(var itemStack: ItemStack) { init { require(!itemStack.isEmpty) + itemStack.count = 1 } var lore = itemStack.loreAccordingToNbt @@ -171,7 +174,11 @@ class LegacyItemExporter private constructor(var itemStack: ItemStack) { } private fun copyColour() { - val leatherTint = itemStack.get(DataComponentTypes.DYED_COLOR) ?: return + if (!itemStack.isIn(ItemTags.DYEABLE)) { + itemStack.remove(DataComponentTypes.DYED_COLOR) + return + } + val leatherTint = itemStack.componentChanges.get(DataComponentTypes.DYED_COLOR)?.getOrNull() ?: return legacyNbt.getOrPutCompound("display").put("color", NbtInt.of(leatherTint.rgb)) } diff --git a/src/main/kotlin/features/inventory/SlotLocking.kt b/src/main/kotlin/features/inventory/SlotLocking.kt index d3348a2..0a3f01b 100644 --- a/src/main/kotlin/features/inventory/SlotLocking.kt +++ b/src/main/kotlin/features/inventory/SlotLocking.kt @@ -4,7 +4,6 @@ package moe.nea.firmament.features.inventory import java.util.UUID import org.lwjgl.glfw.GLFW -import util.render.CustomRenderLayers import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers @@ -19,9 +18,8 @@ import kotlinx.serialization.json.int import kotlinx.serialization.serializer import net.minecraft.client.gui.screen.ingame.HandledScreen import net.minecraft.client.render.RenderLayer -import net.minecraft.client.render.RenderLayers -import net.minecraft.client.render.TexturedRenderLayers import net.minecraft.entity.player.PlayerInventory +import net.minecraft.item.ItemStack import net.minecraft.screen.GenericContainerScreenHandler import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.SlotActionType @@ -44,14 +42,20 @@ import moe.nea.firmament.util.MC import moe.nea.firmament.util.SBData import moe.nea.firmament.util.SkyBlockIsland import moe.nea.firmament.util.data.ProfileSpecificDataHolder +import moe.nea.firmament.util.extraAttributes import moe.nea.firmament.util.json.DashlessUUIDSerializer +import moe.nea.firmament.util.lime import moe.nea.firmament.util.mc.ScreenUtil.getSlotByIndex import moe.nea.firmament.util.mc.SlotUtils.swapWithHotBar import moe.nea.firmament.util.mc.displayNameAccordingToNbt import moe.nea.firmament.util.mc.loreAccordingToNbt +import moe.nea.firmament.util.red import moe.nea.firmament.util.render.drawLine +import moe.nea.firmament.util.skyBlockId import moe.nea.firmament.util.skyblock.DungeonUtil +import moe.nea.firmament.util.skyblock.SkyBlockItems import moe.nea.firmament.util.skyblockUUID +import moe.nea.firmament.util.tr import moe.nea.firmament.util.unformattedString object SlotLocking : FirmamentFeature { @@ -132,6 +136,7 @@ object SlotLocking : FirmamentFeature { val slotBindRequireShift by toggle("require-quick-move") { true } val slotRenderLines by choice("bind-render") { SlotRenderLinesMode.ONLY_BOXES } val allowMultiBinding by toggle("multi-bind") { true } // TODO: filter based on this option + val protectAllHuntingBoxes by toggle("hunting-box") { false } val allowDroppingInDungeons by toggle("drop-in-dungeons") { true } } @@ -193,10 +198,12 @@ object SlotLocking : FirmamentFeature { var anyBlocked = false for (i in 0 until event.slot.index) { val stack = inv.getStack(i) - if (IsSlotProtectedEvent.shouldBlockInteraction(null, - SlotActionType.THROW, - IsSlotProtectedEvent.MoveOrigin.SALVAGE, - stack) + if (IsSlotProtectedEvent.shouldBlockInteraction( + null, + SlotActionType.THROW, + IsSlotProtectedEvent.MoveOrigin.SALVAGE, + stack + ) ) anyBlocked = true } @@ -219,12 +226,20 @@ object SlotLocking : FirmamentFeature { && doesNotDeleteItem ) return val stack = event.itemStack ?: return + if (TConfig.protectAllHuntingBoxes && (stack.isHuntingBox())) { + event.protect() + return + } val uuid = stack.skyblockUUID ?: return if (uuid in (lockedUUIDs ?: return)) { event.protect() } } + fun ItemStack.isHuntingBox(): Boolean { + return skyBlockId == SkyBlockItems.HUNTING_TOOLKIT || extraAttributes.get("tool_kit") != null + } + @Subscribe fun onProtectSlot(it: IsSlotProtectedEvent) { if (it.slot != null && it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())) { @@ -271,6 +286,21 @@ object SlotLocking : FirmamentFeature { val slot = inventory.focusedSlot_Firmament ?: return val stack = slot.stack ?: return + if (stack.isHuntingBox()) { + MC.sendChat( + tr( + "firmament.slot-locking.hunting-box-unbindable-hint", + "The hunting box cannot be UUID bound reliably. It changes its own UUID frequently when switching tools. " + ).red().append( + tr( + "firmament.slot-locking.hunting-box-unbindable-hint.solution", + "Use the Firmament config option for locking all hunting boxes instead." + ).lime() + ) + ) + CommonSoundEffects.playFailure() + return + } val uuid = stack.skyblockUUID ?: return val lockedUUIDs = lockedUUIDs ?: return if (uuid in lockedUUIDs) { @@ -350,12 +380,16 @@ object SlotLocking : FirmamentFeature { hotX + sx, hotY + sy, color(anyHovered) ) - event.context.drawBorder(hotbarSlot.x + sx, - hotbarSlot.y + sy, - 16, 16, color(hotbarSlot in highlitSlots).color) - event.context.drawBorder(inventorySlot.x + sx, - inventorySlot.y + sy, - 16, 16, color(inventorySlot in highlitSlots).color) + event.context.drawBorder( + hotbarSlot.x + sx, + hotbarSlot.y + sy, + 16, 16, color(hotbarSlot in highlitSlots).color + ) + event.context.drawBorder( + inventorySlot.x + sx, + inventorySlot.y + sy, + 16, 16, color(inventorySlot in highlitSlots).color + ) } } @@ -383,9 +417,11 @@ object SlotLocking : FirmamentFeature { hovX + sx, hovY + sy, me.shedaniel.math.Color.ofOpaque(0x00FF00) ) - event.context.drawBorder(hoveredSlot.x + sx, - hoveredSlot.y + sy, - 16, 16, 0xFF00FF00u.toInt()) + event.context.drawBorder( + hoveredSlot.x + sx, + hoveredSlot.y + sy, + 16, 16, 0xFF00FF00u.toInt() + ) } } diff --git a/src/main/kotlin/features/inventory/TimerInLore.kt b/src/main/kotlin/features/inventory/TimerInLore.kt index cc1df9a..e939404 100644 --- a/src/main/kotlin/features/inventory/TimerInLore.kt +++ b/src/main/kotlin/features/inventory/TimerInLore.kt @@ -47,6 +47,7 @@ object TimerInLore { appendValue(ChronoField.SECOND_OF_MINUTE, 2) }), AMERICAN("EEEE, MMM d h:mm a yyyy"), + RFCPrecise(DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss.SSS Z")), ; constructor(block: DateTimeFormatterBuilder.() -> Unit) diff --git a/src/main/kotlin/features/items/EtherwarpOverlay.kt b/src/main/kotlin/features/items/EtherwarpOverlay.kt index b1f695a..f6ab1a2 100644 --- a/src/main/kotlin/features/items/EtherwarpOverlay.kt +++ b/src/main/kotlin/features/items/EtherwarpOverlay.kt @@ -19,6 +19,7 @@ object EtherwarpOverlay : FirmamentFeature { object TConfig : ManagedConfig(identifier, Category.ITEMS) { var etherwarpOverlay by toggle("etherwarp-overlay") { false } + var onlyShowWhileSneaking by toggle("only-show-while-sneaking") { true } var cube by toggle("cube") { true } val cubeColour by colour("cube-colour") { ChromaColour.fromStaticRGB(172, 0, 255, 60) } var wireframe by toggle("wireframe") { false } @@ -32,7 +33,7 @@ object EtherwarpOverlay : FirmamentFeature { fun renderEtherwarpOverlay(event: WorldRenderLastEvent) { if (!TConfig.etherwarpOverlay) return val player = MC.player ?: return - if (!player.isSneaking) return + if (TConfig.onlyShowWhileSneaking && !player.isSneaking) return val world = player.world val camera = MC.camera ?: return val heldItem = MC.stackInHand diff --git a/src/main/kotlin/features/macros/RadialMenu.kt b/src/main/kotlin/features/macros/RadialMenu.kt index 2e09c44..9e5222f 100644 --- a/src/main/kotlin/features/macros/RadialMenu.kt +++ b/src/main/kotlin/features/macros/RadialMenu.kt @@ -36,7 +36,11 @@ object RadialMenuViewer { var activeMenu: RadialMenu? = null set(value) { - field = value + if (value?.options.isNullOrEmpty()) { + field = null + } else { + field = value + } delta = Vector2f(0F, 0F) } var delta = Vector2f(0F, 0F) diff --git a/src/main/kotlin/repo/RepoManager.kt b/src/main/kotlin/repo/RepoManager.kt index df89092..c3d1c52 100644 --- a/src/main/kotlin/repo/RepoManager.kt +++ b/src/main/kotlin/repo/RepoManager.kt @@ -7,7 +7,9 @@ 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.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import net.minecraft.client.MinecraftClient import net.minecraft.network.packet.s2c.play.SynchronizeRecipesS2CPacket import net.minecraft.recipe.display.CuttingRecipeDisplay @@ -35,11 +37,13 @@ object RepoManager { branch = "master" save() } - + val enableREI by toggle("enable-rei") { true } val disableItemGroups by toggle("disable-item-groups") { true } val reload by button("reload") { save() - RepoManager.reload() + Firmament.coroutineScope.launch { + RepoManager.reload() + } } val redownload by button("redownload") { save() @@ -56,6 +60,9 @@ object RepoManager { RENDER_AND_TEXT("text"), ; + fun rendersPerfectText() = this == RENDER_AND_TEXT + fun rendersPerfectVisuals() = this == RENDER || this == RENDER_AND_TEXT + override fun asString(): String? = label } @@ -131,16 +138,17 @@ object RepoManager { fun reloadForTest(from: Path) { neuRepo = makeNEURepository(from) - reload() + reloadSync() } - fun reload() { - if (!TestUtil.isInTest && !MC.instance.isOnThread) { - MC.instance.send { - reload() - } - return + + suspend fun reload() { + withContext(Dispatchers.IO) { + reloadSync() } + } + + fun reloadSync() { try { logger.info("Repo reload started.") neuRepo.reload() @@ -168,7 +176,9 @@ object RepoManager { if (Config.autoUpdate) { launchAsyncUpdate() } else { - reload() + Firmament.coroutineScope.launch { + reload() + } } } @@ -196,4 +206,6 @@ object RepoManager { fun getRepoRef(): String { return "${Config.username}/${Config.reponame}#${Config.branch}" } + + fun shouldLoadREI(): Boolean = Config.enableREI } diff --git a/src/main/kotlin/util/render/LerpUtils.kt b/src/main/kotlin/util/render/LerpUtils.kt index 63a13ec..e7f226c 100644 --- a/src/main/kotlin/util/render/LerpUtils.kt +++ b/src/main/kotlin/util/render/LerpUtils.kt @@ -1,11 +1,15 @@ package moe.nea.firmament.util.render import me.shedaniel.math.Color +import kotlin.math.absoluteValue val π = Math.PI val τ = Math.PI * 2 -fun lerpAngle(a: Float, b: Float, progress: Float): Float { +fun lerpAngle(a: Float, b: Float, progress: Float): Float { // TODO: there is at least 10 mods to many in here lol + if (((b - a).absoluteValue - π).absoluteValue < 0.0001) { + return lerp(a, b, progress) + } val shortestAngle = ((((b.mod(τ) - a.mod(τ)).mod(τ)) + τ + π).mod(τ)) - π return ((a + (shortestAngle) * progress).mod(τ)).toFloat() } diff --git a/src/main/kotlin/util/skyblock/SkyBlockItems.kt b/src/main/kotlin/util/skyblock/SkyBlockItems.kt index 4f208dd..d552fd7 100644 --- a/src/main/kotlin/util/skyblock/SkyBlockItems.kt +++ b/src/main/kotlin/util/skyblock/SkyBlockItems.kt @@ -19,4 +19,5 @@ object SkyBlockItems { val BONE_BOOMERANG = SkyblockId("BONE_BOOMERANG") val STARRED_BONE_BOOMERANG = SkyblockId("STARRED_BONE_BOOMERANG") val TRIBAL_SPEAR = SkyblockId("TRIBAL_SPEAR") + val HUNTING_TOOLKIT = SkyblockId("HUNTING_TOOLKIT") } diff --git a/src/main/resources/firmament.accesswidener b/src/main/resources/firmament.accesswidener index 71f63ac..0b7b830 100644 --- a/src/main/resources/firmament.accesswidener +++ b/src/main/resources/firmament.accesswidener @@ -16,6 +16,11 @@ accessible method net/minecraft/entity/decoration/ArmorStandEntity setSmall (Z)V accessible method net/minecraft/resource/NamespaceResourceManager loadMetadata (Lnet/minecraft/resource/InputSupplier;)Lnet/minecraft/resource/metadata/ResourceMetadata; accessible method net/minecraft/client/gui/DrawContext drawTexturedQuad (Ljava/util/function/Function;Lnet/minecraft/util/Identifier;IIIIFFFFI)V +accessible class net/minecraft/client/render/model/BlockStatesLoader$LoadedBlockStateDefinition +accessible field net/minecraft/client/render/model/BlockStatesLoader FINDER Lnet/minecraft/resource/ResourceFinder; +accessible method net/minecraft/client/render/model/BlockStatesLoader$LoadedBlockStateDefinition <init> (Ljava/lang/String;Lnet/minecraft/client/render/model/json/BlockModelDefinition;)V +accessible method net/minecraft/client/render/model/BlockStatesLoader combine (Lnet/minecraft/util/Identifier;Lnet/minecraft/state/StateManager;Ljava/util/List;)Lnet/minecraft/client/render/model/BlockStatesLoader$LoadedModels; + mutable field net/minecraft/screen/slot/Slot x I mutable field net/minecraft/screen/slot/Slot y I diff --git a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt index 462b1e1..2d7a978 100644 --- a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt +++ b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt @@ -2,6 +2,9 @@ package moe.nea.firmament.features.texturepack +import com.google.gson.JsonParseException +import com.google.gson.JsonParser +import com.mojang.serialization.JsonOps import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor import java.util.function.Function @@ -21,15 +24,21 @@ import kotlinx.serialization.serializer import kotlin.jvm.optionals.getOrNull import net.minecraft.block.Block import net.minecraft.block.BlockState +import net.minecraft.block.Blocks import net.minecraft.client.render.model.Baker import net.minecraft.client.render.model.BlockStateModel +import net.minecraft.client.render.model.BlockStatesLoader import net.minecraft.client.render.model.ReferencedModelsCollector import net.minecraft.client.render.model.SimpleBlockStateModel +import net.minecraft.client.render.model.json.BlockModelDefinition import net.minecraft.client.render.model.json.ModelVariant +import net.minecraft.registry.Registries import net.minecraft.registry.RegistryKey import net.minecraft.registry.RegistryKeys +import net.minecraft.resource.Resource import net.minecraft.resource.ResourceManager import net.minecraft.resource.SinglePreparationResourceReloader +import net.minecraft.state.StateManager import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos import net.minecraft.util.profiler.Profiler @@ -41,6 +50,7 @@ import moe.nea.firmament.events.FinalizeResourceManagerEvent import moe.nea.firmament.events.SkyblockServerUpdateEvent import moe.nea.firmament.features.texturepack.CustomBlockTextures.createBakedModels import moe.nea.firmament.features.texturepack.CustomGlobalTextures.logger +import moe.nea.firmament.util.ErrorUtil import moe.nea.firmament.util.IdentifierSerializer import moe.nea.firmament.util.MC import moe.nea.firmament.util.SBData @@ -62,12 +72,28 @@ object CustomBlockTextures { val block: Identifier, val sound: Identifier?, ) { + fun replace(block: BlockState): BlockStateModel? { + blockStateMap?.let { return it[block] } + return blockModel + } + + @Transient + lateinit var overridingBlock: Block @Transient val blockModelIdentifier get() = block.withPrefixedPath("block/") /** - * Guaranteed to be set after [BakedReplacements.modelBakingFuture] is complete. + * Guaranteed to be set after [BakedReplacements.modelBakingFuture] is complete, if [unbakedBlockStateMap] is set. + */ + @Transient + var blockStateMap: Map<BlockState, BlockStateModel>? = null + + @Transient + var unbakedBlockStateMap: Map<BlockState, BlockStateModel.UnbakedGrouped>? = null + + /** + * Guaranteed to be set after [BakedReplacements.modelBakingFuture] is complete. Prefer [blockStateMap] if present. */ @Transient lateinit var blockModel: BlockStateModel @@ -139,7 +165,15 @@ object CustomBlockTextures { data class LocationReplacements( val lookup: Map<Block, List<BlockReplacement>> - ) + ) { + init { + lookup.forEach { (block, replacements) -> + for (replacement in replacements) { + replacement.replacement.overridingBlock = block + } + } + } + } data class BlockReplacement( val checks: List<Area>?, @@ -213,7 +247,7 @@ object CustomBlockTextures { @JvmStatic fun getReplacementModel(block: BlockState, blockPos: BlockPos?): BlockStateModel? { - return getReplacement(block, blockPos)?.blockModel + return getReplacement(block, blockPos)?.replace(block) } @JvmStatic @@ -236,8 +270,12 @@ object CustomBlockTextures { } @Volatile - var preparationFuture: CompletableFuture<BakedReplacements> = CompletableFuture.completedFuture(BakedReplacements( - mapOf())) + @get:JvmStatic + var preparationFuture: CompletableFuture<BakedReplacements> = CompletableFuture.completedFuture( + BakedReplacements( + mapOf() + ) + ) val insideFallbackCall = ThreadLocal.withInitial { 0 } @@ -257,7 +295,8 @@ object CustomBlockTextures { fun onEarlyReload(event: EarlyResourceReloadEvent) { preparationFuture = CompletableFuture .supplyAsync( - { prepare(event.resourceManager) }, event.preparationExecutor) + { prepare(event.resourceManager) }, event.preparationExecutor + ) } private fun prepare(manager: ResourceManager): BakedReplacements { @@ -295,7 +334,7 @@ object CustomBlockTextures { @Subscribe fun onStart(event: FinalizeResourceManagerEvent) { event.resourceManager.registerReloader(object : - SinglePreparationResourceReloader<BakedReplacements>() { + SinglePreparationResourceReloader<BakedReplacements>() { override fun prepare(manager: ResourceManager, profiler: Profiler): BakedReplacements { return preparationFuture.join().also { it.modelBakingFuture.join() @@ -328,12 +367,28 @@ object CustomBlockTextures { @JvmStatic fun collectExtraModels(modelsCollector: ReferencedModelsCollector) { preparationFuture.join().collectAllReplacements() - .forEach { modelsCollector.resolve(simpleBlockModel(it.blockModelIdentifier)) } + .forEach { + modelsCollector.resolve(simpleBlockModel(it.blockModelIdentifier)) + it.unbakedBlockStateMap?.values?.forEach { + modelsCollector.resolve(it) + } + } } @JvmStatic fun createBakedModels(baker: Baker, executor: Executor): CompletableFuture<Void?> { return preparationFuture.thenComposeAsync(Function { replacements -> + val allBlockStates = CompletableFuture.allOf( + *replacements.collectAllReplacements().filter { it.unbakedBlockStateMap != null }.map { + CompletableFuture.supplyAsync({ + it.blockStateMap = it.unbakedBlockStateMap + ?.map { + it.key to it.value.bake(it.key, baker) + } + ?.toMap() + }, executor) + }.toList().toTypedArray() + ) val byModel = replacements.collectAllReplacements().groupBy { it.blockModelIdentifier } val modelBakingTask = AsyncHelper.mapValues(byModel, { blockId, replacements -> val unbakedModel = SimpleBlockStateModel.Unbaked( @@ -344,7 +399,55 @@ object CustomBlockTextures { it.blockModel = baked } }, executor) - modelBakingTask.thenAcceptAsync { replacements.modelBakingFuture.complete(Unit) } + modelBakingTask.thenComposeAsync { + allBlockStates + }.thenAcceptAsync { + replacements.modelBakingFuture.complete(Unit) + } }, executor) } + + @JvmStatic + fun collectExtraBlockStateMaps( + extra: BakedReplacements, + original: Map<Identifier, List<Resource>>, + stateManagers: Function<Identifier, StateManager<Block, BlockState>?> + ) { + extra.collectAllReplacements().forEach { + val blockId = Registries.BLOCK.getKey(it.overridingBlock).getOrNull()?.value ?: return@forEach + val allModels = mutableListOf<BlockStatesLoader.LoadedBlockStateDefinition>() + val stateManager = stateManagers.apply(blockId) ?: return@forEach + for (resource in original[BlockStatesLoader.FINDER.toResourcePath(it.block)] ?: return@forEach) { + try { + resource.reader.use { reader -> + val jsonElement = JsonParser.parseReader(reader) + val blockModelDefinition = + BlockModelDefinition.CODEC.parse(JsonOps.INSTANCE, jsonElement) + .getOrThrow { msg: String? -> JsonParseException(msg) } + allModels.add( + BlockStatesLoader.LoadedBlockStateDefinition( + resource.getPackId(), + blockModelDefinition + ) + ) + } + } catch (exception: Exception) { + ErrorUtil.softError( + "Failed to load custom blockstate definition ${it.block} from pack ${resource.packId}", + exception + ) + } + } + + try { + it.unbakedBlockStateMap = BlockStatesLoader.combine( + blockId, + stateManager, + allModels + ).models + } catch (exception: Exception) { + ErrorUtil.softError("Failed to combine custom blockstate definitions for ${it.block}", exception) + } + } + } } diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/LoadExtraBlockStates.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/LoadExtraBlockStates.java new file mode 100644 index 0000000..c33fd04 --- /dev/null +++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/LoadExtraBlockStates.java @@ -0,0 +1,34 @@ +package moe.nea.firmament.mixins.custommodels; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.sugar.Local; +import moe.nea.firmament.features.texturepack.CustomBlockTextures; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.model.BlockStatesLoader; +import net.minecraft.resource.Resource; +import net.minecraft.state.StateManager; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.Function; + +@Mixin(BlockStatesLoader.class) +public class LoadExtraBlockStates { + @ModifyExpressionValue(method = "load", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;supplyAsync(Ljava/util/function/Supplier;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;")) + private static CompletableFuture<Map<Identifier, List<Resource>>> loadExtraModels( + CompletableFuture<Map<Identifier, List<Resource>>> x, + @Local(argsOnly = true) Executor executor, + @Local Function<Identifier, StateManager<Block, BlockState>> stateManagers + ) { + return x.thenCombineAsync(CustomBlockTextures.getPreparationFuture(), (original, extra) -> { + CustomBlockTextures.collectExtraBlockStateMaps(extra, original, stateManagers); + return original; + }, executor); + } +} |