diff options
105 files changed, 2986 insertions, 2778 deletions
@@ -18,7 +18,7 @@ SPDX-License-Identifier = "CC-BY-4.0" SPDX-FileCopyrightText = ["Linnea Gräf <nea@nea.moe>", "Firmament Contributors"] [[annotations]] -path = ["src/main/resources/assets/minecraft/shaders/core/**/*"] +path = ["src/main/resources/assets/firmament/shaders/**/*"] SPDX-License-Identifier = "GPL-3.0-or-later" SPDX-FileCopyrightText = ["Linnea Gräf <nea@nea.moe>", "Firmament Contributors"] diff --git a/build.gradle.kts b/build.gradle.kts index 64d80fe..b157f58 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -21,7 +21,9 @@ plugins { alias(libs.plugins.kotlin.plugin.serialization) alias(libs.plugins.kotlin.plugin.powerassert) alias(libs.plugins.kotlin.plugin.ksp) - alias(libs.plugins.loom) + // alias(libs.plugins.loom) + // TODO: use arch loom once they update to 1.8 + id("fabric-loom") version "1.8.9" id("com.github.johnrengelman.shadow") version "8.1.1" id("moe.nea.licenseextractificator") id("moe.nea.mc-auto-translations") version "0.0.1" @@ -104,16 +106,12 @@ fun String.capitalizeN() = replaceFirstChar { it.uppercaseChar() } val unpackAllJars by tasks.registering fun innerJarsOf(name: String, dependency: Dependency): Provider<FileTree> { val task = tasks.create("unpackInnerJarsFor${name.capitalizeN()}", InnerJarsUnpacker::class) { - doFirst { - println("Unpacking JARs for $name") - } this.inputJars.setFrom(files(configurations.detachedConfiguration(dependency))) this.outputDir.set(layout.buildDirectory.dir("unpackedJars/$name").also { it.get().asFile.mkdirs() }) } unpackAllJars { dependsOn(task) } - println("Constructed innerJars task: ${project.files(task).asFileTree.toList().map {it to it.exists()}}") return project.provider { project.files(task).asFileTree } @@ -125,15 +123,32 @@ val collectTranslations by tasks.registering(CollectTranslations::class) { } val compatSourceSets: MutableSet<SourceSet> = mutableSetOf() -fun createIsolatedSourceSet(name: String, path: String = "compat/$name"): SourceSet { +fun createIsolatedSourceSet(name: String, path: String = "compat/$name", isEnabled: Boolean = true): SourceSet { val ss = sourceSets.create(name) { - this.java.setSrcDirs(listOf(layout.projectDirectory.dir("src/$path/java"))) - this.kotlin.setSrcDirs(listOf(layout.projectDirectory.dir("src/$path/java"))) + if (isEnabled) { + this.java.setSrcDirs(listOf(layout.projectDirectory.dir("src/$path/java"))) + this.kotlin.setSrcDirs(listOf(layout.projectDirectory.dir("src/$path/java"))) + } else { + this.java.setSrcDirs(listOf<File>()) + this.kotlin.setSrcDirs(listOf<File>()) + } } - compatSourceSets.add(ss) - loom.createRemapConfigurations(ss) val mainSS = sourceSets.main.get() val upperName = ss.name.capitalizeN() + afterEvaluate { + tasks.named("ksp${upperName}Kotlin", KspTaskJvm::class) { + this.options.add(SubpluginOption("apoption", "firmament.sourceset=${ss.name}")) + } + tasks.named("compile${upperName}Kotlin", KotlinCompile::class) { + this.enabled = isEnabled + } + tasks.named("compile${upperName}Java", JavaCompile::class) { + this.enabled = isEnabled + } + } + compatSourceSets.add(ss) + loom.createRemapConfigurations(ss) + if (!isEnabled) return ss configurations { (ss.implementationConfigurationName) { extendsFrom(getByName(mainSS.compileClasspathConfigurationName)) @@ -148,11 +163,6 @@ fun createIsolatedSourceSet(name: String, path: String = "compat/$name"): Source extendsFrom(ksp.get()) } } - afterEvaluate { - tasks.named("ksp${upperName}Kotlin", KspTaskJvm::class) { - this.options.add(SubpluginOption("apoption", "firmament.sourceset=${ss.name}")) - } - } dependencies { runtimeOnly(ss.output) (ss.implementationConfigurationName)(sourceSets.main.get().output) @@ -195,14 +205,15 @@ val nonModImplentation by configurations.creating { } -val configuredSourceSet = createIsolatedSourceSet("configured") +val configuredSourceSet = createIsolatedSourceSet("configured", + isEnabled = false) // Wait for update (also low prio, because configured sucks) val sodiumSourceSet = createIsolatedSourceSet("sodium") -val citResewnSourceSet = createIsolatedSourceSet("citresewn") +val citResewnSourceSet = createIsolatedSourceSet("citresewn", isEnabled = false) // TODO: Wait for update val yaclSourceSet = createIsolatedSourceSet("yacl") -val explosiveEnhancementSourceSet = createIsolatedSourceSet("explosiveEnhancement") -val wildfireGenderSourceSet = createIsolatedSourceSet("wildfireGender") +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") +val reiSourceSet = createIsolatedSourceSet("rei") // TODO: read through https://hackmd.io/@shedaniel/rei17_primer dependencies { // Minecraft dependencies @@ -299,7 +310,7 @@ loom { compatSourceSets.joinToString(File.pathSeparator) { File(it.output.classesDirs.asPath).absolutePath }) - property("mixin.debug", "true") + property("mixin.debug.export", "true") parseEnvFile(file(".env")).forEach { (t, u) -> environmentVariable(t, u) @@ -360,7 +371,7 @@ tasks.shadowJar { } tasks.remapJar { - injectAccessWidener.set(true) +// injectAccessWidener.set(true) inputFile.set(tasks.shadowJar.flatMap { it.archiveFile }) dependsOn(tasks.shadowJar) archiveClassifier.set("") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c11d939..117704f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,36 +3,36 @@ # SPDX-License-Identifier: CC0-1.0 [versions] -minecraft = "1.21" +minecraft = "1.21.3" # Update from https://kotlinlang.org/ -kotlin = "2.0.20" +kotlin = "2.0.21" # Update from https://github.com/google/ksp/releases -kotlin_ksp = "2.0.20-1.0.24" +kotlin_ksp = "2.0.21-1.0.26" # Update from https://linkie.shedaniel.me/dependencies?loader=fabric -fabric_loader = "0.16.3" -fabric_api = "0.100.4+1.21" -fabric_kotlin = "1.11.0+kotlin.2.0.0" -yarn = "1.21+build.7" -rei = "16.0.729" -modmenu = "11.0.1" -architectury = "13.0.3" +fabric_loader = "0.16.9" +fabric_api = "0.107.0+1.21.3" +fabric_kotlin = "1.12.3+kotlin.2.0.21" +yarn = "1.21.3+build.2" +rei = "17.0.789" +modmenu = "12.0.0-beta.1" +architectury = "14.0.4" # Update from https://maven.architectury.dev/dev/architectury/loom/dev.architectury.loom.gradle.plugin/ -loom = "1.7.412" +loom = "1.7.414" # Update from https://modrinth.com/mod/qolify/versions?l=fabric qolify = "1.6.0-1.21.1" # Update from https://modrinth.com/mod/sodium/versions?l=fabric -sodium = "mc1.21-0.5.11" +sodium = "mc1.21.3-0.6.0-beta.4-fabric" # Update from https://modrinth.com/mod/freecam/versions?l=fabric freecammod = "vomskVK3" # Update from https://modrinth.com/mod/no-chat-reports/versions?l=fabric -ncr = "Fabric-1.21-v2.8.0" +ncr = "Fabric-1.21.3-v2.10.0" # Update from https://modrinth.com/mod/female-gender/versions?l=fabric femalegender = "kJmjQvAS" @@ -49,7 +49,7 @@ citresewn = "1.2.0+1.21" devauth = "1.2.0" # Update from https://ktor.io/ -ktor = "2.3.12" +ktor = "3.0.1" # Update from https://repo.nea.moe/#/releases/moe/nea/neurepoparser neurepoparser = "1.6.0" @@ -58,13 +58,13 @@ neurepoparser = "1.6.0" hotswap_agent = "1.4.2-SNAPSHOT" # Update from https://github.com/LlamaLad7/MixinExtras/tags -mixinextras = "0.3.5" +mixinextras = "0.4.1" jarvis = "1.1.3" -nealisp = "1.0.0" +nealisp = "1.1.0" # Update from https://github.com/NotEnoughUpdates/MoulConfig/tags -moulconfig = "3.0.0-beta.15" +moulconfig = "3.0.0" # Update from https://www.curseforge.com/minecraft/mc-mods/configured/files/all?page=1&pageSize=20 configured = "5441234" @@ -77,7 +77,7 @@ hypixelmodapi_fabric = "1.0.1+build.1+mc1.21" manninghamMills = "2.4.1" # Update from https://docs.isxander.dev/yet-another-config-lib/installing-yacl -yacl = "3.5.0+1.21-fabric" +yacl = "3.6.1+1.21.2-fabric" # Update from https://maven.shedaniel.me/me/shedaniel/cloth/basic-math/0.6.1/ basicMath = "0.6.1" @@ -120,10 +120,8 @@ basicMath = { module = "me.shedaniel.cloth:basic-math", version.ref = "basicMath [bundles] runtime_required = [ - "architectury_fabric", "rei_fabric", - "notenoughanimations", - "femalegender", +# "notenoughanimations", "hypixelmodapi_fabric", ] runtime_optional = [ @@ -132,7 +130,7 @@ runtime_optional = [ # "sodium", # "qolify", "ncr", - "citresewn", +# "citresewn", ] [plugins] diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/EntityWidget.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/EntityWidget.kt index 9b7b190..b0efc98 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/EntityWidget.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/EntityWidget.kt @@ -4,32 +4,32 @@ import me.shedaniel.math.Dimension import me.shedaniel.math.Point import me.shedaniel.math.Rectangle import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds -import moe.nea.firmament.gui.entity.EntityRenderer import net.minecraft.client.gui.DrawContext import net.minecraft.client.gui.Element import net.minecraft.entity.LivingEntity +import moe.nea.firmament.gui.entity.EntityRenderer class EntityWidget(val entity: LivingEntity, val point: Point) : WidgetWithBounds() { - override fun children(): List<Element> { - return emptyList() - } + override fun children(): List<Element> { + return emptyList() + } - var hasErrored = false + var hasErrored = false - override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { - try { - if (!hasErrored) - EntityRenderer.renderEntity(entity, context, point.x, point.y, mouseX.toFloat(), mouseY.toFloat()) - } catch (ex: Exception) { - EntityRenderer.logger.error("Failed to render constructed entity: $entity", ex) - hasErrored = true - } - if (hasErrored) { - context.fill(point.x, point.y, point.x + 50, point.y + 80, 0xFFAA2222.toInt()) - } - } + override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { + try { + if (!hasErrored) + EntityRenderer.renderEntity(entity, context, point.x, point.y, mouseX.toFloat(), mouseY.toFloat()) + } catch (ex: Exception) { + EntityRenderer.logger.error("Failed to render constructed entity: $entity", ex) + hasErrored = true + } + if (hasErrored) { + context.fill(point.x, point.y, point.x + 50, point.y + 80, 0xFFAA2222.toInt()) + } + } - override fun getBounds(): Rectangle { - return Rectangle(point, Dimension(50, 80)) - } + override fun getBounds(): Rectangle { + return Rectangle(point, Dimension(50, 80)) + } } 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 new file mode 100644 index 0000000..98ac276 --- /dev/null +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiCommonPlugin.kt @@ -0,0 +1,10 @@ +package moe.nea.firmament.compat.rei + +import me.shedaniel.rei.api.common.entry.type.EntryTypeRegistry +import me.shedaniel.rei.api.common.plugins.REICommonPlugin + +class FirmamentReiCommonPlugin : REICommonPlugin { + override fun registerEntryTypes(registry: EntryTypeRegistry) { + 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 d95d2d1..f576eda 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 @@ -11,7 +11,6 @@ import me.shedaniel.rei.api.client.registry.screen.ScreenRegistry import me.shedaniel.rei.api.client.registry.transfer.TransferHandler import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRegistry import me.shedaniel.rei.api.common.entry.EntryStack -import me.shedaniel.rei.api.common.entry.type.EntryTypeRegistry import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes import net.minecraft.client.gui.screen.Screen import net.minecraft.client.gui.screen.ingame.GenericContainerScreen @@ -20,18 +19,17 @@ import net.minecraft.item.ItemStack import net.minecraft.text.Text import net.minecraft.util.ActionResult import net.minecraft.util.Identifier -import moe.nea.firmament.events.HandledScreenPushREIEvent -import moe.nea.firmament.features.inventory.CraftingOverlay -import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen import moe.nea.firmament.compat.rei.recipes.SBCraftingRecipe import moe.nea.firmament.compat.rei.recipes.SBEssenceUpgradeRecipe import moe.nea.firmament.compat.rei.recipes.SBForgeRecipe import moe.nea.firmament.compat.rei.recipes.SBKatRecipe import moe.nea.firmament.compat.rei.recipes.SBMobDropRecipe +import moe.nea.firmament.events.HandledScreenPushREIEvent +import moe.nea.firmament.features.inventory.CraftingOverlay +import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.repo.SBItemStack import moe.nea.firmament.util.MC -import moe.nea.firmament.util.ScreenUtil import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.guessRecipeId import moe.nea.firmament.util.skyblockId @@ -74,9 +72,6 @@ class FirmamentReiPlugin : REIClientPlugin { }) } - override fun registerEntryTypes(registry: EntryTypeRegistry) { - registry.register(SKYBLOCK_ITEM_TYPE_ID, SBItemEntryDefinition) - } override fun registerCategories(registry: CategoryRegistry) { registry.add(SBCraftingRecipe.Category) diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/HoveredItemStackProvider.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/HoveredItemStackProvider.kt index 3d21b66..b917c3e 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/HoveredItemStackProvider.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/HoveredItemStackProvider.kt @@ -21,6 +21,7 @@ class ScreenRegistryHoveredItemStackProvider : HoveredItemStackProvider { return entryStack.value as? ItemStack ?: entryStack.cheatsAs().value } } + @AutoService(HoveredItemStackProvider::class) @CompatLoader.RequireMod("roughlyenoughitems") class OverlayHoveredItemStackProvider : HoveredItemStackProvider { 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 a02742b..de173ff 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 @@ -25,163 +25,128 @@ import net.minecraft.client.render.LightmapTextureManager import net.minecraft.client.render.OverlayTexture import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.model.BakedModel -import net.minecraft.client.render.model.json.ModelTransformationMode import net.minecraft.client.texture.SpriteAtlasTexture -import net.minecraft.item.Item -import net.minecraft.item.ItemStack -import net.minecraft.item.tooltip.TooltipType +import net.minecraft.item.ModelTransformationMode import moe.nea.firmament.compat.rei.FirmamentReiPlugin.Companion.asItemEntry import moe.nea.firmament.repo.SBItemStack +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.mc.displayNameAccordingToNbt +import moe.nea.firmament.util.mc.loreAccordingToNbt object NEUItemEntryRenderer : EntryRenderer<SBItemStack>, BatchedEntryRenderer<SBItemStack, BakedModel> { - override fun render( - entry: EntryStack<SBItemStack>, - context: DrawContext, - bounds: Rectangle, - mouseX: Int, - mouseY: Int, - delta: Float - ) { - entry.asItemEntry().render(context, bounds, mouseX, mouseY, delta) - } - - val minecraft = MinecraftClient.getInstance() - - override fun getTooltip(entry: EntryStack<SBItemStack>, tooltipContext: TooltipContext): Tooltip? { - val stack = entry.value.asImmutableItemStack() - val lore = stack.getTooltip( - Item.TooltipContext.DEFAULT, - null, - TooltipType.BASIC - ) - return Tooltip.create(lore) - } - - override fun getExtraData(entry: EntryStack<SBItemStack>): BakedModel { - return minecraft.itemRenderer.getModel(entry.asItemEntry().value, minecraft.world, minecraft.player, 0) - } - - override fun getBatchIdentifier(entry: EntryStack<SBItemStack>?, bounds: Rectangle?, extraData: BakedModel): Int { - return 1738923 + if (extraData.isSideLit) 1 else 0 - } - - override fun startBatch( - entry: EntryStack<SBItemStack>, - model: BakedModel, - graphics: DrawContext, - delta: Float - ) { - val modelViewStack = RenderSystem.getModelViewStack() - modelViewStack.pushMatrix() - modelViewStack.scale(20.0f, 20.0f, 1.0f) - RenderSystem.applyModelViewMatrix() - setupGL(model) - } - - fun setupGL(model: BakedModel) { - minecraft.textureManager.getTexture(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE) - .setFilter(false, false) - RenderSystem.setShaderTexture(0, SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE) - RenderSystem.enableBlend() - RenderSystem.blendFunc(SrcFactor.SRC_ALPHA, DstFactor.ONE_MINUS_SRC_ALPHA) - RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f) - val sideLit = model.isSideLit - if (!sideLit) { - DiffuseLighting.disableGuiDepthLighting() - } - } - - override fun renderBase( - entry: EntryStack<SBItemStack>, - model: BakedModel, - graphics: DrawContext, - immediate: VertexConsumerProvider.Immediate, - bounds: Rectangle, - mouseX: Int, - mouseY: Int, - delta: Float - ) { - if (entry.isEmpty) return - val value = entry.asItemEntry().value - graphics.matrices.push() - graphics.matrices.translate(bounds.centerX.toFloat() / 20.0f, bounds.centerY.toFloat() / 20.0f, 0.0f) - graphics.matrices.scale( - bounds.getWidth().toFloat() / 20.0f, - -(bounds.getWidth() + bounds.getHeight()).toFloat() / 2.0f / 20.0f, - 1.0f - ) - minecraft - .itemRenderer - .renderItem( - value, - ModelTransformationMode.GUI, - false, - graphics.matrices, - immediate, - LightmapTextureManager.MAX_LIGHT_COORDINATE, - OverlayTexture.DEFAULT_UV, - model - ) - graphics.matrices.pop() - - } - - override fun afterBase( - entry: EntryStack<SBItemStack>, - model: BakedModel, - graphics: DrawContext, - delta: Float - ) { - RenderSystem.getModelViewStack().popMatrix() - RenderSystem.applyModelViewMatrix() - this.endGL(model) - } - - fun endGL(model: BakedModel) { - RenderSystem.enableDepthTest() - val sideLit = model.isSideLit - if (!sideLit) { - DiffuseLighting.enableGuiDepthLighting() - } - } - - override fun renderOverlay( - entry: EntryStack<SBItemStack>, - extraData: BakedModel, - graphics: DrawContext, - immediate: VertexConsumerProvider.Immediate, - bounds: Rectangle, - mouseX: Int, - mouseY: Int, - delta: Float - ) { - val modelViewStack = RenderSystem.getModelViewStack() - modelViewStack.pushMatrix() - modelViewStack.mul(graphics.matrices.peek().positionMatrix) - modelViewStack.translate(bounds.x.toFloat(), bounds.y.toFloat(), 0.0f) - modelViewStack.scale( - bounds.width.toFloat() / 16.0f, - -(bounds.getWidth() + bounds.getHeight()).toFloat() / 2.0f / 16.0f, - 1.0f - ) - RenderSystem.applyModelViewMatrix() - renderOverlay(DrawContext(minecraft, graphics.vertexConsumers), entry.asItemEntry()) - modelViewStack.popMatrix() - RenderSystem.applyModelViewMatrix() - } - - fun renderOverlay(graphics: DrawContext, entry: EntryStack<ItemStack>) { - if (!entry.isEmpty) { - graphics.drawItemInSlot(MinecraftClient.getInstance().textRenderer, entry.value, 0, 0, null) - } - } - - override fun endBatch( - entry: EntryStack<SBItemStack>?, - extraData: BakedModel?, - graphics: DrawContext?, - delta: Float - ) { - } + override fun render( + entry: EntryStack<SBItemStack>, + context: DrawContext, + bounds: Rectangle, + mouseX: Int, + mouseY: Int, + delta: Float + ) { + entry.asItemEntry().render(context, bounds, mouseX, mouseY, delta) + } + + val minecraft = MinecraftClient.getInstance() + + override fun getTooltip(entry: EntryStack<SBItemStack>, tooltipContext: TooltipContext): Tooltip? { + val stack = entry.value.asImmutableItemStack() + + val lore = mutableListOf(stack.displayNameAccordingToNbt) + lore.addAll(stack.loreAccordingToNbt) + + // TODO: tags aren't sent as early now so some tooltip components that use tags will crash the game +// stack.getTooltip( +// Item.TooltipContext.create( +// tooltipContext.vanillaContext().registryLookup +// ?: MC.defaultRegistries +// ), +// MC.player, +// TooltipType.BASIC +// ) + return Tooltip.create(lore) + } + + override fun getExtraData(entry: EntryStack<SBItemStack>): BakedModel { + return MC.itemRenderer.getModel(entry.asItemEntry().value, + MC.world, + MC.player, 0) + + } + + override fun getBatchIdentifier(entry: EntryStack<SBItemStack>, bounds: Rectangle?, extraData: BakedModel): Int { + return 1738923 + if (extraData.isSideLit) 1 else 0 + } + + + override fun startBatch(entryStack: EntryStack<SBItemStack>, e: BakedModel, drawContext: DrawContext, v: Float) { + MC.textureManager.getTexture(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE) + .setFilter(false, false) + RenderSystem.setShaderTexture(0, SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE) + RenderSystem.enableBlend() + RenderSystem.blendFunc(SrcFactor.SRC_ALPHA, DstFactor.ONE_MINUS_SRC_ALPHA) + RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f) + if (!e.isSideLit) { + DiffuseLighting.disableGuiDepthLighting() + } + } + + override fun renderBase( + entryStack: EntryStack<SBItemStack>, + model: BakedModel, + drawContext: DrawContext, + immediate: VertexConsumerProvider.Immediate, + bounds: Rectangle, + i: Int, + i1: Int, + v: Float + ) { + if (entryStack.isEmpty) return + drawContext.matrices.push() + drawContext.matrices.translate(bounds.centerX.toDouble(), bounds.centerY.toDouble(), 0.0) + // TODO: check the scaling here again + drawContext.matrices.scale( + bounds.width.toFloat(), + (bounds.height + bounds.height) / -2F, + (bounds.width + bounds.height) / 2f) + MC.itemRenderer.renderItem( + entryStack.value.asImmutableItemStack(), + ModelTransformationMode.GUI, + false, drawContext.matrices, + immediate, LightmapTextureManager.MAX_LIGHT_COORDINATE, + OverlayTexture.DEFAULT_UV, + model + ) + drawContext.matrices.pop() + } + + override fun afterBase(entryStack: EntryStack<SBItemStack>?, e: BakedModel, drawContext: DrawContext?, v: Float) { + RenderSystem.enableDepthTest() + if (!e.isSideLit) + DiffuseLighting.enableGuiDepthLighting() + } + + override fun renderOverlay( + entryStack: EntryStack<SBItemStack>, + e: BakedModel, + drawContext: DrawContext, + immediate: VertexConsumerProvider.Immediate, + bounds: Rectangle, + i: Int, + i1: Int, + v: Float + ) { + if (entryStack.isEmpty) return + val modelViewStack = RenderSystem.getModelViewStack() + modelViewStack.pushMatrix() + modelViewStack.mul(drawContext.matrices.peek().positionMatrix) + modelViewStack.translate(bounds.x.toFloat(), bounds.y.toFloat(), 0F) + modelViewStack.scale(bounds.width / 16.0f, + (bounds.width + bounds.height) / 2.0f / 16.0f, + 1.0f) // TODO: weird scale again + drawContext.drawStackOverlay(MC.font, entryStack.value.asImmutableItemStack(), 0, 0, null) + modelViewStack.popMatrix() + } + + override fun endBatch(entryStack: EntryStack<SBItemStack>?, e: BakedModel?, drawContext: DrawContext?, v: Float) { + } } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntrySerializer.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntrySerializer.kt index 6a03a48..724d193 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntrySerializer.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntrySerializer.kt @@ -1,30 +1,17 @@ - - package moe.nea.firmament.compat.rei +import com.mojang.serialization.Codec import me.shedaniel.rei.api.common.entry.EntrySerializer -import me.shedaniel.rei.api.common.entry.EntryStack -import net.minecraft.nbt.NbtCompound +import net.minecraft.network.RegistryByteBuf +import net.minecraft.network.codec.PacketCodec import moe.nea.firmament.repo.SBItemStack -import moe.nea.firmament.util.SkyblockId object NEUItemEntrySerializer : EntrySerializer<SBItemStack> { - const val SKYBLOCK_ID_ENTRY = "SKYBLOCK_ID" - const val SKYBLOCK_ITEM_COUNT = "SKYBLOCK_ITEM_COUNT" - - override fun supportSaving(): Boolean = true - override fun supportReading(): Boolean = true - - override fun read(tag: NbtCompound): SBItemStack { - val id = SkyblockId(tag.getString(SKYBLOCK_ID_ENTRY)) - val count = if (tag.contains(SKYBLOCK_ITEM_COUNT)) tag.getInt(SKYBLOCK_ITEM_COUNT) else 1 - return SBItemStack(id, count) - } + override fun codec(): Codec<SBItemStack> { + return SBItemStack.CODEC + } - override fun save(entry: EntryStack<SBItemStack>, value: SBItemStack): NbtCompound { - return NbtCompound().apply { - putString(SKYBLOCK_ID_ENTRY, value.skyblockId.neuItem) - putInt(SKYBLOCK_ITEM_COUNT, value.getStackSize()) - } - } + override fun streamCodec(): PacketCodec<RegistryByteBuf, SBItemStack> { + return SBItemStack.PACKET_CODEC.cast() + } } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/math.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/math.kt index 7db36f2..f4808c7 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/math.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/math.kt @@ -1,10 +1,8 @@ - - package moe.nea.firmament.compat.rei import me.shedaniel.math.Point operator fun Point.plus(other: Point): Point = Point( - this.x + other.x, - this.y + other.y, + this.x + other.x, + this.y + other.y, ) diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt index ed18c6e..fd04abc 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt @@ -1,5 +1,3 @@ - - package moe.nea.firmament.compat.rei.recipes import io.github.moulberry.repo.data.NEUCraftingRecipe @@ -11,6 +9,8 @@ import me.shedaniel.rei.api.client.gui.widgets.Widget import me.shedaniel.rei.api.client.gui.widgets.Widgets import me.shedaniel.rei.api.client.registry.display.DisplayCategory import me.shedaniel.rei.api.common.category.CategoryIdentifier +import me.shedaniel.rei.api.common.display.Display +import me.shedaniel.rei.api.common.display.DisplaySerializer import me.shedaniel.rei.api.common.util.EntryStacks import net.minecraft.block.Blocks import net.minecraft.text.Text @@ -18,38 +18,38 @@ import moe.nea.firmament.Firmament import moe.nea.firmament.compat.rei.SBItemEntryDefinition class SBCraftingRecipe(override val neuRecipe: NEUCraftingRecipe) : SBRecipe() { - override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.catIdentifier - - object Category : DisplayCategory<SBCraftingRecipe> { - val catIdentifier = CategoryIdentifier.of<SBCraftingRecipe>(Firmament.MOD_ID, "crafing_recipe") - override fun getCategoryIdentifier(): CategoryIdentifier<out SBCraftingRecipe> = catIdentifier - - override fun getTitle(): Text = Text.literal("SkyBlock Crafting") - - override fun getIcon(): Renderer = EntryStacks.of(Blocks.CRAFTING_TABLE) - override fun setupDisplay(display: SBCraftingRecipe, bounds: Rectangle): List<Widget> { - val point = Point(bounds.centerX - 58, bounds.centerY - 27) - return buildList { - add(Widgets.createRecipeBase(bounds)) - add(Widgets.createArrow(Point(point.x + 60, point.y + 18))) - add(Widgets.createResultSlotBackground(Point(point.x + 95, point.y + 19))) - for (i in 0 until 3) { - for (j in 0 until 3) { - val slot = Widgets.createSlot(Point(point.x + 1 + i * 18, point.y + 1 + j * 18)).markInput() - add(slot) - val item = display.neuRecipe.inputs[i + j * 3] - if (item == NEUIngredient.SENTINEL_EMPTY) continue - slot.entry(SBItemEntryDefinition.getEntry(item)) // TODO: make use of stackable item entries - } - } - add( - Widgets.createSlot(Point(point.x + 95, point.y + 19)) - .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.output)) - .disableBackground().markOutput() - ) - } - } - - } + override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.catIdentifier + + object Category : DisplayCategory<SBCraftingRecipe> { + val catIdentifier = CategoryIdentifier.of<SBCraftingRecipe>(Firmament.MOD_ID, "crafing_recipe") + override fun getCategoryIdentifier(): CategoryIdentifier<out SBCraftingRecipe> = catIdentifier + + override fun getTitle(): Text = Text.literal("SkyBlock Crafting") + + override fun getIcon(): Renderer = EntryStacks.of(Blocks.CRAFTING_TABLE) + override fun setupDisplay(display: SBCraftingRecipe, bounds: Rectangle): List<Widget> { + val point = Point(bounds.centerX - 58, bounds.centerY - 27) + return buildList { + add(Widgets.createRecipeBase(bounds)) + add(Widgets.createArrow(Point(point.x + 60, point.y + 18))) + add(Widgets.createResultSlotBackground(Point(point.x + 95, point.y + 19))) + for (i in 0 until 3) { + for (j in 0 until 3) { + val slot = Widgets.createSlot(Point(point.x + 1 + i * 18, point.y + 1 + j * 18)).markInput() + add(slot) + val item = display.neuRecipe.inputs[i + j * 3] + if (item == NEUIngredient.SENTINEL_EMPTY) continue + slot.entry(SBItemEntryDefinition.getEntry(item)) // TODO: make use of stackable item entries + } + } + add( + Widgets.createSlot(Point(point.x + 95, point.y + 19)) + .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.output)) + .disableBackground().markOutput() + ) + } + } + + } } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBEssenceUpgradeRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBEssenceUpgradeRecipe.kt index f81d529..ec71ec8 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBEssenceUpgradeRecipe.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBEssenceUpgradeRecipe.kt @@ -1,4 +1,3 @@ - package moe.nea.firmament.compat.rei.recipes import me.shedaniel.math.Point @@ -16,47 +15,47 @@ import moe.nea.firmament.repo.SBItemStack import moe.nea.firmament.util.SkyblockId class SBEssenceUpgradeRecipe(override val neuRecipe: EssenceRecipeProvider.EssenceUpgradeRecipe) : SBRecipe() { - object Category : DisplayCategory<SBEssenceUpgradeRecipe> { - override fun getCategoryIdentifier(): CategoryIdentifier<SBEssenceUpgradeRecipe> = - CategoryIdentifier.of(Firmament.MOD_ID, "essence_upgrade") + object Category : DisplayCategory<SBEssenceUpgradeRecipe> { + override fun getCategoryIdentifier(): CategoryIdentifier<SBEssenceUpgradeRecipe> = + CategoryIdentifier.of(Firmament.MOD_ID, "essence_upgrade") - override fun getTitle(): Text { - return Text.literal("Essence Upgrades") - } + override fun getTitle(): Text { + return Text.literal("Essence Upgrades") + } - override fun getIcon(): Renderer { - return SBItemEntryDefinition.getEntry(SkyblockId("ESSENCE_WITHER")) - } + override fun getIcon(): Renderer { + return SBItemEntryDefinition.getEntry(SkyblockId("ESSENCE_WITHER")) + } - override fun setupDisplay(display: SBEssenceUpgradeRecipe, bounds: Rectangle): List<Widget> { - val recipe = display.neuRecipe - val list = mutableListOf<Widget>() - list.add(Widgets.createRecipeBase(bounds)) - list.add(Widgets.createSlot(Point(bounds.minX + 12, bounds.centerY - 8 - 18 / 2)) - .markInput() - .entry(SBItemEntryDefinition.getEntry(SBItemStack(recipe.itemId).copy(stars = recipe.starCountAfter - 1)))) - list.add(Widgets.createSlot(Point(bounds.minX + 12, bounds.centerY - 8 + 18 / 2)) - .markInput() - .entry(SBItemEntryDefinition.getEntry(recipe.essenceIngredient))) - list.add(Widgets.createSlot(Point(bounds.maxX - 12 - 16, bounds.centerY - 8)) - .markOutput() - .entry(SBItemEntryDefinition.getEntry(SBItemStack(recipe.itemId).copy(stars = recipe.starCountAfter)))) - val extraItems = recipe.extraItems - list.add(Widgets.createArrow(Point(bounds.centerX - 24 / 2, - if (extraItems.isEmpty()) bounds.centerY - 17 / 2 - else bounds.centerY + 18 / 2))) - for ((index, item) in extraItems.withIndex()) { - list.add(Widgets.createSlot( - Point(bounds.centerX - extraItems.size * 16 / 2 - 2 / 2 + index * 18, - bounds.centerY - 18 / 2)) - .markInput() - .entry(SBItemEntryDefinition.getEntry(item))) - } - return list - } - } + override fun setupDisplay(display: SBEssenceUpgradeRecipe, bounds: Rectangle): List<Widget> { + val recipe = display.neuRecipe + val list = mutableListOf<Widget>() + list.add(Widgets.createRecipeBase(bounds)) + list.add(Widgets.createSlot(Point(bounds.minX + 12, bounds.centerY - 8 - 18 / 2)) + .markInput() + .entry(SBItemEntryDefinition.getEntry(SBItemStack(recipe.itemId).copy(stars = recipe.starCountAfter - 1)))) + list.add(Widgets.createSlot(Point(bounds.minX + 12, bounds.centerY - 8 + 18 / 2)) + .markInput() + .entry(SBItemEntryDefinition.getEntry(recipe.essenceIngredient))) + list.add(Widgets.createSlot(Point(bounds.maxX - 12 - 16, bounds.centerY - 8)) + .markOutput() + .entry(SBItemEntryDefinition.getEntry(SBItemStack(recipe.itemId).copy(stars = recipe.starCountAfter)))) + val extraItems = recipe.extraItems + list.add(Widgets.createArrow(Point(bounds.centerX - 24 / 2, + if (extraItems.isEmpty()) bounds.centerY - 17 / 2 + else bounds.centerY + 18 / 2))) + for ((index, item) in extraItems.withIndex()) { + list.add(Widgets.createSlot( + Point(bounds.centerX - extraItems.size * 16 / 2 - 2 / 2 + index * 18, + bounds.centerY - 18 / 2)) + .markInput() + .entry(SBItemEntryDefinition.getEntry(item))) + } + return list + } + } - override fun getCategoryIdentifier(): CategoryIdentifier<*> { - return Category.categoryIdentifier - } + override fun getCategoryIdentifier(): CategoryIdentifier<*> { + return Category.categoryIdentifier + } } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt index bb51021..96af3fd 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt @@ -1,5 +1,3 @@ - - package moe.nea.firmament.compat.rei.recipes import io.github.moulberry.repo.data.NEUForgeRecipe @@ -21,51 +19,53 @@ import moe.nea.firmament.compat.rei.SBItemEntryDefinition import moe.nea.firmament.compat.rei.plus class SBForgeRecipe(override val neuRecipe: NEUForgeRecipe) : SBRecipe() { - override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier + override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier - object Category : DisplayCategory<SBForgeRecipe> { - override fun getCategoryIdentifier(): CategoryIdentifier<SBForgeRecipe> = - CategoryIdentifier.of(Firmament.MOD_ID, "forge_recipe") + object Category : DisplayCategory<SBForgeRecipe> { + override fun getCategoryIdentifier(): CategoryIdentifier<SBForgeRecipe> = + CategoryIdentifier.of(Firmament.MOD_ID, "forge_recipe") - override fun getTitle(): Text = Text.literal("Forge Recipes") - override fun getDisplayHeight(): Int { - return 104 - } + override fun getTitle(): Text = Text.literal("Forge Recipes") + override fun getDisplayHeight(): Int { + return 104 + } - override fun getIcon(): Renderer = EntryStacks.of(Blocks.ANVIL) - override fun setupDisplay(display: SBForgeRecipe, bounds: Rectangle): List<Widget> { - return buildList { - add(Widgets.createRecipeBase(bounds)) - add(Widgets.createResultSlotBackground(Point(bounds.minX + 124, bounds.minY + 46))) - val arrow = Widgets.createArrow(Point(bounds.minX + 90, bounds.minY + 54 - 18 / 2)) - add(arrow) - add(Widgets.createTooltip(arrow.bounds, Text.stringifiedTranslatable("firmament.recipe.forge.time", display.neuRecipe.duration.seconds))) - val ingredientsCenter = Point(bounds.minX + 49 - 8, bounds.minY + 54 - 8) - val count = display.neuRecipe.inputs.size - if (count == 1) { - add( - Widgets.createSlot(Point(ingredientsCenter.x, ingredientsCenter.y)).markInput() - .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.inputs.single())) - ) - } else { - display.neuRecipe.inputs.forEachIndexed { idx, ingredient -> - val rad = Math.PI * 2 * idx / count - add( - Widgets.createSlot( - Point( - cos(rad) * 30, - sin(rad) * 30, - ) + ingredientsCenter - ).markInput().entry(SBItemEntryDefinition.getEntry(ingredient)) - ) - } - } - add( - Widgets.createSlot(Point(bounds.minX + 124, bounds.minY + 46)).markOutput().disableBackground() - .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.outputStack)) - ) - } - } - } + override fun getIcon(): Renderer = EntryStacks.of(Blocks.ANVIL) + override fun setupDisplay(display: SBForgeRecipe, bounds: Rectangle): List<Widget> { + return buildList { + add(Widgets.createRecipeBase(bounds)) + add(Widgets.createResultSlotBackground(Point(bounds.minX + 124, bounds.minY + 46))) + val arrow = Widgets.createArrow(Point(bounds.minX + 90, bounds.minY + 54 - 18 / 2)) + add(arrow) + add(Widgets.createTooltip(arrow.bounds, + Text.stringifiedTranslatable("firmament.recipe.forge.time", + display.neuRecipe.duration.seconds))) + val ingredientsCenter = Point(bounds.minX + 49 - 8, bounds.minY + 54 - 8) + val count = display.neuRecipe.inputs.size + if (count == 1) { + add( + Widgets.createSlot(Point(ingredientsCenter.x, ingredientsCenter.y)).markInput() + .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.inputs.single())) + ) + } else { + display.neuRecipe.inputs.forEachIndexed { idx, ingredient -> + val rad = Math.PI * 2 * idx / count + add( + Widgets.createSlot( + Point( + cos(rad) * 30, + sin(rad) * 30, + ) + ingredientsCenter + ).markInput().entry(SBItemEntryDefinition.getEntry(ingredient)) + ) + } + } + add( + Widgets.createSlot(Point(bounds.minX + 124, bounds.minY + 46)).markOutput().disableBackground() + .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.outputStack)) + ) + } + } + } } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBKatRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBKatRecipe.kt index fc77fa6..bafbdcc 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBKatRecipe.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBKatRecipe.kt @@ -1,4 +1,3 @@ - package moe.nea.firmament.compat.rei.recipes import io.github.moulberry.repo.data.NEUKatUpgradeRecipe @@ -20,7 +19,6 @@ import me.shedaniel.rei.api.client.registry.display.DisplayCategory import me.shedaniel.rei.api.common.category.CategoryIdentifier import me.shedaniel.rei.api.common.util.EntryStacks import kotlin.time.Duration.Companion.seconds -import net.minecraft.block.Blocks import net.minecraft.client.gui.DrawContext import net.minecraft.client.gui.Element import net.minecraft.item.Items @@ -34,191 +32,191 @@ import moe.nea.firmament.util.MC import moe.nea.firmament.util.SkyblockId class SBKatRecipe(override val neuRecipe: NEUKatUpgradeRecipe) : SBRecipe() { - override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier - - object Category : DisplayCategory<SBKatRecipe> { - override fun getCategoryIdentifier(): CategoryIdentifier<SBKatRecipe> = - CategoryIdentifier.of(Firmament.MOD_ID, "kat_recipe") - - override fun getTitle(): Text = Text.literal("Kat Pet Upgrade") - override fun getDisplayHeight(): Int { - return 100 - } - - override fun getIcon(): Renderer = EntryStacks.of(Items.BONE) - override fun setupDisplay(display: SBKatRecipe, bounds: Rectangle): List<Widget> { - return buildList { - val arrowWidth = 24 - val recipe = display.neuRecipe - val levelValue = Property.upgrade(GetSetter.floating(0F)) - val slider = SliderComponent(levelValue, 1F, 100F, 1f, 100) - val outputStack = SBItemStack(SkyblockId(recipe.output.itemId)) - val inputStack = SBItemStack(SkyblockId(recipe.input.itemId)) - val inputLevelLabelCenter = Point(bounds.minX + 30 - 18 + 5 + 8, bounds.minY + 25) - val inputLevelLabel = Widgets.createLabel( - inputLevelLabelCenter, - Text.literal("")).centered() - val outputLevelLabelCenter = Point(bounds.maxX - 30 + 8, bounds.minY + 25) - val outputLevelLabel = Widgets.createLabel( - outputLevelLabelCenter, - Text.literal("")).centered() - val coinStack = SBItemStack(SkyblockId.COINS, recipe.coins.toInt()) - levelValue.whenChanged { oldValue, newValue -> - if (oldValue.toInt() == newValue.toInt()) return@whenChanged - val oldInput = inputStack.getPetData() ?: return@whenChanged - val newInput = PetData.forLevel(oldInput.petId, oldInput.rarity, newValue.toInt()) - inputStack.setPetData(newInput) - val oldOutput = outputStack.getPetData() ?: return@whenChanged - val newOutput = PetData(oldOutput.rarity, oldOutput.petId, newInput.exp) - outputStack.setPetData(newOutput) - inputLevelLabel.message = Text.literal(newInput.levelData.currentLevel.toString()) - inputLevelLabel.bounds.location = Point( - inputLevelLabelCenter.x - MC.font.getWidth(inputLevelLabel.message) / 2, - inputLevelLabelCenter.y) - outputLevelLabel.message = Text.literal(newOutput.levelData.currentLevel.toString()) - outputLevelLabel.bounds.location = Point( - outputLevelLabelCenter.x - MC.font.getWidth(outputLevelLabel.message) / 2, - outputLevelLabelCenter.y) - coinStack.setStackSize((recipe.coins * (1 - 0.3 * newValue / 100)).toInt()) - } - levelValue.set(1F) - add(Widgets.createRecipeBase(bounds)) - add(wrapWidget(Rectangle(bounds.centerX - slider.width / 2, - bounds.maxY - 30, - slider.width, - slider.height), - slider)) - add(Widgets.withTooltip( - Widgets.createArrow(Point(bounds.centerX - arrowWidth / 2, bounds.minY + 40)), - Text.literal("Upgrade time: " + FirmFormatters.formatTimespan(recipe.seconds.seconds)))) - - add(Widgets.createResultSlotBackground(Point(bounds.maxX - 30, bounds.minY + 40))) - add(inputLevelLabel) - add(outputLevelLabel) - add(Widgets.createSlot(Point(bounds.maxX - 30, bounds.minY + 40)).markOutput().disableBackground() - .entry(SBItemEntryDefinition.getEntry(outputStack))) - add(Widgets.createSlot(Point(bounds.minX + 30 - 18 + 5, bounds.minY + 40)).markInput() - .entry(SBItemEntryDefinition.getEntry(inputStack))) - - val allInputs = recipe.items.map { SBItemEntryDefinition.getEntry(it) } + - listOf(SBItemEntryDefinition.getEntry(coinStack)) - for ((index, item) in allInputs.withIndex()) { - add(Widgets.createSlot( - Point(bounds.centerX + index * 20 - allInputs.size * 18 / 2 - (allInputs.size - 1) * 2 / 2, - bounds.minY + 20)) - .markInput() - .entry(item)) - } - } - } - } + override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier + + object Category : DisplayCategory<SBKatRecipe> { + override fun getCategoryIdentifier(): CategoryIdentifier<SBKatRecipe> = + CategoryIdentifier.of(Firmament.MOD_ID, "kat_recipe") + + override fun getTitle(): Text = Text.literal("Kat Pet Upgrade") + override fun getDisplayHeight(): Int { + return 100 + } + + override fun getIcon(): Renderer = EntryStacks.of(Items.BONE) + override fun setupDisplay(display: SBKatRecipe, bounds: Rectangle): List<Widget> { + return buildList { + val arrowWidth = 24 + val recipe = display.neuRecipe + val levelValue = Property.upgrade(GetSetter.floating(0F)) + val slider = SliderComponent(levelValue, 1F, 100F, 1f, 100) + val outputStack = SBItemStack(SkyblockId(recipe.output.itemId)) + val inputStack = SBItemStack(SkyblockId(recipe.input.itemId)) + val inputLevelLabelCenter = Point(bounds.minX + 30 - 18 + 5 + 8, bounds.minY + 25) + val inputLevelLabel = Widgets.createLabel( + inputLevelLabelCenter, + Text.literal("")).centered() + val outputLevelLabelCenter = Point(bounds.maxX - 30 + 8, bounds.minY + 25) + val outputLevelLabel = Widgets.createLabel( + outputLevelLabelCenter, + Text.literal("")).centered() + val coinStack = SBItemStack(SkyblockId.COINS, recipe.coins.toInt()) + levelValue.whenChanged { oldValue, newValue -> + if (oldValue.toInt() == newValue.toInt()) return@whenChanged + val oldInput = inputStack.getPetData() ?: return@whenChanged + val newInput = PetData.forLevel(oldInput.petId, oldInput.rarity, newValue.toInt()) + inputStack.setPetData(newInput) + val oldOutput = outputStack.getPetData() ?: return@whenChanged + val newOutput = PetData(oldOutput.rarity, oldOutput.petId, newInput.exp) + outputStack.setPetData(newOutput) + inputLevelLabel.message = Text.literal(newInput.levelData.currentLevel.toString()) + inputLevelLabel.bounds.location = Point( + inputLevelLabelCenter.x - MC.font.getWidth(inputLevelLabel.message) / 2, + inputLevelLabelCenter.y) + outputLevelLabel.message = Text.literal(newOutput.levelData.currentLevel.toString()) + outputLevelLabel.bounds.location = Point( + outputLevelLabelCenter.x - MC.font.getWidth(outputLevelLabel.message) / 2, + outputLevelLabelCenter.y) + coinStack.setStackSize((recipe.coins * (1 - 0.3 * newValue / 100)).toInt()) + } + levelValue.set(1F) + add(Widgets.createRecipeBase(bounds)) + add(wrapWidget(Rectangle(bounds.centerX - slider.width / 2, + bounds.maxY - 30, + slider.width, + slider.height), + slider)) + add(Widgets.withTooltip( + Widgets.createArrow(Point(bounds.centerX - arrowWidth / 2, bounds.minY + 40)), + Text.literal("Upgrade time: " + FirmFormatters.formatTimespan(recipe.seconds.seconds)))) + + add(Widgets.createResultSlotBackground(Point(bounds.maxX - 30, bounds.minY + 40))) + add(inputLevelLabel) + add(outputLevelLabel) + add(Widgets.createSlot(Point(bounds.maxX - 30, bounds.minY + 40)).markOutput().disableBackground() + .entry(SBItemEntryDefinition.getEntry(outputStack))) + add(Widgets.createSlot(Point(bounds.minX + 30 - 18 + 5, bounds.minY + 40)).markInput() + .entry(SBItemEntryDefinition.getEntry(inputStack))) + + val allInputs = recipe.items.map { SBItemEntryDefinition.getEntry(it) } + + listOf(SBItemEntryDefinition.getEntry(coinStack)) + for ((index, item) in allInputs.withIndex()) { + add(Widgets.createSlot( + Point(bounds.centerX + index * 20 - allInputs.size * 18 / 2 - (allInputs.size - 1) * 2 / 2, + bounds.minY + 20)) + .markInput() + .entry(item)) + } + } + } + } } fun wrapWidget(bounds: Rectangle, component: GuiComponent): Widget { - return object : WidgetWithBounds() { - override fun getBounds(): Rectangle { - return bounds - } - - override fun children(): List<Element> { - return listOf() - } - - override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { - context.matrices.push() - context.matrices.translate(bounds.minX.toFloat(), bounds.minY.toFloat(), 0F) - component.render( - GuiImmediateContext( - ModernRenderContext(context), - bounds.minX, bounds.minY, - bounds.width, bounds.height, - mouseX - bounds.minX, mouseY - bounds.minY, - mouseX, mouseY, - mouseX.toFloat(), mouseY.toFloat() - )) - context.matrices.pop() - } - - override fun mouseMoved(mouseX: Double, mouseY: Double) { - val mouseXInt = mouseX.toInt() - val mouseYInt = mouseY.toInt() - component.mouseEvent(MouseEvent.Move(0F, 0F), - GuiImmediateContext( - IMinecraft.instance.provideTopLevelRenderContext(), - bounds.minX, bounds.minY, - bounds.width, bounds.height, - mouseXInt - bounds.minX, mouseYInt - bounds.minY, - mouseXInt, mouseYInt, - mouseX.toFloat(), mouseY.toFloat() - )) - } - - override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { - val mouseXInt = mouseX.toInt() - val mouseYInt = mouseY.toInt() - return component.mouseEvent(MouseEvent.Click(button, true), - GuiImmediateContext( - IMinecraft.instance.provideTopLevelRenderContext(), - bounds.minX, bounds.minY, - bounds.width, bounds.height, - mouseXInt - bounds.minX, mouseYInt - bounds.minY, - mouseXInt, mouseYInt, - mouseX.toFloat(), mouseY.toFloat() - )) - } - - override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { - val mouseXInt = mouseX.toInt() - val mouseYInt = mouseY.toInt() - return component.mouseEvent(MouseEvent.Click(button, false), - GuiImmediateContext( - IMinecraft.instance.provideTopLevelRenderContext(), - bounds.minX, bounds.minY, - bounds.width, bounds.height, - mouseXInt - bounds.minX, mouseYInt - bounds.minY, - mouseXInt, mouseYInt, - mouseX.toFloat(), mouseY.toFloat() - )) - } - - override fun mouseDragged( - mouseX: Double, - mouseY: Double, - button: Int, - deltaX: Double, - deltaY: Double - ): Boolean { - val mouseXInt = mouseX.toInt() - val mouseYInt = mouseY.toInt() - return component.mouseEvent(MouseEvent.Move(deltaX.toFloat(), deltaY.toFloat()), - GuiImmediateContext( - IMinecraft.instance.provideTopLevelRenderContext(), - bounds.minX, bounds.minY, - bounds.width, bounds.height, - mouseXInt - bounds.minX, mouseYInt - bounds.minY, - mouseXInt, mouseYInt, - mouseX.toFloat(), mouseY.toFloat() - )) - - } - - override fun mouseScrolled( - mouseX: Double, - mouseY: Double, - horizontalAmount: Double, - verticalAmount: Double - ): Boolean { - val mouseXInt = mouseX.toInt() - val mouseYInt = mouseY.toInt() - return component.mouseEvent(MouseEvent.Scroll(verticalAmount.toFloat()), - GuiImmediateContext( - IMinecraft.instance.provideTopLevelRenderContext(), - bounds.minX, bounds.minY, - bounds.width, bounds.height, - mouseXInt - bounds.minX, mouseYInt - bounds.minY, - mouseXInt, mouseYInt, - mouseX.toFloat(), mouseY.toFloat() - )) - } - } + return object : WidgetWithBounds() { + override fun getBounds(): Rectangle { + return bounds + } + + override fun children(): List<Element> { + return listOf() + } + + override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { + context.matrices.push() + context.matrices.translate(bounds.minX.toFloat(), bounds.minY.toFloat(), 0F) + component.render( + GuiImmediateContext( + ModernRenderContext(context), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseX - bounds.minX, mouseY - bounds.minY, + mouseX, mouseY, + mouseX.toFloat(), mouseY.toFloat() + )) + context.matrices.pop() + } + + override fun mouseMoved(mouseX: Double, mouseY: Double) { + val mouseXInt = mouseX.toInt() + val mouseYInt = mouseY.toInt() + component.mouseEvent(MouseEvent.Move(0F, 0F), + GuiImmediateContext( + IMinecraft.instance.provideTopLevelRenderContext(), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseXInt - bounds.minX, mouseYInt - bounds.minY, + mouseXInt, mouseYInt, + mouseX.toFloat(), mouseY.toFloat() + )) + } + + override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { + val mouseXInt = mouseX.toInt() + val mouseYInt = mouseY.toInt() + return component.mouseEvent(MouseEvent.Click(button, true), + GuiImmediateContext( + IMinecraft.instance.provideTopLevelRenderContext(), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseXInt - bounds.minX, mouseYInt - bounds.minY, + mouseXInt, mouseYInt, + mouseX.toFloat(), mouseY.toFloat() + )) + } + + override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { + val mouseXInt = mouseX.toInt() + val mouseYInt = mouseY.toInt() + return component.mouseEvent(MouseEvent.Click(button, false), + GuiImmediateContext( + IMinecraft.instance.provideTopLevelRenderContext(), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseXInt - bounds.minX, mouseYInt - bounds.minY, + mouseXInt, mouseYInt, + mouseX.toFloat(), mouseY.toFloat() + )) + } + + override fun mouseDragged( + mouseX: Double, + mouseY: Double, + button: Int, + deltaX: Double, + deltaY: Double + ): Boolean { + val mouseXInt = mouseX.toInt() + val mouseYInt = mouseY.toInt() + return component.mouseEvent(MouseEvent.Move(deltaX.toFloat(), deltaY.toFloat()), + GuiImmediateContext( + IMinecraft.instance.provideTopLevelRenderContext(), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseXInt - bounds.minX, mouseYInt - bounds.minY, + mouseXInt, mouseYInt, + mouseX.toFloat(), mouseY.toFloat() + )) + + } + + override fun mouseScrolled( + mouseX: Double, + mouseY: Double, + horizontalAmount: Double, + verticalAmount: Double + ): Boolean { + val mouseXInt = mouseX.toInt() + val mouseYInt = mouseY.toInt() + return component.mouseEvent(MouseEvent.Scroll(verticalAmount.toFloat()), + GuiImmediateContext( + IMinecraft.instance.provideTopLevelRenderContext(), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseXInt - bounds.minX, mouseYInt - bounds.minY, + mouseXInt, mouseYInt, + mouseX.toFloat(), mouseY.toFloat() + )) + } + } } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBMobDropRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBMobDropRecipe.kt index cb240fc..81ec41b 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBMobDropRecipe.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBMobDropRecipe.kt @@ -1,4 +1,3 @@ - package moe.nea.firmament.compat.rei.recipes import io.github.moulberry.repo.data.NEUMobDropRecipe @@ -14,95 +13,95 @@ import net.minecraft.item.Items import net.minecraft.text.Text import net.minecraft.util.Identifier import moe.nea.firmament.Firmament -import moe.nea.firmament.gui.entity.EntityRenderer import moe.nea.firmament.compat.rei.EntityWidget import moe.nea.firmament.compat.rei.SBItemEntryDefinition +import moe.nea.firmament.gui.entity.EntityRenderer class SBMobDropRecipe(override val neuRecipe: NEUMobDropRecipe) : SBRecipe() { - override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier + override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier - object Category : DisplayCategory<SBMobDropRecipe> { - override fun getCategoryIdentifier(): CategoryIdentifier<SBMobDropRecipe> = - CategoryIdentifier.of(Firmament.MOD_ID, "mob_drop_recipe") + object Category : DisplayCategory<SBMobDropRecipe> { + override fun getCategoryIdentifier(): CategoryIdentifier<SBMobDropRecipe> = + CategoryIdentifier.of(Firmament.MOD_ID, "mob_drop_recipe") - override fun getTitle(): Text = Text.literal("Mob Drops") - override fun getDisplayHeight(): Int { - return 100 - } + override fun getTitle(): Text = Text.literal("Mob Drops") + override fun getDisplayHeight(): Int { + return 100 + } - override fun getIcon(): Renderer = EntryStacks.of(Items.DIAMOND_SWORD) - override fun setupDisplay(display: SBMobDropRecipe, bounds: Rectangle): List<Widget> { - return buildList { - add(Widgets.createRecipeBase(bounds)) - val source = display.neuRecipe.render - val entity = if (source.startsWith("@")) { - EntityRenderer.constructEntity(Identifier.of(source.substring(1))) - } else { - EntityRenderer.applyModifiers(source, listOf()) - } - if (entity != null) { - val level = display.neuRecipe.level - val fullMobName = - if (level > 0) Text.translatable("firmament.recipe.mobs.name", level, display.neuRecipe.name) - else Text.translatable("firmament.recipe.mobs.name.nolevel", display.neuRecipe.name) - val tt = mutableListOf<Text>() - tt.add((fullMobName)) - tt.add(Text.literal("")) - if (display.neuRecipe.coins > 0) { - tt.add(Text.stringifiedTranslatable("firmament.recipe.mobs.coins", display.neuRecipe.coins)) - } - if (display.neuRecipe.combatExperience > 0) { - tt.add( - Text.stringifiedTranslatable( - "firmament.recipe.mobs.combat", - display.neuRecipe.combatExperience - ) - ) - } - if (display.neuRecipe.enchantingExperience > 0) { - tt.add( - Text.stringifiedTranslatable( - "firmament.recipe.mobs.exp", - display.neuRecipe.enchantingExperience - ) - ) - } - if (display.neuRecipe.extra != null) - display.neuRecipe.extra.mapTo(tt) { Text.literal(it) } - if (tt.size == 2) - tt.removeAt(1) - add( - Widgets.withTooltip( - EntityWidget(entity, Point(bounds.minX + 5, bounds.minY + 15)), - tt - ) - ) - } - add( - Widgets.createLabel(Point(bounds.minX + 15, bounds.minY + 5), Text.literal(display.neuRecipe.name)) - .leftAligned() - ) - var x = bounds.minX + 60 - var y = bounds.minY + 20 - for (drop in display.neuRecipe.drops) { - val lore = drop.extra.mapTo(mutableListOf()) { Text.literal(it) } - if (drop.chance != null) { - lore += listOf(Text.translatable("firmament.recipe.mobs.drops", drop.chance)) - } - val item = SBItemEntryDefinition.getEntry(drop.dropItem) - .value.copy(extraLore = lore) - add( - Widgets.createSlot(Point(x, y)).markOutput() - .entries(listOf(SBItemEntryDefinition.getEntry(item))) - ) - x += 18 - if (x > bounds.maxX - 30) { - x = bounds.minX + 60 - y += 18 - } - } - } - } - } + override fun getIcon(): Renderer = EntryStacks.of(Items.DIAMOND_SWORD) + override fun setupDisplay(display: SBMobDropRecipe, bounds: Rectangle): List<Widget> { + return buildList { + add(Widgets.createRecipeBase(bounds)) + val source = display.neuRecipe.render + val entity = if (source.startsWith("@")) { + EntityRenderer.constructEntity(Identifier.of(source.substring(1))) + } else { + EntityRenderer.applyModifiers(source, listOf()) + } + if (entity != null) { + val level = display.neuRecipe.level + val fullMobName = + if (level > 0) Text.translatable("firmament.recipe.mobs.name", level, display.neuRecipe.name) + else Text.translatable("firmament.recipe.mobs.name.nolevel", display.neuRecipe.name) + val tt = mutableListOf<Text>() + tt.add((fullMobName)) + tt.add(Text.literal("")) + if (display.neuRecipe.coins > 0) { + tt.add(Text.stringifiedTranslatable("firmament.recipe.mobs.coins", display.neuRecipe.coins)) + } + if (display.neuRecipe.combatExperience > 0) { + tt.add( + Text.stringifiedTranslatable( + "firmament.recipe.mobs.combat", + display.neuRecipe.combatExperience + ) + ) + } + if (display.neuRecipe.enchantingExperience > 0) { + tt.add( + Text.stringifiedTranslatable( + "firmament.recipe.mobs.exp", + display.neuRecipe.enchantingExperience + ) + ) + } + if (display.neuRecipe.extra != null) + display.neuRecipe.extra.mapTo(tt) { Text.literal(it) } + if (tt.size == 2) + tt.removeAt(1) + add( + Widgets.withTooltip( + EntityWidget(entity, Point(bounds.minX + 5, bounds.minY + 15)), + tt + ) + ) + } + add( + Widgets.createLabel(Point(bounds.minX + 15, bounds.minY + 5), Text.literal(display.neuRecipe.name)) + .leftAligned() + ) + var x = bounds.minX + 60 + var y = bounds.minY + 20 + for (drop in display.neuRecipe.drops) { + val lore = drop.extra.mapTo(mutableListOf()) { Text.literal(it) } + if (drop.chance != null) { + lore += listOf(Text.translatable("firmament.recipe.mobs.drops", drop.chance)) + } + val item = SBItemEntryDefinition.getEntry(drop.dropItem) + .value.copy(extraLore = lore) + add( + Widgets.createSlot(Point(x, y)).markOutput() + .entries(listOf(SBItemEntryDefinition.getEntry(item))) + ) + x += 18 + if (x > bounds.maxX - 30) { + x = bounds.minX + 60 + y += 18 + } + } + } + } + } } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBRecipe.kt index a66a529..de7779f 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBRecipe.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBRecipe.kt @@ -2,28 +2,41 @@ package moe.nea.firmament.compat.rei.recipes import io.github.moulberry.repo.data.NEUIngredient import io.github.moulberry.repo.data.NEURecipe +import java.util.Optional import me.shedaniel.rei.api.common.display.Display +import me.shedaniel.rei.api.common.display.DisplaySerializer import me.shedaniel.rei.api.common.entry.EntryIngredient +import net.minecraft.util.Identifier import moe.nea.firmament.compat.rei.SBItemEntryDefinition import moe.nea.firmament.util.SkyblockId abstract class SBRecipe : Display { - abstract val neuRecipe: NEURecipe - override fun getInputEntries(): List<EntryIngredient> { - return neuRecipe.allInputs - .filter { it.itemId != NEUIngredient.NEU_SENTINEL_EMPTY } - .map { - val entryStack = SBItemEntryDefinition.getEntry(SkyblockId(it.itemId)) - EntryIngredient.of(entryStack) - } - } + override fun getDisplayLocation(): Optional<Identifier> { + // In theory, we could return a location for the neuRecipe here. (Something along the lines of neurepo:items/item_id.json/0 for the 0th recipe in the items/item_id.json recipes array). + return Optional.empty() + } - override fun getOutputEntries(): List<EntryIngredient> { - return neuRecipe.allOutputs - .filter { it.itemId != NEUIngredient.NEU_SENTINEL_EMPTY } - .map { - val entryStack = SBItemEntryDefinition.getEntry(SkyblockId(it.itemId)) - EntryIngredient.of(entryStack) - } - } + override fun getSerializer(): DisplaySerializer<out Display>? { + // While returning null here is discouraged, we are fine to do so, since this recipe will never travel through the network + return null + } + + abstract val neuRecipe: NEURecipe + override fun getInputEntries(): List<EntryIngredient> { + return neuRecipe.allInputs + .filter { it.itemId != NEUIngredient.NEU_SENTINEL_EMPTY } + .map { + val entryStack = SBItemEntryDefinition.getEntry(SkyblockId(it.itemId)) + EntryIngredient.of(entryStack) + } + } + + override fun getOutputEntries(): List<EntryIngredient> { + return neuRecipe.allOutputs + .filter { it.itemId != NEUIngredient.NEU_SENTINEL_EMPTY } + .map { + val entryStack = SBItemEntryDefinition.getEntry(SkyblockId(it.itemId)) + EntryIngredient.of(entryStack) + } + } } diff --git a/src/compat/sodium/java/SodiumChunkReloader.kt b/src/compat/sodium/java/SodiumChunkReloader.kt index 932c338..0256b88 100644 --- a/src/compat/sodium/java/SodiumChunkReloader.kt +++ b/src/compat/sodium/java/SodiumChunkReloader.kt @@ -1,6 +1,6 @@ package moe.nea.firmament.compat.sodium -import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer +import net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer import moe.nea.firmament.mixins.accessor.sodium.AccessorSodiumWorldRenderer class SodiumChunkReloader : Runnable { diff --git a/src/compat/sodium/java/moe/nea/firmament/mixins/accessor/sodium/AccessorSodiumWorldRenderer.java b/src/compat/sodium/java/moe/nea/firmament/mixins/accessor/sodium/AccessorSodiumWorldRenderer.java index d585cbc..f75874d 100644 --- a/src/compat/sodium/java/moe/nea/firmament/mixins/accessor/sodium/AccessorSodiumWorldRenderer.java +++ b/src/compat/sodium/java/moe/nea/firmament/mixins/accessor/sodium/AccessorSodiumWorldRenderer.java @@ -1,7 +1,7 @@ package moe.nea.firmament.mixins.accessor.sodium; -import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer; -import me.jellysquid.mods.sodium.client.render.chunk.RenderSectionManager; +import net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer; +import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.gen.Accessor; @@ -9,6 +9,6 @@ import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(SodiumWorldRenderer.class) @Pseudo public interface AccessorSodiumWorldRenderer { - @Accessor(value = "renderSectionManager", remap = false) - RenderSectionManager getRenderSectionManager_firmament(); + @Accessor(value = "renderSectionManager", remap = false) + RenderSectionManager getRenderSectionManager_firmament(); } diff --git a/src/compat/sodium/java/moe/nea/firmament/mixins/custommodels/PatchBlockModelInSodiumChunkGenerator.java b/src/compat/sodium/java/moe/nea/firmament/mixins/custommodels/PatchBlockModelInSodiumChunkGenerator.java index 90f20bc..fe87310 100644 --- a/src/compat/sodium/java/moe/nea/firmament/mixins/custommodels/PatchBlockModelInSodiumChunkGenerator.java +++ b/src/compat/sodium/java/moe/nea/firmament/mixins/custommodels/PatchBlockModelInSodiumChunkGenerator.java @@ -3,8 +3,8 @@ package moe.nea.firmament.mixins.custommodels; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; -import me.jellysquid.mods.sodium.client.render.chunk.compile.tasks.ChunkBuilderMeshingTask; import moe.nea.firmament.features.texturepack.CustomBlockTextures; +import net.caffeinemc.mods.sodium.client.render.chunk.compile.tasks.ChunkBuilderMeshingTask; import net.minecraft.block.BlockState; import net.minecraft.client.render.block.BlockModels; import net.minecraft.client.render.model.BakedModel; @@ -15,7 +15,7 @@ import org.spongepowered.asm.mixin.injection.At; @Mixin(ChunkBuilderMeshingTask.class) public class PatchBlockModelInSodiumChunkGenerator { @WrapOperation( - method = "execute(Lme/jellysquid/mods/sodium/client/render/chunk/compile/ChunkBuildContext;Lme/jellysquid/mods/sodium/client/util/task/CancellationToken;)Lme/jellysquid/mods/sodium/client/render/chunk/compile/ChunkBuildOutput;", + method = "execute(Lnet/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkBuildContext;Lnet/caffeinemc/mods/sodium/client/util/task/CancellationToken;)Lnet/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkBuildOutput;", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/block/BlockModels;getModel(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/model/BakedModel;")) private BakedModel replaceBlockModel(BlockModels instance, BlockState state, Operation<BakedModel> original, @Local(name = "blockPos") BlockPos.Mutable pos) { diff --git a/src/main/java/moe/nea/firmament/mixins/CustomDurabilityBarPatch.java b/src/main/java/moe/nea/firmament/mixins/CustomDurabilityBarPatch.java index 0f4d324..fde3580 100644 --- a/src/main/java/moe/nea/firmament/mixins/CustomDurabilityBarPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/CustomDurabilityBarPatch.java @@ -14,7 +14,7 @@ import org.spongepowered.asm.mixin.injection.At; @Mixin(DrawContext.class) public class CustomDurabilityBarPatch { @WrapOperation( - method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", + method = "drawItemBar", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isItemBarVisible()Z") ) private boolean onIsItemBarVisible( @@ -29,7 +29,7 @@ public class CustomDurabilityBarPatch { return barOverride.get() != null; } - @WrapOperation(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", + @WrapOperation(method = "drawItemBar", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getItemBarStep()I")) private int overrideItemStep( ItemStack instance, Operation<Integer> original, @@ -40,7 +40,7 @@ public class CustomDurabilityBarPatch { return original.call(instance); } - @WrapOperation(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", + @WrapOperation(method = "drawItemBar", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getItemBarColor()I")) private int overrideItemColor( ItemStack instance, Operation<Integer> original, diff --git a/src/main/java/moe/nea/firmament/mixins/CustomModelBakerPatch.java b/src/main/java/moe/nea/firmament/mixins/CustomModelBakerPatch.java deleted file mode 100644 index c1e359d..0000000 --- a/src/main/java/moe/nea/firmament/mixins/CustomModelBakerPatch.java +++ /dev/null @@ -1,49 +0,0 @@ -package moe.nea.firmament.mixins; - -import moe.nea.firmament.events.BakeExtraModelsEvent; -import net.minecraft.client.render.model.ModelLoader; -import net.minecraft.client.render.model.UnbakedModel; -import net.minecraft.client.util.ModelIdentifier; -import net.minecraft.util.Identifier; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.Map; - -@Mixin(ModelLoader.class) -public abstract class CustomModelBakerPatch { - - @Shadow - @Final - private Map<ModelIdentifier, UnbakedModel> modelsToBake; - - @Shadow - protected abstract void loadItemModel(ModelIdentifier id); - - @Shadow - abstract UnbakedModel getOrLoadModel(Identifier id); - - @Shadow - protected abstract void add(ModelIdentifier id, UnbakedModel model); - - @Unique - private void loadNonItemModel(ModelIdentifier identifier) { - UnbakedModel unbakedModel = this.getOrLoadModel(identifier.id()); - this.add(identifier, unbakedModel); - } - - - @Inject(method = "bake", at = @At("HEAD")) - public void onBake(ModelLoader.SpriteGetter spliteGetter, CallbackInfo ci) { - BakeExtraModelsEvent.Companion.publish(new BakeExtraModelsEvent(this::loadItemModel, this::loadNonItemModel)); - modelsToBake.values().forEach(model -> model.setParents(this::getOrLoadModel)); -// modelsToBake.keySet().stream() -// .filter(it -> !it.id().getNamespace().equals("minecraft")) -// .forEach(it -> System.out.println("Non minecraft texture is being loaded: " + it)); - } -} diff --git a/src/main/java/moe/nea/firmament/mixins/CustomModelEventPatch.java b/src/main/java/moe/nea/firmament/mixins/CustomModelEventPatch.java index 61fc82e..e7207f4 100644 --- a/src/main/java/moe/nea/firmament/mixins/CustomModelEventPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/CustomModelEventPatch.java @@ -7,6 +7,7 @@ import net.minecraft.client.render.item.ItemModels; import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModelManager; import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -14,16 +15,15 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.Map; + @Mixin(ItemModels.class) public class CustomModelEventPatch { - @Shadow - @Final - private BakedModelManager modelManager; - @Inject(method = "getModel(Lnet/minecraft/item/ItemStack;)Lnet/minecraft/client/render/model/BakedModel;", at = @At("HEAD"), cancellable = true) - public void onGetModel(ItemStack stack, CallbackInfoReturnable<BakedModel> cir) { - var model = CustomItemModelEvent.getModel(stack, modelManager); - if (model != null) - cir.setReturnValue(model); - } + @Inject(method = "getModel(Lnet/minecraft/item/ItemStack;)Lnet/minecraft/client/render/model/BakedModel;", at = @At("HEAD"), cancellable = true) + public void onGetModel(ItemStack stack, CallbackInfoReturnable<BakedModel> cir) { + var model = CustomItemModelEvent.getModel(stack, (ItemModels) (Object) this); + if (model != null) + cir.setReturnValue(model); + } } diff --git a/src/main/java/moe/nea/firmament/mixins/CustomSkullTexturePatch.java b/src/main/java/moe/nea/firmament/mixins/CustomSkullTexturePatch.java index 4b3f3c3..f3b616a 100644 --- a/src/main/java/moe/nea/firmament/mixins/CustomSkullTexturePatch.java +++ b/src/main/java/moe/nea/firmament/mixins/CustomSkullTexturePatch.java @@ -2,7 +2,6 @@ package moe.nea.firmament.mixins; -import com.mojang.authlib.GameProfile; import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures; import net.minecraft.block.SkullBlock; import net.minecraft.client.render.RenderLayer; @@ -15,8 +14,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(SkullBlockEntityRenderer.class) public class CustomSkullTexturePatch { - @Inject(method = "getRenderLayer", at = @At("HEAD"), cancellable = true) - private static void onGetRenderLayer(SkullBlock.SkullType type, ProfileComponent profile, CallbackInfoReturnable<RenderLayer> cir) { - CustomSkyBlockTextures.INSTANCE.modifySkullTexture(type, profile, cir); - } + @Inject(method = "getRenderLayer", at = @At("HEAD"), cancellable = true) + private static void onGetRenderLayer(SkullBlock.SkullType type, ProfileComponent profile, CallbackInfoReturnable<RenderLayer> cir) { + CustomSkyBlockTextures.INSTANCE.modifySkullTexture(type, profile, cir); + } } diff --git a/src/main/java/moe/nea/firmament/mixins/DFUEntityIdFixPatch.java b/src/main/java/moe/nea/firmament/mixins/DFUEntityIdFixPatch.java index da04ca2..717d404 100644 --- a/src/main/java/moe/nea/firmament/mixins/DFUEntityIdFixPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/DFUEntityIdFixPatch.java @@ -17,6 +17,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.Map; +// TODO: rework this @Mixin(EntityIdFix.class) public abstract class DFUEntityIdFixPatch extends DataFix { @Shadow diff --git a/src/main/java/moe/nea/firmament/mixins/InjectCustomShaderPrograms.java b/src/main/java/moe/nea/firmament/mixins/InjectCustomShaderPrograms.java deleted file mode 100644 index 5306e42..0000000 --- a/src/main/java/moe/nea/firmament/mixins/InjectCustomShaderPrograms.java +++ /dev/null @@ -1,31 +0,0 @@ -package moe.nea.firmament.mixins; - -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.llamalad7.mixinextras.sugar.Local; -import com.mojang.datafixers.util.Pair; -import moe.nea.firmament.events.RegisterCustomShadersEvent; -import net.minecraft.client.gl.ShaderProgram; -import net.minecraft.client.render.GameRenderer; -import net.minecraft.resource.ResourceFactory; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.List; -import java.util.function.Consumer; - -@Mixin(GameRenderer.class) -public class InjectCustomShaderPrograms { - - @Inject(method = "loadPrograms", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/GameRenderer;loadBlurPostProcessor(Lnet/minecraft/resource/ResourceFactory;)V", - shift = At.Shift.AFTER)) - void addFirmamentShaders( - ResourceFactory resourceFactory, CallbackInfo ci, - @Local(index = 3) List<Pair<ShaderProgram, Consumer<ShaderProgram>>> list - ) { - var event = new RegisterCustomShadersEvent(list, resourceFactory); - RegisterCustomShadersEvent.Companion.publish(event); - } -} diff --git a/src/main/java/moe/nea/firmament/mixins/MainWindowFirstLoadPatch.java b/src/main/java/moe/nea/firmament/mixins/MainWindowFirstLoadPatch.java new file mode 100644 index 0000000..0a90b35 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/MainWindowFirstLoadPatch.java @@ -0,0 +1,31 @@ +package moe.nea.firmament.mixins; + +import moe.nea.firmament.Firmament; +import moe.nea.firmament.events.DebugInstantiateEvent; +import net.minecraft.client.gui.LogoDrawer; +import net.minecraft.client.gui.screen.TitleScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(TitleScreen.class) +public class MainWindowFirstLoadPatch { + @Unique + private static boolean hasInited = false; + + @Inject(method = "<init>(ZLnet/minecraft/client/gui/LogoDrawer;)V", at = @At("RETURN")) + private void onCreate(boolean doBackgroundFade, LogoDrawer logoDrawer, CallbackInfo ci) { + if (!hasInited) { + try { + DebugInstantiateEvent.Companion.publish(new DebugInstantiateEvent()); + } catch (Throwable t) { + Firmament.INSTANCE.getLogger().error("Failed to instantiate debug instances", t); + System.exit(1); + throw t; + } + } + hasInited = true; + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java b/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java index fd50c72..1034a12 100644 --- a/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java +++ b/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java @@ -2,6 +2,9 @@ package moe.nea.firmament.mixins; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; import moe.nea.firmament.events.*; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.screen.ingame.HandledScreen; @@ -21,6 +24,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import java.util.Iterator; + @Mixin(HandledScreen.class) public abstract class MixinHandledScreen<T extends ScreenHandler> { @@ -90,15 +95,12 @@ public abstract class MixinHandledScreen<T extends ScreenHandler> { } - @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/screen/slot/Slot;)V", shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILHARD) - public void onAfterDrawSlot(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci, int i, int j, int k, Slot slot) { - SlotRenderEvents.After event = new SlotRenderEvents.After(context, slot, mouseX, mouseY, delta); - SlotRenderEvents.After.Companion.publish(event); - } - - @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/screen/slot/Slot;)V", shift = At.Shift.BEFORE), locals = LocalCapture.CAPTURE_FAILHARD) - public void onBeforeDrawSlot(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci, int i, int j, int k, Slot slot) { - SlotRenderEvents.Before event = new SlotRenderEvents.Before(context, slot, mouseX, mouseY, delta); - SlotRenderEvents.Before.Companion.publish(event); + @WrapOperation(method = "drawSlots", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/screen/slot/Slot;)V")) + public void onDrawSlots(HandledScreen instance, DrawContext context, Slot slot, Operation<Void> original) { + var before = new SlotRenderEvents.Before(context, slot); + SlotRenderEvents.Before.Companion.publish(before); + original.call(instance, context, slot); + var after = new SlotRenderEvents.After(context, slot); + SlotRenderEvents.After.Companion.publish(after); } } diff --git a/src/main/java/moe/nea/firmament/mixins/ReplaceTextColorInHandledScreen.java b/src/main/java/moe/nea/firmament/mixins/ReplaceTextColorInHandledScreen.java index 190fda0..c9fb073 100644 --- a/src/main/java/moe/nea/firmament/mixins/ReplaceTextColorInHandledScreen.java +++ b/src/main/java/moe/nea/firmament/mixins/ReplaceTextColorInHandledScreen.java @@ -20,12 +20,16 @@ import org.spongepowered.asm.mixin.injection.At; AnvilScreen.class, BeaconScreen.class}) public class ReplaceTextColorInHandledScreen { + // To my future self: double check those mixins, but don't be too concerned about errors. Some of the wrapopertions + // only apply in some of the specified subclasses. + @WrapOperation( method = "drawForeground", at = @At( value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawText(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/Text;IIIZ)I"), - expect = 0) + expect = 0, + require = 0) private int replaceTextColorWithVariableShadow(DrawContext instance, TextRenderer textRenderer, Text text, int x, int y, int color, boolean shadow, Operation<Integer> original) { return original.call(instance, textRenderer, text, x, y, CustomTextColors.INSTANCE.mapTextColor(text, color), shadow); } @@ -35,7 +39,8 @@ public class ReplaceTextColorInHandledScreen { at = @At( value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTextWithShadow(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/Text;III)I"), - expect = 0) + expect = 0, + require = 0) private int replaceTextColorWithShadow(DrawContext instance, TextRenderer textRenderer, Text text, int x, int y, int color, Operation<Integer> original) { return original.call(instance, textRenderer, text, x, y, CustomTextColors.INSTANCE.mapTextColor(text, color)); } diff --git a/src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java b/src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java index 6c854d4..06ecbd4 100644 --- a/src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java +++ b/src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java @@ -23,14 +23,13 @@ public abstract class SlotUpdateListener extends ClientCommonNetworkHandler { @Inject( method = "onScreenHandlerSlotUpdate", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/tutorial/TutorialManager;onSlotUpdate(Lnet/minecraft/item/ItemStack;)V")) + at = @At(value = "TAIL")) private void onSingleSlotUpdate( ScreenHandlerSlotUpdateS2CPacket packet, CallbackInfo ci) { var player = this.client.player; assert player != null; - if (packet.getSyncId() == ScreenHandlerSlotUpdateS2CPacket.UPDATE_PLAYER_INVENTORY_SYNC_ID - || packet.getSyncId() == 0) { + if (packet.getSyncId() == 0) { PlayerInventoryUpdate.Companion.publish(new PlayerInventoryUpdate.Single(packet.getSlot(), packet.getStack())); } else if (packet.getSyncId() == player.currentScreenHandler.syncId) { ChestInventoryUpdateEvent.Companion.publish( @@ -40,8 +39,7 @@ public abstract class SlotUpdateListener extends ClientCommonNetworkHandler { } @Inject(method = "onInventory", - at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkThreadUtils;forceMainThread(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/listener/PacketListener;Lnet/minecraft/util/thread/ThreadExecutor;)V", - shift = At.Shift.AFTER)) + at = @At("TAIL")) private void onMultiSlotUpdate(InventoryS2CPacket packet, CallbackInfo ci) { var player = this.client.player; assert player != null; diff --git a/src/main/java/moe/nea/firmament/mixins/WorldReadyEventPatch.java b/src/main/java/moe/nea/firmament/mixins/WorldReadyEventPatch.java index c444f12..d4b8c9e 100644 --- a/src/main/java/moe/nea/firmament/mixins/WorldReadyEventPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/WorldReadyEventPatch.java @@ -3,16 +3,17 @@ package moe.nea.firmament.mixins; import moe.nea.firmament.events.WorldReadyEvent; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.DownloadingTerrainScreen; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(DownloadingTerrainScreen.class) +@Mixin(MinecraftClient.class) public class WorldReadyEventPatch { - @Inject(method = "close", at = @At("HEAD")) - public void onClose(CallbackInfo ci) { - WorldReadyEvent.Companion.publish(new WorldReadyEvent()); - } + @Inject(method = "joinWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;setWorld(Lnet/minecraft/client/world/ClientWorld;)V", shift = At.Shift.AFTER)) + public void onClose(CallbackInfo ci) { + WorldReadyEvent.Companion.publish(new WorldReadyEvent()); + } } diff --git a/src/main/java/moe/nea/firmament/mixins/WorldRenderLastEventPatch.java b/src/main/java/moe/nea/firmament/mixins/WorldRenderLastEventPatch.java index 2ff4560..847fb4d 100644 --- a/src/main/java/moe/nea/firmament/mixins/WorldRenderLastEventPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/WorldRenderLastEventPatch.java @@ -5,8 +5,10 @@ package moe.nea.firmament.mixins; import com.llamalad7.mixinextras.sugar.Local; import moe.nea.firmament.events.WorldRenderLastEvent; import net.minecraft.client.render.*; +import net.minecraft.client.util.Handle; +import net.minecraft.client.util.ObjectAllocator; import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.world.tick.TickManager; +import net.minecraft.util.profiler.Profiler; import org.joml.Matrix4f; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -16,22 +18,30 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(WorldRenderer.class) -public class WorldRenderLastEventPatch { - @Shadow - @Final - private BufferBuilderStorage bufferBuilders; +public abstract class WorldRenderLastEventPatch { + @Shadow + @Final + private BufferBuilderStorage bufferBuilders; - @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;renderChunkDebugInfo(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;Lnet/minecraft/client/render/Camera;)V", shift = At.Shift.BEFORE)) - public void onWorldRenderLast( - RenderTickCounter tickCounter, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, - LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, Matrix4f matrix4f2, - CallbackInfo ci, @Local MatrixStack matrixStack - ) { - var event = new WorldRenderLastEvent( - matrixStack, tickCounter, renderBlockOutline, - camera, gameRenderer, lightmapTextureManager, - this.bufferBuilders.getEntityVertexConsumers() - ); - WorldRenderLastEvent.Companion.publish(event); - } + @Shadow + @Final + private DefaultFramebufferSet framebufferSet; + + @Shadow + protected abstract void checkEmpty(MatrixStack matrices); + + @Inject(method = "method_62214", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiler/Profiler;pop()V", shift = At.Shift.AFTER)) + public void onWorldRenderLast(Fog fog, RenderTickCounter tickCounter, Camera camera, Profiler profiler, Matrix4f matrix4f, Matrix4f matrix4f2, Handle handle, Handle handle2, Handle handle3, Handle handle4, boolean bl, Frustum frustum, Handle handle5, CallbackInfo ci) { + var imm = this.bufferBuilders.getEntityVertexConsumers(); + var stack = new MatrixStack(); + // TODO: pre-cancel this event if F1 is active + var event = new WorldRenderLastEvent( + stack, tickCounter, + camera, + imm + ); + WorldRenderLastEvent.Companion.publish(event); + imm.draw(); + checkEmpty(stack); + } } diff --git a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorGameRenderer.java b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorGameRenderer.java deleted file mode 100644 index f5d2202..0000000 --- a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorGameRenderer.java +++ /dev/null @@ -1,14 +0,0 @@ - - -package moe.nea.firmament.mixins.accessor; - -import net.minecraft.client.render.Camera; -import net.minecraft.client.render.GameRenderer; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; - -@Mixin(GameRenderer.class) -public interface AccessorGameRenderer { - @Invoker("getFov") - double getFov_firmament(Camera camera, float tickDelta, boolean changingFov); -} diff --git a/src/main/java/moe/nea/firmament/mixins/customgui/PatchHandledScreen.java b/src/main/java/moe/nea/firmament/mixins/customgui/PatchHandledScreen.java index deac0a4..814f172 100644 --- a/src/main/java/moe/nea/firmament/mixins/customgui/PatchHandledScreen.java +++ b/src/main/java/moe/nea/firmament/mixins/customgui/PatchHandledScreen.java @@ -14,7 +14,6 @@ import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.slot.Slot; import net.minecraft.text.Text; -import net.minecraft.util.collection.DefaultedList; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -88,29 +87,18 @@ public class PatchHandledScreen<T extends ScreenHandler> extends Screen implemen } - @Unique - private Slot didBeforeSlotRender; - @WrapOperation( - method = "render", + method = "drawSlots", at = @At( value = "INVOKE", - target = "Lnet/minecraft/util/collection/DefaultedList;get(I)Ljava/lang/Object;")) - private Object beforeSlotRender(DefaultedList instance, int index, Operation<Object> original, @Local(argsOnly = true) DrawContext context) { - var slot = (Slot) original.call(instance, index); + target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/screen/slot/Slot;)V")) + private void beforeSlotRender(HandledScreen instance, DrawContext context, Slot slot, Operation<Void> original) { if (override != null) { - didBeforeSlotRender = slot; override.beforeSlotRender(context, slot); } - return slot; - } - - @Inject(method = "render", - at = @At(value = "INVOKE", target = "Lnet/minecraft/util/collection/DefaultedList;size()I")) - private void afterSlotRender(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { - if (override != null && didBeforeSlotRender != null) { - override.afterSlotRender(context, didBeforeSlotRender); - didBeforeSlotRender = null; + original.call(instance, context, slot); + if (override != null) { + override.afterSlotRender(context, slot); } } diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/ApplyHeadModelInItemRenderer.java b/src/main/java/moe/nea/firmament/mixins/custommodels/ApplyHeadModelInItemRenderer.java index 64b358f..dac65fe 100644 --- a/src/main/java/moe/nea/firmament/mixins/custommodels/ApplyHeadModelInItemRenderer.java +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/ApplyHeadModelInItemRenderer.java @@ -1,33 +1,35 @@ package moe.nea.firmament.mixins.custommodels; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; import moe.nea.firmament.features.texturepack.BakedModelExtra; +import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.item.ItemRenderer; import net.minecraft.client.render.model.BakedModel; -import net.minecraft.client.render.model.json.ModelTransformationMode; -import net.minecraft.entity.LivingEntity; +import net.minecraft.client.util.math.MatrixStack; import net.minecraft.item.ItemStack; -import net.minecraft.world.World; +import net.minecraft.item.ModelTransformationMode; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ItemRenderer.class) public class ApplyHeadModelInItemRenderer { - @WrapOperation(method = "renderItem(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;Lnet/minecraft/world/World;III)V", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/ItemRenderer;getModel(Lnet/minecraft/item/ItemStack;Lnet/minecraft/world/World;Lnet/minecraft/entity/LivingEntity;I)Lnet/minecraft/client/render/model/BakedModel;")) - private BakedModel applyHeadModel(ItemRenderer instance, ItemStack stack, World world, LivingEntity entity, int seed, Operation<BakedModel> original, - @Local(argsOnly = true) ModelTransformationMode modelTransformationMode) { - var model = original.call(instance, stack, world, entity, seed); - if (modelTransformationMode == ModelTransformationMode.HEAD - && model instanceof BakedModelExtra extra) { - var headModel = extra.getHeadModel_firmament(); - if (headModel != null) { - model = headModel; - } - } - return model; - } + @Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;ZF)V", + at = @At("HEAD")) + private void applyHeadModel(ItemStack stack, ModelTransformationMode transformationMode, boolean leftHanded, + MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, + BakedModel model, boolean useInventoryModel, float z, CallbackInfo ci, + @Local(argsOnly = true) LocalRef<BakedModel> modelMut + ) { + var extra = BakedModelExtra.cast(model); + if (transformationMode == ModelTransformationMode.HEAD && extra != null) { + var headModel = extra.getHeadModel_firmament(); + if (headModel != null) { + modelMut.set(headModel); + } + } + } } diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/GlobalModelOverridePatch.java b/src/main/java/moe/nea/firmament/mixins/custommodels/GlobalModelOverridePatch.java index eee7557..c708862 100644 --- a/src/main/java/moe/nea/firmament/mixins/custommodels/GlobalModelOverridePatch.java +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/GlobalModelOverridePatch.java @@ -8,6 +8,7 @@ import net.minecraft.client.render.model.BakedModel; import net.minecraft.entity.LivingEntity; import net.minecraft.item.ItemStack; import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -17,13 +18,14 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(ItemRenderer.class) public abstract class GlobalModelOverridePatch { - @Shadow - public abstract ItemModels getModels(); + @Shadow + @Final + private ItemModels models; - @Inject(method = "getModel", at = @At("HEAD"), cancellable = true) - private void overrideGlobalModel( - ItemStack stack, World world, LivingEntity entity, - int seed, CallbackInfoReturnable<BakedModel> cir) { - CustomGlobalTextures.replaceGlobalModel(this.getModels(), stack, cir); - } + @Inject(method = "getModel(Lnet/minecraft/item/ItemStack;Lnet/minecraft/world/World;Lnet/minecraft/entity/LivingEntity;I)Lnet/minecraft/client/render/model/BakedModel;", at = @At("HEAD"), cancellable = true) + private void overrideGlobalModel( + ItemStack stack, World world, LivingEntity entity, + int seed, CallbackInfoReturnable<BakedModel> cir) { + CustomGlobalTextures.replaceGlobalModel(this.models, stack, cir); + } } diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/HeadModelReplacerPatch.java b/src/main/java/moe/nea/firmament/mixins/custommodels/HeadModelReplacerPatch.java new file mode 100644 index 0000000..26c331e --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/HeadModelReplacerPatch.java @@ -0,0 +1,57 @@ + +package moe.nea.firmament.mixins.custommodels; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import moe.nea.firmament.features.texturepack.BakedModelExtra; +import net.minecraft.block.AbstractSkullBlock; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.entity.LivingEntityRenderer; +import net.minecraft.client.render.entity.feature.HeadFeatureRenderer; +import net.minecraft.client.render.entity.model.EntityModel; +import net.minecraft.client.render.entity.model.ModelWithHead; +import net.minecraft.client.render.entity.state.LivingEntityRenderState; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.item.BlockItem; +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(HeadFeatureRenderer.class) +public class HeadModelReplacerPatch<S extends LivingEntityRenderState, M extends EntityModel<S> & ModelWithHead> { + /** + * This class serves to disable the replacing of head models with the vanilla block model. Vanilla first selects loads + * the model containing the head model regularly in {@link LivingEntityRenderer#updateRenderState}, but then discards + * the model in {@link HeadFeatureRenderer#render(MatrixStack, VertexConsumerProvider, int, LivingEntityRenderState, float, float)} + * if it detects a skull block. This serves to disable that functionality if a head model override is present. + */ + @WrapOperation(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/render/entity/state/LivingEntityRenderState;FF)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/item/BlockItem;getBlock()Lnet/minecraft/block/Block;")) + private Block replaceSkull(BlockItem instance, Operation<Block> original, @Local BakedModel bakedModel) { + var oldBlock = original.call(instance); + if (oldBlock instanceof AbstractSkullBlock) { + var extra = BakedModelExtra.cast(bakedModel); + if (extra != null && extra.getHeadModel_firmament() != null) + return Blocks.ENCHANTING_TABLE; // Any non skull block. Let's choose the enchanting table because it is very distinct. + } + return oldBlock; + } + + /** + * We disable the has model override, since texture packs get precedent to server data. + */ + @WrapOperation(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/render/entity/state/LivingEntityRenderState;FF)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/feature/ArmorFeatureRenderer;hasModel(Lnet/minecraft/item/ItemStack;Lnet/minecraft/entity/EquipmentSlot;)Z")) + private boolean replaceHasModel(ItemStack stack, EquipmentSlot slot, Operation<Boolean> original, + @Local BakedModel bakedModel) { + var extra = BakedModelExtra.cast(bakedModel); + if (extra != null && extra.getHeadModel_firmament() != null) + return false; + return original.call(stack, slot); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/ItemRendererTintContextPatch.java b/src/main/java/moe/nea/firmament/mixins/custommodels/ItemRendererTintContextPatch.java index 5ed0fbe..8c5411b 100644 --- a/src/main/java/moe/nea/firmament/mixins/custommodels/ItemRendererTintContextPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/ItemRendererTintContextPatch.java @@ -5,28 +5,30 @@ import moe.nea.firmament.features.texturepack.TintOverrides; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.item.ItemRenderer; import net.minecraft.client.render.model.BakedModel; -import net.minecraft.client.render.model.json.ModelTransformationMode; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.item.ItemStack; +import net.minecraft.item.ModelTransformationMode; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(ItemRenderer.class) +@Mixin(value = ItemRenderer.class, priority = 1010) public class ItemRendererTintContextPatch { - @Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/BakedModel;getTransformation()Lnet/minecraft/client/render/model/json/ModelTransformation;"), allow = 1) - private void onStartRendering(ItemStack stack, ModelTransformationMode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) { - if (model instanceof BakedModelExtra extra) { + @Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;ZF)V", + at = @At(value = "HEAD"), allow = 1) + private void onStartRendering(ItemStack stack, ModelTransformationMode transformationMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, boolean useInventoryModel, float z, CallbackInfo ci) { + var extra = BakedModelExtra.cast(model); + if (extra != null) { TintOverrides.Companion.enter(extra.getTintOverrides_firmament()); } } - @Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/math/MatrixStack;pop()V"), allow = 1) - private void onEndRendering(ItemStack stack, ModelTransformationMode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) { - if (model instanceof BakedModelExtra extra) { + @Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;ZF)V", + at = @At("TAIL"), allow = 1) + private void onEndRendering(ItemStack stack, ModelTransformationMode transformationMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, boolean useInventoryModel, float z, CallbackInfo ci) { + var extra = BakedModelExtra.cast(model); + if (extra != null) { TintOverrides.Companion.exit(extra.getTintOverrides_firmament()); } } diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/JsonUnbakedModelDataHolder.java b/src/main/java/moe/nea/firmament/mixins/custommodels/JsonUnbakedModelDataHolder.java index 20c69e2..a5bb34f 100644 --- a/src/main/java/moe/nea/firmament/mixins/custommodels/JsonUnbakedModelDataHolder.java +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/JsonUnbakedModelDataHolder.java @@ -1,11 +1,11 @@ package moe.nea.firmament.mixins.custommodels; -import com.google.gson.annotations.SerializedName; import com.llamalad7.mixinextras.injector.ModifyReturnValue; import com.llamalad7.mixinextras.sugar.Local; import moe.nea.firmament.features.texturepack.BakedModelExtra; import moe.nea.firmament.features.texturepack.JsonUnbakedModelFirmExtra; import moe.nea.firmament.features.texturepack.TintOverrides; +import moe.nea.firmament.util.ErrorUtil; import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.Baker; import net.minecraft.client.render.model.ModelRotation; @@ -18,15 +18,20 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.util.Collection; import java.util.Objects; @Mixin(JsonUnbakedModel.class) -public class JsonUnbakedModelDataHolder implements JsonUnbakedModelFirmExtra { +public abstract class JsonUnbakedModelDataHolder implements JsonUnbakedModelFirmExtra { @Shadow @Nullable protected JsonUnbakedModel parent; + + @Shadow + public abstract String toString(); + @Unique @Nullable public Identifier headModel; @@ -67,31 +72,59 @@ public class JsonUnbakedModelDataHolder implements JsonUnbakedModelFirmExtra { return ((JsonUnbakedModelFirmExtra) this.parent).getHeadModel_firmament(); } - @ModifyReturnValue(method = "getModelDependencies", at = @At("RETURN")) - private Collection<Identifier> addDependencies(Collection<Identifier> original) { + @Inject(method = "resolve", at = @At("HEAD")) + private void addDependencies(UnbakedModel.Resolver resolver, CallbackInfo ci) { var headModel = getHeadModel_firmament(); if (headModel != null) { - original.add(headModel); + resolver.resolve(headModel); } - return original; } - @ModifyReturnValue( - method = "bake(Lnet/minecraft/client/render/model/Baker;Lnet/minecraft/client/render/model/json/JsonUnbakedModel;Ljava/util/function/Function;Lnet/minecraft/client/render/model/ModelBakeSettings;Z)Lnet/minecraft/client/render/model/BakedModel;", - at = @At(value = "RETURN")) - private BakedModel bakeExtraInfo(BakedModel original, @Local(argsOnly = true) Baker baker) { - if (original instanceof BakedModelExtra extra) { + private void addExtraBakeInfo(BakedModel bakedModel, Baker baker) { + if (!this.toString().contains("minecraft") && this.toString().contains("crimson")) { + System.out.println("Found non minecraft model " + this); + } + var extra = BakedModelExtra.cast(bakedModel); + if (extra != null) { var headModel = getHeadModel_firmament(); if (headModel != null) { - UnbakedModel unbakedModel = baker.getOrLoadModel(headModel); - extra.setHeadModel_firmament( - Objects.equals(unbakedModel, parent) - ? null - : baker.bake(headModel, ModelRotation.X0_Y0)); + extra.setHeadModel_firmament(baker.bake(headModel, ModelRotation.X0_Y0)); } - if (getTintOverrides_firmament().hasOverrides()) + if (getTintOverrides_firmament().hasOverrides()) { extra.setTintOverrides_firmament(getTintOverrides_firmament()); + } + } + } + + /** + * @see ProvideBakerToJsonUnbakedModelPatch + */ + @Override + public void storeExtraBaker_firmament(@NotNull Baker baker) { + this.storedBaker = baker; + } + + @Unique + private Baker storedBaker; + + @ModifyReturnValue( + method = "bake(Ljava/util/function/Function;Lnet/minecraft/client/render/model/ModelBakeSettings;Z)Lnet/minecraft/client/render/model/BakedModel;", + at = @At("RETURN")) + private BakedModel bakeExtraInfoWithoutBaker(BakedModel original) { + if (storedBaker != null) { + addExtraBakeInfo(original, storedBaker); + storedBaker = null; } return original; } + + @ModifyReturnValue( + method = { + "bake(Lnet/minecraft/client/render/model/Baker;Ljava/util/function/Function;Lnet/minecraft/client/render/model/ModelBakeSettings;)Lnet/minecraft/client/render/model/BakedModel;" + }, + at = @At(value = "RETURN")) + private BakedModel bakeExtraInfo(BakedModel original, @Local(argsOnly = true) Baker baker) { + addExtraBakeInfo(original, baker); + return original; + } } diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/PatchArmorTexture.java b/src/main/java/moe/nea/firmament/mixins/custommodels/PatchArmorTexture.java index 7c6f69e..4468150 100644 --- a/src/main/java/moe/nea/firmament/mixins/custommodels/PatchArmorTexture.java +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/PatchArmorTexture.java @@ -6,26 +6,24 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; import moe.nea.firmament.features.texturepack.CustomGlobalArmorOverrides; import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; -import net.minecraft.item.ArmorMaterial; +import net.minecraft.component.type.EquippableComponent; import net.minecraft.item.ItemStack; +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.Optional; @Mixin(ArmorFeatureRenderer.class) public class PatchArmorTexture { - @WrapOperation( - method = "renderArmor", - at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ArmorMaterial;layers()Ljava/util/List;")) - private List<ArmorMaterial.Layer> overrideLayers( - ArmorMaterial instance, - Operation<List<ArmorMaterial.Layer>> original, - @Local ItemStack itemStack - ) { - var overrides = CustomGlobalArmorOverrides.overrideArmor(itemStack); - if (overrides == null) - return original.call(instance); - return overrides; - } + @WrapOperation( + method = "renderArmor", + at = @At(value = "INVOKE", target = "Lnet/minecraft/component/type/EquippableComponent;model()Ljava/util/Optional;")) + private Optional<Identifier> overrideLayers( + EquippableComponent instance, Operation<Optional<Identifier>> original, @Local(argsOnly = true) ItemStack itemStack + ) { + // TODO: check that all armour items are naturally equippable and have the equppable component. otherwise our call here will not be reached. + var overrides = CustomGlobalArmorOverrides.overrideArmor(itemStack); + return overrides.or(() -> original.call(instance)); + } } diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/PatchHeadFeatureRenderer.java b/src/main/java/moe/nea/firmament/mixins/custommodels/PatchHeadFeatureRenderer.java deleted file mode 100644 index 610a106..0000000 --- a/src/main/java/moe/nea/firmament/mixins/custommodels/PatchHeadFeatureRenderer.java +++ /dev/null @@ -1,45 +0,0 @@ - -package moe.nea.firmament.mixins.custommodels; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.llamalad7.mixinextras.sugar.Local; -import moe.nea.firmament.features.texturepack.BakedModelExtra; -import net.minecraft.block.AbstractSkullBlock; -import net.minecraft.block.Block; -import net.minecraft.block.Blocks; -import net.minecraft.client.render.entity.feature.HeadFeatureRenderer; -import net.minecraft.client.render.entity.model.EntityModel; -import net.minecraft.client.render.item.HeldItemRenderer; -import net.minecraft.entity.LivingEntity; -import net.minecraft.item.BlockItem; -import net.minecraft.item.ItemStack; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; - -@Mixin(HeadFeatureRenderer.class) -public class PatchHeadFeatureRenderer<T extends LivingEntity, M extends EntityModel<T>> { - - @Shadow - @Final - private HeldItemRenderer heldItemRenderer; - - @WrapOperation(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/entity/LivingEntity;FFFFFF)V", - at = @At(value = "INVOKE", target = "Lnet/minecraft/item/BlockItem;getBlock()Lnet/minecraft/block/Block;")) - private Block replaceSkull(BlockItem instance, Operation<Block> original, @Local ItemStack itemStack, @Local(argsOnly = true) T entity) { - var oldBlock = original.call(instance); - if (oldBlock instanceof AbstractSkullBlock) { - var bakedModel = this.heldItemRenderer.itemRenderer - .getModel(itemStack, entity.getWorld(), entity, 0); - if (bakedModel instanceof BakedModelExtra extra && extra.getHeadModel_firmament() != null) - return Blocks.ENCHANTING_TABLE; // Any non skull block. Let's choose the enchanting table because it is very distinct. - } - return oldBlock; - } - - - - -} diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/PatchLegacyArmorLayerSupport.java b/src/main/java/moe/nea/firmament/mixins/custommodels/PatchLegacyArmorLayerSupport.java new file mode 100644 index 0000000..8c0b3f8 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/PatchLegacyArmorLayerSupport.java @@ -0,0 +1,22 @@ +package moe.nea.firmament.mixins.custommodels; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import moe.nea.firmament.features.texturepack.CustomGlobalArmorOverrides; +import net.minecraft.client.render.entity.equipment.EquipmentModelLoader; +import net.minecraft.client.render.entity.equipment.EquipmentRenderer; +import net.minecraft.item.equipment.EquipmentModel; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(EquipmentRenderer.class) +public class PatchLegacyArmorLayerSupport { + @WrapOperation(method = "render(Lnet/minecraft/item/equipment/EquipmentModel$LayerType;Lnet/minecraft/util/Identifier;Lnet/minecraft/client/model/Model;Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/util/Identifier;)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/equipment/EquipmentModelLoader;get(Lnet/minecraft/util/Identifier;)Lnet/minecraft/item/equipment/EquipmentModel;")) + private EquipmentModel patchModelLayers(EquipmentModelLoader instance, Identifier id, Operation<EquipmentModel> original) { + var modelOverride = CustomGlobalArmorOverrides.overrideArmorLayer(id); + if (modelOverride != null) return modelOverride; + return original.call(instance, id); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/PatchOverrideDeserializer.java b/src/main/java/moe/nea/firmament/mixins/custommodels/PatchOverrideDeserializer.java index 938d14d..abb1792 100644 --- a/src/main/java/moe/nea/firmament/mixins/custommodels/PatchOverrideDeserializer.java +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/PatchOverrideDeserializer.java @@ -22,29 +22,29 @@ import java.util.Map; @Mixin(ModelOverride.Deserializer.class) public class PatchOverrideDeserializer { - @ModifyReturnValue( - method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/render/model/json/ModelOverride;", - at = @At(value = "RETURN")) - private ModelOverride addCustomOverrides(ModelOverride original, @Local JsonObject jsonObject) { - var originalData = (ModelOverrideData) original; - originalData.setFirmamentOverrides(CustomModelOverrideParser.parseCustomModelOverrides(jsonObject)); - return original; - } + @ModifyReturnValue( + method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/render/model/json/ModelOverride;", + at = @At(value = "RETURN")) + private ModelOverride addCustomOverrides(ModelOverride original, @Local JsonObject jsonObject) { + var originalData = (ModelOverrideData) (Object) original; + originalData.setFirmamentOverrides(CustomModelOverrideParser.parseCustomModelOverrides(jsonObject)); + return original; + } - @ModifyExpressionValue( - method = "deserializeMinPropertyValues(Lcom/google/gson/JsonObject;)Ljava/util/List;", - at = @At(value = "INVOKE", target = "Ljava/util/Map$Entry;getValue()Ljava/lang/Object;")) - private Object removeFirmamentPredicatesFromJsonIteration(Object original, @Local Map.Entry<String, JsonElement> entry) { - if (entry.getKey().startsWith("firmament:")) return new JsonPrimitive(0F); - return original; - } + @ModifyExpressionValue( + method = "deserializeMinPropertyValues(Lcom/google/gson/JsonObject;)Ljava/util/List;", + at = @At(value = "INVOKE", target = "Ljava/util/Map$Entry;getValue()Ljava/lang/Object;")) + private Object removeFirmamentPredicatesFromJsonIteration(Object original, @Local Map.Entry<String, JsonElement> entry) { + if (entry.getKey().startsWith("firmament:")) return new JsonPrimitive(0F); + return original; + } - @Inject( - method = "deserializeMinPropertyValues", - at = @At(value = "INVOKE", target = "Ljava/util/Map;entrySet()Ljava/util/Set;") - ) - private void whatever(JsonObject object, CallbackInfoReturnable<List<ModelOverride.Condition>> cir, - @Local Map<Identifier, Float> maps) { - maps.entrySet().removeIf(it -> it.getKey().getNamespace().equals("firmament")); - } + @Inject( + method = "deserializeMinPropertyValues", + at = @At(value = "INVOKE", target = "Ljava/util/Map;entrySet()Ljava/util/Set;") + ) + private void whatever(JsonObject object, CallbackInfoReturnable<List<ModelOverride.Condition>> cir, + @Local Map<Identifier, Float> maps) { + maps.entrySet().removeIf(it -> it.getKey().getNamespace().equals("firmament")); + } } diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/ProvideBakerToJsonUnbakedModelPatch.java b/src/main/java/moe/nea/firmament/mixins/custommodels/ProvideBakerToJsonUnbakedModelPatch.java new file mode 100644 index 0000000..c1ac119 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/ProvideBakerToJsonUnbakedModelPatch.java @@ -0,0 +1,27 @@ +package moe.nea.firmament.mixins.custommodels; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import moe.nea.firmament.features.texturepack.JsonUnbakedModelFirmExtra; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.Baker; +import net.minecraft.client.render.model.ModelBakeSettings; +import net.minecraft.client.render.model.json.JsonUnbakedModel; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.util.SpriteIdentifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.function.Function; + +/** + * @see JsonUnbakedModelDataHolder#storeExtraBaker_firmament + */ +@Mixin(targets = "net.minecraft.client.render.model.ModelBaker$BakerImpl") +public abstract class ProvideBakerToJsonUnbakedModelPatch implements Baker { + @WrapOperation(method = "bake(Lnet/minecraft/client/render/model/UnbakedModel;Lnet/minecraft/client/render/model/ModelBakeSettings;)Lnet/minecraft/client/render/model/BakedModel;", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/json/JsonUnbakedModel;bake(Ljava/util/function/Function;Lnet/minecraft/client/render/model/ModelBakeSettings;Z)Lnet/minecraft/client/render/model/BakedModel;")) + private BakedModel provideExtraBakerToModel(JsonUnbakedModel instance, Function<SpriteIdentifier, Sprite> function, ModelBakeSettings modelBakeSettings, boolean bl, Operation<BakedModel> original) { + ((JsonUnbakedModelFirmExtra) instance).storeExtraBaker_firmament(this); + return original.call(instance, function, modelBakeSettings, bl); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/ReferenceCustomModelsPatch.java b/src/main/java/moe/nea/firmament/mixins/custommodels/ReferenceCustomModelsPatch.java new file mode 100644 index 0000000..14c84ab --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/ReferenceCustomModelsPatch.java @@ -0,0 +1,37 @@ +package moe.nea.firmament.mixins.custommodels; + +import moe.nea.firmament.events.BakeExtraModelsEvent; +import net.minecraft.client.render.model.BlockStatesLoader; +import net.minecraft.client.render.model.ItemModel; +import net.minecraft.client.render.model.ReferencedModelsCollector; +import net.minecraft.client.render.model.UnbakedModel; +import net.minecraft.client.util.ModelIdentifier; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Map; + +@Mixin(ReferencedModelsCollector.class) +public abstract class ReferenceCustomModelsPatch { + @Shadow + protected abstract void addTopLevelModel(ModelIdentifier modelId, UnbakedModel model); + + @Shadow + @Final + private Map<Identifier, UnbakedModel> inputs; + + @Inject(method = "addBlockStates", at = @At("RETURN")) + private void addFirmamentReferencedModels( + BlockStatesLoader.BlockStateDefinition definition, CallbackInfo ci + ) { + inputs.keySet().stream().filter(it->it.toString().contains("firm")).forEach(System.out::println); + BakeExtraModelsEvent.Companion.publish(new BakeExtraModelsEvent( + (modelIdentifier, identifier) -> addTopLevelModel(modelIdentifier, new ItemModel(identifier)))); + + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/TestForFirmamentOverridePredicatesPatch.java b/src/main/java/moe/nea/firmament/mixins/custommodels/TestForFirmamentOverridePredicatesPatch.java index bc47d74..81ae3b8 100644 --- a/src/main/java/moe/nea/firmament/mixins/custommodels/TestForFirmamentOverridePredicatesPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/TestForFirmamentOverridePredicatesPatch.java @@ -17,33 +17,33 @@ import org.spongepowered.asm.mixin.injection.ModifyArg; @Mixin(ModelOverrideList.class) public class TestForFirmamentOverridePredicatesPatch { - @ModifyArg(method = "<init>(Lnet/minecraft/client/render/model/Baker;Lnet/minecraft/client/render/model/json/JsonUnbakedModel;Ljava/util/List;)V", - at = @At( - value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z" - )) - public Object onInit( - Object element, - @Local ModelOverride modelOverride - ) { - var bakedOverride = (ModelOverrideList.BakedOverride) element; - ((BakedOverrideData) bakedOverride) - .setFirmamentOverrides(((ModelOverrideData) modelOverride).getFirmamentOverrides()); - return element; - } + @ModifyArg(method = "<init>(Lnet/minecraft/client/render/model/Baker;Ljava/util/List;)V", + at = @At( + value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z" + )) + public Object onInit( + Object element, + @Local ModelOverride modelOverride + ) { + var bakedOverride = (ModelOverrideList.BakedOverride) element; + ((BakedOverrideData) (Object) bakedOverride) + .setFirmamentOverrides(((ModelOverrideData) (Object) modelOverride).getFirmamentOverrides()); + return element; + } - @ModifyExpressionValue(method = "apply", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/json/ModelOverrideList$BakedOverride;test([F)Z")) - public boolean testFirmamentOverrides(boolean originalValue, - @Local ModelOverrideList.BakedOverride bakedOverride, - @Local(argsOnly = true) ItemStack stack) { - if (!originalValue) return false; - var overrideData = (BakedOverrideData) bakedOverride; - var overrides = overrideData.getFirmamentOverrides(); - if (overrides == null) return true; - if (!CustomSkyBlockTextures.TConfig.INSTANCE.getEnableModelOverrides()) return false; - for (FirmamentModelPredicate firmamentOverride : overrides) { - if (!firmamentOverride.test(stack)) - return false; - } - return true; - } + @ModifyExpressionValue(method = "getModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/json/ModelOverrideList$BakedOverride;test([F)Z")) + public boolean testFirmamentOverrides(boolean originalValue, + @Local ModelOverrideList.BakedOverride bakedOverride, + @Local(argsOnly = true) ItemStack stack) { + if (!originalValue) return false; + var overrideData = (BakedOverrideData) (Object) bakedOverride; + var overrides = overrideData.getFirmamentOverrides(); + if (overrides == null) return true; + if (!CustomSkyBlockTextures.TConfig.INSTANCE.getEnableModelOverrides()) return false; + for (FirmamentModelPredicate firmamentOverride : overrides) { + if (!firmamentOverride.test(stack)) + return false; + } + return true; + } } diff --git a/src/main/kotlin/events/BakeExtraModelsEvent.kt b/src/main/kotlin/events/BakeExtraModelsEvent.kt index f75bedc..adaa495 100644 --- a/src/main/kotlin/events/BakeExtraModelsEvent.kt +++ b/src/main/kotlin/events/BakeExtraModelsEvent.kt @@ -1,21 +1,24 @@ - package moe.nea.firmament.events -import java.util.function.Consumer +import java.util.function.BiConsumer +import net.minecraft.client.render.model.ReferencedModelsCollector import net.minecraft.client.util.ModelIdentifier +import net.minecraft.util.Identifier +// TODO: Rename this event, since it is not really directly baking models anymore class BakeExtraModelsEvent( - private val addItemModel: Consumer<ModelIdentifier>, - private val addAnyModel: Consumer<ModelIdentifier>, + private val addAnyModel: BiConsumer<ModelIdentifier, Identifier>, ) : FirmamentEvent() { - fun addNonItemModel(modelIdentifier: ModelIdentifier) { - this.addAnyModel.accept(modelIdentifier) - } + fun addNonItemModel(modelIdentifier: ModelIdentifier, identifier: Identifier) { + this.addAnyModel.accept(modelIdentifier, identifier) + } - fun addItemModel(modelIdentifier: ModelIdentifier) { - this.addItemModel.accept(modelIdentifier) - } + fun addItemModel(modelIdentifier: ModelIdentifier) { + addNonItemModel( + modelIdentifier, + modelIdentifier.id.withPrefixedPath(ReferencedModelsCollector.ITEM_DIRECTORY)) + } - companion object : FirmamentEventBus<BakeExtraModelsEvent>() + companion object : FirmamentEventBus<BakeExtraModelsEvent>() } diff --git a/src/main/kotlin/events/CustomItemModelEvent.kt b/src/main/kotlin/events/CustomItemModelEvent.kt index e50eca4..4328d77 100644 --- a/src/main/kotlin/events/CustomItemModelEvent.kt +++ b/src/main/kotlin/events/CustomItemModelEvent.kt @@ -2,35 +2,37 @@ package moe.nea.firmament.events import java.util.Optional import kotlin.jvm.optionals.getOrNull +import net.minecraft.client.render.item.ItemModels import net.minecraft.client.render.model.BakedModel -import net.minecraft.client.render.model.BakedModelManager import net.minecraft.client.util.ModelIdentifier import net.minecraft.item.ItemStack +import moe.nea.firmament.util.ErrorUtil import moe.nea.firmament.util.collections.WeakCache data class CustomItemModelEvent( - val itemStack: ItemStack, - var overrideModel: ModelIdentifier? = null, + val itemStack: ItemStack, + var overrideModel: ModelIdentifier? = null, ) : FirmamentEvent() { - companion object : FirmamentEventBus<CustomItemModelEvent>() { - val cache = - WeakCache.memoize<ItemStack, BakedModelManager, Optional<BakedModel>>("CustomItemModels") { stack, models -> - val modelId = getModelIdentifier(stack) ?: return@memoize Optional.empty() - val bakedModel = models.getModel(modelId) - if (bakedModel === models.missingModel) return@memoize Optional.empty() - Optional.of(bakedModel) - } + companion object : FirmamentEventBus<CustomItemModelEvent>() { + val cache = + WeakCache.memoize<ItemStack, ItemModels, Optional<BakedModel>>("CustomItemModels") { stack, models -> + val modelId = getModelIdentifier(stack) ?: return@memoize Optional.empty() + ErrorUtil.softCheck("Model Id needs to have an inventory variant", modelId.variant() == "inventory") + val bakedModel = models.getModel(modelId.id) + if (bakedModel == null || bakedModel === models.missingModelSupplier.get()) return@memoize Optional.empty() + Optional.of(bakedModel) + } - @JvmStatic - fun getModelIdentifier(itemStack: ItemStack?): ModelIdentifier? { - if (itemStack == null) return null - return publish(CustomItemModelEvent(itemStack)).overrideModel - } + @JvmStatic + fun getModelIdentifier(itemStack: ItemStack?): ModelIdentifier? { + if (itemStack == null) return null + return publish(CustomItemModelEvent(itemStack)).overrideModel + } - @JvmStatic - fun getModel(itemStack: ItemStack?, thing: BakedModelManager): BakedModel? { - if (itemStack == null) return null - return cache.invoke(itemStack, thing).getOrNull() - } - } + @JvmStatic + fun getModel(itemStack: ItemStack?, thing: ItemModels): BakedModel? { + if (itemStack == null) return null + return cache.invoke(itemStack, thing).getOrNull() + } + } } diff --git a/src/main/kotlin/events/DebugInstantiateEvent.kt b/src/main/kotlin/events/DebugInstantiateEvent.kt new file mode 100644 index 0000000..3470a8c --- /dev/null +++ b/src/main/kotlin/events/DebugInstantiateEvent.kt @@ -0,0 +1,9 @@ +package moe.nea.firmament.events + +/** + * Called in a devenv after minecraft has been initialized. This event should be used to force instantiation of lazy + * variables (and similar late init) to cause any possible issues to materialize. + */ +class DebugInstantiateEvent : FirmamentEvent() { + companion object : FirmamentEventBus<DebugInstantiateEvent>() +} diff --git a/src/main/kotlin/events/FinalizeResourceManagerEvent.kt b/src/main/kotlin/events/FinalizeResourceManagerEvent.kt index 0d411f1..12167f8 100644 --- a/src/main/kotlin/events/FinalizeResourceManagerEvent.kt +++ b/src/main/kotlin/events/FinalizeResourceManagerEvent.kt @@ -5,31 +5,28 @@ import java.util.concurrent.Executor import net.minecraft.resource.ReloadableResourceManagerImpl import net.minecraft.resource.ResourceManager import net.minecraft.resource.ResourceReloader -import net.minecraft.util.profiler.Profiler data class FinalizeResourceManagerEvent( - val resourceManager: ReloadableResourceManagerImpl, + val resourceManager: ReloadableResourceManagerImpl, ) : FirmamentEvent() { - companion object : FirmamentEventBus<FinalizeResourceManagerEvent>() + companion object : FirmamentEventBus<FinalizeResourceManagerEvent>() - inline fun registerOnApply(name: String, crossinline function: () -> Unit) { - resourceManager.registerReloader(object : ResourceReloader { - override fun reload( - synchronizer: ResourceReloader.Synchronizer, - manager: ResourceManager?, - prepareProfiler: Profiler?, - applyProfiler: Profiler?, - prepareExecutor: Executor?, - applyExecutor: Executor - ): CompletableFuture<Void> { - return CompletableFuture.completedFuture(Unit) - .thenCompose(synchronizer::whenPrepared) - .thenAcceptAsync({ function() }, applyExecutor) - } + inline fun registerOnApply(name: String, crossinline function: () -> Unit) { + resourceManager.registerReloader(object : ResourceReloader { + override fun reload( + synchronizer: ResourceReloader.Synchronizer, + manager: ResourceManager, + prepareExecutor: Executor, + applyExecutor: Executor + ): CompletableFuture<Void> { + return CompletableFuture.completedFuture(Unit) + .thenCompose(synchronizer::whenPrepared) + .thenAcceptAsync({ function() }, applyExecutor) + } - override fun getName(): String { - return name - } - }) - } + override fun getName(): String { + return name + } + }) + } } diff --git a/src/main/kotlin/events/IsSlotProtectedEvent.kt b/src/main/kotlin/events/IsSlotProtectedEvent.kt index cd431f7..cd2b676 100644 --- a/src/main/kotlin/events/IsSlotProtectedEvent.kt +++ b/src/main/kotlin/events/IsSlotProtectedEvent.kt @@ -37,7 +37,7 @@ data class IsSlotProtectedEvent( val event = IsSlotProtectedEvent(slot, action, false, itemStackOverride) publish(event) if (event.isProtected && !event.silent) { - MC.player?.sendMessage(Text.translatable("firmament.protectitem").append(event.itemStack.name)) + MC.sendChat(Text.translatable("firmament.protectitem").append(event.itemStack.name)) CommonSoundEffects.playFailure() } return event.isProtected diff --git a/src/main/kotlin/events/RegisterCustomShadersEvent.kt b/src/main/kotlin/events/RegisterCustomShadersEvent.kt deleted file mode 100644 index 2f6d1f8..0000000 --- a/src/main/kotlin/events/RegisterCustomShadersEvent.kt +++ /dev/null @@ -1,24 +0,0 @@ -package moe.nea.firmament.events - -import com.mojang.datafixers.util.Pair -import java.util.function.Consumer -import net.minecraft.client.gl.ShaderProgram -import net.minecraft.client.render.VertexFormat -import net.minecraft.resource.ResourceFactory -import moe.nea.firmament.Firmament - -data class RegisterCustomShadersEvent( - val list: MutableList<Pair<ShaderProgram, Consumer<ShaderProgram>>>, - val resourceFactory: ResourceFactory, -) : FirmamentEvent() { - companion object : FirmamentEventBus<RegisterCustomShadersEvent>() - - fun register(name: String, vertexFormat: VertexFormat, saver: Consumer<ShaderProgram>) { - require(name.startsWith("firmament_")) - try { - list.add(Pair.of(ShaderProgram(resourceFactory, name, vertexFormat), saver)) - } catch (ex: Exception) { - Firmament.logger.fatal("Could not load firmament shader $name", ex) - } - } -} diff --git a/src/main/kotlin/events/SlotRenderEvents.kt b/src/main/kotlin/events/SlotRenderEvents.kt index 5431573..5234176 100644 --- a/src/main/kotlin/events/SlotRenderEvents.kt +++ b/src/main/kotlin/events/SlotRenderEvents.kt @@ -3,20 +3,19 @@ package moe.nea.firmament.events import net.minecraft.client.gui.DrawContext +import net.minecraft.client.render.RenderLayer import net.minecraft.client.texture.Sprite import net.minecraft.screen.slot.Slot import net.minecraft.util.Identifier import moe.nea.firmament.util.MC +import moe.nea.firmament.util.render.drawGuiTexture interface SlotRenderEvents { val context: DrawContext val slot: Slot - val mouseX: Int - val mouseY: Int - val delta: Float - fun highlight(sprite: Sprite) { - context.drawSprite( + fun highlight(sprite: Identifier) { + context.drawGuiTexture( slot.x, slot.y, 0, 16, 16, sprite ) @@ -24,9 +23,6 @@ interface SlotRenderEvents { data class Before( override val context: DrawContext, override val slot: Slot, - override val mouseX: Int, - override val mouseY: Int, - override val delta: Float ) : FirmamentEvent(), SlotRenderEvents { companion object : FirmamentEventBus<Before>() @@ -34,9 +30,6 @@ interface SlotRenderEvents { data class After( override val context: DrawContext, override val slot: Slot, - override val mouseX: Int, - override val mouseY: Int, - override val delta: Float ) : FirmamentEvent(), SlotRenderEvents { companion object : FirmamentEventBus<After>() diff --git a/src/main/kotlin/events/WorldReadyEvent.kt b/src/main/kotlin/events/WorldReadyEvent.kt index 2c76c44..c79b100 100644 --- a/src/main/kotlin/events/WorldReadyEvent.kt +++ b/src/main/kotlin/events/WorldReadyEvent.kt @@ -1,7 +1,10 @@ - - package moe.nea.firmament.events class WorldReadyEvent : FirmamentEvent() { - companion object : FirmamentEventBus<WorldReadyEvent>() + companion object : FirmamentEventBus<WorldReadyEvent>() +// class FullyLoaded : FirmamentEvent() { +// companion object : FirmamentEventBus<FullyLoaded>() { +// TODO: check WorldLoadingState +// } +// } } diff --git a/src/main/kotlin/events/WorldRenderLastEvent.kt b/src/main/kotlin/events/WorldRenderLastEvent.kt index 21a670d..3c2103d 100644 --- a/src/main/kotlin/events/WorldRenderLastEvent.kt +++ b/src/main/kotlin/events/WorldRenderLastEvent.kt @@ -17,10 +17,7 @@ import net.minecraft.util.math.Vec3d data class WorldRenderLastEvent( val matrices: MatrixStack, val tickCounter: RenderTickCounter, - val renderBlockOutline: Boolean, val camera: Camera, - val gameRenderer: GameRenderer, - val lightmapTextureManager: LightmapTextureManager, val vertexConsumers: VertexConsumerProvider.Immediate, ) : FirmamentEvent() { companion object : FirmamentEventBus<WorldRenderLastEvent>() diff --git a/src/main/kotlin/features/chat/ChatLinks.kt b/src/main/kotlin/features/chat/ChatLinks.kt index 0681f57..5bce3f4 100644 --- a/src/main/kotlin/features/chat/ChatLinks.kt +++ b/src/main/kotlin/features/chat/ChatLinks.kt @@ -13,8 +13,10 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.async import kotlin.math.min import net.minecraft.client.gui.screen.ChatScreen +import net.minecraft.client.render.RenderLayer import net.minecraft.client.texture.NativeImage import net.minecraft.client.texture.NativeImageBackedTexture +import net.minecraft.scoreboard.ScoreboardCriterion.RenderType import net.minecraft.text.ClickEvent import net.minecraft.text.HoverEvent import net.minecraft.text.Style @@ -28,6 +30,7 @@ import moe.nea.firmament.events.ScreenRenderPostEvent import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.util.MC +import moe.nea.firmament.util.render.drawTexture import moe.nea.firmament.util.transformEachRecursively import moe.nea.firmament.util.unformattedString diff --git a/src/main/kotlin/features/debug/DeveloperFeatures.kt b/src/main/kotlin/features/debug/DeveloperFeatures.kt index 2001a3f..d4b118b 100644 --- a/src/main/kotlin/features/debug/DeveloperFeatures.kt +++ b/src/main/kotlin/features/debug/DeveloperFeatures.kt @@ -37,10 +37,10 @@ object DeveloperFeatures : FirmamentFeature { builder.directory(gradleDir.toFile()) builder.inheritIO() val process = builder.start() - MC.player?.sendMessage(Text.translatable("firmament.dev.resourcerebuild.start")) + MC.sendChat(Text.translatable("firmament.dev.resourcerebuild.start")) val startTime = TimeMark.now() process.toHandle().onExit().thenApply { - MC.player?.sendMessage(Text.stringifiedTranslatable( + MC.sendChat(Text.stringifiedTranslatable( "firmament.dev.resourcerebuild.done", startTime.passedTime())) Unit diff --git a/src/main/kotlin/features/debug/PowerUserTools.kt b/src/main/kotlin/features/debug/PowerUserTools.kt index 83e3aff..13320dc 100644 --- a/src/main/kotlin/features/debug/PowerUserTools.kt +++ b/src/main/kotlin/features/debug/PowerUserTools.kt @@ -9,6 +9,7 @@ import net.minecraft.entity.Entity import net.minecraft.entity.LivingEntity import net.minecraft.item.ItemStack import net.minecraft.item.Items +import net.minecraft.nbt.NbtOps import net.minecraft.text.Text import net.minecraft.text.TextCodecs import net.minecraft.util.hit.BlockHitResult @@ -54,15 +55,13 @@ object PowerUserTools : FirmamentFeature { var lastCopiedStack: Pair<ItemStack, Text>? = null set(value) { field = value - if (value != null) - lastCopiedStackViewTime = true + if (value != null) lastCopiedStackViewTime = true } var lastCopiedStackViewTime = false @Subscribe fun resetLastCopiedStack(event: TickEvent) { - if (!lastCopiedStackViewTime) - lastCopiedStack = null + if (!lastCopiedStackViewTime) lastCopiedStack = null lastCopiedStackViewTime = false } @@ -154,13 +153,13 @@ object PowerUserTools : FirmamentFeature { } ClipboardUtils.setTextContent(skullTexture.toString()) lastCopiedStack = - Pair( - item, - Text.stringifiedTranslatable("firmament.tooltip.copied.skull-id", skullTexture.toString()) - ) + Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skull-id", skullTexture.toString())) println("Copied skull id: $skullTexture") } else if (it.matches(TConfig.copyItemStack)) { - ClipboardUtils.setTextContent(item.encode(MC.currentOrDefaultRegistries).toPrettyString()) + ClipboardUtils.setTextContent( + ItemStack.CODEC + .encodeStart(MC.currentOrDefaultRegistries.getOps(NbtOps.INSTANCE), item) + .orThrow.toPrettyString()) lastCopiedStack = Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.stack")) } } diff --git a/src/main/kotlin/features/diana/AncestralSpadeSolver.kt b/src/main/kotlin/features/diana/AncestralSpadeSolver.kt index ef3ead1..8918e66 100644 --- a/src/main/kotlin/features/diana/AncestralSpadeSolver.kt +++ b/src/main/kotlin/features/diana/AncestralSpadeSolver.kt @@ -104,12 +104,13 @@ object AncestralSpadeSolver : SubscriptionOwner { if (!isEnabled()) return RenderInWorldContext.renderInWorld(event) { nextGuess?.let { - color(1f, 1f, 0f, 0.5f) - tinyBlock(it, 1f) + tinyBlock(it, 1f, 0x80FFFFFF.toInt()) + // TODO: replace this color(1f, 1f, 0f, 1f) tracer(it, lineWidth = 3f) } if (particlePositions.size > 2 && lastDing.passedTime() < 10.seconds && nextGuess != null) { + // TODO: replace this // TODO: add toggle color(0f, 1f, 0f, 0.7f) line(particlePositions) } diff --git a/src/main/kotlin/features/diana/NearbyBurrowsSolver.kt b/src/main/kotlin/features/diana/NearbyBurrowsSolver.kt index ab1518a..2fb4002 100644 --- a/src/main/kotlin/features/diana/NearbyBurrowsSolver.kt +++ b/src/main/kotlin/features/diana/NearbyBurrowsSolver.kt @@ -1,6 +1,6 @@ - package moe.nea.firmament.features.diana +import me.shedaniel.math.Color import kotlin.time.Duration.Companion.seconds import net.minecraft.particle.ParticleTypes import net.minecraft.util.math.BlockPos @@ -20,125 +20,125 @@ import moe.nea.firmament.util.render.RenderInWorldContext.Companion.renderInWorl object NearbyBurrowsSolver : SubscriptionOwner { - private val recentlyDugBurrows: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(20) - private val recentEnchantParticles: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(500) - private var lastBlockClick: BlockPos? = null - - enum class BurrowType { - START, MOB, TREASURE - } - - val burrows = mutableMapOf<BlockPos, BurrowType>() - - @Subscribe - fun onChatEvent(event: ProcessChatEvent) { - val lastClickedBurrow = lastBlockClick ?: return - if (event.unformattedString.startsWith("You dug out a Griffin Burrow!") || - event.unformattedString.startsWith(" ☠ You were killed by") || - event.unformattedString.startsWith("You finished the Griffin burrow chain!") - ) { - markAsDug(lastClickedBurrow) - burrows.remove(lastClickedBurrow) - } - } - - - fun wasRecentlyDug(blockPos: BlockPos): Boolean { - val lastDigTime = recentlyDugBurrows[blockPos] ?: TimeMark.farPast() - return lastDigTime.passedTime() < 10.seconds - } - - fun markAsDug(blockPos: BlockPos) { - recentlyDugBurrows[blockPos] = TimeMark.now() - } - - fun wasRecentlyEnchanted(blockPos: BlockPos): Boolean { - val lastEnchantTime = recentEnchantParticles[blockPos] ?: TimeMark.farPast() - return lastEnchantTime.passedTime() < 4.seconds - } - - fun markAsEnchanted(blockPos: BlockPos) { - recentEnchantParticles[blockPos] = TimeMark.now() - } - - @Subscribe - fun onParticles(event: ParticleSpawnEvent) { - if (!DianaWaypoints.TConfig.nearbyWaypoints) return - - val position: BlockPos = event.position.toBlockPos().down() - - if (wasRecentlyDug(position)) return - - val isEven50Spread = (event.offset.x == 0.5f && event.offset.z == 0.5f) - - if (event.particleEffect.type == ParticleTypes.ENCHANT) { - if (event.count == 5 && event.speed == 0.05F && event.offset.y == 0.4F && isEven50Spread) { - markAsEnchanted(position) - } - return - } - - if (!wasRecentlyEnchanted(position)) return - - if (event.particleEffect.type == ParticleTypes.ENCHANTED_HIT - && event.count == 4 - && event.speed == 0.01F - && event.offset.y == 0.1f - && isEven50Spread - ) { - burrows[position] = BurrowType.START - } - if (event.particleEffect.type == ParticleTypes.CRIT - && event.count == 3 - && event.speed == 0.01F - && event.offset.y == 0.1F - && isEven50Spread - ) { - burrows[position] = BurrowType.MOB - } - if (event.particleEffect.type == ParticleTypes.DRIPPING_LAVA - && event.count == 2 - && event.speed == 0.01F - && event.offset.y == 0.1F - && event.offset.x == 0.35F && event.offset.z == 0.35f - ) { - burrows[position] = BurrowType.TREASURE - } - } - - @Subscribe - fun onRender(event: WorldRenderLastEvent) { - if (!DianaWaypoints.TConfig.nearbyWaypoints) return - renderInWorld(event) { - for ((location, burrow) in burrows) { - when (burrow) { - BurrowType.START -> color(.2f, .8f, .2f, 0.4f) - BurrowType.MOB -> color(0.3f, 0.4f, 0.9f, 0.4f) - BurrowType.TREASURE -> color(1f, 0.7f, 0.2f, 0.4f) - } - block(location) - } - } - } - - @Subscribe - fun onSwapWorld(worldReadyEvent: WorldReadyEvent) { - burrows.clear() - recentEnchantParticles.clear() - recentlyDugBurrows.clear() - lastBlockClick = null - } - - fun onBlockClick(blockPos: BlockPos) { - if (!DianaWaypoints.TConfig.nearbyWaypoints) return - burrows.remove(blockPos) - lastBlockClick = blockPos - } - - override val delegateFeature: FirmamentFeature - get() = DianaWaypoints + private val recentlyDugBurrows: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(20) + private val recentEnchantParticles: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(500) + private var lastBlockClick: BlockPos? = null + + enum class BurrowType { + START, MOB, TREASURE + } + + val burrows = mutableMapOf<BlockPos, BurrowType>() + + @Subscribe + fun onChatEvent(event: ProcessChatEvent) { + val lastClickedBurrow = lastBlockClick ?: return + if (event.unformattedString.startsWith("You dug out a Griffin Burrow!") || + event.unformattedString.startsWith(" ☠ You were killed by") || + event.unformattedString.startsWith("You finished the Griffin burrow chain!") + ) { + markAsDug(lastClickedBurrow) + burrows.remove(lastClickedBurrow) + } + } + + + fun wasRecentlyDug(blockPos: BlockPos): Boolean { + val lastDigTime = recentlyDugBurrows[blockPos] ?: TimeMark.farPast() + return lastDigTime.passedTime() < 10.seconds + } + + fun markAsDug(blockPos: BlockPos) { + recentlyDugBurrows[blockPos] = TimeMark.now() + } + + fun wasRecentlyEnchanted(blockPos: BlockPos): Boolean { + val lastEnchantTime = recentEnchantParticles[blockPos] ?: TimeMark.farPast() + return lastEnchantTime.passedTime() < 4.seconds + } + + fun markAsEnchanted(blockPos: BlockPos) { + recentEnchantParticles[blockPos] = TimeMark.now() + } + + @Subscribe + fun onParticles(event: ParticleSpawnEvent) { + if (!DianaWaypoints.TConfig.nearbyWaypoints) return + + val position: BlockPos = event.position.toBlockPos().down() + + if (wasRecentlyDug(position)) return + + val isEven50Spread = (event.offset.x == 0.5f && event.offset.z == 0.5f) + + if (event.particleEffect.type == ParticleTypes.ENCHANT) { + if (event.count == 5 && event.speed == 0.05F && event.offset.y == 0.4F && isEven50Spread) { + markAsEnchanted(position) + } + return + } + + if (!wasRecentlyEnchanted(position)) return + + if (event.particleEffect.type == ParticleTypes.ENCHANTED_HIT + && event.count == 4 + && event.speed == 0.01F + && event.offset.y == 0.1f + && isEven50Spread + ) { + burrows[position] = BurrowType.START + } + if (event.particleEffect.type == ParticleTypes.CRIT + && event.count == 3 + && event.speed == 0.01F + && event.offset.y == 0.1F + && isEven50Spread + ) { + burrows[position] = BurrowType.MOB + } + if (event.particleEffect.type == ParticleTypes.DRIPPING_LAVA + && event.count == 2 + && event.speed == 0.01F + && event.offset.y == 0.1F + && event.offset.x == 0.35F && event.offset.z == 0.35f + ) { + burrows[position] = BurrowType.TREASURE + } + } + + @Subscribe + fun onRender(event: WorldRenderLastEvent) { + if (!DianaWaypoints.TConfig.nearbyWaypoints) return + renderInWorld(event) { + for ((location, burrow) in burrows) { + val color = when (burrow) { + BurrowType.START -> Color.ofRGBA(.2f, .8f, .2f, 0.4f) + BurrowType.MOB -> Color.ofRGBA(0.3f, 0.4f, 0.9f, 0.4f) + BurrowType.TREASURE -> Color.ofRGBA(1f, 0.7f, 0.2f, 0.4f) + } + block(location, color.color) + } + } + } + + @Subscribe + fun onSwapWorld(worldReadyEvent: WorldReadyEvent) { + burrows.clear() + recentEnchantParticles.clear() + recentlyDugBurrows.clear() + lastBlockClick = null + } + + fun onBlockClick(blockPos: BlockPos) { + if (!DianaWaypoints.TConfig.nearbyWaypoints) return + burrows.remove(blockPos) + lastBlockClick = blockPos + } + + override val delegateFeature: FirmamentFeature + get() = DianaWaypoints } fun Position.toBlockPos(): BlockPos { - return BlockPos(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z)) + return BlockPos(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z)) } diff --git a/src/main/kotlin/features/inventory/CraftingOverlay.kt b/src/main/kotlin/features/inventory/CraftingOverlay.kt index a958e25..d2c79fd 100644 --- a/src/main/kotlin/features/inventory/CraftingOverlay.kt +++ b/src/main/kotlin/features/inventory/CraftingOverlay.kt @@ -69,7 +69,7 @@ object CraftingOverlay : FirmamentFeature { if (!slot.hasStack()) { val itemStack = SBItemStack(expectedItem)?.asImmutableItemStack() ?: return event.context.drawItem(itemStack, event.slot.x, event.slot.y) - event.context.drawItemInSlot( + event.context.drawStackOverlay( MC.font, itemStack, event.slot.x, diff --git a/src/main/kotlin/features/inventory/ItemRarityCosmetics.kt b/src/main/kotlin/features/inventory/ItemRarityCosmetics.kt index 77f5071..26712da 100644 --- a/src/main/kotlin/features/inventory/ItemRarityCosmetics.kt +++ b/src/main/kotlin/features/inventory/ItemRarityCosmetics.kt @@ -4,6 +4,7 @@ package moe.nea.firmament.features.inventory import java.awt.Color import net.minecraft.client.gui.DrawContext +import net.minecraft.client.render.RenderLayer import net.minecraft.item.ItemStack import net.minecraft.util.Formatting import net.minecraft.util.Identifier @@ -16,6 +17,7 @@ import moe.nea.firmament.util.MC import moe.nea.firmament.util.mc.loreAccordingToNbt import moe.nea.firmament.util.collections.lastNotNullOfOrNull import moe.nea.firmament.util.collections.memoizeIdentity +import moe.nea.firmament.util.render.drawGuiTexture import moe.nea.firmament.util.unformattedString object ItemRarityCosmetics : FirmamentFeature { @@ -43,10 +45,10 @@ object ItemRarityCosmetics : FirmamentFeature { "SUPREME" to Formatting.DARK_RED, ).mapValues { val c = Color(it.value.colorValue!!) - Triple(c.red / 255F, c.green / 255F, c.blue / 255F) + c.rgb } - private fun getSkyblockRarity0(itemStack: ItemStack): Triple<Float, Float, Float>? { + private fun getSkyblockRarity0(itemStack: ItemStack): Int? { return itemStack.loreAccordingToNbt.lastNotNullOfOrNull { val entry = it.unformattedString rarityToColor.entries.find { (k, v) -> k in entry }?.value @@ -57,13 +59,13 @@ object ItemRarityCosmetics : FirmamentFeature { fun drawItemStackRarity(drawContext: DrawContext, x: Int, y: Int, item: ItemStack) { - val (r, g, b) = getSkyblockRarity(item) ?: return - drawContext.drawSprite( + val rgb = getSkyblockRarity(item) ?: return + drawContext.drawGuiTexture( + RenderLayer::getGuiTextured, + Identifier.of("firmament:item_rarity_background"), x, y, - 0, 16, 16, - MC.guiAtlasManager.getSprite(Identifier.of("firmament:item_rarity_background")), - r, g, b, 1F + rgb ) } diff --git a/src/main/kotlin/features/inventory/PetFeatures.kt b/src/main/kotlin/features/inventory/PetFeatures.kt index 2c11e76..5ca10f7 100644 --- a/src/main/kotlin/features/inventory/PetFeatures.kt +++ b/src/main/kotlin/features/inventory/PetFeatures.kt @@ -7,6 +7,7 @@ import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.util.MC import moe.nea.firmament.util.petData +import moe.nea.firmament.util.render.drawGuiTexture import moe.nea.firmament.util.useMatch object PetFeatures : FirmamentFeature { @@ -28,9 +29,9 @@ object PetFeatures : FirmamentFeature { val stack = event.slot.stack if (stack.petData?.active == true) petMenuTitle.useMatch(MC.screenName ?: return) { - event.context.drawSprite( + event.context.drawGuiTexture( event.slot.x, event.slot.y, 0, 16, 16, - MC.guiAtlasManager.getSprite(Identifier.of("firmament:selected_pet_background")) + Identifier.of("firmament:selected_pet_background") ) } } diff --git a/src/main/kotlin/features/inventory/SlotLocking.kt b/src/main/kotlin/features/inventory/SlotLocking.kt index de54005..fc09476 100644 --- a/src/main/kotlin/features/inventory/SlotLocking.kt +++ b/src/main/kotlin/features/inventory/SlotLocking.kt @@ -35,6 +35,7 @@ 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.render.GuiRenderLayers import moe.nea.firmament.util.render.drawLine import moe.nea.firmament.util.skyblockUUID import moe.nea.firmament.util.unformattedString @@ -211,6 +212,11 @@ object SlotLocking : FirmamentFeature { } if (it.matches(TConfig.slotBind)) { storedLockingSlot = null + val boundSlots = DConfig.data?.boundSlots ?: return + if (slot != null) + boundSlots.entries.removeIf { + it.value == slot.index || it.key == slot.index + } } } @@ -331,24 +337,22 @@ object SlotLocking : FirmamentFeature { val isSlotLocked = it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf()) val isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf()) if (isSlotLocked || isUUIDLocked) { - RenderSystem.disableDepthTest() - it.context.drawSprite( - it.slot.x, it.slot.y, 0, + it.context.drawGuiTexture( + GuiRenderLayers.GUI_TEXTURED_NO_DEPTH, + when { + isSlotLocked -> + (Identifier.of("firmament:slot_locked")) + + isUUIDLocked -> + (Identifier.of("firmament:uuid_locked")) + + else -> + error("unreachable") + }, + it.slot.x, it.slot.y, 16, 16, - MC.guiAtlasManager.getSprite( - when { - isSlotLocked -> - (Identifier.of("firmament:slot_locked")) - - isUUIDLocked -> - (Identifier.of("firmament:uuid_locked")) - - else -> - error("unreachable") - } - ) + -1 ) - RenderSystem.enableDepthTest() } } } diff --git a/src/main/kotlin/features/inventory/buttons/InventoryButton.kt b/src/main/kotlin/features/inventory/buttons/InventoryButton.kt index be173bd..a46bd76 100644 --- a/src/main/kotlin/features/inventory/buttons/InventoryButton.kt +++ b/src/main/kotlin/features/inventory/buttons/InventoryButton.kt @@ -18,6 +18,7 @@ import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.util.MC import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.collections.memoize +import moe.nea.firmament.util.render.drawGuiTexture @Serializable data class InventoryButton( @@ -54,13 +55,13 @@ data class InventoryButton( } fun render(context: DrawContext) { - context.drawSprite( + context.drawGuiTexture( 0, 0, 0, dimensions.width, dimensions.height, - MC.guiAtlasManager.getSprite(Identifier.of("firmament:inventory_button_background")) + Identifier.of("firmament:inventory_button_background") ) context.drawItem(getItem(), 1, 1) } diff --git a/src/main/kotlin/features/inventory/buttons/InventoryButtonEditor.kt b/src/main/kotlin/features/inventory/buttons/InventoryButtonEditor.kt index c57563e..7bf9c73 100644 --- a/src/main/kotlin/features/inventory/buttons/InventoryButtonEditor.kt +++ b/src/main/kotlin/features/inventory/buttons/InventoryButtonEditor.kt @@ -84,7 +84,6 @@ class InventoryButtonEditor( context.matrices.push() context.matrices.translate(0f, 0f, -10f) context.fill(lastGuiRect.minX, lastGuiRect.minY, lastGuiRect.maxX, lastGuiRect.maxY, -1) - context.setShaderColor(1f, 1f, 1f, 1f) context.matrices.pop() for (button in buttons) { val buttonPosition = button.getBounds(lastGuiRect) diff --git a/src/main/kotlin/features/inventory/storageoverlay/StorageBackingHandle.kt b/src/main/kotlin/features/inventory/storageoverlay/StorageBackingHandle.kt index 5c1ac34..8fad4df 100644 --- a/src/main/kotlin/features/inventory/storageoverlay/StorageBackingHandle.kt +++ b/src/main/kotlin/features/inventory/storageoverlay/StorageBackingHandle.kt @@ -40,6 +40,7 @@ sealed interface StorageBackingHandle { * representable as a [StorageBackingHandle], meaning another screen is open, for example the enderchest icon * selection screen. */ + @OptIn(ExperimentalContracts::class) fun fromScreen(screen: Screen?): StorageBackingHandle? { contract { returnsNotNull() implies (screen != null) diff --git a/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt b/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt index f81315d..a5b2fd6 100644 --- a/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt +++ b/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt @@ -21,6 +21,7 @@ import moe.nea.firmament.util.MoulConfigUtils.clickMCComponentInPlace import moe.nea.firmament.util.MoulConfigUtils.drawMCComponentInPlace import moe.nea.firmament.util.assertTrueOr import moe.nea.firmament.util.customgui.customGui +import moe.nea.firmament.util.render.drawGuiTexture class StorageOverlayScreen : Screen(Text.literal("")) { @@ -162,13 +163,11 @@ class StorageOverlayScreen : Screen(Text.literal("")) { context.drawGuiTexture(upperBackgroundSprite, measurements.x, measurements.y, - 0, measurements.overviewWidth, measurements.overviewHeight) context.drawGuiTexture(playerInventorySprite, measurements.playerX, measurements.playerY, - 0, PLAYER_WIDTH, PLAYER_HEIGHT) } @@ -188,7 +187,7 @@ class StorageOverlayScreen : Screen(Text.literal("")) { items.withIndex().forEach { (index, item) -> val (x, y) = getPlayerInventorySlotPosition(index) context.drawItem(item, x, y, 0) - context.drawItemInSlot(textRenderer, item, x, y) + context.drawStackOverlay(textRenderer, item, x, y) } } @@ -357,7 +356,7 @@ class StorageOverlayScreen : Screen(Text.literal("")) { val slotY = (index / 9) * SLOT_SIZE + y + 4 + textRenderer.fontHeight + 1 if (slots == null) { context.drawItem(stack, slotX, slotY) - context.drawItemInSlot(textRenderer, stack, slotX, slotY) + context.drawStackOverlay(textRenderer, stack, slotX, slotY) } else { val slot = slots[index] slot.x = slotX - slotOffset.x diff --git a/src/main/kotlin/features/inventory/storageoverlay/StorageOverviewScreen.kt b/src/main/kotlin/features/inventory/storageoverlay/StorageOverviewScreen.kt index 2cbd54e..9112fab 100644 --- a/src/main/kotlin/features/inventory/storageoverlay/StorageOverviewScreen.kt +++ b/src/main/kotlin/features/inventory/storageoverlay/StorageOverviewScreen.kt @@ -111,7 +111,7 @@ class StorageOverviewScreen() : Screen(Text.empty()) { context.fill(x, y, x + 18, y + 18, 0x40808080.toInt()) } context.drawItem(stack, x + 1, y + 1) - context.drawItemInSlot(MC.font, stack, x + 1, y + 1) + context.drawStackOverlay(MC.font, stack, x + 1, y + 1) } } diff --git a/src/main/kotlin/features/mining/CommissionFeatures.kt b/src/main/kotlin/features/mining/CommissionFeatures.kt index d0acdfd..faba253 100644 --- a/src/main/kotlin/features/mining/CommissionFeatures.kt +++ b/src/main/kotlin/features/mining/CommissionFeatures.kt @@ -1,6 +1,6 @@ package moe.nea.firmament.features.mining -import net.minecraft.util.Identifier +import moe.nea.firmament.Firmament import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.events.SlotRenderEvents import moe.nea.firmament.gui.config.ManagedConfig @@ -19,10 +19,8 @@ object CommissionFeatures { if (!Config.highlightCompletedCommissions) return if (MC.screenName != "Commissions") return val stack = event.slot.stack - if(stack.loreAccordingToNbt.any { it.unformattedString == "COMPLETED" }) { - event.highlight( - MC.guiAtlasManager.getSprite(Identifier.of("firmament:completed_commission_background")) - ) + if (stack.loreAccordingToNbt.any { it.unformattedString == "COMPLETED" }) { + event.highlight(Firmament.identifier("completed_commission_background")) } } } diff --git a/src/main/kotlin/features/mining/HotmPresets.kt b/src/main/kotlin/features/mining/HotmPresets.kt index 3f83f3d..533aa1e 100644 --- a/src/main/kotlin/features/mining/HotmPresets.kt +++ b/src/main/kotlin/features/mining/HotmPresets.kt @@ -9,9 +9,7 @@ import net.minecraft.client.gui.screen.ingame.HandledScreen import net.minecraft.entity.player.PlayerInventory import net.minecraft.item.Items import net.minecraft.screen.GenericContainerScreenHandler -import net.minecraft.screen.ScreenHandler import net.minecraft.screen.slot.Slot -import net.minecraft.screen.slot.SlotActionType import net.minecraft.text.Text import moe.nea.firmament.Firmament import moe.nea.firmament.annotations.Subscribe @@ -31,6 +29,7 @@ import moe.nea.firmament.util.customgui.customGui import moe.nea.firmament.util.mc.CommonTextures import moe.nea.firmament.util.mc.SlotUtils.clickRightMouseButton import moe.nea.firmament.util.mc.displayNameAccordingToNbt +import moe.nea.firmament.util.render.drawGuiTexture import moe.nea.firmament.util.unformattedString import moe.nea.firmament.util.useMatch @@ -81,7 +80,7 @@ object HotmPresets { override fun render(drawContext: DrawContext, delta: Float, mouseX: Int, mouseY: Int) { drawContext.drawGuiTexture( CommonTextures.genericWidget(), - bounds.x, bounds.y, 0, + bounds.x, bounds.y, bounds.width, bounds.height, ) @@ -191,7 +190,7 @@ object HotmPresets { if (hotmInventoryName == MC.screenName && event.slot.stack.displayNameAccordingToNbt.unformattedString in highlightedPerks ) { - event.highlight(MC.guiAtlasManager.getSprite(Firmament.identifier("hotm_perk_preset"))) + event.highlight((Firmament.identifier("hotm_perk_preset"))) } } diff --git a/src/main/kotlin/features/texturepack/BakedModelExtra.kt b/src/main/kotlin/features/texturepack/BakedModelExtra.kt index 32f419a..6305748 100644 --- a/src/main/kotlin/features/texturepack/BakedModelExtra.kt +++ b/src/main/kotlin/features/texturepack/BakedModelExtra.kt @@ -1,11 +1,30 @@ - package moe.nea.firmament.features.texturepack +import net.fabricmc.fabric.api.renderer.v1.model.WrapperBakedModel as WrapperBakedModelFabric import net.minecraft.client.render.model.BakedModel +import net.minecraft.client.render.model.WrapperBakedModel +import moe.nea.firmament.util.ErrorUtil interface BakedModelExtra { + companion object { + @JvmStatic + fun cast(originalModel: BakedModel): BakedModelExtra? { + var p = originalModel + for (i in 0..256) { + p = when (p) { + is BakedModelExtra -> return p + is WrapperBakedModel -> p.wrapped + is WrapperBakedModelFabric -> WrapperBakedModelFabric.unwrap(p) + else -> break + } + } + ErrorUtil.softError("Could not find a baked model for $originalModel") + return null + } + } + var tintOverrides_firmament: TintOverrides? fun getHeadModel_firmament(): BakedModel? - fun setHeadModel_firmament(headModel: BakedModel?) + fun setHeadModel_firmament(headModel: BakedModel?) } diff --git a/src/main/kotlin/features/texturepack/CustomBlockTextures.kt b/src/main/kotlin/features/texturepack/CustomBlockTextures.kt index a149928..2f7f084 100644 --- a/src/main/kotlin/features/texturepack/CustomBlockTextures.kt +++ b/src/main/kotlin/features/texturepack/CustomBlockTextures.kt @@ -244,7 +244,7 @@ object CustomBlockTextures { .flatMap { it.lookup.values } .flatten() .mapTo(mutableSetOf()) { it.replacement.blockModelIdentifier } - .forEach { event.addNonItemModel(it) } + .forEach { event.addNonItemModel(it, it.id) } } private fun prepare(manager: ResourceManager): BakedReplacements { @@ -263,7 +263,7 @@ object CustomBlockTextures { val island = SkyBlockIsland.forMode(mode) val islandMpa = map.getOrPut(island, ::mutableMapOf) for ((blockId, replacement) in json.replacements) { - val block = MC.defaultRegistries.getWrapperOrThrow(RegistryKeys.BLOCK) + val block = MC.defaultRegistries.getOrThrow(RegistryKeys.BLOCK) .getOptional(RegistryKey.of(RegistryKeys.BLOCK, blockId)) .getOrNull() if (block == null) { diff --git a/src/main/kotlin/features/texturepack/CustomGlobalArmorOverrides.kt b/src/main/kotlin/features/texturepack/CustomGlobalArmorOverrides.kt index 7b6e62b..54e9e11 100644 --- a/src/main/kotlin/features/texturepack/CustomGlobalArmorOverrides.kt +++ b/src/main/kotlin/features/texturepack/CustomGlobalArmorOverrides.kt @@ -3,13 +3,13 @@ package moe.nea.firmament.features.texturepack import java.util.Optional +import java.util.concurrent.atomic.AtomicInteger import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import kotlinx.serialization.UseSerializers -import kotlin.jvm.optionals.getOrNull -import net.minecraft.item.ArmorMaterial import net.minecraft.item.ItemStack +import net.minecraft.item.equipment.EquipmentModel import net.minecraft.resource.ResourceManager import net.minecraft.resource.SinglePreparationResourceReloader import net.minecraft.util.Identifier @@ -17,90 +17,139 @@ import net.minecraft.util.profiler.Profiler import moe.nea.firmament.Firmament import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.events.FinalizeResourceManagerEvent -import moe.nea.firmament.events.subscription.SubscriptionOwner -import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.features.texturepack.CustomGlobalTextures.logger import moe.nea.firmament.util.IdentifierSerializer import moe.nea.firmament.util.collections.WeakCache import moe.nea.firmament.util.skyBlockId -object CustomGlobalArmorOverrides : SubscriptionOwner { - @Serializable - data class ArmorOverride( - @SerialName("item_ids") - val itemIds: List<String>, - val layers: List<ArmorOverrideLayer>, - val overrides: List<ArmorOverrideOverride> = listOf(), - ) { - @Transient - val bakedLayers = bakeLayers(layers) - } - - fun bakeLayers(layers: List<ArmorOverrideLayer>): List<ArmorMaterial.Layer> { - return layers.map { ArmorMaterial.Layer(it.identifier, it.suffix, it.tint) } - } - - @Serializable - data class ArmorOverrideLayer( - val tint: Boolean = false, - val identifier: Identifier, - val suffix: String = "", - ) - - @Serializable - data class ArmorOverrideOverride( - val predicate: FirmamentModelPredicate, - val layers: List<ArmorOverrideLayer>, - ) { - @Transient - val bakedLayers = bakeLayers(layers) - } - - override val delegateFeature: FirmamentFeature - get() = CustomSkyBlockTextures - - val overrideCache = WeakCache.memoize<ItemStack, Optional<List<ArmorMaterial.Layer>>>("ArmorOverrides") { stack -> - val id = stack.skyBlockId ?: return@memoize Optional.empty() - val override = overrides[id.neuItem] ?: return@memoize Optional.empty() - for (suboverride in override.overrides) { - if (suboverride.predicate.test(stack)) { - return@memoize Optional.of(suboverride.bakedLayers) - } - } - return@memoize Optional.of(override.bakedLayers) - } - - @JvmStatic - fun overrideArmor(stack: ItemStack): List<ArmorMaterial.Layer>? { - if (!CustomSkyBlockTextures.TConfig.enableArmorOverrides) return null - return overrideCache.invoke(stack).getOrNull() - } - - var overrides: Map<String, ArmorOverride> = mapOf() - - @Subscribe - fun onStart(event: FinalizeResourceManagerEvent) { - event.resourceManager.registerReloader(object : - SinglePreparationResourceReloader<Map<String, ArmorOverride>>() { - override fun prepare(manager: ResourceManager, profiler: Profiler): Map<String, ArmorOverride> { - val overrideFiles = manager.findResources("overrides/armor_models") { - it.namespace == "firmskyblock" && it.path.endsWith(".json") - } - val overrides = overrideFiles.mapNotNull { - Firmament.tryDecodeJsonFromStream<ArmorOverride>(it.value.inputStream).getOrElse { ex -> - logger.error("Failed to load armor texture override at ${it.key}", ex) - null - } - } - val associatedMap = overrides.flatMap { obj -> obj.itemIds.map { it to obj } } - .toMap() - return associatedMap - } - - override fun apply(prepared: Map<String, ArmorOverride>, manager: ResourceManager, profiler: Profiler) { - overrides = prepared - } - }) - } +object CustomGlobalArmorOverrides { + @Serializable + data class ArmorOverride( + @SerialName("item_ids") + val itemIds: List<String>, + val layers: List<ArmorOverrideLayer>? = null, + val model: Identifier? = null, + val overrides: List<ArmorOverrideOverride> = listOf(), + ) { + @Transient + lateinit var modelIdentifier: Identifier + fun bake() { + modelIdentifier = bakeModel(model, layers) + overrides.forEach { it.bake() } + } + + init { + require(layers != null || model != null) { "Either model or layers must be specified for armor override" } + require(layers == null || model == null) { "Can't specify both model and layers for armor override" } + } + } + + @Serializable + data class ArmorOverrideLayer( + val tint: Boolean = false, + val identifier: Identifier, + val suffix: String = "", + ) + + @Serializable + data class ArmorOverrideOverride( + val predicate: FirmamentModelPredicate, + val layers: List<ArmorOverrideLayer>? = null, + val model: Identifier? = null, + ) { + init { + require(layers != null || model != null) { "Either model or layers must be specified for armor override override" } + require(layers == null || model == null) { "Can't specify both model and layers for armor override override" } + } + + @Transient + lateinit var modelIdentifier: Identifier + fun bake() { + modelIdentifier = bakeModel(model, layers) + } + } + + + val overrideCache = WeakCache.memoize<ItemStack, Optional<Identifier>>("ArmorOverrides") { stack -> + val id = stack.skyBlockId ?: return@memoize Optional.empty() + val override = overrides[id.neuItem] ?: return@memoize Optional.empty() + for (suboverride in override.overrides) { + if (suboverride.predicate.test(stack)) { + return@memoize Optional.of(suboverride.modelIdentifier) + } + } + return@memoize Optional.of(override.modelIdentifier) + } + + var overrides: Map<String, ArmorOverride> = mapOf() + private var bakedOverrides: MutableMap<Identifier, EquipmentModel> = mutableMapOf() + private val sentinelFirmRunning = AtomicInteger() + + private fun bakeModel(model: Identifier?, layers: List<ArmorOverrideLayer>?): Identifier { + require(model == null || layers == null) + if (model != null) { + return model + } else if (layers != null) { + val idNumber = sentinelFirmRunning.incrementAndGet() + val identifier = Identifier.of("firmament:sentinel/$idNumber") + val equipmentLayers = layers.map { + EquipmentModel.Layer( + it.identifier, if (it.tint) { + Optional.of(EquipmentModel.Dyeable(Optional.empty())) + } else { + Optional.empty() + }, + false + ) + } + bakedOverrides[identifier] = EquipmentModel( + mapOf( + EquipmentModel.LayerType.HUMANOID to equipmentLayers, + EquipmentModel.LayerType.HUMANOID_LEGGINGS to equipmentLayers, + ) + ) + return identifier + } else { + error("Either model or layers must be non null") + } + } + + + @Subscribe + fun onStart(event: FinalizeResourceManagerEvent) { + event.resourceManager.registerReloader(object : + SinglePreparationResourceReloader<Map<String, ArmorOverride>>() { + override fun prepare(manager: ResourceManager, profiler: Profiler): Map<String, ArmorOverride> { + val overrideFiles = manager.findResources("overrides/armor_models") { + it.namespace == "firmskyblock" && it.path.endsWith(".json") + } + val overrides = overrideFiles.mapNotNull { + Firmament.tryDecodeJsonFromStream<ArmorOverride>(it.value.inputStream).getOrElse { ex -> + logger.error("Failed to load armor texture override at ${it.key}", ex) + null + } + } + val associatedMap = overrides.flatMap { obj -> obj.itemIds.map { it to obj } } + .toMap() + return associatedMap + } + + override fun apply(prepared: Map<String, ArmorOverride>, manager: ResourceManager, profiler: Profiler) { + bakedOverrides.clear() + prepared.forEach { it.value.bake() } + overrides = prepared + } + }) + } + + @JvmStatic + fun overrideArmor(itemStack: ItemStack): Optional<Identifier> { + return overrideCache.invoke(itemStack) + } + + @JvmStatic + fun overrideArmorLayer(id: Identifier): EquipmentModel? { + return bakedOverrides[id] + } } diff --git a/src/main/kotlin/features/texturepack/CustomGlobalTextures.kt b/src/main/kotlin/features/texturepack/CustomGlobalTextures.kt index 2771699..a1203df 100644 --- a/src/main/kotlin/features/texturepack/CustomGlobalTextures.kt +++ b/src/main/kotlin/features/texturepack/CustomGlobalTextures.kt @@ -146,7 +146,7 @@ object CustomGlobalTextures : SinglePreparationResourceReloader<CustomGlobalText it.overrides .asSequence() .filter { it.predicate.test(stack) } - .map { models.modelManager.getModel(ModelIdentifier(it.model, "inventory")) } + .map { models.getModel(it.model) } .firstOrNull() } .intoOptional() diff --git a/src/main/kotlin/features/texturepack/JsonUnbakedModelFirmExtra.kt b/src/main/kotlin/features/texturepack/JsonUnbakedModelFirmExtra.kt index 0d0f8f2..9f641b8 100644 --- a/src/main/kotlin/features/texturepack/JsonUnbakedModelFirmExtra.kt +++ b/src/main/kotlin/features/texturepack/JsonUnbakedModelFirmExtra.kt @@ -1,9 +1,11 @@ package moe.nea.firmament.features.texturepack +import net.minecraft.client.render.model.Baker import net.minecraft.util.Identifier interface JsonUnbakedModelFirmExtra { + fun storeExtraBaker_firmament(baker: Baker) fun setHeadModel_firmament(identifier: Identifier?) fun getHeadModel_firmament(): Identifier? diff --git a/src/main/kotlin/features/texturepack/TintOverrides.kt b/src/main/kotlin/features/texturepack/TintOverrides.kt index 8006db8..85fcae4 100644 --- a/src/main/kotlin/features/texturepack/TintOverrides.kt +++ b/src/main/kotlin/features/texturepack/TintOverrides.kt @@ -3,7 +3,6 @@ package moe.nea.firmament.features.texturepack import com.google.gson.JsonObject import com.google.gson.JsonPrimitive import moe.nea.firmament.util.ErrorUtil -import moe.nea.firmament.util.assertNotNullOr data class TintOverrides( val layerMap: Map<Int, TintOverride> = mapOf() @@ -14,21 +13,22 @@ data class TintOverrides( val EMPTY = TintOverrides() private val threadLocal = object : ThreadLocal<TintOverrides>() {} fun enter(overrides: TintOverrides?) { - ErrorUtil.softCheck("Double entered tintOverrides") { - threadLocal.get() == null - } + ErrorUtil.softCheck("Double entered tintOverrides", + threadLocal.get() == null) threadLocal.set(overrides ?: EMPTY) } fun exit(overrides: TintOverrides?) { - ErrorUtil.softCheck("Exited with non matching enter tintOverrides") { - threadLocal.get() == (overrides ?: EMPTY) - } + ErrorUtil.softCheck("Exited with non matching enter tintOverrides", + threadLocal.get() == (overrides ?: EMPTY)) threadLocal.remove() } - fun getCurrentOverrides() = - assertNotNullOr(threadLocal.get(), "Got current tintOverrides without entering") { EMPTY } + fun getCurrentOverrides(): TintOverrides { + return ErrorUtil.notNullOr(threadLocal.get(), "Got current tintOverrides without entering") { + EMPTY + } + } fun parse(jsonObject: JsonObject): TintOverrides { val map = mutableMapOf<Int, TintOverride>() diff --git a/src/main/kotlin/features/world/FairySouls.kt b/src/main/kotlin/features/world/FairySouls.kt index eec9b04..1263074 100644 --- a/src/main/kotlin/features/world/FairySouls.kt +++ b/src/main/kotlin/features/world/FairySouls.kt @@ -6,6 +6,7 @@ import io.github.moulberry.repo.data.Coordinate import kotlinx.serialization.Serializable import kotlinx.serialization.serializer import net.minecraft.text.Text +import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3d import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.events.ProcessChatEvent @@ -98,9 +99,8 @@ object FairySouls : FirmamentFeature { fun onWorldRender(it: WorldRenderLastEvent) { if (!TConfig.displaySouls) return renderInWorld(it) { - color(1F, 1F, 0F, 0.8F) currentMissingSouls.forEach { - block(it.blockPos) + block(it.blockPos, 0x80FFFF00.toInt()) } color(1f, 0f, 1f, 1f) currentLocationSouls.forEach { diff --git a/src/main/kotlin/features/world/Waypoints.kt b/src/main/kotlin/features/world/Waypoints.kt index d535b4e..16db059 100644 --- a/src/main/kotlin/features/world/Waypoints.kt +++ b/src/main/kotlin/features/world/Waypoints.kt @@ -1,5 +1,3 @@ - - package moe.nea.firmament.features.world import com.mojang.brigadier.arguments.IntegerArgumentType @@ -12,6 +10,7 @@ import kotlin.collections.set import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.seconds import net.minecraft.command.argument.BlockPosArgumentType +import net.minecraft.server.command.CommandOutput import net.minecraft.server.command.ServerCommandSource import net.minecraft.text.Text import net.minecraft.util.math.BlockPos @@ -35,263 +34,273 @@ import moe.nea.firmament.util.TimeMark import moe.nea.firmament.util.render.RenderInWorldContext object Waypoints : FirmamentFeature { - override val identifier: String - get() = "waypoints" + override val identifier: String + get() = "waypoints" - object TConfig : ManagedConfig(identifier, Category.MINING) { // TODO: add to misc - val tempWaypointDuration by duration("temp-waypoint-duration", 0.seconds, 1.hours) { 30.seconds } - val showIndex by toggle("show-index") { true } - val skipToNearest by toggle("skip-to-nearest") { false } - // TODO: look ahead size - } + object TConfig : ManagedConfig(identifier, Category.MINING) { // TODO: add to misc + val tempWaypointDuration by duration("temp-waypoint-duration", 0.seconds, 1.hours) { 30.seconds } + val showIndex by toggle("show-index") { true } + val skipToNearest by toggle("skip-to-nearest") { false } + // TODO: look ahead size + } - data class TemporaryWaypoint( - val pos: BlockPos, - val postedAt: TimeMark, - ) + data class TemporaryWaypoint( + val pos: BlockPos, + val postedAt: TimeMark, + ) - override val config get() = TConfig + override val config get() = TConfig - val temporaryPlayerWaypointList = mutableMapOf<String, TemporaryWaypoint>() - val temporaryPlayerWaypointMatcher = "(?i)x: (-?[0-9]+),? y: (-?[0-9]+),? z: (-?[0-9]+)".toPattern() + val temporaryPlayerWaypointList = mutableMapOf<String, TemporaryWaypoint>() + val temporaryPlayerWaypointMatcher = "(?i)x: (-?[0-9]+),? y: (-?[0-9]+),? z: (-?[0-9]+)".toPattern() - val waypoints = mutableListOf<BlockPos>() - var ordered = false - var orderedIndex = 0 + val waypoints = mutableListOf<BlockPos>() + var ordered = false + var orderedIndex = 0 - @Serializable - data class ColeWeightWaypoint( - val x: Int, - val y: Int, - val z: Int, - val r: Int = 0, - val g: Int = 0, - val b: Int = 0, - ) + @Serializable + data class ColeWeightWaypoint( + val x: Int, + val y: Int, + val z: Int, + val r: Int = 0, + val g: Int = 0, + val b: Int = 0, + ) - @Subscribe - fun onRenderOrderedWaypoints(event: WorldRenderLastEvent) { - if (waypoints.isEmpty()) return - RenderInWorldContext.renderInWorld(event) { - if (!ordered) { - waypoints.withIndex().forEach { - color(0f, 0.3f, 0.7f, 0.5f) - block(it.value) - color(1f, 1f, 1f, 1f) - if (TConfig.showIndex) - withFacingThePlayer(it.value.toCenterPos()) { - text(Text.literal(it.index.toString())) - } - } - } else { - orderedIndex %= waypoints.size - val firstColor = Color.ofRGBA(0, 200, 40, 180) - color(firstColor) - tracer(waypoints[orderedIndex].toCenterPos(), lineWidth = 3f) - waypoints.withIndex().toList() - .wrappingWindow(orderedIndex, 3) - .zip( - listOf( - firstColor, - Color.ofRGBA(180, 200, 40, 150), - Color.ofRGBA(180, 80, 20, 140), - ) - ) - .reversed() - .forEach { (waypoint, col) -> - val (index, pos) = waypoint - color(col) - block(pos) - color(1f, 1f, 1f, 1f) - if (TConfig.showIndex) - withFacingThePlayer(pos.toCenterPos()) { - text(Text.literal(index.toString())) - } - } - } - } - } + @Subscribe + fun onRenderOrderedWaypoints(event: WorldRenderLastEvent) { + if (waypoints.isEmpty()) return + RenderInWorldContext.renderInWorld(event) { + if (!ordered) { + waypoints.withIndex().forEach { + block(it.value, 0x800050A0.toInt()) + if (TConfig.showIndex) + withFacingThePlayer(it.value.toCenterPos()) { + text(Text.literal(it.index.toString())) + } + } + } else { + orderedIndex %= waypoints.size + val firstColor = Color.ofRGBA(0, 200, 40, 180) + color(firstColor) + tracer(waypoints[orderedIndex].toCenterPos(), lineWidth = 3f) + waypoints.withIndex().toList() + .wrappingWindow(orderedIndex, 3) + .zip( + listOf( + firstColor, + Color.ofRGBA(180, 200, 40, 150), + Color.ofRGBA(180, 80, 20, 140), + ) + ) + .reversed() + .forEach { (waypoint, col) -> + val (index, pos) = waypoint + block(pos, col.color) + if (TConfig.showIndex) + withFacingThePlayer(pos.toCenterPos()) { + text(Text.literal(index.toString())) + } + } + } + } + } - @Subscribe - fun onTick(event: TickEvent) { - if (waypoints.isEmpty() || !ordered) return - orderedIndex %= waypoints.size - val p = MC.player?.pos ?: return - if (TConfig.skipToNearest) { - orderedIndex = - (waypoints.withIndex().minBy { it.value.getSquaredDistance(p) }.index + 1) % waypoints.size - } else { - if (waypoints[orderedIndex].isWithinDistance(p, 3.0)) { - orderedIndex = (orderedIndex + 1) % waypoints.size - } - } - } + @Subscribe + fun onTick(event: TickEvent) { + if (waypoints.isEmpty() || !ordered) return + orderedIndex %= waypoints.size + val p = MC.player?.pos ?: return + if (TConfig.skipToNearest) { + orderedIndex = + (waypoints.withIndex().minBy { it.value.getSquaredDistance(p) }.index + 1) % waypoints.size + } else { + if (waypoints[orderedIndex].isWithinDistance(p, 3.0)) { + orderedIndex = (orderedIndex + 1) % waypoints.size + } + } + } - @Subscribe - fun onProcessChat(it: ProcessChatEvent) { - val matcher = temporaryPlayerWaypointMatcher.matcher(it.unformattedString) - if (it.nameHeuristic != null && TConfig.tempWaypointDuration > 0.seconds && matcher.find()) { - temporaryPlayerWaypointList[it.nameHeuristic] = TemporaryWaypoint( - BlockPos( - matcher.group(1).toInt(), - matcher.group(2).toInt(), - matcher.group(3).toInt(), - ), - TimeMark.now() - ) - } - } + @Subscribe + fun onProcessChat(it: ProcessChatEvent) { + val matcher = temporaryPlayerWaypointMatcher.matcher(it.unformattedString) + if (it.nameHeuristic != null && TConfig.tempWaypointDuration > 0.seconds && matcher.find()) { + temporaryPlayerWaypointList[it.nameHeuristic] = TemporaryWaypoint( + BlockPos( + matcher.group(1).toInt(), + matcher.group(2).toInt(), + matcher.group(3).toInt(), + ), + TimeMark.now() + ) + } + } - @Subscribe - fun onCommand(event: CommandEvent.SubCommand) { - event.subcommand("waypoint") { - thenArgument("pos", BlockPosArgumentType.blockPos()) { pos -> - thenExecute { - val position = pos.get(this).toAbsoluteBlockPos(source.asFakeServer()) - waypoints.add(position) - source.sendFeedback( - Text.stringifiedTranslatable( - "firmament.command.waypoint.added", - position.x, - position.y, - position.z - ) - ) - } - } - } - event.subcommand("waypoints") { - thenLiteral("clear") { - thenExecute { - waypoints.clear() - source.sendFeedback(Text.translatable("firmament.command.waypoint.clear")) - } - } - thenLiteral("toggleordered") { - thenExecute { - ordered = !ordered - if (ordered) { - val p = MC.player?.pos ?: Vec3d.ZERO - orderedIndex = - waypoints.withIndex().minByOrNull { it.value.getSquaredDistance(p) }?.index ?: 0 - } - source.sendFeedback(Text.translatable("firmament.command.waypoint.ordered.toggle.$ordered")) - } - } - thenLiteral("skip") { - thenExecute { - if (ordered && waypoints.isNotEmpty()) { - orderedIndex = (orderedIndex + 1) % waypoints.size - source.sendFeedback(Text.translatable("firmament.command.waypoint.skip")) - } else { - source.sendError(Text.translatable("firmament.command.waypoint.skip.error")) - } - } - } - thenLiteral("remove") { - thenArgument("index", IntegerArgumentType.integer(0)) { indexArg -> - thenExecute { - val index = get(indexArg) - if (index in waypoints.indices) { - waypoints.removeAt(index) - source.sendFeedback(Text.stringifiedTranslatable( - "firmament.command.waypoint.remove", - index)) - } else { - source.sendError(Text.stringifiedTranslatable("firmament.command.waypoint.remove.error")) - } - } - } - } - thenLiteral("import") { - thenExecute { - val contents = ClipboardUtils.getTextContents() - val data = try { - Firmament.json.decodeFromString<List<ColeWeightWaypoint>>(contents) - } catch (ex: Exception) { - Firmament.logger.error("Could not load waypoints from clipboard", ex) - source.sendError(Text.translatable("firmament.command.waypoint.import.error")) - return@thenExecute - } - waypoints.clear() - data.mapTo(waypoints) { BlockPos(it.x, it.y, it.z) } - source.sendFeedback( - Text.stringifiedTranslatable( - "firmament.command.waypoint.import", - data.size - ) - ) - } - } - } - } + @Subscribe + fun onCommand(event: CommandEvent.SubCommand) { + event.subcommand("waypoint") { + thenArgument("pos", BlockPosArgumentType.blockPos()) { pos -> + thenExecute { + val position = pos.get(this).toAbsoluteBlockPos(source.asFakeServer()) + waypoints.add(position) + source.sendFeedback( + Text.stringifiedTranslatable( + "firmament.command.waypoint.added", + position.x, + position.y, + position.z + ) + ) + } + } + } + event.subcommand("waypoints") { + thenLiteral("clear") { + thenExecute { + waypoints.clear() + source.sendFeedback(Text.translatable("firmament.command.waypoint.clear")) + } + } + thenLiteral("toggleordered") { + thenExecute { + ordered = !ordered + if (ordered) { + val p = MC.player?.pos ?: Vec3d.ZERO + orderedIndex = + waypoints.withIndex().minByOrNull { it.value.getSquaredDistance(p) }?.index ?: 0 + } + source.sendFeedback(Text.translatable("firmament.command.waypoint.ordered.toggle.$ordered")) + } + } + thenLiteral("skip") { + thenExecute { + if (ordered && waypoints.isNotEmpty()) { + orderedIndex = (orderedIndex + 1) % waypoints.size + source.sendFeedback(Text.translatable("firmament.command.waypoint.skip")) + } else { + source.sendError(Text.translatable("firmament.command.waypoint.skip.error")) + } + } + } + thenLiteral("remove") { + thenArgument("index", IntegerArgumentType.integer(0)) { indexArg -> + thenExecute { + val index = get(indexArg) + if (index in waypoints.indices) { + waypoints.removeAt(index) + source.sendFeedback(Text.stringifiedTranslatable( + "firmament.command.waypoint.remove", + index)) + } else { + source.sendError(Text.stringifiedTranslatable("firmament.command.waypoint.remove.error")) + } + } + } + } + thenLiteral("import") { + thenExecute { + val contents = ClipboardUtils.getTextContents() + val data = try { + Firmament.json.decodeFromString<List<ColeWeightWaypoint>>(contents) + } catch (ex: Exception) { + Firmament.logger.error("Could not load waypoints from clipboard", ex) + source.sendError(Text.translatable("firmament.command.waypoint.import.error")) + return@thenExecute + } + waypoints.clear() + data.mapTo(waypoints) { BlockPos(it.x, it.y, it.z) } + source.sendFeedback( + Text.stringifiedTranslatable( + "firmament.command.waypoint.import", + data.size + ) + ) + } + } + } + } - @Subscribe - fun onRenderTemporaryWaypoints(event: WorldRenderLastEvent) { - temporaryPlayerWaypointList.entries.removeIf { it.value.postedAt.passedTime() > TConfig.tempWaypointDuration } - if (temporaryPlayerWaypointList.isEmpty()) return - RenderInWorldContext.renderInWorld(event) { - color(1f, 1f, 0f, 1f) - temporaryPlayerWaypointList.forEach { (player, waypoint) -> - block(waypoint.pos) - } - color(1f, 1f, 1f, 1f) - temporaryPlayerWaypointList.forEach { (player, waypoint) -> - val skin = - MC.networkHandler?.listedPlayerListEntries?.find { it.profile.name == player } - ?.skinTextures - ?.texture - withFacingThePlayer(waypoint.pos.toCenterPos()) { - waypoint(waypoint.pos, Text.stringifiedTranslatable("firmament.waypoint.temporary", player)) - if (skin != null) { - matrixStack.translate(0F, -20F, 0F) - // Head front - texture( - skin, 16, 16, - 1 / 8f, 1 / 8f, - 2 / 8f, 2 / 8f, - ) - // Head overlay - texture( - skin, 16, 16, - 5 / 8f, 1 / 8f, - 6 / 8f, 2 / 8f, - ) - } - } - } - } - } + @Subscribe + fun onRenderTemporaryWaypoints(event: WorldRenderLastEvent) { + temporaryPlayerWaypointList.entries.removeIf { it.value.postedAt.passedTime() > TConfig.tempWaypointDuration } + if (temporaryPlayerWaypointList.isEmpty()) return + RenderInWorldContext.renderInWorld(event) { + temporaryPlayerWaypointList.forEach { (player, waypoint) -> + block(waypoint.pos, 0xFFFFFF00.toInt()) + } + temporaryPlayerWaypointList.forEach { (player, waypoint) -> + val skin = + MC.networkHandler?.listedPlayerListEntries?.find { it.profile.name == player } + ?.skinTextures + ?.texture + withFacingThePlayer(waypoint.pos.toCenterPos()) { + waypoint(waypoint.pos, Text.stringifiedTranslatable("firmament.waypoint.temporary", player)) + if (skin != null) { + matrixStack.translate(0F, -20F, 0F) + // Head front + texture( + skin, 16, 16, + 1 / 8f, 1 / 8f, + 2 / 8f, 2 / 8f, + ) + // Head overlay + texture( + skin, 16, 16, + 5 / 8f, 1 / 8f, + 6 / 8f, 2 / 8f, + ) + } + } + } + } + } - @Subscribe - fun onWorldReady(event: WorldReadyEvent) { - temporaryPlayerWaypointList.clear() - } + @Subscribe + fun onWorldReady(event: WorldReadyEvent) { + temporaryPlayerWaypointList.clear() + } } fun <E> List<E>.wrappingWindow(startIndex: Int, windowSize: Int): List<E> { - val result = ArrayList<E>(windowSize) - if (startIndex + windowSize < size) { - result.addAll(subList(startIndex, startIndex + windowSize)) - } else { - result.addAll(subList(startIndex, size)) - result.addAll(subList(0, minOf(windowSize - (size - startIndex), startIndex))) - } - return result + val result = ArrayList<E>(windowSize) + if (startIndex + windowSize < size) { + result.addAll(subList(startIndex, startIndex + windowSize)) + } else { + result.addAll(subList(startIndex, size)) + result.addAll(subList(0, minOf(windowSize - (size - startIndex), startIndex))) + } + return result } fun FabricClientCommandSource.asFakeServer(): ServerCommandSource { - val source = this - return ServerCommandSource( - source.player, - source.position, - source.rotation, - null, - 0, - "FakeServerCommandSource", - Text.literal("FakeServerCommandSource"), - null, - source.player - ) + val source = this + return ServerCommandSource( + object : CommandOutput { + override fun sendMessage(message: Text?) { + source.player.sendMessage(message, false) + } + + override fun shouldReceiveFeedback(): Boolean { + return true + } + + override fun shouldTrackOutput(): Boolean { + return true + } + + override fun shouldBroadcastConsoleToOps(): Boolean { + return true + } + }, + source.position, + source.rotation, + null, + 0, + "FakeServerCommandSource", + Text.literal("FakeServerCommandSource"), + null, + source.player + ) } diff --git a/src/main/kotlin/gui/BarComponent.kt b/src/main/kotlin/gui/BarComponent.kt index 8ef0753..b82c666 100644 --- a/src/main/kotlin/gui/BarComponent.kt +++ b/src/main/kotlin/gui/BarComponent.kt @@ -1,4 +1,3 @@ - package moe.nea.firmament.gui import com.mojang.blaze3d.systems.RenderSystem @@ -10,116 +9,115 @@ import io.github.notenoughupdates.moulconfig.observer.GetSetter import io.github.notenoughupdates.moulconfig.platform.ModernRenderContext import me.shedaniel.math.Color import net.minecraft.client.gui.DrawContext +import net.minecraft.client.render.RenderLayer import net.minecraft.util.Identifier import moe.nea.firmament.Firmament class BarComponent( - val progress: GetSetter<Double>, val total: GetSetter<Double>, - val fillColor: Color, - val emptyColor: Color, + val progress: GetSetter<Double>, val total: GetSetter<Double>, + val fillColor: Color, + val emptyColor: Color, ) : GuiComponent() { - override fun getWidth(): Int { - return 80 - } + override fun getWidth(): Int { + return 80 + } - override fun getHeight(): Int { - return 8 - } + override fun getHeight(): Int { + return 8 + } - data class Texture( - val identifier: Identifier, - val u1: Float, val v1: Float, - val u2: Float, val v2: Float, - ) { - fun draw(context: DrawContext, x: Int, y: Int, width: Int, height: Int, color: Color) { - context.drawTexturedQuad( - identifier, - x, y, x + width, x + height, 0, - u1, u2, v1, v2, - color.red / 255F, - color.green / 255F, - color.blue / 255F, - color.alpha / 255F, - ) - } - } + data class Texture( + val identifier: Identifier, + val u1: Float, val v1: Float, + val u2: Float, val v2: Float, + ) { + fun draw(context: DrawContext, x: Int, y: Int, width: Int, height: Int, color: Color) { + context.drawTexturedQuad( + RenderLayer::getGuiTextured, + identifier, + x, y, x + width, x + height, + u1, u2, v1, v2, + color.color + ) + } + } - companion object { - val resource = Firmament.identifier("textures/gui/bar.png") - val left = Texture(resource, 0 / 64F, 0 / 64F, 4 / 64F, 8 / 64F) - val middle = Texture(resource, 4 / 64F, 0 / 64F, 8 / 64F, 8 / 64F) - val right = Texture(resource, 8 / 64F, 0 / 64F, 12 / 64F, 8 / 64F) - val segmentOverlay = Texture(resource, 12 / 64F, 0 / 64F, 15 / 64F, 8 / 64F) - } + companion object { + val resource = Firmament.identifier("textures/gui/bar.png") + val left = Texture(resource, 0 / 64F, 0 / 64F, 4 / 64F, 8 / 64F) + val middle = Texture(resource, 4 / 64F, 0 / 64F, 8 / 64F, 8 / 64F) + val right = Texture(resource, 8 / 64F, 0 / 64F, 12 / 64F, 8 / 64F) + val segmentOverlay = Texture(resource, 12 / 64F, 0 / 64F, 15 / 64F, 8 / 64F) + } - private fun drawSection( - context: DrawContext, - texture: Texture, - x: Int, - y: Int, - width: Int, - sectionStart: Double, - sectionEnd: Double - ) { - if (sectionEnd < progress.get() && width == 4) { - texture.draw(context, x, y, 4, 8, fillColor) - return - } - if (sectionStart > progress.get() && width == 4) { - texture.draw(context, x, y, 4, 8, emptyColor) - return - } - val increasePerPixel = (sectionEnd - sectionStart) / width - var valueAtPixel = sectionStart - for (i in (0 until width)) { - val newTex = - Texture(texture.identifier, texture.u1 + i / 64F, texture.v1, texture.u1 + (i + 1) / 64F, texture.v2) - newTex.draw( - context, x + i, y, 1, 8, - if (valueAtPixel < progress.get()) fillColor else emptyColor - ) - valueAtPixel += increasePerPixel - } - } + private fun drawSection( + context: DrawContext, + texture: Texture, + x: Int, + y: Int, + width: Int, + sectionStart: Double, + sectionEnd: Double + ) { + if (sectionEnd < progress.get() && width == 4) { + texture.draw(context, x, y, 4, 8, fillColor) + return + } + if (sectionStart > progress.get() && width == 4) { + texture.draw(context, x, y, 4, 8, emptyColor) + return + } + val increasePerPixel = (sectionEnd - sectionStart) / width + var valueAtPixel = sectionStart + for (i in (0 until width)) { + val newTex = + Texture(texture.identifier, texture.u1 + i / 64F, texture.v1, texture.u1 + (i + 1) / 64F, texture.v2) + newTex.draw( + context, x + i, y, 1, 8, + if (valueAtPixel < progress.get()) fillColor else emptyColor + ) + valueAtPixel += increasePerPixel + } + } - override fun render(context: GuiImmediateContext) { - val renderContext = (context.renderContext as ModernRenderContext).drawContext - var i = 0 - val x = 0 - val y = 0 - while (i < context.width - 4) { - drawSection( - renderContext, - if (i == 0) left else middle, - x + i, y, - (context.width - (i + 4)).coerceAtMost(4), - i * total.get() / context.width, (i + 4) * total.get() / context.width - ) - i += 4 - } - drawSection( - renderContext, - right, - x + context.width - 4, - y, - 4, - (context.width - 4) * total.get() / context.width, - total.get() - ) - RenderSystem.setShaderColor(1F, 1F, 1F, 1F) + override fun render(context: GuiImmediateContext) { + val renderContext = (context.renderContext as ModernRenderContext).drawContext + var i = 0 + val x = 0 + val y = 0 + while (i < context.width - 4) { + drawSection( + renderContext, + if (i == 0) left else middle, + x + i, y, + (context.width - (i + 4)).coerceAtMost(4), + i * total.get() / context.width, (i + 4) * total.get() / context.width + ) + i += 4 + } + drawSection( + renderContext, + right, + x + context.width - 4, + y, + 4, + (context.width - 4) * total.get() / context.width, + total.get() + ) + RenderSystem.setShaderColor(1F, 1F, 1F, 1F) - } + } } fun Identifier.toMoulConfig(): MyResourceLocation { - return MyResourceLocation(this.namespace, this.path) + return MyResourceLocation(this.namespace, this.path) } fun RenderContext.color(color: Color) { - color(color.red, color.green, color.blue, color.alpha) + color(color.red, color.green, color.blue, color.alpha) } fun RenderContext.color(red: Int, green: Int, blue: Int, alpha: Int) { - color(red / 255f, green / 255f, blue / 255f, alpha / 255f) + color(red / 255f, green / 255f, blue / 255f, alpha / 255f) } diff --git a/src/main/kotlin/gui/entity/EntityRenderer.kt b/src/main/kotlin/gui/entity/EntityRenderer.kt index 8c7428d..ddb862f 100644 --- a/src/main/kotlin/gui/entity/EntityRenderer.kt +++ b/src/main/kotlin/gui/entity/EntityRenderer.kt @@ -1,4 +1,3 @@ - package moe.nea.firmament.gui.entity import com.google.gson.Gson @@ -13,7 +12,9 @@ import net.minecraft.client.gui.screen.ingame.InventoryScreen import net.minecraft.entity.Entity import net.minecraft.entity.EntityType import net.minecraft.entity.LivingEntity +import net.minecraft.entity.SpawnReason import net.minecraft.util.Identifier +import net.minecraft.world.World import moe.nea.firmament.util.MC import moe.nea.firmament.util.assertNotNullOr import moe.nea.firmament.util.iterate @@ -21,177 +22,177 @@ import moe.nea.firmament.util.openFirmamentResource import moe.nea.firmament.util.render.enableScissorWithTranslation object EntityRenderer { - val fakeWorld = FakeWorld() - private fun <T : Entity> t(entityType: EntityType<T>): () -> T { - return { entityType.create(fakeWorld)!! } - } + val fakeWorld: World get() = MC.lastWorld!! + private fun <T : Entity> t(entityType: EntityType<T>): () -> T { + return { entityType.create(fakeWorld, SpawnReason.LOAD)!! } + } - val entityIds: Map<String, () -> LivingEntity> = mapOf( - "Zombie" to t(EntityType.ZOMBIE), - "Chicken" to t(EntityType.CHICKEN), - "Slime" to t(EntityType.SLIME), - "Wolf" to t(EntityType.WOLF), - "Skeleton" to t(EntityType.SKELETON), - "Creeper" to t(EntityType.CREEPER), - "Ocelot" to t(EntityType.OCELOT), - "Blaze" to t(EntityType.BLAZE), - "Rabbit" to t(EntityType.RABBIT), - "Sheep" to t(EntityType.SHEEP), - "Horse" to t(EntityType.HORSE), - "Eisengolem" to t(EntityType.IRON_GOLEM), - "Silverfish" to t(EntityType.SILVERFISH), - "Witch" to t(EntityType.WITCH), - "Endermite" to t(EntityType.ENDERMITE), - "Snowman" to t(EntityType.SNOW_GOLEM), - "Villager" to t(EntityType.VILLAGER), - "Guardian" to t(EntityType.GUARDIAN), - "ArmorStand" to t(EntityType.ARMOR_STAND), - "Squid" to t(EntityType.SQUID), - "Bat" to t(EntityType.BAT), - "Spider" to t(EntityType.SPIDER), - "CaveSpider" to t(EntityType.CAVE_SPIDER), - "Pigman" to t(EntityType.ZOMBIFIED_PIGLIN), - "Ghast" to t(EntityType.GHAST), - "MagmaCube" to t(EntityType.MAGMA_CUBE), - "Wither" to t(EntityType.WITHER), - "Enderman" to t(EntityType.ENDERMAN), - "Mooshroom" to t(EntityType.MOOSHROOM), - "WitherSkeleton" to t(EntityType.WITHER_SKELETON), - "Cow" to t(EntityType.COW), - "Dragon" to t(EntityType.ENDER_DRAGON), - "Player" to { makeGuiPlayer(fakeWorld) }, - "Pig" to t(EntityType.PIG), - "Giant" to t(EntityType.GIANT), - ) - val entityModifiers: Map<String, EntityModifier> = mapOf( - "playerdata" to ModifyPlayerSkin, - "equipment" to ModifyEquipment, - "riding" to ModifyRiding, - "charged" to ModifyCharged, - "witherdata" to ModifyWither, - "invisible" to ModifyInvisible, - "age" to ModifyAge, - "horse" to ModifyHorse, - "name" to ModifyName, - ) + val entityIds: Map<String, () -> LivingEntity> = mapOf( + "Zombie" to t(EntityType.ZOMBIE), + "Chicken" to t(EntityType.CHICKEN), + "Slime" to t(EntityType.SLIME), + "Wolf" to t(EntityType.WOLF), + "Skeleton" to t(EntityType.SKELETON), + "Creeper" to t(EntityType.CREEPER), + "Ocelot" to t(EntityType.OCELOT), + "Blaze" to t(EntityType.BLAZE), + "Rabbit" to t(EntityType.RABBIT), + "Sheep" to t(EntityType.SHEEP), + "Horse" to t(EntityType.HORSE), + "Eisengolem" to t(EntityType.IRON_GOLEM), + "Silverfish" to t(EntityType.SILVERFISH), + "Witch" to t(EntityType.WITCH), + "Endermite" to t(EntityType.ENDERMITE), + "Snowman" to t(EntityType.SNOW_GOLEM), + "Villager" to t(EntityType.VILLAGER), + "Guardian" to t(EntityType.GUARDIAN), + "ArmorStand" to t(EntityType.ARMOR_STAND), + "Squid" to t(EntityType.SQUID), + "Bat" to t(EntityType.BAT), + "Spider" to t(EntityType.SPIDER), + "CaveSpider" to t(EntityType.CAVE_SPIDER), + "Pigman" to t(EntityType.ZOMBIFIED_PIGLIN), + "Ghast" to t(EntityType.GHAST), + "MagmaCube" to t(EntityType.MAGMA_CUBE), + "Wither" to t(EntityType.WITHER), + "Enderman" to t(EntityType.ENDERMAN), + "Mooshroom" to t(EntityType.MOOSHROOM), + "WitherSkeleton" to t(EntityType.WITHER_SKELETON), + "Cow" to t(EntityType.COW), + "Dragon" to t(EntityType.ENDER_DRAGON), + "Player" to { makeGuiPlayer(fakeWorld) }, + "Pig" to t(EntityType.PIG), + "Giant" to t(EntityType.GIANT), + ) + val entityModifiers: Map<String, EntityModifier> = mapOf( + "playerdata" to ModifyPlayerSkin, + "equipment" to ModifyEquipment, + "riding" to ModifyRiding, + "charged" to ModifyCharged, + "witherdata" to ModifyWither, + "invisible" to ModifyInvisible, + "age" to ModifyAge, + "horse" to ModifyHorse, + "name" to ModifyName, + ) - val logger = LogManager.getLogger("Firmament.Entity") - fun applyModifiers(entityId: String, modifiers: List<JsonObject>): LivingEntity? { - val entityType = assertNotNullOr(entityIds[entityId]) { - logger.error("Could not create entity with id $entityId") - return null - } - var entity = entityType() - for (modifierJson in modifiers) { - val modifier = assertNotNullOr(modifierJson["type"]?.asString?.let(entityModifiers::get)) { - logger.error("Unknown modifier $modifierJson") - return null - } - entity = modifier.apply(entity, modifierJson) - } - return entity - } + val logger = LogManager.getLogger("Firmament.Entity") + fun applyModifiers(entityId: String, modifiers: List<JsonObject>): LivingEntity? { + val entityType = assertNotNullOr(entityIds[entityId]) { + logger.error("Could not create entity with id $entityId") + return null + } + var entity = entityType() + for (modifierJson in modifiers) { + val modifier = assertNotNullOr(modifierJson["type"]?.asString?.let(entityModifiers::get)) { + logger.error("Unknown modifier $modifierJson") + return null + } + entity = modifier.apply(entity, modifierJson) + } + return entity + } - fun constructEntity(info: JsonObject): LivingEntity? { - val modifiers = (info["modifiers"] as JsonArray?)?.map { it.asJsonObject } ?: emptyList() - val entityType = assertNotNullOr(info["entity"]?.asString) { - logger.error("Missing entity type on entity object") - return null - } - return applyModifiers(entityType, modifiers) - } + fun constructEntity(info: JsonObject): LivingEntity? { + val modifiers = (info["modifiers"] as JsonArray?)?.map { it.asJsonObject } ?: emptyList() + val entityType = assertNotNullOr(info["entity"]?.asString) { + logger.error("Missing entity type on entity object") + return null + } + return applyModifiers(entityType, modifiers) + } - private val gson = Gson() - fun constructEntity(location: Identifier): LivingEntity? { - return constructEntity( - gson.fromJson( - location.openFirmamentResource().bufferedReader(), JsonObject::class.java - ) - ) - } + private val gson = Gson() + fun constructEntity(location: Identifier): LivingEntity? { + return constructEntity( + gson.fromJson( + location.openFirmamentResource().bufferedReader(), JsonObject::class.java + ) + ) + } - fun renderEntity( - entity: LivingEntity, - renderContext: DrawContext, - posX: Int, - posY: Int, - mouseX: Float, - mouseY: Float - ) { - var bottomOffset = 0.0F - var currentEntity = entity - val maxSize = entity.iterate { it.firstPassenger as? LivingEntity } - .map { it.height } - .sum() - while (true) { - currentEntity.age = MC.player?.age ?: 0 - drawEntity( - renderContext, - posX, - posY, - posX + 50, - posY + 80, - minOf(2F / maxSize, 1F) * 30, - -bottomOffset, - mouseX, - mouseY, - currentEntity - ) - val next = currentEntity.firstPassenger as? LivingEntity ?: break - bottomOffset += currentEntity.getPassengerRidingPos(next).y.toFloat() * 0.75F - currentEntity = next - } - } + fun renderEntity( + entity: LivingEntity, + renderContext: DrawContext, + posX: Int, + posY: Int, + mouseX: Float, + mouseY: Float + ) { + var bottomOffset = 0.0F + var currentEntity = entity + val maxSize = entity.iterate { it.firstPassenger as? LivingEntity } + .map { it.height } + .sum() + while (true) { + currentEntity.age = MC.player?.age ?: 0 + drawEntity( + renderContext, + posX, + posY, + posX + 50, + posY + 80, + minOf(2F / maxSize, 1F) * 30, + -bottomOffset, + mouseX, + mouseY, + currentEntity + ) + val next = currentEntity.firstPassenger as? LivingEntity ?: break + bottomOffset += currentEntity.getPassengerRidingPos(next).y.toFloat() * 0.75F + currentEntity = next + } + } - fun drawEntity( - context: DrawContext, - x1: Int, - y1: Int, - x2: Int, - y2: Int, - size: Float, - bottomOffset: Float, - mouseX: Float, - mouseY: Float, - entity: LivingEntity - ) { - context.enableScissorWithTranslation(x1.toFloat(), y1.toFloat(), x2.toFloat(), y2.toFloat()) - val centerX = (x1 + x2) / 2f - val centerY = (y1 + y2) / 2f - val targetYaw = atan(((centerX - mouseX) / 40.0f).toDouble()).toFloat() - val targetPitch = atan(((centerY - mouseY) / 40.0f).toDouble()).toFloat() - val rotateToFaceTheFront = Quaternionf().rotateZ(Math.PI.toFloat()) - val rotateToFaceTheCamera = Quaternionf().rotateX(targetPitch * 20.0f * (Math.PI.toFloat() / 180)) - rotateToFaceTheFront.mul(rotateToFaceTheCamera) - val oldBodyYaw = entity.bodyYaw - val oldYaw = entity.yaw - val oldPitch = entity.pitch - val oldPrevHeadYaw = entity.prevHeadYaw - val oldHeadYaw = entity.headYaw - entity.bodyYaw = 180.0f + targetYaw * 20.0f - entity.yaw = 180.0f + targetYaw * 40.0f - entity.pitch = -targetPitch * 20.0f - entity.headYaw = entity.yaw - entity.prevHeadYaw = entity.yaw - val vector3f = Vector3f(0.0f, entity.height / 2.0f + bottomOffset, 0.0f) - InventoryScreen.drawEntity( - context, - centerX, - centerY, - size, - vector3f, - rotateToFaceTheFront, - rotateToFaceTheCamera, - entity - ) - entity.bodyYaw = oldBodyYaw - entity.yaw = oldYaw - entity.pitch = oldPitch - entity.prevHeadYaw = oldPrevHeadYaw - entity.headYaw = oldHeadYaw - context.disableScissor() - } + fun drawEntity( + context: DrawContext, + x1: Int, + y1: Int, + x2: Int, + y2: Int, + size: Float, + bottomOffset: Float, + mouseX: Float, + mouseY: Float, + entity: LivingEntity + ) { + context.enableScissorWithTranslation(x1.toFloat(), y1.toFloat(), x2.toFloat(), y2.toFloat()) + val centerX = (x1 + x2) / 2f + val centerY = (y1 + y2) / 2f + val targetYaw = atan(((centerX - mouseX) / 40.0f).toDouble()).toFloat() + val targetPitch = atan(((centerY - mouseY) / 40.0f).toDouble()).toFloat() + val rotateToFaceTheFront = Quaternionf().rotateZ(Math.PI.toFloat()) + val rotateToFaceTheCamera = Quaternionf().rotateX(targetPitch * 20.0f * (Math.PI.toFloat() / 180)) + rotateToFaceTheFront.mul(rotateToFaceTheCamera) + val oldBodyYaw = entity.bodyYaw + val oldYaw = entity.yaw + val oldPitch = entity.pitch + val oldPrevHeadYaw = entity.prevHeadYaw + val oldHeadYaw = entity.headYaw + entity.bodyYaw = 180.0f + targetYaw * 20.0f + entity.yaw = 180.0f + targetYaw * 40.0f + entity.pitch = -targetPitch * 20.0f + entity.headYaw = entity.yaw + entity.prevHeadYaw = entity.yaw + val vector3f = Vector3f(0.0f, entity.height / 2.0f + bottomOffset, 0.0f) + InventoryScreen.drawEntity( + context, + centerX, + centerY, + size, + vector3f, + rotateToFaceTheFront, + rotateToFaceTheCamera, + entity + ) + entity.bodyYaw = oldBodyYaw + entity.yaw = oldYaw + entity.pitch = oldPitch + entity.prevHeadYaw = oldPrevHeadYaw + entity.headYaw = oldHeadYaw + context.disableScissor() + } } diff --git a/src/main/kotlin/gui/entity/FakeWorld.kt b/src/main/kotlin/gui/entity/FakeWorld.kt index f354d5a..7ec385c 100644 --- a/src/main/kotlin/gui/entity/FakeWorld.kt +++ b/src/main/kotlin/gui/entity/FakeWorld.kt @@ -1,38 +1,37 @@ - package moe.nea.firmament.gui.entity -import com.mojang.datafixers.util.Pair -import com.mojang.serialization.Lifecycle -import java.util.* +import java.util.UUID import java.util.function.BooleanSupplier import java.util.function.Consumer -import java.util.stream.Stream -import kotlin.jvm.optionals.getOrNull -import kotlin.streams.asSequence import net.minecraft.block.Block import net.minecraft.block.BlockState +import net.minecraft.client.gui.screen.world.SelectWorldScreen import net.minecraft.component.type.MapIdComponent import net.minecraft.entity.Entity +import net.minecraft.entity.damage.DamageSource import net.minecraft.entity.player.PlayerEntity import net.minecraft.fluid.Fluid +import net.minecraft.item.FuelRegistry import net.minecraft.item.map.MapState +import net.minecraft.particle.ParticleEffect import net.minecraft.recipe.BrewingRecipeRegistry -import net.minecraft.recipe.Ingredient import net.minecraft.recipe.RecipeManager -import net.minecraft.registry.BuiltinRegistries +import net.minecraft.recipe.RecipePropertySet +import net.minecraft.recipe.StonecuttingRecipe +import net.minecraft.recipe.display.CuttingRecipeDisplay import net.minecraft.registry.DynamicRegistryManager -import net.minecraft.registry.Registry +import net.minecraft.registry.Registries import net.minecraft.registry.RegistryKey import net.minecraft.registry.RegistryKeys -import net.minecraft.registry.RegistryWrapper +import net.minecraft.registry.ServerDynamicRegistryType import net.minecraft.registry.entry.RegistryEntry -import net.minecraft.registry.entry.RegistryEntryInfo -import net.minecraft.registry.entry.RegistryEntryList -import net.minecraft.registry.entry.RegistryEntryOwner -import net.minecraft.registry.tag.TagKey +import net.minecraft.resource.DataConfiguration +import net.minecraft.resource.ResourcePackManager import net.minecraft.resource.featuretoggle.FeatureFlags import net.minecraft.resource.featuretoggle.FeatureSet import net.minecraft.scoreboard.Scoreboard +import net.minecraft.server.SaveLoading +import net.minecraft.server.command.CommandManager import net.minecraft.sound.SoundCategory import net.minecraft.sound.SoundEvent import net.minecraft.util.Identifier @@ -43,11 +42,8 @@ import net.minecraft.util.math.Box import net.minecraft.util.math.ChunkPos import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d -import net.minecraft.util.math.random.Random -import net.minecraft.util.profiler.DummyProfiler import net.minecraft.world.BlockView import net.minecraft.world.Difficulty -import net.minecraft.world.GameRules import net.minecraft.world.MutableWorldProperties import net.minecraft.world.World import net.minecraft.world.biome.Biome @@ -59,430 +55,284 @@ import net.minecraft.world.chunk.EmptyChunk import net.minecraft.world.chunk.light.LightingProvider import net.minecraft.world.entity.EntityLookup import net.minecraft.world.event.GameEvent +import net.minecraft.world.explosion.ExplosionBehavior import net.minecraft.world.tick.OrderedTick import net.minecraft.world.tick.QueryableTickScheduler import net.minecraft.world.tick.TickManager - -fun <T> makeRegistry(registryWrapper: RegistryWrapper.Impl<T>, key: RegistryKey<out Registry<T>>): Registry<T> { - val inverseLookup = registryWrapper.streamEntries() - .asSequence().map { it.value() to it.registryKey() } - .toMap() - val idLookup = registryWrapper.streamEntries() - .asSequence() - .map { it.registryKey() } - .withIndex() - .associate { it.value to it.index } - val map = registryWrapper.streamEntries().asSequence().map { it.registryKey() to it.value() }.toMap(mutableMapOf()) - val inverseIdLookup = idLookup.asIterable().associate { (k, v) -> v to k } - return object : Registry<T> { - override fun get(key: RegistryKey<T>?): T? { - return registryWrapper.getOptional(key).getOrNull()?.value() - } - - override fun iterator(): MutableIterator<T> { - return object : MutableIterator<T> { - val iterator = registryWrapper.streamEntries().iterator() - override fun hasNext(): Boolean { - return iterator.hasNext() - } - - override fun next(): T { - return iterator.next().value() - } - - override fun remove() { - TODO("Not yet implemented") - } - } - } - - override fun getRawId(value: T?): Int { - return idLookup[inverseLookup[value ?: return -1] ?: return -1] ?: return -1 - } - - override fun get(id: Identifier?): T? { - return get(RegistryKey.of(key, id)) - } - - override fun get(index: Int): T? { - return get(inverseIdLookup[index] ?: return null) - } - - override fun size(): Int { - return idLookup.size - } - - override fun getKey(): RegistryKey<out Registry<T>> { - return key - } - - override fun getEntryInfo(key: RegistryKey<T>?): Optional<RegistryEntryInfo> { - TODO("Not yet implemented") - } - - override fun getLifecycle(): Lifecycle { - return Lifecycle.stable() - } - - override fun getDefaultEntry(): Optional<RegistryEntry.Reference<T>> { - return Optional.empty() - } - - override fun getIds(): MutableSet<Identifier> { - return idLookup.keys.mapTo(mutableSetOf()) { it.value } - } - - override fun getEntrySet(): MutableSet<MutableMap.MutableEntry<RegistryKey<T>, T>> { - return map.entries - } - - override fun getKeys(): MutableSet<RegistryKey<T>> { - return map.keys - } - - override fun getRandom(random: Random?): Optional<RegistryEntry.Reference<T>> { - return registryWrapper.streamEntries().findFirst() - } - - override fun containsId(id: Identifier?): Boolean { - return idLookup.containsKey(RegistryKey.of(key, id ?: return false)) - } - - override fun freeze(): Registry<T> { - return this - } - - override fun getEntry(rawId: Int): Optional<RegistryEntry.Reference<T>> { - val x = inverseIdLookup[rawId] ?: return Optional.empty() - return Optional.of(RegistryEntry.Reference.standAlone(registryWrapper, x)) - } - - override fun streamEntries(): Stream<RegistryEntry.Reference<T>> { - return registryWrapper.streamEntries() - } - - override fun streamTagsAndEntries(): Stream<Pair<TagKey<T>, RegistryEntryList.Named<T>>> { - return streamTags().map { Pair(it, getOrCreateEntryList(it)) } - } - - override fun streamTags(): Stream<TagKey<T>> { - return registryWrapper.streamTagKeys() - } - - override fun clearTags() { - } - - override fun getEntryOwner(): RegistryEntryOwner<T> { - return registryWrapper - } - - override fun getReadOnlyWrapper(): RegistryWrapper.Impl<T> { - return registryWrapper - } - - override fun populateTags(tagEntries: MutableMap<TagKey<T>, MutableList<RegistryEntry<T>>>?) { - } - - override fun getOrCreateEntryList(tag: TagKey<T>?): RegistryEntryList.Named<T> { - return getEntryList(tag).orElseGet { RegistryEntryList.of(registryWrapper, tag) } - } - - override fun getEntryList(tag: TagKey<T>?): Optional<RegistryEntryList.Named<T>> { - return registryWrapper.getOptional(tag ?: return Optional.empty()) - } - - override fun getEntry(value: T): RegistryEntry<T> { - return registryWrapper.getOptional(inverseLookup[value]!!).get() - } - - override fun getEntry(key: RegistryKey<T>?): Optional<RegistryEntry.Reference<T>> { - return registryWrapper.getOptional(key ?: return Optional.empty()) - } - - override fun getEntry(id: Identifier?): Optional<RegistryEntry.Reference<T>> { - TODO("Not yet implemented") - } - - override fun createEntry(value: T): RegistryEntry.Reference<T> { - TODO("Not yet implemented") - } - - override fun contains(key: RegistryKey<T>?): Boolean { - return getEntry(key).isPresent - } - - override fun getId(value: T): Identifier? { - return (inverseLookup[value] ?: return null).value - } - - override fun getKey(entry: T): Optional<RegistryKey<T>> { - return Optional.ofNullable(inverseLookup[entry ?: return Optional.empty()]) - } - } -} +import moe.nea.firmament.util.MC fun createDynamicRegistry(): DynamicRegistryManager.Immutable { - val wrapperLookup = BuiltinRegistries.createWrapperLookup() - return object : DynamicRegistryManager.Immutable { - override fun <E : Any?> getOptional(key: RegistryKey<out Registry<out E>>): Optional<Registry<E>> { - val lookup = wrapperLookup.getOptionalWrapper(key).getOrNull() ?: return Optional.empty() - val registry = makeRegistry(lookup, key as RegistryKey<out Registry<E>>) - return Optional.of(registry) - } - - fun <T> entry(reg: RegistryKey<out Registry<T>>): DynamicRegistryManager.Entry<T> { - return DynamicRegistryManager.Entry(reg, getOptional(reg).get()) - } - - override fun streamAllRegistries(): Stream<DynamicRegistryManager.Entry<*>> { - return wrapperLookup.streamAllRegistryKeys() - .map { entry(it as RegistryKey<out Registry<Any>>) } - } - } + // TODO: use SaveLoading.load() to properly load a full registry + return DynamicRegistryManager.of(Registries.REGISTRIES) } class FakeWorld( - registries: DynamicRegistryManager.Immutable = createDynamicRegistry(), + registries: DynamicRegistryManager.Immutable = createDynamicRegistry(), ) : World( - Properties, - RegistryKey.of(RegistryKeys.WORLD, Identifier.of("firmament", "fakeworld")), - registries, - registries[RegistryKeys.DIMENSION_TYPE].entryOf( - RegistryKey.of( - RegistryKeys.DIMENSION_TYPE, - Identifier.of("minecraft", "overworld") - ) - ), - { DummyProfiler.INSTANCE }, - true, - false, - 0, 0 + Properties, + RegistryKey.of(RegistryKeys.WORLD, Identifier.of("firmament", "fakeworld")), + registries, + MC.defaultRegistries.getOrThrow(RegistryKeys.DIMENSION_TYPE) + .getOrThrow(RegistryKey.of(RegistryKeys.DIMENSION_TYPE, Identifier.of("minecraft", "overworld"))), + true, + false, + 0L, + 0 ) { - object Properties : MutableWorldProperties { - override fun getSpawnPos(): BlockPos { - return BlockPos.ORIGIN - } + object Properties : MutableWorldProperties { + override fun getSpawnPos(): BlockPos { + return BlockPos.ORIGIN + } - override fun getSpawnAngle(): Float { - return 0F - } + override fun getSpawnAngle(): Float { + return 0F + } - override fun getTime(): Long { - return 0 - } + override fun getTime(): Long { + return 0 + } - override fun getTimeOfDay(): Long { - return 0 - } + override fun getTimeOfDay(): Long { + return 0 + } - override fun isThundering(): Boolean { - return false - } + override fun isThundering(): Boolean { + return false + } - override fun isRaining(): Boolean { - return false - } + override fun isRaining(): Boolean { + return false + } - override fun setRaining(raining: Boolean) { - } + override fun setRaining(raining: Boolean) { + } - override fun isHardcore(): Boolean { - return false - } - - override fun getGameRules(): GameRules { - return GameRules() - } - - override fun getDifficulty(): Difficulty { - return Difficulty.HARD - } - - override fun isDifficultyLocked(): Boolean { - return false - } - - override fun setSpawnPos(pos: BlockPos?, angle: Float) {} - } + override fun isHardcore(): Boolean { + return false + } + + override fun getDifficulty(): Difficulty { + return Difficulty.HARD + } + + override fun isDifficultyLocked(): Boolean { + return false + } + + override fun setSpawnPos(pos: BlockPos?, angle: Float) {} + } + + override fun getPlayers(): List<PlayerEntity> { + return emptyList() + } - override fun getPlayers(): List<PlayerEntity> { - return emptyList() - } - - override fun getBrightness(direction: Direction?, shaded: Boolean): Float { - return 1f - } - - override fun getGeneratorStoredBiome(biomeX: Int, biomeY: Int, biomeZ: Int): RegistryEntry<Biome> { - return registryManager.get(RegistryKeys.BIOME).entryOf(BiomeKeys.PLAINS) - } - - override fun getEnabledFeatures(): FeatureSet { - return FeatureFlags.VANILLA_FEATURES - } - - class FakeTickScheduler<T> : QueryableTickScheduler<T> { - override fun scheduleTick(orderedTick: OrderedTick<T>?) { - } - - override fun isQueued(pos: BlockPos?, type: T): Boolean { - return true - } - - override fun getTickCount(): Int { - return 0 - } - - override fun isTicking(pos: BlockPos?, type: T): Boolean { - return true - } - - } - - override fun getBlockTickScheduler(): QueryableTickScheduler<Block> { - return FakeTickScheduler() - } - - override fun getFluidTickScheduler(): QueryableTickScheduler<Fluid> { - return FakeTickScheduler() - } - - - class FakeChunkManager(val world: FakeWorld) : ChunkManager() { - override fun getChunk(x: Int, z: Int, leastStatus: ChunkStatus?, create: Boolean): Chunk { - return EmptyChunk( - world, - ChunkPos(x, z), - world.registryManager.get(RegistryKeys.BIOME).entryOf(BiomeKeys.PLAINS) - ) - } - - override fun getWorld(): BlockView { - return world - } - - override fun tick(shouldKeepTicking: BooleanSupplier?, tickChunks: Boolean) { - } - - override fun getDebugString(): String { - return "FakeChunkManager" - } - - override fun getLoadedChunkCount(): Int { - return 0 - } - - override fun getLightingProvider(): LightingProvider { - return FakeLightingProvider(this) - } - } - - class FakeLightingProvider(chunkManager: FakeChunkManager) : LightingProvider(chunkManager, false, false) - - override fun getChunkManager(): ChunkManager { - return FakeChunkManager(this) - } - - override fun playSound( - source: PlayerEntity?, - x: Double, - y: Double, - z: Double, - sound: RegistryEntry<SoundEvent>?, - category: SoundCategory?, - volume: Float, - pitch: Float, - seed: Long - ) { - } - - override fun syncWorldEvent(player: PlayerEntity?, eventId: Int, pos: BlockPos?, data: Int) { - } - - override fun emitGameEvent(event: RegistryEntry<GameEvent>?, emitterPos: Vec3d?, emitter: GameEvent.Emitter?) { - } - - override fun updateListeners(pos: BlockPos?, oldState: BlockState?, newState: BlockState?, flags: Int) { - } - - override fun playSoundFromEntity( - source: PlayerEntity?, - entity: Entity?, - sound: RegistryEntry<SoundEvent>?, - category: SoundCategory?, - volume: Float, - pitch: Float, - seed: Long - ) { - } - - override fun asString(): String { - return "FakeWorld" - } - - override fun getEntityById(id: Int): Entity? { - return null - } - - override fun getTickManager(): TickManager { - return TickManager() - } - - override fun getMapState(id: MapIdComponent?): MapState? { - return null - } - - override fun putMapState(id: MapIdComponent?, state: MapState?) { - } - - override fun increaseAndGetMapId(): MapIdComponent { - return MapIdComponent(0) - } - - override fun setBlockBreakingInfo(entityId: Int, pos: BlockPos?, progress: Int) { - } - - override fun getScoreboard(): Scoreboard { - return Scoreboard() - } - - override fun getRecipeManager(): RecipeManager { - return RecipeManager(registryManager) - } - - object FakeEntityLookup : EntityLookup<Entity> { - override fun get(id: Int): Entity? { - return null - } - - override fun get(uuid: UUID?): Entity? { - return null - } - - override fun iterate(): MutableIterable<Entity> { - return mutableListOf() - } - - override fun <U : Entity?> forEachIntersects( - filter: TypeFilter<Entity, U>?, - box: Box?, - consumer: LazyIterationConsumer<U>? - ) { - } - - override fun forEachIntersects(box: Box?, action: Consumer<Entity>?) { - } - - override fun <U : Entity?> forEach(filter: TypeFilter<Entity, U>?, consumer: LazyIterationConsumer<U>?) { - } - - } - - override fun getEntityLookup(): EntityLookup<Entity> { - return FakeEntityLookup - } - - override fun getBrewingRecipeRegistry(): BrewingRecipeRegistry { - return BrewingRecipeRegistry.EMPTY - } + override fun getBrightness(direction: Direction?, shaded: Boolean): Float { + return 1f + } + + override fun getGeneratorStoredBiome(biomeX: Int, biomeY: Int, biomeZ: Int): RegistryEntry<Biome> { + return registryManager.getOptionalEntry(BiomeKeys.PLAINS).get() + } + + override fun getSeaLevel(): Int { + return 0 + } + + override fun getEnabledFeatures(): FeatureSet { + return FeatureFlags.VANILLA_FEATURES + } + + class FakeTickScheduler<T> : QueryableTickScheduler<T> { + override fun scheduleTick(orderedTick: OrderedTick<T>?) { + } + + override fun isQueued(pos: BlockPos?, type: T): Boolean { + return true + } + + override fun getTickCount(): Int { + return 0 + } + + override fun isTicking(pos: BlockPos?, type: T): Boolean { + return true + } + + } + + override fun getBlockTickScheduler(): QueryableTickScheduler<Block> { + return FakeTickScheduler() + } + + override fun getFluidTickScheduler(): QueryableTickScheduler<Fluid> { + return FakeTickScheduler() + } + + + class FakeChunkManager(val world: FakeWorld) : ChunkManager() { + override fun getChunk(x: Int, z: Int, leastStatus: ChunkStatus?, create: Boolean): Chunk { + return EmptyChunk( + world, + ChunkPos(x, z), + world.registryManager.getOptionalEntry(BiomeKeys.PLAINS).get() + ) + } + + override fun getWorld(): BlockView { + return world + } + + override fun tick(shouldKeepTicking: BooleanSupplier?, tickChunks: Boolean) { + } + + override fun getDebugString(): String { + return "FakeChunkManager" + } + + override fun getLoadedChunkCount(): Int { + return 0 + } + + override fun getLightingProvider(): LightingProvider { + return FakeLightingProvider(this) + } + } + + class FakeLightingProvider(chunkManager: FakeChunkManager) : LightingProvider(chunkManager, false, false) + + override fun getChunkManager(): ChunkManager { + return FakeChunkManager(this) + } + + override fun playSound( + source: PlayerEntity?, + x: Double, + y: Double, + z: Double, + sound: RegistryEntry<SoundEvent>?, + category: SoundCategory?, + volume: Float, + pitch: Float, + seed: Long + ) { + } + + override fun syncWorldEvent(player: PlayerEntity?, eventId: Int, pos: BlockPos?, data: Int) { + } + + override fun emitGameEvent(event: RegistryEntry<GameEvent>?, emitterPos: Vec3d?, emitter: GameEvent.Emitter?) { + } + + override fun updateListeners(pos: BlockPos?, oldState: BlockState?, newState: BlockState?, flags: Int) { + } + + override fun playSoundFromEntity( + source: PlayerEntity?, + entity: Entity?, + sound: RegistryEntry<SoundEvent>?, + category: SoundCategory?, + volume: Float, + pitch: Float, + seed: Long + ) { + } + + override fun createExplosion( + entity: Entity?, + damageSource: DamageSource?, + behavior: ExplosionBehavior?, + x: Double, + y: Double, + z: Double, + power: Float, + createFire: Boolean, + explosionSourceType: ExplosionSourceType?, + smallParticle: ParticleEffect?, + largeParticle: ParticleEffect?, + soundEvent: RegistryEntry<SoundEvent>? + ) { + TODO("Not yet implemented") + } + + override fun asString(): String { + return "FakeWorld" + } + + override fun getEntityById(id: Int): Entity? { + return null + } + + override fun getTickManager(): TickManager { + return TickManager() + } + + override fun getMapState(id: MapIdComponent?): MapState? { + return null + } + + override fun putMapState(id: MapIdComponent?, state: MapState?) { + } + + override fun increaseAndGetMapId(): MapIdComponent { + return MapIdComponent(0) + } + + override fun setBlockBreakingInfo(entityId: Int, pos: BlockPos?, progress: Int) { + } + + override fun getScoreboard(): Scoreboard { + return Scoreboard() + } + + override fun getRecipeManager(): RecipeManager { + return object : RecipeManager { + override fun getPropertySet(key: RegistryKey<RecipePropertySet>?): RecipePropertySet { + return RecipePropertySet.EMPTY + } + + override fun getStonecutterRecipes(): CuttingRecipeDisplay.Grouping<StonecuttingRecipe> { + return CuttingRecipeDisplay.Grouping.empty() + } + } + } + + object FakeEntityLookup : EntityLookup<Entity> { + override fun get(id: Int): Entity? { + return null + } + + override fun get(uuid: UUID?): Entity? { + return null + } + + override fun iterate(): MutableIterable<Entity> { + return mutableListOf() + } + + override fun <U : Entity?> forEachIntersects( + filter: TypeFilter<Entity, U>?, + box: Box?, + consumer: LazyIterationConsumer<U>? + ) { + } + + override fun forEachIntersects(box: Box?, action: Consumer<Entity>?) { + } + + override fun <U : Entity?> forEach(filter: TypeFilter<Entity, U>?, consumer: LazyIterationConsumer<U>?) { + } + + } + + override fun getEntityLookup(): EntityLookup<Entity> { + return FakeEntityLookup + } + + override fun getBrewingRecipeRegistry(): BrewingRecipeRegistry { + return BrewingRecipeRegistry.EMPTY + } + + override fun getFuelRegistry(): FuelRegistry { + TODO("Not yet implemented") + } } diff --git a/src/main/kotlin/gui/entity/GuiPlayer.kt b/src/main/kotlin/gui/entity/GuiPlayer.kt index d00b44d..aa0bea8 100644 --- a/src/main/kotlin/gui/entity/GuiPlayer.kt +++ b/src/main/kotlin/gui/entity/GuiPlayer.kt @@ -1,8 +1,7 @@ - package moe.nea.firmament.gui.entity import com.mojang.authlib.GameProfile -import java.util.* +import java.util.UUID import net.minecraft.client.network.AbstractClientPlayerEntity import net.minecraft.client.util.DefaultSkinHelper import net.minecraft.client.util.SkinTextures @@ -15,40 +14,40 @@ import net.minecraft.world.World /** * @see moe.nea.firmament.init.EarlyRiser */ -fun makeGuiPlayer(world: FakeWorld): GuiPlayer { - val constructor = GuiPlayer::class.java.getDeclaredConstructor( - World::class.java, - BlockPos::class.java, - Float::class.javaPrimitiveType, - GameProfile::class.java - ) - return constructor.newInstance(world, BlockPos.ORIGIN, 0F, GameProfile(UUID.randomUUID(), "Linnea")) +fun makeGuiPlayer(world: World): GuiPlayer { + val constructor = GuiPlayer::class.java.getDeclaredConstructor( + World::class.java, + BlockPos::class.java, + Float::class.javaPrimitiveType, + GameProfile::class.java + ) + return constructor.newInstance(world, BlockPos.ORIGIN, 0F, GameProfile(UUID.randomUUID(), "Linnea")) } class GuiPlayer(world: ClientWorld?, profile: GameProfile?) : AbstractClientPlayerEntity(world, profile) { - override fun isSpectator(): Boolean { - return false - } + override fun isSpectator(): Boolean { + return false + } - override fun isCreative(): Boolean { - return false - } + override fun isCreative(): Boolean { + return false + } - override fun shouldRenderName(): Boolean { - return false - } + override fun shouldRenderName(): Boolean { + return false + } - var skinTexture: Identifier = DefaultSkinHelper.getSkinTextures(this.getUuid()).texture - var capeTexture: Identifier? = null - var model: Model = Model.WIDE - override fun getSkinTextures(): SkinTextures { - return SkinTextures( - skinTexture, - null, - capeTexture, - null, - model, - true - ) - } + var skinTexture: Identifier = DefaultSkinHelper.getSkinTextures(this.getUuid()).texture + var capeTexture: Identifier? = null + var model: Model = Model.WIDE + override fun getSkinTextures(): SkinTextures { + return SkinTextures( + skinTexture, + null, + capeTexture, + null, + model, + true + ) + } } diff --git a/src/main/kotlin/gui/entity/ModifyHorse.kt b/src/main/kotlin/gui/entity/ModifyHorse.kt index 8ac011b..f094ca4 100644 --- a/src/main/kotlin/gui/entity/ModifyHorse.kt +++ b/src/main/kotlin/gui/entity/ModifyHorse.kt @@ -8,6 +8,7 @@ import kotlin.experimental.inv import kotlin.experimental.or import net.minecraft.entity.EntityType import net.minecraft.entity.LivingEntity +import net.minecraft.entity.SpawnReason import net.minecraft.entity.passive.AbstractHorseEntity import net.minecraft.item.ItemStack import net.minecraft.item.Items @@ -19,11 +20,11 @@ object ModifyHorse : EntityModifier { var entity: AbstractHorseEntity = entity info["kind"]?.let { entity = when (it.asString) { - "skeleton" -> EntityType.SKELETON_HORSE.create(fakeWorld)!! - "zombie" -> EntityType.ZOMBIE_HORSE.create(fakeWorld)!! - "mule" -> EntityType.MULE.create(fakeWorld)!! - "donkey" -> EntityType.DONKEY.create(fakeWorld)!! - "horse" -> EntityType.HORSE.create(fakeWorld)!! + "skeleton" -> EntityType.SKELETON_HORSE.create(fakeWorld, SpawnReason.LOAD)!! + "zombie" -> EntityType.ZOMBIE_HORSE.create(fakeWorld, SpawnReason.LOAD)!! + "mule" -> EntityType.MULE.create(fakeWorld, SpawnReason.LOAD)!! + "donkey" -> EntityType.DONKEY.create(fakeWorld, SpawnReason.LOAD)!! + "horse" -> EntityType.HORSE.create(fakeWorld, SpawnReason.LOAD)!! else -> error("Unknown horse kind $it") } } diff --git a/src/main/kotlin/repo/RepoDownloadManager.kt b/src/main/kotlin/repo/RepoDownloadManager.kt index d674f23..3efd83b 100644 --- a/src/main/kotlin/repo/RepoDownloadManager.kt +++ b/src/main/kotlin/repo/RepoDownloadManager.kt @@ -5,7 +5,7 @@ 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.jvm.nio.copyTo +import io.ktor.utils.io.copyTo import java.io.IOException import java.nio.file.Files import java.nio.file.Path diff --git a/src/main/kotlin/repo/RepoManager.kt b/src/main/kotlin/repo/RepoManager.kt index c052fb9..725642e 100644 --- a/src/main/kotlin/repo/RepoManager.kt +++ b/src/main/kotlin/repo/RepoManager.kt @@ -9,10 +9,12 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents import kotlinx.coroutines.launch import net.minecraft.client.MinecraftClient import net.minecraft.network.packet.s2c.play.SynchronizeRecipesS2CPacket +import net.minecraft.recipe.display.CuttingRecipeDisplay 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.MC import moe.nea.firmament.util.MinecraftDispatcher import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.tr @@ -77,7 +79,7 @@ object RepoManager { private fun trySendClientboundUpdateRecipesPacket(): Boolean { return MinecraftClient.getInstance().world != null && MinecraftClient.getInstance().networkHandler?.onSynchronizeRecipes( - SynchronizeRecipesS2CPacket(mutableListOf()) + SynchronizeRecipesS2CPacket(mutableMapOf(), CuttingRecipeDisplay.Grouping.empty()) ) != null } @@ -92,7 +94,7 @@ object RepoManager { fun launchAsyncUpdate(force: Boolean = false) { Firmament.coroutineScope.launch { - ItemCache.ReloadProgressHud.reportProgress("Downloading", 0, -1) // TODO: replace with a proper boundy bar + ItemCache.ReloadProgressHud.reportProgress("Downloading", 0, -1) // TODO: replace with a proper bouncy bar ItemCache.ReloadProgressHud.isEnabled = true try { RepoDownloadManager.downloadUpdate(force) @@ -112,7 +114,7 @@ object RepoManager { ItemCache.ReloadProgressHud.isEnabled = true neuRepo.reload() } catch (exc: NEURepositoryException) { - MinecraftClient.getInstance().player?.sendMessage( + MC.sendChat( tr("firmament.repo.reloadfail", "Failed to reload repository. This will result in some mod features not working.") ) diff --git a/src/main/kotlin/repo/SBItemStack.kt b/src/main/kotlin/repo/SBItemStack.kt index 281075d..18126ee 100644 --- a/src/main/kotlin/repo/SBItemStack.kt +++ b/src/main/kotlin/repo/SBItemStack.kt @@ -1,9 +1,14 @@ package moe.nea.firmament.repo +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder import io.github.moulberry.repo.constants.PetNumbers import io.github.moulberry.repo.data.NEUIngredient import io.github.moulberry.repo.data.NEUItem import net.minecraft.item.ItemStack +import net.minecraft.network.RegistryByteBuf +import net.minecraft.network.codec.PacketCodec +import net.minecraft.network.codec.PacketCodecs import net.minecraft.text.Text import net.minecraft.util.Formatting import moe.nea.firmament.repo.ItemCache.asItemStack @@ -40,6 +45,21 @@ data class SBItemStack constructor( } companion object { + val PACKET_CODEC: PacketCodec<in RegistryByteBuf, SBItemStack> = PacketCodec.tuple( + SkyblockId.PACKET_CODEC, { it.skyblockId }, + PacketCodecs.VAR_INT, { it.stackSize }, + { id, count -> SBItemStack(id, count) } + ) + val CODEC: Codec<SBItemStack> = RecordCodecBuilder.create { + it.group( + SkyblockId.CODEC.fieldOf("skyblockId").forGetter { it.skyblockId }, + Codec.INT.fieldOf("count").forGetter { it.stackSize }, + ).apply(it) { id, count -> + SBItemStack(id, count) + } + } + val EMPTY = SBItemStack(SkyblockId.NULL, 0) + operator fun invoke(itemStack: ItemStack): SBItemStack { val skyblockId = itemStack.skyBlockId ?: SkyblockId.NULL return SBItemStack( @@ -114,6 +134,8 @@ data class SBItemStack constructor( val itemStack = itemStack_ ?: run { if (skyblockId == SkyblockId.COINS) return@run ItemCache.coinItem(stackSize).also { it.appendLore(extraLore) } + if (stackSize == 0) + return@run ItemStack.EMPTY val replacementData = mutableMapOf<String, String>() injectReplacementDataForPets(replacementData) return@run neuItem.asItemStack(idHint = skyblockId, replacementData) diff --git a/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt b/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt new file mode 100644 index 0000000..9a1aea5 --- /dev/null +++ b/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt @@ -0,0 +1,19 @@ +package moe.nea.firmament.repo.recipes + +import io.github.moulberry.repo.NEURepository +import io.github.moulberry.repo.data.NEURecipe +import me.shedaniel.math.Rectangle +import net.minecraft.item.ItemStack +import net.minecraft.text.Text +import net.minecraft.util.Identifier +import moe.nea.firmament.repo.SBItemStack + +interface GenericRecipeRenderer<T : NEURecipe> { + fun render(recipe: T, bounds: Rectangle, layouter: RecipeLayouter) + fun getInputs(recipe: T): Collection<SBItemStack> + fun getOutputs(recipe: T): Collection<SBItemStack> + val icon: ItemStack + val title: Text + val identifier: Identifier + fun findAllRecipes(neuRepository: NEURepository): Iterable<T> +} diff --git a/src/main/kotlin/repo/recipes/RecipeLayouter.kt b/src/main/kotlin/repo/recipes/RecipeLayouter.kt new file mode 100644 index 0000000..109bff5 --- /dev/null +++ b/src/main/kotlin/repo/recipes/RecipeLayouter.kt @@ -0,0 +1,33 @@ +package moe.nea.firmament.repo.recipes + +import io.github.notenoughupdates.moulconfig.gui.GuiComponent +import net.minecraft.text.Text +import moe.nea.firmament.repo.SBItemStack + +interface RecipeLayouter { + enum class SlotKind { + SMALL_INPUT, + SMALL_OUTPUT, + + /** + * Create a bigger background and mark the slot as output. The coordinates should still refer the upper left corner of the item stack, not of the bigger background. + */ + BIG_OUTPUT, + } + + fun createItemSlot( + x: Int, y: Int, + content: SBItemStack?, + slotKind: SlotKind, + ) + + fun createLabel( + x: Int, y: Int, + text: Text + ) + + fun createArrow(x: Int, y: Int) + + fun createMoulConfig(x: Int, y: Int, w: Int, h: Int, component: GuiComponent) +} + diff --git a/src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt b/src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt new file mode 100644 index 0000000..fd0c750 --- /dev/null +++ b/src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt @@ -0,0 +1,50 @@ +package moe.nea.firmament.repo.recipes + +import io.github.moulberry.repo.NEURepository +import io.github.moulberry.repo.data.NEUCraftingRecipe +import me.shedaniel.math.Point +import me.shedaniel.math.Rectangle +import net.minecraft.block.Blocks +import net.minecraft.item.ItemStack +import net.minecraft.text.Text +import net.minecraft.util.Identifier +import moe.nea.firmament.Firmament +import moe.nea.firmament.repo.SBItemStack +import moe.nea.firmament.util.tr + +class SBCraftingRecipeRenderer : GenericRecipeRenderer<NEUCraftingRecipe> { + override fun render(recipe: NEUCraftingRecipe, bounds: Rectangle, layouter: RecipeLayouter) { + val point = Point(bounds.centerX - 58, bounds.centerY - 27) + layouter.createArrow(point.x + 60, point.y + 18) + for (i in 0 until 3) { + for (j in 0 until 3) { + val item = recipe.inputs[i + j * 3] + layouter.createItemSlot(point.x + 1 + i * 18, + point.y + 1 + j * 18, + SBItemStack(item), + RecipeLayouter.SlotKind.SMALL_INPUT) + } + } + layouter.createItemSlot( + point.x + 95, point.y + 19, + SBItemStack(recipe.output), + RecipeLayouter.SlotKind.BIG_OUTPUT + ) + } + + override fun getInputs(recipe: NEUCraftingRecipe): Collection<SBItemStack> { + return recipe.allInputs.mapNotNull { SBItemStack(it) } + } + + override fun getOutputs(recipe: NEUCraftingRecipe): Collection<SBItemStack> { + return SBItemStack(recipe.output)?.let(::listOf) ?: emptyList() + } + + override fun findAllRecipes(neuRepository: NEURepository): Iterable<NEUCraftingRecipe> { + return neuRepository.items.items.values.flatMap { it.recipes }.filterIsInstance<NEUCraftingRecipe>() + } + + override val icon: ItemStack = ItemStack(Blocks.CRAFTING_TABLE) + override val title: Text = tr("firmament.category.crafting", "SkyBlock Crafting") + override val identifier: Identifier = Firmament.identifier("crafting_recipe") +} diff --git a/src/main/kotlin/util/ErrorUtil.kt b/src/main/kotlin/util/ErrorUtil.kt index afecf25..b06093b 100644 --- a/src/main/kotlin/util/ErrorUtil.kt +++ b/src/main/kotlin/util/ErrorUtil.kt @@ -1,25 +1,46 @@ +@file:OptIn(ExperimentalContracts::class) + package moe.nea.firmament.util +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract 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 + || ErrorUtil::class.java.desiredAssertionStatus() + } + + inline fun softCheck(message: String, check: Boolean) { + if (!check) softError(message) } - inline fun softCheck(message: String, func: () -> Boolean) { + inline fun lazyCheck(message: String, func: () -> Boolean) { + contract { + callsInPlace(func, InvocationKind.AT_MOST_ONCE) + } if (!aggressiveErrors) return if (func()) return error(message) } - @Suppress("NOTHING_TO_INLINE") // Suppressed since i want the logger to not pick up the ErrorUtil stack-frame + inline fun softError(message: String, exception: Throwable) { + if (aggressiveErrors) throw IllegalStateException(message, exception) + else Firmament.logger.error(message, exception) + } + inline fun softError(message: String) { if (aggressiveErrors) error(message) else Firmament.logger.error(message) } inline fun <T : Any> notNullOr(nullable: T?, message: String, orElse: () -> T): T { + contract { + callsInPlace(orElse, InvocationKind.AT_MOST_ONCE) + } if (nullable == null) { softError(message) return orElse() diff --git a/src/main/kotlin/util/MC.kt b/src/main/kotlin/util/MC.kt index fc42be9..1b7739f 100644 --- a/src/main/kotlin/util/MC.kt +++ b/src/main/kotlin/util/MC.kt @@ -3,9 +3,13 @@ package moe.nea.firmament.util import io.github.moulberry.repo.data.Coordinate import java.util.concurrent.ConcurrentLinkedQueue import net.minecraft.client.MinecraftClient +import net.minecraft.client.gui.screen.Screen import net.minecraft.client.gui.screen.ingame.HandledScreen -import net.minecraft.client.option.GameOptions +import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.client.render.WorldRenderer +import net.minecraft.client.render.item.ItemRenderer +import net.minecraft.client.world.ClientWorld +import net.minecraft.entity.Entity import net.minecraft.item.Item import net.minecraft.network.packet.c2s.play.CommandExecutionC2SPacket import net.minecraft.registry.BuiltinRegistries @@ -14,7 +18,9 @@ import net.minecraft.registry.RegistryWrapper import net.minecraft.resource.ReloadableResourceManagerImpl import net.minecraft.text.Text import net.minecraft.util.math.BlockPos +import net.minecraft.world.World import moe.nea.firmament.events.TickEvent +import moe.nea.firmament.events.WorldReadyEvent object MC { @@ -29,6 +35,9 @@ object MC { (nextTickTodos.poll() ?: break).invoke() } } + WorldReadyEvent.subscribe("MC:ready") { + this.lastWorld + } } fun sendChat(text: Text) { @@ -69,6 +78,7 @@ object MC { inline val resourceManager get() = (instance.resourceManager as ReloadableResourceManagerImpl) + inline val itemRenderer: ItemRenderer get() = instance.itemRenderer inline val worldRenderer: WorldRenderer get() = instance.worldRenderer inline val networkHandler get() = player?.networkHandler inline val instance get() = MinecraftClient.getInstance() @@ -79,11 +89,11 @@ object MC { inline val inGameHud get() = instance.inGameHud inline val font get() = instance.textRenderer inline val soundManager get() = instance.soundManager - inline val player get() = instance.player - inline val camera get() = instance.cameraEntity + inline val player: ClientPlayerEntity? get() = instance.player + inline val camera: Entity? get() = instance.cameraEntity inline val guiAtlasManager get() = instance.guiAtlasManager - inline val world get() = instance.world - inline var screen + inline val world: ClientWorld? get() = instance.world + inline var screen: Screen? get() = instance.currentScreen set(value) = instance.setScreen(value) val screenName get() = screen?.title?.unformattedString?.trim() @@ -92,7 +102,13 @@ object MC { inline val currentRegistries: RegistryWrapper.WrapperLookup? get() = world?.registryManager val defaultRegistries: RegistryWrapper.WrapperLookup = BuiltinRegistries.createWrapperLookup() inline val currentOrDefaultRegistries get() = currentRegistries ?: defaultRegistries - val defaultItems: RegistryWrapper.Impl<Item> = defaultRegistries.getWrapperOrThrow(RegistryKeys.ITEM) + val defaultItems: RegistryWrapper.Impl<Item> = defaultRegistries.getOrThrow(RegistryKeys.ITEM) + var lastWorld: World? = null + get() { + field = world ?: field + return field + } + private set } diff --git a/src/main/kotlin/util/SBData.kt b/src/main/kotlin/util/SBData.kt index 0b2c404..051d070 100644 --- a/src/main/kotlin/util/SBData.kt +++ b/src/main/kotlin/util/SBData.kt @@ -37,7 +37,7 @@ object SBData { it.serverType.getOrNull()?.name?.uppercase(), it.mode.getOrNull(), it.map.getOrNull()) - SkyblockServerUpdateEvent.publish(SkyblockServerUpdateEvent(lastLocraw, null)) + SkyblockServerUpdateEvent.publish(SkyblockServerUpdateEvent(lastLocraw, locraw)) profileIdCommandDebounce = TimeMark.now() } } diff --git a/src/main/kotlin/util/SkyblockId.kt b/src/main/kotlin/util/SkyblockId.kt index 059e746..9c9287b 100644 --- a/src/main/kotlin/util/SkyblockId.kt +++ b/src/main/kotlin/util/SkyblockId.kt @@ -2,6 +2,7 @@ package moe.nea.firmament.util +import com.mojang.serialization.Codec import io.github.moulberry.repo.data.NEUIngredient import io.github.moulberry.repo.data.NEUItem import io.github.moulberry.repo.data.Rarity @@ -16,6 +17,9 @@ import net.minecraft.component.type.NbtComponent import net.minecraft.item.ItemStack import net.minecraft.item.Items import net.minecraft.nbt.NbtCompound +import net.minecraft.network.RegistryByteBuf +import net.minecraft.network.codec.PacketCodec +import net.minecraft.network.codec.PacketCodecs import net.minecraft.util.Identifier import moe.nea.firmament.repo.ItemCache.asItemStack import moe.nea.firmament.repo.set @@ -68,6 +72,9 @@ value class SkyblockId(val neuItem: String) { val NULL: SkyblockId = SkyblockId("null") val PET_NULL: SkyblockId = SkyblockId("null_pet") private val illlegalPathRegex = "[^a-z0-9_.-/]".toRegex() + val CODEC = Codec.STRING.xmap({ SkyblockId(it) }, { it.neuItem }) + val PACKET_CODEC: PacketCodec<in RegistryByteBuf, SkyblockId> = + PacketCodecs.STRING.xmap({ SkyblockId(it) }, { it.neuItem }) } } diff --git a/src/main/kotlin/util/data/IDataHolder.kt b/src/main/kotlin/util/data/IDataHolder.kt index cc97b58..1e9ba98 100644 --- a/src/main/kotlin/util/data/IDataHolder.kt +++ b/src/main/kotlin/util/data/IDataHolder.kt @@ -1,77 +1,71 @@ - - package moe.nea.firmament.util.data import java.util.concurrent.CopyOnWriteArrayList import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents import kotlin.reflect.KClass -import net.minecraft.client.MinecraftClient -import net.minecraft.server.command.CommandOutput import net.minecraft.text.Text import moe.nea.firmament.Firmament import moe.nea.firmament.events.ScreenChangeEvent +import moe.nea.firmament.util.MC interface IDataHolder<T> { - companion object { - internal var badLoads: MutableList<String> = CopyOnWriteArrayList() - private val allConfigs: MutableMap<KClass<out IDataHolder<*>>, IDataHolder<*>> = mutableMapOf() - private val dirty: MutableSet<KClass<out IDataHolder<*>>> = mutableSetOf() + companion object { + internal var badLoads: MutableList<String> = CopyOnWriteArrayList() + private val allConfigs: MutableMap<KClass<out IDataHolder<*>>, IDataHolder<*>> = mutableMapOf() + private val dirty: MutableSet<KClass<out IDataHolder<*>>> = mutableSetOf() - internal fun <T : IDataHolder<K>, K> putDataHolder(kClass: KClass<T>, inst: IDataHolder<K>) { - allConfigs[kClass] = inst - } + internal fun <T : IDataHolder<K>, K> putDataHolder(kClass: KClass<T>, inst: IDataHolder<K>) { + allConfigs[kClass] = inst + } - fun <T : IDataHolder<K>, K> markDirty(kClass: KClass<T>) { - if (kClass !in allConfigs) { - Firmament.logger.error("Tried to markDirty '${kClass.qualifiedName}', which isn't registered as 'IConfigHolder'") - return - } - dirty.add(kClass) - } + fun <T : IDataHolder<K>, K> markDirty(kClass: KClass<T>) { + if (kClass !in allConfigs) { + Firmament.logger.error("Tried to markDirty '${kClass.qualifiedName}', which isn't registered as 'IConfigHolder'") + return + } + dirty.add(kClass) + } - private fun performSaves() { - val toSave = dirty.toList().also { - dirty.clear() - } - for (it in toSave) { - val obj = allConfigs[it] - if (obj == null) { - Firmament.logger.error("Tried to save '${it}', which isn't registered as 'ConfigHolder'") - continue - } - obj.save() - } - } + private fun performSaves() { + val toSave = dirty.toList().also { + dirty.clear() + } + for (it in toSave) { + val obj = allConfigs[it] + if (obj == null) { + Firmament.logger.error("Tried to save '${it}', which isn't registered as 'ConfigHolder'") + continue + } + obj.save() + } + } - private fun warnForResetConfigs(player: CommandOutput) { - if (badLoads.isNotEmpty()) { - player.sendMessage( - Text.literal( - "The following configs have been reset: ${badLoads.joinToString(", ")}. " + - "This can be intentional, but probably isn't." - ) - ) - badLoads.clear() - } - } + private fun warnForResetConfigs() { + if (badLoads.isNotEmpty()) { + MC.sendChat( + Text.literal( + "The following configs have been reset: ${badLoads.joinToString(", ")}. " + + "This can be intentional, but probably isn't." + ) + ) + badLoads.clear() + } + } - fun registerEvents() { - ScreenChangeEvent.subscribe("IDataHolder:saveOnScreenChange") { event -> - performSaves() - val p = MinecraftClient.getInstance().player - if (p != null) { - warnForResetConfigs(p) - } - } - ClientLifecycleEvents.CLIENT_STOPPING.register(ClientLifecycleEvents.ClientStopping { - performSaves() - }) - } + fun registerEvents() { + ScreenChangeEvent.subscribe("IDataHolder:saveOnScreenChange") { event -> + performSaves() + warnForResetConfigs() + } + ClientLifecycleEvents.CLIENT_STOPPING.register(ClientLifecycleEvents.ClientStopping { + performSaves() + }) + } - } + } - val data: T - fun save() - fun markDirty() - fun load() + val data: T + fun save() + fun markDirty() + fun load() } diff --git a/src/main/kotlin/util/render/DrawContextExt.kt b/src/main/kotlin/util/render/DrawContextExt.kt index fc38aa6..da0b0b0 100644 --- a/src/main/kotlin/util/render/DrawContextExt.kt +++ b/src/main/kotlin/util/render/DrawContextExt.kt @@ -4,12 +4,70 @@ import com.mojang.blaze3d.systems.RenderSystem import me.shedaniel.math.Color import org.joml.Matrix4f import net.minecraft.client.gui.DrawContext +import net.minecraft.client.render.RenderLayer +import net.minecraft.client.render.RenderLayer.MultiPhaseParameters +import net.minecraft.client.render.RenderPhase +import net.minecraft.client.render.VertexFormat +import net.minecraft.client.render.VertexFormats +import net.minecraft.util.Identifier +import net.minecraft.util.TriState +import net.minecraft.util.Util import moe.nea.firmament.util.MC fun DrawContext.isUntranslatedGuiDrawContext(): Boolean { return (matrices.peek().positionMatrix.properties() and Matrix4f.PROPERTY_IDENTITY.toInt()) != 0 } +object GuiRenderLayers { + val GUI_TEXTURED_NO_DEPTH = Util.memoize<Identifier, RenderLayer> { texture: Identifier -> + RenderLayer.of("firmament_gui_textured_no_depth", + VertexFormats.POSITION_TEXTURE_COLOR, + VertexFormat.DrawMode.QUADS, + RenderLayer.CUTOUT_BUFFER_SIZE, + MultiPhaseParameters.builder() + .texture(RenderPhase.Texture(texture, TriState.FALSE, false)) + .program(RenderPhase.POSITION_TEXTURE_COLOR_PROGRAM) + .transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY) + .depthTest(RenderPhase.ALWAYS_DEPTH_TEST) + .build(false)) + } +} + +@Deprecated("Use the other drawGuiTexture") +fun DrawContext.drawGuiTexture( + x: Int, y: Int, z: Int, width: Int, height: Int, sprite: Identifier +) = this.drawGuiTexture(RenderLayer::getGuiTextured, sprite, x, y, width, height) + +fun DrawContext.drawGuiTexture( + sprite: Identifier, + x: Int, y: Int, width: Int, height: Int +) = this.drawGuiTexture(RenderLayer::getGuiTextured, sprite, x, y, width, height) + +fun DrawContext.drawTexture( + sprite: Identifier, + x: Int, + y: Int, + u: Float, + v: Float, + width: Int, + height: Int, + textureWidth: Int, + textureHeight: Int +) { + this.drawTexture(RenderLayer::getGuiTextured, + sprite, + x, + y, + u, + v, + width, + height, + width, + height, + textureWidth, + textureHeight) +} + fun DrawContext.drawLine(fromX: Int, fromY: Int, toX: Int, toY: Int, color: Color) { // TODO: push scissors // TODO: use matrix translations and a different render layer @@ -18,11 +76,12 @@ fun DrawContext.drawLine(fromX: Int, fromY: Int, toX: Int, toY: Int, color: Colo return } RenderSystem.lineWidth(MC.window.scaleFactor.toFloat()) - val buf = this.vertexConsumers.getBuffer(RenderInWorldContext.RenderLayers.LINES) - buf.vertex(fromX.toFloat(), fromY.toFloat(), 0F).color(color.color) - .normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F) - buf.vertex(toX.toFloat(), toY.toFloat(), 0F).color(color.color) - .normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F) - this.draw() + draw { vertexConsumers -> + val buf = vertexConsumers.getBuffer(RenderInWorldContext.RenderLayers.LINES) + buf.vertex(fromX.toFloat(), fromY.toFloat(), 0F).color(color.color) + .normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F) + buf.vertex(toX.toFloat(), toY.toFloat(), 0F).color(color.color) + .normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F) + } } diff --git a/src/main/kotlin/util/render/FacingThePlayerContext.kt b/src/main/kotlin/util/render/FacingThePlayerContext.kt index eb37e35..daa8da9 100644 --- a/src/main/kotlin/util/render/FacingThePlayerContext.kt +++ b/src/main/kotlin/util/render/FacingThePlayerContext.kt @@ -76,13 +76,10 @@ class FacingThePlayerContext(val worldContext: RenderInWorldContext) { u1: Float, v1: Float, u2: Float, v2: Float, ) { - RenderSystem.setShaderTexture(0, texture) - RenderSystem.setShader(GameRenderer::getPositionTexColorProgram) + val buf = worldContext.vertexConsumers.getBuffer(RenderLayer.getGuiTexturedOverlay(texture)) val hw = width / 2F val hh = height / 2F val matrix4f: Matrix4f = worldContext.matrixStack.peek().positionMatrix - val buf = Tessellator.getInstance() - .begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR) buf.vertex(matrix4f, -hw, -hh, 0F) .color(-1) .texture(u1, v1).next() @@ -95,7 +92,7 @@ class FacingThePlayerContext(val worldContext: RenderInWorldContext) { buf.vertex(matrix4f, +hw, -hh, 0F) .color(-1) .texture(u2, v1).next() - BufferRenderer.drawWithGlobalProgram(buf.end()) + worldContext.vertexConsumers.draw() } } diff --git a/src/main/kotlin/util/render/FirmamentShaders.kt b/src/main/kotlin/util/render/FirmamentShaders.kt index 1094bc2..ba67dbb 100644 --- a/src/main/kotlin/util/render/FirmamentShaders.kt +++ b/src/main/kotlin/util/render/FirmamentShaders.kt @@ -1,23 +1,30 @@ package moe.nea.firmament.util.render -import net.minecraft.client.gl.ShaderProgram +import net.minecraft.client.gl.Defines +import net.minecraft.client.gl.ShaderProgramKey import net.minecraft.client.render.RenderPhase +import net.minecraft.client.render.VertexFormat import net.minecraft.client.render.VertexFormats +import moe.nea.firmament.Firmament import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.RegisterCustomShadersEvent +import moe.nea.firmament.events.DebugInstantiateEvent +import moe.nea.firmament.util.MC object FirmamentShaders { + val shaders = mutableListOf<ShaderProgramKey>() + private fun shader(name: String, format: VertexFormat, defines: Defines): ShaderProgramKey { + val key = ShaderProgramKey(Firmament.identifier(name), format, defines) + shaders.add(key) + return key + } - private lateinit var _LINES: ShaderProgram - val LINES = RenderPhase.ShaderProgram({ _LINES }) + val LINES = RenderPhase.ShaderProgram(shader("core/rendertype_lines", VertexFormats.LINES, Defines.EMPTY)) - @Subscribe - fun registerCustomShaders(event: RegisterCustomShadersEvent) { - event.register( - "firmament_rendertype_lines", - VertexFormats.LINES, - { _LINES = it }, - ) - } + @Subscribe + fun debugLoad(event: DebugInstantiateEvent) { + shaders.forEach { + MC.instance.shaderLoader.getOrCreateProgram(it) + } + } } diff --git a/src/main/kotlin/util/render/RenderCircleProgress.kt b/src/main/kotlin/util/render/RenderCircleProgress.kt index a2f42b5..9cc383c 100644 --- a/src/main/kotlin/util/render/RenderCircleProgress.kt +++ b/src/main/kotlin/util/render/RenderCircleProgress.kt @@ -1,4 +1,3 @@ - package moe.nea.firmament.util.render import com.mojang.blaze3d.systems.RenderSystem @@ -9,7 +8,8 @@ import kotlin.math.atan2 import kotlin.math.tan import net.minecraft.client.gui.DrawContext import net.minecraft.client.render.BufferRenderer -import net.minecraft.client.render.GameRenderer +import net.minecraft.client.render.RenderLayer +import net.minecraft.client.render.RenderPhase import net.minecraft.client.render.Tessellator import net.minecraft.client.render.VertexFormat.DrawMode import net.minecraft.client.render.VertexFormats @@ -17,79 +17,77 @@ import net.minecraft.util.Identifier object RenderCircleProgress { - fun renderCircle( - drawContext: DrawContext, - texture: Identifier, - progress: Float, - u1: Float, - u2: Float, - v1: Float, - v2: Float, - ) { - RenderSystem.setShaderTexture(0, texture) - RenderSystem.setShader(GameRenderer::getPositionTexColorProgram) - RenderSystem.enableBlend() - val matrix: Matrix4f = drawContext.matrices.peek().positionMatrix - val bufferBuilder = Tessellator.getInstance().begin(DrawMode.TRIANGLES, VertexFormats.POSITION_TEXTURE_COLOR) - - val corners = listOf( - Vector2f(0F, -1F), - Vector2f(1F, -1F), - Vector2f(1F, 0F), - Vector2f(1F, 1F), - Vector2f(0F, 1F), - Vector2f(-1F, 1F), - Vector2f(-1F, 0F), - Vector2f(-1F, -1F), - ) + fun renderCircle( + drawContext: DrawContext, + texture: Identifier, + progress: Float, + u1: Float, + u2: Float, + v1: Float, + v2: Float, + ) { + RenderSystem.enableBlend() + drawContext.draw { + val bufferBuilder = it.getBuffer(RenderLayer.getGuiTexturedOverlay(texture)) + val matrix: Matrix4f = drawContext.matrices.peek().positionMatrix - for (i in (0 until 8)) { - if (progress < i / 8F) { - break - } - val second = corners[(i + 1) % 8] - val first = corners[i] - if (progress <= (i + 1) / 8F) { - val internalProgress = 1 - (progress - i / 8F) * 8F - val angle = lerpAngle( - atan2(second.y, second.x), - atan2(first.y, first.x), - internalProgress - ) - if (angle < tau / 8 || angle >= tau * 7 / 8) { - second.set(1F, tan(angle)) - } else if (angle < tau * 3 / 8) { - second.set(1 / tan(angle), 1F) - } else if (angle < tau * 5 / 8) { - second.set(-1F, -tan(angle)) - } else { - second.set(-1 / tan(angle), -1F) - } - } + val corners = listOf( + Vector2f(0F, -1F), + Vector2f(1F, -1F), + Vector2f(1F, 0F), + Vector2f(1F, 1F), + Vector2f(0F, 1F), + Vector2f(-1F, 1F), + Vector2f(-1F, 0F), + Vector2f(-1F, -1F), + ) - fun ilerp(f: Float): Float = - ilerp(-1f, 1f, f) + for (i in (0 until 8)) { + if (progress < i / 8F) { + break + } + val second = corners[(i + 1) % 8] + val first = corners[i] + if (progress <= (i + 1) / 8F) { + val internalProgress = 1 - (progress - i / 8F) * 8F + val angle = lerpAngle( + atan2(second.y, second.x), + atan2(first.y, first.x), + internalProgress + ) + if (angle < tau / 8 || angle >= tau * 7 / 8) { + second.set(1F, tan(angle)) + } else if (angle < tau * 3 / 8) { + second.set(1 / tan(angle), 1F) + } else if (angle < tau * 5 / 8) { + second.set(-1F, -tan(angle)) + } else { + second.set(-1 / tan(angle), -1F) + } + } - bufferBuilder - .vertex(matrix, second.x, second.y, 0F) - .texture(lerp(u1, u2, ilerp(second.x)), lerp(v1, v2, ilerp(second.y))) - .color(-1) - .next() - bufferBuilder - .vertex(matrix, first.x, first.y, 0F) - .texture(lerp(u1, u2, ilerp(first.x)), lerp(v1, v2, ilerp(first.y))) - .color(-1) - .next() - bufferBuilder - .vertex(matrix, 0F, 0F, 0F) - .texture(lerp(u1, u2, ilerp(0F)), lerp(v1, v2, ilerp(0F))) - .color(-1) - .next() - } - BufferRenderer.drawWithGlobalProgram(bufferBuilder.end()) - RenderSystem.disableBlend() - } + fun ilerp(f: Float): Float = + ilerp(-1f, 1f, f) + bufferBuilder + .vertex(matrix, second.x, second.y, 0F) + .texture(lerp(u1, u2, ilerp(second.x)), lerp(v1, v2, ilerp(second.y))) + .color(-1) + .next() + bufferBuilder + .vertex(matrix, first.x, first.y, 0F) + .texture(lerp(u1, u2, ilerp(first.x)), lerp(v1, v2, ilerp(first.y))) + .color(-1) + .next() + bufferBuilder + .vertex(matrix, 0F, 0F, 0F) + .texture(lerp(u1, u2, ilerp(0F)), lerp(v1, v2, ilerp(0F))) + .color(-1) + .next() + } + } + RenderSystem.disableBlend() + } } diff --git a/src/main/kotlin/util/render/RenderInWorldContext.kt b/src/main/kotlin/util/render/RenderInWorldContext.kt index b61b9aa..bb58200 100644 --- a/src/main/kotlin/util/render/RenderInWorldContext.kt +++ b/src/main/kotlin/util/render/RenderInWorldContext.kt @@ -1,5 +1,3 @@ - - package moe.nea.firmament.util.render import com.mojang.blaze3d.systems.RenderSystem @@ -8,14 +6,12 @@ import java.lang.Math.pow import org.joml.Matrix4f import org.joml.Vector3f import net.minecraft.client.gl.VertexBuffer -import net.minecraft.client.render.BufferBuilder -import net.minecraft.client.render.BufferRenderer import net.minecraft.client.render.Camera -import net.minecraft.client.render.GameRenderer import net.minecraft.client.render.RenderLayer import net.minecraft.client.render.RenderPhase import net.minecraft.client.render.RenderTickCounter import net.minecraft.client.render.Tessellator +import net.minecraft.client.render.VertexConsumer import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.VertexFormat import net.minecraft.client.render.VertexFormats @@ -31,273 +27,287 @@ import moe.nea.firmament.util.MC @RenderContextDSL class RenderInWorldContext private constructor( - private val tesselator: Tessellator, - val matrixStack: MatrixStack, - private val camera: Camera, - private val tickCounter: RenderTickCounter, - val vertexConsumers: VertexConsumerProvider.Immediate, + private val tesselator: Tessellator, + val matrixStack: MatrixStack, + private val camera: Camera, + private val tickCounter: RenderTickCounter, + val vertexConsumers: VertexConsumerProvider.Immediate, ) { - object RenderLayers { - val TRANSLUCENT_TRIS = RenderLayer.of("firmament_translucent_tris", - VertexFormats.POSITION_COLOR, - VertexFormat.DrawMode.TRIANGLES, - RenderLayer.DEFAULT_BUFFER_SIZE, - false, true, - RenderLayer.MultiPhaseParameters.builder() - .depthTest(RenderPhase.ALWAYS_DEPTH_TEST) - .transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY) - .program(RenderPhase.COLOR_PROGRAM) - .build(false)) - val LINES = RenderLayer.of("firmament_rendertype_lines", - VertexFormats.LINES, - VertexFormat.DrawMode.LINES, - RenderLayer.DEFAULT_BUFFER_SIZE, - false, false, // do we need translucent? i dont think so - RenderLayer.MultiPhaseParameters.builder() - .depthTest(RenderPhase.ALWAYS_DEPTH_TEST) - .program(FirmamentShaders.LINES) - .build(false) - ) - } - - fun color(color: me.shedaniel.math.Color) { - color(color.red / 255F, color.green / 255f, color.blue / 255f, color.alpha / 255f) - } - - fun color(red: Float, green: Float, blue: Float, alpha: Float) { - RenderSystem.setShaderColor(red, green, blue, alpha) - } - - fun block(blockPos: BlockPos) { - matrixStack.push() - matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat()) - buildCube(matrixStack.peek().positionMatrix, tesselator) - matrixStack.pop() - } - - enum class VerticalAlign { - TOP, BOTTOM, CENTER; - - fun align(index: Int, count: Int): Float { - return when (this) { - CENTER -> (index - count / 2F) * (1 + MC.font.fontHeight.toFloat()) - BOTTOM -> (index - count) * (1 + MC.font.fontHeight.toFloat()) - TOP -> (index) * (1 + MC.font.fontHeight.toFloat()) - } - } - } - - fun waypoint(position: BlockPos, vararg label: Text) { - text( - position.toCenterPos(), - *label, - Text.literal("§e${FirmFormatters.formatDistance(MC.player?.pos?.distanceTo(position.toCenterPos()) ?: 42069.0)}"), - background = 0xAA202020.toInt() - ) - } - - fun withFacingThePlayer(position: Vec3d, block: FacingThePlayerContext.() -> Unit) { - matrixStack.push() - matrixStack.translate(position.x, position.y, position.z) - val actualCameraDistance = position.distanceTo(camera.pos) - val distanceToMoveTowardsCamera = if (actualCameraDistance < 10) 0.0 else -(actualCameraDistance - 10.0) - val vec = position.subtract(camera.pos).multiply(distanceToMoveTowardsCamera / actualCameraDistance) - matrixStack.translate(vec.x, vec.y, vec.z) - matrixStack.multiply(camera.rotation) - matrixStack.scale(0.025F, -0.025F, 1F) - - FacingThePlayerContext(this).run(block) - - matrixStack.pop() - vertexConsumers.drawCurrentLayer() - } - - fun sprite(position: Vec3d, sprite: Sprite, width: Int, height: Int) { - texture( - position, sprite.atlasId, width, height, sprite.minU, sprite.minV, sprite.maxU, sprite.maxV - ) - } - - fun texture( - position: Vec3d, texture: Identifier, width: Int, height: Int, - u1: Float, v1: Float, - u2: Float, v2: Float, - ) { - withFacingThePlayer(position) { - texture(texture, width, height, u1, v1, u2, v2) - } - } - - fun text(position: Vec3d, vararg texts: Text, verticalAlign: VerticalAlign = VerticalAlign.CENTER, background: Int = 0x70808080) { - withFacingThePlayer(position) { - text(*texts, verticalAlign = verticalAlign, background = background) - } - } - - fun tinyBlock(vec3d: Vec3d, size: Float) { - RenderSystem.setShader(GameRenderer::getPositionColorProgram) - matrixStack.push() - matrixStack.translate(vec3d.x, vec3d.y, vec3d.z) - matrixStack.scale(size, size, size) - matrixStack.translate(-.5, -.5, -.5) - buildCube(matrixStack.peek().positionMatrix, tesselator) - matrixStack.pop() - } - - fun wireframeCube(blockPos: BlockPos, lineWidth: Float = 10F) { - RenderSystem.setShader(GameRenderer::getRenderTypeLinesProgram) - matrixStack.push() - RenderSystem.lineWidth(lineWidth / pow(camera.pos.squaredDistanceTo(blockPos.toCenterPos()), 0.25).toFloat()) - matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat()) - buildWireFrameCube(matrixStack.peek(), tesselator) - matrixStack.pop() - } - - fun line(vararg points: Vec3d, lineWidth: Float = 10F) { - line(points.toList(), lineWidth) - } - - fun tracer(toWhere: Vec3d, lineWidth: Float = 3f) { - val cameraForward = Vector3f(0f, 0f, -1f).rotate(camera.rotation) - line(camera.pos.add(Vec3d(cameraForward)), toWhere, lineWidth = lineWidth) - } - - fun line(points: List<Vec3d>, lineWidth: Float = 10F) { - RenderSystem.lineWidth(lineWidth) - val buffer = tesselator.begin(VertexFormat.DrawMode.LINES, VertexFormats.LINES) - - val matrix = matrixStack.peek() - var lastNormal: Vector3f? = null - points.zipWithNext().forEach { (a, b) -> - val normal = Vector3f(b.x.toFloat(), b.y.toFloat(), b.z.toFloat()) - .sub(a.x.toFloat(), a.y.toFloat(), a.z.toFloat()) - .normalize() - val lastNormal0 = lastNormal ?: normal - lastNormal = normal - buffer.vertex(matrix.positionMatrix, a.x.toFloat(), a.y.toFloat(), a.z.toFloat()) - .color(-1) - .normal(matrix, lastNormal0.x, lastNormal0.y, lastNormal0.z) - .next() - buffer.vertex(matrix.positionMatrix, b.x.toFloat(), b.y.toFloat(), b.z.toFloat()) - .color(-1) - .normal(matrix, normal.x, normal.y, normal.z) - .next() - } - - RenderLayers.LINES.draw(buffer.end()) - } - - companion object { - private fun doLine( - matrix: MatrixStack.Entry, - buf: BufferBuilder, - i: Float, - j: Float, - k: Float, - x: Float, - y: Float, - z: Float - ) { - val normal = Vector3f(x, y, z) - .sub(i, j, k) - .normalize() - buf.vertex(matrix.positionMatrix, i, j, k) - .normal(matrix, normal.x, normal.y, normal.z) - .color(-1) - .next() - buf.vertex(matrix.positionMatrix, x, y, z) - .normal(matrix, normal.x, normal.y, normal.z) - .color(-1) - .next() - } - - - private fun buildWireFrameCube(matrix: MatrixStack.Entry, tessellator: Tessellator) { - val buf = tessellator.begin(VertexFormat.DrawMode.LINES, VertexFormats.LINES) - - for (i in 0..1) { - for (j in 0..1) { - val i = i.toFloat() - val j = j.toFloat() - doLine(matrix, buf, 0F, i, j, 1F, i, j) - doLine(matrix, buf, i, 0F, j, i, 1F, j) - doLine(matrix, buf, i, j, 0F, i, j, 1F) - } - } - BufferRenderer.drawWithGlobalProgram(buf.end()) - } - - private fun buildCube(matrix: Matrix4f, tessellator: Tessellator) { - val buf = tessellator.begin(VertexFormat.DrawMode.TRIANGLES, VertexFormats.POSITION_COLOR) - buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 0.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 0.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 0.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next() - RenderLayers.TRANSLUCENT_TRIS.draw(buf.end()) - } - - - fun renderInWorld(event: WorldRenderLastEvent, block: RenderInWorldContext. () -> Unit) { - RenderSystem.disableDepthTest() - RenderSystem.enableBlend() - RenderSystem.defaultBlendFunc() - RenderSystem.disableCull() - - event.matrices.push() - event.matrices.translate(-event.camera.pos.x, -event.camera.pos.y, -event.camera.pos.z) - - val ctx = RenderInWorldContext( - RenderSystem.renderThreadTesselator(), - event.matrices, - event.camera, - event.tickCounter, - event.vertexConsumers - ) - - block(ctx) - - event.matrices.pop() - - RenderSystem.setShaderColor(1F, 1F, 1F, 1F) - VertexBuffer.unbind() - RenderSystem.enableDepthTest() - RenderSystem.enableCull() - RenderSystem.disableBlend() - } - } + object RenderLayers { + val TRANSLUCENT_TRIS = RenderLayer.of("firmament_translucent_tris", + VertexFormats.POSITION_COLOR, + VertexFormat.DrawMode.TRIANGLES, + RenderLayer.CUTOUT_BUFFER_SIZE, + false, true, + RenderLayer.MultiPhaseParameters.builder() + .depthTest(RenderPhase.ALWAYS_DEPTH_TEST) + .transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY) + .program(RenderPhase.POSITION_COLOR_PROGRAM) + .build(false)) + val LINES = RenderLayer.of("firmament_rendertype_lines", + VertexFormats.LINES, + VertexFormat.DrawMode.LINES, + RenderLayer.CUTOUT_BUFFER_SIZE, + false, false, // do we need translucent? i dont think so + RenderLayer.MultiPhaseParameters.builder() + .depthTest(RenderPhase.ALWAYS_DEPTH_TEST) + .program(FirmamentShaders.LINES) + .build(false) + ) + val COLORED_QUADS = RenderLayer.of( + "firmament_quads", + VertexFormats.POSITION_COLOR, + VertexFormat.DrawMode.QUADS, + RenderLayer.CUTOUT_BUFFER_SIZE, + false, true, + RenderLayer.MultiPhaseParameters.builder() + .depthTest(RenderPhase.ALWAYS_DEPTH_TEST) + .program(RenderPhase.POSITION_COLOR_PROGRAM) + .transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY) + .build(false) + ) + } + + @Deprecated("stateful color management is no longer a thing") + fun color(color: me.shedaniel.math.Color) { + color(color.red / 255F, color.green / 255f, color.blue / 255f, color.alpha / 255f) + } + + @Deprecated("stateful color management is no longer a thing") + fun color(red: Float, green: Float, blue: Float, alpha: Float) { + RenderSystem.setShaderColor(red, green, blue, alpha) + } + + fun block(blockPos: BlockPos, color: Int) { + matrixStack.push() + matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat()) + buildCube(matrixStack.peek().positionMatrix, vertexConsumers.getBuffer(RenderLayers.COLORED_QUADS), color) + matrixStack.pop() + } + + enum class VerticalAlign { + TOP, BOTTOM, CENTER; + + fun align(index: Int, count: Int): Float { + return when (this) { + CENTER -> (index - count / 2F) * (1 + MC.font.fontHeight.toFloat()) + BOTTOM -> (index - count) * (1 + MC.font.fontHeight.toFloat()) + TOP -> (index) * (1 + MC.font.fontHeight.toFloat()) + } + } + } + + fun waypoint(position: BlockPos, vararg label: Text) { + text( + position.toCenterPos(), + *label, + Text.literal("§e${FirmFormatters.formatDistance(MC.player?.pos?.distanceTo(position.toCenterPos()) ?: 42069.0)}"), + background = 0xAA202020.toInt() + ) + } + + fun withFacingThePlayer(position: Vec3d, block: FacingThePlayerContext.() -> Unit) { + matrixStack.push() + matrixStack.translate(position.x, position.y, position.z) + val actualCameraDistance = position.distanceTo(camera.pos) + val distanceToMoveTowardsCamera = if (actualCameraDistance < 10) 0.0 else -(actualCameraDistance - 10.0) + val vec = position.subtract(camera.pos).multiply(distanceToMoveTowardsCamera / actualCameraDistance) + matrixStack.translate(vec.x, vec.y, vec.z) + matrixStack.multiply(camera.rotation) + matrixStack.scale(0.025F, -0.025F, 1F) + + FacingThePlayerContext(this).run(block) + + matrixStack.pop() + vertexConsumers.drawCurrentLayer() + } + + fun sprite(position: Vec3d, sprite: Sprite, width: Int, height: Int) { + texture( + position, sprite.atlasId, width, height, sprite.minU, sprite.minV, sprite.maxU, sprite.maxV + ) + } + + fun texture( + position: Vec3d, texture: Identifier, width: Int, height: Int, + u1: Float, v1: Float, + u2: Float, v2: Float, + ) { + withFacingThePlayer(position) { + texture(texture, width, height, u1, v1, u2, v2) + } + } + + fun text( + position: Vec3d, + vararg texts: Text, + verticalAlign: VerticalAlign = VerticalAlign.CENTER, + background: Int = 0x70808080 + ) { + withFacingThePlayer(position) { + text(*texts, verticalAlign = verticalAlign, background = background) + } + } + + fun tinyBlock(vec3d: Vec3d, size: Float, color: Int) { + matrixStack.push() + matrixStack.translate(vec3d.x, vec3d.y, vec3d.z) + matrixStack.scale(size, size, size) + matrixStack.translate(-.5, -.5, -.5) + buildCube(matrixStack.peek().positionMatrix, vertexConsumers.getBuffer(RenderLayers.COLORED_QUADS), color) + matrixStack.pop() + vertexConsumers.draw() + } + + fun wireframeCube(blockPos: BlockPos, lineWidth: Float = 10F) { + val buf = vertexConsumers.getBuffer(RenderLayer.LINES) + matrixStack.push() + // TODO: this does not render through blocks (or water layers) anymore + RenderSystem.lineWidth(lineWidth / pow(camera.pos.squaredDistanceTo(blockPos.toCenterPos()), 0.25).toFloat()) + matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat()) + buildWireFrameCube(matrixStack.peek(), buf) + matrixStack.pop() + vertexConsumers.draw() + } + + fun line(vararg points: Vec3d, lineWidth: Float = 10F) { + line(points.toList(), lineWidth) + } + + fun tracer(toWhere: Vec3d, lineWidth: Float = 3f) { + val cameraForward = Vector3f(0f, 0f, -1f).rotate(camera.rotation) + line(camera.pos.add(Vec3d(cameraForward)), toWhere, lineWidth = lineWidth) + } + + fun line(points: List<Vec3d>, lineWidth: Float = 10F) { + RenderSystem.lineWidth(lineWidth) + // TODO: replace with renderlayers + val buffer = tesselator.begin(VertexFormat.DrawMode.LINES, VertexFormats.LINES) + + val matrix = matrixStack.peek() + var lastNormal: Vector3f? = null + points.zipWithNext().forEach { (a, b) -> + val normal = Vector3f(b.x.toFloat(), b.y.toFloat(), b.z.toFloat()) + .sub(a.x.toFloat(), a.y.toFloat(), a.z.toFloat()) + .normalize() + val lastNormal0 = lastNormal ?: normal + lastNormal = normal + buffer.vertex(matrix.positionMatrix, a.x.toFloat(), a.y.toFloat(), a.z.toFloat()) + .color(-1) + .normal(matrix, lastNormal0.x, lastNormal0.y, lastNormal0.z) + .next() + buffer.vertex(matrix.positionMatrix, b.x.toFloat(), b.y.toFloat(), b.z.toFloat()) + .color(-1) + .normal(matrix, normal.x, normal.y, normal.z) + .next() + } + + RenderLayers.LINES.draw(buffer.end()) + } + // TODO: put the favourite icons in front of items again + + companion object { + private fun doLine( + matrix: MatrixStack.Entry, + buf: VertexConsumer, + i: Float, + j: Float, + k: Float, + x: Float, + y: Float, + z: Float + ) { + val normal = Vector3f(x, y, z) + .sub(i, j, k) + .normalize() + buf.vertex(matrix.positionMatrix, i, j, k) + .normal(matrix, normal.x, normal.y, normal.z) + .color(-1) + .next() + buf.vertex(matrix.positionMatrix, x, y, z) + .normal(matrix, normal.x, normal.y, normal.z) + .color(-1) + .next() + } + + + private fun buildWireFrameCube(matrix: MatrixStack.Entry, buf: VertexConsumer) { + for (i in 0..1) { + for (j in 0..1) { + val i = i.toFloat() + val j = j.toFloat() + doLine(matrix, buf, 0F, i, j, 1F, i, j) + doLine(matrix, buf, i, 0F, j, i, 1F, j) + doLine(matrix, buf, i, j, 0F, i, j, 1F) + } + } + } + + private fun buildCube(matrix: Matrix4f, buf: VertexConsumer, color: Int) { + // Y- + buf.vertex(matrix, 0F, 0F, 0F).color(color) + buf.vertex(matrix, 0F, 0F, 1F).color(color) + buf.vertex(matrix, 1F, 0F, 1F).color(color) + buf.vertex(matrix, 1F, 0F, 0F).color(color) + // Y+ + buf.vertex(matrix, 0F, 1F, 0F).color(color) + buf.vertex(matrix, 1F, 1F, 0F).color(color) + buf.vertex(matrix, 1F, 1F, 1F).color(color) + buf.vertex(matrix, 0F, 1F, 1F).color(color) + // X- + buf.vertex(matrix, 0F, 0F, 0F).color(color) + buf.vertex(matrix, 0F, 0F, 1F).color(color) + buf.vertex(matrix, 0F, 1F, 1F).color(color) + buf.vertex(matrix, 0F, 1F, 0F).color(color) + // X+ + buf.vertex(matrix, 1F, 0F, 0F).color(color) + buf.vertex(matrix, 1F, 1F, 0F).color(color) + buf.vertex(matrix, 1F, 1F, 1F).color(color) + buf.vertex(matrix, 1F, 0F, 1F).color(color) + // Z- + buf.vertex(matrix, 0F, 0F, 0F).color(color) + buf.vertex(matrix, 1F, 0F, 0F).color(color) + buf.vertex(matrix, 1F, 1F, 0F).color(color) + buf.vertex(matrix, 0F, 1F, 0F).color(color) + // Z+ + buf.vertex(matrix, 0F, 0F, 1F).color(color) + buf.vertex(matrix, 0F, 1F, 1F).color(color) + buf.vertex(matrix, 1F, 1F, 1F).color(color) + buf.vertex(matrix, 1F, 0F, 1F).color(color) + } + + + fun renderInWorld(event: WorldRenderLastEvent, block: RenderInWorldContext. () -> Unit) { + // TODO: there should be *no more global state*. the only thing we should be doing is render layers. that includes settings like culling, blending, shader color, and depth testing + // For now i will let these functions remain, but this needs to go before i do a full (non-beta) release + RenderSystem.disableDepthTest() + RenderSystem.enableBlend() + RenderSystem.defaultBlendFunc() + RenderSystem.disableCull() + + event.matrices.push() + event.matrices.translate(-event.camera.pos.x, -event.camera.pos.y, -event.camera.pos.z) + + val ctx = RenderInWorldContext( + RenderSystem.renderThreadTesselator(), + event.matrices, + event.camera, + event.tickCounter, + event.vertexConsumers + ) + + block(ctx) + + event.matrices.pop() + event.vertexConsumers.draw() + RenderSystem.setShaderColor(1F, 1F, 1F, 1F) + VertexBuffer.unbind() + RenderSystem.enableDepthTest() + RenderSystem.enableCull() + RenderSystem.disableBlend() + } + } } diff --git a/src/main/resources/assets/minecraft/shaders/core/firmament_rendertype_lines.fsh b/src/main/resources/assets/firmament/shaders/core/rendertype_lines.fsh index 057f31f..057f31f 100644 --- a/src/main/resources/assets/minecraft/shaders/core/firmament_rendertype_lines.fsh +++ b/src/main/resources/assets/firmament/shaders/core/rendertype_lines.fsh diff --git a/src/main/resources/assets/minecraft/shaders/core/firmament_rendertype_lines.json b/src/main/resources/assets/firmament/shaders/core/rendertype_lines.json index 0828480..e4537ca 100644 --- a/src/main/resources/assets/minecraft/shaders/core/firmament_rendertype_lines.json +++ b/src/main/resources/assets/firmament/shaders/core/rendertype_lines.json @@ -1,6 +1,6 @@ { - "vertex": "firmament_rendertype_lines", - "fragment": "firmament_rendertype_lines", + "vertex": "firmament:core/rendertype_lines", + "fragment": "firmament:core/rendertype_lines", "samplers": [ ], "uniforms": [ @@ -8,7 +8,7 @@ { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }, { "name": "LineWidth", "type": "float", "count": 1, "values": [ 1.0 ] }, - { "name": "ScreenSize", "type": "float", "count": 2, "values": [ 1.0, 1.0 ] }, + { "name": "ScreenSize", "type": "float", "count": 2, "values": [ 1.0, 1.0 ] }, { "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] }, { "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] }, { "name": "FogColor", "type": "float", "count": 4, "values": [ 0.0, 0.0, 0.0, 0.0 ] }, diff --git a/src/main/resources/assets/minecraft/shaders/core/firmament_rendertype_lines.vsh b/src/main/resources/assets/firmament/shaders/core/rendertype_lines.vsh index b2d0f99..b2d0f99 100644 --- a/src/main/resources/assets/minecraft/shaders/core/firmament_rendertype_lines.vsh +++ b/src/main/resources/assets/firmament/shaders/core/rendertype_lines.vsh diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 9997018..9b00adf 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -33,6 +33,9 @@ "rei_client": [ "moe.nea.firmament.compat.rei.FirmamentReiPlugin" ], + "rei_common": [ + "moe.nea.firmament.compat.rei.FirmamentReiCommonPlugin" + ], "modmenu": [ "moe.nea.firmament.compat.modmenu.FirmamentModMenuPlugin" ], diff --git a/src/main/resources/firmament.accesswidener b/src/main/resources/firmament.accesswidener index 60b31e3..f1a897b 100644 --- a/src/main/resources/firmament.accesswidener +++ b/src/main/resources/firmament.accesswidener @@ -5,6 +5,7 @@ accessible class net/minecraft/client/font/TextRenderer$Drawer accessible field net/minecraft/client/gui/hud/InGameHud SCOREBOARD_ENTRY_COMPARATOR Ljava/util/Comparator; accessible field net/minecraft/client/render/item/HeldItemRenderer itemRenderer Lnet/minecraft/client/render/item/ItemRenderer; +accessible field net/minecraft/client/render/item/ItemModels missingModelSupplier Ljava/util/function/Supplier; accessible class net/minecraft/client/render/model/json/ModelOverride$Deserializer accessible class net/minecraft/client/render/model/json/ModelOverrideList$BakedOverride @@ -14,7 +15,7 @@ accessible field net/minecraft/entity/passive/AbstractHorseEntity items Lnet/min accessible field net/minecraft/entity/passive/AbstractHorseEntity SADDLED_FLAG I accessible field net/minecraft/entity/passive/AbstractHorseEntity HORSE_FLAGS Lnet/minecraft/entity/data/TrackedData; accessible method net/minecraft/resource/NamespaceResourceManager loadMetadata (Lnet/minecraft/resource/InputSupplier;)Lnet/minecraft/resource/metadata/ResourceMetadata; -accessible method net/minecraft/client/gui/DrawContext drawTexturedQuad (Lnet/minecraft/util/Identifier;IIIIIFFFFFFFF)V +accessible method net/minecraft/client/gui/DrawContext drawTexturedQuad (Ljava/util/function/Function;Lnet/minecraft/util/Identifier;IIIIFFFFI)V mutable field net/minecraft/screen/slot/Slot x I mutable field net/minecraft/screen/slot/Slot y I @@ -27,3 +28,4 @@ accessible method net/minecraft/registry/entry/RegistryEntry$Reference setRegist accessible method net/minecraft/entity/LivingEntity getHitbox ()Lnet/minecraft/util/math/Box; accessible method net/minecraft/registry/entry/RegistryEntryList$Named <init> (Lnet/minecraft/registry/entry/RegistryEntryOwner;Lnet/minecraft/registry/tag/TagKey;)V accessible method net/minecraft/registry/entry/RegistryEntry$Reference setValue (Ljava/lang/Object;)V +accessible field net/minecraft/client/render/model/WrapperBakedModel wrapped Lnet/minecraft/client/render/model/BakedModel; diff --git a/src/main/resources/firmament.mixins.json b/src/main/resources/firmament.mixins.json index dbb8290..d78d124 100644 --- a/src/main/resources/firmament.mixins.json +++ b/src/main/resources/firmament.mixins.json @@ -1,7 +1,10 @@ { - "required": true, - "plugin": "moe.nea.firmament.init.MixinPlugin", - "package": "moe.nea.firmament.mixins", - "compatibilityLevel": "JAVA_21", - "refmap": "Firmament-refmap.json" + "required": true, + "plugin": "moe.nea.firmament.init.MixinPlugin", + "package": "moe.nea.firmament.mixins", + "compatibilityLevel": "JAVA_21", + "refmap": "Firmament-refmap.json", + "injectors": { + "defaultRequire": 1 + } } |