Integrate ClothConfig for Forge

This commit is contained in:
octarine-noise
2021-05-22 17:27:28 +02:00
parent eeabc1922e
commit 25cea8633c
7 changed files with 375 additions and 247 deletions

View File

@@ -6,9 +6,10 @@ plugins {
apply(plugin = "org.spongepowered.mixin")
repositories {
maven("http://files.minecraftforge.net/maven")
maven("https://files.minecraftforge.net/maven")
maven("https://repo.spongepowered.org/maven")
maven("https://minecraft.curseforge.com/api/maven")
maven("https://www.cursemaven.com")
}
dependencies {
@@ -16,6 +17,8 @@ dependencies {
"implementation"("kottle:Kottle:${properties["kottleVersion"]}")
"implementation"("org.spongepowered:mixin:0.8-SNAPSHOT")
"api"(fg.deobf("curse.maven:clothconfig-348521:2813656"))
}
configurations["annotationProcessor"].extendsFrom(configurations["implementation"])
@@ -45,6 +48,7 @@ java {
kotlin {
target.compilations.configureEach {
kotlinOptions.freeCompilerArgs += listOf("-Xno-param-assertions", "-Xno-call-assertions")
kotlinOptions.jvmTarget = "1.8"
}
}

View File

@@ -1,33 +1,47 @@
package mods.betterfoliage
import me.shedaniel.forge.clothconfig2.api.ConfigBuilder
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.resource.AsnycSpriteProviderManager
import mods.octarinecore.client.resource.GeneratedBlockTexturePack
import mods.betterfoliage.client.config.MainConfig
import mods.octarinecore.common.config.clothGuiRoot
import mods.octarinecore.common.config.forgeSpecRoot
import net.alexwells.kottle.FMLKotlinModLoadingContext
import net.minecraft.client.Minecraft
import net.minecraft.client.particle.ParticleManager
import net.minecraft.client.renderer.model.ModelBakery
import net.minecraft.client.gui.screen.Screen
import net.minecraft.util.ResourceLocation
import net.minecraftforge.fml.ExtensionPoint.CONFIGGUIFACTORY
import net.minecraftforge.fml.ModLoadingContext
import net.minecraftforge.fml.common.Mod
import net.minecraftforge.fml.config.ModConfig
import org.apache.logging.log4j.Level.DEBUG
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.simple.SimpleLogger
import org.apache.logging.log4j.util.PropertiesUtil
import java.io.File
import java.io.PrintStream
import java.util.*
import org.apache.commons.lang3.tuple.Pair
import java.util.function.BiFunction
import java.util.function.BiPredicate
import java.util.function.Supplier
@Mod(BetterFoliageMod.MOD_ID)
object BetterFoliageMod {
const val MOD_ID = "betterfoliage"
val bus = FMLKotlinModLoadingContext.get().modEventBus
val config = MainConfig()
init {
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, Config.build())
val ctx = ModLoadingContext.get()
// Config instance + GUI handler
val configSpec = config.forgeSpecRoot()
ctx.registerConfig(ModConfig.Type.CLIENT, configSpec)
ctx.registerExtensionPoint(CONFIGGUIFACTORY) { BiFunction<Minecraft, Screen, Screen> { client, parent ->
config.clothGuiRoot(
parentScreen = parent,
prefix = listOf(MOD_ID),
background = ResourceLocation("minecraft:textures/block/spruce_wood.png"),
saveAction = { configSpec.save() }
)
} }
Minecraft.getInstance().resourcePackList.addPackFinder(BetterFoliage.asyncPack.finder)
bus.register(BlockConfig)
Client.init()

View File

@@ -1,30 +1,38 @@
package mods.betterfoliage.client
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.chunk.ChunkOverlayManager
import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.integration.*
import mods.betterfoliage.client.render.*
import mods.betterfoliage.client.integration.OptifineCustomColors
import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.betterfoliage.client.render.AsyncCactusDiscovery
import mods.betterfoliage.client.render.AsyncLogDiscovery
import mods.betterfoliage.client.render.LeafWindTracker
import mods.betterfoliage.client.render.RenderAlgae
import mods.betterfoliage.client.render.RenderCactus
import mods.betterfoliage.client.render.RenderConnectedGrass
import mods.betterfoliage.client.render.RenderConnectedGrassLog
import mods.betterfoliage.client.render.RenderCoral
import mods.betterfoliage.client.render.RenderGrass
import mods.betterfoliage.client.render.RenderLeaves
import mods.betterfoliage.client.render.RenderLilypad
import mods.betterfoliage.client.render.RenderLog
import mods.betterfoliage.client.render.RenderMycelium
import mods.betterfoliage.client.render.RenderNetherrack
import mods.betterfoliage.client.render.RenderReeds
import mods.betterfoliage.client.render.RisingSoulTextures
import mods.betterfoliage.client.texture.AsyncGrassDiscovery
import mods.betterfoliage.client.texture.AsyncLeafDiscovery
import mods.betterfoliage.client.texture.LeafParticleRegistry
import mods.octarinecore.client.gui.textComponent
import mods.octarinecore.client.render.RenderDecorator
import mods.octarinecore.client.resource.IConfigChangeListener
import net.minecraft.block.BlockState
import net.minecraft.client.Minecraft
import net.minecraft.util.math.BlockPos
import net.minecraft.util.text.TextFormatting
import net.minecraft.util.text.TranslationTextComponent
import net.minecraftforge.registries.ForgeRegistries
import org.apache.logging.log4j.Level
/**
* Object responsible for initializing (and holding a reference to) all the infrastructure of the mod
* except for the call hooks.
*/
object Client {
var renderers= emptyList<RenderDecorator>()
var renderers = emptyList<RenderDecorator>()
var configListeners = emptyList<IConfigChangeListener>()
val suppressRenderErrors = mutableSetOf<BlockState>()

View File

@@ -8,139 +8,6 @@ import net.minecraft.util.ResourceLocation
import net.minecraftforge.eventbus.api.SubscribeEvent
import net.minecraftforge.fml.config.ModConfig
private fun featureEnable() = boolean(true).lang("enabled")
// Config singleton
object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.MOD_ID) {
val enabled by boolean(true)
val nVidia by boolean(false)
object leaves : ConfigCategory() {
val enabled by featureEnable()
val snowEnabled by boolean(true)
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
val vOffset by double(max=0.4, default=0.1).lang("vOffset")
val size by double(min=0.75, max=2.5, default=1.4).lang("size")
val dense by boolean(false)
val hideInternal by boolean(true)
}
object shortGrass : ConfigCategory(){
val grassEnabled by boolean(true)
val myceliumEnabled by boolean(true)
val snowEnabled by boolean(true)
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
val heightMin by double(min=0.1, max=2.5, default=0.6).lang("heightMin")
val heightMax by double(min=0.1, max=2.5, default=0.8).lang("heightMax")
val size by double(min=0.5, max=1.5, default=1.0).lang("size")
val population by int(max=64, default=64).lang("population")
val useGenerated by boolean(false)
val shaderWind by boolean(true).lang("shaderWind")
val saturationThreshold by double(default=0.1)
}
object connectedGrass : ConfigCategory(){
val enabled by boolean(true)
val snowEnabled by boolean(false)
}
object roundLogs : ConfigCategory(){
val enabled by featureEnable()
val radiusSmall by double(max=0.5, default=0.25)
val radiusLarge by double(max=0.5, default=0.44)
val dimming by double(default = 0.7)
val connectSolids by boolean(false)
val lenientConnect by boolean(true)
val connectPerpendicular by boolean(true)
val connectGrass by boolean(true)
val defaultY by boolean(false)
val zProtection by double(min = 0.9, default = 0.99)
}
object cactus : ConfigCategory(){
val enabled by featureEnable()
val size by double(min=0.5, max=1.5, default=0.8).lang("size")
val sizeVariation by double(max=0.5, default=0.1)
val hOffset by double(max=0.5, default=0.1).lang("hOffset")
}
object lilypad : ConfigCategory(){
val enabled by featureEnable()
val hOffset by double(max=0.25, default=0.1).lang("hOffset")
val flowerChance by int(max=64, default=16, min=0)
}
object reed : ConfigCategory(){
val enabled by featureEnable()
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
val heightMin by double(min=1.5, max=3.5, default=1.7).lang("heightMin")
val heightMax by double(min=1.5, max=3.5, default=2.2).lang("heightMax")
val population by int(max=64, default=32).lang("population")
val minBiomeTemp by double(default=0.4)
val minBiomeRainfall by double(default=0.4)
// val biomes by biomeList { it.filterTemp(0.4f, null) && it.filterRain(0.4f, null) }
val shaderWind by boolean(true).lang("shaderWind")
}
object algae : ConfigCategory(){
val enabled by featureEnable()
val hOffset by double(max=0.25, default=0.1).lang("hOffset")
val size by double(min=0.5, max=1.5, default=1.0).lang("size")
val heightMin by double(min=0.1, max=1.5, default=0.5).lang("heightMin")
val heightMax by double(min=0.1, max=1.5, default=1.0).lang("heightMax")
val population by int(max=64, default=48).lang("population")
// val biomes by biomeList { it.filterClass("river", "ocean") }
val shaderWind by boolean(true).lang("shaderWind")
}
object coral : ConfigCategory(){
val enabled by featureEnable()
val shallowWater by boolean(false)
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
val vOffset by double(max=0.4, default=0.1).lang("vOffset")
val size by double(min=0.5, max=1.5, default=0.7).lang("size")
val crustSize by double(min=0.5, max=1.5, default=1.4)
val chance by int(max=64, default=32)
val population by int(max=64, default=48).lang("population")
// val biomes by biomeList { it.filterClass("river", "ocean", "beach") }
}
object netherrack : ConfigCategory(){
val enabled by featureEnable()
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
val heightMin by double(min=0.1, max=1.5, default=0.6).lang("heightMin")
val heightMax by double(min=0.1, max=1.5, default=0.8).lang("heightMax")
val size by double(min=0.5, max=1.5, default=1.0).lang("size")
}
object fallingLeaves : ConfigCategory(){
val enabled by featureEnable()
val speed by double(min=0.01, max=0.15, default=0.05)
val windStrength by double(min=0.1, max=2.0, default=0.5)
val stormStrength by double(min=0.1, max=2.0, default=0.8)
val size by double(min=0.25, max=1.5, default=0.75).lang("size")
val chance by double(min=0.001, max=1.0, default=0.02)
val perturb by double(min=0.01, max=1.0, default=0.25)
val lifetime by double(min=1.0, max=15.0, default=5.0)
val opacityHack by boolean(true)
}
object risingSoul : ConfigCategory(){
val enabled by featureEnable()
val chance by double(min=0.001, max=1.0, default=0.02)
val perturb by double(min=0.01, max=0.25, default=0.05)
val headSize by double(min=0.25, max=1.5, default=1.0)
val trailSize by double(min=0.25, max=1.5, default=0.75)
val opacity by double(min=0.05, max=1.0, default=0.5)
val sizeDecay by double(min=0.5, max=1.0, default=0.97)
val opacityDecay by double(min=0.5, max=1.0, default=0.97)
val lifetime by double(min=1.0, max=15.0, default=4.0)
val trailLength by int(min=2, max=128, default=48)
val trailDensity by int(min=1, max=16, default=3)
}
}
object BlockConfig {
private val list = mutableListOf<Any>()

View File

@@ -0,0 +1,152 @@
package mods.betterfoliage.client.config
import mods.betterfoliage.BetterFoliageMod
import mods.octarinecore.common.config.DelegatingConfigGroup
import mods.octarinecore.common.config.boolean
import mods.octarinecore.common.config.double
import mods.octarinecore.common.config.integer
import mods.octarinecore.common.config.recurring
import mods.octarinecore.common.config.subNode
fun featureEnable(default: Boolean = true) = boolean(default, langKey = recurring)
val Config get() = BetterFoliageMod.config
class MainConfig : DelegatingConfigGroup() {
val enabled by boolean(true, langKey = { "betterfoliage.global.enabled" })
val nVidia by boolean(false)
val leaves by subNode(::LeavesConfig)
val shortGrass by subNode(::ShortGrassConfig)
val connectedGrass by subNode(::ConnectedGrassConfig)
val roundLogs by subNode(::RoundLogConfig)
val cactus by subNode(::CactusConfig)
val lilypad by subNode(::LilypadConfig)
val reed by subNode(::ReedConfig)
val algae by subNode(::AlgaeConfig)
val coral by subNode(::CoralConfig)
val netherrack by subNode(::NetherrackConfig)
val fallingLeaves by subNode(::FallingLeavesConfig)
val risingSoul by subNode(::RisingSoulConfig)
}
class LeavesConfig : DelegatingConfigGroup() {
val enabled by featureEnable()
val snowEnabled by boolean(true)
val hOffset by double(max=0.4, default=0.2, langKey = recurring)
val vOffset by double(max=0.4, default=0.1, langKey = recurring)
val size by double(min=0.75, max=2.5, default=1.4, langKey = recurring)
val dense by boolean(false)
val hideInternal by boolean(true)
}
class ShortGrassConfig : DelegatingConfigGroup() {
val grassEnabled by boolean(true)
val myceliumEnabled by boolean(true)
val snowEnabled by boolean(true)
val hOffset by double(max=0.4, default=0.2, langKey = recurring)
val heightMin by double(min=0.1, max=2.5, default=0.6, langKey = recurring)
val heightMax by double(min=0.1, max=2.5, default=0.8, langKey = recurring)
val size by double(min=0.5, max=1.5, default=1.0, langKey = recurring)
val population by integer(max=64, default=64)
val useGenerated by boolean(false)
val shaderWind by boolean(true, langKey = recurring)
val saturationThreshold by double(default=0.1)
}
class ConnectedGrassConfig() : DelegatingConfigGroup() {
val enabled by boolean(true)
val snowEnabled by boolean(false)
}
class RoundLogConfig() : DelegatingConfigGroup() {
val enabled by featureEnable()
val radiusSmall by double(max=0.5, default=0.25)
val radiusLarge by double(max=0.5, default=0.44)
val dimming by double(default = 0.7)
val connectSolids by boolean(false)
val lenientConnect by boolean(true)
val connectPerpendicular by boolean(true)
val connectGrass by boolean(true)
val defaultY by boolean(false)
val zProtection by double(min = 0.9, default = 0.99)
}
class CactusConfig() : DelegatingConfigGroup() {
val enabled by featureEnable()
val size by double(min=0.5, max=1.5, default=0.8, langKey = recurring)
val sizeVariation by double(max=0.5, default=0.1)
val hOffset by double(max=0.5, default=0.1, langKey = recurring)
}
class LilypadConfig() : DelegatingConfigGroup() {
val enabled by featureEnable()
val hOffset by double(max=0.25, default=0.1, langKey = recurring)
val flowerChance by integer(max=64, default=16, min=0)
}
class ReedConfig() : DelegatingConfigGroup() {
val enabled by featureEnable()
val hOffset by double(max=0.4, default=0.2, langKey = recurring)
val heightMin by double(min=1.5, max=3.5, default=1.7, langKey = recurring)
val heightMax by double(min=1.5, max=3.5, default=2.2, langKey = recurring)
val population by integer(max=64, default=32, langKey = recurring)
val minBiomeTemp by double(default=0.4)
val minBiomeRainfall by double(default=0.4)
val shaderWind by boolean(true, langKey = recurring)
}
class AlgaeConfig() : DelegatingConfigGroup() {
val enabled by featureEnable()
val hOffset by double(max=0.25, default=0.1, langKey = recurring)
val size by double(min=0.5, max=1.5, default=1.0, langKey = recurring)
val heightMin by double(min=0.1, max=1.5, default=0.5, langKey = recurring)
val heightMax by double(min=0.1, max=1.5, default=1.0, langKey = recurring)
val population by integer(max=64, default=48, langKey = recurring)
val shaderWind by boolean(true, langKey = recurring)
}
class CoralConfig() : DelegatingConfigGroup() {
val enabled by featureEnable()
val shallowWater by boolean(false)
val hOffset by double(max=0.4, default=0.2, langKey = recurring)
val vOffset by double(max=0.4, default=0.1, langKey = recurring)
val size by double(min=0.5, max=1.5, default=0.7, langKey = recurring)
val crustSize by double(min=0.5, max=1.5, default=1.4)
val chance by integer(max=64, default=32)
val population by integer(max=64, default=48, langKey = recurring)
}
class NetherrackConfig() : DelegatingConfigGroup() {
val enabled by featureEnable()
val hOffset by double(max=0.4, default=0.2, langKey = recurring)
val heightMin by double(min=0.1, max=1.5, default=0.6, langKey = recurring)
val heightMax by double(min=0.1, max=1.5, default=0.8, langKey = recurring)
val size by double(min=0.5, max=1.5, default=1.0, langKey = recurring)
}
class FallingLeavesConfig() : DelegatingConfigGroup() {
val enabled by featureEnable()
val speed by double(min=0.01, max=0.15, default=0.05)
val windStrength by double(min=0.1, max=2.0, default=0.5)
val stormStrength by double(min=0.1, max=2.0, default=0.8)
val size by double(min=0.25, max=1.5, default=0.75, langKey = recurring)
val chance by double(min=0.001, max=1.0, default=0.02)
val perturb by double(min=0.01, max=1.0, default=0.25)
val lifetime by double(min=1.0, max=15.0, default=5.0)
val opacityHack by boolean(true)
}
class RisingSoulConfig() : DelegatingConfigGroup() {
val enabled by featureEnable()
val chance by double(min=0.001, max=1.0, default=0.02)
val perturb by double(min=0.01, max=0.25, default=0.05)
val headSize by double(min=0.25, max=1.5, default=1.0)
val trailSize by double(min=0.25, max=1.5, default=0.75)
val opacity by double(min=0.05, max=1.0, default=0.5)
val sizeDecay by double(min=0.5, max=1.0, default=0.97)
val opacityDecay by double(min=0.5, max=1.0, default=0.97)
val lifetime by double(min=1.0, max=15.0, default=4.0)
val trailLength by integer(min=2, max=128, default=48)
val trailDensity by integer(min=1, max=16, default=3)
}

View File

@@ -0,0 +1,172 @@
package mods.octarinecore.common.config
import me.shedaniel.forge.clothconfig2.api.AbstractConfigListEntry
import me.shedaniel.forge.clothconfig2.api.ConfigBuilder
import me.shedaniel.forge.clothconfig2.api.ConfigEntryBuilder
import me.shedaniel.forge.clothconfig2.gui.entries.SubCategoryListEntry
import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.resources.I18n
import net.minecraft.util.ResourceLocation
import net.minecraftforge.common.ForgeConfigSpec
import java.util.*
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
const val MAX_LINE_LEN = 30
fun DelegatingConfigGroup.forgeSpecRoot() =
ForgeConfigSpec.Builder()
.also { createForgeNode(it) }
.build()
fun DelegatingConfigGroup.clothGuiRoot(
parentScreen: Screen,
prefix: List<String>,
background: ResourceLocation,
saveAction: ()->Unit
) = ConfigBuilder.create()
.setParentScreen(parentScreen)
.setTitle(I18n.format((prefix + "title").joinToString(".")))
.setDefaultBackgroundTexture(background)
.setSavingRunnable(saveAction)
.also { builder ->
createClothNode(prefix).value.forEach { rootCategory ->
builder.getOrCreateCategory("main").addEntry(rootCategory)
}
}
.build()
sealed class DelegatingConfigNode {
abstract fun createClothNode(path: List<String>): AbstractConfigListEntry<*>
}
abstract class DelegatingConfigValue<T> : DelegatingConfigNode(), ReadOnlyProperty<DelegatingConfigGroup, T> {
lateinit var forgeValue: ForgeConfigSpec.ConfigValue<T>
abstract fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String)
}
open class DelegatingConfigGroup : DelegatingConfigNode() {
val children = mutableMapOf<String, DelegatingConfigNode>()
fun createForgeNode(builder: ForgeConfigSpec.Builder) {
children.forEach { (name, node) ->
when(node) {
is DelegatingConfigGroup -> {
builder.push(name)
node.createForgeNode(builder)
builder.pop()
}
is DelegatingConfigValue<*> -> node.createForgeNode(builder, name)
}
}
}
override fun createClothNode(path: List<String>): SubCategoryListEntry {
val builder = ConfigEntryBuilder.create()
.startSubCategory(path.joinToString(".").translate())
.setTooltip(*path.joinToString(".").translateTooltip())
.setExpended(false)
children.forEach { (name, node) -> builder.add(node.createClothNode(path + name)) }
return builder.build()
}
}
interface DelegatingConfigGroupFactory<T> {
operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty<DelegatingConfigGroup, T>
}
fun <T: DelegatingConfigGroup> subNode(factory: ()->T) = object : DelegatingConfigGroupFactory<T> {
override operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty<DelegatingConfigGroup, T> {
val child = factory()
parent.children[property.name] = child
return object : ReadOnlyProperty<DelegatingConfigGroup, T> {
override fun getValue(thisRef: DelegatingConfigGroup, property: KProperty<*>) = child
}
}
}
interface DelegatingConfigValueFactory<T> {
fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String): ForgeConfigSpec.ConfigValue<T>
fun createClothNode(prop: CachingConfigProperty<T>, path: List<String>): AbstractConfigListEntry<T>
operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty<DelegatingConfigGroup, T> {
return object : CachingConfigProperty<T>(parent, property) {
override fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) {
forgeValue = this@DelegatingConfigValueFactory.createForgeNode(builder, name)
}
override fun createClothNode(path: List<String>): AbstractConfigListEntry<*> = createClothNode(this, path)
}.apply { parent.children[property.name] = this }
}
}
abstract class CachingConfigProperty<T>(parent: DelegatingConfigGroup, property: KProperty<*>) : DelegatingConfigValue<T>() {
var value: T? = null
override fun getValue(thisRef: DelegatingConfigGroup, property: KProperty<*>) =
value ?: forgeValue.get().apply { value = this }
}
fun String.translate() = I18n.format(this)
fun String.translateTooltip(lineLength: Int = MAX_LINE_LEN) = ("$this.tooltip").translate().let { tooltip ->
tooltip.splitToSequence(" ").fold(mutableListOf("")) { tooltips, word ->
if (tooltips.last().length + word.length < lineLength) {
tooltips[tooltips.lastIndex] += "$word "
} else {
tooltips.add("$word ")
}
tooltips
}.map { it.trim() }.toTypedArray()
}
fun boolean(
default: Boolean,
langKey: (List<String>)->String = { it.joinToString(".") },
valueOverride: (Boolean)->Boolean = { it }
) = object : DelegatingConfigValueFactory<Boolean> {
override fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) =
builder.define(name, default)
override fun createClothNode(prop: CachingConfigProperty<Boolean>, path: List<String>) = ConfigEntryBuilder.create()
.startBooleanToggle(langKey(path).translate(), prop.forgeValue.get())
.setTooltip(langKey(path).let { if (I18n.hasKey("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() })
.setSaveConsumer { prop.forgeValue.set(valueOverride(it)); prop.value = null }
.build()
}
fun integer(
default: Int = 0, min: Int = 0, max: Int,
langKey: (List<String>)->String = { it.joinToString(".") },
valueOverride: (Int)->Int = { it }
) = object : DelegatingConfigValueFactory<Int> {
override fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) =
builder.defineInRange(name, default, min, max)
override fun createClothNode(prop: CachingConfigProperty<Int>, path: List<String>) = ConfigEntryBuilder.create()
.startIntField(langKey(path).translate(), prop.forgeValue.get())
.setTooltip(langKey(path).let { if (I18n.hasKey("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() })
.setMin(min).setMax(max)
.setSaveConsumer { prop.forgeValue.set(valueOverride(it)); prop.value = null }
.build()
}
fun double(
default: Double = 0.0, min: Double = 0.0, max: Double = 1.0,
langKey: (List<String>)->String = { it.joinToString(".") },
valueOverride: (Double)->Double = { it }
) = object : DelegatingConfigValueFactory<Double> {
override fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) =
builder.defineInRange(name, default, min, max)
override fun createClothNode(prop: CachingConfigProperty<Double>, path: List<String>) = ConfigEntryBuilder.create()
.startDoubleField(langKey(path).translate(), prop.forgeValue.get())
.setTooltip(langKey(path).let { if (I18n.hasKey("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() })
.setMin(min).setMax(max)
.setSaveConsumer { prop.forgeValue.set(valueOverride(it)); prop.value = null }
.build()
}
val recurring = { path: List<String> -> "${path.first()}.${path.last()}" }
fun fakeCategory(name: String) = { names: List<String> ->
(listOf(names.first(), name) + names.drop(1)).joinToString(".")
}

View File

@@ -1,89 +0,0 @@
@file:JvmName("DelegatingConfigKt")
package mods.octarinecore.common.config
import mods.octarinecore.metaprog.reflectDelegates
import mods.octarinecore.metaprog.reflectNestedObjects
import net.minecraftforge.common.ForgeConfigSpec
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
open class DelegatingConfig(val modId: String, val langPrefix: String) {
fun build() = ForgeConfigSpec.Builder().apply { ConfigBuildContext(langPrefix, emptyList(), this).addCategory(this@DelegatingConfig) }.build()
}
class ConfigBuildContext(val langPrefix: String, val path: List<String>, val builder: ForgeConfigSpec.Builder) {
fun addCategory(configObj: Any) {
configObj.reflectNestedObjects.forEach { (name, category) ->
builder.push(name)
descend(name).addCategory(category)
builder.pop()
}
configObj.reflectDelegates(ConfigDelegate::class.java).forEach { (name, delegate) ->
descend(name).apply { delegate.addToBuilder(this) }
}
}
fun descend(pathName: String) = ConfigBuildContext(langPrefix, path + pathName, builder)
}
open class ConfigCategory(val comment: String? = null) {
}
abstract class ConfigDelegate<T> : ReadOnlyProperty<Any, T> {
lateinit var configValue: ForgeConfigSpec.ConfigValue<T>
var cachedValue: T? = null
override fun getValue(thisRef: Any, property: KProperty<*>): T {
if (cachedValue == null) cachedValue = configValue.get()
return cachedValue!!
}
abstract fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder): ForgeConfigSpec.ConfigValue<T>
fun addToBuilder(ctx: ConfigBuildContext) {
val langKey = ctx.langPrefix + "." + (langPrefixOverride ?: ctx.path.joinToString("."))
ctx.builder.translation(langKey)
configValue = getConfigValue(ctx.path.last(), ctx.builder)
}
var langPrefixOverride: String? = null
fun lang(prefix: String) = apply { langPrefixOverride = prefix }
}
class DelegatingBooleanValue(val defaultValue: Boolean) : ConfigDelegate<Boolean>() {
override fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder) = builder.define(name, defaultValue)
}
class DelegatingIntValue(
val minValue: Int = 0,
val maxValue: Int = 1,
val defaultValue: Int = 0
) : ConfigDelegate<Int>() {
override fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder) = builder.defineInRange(name, defaultValue, minValue, maxValue)
}
class DelegatingLongValue(
val minValue: Long = 0,
val maxValue: Long = 1,
val defaultValue: Long = 0
) : ConfigDelegate<Long>() {
override fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder) = builder.defineInRange(name, defaultValue, minValue, maxValue)
}
class DelegatingDoubleValue(
val minValue: Double = 0.0,
val maxValue: Double = 1.0,
val defaultValue: Double = 0.0
) : ConfigDelegate<Double>() {
override fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder) = builder.defineInRange(name, defaultValue, minValue, maxValue)
}
// ============================
// Delegate factory methods
// ============================
fun double(min: Double = 0.0, max: Double = 1.0, default: Double) = DelegatingDoubleValue(min, max, default)
fun int(min: Int = 0, max: Int, default: Int) = DelegatingIntValue(min, max, default)
fun long(min: Long = 0, max: Long, default: Long) = DelegatingLongValue(min, max, default)
fun boolean(default: Boolean) = DelegatingBooleanValue(default)