mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-17 03:15:35 -04:00
profile reading
This commit is contained in:
parent
7f1c74c36f
commit
c37164f63f
@ -33,7 +33,7 @@ import de.bixilon.kutil.unit.UnitFormatter.formatNanos
|
|||||||
import de.bixilon.minosoft.assets.file.ResourcesAssetsUtil
|
import de.bixilon.minosoft.assets.file.ResourcesAssetsUtil
|
||||||
import de.bixilon.minosoft.assets.meta.MinosoftMeta
|
import de.bixilon.minosoft.assets.meta.MinosoftMeta
|
||||||
import de.bixilon.minosoft.assets.properties.version.AssetsVersionProperties
|
import de.bixilon.minosoft.assets.properties.version.AssetsVersionProperties
|
||||||
import de.bixilon.minosoft.config.profile.manager.GlobalProfileManager
|
import de.bixilon.minosoft.config.profile.manager.ProfileManagers
|
||||||
import de.bixilon.minosoft.config.profile.profiles.eros.ErosProfileManager
|
import de.bixilon.minosoft.config.profile.profiles.eros.ErosProfileManager
|
||||||
import de.bixilon.minosoft.data.entities.event.EntityEvents
|
import de.bixilon.minosoft.data.entities.event.EntityEvents
|
||||||
import de.bixilon.minosoft.data.language.LanguageUtil
|
import de.bixilon.minosoft.data.language.LanguageUtil
|
||||||
@ -102,7 +102,7 @@ object Minosoft {
|
|||||||
|
|
||||||
taskWorker += WorkerTask(identifier = BootTasks.VERSIONS, priority = ThreadPool.HIGHER, executor = VersionLoader::load)
|
taskWorker += WorkerTask(identifier = BootTasks.VERSIONS, priority = ThreadPool.HIGHER, executor = VersionLoader::load)
|
||||||
taskWorker += WorkerTask(identifier = BootTasks.FILE_WATCHER, priority = ThreadPool.HIGHER, optional = true, executor = this::startFileWatcherService)
|
taskWorker += WorkerTask(identifier = BootTasks.FILE_WATCHER, priority = ThreadPool.HIGHER, optional = true, executor = this::startFileWatcherService)
|
||||||
taskWorker += WorkerTask(identifier = BootTasks.PROFILES, priority = ThreadPool.HIGHER, dependencies = arrayOf(BootTasks.FILE_WATCHER), executor = GlobalProfileManager::initialize)
|
taskWorker += WorkerTask(identifier = BootTasks.PROFILES, priority = ThreadPool.HIGHER, dependencies = arrayOf(BootTasks.FILE_WATCHER), executor = ProfileManagers::load)
|
||||||
|
|
||||||
taskWorker += WorkerTask(identifier = BootTasks.LANGUAGE_FILES, dependencies = arrayOf(BootTasks.PROFILES), executor = this::loadLanguageFiles)
|
taskWorker += WorkerTask(identifier = BootTasks.LANGUAGE_FILES, dependencies = arrayOf(BootTasks.PROFILES), executor = this::loadLanguageFiles)
|
||||||
taskWorker += WorkerTask(identifier = BootTasks.ASSETS_PROPERTIES, dependencies = arrayOf(BootTasks.VERSIONS), executor = AssetsVersionProperties::load)
|
taskWorker += WorkerTask(identifier = BootTasks.ASSETS_PROPERTIES, dependencies = arrayOf(BootTasks.VERSIONS), executor = AssetsVersionProperties::load)
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.config.profile.manager
|
package de.bixilon.minosoft.config.profile.manager
|
||||||
|
|
||||||
|
import de.bixilon.kutil.concurrent.worker.unconditional.UnconditionalWorker
|
||||||
|
import de.bixilon.kutil.latch.AbstractLatch
|
||||||
import de.bixilon.minosoft.config.profile.profiles.account.AccountProfileManager
|
import de.bixilon.minosoft.config.profile.profiles.account.AccountProfileManager
|
||||||
import de.bixilon.minosoft.config.profile.profiles.audio.AudioProfileManager
|
import de.bixilon.minosoft.config.profile.profiles.audio.AudioProfileManager
|
||||||
import de.bixilon.minosoft.config.profile.profiles.block.BlockProfileManager
|
import de.bixilon.minosoft.config.profile.profiles.block.BlockProfileManager
|
||||||
@ -41,4 +43,14 @@ object ProfileManagers : DefaultFactory<StorageProfileManager<*>>(
|
|||||||
GUIProfileManager,
|
GUIProfileManager,
|
||||||
ControlsProfileManager,
|
ControlsProfileManager,
|
||||||
OtherProfileManager,
|
OtherProfileManager,
|
||||||
)
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
fun load(latch: AbstractLatch?) {
|
||||||
|
val worker = UnconditionalWorker()
|
||||||
|
for (manager in ProfileManagers) {
|
||||||
|
worker += { manager.load() }
|
||||||
|
}
|
||||||
|
worker.work(latch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -14,12 +14,10 @@
|
|||||||
package de.bixilon.minosoft.config.profile.profiles
|
package de.bixilon.minosoft.config.profile.profiles
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude
|
|
||||||
import de.bixilon.kutil.concurrent.lock.Lock
|
import de.bixilon.kutil.concurrent.lock.Lock
|
||||||
import de.bixilon.minosoft.config.profile.storage.ProfileStorage
|
import de.bixilon.minosoft.config.profile.storage.ProfileStorage
|
||||||
|
|
||||||
interface Profile {
|
interface Profile {
|
||||||
@get:JsonIgnore val storage: ProfileStorage?
|
@get:JsonIgnore val storage: ProfileStorage?
|
||||||
@get:JsonInclude(JsonInclude.Include.NON_NULL) val version: Int? get() = storage?.version
|
|
||||||
@get:JsonIgnore val lock: Lock
|
@get:JsonIgnore val lock: Lock
|
||||||
}
|
}
|
||||||
|
@ -11,19 +11,19 @@
|
|||||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.bixilon.minosoft.config.profile.manager
|
package de.bixilon.minosoft.config.profile.storage
|
||||||
|
|
||||||
import de.bixilon.kutil.latch.AbstractLatch
|
class FileStorage(
|
||||||
import de.bixilon.minosoft.terminal.RunConfiguration
|
override val name: String,
|
||||||
|
val manager: StorageProfileManager<*>,
|
||||||
|
override val path: String,
|
||||||
|
) : ProfileStorage {
|
||||||
|
var updating = false
|
||||||
|
var invalid = false
|
||||||
|
|
||||||
object GlobalProfileManager {
|
|
||||||
private val SELECTED_PROFILES_PATH = RunConfiguration.CONFIG_DIRECTORY.resolve("selected_profiles.json").toFile()
|
|
||||||
|
|
||||||
init {
|
override fun invalidate() {
|
||||||
|
if (updating) return
|
||||||
|
invalid = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
fun initialize(latch: AbstractLatch?) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -11,10 +11,12 @@
|
|||||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.bixilon.minosoft.config.profile
|
package de.bixilon.minosoft.config.profile.storage
|
||||||
|
|
||||||
data class ProfileState(
|
object ProfileIOUtil {
|
||||||
var initializing: Boolean = true,
|
val NAME_REGEX = "[\\w_ ]{1,32}".toRegex()
|
||||||
var reloading: Boolean = false,
|
|
||||||
var saving: Boolean = false,
|
fun String.isValidName(): Boolean {
|
||||||
)
|
return NAME_REGEX.matches(this)
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,6 @@ package de.bixilon.minosoft.config.profile.storage
|
|||||||
|
|
||||||
interface ProfileStorage {
|
interface ProfileStorage {
|
||||||
val name: String
|
val name: String
|
||||||
val version: Int
|
|
||||||
val path: String?
|
val path: String?
|
||||||
|
|
||||||
fun invalidate() = Unit
|
fun invalidate() = Unit
|
||||||
|
@ -13,17 +13,34 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.config.profile.storage
|
package de.bixilon.minosoft.config.profile.storage
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.InjectableValues
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode
|
||||||
|
import de.bixilon.kutil.cast.CastUtil.nullCast
|
||||||
import de.bixilon.kutil.cast.CastUtil.unsafeNull
|
import de.bixilon.kutil.cast.CastUtil.unsafeNull
|
||||||
import de.bixilon.kutil.collections.CollectionUtil.mutableBiMapOf
|
import de.bixilon.kutil.collections.CollectionUtil.mutableBiMapOf
|
||||||
import de.bixilon.kutil.collections.map.bi.AbstractMutableBiMap
|
import de.bixilon.kutil.collections.map.bi.AbstractMutableBiMap
|
||||||
|
import de.bixilon.kutil.exception.Broken
|
||||||
import de.bixilon.kutil.json.MutableJsonObject
|
import de.bixilon.kutil.json.MutableJsonObject
|
||||||
import de.bixilon.kutil.observer.DataObserver.Companion.observed
|
import de.bixilon.kutil.observer.DataObserver.Companion.observed
|
||||||
import de.bixilon.kutil.observer.map.bi.BiMapObserver.Companion.observedBiMap
|
import de.bixilon.kutil.observer.map.bi.BiMapObserver.Companion.observedBiMap
|
||||||
import de.bixilon.kutil.primitive.IntUtil.toInt
|
import de.bixilon.kutil.primitive.IntUtil.toInt
|
||||||
|
import de.bixilon.minosoft.assets.util.FileUtil.mkdirParent
|
||||||
|
import de.bixilon.minosoft.assets.util.InputStreamUtil.readJsonObject
|
||||||
import de.bixilon.minosoft.config.profile.ProfileType
|
import de.bixilon.minosoft.config.profile.ProfileType
|
||||||
import de.bixilon.minosoft.config.profile.profiles.Profile
|
import de.bixilon.minosoft.config.profile.profiles.Profile
|
||||||
|
import de.bixilon.minosoft.config.profile.storage.ProfileIOUtil.isValidName
|
||||||
import de.bixilon.minosoft.data.registries.identified.Identified
|
import de.bixilon.minosoft.data.registries.identified.Identified
|
||||||
|
import de.bixilon.minosoft.protocol.ProtocolUtil.encodeNetwork
|
||||||
|
import de.bixilon.minosoft.terminal.RunConfiguration
|
||||||
import de.bixilon.minosoft.util.json.Jackson
|
import de.bixilon.minosoft.util.json.Jackson
|
||||||
|
import de.bixilon.minosoft.util.logging.Log
|
||||||
|
import de.bixilon.minosoft.util.logging.LogLevels
|
||||||
|
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import kotlin.io.path.absolutePathString
|
||||||
|
|
||||||
|
|
||||||
abstract class StorageProfileManager<P : Profile> : Iterable<P>, Identified {
|
abstract class StorageProfileManager<P : Profile> : Iterable<P>, Identified {
|
||||||
@ -40,26 +57,124 @@ abstract class StorageProfileManager<P : Profile> : Iterable<P>, Identified {
|
|||||||
|
|
||||||
|
|
||||||
open fun migrate(version: Int, data: MutableJsonObject) = Unit
|
open fun migrate(version: Int, data: MutableJsonObject) = Unit
|
||||||
open fun migrate(data: MutableJsonObject) {
|
open fun migrate(data: MutableJsonObject): Int {
|
||||||
val version = data["version"]?.toInt() ?: throw IllegalArgumentException("Data has no version set!")
|
val version = data["version"]?.toInt() ?: throw IllegalArgumentException("Data has no version set!")
|
||||||
when {
|
when {
|
||||||
version == latestVersion -> return
|
version == latestVersion -> return -1
|
||||||
version > latestVersion -> throw IllegalArgumentException("Profile was created with a newer version!")
|
version > latestVersion -> throw IllegalArgumentException("Profile was created with a newer version!")
|
||||||
version < latestVersion -> {
|
version < latestVersion -> {
|
||||||
for (version in version until latestVersion) {
|
for (version in version until latestVersion) {
|
||||||
migrate(version, data)
|
migrate(version, data)
|
||||||
}
|
}
|
||||||
// TODO: log, save
|
return version
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Broken()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun load(name: String, data: MutableJsonObject) = Unit
|
private fun createDefault() {
|
||||||
fun create(name: String): P = TODO()
|
val default = create(DEFAULT_NAME)
|
||||||
|
selected = default
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadAll() {
|
||||||
|
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")
|
||||||
|
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 (!name.isValidName()) {
|
||||||
|
Log.log(LogMessageType.PROFILES, LogLevels.WARN) { "Not loading $file: Invalid name!" }
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val profile = load(name, file)
|
||||||
|
profiles[name] = profile
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selected = this[selected] ?: create(selected)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun load(name: String, path: File): P {
|
||||||
|
val content = FileInputStream(path).readJsonObject(true).toMutableMap() // TODO: is copy needed?
|
||||||
|
val storage = FileStorage(name, this, path.absolutePath)
|
||||||
|
return load(storage, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun load() {
|
||||||
|
loadAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun load(storage: FileStorage, data: MutableJsonObject): P {
|
||||||
|
val profile = type.create(storage)
|
||||||
|
update(profile, data)
|
||||||
|
return profile
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(profile: P, data: MutableJsonObject) {
|
||||||
|
val storage = profile.storage.nullCast<FileStorage>() ?: throw IllegalArgumentException("Storage not set!")
|
||||||
|
val migrated = migrate(data)
|
||||||
|
if (migrated >= 0) {
|
||||||
|
Log.log(LogMessageType.PROFILES, LogLevels.INFO) { "Profile ${storage.name} (type=$identifier) was migrated from version $migrated to $latestVersion" }
|
||||||
|
storage.invalidate()
|
||||||
|
}
|
||||||
|
profile.lock.lock()
|
||||||
|
storage.updating = true
|
||||||
|
|
||||||
|
val injectable = InjectableValues.Std()
|
||||||
|
injectable.addValue(type.clazz, profile)
|
||||||
|
Jackson.MAPPER
|
||||||
|
.readerForUpdating(profile)
|
||||||
|
.with(injectable)
|
||||||
|
.readValue<P>(Jackson.MAPPER.valueToTree(data) as JsonNode)
|
||||||
|
|
||||||
|
storage.updating = false
|
||||||
|
storage.invalid = false
|
||||||
|
profile.lock.unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun create(name: String): P {
|
||||||
|
if (!name.isValidName()) throw IllegalArgumentException("Invalid profile name!")
|
||||||
|
val path = RunConfiguration.CONFIG_DIRECTORY.resolve(identifier.namespace).resolve(identifier.path).resolve("$name.json")
|
||||||
|
val storage = FileStorage(name, this, path.absolutePathString())
|
||||||
|
val profile = type.create(storage)
|
||||||
|
this.profiles[name] = profile
|
||||||
|
|
||||||
|
storage.invalidate()
|
||||||
|
|
||||||
|
return profile
|
||||||
|
}
|
||||||
|
|
||||||
|
fun save(profile: P) {
|
||||||
|
val storage = profile.storage?.nullCast<FileStorage>() ?: throw IllegalArgumentException("Storage unset!")
|
||||||
|
if (!storage.invalid) return
|
||||||
|
val path = File(storage.path)
|
||||||
|
path.mkdirParent()
|
||||||
|
|
||||||
|
profile.lock.acquire()
|
||||||
|
val node = Jackson.MAPPER.valueToTree<ObjectNode>(profile)
|
||||||
|
node.put("version", latestVersion)
|
||||||
|
val string = Jackson.MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(node)
|
||||||
|
val stream = FileOutputStream(path)
|
||||||
|
stream.write(string.encodeNetwork())
|
||||||
|
stream.close()
|
||||||
|
|
||||||
|
storage.invalid = false
|
||||||
|
|
||||||
|
profile.lock.release()
|
||||||
|
}
|
||||||
|
|
||||||
fun delete(name: String) = Unit
|
fun delete(name: String) = Unit
|
||||||
fun delete(profile: P) = Unit
|
fun delete(profile: P) = Unit
|
||||||
operator fun get(name: String): P = TODO()
|
operator fun get(name: String): P? {
|
||||||
|
return profiles[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun iterator(): Iterator<P> {
|
override fun iterator(): Iterator<P> {
|
||||||
@ -69,6 +184,5 @@ abstract class StorageProfileManager<P : Profile> : Iterable<P>, Identified {
|
|||||||
companion object {
|
companion object {
|
||||||
const val DEFAULT_NAME = "Default"
|
const val DEFAULT_NAME = "Default"
|
||||||
|
|
||||||
val NAME_REGEX = "[\\w_ ]{1,32}".toRegex()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ package de.bixilon.minosoft.gui.eros.dialog.profiles
|
|||||||
import de.bixilon.kutil.cast.CastUtil.unsafeCast
|
import de.bixilon.kutil.cast.CastUtil.unsafeCast
|
||||||
import de.bixilon.minosoft.config.profile.manager.ProfileManagers
|
import de.bixilon.minosoft.config.profile.manager.ProfileManagers
|
||||||
import de.bixilon.minosoft.config.profile.profiles.Profile
|
import de.bixilon.minosoft.config.profile.profiles.Profile
|
||||||
|
import de.bixilon.minosoft.config.profile.storage.ProfileIOUtil.isValidName
|
||||||
import de.bixilon.minosoft.config.profile.storage.StorageProfileManager
|
import de.bixilon.minosoft.config.profile.storage.StorageProfileManager
|
||||||
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
|
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
|
||||||
import de.bixilon.minosoft.gui.eros.controller.JavaFXWindowController
|
import de.bixilon.minosoft.gui.eros.controller.JavaFXWindowController
|
||||||
@ -81,7 +82,7 @@ class ProfileCreateDialog<T : Profile>(
|
|||||||
cancelButtonFX.ctext = CANCEL_BUTTON
|
cancelButtonFX.ctext = CANCEL_BUTTON
|
||||||
|
|
||||||
nameFX.textProperty().addListener { _, _, new ->
|
nameFX.textProperty().addListener { _, _, new ->
|
||||||
createButtonFX.isDisable = !StorageProfileManager.NAME_REGEX.matches(new) || manager.profiles[new] != null
|
createButtonFX.isDisable = !new.isValidName() || manager[new] != null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user