aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/Category.kt3
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigFeatures.kt99
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigOptionGui.kt197
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleProcessor.kt91
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleableOption.kt9
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/ResetSuggestionState.kt10
6 files changed, 409 insertions, 0 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/Category.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/Category.kt
new file mode 100644
index 000000000..3172df6e0
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/Category.kt
@@ -0,0 +1,3 @@
+package at.hannibal2.skyhanni.features.misc.massconfiguration
+
+data class Category(val name: String, val description: String) \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigFeatures.kt
new file mode 100644
index 000000000..ef9bd1e75
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigFeatures.kt
@@ -0,0 +1,99 @@
+package at.hannibal2.skyhanni.features.misc.massconfiguration
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.LorenzTickEvent
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import io.github.moulberry.moulconfig.processor.ConfigProcessorDriver
+import net.minecraft.client.Minecraft
+import net.minecraft.command.CommandBase
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+object DefaultConfigFeatures {
+ private var didNotifyOnce = false
+
+ @SubscribeEvent
+ fun onTick(event: LorenzTickEvent) {
+ if (didNotifyOnce) return
+ Minecraft.getMinecraft().thePlayer ?: return
+ didNotifyOnce = true
+
+ val knownToggles = SkyHanniMod.feature.storage.knownFeatureToggles
+ val updated = SkyHanniMod.version !in knownToggles
+ val processor = FeatureToggleProcessor()
+ ConfigProcessorDriver.processConfig(SkyHanniMod.feature.javaClass, SkyHanniMod.feature, processor)
+ knownToggles[SkyHanniMod.version] = processor.allOptions.map { it.path }
+ SkyHanniMod.configManager.saveConfig("Updated known feature flags")
+ if (!SkyHanniMod.feature.storage.hasPlayedBefore) {
+ SkyHanniMod.feature.storage.hasPlayedBefore = true
+ LorenzUtils.clickableChat(
+ "§e[SkyHanni] Looks like this is the first time you are using SkyHanni. " +
+ "Click here to configure default options, or run /shdefaultoptions.",
+ "shdefaultoptions"
+ )
+ } else if (updated) {
+ val mostFeatureFulOldVersion =
+ knownToggles.maxByOrNull { if (it.key != SkyHanniMod.version) it.value.size else -1 }
+ val command = "/shdefaultoptions ${mostFeatureFulOldVersion?.key} ${SkyHanniMod.version}"
+ LorenzUtils.clickableChat(
+ "§e[SkyHanni] Looks like you updated SkyHanni. " +
+ "Click here to configure the newly introduced options, or run $command.",
+ command
+ )
+ }
+ }
+
+ fun onCommand(old: String, new: String) {
+ val processor = FeatureToggleProcessor()
+ ConfigProcessorDriver.processConfig(SkyHanniMod.feature.javaClass, SkyHanniMod.feature, processor)
+ var optionList = processor.orderedOptions
+ val knownToggles = SkyHanniMod.feature.storage.knownFeatureToggles
+ val togglesInNewVersion = knownToggles[new]
+ if (new != "null" && togglesInNewVersion == null) {
+ LorenzUtils.chat("§e[SkyHanni] Unknown version $new")
+ return
+ }
+ val togglesInOldVersion = knownToggles[old]
+ if (old != "null" && togglesInOldVersion == null) {
+ LorenzUtils.chat("§e[SkyHanni] Unknown version $old")
+ return
+ }
+ optionList = optionList
+ .mapValues { it ->
+ it.value.filter {
+ (togglesInNewVersion == null || it.path in togglesInNewVersion) &&
+ (togglesInOldVersion == null || it.path !in togglesInOldVersion)
+ }
+ }
+ .filter { (_, filteredOptions) -> filteredOptions.isNotEmpty() }
+
+ SkyHanniMod.screenToOpen = DefaultConfigOptionGui(optionList, old, new)
+ }
+
+ fun applyCategorySelections(
+ resetSuggestionState: MutableMap<Category, ResetSuggestionState>,
+ orderedOptions: Map<Category, List<FeatureToggleableOption>>
+ ) {
+ orderedOptions.forEach { (cat, options) ->
+ for (option in options) {
+ val resetState = option.toggleOverride ?: resetSuggestionState[cat]!!
+ if (resetState == ResetSuggestionState.LEAVE_DEFAULTS) continue
+ val onState = option.isTrueEnabled
+ val setTo = if (resetState == ResetSuggestionState.TURN_ALL_ON) {
+ onState
+ } else {
+ !onState
+ }
+ option.setter(setTo)
+ }
+ }
+ }
+
+ fun onComplete(strings: Array<String>): List<String> {
+ if (strings.size <= 2)
+ return CommandBase.getListOfStringsMatchingLastWord(
+ strings,
+ SkyHanniMod.feature.storage.knownFeatureToggles.keys + listOf("null")
+ )
+ return listOf()
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigOptionGui.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigOptionGui.kt
new file mode 100644
index 000000000..ac1a497fe
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigOptionGui.kt
@@ -0,0 +1,197 @@
+package at.hannibal2.skyhanni.features.misc.massconfiguration
+
+import io.github.moulberry.moulconfig.internal.GlScissorStack
+import io.github.moulberry.moulconfig.internal.RenderUtils
+import io.github.moulberry.moulconfig.internal.TextRenderUtils
+import io.github.moulberry.notenoughupdates.util.Utils
+import net.minecraft.client.gui.GuiScreen
+import net.minecraft.client.gui.ScaledResolution
+import net.minecraft.client.renderer.GlStateManager
+import org.lwjgl.input.Mouse
+import kotlin.math.max
+import kotlin.math.min
+
+class DefaultConfigOptionGui(
+ private val orderedOptions: Map<Category, List<FeatureToggleableOption>>,
+ old: String,
+ new: String
+) :
+ GuiScreen() {
+ val title = if (old == "null") {
+ if (new == "null")
+ "§5SkyHanni Default Options"
+ else
+ "§5SkyHanni Options In Version $new"
+ } else {
+ if (new == "null")
+ "§5SkyHanni Options since $old"
+ else
+ "§5SkyHanni Options $old → $new"
+ }
+
+ private val xSize = 400
+ private val ySize = 300
+ private val barSize = 40
+ private val padding = 10
+ private var wasMouseDown = false
+ private val cardHeight = 30
+
+ private var currentScrollOffset = 0
+
+ private val resetSuggestionState =
+ orderedOptions.keys.associateWith { ResetSuggestionState.LEAVE_DEFAULTS }.toMutableMap()
+
+ override fun drawScreen(mouseX: Int, mouseY: Int, partialTicks: Float) {
+ super.drawScreen(mouseX, mouseY, partialTicks)
+ drawDefaultBackground()
+ RenderUtils.drawFloatingRectDark((width - xSize) / 2, (height - ySize) / 2, xSize, ySize)
+ val scaledResolution = ScaledResolution(mc)
+ var hoveringTextToDraw: List<String>? = null
+ val x = mouseX - ((width - xSize) / 2) - padding
+ val isMouseDown = Mouse.isButtonDown(0)
+ val shouldClick = isMouseDown && !wasMouseDown
+ wasMouseDown = isMouseDown
+ val isMouseInScrollArea =
+ x in 0..xSize && mouseY in ((height - ySize) / 2) + barSize..((height + ySize) / 2 - barSize)
+ var y = mouseY - ((height - ySize) / 2 + barSize) + currentScrollOffset
+
+ GlStateManager.pushMatrix()
+ GlStateManager.translate(width / 2F, (height - ySize) / 2F, 0F)
+ GlStateManager.scale(2f, 2f, 1f)
+ TextRenderUtils.drawStringCenteredScaledMaxWidth(
+ title,
+ mc.fontRendererObj,
+ 0F,
+ mc.fontRendererObj.FONT_HEIGHT.toFloat(),
+ false,
+ xSize / 2 - padding,
+ -1
+ )
+ GlStateManager.popMatrix()
+
+ GlStateManager.pushMatrix()
+ GlStateManager.translate(
+ (width - xSize) / 2F + padding,
+ (height + ySize) / 2F - mc.fontRendererObj.FONT_HEIGHT * 2,
+ 0F
+ )
+ var i = 0
+ fun button(title: String, tooltip: List<String>, func: () -> Unit) {
+ val width = mc.fontRendererObj.getStringWidth(title)
+ var overMouse = false
+ if (mouseX - ((this.width - xSize) / 2 + padding) in i..(i + width)
+ && mouseY - (height + ySize) / 2 in -barSize..0
+ ) {
+ overMouse = true
+ hoveringTextToDraw = tooltip
+ if (shouldClick) {
+ func()
+ }
+ }
+ RenderUtils.drawFloatingRectDark(i - 1, -3, width + 4, 14)
+ mc.fontRendererObj.drawString(
+ title,
+ 2 + i.toFloat(),
+ 0F,
+ if (overMouse) 0xFF00FF00.toInt() else -1,
+ overMouse
+ )
+ i += width + 12
+ }
+ button("Apply choices", listOf()) {
+ DefaultConfigFeatures.applyCategorySelections(resetSuggestionState, orderedOptions)
+ mc.displayGuiScreen(null)
+ }
+ button("Turn all on", listOf()) {
+ resetSuggestionState.entries.forEach { entry ->
+ entry.setValue(ResetSuggestionState.TURN_ALL_ON)
+ orderedOptions[entry.key]!!.forEach { it.toggleOverride = null }
+ }
+ }
+ button("Turn all off", listOf()) {
+ resetSuggestionState.entries.forEach { entry ->
+ entry.setValue(ResetSuggestionState.TURN_ALL_OFF)
+ orderedOptions[entry.key]!!.forEach { it.toggleOverride = null }
+ }
+ }
+ button("Leave all untouched", listOf()) {
+ resetSuggestionState.entries.forEach { entry ->
+ entry.setValue(ResetSuggestionState.LEAVE_DEFAULTS)
+ orderedOptions[entry.key]!!.forEach { it.toggleOverride = null }
+ }
+ }
+ button("Cancel", listOf()) {
+ mc.displayGuiScreen(null)
+ }
+ GlStateManager.popMatrix()
+
+ GlStateManager.pushMatrix()
+ GlScissorStack.push(
+ (width - xSize) / 2,
+ (height - ySize) / 2 + barSize,
+ (width + xSize) / 2,
+ (height + ySize) / 2 - barSize,
+ scaledResolution
+ )
+ GlStateManager.translate(
+ (width - xSize) / 2F + padding,
+ (height - ySize) / 2F + barSize - currentScrollOffset,
+ 0F
+ )
+
+ for ((cat) in orderedOptions.entries) {
+ val suggestionState = resetSuggestionState[cat]!!
+
+ drawRect(0, 0, xSize - padding * 2, 1, 0xFF808080.toInt())
+ drawRect(0, 30, xSize - padding * 2, cardHeight + 1, 0xFF808080.toInt())
+ drawRect(0, 0, 1, cardHeight, 0xFF808080.toInt())
+ drawRect(xSize - padding * 2 - 1, 0, xSize - padding * 2, cardHeight, 0xFF808080.toInt())
+
+ mc.fontRendererObj.drawString("§e${cat.name} ${suggestionState.label}", 4, 4, -1)
+ mc.fontRendererObj.drawSplitString("§7${cat.description}", 4, 14, xSize - padding * 2 - 8, -1)
+
+ if (isMouseInScrollArea && y in 0..cardHeight) {
+ hoveringTextToDraw = listOf(
+ "§e${cat.name}",
+ "§7${cat.description}",
+ "§7Current plan: ${suggestionState.label}",
+ "§aClick to toggle!",
+ "§7Hold shift to show all options"
+ )
+
+ if (isShiftKeyDown()) {
+ hoveringTextToDraw = listOf(
+ "§e${cat.name}",
+ "§7${cat.description}"
+ ) + orderedOptions[cat]!!.map { "§7 - §a" + it.name }
+ }
+
+ if (shouldClick) {
+ resetSuggestionState[cat] = suggestionState.next
+ orderedOptions[cat]!!.forEach { it.toggleOverride = null }
+ }
+ }
+
+ y -= cardHeight
+ GlStateManager.translate(0F, cardHeight.toFloat(), 0F)
+ }
+
+ GlStateManager.popMatrix()
+ GlScissorStack.pop(scaledResolution)
+ if (hoveringTextToDraw != null) {
+ Utils.drawHoveringText(hoveringTextToDraw, mouseX, mouseY, width, height, 100, mc.fontRendererObj)
+ }
+
+ }
+
+ private fun scroll(s: Int) {
+ currentScrollOffset =
+ max(0, min(s, (orderedOptions.size + 1) * cardHeight - ySize + barSize + padding * 2))
+ }
+
+ override fun handleMouseInput() {
+ super.handleMouseInput()
+ if (Mouse.getEventDWheel() != 0)
+ scroll(currentScrollOffset - Mouse.getEventDWheel())
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleProcessor.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleProcessor.kt
new file mode 100644
index 000000000..b3d3f74b7
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleProcessor.kt
@@ -0,0 +1,91 @@
+package at.hannibal2.skyhanni.features.misc.massconfiguration
+
+import at.hannibal2.skyhanni.config.FeatureToggle
+import io.github.moulberry.moulconfig.annotations.ConfigEditorBoolean
+import io.github.moulberry.moulconfig.annotations.ConfigOption
+import io.github.moulberry.moulconfig.observer.Property
+import io.github.moulberry.moulconfig.processor.ConfigStructureReader
+import java.lang.reflect.Field
+import java.lang.reflect.ParameterizedType
+import java.util.*
+
+class FeatureToggleProcessor : ConfigStructureReader {
+
+ private var latestCategory: Category? = null
+ private val pathStack = Stack<String>()
+ private val accordionStack = Stack<String>()
+
+ val allOptions = mutableListOf<FeatureToggleableOption>()
+ val orderedOptions by lazy {
+ allOptions.groupBy { it.category }
+ }
+
+ override fun beginCategory(baseObject: Any?, field: Field?, name: String, description: String) {
+ latestCategory = Category(name, description)
+ }
+
+ override fun endCategory() {
+ }
+
+ override fun beginAccordion(baseObject: Any?, field: Field?, o: ConfigOption?, id: Int) {
+ val option = o ?: return
+ accordionStack.push(option.name)
+ }
+
+ override fun endAccordion() {
+ accordionStack.pop()
+ }
+
+ override fun pushPath(fieldPath: String) {
+ pathStack.push(fieldPath)
+ }
+
+ override fun popPath() {
+ pathStack.pop()
+ }
+
+ override fun emitOption(baseObject: Any, field: Field, option: ConfigOption) {
+ val featureToggle = field.getAnnotation(FeatureToggle::class.java) ?: return
+ field.getAnnotation(ConfigEditorBoolean::class.java)
+ ?: error("Feature toggle found without ConfigEditorBoolean: $field")
+ val setter: (Boolean) -> Unit
+ val value: Boolean
+ when (field.type) {
+ java.lang.Boolean.TYPE -> {
+ setter = { field.setBoolean(baseObject, it) }
+ value = field.getBoolean(baseObject)
+ }
+
+ Property::class.java -> {
+ val genericType = field.genericType
+ require(genericType is ParameterizedType)
+ require((genericType.actualTypeArguments[0] as Class<*>) == (java.lang.Boolean::class.java))
+ val prop = field.get(baseObject) as Property<Boolean>
+ setter = { prop.set(it) }
+ value = prop.get()
+ }
+
+ else -> error("Invalid FeatureToggle type: $field")
+ }
+
+ var name = option.name
+ if ((name == "Enable" || name == "Enabled") && !accordionStack.empty()) {
+ name = accordionStack.peek()
+ }
+
+ allOptions.add(
+ FeatureToggleableOption(
+ name,
+ option.desc,
+ value,
+ featureToggle.trueIsEnabled,
+ latestCategory!!,
+ setter,
+ pathStack.joinToString(".") + "." + field.name
+ )
+ )
+ }
+
+ override fun emitGuiOverlay(baseObject: Any?, field: Field?, option: ConfigOption?) {
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleableOption.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleableOption.kt
new file mode 100644
index 000000000..88b9f1a8e
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleableOption.kt
@@ -0,0 +1,9 @@
+package at.hannibal2.skyhanni.features.misc.massconfiguration
+
+data class FeatureToggleableOption(
+ val name: String, val description: String, val previouslyEnabled: Boolean,
+ val isTrueEnabled: Boolean, val category: Category,
+ val setter: (Boolean) -> Unit,
+ val path: String,
+ var toggleOverride: ResetSuggestionState? = null
+) \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/ResetSuggestionState.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/ResetSuggestionState.kt
new file mode 100644
index 000000000..30a78a7b2
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/ResetSuggestionState.kt
@@ -0,0 +1,10 @@
+package at.hannibal2.skyhanni.features.misc.massconfiguration
+
+enum class ResetSuggestionState(val label: String) {
+ TURN_ALL_OFF("§c§lTurn all off"),
+ TURN_ALL_ON("§a§lTurn all on"),
+ LEAVE_DEFAULTS("§b§lLeave unchanged"),
+ ;
+
+ val next get() = entries[(ordinal + 1) % entries.size]
+} \ No newline at end of file