aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/features/misc
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/features/misc')
-rw-r--r--src/main/kotlin/features/misc/CustomCapes.kt192
-rw-r--r--src/main/kotlin/features/misc/Devs.kt38
-rw-r--r--src/main/kotlin/features/misc/Hud.kt77
-rw-r--r--src/main/kotlin/features/misc/LicenseViewer.kt128
4 files changed, 435 insertions, 0 deletions
diff --git a/src/main/kotlin/features/misc/CustomCapes.kt b/src/main/kotlin/features/misc/CustomCapes.kt
new file mode 100644
index 0000000..dc5187a
--- /dev/null
+++ b/src/main/kotlin/features/misc/CustomCapes.kt
@@ -0,0 +1,192 @@
+package moe.nea.firmament.features.misc
+
+import com.mojang.blaze3d.systems.RenderSystem
+import java.util.OptionalDouble
+import java.util.OptionalInt
+import util.render.CustomRenderPipelines
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+import net.minecraft.client.network.AbstractClientPlayerEntity
+import net.minecraft.client.render.BufferBuilder
+import net.minecraft.client.render.RenderLayer
+import net.minecraft.client.render.VertexConsumer
+import net.minecraft.client.render.VertexConsumerProvider
+import net.minecraft.client.render.entity.state.PlayerEntityRenderState
+import net.minecraft.client.util.BufferAllocator
+import net.minecraft.client.util.SkinTextures
+import net.minecraft.util.Identifier
+import moe.nea.firmament.Firmament
+import moe.nea.firmament.features.FirmamentFeature
+import moe.nea.firmament.gui.config.ManagedConfig
+import moe.nea.firmament.util.MC
+import moe.nea.firmament.util.TimeMark
+
+object CustomCapes : FirmamentFeature {
+ override val identifier: String
+ get() = "developer-capes"
+
+ object TConfig : ManagedConfig(identifier, Category.DEV) {
+ val showCapes by toggle("show-cape") { true }
+ }
+
+ override val config: ManagedConfig
+ get() = TConfig
+
+ interface CustomCapeRenderer {
+ fun replaceRender(
+ renderLayer: RenderLayer,
+ vertexConsumerProvider: VertexConsumerProvider,
+ model: (VertexConsumer) -> Unit
+ )
+ }
+
+ data class TexturedCapeRenderer(
+ val location: Identifier
+ ) : CustomCapeRenderer {
+ override fun replaceRender(
+ renderLayer: RenderLayer,
+ vertexConsumerProvider: VertexConsumerProvider,
+ model: (VertexConsumer) -> Unit
+ ) {
+ model(vertexConsumerProvider.getBuffer(RenderLayer.getEntitySolid(location)))
+ }
+ }
+
+ data class ParallaxedHighlightCapeRenderer(
+ val template: Identifier,
+ val background: Identifier,
+ val overlay: Identifier,
+ val animationSpeed: Duration,
+ ) : CustomCapeRenderer {
+ override fun replaceRender(
+ renderLayer: RenderLayer,
+ vertexConsumerProvider: VertexConsumerProvider,
+ model: (VertexConsumer) -> Unit
+ ) {
+ BufferAllocator(2048).use { allocator ->
+ val bufferBuilder = BufferBuilder(allocator, renderLayer.drawMode, renderLayer.vertexFormat)
+ model(bufferBuilder)
+ bufferBuilder.end().use { buffer ->
+ val commandEncoder = RenderSystem.getDevice().createCommandEncoder()
+ val vertexBuffer = renderLayer.vertexFormat.uploadImmediateVertexBuffer(buffer.buffer)
+ val indexBufferConstructor = RenderSystem.getSequentialBuffer(renderLayer.drawMode)
+ val indexBuffer = indexBufferConstructor.getIndexBuffer(buffer.drawParameters.indexCount)
+ val templateTexture = MC.textureManager.getTexture(template)
+ val backgroundTexture = MC.textureManager.getTexture(background)
+ val foregroundTexture = MC.textureManager.getTexture(overlay)
+ commandEncoder.createRenderPass(
+ MC.instance.framebuffer.colorAttachment,
+ OptionalInt.empty(),
+ MC.instance.framebuffer.depthAttachment,
+ OptionalDouble.empty(),
+ ).use { renderPass ->
+ // TODO: account for lighting
+ renderPass.setPipeline(CustomRenderPipelines.PARALLAX_CAPE_SHADER)
+ renderPass.bindSampler("Sampler0", templateTexture.glTexture)
+ renderPass.bindSampler("Sampler1", backgroundTexture.glTexture)
+ renderPass.bindSampler("Sampler3", foregroundTexture.glTexture)
+ val animationValue = (startTime.passedTime() / animationSpeed).mod(1F)
+ renderPass.setUniform("Animation", animationValue.toFloat())
+ renderPass.setIndexBuffer(indexBuffer, indexBufferConstructor.indexType)
+ renderPass.setVertexBuffer(0, vertexBuffer)
+ renderPass.drawIndexed(0, buffer.drawParameters.indexCount)
+ }
+ }
+ }
+ }
+ }
+
+ interface CapeStorage {
+ companion object {
+ @JvmStatic
+ fun cast(playerEntityRenderState: PlayerEntityRenderState) =
+ playerEntityRenderState as CapeStorage
+
+ }
+
+ var cape_firmament: CustomCape?
+ }
+
+ data class CustomCape(
+ val id: String,
+ val label: String,
+ val render: CustomCapeRenderer,
+ )
+
+ enum class AllCapes(val label: String, val render: CustomCapeRenderer) {
+ FIRMAMENT_ANIMATED(
+ "Animated Firmament", ParallaxedHighlightCapeRenderer(
+ Firmament.identifier("textures/cape/parallax_template.png"),
+ Firmament.identifier("textures/cape/parallax_background.png"),
+ Firmament.identifier("textures/cape/firmament_star.png"),
+ 110.seconds
+ )
+ ),
+
+ FURFSKY_STATIC(
+ "FurfSky",
+ TexturedCapeRenderer(Firmament.identifier("textures/cape/fsr_static.png"))
+ ),
+
+ FIRMAMENT_STATIC(
+ "Firmament",
+ TexturedCapeRenderer(Firmament.identifier("textures/cape/firm_static.png"))
+ )
+ ;
+
+ val cape = CustomCape(name, label, render)
+ }
+
+ val byId = AllCapes.entries.associateBy { it.cape.id }
+ val byUuid =
+ listOf(
+ listOf(
+ Devs.nea to AllCapes.FIRMAMENT_ANIMATED,
+ Devs.kath to AllCapes.FIRMAMENT_STATIC,
+ Devs.jani to AllCapes.FIRMAMENT_ANIMATED,
+ ),
+ Devs.FurfSky.all.map { it to AllCapes.FURFSKY_STATIC },
+ ).flatten().flatMap { (dev, cape) -> dev.uuids.map { it to cape.cape } }.toMap()
+
+ @JvmStatic
+ fun render(
+ playerEntityRenderState: PlayerEntityRenderState,
+ vertexConsumer: VertexConsumer,
+ renderLayer: RenderLayer,
+ vertexConsumerProvider: VertexConsumerProvider,
+ model: (VertexConsumer) -> Unit
+ ) {
+ val capeStorage = CapeStorage.cast(playerEntityRenderState)
+ val firmCape = capeStorage.cape_firmament
+ if (firmCape != null) {
+ firmCape.render.replaceRender(renderLayer, vertexConsumerProvider, model)
+ } else {
+ model(vertexConsumer)
+ }
+ }
+
+ @JvmStatic
+ fun addCapeData(
+ player: AbstractClientPlayerEntity,
+ playerEntityRenderState: PlayerEntityRenderState
+ ) {
+ val cape = if (TConfig.showCapes) byUuid[player.uuid] else null
+ val capeStorage = CapeStorage.cast(playerEntityRenderState)
+ if (cape == null) {
+ capeStorage.cape_firmament = null
+ } else {
+ capeStorage.cape_firmament = cape
+ playerEntityRenderState.skinTextures = SkinTextures(
+ playerEntityRenderState.skinTextures.texture,
+ playerEntityRenderState.skinTextures.textureUrl,
+ Firmament.identifier("placeholder/fake_cape"),
+ playerEntityRenderState.skinTextures.elytraTexture,
+ playerEntityRenderState.skinTextures.model,
+ playerEntityRenderState.skinTextures.secure,
+ )
+ playerEntityRenderState.capeVisible = true
+ }
+ }
+
+ val startTime = TimeMark.now()
+}
diff --git a/src/main/kotlin/features/misc/Devs.kt b/src/main/kotlin/features/misc/Devs.kt
new file mode 100644
index 0000000..1f16400
--- /dev/null
+++ b/src/main/kotlin/features/misc/Devs.kt
@@ -0,0 +1,38 @@
+package moe.nea.firmament.features.misc
+
+import java.util.UUID
+
+object Devs {
+ data class Dev(
+ val uuids: List<UUID>,
+ ) {
+ constructor(vararg uuid: UUID) : this(uuid.toList())
+ constructor(vararg uuid: String) : this(uuid.map { UUID.fromString(it) })
+ }
+
+ val nea = Dev("d3cb85e2-3075-48a1-b213-a9bfb62360c1", "842204e6-6880-487b-ae5a-0595394f9948")
+ val kath = Dev("add71246-c46e-455c-8345-c129ea6f146c", "b491990d-53fd-4c5f-a61e-19d58cc7eddf")
+ val jani = Dev("8a9f1841-48e9-48ed-b14f-76a124e6c9df")
+
+ object FurfSky {
+ val smolegit = Dev("02b38b96-eb19-405a-b319-d6bc00b26ab3")
+ val itsCen = Dev("ada70b5a-ac37-49d2-b18c-1351672f8051")
+ val webster = Dev("02166f1b-9e8d-4e48-9e18-ea7a4499492d")
+ val vrachel = Dev("22e98637-ba97-4b6b-a84f-fb57a461ce43")
+ val cunuduh = Dev("2a15e3b3-c46e-4718-b907-166e1ab2efdc")
+ val eiiies = Dev("2ae162f2-81a7-4f91-a4b2-104e78a0a7e1")
+ val june = Dev("2584a4e3-f917-4493-8ced-618391f3b44f")
+ val denasu = Dev("313cbd25-8ade-4e41-845c-5cab555a30c9")
+ val libyKiwii = Dev("4265c52e-bd6f-4d3c-9cf6-bdfc8fb58023")
+ val madeleaan = Dev("bcb119a3-6000-4324-bda1-744f00c44b31")
+ val turtleSP = Dev("f1ca1934-a582-4723-8283-89921d008657")
+ val papayamm = Dev("ae0eea30-f6a2-40fe-ac17-9c80b3423409")
+ val persuasiveViksy = Dev("ba7ac144-28e0-4f55-a108-1a72fe744c9e")
+ val all = listOf(
+ smolegit, itsCen, webster, vrachel, cunuduh, eiiies,
+ june, denasu, libyKiwii, madeleaan, turtleSP, papayamm,
+ persuasiveViksy
+ )
+ }
+
+}
diff --git a/src/main/kotlin/features/misc/Hud.kt b/src/main/kotlin/features/misc/Hud.kt
new file mode 100644
index 0000000..9661fc5
--- /dev/null
+++ b/src/main/kotlin/features/misc/Hud.kt
@@ -0,0 +1,77 @@
+package moe.nea.firmament.features.misc
+
+import moe.nea.firmament.annotations.Subscribe
+import moe.nea.firmament.events.HudRenderEvent
+import moe.nea.firmament.features.FirmamentFeature
+import moe.nea.firmament.gui.config.ManagedConfig
+import moe.nea.firmament.util.MC
+import moe.nea.firmament.util.tr
+import moe.nea.jarvis.api.Point
+import net.minecraft.client.network.PlayerListEntry
+import net.minecraft.text.Text
+
+object Hud : FirmamentFeature {
+ override val identifier: String
+ get() = "hud"
+
+ object TConfig : ManagedConfig(identifier, Category.MISC) {
+ var dayCount by toggle("day-count") { false }
+ val dayCountHud by position("day-count-hud", 80, 10) { Point(0.5, 0.8) }
+ var fpsCount by toggle("fps-count") { false }
+ val fpsCountHud by position("fps-count-hud", 80, 10) { Point(0.5, 0.9) }
+ var pingCount by toggle("ping-count") { false }
+ val pingCountHud by position("ping-count-hud", 80, 10) { Point(0.5, 1.0) }
+ }
+
+ override val config: ManagedConfig
+ get() = TConfig
+
+ @Subscribe
+ fun onRenderHud(it: HudRenderEvent) {
+ if (TConfig.dayCount) {
+ it.context.matrices.push()
+ TConfig.dayCountHud.applyTransformations(it.context.matrices)
+ val day = (MC.world?.timeOfDay ?: 0L) / 24000
+ it.context.drawText(
+ MC.font,
+ Text.literal(String.format(tr("firmament.config.hud.day-count-hud.display", "Day: %s").string, day)),
+ 36,
+ MC.font.fontHeight,
+ -1,
+ true
+ )
+ it.context.matrices.pop()
+ }
+
+ if (TConfig.fpsCount) {
+ it.context.matrices.push()
+ TConfig.fpsCountHud.applyTransformations(it.context.matrices)
+ it.context.drawText(
+ MC.font, Text.literal(
+ String.format(
+ tr("firmament.config.hud.fps-count-hud.display", "FPS: %s").string, MC.instance.currentFps
+ )
+ ), 36, MC.font.fontHeight, -1, true
+ )
+ it.context.matrices.pop()
+ }
+
+ if (TConfig.pingCount) {
+ it.context.matrices.push()
+ TConfig.pingCountHud.applyTransformations(it.context.matrices)
+ val ping = MC.player?.let {
+ val entry: PlayerListEntry? = MC.networkHandler?.getPlayerListEntry(it.uuid)
+ entry?.latency ?: -1
+ } ?: -1
+ it.context.drawText(
+ MC.font, Text.literal(
+ String.format(
+ tr("firmament.config.hud.ping-count-hud.display", "Ping: %s ms").string, ping
+ )
+ ), 36, MC.font.fontHeight, -1, true
+ )
+
+ it.context.matrices.pop()
+ }
+ }
+}
diff --git a/src/main/kotlin/features/misc/LicenseViewer.kt b/src/main/kotlin/features/misc/LicenseViewer.kt
new file mode 100644
index 0000000..4219177
--- /dev/null
+++ b/src/main/kotlin/features/misc/LicenseViewer.kt
@@ -0,0 +1,128 @@
+package moe.nea.firmament.features.misc
+
+import io.github.notenoughupdates.moulconfig.observer.ObservableList
+import io.github.notenoughupdates.moulconfig.xml.Bind
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.Transient
+import kotlinx.serialization.json.decodeFromStream
+import moe.nea.firmament.Firmament
+import moe.nea.firmament.annotations.Subscribe
+import moe.nea.firmament.commands.thenExecute
+import moe.nea.firmament.events.CommandEvent
+import moe.nea.firmament.util.ErrorUtil
+import moe.nea.firmament.util.MC
+import moe.nea.firmament.util.MoulConfigUtils
+import moe.nea.firmament.util.ScreenUtil
+import moe.nea.firmament.util.tr
+
+object LicenseViewer {
+ @Serializable
+ data class Software(
+ val licenses: List<License> = listOf(),
+ val webPresence: String? = null,
+ val projectName: String,
+ val projectDescription: String? = null,
+ val developers: List<Developer> = listOf(),
+ ) {
+
+ @Bind
+ fun hasWebPresence() = webPresence != null
+
+ @Bind
+ fun webPresence() = webPresence ?: "<no web presence>"
+ @Bind
+ fun open() {
+ MC.openUrl(webPresence ?: return)
+ }
+
+ @Bind
+ fun projectName() = projectName
+
+ @Bind
+ fun projectDescription() = projectDescription ?: "<no project description>"
+
+ @get:Bind("developers")
+ @Transient
+ val developersO = ObservableList(developers)
+
+ @get:Bind("licenses")
+ @Transient
+ val licenses0 = ObservableList(licenses)
+ }
+
+ @Serializable
+ data class Developer(
+ @get:Bind("name") val name: String,
+ val webPresence: String? = null
+ ) {
+
+ @Bind
+ fun open() {
+ MC.openUrl(webPresence ?: return)
+ }
+
+ @Bind
+ fun hasWebPresence() = webPresence != null
+
+ @Bind
+ fun webPresence() = webPresence ?: "<no web presence>"
+ }
+
+ @Serializable
+ data class License(
+ @get:Bind("name") val licenseName: String,
+ val licenseUrl: String? = null
+ ) {
+ @Bind
+ fun open() {
+ MC.openUrl(licenseUrl ?: return)
+ }
+
+ @Bind
+ fun hasUrl() = licenseUrl != null
+
+ @Bind
+ fun url() = licenseUrl ?: "<no link to license text>"
+ }
+
+ data class LicenseList(
+ val softwares: List<Software>
+ ) {
+ @get:Bind("softwares")
+ val obs = ObservableList(softwares)
+ }
+
+ @OptIn(ExperimentalSerializationApi::class)
+ val licenses: LicenseList? = ErrorUtil.catch("Could not load licenses") {
+ Firmament.json.decodeFromStream<List<Software>?>(
+ javaClass.getResourceAsStream("/LICENSES-FIRMAMENT.json") ?: error("Could not find LICENSES-FIRMAMENT.json")
+ )?.let { LicenseList(it) }
+ }.orNull()
+
+ fun showLicenses() {
+ ErrorUtil.catch("Could not display licenses") {
+ ScreenUtil.setScreenLater(
+ MoulConfigUtils.loadScreen(
+ "license_viewer/index", licenses!!, null
+ )
+ )
+ }.or {
+ MC.sendChat(
+ tr(
+ "firmament.licenses.notfound",
+ "Could not load licenses. Please check the Firmament source code for information directly."
+ )
+ )
+ }
+ }
+
+ @Subscribe
+ fun onSubcommand(event: CommandEvent.SubCommand) {
+ event.subcommand("licenses") {
+ thenExecute {
+ showLicenses()
+ }
+ }
+ }
+}