1.21.3 WIP

This commit is contained in:
Linnea Gräf
2024-11-03 01:24:24 +01:00
parent 646843ba3b
commit 22f0cc59a2
105 changed files with 2854 additions and 2646 deletions

View File

@@ -18,7 +18,7 @@ SPDX-License-Identifier = "CC-BY-4.0"
SPDX-FileCopyrightText = ["Linnea Gräf <nea@nea.moe>", "Firmament Contributors"] SPDX-FileCopyrightText = ["Linnea Gräf <nea@nea.moe>", "Firmament Contributors"]
[[annotations]] [[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-License-Identifier = "GPL-3.0-or-later"
SPDX-FileCopyrightText = ["Linnea Gräf <nea@nea.moe>", "Firmament Contributors"] SPDX-FileCopyrightText = ["Linnea Gräf <nea@nea.moe>", "Firmament Contributors"]

View File

@@ -21,7 +21,9 @@ plugins {
alias(libs.plugins.kotlin.plugin.serialization) alias(libs.plugins.kotlin.plugin.serialization)
alias(libs.plugins.kotlin.plugin.powerassert) alias(libs.plugins.kotlin.plugin.powerassert)
alias(libs.plugins.kotlin.plugin.ksp) 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("com.github.johnrengelman.shadow") version "8.1.1"
id("moe.nea.licenseextractificator") id("moe.nea.licenseextractificator")
id("moe.nea.mc-auto-translations") version "0.0.1" 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 val unpackAllJars by tasks.registering
fun innerJarsOf(name: String, dependency: Dependency): Provider<FileTree> { fun innerJarsOf(name: String, dependency: Dependency): Provider<FileTree> {
val task = tasks.create("unpackInnerJarsFor${name.capitalizeN()}", InnerJarsUnpacker::class) { val task = tasks.create("unpackInnerJarsFor${name.capitalizeN()}", InnerJarsUnpacker::class) {
doFirst {
println("Unpacking JARs for $name")
}
this.inputJars.setFrom(files(configurations.detachedConfiguration(dependency))) this.inputJars.setFrom(files(configurations.detachedConfiguration(dependency)))
this.outputDir.set(layout.buildDirectory.dir("unpackedJars/$name").also { this.outputDir.set(layout.buildDirectory.dir("unpackedJars/$name").also {
it.get().asFile.mkdirs() it.get().asFile.mkdirs()
}) })
} }
unpackAllJars { dependsOn(task) } unpackAllJars { dependsOn(task) }
println("Constructed innerJars task: ${project.files(task).asFileTree.toList().map {it to it.exists()}}")
return project.provider { return project.provider {
project.files(task).asFileTree project.files(task).asFileTree
} }
@@ -125,15 +123,32 @@ val collectTranslations by tasks.registering(CollectTranslations::class) {
} }
val compatSourceSets: MutableSet<SourceSet> = mutableSetOf() 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) { val ss = sourceSets.create(name) {
this.java.setSrcDirs(listOf(layout.projectDirectory.dir("src/$path/java"))) if (isEnabled) {
this.kotlin.setSrcDirs(listOf(layout.projectDirectory.dir("src/$path/java"))) 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>())
}
}
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) compatSourceSets.add(ss)
loom.createRemapConfigurations(ss) loom.createRemapConfigurations(ss)
val mainSS = sourceSets.main.get() if (!isEnabled) return ss
val upperName = ss.name.capitalizeN()
configurations { configurations {
(ss.implementationConfigurationName) { (ss.implementationConfigurationName) {
extendsFrom(getByName(mainSS.compileClasspathConfigurationName)) extendsFrom(getByName(mainSS.compileClasspathConfigurationName))
@@ -148,11 +163,6 @@ fun createIsolatedSourceSet(name: String, path: String = "compat/$name"): Source
extendsFrom(ksp.get()) extendsFrom(ksp.get())
} }
} }
afterEvaluate {
tasks.named("ksp${upperName}Kotlin", KspTaskJvm::class) {
this.options.add(SubpluginOption("apoption", "firmament.sourceset=${ss.name}"))
}
}
dependencies { dependencies {
runtimeOnly(ss.output) runtimeOnly(ss.output)
(ss.implementationConfigurationName)(sourceSets.main.get().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 sodiumSourceSet = createIsolatedSourceSet("sodium")
val citResewnSourceSet = createIsolatedSourceSet("citresewn") val citResewnSourceSet = createIsolatedSourceSet("citresewn", isEnabled = false) // TODO: Wait for update
val yaclSourceSet = createIsolatedSourceSet("yacl") val yaclSourceSet = createIsolatedSourceSet("yacl")
val explosiveEnhancementSourceSet = createIsolatedSourceSet("explosiveEnhancement") val explosiveEnhancementSourceSet = createIsolatedSourceSet("explosiveEnhancement", isEnabled = false) // TODO: wait for their port
val wildfireGenderSourceSet = createIsolatedSourceSet("wildfireGender") val wildfireGenderSourceSet = createIsolatedSourceSet("wildfireGender", isEnabled = false) // TODO: wait on their port
val modmenuSourceSet = createIsolatedSourceSet("modmenu") val modmenuSourceSet = createIsolatedSourceSet("modmenu")
val reiSourceSet = createIsolatedSourceSet("rei") val reiSourceSet = createIsolatedSourceSet("rei") // TODO: read through https://hackmd.io/@shedaniel/rei17_primer
dependencies { dependencies {
// Minecraft dependencies // Minecraft dependencies
@@ -299,7 +310,7 @@ loom {
compatSourceSets.joinToString(File.pathSeparator) { compatSourceSets.joinToString(File.pathSeparator) {
File(it.output.classesDirs.asPath).absolutePath File(it.output.classesDirs.asPath).absolutePath
}) })
property("mixin.debug", "true") property("mixin.debug.export", "true")
parseEnvFile(file(".env")).forEach { (t, u) -> parseEnvFile(file(".env")).forEach { (t, u) ->
environmentVariable(t, u) environmentVariable(t, u)
@@ -360,7 +371,7 @@ tasks.shadowJar {
} }
tasks.remapJar { tasks.remapJar {
injectAccessWidener.set(true) // injectAccessWidener.set(true)
inputFile.set(tasks.shadowJar.flatMap { it.archiveFile }) inputFile.set(tasks.shadowJar.flatMap { it.archiveFile })
dependsOn(tasks.shadowJar) dependsOn(tasks.shadowJar)
archiveClassifier.set("") archiveClassifier.set("")

View File

@@ -3,36 +3,36 @@
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
[versions] [versions]
minecraft = "1.21" minecraft = "1.21.3"
# Update from https://kotlinlang.org/ # Update from https://kotlinlang.org/
kotlin = "2.0.20" kotlin = "2.0.21"
# Update from https://github.com/google/ksp/releases # 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 # Update from https://linkie.shedaniel.me/dependencies?loader=fabric
fabric_loader = "0.16.3" fabric_loader = "0.16.9"
fabric_api = "0.100.4+1.21" fabric_api = "0.107.0+1.21.3"
fabric_kotlin = "1.11.0+kotlin.2.0.0" fabric_kotlin = "1.12.3+kotlin.2.0.21"
yarn = "1.21+build.7" yarn = "1.21.3+build.2"
rei = "16.0.729" rei = "17.0.789"
modmenu = "11.0.1" modmenu = "12.0.0-beta.1"
architectury = "13.0.3" architectury = "14.0.4"
# Update from https://maven.architectury.dev/dev/architectury/loom/dev.architectury.loom.gradle.plugin/ # 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 # Update from https://modrinth.com/mod/qolify/versions?l=fabric
qolify = "1.6.0-1.21.1" qolify = "1.6.0-1.21.1"
# Update from https://modrinth.com/mod/sodium/versions?l=fabric # 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 # Update from https://modrinth.com/mod/freecam/versions?l=fabric
freecammod = "vomskVK3" freecammod = "vomskVK3"
# Update from https://modrinth.com/mod/no-chat-reports/versions?l=fabric # 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 # Update from https://modrinth.com/mod/female-gender/versions?l=fabric
femalegender = "kJmjQvAS" femalegender = "kJmjQvAS"
@@ -49,7 +49,7 @@ citresewn = "1.2.0+1.21"
devauth = "1.2.0" devauth = "1.2.0"
# Update from https://ktor.io/ # Update from https://ktor.io/
ktor = "2.3.12" ktor = "3.0.1"
# Update from https://repo.nea.moe/#/releases/moe/nea/neurepoparser # Update from https://repo.nea.moe/#/releases/moe/nea/neurepoparser
neurepoparser = "1.6.0" neurepoparser = "1.6.0"
@@ -58,13 +58,13 @@ neurepoparser = "1.6.0"
hotswap_agent = "1.4.2-SNAPSHOT" hotswap_agent = "1.4.2-SNAPSHOT"
# Update from https://github.com/LlamaLad7/MixinExtras/tags # Update from https://github.com/LlamaLad7/MixinExtras/tags
mixinextras = "0.3.5" mixinextras = "0.4.1"
jarvis = "1.1.3" jarvis = "1.1.3"
nealisp = "1.0.0" nealisp = "1.1.0"
# Update from https://github.com/NotEnoughUpdates/MoulConfig/tags # 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 # Update from https://www.curseforge.com/minecraft/mc-mods/configured/files/all?page=1&pageSize=20
configured = "5441234" configured = "5441234"
@@ -77,7 +77,7 @@ hypixelmodapi_fabric = "1.0.1+build.1+mc1.21"
manninghamMills = "2.4.1" manninghamMills = "2.4.1"
# Update from https://docs.isxander.dev/yet-another-config-lib/installing-yacl # 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/ # Update from https://maven.shedaniel.me/me/shedaniel/cloth/basic-math/0.6.1/
basicMath = "0.6.1" basicMath = "0.6.1"
@@ -120,10 +120,8 @@ basicMath = { module = "me.shedaniel.cloth:basic-math", version.ref = "basicMath
[bundles] [bundles]
runtime_required = [ runtime_required = [
"architectury_fabric",
"rei_fabric", "rei_fabric",
"notenoughanimations", # "notenoughanimations",
"femalegender",
"hypixelmodapi_fabric", "hypixelmodapi_fabric",
] ]
runtime_optional = [ runtime_optional = [
@@ -132,7 +130,7 @@ runtime_optional = [
# "sodium", # "sodium",
# "qolify", # "qolify",
"ncr", "ncr",
"citresewn", # "citresewn",
] ]
[plugins] [plugins]

View File

@@ -4,32 +4,32 @@ import me.shedaniel.math.Dimension
import me.shedaniel.math.Point import me.shedaniel.math.Point
import me.shedaniel.math.Rectangle import me.shedaniel.math.Rectangle
import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds 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.DrawContext
import net.minecraft.client.gui.Element import net.minecraft.client.gui.Element
import net.minecraft.entity.LivingEntity import net.minecraft.entity.LivingEntity
import moe.nea.firmament.gui.entity.EntityRenderer
class EntityWidget(val entity: LivingEntity, val point: Point) : WidgetWithBounds() { class EntityWidget(val entity: LivingEntity, val point: Point) : WidgetWithBounds() {
override fun children(): List<Element> { override fun children(): List<Element> {
return emptyList() return emptyList()
} }
var hasErrored = false var hasErrored = false
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
try { try {
if (!hasErrored) if (!hasErrored)
EntityRenderer.renderEntity(entity, context, point.x, point.y, mouseX.toFloat(), mouseY.toFloat()) EntityRenderer.renderEntity(entity, context, point.x, point.y, mouseX.toFloat(), mouseY.toFloat())
} catch (ex: Exception) { } catch (ex: Exception) {
EntityRenderer.logger.error("Failed to render constructed entity: $entity", ex) EntityRenderer.logger.error("Failed to render constructed entity: $entity", ex)
hasErrored = true hasErrored = true
} }
if (hasErrored) { if (hasErrored) {
context.fill(point.x, point.y, point.x + 50, point.y + 80, 0xFFAA2222.toInt()) context.fill(point.x, point.y, point.x + 50, point.y + 80, 0xFFAA2222.toInt())
} }
} }
override fun getBounds(): Rectangle { override fun getBounds(): Rectangle {
return Rectangle(point, Dimension(50, 80)) return Rectangle(point, Dimension(50, 80))
} }
} }

View File

@@ -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)
}
}

View File

@@ -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.TransferHandler
import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRegistry import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRegistry
import me.shedaniel.rei.api.common.entry.EntryStack 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 me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes
import net.minecraft.client.gui.screen.Screen import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen 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.text.Text
import net.minecraft.util.ActionResult import net.minecraft.util.ActionResult
import net.minecraft.util.Identifier 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.SBCraftingRecipe
import moe.nea.firmament.compat.rei.recipes.SBEssenceUpgradeRecipe import moe.nea.firmament.compat.rei.recipes.SBEssenceUpgradeRecipe
import moe.nea.firmament.compat.rei.recipes.SBForgeRecipe import moe.nea.firmament.compat.rei.recipes.SBForgeRecipe
import moe.nea.firmament.compat.rei.recipes.SBKatRecipe import moe.nea.firmament.compat.rei.recipes.SBKatRecipe
import moe.nea.firmament.compat.rei.recipes.SBMobDropRecipe 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.RepoManager
import moe.nea.firmament.repo.SBItemStack import moe.nea.firmament.repo.SBItemStack
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
import moe.nea.firmament.util.ScreenUtil
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.guessRecipeId import moe.nea.firmament.util.guessRecipeId
import moe.nea.firmament.util.skyblockId 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) { override fun registerCategories(registry: CategoryRegistry) {
registry.add(SBCraftingRecipe.Category) registry.add(SBCraftingRecipe.Category)

View File

@@ -21,6 +21,7 @@ class ScreenRegistryHoveredItemStackProvider : HoveredItemStackProvider {
return entryStack.value as? ItemStack ?: entryStack.cheatsAs().value return entryStack.value as? ItemStack ?: entryStack.cheatsAs().value
} }
} }
@AutoService(HoveredItemStackProvider::class) @AutoService(HoveredItemStackProvider::class)
@CompatLoader.RequireMod("roughlyenoughitems") @CompatLoader.RequireMod("roughlyenoughitems")
class OverlayHoveredItemStackProvider : HoveredItemStackProvider { class OverlayHoveredItemStackProvider : HoveredItemStackProvider {

View File

@@ -25,163 +25,128 @@ import net.minecraft.client.render.LightmapTextureManager
import net.minecraft.client.render.OverlayTexture import net.minecraft.client.render.OverlayTexture
import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.VertexConsumerProvider
import net.minecraft.client.render.model.BakedModel import net.minecraft.client.render.model.BakedModel
import net.minecraft.client.render.model.json.ModelTransformationMode
import net.minecraft.client.texture.SpriteAtlasTexture import net.minecraft.client.texture.SpriteAtlasTexture
import net.minecraft.item.Item import net.minecraft.item.ModelTransformationMode
import net.minecraft.item.ItemStack
import net.minecraft.item.tooltip.TooltipType
import moe.nea.firmament.compat.rei.FirmamentReiPlugin.Companion.asItemEntry import moe.nea.firmament.compat.rei.FirmamentReiPlugin.Companion.asItemEntry
import moe.nea.firmament.repo.SBItemStack 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> { object NEUItemEntryRenderer : EntryRenderer<SBItemStack>, BatchedEntryRenderer<SBItemStack, BakedModel> {
override fun render( override fun render(
entry: EntryStack<SBItemStack>, entry: EntryStack<SBItemStack>,
context: DrawContext, context: DrawContext,
bounds: Rectangle, bounds: Rectangle,
mouseX: Int, mouseX: Int,
mouseY: Int, mouseY: Int,
delta: Float delta: Float
) { ) {
entry.asItemEntry().render(context, bounds, mouseX, mouseY, delta) entry.asItemEntry().render(context, bounds, mouseX, mouseY, delta)
} }
val minecraft = MinecraftClient.getInstance() val minecraft = MinecraftClient.getInstance()
override fun getTooltip(entry: EntryStack<SBItemStack>, tooltipContext: TooltipContext): Tooltip? { override fun getTooltip(entry: EntryStack<SBItemStack>, tooltipContext: TooltipContext): Tooltip? {
val stack = entry.value.asImmutableItemStack() 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 { val lore = mutableListOf(stack.displayNameAccordingToNbt)
return minecraft.itemRenderer.getModel(entry.asItemEntry().value, minecraft.world, minecraft.player, 0) lore.addAll(stack.loreAccordingToNbt)
}
override fun getBatchIdentifier(entry: EntryStack<SBItemStack>?, bounds: Rectangle?, extraData: BakedModel): Int { // TODO: tags aren't sent as early now so some tooltip components that use tags will crash the game
return 1738923 + if (extraData.isSideLit) 1 else 0 // stack.getTooltip(
} // Item.TooltipContext.create(
// tooltipContext.vanillaContext().registryLookup
// ?: MC.defaultRegistries
// ),
// MC.player,
// TooltipType.BASIC
// )
return Tooltip.create(lore)
}
override fun startBatch( override fun getExtraData(entry: EntryStack<SBItemStack>): BakedModel {
entry: EntryStack<SBItemStack>, return MC.itemRenderer.getModel(entry.asItemEntry().value,
model: BakedModel, MC.world,
graphics: DrawContext, MC.player, 0)
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( override fun getBatchIdentifier(entry: EntryStack<SBItemStack>, bounds: Rectangle?, extraData: BakedModel): Int {
entry: EntryStack<SBItemStack>, return 1738923 + if (extraData.isSideLit) 1 else 0
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( override fun startBatch(entryStack: EntryStack<SBItemStack>, e: BakedModel, drawContext: DrawContext, v: Float) {
entry: EntryStack<SBItemStack>, MC.textureManager.getTexture(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE)
model: BakedModel, .setFilter(false, false)
graphics: DrawContext, RenderSystem.setShaderTexture(0, SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE)
delta: Float RenderSystem.enableBlend()
) { RenderSystem.blendFunc(SrcFactor.SRC_ALPHA, DstFactor.ONE_MINUS_SRC_ALPHA)
RenderSystem.getModelViewStack().popMatrix() RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f)
RenderSystem.applyModelViewMatrix() if (!e.isSideLit) {
this.endGL(model) DiffuseLighting.disableGuiDepthLighting()
} }
}
fun endGL(model: BakedModel) { override fun renderBase(
RenderSystem.enableDepthTest() entryStack: EntryStack<SBItemStack>,
val sideLit = model.isSideLit model: BakedModel,
if (!sideLit) { drawContext: DrawContext,
DiffuseLighting.enableGuiDepthLighting() 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 renderOverlay( override fun afterBase(entryStack: EntryStack<SBItemStack>?, e: BakedModel, drawContext: DrawContext?, v: Float) {
entry: EntryStack<SBItemStack>, RenderSystem.enableDepthTest()
extraData: BakedModel, if (!e.isSideLit)
graphics: DrawContext, DiffuseLighting.enableGuiDepthLighting()
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>) { override fun renderOverlay(
if (!entry.isEmpty) { entryStack: EntryStack<SBItemStack>,
graphics.drawItemInSlot(MinecraftClient.getInstance().textRenderer, entry.value, 0, 0, null) 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( override fun endBatch(entryStack: EntryStack<SBItemStack>?, e: BakedModel?, drawContext: DrawContext?, v: Float) {
entry: EntryStack<SBItemStack>?, }
extraData: BakedModel?,
graphics: DrawContext?,
delta: Float
) {
}
} }

View File

@@ -1,30 +1,17 @@
package moe.nea.firmament.compat.rei 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.EntrySerializer
import me.shedaniel.rei.api.common.entry.EntryStack import net.minecraft.network.RegistryByteBuf
import net.minecraft.nbt.NbtCompound import net.minecraft.network.codec.PacketCodec
import moe.nea.firmament.repo.SBItemStack import moe.nea.firmament.repo.SBItemStack
import moe.nea.firmament.util.SkyblockId
object NEUItemEntrySerializer : EntrySerializer<SBItemStack> { object NEUItemEntrySerializer : EntrySerializer<SBItemStack> {
const val SKYBLOCK_ID_ENTRY = "SKYBLOCK_ID" override fun codec(): Codec<SBItemStack> {
const val SKYBLOCK_ITEM_COUNT = "SKYBLOCK_ITEM_COUNT" return SBItemStack.CODEC
}
override fun supportSaving(): Boolean = true override fun streamCodec(): PacketCodec<RegistryByteBuf, SBItemStack> {
override fun supportReading(): Boolean = true return SBItemStack.PACKET_CODEC.cast()
}
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 save(entry: EntryStack<SBItemStack>, value: SBItemStack): NbtCompound {
return NbtCompound().apply {
putString(SKYBLOCK_ID_ENTRY, value.skyblockId.neuItem)
putInt(SKYBLOCK_ITEM_COUNT, value.getStackSize())
}
}
} }

View File

@@ -1,10 +1,8 @@
package moe.nea.firmament.compat.rei package moe.nea.firmament.compat.rei
import me.shedaniel.math.Point import me.shedaniel.math.Point
operator fun Point.plus(other: Point): Point = Point( operator fun Point.plus(other: Point): Point = Point(
this.x + other.x, this.x + other.x,
this.y + other.y, this.y + other.y,
) )

View File

@@ -1,5 +1,3 @@
package moe.nea.firmament.compat.rei.recipes package moe.nea.firmament.compat.rei.recipes
import io.github.moulberry.repo.data.NEUCraftingRecipe 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.gui.widgets.Widgets
import me.shedaniel.rei.api.client.registry.display.DisplayCategory import me.shedaniel.rei.api.client.registry.display.DisplayCategory
import me.shedaniel.rei.api.common.category.CategoryIdentifier 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 me.shedaniel.rei.api.common.util.EntryStacks
import net.minecraft.block.Blocks import net.minecraft.block.Blocks
import net.minecraft.text.Text import net.minecraft.text.Text
@@ -18,38 +18,38 @@ import moe.nea.firmament.Firmament
import moe.nea.firmament.compat.rei.SBItemEntryDefinition import moe.nea.firmament.compat.rei.SBItemEntryDefinition
class SBCraftingRecipe(override val neuRecipe: NEUCraftingRecipe) : SBRecipe() { class SBCraftingRecipe(override val neuRecipe: NEUCraftingRecipe) : SBRecipe() {
override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.catIdentifier override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.catIdentifier
object Category : DisplayCategory<SBCraftingRecipe> { object Category : DisplayCategory<SBCraftingRecipe> {
val catIdentifier = CategoryIdentifier.of<SBCraftingRecipe>(Firmament.MOD_ID, "crafing_recipe") val catIdentifier = CategoryIdentifier.of<SBCraftingRecipe>(Firmament.MOD_ID, "crafing_recipe")
override fun getCategoryIdentifier(): CategoryIdentifier<out SBCraftingRecipe> = catIdentifier override fun getCategoryIdentifier(): CategoryIdentifier<out SBCraftingRecipe> = catIdentifier
override fun getTitle(): Text = Text.literal("SkyBlock Crafting") override fun getTitle(): Text = Text.literal("SkyBlock Crafting")
override fun getIcon(): Renderer = EntryStacks.of(Blocks.CRAFTING_TABLE) override fun getIcon(): Renderer = EntryStacks.of(Blocks.CRAFTING_TABLE)
override fun setupDisplay(display: SBCraftingRecipe, bounds: Rectangle): List<Widget> { override fun setupDisplay(display: SBCraftingRecipe, bounds: Rectangle): List<Widget> {
val point = Point(bounds.centerX - 58, bounds.centerY - 27) val point = Point(bounds.centerX - 58, bounds.centerY - 27)
return buildList { return buildList {
add(Widgets.createRecipeBase(bounds)) add(Widgets.createRecipeBase(bounds))
add(Widgets.createArrow(Point(point.x + 60, point.y + 18))) add(Widgets.createArrow(Point(point.x + 60, point.y + 18)))
add(Widgets.createResultSlotBackground(Point(point.x + 95, point.y + 19))) add(Widgets.createResultSlotBackground(Point(point.x + 95, point.y + 19)))
for (i in 0 until 3) { for (i in 0 until 3) {
for (j 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() val slot = Widgets.createSlot(Point(point.x + 1 + i * 18, point.y + 1 + j * 18)).markInput()
add(slot) add(slot)
val item = display.neuRecipe.inputs[i + j * 3] val item = display.neuRecipe.inputs[i + j * 3]
if (item == NEUIngredient.SENTINEL_EMPTY) continue if (item == NEUIngredient.SENTINEL_EMPTY) continue
slot.entry(SBItemEntryDefinition.getEntry(item)) // TODO: make use of stackable item entries slot.entry(SBItemEntryDefinition.getEntry(item)) // TODO: make use of stackable item entries
} }
} }
add( add(
Widgets.createSlot(Point(point.x + 95, point.y + 19)) Widgets.createSlot(Point(point.x + 95, point.y + 19))
.entry(SBItemEntryDefinition.getEntry(display.neuRecipe.output)) .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.output))
.disableBackground().markOutput() .disableBackground().markOutput()
) )
} }
} }
} }
} }

View File

@@ -1,4 +1,3 @@
package moe.nea.firmament.compat.rei.recipes package moe.nea.firmament.compat.rei.recipes
import me.shedaniel.math.Point import me.shedaniel.math.Point
@@ -16,47 +15,47 @@ import moe.nea.firmament.repo.SBItemStack
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
class SBEssenceUpgradeRecipe(override val neuRecipe: EssenceRecipeProvider.EssenceUpgradeRecipe) : SBRecipe() { class SBEssenceUpgradeRecipe(override val neuRecipe: EssenceRecipeProvider.EssenceUpgradeRecipe) : SBRecipe() {
object Category : DisplayCategory<SBEssenceUpgradeRecipe> { object Category : DisplayCategory<SBEssenceUpgradeRecipe> {
override fun getCategoryIdentifier(): CategoryIdentifier<SBEssenceUpgradeRecipe> = override fun getCategoryIdentifier(): CategoryIdentifier<SBEssenceUpgradeRecipe> =
CategoryIdentifier.of(Firmament.MOD_ID, "essence_upgrade") CategoryIdentifier.of(Firmament.MOD_ID, "essence_upgrade")
override fun getTitle(): Text { override fun getTitle(): Text {
return Text.literal("Essence Upgrades") return Text.literal("Essence Upgrades")
} }
override fun getIcon(): Renderer { override fun getIcon(): Renderer {
return SBItemEntryDefinition.getEntry(SkyblockId("ESSENCE_WITHER")) return SBItemEntryDefinition.getEntry(SkyblockId("ESSENCE_WITHER"))
} }
override fun setupDisplay(display: SBEssenceUpgradeRecipe, bounds: Rectangle): List<Widget> { override fun setupDisplay(display: SBEssenceUpgradeRecipe, bounds: Rectangle): List<Widget> {
val recipe = display.neuRecipe val recipe = display.neuRecipe
val list = mutableListOf<Widget>() val list = mutableListOf<Widget>()
list.add(Widgets.createRecipeBase(bounds)) list.add(Widgets.createRecipeBase(bounds))
list.add(Widgets.createSlot(Point(bounds.minX + 12, bounds.centerY - 8 - 18 / 2)) list.add(Widgets.createSlot(Point(bounds.minX + 12, bounds.centerY - 8 - 18 / 2))
.markInput() .markInput()
.entry(SBItemEntryDefinition.getEntry(SBItemStack(recipe.itemId).copy(stars = recipe.starCountAfter - 1)))) .entry(SBItemEntryDefinition.getEntry(SBItemStack(recipe.itemId).copy(stars = recipe.starCountAfter - 1))))
list.add(Widgets.createSlot(Point(bounds.minX + 12, bounds.centerY - 8 + 18 / 2)) list.add(Widgets.createSlot(Point(bounds.minX + 12, bounds.centerY - 8 + 18 / 2))
.markInput() .markInput()
.entry(SBItemEntryDefinition.getEntry(recipe.essenceIngredient))) .entry(SBItemEntryDefinition.getEntry(recipe.essenceIngredient)))
list.add(Widgets.createSlot(Point(bounds.maxX - 12 - 16, bounds.centerY - 8)) list.add(Widgets.createSlot(Point(bounds.maxX - 12 - 16, bounds.centerY - 8))
.markOutput() .markOutput()
.entry(SBItemEntryDefinition.getEntry(SBItemStack(recipe.itemId).copy(stars = recipe.starCountAfter)))) .entry(SBItemEntryDefinition.getEntry(SBItemStack(recipe.itemId).copy(stars = recipe.starCountAfter))))
val extraItems = recipe.extraItems val extraItems = recipe.extraItems
list.add(Widgets.createArrow(Point(bounds.centerX - 24 / 2, list.add(Widgets.createArrow(Point(bounds.centerX - 24 / 2,
if (extraItems.isEmpty()) bounds.centerY - 17 / 2 if (extraItems.isEmpty()) bounds.centerY - 17 / 2
else bounds.centerY + 18 / 2))) else bounds.centerY + 18 / 2)))
for ((index, item) in extraItems.withIndex()) { for ((index, item) in extraItems.withIndex()) {
list.add(Widgets.createSlot( list.add(Widgets.createSlot(
Point(bounds.centerX - extraItems.size * 16 / 2 - 2 / 2 + index * 18, Point(bounds.centerX - extraItems.size * 16 / 2 - 2 / 2 + index * 18,
bounds.centerY - 18 / 2)) bounds.centerY - 18 / 2))
.markInput() .markInput()
.entry(SBItemEntryDefinition.getEntry(item))) .entry(SBItemEntryDefinition.getEntry(item)))
} }
return list return list
} }
} }
override fun getCategoryIdentifier(): CategoryIdentifier<*> { override fun getCategoryIdentifier(): CategoryIdentifier<*> {
return Category.categoryIdentifier return Category.categoryIdentifier
} }
} }

View File

@@ -1,5 +1,3 @@
package moe.nea.firmament.compat.rei.recipes package moe.nea.firmament.compat.rei.recipes
import io.github.moulberry.repo.data.NEUForgeRecipe 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 import moe.nea.firmament.compat.rei.plus
class SBForgeRecipe(override val neuRecipe: NEUForgeRecipe) : SBRecipe() { class SBForgeRecipe(override val neuRecipe: NEUForgeRecipe) : SBRecipe() {
override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier
object Category : DisplayCategory<SBForgeRecipe> { object Category : DisplayCategory<SBForgeRecipe> {
override fun getCategoryIdentifier(): CategoryIdentifier<SBForgeRecipe> = override fun getCategoryIdentifier(): CategoryIdentifier<SBForgeRecipe> =
CategoryIdentifier.of(Firmament.MOD_ID, "forge_recipe") CategoryIdentifier.of(Firmament.MOD_ID, "forge_recipe")
override fun getTitle(): Text = Text.literal("Forge Recipes") override fun getTitle(): Text = Text.literal("Forge Recipes")
override fun getDisplayHeight(): Int { override fun getDisplayHeight(): Int {
return 104 return 104
} }
override fun getIcon(): Renderer = EntryStacks.of(Blocks.ANVIL) override fun getIcon(): Renderer = EntryStacks.of(Blocks.ANVIL)
override fun setupDisplay(display: SBForgeRecipe, bounds: Rectangle): List<Widget> { override fun setupDisplay(display: SBForgeRecipe, bounds: Rectangle): List<Widget> {
return buildList { return buildList {
add(Widgets.createRecipeBase(bounds)) add(Widgets.createRecipeBase(bounds))
add(Widgets.createResultSlotBackground(Point(bounds.minX + 124, bounds.minY + 46))) add(Widgets.createResultSlotBackground(Point(bounds.minX + 124, bounds.minY + 46)))
val arrow = Widgets.createArrow(Point(bounds.minX + 90, bounds.minY + 54 - 18 / 2)) val arrow = Widgets.createArrow(Point(bounds.minX + 90, bounds.minY + 54 - 18 / 2))
add(arrow) add(arrow)
add(Widgets.createTooltip(arrow.bounds, Text.stringifiedTranslatable("firmament.recipe.forge.time", display.neuRecipe.duration.seconds))) add(Widgets.createTooltip(arrow.bounds,
val ingredientsCenter = Point(bounds.minX + 49 - 8, bounds.minY + 54 - 8) Text.stringifiedTranslatable("firmament.recipe.forge.time",
val count = display.neuRecipe.inputs.size display.neuRecipe.duration.seconds)))
if (count == 1) { val ingredientsCenter = Point(bounds.minX + 49 - 8, bounds.minY + 54 - 8)
add( val count = display.neuRecipe.inputs.size
Widgets.createSlot(Point(ingredientsCenter.x, ingredientsCenter.y)).markInput() if (count == 1) {
.entry(SBItemEntryDefinition.getEntry(display.neuRecipe.inputs.single())) add(
) Widgets.createSlot(Point(ingredientsCenter.x, ingredientsCenter.y)).markInput()
} else { .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.inputs.single()))
display.neuRecipe.inputs.forEachIndexed { idx, ingredient -> )
val rad = Math.PI * 2 * idx / count } else {
add( display.neuRecipe.inputs.forEachIndexed { idx, ingredient ->
Widgets.createSlot( val rad = Math.PI * 2 * idx / count
Point( add(
cos(rad) * 30, Widgets.createSlot(
sin(rad) * 30, Point(
) + ingredientsCenter cos(rad) * 30,
).markInput().entry(SBItemEntryDefinition.getEntry(ingredient)) 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)) add(
) Widgets.createSlot(Point(bounds.minX + 124, bounds.minY + 46)).markOutput().disableBackground()
} .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.outputStack))
} )
} }
}
}
} }

View File

@@ -1,4 +1,3 @@
package moe.nea.firmament.compat.rei.recipes package moe.nea.firmament.compat.rei.recipes
import io.github.moulberry.repo.data.NEUKatUpgradeRecipe 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.category.CategoryIdentifier
import me.shedaniel.rei.api.common.util.EntryStacks import me.shedaniel.rei.api.common.util.EntryStacks
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
import net.minecraft.block.Blocks
import net.minecraft.client.gui.DrawContext import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.Element import net.minecraft.client.gui.Element
import net.minecraft.item.Items import net.minecraft.item.Items
@@ -34,191 +32,191 @@ import moe.nea.firmament.util.MC
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
class SBKatRecipe(override val neuRecipe: NEUKatUpgradeRecipe) : SBRecipe() { class SBKatRecipe(override val neuRecipe: NEUKatUpgradeRecipe) : SBRecipe() {
override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier
object Category : DisplayCategory<SBKatRecipe> { object Category : DisplayCategory<SBKatRecipe> {
override fun getCategoryIdentifier(): CategoryIdentifier<SBKatRecipe> = override fun getCategoryIdentifier(): CategoryIdentifier<SBKatRecipe> =
CategoryIdentifier.of(Firmament.MOD_ID, "kat_recipe") CategoryIdentifier.of(Firmament.MOD_ID, "kat_recipe")
override fun getTitle(): Text = Text.literal("Kat Pet Upgrade") override fun getTitle(): Text = Text.literal("Kat Pet Upgrade")
override fun getDisplayHeight(): Int { override fun getDisplayHeight(): Int {
return 100 return 100
} }
override fun getIcon(): Renderer = EntryStacks.of(Items.BONE) override fun getIcon(): Renderer = EntryStacks.of(Items.BONE)
override fun setupDisplay(display: SBKatRecipe, bounds: Rectangle): List<Widget> { override fun setupDisplay(display: SBKatRecipe, bounds: Rectangle): List<Widget> {
return buildList { return buildList {
val arrowWidth = 24 val arrowWidth = 24
val recipe = display.neuRecipe val recipe = display.neuRecipe
val levelValue = Property.upgrade(GetSetter.floating(0F)) val levelValue = Property.upgrade(GetSetter.floating(0F))
val slider = SliderComponent(levelValue, 1F, 100F, 1f, 100) val slider = SliderComponent(levelValue, 1F, 100F, 1f, 100)
val outputStack = SBItemStack(SkyblockId(recipe.output.itemId)) val outputStack = SBItemStack(SkyblockId(recipe.output.itemId))
val inputStack = SBItemStack(SkyblockId(recipe.input.itemId)) val inputStack = SBItemStack(SkyblockId(recipe.input.itemId))
val inputLevelLabelCenter = Point(bounds.minX + 30 - 18 + 5 + 8, bounds.minY + 25) val inputLevelLabelCenter = Point(bounds.minX + 30 - 18 + 5 + 8, bounds.minY + 25)
val inputLevelLabel = Widgets.createLabel( val inputLevelLabel = Widgets.createLabel(
inputLevelLabelCenter, inputLevelLabelCenter,
Text.literal("")).centered() Text.literal("")).centered()
val outputLevelLabelCenter = Point(bounds.maxX - 30 + 8, bounds.minY + 25) val outputLevelLabelCenter = Point(bounds.maxX - 30 + 8, bounds.minY + 25)
val outputLevelLabel = Widgets.createLabel( val outputLevelLabel = Widgets.createLabel(
outputLevelLabelCenter, outputLevelLabelCenter,
Text.literal("")).centered() Text.literal("")).centered()
val coinStack = SBItemStack(SkyblockId.COINS, recipe.coins.toInt()) val coinStack = SBItemStack(SkyblockId.COINS, recipe.coins.toInt())
levelValue.whenChanged { oldValue, newValue -> levelValue.whenChanged { oldValue, newValue ->
if (oldValue.toInt() == newValue.toInt()) return@whenChanged if (oldValue.toInt() == newValue.toInt()) return@whenChanged
val oldInput = inputStack.getPetData() ?: return@whenChanged val oldInput = inputStack.getPetData() ?: return@whenChanged
val newInput = PetData.forLevel(oldInput.petId, oldInput.rarity, newValue.toInt()) val newInput = PetData.forLevel(oldInput.petId, oldInput.rarity, newValue.toInt())
inputStack.setPetData(newInput) inputStack.setPetData(newInput)
val oldOutput = outputStack.getPetData() ?: return@whenChanged val oldOutput = outputStack.getPetData() ?: return@whenChanged
val newOutput = PetData(oldOutput.rarity, oldOutput.petId, newInput.exp) val newOutput = PetData(oldOutput.rarity, oldOutput.petId, newInput.exp)
outputStack.setPetData(newOutput) outputStack.setPetData(newOutput)
inputLevelLabel.message = Text.literal(newInput.levelData.currentLevel.toString()) inputLevelLabel.message = Text.literal(newInput.levelData.currentLevel.toString())
inputLevelLabel.bounds.location = Point( inputLevelLabel.bounds.location = Point(
inputLevelLabelCenter.x - MC.font.getWidth(inputLevelLabel.message) / 2, inputLevelLabelCenter.x - MC.font.getWidth(inputLevelLabel.message) / 2,
inputLevelLabelCenter.y) inputLevelLabelCenter.y)
outputLevelLabel.message = Text.literal(newOutput.levelData.currentLevel.toString()) outputLevelLabel.message = Text.literal(newOutput.levelData.currentLevel.toString())
outputLevelLabel.bounds.location = Point( outputLevelLabel.bounds.location = Point(
outputLevelLabelCenter.x - MC.font.getWidth(outputLevelLabel.message) / 2, outputLevelLabelCenter.x - MC.font.getWidth(outputLevelLabel.message) / 2,
outputLevelLabelCenter.y) outputLevelLabelCenter.y)
coinStack.setStackSize((recipe.coins * (1 - 0.3 * newValue / 100)).toInt()) coinStack.setStackSize((recipe.coins * (1 - 0.3 * newValue / 100)).toInt())
} }
levelValue.set(1F) levelValue.set(1F)
add(Widgets.createRecipeBase(bounds)) add(Widgets.createRecipeBase(bounds))
add(wrapWidget(Rectangle(bounds.centerX - slider.width / 2, add(wrapWidget(Rectangle(bounds.centerX - slider.width / 2,
bounds.maxY - 30, bounds.maxY - 30,
slider.width, slider.width,
slider.height), slider.height),
slider)) slider))
add(Widgets.withTooltip( add(Widgets.withTooltip(
Widgets.createArrow(Point(bounds.centerX - arrowWidth / 2, bounds.minY + 40)), Widgets.createArrow(Point(bounds.centerX - arrowWidth / 2, bounds.minY + 40)),
Text.literal("Upgrade time: " + FirmFormatters.formatTimespan(recipe.seconds.seconds)))) Text.literal("Upgrade time: " + FirmFormatters.formatTimespan(recipe.seconds.seconds))))
add(Widgets.createResultSlotBackground(Point(bounds.maxX - 30, bounds.minY + 40))) add(Widgets.createResultSlotBackground(Point(bounds.maxX - 30, bounds.minY + 40)))
add(inputLevelLabel) add(inputLevelLabel)
add(outputLevelLabel) add(outputLevelLabel)
add(Widgets.createSlot(Point(bounds.maxX - 30, bounds.minY + 40)).markOutput().disableBackground() add(Widgets.createSlot(Point(bounds.maxX - 30, bounds.minY + 40)).markOutput().disableBackground()
.entry(SBItemEntryDefinition.getEntry(outputStack))) .entry(SBItemEntryDefinition.getEntry(outputStack)))
add(Widgets.createSlot(Point(bounds.minX + 30 - 18 + 5, bounds.minY + 40)).markInput() add(Widgets.createSlot(Point(bounds.minX + 30 - 18 + 5, bounds.minY + 40)).markInput()
.entry(SBItemEntryDefinition.getEntry(inputStack))) .entry(SBItemEntryDefinition.getEntry(inputStack)))
val allInputs = recipe.items.map { SBItemEntryDefinition.getEntry(it) } + val allInputs = recipe.items.map { SBItemEntryDefinition.getEntry(it) } +
listOf(SBItemEntryDefinition.getEntry(coinStack)) listOf(SBItemEntryDefinition.getEntry(coinStack))
for ((index, item) in allInputs.withIndex()) { for ((index, item) in allInputs.withIndex()) {
add(Widgets.createSlot( add(Widgets.createSlot(
Point(bounds.centerX + index * 20 - allInputs.size * 18 / 2 - (allInputs.size - 1) * 2 / 2, Point(bounds.centerX + index * 20 - allInputs.size * 18 / 2 - (allInputs.size - 1) * 2 / 2,
bounds.minY + 20)) bounds.minY + 20))
.markInput() .markInput()
.entry(item)) .entry(item))
} }
} }
} }
} }
} }
fun wrapWidget(bounds: Rectangle, component: GuiComponent): Widget { fun wrapWidget(bounds: Rectangle, component: GuiComponent): Widget {
return object : WidgetWithBounds() { return object : WidgetWithBounds() {
override fun getBounds(): Rectangle { override fun getBounds(): Rectangle {
return bounds return bounds
} }
override fun children(): List<Element> { override fun children(): List<Element> {
return listOf() return listOf()
} }
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
context.matrices.push() context.matrices.push()
context.matrices.translate(bounds.minX.toFloat(), bounds.minY.toFloat(), 0F) context.matrices.translate(bounds.minX.toFloat(), bounds.minY.toFloat(), 0F)
component.render( component.render(
GuiImmediateContext( GuiImmediateContext(
ModernRenderContext(context), ModernRenderContext(context),
bounds.minX, bounds.minY, bounds.minX, bounds.minY,
bounds.width, bounds.height, bounds.width, bounds.height,
mouseX - bounds.minX, mouseY - bounds.minY, mouseX - bounds.minX, mouseY - bounds.minY,
mouseX, mouseY, mouseX, mouseY,
mouseX.toFloat(), mouseY.toFloat() mouseX.toFloat(), mouseY.toFloat()
)) ))
context.matrices.pop() context.matrices.pop()
} }
override fun mouseMoved(mouseX: Double, mouseY: Double) { override fun mouseMoved(mouseX: Double, mouseY: Double) {
val mouseXInt = mouseX.toInt() val mouseXInt = mouseX.toInt()
val mouseYInt = mouseY.toInt() val mouseYInt = mouseY.toInt()
component.mouseEvent(MouseEvent.Move(0F, 0F), component.mouseEvent(MouseEvent.Move(0F, 0F),
GuiImmediateContext( GuiImmediateContext(
IMinecraft.instance.provideTopLevelRenderContext(), IMinecraft.instance.provideTopLevelRenderContext(),
bounds.minX, bounds.minY, bounds.minX, bounds.minY,
bounds.width, bounds.height, bounds.width, bounds.height,
mouseXInt - bounds.minX, mouseYInt - bounds.minY, mouseXInt - bounds.minX, mouseYInt - bounds.minY,
mouseXInt, mouseYInt, mouseXInt, mouseYInt,
mouseX.toFloat(), mouseY.toFloat() mouseX.toFloat(), mouseY.toFloat()
)) ))
} }
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
val mouseXInt = mouseX.toInt() val mouseXInt = mouseX.toInt()
val mouseYInt = mouseY.toInt() val mouseYInt = mouseY.toInt()
return component.mouseEvent(MouseEvent.Click(button, true), return component.mouseEvent(MouseEvent.Click(button, true),
GuiImmediateContext( GuiImmediateContext(
IMinecraft.instance.provideTopLevelRenderContext(), IMinecraft.instance.provideTopLevelRenderContext(),
bounds.minX, bounds.minY, bounds.minX, bounds.minY,
bounds.width, bounds.height, bounds.width, bounds.height,
mouseXInt - bounds.minX, mouseYInt - bounds.minY, mouseXInt - bounds.minX, mouseYInt - bounds.minY,
mouseXInt, mouseYInt, mouseXInt, mouseYInt,
mouseX.toFloat(), mouseY.toFloat() mouseX.toFloat(), mouseY.toFloat()
)) ))
} }
override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean {
val mouseXInt = mouseX.toInt() val mouseXInt = mouseX.toInt()
val mouseYInt = mouseY.toInt() val mouseYInt = mouseY.toInt()
return component.mouseEvent(MouseEvent.Click(button, false), return component.mouseEvent(MouseEvent.Click(button, false),
GuiImmediateContext( GuiImmediateContext(
IMinecraft.instance.provideTopLevelRenderContext(), IMinecraft.instance.provideTopLevelRenderContext(),
bounds.minX, bounds.minY, bounds.minX, bounds.minY,
bounds.width, bounds.height, bounds.width, bounds.height,
mouseXInt - bounds.minX, mouseYInt - bounds.minY, mouseXInt - bounds.minX, mouseYInt - bounds.minY,
mouseXInt, mouseYInt, mouseXInt, mouseYInt,
mouseX.toFloat(), mouseY.toFloat() mouseX.toFloat(), mouseY.toFloat()
)) ))
} }
override fun mouseDragged( override fun mouseDragged(
mouseX: Double, mouseX: Double,
mouseY: Double, mouseY: Double,
button: Int, button: Int,
deltaX: Double, deltaX: Double,
deltaY: Double deltaY: Double
): Boolean { ): Boolean {
val mouseXInt = mouseX.toInt() val mouseXInt = mouseX.toInt()
val mouseYInt = mouseY.toInt() val mouseYInt = mouseY.toInt()
return component.mouseEvent(MouseEvent.Move(deltaX.toFloat(), deltaY.toFloat()), return component.mouseEvent(MouseEvent.Move(deltaX.toFloat(), deltaY.toFloat()),
GuiImmediateContext( GuiImmediateContext(
IMinecraft.instance.provideTopLevelRenderContext(), IMinecraft.instance.provideTopLevelRenderContext(),
bounds.minX, bounds.minY, bounds.minX, bounds.minY,
bounds.width, bounds.height, bounds.width, bounds.height,
mouseXInt - bounds.minX, mouseYInt - bounds.minY, mouseXInt - bounds.minX, mouseYInt - bounds.minY,
mouseXInt, mouseYInt, mouseXInt, mouseYInt,
mouseX.toFloat(), mouseY.toFloat() mouseX.toFloat(), mouseY.toFloat()
)) ))
} }
override fun mouseScrolled( override fun mouseScrolled(
mouseX: Double, mouseX: Double,
mouseY: Double, mouseY: Double,
horizontalAmount: Double, horizontalAmount: Double,
verticalAmount: Double verticalAmount: Double
): Boolean { ): Boolean {
val mouseXInt = mouseX.toInt() val mouseXInt = mouseX.toInt()
val mouseYInt = mouseY.toInt() val mouseYInt = mouseY.toInt()
return component.mouseEvent(MouseEvent.Scroll(verticalAmount.toFloat()), return component.mouseEvent(MouseEvent.Scroll(verticalAmount.toFloat()),
GuiImmediateContext( GuiImmediateContext(
IMinecraft.instance.provideTopLevelRenderContext(), IMinecraft.instance.provideTopLevelRenderContext(),
bounds.minX, bounds.minY, bounds.minX, bounds.minY,
bounds.width, bounds.height, bounds.width, bounds.height,
mouseXInt - bounds.minX, mouseYInt - bounds.minY, mouseXInt - bounds.minX, mouseYInt - bounds.minY,
mouseXInt, mouseYInt, mouseXInt, mouseYInt,
mouseX.toFloat(), mouseY.toFloat() mouseX.toFloat(), mouseY.toFloat()
)) ))
} }
} }
} }

View File

@@ -1,4 +1,3 @@
package moe.nea.firmament.compat.rei.recipes package moe.nea.firmament.compat.rei.recipes
import io.github.moulberry.repo.data.NEUMobDropRecipe import io.github.moulberry.repo.data.NEUMobDropRecipe
@@ -14,95 +13,95 @@ import net.minecraft.item.Items
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import moe.nea.firmament.Firmament 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.EntityWidget
import moe.nea.firmament.compat.rei.SBItemEntryDefinition import moe.nea.firmament.compat.rei.SBItemEntryDefinition
import moe.nea.firmament.gui.entity.EntityRenderer
class SBMobDropRecipe(override val neuRecipe: NEUMobDropRecipe) : SBRecipe() { class SBMobDropRecipe(override val neuRecipe: NEUMobDropRecipe) : SBRecipe() {
override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier
object Category : DisplayCategory<SBMobDropRecipe> { object Category : DisplayCategory<SBMobDropRecipe> {
override fun getCategoryIdentifier(): CategoryIdentifier<SBMobDropRecipe> = override fun getCategoryIdentifier(): CategoryIdentifier<SBMobDropRecipe> =
CategoryIdentifier.of(Firmament.MOD_ID, "mob_drop_recipe") CategoryIdentifier.of(Firmament.MOD_ID, "mob_drop_recipe")
override fun getTitle(): Text = Text.literal("Mob Drops") override fun getTitle(): Text = Text.literal("Mob Drops")
override fun getDisplayHeight(): Int { override fun getDisplayHeight(): Int {
return 100 return 100
} }
override fun getIcon(): Renderer = EntryStacks.of(Items.DIAMOND_SWORD) override fun getIcon(): Renderer = EntryStacks.of(Items.DIAMOND_SWORD)
override fun setupDisplay(display: SBMobDropRecipe, bounds: Rectangle): List<Widget> { override fun setupDisplay(display: SBMobDropRecipe, bounds: Rectangle): List<Widget> {
return buildList { return buildList {
add(Widgets.createRecipeBase(bounds)) add(Widgets.createRecipeBase(bounds))
val source = display.neuRecipe.render val source = display.neuRecipe.render
val entity = if (source.startsWith("@")) { val entity = if (source.startsWith("@")) {
EntityRenderer.constructEntity(Identifier.of(source.substring(1))) EntityRenderer.constructEntity(Identifier.of(source.substring(1)))
} else { } else {
EntityRenderer.applyModifiers(source, listOf()) EntityRenderer.applyModifiers(source, listOf())
} }
if (entity != null) { if (entity != null) {
val level = display.neuRecipe.level val level = display.neuRecipe.level
val fullMobName = val fullMobName =
if (level > 0) Text.translatable("firmament.recipe.mobs.name", level, display.neuRecipe.name) if (level > 0) Text.translatable("firmament.recipe.mobs.name", level, display.neuRecipe.name)
else Text.translatable("firmament.recipe.mobs.name.nolevel", display.neuRecipe.name) else Text.translatable("firmament.recipe.mobs.name.nolevel", display.neuRecipe.name)
val tt = mutableListOf<Text>() val tt = mutableListOf<Text>()
tt.add((fullMobName)) tt.add((fullMobName))
tt.add(Text.literal("")) tt.add(Text.literal(""))
if (display.neuRecipe.coins > 0) { if (display.neuRecipe.coins > 0) {
tt.add(Text.stringifiedTranslatable("firmament.recipe.mobs.coins", display.neuRecipe.coins)) tt.add(Text.stringifiedTranslatable("firmament.recipe.mobs.coins", display.neuRecipe.coins))
} }
if (display.neuRecipe.combatExperience > 0) { if (display.neuRecipe.combatExperience > 0) {
tt.add( tt.add(
Text.stringifiedTranslatable( Text.stringifiedTranslatable(
"firmament.recipe.mobs.combat", "firmament.recipe.mobs.combat",
display.neuRecipe.combatExperience display.neuRecipe.combatExperience
) )
) )
} }
if (display.neuRecipe.enchantingExperience > 0) { if (display.neuRecipe.enchantingExperience > 0) {
tt.add( tt.add(
Text.stringifiedTranslatable( Text.stringifiedTranslatable(
"firmament.recipe.mobs.exp", "firmament.recipe.mobs.exp",
display.neuRecipe.enchantingExperience display.neuRecipe.enchantingExperience
) )
) )
} }
if (display.neuRecipe.extra != null) if (display.neuRecipe.extra != null)
display.neuRecipe.extra.mapTo(tt) { Text.literal(it) } display.neuRecipe.extra.mapTo(tt) { Text.literal(it) }
if (tt.size == 2) if (tt.size == 2)
tt.removeAt(1) tt.removeAt(1)
add( add(
Widgets.withTooltip( Widgets.withTooltip(
EntityWidget(entity, Point(bounds.minX + 5, bounds.minY + 15)), EntityWidget(entity, Point(bounds.minX + 5, bounds.minY + 15)),
tt tt
) )
) )
} }
add( add(
Widgets.createLabel(Point(bounds.minX + 15, bounds.minY + 5), Text.literal(display.neuRecipe.name)) Widgets.createLabel(Point(bounds.minX + 15, bounds.minY + 5), Text.literal(display.neuRecipe.name))
.leftAligned() .leftAligned()
) )
var x = bounds.minX + 60 var x = bounds.minX + 60
var y = bounds.minY + 20 var y = bounds.minY + 20
for (drop in display.neuRecipe.drops) { for (drop in display.neuRecipe.drops) {
val lore = drop.extra.mapTo(mutableListOf()) { Text.literal(it) } val lore = drop.extra.mapTo(mutableListOf()) { Text.literal(it) }
if (drop.chance != null) { if (drop.chance != null) {
lore += listOf(Text.translatable("firmament.recipe.mobs.drops", drop.chance)) lore += listOf(Text.translatable("firmament.recipe.mobs.drops", drop.chance))
} }
val item = SBItemEntryDefinition.getEntry(drop.dropItem) val item = SBItemEntryDefinition.getEntry(drop.dropItem)
.value.copy(extraLore = lore) .value.copy(extraLore = lore)
add( add(
Widgets.createSlot(Point(x, y)).markOutput() Widgets.createSlot(Point(x, y)).markOutput()
.entries(listOf(SBItemEntryDefinition.getEntry(item))) .entries(listOf(SBItemEntryDefinition.getEntry(item)))
) )
x += 18 x += 18
if (x > bounds.maxX - 30) { if (x > bounds.maxX - 30) {
x = bounds.minX + 60 x = bounds.minX + 60
y += 18 y += 18
} }
} }
} }
} }
} }
} }

View File

@@ -2,28 +2,41 @@ package moe.nea.firmament.compat.rei.recipes
import io.github.moulberry.repo.data.NEUIngredient import io.github.moulberry.repo.data.NEUIngredient
import io.github.moulberry.repo.data.NEURecipe 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.Display
import me.shedaniel.rei.api.common.display.DisplaySerializer
import me.shedaniel.rei.api.common.entry.EntryIngredient import me.shedaniel.rei.api.common.entry.EntryIngredient
import net.minecraft.util.Identifier
import moe.nea.firmament.compat.rei.SBItemEntryDefinition import moe.nea.firmament.compat.rei.SBItemEntryDefinition
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
abstract class SBRecipe : Display { abstract class SBRecipe : Display {
abstract val neuRecipe: NEURecipe override fun getDisplayLocation(): Optional<Identifier> {
override fun getInputEntries(): List<EntryIngredient> { // 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 neuRecipe.allInputs return Optional.empty()
.filter { it.itemId != NEUIngredient.NEU_SENTINEL_EMPTY } }
.map {
val entryStack = SBItemEntryDefinition.getEntry(SkyblockId(it.itemId))
EntryIngredient.of(entryStack)
}
}
override fun getOutputEntries(): List<EntryIngredient> { override fun getSerializer(): DisplaySerializer<out Display>? {
return neuRecipe.allOutputs // While returning null here is discouraged, we are fine to do so, since this recipe will never travel through the network
.filter { it.itemId != NEUIngredient.NEU_SENTINEL_EMPTY } return null
.map { }
val entryStack = SBItemEntryDefinition.getEntry(SkyblockId(it.itemId))
EntryIngredient.of(entryStack) 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)
}
}
} }

View File

@@ -1,6 +1,6 @@
package moe.nea.firmament.compat.sodium 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 import moe.nea.firmament.mixins.accessor.sodium.AccessorSodiumWorldRenderer
class SodiumChunkReloader : Runnable { class SodiumChunkReloader : Runnable {

View File

@@ -1,7 +1,7 @@
package moe.nea.firmament.mixins.accessor.sodium; package moe.nea.firmament.mixins.accessor.sodium;
import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer; import net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer;
import me.jellysquid.mods.sodium.client.render.chunk.RenderSectionManager; import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Accessor;
@@ -9,6 +9,6 @@ import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(SodiumWorldRenderer.class) @Mixin(SodiumWorldRenderer.class)
@Pseudo @Pseudo
public interface AccessorSodiumWorldRenderer { public interface AccessorSodiumWorldRenderer {
@Accessor(value = "renderSectionManager", remap = false) @Accessor(value = "renderSectionManager", remap = false)
RenderSectionManager getRenderSectionManager_firmament(); RenderSectionManager getRenderSectionManager_firmament();
} }

View File

@@ -3,8 +3,8 @@ package moe.nea.firmament.mixins.custommodels;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local; 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 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.block.BlockState;
import net.minecraft.client.render.block.BlockModels; import net.minecraft.client.render.block.BlockModels;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
@@ -15,7 +15,7 @@ import org.spongepowered.asm.mixin.injection.At;
@Mixin(ChunkBuilderMeshingTask.class) @Mixin(ChunkBuilderMeshingTask.class)
public class PatchBlockModelInSodiumChunkGenerator { public class PatchBlockModelInSodiumChunkGenerator {
@WrapOperation( @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;")) 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, private BakedModel replaceBlockModel(BlockModels instance, BlockState state, Operation<BakedModel> original,
@Local(name = "blockPos") BlockPos.Mutable pos) { @Local(name = "blockPos") BlockPos.Mutable pos) {

View File

@@ -14,7 +14,7 @@ import org.spongepowered.asm.mixin.injection.At;
@Mixin(DrawContext.class) @Mixin(DrawContext.class)
public class CustomDurabilityBarPatch { public class CustomDurabilityBarPatch {
@WrapOperation( @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") at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isItemBarVisible()Z")
) )
private boolean onIsItemBarVisible( private boolean onIsItemBarVisible(
@@ -29,7 +29,7 @@ public class CustomDurabilityBarPatch {
return barOverride.get() != null; 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")) at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getItemBarStep()I"))
private int overrideItemStep( private int overrideItemStep(
ItemStack instance, Operation<Integer> original, ItemStack instance, Operation<Integer> original,
@@ -40,7 +40,7 @@ public class CustomDurabilityBarPatch {
return original.call(instance); 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")) at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getItemBarColor()I"))
private int overrideItemColor( private int overrideItemColor(
ItemStack instance, Operation<Integer> original, ItemStack instance, Operation<Integer> original,

View File

@@ -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));
}
}

View File

@@ -7,6 +7,7 @@ import net.minecraft.client.render.item.ItemModels;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedModelManager; import net.minecraft.client.render.model.BakedModelManager;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; 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.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Map;
@Mixin(ItemModels.class) @Mixin(ItemModels.class)
public class CustomModelEventPatch { 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) @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) { public void onGetModel(ItemStack stack, CallbackInfoReturnable<BakedModel> cir) {
var model = CustomItemModelEvent.getModel(stack, modelManager); var model = CustomItemModelEvent.getModel(stack, (ItemModels) (Object) this);
if (model != null) if (model != null)
cir.setReturnValue(model); cir.setReturnValue(model);
} }
} }

View File

@@ -2,7 +2,6 @@
package moe.nea.firmament.mixins; package moe.nea.firmament.mixins;
import com.mojang.authlib.GameProfile;
import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures; import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures;
import net.minecraft.block.SkullBlock; import net.minecraft.block.SkullBlock;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
@@ -15,8 +14,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(SkullBlockEntityRenderer.class) @Mixin(SkullBlockEntityRenderer.class)
public class CustomSkullTexturePatch { public class CustomSkullTexturePatch {
@Inject(method = "getRenderLayer", at = @At("HEAD"), cancellable = true) @Inject(method = "getRenderLayer", at = @At("HEAD"), cancellable = true)
private static void onGetRenderLayer(SkullBlock.SkullType type, ProfileComponent profile, CallbackInfoReturnable<RenderLayer> cir) { private static void onGetRenderLayer(SkullBlock.SkullType type, ProfileComponent profile, CallbackInfoReturnable<RenderLayer> cir) {
CustomSkyBlockTextures.INSTANCE.modifySkullTexture(type, profile, cir); CustomSkyBlockTextures.INSTANCE.modifySkullTexture(type, profile, cir);
} }
} }

View File

@@ -17,6 +17,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Map; import java.util.Map;
// TODO: rework this
@Mixin(EntityIdFix.class) @Mixin(EntityIdFix.class)
public abstract class DFUEntityIdFixPatch extends DataFix { public abstract class DFUEntityIdFixPatch extends DataFix {
@Shadow @Shadow

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -2,6 +2,9 @@
package moe.nea.firmament.mixins; 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 moe.nea.firmament.events.*;
import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.ingame.HandledScreen; 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.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import java.util.Iterator;
@Mixin(HandledScreen.class) @Mixin(HandledScreen.class)
public abstract class MixinHandledScreen<T extends ScreenHandler> { 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) @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 onAfterDrawSlot(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci, int i, int j, int k, Slot slot) { public void onDrawSlots(HandledScreen instance, DrawContext context, Slot slot, Operation<Void> original) {
SlotRenderEvents.After event = new SlotRenderEvents.After(context, slot, mouseX, mouseY, delta); var before = new SlotRenderEvents.Before(context, slot);
SlotRenderEvents.After.Companion.publish(event); SlotRenderEvents.Before.Companion.publish(before);
} original.call(instance, context, slot);
var after = new SlotRenderEvents.After(context, slot);
@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) SlotRenderEvents.After.Companion.publish(after);
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);
} }
} }

View File

@@ -20,12 +20,16 @@ import org.spongepowered.asm.mixin.injection.At;
AnvilScreen.class, BeaconScreen.class}) AnvilScreen.class, BeaconScreen.class})
public class ReplaceTextColorInHandledScreen { 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( @WrapOperation(
method = "drawForeground", method = "drawForeground",
at = @At( at = @At(
value = "INVOKE", value = "INVOKE",
target = "Lnet/minecraft/client/gui/DrawContext;drawText(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/Text;IIIZ)I"), 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) { 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); return original.call(instance, textRenderer, text, x, y, CustomTextColors.INSTANCE.mapTextColor(text, color), shadow);
} }
@@ -35,7 +39,8 @@ public class ReplaceTextColorInHandledScreen {
at = @At( at = @At(
value = "INVOKE", value = "INVOKE",
target = "Lnet/minecraft/client/gui/DrawContext;drawTextWithShadow(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/Text;III)I"), 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) { 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)); return original.call(instance, textRenderer, text, x, y, CustomTextColors.INSTANCE.mapTextColor(text, color));
} }

View File

@@ -23,14 +23,13 @@ public abstract class SlotUpdateListener extends ClientCommonNetworkHandler {
@Inject( @Inject(
method = "onScreenHandlerSlotUpdate", 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( private void onSingleSlotUpdate(
ScreenHandlerSlotUpdateS2CPacket packet, ScreenHandlerSlotUpdateS2CPacket packet,
CallbackInfo ci) { CallbackInfo ci) {
var player = this.client.player; var player = this.client.player;
assert player != null; assert player != null;
if (packet.getSyncId() == ScreenHandlerSlotUpdateS2CPacket.UPDATE_PLAYER_INVENTORY_SYNC_ID if (packet.getSyncId() == 0) {
|| packet.getSyncId() == 0) {
PlayerInventoryUpdate.Companion.publish(new PlayerInventoryUpdate.Single(packet.getSlot(), packet.getStack())); PlayerInventoryUpdate.Companion.publish(new PlayerInventoryUpdate.Single(packet.getSlot(), packet.getStack()));
} else if (packet.getSyncId() == player.currentScreenHandler.syncId) { } else if (packet.getSyncId() == player.currentScreenHandler.syncId) {
ChestInventoryUpdateEvent.Companion.publish( ChestInventoryUpdateEvent.Companion.publish(
@@ -40,8 +39,7 @@ public abstract class SlotUpdateListener extends ClientCommonNetworkHandler {
} }
@Inject(method = "onInventory", @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", at = @At("TAIL"))
shift = At.Shift.AFTER))
private void onMultiSlotUpdate(InventoryS2CPacket packet, CallbackInfo ci) { private void onMultiSlotUpdate(InventoryS2CPacket packet, CallbackInfo ci) {
var player = this.client.player; var player = this.client.player;
assert player != null; assert player != null;

View File

@@ -3,16 +3,17 @@
package moe.nea.firmament.mixins; package moe.nea.firmament.mixins;
import moe.nea.firmament.events.WorldReadyEvent; import moe.nea.firmament.events.WorldReadyEvent;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.DownloadingTerrainScreen; import net.minecraft.client.gui.screen.DownloadingTerrainScreen;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(DownloadingTerrainScreen.class) @Mixin(MinecraftClient.class)
public class WorldReadyEventPatch { public class WorldReadyEventPatch {
@Inject(method = "close", at = @At("HEAD")) @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) { public void onClose(CallbackInfo ci) {
WorldReadyEvent.Companion.publish(new WorldReadyEvent()); WorldReadyEvent.Companion.publish(new WorldReadyEvent());
} }
} }

View File

@@ -5,8 +5,10 @@ package moe.nea.firmament.mixins;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import moe.nea.firmament.events.WorldRenderLastEvent; import moe.nea.firmament.events.WorldRenderLastEvent;
import net.minecraft.client.render.*; 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.client.util.math.MatrixStack;
import net.minecraft.world.tick.TickManager; import net.minecraft.util.profiler.Profiler;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; 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; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(WorldRenderer.class) @Mixin(WorldRenderer.class)
public class WorldRenderLastEventPatch { public abstract class WorldRenderLastEventPatch {
@Shadow @Shadow
@Final @Final
private BufferBuilderStorage bufferBuilders; 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)) @Shadow
public void onWorldRenderLast( @Final
RenderTickCounter tickCounter, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, private DefaultFramebufferSet framebufferSet;
LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, Matrix4f matrix4f2,
CallbackInfo ci, @Local MatrixStack matrixStack @Shadow
) { protected abstract void checkEmpty(MatrixStack matrices);
var event = new WorldRenderLastEvent(
matrixStack, tickCounter, renderBlockOutline, @Inject(method = "method_62214", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiler/Profiler;pop()V", shift = At.Shift.AFTER))
camera, gameRenderer, lightmapTextureManager, 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) {
this.bufferBuilders.getEntityVertexConsumers() var imm = this.bufferBuilders.getEntityVertexConsumers();
); var stack = new MatrixStack();
WorldRenderLastEvent.Companion.publish(event); // 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);
}
} }

View File

@@ -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);
}

View File

@@ -14,7 +14,6 @@ import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.ScreenHandler;
import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.Slot;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.collection.DefaultedList;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -88,29 +87,18 @@ public class PatchHandledScreen<T extends ScreenHandler> extends Screen implemen
} }
@Unique
private Slot didBeforeSlotRender;
@WrapOperation( @WrapOperation(
method = "render", method = "drawSlots",
at = @At( at = @At(
value = "INVOKE", value = "INVOKE",
target = "Lnet/minecraft/util/collection/DefaultedList;get(I)Ljava/lang/Object;")) target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/screen/slot/Slot;)V"))
private Object beforeSlotRender(DefaultedList instance, int index, Operation<Object> original, @Local(argsOnly = true) DrawContext context) { private void beforeSlotRender(HandledScreen instance, DrawContext context, Slot slot, Operation<Void> original) {
var slot = (Slot) original.call(instance, index);
if (override != null) { if (override != null) {
didBeforeSlotRender = slot;
override.beforeSlotRender(context, slot); override.beforeSlotRender(context, slot);
} }
return slot; original.call(instance, context, slot);
} if (override != null) {
override.afterSlotRender(context, 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;
} }
} }

View File

@@ -1,33 +1,35 @@
package moe.nea.firmament.mixins.custommodels; 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.Local;
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
import moe.nea.firmament.features.texturepack.BakedModelExtra; 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.item.ItemRenderer;
import net.minecraft.client.render.model.BakedModel; 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.entity.LivingEntity;
import net.minecraft.item.ItemStack; 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.Mixin;
import org.spongepowered.asm.mixin.injection.At; 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(ItemRenderer.class)
public class ApplyHeadModelInItemRenderer { 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", @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 = "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;")) at = @At("HEAD"))
private BakedModel applyHeadModel(ItemRenderer instance, ItemStack stack, World world, LivingEntity entity, int seed, Operation<BakedModel> original, private void applyHeadModel(ItemStack stack, ModelTransformationMode transformationMode, boolean leftHanded,
@Local(argsOnly = true) ModelTransformationMode modelTransformationMode) { MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay,
var model = original.call(instance, stack, world, entity, seed); BakedModel model, boolean useInventoryModel, float z, CallbackInfo ci,
if (modelTransformationMode == ModelTransformationMode.HEAD @Local(argsOnly = true) LocalRef<BakedModel> modelMut
&& model instanceof BakedModelExtra extra) { ) {
var headModel = extra.getHeadModel_firmament(); var extra = BakedModelExtra.cast(model);
if (headModel != null) { if (transformationMode == ModelTransformationMode.HEAD && extra != null) {
model = headModel; var headModel = extra.getHeadModel_firmament();
} if (headModel != null) {
} modelMut.set(headModel);
return model; }
} }
}
} }

View File

@@ -8,6 +8,7 @@ import net.minecraft.client.render.model.BakedModel;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -17,13 +18,14 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ItemRenderer.class) @Mixin(ItemRenderer.class)
public abstract class GlobalModelOverridePatch { public abstract class GlobalModelOverridePatch {
@Shadow @Shadow
public abstract ItemModels getModels(); @Final
private ItemModels models;
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true) @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( private void overrideGlobalModel(
ItemStack stack, World world, LivingEntity entity, ItemStack stack, World world, LivingEntity entity,
int seed, CallbackInfoReturnable<BakedModel> cir) { int seed, CallbackInfoReturnable<BakedModel> cir) {
CustomGlobalTextures.replaceGlobalModel(this.getModels(), stack, cir); CustomGlobalTextures.replaceGlobalModel(this.models, stack, cir);
} }
} }

View File

@@ -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);
}
}

