aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2023-09-16 13:19:12 +0200
committerGitHub <noreply@github.com>2023-09-16 13:19:12 +0200
commit83b5cd3c93bab32f16365c6b6810362f4de5598e (patch)
treef9f612f021ef7f4283d74312edfaca30badc6749 /src/main/java
parent024ba52fb69b6cd44b4e31542867f802de656f15 (diff)
downloadskyhanni-83b5cd3c93bab32f16365c6b6810362f4de5598e.tar.gz
skyhanni-83b5cd3c93bab32f16365c6b6810362f4de5598e.tar.bz2
skyhanni-83b5cd3c93bab32f16365c6b6810362f4de5598e.zip
Config migration and also more atomic config saves (#420)
Config migration and also more atomic config saves #420
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt20
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/ConfigUpdaterMigrator.kt85
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/Features.java4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/AshfangConfig.java2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/nether/ashfang/AshfangFreezeCooldown.kt13
5 files changed, 114 insertions, 10 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt b/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt
index a882af5d4..23e9b6261 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt
+++ b/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt
@@ -10,6 +10,7 @@ import at.hannibal2.skyhanni.utils.NEUInternalName
import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName
import at.hannibal2.skyhanni.utils.NEUItems
import com.google.gson.GsonBuilder
+import com.google.gson.JsonObject
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
@@ -100,10 +101,6 @@ class ConfigManager {
configFile = File(configDirectory, "config.json")
sackFile = File(configDirectory, "sacks.json")
- fixedRateTimer(name = "skyhanni-config-auto-save", period = 60_000L, initialDelay = 60_000L) {
- saveConfig("auto-save-60s")
- }
-
logger.log("Trying to load config from $configFile")
if (configFile!!.exists()) {
@@ -119,8 +116,10 @@ class ConfigManager {
logger.log("load-config-now")
+ val jsonObject = gson.fromJson(builder.toString(), JsonObject::class.java)
+ val newJsonObject = ConfigUpdaterMigrator.fixConfig(jsonObject)
features = gson.fromJson(
- builder.toString(),
+ newJsonObject,
Features::class.java
)
logger.log("Loaded config from file")
@@ -166,6 +165,10 @@ class ConfigManager {
saveConfig("blank config")
}
+ fixedRateTimer(name = "skyhanni-config-auto-save", period = 60_000L, initialDelay = 60_000L) {
+ saveConfig("auto-save-60s")
+ }
+
if (!::sackData.isInitialized) {
logger.log("Creating blank sack data and saving")
sackData = SackData()
@@ -201,11 +204,14 @@ class ConfigManager {
try {
logger.log("Saving config file")
file.parentFile.mkdirs()
- file.createNewFile()
- BufferedWriter(OutputStreamWriter(FileOutputStream(file), StandardCharsets.UTF_8)).use { writer ->
+ val unit = file.parentFile.resolve("config.json.write")
+ unit.createNewFile()
+ BufferedWriter(OutputStreamWriter(FileOutputStream(unit), StandardCharsets.UTF_8)).use { writer ->
// TODO remove old "hidden" area
writer.write(gson.toJson(SkyHanniMod.feature))
}
+ // Perform move — which is atomic, unlike writing — after writing is done.
+ unit.renameTo(file)
} catch (e: IOException) {
logger.log("Could not save config file to $file")
e.printStackTrace()
diff --git a/src/main/java/at/hannibal2/skyhanni/config/ConfigUpdaterMigrator.kt b/src/main/java/at/hannibal2/skyhanni/config/ConfigUpdaterMigrator.kt
new file mode 100644
index 000000000..6b590a389
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/ConfigUpdaterMigrator.kt
@@ -0,0 +1,85 @@
+package at.hannibal2.skyhanni.config
+
+import at.hannibal2.skyhanni.events.LorenzEvent
+import at.hannibal2.skyhanni.utils.LorenzLogger
+import com.google.gson.JsonElement
+import com.google.gson.JsonObject
+import com.google.gson.JsonPrimitive
+
+object ConfigUpdaterMigrator {
+ val logger = LorenzLogger("ConfigMigration")
+ val configVersion = 0
+ fun JsonElement.at(chain: List<String>, init: Boolean): JsonElement? {
+ if (chain.isEmpty()) return this
+ if (this !is JsonObject) return null
+ var obj = this.get(chain.first())
+ if (obj == null && init) {
+ obj = JsonObject()
+ this.add(chain.first(), obj)
+ }
+ return obj?.at(chain.drop(1), init)
+ }
+
+
+ data class ConfigFixEvent(
+ val old: JsonObject,
+ val new: JsonObject,
+ val oldVersion: Int,
+ var movesPerformed: Int,
+ ) : LorenzEvent() {
+ fun move(since: Int, oldPath: String, newPath: String, transform: (JsonElement) -> JsonElement = { it }) {
+ if (since <= oldVersion) {
+ logger.log("Skipping move from $oldPath to $newPath ($since <= $oldVersion)")
+ return
+ }
+ val op = oldPath.split(".")
+ val np = newPath.split(".")
+ val oldElem = old.at(op, false)
+ if (oldElem == null) {
+ logger.log("Skipping move from $oldPath to $newPath ($oldPath not present)")
+ return
+ }
+ val x = new.at(np.dropLast(1), true)
+ if (x !is JsonObject) {
+ logger.log("Catastrophic: element at path $old could not be relocated to $new, since another element already inhabits that path")
+ return
+ }
+ movesPerformed++
+ x.add(np.last(), transform(oldElem))
+ logger.log("Moved element from $oldPath to $newPath")
+ }
+ }
+
+ fun merge(a: JsonObject, b: JsonObject): Int {
+ var c = 0
+ b.entrySet().forEach {
+ val e = a.get(it.key)
+ val n = it.value
+ if (e is JsonObject && n is JsonObject) {
+ c += merge(e, n)
+ } else {
+ if (e != null) {
+ logger.log("Encountered destructive merge. Erasing $e in favour of $n.")
+ c++
+ }
+ a.add(it.key, n)
+ }
+ }
+ return c
+ }
+
+ fun fixConfig(config: JsonObject): JsonObject {
+ val lV = (config.get("lastVersion") as? JsonPrimitive)
+ ?.takeIf { it.isNumber }?.asInt ?: -1
+ if (lV == configVersion) return config
+ logger.log("Starting config transformation from $lV to $configVersion")
+ val migration = ConfigFixEvent(config, JsonObject().also {
+ it.add("lastVersion", JsonPrimitive(configVersion))
+ }, lV, 0).also { it.postAndCatch() }
+ logger.log("Transformations scheduled: ${migration.new}")
+ val mergesPerformed = merge(migration.old, migration.new)
+ logger.log("Migration done with $mergesPerformed merges and ${migration.movesPerformed} moves performed")
+ return migration.old
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/config/Features.java b/src/main/java/at/hannibal2/skyhanni/config/Features.java
index 541b6e72b..149f66020 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/Features.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/Features.java
@@ -136,4 +136,8 @@ public class Features extends Config {
@Expose
public Storage storage = new Storage();
+
+ @Expose
+ public int lastVersion = ConfigUpdaterMigrator.INSTANCE.getConfigVersion();
+
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/AshfangConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/AshfangConfig.java
index 575a1987d..9cffc4ed8 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/AshfangConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/AshfangConfig.java
@@ -13,7 +13,7 @@ public class AshfangConfig {
@ConfigOption(name = "Freeze", desc = "Show the cooldown for how long Ashfang blocks your abilities.")
@ConfigEditorBoolean
@FeatureToggle
- public boolean freezeCooldown = false;
+ public boolean brrrrItsSoColdCooldownCooldown = false;
@Expose
public Position freezeCooldownPos = new Position(10, 10, false, true);
diff --git a/src/main/java/at/hannibal2/skyhanni/features/nether/ashfang/AshfangFreezeCooldown.kt b/src/main/java/at/hannibal2/skyhanni/features/nether/ashfang/AshfangFreezeCooldown.kt
index 0bccfe254..264b443eb 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/nether/ashfang/AshfangFreezeCooldown.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/nether/ashfang/AshfangFreezeCooldown.kt
@@ -1,6 +1,7 @@
package at.hannibal2.skyhanni.features.nether.ashfang
import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator
import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.events.LorenzChatEvent
import at.hannibal2.skyhanni.features.damageindicator.BossType
@@ -34,12 +35,20 @@ class AshfangFreezeCooldown {
val remainingLong = maxDuration - duration
if (remainingLong > 0) {
var format = TimeUtils.formatDuration(remainingLong, showMilliSeconds = true)
- SkyHanniMod.feature.ashfang.freezeCooldownPos.renderString("§cAshfang Freeze: §a$format", posLabel = "Ashfang Freeze Cooldown")
+ SkyHanniMod.feature.ashfang.freezeCooldownPos.renderString(
+ "§cAshfang Freeze: §a$format",
+ posLabel = "Ashfang Freeze Cooldown"
+ )
}
}
+ @SubscribeEvent
+ fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) {
+ event.move(0, "ashfang.freezeCooldown", "ashfang.brrrrItsSoColdCooldownCooldown")
+ }
+
private fun isEnabled(): Boolean {
- return LorenzUtils.inSkyBlock && SkyHanniMod.feature.ashfang.freezeCooldown &&
+ return LorenzUtils.inSkyBlock && SkyHanniMod.feature.ashfang.brrrrItsSoColdCooldownCooldown &&
DamageIndicatorManager.isBossSpawned(BossType.NETHER_ASHFANG)
}
} \ No newline at end of file