diff --git a/gradle/fabric/26.1.versions.toml b/gradle/fabric/26.1.versions.toml index 966070e2b..c9374e1f8 100644 --- a/gradle/fabric/26.1.versions.toml +++ b/gradle/fabric/26.1.versions.toml @@ -12,6 +12,7 @@ yacl = "3.9.0+26.1-fabric" moulconfig = "3.11.0" odin = "0.2.2" skyblocker = "7OtCaJGB" +stella = "d12aec9c69" vulkanmod = "0.6.8+26.1.2" @@ -32,6 +33,8 @@ modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu"} yacl = { module = "dev.isxander:yet-another-config-lib", version.ref = "yacl" } odin = { module = "maven.modrinth:odin", version.ref = "odin" } skyblocker = { module = "maven.modrinth:skyblocker-liap", version.ref = "skyblocker" } +#stella = { module = "maven.modrinth:stella", version.ref = "stella"} I prefer modrinth but needed this to work befor I release +stella = { module = "com.github.Eclipse-5214.stella:stella-26.1", version.ref = "stella" } moulconfig = { module = "org.notenoughupdates.moulconfig:common", version.ref = "moulconfig" } @@ -42,5 +45,5 @@ meowdding-lib = { module = "me.owdding.meowdding-lib:meowdding-lib", version.ref skyblock-api = { module = "tech.thatgravyboat:skyblock-api", version.ref = "skyblock-api" } [bundles] -compat = ["modmenu", "yacl", "odin", "skyblocker"] +compat = ["modmenu", "yacl", "odin", "skyblocker", "stella"] fabric-api = ["api-base", "lifecycle-events-v1", "transitive-access-wideners-v1", "command-api-v2", "key-mapping-api-v1", "resource-loader-v0", "resource-loader-v1"] diff --git a/gradle/fabric/26.2.versions.toml b/gradle/fabric/26.2.versions.toml index c58108c09..d16763d11 100644 --- a/gradle/fabric/26.2.versions.toml +++ b/gradle/fabric/26.2.versions.toml @@ -11,6 +11,7 @@ modmenu = "20.0.0-beta.4" yacl = "3.9.4+26.2-fabric" moulconfig = "3.11.0" skyblocker = "PwRyrygz" +stella = "d12aec9c69" [libraries] api-base = { module = "net.fabricmc.fabric-api:fabric-api-base", version.ref = "api-base" } @@ -24,7 +25,9 @@ modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu"} yacl = { module = "dev.isxander:yet-another-config-lib", version.ref = "yacl" } moulconfig = { module = "org.notenoughupdates.moulconfig:common", version.ref = "moulconfig" } skyblocker = { module = "maven.modrinth:skyblocker-liap", version.ref = "skyblocker" } +#stella = { module = "maven.modrinth:stella", version.ref = "stella"} I prefer modrinth but needed this to work befor I release +stella = { module = "com.github.Eclipse-5214.stella:stella-26.1", version.ref = "stella" } [bundles] -compat = ["modmenu", "yacl", "skyblocker"] +compat = ["modmenu", "yacl", "skyblocker", "stella"] fabric-api = ["api-base", "lifecycle-events-v1", "transitive-access-wideners-v1", "command-api-v2", "key-mapping-api-v1", "resource-loader-v1"] diff --git a/minecraft/src/main/java/org/polyfrost/oneconfig/internal/OneConfigMixinInit.java b/minecraft/src/main/java/org/polyfrost/oneconfig/internal/OneConfigMixinInit.java index 4bcd1c0e2..eb4bdaf7f 100644 --- a/minecraft/src/main/java/org/polyfrost/oneconfig/internal/OneConfigMixinInit.java +++ b/minecraft/src/main/java/org/polyfrost/oneconfig/internal/OneConfigMixinInit.java @@ -116,6 +116,9 @@ public List getMixins() { mixins.add("compat.skyblocker.Mixin_SkyblockerScreenBuilder"); //? } + //? stella_compat + mixins.add("compat.stella.Mixin_Stella"); + //? apec_compat //mixins.add("compat.apec.Mixin_ApecMenu"); diff --git a/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/compat/stella/Mixin_Stella.java b/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/compat/stella/Mixin_Stella.java new file mode 100644 index 000000000..2c86d1574 --- /dev/null +++ b/minecraft/src/main/java/org/polyfrost/oneconfig/internal/mixin/compat/stella/Mixin_Stella.java @@ -0,0 +1,20 @@ +package org.polyfrost.oneconfig.internal.mixin.compat.stella; + +//? stella_compat { +import co.stellarskys.stella.Stella; +import org.polyfrost.oneconfig.internal.compat.StellaCompat; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Pseudo +@Mixin(Stella.class) +public class Mixin_Stella { + @Inject(method = "onInitializeClient", at = @At("TAIL"), require = 0) + private void oneconfig$registerStella(CallbackInfo ci) { + StellaCompat.initialize(); + } +} +//? } \ No newline at end of file diff --git a/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/compat/CompatLoader.kt b/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/compat/CompatLoader.kt index 1b1129487..34569df13 100644 --- a/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/compat/CompatLoader.kt +++ b/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/compat/CompatLoader.kt @@ -31,6 +31,7 @@ object CompatLoader { "net.azureaaron.dandelion", "com.odtheking.odin", "de.hysky.skyblocker", + "co.stellarskys.stella", "uk.co.hexeption.apec", "moe.nea.firmament.deps.moulconfig", "dev.tr7zw.trender", diff --git a/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/compat/StellaCompat.kt b/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/compat/StellaCompat.kt new file mode 100644 index 000000000..2e529d1d5 --- /dev/null +++ b/minecraft/src/main/kotlin/org/polyfrost/oneconfig/internal/compat/StellaCompat.kt @@ -0,0 +1,293 @@ +package org.polyfrost.oneconfig.internal.compat + +//? stella_compat { +import co.stellarskys.stella.Stella +import co.stellarskys.stella.api.config.core.* +import co.stellarskys.stella.hud.HUDElement +import co.stellarskys.stella.hud.HUDManager +import co.stellarskys.stella.hud.HUDManager.customRenderers +import co.stellarskys.stella.utils.config +import co.stellarskys.stella.utils.render.Render2D +import co.stellarskys.stella.utils.render.Render2D.height +import co.stellarskys.stella.utils.render.Render2D.width +import net.minecraft.client.gui.GuiGraphicsExtractor +import org.apache.logging.log4j.LogManager +import org.polyfrost.oneconfig.api.config.v1.CompatSnapshots +import org.polyfrost.oneconfig.api.config.v1.ConfigManager +import org.polyfrost.oneconfig.api.config.v1.Properties +import org.polyfrost.oneconfig.api.config.v1.Property +import org.polyfrost.oneconfig.api.config.v1.Tree +import org.polyfrost.oneconfig.api.config.v1.Visualizer +import org.polyfrost.oneconfig.api.config.v1.backend.Backend +import org.polyfrost.oneconfig.api.config.v1.dsl.category +import org.polyfrost.oneconfig.api.config.v1.dsl.saveFunction +import org.polyfrost.oneconfig.api.config.v1.dsl.subcategory +import org.polyfrost.oneconfig.api.config.v1.dsl.visualizer +import org.polyfrost.oneconfig.api.event.v1.EventManager +import org.polyfrost.oneconfig.api.hud.v1.OneConfigHudWrapper +import org.polyfrost.oneconfig.api.hud.v1.events.HudEditorToggleEvent +import org.polyfrost.oneconfig.api.platform.v1.ModInfo +import org.polyfrost.oneconfig.api.ui.v1.keybind.KeyModifiers +import org.polyfrost.oneconfig.api.ui.v1.keybind.OneConfigKeybind +import org.polyfrost.oneconfig.internal.ui.hud.CompatOverlayRenderer +import java.awt.Color +import java.util.function.Consumer + +object StellaCompat { + private val LOGGER = LogManager.getLogger("OneConfig/Stella-Compat") + private val hudProperties = mutableMapOf>() + private val tree = Tree.tree() + private var initialized = false + + @JvmStatic + fun initialize() { + CompatLoader.requireTranslations(skip = true) { + ensureRegistered() + } + } + + fun ensureRegistered() { + if (initialized) return + initialized = true + + try { + addConfigTree() + addHuds() + } catch (_: Throwable) { + fallbackTree() + return + } + + config.registerListener { _, _ -> + for (node in tree.map.values) { + (node as? Property<*>)?.revaluateDisplay() + } + for (property in hudProperties.values) { + property.revaluateDisplay() + } + } + + EventManager.register(HudEditorToggleEvent::class.java, Consumer { event -> + HUDManager.shouldRenderHuds = !event.open + if (!event.open) runCatching { HUDManager.saveAllLayouts() } + .onFailure { LOGGER.warn("Failed to save Stella HUD layouts", it) } + }) + } + + // Settings + private fun addConfigTree() { + val info = ModInfo.loadedMods.firstOrNull { it.id == "stella" } + + tree.id = Stella.NAMESPACE + tree.title = "Stella" + tree.saveFunction = { config.save() } + info?.extractIconFile()?.let { tree.addMetadata("icon_path", it) } + + for ((catName, category) in config.categories) { + for (subcategory in category.subcategories.values) { + buildProperty(subcategory.configName, subcategory)?.let { property -> + property.category = catName + property.subcategory = subcategory.subName + hookShouldShow(property, subcategory) + tree.put(property) + } + + for ((key, element) in subcategory.elements) { + val property = buildProperty(key, element) ?: continue + property.category = catName + property.subcategory = subcategory.subName + hookShouldShow(property, element) + tree.put(property) + } + } + } + + CompatSnapshots.register(tree) + } + + fun fallbackTree() { + val fallback = Tree.tree() + fallback.id = Stella.NAMESPACE + fallback.title = "Stella" + ModInfo.loadedMods.firstOrNull { it.id == "stella" }?.extractIconFile()?.let { fallback.addMetadata("icon_path", it) } + fallback.addMetadata("on_click") { runCatching { config.open() } } + fallback.addMetadata(Backend.UI_ONLY_METADATA, true) + fallback.addMetadata(Backend.UI_PLACEHOLDER_METADATA, true) + ConfigManager.active().register(fallback) + } + + private fun buildProperty(key: String, element: ConfigElement): Property<*>? { + if (element.configName.isBlank()) return null + + val name = element.name + val desc = element.description + + return when (element) { + is Toggle -> Properties.functional( + getter = { element.value as Boolean }, + setter = { element.value = it }, + id = key, name = name, description = desc, type = Boolean::class.javaPrimitiveType + ).apply { visualizer = Visualizer.SwitchVisualizer::class.java } + + is ConfigSubcategory -> Properties.functional( + getter = { element.value as Boolean }, + setter = { element.value = it }, + id = key, name = "Enable", description = desc, type = Boolean::class.javaPrimitiveType + ).apply { visualizer = Visualizer.SwitchVisualizer::class.java } + + is StepSlider -> Properties.functional( + getter = { element.value as Int }, + setter = { element.value = it }, + id = key, name = name, description = desc, type = Int::class.javaPrimitiveType, + ).apply { + visualizer = Visualizer.SliderVisualizer::class.java + addMetadata("min", element.min.toFloat()) + addMetadata("max", element.max.toFloat()) + addMetadata("step", element.step.toFloat()) + } + + is Slider -> Properties.functional( + getter = { element.value as Float }, + setter = { element.value = it }, + id = key, name = name, description = desc, type = Float::class.javaPrimitiveType, + ).apply { + visualizer = Visualizer.SliderVisualizer::class.java + addMetadata("min", element.min) + addMetadata("max", element.max) + } + + is Dropdown -> Properties.functional( + getter = { element.value as Int }, + setter = { element.value = it }, + id = key, name = name, description = desc, type = Int::class.javaPrimitiveType, + ).apply { + visualizer = Visualizer.DropdownVisualizer::class.java + addMetadata("options", element.options) + } + + is ColorPicker -> Properties.functional( + getter = { element.value as Color }, + setter = { element.value = it }, + id = key, name = name, description = desc, type = Color::class.java, + ).apply { visualizer = Visualizer.ColorVisualizer::class.java } + + is TextInput -> Properties.functional( + getter = { element.value as String }, + setter = { element.value = it }, + id = key, name = name, description = desc, type = String::class.java, + ).apply { visualizer = Visualizer.TextVisualizer::class.java } + + is Button -> Properties.dummy(id = key, name = name, description = desc).apply { + visualizer = Visualizer.ButtonVisualizer::class.java + addMetadata("runnable", Runnable { element.onClick?.invoke() }) + } + + is TextParagraph -> Properties.dummy(id = key, name = name, description = desc) + .apply { visualizer = Visualizer.InfoVisualizer::class.java } + + is Keybind -> { + val handler = element.value as Keybind.Handler + Properties.functional( + getter = { + val code = handler.keyCode() + OneConfigKeybind(if (code > 0) intArrayOf(code) else null, null, KeyModifiers.NONE, 0L) { false } + }, + setter = { kb -> handler.setCode(kb.keyCodes?.firstOrNull() ?: 0) }, + id = key, name = name, description = desc, type = OneConfigKeybind::class.java, + ).apply { visualizer = Visualizer.KeybindVisualizer::class.java } + } + else -> null + } + } + + private fun hookShouldShow (property: Property<*>, element: ConfigElement) { + val shouldShow = element.showIf ?: return + property.addDisplayCondition { + if (shouldShow(config)) Property.Display.SHOWN else Property.Display.HIDDEN + } + } + + // Hud + private class StellaHudWrapper(private val element: HUDElement): OneConfigHudWrapper { + private val confElement: ConfigElement? get() = config.elementMap[element.configKey] + + override var id: String = "stella/${element.id}" + override var name: String = confElement?.name ?: element.id + override var modId: String = Stella.NAMESPACE + + override var x: Float + get() = element.x + set(value) { element.x = value } + + override var y: Float + get() = element.y + set(value) { element.y = value } + + override var scale: Float + get() = element.scale + set(value) { element.scale = value } + + override var hidden: Boolean + get() = !element.isEnabled() + set(value) { confElement?.value = !value } + + override var scaledWidth: Float + get() = element.width * element.scale + set(_) {} + + override var scaledHeight: Float + get() = element.height * element.scale + set(_) {} + + override fun linkedProperties(): List> = buildHudProperties(element) + } + + private fun addHuds() { + for (element in HUDManager.elements.values) { + runCatching { StellaHudWrapper(element).register() } + .onFailure { LOGGER.error("Failed to register Stella HUD '${element.id}'", it) } + } + + CompatOverlayRenderer.register(::renderExamples) + } + + private fun renderExamples(ctx: GuiGraphicsExtractor) { + HUDManager.elements.values.forEach { element -> + if (!element.isEnabled()) return@forEach + + ctx.pose().pushMatrix() + ctx.pose().translate(element.x, element.y) + ctx.pose().scale(element.scale, element.scale) + + val custom = customRenderers[element.id] + if (custom != null) custom(ctx) + else { + if (element.width == 0 && element.height == 0) { + element.width = element.text.width() + 4 + element.height = element.text.height() + 6 + } + + Render2D.drawString(ctx, element.text, 2, 3, shadow = false) + } + + ctx.pose().popMatrix() + } + } + + private fun buildHudProperties(element: HUDElement): List> { + val dynamic = element.dynamicVisibility + val toggle = element.configKey?.let { buildOrGetProp(it, dynamic) } + val related = element.related.mapNotNull { buildOrGetProp(it, dynamic) } + return listOfNotNull(toggle) + related + } + + private fun buildOrGetProp(key: String, dynamic: Boolean): Property<*>? { + hudProperties[key]?.let { return it } + val element = config.elementMap[key] ?: return null + val property = buildProperty(key, element) ?: return null + if (dynamic) hookShouldShow(property, element) + hudProperties[key] = property + return property + } +} +//? } diff --git a/minecraft/stonecutter.gradle.kts b/minecraft/stonecutter.gradle.kts index 04ab5220e..6292a2c21 100644 --- a/minecraft/stonecutter.gradle.kts +++ b/minecraft/stonecutter.gradle.kts @@ -26,6 +26,7 @@ stonecutter { this["odin_compat"] = current.project.endsWith("-fabric") && catalogue.has("odin") this["skycubed_compat"] = current.project.endsWith("-fabric") && catalogue.has("skycubed") this["skyblocker_compat"] = current.project.endsWith("-fabric") && catalogue.has("skyblocker") + this["stella_compat"] = current.project.endsWith("-fabric") && catalogue.has("stella") this["apec_compat"] = current.project.endsWith("-fabric") && catalogue.has("apec") this["tr7zw_compat"] = true this["wwaypoints_compat"] = true