View File

@@ -5,28 +5,30 @@ import moe.nea.firmament.features.texturepack.TintOverrides;
import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.item.ItemRenderer; import net.minecraft.client.render.item.ItemRenderer;
import net.minecraft.client.render.model.BakedModel; 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.client.util.math.MatrixStack;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.ModelTransformationMode;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ItemRenderer.class) @Mixin(value = ItemRenderer.class, priority = 1010)
public class ItemRendererTintContextPatch { 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", @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 = "INVOKE", target = "Lnet/minecraft/client/render/model/BakedModel;getTransformation()Lnet/minecraft/client/render/model/json/ModelTransformation;"), allow = 1) at = @At(value = "HEAD"), allow = 1)
private void onStartRendering(ItemStack stack, ModelTransformationMode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) { 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) {
if (model instanceof BakedModelExtra extra) { var extra = BakedModelExtra.cast(model);
if (extra != null) {
TintOverrides.Companion.enter(extra.getTintOverrides_firmament()); 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", @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 = "INVOKE", target = "Lnet/minecraft/client/util/math/MatrixStack;pop()V"), allow = 1) at = @At("TAIL"), allow = 1)
private void onEndRendering(ItemStack stack, ModelTransformationMode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) { 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) {
if (model instanceof BakedModelExtra extra) { var extra = BakedModelExtra.cast(model);
if (extra != null) {
TintOverrides.Companion.exit(extra.getTintOverrides_firmament()); TintOverrides.Companion.exit(extra.getTintOverrides_firmament());
} }
} }

View File

@@ -1,11 +1,11 @@
package moe.nea.firmament.mixins.custommodels; package moe.nea.firmament.mixins.custommodels;
import com.google.gson.annotations.SerializedName;
import com.llamalad7.mixinextras.injector.ModifyReturnValue; import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import moe.nea.firmament.features.texturepack.BakedModelExtra; import moe.nea.firmament.features.texturepack.BakedModelExtra;
import moe.nea.firmament.features.texturepack.JsonUnbakedModelFirmExtra; import moe.nea.firmament.features.texturepack.JsonUnbakedModelFirmExtra;
import moe.nea.firmament.features.texturepack.TintOverrides; 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.BakedModel;
import net.minecraft.client.render.model.Baker; import net.minecraft.client.render.model.Baker;
import net.minecraft.client.render.model.ModelRotation; 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.Shadow;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; 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; import java.util.Objects;
@Mixin(JsonUnbakedModel.class) @Mixin(JsonUnbakedModel.class)
public class JsonUnbakedModelDataHolder implements JsonUnbakedModelFirmExtra { public abstract class JsonUnbakedModelDataHolder implements JsonUnbakedModelFirmExtra {
@Shadow @Shadow
@Nullable @Nullable
protected JsonUnbakedModel parent; protected JsonUnbakedModel parent;
@Shadow
public abstract String toString();
@Unique @Unique
@Nullable @Nullable
public Identifier headModel; public Identifier headModel;
@@ -67,31 +72,59 @@ public class JsonUnbakedModelDataHolder implements JsonUnbakedModelFirmExtra {
return ((JsonUnbakedModelFirmExtra) this.parent).getHeadModel_firmament(); return ((JsonUnbakedModelFirmExtra) this.parent).getHeadModel_firmament();
} }
@ModifyReturnValue(method = "getModelDependencies", at = @At("RETURN")) @Inject(method = "resolve", at = @At("HEAD"))
private Collection<Identifier> addDependencies(Collection<Identifier> original) { private void addDependencies(UnbakedModel.Resolver resolver, CallbackInfo ci) {
var headModel = getHeadModel_firmament(); var headModel = getHeadModel_firmament();
if (headModel != null) { if (headModel != null) {
original.add(headModel); resolver.resolve(headModel);
}
}
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) {
extra.setHeadModel_firmament(baker.bake(headModel, ModelRotation.X0_Y0));
}
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; return original;
} }
@ModifyReturnValue( @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;", 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")) at = @At(value = "RETURN"))
private BakedModel bakeExtraInfo(BakedModel original, @Local(argsOnly = true) Baker baker) { private BakedModel bakeExtraInfo(BakedModel original, @Local(argsOnly = true) Baker baker) {
if (original instanceof BakedModelExtra extra) { addExtraBakeInfo(original, baker);
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));
}
if (getTintOverrides_firmament().hasOverrides())
extra.setTintOverrides_firmament(getTintOverrides_firmament());
}
return original; return original;
} }
} }

