ReModel the Alteration system into subfiles.
This commit is contained in:
parent
b314a28f82
commit
0d94362ca2
4 changed files with 115 additions and 155 deletions
|
|
@ -1,8 +1,6 @@
|
|||
package com.pixelized.server.lwa.model.alteration
|
||||
|
||||
import com.pixelized.shared.lwa.model.alteration.AlterationJson
|
||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
|
||||
import com.pixelized.shared.lwa.utils.PathProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
@ -15,12 +13,10 @@ import java.io.File
|
|||
|
||||
class AlterationStore(
|
||||
private val pathProvider: PathProvider,
|
||||
private val campaignJsonFactory: CampaignJsonFactory,
|
||||
private val json: Json,
|
||||
) {
|
||||
private val directory = File(pathProvider.alterationsPath()).also { it.mkdirs() }
|
||||
private val alterationsFlow = MutableStateFlow<List<AlterationJson>>(emptyList())
|
||||
private val activeFlow = MutableStateFlow<Map<String, List<String>>>(emptyMap())
|
||||
|
||||
init {
|
||||
// build a coroutine scope for async calls
|
||||
|
|
@ -28,113 +24,44 @@ class AlterationStore(
|
|||
// load the initial data
|
||||
scope.launch {
|
||||
updateAlterations()
|
||||
updateActiveAlterations()
|
||||
}
|
||||
}
|
||||
|
||||
fun alterationsFlow(): StateFlow<List<AlterationJson>> = alterationsFlow
|
||||
|
||||
fun activeFlow(): StateFlow<Map<String, List<String>>> = activeFlow
|
||||
|
||||
private fun updateAlterations() {
|
||||
alterationsFlow.value = loadAlterations()
|
||||
}
|
||||
|
||||
private fun updateActiveAlterations() {
|
||||
activeFlow.value = loadActiveAlterations()
|
||||
}
|
||||
|
||||
private fun loadAlterations(): List<AlterationJson> {
|
||||
return try {
|
||||
val alterationFile = file()
|
||||
val json = alterationFile.readText(charset = Charsets.UTF_8)
|
||||
if (json.isBlank()) error("alterations file is empty")
|
||||
this.json.decodeFromString<List<AlterationJson>>(json)
|
||||
alterationsFlow.value = try {
|
||||
loadAlterations()
|
||||
} catch (exception: Exception) {
|
||||
// TODO log exception
|
||||
println(exception) // TODO proper exception handling
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadActiveAlterations(): Map<String, List<String>> {
|
||||
val mainFile = file()
|
||||
val jsonExt = ".json"
|
||||
@Throws(
|
||||
FileReadException::class,
|
||||
JsonConversionException::class,
|
||||
)
|
||||
private fun loadAlterations(): List<AlterationJson> {
|
||||
return directory
|
||||
.listFiles()
|
||||
?.filter { file ->
|
||||
// guard ignore the main alteration file and non json files.
|
||||
file.name != mainFile.name && file.name.contains(jsonExt)
|
||||
}
|
||||
?.mapNotNull { file ->
|
||||
// read the alteration file.
|
||||
val json = try {
|
||||
file.readText(charset = Charsets.UTF_8)
|
||||
} catch (exception: Exception) {
|
||||
throw FileReadException(root = exception)
|
||||
}
|
||||
// Guard, if the json is blank no alteration have been save, ignore this file.
|
||||
if (json.isBlank()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
try {
|
||||
val alterationIds = this.json.decodeFromString<List<String>>(json)
|
||||
val characterInstanceId = file.name.dropLast(n = jsonExt.length)
|
||||
characterInstanceId to alterationIds
|
||||
this.json.decodeFromString<AlterationJson>(json)
|
||||
} catch (exception: Exception) {
|
||||
// TODO log exception
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
}
|
||||
?.toMap()
|
||||
?: emptyMap()
|
||||
}
|
||||
|
||||
fun toggleActiveAlteration(
|
||||
characterInstanceId: Campaign.CharacterInstance.Id,
|
||||
alterationId: String,
|
||||
): Boolean {
|
||||
val id = campaignJsonFactory.convertToJson(id = characterInstanceId)
|
||||
// toggle the activation state
|
||||
val characterActiveAlterationIds = activeFlow.value[id]
|
||||
?.toMutableList()
|
||||
?.toggle(alterationId = alterationId)
|
||||
?: listOf(alterationId)
|
||||
// build the json string to save
|
||||
val json = try {
|
||||
this.json.encodeToString(characterActiveAlterationIds)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
// write the file
|
||||
try {
|
||||
val file = file(id = id)
|
||||
file.writeText(
|
||||
text = json,
|
||||
charset = Charsets.UTF_8,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw FileWriteException(root = exception)
|
||||
}
|
||||
// Update the dataflow.
|
||||
activeFlow.value = activeFlow.value.toMutableMap().also {
|
||||
it[id] = characterActiveAlterationIds
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun file(): File {
|
||||
return File("${pathProvider.alterationsPath()}alterations.json")
|
||||
}
|
||||
|
||||
private fun file(
|
||||
id: String,
|
||||
): File {
|
||||
return File("${pathProvider.alterationsPath()}$id.json")
|
||||
}
|
||||
|
||||
private fun MutableList<String>.toggle(alterationId: String): MutableList<String> {
|
||||
if (contains(alterationId)) {
|
||||
remove(alterationId)
|
||||
} else {
|
||||
add(alterationId)
|
||||
}
|
||||
return this
|
||||
?: emptyList()
|
||||
}
|
||||
|
||||
sealed class AlterationStoreException(root: Exception) : Exception(root)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class CampaignStore(
|
|||
private val factory: CampaignJsonFactory,
|
||||
private val json: Json,
|
||||
) {
|
||||
private val flow = MutableStateFlow(value = Campaign.EMPTY)
|
||||
private val campaignFlow = MutableStateFlow(value = Campaign.EMPTY)
|
||||
|
||||
init {
|
||||
// create the directory if needed.
|
||||
|
|
@ -27,28 +27,51 @@ class CampaignStore(
|
|||
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
// load the initial data
|
||||
scope.launch {
|
||||
update()
|
||||
updateCampaignFromDisk()
|
||||
}
|
||||
}
|
||||
|
||||
fun campaignFlow(): StateFlow<Campaign> = flow
|
||||
fun campaignFlow(): StateFlow<Campaign> = campaignFlow
|
||||
|
||||
suspend fun update() {
|
||||
flow.value = load()
|
||||
}
|
||||
|
||||
suspend fun load(): Campaign {
|
||||
return try {
|
||||
val json = file().readText(charset = Charsets.UTF_8)
|
||||
if (json.isBlank()) error("Campaign file is empty")
|
||||
val campaign = this.json.decodeFromString<CampaignJson>(json)
|
||||
factory.convertFromJson(campaign)
|
||||
private fun updateCampaignFromDisk() {
|
||||
campaignFlow.value = try {
|
||||
loadCampaign()
|
||||
} catch (exception: Exception) {
|
||||
println(exception) // TODO proper exception handling
|
||||
Campaign.EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun save(campaign: Campaign) {
|
||||
@Throws(
|
||||
FileReadException::class,
|
||||
JsonConversionException::class,
|
||||
)
|
||||
fun loadCampaign(): Campaign {
|
||||
val file = file()
|
||||
val json = try {
|
||||
file.readText(charset = Charsets.UTF_8)
|
||||
} catch (exception: Exception) {
|
||||
throw FileReadException(root = exception)
|
||||
}
|
||||
|
||||
// Guard, if the file is empty we load a default campaign.
|
||||
if (json.isBlank()) return Campaign.EMPTY
|
||||
|
||||
val campaign = try {
|
||||
val data = this.json.decodeFromString<CampaignJson>(json)
|
||||
factory.convertFromJson(data)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
|
||||
return campaign
|
||||
}
|
||||
|
||||
@Throws(
|
||||
JsonConversionException::class,
|
||||
FileWriteException::class,
|
||||
)
|
||||
fun save(campaign: Campaign) {
|
||||
// convert the data to json format
|
||||
val json = try {
|
||||
factory.convertToJson(data = campaign).let(json::encodeToString)
|
||||
|
|
@ -66,12 +89,13 @@ class CampaignStore(
|
|||
throw FileWriteException(root = exception)
|
||||
}
|
||||
// Update the dataflow.
|
||||
flow.value = campaign
|
||||
campaignFlow.value = campaign
|
||||
}
|
||||
|
||||
sealed class CampaignStoreException(root: Exception) : Exception(root)
|
||||
class JsonConversionException(root: Exception) : CampaignStoreException(root)
|
||||
class FileWriteException(root: Exception) : CampaignStoreException(root)
|
||||
class FileReadException(root: Exception) : CampaignStoreException(root)
|
||||
|
||||
private fun file(): File {
|
||||
return File("${pathProvider.campaignPath()}campaign.json")
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class CharacterSheetService(
|
|||
) {
|
||||
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
private val sheets get() = sheetsFlow.value
|
||||
private val sheetsFlow = store.characterSheetFlow()
|
||||
private val sheetsFlow = store.characterSheetsFlow()
|
||||
.map { entry -> entry.associateBy { character -> character.id } }
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
|
|
|
|||
|
|
@ -20,74 +20,33 @@ class CharacterSheetStore(
|
|||
private val json: Json,
|
||||
) {
|
||||
private val directory = File(pathProvider.characterStorePath()).also { it.mkdirs() }
|
||||
private val flow = MutableStateFlow<List<CharacterSheet>>(value = emptyList())
|
||||
private val characterSheetsFlow = MutableStateFlow<List<CharacterSheet>>(value = emptyList())
|
||||
|
||||
init {
|
||||
// build a coroutine scope for async calls
|
||||
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
// load the initial data
|
||||
scope.launch {
|
||||
flow.value = load()
|
||||
updateCharacterSheets()
|
||||
}
|
||||
}
|
||||
|
||||
fun characterSheetFlow(): StateFlow<List<CharacterSheet>> = flow
|
||||
fun characterSheetsFlow(): StateFlow<List<CharacterSheet>> = characterSheetsFlow
|
||||
|
||||
@Throws(
|
||||
CharacterSheetStoreException::class,
|
||||
FileWriteException::class,
|
||||
JsonConversionException::class,
|
||||
)
|
||||
fun save(sheet: CharacterSheet) {
|
||||
// convert the character sheet into json format.
|
||||
val json = try {
|
||||
factory.convertToJson(sheet = sheet).let(json::encodeToString)
|
||||
private suspend fun updateCharacterSheets() {
|
||||
characterSheetsFlow.value = try {
|
||||
loadCharacterSheets()
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
println(exception) // TODO proper exception handling
|
||||
emptyList()
|
||||
}
|
||||
// write the character file.
|
||||
try {
|
||||
val file = characterSheetFile(id = sheet.id)
|
||||
file.writeText(
|
||||
text = json,
|
||||
charset = Charsets.UTF_8,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw FileWriteException(root = exception)
|
||||
}
|
||||
// Update the dataflow.
|
||||
flow.value = flow.value
|
||||
.toMutableList()
|
||||
.also { data ->
|
||||
val index = data.indexOfFirst { it.id == sheet.id }
|
||||
if (index >= 0) {
|
||||
data[index] = sheet
|
||||
} else {
|
||||
data.add(sheet)
|
||||
}
|
||||
}
|
||||
.sortedWith(compareBy(Collator.getInstance()) { it.name })
|
||||
}
|
||||
|
||||
fun delete(id: String): Boolean {
|
||||
val file = characterSheetFile(id = id)
|
||||
val deleted = file.delete()
|
||||
if (deleted) {
|
||||
flow.value = flow.value.toMutableList()
|
||||
.also { data ->
|
||||
data.removeIf { it.id == id }
|
||||
}
|
||||
.sortedBy {
|
||||
it.name
|
||||
}
|
||||
}
|
||||
return deleted
|
||||
}
|
||||
|
||||
@Throws(
|
||||
CharacterSheetStoreException::class,
|
||||
FileReadException::class,
|
||||
JsonConversionException::class,
|
||||
)
|
||||
suspend fun load(): List<CharacterSheet> {
|
||||
suspend fun loadCharacterSheets(): List<CharacterSheet> {
|
||||
return directory
|
||||
.listFiles()
|
||||
?.mapNotNull { file ->
|
||||
|
|
@ -111,6 +70,56 @@ class CharacterSheetStore(
|
|||
?: emptyList()
|
||||
}
|
||||
|
||||
@Throws(
|
||||
FileWriteException::class,
|
||||
JsonConversionException::class,
|
||||
)
|
||||
fun save(sheet: CharacterSheet) {
|
||||
// convert the character sheet into json format.
|
||||
val json = try {
|
||||
factory.convertToJson(sheet = sheet).let(json::encodeToString)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
// write the character file.
|
||||
try {
|
||||
val file = characterSheetFile(id = sheet.id)
|
||||
file.writeText(
|
||||
text = json,
|
||||
charset = Charsets.UTF_8,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw FileWriteException(root = exception)
|
||||
}
|
||||
// Update the dataflow.
|
||||
characterSheetsFlow.value = characterSheetsFlow.value
|
||||
.toMutableList()
|
||||
.also { data ->
|
||||
val index = data.indexOfFirst { it.id == sheet.id }
|
||||
if (index >= 0) {
|
||||
data[index] = sheet
|
||||
} else {
|
||||
data.add(sheet)
|
||||
}
|
||||
}
|
||||
.sortedWith(compareBy(Collator.getInstance()) { it.name })
|
||||
}
|
||||
|
||||
fun delete(id: String): Boolean {
|
||||
val file = characterSheetFile(id = id)
|
||||
val deleted = file.delete()
|
||||
if (deleted) {
|
||||
characterSheetsFlow.value = characterSheetsFlow.value.toMutableList()
|
||||
.also { data ->
|
||||
data.removeIf { it.id == id }
|
||||
}
|
||||
.sortedBy {
|
||||
it.name
|
||||
}
|
||||
}
|
||||
return deleted
|
||||
}
|
||||
|
||||
private fun characterSheetFile(id: String): File {
|
||||
return File("${pathProvider.characterStorePath()}${id}.json")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue