mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-03 13:58:28 +08:00
Persisters now use an injected KeyValueStore to facilitate testing of persistence code. Added LocalStorage and in-memory implementation of KeyValueStore.
This commit is contained in:
parent
f629f56e3a
commit
6374a3f054
@ -18,6 +18,7 @@ import world.phantasmal.core.disposable.disposable
|
||||
import world.phantasmal.observable.cell.mutableCell
|
||||
import world.phantasmal.web.application.Application
|
||||
import world.phantasmal.web.core.loading.AssetLoader
|
||||
import world.phantasmal.web.core.persistence.LocalStorageKeyValueStore
|
||||
import world.phantasmal.web.core.rendering.DisposableThreeRenderer
|
||||
import world.phantasmal.web.core.stores.ApplicationUrl
|
||||
import world.phantasmal.web.externals.three.WebGLRenderer
|
||||
@ -58,6 +59,7 @@ private fun init(): Disposable {
|
||||
disposer.add(
|
||||
Application(
|
||||
rootElement,
|
||||
LocalStorageKeyValueStore(),
|
||||
AssetLoader(httpClient),
|
||||
disposer.add(HistoryApplicationUrl()),
|
||||
::createThreeRenderer,
|
||||
|
@ -14,6 +14,7 @@ import world.phantasmal.web.application.widgets.MainContentWidget
|
||||
import world.phantasmal.web.application.widgets.NavigationWidget
|
||||
import world.phantasmal.web.core.PwTool
|
||||
import world.phantasmal.web.core.loading.AssetLoader
|
||||
import world.phantasmal.web.core.persistence.KeyValueStore
|
||||
import world.phantasmal.web.core.rendering.DisposableThreeRenderer
|
||||
import world.phantasmal.web.core.stores.ApplicationUrl
|
||||
import world.phantasmal.web.core.stores.UiStore
|
||||
@ -25,6 +26,7 @@ import world.phantasmal.webui.dom.disposableListener
|
||||
|
||||
class Application(
|
||||
rootElement: HTMLElement,
|
||||
keyValueStore: KeyValueStore,
|
||||
assetLoader: AssetLoader,
|
||||
applicationUrl: ApplicationUrl,
|
||||
createThreeRenderer: (HTMLCanvasElement) -> DisposableThreeRenderer,
|
||||
@ -34,7 +36,7 @@ class Application(
|
||||
addDisposables(
|
||||
// Disable native undo/redo.
|
||||
document.disposableListener("beforeinput", ::beforeInput),
|
||||
// Work-around for FireFox:
|
||||
// Disable native undo/redo in FireFox.
|
||||
document.disposableListener("keydown", ::keydown),
|
||||
|
||||
// Disable native drag-and-drop to avoid users dragging in unsupported file formats and
|
||||
@ -50,8 +52,8 @@ class Application(
|
||||
// The various tools Phantasmal World consists of.
|
||||
val tools: List<PwTool> = listOf(
|
||||
addDisposable(Viewer(assetLoader, uiStore, createThreeRenderer)),
|
||||
addDisposable(QuestEditor(assetLoader, uiStore, createThreeRenderer)),
|
||||
addDisposable(HuntOptimizer(assetLoader, uiStore)),
|
||||
addDisposable(QuestEditor(keyValueStore, assetLoader, uiStore, createThreeRenderer)),
|
||||
addDisposable(HuntOptimizer(keyValueStore, assetLoader, uiStore)),
|
||||
)
|
||||
|
||||
// Controllers.
|
||||
|
@ -0,0 +1,28 @@
|
||||
package world.phantasmal.web.core.persistence
|
||||
|
||||
import kotlinx.browser.localStorage
|
||||
|
||||
interface KeyValueStore {
|
||||
suspend fun get(key: String): String?
|
||||
suspend fun put(key: String, value: String)
|
||||
}
|
||||
|
||||
class LocalStorageKeyValueStore : KeyValueStore {
|
||||
override suspend fun get(key: String): String? =
|
||||
localStorage.getItem(key)
|
||||
|
||||
override suspend fun put(key: String, value: String) {
|
||||
localStorage.setItem(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
class MemoryKeyValueStore : KeyValueStore {
|
||||
private val map = mutableMapOf<String, String>()
|
||||
|
||||
override suspend fun get(key: String): String? =
|
||||
map[key]
|
||||
|
||||
override suspend fun put(key: String, value: String) {
|
||||
map[key] = value
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package world.phantasmal.web.core.persistence
|
||||
|
||||
import kotlinx.browser.localStorage
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.serializer
|
||||
@ -9,7 +8,7 @@ import world.phantasmal.web.core.models.Server
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
abstract class Persister {
|
||||
abstract class Persister(private val store: KeyValueStore) {
|
||||
private val format = Json {
|
||||
classDiscriminator = "#type"
|
||||
ignoreUnknownKeys = true
|
||||
@ -23,7 +22,7 @@ abstract class Persister {
|
||||
@Suppress("RedundantSuspendModifier")
|
||||
protected suspend fun <T> persist(key: String, data: T, serializer: KSerializer<T>) {
|
||||
try {
|
||||
localStorage.setItem(key, format.encodeToString(serializer, data))
|
||||
store.put(key, format.encodeToString(serializer, data))
|
||||
} catch (e: Throwable) {
|
||||
logger.error(e) { "Couldn't persist ${key}." }
|
||||
}
|
||||
@ -44,7 +43,7 @@ abstract class Persister {
|
||||
@Suppress("RedundantSuspendModifier")
|
||||
protected suspend fun <T> load(key: String, serializer: KSerializer<T>): T? =
|
||||
try {
|
||||
val json = localStorage.getItem(key)
|
||||
val json = store.get(key)
|
||||
json?.let { format.decodeFromString(serializer, it) }
|
||||
} catch (e: Throwable) {
|
||||
logger.error(e) { "Couldn't load ${key}." }
|
||||
|
@ -3,6 +3,7 @@ package world.phantasmal.web.huntOptimizer
|
||||
import world.phantasmal.web.core.PwTool
|
||||
import world.phantasmal.web.core.PwToolType
|
||||
import world.phantasmal.web.core.loading.AssetLoader
|
||||
import world.phantasmal.web.core.persistence.KeyValueStore
|
||||
import world.phantasmal.web.core.stores.ItemDropStore
|
||||
import world.phantasmal.web.core.stores.ItemTypeStore
|
||||
import world.phantasmal.web.core.stores.UiStore
|
||||
@ -16,6 +17,7 @@ import world.phantasmal.webui.DisposableContainer
|
||||
import world.phantasmal.webui.widgets.Widget
|
||||
|
||||
class HuntOptimizer(
|
||||
private val keyValueStore: KeyValueStore,
|
||||
private val assetLoader: AssetLoader,
|
||||
private val uiStore: UiStore,
|
||||
) : DisposableContainer(), PwTool {
|
||||
@ -25,8 +27,8 @@ class HuntOptimizer(
|
||||
val itemTypeStore = addDisposable(ItemTypeStore(assetLoader))
|
||||
|
||||
// Persistence
|
||||
val huntMethodPersister = HuntMethodPersister()
|
||||
val wantedItemPersister = WantedItemPersister(itemTypeStore)
|
||||
val huntMethodPersister = HuntMethodPersister(keyValueStore)
|
||||
val wantedItemPersister = WantedItemPersister(keyValueStore, itemTypeStore)
|
||||
|
||||
// Stores
|
||||
val huntMethodStore =
|
||||
|
@ -1,12 +1,13 @@
|
||||
package world.phantasmal.web.huntOptimizer.persistence
|
||||
|
||||
import world.phantasmal.web.core.models.Server
|
||||
import world.phantasmal.web.core.persistence.KeyValueStore
|
||||
import world.phantasmal.web.core.persistence.Persister
|
||||
import world.phantasmal.web.huntOptimizer.models.HuntMethodModel
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.DurationUnit.HOURS
|
||||
|
||||
class HuntMethodPersister : Persister() {
|
||||
class HuntMethodPersister(keyValueStore: KeyValueStore) : Persister(keyValueStore) {
|
||||
suspend fun persistMethodUserTimes(huntMethods: List<HuntMethodModel>, server: Server) {
|
||||
val userTimes = mutableMapOf<String, Double>()
|
||||
|
||||
|
@ -1,12 +1,17 @@
|
||||
package world.phantasmal.web.huntOptimizer.persistence
|
||||
|
||||
import world.phantasmal.web.core.models.Server
|
||||
import world.phantasmal.web.core.persistence.KeyValueStore
|
||||
import world.phantasmal.web.core.persistence.Persister
|
||||
import world.phantasmal.web.core.stores.ItemTypeStore
|
||||
import world.phantasmal.web.shared.dto.WantedItemDto
|
||||
import world.phantasmal.web.huntOptimizer.models.WantedItemModel
|
||||
import world.phantasmal.web.shared.dto.WantedItemDto
|
||||
|
||||
class WantedItemPersister(
|
||||
keyValueStore: KeyValueStore,
|
||||
private val itemTypeStore: ItemTypeStore,
|
||||
) : Persister(keyValueStore) {
|
||||
|
||||
class WantedItemPersister(private val itemTypeStore: ItemTypeStore) : Persister() {
|
||||
suspend fun persistWantedItems(wantedItems: List<WantedItemModel>, server: Server) {
|
||||
persistForServer(server, WANTED_ITEMS_KEY, wantedItems.map {
|
||||
WantedItemDto(it.itemType.id, it.amount.value)
|
||||
|
@ -6,6 +6,7 @@ import org.w3c.dom.HTMLCanvasElement
|
||||
import world.phantasmal.web.core.PwTool
|
||||
import world.phantasmal.web.core.PwToolType
|
||||
import world.phantasmal.web.core.loading.AssetLoader
|
||||
import world.phantasmal.web.core.persistence.KeyValueStore
|
||||
import world.phantasmal.web.core.rendering.DisposableThreeRenderer
|
||||
import world.phantasmal.web.core.stores.UiStore
|
||||
import world.phantasmal.web.core.undo.UndoManager
|
||||
@ -25,6 +26,7 @@ import world.phantasmal.webui.dom.disposableListener
|
||||
import world.phantasmal.webui.widgets.Widget
|
||||
|
||||
class QuestEditor(
|
||||
private val keyValueStore: KeyValueStore,
|
||||
private val assetLoader: AssetLoader,
|
||||
private val uiStore: UiStore,
|
||||
private val createThreeRenderer: (HTMLCanvasElement) -> DisposableThreeRenderer,
|
||||
@ -38,7 +40,7 @@ class QuestEditor(
|
||||
val entityAssetLoader = addDisposable(EntityAssetLoader(assetLoader))
|
||||
|
||||
// Persistence
|
||||
val questEditorUiPersister = QuestEditorUiPersister()
|
||||
val questEditorUiPersister = QuestEditorUiPersister(keyValueStore)
|
||||
|
||||
// Undo
|
||||
val undoManager = UndoManager()
|
||||
|
@ -1,10 +1,11 @@
|
||||
package world.phantasmal.web.questEditor.persistence
|
||||
|
||||
import world.phantasmal.web.core.controllers.*
|
||||
import world.phantasmal.web.core.persistence.KeyValueStore
|
||||
import world.phantasmal.web.core.persistence.Persister
|
||||
import world.phantasmal.web.shared.dto.*
|
||||
|
||||
class QuestEditorUiPersister : Persister() {
|
||||
class QuestEditorUiPersister(keyValueStore: KeyValueStore) : Persister(keyValueStore) {
|
||||
// TODO: Throttle this method.
|
||||
suspend fun persistLayoutConfig(config: DockedItem) {
|
||||
persist(LAYOUT_CONFIG_KEY, toDto(config))
|
||||
|
@ -2,6 +2,7 @@ package world.phantasmal.web.application
|
||||
|
||||
import kotlinx.browser.document
|
||||
import world.phantasmal.web.core.PwToolType
|
||||
import world.phantasmal.web.core.persistence.MemoryKeyValueStore
|
||||
import world.phantasmal.web.test.TestApplicationUrl
|
||||
import world.phantasmal.web.test.WebTestContext
|
||||
import world.phantasmal.web.test.WebTestSuite
|
||||
@ -30,8 +31,10 @@ class ApplicationTests : WebTestSuite {
|
||||
|
||||
private fun WebTestContext.initialization_and_shutdown_succeeds(url: String) {
|
||||
components.applicationUrl = TestApplicationUrl(url)
|
||||
|
||||
disposer.add(
|
||||
Application(
|
||||
keyValueStore = MemoryKeyValueStore(),
|
||||
rootElement = document.body!!,
|
||||
assetLoader = components.assetLoader,
|
||||
applicationUrl = components.applicationUrl,
|
||||
|
@ -1,6 +1,7 @@
|
||||
package world.phantasmal.web.huntOptimizer
|
||||
|
||||
import world.phantasmal.web.core.PwToolType
|
||||
import world.phantasmal.web.core.persistence.MemoryKeyValueStore
|
||||
import world.phantasmal.web.test.TestApplicationUrl
|
||||
import world.phantasmal.web.test.WebTestSuite
|
||||
import kotlin.test.Test
|
||||
@ -10,7 +11,10 @@ class HuntOptimizerTests : WebTestSuite {
|
||||
fun initialization_and_shutdown_should_succeed_without_throwing() = test {
|
||||
components.applicationUrl = TestApplicationUrl("/${PwToolType.HuntOptimizer}")
|
||||
|
||||
val huntOptimizer = disposer.add(HuntOptimizer(components.assetLoader, components.uiStore))
|
||||
val huntOptimizer = disposer.add(
|
||||
HuntOptimizer(MemoryKeyValueStore(), components.assetLoader, components.uiStore)
|
||||
)
|
||||
|
||||
disposer.add(huntOptimizer.initialize())
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package world.phantasmal.web.questEditor
|
||||
|
||||
import world.phantasmal.web.core.PwToolType
|
||||
import world.phantasmal.web.core.persistence.MemoryKeyValueStore
|
||||
import world.phantasmal.web.test.TestApplicationUrl
|
||||
import world.phantasmal.web.test.WebTestSuite
|
||||
import kotlin.test.Test
|
||||
@ -11,8 +12,14 @@ class QuestEditorTests : WebTestSuite {
|
||||
components.applicationUrl = TestApplicationUrl("/${PwToolType.QuestEditor}")
|
||||
|
||||
val questEditor = disposer.add(
|
||||
QuestEditor(components.assetLoader, components.uiStore, components.createThreeRenderer)
|
||||
QuestEditor(
|
||||
MemoryKeyValueStore(),
|
||||
components.assetLoader,
|
||||
components.uiStore,
|
||||
components.createThreeRenderer,
|
||||
)
|
||||
)
|
||||
|
||||
disposer.add(questEditor.initialize())
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ class ViewerTests : WebTestSuite {
|
||||
val viewer = disposer.add(
|
||||
Viewer(components.assetLoader, components.uiStore, components.createThreeRenderer)
|
||||
)
|
||||
|
||||
disposer.add(viewer.initialize())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user