save selected profile

This commit is contained in:
Moritz Zwerger 2023-11-20 22:38:53 +01:00
parent 0ff5430ac4
commit 0fe5b949ad
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
2 changed files with 45 additions and 10 deletions

View File

@ -45,8 +45,10 @@ object ProfileIOManager {
lock.unlock()
notify.awaitOrChange()
Thread.sleep(50L) // sometimes changes happen very quickly, we don't want to save 10 times while in change
while (notify.count == 0) {
notify.awaitOrChange()
Thread.sleep(50L) // sometimes changes happen very quickly, we don't want to save 10 times while in change
}
}
private fun MutableSet<FileStorage>.work(worker: (Profile, FileStorage, StorageProfileManager<Profile>) -> Unit) {

View File

@ -21,11 +21,14 @@ import de.bixilon.kutil.cast.CastUtil.unsafeNull
import de.bixilon.kutil.collections.CollectionUtil.mutableBiMapOf
import de.bixilon.kutil.collections.map.bi.AbstractMutableBiMap
import de.bixilon.kutil.concurrent.lock.simple.SimpleLock
import de.bixilon.kutil.concurrent.pool.DefaultThreadPool
import de.bixilon.kutil.exception.Broken
import de.bixilon.kutil.file.watcher.FileWatcherService
import de.bixilon.kutil.observer.DataObserver.Companion.observe
import de.bixilon.kutil.observer.DataObserver.Companion.observed
import de.bixilon.kutil.observer.map.bi.BiMapObserver.Companion.observedBiMap
import de.bixilon.minosoft.assets.util.FileUtil.mkdirParent
import de.bixilon.minosoft.assets.util.InputStreamUtil.readAsString
import de.bixilon.minosoft.config.profile.ProfileType
import de.bixilon.minosoft.config.profile.profiles.Profile
import de.bixilon.minosoft.config.profile.storage.ProfileIOUtil.isValidName
@ -62,7 +65,7 @@ abstract class StorageProfileManager<P : Profile> : Iterable<P>, Identified {
open fun migrate(version: Int, data: ObjectNode) = Unit
open fun migrate(data: ObjectNode): Int {
val version = data["version"]?.intValue() ?: throw IllegalArgumentException("Data has no version set!")
val version = data[VERSION]?.intValue() ?: throw IllegalArgumentException("Data has no version set!")
when {
version == latestVersion -> return -1
version > latestVersion -> throw IllegalArgumentException("Profile was created with a newer version!")
@ -82,27 +85,46 @@ abstract class StorageProfileManager<P : Profile> : Iterable<P>, Identified {
}
private fun load(name: String, path: File): P {
Log.log(LogMessageType.PROFILES, LogLevels.VERBOSE) { "Loading profile from $path" }
val stream = FileInputStream(path)
val content = Jackson.MAPPER.readTree(stream).unsafeCast<ObjectNode>()
stream.close()
val storage = FileStorage(name, this, path)
Log.log(LogMessageType.PROFILES, LogLevels.VERBOSE) { "Loading profile from $path" }
return load(storage, content)
}
private fun loadSelected(root: File): String? {
val file = root.resolve(SELECTED)
if (!file.isFile) return null
val stream = FileInputStream(file)
return stream.readAsString()
}
private fun saveSelected(root: File, name: String) {
val file = root.resolve(SELECTED)
file.mkdirParent()
val stream = FileOutputStream(file)
stream.write(name.encodeNetwork())
stream.close()
}
open fun load() {
val root = RunConfiguration.CONFIG_DIRECTORY.resolve(identifier.namespace).resolve(identifier.path).toFile()
if (!root.exists()) {
root.mkdirs()
return createDefault()
}
var selected = DEFAULT_NAME // root.resolve("selected") TODO
var selected = loadSelected(root)
if (selected == null) {
selected = DEFAULT_NAME
saveSelected(root, selected)
}
if (!selected.isValidName()) selected = DEFAULT_NAME
val files = root.listFiles() ?: return createDefault()
for (file in files) {
if (!file.name.endsWith(".json")) continue
val name = file.name.removeSuffix(".json")
if (!file.name.endsWith(FILE_SUFFIX)) continue
val name = file.name.removeSuffix(FILE_SUFFIX)
if (!name.isValidName()) {
Log.log(LogMessageType.PROFILES, LogLevels.WARN) { "Not loading $file: Invalid name!" }
continue
@ -120,6 +142,14 @@ abstract class StorageProfileManager<P : Profile> : Iterable<P>, Identified {
}
this.selected = this[selected] ?: create(selected)
this::selected.observe(this) {
val name = it.storage.unsafeCast<FileStorage>().name
DefaultThreadPool += {
this.lock.acquire()
saveSelected(root, name)
this.lock.release()
}
}
observe(root.toPath())
}
@ -127,8 +157,8 @@ abstract class StorageProfileManager<P : Profile> : Iterable<P>, Identified {
private fun observe(root: Path) {
FileWatcherService.watchAsync(root, setOf(ENTRY_MODIFY, ENTRY_CREATE)) { _, path ->
val filename = path.fileName.toString()
if (!filename.endsWith(".json")) return@watchAsync
val profile = this[filename.removeSuffix(".json")] ?: return@watchAsync
if (!filename.endsWith(FILE_SUFFIX)) return@watchAsync
val profile = this[filename.removeSuffix(FILE_SUFFIX)] ?: return@watchAsync
val storage = profile.storage?.nullCast<FileStorage>() ?: return@watchAsync
if (storage.path.toPath() != path) return@watchAsync
ProfileIOManager.reload(storage)
@ -187,7 +217,7 @@ abstract class StorageProfileManager<P : Profile> : Iterable<P>, Identified {
profile.lock.acquire()
storage.saved++
val node = Jackson.MAPPER.valueToTree<ObjectNode>(profile) // TODO: cache jacksonType
node.put("version", latestVersion)
node.put(VERSION, latestVersion)
val string = Jackson.MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(node)
val stream = FileOutputStream(path)
stream.write(string.encodeNetwork())
@ -228,5 +258,8 @@ abstract class StorageProfileManager<P : Profile> : Iterable<P>, Identified {
companion object {
const val DEFAULT_NAME = "Default"
const val SELECTED = "selected"
const val VERSION = "version"
const val FILE_SUFFIX = ".json"
}
}