config option listening

This commit is contained in:
Bixilon 2021-12-01 22:51:46 +01:00
parent b0574500c4
commit e8c5e94902
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
13 changed files with 164 additions and 38 deletions

View File

@ -17,7 +17,6 @@ import de.bixilon.minosoft.config.config.account.AccountConfig
import de.bixilon.minosoft.config.config.chat.ChatConfig
import de.bixilon.minosoft.config.config.debug.DebugConfig
import de.bixilon.minosoft.config.config.download.DownloadConfig
import de.bixilon.minosoft.config.config.eros.ErosConfig
import de.bixilon.minosoft.config.config.game.GameConfig
import de.bixilon.minosoft.config.config.general.GeneralConfig
import de.bixilon.minosoft.config.config.network.NetworkConfig
@ -32,5 +31,4 @@ data class Config(
val server: ServerConfig = ServerConfig(),
val download: DownloadConfig = DownloadConfig(),
val debug: DebugConfig = DebugConfig(),
val eros: ErosConfig = ErosConfig(),
)

View File

@ -1,21 +0,0 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.config.config.eros
import com.squareup.moshi.Json
data class ErosConfig(
@Json(name = "show_releases") var showReleases: Boolean = true,
@Json(name = "show_snapshots") var showSnapshots: Boolean = false,
)

View File

@ -22,6 +22,15 @@ object GlobalProfileManager {
ErosProfileManager,
)
private val SELECTED_PROFILES_TYPE: MapType = Jackson.MAPPER.typeFactory.constructMapType(HashMap::class.java, ResourceLocation::class.java, String::class.java)
val CLASS_MAPPING: Map<Class<out Profile>, ProfileManager<*>>
init {
val classMapping: MutableMap<Class<out Profile>, ProfileManager<*>> = mutableMapOf()
for (manager in DEFAULT_MANAGERS) {
classMapping[manager.profileClass] = manager
}
CLASS_MAPPING = classMapping.toMap()
}
private var initialized = false
private var loading = true

View File

