diff options
| author | hannibal2 <24389977+hannibal00212@users.noreply.github.com> | 2023-09-16 12:34:18 +0200 |
|---|---|---|
| committer | hannibal2 <24389977+hannibal00212@users.noreply.github.com> | 2023-09-16 12:34:18 +0200 |
| commit | 4293cfd919c3c93d4532534f722c407d7ad1370d (patch) | |
| tree | f9f612f021ef7f4283d74312edfaca30badc6749 /src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration | |
| parent | 538e3ceb76f8e0b590291ce9aa90aa94896cdcb6 (diff) | |
| parent | 024ba52fb69b6cd44b4e31542867f802de656f15 (diff) | |
| download | SkyHanni-cum.tar.gz SkyHanni-cum.tar.bz2 SkyHanni-cum.zip | |
Merge branch 'beta' into cumcum
# Conflicts:
# src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt
# src/main/java/at/hannibal2/skyhanni/config/features/AshfangConfig.java
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration')
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 |