View File

@@ -6,26 +6,24 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import moe.nea.firmament.features.texturepack.CustomGlobalArmorOverrides; import moe.nea.firmament.features.texturepack.CustomGlobalArmorOverrides;
import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; 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.item.ItemStack;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import java.util.List; import java.util.Optional;
@Mixin(ArmorFeatureRenderer.class) @Mixin(ArmorFeatureRenderer.class)
public class PatchArmorTexture { public class PatchArmorTexture {
@WrapOperation( @WrapOperation(
method = "renderArmor", method = "renderArmor",
at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ArmorMaterial;layers()Ljava/util/List;")) at = @At(value = "INVOKE", target = "Lnet/minecraft/component/type/EquippableComponent;model()Ljava/util/Optional;"))
private List<ArmorMaterial.Layer> overrideLayers( private Optional<Identifier> overrideLayers(
ArmorMaterial instance, EquippableComponent instance, Operation<Optional<Identifier>> original, @Local(argsOnly = true) ItemStack itemStack
Operation<List<ArmorMaterial.Layer>> original, ) {
@Local 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);
var overrides = CustomGlobalArmorOverrides.overrideArmor(itemStack); return overrides.or(() -> original.call(instance));
if (overrides == null) }
return original.call(instance);
return overrides;
}
} }

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -22,29 +22,29 @@ import java.util.Map;
@Mixin(ModelOverride.Deserializer.class) @Mixin(ModelOverride.Deserializer.class)
public class PatchOverrideDeserializer { public class PatchOverrideDeserializer {
@ModifyReturnValue( @ModifyReturnValue(
method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/render/model/json/ModelOverride;", 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")) at = @At(value = "RETURN"))
private ModelOverride addCustomOverrides(ModelOverride original, @Local JsonObject jsonObject) { private ModelOverride addCustomOverrides(ModelOverride original, @Local JsonObject jsonObject) {
var originalData = (ModelOverrideData) original; var originalData = (ModelOverrideData) (Object) original;
originalData.setFirmamentOverrides(CustomModelOverrideParser.parseCustomModelOverrides(jsonObject)); originalData.setFirmamentOverrides(CustomModelOverrideParser.parseCustomModelOverrides(jsonObject));
return original; return original;
} }
@ModifyExpressionValue( @ModifyExpressionValue(
method = "deserializeMinPropertyValues(Lcom/google/gson/JsonObject;)Ljava/util/List;", method = "deserializeMinPropertyValues(Lcom/google/gson/JsonObject;)Ljava/util/List;",
at = @At(value = "INVOKE", target = "Ljava/util/Map$Entry;getValue()Ljava/lang/Object;")) at = @At(value = "INVOKE", target = "Ljava/util/Map$Entry;getValue()Ljava/lang/Object;"))
private Object removeFirmamentPredicatesFromJsonIteration(Object original, @Local Map.Entry<String, JsonElement> entry) { private Object removeFirmamentPredicatesFromJsonIteration(Object original, @Local Map.Entry<String, JsonElement> entry) {
if (entry.getKey().startsWith("firmament:")) return new JsonPrimitive(0F); if (entry.getKey().startsWith("firmament:")) return new JsonPrimitive(0F);
return original; return original;
} }
@Inject( @Inject(
method = "deserializeMinPropertyValues", method = "deserializeMinPropertyValues",
at = @At(value = "INVOKE", target = "Ljava/util/Map;entrySet()Ljava/util/Set;") at = @At(value = "INVOKE", target = "Ljava/util/Map;entrySet()Ljava/util/Set;")
) )
private void whatever(JsonObject object, CallbackInfoReturnable<List<ModelOverride.Condition>> cir, private void whatever(JsonObject object, CallbackInfoReturnable<List<ModelOverride.Condition>> cir,
@Local Map<Identifier, Float> maps) { @Local Map<Identifier, Float> maps) {
maps.entrySet().removeIf(it -> it.getKey().getNamespace().equals("firmament")); maps.entrySet().removeIf(it -> it.getKey().getNamespace().equals("firmament"));
} }
} }

View File

@@ -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);
}
}

View File

@@ -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))));
}
}

View File