@ -24,6 +24,7 @@ interface ProfileManager<T : Profile> {
val namespace: ResourceLocation
val latestVersion: Int
val saveLock: ReentrantLock
val profileClass: Class<T>
val profiles: HashBiMap<String, T>
var selected: T
@ -93,7 +94,7 @@ interface ProfileManager<T : Profile> {
if (selected == null || profileNames.isEmpty()) {
initDefaultProfile()
}
var migrated = false
var saveFile = false
for (profileName in profileNames) {
val path = getPath(profileName, baseDirectory)
val json: MutableMap<String, Any?>?
@ -110,14 +111,15 @@ interface ProfileManager<T : Profile> {
}
Log.log(LogMessageType.LOAD_PROFILES, LogLevels.INFO) { "Migrated profile ($path) from version $version to $latestVersion" }
json["version"] = latestVersion
migrated = true
saveFile = true
}
} else {
json = null
saveFile = true
}
val profile = load(profileName, json)
if (migrated) {
if (saveFile) {
profile.saved = false
save(profile)
}

View File

@ -0,0 +1,59 @@
package de.bixilon.minosoft.config.profile.change
import de.bixilon.minosoft.config.profile.GlobalProfileManager
import de.bixilon.minosoft.config.profile.change.listener.ProfileChangeListener
import de.bixilon.minosoft.config.profile.profiles.Profile
import de.bixilon.minosoft.util.KUtil.synchronizedMapOf
import de.bixilon.minosoft.util.KUtil.synchronizedSetOf
import de.bixilon.minosoft.util.KUtil.toSynchronizedSet
import de.bixilon.minosoft.util.KUtil.unsafeCast
import de.bixilon.minosoft.util.collections.SynchronizedMap
import java.lang.ref.WeakReference
import java.lang.reflect.Field
object ProfilesChangeManager {
private val listeners: SynchronizedMap<Field, SynchronizedMap<Profile?, MutableSet<Pair<WeakReference<Any>, ProfileChangeListener<Any>>>>> = synchronizedMapOf()
fun <T> register(reference: Any, listener: ProfileChangeListener<T>) {
this.listeners.getOrPut(listener.field) { synchronizedMapOf() }.getOrPut(listener.profile) { synchronizedSetOf() }.add(Pair(WeakReference(reference), listener.unsafeCast()))
}
fun onChange(profile: Profile, field: Field, previous: Any?, value: Any?) {
val fieldListeners = listeners[field] ?: return
fun work(queue: MutableSet<Pair<WeakReference<Any>, ProfileChangeListener<Any>>>) {
val toRemove: MutableSet<Pair<WeakReference<Any>, ProfileChangeListener<Any>>> = mutableSetOf()
for (pair in queue.toSynchronizedSet()) {
val (reference, listener) = pair
if (reference.get() == null) {
toRemove += pair
}
listener.invoke(previous.unsafeCast(), value.unsafeCast())
}
if (toRemove.isNotEmpty()) {
if (queue.size == toRemove.size) {
queue.clear()
} else {
queue.removeAll(toRemove)
}
}
}
fieldListeners[profile]?.let {
work(it)
if (it.isEmpty()) {
fieldListeners -= profile
}
}
val manager = GlobalProfileManager.CLASS_MAPPING[profile::class.java] ?: return
if (profile == manager.selected) {
fieldListeners[null]?.let {
work(it)
if (it.isEmpty()) {
fieldListeners -= null
}
}
}
}
}

View File

@ -0,0 +1,14 @@
package de.bixilon.minosoft.config.profile.change.listener
import de.bixilon.minosoft.config.profile.profiles.Profile
import java.lang.reflect.Field
import kotlin.reflect.KProperty
interface ProfileChangeListener<T> {
val property: KProperty<T>
val field: Field
val profile: Profile?
fun invoke(previous: T, value: T)
}

View File

@ -0,0 +1,31 @@
package de.bixilon.minosoft.config.profile.change.listener
import de.bixilon.minosoft.config.profile.change.ProfilesChangeManager
import de.bixilon.minosoft.config.profile.profiles.Profile
import de.bixilon.minosoft.gui.eros.util.JavaFXUtil
import java.lang.reflect.Field
import kotlin.reflect.KProperty
import kotlin.reflect.jvm.javaField
class SimpleProfileChangeListener<T>(
override val property: KProperty<T>,
override val field: Field,
override val profile: Profile?,
private val callback: (T) -> Unit,
) : ProfileChangeListener<T> {
override fun invoke(previous: T, value: T) {
callback(value)
}
companion object {
fun <T> KProperty<T>.listen(reference: Any, profile: Profile? = null, callback: ((T) -> Unit)) {
ProfilesChangeManager.register(reference, SimpleProfileChangeListener(this, javaField!!, profile, callback))
}
fun <T> KProperty<T>.listenFX(reference: Any, profile: Profile? = null, callback: ((T) -> Unit)) {
ProfilesChangeManager.register(reference, SimpleProfileChangeListener(this, javaField!!, profile) { JavaFXUtil.runLater { callback(it) } })
}
}
}

View File

@ -4,6 +4,7 @@ import de.bixilon.minosoft.config.profile.profiles.Profile
import de.bixilon.minosoft.config.profile.profiles.eros.ErosProfileManager.delegate
import de.bixilon.minosoft.config.profile.profiles.eros.ErosProfileManager.latestVersion
import de.bixilon.minosoft.config.profile.profiles.eros.general.GeneralC
import de.bixilon.minosoft.config.profile.profiles.eros.server.ServerC
class ErosProfile(
description: String? = null,
@ -16,6 +17,7 @@ class ErosProfile(
override var saved: Boolean = true
val general: GeneralC = GeneralC()
val server: ServerC = ServerC()
override fun toString(): String {
return ErosProfileManager.getName(this)

View File

@ -14,6 +14,7 @@ object ErosProfileManager : ProfileManager<ErosProfile> {
override val namespace = "minosoft:eros".toResourceLocation()
override val latestVersion = 1
override val saveLock = ReentrantLock()
override val profileClass = ErosProfile::class.java
private var currentLoadingPath: String? = null

View File

@ -0,0 +1,7 @@
package de.bixilon.minosoft.config.profile.profiles.eros.server
import de.bixilon.minosoft.config.profile.profiles.eros.server.modify.ModifyC
class ServerC {
val modify = ModifyC()
}

View File

@ -0,0 +1,8 @@
package de.bixilon.minosoft.config.profile.profiles.eros.server.modify
import de.bixilon.minosoft.config.profile.profiles.eros.ErosProfileManager.delegate
class ModifyC {
var showReleases by delegate(true)
var showSnapshots by delegate(false)
}

View File

@ -1,11 +1,14 @@
package de.bixilon.minosoft.config.profile.util
import de.bixilon.minosoft.config.profile.ProfileManager
import de.bixilon.minosoft.config.profile.change.ProfilesChangeManager
import de.bixilon.minosoft.config.profile.profiles.Profile
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
import kotlin.reflect.jvm.javaField
open class ProfileDelegate<V>(
private var value: V,
@ -13,7 +16,7 @@ open class ProfileDelegate<V>(
private val profileManager: ProfileManager<*>,
private val profileName: String,
) : ReadWriteProperty<Any, V> {
private lateinit var profile: Profile
override fun getValue(thisRef: Any, property: KProperty<*>): V {
return value
@ -23,16 +26,24 @@ open class ProfileDelegate<V>(
if (checkEquals && this.value == value) {
return
}
val profile = profileManager.profiles[profileName]
if (profile == null || profile.initializing) {
if (!this::profile.isInitialized) {
val profile = profileManager.profiles[profileName]
if (profile == null) {
this.value = value
return
}
this.profile = profile
}
if (profile.initializing) {
this.value = value
return
}
Log.log(LogMessageType.OTHER, LogLevels.VERBOSE) { "Changed option $property in $thisRef in profile ${profileName::class.java} from ${this.value} to $value" }
Log.log(LogMessageType.OTHER, LogLevels.VERBOSE) { "Changed option $property in profile $profileName from ${this.value} to $value" }
profileManager.profiles[profileName]?.saved = false
// ToDo: Fire event
val previous = this.value
this.value = value
ProfilesChangeManager.onChange(profile, property.javaField ?: return, previous, value)
}
}

View File

@ -14,6 +14,9 @@
package de.bixilon.minosoft.gui.eros.dialog
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.config.profile.change.listener.SimpleProfileChangeListener.Companion.listenFX
import de.bixilon.minosoft.config.profile.profiles.eros.ErosProfileManager
import de.bixilon.minosoft.config.profile.profiles.eros.server.modify.ModifyC
import de.bixilon.minosoft.config.server.Server
import de.bixilon.minosoft.data.registries.versions.Version
import de.bixilon.minosoft.data.registries.versions.VersionTypes
@ -106,22 +109,24 @@ class UpdateServerDialog(
cancelButtonFX.ctext = TranslatableComponents.GENERAL_CANCEL
val modifyConfig = ErosProfileManager.selected.server.modify
ModifyC::showReleases.listenFX(this) { showReleasesFX.isSelected = it }
ModifyC::showSnapshots.listenFX(this) { showSnapshotsFX.isSelected = it }
showReleasesFX.apply {
isSelected = Minosoft.config.config.eros.showReleases
isSelected = modifyConfig.showReleases
ctext = SHOW_RELEASES
setOnAction {
Minosoft.config.config.eros.showReleases = isSelected
Minosoft.config.saveToFile()
modifyConfig.showReleases = isSelected
refreshVersions()
}
}
showSnapshotsFX.apply {
isSelected = Minosoft.config.config.eros.showSnapshots
isSelected = modifyConfig.showSnapshots
ctext = SHOW_SNAPSHOTS
setOnAction {
Minosoft.config.config.eros.showSnapshots = isSelected
Minosoft.config.saveToFile()
modifyConfig.showSnapshots = isSelected
refreshVersions()
}
}