aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/util')
-rw-r--r--src/main/kotlin/util/Base64Util.kt7
-rw-r--r--src/main/kotlin/util/ErrorUtil.kt5
-rw-r--r--src/main/kotlin/util/FirmFormatters.kt4
-rw-r--r--src/main/kotlin/util/MC.kt27
-rw-r--r--src/main/kotlin/util/MoulConfigUtils.kt36
-rw-r--r--src/main/kotlin/util/SkyblockId.kt74
-rw-r--r--src/main/kotlin/util/TestUtil.kt1
-rw-r--r--src/main/kotlin/util/asm/AsmAnnotationUtil.kt89
-rw-r--r--src/main/kotlin/util/collections/WeakCache.kt202
-rw-r--r--src/main/kotlin/util/json/DashlessUUIDSerializer.kt6
-rw-r--r--src/main/kotlin/util/math/GChainReconciliation.kt102
-rw-r--r--src/main/kotlin/util/mc/ArmorUtil.kt8
-rw-r--r--src/main/kotlin/util/mc/NbtPrism.kt91
-rw-r--r--src/main/kotlin/util/mc/PlayerUtil.kt7
-rw-r--r--src/main/kotlin/util/mc/SNbtFormatter.kt9
-rw-r--r--src/main/kotlin/util/render/CustomRenderLayers.kt74
-rw-r--r--src/main/kotlin/util/render/DrawContextExt.kt42
-rw-r--r--src/main/kotlin/util/render/FacingThePlayerContext.kt6
-rw-r--r--src/main/kotlin/util/render/FirmamentShaders.kt18
-rw-r--r--src/main/kotlin/util/render/RenderCircleProgress.kt12
-rw-r--r--src/main/kotlin/util/render/RenderInWorldContext.kt61
-rw-r--r--src/main/kotlin/util/render/TintedOverlayTexture.kt11
-rw-r--r--src/main/kotlin/util/skyblock/SackUtil.kt2
-rw-r--r--src/main/kotlin/util/textutil.kt14
-rw-r--r--src/main/kotlin/util/uuid.kt6
25 files changed, 643 insertions, 271 deletions
diff --git a/src/main/kotlin/util/Base64Util.kt b/src/main/kotlin/util/Base64Util.kt
index 44bcdfd..c39c601 100644
--- a/src/main/kotlin/util/Base64Util.kt
+++ b/src/main/kotlin/util/Base64Util.kt
@@ -1,7 +1,14 @@
package moe.nea.firmament.util
+import java.util.Base64
+
object Base64Util {
+ fun decodeString(str: String): String {
+ return Base64.getDecoder().decode(str.padToValidBase64())
+ .decodeToString()
+ }
+
fun String.padToValidBase64(): String {
val align = this.length % 4
if (align == 0) return this
diff --git a/src/main/kotlin/util/ErrorUtil.kt b/src/main/kotlin/util/ErrorUtil.kt
index 190381d..e82d369 100644
--- a/src/main/kotlin/util/ErrorUtil.kt
+++ b/src/main/kotlin/util/ErrorUtil.kt
@@ -38,6 +38,8 @@ object ErrorUtil {
}
class Catch<T> private constructor(val value: T?, val exc: Throwable?) {
+ fun orNull(): T? = value
+
inline fun or(block: (exc: Throwable) -> T): T {
contract {
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
@@ -73,4 +75,7 @@ object ErrorUtil {
return nullable
}
+ fun softUserError(string: String) {
+ MC.sendChat(tr("frimanet.usererror", "Firmament encountered a user caused error: $string"))
+ }
}
diff --git a/src/main/kotlin/util/FirmFormatters.kt b/src/main/kotlin/util/FirmFormatters.kt
index a660f51..03dafc5 100644
--- a/src/main/kotlin/util/FirmFormatters.kt
+++ b/src/main/kotlin/util/FirmFormatters.kt
@@ -135,4 +135,8 @@ object FirmFormatters {
fun formatPosition(position: BlockPos): Text {
return Text.literal("x: ${position.x}, y: ${position.y}, z: ${position.z}")
}
+
+ fun formatPercent(value: Double, decimals: Int = 1): String {
+ return "%.${decimals}f%%".format(value * 100)
+ }
}
diff --git a/src/main/kotlin/util/MC.kt b/src/main/kotlin/util/MC.kt
index c1a5e65..e85b119 100644
--- a/src/main/kotlin/util/MC.kt
+++ b/src/main/kotlin/util/MC.kt
@@ -1,7 +1,9 @@
package moe.nea.firmament.util
import io.github.moulberry.repo.data.Coordinate
+import io.github.notenoughupdates.moulconfig.gui.GuiComponentWrapper
import java.util.concurrent.ConcurrentLinkedQueue
+import kotlin.jvm.optionals.getOrNull
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.hud.InGameHud
import net.minecraft.client.gui.screen.Screen
@@ -16,10 +18,14 @@ import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.network.packet.c2s.play.CommandExecutionC2SPacket
import net.minecraft.registry.BuiltinRegistries
+import net.minecraft.registry.Registry
+import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.registry.RegistryWrapper
import net.minecraft.resource.ReloadableResourceManagerImpl
import net.minecraft.text.Text
+import net.minecraft.util.Identifier
+import net.minecraft.util.Util
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import moe.nea.firmament.events.TickEvent
@@ -99,7 +105,7 @@ object MC {
inline val soundManager get() = instance.soundManager
inline val player: ClientPlayerEntity? get() = TestUtil.unlessTesting { instance.player }
inline val camera: Entity? get() = instance.cameraEntity
- inline val stackInHand: ItemStack get() = player?.inventory?.mainHandStack ?: ItemStack.EMPTY
+ inline val stackInHand: ItemStack get() = player?.mainHandStack ?: ItemStack.EMPTY
inline val guiAtlasManager get() = instance.guiAtlasManager
inline val world: ClientWorld? get() = TestUtil.unlessTesting { instance.world }
inline val playerName: String? get() = player?.name?.unformattedString
@@ -120,6 +126,25 @@ object MC {
return field
}
private set
+
+ val currentMoulConfigContext
+ get() = (screen as? GuiComponentWrapper)?.context
+
+ fun openUrl(uri: String) {
+ Util.getOperatingSystem().open(uri)
+ }
+
+ fun <T> unsafeGetRegistryEntry(registry: RegistryKey<out Registry<T>>, identifier: Identifier) =
+ unsafeGetRegistryEntry(RegistryKey.of(registry, identifier))
+
+
+ fun <T> unsafeGetRegistryEntry(registryKey: RegistryKey<T>): T? {
+ return currentOrDefaultRegistries
+ .getOrThrow(registryKey.registryRef)
+ .getOptional(registryKey)
+ .getOrNull()
+ ?.value()
+ }
}
diff --git a/src/main/kotlin/util/MoulConfigUtils.kt b/src/main/kotlin/util/MoulConfigUtils.kt
index 362a4d9..a9e3241 100644
--- a/src/main/kotlin/util/MoulConfigUtils.kt
+++ b/src/main/kotlin/util/MoulConfigUtils.kt
@@ -9,6 +9,7 @@ import io.github.notenoughupdates.moulconfig.gui.GuiContext
import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent
import io.github.notenoughupdates.moulconfig.gui.MouseEvent
+import io.github.notenoughupdates.moulconfig.gui.component.PanelComponent
import io.github.notenoughupdates.moulconfig.observer.GetSetter
import io.github.notenoughupdates.moulconfig.platform.ModernRenderContext
import io.github.notenoughupdates.moulconfig.xml.ChildCount
@@ -20,6 +21,7 @@ import java.io.File
import java.util.function.Supplier
import javax.xml.namespace.QName
import me.shedaniel.math.Color
+import org.jetbrains.annotations.Unmodifiable
import org.w3c.dom.Element
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
@@ -35,6 +37,19 @@ import moe.nea.firmament.gui.TickComponent
import moe.nea.firmament.util.render.isUntranslatedGuiDrawContext
object MoulConfigUtils {
+ @JvmStatic
+ fun main(args: Array<out String>) {
+ generateXSD(File("MoulConfig.xsd"), XMLUniverse.MOULCONFIG_XML_NS)
+ generateXSD(File("MoulConfig.Firmament.xsd"), firmUrl)
+ File("wrapper.xsd").writeText("""
+<?xml version="1.0" encoding="UTF-8" ?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <xs:import namespace="http://notenoughupdates.org/moulconfig" schemaLocation="MoulConfig.xsd"/>
+ <xs:import namespace="http://firmament.nea.moe/moulconfig" schemaLocation="MoulConfig.Firmament.xsd"/>
+</xs:schema>
+ """.trimIndent())
+ }
+
val firmUrl = "http://firmament.nea.moe/moulconfig"
val universe = XMLUniverse.getDefaultUniverse().also { uni ->
uni.registerMapper(java.awt.Color::class.java) {
@@ -179,10 +194,8 @@ object MoulConfigUtils {
uni.registerLoader(object : XMLGuiLoader.Basic<FixedComponent> {
override fun createInstance(context: XMLContext<*>, element: Element): FixedComponent {
return FixedComponent(
- context.getPropertyFromAttribute(element, QName("width"), Int::class.java)
- ?: error("Requires width specified"),
- context.getPropertyFromAttribute(element, QName("height"), Int::class.java)
- ?: error("Requires height specified"),
+ context.getPropertyFromAttribute(element, QName("width"), Int::class.java),
+ context.getPropertyFromAttribute(element, QName("height"), Int::class.java),
context.getChildFragment(element)
)
}
@@ -196,7 +209,7 @@ object MoulConfigUtils {
}
override fun getAttributeNames(): Map<String, Boolean> {
- return mapOf("width" to true, "height" to true)
+ return mapOf("width" to false, "height" to false)
}
})
}
@@ -210,19 +223,6 @@ object MoulConfigUtils {
generator.dumpToFile(file)
}
- @JvmStatic
- fun main(args: Array<out String>) {
- generateXSD(File("MoulConfig.xsd"), XMLUniverse.MOULCONFIG_XML_NS)
- generateXSD(File("MoulConfig.Firmament.xsd"), firmUrl)
- File("wrapper.xsd").writeText("""
-<?xml version="1.0" encoding="UTF-8" ?>
-<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
- <xs:import namespace="http://notenoughupdates.org/moulconfig" schemaLocation="MoulConfig.xsd"/>
- <xs:import namespace="http://firmament.nea.moe/moulconfig" schemaLocation="MoulConfig.Firmament.xsd"/>
-</xs:schema>
- """.trimIndent())
- }
-
fun loadScreen(name: String, bindTo: Any, parent: Screen?): Screen {
return object : GuiComponentWrapper(loadGui(name, bindTo)) {
override fun close() {
diff --git a/src/main/kotlin/util/SkyblockId.kt b/src/main/kotlin/util/SkyblockId.kt
index a31255c..7d8c96c 100644
--- a/src/main/kotlin/util/SkyblockId.kt
+++ b/src/main/kotlin/util/SkyblockId.kt
@@ -21,6 +21,7 @@ import net.minecraft.network.RegistryByteBuf
import net.minecraft.network.codec.PacketCodec
import net.minecraft.network.codec.PacketCodecs
import net.minecraft.util.Identifier
+import moe.nea.firmament.repo.ExpLadders
import moe.nea.firmament.repo.ItemCache.asItemStack
import moe.nea.firmament.repo.set
import moe.nea.firmament.util.collections.WeakCache
@@ -28,8 +29,8 @@ import moe.nea.firmament.util.json.DashlessUUIDSerializer
/**
* A SkyBlock item id, as used by the NEU repo.
- * This is not exactly the format used by HyPixel, but is mostly the same.
- * Usually this id splits an id used by HyPixel into more sub items. For example `PET` becomes `$PET_ID;$PET_RARITY`,
+ * This is not exactly the format used by Hypixel, but is mostly the same.
+ * Usually this id splits an id used by Hypixel into more sub items. For example `PET` becomes `$PET_ID;$PET_RARITY`,
* with those values extracted from other metadata.
*/
@JvmInline
@@ -53,7 +54,7 @@ value class SkyblockId(val neuItem: String) : Comparable<SkyblockId> {
}
/**
- * A bazaar stock item id, as returned by the HyPixel bazaar api endpoint.
+ * A bazaar stock item id, as returned by the Hypixel bazaar api endpoint.
* These are not equivalent to the in-game ids, or the NEU repo ids, and in fact, do not refer to items, but instead
* to bazaar stocks. The main difference from [SkyblockId]s is concerning enchanted books. There are probably more,
* but for now this holds.
@@ -104,8 +105,10 @@ data class HypixelPetInfo(
val candyUsed: Int = 0,
val uuid: UUID? = null,
val active: Boolean = false,
+ val heldItem: String? = null,
) {
val skyblockId get() = SkyblockId("${type.uppercase()};${tier.ordinal}") // TODO: is this ordinal set up correctly?
+ val level get() = ExpLadders.getExpLadder(type, tier).getPetLevel(exp)
}
private val jsonparser = Json { ignoreUnknownKeys = true }
@@ -130,13 +133,14 @@ fun ItemStack.modifyExtraAttributes(block: (NbtCompound) -> Unit) {
}
val ItemStack.skyblockUUIDString: String?
- get() = extraAttributes.getString("uuid")?.takeIf { it.isNotBlank() }
+ get() = extraAttributes.getString("uuid").getOrNull()?.takeIf { it.isNotBlank() }
val ItemStack.skyblockUUID: UUID?
get() = skyblockUUIDString?.let { UUID.fromString(it) }
private val petDataCache = WeakCache.memoize<ItemStack, Optional<HypixelPetInfo>>("PetInfo") {
val jsonString = it.extraAttributes.getString("petInfo")
+ .getOrNull()
if (jsonString.isNullOrBlank()) return@memoize Optional.empty()
ErrorUtil.catch<HypixelPetInfo?>("Could not decode hypixel pet info") {
jsonparser.decodeFromString<HypixelPetInfo>(jsonString)
@@ -145,8 +149,8 @@ private val petDataCache = WeakCache.memoize<ItemStack, Optional<HypixelPetInfo>
}
fun ItemStack.getUpgradeStars(): Int {
- return extraAttributes.getInt("upgrade_level").takeIf { it > 0 }
- ?: extraAttributes.getInt("dungeon_item_level").takeIf { it > 0 }
+ return extraAttributes.getInt("upgrade_level").getOrNull()?.takeIf { it > 0 }
+ ?: extraAttributes.getInt("dungeon_item_level").getOrNull()?.takeIf { it > 0 }
?: 0
}
@@ -155,7 +159,7 @@ fun ItemStack.getUpgradeStars(): Int {
value class ReforgeId(val id: String)
fun ItemStack.getReforgeId(): ReforgeId? {
- return extraAttributes.getString("modifier").takeIf { it.isNotBlank() }?.let(::ReforgeId)
+ return extraAttributes.getString("modifier").getOrNull()?.takeIf { it.isNotBlank() }?.let(::ReforgeId)
}
val ItemStack.petData: HypixelPetInfo?
@@ -169,8 +173,8 @@ fun ItemStack.setSkyBlockId(skyblockId: SkyblockId): ItemStack {
val ItemStack.skyBlockId: SkyblockId?
get() {
- return when (val id = extraAttributes.getString("id")) {
- "" -> {
+ return when (val id = extraAttributes.getString("id").getOrNull()) {
+ "", null -> {
null
}
@@ -180,23 +184,63 @@ val ItemStack.skyBlockId: SkyblockId?
"RUNE", "UNIQUE_RUNE" -> {
val runeData = extraAttributes.getCompound("runes")
- val runeKind = runeData.keys.singleOrNull()
+ .getOrNull()
+ val runeKind = runeData?.keys?.singleOrNull()
if (runeKind == null) SkyblockId("RUNE")
- else SkyblockId("${runeKind.uppercase()}_RUNE;${runeData.getInt(runeKind)}")
+ else SkyblockId("${runeKind.uppercase()}_RUNE;${runeData.getInt(runeKind).getOrNull()}")
}
"ABICASE" -> {
- SkyblockId("ABICASE_${extraAttributes.getString("model").uppercase()}")
+ SkyblockId("ABICASE_${extraAttributes.getString("model").getOrNull()?.uppercase()}")
}
"ENCHANTED_BOOK" -> {
val enchantmentData = extraAttributes.getCompound("enchantments")
- val enchantName = enchantmentData.keys.singleOrNull()
+ .getOrNull()
+ val enchantName = enchantmentData?.keys?.singleOrNull()
if (enchantName == null) SkyblockId("ENCHANTED_BOOK")
- else SkyblockId("${enchantName.uppercase()};${enchantmentData.getInt(enchantName)}")
+ else SkyblockId("${enchantName.uppercase()};${enchantmentData.getInt(enchantName).getOrNull()}")
+ }
+
+ "ATTRIBUTE_SHARD" -> {
+ val attributeData = extraAttributes.getCompound("attributes").getOrNull()
+ val attributeName = attributeData?.keys?.singleOrNull()
+ if (attributeName == null) SkyblockId("ATTRIBUTE_SHARD")
+ else SkyblockId(
+ "ATTRIBUTE_SHARD_${attributeName.uppercase()};${
+ attributeData.getInt(attributeName).getOrNull()
+ }"
+ )
+ }
+
+ "POTION" -> {
+ val potionData = extraAttributes.getString("potion").getOrNull()
+ val potionName = extraAttributes.getString("potion_name").getOrNull()
+ val potionLevel = extraAttributes.getInt("potion_level").getOrNull()
+ val potionType = extraAttributes.getString("potion_type").getOrNull()
+ when {
+ potionName != null -> SkyblockId("POTION_${potionName.uppercase()};$potionLevel")
+ potionData != null -> SkyblockId("POTION_${potionData.uppercase()};$potionLevel")
+ potionType != null -> SkyblockId("POTION_${potionType.uppercase()}")
+ else -> SkyblockId("WATER_BOTTLE")
+ }
+ }
+
+ "PARTY_HAT_SLOTH", "PARTY_HAT_CRAB", "PARTY_HAT_CRAB_ANIMATED" -> {
+ val partyHatEmoji = extraAttributes.getString("party_hat_emoji").getOrNull()
+ val partyHatYear = extraAttributes.getInt("party_hat_year").getOrNull()
+ val partyHatColor = extraAttributes.getString("party_hat_color").getOrNull()
+ when {
+ partyHatEmoji != null -> SkyblockId("PARTY_HAT_SLOTH_${partyHatEmoji.uppercase()}")
+ partyHatYear == 2022 -> SkyblockId("PARTY_HAT_CRAB_${partyHatColor?.uppercase()}_ANIMATED")
+ else -> SkyblockId("PARTY_HAT_CRAB_${partyHatColor?.uppercase()}")
+ }
+ }
+
+ "BALLOON_HAT_2024" -> {
+ SkyblockId("BALLOON_HAT_2024_${extraAttributes.getString("party_hat_color").getOrNull()?.uppercase()}")
}
- // TODO: PARTY_HAT_CRAB{,_ANIMATED,_SLOTH},POTION
else -> {
SkyblockId(id)
}
diff --git a/src/main/kotlin/util/TestUtil.kt b/src/main/kotlin/util/TestUtil.kt
index 45e3dde..da8ba38 100644
--- a/src/main/kotlin/util/TestUtil.kt
+++ b/src/main/kotlin/util/TestUtil.kt
@@ -2,6 +2,7 @@ package moe.nea.firmament.util
object TestUtil {
inline fun <T> unlessTesting(block: () -> T): T? = if (isInTest) null else block()
+ @JvmField
val isInTest =
Thread.currentThread().stackTrace.any {
it.className.startsWith("org.junit.") || it.className.startsWith("io.kotest.")
diff --git a/src/main/kotlin/util/asm/AsmAnnotationUtil.kt b/src/main/kotlin/util/asm/AsmAnnotationUtil.kt
new file mode 100644
index 0000000..fb0e92c
--- /dev/null
+++ b/src/main/kotlin/util/asm/AsmAnnotationUtil.kt
@@ -0,0 +1,89 @@
+package moe.nea.firmament.util.asm
+
+import com.google.common.base.Defaults
+import java.lang.reflect.InvocationHandler
+import java.lang.reflect.Method
+import java.lang.reflect.Proxy
+import org.objectweb.asm.Type
+import org.objectweb.asm.tree.AnnotationNode
+
+object AsmAnnotationUtil {
+ class AnnotationProxy(
+ val originalType: Class<out Annotation>,
+ val annotationNode: AnnotationNode,
+ ) : InvocationHandler {
+ val offsets = annotationNode.values.withIndex()
+ .chunked(2)
+ .map { it.first() }
+ .associate { (idx, value) -> value as String to idx + 1 }
+
+ fun nestArrayType(depth: Int, comp: Class<*>): Class<*> =
+ if (depth == 0) comp
+ else java.lang.reflect.Array.newInstance(nestArrayType(depth - 1, comp), 0).javaClass
+
+ fun unmap(
+ value: Any?,
+ comp: Class<*>,
+ depth: Int,
+ ): Any? {
+ value ?: return null
+ if (depth > 0)
+ return ((value as List<Any>)
+ .map { unmap(it, comp, depth - 1) } as java.util.List<Any>)
+ .toArray(java.lang.reflect.Array.newInstance(nestArrayType(depth - 1, comp), 0) as Array<*>)
+ if (comp.isEnum) {
+ comp as Class<out Enum<*>>
+ when (value) {
+ is String -> return java.lang.Enum.valueOf(comp, value)
+ is List<*> -> return java.lang.Enum.valueOf(comp, value[1] as String)
+ else -> error("Unknown enum variant $value for $comp")
+ }
+ }
+ when (value) {
+ is Type -> return Class.forName(value.className)
+ is AnnotationNode -> return createProxy(comp as Class<out Annotation>, value)
+ is String, is Boolean, is Byte, is Double, is Int, is Float, is Long, is Short, is Char -> return value
+ }
+ error("Unknown enum variant $value for $comp")
+ }
+
+ fun defaultFor(fullType: Class<*>): Any? {
+ if (fullType.isArray) return java.lang.reflect.Array.newInstance(fullType.componentType, 0)
+ if (fullType.isPrimitive) {
+ return Defaults.defaultValue(fullType)
+ }
+ if (fullType == String::class.java)
+ return ""
+ return null
+ }
+
+ override fun invoke(
+ proxy: Any,
+ method: Method,
+ args: Array<out Any?>?
+ ): Any? {
+ val name = method.name
+ val ret = method.returnType
+ val retU = generateSequence(ret) { if (it.isArray) it.componentType else null }
+ .toList()
+ val arrayDepth = retU.size - 1
+ val componentType = retU.last()
+
+ val off = offsets[name]
+ if (off == null) {
+ return defaultFor(ret)
+ }
+ return unmap(annotationNode.values[off], componentType, arrayDepth)
+ }
+ }
+
+ fun <T : Annotation> createProxy(
+ annotationClass: Class<T>,
+ annotationNode: AnnotationNode
+ ): T {
+ require(Type.getType(annotationClass) == Type.getType(annotationNode.desc))
+ return Proxy.newProxyInstance(javaClass.classLoader,
+ arrayOf(annotationClass),
+ AnnotationProxy(annotationClass, annotationNode)) as T
+ }
+}
diff --git a/src/main/kotlin/util/collections/WeakCache.kt b/src/main/kotlin/util/collections/WeakCache.kt
index 38f9886..4a48c63 100644
--- a/src/main/kotlin/util/collections/WeakCache.kt
+++ b/src/main/kotlin/util/collections/WeakCache.kt
@@ -9,102 +9,108 @@ import moe.nea.firmament.features.debug.DebugLogger
* the key. Each key can have additional extra data that is used to look up values. That extra data is not required to
* be a life reference. The main Key is compared using strict reference equality. This map is not synchronized.
*/
-class WeakCache<Key : Any, ExtraKey : Any, Value : Any>(val name: String) {
- private val queue = object : ReferenceQueue<Key>() {}
- private val map = mutableMapOf<Ref, Value>()
-
- val size: Int
- get() {
- clearOldReferences()
- return map.size
- }
-
- fun clearOldReferences() {
- var successCount = 0
- var totalCount = 0
- while (true) {
- val reference = queue.poll() ?: break
- totalCount++
- if (map.remove(reference) != null)
- successCount++
- }
- if (totalCount > 0)
- logger.log { "Cleared $successCount/$totalCount references from queue" }
- }
-
- fun get(key: Key, extraData: ExtraKey): Value? {
- clearOldReferences()
- return map[Ref(key, extraData)]
- }
-
- fun put(key: Key, extraData: ExtraKey, value: Value) {
- clearOldReferences()
- map[Ref(key, extraData)] = value
- }
-
- fun getOrPut(key: Key, extraData: ExtraKey, value: (Key, ExtraKey) -> Value): Value {
- clearOldReferences()
- return map.getOrPut(Ref(key, extraData)) { value(key, extraData) }
- }
-
- fun clear() {
- map.clear()
- }
-
- init {
- allInstances.add(this)
- }
-
- companion object {
- val allInstances = InstanceList<WeakCache<*, *, *>>("WeakCaches")
- private val logger = DebugLogger("WeakCache")
- fun <Key : Any, Value : Any> memoize(name: String, function: (Key) -> Value):
- CacheFunction.NoExtraData<Key, Value> {
- return CacheFunction.NoExtraData(WeakCache(name), function)
- }
-
- fun <Key : Any, ExtraKey : Any, Value : Any> memoize(name: String, function: (Key, ExtraKey) -> Value):
- CacheFunction.WithExtraData<Key, ExtraKey, Value> {
- return CacheFunction.WithExtraData(WeakCache(name), function)
- }
- }
-
- inner class Ref(
- weakInstance: Key,
- val extraData: ExtraKey,
- ) : WeakReference<Key>(weakInstance, queue) {
- val hashCode = System.identityHashCode(weakInstance) * 31 + extraData.hashCode()
- override fun equals(other: Any?): Boolean {
- if (other !is WeakCache<*, *, *>.Ref) return false
- return other.hashCode == this.hashCode
- && other.get() === this.get()
- && other.extraData == this.extraData
- }
-
- override fun hashCode(): Int {
- return hashCode
- }
- }
-
- interface CacheFunction {
- val cache: WeakCache<*, *, *>
-
- data class NoExtraData<Key : Any, Value : Any>(
- override val cache: WeakCache<Key, Unit, Value>,
- val wrapped: (Key) -> Value,
- ) : CacheFunction, (Key) -> Value {
- override fun invoke(p1: Key): Value {
- return cache.getOrPut(p1, Unit, { a, _ -> wrapped(a) })
- }
- }
-
- data class WithExtraData<Key : Any, ExtraKey : Any, Value : Any>(
- override val cache: WeakCache<Key, ExtraKey, Value>,
- val wrapped: (Key, ExtraKey) -> Value,
- ) : CacheFunction, (Key, ExtraKey) -> Value {
- override fun invoke(p1: Key, p2: ExtraKey): Value {
- return cache.getOrPut(p1, p2, wrapped)
- }
- }
- }
+open class WeakCache<Key : Any, ExtraKey : Any, Value : Any>(val name: String) {
+ private val queue = object : ReferenceQueue<Key>() {}
+ private val map = mutableMapOf<Ref, Value>()
+
+ val size: Int
+ get() {
+ clearOldReferences()
+ return map.size
+ }
+
+ fun clearOldReferences() {
+ var successCount = 0
+ var totalCount = 0
+ while (true) {
+ val reference = queue.poll() as WeakCache<*, *, *>.Ref? ?: break
+ totalCount++
+ if (reference.shouldBeEvicted() && map.remove(reference) != null)
+ successCount++
+ }
+ if (totalCount > 0)
+ logger.log("Cleared $successCount/$totalCount references from queue")
+ }
+
+ open fun mkRef(key: Key, extraData: ExtraKey): Ref {
+ return Ref(key, extraData)
+ }
+
+ fun get(key: Key, extraData: ExtraKey): Value? {
+ clearOldReferences()
+ return map[mkRef(key, extraData)]
+ }
+
+ fun put(key: Key, extraData: ExtraKey, value: Value) {
+ clearOldReferences()
+ map[mkRef(key, extraData)] = value
+ }
+
+ fun getOrPut(key: Key, extraData: ExtraKey, value: (Key, ExtraKey) -> Value): Value {
+ clearOldReferences()
+ return map.getOrPut(mkRef(key, extraData)) { value(key, extraData) }
+ }
+
+ fun clear() {
+ map.clear()
+ }
+
+ init {
+ allInstances.add(this)
+ }
+
+ companion object {
+ val allInstances = InstanceList<WeakCache<*, *, *>>("WeakCaches")
+ private val logger = DebugLogger("WeakCache")
+ fun <Key : Any, Value : Any> memoize(name: String, function: (Key) -> Value):
+ CacheFunction.NoExtraData<Key, Value> {
+ return CacheFunction.NoExtraData(WeakCache(name), function)
+ }
+
+ fun <Key : Any, ExtraKey : Any, Value : Any> dontMemoize(name: String, function: (Key, ExtraKey) -> Value) = function
+ fun <Key : Any, ExtraKey : Any, Value : Any> memoize(name: String, function: (Key, ExtraKey) -> Value):
+ CacheFunction.WithExtraData<Key, ExtraKey, Value> {
+ return CacheFunction.WithExtraData(WeakCache(name), function)
+ }
+ }
+
+ open inner class Ref(
+ weakInstance: Key,
+ val extraData: ExtraKey,
+ ) : WeakReference<Key>(weakInstance, queue) {
+ open fun shouldBeEvicted() = true
+ val hashCode = System.identityHashCode(weakInstance) * 31 + extraData.hashCode()
+ override fun equals(other: Any?): Boolean {
+ if (other !is WeakCache<*, *, *>.Ref) return false
+ return other.hashCode == this.hashCode
+ && other.get() === this.get()
+ && other.extraData == this.extraData
+ }
+
+ override fun hashCode(): Int {
+ return hashCode
+ }
+ }
+
+ interface CacheFunction {
+ val cache: WeakCache<*, *, *>
+
+ data class NoExtraData<Key : Any, Value : Any>(
+ override val cache: WeakCache<Key, Unit, Value>,
+ val wrapped: (Key) -> Value,
+ ) : CacheFunction, (Key) -> Value {
+ override fun invoke(p1: Key): Value {
+ return cache.getOrPut(p1, Unit, { a, _ -> wrapped(a) })
+ }
+ }
+
+ data class WithExtraData<Key : Any, ExtraKey : Any, Value : Any>(
+ override val cache: WeakCache<Key, ExtraKey, Value>,
+ val wrapped: (Key, ExtraKey) -> Value,
+ ) : CacheFunction, (Key, ExtraKey) -> Value {
+ override fun invoke(p1: Key, p2: ExtraKey): Value {
+ return cache.getOrPut(p1, p2, wrapped)
+ }
+ }
+ }
}
diff --git a/src/main/kotlin/util/json/DashlessUUIDSerializer.kt b/src/main/kotlin/util/json/DashlessUUIDSerializer.kt
index acb1dc8..6bafebe 100644
--- a/src/main/kotlin/util/json/DashlessUUIDSerializer.kt
+++ b/src/main/kotlin/util/json/DashlessUUIDSerializer.kt
@@ -10,6 +10,7 @@ import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import moe.nea.firmament.util.parseDashlessUUID
+import moe.nea.firmament.util.parsePotentiallyDashlessUUID
object DashlessUUIDSerializer : KSerializer<UUID> {
override val descriptor: SerialDescriptor =
@@ -17,10 +18,7 @@ object DashlessUUIDSerializer : KSerializer<UUID> {
override fun deserialize(decoder: Decoder): UUID {
val str = decoder.decodeString()
- if ("-" in str) {
- return UUID.fromString(str)
- }
- return parseDashlessUUID(str)
+ return parsePotentiallyDashlessUUID(str)
}
override fun serialize(encoder: Encoder, value: UUID) {
diff --git a/src/main/kotlin/util/math/GChainReconciliation.kt b/src/main/kotlin/util/math/GChainReconciliation.kt
new file mode 100644
index 0000000..37998d5
--- /dev/null
+++ b/src/main/kotlin/util/math/GChainReconciliation.kt
@@ -0,0 +1,102 @@
+package moe.nea.firmament.util.math
+
+import kotlin.math.min
+
+/**
+ * Algorithm for (sort of) cheap reconciliation of two cycles with missing frames.
+ */
+object GChainReconciliation {
+ // Step one: Find the most common element and shift the arrays until it is at the start in both (this could be just rotating until minimal levenshtein distance or smth. that would be way better for cycles with duplicates, but i do not want to implement levenshtein as well)
+ // Step two: Find the first different element.
+ // Step three: Find the next index of both of the elements.
+ // Step four: Insert the element that is further away.
+
+ fun <T> Iterable<T>.frequencies(): Map<T, Int> {
+ val acc = mutableMapOf<T, Int>()
+ for (t in this) {
+ acc.compute(t, { _, old -> (old ?: 0) + 1 })
+ }
+ return acc
+ }
+
+ fun <T> findMostCommonlySharedElement(
+ leftChain: List<T>,
+ rightChain: List<T>,
+ ): T {
+ val lf = leftChain.frequencies()
+ val rf = rightChain.frequencies()
+ val mostCommonlySharedElement = lf.maxByOrNull { min(it.value, rf[it.key] ?: 0) }?.key
+ if (mostCommonlySharedElement == null || mostCommonlySharedElement !in rf)
+ error("Could not find a shared element")
+ return mostCommonlySharedElement
+ }
+
+ fun <T> List<T>.getMod(index: Int): T {
+ return this[index.mod(size)]
+ }
+
+ fun <T> List<T>.rotated(offset: Int): List<T> {
+ val newList = mutableListOf<T>()
+ for (index in indices) {
+ newList.add(getMod(index - offset))
+ }
+ return newList
+ }
+
+ fun <T> shiftToFront(list: List<T>, element: T): List<T> {
+ val shiftDistance = list.indexOf(element)
+ require(shiftDistance >= 0)
+ return list.rotated(-shiftDistance)
+ }
+
+ fun <T> List<T>.indexOfOrMaxInt(element: T): Int = indexOf(element).takeUnless { it < 0 } ?: Int.MAX_VALUE
+
+ fun <T> reconcileCycles(
+ leftChain: List<T>,
+ rightChain: List<T>,
+ ): List<T> {
+ val mostCommonElement = findMostCommonlySharedElement(leftChain, rightChain)
+ val left = shiftToFront(leftChain, mostCommonElement).toMutableList()
+ val right = shiftToFront(rightChain, mostCommonElement).toMutableList()
+
+ var index = 0
+ while (index < left.size && index < right.size) {
+ val leftEl = left[index]
+ val rightEl = right[index]
+ if (leftEl == rightEl) {
+ index++
+ continue
+ }
+ val nextLeftInRight = right.subList(index, right.size)
+ .indexOfOrMaxInt(leftEl)
+
+ val nextRightInLeft = left.subList(index, left.size)
+ .indexOfOrMaxInt(rightEl)
+ if (nextLeftInRight < nextRightInLeft) {
+ left.add(index, rightEl)
+ } else if (nextRightInLeft < nextLeftInRight) {
+ right.add(index, leftEl)
+ } else {
+ index++
+ }
+ }
+ return if (left.size < right.size) right else left
+ }
+
+ fun <T> isValidCycle(longList: List<T>, cycle: List<T>): Boolean {
+ for ((i, value) in longList.withIndex()) {
+ if (cycle.getMod(i) != value)
+ return false
+ }
+ return true
+ }
+
+ fun <T> List<T>.shortenCycle(): List<T> {
+ for (i in (1..<size)) {
+ if (isValidCycle(this, subList(0, i)))
+ return subList(0, i)
+ }
+ return this
+ }
+
+}
diff --git a/src/main/kotlin/util/mc/ArmorUtil.kt b/src/main/kotlin/util/mc/ArmorUtil.kt
new file mode 100644
index 0000000..fd1867c
--- /dev/null
+++ b/src/main/kotlin/util/mc/ArmorUtil.kt
@@ -0,0 +1,8 @@
+package moe.nea.firmament.util.mc
+
+import net.minecraft.entity.EquipmentSlot
+import net.minecraft.entity.LivingEntity
+
+val LivingEntity.iterableArmorItems
+ get() = EquipmentSlot.entries.asSequence()
+ .map { it to getEquippedStack(it) }
diff --git a/src/main/kotlin/util/mc/NbtPrism.kt b/src/main/kotlin/util/mc/NbtPrism.kt
new file mode 100644
index 0000000..f034210
--- /dev/null
+++ b/src/main/kotlin/util/mc/NbtPrism.kt
@@ -0,0 +1,91 @@
+package moe.nea.firmament.util.mc
+
+import com.google.gson.Gson
+import com.google.gson.JsonArray
+import com.google.gson.JsonElement
+import com.google.gson.JsonPrimitive
+import com.mojang.brigadier.StringReader
+import com.mojang.brigadier.arguments.ArgumentType
+import com.mojang.brigadier.arguments.StringArgumentType
+import com.mojang.brigadier.context.CommandContext
+import com.mojang.brigadier.suggestion.Suggestions
+import com.mojang.brigadier.suggestion.SuggestionsBuilder
+import com.mojang.serialization.JsonOps
+import java.util.concurrent.CompletableFuture
+import kotlin.collections.indices
+import kotlin.collections.map
+import kotlin.jvm.optionals.getOrNull
+import net.minecraft.nbt.NbtCompound
+import net.minecraft.nbt.NbtElement
+import net.minecraft.nbt.NbtList
+import net.minecraft.nbt.NbtOps
+import net.minecraft.nbt.NbtString
+import moe.nea.firmament.util.Base64Util
+
+class NbtPrism(val path: List<String>) {
+ companion object {
+ fun fromElement(path: JsonElement): NbtPrism? {
+ if (path is JsonArray) {
+ return NbtPrism(path.map { (it as JsonPrimitive).asString })
+ } else if (path is JsonPrimitive && path.isString) {
+ return NbtPrism(path.asString.split("."))
+ }
+ return null
+ }
+ }
+
+ object Argument : ArgumentType<NbtPrism> {
+ override fun parse(reader: StringReader): NbtPrism? {
+ return fromElement(JsonPrimitive(StringArgumentType.string().parse(reader)))
+ }
+
+ override fun getExamples(): Collection<String?>? {
+ return listOf("some.nbt.path", "some.other.*", "some.path.*json.in.a.json.string")
+ }
+ }
+
+ override fun toString(): String {
+ return "Prism($path)"
+ }
+
+ fun access(root: NbtElement): Collection<NbtElement> {
+ var rootSet = mutableListOf(root)
+ var switch = mutableListOf<NbtElement>()
+ for (pathSegment in path) {
+ if (pathSegment == ".") continue
+ if (pathSegment != "*" && pathSegment.startsWith("*")) {
+ if (pathSegment == "*json") {
+ for (element in rootSet) {
+ val eString = element.asString().getOrNull() ?: continue
+ val element = Gson().fromJson(eString, JsonElement::class.java)
+ switch.add(JsonOps.INSTANCE.convertTo(NbtOps.INSTANCE, element))
+ }
+ } else if (pathSegment == "*base64") {
+ for (element in rootSet) {
+ val string = element.asString().getOrNull() ?: continue
+ switch.add(NbtString.of(Base64Util.decodeString(string)))
+ }
+ }
+ }
+ for (element in rootSet) {
+ if (element is NbtList) {
+ if (pathSegment == "*")
+ switch.addAll(element)
+ val index = pathSegment.toIntOrNull() ?: continue
+ if (index !in element.indices) continue
+ switch.add(element[index])
+ }
+ if (element is NbtCompound) {
+ if (pathSegment == "*")
+ element.keys.mapTo(switch) { element.get(it)!! }
+ switch.add(element.get(pathSegment) ?: continue)
+ }
+ }
+ val temp = switch
+ switch = rootSet
+ rootSet = temp
+ switch.clear()
+ }
+ return rootSet
+ }
+}
diff --git a/src/main/kotlin/util/mc/PlayerUtil.kt b/src/main/kotlin/util/mc/PlayerUtil.kt
new file mode 100644
index 0000000..53ef1f4
--- /dev/null
+++ b/src/main/kotlin/util/mc/PlayerUtil.kt
@@ -0,0 +1,7 @@
+package moe.nea.firmament.util.mc
+
+import net.minecraft.entity.EquipmentSlot
+import net.minecraft.entity.player.PlayerEntity
+
+
+val PlayerEntity.mainHandStack get() = this.getEquippedStack(EquipmentSlot.MAINHAND)
diff --git a/src/main/kotlin/util/mc/SNbtFormatter.kt b/src/main/kotlin/util/mc/SNbtFormatter.kt
index e773927..e2c24f6 100644
--- a/src/main/kotlin/util/mc/SNbtFormatter.kt
+++ b/src/main/kotlin/util/mc/SNbtFormatter.kt
@@ -1,5 +1,6 @@
package moe.nea.firmament.util.mc
+import net.minecraft.nbt.AbstractNbtList
import net.minecraft.nbt.NbtByte
import net.minecraft.nbt.NbtByteArray
import net.minecraft.nbt.NbtCompound
@@ -38,7 +39,7 @@ class SNbtFormatter private constructor() : NbtElementVisitor {
override fun visitString(element: NbtString) {
- result.append(NbtString.escape(element.asString()))
+ result.append(NbtString.escape(element.value))
}
override fun visitByte(element: NbtByte) {
@@ -65,18 +66,18 @@ class SNbtFormatter private constructor() : NbtElementVisitor {
result.append(element.doubleValue()).append("d")
}
- private fun visitArrayContents(array: List<NbtElement>) {
+ private fun visitArrayContents(array: AbstractNbtList) {
array.forEachIndexed { index, element ->
writeIndent()
element.accept(this)
- if (array.size != index + 1) {
+ if (array.size() != index + 1) {
result.append(",")
}
result.append("\n")
}
}
- private fun writeArray(arrayTypeTag: String, array: List<NbtElement>) {
+ private fun writeArray(arrayTypeTag: String, array: AbstractNbtList) {
result.append("[").append(arrayTypeTag).append("\n")
pushIndent()
visitArrayContents(array)
diff --git a/src/main/kotlin/util/render/CustomRenderLayers.kt b/src/main/kotlin/util/render/CustomRenderLayers.kt
new file mode 100644
index 0000000..7f3cdec
--- /dev/null
+++ b/src/main/kotlin/util/render/CustomRenderLayers.kt
@@ -0,0 +1,74 @@
+package util.render
+
+import com.mojang.blaze3d.pipeline.RenderPipeline
+import com.mojang.blaze3d.platform.DepthTestFunction
+import com.mojang.blaze3d.vertex.VertexFormat.DrawMode
+import java.util.function.Function
+import net.minecraft.client.gl.RenderPipelines
+import net.minecraft.client.render.RenderLayer
+import net.minecraft.client.render.RenderPhase
+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.Firmament
+
+object CustomRenderPipelines {
+ val GUI_TEXTURED_NO_DEPTH_TRIS =
+ RenderPipeline.builder(RenderPipelines.POSITION_TEX_COLOR_SNIPPET)
+ .withVertexFormat(VertexFormats.POSITION_TEXTURE_COLOR, DrawMode.TRIANGLES)
+ .withLocation(Firmament.identifier("gui_textured_overlay_tris"))
+ .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST)
+ .withCull(false)
+ .withDepthWrite(false)
+ .build()
+ val OMNIPRESENT_LINES = RenderPipeline
+ .builder(RenderPipelines.RENDERTYPE_LINES_SNIPPET)
+ .withLocation(Firmament.identifier("lines"))
+ .withDepthWrite(false)
+ .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST)
+ .build()
+ val COLORED_OMNIPRESENT_QUADS =
+ RenderPipeline.builder(RenderPipelines.MATRICES_COLOR_SNIPPET)// TODO: split this up to support better transparent ordering.
+ .withLocation(Firmament.identifier("colored_omnipresent_quads"))
+ .withVertexShader("core/position_color")
+ .withFragmentShader("core/position_color")
+ .withVertexFormat(VertexFormats.POSITION_COLOR, DrawMode.QUADS)
+ .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST)
+ .withCull(false)
+ .withDepthWrite(false)
+ .build()
+}
+
+object CustomRenderLayers {
+
+
+ inline fun memoizeTextured(crossinline func: (Identifier) -> RenderLayer) = memoize(func)
+ inline fun <T, R> memoize(crossinline func: (T) -> R): Function<T, R> {
+ return Util.memoize { it: T -> func(it) }
+ }
+
+ val GUI_TEXTURED_NO_DEPTH_TRIS = memoizeTextured { texture ->
+ RenderLayer.of("firmament_gui_textured_overlay_tris",
+ RenderLayer.DEFAULT_BUFFER_SIZE,
+ CustomRenderPipelines.GUI_TEXTURED_NO_DEPTH_TRIS,
+ RenderLayer.MultiPhaseParameters.builder().texture(
+ RenderPhase.Texture(texture, TriState.DEFAULT, false))
+ .build(false))
+ }
+ val LINES = RenderLayer.of(
+ "firmament_lines",
+ RenderLayer.DEFAULT_BUFFER_SIZE,
+ CustomRenderPipelines.OMNIPRESENT_LINES,
+ RenderLayer.MultiPhaseParameters.builder() // TODO: accept linewidth here
+ .build(false)
+ )
+ val COLORED_QUADS = RenderLayer.of(
+ "firmament_quads",
+ RenderLayer.DEFAULT_BUFFER_SIZE,
+ CustomRenderPipelines.COLORED_OMNIPRESENT_QUADS,
+ RenderLayer.MultiPhaseParameters.builder()
+ .lightmap(RenderPhase.DISABLE_LIGHTMAP)
+ .build(false)
+ )
+}
diff --git a/src/main/kotlin/util/render/DrawContextExt.kt b/src/main/kotlin/util/render/DrawContextExt.kt
index a143d4d..fa92cd7 100644
--- a/src/main/kotlin/util/render/DrawContextExt.kt
+++ b/src/main/kotlin/util/render/DrawContextExt.kt
@@ -1,52 +1,24 @@
package moe.nea.firmament.util.render
+import com.mojang.blaze3d.pipeline.RenderPipeline
+import com.mojang.blaze3d.platform.DepthTestFunction
import com.mojang.blaze3d.systems.RenderSystem
+import com.mojang.blaze3d.vertex.VertexFormat.DrawMode
import me.shedaniel.math.Color
import org.joml.Matrix4f
+import util.render.CustomRenderLayers
+import net.minecraft.client.gl.RenderPipelines
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.VertexFormat.DrawMode
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.Firmament
import moe.nea.firmament.util.MC
fun DrawContext.isUntranslatedGuiDrawContext(): Boolean {
return (matrices.peek().positionMatrix.properties() and Matrix4f.PROPERTY_IDENTITY.toInt()) != 0
}
-object GuiRenderLayers {
- val GUI_TEXTURED_NO_DEPTH = Util.memoize<Identifier, RenderLayer> { texture: Identifier ->
- RenderLayer.of("firmament_gui_textured_no_depth",
- VertexFormats.POSITION_TEXTURE_COLOR,
- DrawMode.QUADS,
- DEFAULT_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))
- }
- val GUI_TEXTURED_TRIS = Util.memoize { texture: Identifier ->
- RenderLayer.of("firmament_gui_textured_overlay_tris",
- VertexFormats.POSITION_TEXTURE_COLOR,
- DrawMode.TRIANGLES,
- DEFAULT_BUFFER_SIZE,
- MultiPhaseParameters.builder()
- .texture(RenderPhase.Texture(texture, TriState.DEFAULT, false))
- .program(RenderPhase.POSITION_TEXTURE_COLOR_PROGRAM)
- .transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY)
- .depthTest(RenderPhase.ALWAYS_DEPTH_TEST)
- .writeMaskState(RenderPhase.COLOR_MASK)
- .build(false))
- }
-}
-
@Deprecated("Use the other drawGuiTexture")
fun DrawContext.drawGuiTexture(
x: Int, y: Int, z: Int, width: Int, height: Int, sprite: Identifier
@@ -91,7 +63,7 @@ fun DrawContext.drawLine(fromX: Int, fromY: Int, toX: Int, toY: Int, color: Colo
}
RenderSystem.lineWidth(MC.window.scaleFactor.toFloat())
draw { vertexConsumers ->
- val buf = vertexConsumers.getBuffer(RenderInWorldContext.RenderLayers.LINES)
+ val buf = vertexConsumers.getBuffer(CustomRenderLayers.LINES)
buf.vertex(fromX.toFloat(), fromY.toFloat(), 0F).color(color.color)
.normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F)
buf.vertex(toX.toFloat(), toY.toFloat(), 0F).color(color.color)
diff --git a/src/main/kotlin/util/render/FacingThePlayerContext.kt b/src/main/kotlin/util/render/FacingThePlayerContext.kt
index daa8da9..670beb6 100644
--- a/src/main/kotlin/util/render/FacingThePlayerContext.kt
+++ b/src/main/kotlin/util/render/FacingThePlayerContext.kt
@@ -1,18 +1,12 @@
package moe.nea.firmament.util.render
-import com.mojang.blaze3d.systems.RenderSystem
import io.github.notenoughupdates.moulconfig.platform.next
import org.joml.Matrix4f
import net.minecraft.client.font.TextRenderer
-import net.minecraft.client.render.BufferRenderer
-import net.minecraft.client.render.GameRenderer
import net.minecraft.client.render.LightmapTextureManager
import net.minecraft.client.render.RenderLayer
-import net.minecraft.client.render.Tessellator
import net.minecraft.client.render.VertexConsumer
-import net.minecraft.client.render.VertexFormat
-import net.minecraft.client.render.VertexFormats
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
diff --git a/src/main/kotlin/util/render/FirmamentShaders.kt b/src/main/kotlin/util/render/FirmamentShaders.kt
index ba67dbb..cc6cd49 100644
--- a/src/main/kotlin/util/render/FirmamentShaders.kt
+++ b/src/main/kotlin/util/render/FirmamentShaders.kt
@@ -1,9 +1,10 @@
package moe.nea.firmament.util.render
+import com.mojang.blaze3d.vertex.VertexFormat
+import net.minecraft.client.gl.CompiledShader
import net.minecraft.client.gl.Defines
-import net.minecraft.client.gl.ShaderProgramKey
+import net.minecraft.client.gl.ShaderProgram
import net.minecraft.client.render.RenderPhase
-import net.minecraft.client.render.VertexFormat
import net.minecraft.client.render.VertexFormats
import moe.nea.firmament.Firmament
import moe.nea.firmament.annotations.Subscribe
@@ -11,20 +12,9 @@ import moe.nea.firmament.events.DebugInstantiateEvent
import moe.nea.firmament.util.MC
object FirmamentShaders {
- val shaders = mutableListOf<ShaderProgramKey>()
-
- private fun shader(name: String, format: VertexFormat, defines: Defines): ShaderProgramKey {
- val key = ShaderProgramKey(Firmament.identifier(name), format, defines)
- shaders.add(key)
- return key
- }
-
- val LINES = RenderPhase.ShaderProgram(shader("core/rendertype_lines", VertexFormats.LINES, Defines.EMPTY))
@Subscribe
fun debugLoad(event: DebugInstantiateEvent) {
- shaders.forEach {
- MC.instance.shaderLoader.getOrCreateProgram(it)
- }
+ // TODO: do i still need to work with shaders like this?
}
}
diff --git a/src/main/kotlin/util/render/RenderCircleProgress.kt b/src/main/kotlin/util/render/RenderCircleProgress.kt
index 805633c..d759033 100644
--- a/src/main/kotlin/util/render/RenderCircleProgress.kt
+++ b/src/main/kotlin/util/render/RenderCircleProgress.kt
@@ -1,18 +1,12 @@
package moe.nea.firmament.util.render
-import com.mojang.blaze3d.systems.RenderSystem
import io.github.notenoughupdates.moulconfig.platform.next
import org.joml.Matrix4f
import org.joml.Vector2f
+import util.render.CustomRenderLayers
import kotlin.math.atan2
import kotlin.math.tan
import net.minecraft.client.gui.DrawContext
-import net.minecraft.client.render.BufferRenderer
-import net.minecraft.client.render.RenderLayer
-import net.minecraft.client.render.RenderPhase
-import net.minecraft.client.render.Tessellator
-import net.minecraft.client.render.VertexFormat.DrawMode
-import net.minecraft.client.render.VertexFormats
import net.minecraft.util.Identifier
object RenderCircleProgress {
@@ -26,9 +20,8 @@ object RenderCircleProgress {
v1: Float,
v2: Float,
) {
- RenderSystem.enableBlend()
drawContext.draw {
- val bufferBuilder = it.getBuffer(GuiRenderLayers.GUI_TEXTURED_TRIS.apply(texture))
+ val bufferBuilder = it.getBuffer(CustomRenderLayers.GUI_TEXTURED_NO_DEPTH_TRIS.apply(texture))
val matrix: Matrix4f = drawContext.matrices.peek().positionMatrix
val corners = listOf(
@@ -86,7 +79,6 @@ object RenderCircleProgress {
.next()
}
}
- RenderSystem.disableBlend()
}
diff --git a/src/main/kotlin/util/render/RenderInWorldContext.kt b/src/main/kotlin/util/render/RenderInWorldContext.kt
index bb58200..98b10ca 100644
--- a/src/main/kotlin/util/render/RenderInWorldContext.kt
+++ b/src/main/kotlin/util/render/RenderInWorldContext.kt
@@ -5,15 +5,12 @@ import io.github.notenoughupdates.moulconfig.platform.next
import java.lang.Math.pow
import org.joml.Matrix4f
import org.joml.Vector3f
-import net.minecraft.client.gl.VertexBuffer
+import util.render.CustomRenderLayers
import net.minecraft.client.render.Camera
import net.minecraft.client.render.RenderLayer
-import net.minecraft.client.render.RenderPhase
import net.minecraft.client.render.RenderTickCounter
-import net.minecraft.client.render.Tessellator
import net.minecraft.client.render.VertexConsumer
import net.minecraft.client.render.VertexConsumerProvider
-import net.minecraft.client.render.VertexFormat
import net.minecraft.client.render.VertexFormats
import net.minecraft.client.texture.Sprite
import net.minecraft.client.util.math.MatrixStack
@@ -27,47 +24,12 @@ import moe.nea.firmament.util.MC
@RenderContextDSL
class RenderInWorldContext private constructor(
- private val tesselator: Tessellator,
val matrixStack: MatrixStack,
private val camera: Camera,
private val tickCounter: RenderTickCounter,
val vertexConsumers: VertexConsumerProvider.Immediate,
) {
- object RenderLayers {
- val TRANSLUCENT_TRIS = RenderLayer.of("firmament_translucent_tris",
- VertexFormats.POSITION_COLOR,
- VertexFormat.DrawMode.TRIANGLES,
- RenderLayer.CUTOUT_BUFFER_SIZE,
- false, true,
- RenderLayer.MultiPhaseParameters.builder()
- .depthTest(RenderPhase.ALWAYS_DEPTH_TEST)
- .transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY)
- .program(RenderPhase.POSITION_COLOR_PROGRAM)
- .build(false))
- val LINES = RenderLayer.of("firmament_rendertype_lines",
- VertexFormats.LINES,
- VertexFormat.DrawMode.LINES,
- RenderLayer.CUTOUT_BUFFER_SIZE,
- false, false, // do we need translucent? i dont think so
- RenderLayer.MultiPhaseParameters.builder()
- .depthTest(RenderPhase.ALWAYS_DEPTH_TEST)
- .program(FirmamentShaders.LINES)
- .build(false)
- )
- val COLORED_QUADS = RenderLayer.of(
- "firmament_quads",
- VertexFormats.POSITION_COLOR,
- VertexFormat.DrawMode.QUADS,
- RenderLayer.CUTOUT_BUFFER_SIZE,
- false, true,
- RenderLayer.MultiPhaseParameters.builder()
- .depthTest(RenderPhase.ALWAYS_DEPTH_TEST)
- .program(RenderPhase.POSITION_COLOR_PROGRAM)
- .transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY)
- .build(false)
- )
- }
@Deprecated("stateful color management is no longer a thing")
fun color(color: me.shedaniel.math.Color) {
@@ -82,7 +44,7 @@ class RenderInWorldContext private constructor(
fun block(blockPos: BlockPos, color: Int) {
matrixStack.push()
matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat())
- buildCube(matrixStack.peek().positionMatrix, vertexConsumers.getBuffer(RenderLayers.COLORED_QUADS), color)
+ buildCube(matrixStack.peek().positionMatrix, vertexConsumers.getBuffer(CustomRenderLayers.COLORED_QUADS), color)
matrixStack.pop()
}
@@ -155,7 +117,7 @@ class RenderInWorldContext private constructor(
matrixStack.translate(vec3d.x, vec3d.y, vec3d.z)
matrixStack.scale(size, size, size)
matrixStack.translate(-.5, -.5, -.5)
- buildCube(matrixStack.peek().positionMatrix, vertexConsumers.getBuffer(RenderLayers.COLORED_QUADS), color)
+ buildCube(matrixStack.peek().positionMatrix, vertexConsumers.getBuffer(CustomRenderLayers.COLORED_QUADS), color)
matrixStack.pop()
vertexConsumers.draw()
}
@@ -182,8 +144,7 @@ class RenderInWorldContext private constructor(
fun line(points: List<Vec3d>, lineWidth: Float = 10F) {
RenderSystem.lineWidth(lineWidth)
- // TODO: replace with renderlayers
- val buffer = tesselator.begin(VertexFormat.DrawMode.LINES, VertexFormats.LINES)
+ val buffer = vertexConsumers.getBuffer(CustomRenderLayers.LINES)
val matrix = matrixStack.peek()
var lastNormal: Vector3f? = null
@@ -203,7 +164,6 @@ class RenderInWorldContext private constructor(
.next()
}
- RenderLayers.LINES.draw(buffer.end())
}
// TODO: put the favourite icons in front of items again
@@ -281,16 +241,15 @@ class RenderInWorldContext private constructor(
fun renderInWorld(event: WorldRenderLastEvent, block: RenderInWorldContext. () -> Unit) {
// TODO: there should be *no more global state*. the only thing we should be doing is render layers. that includes settings like culling, blending, shader color, and depth testing
// For now i will let these functions remain, but this needs to go before i do a full (non-beta) release
- RenderSystem.disableDepthTest()
- RenderSystem.enableBlend()
- RenderSystem.defaultBlendFunc()
- RenderSystem.disableCull()
+// RenderSystem.disableDepthTest()
+// RenderSystem.enableBlend()
+// RenderSystem.defaultBlendFunc()
+// RenderSystem.disableCull()
event.matrices.push()
event.matrices.translate(-event.camera.pos.x, -event.camera.pos.y, -event.camera.pos.z)
val ctx = RenderInWorldContext(
- RenderSystem.renderThreadTesselator(),
event.matrices,
event.camera,
event.tickCounter,
@@ -302,10 +261,6 @@ class RenderInWorldContext private constructor(
event.matrices.pop()
event.vertexConsumers.draw()
RenderSystem.setShaderColor(1F, 1F, 1F, 1F)
- VertexBuffer.unbind()
- RenderSystem.enableDepthTest()
- RenderSystem.enableCull()
- RenderSystem.disableBlend()
}
}
}
diff --git a/src/main/kotlin/util/render/TintedOverlayTexture.kt b/src/main/kotlin/util/render/TintedOverlayTexture.kt
index a02eccc..0677846 100644
--- a/src/main/kotlin/util/render/TintedOverlayTexture.kt
+++ b/src/main/kotlin/util/render/TintedOverlayTexture.kt
@@ -1,7 +1,5 @@
package moe.nea.firmament.util.render
-import com.mojang.blaze3d.platform.GlConst
-import com.mojang.blaze3d.systems.RenderSystem
import me.shedaniel.math.Color
import net.minecraft.client.render.OverlayTexture
import net.minecraft.util.math.ColorHelper
@@ -29,16 +27,9 @@ class TintedOverlayTexture : OverlayTexture() {
}
}
- RenderSystem.activeTexture(GlConst.GL_TEXTURE1)
- texture.bindTexture()
texture.setFilter(false, false)
texture.setClamp(true)
- image.upload(0,
- 0, 0,
- 0, 0,
- image.width, image.height,
- false)
- RenderSystem.activeTexture(GlConst.GL_TEXTURE0)
+ texture.upload()
return this
}
}
diff --git a/src/main/kotlin/util/skyblock/SackUtil.kt b/src/main/kotlin/util/skyblock/SackUtil.kt
index fd67c44..c46542e 100644
--- a/src/main/kotlin/util/skyblock/SackUtil.kt
+++ b/src/main/kotlin/util/skyblock/SackUtil.kt
@@ -93,7 +93,7 @@ object SackUtil {
fun updateFromHoverText(text: Text) {
text.siblings.forEach(::updateFromHoverText)
- val hoverText = text.style.hoverEvent?.getValue(HoverEvent.Action.SHOW_TEXT) ?: return
+ val hoverText = (text.style.hoverEvent as? HoverEvent.ShowText)?.value ?: return
val cleanedText = hoverText.unformattedString
if (cleanedText.startsWith("Added items:\n")) {
if (!foundAdded) {
diff --git a/src/main/kotlin/util/textutil.kt b/src/main/kotlin/util/textutil.kt
index 806f61e..2458891 100644
--- a/src/main/kotlin/util/textutil.kt
+++ b/src/main/kotlin/util/textutil.kt
@@ -127,13 +127,13 @@ fun MutableText.darkGrey() = withColor(Formatting.DARK_GRAY)
fun MutableText.red() = withColor(Formatting.RED)
fun MutableText.white() = withColor(Formatting.WHITE)
fun MutableText.bold(): MutableText = styled { it.withBold(true) }
-fun MutableText.hover(text: Text): MutableText = styled {it.withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, text))}
+fun MutableText.hover(text: Text): MutableText = styled {it.withHoverEvent(HoverEvent.ShowText(text))}
fun MutableText.clickCommand(command: String): MutableText {
require(command.startsWith("/"))
return this.styled {
- it.withClickEvent(ClickEvent(ClickEvent.Action.RUN_COMMAND, command))
+ it.withClickEvent(ClickEvent.RunCommand(command))
}
}
@@ -164,4 +164,14 @@ fun Text.transformEachRecursively(function: (Text) -> Text): Text {
fun tr(key: String, default: String): MutableText = error("Compiler plugin did not run.")
fun trResolved(key: String, vararg args: Any): MutableText = Text.stringifiedTranslatable(key, *args)
+fun titleCase(str: String): String {
+ return str
+ .lowercase()
+ .replace("_", " ")
+ .split(" ")
+ .joinToString(" ") { word ->
+ word.replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }
+ }
+}
+
diff --git a/src/main/kotlin/util/uuid.kt b/src/main/kotlin/util/uuid.kt
index cccfdd2..14aa83d 100644
--- a/src/main/kotlin/util/uuid.kt
+++ b/src/main/kotlin/util/uuid.kt
@@ -3,6 +3,12 @@ package moe.nea.firmament.util
import java.math.BigInteger
import java.util.UUID
+fun parsePotentiallyDashlessUUID(unknownFormattedUUID: String): UUID {
+ if ("-" in unknownFormattedUUID)
+ return UUID.fromString(unknownFormattedUUID)
+ return parseDashlessUUID(unknownFormattedUUID)
+}
+
fun parseDashlessUUID(dashlessUuid: String): UUID {
val most = BigInteger(dashlessUuid.substring(0, 16), 16)
val least = BigInteger(dashlessUuid.substring(16, 32), 16)