@@ -17,33 +17,33 @@ import org.spongepowered.asm.mixin.injection.ModifyArg;
@Mixin(ModelOverrideList.class) @Mixin(ModelOverrideList.class)
public class TestForFirmamentOverridePredicatesPatch { public class TestForFirmamentOverridePredicatesPatch {
@ModifyArg(method = "<init>(Lnet/minecraft/client/render/model/Baker;Lnet/minecraft/client/render/model/json/JsonUnbakedModel;Ljava/util/List;)V", @ModifyArg(method = "<init>(Lnet/minecraft/client/render/model/Baker;Ljava/util/List;)V",
at = @At( at = @At(
value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z" value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z"
)) ))
public Object onInit( public Object onInit(
Object element, Object element,
@Local ModelOverride modelOverride @Local ModelOverride modelOverride
) { ) {
var bakedOverride = (ModelOverrideList.BakedOverride) element; var bakedOverride = (ModelOverrideList.BakedOverride) element;
((BakedOverrideData) bakedOverride) ((BakedOverrideData) (Object) bakedOverride)
.setFirmamentOverrides(((ModelOverrideData) modelOverride).getFirmamentOverrides()); .setFirmamentOverrides(((ModelOverrideData) (Object) modelOverride).getFirmamentOverrides());
return element; return element;
} }
@ModifyExpressionValue(method = "apply", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/json/ModelOverrideList$BakedOverride;test([F)Z")) @ModifyExpressionValue(method = "getModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/json/ModelOverrideList$BakedOverride;test([F)Z"))
public boolean testFirmamentOverrides(boolean originalValue, public boolean testFirmamentOverrides(boolean originalValue,
@Local ModelOverrideList.BakedOverride bakedOverride, @Local ModelOverrideList.BakedOverride bakedOverride,
@Local(argsOnly = true) ItemStack stack) { @Local(argsOnly = true) ItemStack stack) {
if (!originalValue) return false; if (!originalValue) return false;
var overrideData = (BakedOverrideData) bakedOverride; var overrideData = (BakedOverrideData) (Object) bakedOverride;
var overrides = overrideData.getFirmamentOverrides(); var overrides = overrideData.getFirmamentOverrides();
if (overrides == null) return true; if (overrides == null) return true;
if (!CustomSkyBlockTextures.TConfig.INSTANCE.getEnableModelOverrides()) return false; if (!CustomSkyBlockTextures.TConfig.INSTANCE.getEnableModelOverrides()) return false;
for (FirmamentModelPredicate firmamentOverride : overrides) { for (FirmamentModelPredicate firmamentOverride : overrides) {
if (!firmamentOverride.test(stack)) if (!firmamentOverride.test(stack))
return false; return false;
} }
return true; return true;
} }
} }

View File

@@ -1,21 +1,24 @@
package moe.nea.firmament.events 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.client.util.ModelIdentifier
import net.minecraft.util.Identifier
// TODO: Rename this event, since it is not really directly baking models anymore
class BakeExtraModelsEvent( class BakeExtraModelsEvent(
private val addItemModel: Consumer<ModelIdentifier>, private val addAnyModel: BiConsumer<ModelIdentifier, Identifier>,
private val addAnyModel: Consumer<ModelIdentifier>,
) : FirmamentEvent() { ) : FirmamentEvent() {
fun addNonItemModel(modelIdentifier: ModelIdentifier) { fun addNonItemModel(modelIdentifier: ModelIdentifier, identifier: Identifier) {
this.addAnyModel.accept(modelIdentifier) this.addAnyModel.accept(modelIdentifier, identifier)
} }
fun addItemModel(modelIdentifier: ModelIdentifier) { fun addItemModel(modelIdentifier: ModelIdentifier) {
this.addItemModel.accept(modelIdentifier) addNonItemModel(
} modelIdentifier,
modelIdentifier.id.withPrefixedPath(ReferencedModelsCollector.ITEM_DIRECTORY))
}
companion object : FirmamentEventBus<BakeExtraModelsEvent>() companion object : FirmamentEventBus<BakeExtraModelsEvent>()
} }

View File

@@ -2,35 +2,37 @@ package moe.nea.firmament.events
import java.util.Optional import java.util.Optional
import kotlin.jvm.optionals.getOrNull 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.BakedModel
import net.minecraft.client.render.model.BakedModelManager
import net.minecraft.client.util.ModelIdentifier import net.minecraft.client.util.ModelIdentifier
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import moe.nea.firmament.util.ErrorUtil
import moe.nea.firmament.util.collections.WeakCache import moe.nea.firmament.util.collections.WeakCache
data class CustomItemModelEvent( data class CustomItemModelEvent(
val itemStack: ItemStack, val itemStack: ItemStack,
var overrideModel: ModelIdentifier? = null, var overrideModel: ModelIdentifier? = null,
) : FirmamentEvent() { ) : FirmamentEvent() {
companion object : FirmamentEventBus<CustomItemModelEvent>() { companion object : FirmamentEventBus<CustomItemModelEvent>() {
val cache = val cache =
WeakCache.memoize<ItemStack, BakedModelManager, Optional<BakedModel>>("CustomItemModels") { stack, models -> WeakCache.memoize<ItemStack, ItemModels, Optional<BakedModel>>("CustomItemModels") { stack, models ->
val modelId = getModelIdentifier(stack) ?: return@memoize Optional.empty() val modelId = getModelIdentifier(stack) ?: return@memoize Optional.empty()
val bakedModel = models.getModel(modelId) ErrorUtil.softCheck("Model Id needs to have an inventory variant", modelId.variant() == "inventory")
if (bakedModel === models.missingModel) return@memoize Optional.empty() val bakedModel = models.getModel(modelId.id)
Optional.of(bakedModel) if (bakedModel == null || bakedModel === models.missingModelSupplier.get()) return@memoize Optional.empty()
} Optional.of(bakedModel)
}
@JvmStatic @JvmStatic
fun getModelIdentifier(itemStack: ItemStack?): ModelIdentifier? { fun getModelIdentifier(itemStack: ItemStack?): ModelIdentifier? {
if (itemStack == null) return null if (itemStack == null) return null
return publish(CustomItemModelEvent(itemStack)).overrideModel return publish(CustomItemModelEvent(itemStack)).overrideModel
} }
@JvmStatic @JvmStatic
fun getModel(itemStack: ItemStack?, thing: BakedModelManager): BakedModel? { fun getModel(itemStack: ItemStack?, thing: ItemModels): BakedModel? {
if (itemStack == null) return null if (itemStack == null) return null
return cache.invoke(itemStack, thing).getOrNull() return cache.invoke(itemStack, thing).getOrNull()
} }
} }
} }

View File

@@ -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>()
}

View File

@@ -5,31 +5,28 @@ import java.util.concurrent.Executor
import net.minecraft.resource.ReloadableResourceManagerImpl import net.minecraft.resource.ReloadableResourceManagerImpl
import net.minecraft.resource.ResourceManager import net.minecraft.resource.ResourceManager
import net.minecraft.resource.ResourceReloader import net.minecraft.resource.ResourceReloader
import net.minecraft.util.profiler.Profiler
data class FinalizeResourceManagerEvent( data class FinalizeResourceManagerEvent(
val resourceManager: ReloadableResourceManagerImpl, val resourceManager: ReloadableResourceManagerImpl,
) : FirmamentEvent() { ) : FirmamentEvent() {
companion object : FirmamentEventBus<FinalizeResourceManagerEvent>() companion object : FirmamentEventBus<FinalizeResourceManagerEvent>()
inline fun registerOnApply(name: String, crossinline function: () -> Unit) { inline fun registerOnApply(name: String, crossinline function: () -> Unit) {
resourceManager.registerReloader(object : ResourceReloader { resourceManager.registerReloader(object : ResourceReloader {
override fun reload( override fun reload(
synchronizer: ResourceReloader.Synchronizer, synchronizer: ResourceReloader.Synchronizer,
manager: ResourceManager?, manager: ResourceManager,
prepareProfiler: Profiler?, prepareExecutor: Executor,
applyProfiler: Profiler?, applyExecutor: Executor
prepareExecutor: Executor?, ): CompletableFuture<Void> {
applyExecutor: Executor return CompletableFuture.completedFuture(Unit)
): CompletableFuture<Void> { .thenCompose(synchronizer::whenPrepared)
return CompletableFuture.completedFuture(Unit) .thenAcceptAsync({ function() }, applyExecutor)
.thenCompose(synchronizer::whenPrepared) }
.thenAcceptAsync({ function() }, applyExecutor)
}
override fun getName(): String { override fun getName(): String {
return name return name
} }
}) })
} }
} }

View File

@@ -37,7 +37,7 @@ data class IsSlotProtectedEvent(
val event = IsSlotProtectedEvent(slot, action, false, itemStackOverride) val event = IsSlotProtectedEvent(slot, action, false, itemStackOverride)
publish(event) publish(event)
if (event.isProtected && !event.silent) { 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() CommonSoundEffects.playFailure()
} }
return event.isProtected return event.isProtected

View File

@@ -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)
}
}
}

View File

