diff options
Diffstat (limited to 'src/main/kotlin/features/misc')
| -rw-r--r-- | src/main/kotlin/features/misc/CustomCapes.kt | 172 | ||||
| -rw-r--r-- | src/main/kotlin/features/misc/Devs.kt | 42 | ||||
| -rw-r--r-- | src/main/kotlin/features/misc/Hud.kt | 75 | ||||
| -rw-r--r-- | src/main/kotlin/features/misc/LicenseViewer.kt | 136 | ||||
| -rw-r--r-- | src/main/kotlin/features/misc/ModAnnouncer.kt | 80 |
5 files changed, 505 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..fe54e6b --- /dev/null +++ b/src/main/kotlin/features/misc/CustomCapes.kt @@ -0,0 +1,172 @@ +package moe.nea.firmament.features.misc + +import util.render.CustomRenderPipelines +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds +import net.minecraft.client.player.AbstractClientPlayer +import net.minecraft.client.renderer.RenderType +import com.mojang.blaze3d.vertex.VertexConsumer +import net.minecraft.client.renderer.MultiBufferSource +import net.minecraft.client.renderer.entity.state.AvatarRenderState +import com.mojang.blaze3d.vertex.PoseStack +import net.minecraft.world.entity.player.PlayerSkin +import net.minecraft.core.ClientAsset +import net.minecraft.resources.ResourceLocation +import moe.nea.firmament.Firmament +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.TimeMark +import moe.nea.firmament.util.data.Config +import moe.nea.firmament.util.data.ManagedConfig +import moe.nea.firmament.util.mc.CustomRenderPassHelper + +object CustomCapes { + val identifier: String + get() = "developer-capes" + + @Config + object TConfig : ManagedConfig(identifier, Category.DEV) { + val showCapes by toggle("show-cape") { true } + } + + interface CustomCapeRenderer { + fun replaceRender( + renderLayer: RenderType, + vertexConsumerProvider: MultiBufferSource, + matrixStack: PoseStack, + model: (VertexConsumer) -> Unit + ) + } + + data class TexturedCapeRenderer( + val location: ResourceLocation + ) : CustomCapeRenderer { + override fun replaceRender( + renderLayer: RenderType, + vertexConsumerProvider: MultiBufferSource, + matrixStack: PoseStack, + model: (VertexConsumer) -> Unit + ) { + model(vertexConsumerProvider.getBuffer(RenderType.entitySolid(location))) + } + } + + data class ParallaxedHighlightCapeRenderer( + val template: ResourceLocation, + val background: ResourceLocation, + val overlay: ResourceLocation, + val animationSpeed: Duration, + ) : CustomCapeRenderer { + override fun replaceRender( + renderLayer: RenderType, + vertexConsumerProvider: MultiBufferSource, + matrixStack: PoseStack, + model: (VertexConsumer) -> Unit + ) { + val animationValue = (startTime.passedTime() / animationSpeed).mod(1F) + CustomRenderPassHelper( + { "Firmament Cape Renderer" }, + renderLayer.mode(), + renderLayer.format(), + MC.instance.mainRenderTarget, + true, + ).use { renderPass -> + renderPass.setPipeline(CustomRenderPipelines.PARALLAX_CAPE_SHADER) + renderPass.setAllDefaultUniforms() + renderPass.setUniform("Animation", 4) { + it.putFloat(animationValue.toFloat()) + } + renderPass.bindSampler("Sampler0", template) + renderPass.bindSampler("Sampler1", background) + renderPass.bindSampler("Sampler3", overlay) + renderPass.uploadVertices(2048, model) + renderPass.draw() + } + } + } + + interface CapeStorage { + companion object { + @JvmStatic + fun cast(playerEntityRenderState: AvatarRenderState) = + 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 + ) + ), + UNPLEASANT_GRADIENT( + "unpleasant_gradient", + TexturedCapeRenderer(Firmament.identifier("textures/cape/unpleasant_gradient.png")) + ), + FURFSKY_STATIC( + "FurfSky", + TexturedCapeRenderer(Firmament.identifier("textures/cape/fsr_static.png")) + ), + + FIRMAMENT_STATIC( + "Firmament", + TexturedCapeRenderer(Firmament.identifier("textures/cape/firm_static.png")) + ), + HYPIXEL_PLUS( + "Hypixel+", + TexturedCapeRenderer(Firmament.identifier("textures/cape/h_plus.png")) + ), + ; + + val cape = CustomCape(name, label, render) + } + + val byId = AllCapes.entries.associateBy { it.cape.id } + val byUuid = + listOf( + listOf( + Devs.nea to AllCapes.UNPLEASANT_GRADIENT, + Devs.kath to AllCapes.FIRMAMENT_STATIC, + Devs.jani to AllCapes.FIRMAMENT_ANIMATED, + Devs.nat to AllCapes.FIRMAMENT_ANIMATED, + Devs.HPlus.ic22487 to AllCapes.HYPIXEL_PLUS, + ), + Devs.FurfSky.all.map { it to AllCapes.FURFSKY_STATIC }, + ).flatten().flatMap { (dev, cape) -> dev.uuids.map { it to cape.cape } }.toMap() + + @JvmStatic + fun addCapeData( + player: AbstractClientPlayer, + playerEntityRenderState: AvatarRenderState + ) { + if (true) return // TODO: see capefeaturerenderer mixin + 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.skin = PlayerSkin( + playerEntityRenderState.skin.body, + ClientAsset.ResourceTexture(Firmament.identifier("placeholder/fake_cape"), Firmament.identifier("placeholder/fake_cape")), + playerEntityRenderState.skin.elytra, + playerEntityRenderState.skin.model, + playerEntityRenderState.skin.secure, + ) + playerEntityRenderState.showCape = 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..b9f7e03 --- /dev/null +++ b/src/main/kotlin/features/misc/Devs.kt @@ -0,0 +1,42 @@ +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") + val nat = Dev("168300e6-4e74-4a6d-89a0-7b7cf8ad6a7d", "06914e9d-7bc2-4cb7-b112-62c4cc958d96") + + 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 + ) + } + object HPlus { + val ic22487 = Dev("ab2be3b2-bb75-4aaa-892d-9fff5a7e3953") + } + +} diff --git a/src/main/kotlin/features/misc/Hud.kt b/src/main/kotlin/features/misc/Hud.kt new file mode 100644 index 0000000..bbe8044 --- /dev/null +++ b/src/main/kotlin/features/misc/Hud.kt @@ -0,0 +1,75 @@ +package moe.nea.firmament.features.misc + +import org.joml.Vector2i +import net.minecraft.client.multiplayer.PlayerInfo +import net.minecraft.network.chat.Component +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.HudRenderEvent +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.data.Config +import moe.nea.firmament.util.data.ManagedConfig +import moe.nea.firmament.util.tr + +object Hud { + val identifier: String + get() = "hud" + + @Config + object TConfig : ManagedConfig(identifier, Category.MISC) { + var dayCount by toggle("day-count") { false } + val dayCountHud by position("day-count-hud", 80, 10) { Vector2i() } + var fpsCount by toggle("fps-count") { false } + val fpsCountHud by position("fps-count-hud", 80, 10) { Vector2i() } + var pingCount by toggle("ping-count") { false } + val pingCountHud by position("ping-count-hud", 80, 10) { Vector2i() } + } + + @Subscribe + fun onRenderHud(it: HudRenderEvent) { + if (TConfig.dayCount) { + it.context.pose().pushMatrix() + TConfig.dayCountHud.applyTransformations(it.context.pose()) + val day = (MC.world?.dayTime ?: 0L) / 24000 + it.context.drawString( + MC.font, + Component.literal(String.format(tr("firmament.config.hud.day-count-hud.display", "Day: %s").string, day)), + 36, + MC.font.lineHeight, + -1, + true + ) + it.context.pose().popMatrix() + } + + if (TConfig.fpsCount) { + it.context.pose().pushMatrix() + TConfig.fpsCountHud.applyTransformations(it.context.pose()) + it.context.drawString( + MC.font, Component.literal( + String.format( + tr("firmament.config.hud.fps-count-hud.display", "FPS: %s").string, MC.instance.fps + ) + ), 36, MC.font.lineHeight, -1, true + ) + it.context.pose().popMatrix() + } + + if (TConfig.pingCount) { + it.context.pose().pushMatrix() + TConfig.pingCountHud.applyTransformations(it.context.pose()) + val ping = MC.player?.let { + val entry: PlayerInfo? = MC.networkHandler?.getPlayerInfo(it.uuid) + entry?.latency ?: -1 + } ?: -1 + it.context.drawString( + MC.font, Component.literal( + String.format( + tr("firmament.config.hud.ping-count-hud.display", "Ping: %s ms").string, ping + ) + ), 36, MC.font.lineHeight, -1, true + ) + + it.context.pose().popMatrix() + } + } +} diff --git a/src/main/kotlin/features/misc/LicenseViewer.kt b/src/main/kotlin/features/misc/LicenseViewer.kt new file mode 100644 index 0000000..26bec80 --- /dev/null +++ b/src/main/kotlin/features/misc/LicenseViewer.kt @@ -0,0 +1,136 @@ +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 net.minecraft.network.chat.Component +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() = Component.literal(webPresence ?: "<no web presence>") + + @Bind + fun open() { + MC.openUrl(webPresence ?: return) + } + + @Bind + fun projectName() = Component.literal(projectName) + + @Bind + fun projectDescription() = Component.literal(projectDescription ?: "<no project description>") + + @get:Bind("developers") + @Transient + val developersO = ObservableList(developers) + + @get:Bind("licenses") + @Transient + val licenses0 = ObservableList(licenses) + } + + @Serializable + data class Developer( + val name: String, + val webPresence: String? = null + ) { + + @Bind("name") + fun nameT() = Component.literal(name) + + @Bind + fun open() { + MC.openUrl(webPresence ?: return) + } + + @Bind + fun hasWebPresence() = webPresence != null + + @Bind + fun webPresence() = Component.literal(webPresence ?: "<no web presence>") + } + + @Serializable + data class License( + val licenseName: String, + val licenseUrl: String? = null + ) { + @Bind("name") + fun nameG() = Component.literal(licenseName) + + @Bind + fun open() { + MC.openUrl(licenseUrl ?: return) + } + + @Bind + fun hasUrl() = licenseUrl != null + + @Bind + fun url() = Component.literal(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() + } + } + } +} diff --git a/src/main/kotlin/features/misc/ModAnnouncer.kt b/src/main/kotlin/features/misc/ModAnnouncer.kt new file mode 100644 index 0000000..c2aa013 --- /dev/null +++ b/src/main/kotlin/features/misc/ModAnnouncer.kt @@ -0,0 +1,80 @@ +package moe.nea.firmament.features.misc + +import io.netty.buffer.ByteBuf +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs +import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry +import net.fabricmc.loader.api.FabricLoader +import net.minecraft.network.codec.StreamCodec +import net.minecraft.network.codec.ByteBufCodecs +import net.minecraft.network.protocol.common.custom.CustomPacketPayload +import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket +import moe.nea.firmament.Firmament +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.JoinServerEvent + +/** + * This is a class that announces all mods to hypixel (or any other server you connect to). + * + * I don't mind cheat mods, i just think they should be honest about themselves. + * + * If you are a cheat mod (or think you could possibly be seen as one), you have two options: + * + * - risk getting your users banned from hypixel by exposing your modid + version number in a packet sent on first connect + * - hide yourself from this list by either a mixin, or (more easily) by specifying a tag in your fabric.mod.json: + * + * ```json + * { + * "schemaVersion": 1, + * "id": "my-cheat-mod", + * "custom": { "firmament:hide_from_modlist": true } + * } + * ``` + */ +object ModAnnouncer { + + data class ModEntry( + val modid: String, + val modVersion: String, + ) { + companion object { + val CODEC: StreamCodec<ByteBuf, ModEntry> = StreamCodec.composite( + ByteBufCodecs.STRING_UTF8, ModEntry::modid, + ByteBufCodecs.STRING_UTF8, ModEntry::modVersion, + ::ModEntry + ) + } + } + + data class ModPacket( + val mods: List<ModEntry>, + ) : CustomPacketPayload { + override fun type(): CustomPacketPayload.Type<out ModPacket> { + return ID + } + + companion object { + val ID = CustomPacketPayload.Type<ModPacket>(Firmament.identifier("mod_list")) + val CODEC: StreamCodec<ByteBuf, ModPacket> = ModEntry.CODEC.apply(ByteBufCodecs.list()) + .map(::ModPacket, ModPacket::mods) + } + } + + @Subscribe + fun onServerJoin(event: JoinServerEvent) { + val packet = ModPacket( + FabricLoader.getInstance() + .allMods + .filter { !it.metadata.containsCustomValue("firmament:hide_from_modlist") } + .map { ModEntry(it.metadata.id, it.metadata.version.friendlyString) }) + val pbb = PacketByteBufs.create() + ModPacket.CODEC.encode(pbb, packet) + if (pbb.writerIndex() > ServerboundCustomPayloadPacket.MAX_PAYLOAD_SIZE) + return + + event.networkHandler.send(event.packetSender.createPacket(packet)) + } + + init { + PayloadTypeRegistry.playC2S().register(ModPacket.ID, ModPacket.CODEC) + } +} |
