mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-15 10:25:06 -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.meta.MinosoftMeta
|
||||
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.data.entities.event.EntityEvents
|
||||
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.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.ASSETS_PROPERTIES, dependencies = arrayOf(BootTasks.VERSIONS), executor = AssetsVersionProperties::load)
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
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.audio.AudioProfileManager
|
||||
import de.bixilon.minosoft.config.profile.profiles.block.BlockProfileManager
|
||||
@ -41,4 +43,14 @@ object ProfileManagers : DefaultFactory<StorageProfileManager<*>>(
|
||||
GUIProfileManager,
|
||||
ControlsProfileManager,
|
||||
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
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import de.bixilon.kutil.concurrent.lock.Lock
|
||||
import de.bixilon.minosoft.config.profile.storage.ProfileStorage
|
||||
|
||||
interface Profile {
|
||||
@get:JsonIgnore val storage: ProfileStorage?
|
||||
@get:JsonInclude(JsonInclude.Include.NON_NULL) val version: Int? get() = storage?.version
|
||||
@get:JsonIgnore val lock: Lock
|
||||
}
|
||||
|
@ -11,19 +11,19 @@
|
||||
* 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
|
||||
import de.bixilon.minosoft.terminal.RunConfiguration
|
||||
class FileStorage(
|
||||
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.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.config.profile
|
||||
package de.bixilon.minosoft.config.profile.storage
|
||||
|
||||
data class ProfileState(
|
||||
var initializing: Boolean = true,
|
||||
var reloading: Boolean = false,
|
||||
var saving: Boolean = false,
|
||||
)
|
||||
object ProfileIOUtil {
|
||||
val NAME_REGEX = "[\\w_ ]{1,32}".toRegex()
|
||||
|
||||
fun String.isValidName(): Boolean {
|
||||
return NAME_REGEX.matches(this)
|
||||
}
|
||||
}
|
@ -15,7 +15,6 @@ package de.bixilon.minosoft.config.profile.storage
|
||||
|
||||
interface ProfileStorage {
|
||||
val name: String
|
||||
val version: Int
|
||||
val path: String?
|
||||
|
||||
fun invalidate() = Unit
|
||||
|
@ -13,17 +13,34 @@
|
||||
|
||||
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.collections.CollectionUtil.mutableBiMapOf
|
||||
import de.bixilon.kutil.collections.map.bi.AbstractMutableBiMap
|
||||
import de.bixilon.kutil.exception.Broken
|
||||
import de.bixilon.kutil.json.MutableJsonObject
|
||||
import de.bixilon.kutil.observer.DataObserver.Companion.observed
|
||||
import de.bixilon.kutil.observer.map.bi.BiMapObserver.Companion.observedBiMap
|
||||
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.profiles.Profile
|
||||
import de.bixilon.minosoft.config.profile.storage.ProfileIOUtil.isValidName
|
||||
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.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 {
|
||||
@ -40,26 +57,124 @@ abstract class StorageProfileManager<P : Profile> : Iterable<P>, Identified {
|
||||
|
||||
|
||||
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!")
|
||||
when {
|
||||
version == latestVersion -> return
|
||||
version == latestVersion -> return -1
|
||||
version > latestVersion -> throw IllegalArgumentException("Profile was created with a newer version!")
|
||||
version < latestVersion -> {
|
||||
for (version in version until latestVersion) {
|
||||
migrate(version, data)
|
||||
}
|
||||
// TODO: log, save
|
||||
return version
|
||||
}
|
||||
}
|
||||
Broken()
|
||||
}
|
||||
|
||||
fun load(name: String, data: MutableJsonObject) = Unit
|
||||
fun create(name: String): P = TODO()
|
||||
private fun createDefault() {
|
||||
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(profile: P) = Unit
|
||||
operator fun get(name: String): P = TODO()
|
||||
operator fun get(name: String): P? {
|
||||
return profiles[name]
|
||||
}
|
||||
|
||||
|
||||
override fun iterator(): Iterator<P> {
|
||||
@ -69,6 +184,5 @@ abstract class StorageProfileManager<P : Profile> : Iterable<P>, Identified {
|
||||
companion object {
|
||||
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.minosoft.config.profile.manager.ProfileManagers
|
||||
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.data.registries.identified.ResourceLocation
|
||||
import de.bixilon.minosoft.gui.eros.controller.JavaFXWindowController
|
||||
@ -81,7 +82,7 @@ class ProfileCreateDialog<T : Profile>(
|
||||
cancelButtonFX.ctext = CANCEL_BUTTON
|
||||
|
||||
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