@@ -3,20 +3,19 @@
package moe.nea.firmament.events package moe.nea.firmament.events
import net.minecraft.client.gui.DrawContext import net.minecraft.client.gui.DrawContext
import net.minecraft.client.render.RenderLayer
import net.minecraft.client.texture.Sprite import net.minecraft.client.texture.Sprite
import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.Slot
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
import moe.nea.firmament.util.render.drawGuiTexture
interface SlotRenderEvents { interface SlotRenderEvents {
val context: DrawContext val context: DrawContext
val slot: Slot val slot: Slot
val mouseX: Int
val mouseY: Int
val delta: Float
fun highlight(sprite: Sprite) { fun highlight(sprite: Identifier) {
context.drawSprite( context.drawGuiTexture(
slot.x, slot.y, 0, 16, 16, slot.x, slot.y, 0, 16, 16,
sprite sprite
) )
@@ -24,9 +23,6 @@ interface SlotRenderEvents {
data class Before( data class Before(
override val context: DrawContext, override val slot: Slot, override val context: DrawContext, override val slot: Slot,
override val mouseX: Int,
override val mouseY: Int,
override val delta: Float
) : FirmamentEvent(), ) : FirmamentEvent(),
SlotRenderEvents { SlotRenderEvents {
companion object : FirmamentEventBus<Before>() companion object : FirmamentEventBus<Before>()
@@ -34,9 +30,6 @@ interface SlotRenderEvents {
data class After( data class After(
override val context: DrawContext, override val slot: Slot, override val context: DrawContext, override val slot: Slot,
override val mouseX: Int,
override val mouseY: Int,
override val delta: Float
) : FirmamentEvent(), ) : FirmamentEvent(),
SlotRenderEvents { SlotRenderEvents {
companion object : FirmamentEventBus<After>() companion object : FirmamentEventBus<After>()

View File

@@ -1,7 +1,10 @@
package moe.nea.firmament.events package moe.nea.firmament.events
class WorldReadyEvent : FirmamentEvent() { class WorldReadyEvent : FirmamentEvent() {
companion object : FirmamentEventBus<WorldReadyEvent>() companion object : FirmamentEventBus<WorldReadyEvent>()
// class FullyLoaded : FirmamentEvent() {
// companion object : FirmamentEventBus<FullyLoaded>() {
// TODO: check WorldLoadingState
// }
// }
} }

View File

@@ -17,10 +17,7 @@ import net.minecraft.util.math.Vec3d
data class WorldRenderLastEvent( data class WorldRenderLastEvent(
val matrices: MatrixStack, val matrices: MatrixStack,
val tickCounter: RenderTickCounter, val tickCounter: RenderTickCounter,
val renderBlockOutline: Boolean,
val camera: Camera, val camera: Camera,
val gameRenderer: GameRenderer,
val lightmapTextureManager: LightmapTextureManager,
val vertexConsumers: VertexConsumerProvider.Immediate, val vertexConsumers: VertexConsumerProvider.Immediate,
) : FirmamentEvent() { ) : FirmamentEvent() {
companion object : FirmamentEventBus<WorldRenderLastEvent>() companion object : FirmamentEventBus<WorldRenderLastEvent>()

View File

@@ -13,8 +13,10 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlin.math.min import kotlin.math.min
import net.minecraft.client.gui.screen.ChatScreen import net.minecraft.client.gui.screen.ChatScreen
import net.minecraft.client.render.RenderLayer
import net.minecraft.client.texture.NativeImage import net.minecraft.client.texture.NativeImage
import net.minecraft.client.texture.NativeImageBackedTexture import net.minecraft.client.texture.NativeImageBackedTexture
import net.minecraft.scoreboard.ScoreboardCriterion.RenderType
import net.minecraft.text.ClickEvent import net.minecraft.text.ClickEvent
import net.minecraft.text.HoverEvent import net.minecraft.text.HoverEvent
import net.minecraft.text.Style 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.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
import moe.nea.firmament.util.render.drawTexture
import moe.nea.firmament.util.transformEachRecursively import moe.nea.firmament.util.transformEachRecursively
import moe.nea.firmament.util.unformattedString import moe.nea.firmament.util.unformattedString

View File

@@ -37,10 +37,10 @@ object DeveloperFeatures : FirmamentFeature {
builder.directory(gradleDir.toFile()) builder.directory(gradleDir.toFile())
builder.inheritIO() builder.inheritIO()
val process = builder.start() 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() val startTime = TimeMark.now()
process.toHandle().onExit().thenApply { process.toHandle().onExit().thenApply {
MC.player?.sendMessage(Text.stringifiedTranslatable( MC.sendChat(Text.stringifiedTranslatable(
"firmament.dev.resourcerebuild.done", "firmament.dev.resourcerebuild.done",
startTime.passedTime())) startTime.passedTime()))
Unit Unit

View File

@@ -9,6 +9,7 @@ import net.minecraft.entity.Entity
import net.minecraft.entity.LivingEntity import net.minecraft.entity.LivingEntity
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.item.Items import net.minecraft.item.Items
import net.minecraft.nbt.NbtOps
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.text.TextCodecs import net.minecraft.text.TextCodecs
import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.hit.BlockHitResult
@@ -54,15 +55,13 @@ object PowerUserTools : FirmamentFeature {
var lastCopiedStack: Pair<ItemStack, Text>? = null var lastCopiedStack: Pair<ItemStack, Text>? = null
set(value) { set(value) {
field = value field = value
if (value != null) if (value != null) lastCopiedStackViewTime = true
lastCopiedStackViewTime = true
} }
var lastCopiedStackViewTime = false var lastCopiedStackViewTime = false
@Subscribe @Subscribe
fun resetLastCopiedStack(event: TickEvent) { fun resetLastCopiedStack(event: TickEvent) {
if (!lastCopiedStackViewTime) if (!lastCopiedStackViewTime) lastCopiedStack = null
lastCopiedStack = null
lastCopiedStackViewTime = false lastCopiedStackViewTime = false
} }
@@ -154,13 +153,13 @@ object PowerUserTools : FirmamentFeature {
} }
ClipboardUtils.setTextContent(skullTexture.toString()) ClipboardUtils.setTextContent(skullTexture.toString())
lastCopiedStack = lastCopiedStack =
Pair( Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skull-id", skullTexture.toString()))
item,
Text.stringifiedTranslatable("firmament.tooltip.copied.skull-id", skullTexture.toString())
)
println("Copied skull id: $skullTexture") println("Copied skull id: $skullTexture")
} else if (it.matches(TConfig.copyItemStack)) { } 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")) lastCopiedStack = Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.stack"))
} }
} }

View File

@@ -104,12 +104,13 @@ object AncestralSpadeSolver : SubscriptionOwner {
if (!isEnabled()) return if (!isEnabled()) return
RenderInWorldContext.renderInWorld(event) { RenderInWorldContext.renderInWorld(event) {
nextGuess?.let { nextGuess?.let {
color(1f, 1f, 0f, 0.5f) tinyBlock(it, 1f, 0x80FFFFFF.toInt())
tinyBlock(it, 1f) // TODO: replace this
color(1f, 1f, 0f, 1f) color(1f, 1f, 0f, 1f)
tracer(it, lineWidth = 3f) tracer(it, lineWidth = 3f)
} }
if (particlePositions.size > 2 && lastDing.passedTime() < 10.seconds && nextGuess != null) { if (particlePositions.size > 2 && lastDing.passedTime() < 10.seconds && nextGuess != null) {
// TODO: replace this // TODO: add toggle
color(0f, 1f, 0f, 0.7f) color(0f, 1f, 0f, 0.7f)
line(particlePositions) line(particlePositions)
} }

View File

@@ -1,6 +1,6 @@
package moe.nea.firmament.features.diana package moe.nea.firmament.features.diana
import me.shedaniel.math.Color
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
import net.minecraft.particle.ParticleTypes import net.minecraft.particle.ParticleTypes
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
@@ -20,125 +20,125 @@ import moe.nea.firmament.util.render.RenderInWorldContext.Companion.renderInWorl
object NearbyBurrowsSolver : SubscriptionOwner { object NearbyBurrowsSolver : SubscriptionOwner {
private val recentlyDugBurrows: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(20) private val recentlyDugBurrows: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(20)
private val recentEnchantParticles: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(500) private val recentEnchantParticles: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(500)
private var lastBlockClick: BlockPos? = null private var lastBlockClick: BlockPos? = null
enum class BurrowType { enum class BurrowType {
START, MOB, TREASURE START, MOB, TREASURE
} }
val burrows = mutableMapOf<BlockPos, BurrowType>() val burrows = mutableMapOf<BlockPos, BurrowType>()
@Subscribe @Subscribe
fun onChatEvent(event: ProcessChatEvent) { fun onChatEvent(event: ProcessChatEvent) {
val lastClickedBurrow = lastBlockClick ?: return val lastClickedBurrow = lastBlockClick ?: return
if (event.unformattedString.startsWith("You dug out a Griffin Burrow!") || if (event.unformattedString.startsWith("You dug out a Griffin Burrow!") ||
event.unformattedString.startsWith(" ☠ You were killed by") || event.unformattedString.startsWith(" ☠ You were killed by") ||
event.unformattedString.startsWith("You finished the Griffin burrow chain!") event.unformattedString.startsWith("You finished the Griffin burrow chain!")
) { ) {
markAsDug(lastClickedBurrow) markAsDug(lastClickedBurrow)
burrows.remove(lastClickedBurrow) burrows.remove(lastClickedBurrow)
} }
} }
fun wasRecentlyDug(blockPos: BlockPos): Boolean { fun wasRecentlyDug(blockPos: BlockPos): Boolean {
val lastDigTime = recentlyDugBurrows[blockPos] ?: TimeMark.farPast() val lastDigTime = recentlyDugBurrows[blockPos] ?: TimeMark.farPast()
return lastDigTime.passedTime() < 10.seconds return lastDigTime.passedTime() < 10.seconds
} }
fun markAsDug(blockPos: BlockPos) { fun markAsDug(blockPos: BlockPos) {
recentlyDugBurrows[blockPos] = TimeMark.now() recentlyDugBurrows[blockPos] = TimeMark.now()
} }
fun wasRecentlyEnchanted(blockPos: BlockPos): Boolean { fun wasRecentlyEnchanted(blockPos: BlockPos): Boolean {
val lastEnchantTime = recentEnchantParticles[blockPos] ?: TimeMark.farPast() val lastEnchantTime = recentEnchantParticles[blockPos] ?: TimeMark.farPast()
return lastEnchantTime.passedTime() < 4.seconds return lastEnchantTime.passedTime() < 4.seconds
} }
fun markAsEnchanted(blockPos: BlockPos) { fun markAsEnchanted(blockPos: BlockPos) {
recentEnchantParticles[blockPos] = TimeMark.now() recentEnchantParticles[blockPos] = TimeMark.now()
} }
@Subscribe @Subscribe
fun onParticles(event: ParticleSpawnEvent) { fun onParticles(event: ParticleSpawnEvent) {
if (!DianaWaypoints.TConfig.nearbyWaypoints) return if (!DianaWaypoints.TConfig.nearbyWaypoints) return
val position: BlockPos = event.position.toBlockPos().down() val position: BlockPos = event.position.toBlockPos().down()
if (wasRecentlyDug(position)) return if (wasRecentlyDug(position)) return
val isEven50Spread = (event.offset.x == 0.5f && event.offset.z == 0.5f) val isEven50Spread = (event.offset.x == 0.5f && event.offset.z == 0.5f)
if (event.particleEffect.type == ParticleTypes.ENCHANT) { if (event.particleEffect.type == ParticleTypes.ENCHANT) {
if (event.count == 5 && event.speed == 0.05F && event.offset.y == 0.4F && isEven50Spread) { if (event.count == 5 && event.speed == 0.05F && event.offset.y == 0.4F && isEven50Spread) {
markAsEnchanted(position) markAsEnchanted(position)
} }
return return
} }
if (!wasRecentlyEnchanted(position)) return if (!wasRecentlyEnchanted(position)) return
if (event.particleEffect.type == ParticleTypes.ENCHANTED_HIT if (event.particleEffect.type == ParticleTypes.ENCHANTED_HIT
&& event.count == 4 && event.count == 4
&& event.speed == 0.01F && event.speed == 0.01F
&& event.offset.y == 0.1f && event.offset.y == 0.1f
&& isEven50Spread && isEven50Spread
) { ) {
burrows[position] = BurrowType.START burrows[position] = BurrowType.START
} }
if (event.particleEffect.type == ParticleTypes.CRIT if (event.particleEffect.type == ParticleTypes.CRIT
&& event.count == 3 && event.count == 3
&& event.speed == 0.01F && event.speed == 0.01F
&& event.offset.y == 0.1F && event.offset.y == 0.1F
&& isEven50Spread && isEven50Spread
) { ) {
burrows[position] = BurrowType.MOB burrows[position] = BurrowType.MOB
} }
if (event.particleEffect.type == ParticleTypes.DRIPPING_LAVA if (event.particleEffect.type == ParticleTypes.DRIPPING_LAVA
&& event.count == 2 && event.count == 2
&& event.speed == 0.01F && event.speed == 0.01F
&& event.offset.y == 0.1F && event.offset.y == 0.1F
&& event.offset.x == 0.35F && event.offset.z == 0.35f && event.offset.x == 0.35F && event.offset.z == 0.35f
) { ) {
burrows[position] = BurrowType.TREASURE burrows[position] = BurrowType.TREASURE
} }
} }
@Subscribe @Subscribe
fun onRender(event: WorldRenderLastEvent) { fun onRender(event: WorldRenderLastEvent) {
if (!DianaWaypoints.TConfig.nearbyWaypoints) return if (!DianaWaypoints.TConfig.nearbyWaypoints) return
renderInWorld(event) { renderInWorld(event) {
for ((location, burrow) in burrows) { for ((location, burrow) in burrows) {
when (burrow) { val color = when (burrow) {
BurrowType.START -> color(.2f, .8f, .2f, 0.4f) BurrowType.START -> Color.ofRGBA(.2f, .8f, .2f, 0.4f)
BurrowType.MOB -> color(0.3f, 0.4f, 0.9f, 0.4f) BurrowType.MOB -> Color.ofRGBA(0.3f, 0.4f, 0.9f, 0.4f)
BurrowType.TREASURE -> color(1f, 0.7f, 0.2f, 0.4f) BurrowType.TREASURE -> Color.ofRGBA(1f, 0.7f, 0.2f, 0.4f)
} }
block(location) block(location, color.color)
} }
} }
} }
@Subscribe @Subscribe
fun onSwapWorld(worldReadyEvent: WorldReadyEvent) { fun onSwapWorld(worldReadyEvent: WorldReadyEvent) {
burrows.clear() burrows.clear()
recentEnchantParticles.clear() recentEnchantParticles.clear()
recentlyDugBurrows.clear() recentlyDugBurrows.clear()
lastBlockClick = null lastBlockClick = null
} }
fun onBlockClick(blockPos: BlockPos) { fun onBlockClick(blockPos: BlockPos) {
if (!DianaWaypoints.TConfig.nearbyWaypoints) return if (!DianaWaypoints.TConfig.nearbyWaypoints) return
burrows.remove(blockPos) burrows.remove(blockPos)
lastBlockClick = blockPos lastBlockClick = blockPos
} }
override val delegateFeature: FirmamentFeature override val delegateFeature: FirmamentFeature
get() = DianaWaypoints get() = DianaWaypoints
} }
fun Position.toBlockPos(): BlockPos { 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))
} }

View File

@@ -69,7 +69,7 @@ object CraftingOverlay : FirmamentFeature {
if (!slot.hasStack()) { if (!slot.hasStack()) {
val itemStack = SBItemStack(expectedItem)?.asImmutableItemStack() ?: return val itemStack = SBItemStack(expectedItem)?.asImmutableItemStack() ?: return
event.context.drawItem(itemStack, event.slot.x, event.slot.y) event.context.drawItem(itemStack, event.slot.x, event.slot.y)
event.context.drawItemInSlot( event.context.drawStackOverlay(
MC.font, MC.font,
itemStack, itemStack,
event.slot.x, event.slot.x,

View File

@@ -4,6 +4,7 @@ package moe.nea.firmament.features.inventory
import java.awt.Color import java.awt.Color
import net.minecraft.client.gui.DrawContext import net.minecraft.client.gui.DrawContext
import net.minecraft.client.render.RenderLayer
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.util.Formatting import net.minecraft.util.Formatting
import net.minecraft.util.Identifier 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.mc.loreAccordingToNbt
import moe.nea.firmament.util.collections.lastNotNullOfOrNull import moe.nea.firmament.util.collections.lastNotNullOfOrNull
import moe.nea.firmament.util.collections.memoizeIdentity import moe.nea.firmament.util.collections.memoizeIdentity
import moe.nea.firmament.util.render.drawGuiTexture
import moe.nea.firmament.util.unformattedString import moe.nea.firmament.util.unformattedString
object ItemRarityCosmetics : FirmamentFeature { object ItemRarityCosmetics : FirmamentFeature {
@@ -43,10 +45,10 @@ object ItemRarityCosmetics : FirmamentFeature {
"SUPREME" to Formatting.DARK_RED, "SUPREME" to Formatting.DARK_RED,
).mapValues { ).mapValues {
val c = Color(it.value.colorValue!!) 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 { return itemStack.loreAccordingToNbt.lastNotNullOfOrNull {
val entry = it.unformattedString val entry = it.unformattedString
rarityToColor.entries.find { (k, v) -> k in entry }?.value 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) { fun drawItemStackRarity(drawContext: DrawContext, x: Int, y: Int, item: ItemStack) {
val (r, g, b) = getSkyblockRarity(item) ?: return val rgb = getSkyblockRarity(item) ?: return
drawContext.drawSprite( drawContext.drawGuiTexture(
RenderLayer::getGuiTextured,
Identifier.of("firmament:item_rarity_background"),
x, y, x, y,
0,
16, 16, 16, 16,
MC.guiAtlasManager.getSprite(Identifier.of("firmament:item_rarity_background")), rgb
r, g, b, 1F
) )
} }

View File

@@ -7,6 +7,7 @@ import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
import moe.nea.firmament.util.petData import moe.nea.firmament.util.petData
import moe.nea.firmament.util.render.drawGuiTexture
import moe.nea.firmament.util.useMatch import moe.nea.firmament.util.useMatch
object PetFeatures : FirmamentFeature { object PetFeatures : FirmamentFeature {
@@ -28,9 +29,9 @@ object PetFeatures : FirmamentFeature {
val stack = event.slot.stack val stack = event.slot.stack
if (stack.petData?.active == true) if (stack.petData?.active == true)
petMenuTitle.useMatch(MC.screenName ?: return) { petMenuTitle.useMatch(MC.screenName ?: return) {
event.context.drawSprite( event.context.drawGuiTexture(
event.slot.x, event.slot.y, 0, 16, 16, event.slot.x, event.slot.y, 0, 16, 16,
MC.guiAtlasManager.getSprite(Identifier.of("firmament:selected_pet_background")) Identifier.of("firmament:selected_pet_background")
) )
} }
} }

View File

@@ -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.SlotUtils.swapWithHotBar
import moe.nea.firmament.util.mc.displayNameAccordingToNbt import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.mc.loreAccordingToNbt 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.render.drawLine
import moe.nea.firmament.util.skyblockUUID import moe.nea.firmament.util.skyblockUUID
import moe.nea.firmament.util.unformattedString import moe.nea.firmament.util.unformattedString
@@ -211,6 +212,11 @@ object SlotLocking : FirmamentFeature {
} }
if (it.matches(TConfig.slotBind)) { if (it.matches(TConfig.slotBind)) {
storedLockingSlot = null 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 isSlotLocked = it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())
val isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf()) val isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf())
if (isSlotLocked || isUUIDLocked) { if (isSlotLocked || isUUIDLocked) {
RenderSystem.disableDepthTest() it.context.drawGuiTexture(
it.context.drawSprite( GuiRenderLayers.GUI_TEXTURED_NO_DEPTH,
it.slot.x, it.slot.y, 0, when {
isSlotLocked ->
(Identifier.of("firmament:slot_locked"))
isUUIDLocked ->
(Identifier.of("firmament:uuid_locked"))
else ->
error("unreachable")
},
it.slot.x, it.slot.y,
16, 16, 16, 16,
MC.guiAtlasManager.getSprite( -1
when {
isSlotLocked ->
(Identifier.of("firmament:slot_locked"))
isUUIDLocked ->
(Identifier.of("firmament:uuid_locked"))
else ->
error("unreachable")
}
)
) )
RenderSystem.enableDepthTest()
} }
} }
} }

View File

@@ -18,6 +18,7 @@ import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.collections.memoize import moe.nea.firmament.util.collections.memoize
import moe.nea.firmament.util.render.drawGuiTexture
@Serializable @Serializable
data class InventoryButton( data class InventoryButton(
@@ -54,13 +55,13 @@ data class InventoryButton(
} }
fun render(context: DrawContext) { fun render(context: DrawContext) {
context.drawSprite( context.drawGuiTexture(
0, 0,
0, 0,
0, 0,
dimensions.width, dimensions.width,
dimensions.height, dimensions.height,
MC.guiAtlasManager.getSprite(Identifier.of("firmament:inventory_button_background")) Identifier.of("firmament:inventory_button_background")
) )
context.drawItem(getItem(), 1, 1) context.drawItem(getItem(), 1, 1)
} }

View File

@@ -84,7 +84,6 @@ class InventoryButtonEditor(
context.matrices.push() context.matrices.push()
context.matrices.translate(0f, 0f, -10f) context.matrices.translate(0f, 0f, -10f)
context.fill(lastGuiRect.minX, lastGuiRect.minY, lastGuiRect.maxX, lastGuiRect.maxY, -1) context.fill(lastGuiRect.minX, lastGuiRect.minY, lastGuiRect.maxX, lastGuiRect.maxY, -1)
context.setShaderColor(1f, 1f, 1f, 1f)
context.matrices.pop() context.matrices.pop()
for (button in buttons) { for (button in buttons) {
val buttonPosition = button.getBounds(lastGuiRect) val buttonPosition = button.getBounds(lastGuiRect)

View File

@@ -40,6 +40,7 @@ sealed interface StorageBackingHandle {
* representable as a [StorageBackingHandle], meaning another screen is open, for example the enderchest icon * representable as a [StorageBackingHandle], meaning another screen is open, for example the enderchest icon
* selection screen. * selection screen.
*/ */
@OptIn(ExperimentalContracts::class)
fun fromScreen(screen: Screen?): StorageBackingHandle? { fun fromScreen(screen: Screen?): StorageBackingHandle? {
contract { contract {
returnsNotNull() implies (screen != null) returnsNotNull() implies (screen != null)

View File

@@ -21,6 +21,7 @@ import moe.nea.firmament.util.MoulConfigUtils.clickMCComponentInPlace
import moe.nea.firmament.util.MoulConfigUtils.drawMCComponentInPlace import moe.nea.firmament.util.MoulConfigUtils.drawMCComponentInPlace
import moe.nea.firmament.util.assertTrueOr import moe.nea.firmament.util.assertTrueOr
import moe.nea.firmament.util.customgui.customGui import moe.nea.firmament.util.customgui.customGui
import moe.nea.firmament.util.render.drawGuiTexture
class StorageOverlayScreen : Screen(Text.literal("")) { class StorageOverlayScreen : Screen(Text.literal("")) {
@@ -162,13 +163,11 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
context.drawGuiTexture(upperBackgroundSprite, context.drawGuiTexture(upperBackgroundSprite,
measurements.x, measurements.x,
measurements.y, measurements.y,
0,
measurements.overviewWidth, measurements.overviewWidth,
measurements.overviewHeight) measurements.overviewHeight)
context.drawGuiTexture(playerInventorySprite, context.drawGuiTexture(playerInventorySprite,
measurements.playerX, measurements.playerX,
measurements.playerY, measurements.playerY,
0,
PLAYER_WIDTH, PLAYER_WIDTH,
PLAYER_HEIGHT) PLAYER_HEIGHT)
} }
@@ -188,7 +187,7 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
items.withIndex().forEach { (index, item) -> items.withIndex().forEach { (index, item) ->
val (x, y) = getPlayerInventorySlotPosition(index) val (x, y) = getPlayerInventorySlotPosition(index)
context.drawItem(item, x, y, 0) 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 val slotY = (index / 9) * SLOT_SIZE + y + 4 + textRenderer.fontHeight + 1
if (slots == null) { if (slots == null) {
context.drawItem(stack, slotX, slotY) context.drawItem(stack, slotX, slotY)
context.drawItemInSlot(textRenderer, stack, slotX, slotY) context.drawStackOverlay(textRenderer, stack, slotX, slotY)
} else { } else {
val slot = slots[index] val slot = slots[index]
slot.x = slotX - slotOffset.x slot.x = slotX - slotOffset.x

View File

@@ -111,7 +111,7 @@ class StorageOverviewScreen() : Screen(Text.empty()) {
context.fill(x, y, x + 18, y + 18, 0x40808080.toInt()) context.fill(x, y, x + 18, y + 18, 0x40808080.toInt())
} }
context.drawItem(stack, x + 1, y + 1) 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)
} }
} }

View File

@@ -1,6 +1,6 @@
package moe.nea.firmament.features.mining 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.annotations.Subscribe
import moe.nea.firmament.events.SlotRenderEvents import moe.nea.firmament.events.SlotRenderEvents
import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedConfig
@@ -19,10 +19,8 @@ object CommissionFeatures {
if (!Config.highlightCompletedCommissions) return if (!Config.highlightCompletedCommissions) return
if (MC.screenName != "Commissions") return if (MC.screenName != "Commissions") return
val stack = event.slot.stack val stack = event.slot.stack
if(stack.loreAccordingToNbt.any { it.unformattedString == "COMPLETED" }) { if (stack.loreAccordingToNbt.any { it.unformattedString == "COMPLETED" }) {
event.highlight( event.highlight(Firmament.identifier("completed_commission_background"))
MC.guiAtlasManager.getSprite(Identifier.of("firmament:completed_commission_background"))
)
} }
} }
} }

View File

@@ -9,9 +9,7 @@ import net.minecraft.client.gui.screen.ingame.HandledScreen
import net.minecraft.entity.player.PlayerInventory import net.minecraft.entity.player.PlayerInventory
import net.minecraft.item.Items import net.minecraft.item.Items
import net.minecraft.screen.GenericContainerScreenHandler import net.minecraft.screen.GenericContainerScreenHandler
import net.minecraft.screen.ScreenHandler
import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.Slot
import net.minecraft.screen.slot.SlotActionType
import net.minecraft.text.Text import net.minecraft.text.Text
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.annotations.Subscribe 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.CommonTextures
import moe.nea.firmament.util.mc.SlotUtils.clickRightMouseButton import moe.nea.firmament.util.mc.SlotUtils.clickRightMouseButton
import moe.nea.firmament.util.mc.displayNameAccordingToNbt 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.unformattedString
import moe.nea.firmament.util.useMatch import moe.nea.firmament.util.useMatch
@@ -81,7 +80,7 @@ object HotmPresets {
override fun render(drawContext: DrawContext, delta: Float, mouseX: Int, mouseY: Int) { override fun render(drawContext: DrawContext, delta: Float, mouseX: Int, mouseY: Int) {
drawContext.drawGuiTexture( drawContext.drawGuiTexture(
CommonTextures.genericWidget(), CommonTextures.genericWidget(),
bounds.x, bounds.y, 0, bounds.x, bounds.y,
bounds.width, bounds.width,
bounds.height, bounds.height,
) )
@@ -191,7 +190,7 @@ object HotmPresets {
if (hotmInventoryName == MC.screenName if (hotmInventoryName == MC.screenName
&& event.slot.stack.displayNameAccordingToNbt.unformattedString in highlightedPerks && event.slot.stack.displayNameAccordingToNbt.unformattedString in highlightedPerks
) { ) {
event.highlight(MC.guiAtlasManager.getSprite(Firmament.identifier("hotm_perk_preset"))) event.highlight((Firmament.identifier("hotm_perk_preset")))
} }
} }

View File

@@ -1,11 +1,30 @@
package moe.nea.firmament.features.texturepack 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.BakedModel
import net.minecraft.client.render.model.WrapperBakedModel
import moe.nea.firmament.util.ErrorUtil
interface BakedModelExtra { 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? var tintOverrides_firmament: TintOverrides?
fun getHeadModel_firmament(): BakedModel? fun getHeadModel_firmament(): BakedModel?
fun setHeadModel_firmament(headModel: BakedModel?) fun setHeadModel_firmament(headModel: BakedModel?)
} }

View File

@@ -244,7 +244,7 @@ object CustomBlockTextures {
.flatMap { it.lookup.values } .flatMap { it.lookup.values }
.flatten() .flatten()
.mapTo(mutableSetOf()) { it.replacement.blockModelIdentifier } .mapTo(mutableSetOf()) { it.replacement.blockModelIdentifier }
.forEach { event.addNonItemModel(it) } .forEach { event.addNonItemModel(it, it.id) }
} }
private fun prepare(manager: ResourceManager): BakedReplacements { private fun prepare(manager: ResourceManager): BakedReplacements {
@@ -263,7 +263,7 @@ object CustomBlockTextures {
val island = SkyBlockIsland.forMode(mode) val island = SkyBlockIsland.forMode(mode)
val islandMpa = map.getOrPut(island, ::mutableMapOf) val islandMpa = map.getOrPut(island, ::mutableMapOf)
for ((blockId, replacement) in json.replacements) { 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)) .getOptional(RegistryKey.of(RegistryKeys.BLOCK, blockId))
.getOrNull() .getOrNull()
if (block == null) { if (block == null) {

View File

@@ -3,13 +3,13 @@
package moe.nea.firmament.features.texturepack package moe.nea.firmament.features.texturepack
import java.util.Optional import java.util.Optional
import java.util.concurrent.atomic.AtomicInteger
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
import kotlin.jvm.optionals.getOrNull
import net.minecraft.item.ArmorMaterial
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.item.equipment.EquipmentModel
import net.minecraft.resource.ResourceManager import net.minecraft.resource.ResourceManager
import net.minecraft.resource.SinglePreparationResourceReloader import net.minecraft.resource.SinglePreparationResourceReloader
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
@@ -17,90 +17,139 @@ import net.minecraft.util.profiler.Profiler
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.FinalizeResourceManagerEvent 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.features.texturepack.CustomGlobalTextures.logger
import moe.nea.firmament.util.IdentifierSerializer import moe.nea.firmament.util.IdentifierSerializer
import moe.nea.firmament.util.collections.WeakCache import moe.nea.firmament.util.collections.WeakCache
import moe.nea.firmament.util.skyBlockId import moe.nea.firmament.util.skyBlockId
object CustomGlobalArmorOverrides : SubscriptionOwner { object CustomGlobalArmorOverrides {
@Serializable @Serializable
data class ArmorOverride( data class ArmorOverride(
@SerialName("item_ids") @SerialName("item_ids")
val itemIds: List<String>, val itemIds: List<String>,
val layers: List<ArmorOverrideLayer>, val layers: List<ArmorOverrideLayer>? = null,
val overrides: List<ArmorOverrideOverride> = listOf(), val model: Identifier? = null,
) { val overrides: List<ArmorOverrideOverride> = listOf(),
@Transient ) {
val bakedLayers = bakeLayers(layers) @Transient
} lateinit var modelIdentifier: Identifier
fun bake() {
modelIdentifier = bakeModel(model, layers)
overrides.forEach { it.bake() }
}
fun bakeLayers(layers: List<ArmorOverrideLayer>): List<ArmorMaterial.Layer> { init {
return layers.map { ArmorMaterial.Layer(it.identifier, it.suffix, it.tint) } 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 @Serializable
data class ArmorOverrideLayer( data class ArmorOverrideLayer(
val tint: Boolean = false, val tint: Boolean = false,
val identifier: Identifier, val identifier: Identifier,
val suffix: String = "", val suffix: String = "",
) )
@Serializable @Serializable
data class ArmorOverrideOverride( data class ArmorOverrideOverride(
val predicate: FirmamentModelPredicate, val predicate: FirmamentModelPredicate,
val layers: List<ArmorOverrideLayer>, val layers: List<ArmorOverrideLayer>? = null,
) { val model: Identifier? = null,
@Transient ) {
val bakedLayers = bakeLayers(layers) 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" }
}
override val delegateFeature: FirmamentFeature @Transient
get() = CustomSkyBlockTextures lateinit var modelIdentifier: Identifier
fun bake() {
modelIdentifier = bakeModel(model, layers)
}
}
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 val overrideCache = WeakCache.memoize<ItemStack, Optional<Identifier>>("ArmorOverrides") { stack ->
fun overrideArmor(stack: ItemStack): List<ArmorMaterial.Layer>? { val id = stack.skyBlockId ?: return@memoize Optional.empty()
if (!CustomSkyBlockTextures.TConfig.enableArmorOverrides) return null val override = overrides[id.neuItem] ?: return@memoize Optional.empty()
return overrideCache.invoke(stack).getOrNull() 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() var overrides: Map<String, ArmorOverride> = mapOf()
private var bakedOverrides: MutableMap<Identifier, EquipmentModel> = mutableMapOf()
private val sentinelFirmRunning = AtomicInteger()
@Subscribe private fun bakeModel(model: Identifier?, layers: List<ArmorOverrideLayer>?): Identifier {
fun onStart(event: FinalizeResourceManagerEvent) { require(model == null || layers == null)
event.resourceManager.registerReloader(object : if (model != null) {
SinglePreparationResourceReloader<Map<String, ArmorOverride>>() { return model
override fun prepare(manager: ResourceManager, profiler: Profiler): Map<String, ArmorOverride> { } else if (layers != null) {
val overrideFiles = manager.findResources("overrides/armor_models") { val idNumber = sentinelFirmRunning.incrementAndGet()
it.namespace == "firmskyblock" && it.path.endsWith(".json") val identifier = Identifier.of("firmament:sentinel/$idNumber")
} val equipmentLayers = layers.map {
val overrides = overrideFiles.mapNotNull { EquipmentModel.Layer(
Firmament.tryDecodeJsonFromStream<ArmorOverride>(it.value.inputStream).getOrElse { ex -> it.identifier, if (it.tint) {
logger.error("Failed to load armor texture override at ${it.key}", ex) Optional.of(EquipmentModel.Dyeable(Optional.empty()))
null } else {
} Optional.empty()
} },
val associatedMap = overrides.flatMap { obj -> obj.itemIds.map { it to obj } } false
.toMap() )
return associatedMap }
} 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")
}
}
override fun apply(prepared: Map<String, ArmorOverride>, manager: ResourceManager, profiler: Profiler) {
overrides = prepared @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]
}
} }

View File

@@ -146,7 +146,7 @@ object CustomGlobalTextures : SinglePreparationResourceReloader<CustomGlobalText
it.overrides it.overrides
.asSequence() .asSequence()
.filter { it.predicate.test(stack) } .filter { it.predicate.test(stack) }
.map { models.modelManager.getModel(ModelIdentifier(it.model, "inventory")) } .map { models.getModel(it.model) }
.firstOrNull() .firstOrNull()
} }
.intoOptional() .intoOptional()

View File

@@ -1,9 +1,11 @@
package moe.nea.firmament.features.texturepack package moe.nea.firmament.features.texturepack
import net.minecraft.client.render.model.Baker
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
interface JsonUnbakedModelFirmExtra { interface JsonUnbakedModelFirmExtra {
fun storeExtraBaker_firmament(baker: Baker)
fun setHeadModel_firmament(identifier: Identifier?) fun setHeadModel_firmament(identifier: Identifier?)
fun getHeadModel_firmament(): Identifier? fun getHeadModel_firmament(): Identifier?

View File

@@ -3,7 +3,6 @@ package moe.nea.firmament.features.texturepack
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive import com.google.gson.JsonPrimitive
import moe.nea.firmament.util.ErrorUtil import moe.nea.firmament.util.ErrorUtil
import moe.nea.firmament.util.assertNotNullOr
data class TintOverrides( data class TintOverrides(
val layerMap: Map<Int, TintOverride> = mapOf() val layerMap: Map<Int, TintOverride> = mapOf()
@@ -14,21 +13,22 @@ data class TintOverrides(
val EMPTY = TintOverrides() val EMPTY = TintOverrides()
private val threadLocal = object : ThreadLocal<TintOverrides>() {} private val threadLocal = object : ThreadLocal<TintOverrides>() {}
fun enter(overrides: TintOverrides?) { fun enter(overrides: TintOverrides?) {
ErrorUtil.softCheck("Double entered tintOverrides") { ErrorUtil.softCheck("Double entered tintOverrides",
threadLocal.get() == null threadLocal.get() == null)
}
threadLocal.set(overrides ?: EMPTY) threadLocal.set(overrides ?: EMPTY)
} }
fun exit(overrides: TintOverrides?) { fun exit(overrides: TintOverrides?) {
ErrorUtil.softCheck("Exited with non matching enter tintOverrides") { ErrorUtil.softCheck("Exited with non matching enter tintOverrides",
threadLocal.get() == (overrides ?: EMPTY) threadLocal.get() == (overrides ?: EMPTY))
}
threadLocal.remove() threadLocal.remove()
} }
fun getCurrentOverrides() = fun getCurrentOverrides(): TintOverrides {
assertNotNullOr(threadLocal.get(), "Got current tintOverrides without entering") { EMPTY } return ErrorUtil.notNullOr(threadLocal.get(), "Got current tintOverrides without entering") {
EMPTY
}
}
fun parse(jsonObject: JsonObject): TintOverrides { fun parse(jsonObject: JsonObject): TintOverrides {
val map = mutableMapOf<Int, TintOverride>() val map = mutableMapOf<Int, TintOverride>()

View File

@@ -6,6 +6,7 @@ import io.github.moulberry.repo.data.Coordinate
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.serializer import kotlinx.serialization.serializer
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec3d import net.minecraft.util.math.Vec3d
import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.ProcessChatEvent import moe.nea.firmament.events.ProcessChatEvent
@@ -98,9 +99,8 @@ object FairySouls : FirmamentFeature {
fun onWorldRender(it: WorldRenderLastEvent) { fun onWorldRender(it: WorldRenderLastEvent) {
if (!TConfig.displaySouls) return if (!TConfig.displaySouls) return
renderInWorld(it) { renderInWorld(it) {
color(1F, 1F, 0F, 0.8F)
currentMissingSouls.forEach { currentMissingSouls.forEach {
block(it.blockPos) block(it.blockPos, 0x80FFFF00.toInt())
} }
color(1f, 0f, 1f, 1f) color(1f, 0f, 1f, 1f)
currentLocationSouls.forEach { currentLocationSouls.forEach {

View File

@@ -1,5 +1,3 @@
package moe.nea.firmament.features.world package moe.nea.firmament.features.world
import com.mojang.brigadier.arguments.IntegerArgumentType 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.hours
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
import net.minecraft.command.argument.BlockPosArgumentType import net.minecraft.command.argument.BlockPosArgumentType
import net.minecraft.server.command.CommandOutput
import net.minecraft.server.command.ServerCommandSource import net.minecraft.server.command.ServerCommandSource
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
@@ -35,263 +34,273 @@ import moe.nea.firmament.util.TimeMark
import moe.nea.firmament.util.render.RenderInWorldContext import moe.nea.firmament.util.render.RenderInWorldContext
object Waypoints : FirmamentFeature { object Waypoints : FirmamentFeature {
override val identifier: String override val identifier: String
get() = "waypoints" get() = "waypoints"
object TConfig : ManagedConfig(identifier, Category.MINING) { // TODO: add to misc object TConfig : ManagedConfig(identifier, Category.MINING) { // TODO: add to misc
val tempWaypointDuration by duration("temp-waypoint-duration", 0.seconds, 1.hours) { 30.seconds } val tempWaypointDuration by duration("temp-waypoint-duration", 0.seconds, 1.hours) { 30.seconds }
val showIndex by toggle("show-index") { true } val showIndex by toggle("show-index") { true }
val skipToNearest by toggle("skip-to-nearest") { false } val skipToNearest by toggle("skip-to-nearest") { false }
// TODO: look ahead size // TODO: look ahead size
} }
data class TemporaryWaypoint( data class TemporaryWaypoint(
val pos: BlockPos, val pos: BlockPos,
val postedAt: TimeMark, val postedAt: TimeMark,
) )
override val config get() = TConfig override val config get() = TConfig
val temporaryPlayerWaypointList = mutableMapOf<String, TemporaryWaypoint>() val temporaryPlayerWaypointList = mutableMapOf<String, TemporaryWaypoint>()
val temporaryPlayerWaypointMatcher = "(?i)x: (-?[0-9]+),? y: (-?[0-9]+),? z: (-?[0-9]+)".toPattern() val temporaryPlayerWaypointMatcher = "(?i)x: (-?[0-9]+),? y: (-?[0-9]+),? z: (-?[0-9]+)".toPattern()
val waypoints = mutableListOf<BlockPos>() val waypoints = mutableListOf<BlockPos>()
var ordered = false var ordered = false
var orderedIndex = 0 var orderedIndex = 0
@Serializable @Serializable
data class ColeWeightWaypoint( data class ColeWeightWaypoint(
val x: Int, val x: Int,
val y: Int, val y: Int,
val z: Int, val z: Int,
val r: Int = 0, val r: Int = 0,
val g: Int = 0, val g: Int = 0,
val b: Int = 0, val b: Int = 0,
) )
@Subscribe @Subscribe
fun onRenderOrderedWaypoints(event: WorldRenderLastEvent) { fun onRenderOrderedWaypoints(event: WorldRenderLastEvent) {
if (waypoints.isEmpty()) return if (waypoints.isEmpty()) return
RenderInWorldContext.renderInWorld(event) { RenderInWorldContext.renderInWorld(event) {
if (!ordered) { if (!ordered) {
waypoints.withIndex().forEach { waypoints.withIndex().forEach {
color(0f, 0.3f, 0.7f, 0.5f) block(it.value, 0x800050A0.toInt())
block(it.value) if (TConfig.showIndex)
color(1f, 1f, 1f, 1f) withFacingThePlayer(it.value.toCenterPos()) {
if (TConfig.showIndex) text(Text.literal(it.index.toString()))
withFacingThePlayer(it.value.toCenterPos()) { }
text(Text.literal(it.index.toString())) }
} } else {
} orderedIndex %= waypoints.size
} else { val firstColor = Color.ofRGBA(0, 200, 40, 180)
orderedIndex %= waypoints.size color(firstColor)
val firstColor = Color.ofRGBA(0, 200, 40, 180) tracer(waypoints[orderedIndex].toCenterPos(), lineWidth = 3f)
color(firstColor) waypoints.withIndex().toList()
tracer(waypoints[orderedIndex].toCenterPos(), lineWidth = 3f) .wrappingWindow(orderedIndex, 3)
waypoints.withIndex().toList() .zip(
.wrappingWindow(orderedIndex, 3) listOf(
.zip( firstColor,
listOf( Color.ofRGBA(180, 200, 40, 150),
firstColor, Color.ofRGBA(180, 80, 20, 140),
Color.ofRGBA(180, 200, 40, 150), )
Color.ofRGBA(180, 80, 20, 140), )
) .reversed()
) .forEach { (waypoint, col) ->
.reversed() val (index, pos) = waypoint
.forEach { (waypoint, col) -> block(pos, col.color)
val (index, pos) = waypoint if (TConfig.showIndex)
color(col) withFacingThePlayer(pos.toCenterPos()) {
block(pos) text(Text.literal(index.toString()))
color(1f, 1f, 1f, 1f) }
if (TConfig.showIndex) }
withFacingThePlayer(pos.toCenterPos()) { }
text(Text.literal(index.toString())) }
} }
}
}
}
}
@Subscribe @Subscribe
fun onTick(event: TickEvent) { fun onTick(event: TickEvent) {
if (waypoints.isEmpty() || !ordered) return if (waypoints.isEmpty() || !ordered) return
orderedIndex %= waypoints.size orderedIndex %= waypoints.size
val p = MC.player?.pos ?: return val p = MC.player?.pos ?: return
if (TConfig.skipToNearest) { if (TConfig.skipToNearest) {
orderedIndex = orderedIndex =
(waypoints.withIndex().minBy { it.value.getSquaredDistance(p) }.index + 1) % waypoints.size (waypoints.withIndex().minBy { it.value.getSquaredDistance(p) }.index + 1) % waypoints.size
} else { } else {
if (waypoints[orderedIndex].isWithinDistance(p, 3.0)) { if (waypoints[orderedIndex].isWithinDistance(p, 3.0)) {
orderedIndex = (orderedIndex + 1) % waypoints.size orderedIndex = (orderedIndex + 1) % waypoints.size
} }
} }
} }
@Subscribe @Subscribe
fun onProcessChat(it: ProcessChatEvent) { fun onProcessChat(it: ProcessChatEvent) {
val matcher = temporaryPlayerWaypointMatcher.matcher(it.unformattedString) val matcher = temporaryPlayerWaypointMatcher.matcher(it.unformattedString)
if (it.nameHeuristic != null && TConfig.tempWaypointDuration > 0.seconds && matcher.find()) { if (it.nameHeuristic != null && TConfig.tempWaypointDuration > 0.seconds && matcher.find()) {
temporaryPlayerWaypointList[it.nameHeuristic] = TemporaryWaypoint( temporaryPlayerWaypointList[it.nameHeuristic] = TemporaryWaypoint(
BlockPos( BlockPos(
matcher.group(1).toInt(), matcher.group(1).toInt(),
matcher.group(2).toInt(), matcher.group(2).toInt(),
matcher.group(3).toInt(), matcher.group(3).toInt(),
), ),
TimeMark.now() TimeMark.now()
) )
} }
} }
@Subscribe @Subscribe
fun onCommand(event: CommandEvent.SubCommand) { fun onCommand(event: CommandEvent.SubCommand) {
event.subcommand("waypoint") { event.subcommand("waypoint") {
thenArgument("pos", BlockPosArgumentType.blockPos()) { pos -> thenArgument("pos", BlockPosArgumentType.blockPos()) { pos ->
thenExecute { thenExecute {
val position = pos.get(this).toAbsoluteBlockPos(source.asFakeServer()) val position = pos.get(this).toAbsoluteBlockPos(source.asFakeServer())
waypoints.add(position) waypoints.add(position)
source.sendFeedback( source.sendFeedback(
Text.stringifiedTranslatable( Text.stringifiedTranslatable(
"firmament.command.waypoint.added", "firmament.command.waypoint.added",
position.x, position.x,
position.y, position.y,
position.z position.z
) )
) )
} }
} }
} }
event.subcommand("waypoints") { event.subcommand("waypoints") {
thenLiteral("clear") { thenLiteral("clear") {
thenExecute { thenExecute {
waypoints.clear() waypoints.clear()
source.sendFeedback(Text.translatable("firmament.command.waypoint.clear")) source.sendFeedback(Text.translatable("firmament.command.waypoint.clear"))
} }
} }
thenLiteral("toggleordered") { thenLiteral("toggleordered") {
thenExecute { thenExecute {
ordered = !ordered ordered = !ordered
if (ordered) { if (ordered) {
val p = MC.player?.pos ?: Vec3d.ZERO val p = MC.player?.pos ?: Vec3d.ZERO
orderedIndex = orderedIndex =
waypoints.withIndex().minByOrNull { it.value.getSquaredDistance(p) }?.index ?: 0 waypoints.withIndex().minByOrNull { it.value.getSquaredDistance(p) }?.index ?: 0
} }
source.sendFeedback(Text.translatable("firmament.command.waypoint.ordered.toggle.$ordered")) source.sendFeedback(Text.translatable("firmament.command.waypoint.ordered.toggle.$ordered"))
} }
} }
thenLiteral("skip") { thenLiteral("skip") {
thenExecute { thenExecute {
if (ordered && waypoints.isNotEmpty()) { if (ordered && waypoints.isNotEmpty()) {
orderedIndex = (orderedIndex + 1) % waypoints.size orderedIndex = (orderedIndex + 1) % waypoints.size
source.sendFeedback(Text.translatable("firmament.command.waypoint.skip")) source.sendFeedback(Text.translatable("firmament.command.waypoint.skip"))
} else { } else {
source.sendError(Text.translatable("firmament.command.waypoint.skip.error")) source.sendError(Text.translatable("firmament.command.waypoint.skip.error"))
} }
} }
} }
thenLiteral("remove") { thenLiteral("remove") {
thenArgument("index", IntegerArgumentType.integer(0)) { indexArg -> thenArgument("index", IntegerArgumentType.integer(0)) { indexArg ->
thenExecute { thenExecute {
val index = get(indexArg) val index = get(indexArg)
if (index in waypoints.indices) { if (index in waypoints.indices) {
waypoints.removeAt(index) waypoints.removeAt(index)
source.sendFeedback(Text.stringifiedTranslatable( source.sendFeedback(Text.stringifiedTranslatable(
"firmament.command.waypoint.remove", "firmament.command.waypoint.remove",
index)) index))
} else { } else {
source.sendError(Text.stringifiedTranslatable("firmament.command.waypoint.remove.error")) source.sendError(Text.stringifiedTranslatable("firmament.command.waypoint.remove.error"))
} }
} }
} }
} }
thenLiteral("import") { thenLiteral("import") {
thenExecute { thenExecute {
val contents = ClipboardUtils.getTextContents() val contents = ClipboardUtils.getTextContents()
val data = try { val data = try {
Firmament.json.decodeFromString<List<ColeWeightWaypoint>>(contents) Firmament.json.decodeFromString<List<ColeWeightWaypoint>>(contents)
} catch (ex: Exception) { } catch (ex: Exception) {
Firmament.logger.error("Could not load waypoints from clipboard", ex) Firmament.logger.error("Could not load waypoints from clipboard", ex)
source.sendError(Text.translatable("firmament.command.waypoint.import.error")) source.sendError(Text.translatable("firmament.command.waypoint.import.error"))
return@thenExecute return@thenExecute
} }
waypoints.clear() waypoints.clear()
data.mapTo(waypoints) { BlockPos(it.x, it.y, it.z) } data.mapTo(waypoints) { BlockPos(it.x, it.y, it.z) }
source.sendFeedback( source.sendFeedback(
Text.stringifiedTranslatable( Text.stringifiedTranslatable(
"firmament.command.waypoint.import", "firmament.command.waypoint.import",
data.size data.size
) )
) )
} }
} }
} }
} }
@Subscribe @Subscribe
fun onRenderTemporaryWaypoints(event: WorldRenderLastEvent) { fun onRenderTemporaryWaypoints(event: WorldRenderLastEvent) {
temporaryPlayerWaypointList.entries.removeIf { it.value.postedAt.passedTime() > TConfig.tempWaypointDuration } temporaryPlayerWaypointList.entries.removeIf { it.value.postedAt.passedTime() > TConfig.tempWaypointDuration }
if (temporaryPlayerWaypointList.isEmpty()) return if (temporaryPlayerWaypointList.isEmpty()) return
RenderInWorldContext.renderInWorld(event) { RenderInWorldContext.renderInWorld(event) {
color(1f, 1f, 0f, 1f) temporaryPlayerWaypointList.forEach { (player, waypoint) ->
temporaryPlayerWaypointList.forEach { (player, waypoint) -> block(waypoint.pos, 0xFFFFFF00.toInt())
block(waypoint.pos) }
} temporaryPlayerWaypointList.forEach { (player, waypoint) ->
color(1f, 1f, 1f, 1f) val skin =
temporaryPlayerWaypointList.forEach { (player, waypoint) -> MC.networkHandler?.listedPlayerListEntries?.find { it.profile.name == player }
val skin = ?.skinTextures
MC.networkHandler?.listedPlayerListEntries?.find { it.profile.name == player } ?.texture
?.skinTextures withFacingThePlayer(waypoint.pos.toCenterPos()) {
?.texture waypoint(waypoint.pos, Text.stringifiedTranslatable("firmament.waypoint.temporary", player))
withFacingThePlayer(waypoint.pos.toCenterPos()) { if (skin != null) {
waypoint(waypoint.pos, Text.stringifiedTranslatable("firmament.waypoint.temporary", player)) matrixStack.translate(0F, -20F, 0F)
if (skin != null) { // Head front
matrixStack.translate(0F, -20F, 0F) texture(
// Head front skin, 16, 16,
texture( 1 / 8f, 1 / 8f,
skin, 16, 16, 2 / 8f, 2 / 8f,
1 / 8f, 1 / 8f, )
2 / 8f, 2 / 8f, // Head overlay
) texture(
// Head overlay skin, 16, 16,
texture( 5 / 8f, 1 / 8f,
skin, 16, 16, 6 / 8f, 2 / 8f,
5 / 8f, 1 / 8f, )
6 / 8f, 2 / 8f, }
) }
} }
} }
} }
}
}
@Subscribe @Subscribe
fun onWorldReady(event: WorldReadyEvent) { fun onWorldReady(event: WorldReadyEvent) {
temporaryPlayerWaypointList.clear() temporaryPlayerWaypointList.clear()
} }
} }
fun <E> List<E>.wrappingWindow(startIndex: Int, windowSize: Int): List<E> { fun <E> List<E>.wrappingWindow(startIndex: Int, windowSize: Int): List<E> {
val result = ArrayList<E>(windowSize) val result = ArrayList<E>(windowSize)
if (startIndex + windowSize < size) { if (startIndex + windowSize < size) {
result.addAll(subList(startIndex, startIndex + windowSize)) result.addAll(subList(startIndex, startIndex + windowSize))
} else { } else {
result.addAll(subList(startIndex, size)) result.addAll(subList(startIndex, size))
result.addAll(subList(0, minOf(windowSize - (size - startIndex), startIndex))) result.addAll(subList(0, minOf(windowSize - (size - startIndex), startIndex)))
} }
return result return result
} }
fun FabricClientCommandSource.asFakeServer(): ServerCommandSource { fun FabricClientCommandSource.asFakeServer(): ServerCommandSource {
val source = this val source = this
return ServerCommandSource( return ServerCommandSource(
source.player, object : CommandOutput {
source.position, override fun sendMessage(message: Text?) {
source.rotation, source.player.sendMessage(message, false)
null, }
0,
"FakeServerCommandSource", override fun shouldReceiveFeedback(): Boolean {
Text.literal("FakeServerCommandSource"), return true
null, }
source.player
) 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
)
} }

View File

@@ -1,4 +1,3 @@
package moe.nea.firmament.gui package moe.nea.firmament.gui
import com.mojang.blaze3d.systems.RenderSystem 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 io.github.notenoughupdates.moulconfig.platform.ModernRenderContext
import me.shedaniel.math.Color import me.shedaniel.math.Color
import net.minecraft.client.gui.DrawContext import net.minecraft.client.gui.DrawContext
import net.minecraft.client.render.RenderLayer
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
class BarComponent( class BarComponent(
val progress: GetSetter<Double>, val total: GetSetter<Double>, val progress: GetSetter<Double>, val total: GetSetter<Double>,
val fillColor: Color, val fillColor: Color,
val emptyColor: Color, val emptyColor: Color,
) : GuiComponent() { ) : GuiComponent() {
override fun getWidth(): Int { override fun getWidth(): Int {
return 80 return 80
} }
override fun getHeight(): Int { override fun getHeight(): Int {
return 8 return 8
} }
data class Texture( data class Texture(
val identifier: Identifier, val identifier: Identifier,
val u1: Float, val v1: Float, val u1: Float, val v1: Float,
val u2: Float, val v2: Float, val u2: Float, val v2: Float,
) { ) {
fun draw(context: DrawContext, x: Int, y: Int, width: Int, height: Int, color: Color) { fun draw(context: DrawContext, x: Int, y: Int, width: Int, height: Int, color: Color) {
context.drawTexturedQuad( context.drawTexturedQuad(
identifier, RenderLayer::getGuiTextured,
x, y, x + width, x + height, 0, identifier,
u1, u2, v1, v2, x, y, x + width, x + height,
color.red / 255F, u1, u2, v1, v2,
color.green / 255F, color.color
color.blue / 255F, )
color.alpha / 255F, }
) }
}
}
companion object { companion object {
val resource = Firmament.identifier("textures/gui/bar.png") val resource = Firmament.identifier("textures/gui/bar.png")
val left = Texture(resource, 0 / 64F, 0 / 64F, 4 / 64F, 8 / 64F) 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 middle = Texture(resource, 4 / 64F, 0 / 64F, 8 / 64F, 8 / 64F)
val right = Texture(resource, 8 / 64F, 0 / 64F, 12 / 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) val segmentOverlay = Texture(resource, 12 / 64F, 0 / 64F, 15 / 64F, 8 / 64F)
} }
private fun drawSection( private fun drawSection(
context: DrawContext, context: DrawContext,
texture: Texture, texture: Texture,
x: Int, x: Int,
y: Int, y: Int,
width: Int, width: Int,
sectionStart: Double, sectionStart: Double,
sectionEnd: Double sectionEnd: Double
) { ) {
if (sectionEnd < progress.get() && width == 4) { if (sectionEnd < progress.get() && width == 4) {
texture.draw(context, x, y, 4, 8, fillColor) texture.draw(context, x, y, 4, 8, fillColor)
return return
} }
if (sectionStart > progress.get() && width == 4) { if (sectionStart > progress.get() && width == 4) {
texture.draw(context, x, y, 4, 8, emptyColor) texture.draw(context, x, y, 4, 8, emptyColor)
return return
} }
val increasePerPixel = (sectionEnd - sectionStart) / width val increasePerPixel = (sectionEnd - sectionStart) / width
var valueAtPixel = sectionStart var valueAtPixel = sectionStart
for (i in (0 until width)) { for (i in (0 until width)) {
val newTex = val newTex =
Texture(texture.identifier, texture.u1 + i / 64F, texture.v1, texture.u1 + (i + 1) / 64F, texture.v2) Texture(texture.identifier, texture.u1 + i / 64F, texture.v1, texture.u1 + (i + 1) / 64F, texture.v2)
newTex.draw( newTex.draw(
context, x + i, y, 1, 8, context, x + i, y, 1, 8,
if (valueAtPixel < progress.get()) fillColor else emptyColor if (valueAtPixel < progress.get()) fillColor else emptyColor
) )
valueAtPixel += increasePerPixel valueAtPixel += increasePerPixel
} }
} }
override fun render(context: GuiImmediateContext) { override fun render(context: GuiImmediateContext) {
val renderContext = (context.renderContext as ModernRenderContext).drawContext val renderContext = (context.renderContext as ModernRenderContext).drawContext
var i = 0 var i = 0
val x = 0 val x = 0
val y = 0 val y = 0
while (i < context.width - 4) { while (i < context.width - 4) {
drawSection( drawSection(
renderContext, renderContext,
if (i == 0) left else middle, if (i == 0) left else middle,
x + i, y, x + i, y,
(context.width - (i + 4)).coerceAtMost(4), (context.width - (i + 4)).coerceAtMost(4),
i * total.get() / context.width, (i + 4) * total.get() / context.width i * total.get() / context.width, (i + 4) * total.get() / context.width
) )
i += 4 i += 4
} }
drawSection( drawSection(
renderContext, renderContext,
right, right,
x + context.width - 4, x + context.width - 4,
y, y,
4, 4,
(context.width - 4) * total.get() / context.width, (context.width - 4) * total.get() / context.width,
total.get() total.get()
) )
RenderSystem.setShaderColor(1F, 1F, 1F, 1F) RenderSystem.setShaderColor(1F, 1F, 1F, 1F)
} }
} }
fun Identifier.toMoulConfig(): MyResourceLocation { fun Identifier.toMoulConfig(): MyResourceLocation {
return MyResourceLocation(this.namespace, this.path) return MyResourceLocation(this.namespace, this.path)
} }
fun RenderContext.color(color: Color) { 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) { 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)
} }

View File

@@ -1,4 +1,3 @@
package moe.nea.firmament.gui.entity package moe.nea.firmament.gui.entity
import com.google.gson.Gson 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.Entity
import net.minecraft.entity.EntityType import net.minecraft.entity.EntityType
import net.minecraft.entity.LivingEntity import net.minecraft.entity.LivingEntity
import net.minecraft.entity.SpawnReason
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.world.World
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
import moe.nea.firmament.util.assertNotNullOr import moe.nea.firmament.util.assertNotNullOr
import moe.nea.firmament.util.iterate import moe.nea.firmament.util.iterate
@@ -21,177 +22,177 @@ import moe.nea.firmament.util.openFirmamentResource
import moe.nea.firmament.util.render.enableScissorWithTranslation import moe.nea.firmament.util.render.enableScissorWithTranslation
object EntityRenderer { object EntityRenderer {
val fakeWorld = FakeWorld() val fakeWorld: World get() = MC.lastWorld!!
private fun <T : Entity> t(entityType: EntityType<T>): () -> T { private fun <T : Entity> t(entityType: EntityType<T>): () -> T {
return { entityType.create(fakeWorld)!! } return { entityType.create(fakeWorld, SpawnReason.LOAD)!! }
} }
val entityIds: Map<String, () -> LivingEntity> = mapOf( val entityIds: Map<String, () -> LivingEntity> = mapOf(
"Zombie" to t(EntityType.ZOMBIE), "Zombie" to t(EntityType.ZOMBIE),
"Chicken" to t(EntityType.CHICKEN), "Chicken" to t(EntityType.CHICKEN),
"Slime" to t(EntityType.SLIME), "Slime" to t(EntityType.SLIME),
"Wolf" to t(EntityType.WOLF), "Wolf" to t(EntityType.WOLF),
"Skeleton" to t(EntityType.SKELETON), "Skeleton" to t(EntityType.SKELETON),
"Creeper" to t(EntityType.CREEPER), "Creeper" to t(EntityType.CREEPER),
"Ocelot" to t(EntityType.OCELOT), "Ocelot" to t(EntityType.OCELOT),
"Blaze" to t(EntityType.BLAZE), "Blaze" to t(EntityType.BLAZE),
"Rabbit" to t(EntityType.RABBIT), "Rabbit" to t(EntityType.RABBIT),
"Sheep" to t(EntityType.SHEEP), "Sheep" to t(EntityType.SHEEP),
"Horse" to t(EntityType.HORSE), "Horse" to t(EntityType.HORSE),
"Eisengolem" to t(EntityType.IRON_GOLEM), "Eisengolem" to t(EntityType.IRON_GOLEM),
"Silverfish" to t(EntityType.SILVERFISH), "Silverfish" to t(EntityType.SILVERFISH),
"Witch" to t(EntityType.WITCH), "Witch" to t(EntityType.WITCH),
"Endermite" to t(EntityType.ENDERMITE), "Endermite" to t(EntityType.ENDERMITE),
"Snowman" to t(EntityType.SNOW_GOLEM), "Snowman" to t(EntityType.SNOW_GOLEM),
"Villager" to t(EntityType.VILLAGER), "Villager" to t(EntityType.VILLAGER),
"Guardian" to t(EntityType.GUARDIAN), "Guardian" to t(EntityType.GUARDIAN),
"ArmorStand" to t(EntityType.ARMOR_STAND), "ArmorStand" to t(EntityType.ARMOR_STAND),
"Squid" to t(EntityType.SQUID), "Squid" to t(EntityType.SQUID),
"Bat" to t(EntityType.BAT), "Bat" to t(EntityType.BAT),
"Spider" to t(EntityType.SPIDER), "Spider" to t(EntityType.SPIDER),
"CaveSpider" to t(EntityType.CAVE_SPIDER), "CaveSpider" to t(EntityType.CAVE_SPIDER),
"Pigman" to t(EntityType.ZOMBIFIED_PIGLIN), "Pigman" to t(EntityType.ZOMBIFIED_PIGLIN),
"Ghast" to t(EntityType.GHAST), "Ghast" to t(EntityType.GHAST),
"MagmaCube" to t(EntityType.MAGMA_CUBE), "MagmaCube" to t(EntityType.MAGMA_CUBE),
"Wither" to t(EntityType.WITHER), "Wither" to t(EntityType.WITHER),
"Enderman" to t(EntityType.ENDERMAN), "Enderman" to t(EntityType.ENDERMAN),
"Mooshroom" to t(EntityType.MOOSHROOM), "Mooshroom" to t(EntityType.MOOSHROOM),
"WitherSkeleton" to t(EntityType.WITHER_SKELETON), "WitherSkeleton" to t(EntityType.WITHER_SKELETON),
"Cow" to t(EntityType.COW), "Cow" to t(EntityType.COW),
"Dragon" to t(EntityType.ENDER_DRAGON), "Dragon" to t(EntityType.ENDER_DRAGON),
"Player" to { makeGuiPlayer(fakeWorld) }, "Player" to { makeGuiPlayer(fakeWorld) },
"Pig" to t(EntityType.PIG), "Pig" to t(EntityType.PIG),
"Giant" to t(EntityType.GIANT), "Giant" to t(EntityType.GIANT),
) )
val entityModifiers: Map<String, EntityModifier> = mapOf( val entityModifiers: Map<String, EntityModifier> = mapOf(
"playerdata" to ModifyPlayerSkin, "playerdata" to ModifyPlayerSkin,
"equipment" to ModifyEquipment, "equipment" to ModifyEquipment,
"riding" to ModifyRiding, "riding" to ModifyRiding,
"charged" to ModifyCharged, "charged" to ModifyCharged,
"witherdata" to ModifyWither, "witherdata" to ModifyWither,
"invisible" to ModifyInvisible, "invisible" to ModifyInvisible,
"age" to ModifyAge, "age" to ModifyAge,
"horse" to ModifyHorse, "horse" to ModifyHorse,
"name" to ModifyName, "name" to ModifyName,
) )
val logger = LogManager.getLogger("Firmament.Entity") val logger = LogManager.getLogger("Firmament.Entity")
fun applyModifiers(entityId: String, modifiers: List<JsonObject>): LivingEntity? { fun applyModifiers(entityId: String, modifiers: List<JsonObject>): LivingEntity? {
val entityType = assertNotNullOr(entityIds[entityId]) { val entityType = assertNotNullOr(entityIds[entityId]) {
logger.error("Could not create entity with id $entityId") logger.error("Could not create entity with id $entityId")
return null return null
} }
var entity = entityType() var entity = entityType()
for (modifierJson in modifiers) { for (modifierJson in modifiers) {
val modifier = assertNotNullOr(modifierJson["type"]?.asString?.let(entityModifiers::get)) { val modifier = assertNotNullOr(modifierJson["type"]?.asString?.let(entityModifiers::get)) {
logger.error("Unknown modifier $modifierJson") logger.error("Unknown modifier $modifierJson")
return null return null
} }
entity = modifier.apply(entity, modifierJson) entity = modifier.apply(entity, modifierJson)
} }
return entity return entity
} }
fun constructEntity(info: JsonObject): LivingEntity? { fun constructEntity(info: JsonObject): LivingEntity? {
val modifiers = (info["modifiers"] as JsonArray?)?.map { it.asJsonObject } ?: emptyList() val modifiers = (info["modifiers"] as JsonArray?)?.map { it.asJsonObject } ?: emptyList()
val entityType = assertNotNullOr(info["entity"]?.asString) { val entityType = assertNotNullOr(info["entity"]?.asString) {
logger.error("Missing entity type on entity object") logger.error("Missing entity type on entity object")
return null return null
} }
return applyModifiers(entityType, modifiers) return applyModifiers(entityType, modifiers)
} }
private val gson = Gson() private val gson = Gson()
fun constructEntity(location: Identifier): LivingEntity? { fun constructEntity(location: Identifier): LivingEntity? {
return constructEntity( return constructEntity(
gson.fromJson( gson.fromJson(
location.openFirmamentResource().bufferedReader(), JsonObject::class.java location.openFirmamentResource().bufferedReader(), JsonObject::class.java
) )
) )
} }
fun renderEntity( fun renderEntity(
entity: LivingEntity, entity: LivingEntity,
renderContext: DrawContext, renderContext: DrawContext,
posX: Int, posX: Int,
posY: Int, posY: Int,
mouseX: Float, mouseX: Float,
mouseY: Float mouseY: Float
) { ) {
var bottomOffset = 0.0F var bottomOffset = 0.0F
var currentEntity = entity var currentEntity = entity
val maxSize = entity.iterate { it.firstPassenger as? LivingEntity } val maxSize = entity.iterate { it.firstPassenger as? LivingEntity }
.map { it.height } .map { it.height }
.sum() .sum()
while (true) { while (true) {
currentEntity.age = MC.player?.age ?: 0 currentEntity.age = MC.player?.age ?: 0
drawEntity( drawEntity(
renderContext, renderContext,
posX, posX,
posY, posY,
posX + 50, posX + 50,
posY + 80, posY + 80,
minOf(2F / maxSize, 1F) * 30, minOf(2F / maxSize, 1F) * 30,
-bottomOffset, -bottomOffset,
mouseX, mouseX,
mouseY, mouseY,
currentEntity currentEntity
) )
val next = currentEntity.firstPassenger as? LivingEntity ?: break val next = currentEntity.firstPassenger as? LivingEntity ?: break
bottomOffset += currentEntity.getPassengerRidingPos(next).y.toFloat() * 0.75F bottomOffset += currentEntity.getPassengerRidingPos(next).y.toFloat() * 0.75F
currentEntity = next currentEntity = next
} }
} }
fun drawEntity( fun drawEntity(
context: DrawContext, context: DrawContext,
x1: Int, x1: Int,
y1: Int, y1: Int,
x2: Int, x2: Int,
y2: Int, y2: Int,
size: Float, size: Float,
bottomOffset: Float, bottomOffset: Float,
mouseX: Float, mouseX: Float,
mouseY: Float, mouseY: Float,
entity: LivingEntity entity: LivingEntity
) { ) {
context.enableScissorWithTranslation(x1.toFloat(), y1.toFloat(), x2.toFloat(), y2.toFloat()) context.enableScissorWithTranslation(x1.toFloat(), y1.toFloat(), x2.toFloat(), y2.toFloat())
val centerX = (x1 + x2) / 2f val centerX = (x1 + x2) / 2f
val centerY = (y1 + y2) / 2f val centerY = (y1 + y2) / 2f
val targetYaw = atan(((centerX - mouseX) / 40.0f).toDouble()).toFloat() val targetYaw = atan(((centerX - mouseX) / 40.0f).toDouble()).toFloat()
val targetPitch = atan(((centerY - mouseY) / 40.0f).toDouble()).toFloat() val targetPitch = atan(((centerY - mouseY) / 40.0f).toDouble()).toFloat()
val rotateToFaceTheFront = Quaternionf().rotateZ(Math.PI.toFloat()) val rotateToFaceTheFront = Quaternionf().rotateZ(Math.PI.toFloat())
val rotateToFaceTheCamera = Quaternionf().rotateX(targetPitch * 20.0f * (Math.PI.toFloat() / 180)) val rotateToFaceTheCamera = Quaternionf().rotateX(targetPitch * 20.0f * (Math.PI.toFloat() / 180))
rotateToFaceTheFront.mul(rotateToFaceTheCamera) rotateToFaceTheFront.mul(rotateToFaceTheCamera)
val oldBodyYaw = entity.bodyYaw val oldBodyYaw = entity.bodyYaw
val oldYaw = entity.yaw val oldYaw = entity.yaw
val oldPitch = entity.pitch val oldPitch = entity.pitch
val oldPrevHeadYaw = entity.prevHeadYaw val oldPrevHeadYaw = entity.prevHeadYaw
val oldHeadYaw = entity.headYaw val oldHeadYaw = entity.headYaw
entity.bodyYaw = 180.0f + targetYaw * 20.0f entity.bodyYaw = 180.0f + targetYaw * 20.0f
entity.yaw = 180.0f + targetYaw * 40.0f entity.yaw = 180.0f + targetYaw * 40.0f
entity.pitch = -targetPitch * 20.0f entity.pitch = -targetPitch * 20.0f
entity.headYaw = entity.yaw entity.headYaw = entity.yaw
entity.prevHeadYaw = entity.yaw entity.prevHeadYaw = entity.yaw
val vector3f = Vector3f(0.0f, entity.height / 2.0f + bottomOffset, 0.0f) val vector3f = Vector3f(0.0f, entity.height / 2.0f + bottomOffset, 0.0f)
InventoryScreen.drawEntity( InventoryScreen.drawEntity(
context, context,
centerX, centerX,
centerY, centerY,
size, size,
vector3f, vector3f,
rotateToFaceTheFront, rotateToFaceTheFront,
rotateToFaceTheCamera, rotateToFaceTheCamera,
entity entity
) )
entity.bodyYaw = oldBodyYaw entity.bodyYaw = oldBodyYaw
entity.yaw = oldYaw entity.yaw = oldYaw
entity.pitch = oldPitch entity.pitch = oldPitch
entity.prevHeadYaw = oldPrevHeadYaw entity.prevHeadYaw = oldPrevHeadYaw
entity.headYaw = oldHeadYaw entity.headYaw = oldHeadYaw
context.disableScissor() context.disableScissor()
} }
} }

View File

@@ -1,38 +1,37 @@
package moe.nea.firmament.gui.entity package moe.nea.firmament.gui.entity
import com.mojang.datafixers.util.Pair import java.util.UUID
import com.mojang.serialization.Lifecycle
import java.util.*
import java.util.function.BooleanSupplier import java.util.function.BooleanSupplier
import java.util.function.Consumer 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.Block
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.client.gui.screen.world.SelectWorldScreen
import net.minecraft.component.type.MapIdComponent import net.minecraft.component.type.MapIdComponent
import net.minecraft.entity.Entity import net.minecraft.entity.Entity
import net.minecraft.entity.damage.DamageSource
import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerEntity
import net.minecraft.fluid.Fluid import net.minecraft.fluid.Fluid
import net.minecraft.item.FuelRegistry
import net.minecraft.item.map.MapState import net.minecraft.item.map.MapState
import net.minecraft.particle.ParticleEffect
import net.minecraft.recipe.BrewingRecipeRegistry import net.minecraft.recipe.BrewingRecipeRegistry
import net.minecraft.recipe.Ingredient
import net.minecraft.recipe.RecipeManager 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.DynamicRegistryManager
import net.minecraft.registry.Registry import net.minecraft.registry.Registries
import net.minecraft.registry.RegistryKey import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys 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.RegistryEntry
import net.minecraft.registry.entry.RegistryEntryInfo import net.minecraft.resource.DataConfiguration
import net.minecraft.registry.entry.RegistryEntryList import net.minecraft.resource.ResourcePackManager
import net.minecraft.registry.entry.RegistryEntryOwner
import net.minecraft.registry.tag.TagKey
import net.minecraft.resource.featuretoggle.FeatureFlags import net.minecraft.resource.featuretoggle.FeatureFlags
import net.minecraft.resource.featuretoggle.FeatureSet import net.minecraft.resource.featuretoggle.FeatureSet
import net.minecraft.scoreboard.Scoreboard 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.SoundCategory
import net.minecraft.sound.SoundEvent import net.minecraft.sound.SoundEvent
import net.minecraft.util.Identifier 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.ChunkPos
import net.minecraft.util.math.Direction import net.minecraft.util.math.Direction
import net.minecraft.util.math.Vec3d 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.BlockView
import net.minecraft.world.Difficulty import net.minecraft.world.Difficulty
import net.minecraft.world.GameRules
import net.minecraft.world.MutableWorldProperties import net.minecraft.world.MutableWorldProperties
import net.minecraft.world.World import net.minecraft.world.World
import net.minecraft.world.biome.Biome 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.chunk.light.LightingProvider
import net.minecraft.world.entity.EntityLookup import net.minecraft.world.entity.EntityLookup
import net.minecraft.world.event.GameEvent import net.minecraft.world.event.GameEvent
import net.minecraft.world.explosion.ExplosionBehavior
import net.minecraft.world.tick.OrderedTick import net.minecraft.world.tick.OrderedTick
import net.minecraft.world.tick.QueryableTickScheduler import net.minecraft.world.tick.QueryableTickScheduler
import net.minecraft.world.tick.TickManager import net.minecraft.world.tick.TickManager
import moe.nea.firmament.util.MC
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()])
}
}
}
fun createDynamicRegistry(): DynamicRegistryManager.Immutable { fun createDynamicRegistry(): DynamicRegistryManager.Immutable {
val wrapperLookup = BuiltinRegistries.createWrapperLookup() // TODO: use SaveLoading.load() to properly load a full registry
return object : DynamicRegistryManager.Immutable { return DynamicRegistryManager.of(Registries.REGISTRIES)
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>>) }
}
}
} }
class FakeWorld( class FakeWorld(
registries: DynamicRegistryManager.Immutable = createDynamicRegistry(), registries: DynamicRegistryManager.Immutable = createDynamicRegistry(),
) : World( ) : World(
Properties, Properties,
RegistryKey.of(RegistryKeys.WORLD, Identifier.of("firmament", "fakeworld")), RegistryKey.of(RegistryKeys.WORLD, Identifier.of("firmament", "fakeworld")),
registries, registries,
registries[RegistryKeys.DIMENSION_TYPE].entryOf( MC.defaultRegistries.getOrThrow(RegistryKeys.DIMENSION_TYPE)
RegistryKey.of( .getOrThrow(RegistryKey.of(RegistryKeys.DIMENSION_TYPE, Identifier.of("minecraft", "overworld"))),
RegistryKeys.DIMENSION_TYPE, true,
Identifier.of("minecraft", "overworld") false,
) 0L,
), 0
{ DummyProfiler.INSTANCE },
true,
false,
0, 0
) { ) {
object Properties : MutableWorldProperties { object Properties : MutableWorldProperties {
override fun getSpawnPos(): BlockPos { override fun getSpawnPos(): BlockPos {
return BlockPos.ORIGIN return BlockPos.ORIGIN
} }
override fun getSpawnAngle(): Float { override fun getSpawnAngle(): Float {
return 0F return 0F
} }
override fun getTime(): Long { override fun getTime(): Long {
return 0 return 0
} }
override fun getTimeOfDay(): Long { override fun getTimeOfDay(): Long {
return 0 return 0
} }
override fun isThundering(): Boolean { override fun isThundering(): Boolean {
return false return false
} }
override fun isRaining(): Boolean { override fun isRaining(): Boolean {
return false return false
} }
override fun setRaining(raining: Boolean) { override fun setRaining(raining: Boolean) {
} }
override fun isHardcore(): Boolean { override fun isHardcore(): Boolean {
return false return false
} }
override fun getGameRules(): GameRules { override fun getDifficulty(): Difficulty {
return GameRules() return Difficulty.HARD
} }
override fun getDifficulty(): Difficulty { override fun isDifficultyLocked(): Boolean {
return Difficulty.HARD return false
} }
override fun isDifficultyLocked(): Boolean { override fun setSpawnPos(pos: BlockPos?, angle: Float) {}
return false }
}
override fun setSpawnPos(pos: BlockPos?, angle: Float) {} override fun getPlayers(): List<PlayerEntity> {
} return emptyList()
}
override fun getPlayers(): List<PlayerEntity> { override fun getBrightness(direction: Direction?, shaded: Boolean): Float {
return emptyList() return 1f
} }
override fun getBrightness(direction: Direction?, shaded: Boolean): Float { override fun getGeneratorStoredBiome(biomeX: Int, biomeY: Int, biomeZ: Int): RegistryEntry<Biome> {
return 1f return registryManager.getOptionalEntry(BiomeKeys.PLAINS).get()
} }
override fun getGeneratorStoredBiome(biomeX: Int, biomeY: Int, biomeZ: Int): RegistryEntry<Biome> { override fun getSeaLevel(): Int {
return registryManager.get(RegistryKeys.BIOME).entryOf(BiomeKeys.PLAINS) return 0
} }
override fun getEnabledFeatures(): FeatureSet { override fun getEnabledFeatures(): FeatureSet {
return FeatureFlags.VANILLA_FEATURES return FeatureFlags.VANILLA_FEATURES
} }
class FakeTickScheduler<T> : QueryableTickScheduler<T> { class FakeTickScheduler<T> : QueryableTickScheduler<T> {
override fun scheduleTick(orderedTick: OrderedTick<T>?) { override fun scheduleTick(orderedTick: OrderedTick<T>?) {
} }
override fun isQueued(pos: BlockPos?, type: T): Boolean { override fun isQueued(pos: BlockPos?, type: T): Boolean {
return true return true
} }
override fun getTickCount(): Int { override fun getTickCount(): Int {
return 0 return 0
} }
override fun isTicking(pos: BlockPos?, type: T): Boolean { override fun isTicking(pos: BlockPos?, type: T): Boolean {
return true return true
} }
} }
override fun getBlockTickScheduler(): QueryableTickScheduler<Block> { override fun getBlockTickScheduler(): QueryableTickScheduler<Block> {
return FakeTickScheduler() return FakeTickScheduler()
} }
override fun getFluidTickScheduler(): QueryableTickScheduler<Fluid> { override fun getFluidTickScheduler(): QueryableTickScheduler<Fluid> {
return FakeTickScheduler() return FakeTickScheduler()
} }
class FakeChunkManager(val world: FakeWorld) : ChunkManager() { class FakeChunkManager(val world: FakeWorld) : ChunkManager() {
override fun getChunk(x: Int, z: Int, leastStatus: ChunkStatus?, create: Boolean): Chunk { override fun getChunk(x: Int, z: Int, leastStatus: ChunkStatus?, create: Boolean): Chunk {
return EmptyChunk( return EmptyChunk(
world, world,
ChunkPos(x, z), ChunkPos(x, z),
world.registryManager.get(RegistryKeys.BIOME).entryOf(BiomeKeys.PLAINS) world.registryManager.getOptionalEntry(BiomeKeys.PLAINS).get()
) )
} }
override fun getWorld(): BlockView { override fun getWorld(): BlockView {
return world return world
} }
override fun tick(shouldKeepTicking: BooleanSupplier?, tickChunks: Boolean) { override fun tick(shouldKeepTicking: BooleanSupplier?, tickChunks: Boolean) {
} }
override fun getDebugString(): String { override fun getDebugString(): String {
return "FakeChunkManager" return "FakeChunkManager"
} }
override fun getLoadedChunkCount(): Int { override fun getLoadedChunkCount(): Int {
return 0 return 0
} }
override fun getLightingProvider(): LightingProvider { override fun getLightingProvider(): LightingProvider {
return FakeLightingProvider(this) return FakeLightingProvider(this)
} }
} }
class FakeLightingProvider(chunkManager: FakeChunkManager) : LightingProvider(chunkManager, false, false) class FakeLightingProvider(chunkManager: FakeChunkManager) : LightingProvider(chunkManager, false, false)
override fun getChunkManager(): ChunkManager { override fun getChunkManager(): ChunkManager {
return FakeChunkManager(this) return FakeChunkManager(this)
} }
override fun playSound( override fun playSound(
source: PlayerEntity?, source: PlayerEntity?,
x: Double, x: Double,
y: Double, y: Double,
z: Double, z: Double,
sound: RegistryEntry<SoundEvent>?, sound: RegistryEntry<SoundEvent>?,
category: SoundCategory?, category: SoundCategory?,
volume: Float, volume: Float,
pitch: Float, pitch: Float,
seed: Long seed: Long
) { ) {
} }
override fun syncWorldEvent(player: PlayerEntity?, eventId: Int, pos: BlockPos?, data: Int) { override fun syncWorldEvent(player: PlayerEntity?, eventId: Int, pos: BlockPos?, data: Int) {
} }
override fun emitGameEvent(event: RegistryEntry<GameEvent>?, emitterPos: Vec3d?, emitter: GameEvent.Emitter?) { override fun emitGameEvent(event: RegistryEntry<GameEvent>?, emitterPos: Vec3d?, emitter: GameEvent.Emitter?) {
} }
override fun updateListeners(pos: BlockPos?, oldState: BlockState?, newState: BlockState?, flags: Int) { override fun updateListeners(pos: BlockPos?, oldState: BlockState?, newState: BlockState?, flags: Int) {
} }
override fun playSoundFromEntity( override fun playSoundFromEntity(
source: PlayerEntity?, source: PlayerEntity?,
entity: Entity?, entity: Entity?,
sound: RegistryEntry<SoundEvent>?, sound: RegistryEntry<SoundEvent>?,
category: SoundCategory?, category: SoundCategory?,
volume: Float, volume: Float,
pitch: Float, pitch: Float,
seed: Long seed: Long
) { ) {
} }
override fun asString(): String { override fun createExplosion(
return "FakeWorld" 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 getEntityById(id: Int): Entity? { override fun asString(): String {
return null return "FakeWorld"
} }
override fun getTickManager(): TickManager { override fun getEntityById(id: Int): Entity? {
return TickManager() return null
} }
override fun getMapState(id: MapIdComponent?): MapState? { override fun getTickManager(): TickManager {
return null return TickManager()
} }
override fun putMapState(id: MapIdComponent?, state: MapState?) { override fun getMapState(id: MapIdComponent?): MapState? {
} return null
}
override fun increaseAndGetMapId(): MapIdComponent { override fun putMapState(id: MapIdComponent?, state: MapState?) {
return MapIdComponent(0) }
}
override fun setBlockBreakingInfo(entityId: Int, pos: BlockPos?, progress: Int) { override fun increaseAndGetMapId(): MapIdComponent {
} return MapIdComponent(0)
}
override fun getScoreboard(): Scoreboard { override fun setBlockBreakingInfo(entityId: Int, pos: BlockPos?, progress: Int) {
return Scoreboard() }
}
override fun getRecipeManager(): RecipeManager { override fun getScoreboard(): Scoreboard {
return RecipeManager(registryManager) return Scoreboard()
} }
object FakeEntityLookup : EntityLookup<Entity> { override fun getRecipeManager(): RecipeManager {
override fun get(id: Int): Entity? { return object : RecipeManager {
return null override fun getPropertySet(key: RegistryKey<RecipePropertySet>?): RecipePropertySet {
} return RecipePropertySet.EMPTY
}
override fun get(uuid: UUID?): Entity? { override fun getStonecutterRecipes(): CuttingRecipeDisplay.Grouping<StonecuttingRecipe> {
return null return CuttingRecipeDisplay.Grouping.empty()
} }
}
}
override fun iterate(): MutableIterable<Entity> { object FakeEntityLookup : EntityLookup<Entity> {
return mutableListOf() override fun get(id: Int): Entity? {
} return null
}
override fun <U : Entity?> forEachIntersects( override fun get(uuid: UUID?): Entity? {
filter: TypeFilter<Entity, U>?, return null
box: Box?, }
consumer: LazyIterationConsumer<U>?
) {
}
override fun forEachIntersects(box: Box?, action: Consumer<Entity>?) { override fun iterate(): MutableIterable<Entity> {
} return mutableListOf()
}
override fun <U : Entity?> forEach(filter: TypeFilter<Entity, U>?, consumer: LazyIterationConsumer<U>?) { override fun <U : Entity?> forEachIntersects(
} filter: TypeFilter<Entity, U>?,
box: Box?,
consumer: LazyIterationConsumer<U>?
) {
}
} override fun forEachIntersects(box: Box?, action: Consumer<Entity>?) {
}
override fun getEntityLookup(): EntityLookup<Entity> { override fun <U : Entity?> forEach(filter: TypeFilter<Entity, U>?, consumer: LazyIterationConsumer<U>?) {
return FakeEntityLookup }
}
override fun getBrewingRecipeRegistry(): BrewingRecipeRegistry { }
return BrewingRecipeRegistry.EMPTY
} override fun getEntityLookup(): EntityLookup<Entity> {
return FakeEntityLookup
}
override fun getBrewingRecipeRegistry(): BrewingRecipeRegistry {
return BrewingRecipeRegistry.EMPTY
}
override fun getFuelRegistry(): FuelRegistry {
TODO("Not yet implemented")
}
} }

View File

@@ -1,8 +1,7 @@
package moe.nea.firmament.gui.entity package moe.nea.firmament.gui.entity
import com.mojang.authlib.GameProfile import com.mojang.authlib.GameProfile
import java.util.* import java.util.UUID
import net.minecraft.client.network.AbstractClientPlayerEntity import net.minecraft.client.network.AbstractClientPlayerEntity
import net.minecraft.client.util.DefaultSkinHelper import net.minecraft.client.util.DefaultSkinHelper
import net.minecraft.client.util.SkinTextures import net.minecraft.client.util.SkinTextures
@@ -15,40 +14,40 @@ import net.minecraft.world.World
/** /**
* @see moe.nea.firmament.init.EarlyRiser * @see moe.nea.firmament.init.EarlyRiser
*/ */
fun makeGuiPlayer(world: FakeWorld): GuiPlayer { fun makeGuiPlayer(world: World): GuiPlayer {
val constructor = GuiPlayer::class.java.getDeclaredConstructor( val constructor = GuiPlayer::class.java.getDeclaredConstructor(
World::class.java, World::class.java,
BlockPos::class.java, BlockPos::class.java,
Float::class.javaPrimitiveType, Float::class.javaPrimitiveType,
GameProfile::class.java GameProfile::class.java
) )
return constructor.newInstance(world, BlockPos.ORIGIN, 0F, GameProfile(UUID.randomUUID(), "Linnea")) return constructor.newInstance(world, BlockPos.ORIGIN, 0F, GameProfile(UUID.randomUUID(), "Linnea"))
} }
class GuiPlayer(world: ClientWorld?, profile: GameProfile?) : AbstractClientPlayerEntity(world, profile) { class GuiPlayer(world: ClientWorld?, profile: GameProfile?) : AbstractClientPlayerEntity(world, profile) {
override fun isSpectator(): Boolean { override fun isSpectator(): Boolean {
return false return false
} }
override fun isCreative(): Boolean { override fun isCreative(): Boolean {
return false return false
} }
override fun shouldRenderName(): Boolean { override fun shouldRenderName(): Boolean {
return false return false
} }
var skinTexture: Identifier = DefaultSkinHelper.getSkinTextures(this.getUuid()).texture var skinTexture: Identifier = DefaultSkinHelper.getSkinTextures(this.getUuid()).texture
var capeTexture: Identifier? = null var capeTexture: Identifier? = null
var model: Model = Model.WIDE var model: Model = Model.WIDE
override fun getSkinTextures(): SkinTextures { override fun getSkinTextures(): SkinTextures {
return SkinTextures( return SkinTextures(
skinTexture, skinTexture,
null, null,
capeTexture, capeTexture,
null, null,
model, model,
true true
) )
} }
} }

View File

@@ -8,6 +8,7 @@ import kotlin.experimental.inv
import kotlin.experimental.or import kotlin.experimental.or
import net.minecraft.entity.EntityType import net.minecraft.entity.EntityType
import net.minecraft.entity.LivingEntity import net.minecraft.entity.LivingEntity
import net.minecraft.entity.SpawnReason
import net.minecraft.entity.passive.AbstractHorseEntity import net.minecraft.entity.passive.AbstractHorseEntity
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.item.Items import net.minecraft.item.Items
@@ -19,11 +20,11 @@ object ModifyHorse : EntityModifier {
var entity: AbstractHorseEntity = entity var entity: AbstractHorseEntity = entity
info["kind"]?.let { info["kind"]?.let {
entity = when (it.asString) { entity = when (it.asString) {
"skeleton" -> EntityType.SKELETON_HORSE.create(fakeWorld)!! "skeleton" -> EntityType.SKELETON_HORSE.create(fakeWorld, SpawnReason.LOAD)!!
"zombie" -> EntityType.ZOMBIE_HORSE.create(fakeWorld)!! "zombie" -> EntityType.ZOMBIE_HORSE.create(fakeWorld, SpawnReason.LOAD)!!
"mule" -> EntityType.MULE.create(fakeWorld)!! "mule" -> EntityType.MULE.create(fakeWorld, SpawnReason.LOAD)!!
"donkey" -> EntityType.DONKEY.create(fakeWorld)!! "donkey" -> EntityType.DONKEY.create(fakeWorld, SpawnReason.LOAD)!!
"horse" -> EntityType.HORSE.create(fakeWorld)!! "horse" -> EntityType.HORSE.create(fakeWorld, SpawnReason.LOAD)!!
else -> error("Unknown horse kind $it") else -> error("Unknown horse kind $it")
} }
} }

View File

@@ -5,7 +5,7 @@ package moe.nea.firmament.repo
import io.ktor.client.call.body import io.ktor.client.call.body
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsChannel 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.io.IOException
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path

View File

@@ -9,10 +9,12 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.minecraft.client.MinecraftClient import net.minecraft.client.MinecraftClient
import net.minecraft.network.packet.s2c.play.SynchronizeRecipesS2CPacket import net.minecraft.network.packet.s2c.play.SynchronizeRecipesS2CPacket
import net.minecraft.recipe.display.CuttingRecipeDisplay
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.Firmament.logger import moe.nea.firmament.Firmament.logger
import moe.nea.firmament.events.ReloadRegistrationEvent import moe.nea.firmament.events.ReloadRegistrationEvent
import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.MinecraftDispatcher import moe.nea.firmament.util.MinecraftDispatcher
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.tr import moe.nea.firmament.util.tr
@@ -77,7 +79,7 @@ object RepoManager {
private fun trySendClientboundUpdateRecipesPacket(): Boolean { private fun trySendClientboundUpdateRecipesPacket(): Boolean {
return MinecraftClient.getInstance().world != null && MinecraftClient.getInstance().networkHandler?.onSynchronizeRecipes( return MinecraftClient.getInstance().world != null && MinecraftClient.getInstance().networkHandler?.onSynchronizeRecipes(
SynchronizeRecipesS2CPacket(mutableListOf()) SynchronizeRecipesS2CPacket(mutableMapOf(), CuttingRecipeDisplay.Grouping.empty())
) != null ) != null
} }
@@ -92,7 +94,7 @@ object RepoManager {
fun launchAsyncUpdate(force: Boolean = false) { fun launchAsyncUpdate(force: Boolean = false) {
Firmament.coroutineScope.launch { 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 ItemCache.ReloadProgressHud.isEnabled = true
try { try {
RepoDownloadManager.downloadUpdate(force) RepoDownloadManager.downloadUpdate(force)
@@ -112,7 +114,7 @@ object RepoManager {
ItemCache.ReloadProgressHud.isEnabled = true ItemCache.ReloadProgressHud.isEnabled = true
neuRepo.reload() neuRepo.reload()
} catch (exc: NEURepositoryException) { } catch (exc: NEURepositoryException) {
MinecraftClient.getInstance().player?.sendMessage( MC.sendChat(
tr("firmament.repo.reloadfail", tr("firmament.repo.reloadfail",
"Failed to reload repository. This will result in some mod features not working.") "Failed to reload repository. This will result in some mod features not working.")
) )

View File

@@ -1,9 +1,14 @@
package moe.nea.firmament.repo 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.constants.PetNumbers
import io.github.moulberry.repo.data.NEUIngredient import io.github.moulberry.repo.data.NEUIngredient
import io.github.moulberry.repo.data.NEUItem import io.github.moulberry.repo.data.NEUItem
import net.minecraft.item.ItemStack 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.text.Text
import net.minecraft.util.Formatting import net.minecraft.util.Formatting
import moe.nea.firmament.repo.ItemCache.asItemStack import moe.nea.firmament.repo.ItemCache.asItemStack
@@ -40,6 +45,21 @@ data class SBItemStack constructor(
} }
companion object { 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 { operator fun invoke(itemStack: ItemStack): SBItemStack {
val skyblockId = itemStack.skyBlockId ?: SkyblockId.NULL val skyblockId = itemStack.skyBlockId ?: SkyblockId.NULL
return SBItemStack( return SBItemStack(
@@ -114,6 +134,8 @@ data class SBItemStack constructor(
val itemStack = itemStack_ ?: run { val itemStack = itemStack_ ?: run {
if (skyblockId == SkyblockId.COINS) if (skyblockId == SkyblockId.COINS)
return@run ItemCache.coinItem(stackSize).also { it.appendLore(extraLore) } return@run ItemCache.coinItem(stackSize).also { it.appendLore(extraLore) }
if (stackSize == 0)
return@run ItemStack.EMPTY
val replacementData = mutableMapOf<String, String>() val replacementData = mutableMapOf<String, String>()
injectReplacementDataForPets(replacementData) injectReplacementDataForPets(replacementData)
return@run neuItem.asItemStack(idHint = skyblockId, replacementData) return@run neuItem.asItemStack(idHint = skyblockId, replacementData)

View File

@@ -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>
}

View File

@@ -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)
}

View File

@@ -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")
}

View File

@@ -1,25 +1,46 @@
@file:OptIn(ExperimentalContracts::class)
package moe.nea.firmament.util package moe.nea.firmament.util
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import moe.nea.firmament.Firmament 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 { object ErrorUtil {
var aggressiveErrors = run { var aggressiveErrors = run {
Thread.currentThread().stackTrace.any { it.className.startsWith("org.junit.") } || Firmament.DEBUG Thread.currentThread().stackTrace.any { it.className.startsWith("org.junit.") } || Firmament.DEBUG
|| ErrorUtil::class.java.desiredAssertionStatus()
} }
inline fun softCheck(message: String, func: () -> Boolean) { inline fun softCheck(message: String, check: Boolean) {
if (!check) softError(message)
}
inline fun lazyCheck(message: String, func: () -> Boolean) {
contract {
callsInPlace(func, InvocationKind.AT_MOST_ONCE)
}
if (!aggressiveErrors) return if (!aggressiveErrors) return
if (func()) return if (func()) return
error(message) 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) { inline fun softError(message: String) {
if (aggressiveErrors) error(message) if (aggressiveErrors) error(message)
else Firmament.logger.error(message) else Firmament.logger.error(message)
} }
inline fun <T : Any> notNullOr(nullable: T?, message: String, orElse: () -> T): T { inline fun <T : Any> notNullOr(nullable: T?, message: String, orElse: () -> T): T {
contract {
callsInPlace(orElse, InvocationKind.AT_MOST_ONCE)
}
if (nullable == null) { if (nullable == null) {
softError(message) softError(message)
return orElse() return orElse()

View File

@@ -3,9 +3,13 @@ package moe.nea.firmament.util
import io.github.moulberry.repo.data.Coordinate import io.github.moulberry.repo.data.Coordinate
import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.ConcurrentLinkedQueue
import net.minecraft.client.MinecraftClient import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.gui.screen.ingame.HandledScreen 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.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.item.Item
import net.minecraft.network.packet.c2s.play.CommandExecutionC2SPacket import net.minecraft.network.packet.c2s.play.CommandExecutionC2SPacket
import net.minecraft.registry.BuiltinRegistries import net.minecraft.registry.BuiltinRegistries
@@ -14,7 +18,9 @@ import net.minecraft.registry.RegistryWrapper
import net.minecraft.resource.ReloadableResourceManagerImpl import net.minecraft.resource.ReloadableResourceManagerImpl
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import moe.nea.firmament.events.TickEvent import moe.nea.firmament.events.TickEvent
import moe.nea.firmament.events.WorldReadyEvent
object MC { object MC {
@@ -29,6 +35,9 @@ object MC {
(nextTickTodos.poll() ?: break).invoke() (nextTickTodos.poll() ?: break).invoke()
} }
} }
WorldReadyEvent.subscribe("MC:ready") {
this.lastWorld
}
} }
fun sendChat(text: Text) { fun sendChat(text: Text) {
@@ -69,6 +78,7 @@ object MC {
inline val resourceManager get() = (instance.resourceManager as ReloadableResourceManagerImpl) inline val resourceManager get() = (instance.resourceManager as ReloadableResourceManagerImpl)
inline val itemRenderer: ItemRenderer get() = instance.itemRenderer
inline val worldRenderer: WorldRenderer get() = instance.worldRenderer inline val worldRenderer: WorldRenderer get() = instance.worldRenderer
inline val networkHandler get() = player?.networkHandler inline val networkHandler get() = player?.networkHandler
inline val instance get() = MinecraftClient.getInstance() inline val instance get() = MinecraftClient.getInstance()
@@ -79,11 +89,11 @@ object MC {
inline val inGameHud get() = instance.inGameHud inline val inGameHud get() = instance.inGameHud
inline val font get() = instance.textRenderer inline val font get() = instance.textRenderer
inline val soundManager get() = instance.soundManager inline val soundManager get() = instance.soundManager
inline val player get() = instance.player inline val player: ClientPlayerEntity? get() = instance.player
inline val camera get() = instance.cameraEntity inline val camera: Entity? get() = instance.cameraEntity
inline val guiAtlasManager get() = instance.guiAtlasManager inline val guiAtlasManager get() = instance.guiAtlasManager
inline val world get() = instance.world inline val world: ClientWorld? get() = instance.world
inline var screen inline var screen: Screen?
get() = instance.currentScreen get() = instance.currentScreen
set(value) = instance.setScreen(value) set(value) = instance.setScreen(value)
val screenName get() = screen?.title?.unformattedString?.trim() val screenName get() = screen?.title?.unformattedString?.trim()
@@ -92,7 +102,13 @@ object MC {
inline val currentRegistries: RegistryWrapper.WrapperLookup? get() = world?.registryManager inline val currentRegistries: RegistryWrapper.WrapperLookup? get() = world?.registryManager
val defaultRegistries: RegistryWrapper.WrapperLookup = BuiltinRegistries.createWrapperLookup() val defaultRegistries: RegistryWrapper.WrapperLookup = BuiltinRegistries.createWrapperLookup()
inline val currentOrDefaultRegistries get() = currentRegistries ?: defaultRegistries 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
} }

View File

@@ -37,7 +37,7 @@ object SBData {
it.serverType.getOrNull()?.name?.uppercase(), it.serverType.getOrNull()?.name?.uppercase(),
it.mode.getOrNull(), it.mode.getOrNull(),
it.map.getOrNull()) it.map.getOrNull())
SkyblockServerUpdateEvent.publish(SkyblockServerUpdateEvent(lastLocraw, null)) SkyblockServerUpdateEvent.publish(SkyblockServerUpdateEvent(lastLocraw, locraw))
profileIdCommandDebounce = TimeMark.now() profileIdCommandDebounce = TimeMark.now()
} }
} }

View File

@@ -2,6 +2,7 @@
package moe.nea.firmament.util package moe.nea.firmament.util
import com.mojang.serialization.Codec
import io.github.moulberry.repo.data.NEUIngredient import io.github.moulberry.repo.data.NEUIngredient
import io.github.moulberry.repo.data.NEUItem import io.github.moulberry.repo.data.NEUItem
import io.github.moulberry.repo.data.Rarity 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.ItemStack
import net.minecraft.item.Items import net.minecraft.item.Items
import net.minecraft.nbt.NbtCompound 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 net.minecraft.util.Identifier
import moe.nea.firmament.repo.ItemCache.asItemStack import moe.nea.firmament.repo.ItemCache.asItemStack
import moe.nea.firmament.repo.set import moe.nea.firmament.repo.set
@@ -68,6 +72,9 @@ value class SkyblockId(val neuItem: String) {
val NULL: SkyblockId = SkyblockId("null") val NULL: SkyblockId = SkyblockId("null")
val PET_NULL: SkyblockId = SkyblockId("null_pet") val PET_NULL: SkyblockId = SkyblockId("null_pet")
private val illlegalPathRegex = "[^a-z0-9_.-/]".toRegex() 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 })
} }
} }

View File

@@ -1,77 +1,71 @@
package moe.nea.firmament.util.data package moe.nea.firmament.util.data
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents
import kotlin.reflect.KClass import kotlin.reflect.KClass
import net.minecraft.client.MinecraftClient
import net.minecraft.server.command.CommandOutput
import net.minecraft.text.Text import net.minecraft.text.Text
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.events.ScreenChangeEvent import moe.nea.firmament.events.ScreenChangeEvent
import moe.nea.firmament.util.MC
interface IDataHolder<T> { interface IDataHolder<T> {
companion object { companion object {
internal var badLoads: MutableList<String> = CopyOnWriteArrayList() internal var badLoads: MutableList<String> = CopyOnWriteArrayList()
private val allConfigs: MutableMap<KClass<out IDataHolder<*>>, IDataHolder<*>> = mutableMapOf() private val allConfigs: MutableMap<KClass<out IDataHolder<*>>, IDataHolder<*>> = mutableMapOf()
private val dirty: MutableSet<KClass<out IDataHolder<*>>> = mutableSetOf() private val dirty: MutableSet<KClass<out IDataHolder<*>>> = mutableSetOf()
internal fun <T : IDataHolder<K>, K> putDataHolder(kClass: KClass<T>, inst: IDataHolder<K>) { internal fun <T : IDataHolder<K>, K> putDataHolder(kClass: KClass<T>, inst: IDataHolder<K>) {
allConfigs[kClass] = inst allConfigs[kClass] = inst
} }
fun <T : IDataHolder<K>, K> markDirty(kClass: KClass<T>) { fun <T : IDataHolder<K>, K> markDirty(kClass: KClass<T>) {
if (kClass !in allConfigs) { if (kClass !in allConfigs) {
Firmament.logger.error("Tried to markDirty '${kClass.qualifiedName}', which isn't registered as 'IConfigHolder'") Firmament.logger.error("Tried to markDirty '${kClass.qualifiedName}', which isn't registered as 'IConfigHolder'")
return return
} }
dirty.add(kClass) dirty.add(kClass)
} }
private fun performSaves() { private fun performSaves() {
val toSave = dirty.toList().also { val toSave = dirty.toList().also {
dirty.clear() dirty.clear()
} }
for (it in toSave) { for (it in toSave) {
val obj = allConfigs[it] val obj = allConfigs[it]
if (obj == null) { if (obj == null) {
Firmament.logger.error("Tried to save '${it}', which isn't registered as 'ConfigHolder'") Firmament.logger.error("Tried to save '${it}', which isn't registered as 'ConfigHolder'")
continue continue
} }
obj.save() obj.save()
} }
} }
private fun warnForResetConfigs(player: CommandOutput) { private fun warnForResetConfigs() {
if (badLoads.isNotEmpty()) { if (badLoads.isNotEmpty()) {
player.sendMessage( MC.sendChat(
Text.literal( Text.literal(
"The following configs have been reset: ${badLoads.joinToString(", ")}. " + "The following configs have been reset: ${badLoads.joinToString(", ")}. " +
"This can be intentional, but probably isn't." "This can be intentional, but probably isn't."
) )
) )
badLoads.clear() badLoads.clear()
} }
} }
fun registerEvents() { fun registerEvents() {
ScreenChangeEvent.subscribe("IDataHolder:saveOnScreenChange") { event -> ScreenChangeEvent.subscribe("IDataHolder:saveOnScreenChange") { event ->
performSaves() performSaves()
val p = MinecraftClient.getInstance().player warnForResetConfigs()
if (p != null) { }
warnForResetConfigs(p) ClientLifecycleEvents.CLIENT_STOPPING.register(ClientLifecycleEvents.ClientStopping {
} performSaves()
} })
ClientLifecycleEvents.CLIENT_STOPPING.register(ClientLifecycleEvents.ClientStopping { }
performSaves()
})
}
} }
val data: T val data: T
fun save() fun save()
fun markDirty() fun markDirty()
fun load() fun load()
} }

View File

@@ -4,12 +4,70 @@ import com.mojang.blaze3d.systems.RenderSystem
import me.shedaniel.math.Color import me.shedaniel.math.Color
import org.joml.Matrix4f import org.joml.Matrix4f
import net.minecraft.client.gui.DrawContext 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 import moe.nea.firmament.util.MC
fun DrawContext.isUntranslatedGuiDrawContext(): Boolean { fun DrawContext.isUntranslatedGuiDrawContext(): Boolean {
return (matrices.peek().positionMatrix.properties() and Matrix4f.PROPERTY_IDENTITY.toInt()) != 0 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) { fun DrawContext.drawLine(fromX: Int, fromY: Int, toX: Int, toY: Int, color: Color) {
// TODO: push scissors // TODO: push scissors
// TODO: use matrix translations and a different render layer // 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 return
} }
RenderSystem.lineWidth(MC.window.scaleFactor.toFloat()) RenderSystem.lineWidth(MC.window.scaleFactor.toFloat())
val buf = this.vertexConsumers.getBuffer(RenderInWorldContext.RenderLayers.LINES) draw { vertexConsumers ->
buf.vertex(fromX.toFloat(), fromY.toFloat(), 0F).color(color.color) val buf = vertexConsumers.getBuffer(RenderInWorldContext.RenderLayers.LINES)
.normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F) buf.vertex(fromX.toFloat(), fromY.toFloat(), 0F).color(color.color)
buf.vertex(toX.toFloat(), toY.toFloat(), 0F).color(color.color) .normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F)
.normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F) buf.vertex(toX.toFloat(), toY.toFloat(), 0F).color(color.color)
this.draw() .normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F)
}
} }

View File

@@ -76,13 +76,10 @@ class FacingThePlayerContext(val worldContext: RenderInWorldContext) {
u1: Float, v1: Float, u1: Float, v1: Float,
u2: Float, v2: Float, u2: Float, v2: Float,
) { ) {
RenderSystem.setShaderTexture(0, texture) val buf = worldContext.vertexConsumers.getBuffer(RenderLayer.getGuiTexturedOverlay(texture))
RenderSystem.setShader(GameRenderer::getPositionTexColorProgram)
val hw = width / 2F val hw = width / 2F
val hh = height / 2F val hh = height / 2F
val matrix4f: Matrix4f = worldContext.matrixStack.peek().positionMatrix 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) buf.vertex(matrix4f, -hw, -hh, 0F)
.color(-1) .color(-1)
.texture(u1, v1).next() .texture(u1, v1).next()
@@ -95,7 +92,7 @@ class FacingThePlayerContext(val worldContext: RenderInWorldContext) {
buf.vertex(matrix4f, +hw, -hh, 0F) buf.vertex(matrix4f, +hw, -hh, 0F)
.color(-1) .color(-1)
.texture(u2, v1).next() .texture(u2, v1).next()
BufferRenderer.drawWithGlobalProgram(buf.end()) worldContext.vertexConsumers.draw()
} }
} }

View File

@@ -1,23 +1,30 @@
package moe.nea.firmament.util.render 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.RenderPhase
import net.minecraft.client.render.VertexFormat
import net.minecraft.client.render.VertexFormats import net.minecraft.client.render.VertexFormats
import moe.nea.firmament.Firmament
import moe.nea.firmament.annotations.Subscribe 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 { 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(shader("core/rendertype_lines", VertexFormats.LINES, Defines.EMPTY))
val LINES = RenderPhase.ShaderProgram({ _LINES })
@Subscribe @Subscribe
fun registerCustomShaders(event: RegisterCustomShadersEvent) { fun debugLoad(event: DebugInstantiateEvent) {
event.register( shaders.forEach {
"firmament_rendertype_lines", MC.instance.shaderLoader.getOrCreateProgram(it)
VertexFormats.LINES, }
{ _LINES = it }, }
)
}
} }

View File

@@ -1,4 +1,3 @@
package moe.nea.firmament.util.render package moe.nea.firmament.util.render
import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.systems.RenderSystem
@@ -9,7 +8,8 @@ import kotlin.math.atan2
import kotlin.math.tan import kotlin.math.tan
import net.minecraft.client.gui.DrawContext import net.minecraft.client.gui.DrawContext
import net.minecraft.client.render.BufferRenderer 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.Tessellator
import net.minecraft.client.render.VertexFormat.DrawMode import net.minecraft.client.render.VertexFormat.DrawMode
import net.minecraft.client.render.VertexFormats import net.minecraft.client.render.VertexFormats
@@ -17,79 +17,77 @@ import net.minecraft.util.Identifier
object RenderCircleProgress { object RenderCircleProgress {
fun renderCircle( fun renderCircle(
drawContext: DrawContext, drawContext: DrawContext,
texture: Identifier, texture: Identifier,
progress: Float, progress: Float,
u1: Float, u1: Float,
u2: Float, u2: Float,
v1: Float, v1: Float,
v2: Float, v2: Float,
) { ) {
RenderSystem.setShaderTexture(0, texture) RenderSystem.enableBlend()
RenderSystem.setShader(GameRenderer::getPositionTexColorProgram) drawContext.draw {
RenderSystem.enableBlend() val bufferBuilder = it.getBuffer(RenderLayer.getGuiTexturedOverlay(texture))
val matrix: Matrix4f = drawContext.matrices.peek().positionMatrix val matrix: Matrix4f = drawContext.matrices.peek().positionMatrix
val bufferBuilder = Tessellator.getInstance().begin(DrawMode.TRIANGLES, VertexFormats.POSITION_TEXTURE_COLOR)
val corners = listOf( val corners = listOf(
Vector2f(0F, -1F), Vector2f(0F, -1F),
Vector2f(1F, -1F), Vector2f(1F, -1F),
Vector2f(1F, 0F), Vector2f(1F, 0F),
Vector2f(1F, 1F), Vector2f(1F, 1F),
Vector2f(0F, 1F), Vector2f(0F, 1F),
Vector2f(-1F, 1F), Vector2f(-1F, 1F),
Vector2f(-1F, 0F), Vector2f(-1F, 0F),
Vector2f(-1F, -1F), Vector2f(-1F, -1F),
) )
for (i in (0 until 8)) { for (i in (0 until 8)) {
if (progress < i / 8F) { if (progress < i / 8F) {
break break
} }
val second = corners[(i + 1) % 8] val second = corners[(i + 1) % 8]
val first = corners[i] val first = corners[i]
if (progress <= (i + 1) / 8F) { if (progress <= (i + 1) / 8F) {
val internalProgress = 1 - (progress - i / 8F) * 8F val internalProgress = 1 - (progress - i / 8F) * 8F
val angle = lerpAngle( val angle = lerpAngle(
atan2(second.y, second.x), atan2(second.y, second.x),
atan2(first.y, first.x), atan2(first.y, first.x),
internalProgress internalProgress
) )
if (angle < tau / 8 || angle >= tau * 7 / 8) { if (angle < tau / 8 || angle >= tau * 7 / 8) {
second.set(1F, tan(angle)) second.set(1F, tan(angle))
} else if (angle < tau * 3 / 8) { } else if (angle < tau * 3 / 8) {
second.set(1 / tan(angle), 1F) second.set(1 / tan(angle), 1F)
} else if (angle < tau * 5 / 8) { } else if (angle < tau * 5 / 8) {
second.set(-1F, -tan(angle)) second.set(-1F, -tan(angle))
} else { } else {
second.set(-1 / tan(angle), -1F) second.set(-1 / tan(angle), -1F)
} }
} }
fun ilerp(f: Float): Float = fun ilerp(f: Float): Float =
ilerp(-1f, 1f, f) 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()
}
BufferRenderer.drawWithGlobalProgram(bufferBuilder.end())
RenderSystem.disableBlend()
}
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()
}
} }

View File

@@ -1,5 +1,3 @@
package moe.nea.firmament.util.render package moe.nea.firmament.util.render
import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.systems.RenderSystem
@@ -8,14 +6,12 @@ import java.lang.Math.pow
import org.joml.Matrix4f import org.joml.Matrix4f
import org.joml.Vector3f import org.joml.Vector3f
import net.minecraft.client.gl.VertexBuffer 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.Camera
import net.minecraft.client.render.GameRenderer
import net.minecraft.client.render.RenderLayer import net.minecraft.client.render.RenderLayer
import net.minecraft.client.render.RenderPhase import net.minecraft.client.render.RenderPhase
import net.minecraft.client.render.RenderTickCounter import net.minecraft.client.render.RenderTickCounter
import net.minecraft.client.render.Tessellator import net.minecraft.client.render.Tessellator
import net.minecraft.client.render.VertexConsumer
import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.VertexConsumerProvider
import net.minecraft.client.render.VertexFormat import net.minecraft.client.render.VertexFormat
import net.minecraft.client.render.VertexFormats import net.minecraft.client.render.VertexFormats
@@ -31,273 +27,287 @@ import moe.nea.firmament.util.MC
@RenderContextDSL @RenderContextDSL
class RenderInWorldContext private constructor( class RenderInWorldContext private constructor(
private val tesselator: Tessellator, private val tesselator: Tessellator,
val matrixStack: MatrixStack, val matrixStack: MatrixStack,
private val camera: Camera, private val camera: Camera,
private val tickCounter: RenderTickCounter, private val tickCounter: RenderTickCounter,
val vertexConsumers: VertexConsumerProvider.Immediate, val vertexConsumers: VertexConsumerProvider.Immediate,
) { ) {
object RenderLayers { object RenderLayers {
val TRANSLUCENT_TRIS = RenderLayer.of("firmament_translucent_tris", val TRANSLUCENT_TRIS = RenderLayer.of("firmament_translucent_tris",
VertexFormats.POSITION_COLOR, VertexFormats.POSITION_COLOR,
VertexFormat.DrawMode.TRIANGLES, VertexFormat.DrawMode.TRIANGLES,
RenderLayer.DEFAULT_BUFFER_SIZE, RenderLayer.CUTOUT_BUFFER_SIZE,
false, true, false, true,
RenderLayer.MultiPhaseParameters.builder() RenderLayer.MultiPhaseParameters.builder()
.depthTest(RenderPhase.ALWAYS_DEPTH_TEST) .depthTest(RenderPhase.ALWAYS_DEPTH_TEST)
.transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY) .transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY)
.program(RenderPhase.COLOR_PROGRAM) .program(RenderPhase.POSITION_COLOR_PROGRAM)
.build(false)) .build(false))
val LINES = RenderLayer.of("firmament_rendertype_lines", val LINES = RenderLayer.of("firmament_rendertype_lines",
VertexFormats.LINES, VertexFormats.LINES,
VertexFormat.DrawMode.LINES, VertexFormat.DrawMode.LINES,
RenderLayer.DEFAULT_BUFFER_SIZE, RenderLayer.CUTOUT_BUFFER_SIZE,
false, false, // do we need translucent? i dont think so false, false, // do we need translucent? i dont think so
RenderLayer.MultiPhaseParameters.builder() RenderLayer.MultiPhaseParameters.builder()
.depthTest(RenderPhase.ALWAYS_DEPTH_TEST) .depthTest(RenderPhase.ALWAYS_DEPTH_TEST)
.program(FirmamentShaders.LINES) .program(FirmamentShaders.LINES)
.build(false) .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)
)
}
fun color(color: me.shedaniel.math.Color) { @Deprecated("stateful color management is no longer a thing")
color(color.red / 255F, color.green / 255f, color.blue / 255f, color.alpha / 255f) 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) { @Deprecated("stateful color management is no longer a thing")
RenderSystem.setShaderColor(red, green, blue, alpha) fun color(red: Float, green: Float, blue: Float, alpha: Float) {
} RenderSystem.setShaderColor(red, green, blue, alpha)
}
fun block(blockPos: BlockPos) { fun block(blockPos: BlockPos, color: Int) {
matrixStack.push() matrixStack.push()
matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat()) matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat())
buildCube(matrixStack.peek().positionMatrix, tesselator) buildCube(matrixStack.peek().positionMatrix, vertexConsumers.getBuffer(RenderLayers.COLORED_QUADS), color)
matrixStack.pop() matrixStack.pop()
} }
enum class VerticalAlign { enum class VerticalAlign {
TOP, BOTTOM, CENTER; TOP, BOTTOM, CENTER;
fun align(index: Int, count: Int): Float { fun align(index: Int, count: Int): Float {
return when (this) { return when (this) {
CENTER -> (index - count / 2F) * (1 + MC.font.fontHeight.toFloat()) CENTER -> (index - count / 2F) * (1 + MC.font.fontHeight.toFloat())
BOTTOM -> (index - count) * (1 + MC.font.fontHeight.toFloat()) BOTTOM -> (index - count) * (1 + MC.font.fontHeight.toFloat())
TOP -> (index) * (1 + MC.font.fontHeight.toFloat()) TOP -> (index) * (1 + MC.font.fontHeight.toFloat())
} }
} }
} }
fun waypoint(position: BlockPos, vararg label: Text) { fun waypoint(position: BlockPos, vararg label: Text) {
text( text(
position.toCenterPos(), position.toCenterPos(),
*label, *label,
Text.literal("§e${FirmFormatters.formatDistance(MC.player?.pos?.distanceTo(position.toCenterPos()) ?: 42069.0)}"), Text.literal("§e${FirmFormatters.formatDistance(MC.player?.pos?.distanceTo(position.toCenterPos()) ?: 42069.0)}"),
background = 0xAA202020.toInt() background = 0xAA202020.toInt()
) )
} }
fun withFacingThePlayer(position: Vec3d, block: FacingThePlayerContext.() -> Unit) { fun withFacingThePlayer(position: Vec3d, block: FacingThePlayerContext.() -> Unit) {
matrixStack.push() matrixStack.push()
matrixStack.translate(position.x, position.y, position.z) matrixStack.translate(position.x, position.y, position.z)
val actualCameraDistance = position.distanceTo(camera.pos) val actualCameraDistance = position.distanceTo(camera.pos)
val distanceToMoveTowardsCamera = if (actualCameraDistance < 10) 0.0 else -(actualCameraDistance - 10.0) val distanceToMoveTowardsCamera = if (actualCameraDistance < 10) 0.0 else -(actualCameraDistance - 10.0)
val vec = position.subtract(camera.pos).multiply(distanceToMoveTowardsCamera / actualCameraDistance) val vec = position.subtract(camera.pos).multiply(distanceToMoveTowardsCamera / actualCameraDistance)
matrixStack.translate(vec.x, vec.y, vec.z) matrixStack.translate(vec.x, vec.y, vec.z)
matrixStack.multiply(camera.rotation) matrixStack.multiply(camera.rotation)
matrixStack.scale(0.025F, -0.025F, 1F) matrixStack.scale(0.025F, -0.025F, 1F)
FacingThePlayerContext(this).run(block) FacingThePlayerContext(this).run(block)
matrixStack.pop() matrixStack.pop()
vertexConsumers.drawCurrentLayer() vertexConsumers.drawCurrentLayer()
} }
fun sprite(position: Vec3d, sprite: Sprite, width: Int, height: Int) { fun sprite(position: Vec3d, sprite: Sprite, width: Int, height: Int) {
texture( texture(
position, sprite.atlasId, width, height, sprite.minU, sprite.minV, sprite.maxU, sprite.maxV position, sprite.atlasId, width, height, sprite.minU, sprite.minV, sprite.maxU, sprite.maxV
) )
} }
fun texture( fun texture(
position: Vec3d, texture: Identifier, width: Int, height: Int, position: Vec3d, texture: Identifier, width: Int, height: Int,
u1: Float, v1: Float, u1: Float, v1: Float,
u2: Float, v2: Float, u2: Float, v2: Float,
) { ) {
withFacingThePlayer(position) { withFacingThePlayer(position) {
texture(texture, width, height, u1, v1, u2, v2) texture(texture, width, height, u1, v1, u2, v2)
} }
} }
fun text(position: Vec3d, vararg texts: Text, verticalAlign: VerticalAlign = VerticalAlign.CENTER, background: Int = 0x70808080) { fun text(
withFacingThePlayer(position) { position: Vec3d,
text(*texts, verticalAlign = verticalAlign, background = background) vararg texts: Text,
} verticalAlign: VerticalAlign = VerticalAlign.CENTER,
} background: Int = 0x70808080
) {
withFacingThePlayer(position) {
text(*texts, verticalAlign = verticalAlign, background = background)
}
}
fun tinyBlock(vec3d: Vec3d, size: Float) { fun tinyBlock(vec3d: Vec3d, size: Float, color: Int) {
RenderSystem.setShader(GameRenderer::getPositionColorProgram) matrixStack.push()
matrixStack.push() matrixStack.translate(vec3d.x, vec3d.y, vec3d.z)
matrixStack.translate(vec3d.x, vec3d.y, vec3d.z) matrixStack.scale(size, size, size)
matrixStack.scale(size, size, size) matrixStack.translate(-.5, -.5, -.5)
matrixStack.translate(-.5, -.5, -.5) buildCube(matrixStack.peek().positionMatrix, vertexConsumers.getBuffer(RenderLayers.COLORED_QUADS), color)
buildCube(matrixStack.peek().positionMatrix, tesselator) matrixStack.pop()
matrixStack.pop() vertexConsumers.draw()
} }
fun wireframeCube(blockPos: BlockPos, lineWidth: Float = 10F) { fun wireframeCube(blockPos: BlockPos, lineWidth: Float = 10F) {
RenderSystem.setShader(GameRenderer::getRenderTypeLinesProgram) val buf = vertexConsumers.getBuffer(RenderLayer.LINES)
matrixStack.push() matrixStack.push()
RenderSystem.lineWidth(lineWidth / pow(camera.pos.squaredDistanceTo(blockPos.toCenterPos()), 0.25).toFloat()) // TODO: this does not render through blocks (or water layers) anymore
matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat()) RenderSystem.lineWidth(lineWidth / pow(camera.pos.squaredDistanceTo(blockPos.toCenterPos()), 0.25).toFloat())
buildWireFrameCube(matrixStack.peek(), tesselator) matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat())
matrixStack.pop() buildWireFrameCube(matrixStack.peek(), buf)
} matrixStack.pop()
vertexConsumers.draw()
}
fun line(vararg points: Vec3d, lineWidth: Float = 10F) { fun line(vararg points: Vec3d, lineWidth: Float = 10F) {
line(points.toList(), lineWidth) line(points.toList(), lineWidth)
} }
fun tracer(toWhere: Vec3d, lineWidth: Float = 3f) { fun tracer(toWhere: Vec3d, lineWidth: Float = 3f) {
val cameraForward = Vector3f(0f, 0f, -1f).rotate(camera.rotation) val cameraForward = Vector3f(0f, 0f, -1f).rotate(camera.rotation)
line(camera.pos.add(Vec3d(cameraForward)), toWhere, lineWidth = lineWidth) line(camera.pos.add(Vec3d(cameraForward)), toWhere, lineWidth = lineWidth)
} }
fun line(points: List<Vec3d>, lineWidth: Float = 10F) { fun line(points: List<Vec3d>, lineWidth: Float = 10F) {
RenderSystem.lineWidth(lineWidth) RenderSystem.lineWidth(lineWidth)
val buffer = tesselator.begin(VertexFormat.DrawMode.LINES, VertexFormats.LINES) // TODO: replace with renderlayers
val buffer = tesselator.begin(VertexFormat.DrawMode.LINES, VertexFormats.LINES)
val matrix = matrixStack.peek() val matrix = matrixStack.peek()
var lastNormal: Vector3f? = null var lastNormal: Vector3f? = null
points.zipWithNext().forEach { (a, b) -> points.zipWithNext().forEach { (a, b) ->
val normal = Vector3f(b.x.toFloat(), b.y.toFloat(), b.z.toFloat()) val normal = Vector3f(b.x.toFloat(), b.y.toFloat(), b.z.toFloat())
.sub(a.x.toFloat(), a.y.toFloat(), a.z.toFloat()) .sub(a.x.toFloat(), a.y.toFloat(), a.z.toFloat())
.normalize() .normalize()
val lastNormal0 = lastNormal ?: normal val lastNormal0 = lastNormal ?: normal
lastNormal = normal lastNormal = normal
buffer.vertex(matrix.positionMatrix, a.x.toFloat(), a.y.toFloat(), a.z.toFloat()) buffer.vertex(matrix.positionMatrix, a.x.toFloat(), a.y.toFloat(), a.z.toFloat())
.color(-1) .color(-1)
.normal(matrix, lastNormal0.x, lastNormal0.y, lastNormal0.z) .normal(matrix, lastNormal0.x, lastNormal0.y, lastNormal0.z)
.next() .next()
buffer.vertex(matrix.positionMatrix, b.x.toFloat(), b.y.toFloat(), b.z.toFloat()) buffer.vertex(matrix.positionMatrix, b.x.toFloat(), b.y.toFloat(), b.z.toFloat())
.color(-1) .color(-1)
.normal(matrix, normal.x, normal.y, normal.z) .normal(matrix, normal.x, normal.y, normal.z)
.next() .next()
} }
RenderLayers.LINES.draw(buffer.end()) RenderLayers.LINES.draw(buffer.end())
} }
// TODO: put the favourite icons in front of items again
companion object { companion object {
private fun doLine( private fun doLine(
matrix: MatrixStack.Entry, matrix: MatrixStack.Entry,
buf: BufferBuilder, buf: VertexConsumer,
i: Float, i: Float,
j: Float, j: Float,
k: Float, k: Float,
x: Float, x: Float,
y: Float, y: Float,
z: Float z: Float
) { ) {
val normal = Vector3f(x, y, z) val normal = Vector3f(x, y, z)
.sub(i, j, k) .sub(i, j, k)
.normalize() .normalize()
buf.vertex(matrix.positionMatrix, i, j, k) buf.vertex(matrix.positionMatrix, i, j, k)
.normal(matrix, normal.x, normal.y, normal.z) .normal(matrix, normal.x, normal.y, normal.z)
.color(-1) .color(-1)
.next() .next()
buf.vertex(matrix.positionMatrix, x, y, z) buf.vertex(matrix.positionMatrix, x, y, z)
.normal(matrix, normal.x, normal.y, normal.z) .normal(matrix, normal.x, normal.y, normal.z)
.color(-1) .color(-1)
.next() .next()
} }
private fun buildWireFrameCube(matrix: MatrixStack.Entry, tessellator: Tessellator) { private fun buildWireFrameCube(matrix: MatrixStack.Entry, buf: VertexConsumer) {
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)
}
}
}
for (i in 0..1) { private fun buildCube(matrix: Matrix4f, buf: VertexConsumer, color: Int) {
for (j in 0..1) { // Y-
val i = i.toFloat() buf.vertex(matrix, 0F, 0F, 0F).color(color)
val j = j.toFloat() buf.vertex(matrix, 0F, 0F, 1F).color(color)
doLine(matrix, buf, 0F, i, j, 1F, i, j) buf.vertex(matrix, 1F, 0F, 1F).color(color)
doLine(matrix, buf, i, 0F, j, i, 1F, j) buf.vertex(matrix, 1F, 0F, 0F).color(color)
doLine(matrix, buf, i, j, 0F, i, j, 1F) // Y+
} buf.vertex(matrix, 0F, 1F, 0F).color(color)
} buf.vertex(matrix, 1F, 1F, 0F).color(color)
BufferRenderer.drawWithGlobalProgram(buf.end()) buf.vertex(matrix, 1F, 1F, 1F).color(color)
} buf.vertex(matrix, 0F, 1F, 1F).color(color)
// X-
private fun buildCube(matrix: Matrix4f, tessellator: Tessellator) { buf.vertex(matrix, 0F, 0F, 0F).color(color)
val buf = tessellator.begin(VertexFormat.DrawMode.TRIANGLES, VertexFormats.POSITION_COLOR) buf.vertex(matrix, 0F, 0F, 1F).color(color)
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() buf.vertex(matrix, 0F, 1F, 1F).color(color)
buf.vertex(matrix, 0.0F, 0.0F, 1.0F).color(-1).next() buf.vertex(matrix, 0F, 1F, 0F).color(color)
buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next() // X+
buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next() buf.vertex(matrix, 1F, 0F, 0F).color(color)
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() buf.vertex(matrix, 1F, 1F, 0F).color(color)
buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next() buf.vertex(matrix, 1F, 1F, 1F).color(color)
buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next() buf.vertex(matrix, 1F, 0F, 1F).color(color)
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() // Z-
buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next() buf.vertex(matrix, 0F, 0F, 0F).color(color)
buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next() buf.vertex(matrix, 1F, 0F, 0F).color(color)
buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next() buf.vertex(matrix, 1F, 1F, 0F).color(color)
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() buf.vertex(matrix, 0F, 1F, 0F).color(color)
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() // Z+
buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next() buf.vertex(matrix, 0F, 0F, 1F).color(color)
buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next() buf.vertex(matrix, 0F, 1F, 1F).color(color)
buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next() buf.vertex(matrix, 1F, 1F, 1F).color(color)
buf.vertex(matrix, 0.0F, 0.0F, 1.0F).color(-1).next() buf.vertex(matrix, 1F, 0F, 1F).color(color)
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) { fun renderInWorld(event: WorldRenderLastEvent, block: RenderInWorldContext. () -> Unit) {
RenderSystem.disableDepthTest() // 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
RenderSystem.enableBlend() // For now i will let these functions remain, but this needs to go before i do a full (non-beta) release
RenderSystem.defaultBlendFunc() RenderSystem.disableDepthTest()
RenderSystem.disableCull() RenderSystem.enableBlend()
RenderSystem.defaultBlendFunc()
RenderSystem.disableCull()
event.matrices.push() event.matrices.push()
event.matrices.translate(-event.camera.pos.x, -event.camera.pos.y, -event.camera.pos.z) event.matrices.translate(-event.camera.pos.x, -event.camera.pos.y, -event.camera.pos.z)
val ctx = RenderInWorldContext( val ctx = RenderInWorldContext(
RenderSystem.renderThreadTesselator(), RenderSystem.renderThreadTesselator(),
event.matrices, event.matrices,
event.camera, event.camera,
event.tickCounter, event.tickCounter,
event.vertexConsumers event.vertexConsumers
) )
block(ctx) block(ctx)
event.matrices.pop() event.matrices.pop()
event.vertexConsumers.draw()
RenderSystem.setShaderColor(1F, 1F, 1F, 1F) RenderSystem.setShaderColor(1F, 1F, 1F, 1F)
VertexBuffer.unbind() VertexBuffer.unbind()
RenderSystem.enableDepthTest() RenderSystem.enableDepthTest()
RenderSystem.enableCull() RenderSystem.enableCull()
RenderSystem.disableBlend() RenderSystem.disableBlend()
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show More