diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/DataSyncViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/DataSyncViewModel.kt index 8d90f6f..466fed7 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/DataSyncViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/DataSyncViewModel.kt @@ -48,14 +48,11 @@ class DataSyncViewModel( .combine(campaignRepository.campaignFlow) { _, campaign: Campaign -> campaign } .distinctUntilChanged() .onEach { campaign -> - (campaign.characters.keys + campaign.npcs.keys).forEach { id -> + campaign.instances.keys.forEach { id -> characterRepository.characterDetail( characterSheetId = id.characterSheetId, forceUpdate = true, ) - alterationRepository.updateActiveAlterations( - characterInstanceId = id, - ) } } .launchIn(this) diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/network/LwaClient.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/network/LwaClient.kt index bb13fe6..2c465bf 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/network/LwaClient.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/network/LwaClient.kt @@ -26,17 +26,4 @@ interface LwaClient { suspend fun campaignRemoveNpc(characterSheetId: String, instanceId: Int) suspend fun alterations(): List - - suspend fun activeAlterations( - prefix: Char, - characterSheetId: String, - instanceId: Int, - ): List - - suspend fun toggleActiveAlterations( - prefix: Char, - characterSheetId: String, - instanceId: Int, - alterationId: String, - ) } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/network/LwaClientImpl.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/network/LwaClientImpl.kt index 5bf3676..dfbea5f 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/network/LwaClientImpl.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/network/LwaClientImpl.kt @@ -73,24 +73,4 @@ class LwaClientImpl( override suspend fun alterations(): List = client .get("$root/alterations") .body() - - override suspend fun activeAlterations( - prefix: Char, - characterSheetId: String, - instanceId: Int, - ): List = client - .get("$root/alterations/active?characterSheetId=$characterSheetId&instanceId=$instanceId&prefix=$prefix") - .body() - - override suspend fun toggleActiveAlterations( - prefix: Char, - characterSheetId: String, - instanceId: Int, - alterationId: String, - ) = client - .put("$root/alterations/active/toggle?characterSheetId=$characterSheetId&instanceId=$instanceId&prefix=$prefix") { - contentType(ContentType.Application.Json) - setBody(alterationId) - } - .body() } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/alteration/AlterationRepository.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/alteration/AlterationRepository.kt index 0d21776..78c2653 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/alteration/AlterationRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/alteration/AlterationRepository.kt @@ -1,7 +1,9 @@ package com.pixelized.desktop.lwa.repository.alteration +import com.pixelized.desktop.lwa.repository.campaign.CampaignStore import com.pixelized.shared.lwa.model.alteration.Alteration import com.pixelized.shared.lwa.model.alteration.FieldAlteration +import com.pixelized.shared.lwa.model.campaign.Campaign import com.pixelized.shared.lwa.model.campaign.Campaign.CharacterInstance import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -13,22 +15,20 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -// Theses typealias are there for readability only. -private typealias AlterationId = String - class AlterationRepository( - private val store: AlterationStore, + private val campaignStore: CampaignStore, + private val alterationStore: AlterationStore, ) { private val scope = CoroutineScope(Dispatchers.IO + Job()) private val activeAlterationMapFlow: StateFlow>>> = combine( - store.alterations, - store.active, - ) { alterations, actives -> - actives.map { activeEntry -> + alterationStore.alterationsFlow, + campaignStore.campaignFlow, + ) { alterations, campaign: Campaign -> + campaign.instances.map { activeEntry -> activeEntry.key to transformToAlterationFieldMap( alterations = alterations, - actives = activeEntry.value + actives = activeEntry.value.alterations, ) }.toMap() }.stateIn( @@ -49,32 +49,13 @@ class AlterationRepository( return activeAlterationMapFlow.value[characterInstanceId] ?: emptyMap() } - suspend fun updateActiveAlterations( - characterInstanceId: CharacterInstance.Id, - ) { - store.updateActiveAlterations( - characterInstanceId = characterInstanceId, - ) - } - - suspend fun toggleActiveAlteration( - characterInstanceId: CharacterInstance.Id, - alterationId: String, - ) { - // alteration was active for the character toggle it off. - store.toggleActiveAlteration( - characterInstanceId = characterInstanceId, - alterationId = alterationId, - ) - } - private fun transformToAlterationFieldMap( alterations: Map, actives: List, ): Map> { val fieldAlterations = hashMapOf>() - actives.forEach { id: AlterationId -> - alterations[id]?.let { alteration -> + actives.forEach { alterationId -> + alterations[alterationId]?.let { alteration -> alteration.fields.forEach { field -> fieldAlterations .getOrPut(field.fieldId) { mutableListOf() } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/alteration/AlterationStore.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/alteration/AlterationStore.kt index 47ae649..3d39055 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/alteration/AlterationStore.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/alteration/AlterationStore.kt @@ -1,13 +1,8 @@ package com.pixelized.desktop.lwa.repository.alteration import com.pixelized.desktop.lwa.network.LwaClient -import com.pixelized.desktop.lwa.repository.network.NetworkRepository import com.pixelized.shared.lwa.model.alteration.Alteration import com.pixelized.shared.lwa.model.alteration.AlterationJsonFactory -import com.pixelized.shared.lwa.model.campaign.Campaign.CharacterInstance -import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory -import com.pixelized.shared.lwa.protocol.websocket.SocketMessage -import com.pixelized.shared.lwa.protocol.websocket.ToggleActiveAlteration import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -17,38 +12,20 @@ import kotlinx.coroutines.launch class AlterationStore( private val alterationFactory: AlterationJsonFactory, - private val campaignJsonFactory: CampaignJsonFactory, - private val network: NetworkRepository, private val client: LwaClient, ) { - private val _alterations = MutableStateFlow>(emptyMap()) - val alterations: StateFlow> = _alterations - - private val _active = MutableStateFlow>>(emptyMap()) - val active: StateFlow>> get() = _active + private val _alterationsFlow = MutableStateFlow>(emptyMap()) + val alterationsFlow: StateFlow> = _alterationsFlow init { val scope = CoroutineScope(Dispatchers.IO + Job()) scope.launch { updateAlterations() } - scope.launch { - network.data.collect(::handleMessage) - } } private suspend fun updateAlterations() { - _alterations.value = loadAlteration() - } - - suspend fun updateActiveAlterations( - characterInstanceId: CharacterInstance.Id, - ) { - _active.value = _active.value.toMutableMap().also { - it[characterInstanceId] = loadActiveAlterations( - characterInstanceId = characterInstanceId, - ) - } + _alterationsFlow.value = loadAlteration() } private suspend fun loadAlteration(): Map { @@ -57,69 +34,11 @@ class AlterationStore( return data.associateBy { it.id } } - private suspend fun loadActiveAlterations( - characterInstanceId: CharacterInstance.Id, - ): List { - val request = client.activeAlterations( - prefix = characterInstanceId.prefix, - characterSheetId = characterInstanceId.characterSheetId, - instanceId = characterInstanceId.instanceId, - ) - return request - } - fun alterations(): Collection { - return alterations.value.values + return alterationsFlow.value.values } fun alteration(alterationId: String): Alteration? { - return alterations.value[alterationId] - } - - suspend fun toggleActiveAlteration( - characterInstanceId: CharacterInstance.Id, - alterationId: String, - ) { - client.toggleActiveAlterations( - prefix = characterInstanceId.prefix, - characterSheetId = characterInstanceId.characterSheetId, - instanceId = characterInstanceId.instanceId, - alterationId = alterationId, - ) - } - - private suspend fun handleMessage(message: SocketMessage) { - when (message) { - is ToggleActiveAlteration -> { - setActiveAlteration( - characterInstanceId = CharacterInstance.Id( - prefix = message.prefix, - characterSheetId = message.characterSheetId, - instanceId = message.instanceId, - ), - alterationId = message.alterationId, - active = message.active, - ) - } - - else -> Unit - } - } - - private suspend fun setActiveAlteration( - characterInstanceId: CharacterInstance.Id, - alterationId: String, - active: Boolean, - ) { - _active.value = _active.value.toMutableMap().also { map -> - map[characterInstanceId] = map[characterInstanceId]?.toMutableList() - ?.also { - when { - it.contains(alterationId) && !active -> it.remove(alterationId) - !it.contains(alterationId) && active -> it.add(alterationId) - } - } - ?: listOfNotNull(if (active) alterationId else null) - } + return alterationsFlow.value[alterationId] } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/campaign/CampaignStore.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/campaign/CampaignStore.kt index 826536b..66368e6 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/campaign/CampaignStore.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/campaign/CampaignStore.kt @@ -130,6 +130,11 @@ class CampaignStore( characterInstanceId = instanceId, diminished = message.diminished, ) + + is CampaignMessage.ToggleActiveAlteration -> updateAlterations( + characterInstanceId = instanceId, + alterationId = message.alterationId, + ) } } @@ -162,8 +167,8 @@ class CampaignStore( ) { val campaign = _campaignFlow.value - when { - campaign.characters.containsKey(characterInstanceId) -> { + when (characterInstanceId.prefix) { + Campaign.CharacterInstance.Id.PLAYER -> { val characters = campaign.characters.toMutableMap().also { it[characterInstanceId] = useCase.updateCharacteristic( instance = campaign.character(id = characterInstanceId), @@ -174,7 +179,7 @@ class CampaignStore( _campaignFlow.value = _campaignFlow.value.copy(characters = characters) } - campaign.npcs.containsKey(characterInstanceId) -> { + Campaign.CharacterInstance.Id.NPC -> { val npcs = campaign.npcs.toMutableMap().also { it[characterInstanceId] = useCase.updateCharacteristic( instance = campaign.npc(id = characterInstanceId), @@ -193,8 +198,8 @@ class CampaignStore( ) { val campaign = _campaignFlow.value - when { - campaign.characters.containsKey(characterInstanceId) -> { + when (characterInstanceId.prefix) { + Campaign.CharacterInstance.Id.PLAYER -> { val characters = campaign.characters.toMutableMap().also { it[characterInstanceId] = useCase.updateDiminished( instance = campaign.character(id = characterInstanceId), @@ -204,7 +209,7 @@ class CampaignStore( _campaignFlow.value = _campaignFlow.value.copy(characters = characters) } - campaign.npcs.containsKey(characterInstanceId) -> { + Campaign.CharacterInstance.Id.NPC -> { val npcs = campaign.npcs.toMutableMap().also { it[characterInstanceId] = useCase.updateDiminished( instance = campaign.npc(id = characterInstanceId), @@ -216,6 +221,53 @@ class CampaignStore( } } + suspend fun updateAlterations( + characterInstanceId: Campaign.CharacterInstance.Id, + alterationId: String, + ) { + val campaign = _campaignFlow.value + + when (characterInstanceId.prefix) { + Campaign.CharacterInstance.Id.PLAYER -> { + // fetch all the current campaign character + val characters = campaign.characters.toMutableMap() + // update the corresponding character alterations + characters[characterInstanceId]?.let { character -> + characters[characterInstanceId] = character.copy( + alterations = character.alterations.toMutableList().also { alterations -> + if (alterations.contains(alterationId)) { + alterations.remove(alterationId) + } else { + alterations.add(alterationId) + } + }, + ) + } + // update the flow. + _campaignFlow.value = _campaignFlow.value.copy(characters = characters) + } + + Campaign.CharacterInstance.Id.NPC -> { + // fetch all the current campaign character + val characters = campaign.npcs.toMutableMap() + // update the corresponding character alterations + characters[characterInstanceId]?.let { character -> + characters[characterInstanceId] = character.copy( + alterations = character.alterations.toMutableList().also { alterations -> + if (alterations.contains(alterationId)) { + alterations.remove(alterationId) + } else { + alterations.add(alterationId) + } + }, + ) + } + // update the flow. + _campaignFlow.value = _campaignFlow.value.copy(npcs = characters) + } + } + } + // endregion private fun MutableStateFlow.update(campaign: Campaign): Campaign { diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/TextMessageFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/TextMessageFactory.kt index 800991a..a8afd14 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/TextMessageFactory.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/TextMessageFactory.kt @@ -14,7 +14,6 @@ import com.pixelized.shared.lwa.protocol.websocket.GameMasterEvent import com.pixelized.shared.lwa.protocol.websocket.RestSynchronisation import com.pixelized.shared.lwa.protocol.websocket.RollMessage import com.pixelized.shared.lwa.protocol.websocket.SocketMessage -import com.pixelized.shared.lwa.protocol.websocket.ToggleActiveAlteration import com.pixelized.shared.lwa.protocol.websocket.UpdateSkillUsageMessage import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__hp_down @@ -65,45 +64,48 @@ class TextMessageFactory( ) } - is CampaignMessage.UpdateDiminished -> { - val sheetPreview = characterSheetRepository - .characterPreview(characterId = message.characterSheetId) - ?: return null + is CampaignMessage -> when (message) { + is CampaignMessage.UpdateDiminished -> { + val sheetPreview = characterSheetRepository + .characterPreview(characterId = message.characterSheetId) + ?: return null - DiminishedTextMessageUio( - id = "${message.timestamp}-$id-Diminished", - timestamp = formatTime.format(time), - character = sheetPreview.name, - diminished = message.diminished, - ) - } + DiminishedTextMessageUio( + id = "${message.timestamp}-$id-Diminished", + timestamp = formatTime.format(time), + character = sheetPreview.name, + diminished = message.diminished, + ) + } - is CampaignMessage.UpdateCharacteristic -> { - val sheet = characterSheetRepository.characterDetail( - characterSheetId = message.characterSheetId, - ) ?: return null + is CampaignMessage.UpdateCharacteristic -> { + val sheet = characterSheetRepository.characterDetail( + characterSheetId = message.characterSheetId, + ) ?: return null - // We are talking about damage / consumption there so old value have to be put first. - val value = message.oldValue - message.newValue + // We are talking about damage / consumption there so old value have to be put first. + val value = message.oldValue - message.newValue - CharacteristicTextMessageUio( - id = "${message.timestamp}-$id-Characteristic", - timestamp = formatTime.format(time), - label = when { - message.characteristic == Damage && value < 0 -> Res.string.chat__characteristic_change__hp_down - message.characteristic == Damage -> Res.string.chat__characteristic_change__hp_up - message.characteristic == Power && value < 0 -> Res.string.chat__characteristic_change__pp_down - else -> Res.string.chat__characteristic_change__pp_up - }, - character = sheet.name, - value = abs(value), - ) + CharacteristicTextMessageUio( + id = "${message.timestamp}-$id-Characteristic", + timestamp = formatTime.format(time), + label = when { + message.characteristic == Damage && value < 0 -> Res.string.chat__characteristic_change__hp_down + message.characteristic == Damage -> Res.string.chat__characteristic_change__hp_up + message.characteristic == Power && value < 0 -> Res.string.chat__characteristic_change__pp_down + else -> Res.string.chat__characteristic_change__pp_up + }, + character = sheet.name, + value = abs(value), + ) + } + + is CampaignMessage.ToggleActiveAlteration -> null // TODO } is RestSynchronisation.Campaign -> null is RestSynchronisation.CharacterSheetDelete -> null is RestSynchronisation.CharacterSheetUpdate -> null - is ToggleActiveAlteration -> null is UpdateSkillUsageMessage -> null is GameMasterEvent -> null is GameEvent.DisplayPortrait -> null diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/model/alteration/AlterationService.kt b/server/src/main/kotlin/com/pixelized/server/lwa/model/alteration/AlterationService.kt index 72bb5e4..ce6f3a5 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/model/alteration/AlterationService.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/model/alteration/AlterationService.kt @@ -1,56 +1,13 @@ 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 kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn class AlterationService( - private val store: AlterationStore, - private val campaignJsonFactory: CampaignJsonFactory, + store: AlterationStore, ) { - private val scope = CoroutineScope(Dispatchers.IO + Job()) - private val alterations = store.alterationsFlow() - private val actives = store.activeFlow() - .map { data -> - data.mapKeys { it: Map.Entry> -> - campaignJsonFactory.characterInstanceIdFromJson(characterInstanceIdJson = it.key) - } - }.stateIn( - scope = scope, - started = SharingStarted.Eagerly, - initialValue = emptyMap() - ) + private val alterationsFlow = store.alterationsFlow() fun alterations(): List { - return alterations.value - } - - fun active( - characterInstanceId: Campaign.CharacterInstance.Id, - ): List { - return actives.value[characterInstanceId] ?: emptyList() - } - - fun isAlterationActive( - characterInstanceId: Campaign.CharacterInstance.Id, - alterationId: String, - ): Boolean { - return actives.value[characterInstanceId]?.contains(alterationId) ?: false - } - - suspend fun toggleActiveAlteration( - characterInstanceId: Campaign.CharacterInstance.Id, - alterationId: String, - ): Boolean { - return store.toggleActiveAlteration( - characterInstanceId = characterInstanceId, - alterationId = alterationId, - ) + return alterationsFlow.value } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/model/alteration/AlterationStore.kt b/server/src/main/kotlin/com/pixelized/server/lwa/model/alteration/AlterationStore.kt index db8ed65..cee2d82 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/model/alteration/AlterationStore.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/model/alteration/AlterationStore.kt @@ -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>(emptyList()) - private val activeFlow = MutableStateFlow>>(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> = alterationsFlow - fun activeFlow(): StateFlow>> = activeFlow - private fun updateAlterations() { - alterationsFlow.value = loadAlterations() - } - - private fun updateActiveAlterations() { - activeFlow.value = loadActiveAlterations() - } - - private fun loadAlterations(): List { - return try { - val alterationFile = file() - val json = alterationFile.readText(charset = Charsets.UTF_8) - if (json.isBlank()) error("alterations file is empty") - this.json.decodeFromString>(json) + alterationsFlow.value = try { + loadAlterations() } catch (exception: Exception) { - // TODO log exception + println(exception) // TODO proper exception handling emptyList() } } - private fun loadActiveAlterations(): Map> { - val mainFile = file() - val jsonExt = ".json" + @Throws( + FileReadException::class, + JsonConversionException::class, + ) + private fun loadAlterations(): List { 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>(json) - val characterInstanceId = file.name.dropLast(n = jsonExt.length) - characterInstanceId to alterationIds + this.json.decodeFromString(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.toggle(alterationId: String): MutableList { - if (contains(alterationId)) { - remove(alterationId) - } else { - add(alterationId) - } - return this + ?: emptyList() } sealed class AlterationStoreException(root: Exception) : Exception(root) diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignService.kt b/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignService.kt index 6db9688..f81ebae 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignService.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignService.kt @@ -121,6 +121,40 @@ class CampaignService( } } + suspend fun removeInstance( + characterSheetId: String, + ): Boolean { + // fetch all the current campaign character + val characterIds = campaign.characters + .filterKeys { it.characterSheetId == characterSheetId } + .keys + val npcIds = campaign.npcs + .filterKeys { it.characterSheetId == characterSheetId } + .keys + + // check if the character is in the campaign. + if (characterIds.isEmpty() && npcIds.isEmpty()) return false + + // update the corresponding character + val characters = campaign.characters.toMutableMap() + val npcs = campaign.npcs.toMutableMap() + characterIds.forEach(characters::remove) + npcIds.forEach(npcs::remove) + + // save the campaign to the disk + update the flow. + return try { + store.save( + campaign = campaign.copy( + characters = characters, + npcs = npcs, + ) + ) + true + } catch (exception: Exception) { + false + } + } + suspend fun setScene( scene: Campaign.Scene, ): Boolean { @@ -142,8 +176,8 @@ class CampaignService( characteristic: Campaign.CharacterInstance.Characteristic, value: Int, ) { - when { - campaign.characters.containsKey(characterInstanceId) -> { + when (characterInstanceId.prefix) { + Campaign.CharacterInstance.Id.PLAYER -> { // fetch all the current campaign character val characters = campaign.characters.toMutableMap() // update the corresponding character using the use case. @@ -158,7 +192,7 @@ class CampaignService( ) } - campaign.npcs.containsKey(characterInstanceId) -> { + Campaign.CharacterInstance.Id.NPC -> { // fetch all the current campaign character val npcs = campaign.npcs.toMutableMap() // update the corresponding character using the use case. @@ -179,8 +213,8 @@ class CampaignService( characterInstanceId: Campaign.CharacterInstance.Id, diminished: Int, ) { - when { - campaign.characters.containsKey(characterInstanceId) -> { + when (characterInstanceId.prefix) { + Campaign.CharacterInstance.Id.PLAYER -> { // fetch all the current campaign character val characters = campaign.characters.toMutableMap() // update the corresponding character using the use case. @@ -194,7 +228,7 @@ class CampaignService( ) } - campaign.npcs.containsKey(characterInstanceId) -> { + Campaign.CharacterInstance.Id.NPC -> { // fetch all the current campaign character val npcs = campaign.npcs.toMutableMap() // update the corresponding character using the use case. @@ -210,6 +244,55 @@ class CampaignService( } } + suspend fun toggleAlteration( + characterInstanceId: Campaign.CharacterInstance.Id, + alterationId: String, + ) { + when (characterInstanceId.prefix) { + Campaign.CharacterInstance.Id.PLAYER -> { + // fetch all the current campaign character + val characters = campaign.characters.toMutableMap() + // update the corresponding character alterations + characters[characterInstanceId]?.let { character -> + characters[characterInstanceId] = character.copy( + alterations = character.alterations.toMutableList().also { alterations -> + if (alterations.contains(alterationId)) { + alterations.remove(alterationId) + } else { + alterations.add(alterationId) + } + }, + ) + } + // save the campaign to the disk + update the flow. + store.save( + campaign = campaign.copy(characters = characters) + ) + } + + Campaign.CharacterInstance.Id.NPC -> { + // fetch all the current campaign character + val characters = campaign.npcs.toMutableMap() + // update the corresponding character alterations + characters[characterInstanceId]?.let { character -> + characters[characterInstanceId] = character.copy( + alterations = character.alterations.toMutableList().also { alterations -> + if (alterations.contains(alterationId)) { + alterations.remove(alterationId) + } else { + alterations.add(alterationId) + } + }, + ) + } + // save the campaign to the disk + update the flow. + store.save( + campaign = campaign.copy(npcs = characters) + ) + } + } + } + suspend fun updateToggleParty() { store.save( campaign = campaign.copy( diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignStore.kt b/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignStore.kt index b5eabbf..6f3ef36 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignStore.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignStore.kt @@ -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 = flow + fun campaignFlow(): StateFlow = 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(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(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") diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetService.kt b/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetService.kt index 55fbba8..3756d0c 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetService.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetService.kt @@ -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, @@ -38,8 +38,8 @@ class CharacterSheetService( return store.save(sheet = factory.convertFromJson(character)) } - fun deleteCharacterSheet(characterId: String): Boolean { - return store.delete(id = characterId) + fun deleteCharacterSheet(characterSheetId: String): Boolean { + return store.delete(id = characterSheetId) } // Data manipulation through WebSocket. diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetStore.kt b/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetStore.kt index 2e678d8..20df234 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetStore.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetStore.kt @@ -20,74 +20,33 @@ class CharacterSheetStore( private val json: Json, ) { private val directory = File(pathProvider.characterStorePath()).also { it.mkdirs() } - private val flow = MutableStateFlow>(value = emptyList()) + private val characterSheetsFlow = MutableStateFlow>(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> = flow + fun characterSheetsFlow(): StateFlow> = 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 { + suspend fun loadCharacterSheets(): List { 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") } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/Engine.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/Engine.kt index 210eead..900e4b4 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/Engine.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/Engine.kt @@ -6,12 +6,12 @@ import com.pixelized.server.lwa.model.character.CharacterSheetService import com.pixelized.shared.lwa.model.campaign.Campaign import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory import com.pixelized.shared.lwa.protocol.websocket.CampaignMessage +import com.pixelized.shared.lwa.protocol.websocket.CampaignMessage.ToggleActiveAlteration import com.pixelized.shared.lwa.protocol.websocket.GameEvent import com.pixelized.shared.lwa.protocol.websocket.GameMasterEvent import com.pixelized.shared.lwa.protocol.websocket.RestSynchronisation import com.pixelized.shared.lwa.protocol.websocket.RollMessage import com.pixelized.shared.lwa.protocol.websocket.SocketMessage -import com.pixelized.shared.lwa.protocol.websocket.ToggleActiveAlteration import com.pixelized.shared.lwa.protocol.websocket.UpdateSkillUsageMessage import kotlinx.coroutines.flow.MutableSharedFlow @@ -45,6 +45,11 @@ class Engine( characterInstanceId = instanceId, diminished = message.diminished, ) + + is ToggleActiveAlteration -> campaignService.toggleAlteration( + characterInstanceId = instanceId, + alterationId = message.alterationId, + ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/Server.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/Server.kt index a5dc028..264e138 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/Server.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/Server.kt @@ -1,15 +1,14 @@ package com.pixelized.server.lwa.server -import com.pixelized.server.lwa.server.rest.alteration.getActiveAlteration -import com.pixelized.server.lwa.server.rest.alteration.getAlteration -import com.pixelized.server.lwa.server.rest.alteration.putActiveAlteration -import com.pixelized.server.lwa.server.rest.campaign.removeCampaignCharacter +import com.pixelized.server.lwa.server.rest.alteration.getAlterations import com.pixelized.server.lwa.server.rest.campaign.deleteCampaignNpc import com.pixelized.server.lwa.server.rest.campaign.getCampaign import com.pixelized.server.lwa.server.rest.campaign.putCampaignCharacter import com.pixelized.server.lwa.server.rest.campaign.putCampaignNpc import com.pixelized.server.lwa.server.rest.campaign.putCampaignScene +import com.pixelized.server.lwa.server.rest.campaign.putToggleAlteration +import com.pixelized.server.lwa.server.rest.campaign.removeCampaignCharacter import com.pixelized.server.lwa.server.rest.character.deleteCharacter import com.pixelized.server.lwa.server.rest.character.getCharacter import com.pixelized.server.lwa.server.rest.character.getCharacters @@ -86,7 +85,7 @@ class LocalServer { val frame = Frame.Text(text = data) try { send(frame) - } catch (exception : Exception) { + } catch (exception: Exception) { // TODO println("WebSocket exception: ${exception.localizedMessage}") } @@ -112,6 +111,10 @@ class LocalServer { } } ) + get( + path = "/alterations", + body = engine.getAlterations(), + ) get( path = "/characters", body = engine.getCharacters(), @@ -135,6 +138,10 @@ class LocalServer { path = "", body = engine.getCampaign(), ) + put( + path = "/toggleAlteration", + body = engine.putToggleAlteration(), + ) route(path = "/character") { put( path = "/update", @@ -160,20 +167,6 @@ class LocalServer { body = engine.putCampaignScene(), ) } - route(path = "/alterations") { - get( - path = "", - body = engine.getAlteration(), - ) - get( - path = "/active", - body = engine.getActiveAlteration(), - ) - put( - path = "/active/toggle", - body = engine.putActiveAlteration(), - ) - } } } ) diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Active_Alteration.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Active_Alteration.kt deleted file mode 100644 index 6d1f869..0000000 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Active_Alteration.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.pixelized.server.lwa.server.rest.alteration - -import com.pixelized.server.lwa.server.Engine -import com.pixelized.server.lwa.utils.extentions.characterInstanceId -import io.ktor.http.HttpStatusCode -import io.ktor.server.response.respond -import io.ktor.server.response.respondText - -fun Engine.getActiveAlteration(): suspend io.ktor.server.routing.RoutingContext.() -> Unit { - return { - try { - // get the query parameter - val characterInstanceId = call.queryParameters.characterInstanceId - // fetch the data from the service - val data = alterationService.active(characterInstanceId = characterInstanceId) - // respond to the client. - call.respond(data) - } catch (exception: Exception) { - call.respondText( - text = exception.localizedMessage, - status = HttpStatusCode.UnprocessableEntity, - ) - } - } -} \ No newline at end of file diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Alteration.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Alterations.kt similarity index 71% rename from server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Alteration.kt rename to server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Alterations.kt index 43d298a..885d4e3 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Alteration.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Alterations.kt @@ -3,7 +3,7 @@ package com.pixelized.server.lwa.server.rest.alteration import com.pixelized.server.lwa.server.Engine import io.ktor.server.response.respond -fun Engine.getAlteration(): suspend io.ktor.server.routing.RoutingContext.() -> Unit { +fun Engine.getAlterations(): suspend io.ktor.server.routing.RoutingContext.() -> Unit { return { call.respond(alterationService.alterations()) } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/PUT_ActiveAlteration.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/PUT_ToggleAlteration.kt similarity index 68% rename from server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/PUT_ActiveAlteration.kt rename to server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/PUT_ToggleAlteration.kt index dc5edb0..89b85ec 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/PUT_ActiveAlteration.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/PUT_ToggleAlteration.kt @@ -1,13 +1,13 @@ -package com.pixelized.server.lwa.server.rest.alteration +package com.pixelized.server.lwa.server.rest.campaign import com.pixelized.server.lwa.server.Engine import com.pixelized.server.lwa.utils.extentions.characterInstanceId -import com.pixelized.shared.lwa.protocol.websocket.ToggleActiveAlteration +import com.pixelized.shared.lwa.protocol.websocket.CampaignMessage import io.ktor.http.HttpStatusCode import io.ktor.server.request.receive import io.ktor.server.response.respondText -fun Engine.putActiveAlteration(): suspend io.ktor.server.routing.RoutingContext.() -> Unit { +fun Engine.putToggleAlteration(): suspend io.ktor.server.routing.RoutingContext.() -> Unit { return { try { // get the query parameter @@ -17,30 +17,31 @@ fun Engine.putActiveAlteration(): suspend io.ktor.server.routing.RoutingContext. val alterationId = call.receive() // Update the alteration - val updated = alterationService.toggleActiveAlteration( + campaignService.toggleAlteration( characterInstanceId = characterInstanceId, alterationId = alterationId, ) - if (!updated) { - error("Unexpected error occurred when toggling the alteration (id:$alterationId) for the character (id:$characterInstanceId)") - } // build the Http response & send it call.respondText( text = "$HttpStatusCode.Accepted", status = HttpStatusCode.Accepted, ) + + val isAlterationActive = campaignService.campaign() + .instances[characterInstanceId] + ?.alterations + ?.contains(alterationId) + ?: false + // share the modification to all client through the websocket. webSocket.emit( - ToggleActiveAlteration( + CampaignMessage.ToggleActiveAlteration( timestamp = System.currentTimeMillis(), prefix = characterInstanceId.prefix, characterSheetId = characterInstanceId.characterSheetId, instanceId = characterInstanceId.instanceId, alterationId = alterationId, - active = alterationService.isAlterationActive( - characterInstanceId = characterInstanceId, - alterationId = alterationId - ), + active = isAlterationActive, ) ) } catch (exception: Exception) { diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/DELETE_Character.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/DELETE_Character.kt index 93efcb6..033ec74 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/DELETE_Character.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/DELETE_Character.kt @@ -11,9 +11,10 @@ fun Engine.deleteCharacter(): suspend io.ktor.server.routing.RoutingContext.() - val characterSheetId = call.parameters.characterSheetId val deleted = characterService.deleteCharacterSheet( - characterId = characterSheetId + characterSheetId = characterSheetId + ) && campaignService.removeInstance( + characterSheetId = characterSheetId, ) - // TODO campaign & alteration cleanup. if (deleted) { call.respondText( diff --git a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/model/campaign/Campaign.kt b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/model/campaign/Campaign.kt index c7aceaf..f3c7b8a 100644 --- a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/model/campaign/Campaign.kt +++ b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/model/campaign/Campaign.kt @@ -6,8 +6,11 @@ data class Campaign( val scene: Scene, val options: Options, ) { + val instances = characters + npcs + data class CharacterInstance( val characteristic: Map, + val alterations: List, val diminished: Int, ) { data class Id( @@ -39,6 +42,7 @@ data class Campaign( companion object { fun empty() = CharacterInstance( characteristic = emptyMap(), + alterations = emptyList(), diminished = 0, ) } diff --git a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/model/campaign/CampaignJsonV1.kt b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/model/campaign/CampaignJsonV1.kt index ea0483f..0b15208 100644 --- a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/model/campaign/CampaignJsonV1.kt +++ b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/model/campaign/CampaignJsonV1.kt @@ -13,6 +13,7 @@ data class CampaignJsonV1( @Serializable data class CharacterInstanceJsonV1( val characteristic: Map, + val alterations: List?, val diminished: Int?, ) : CampaignJson.CharacterInstanceJson { enum class CharacteristicV1 : CampaignJson.CharacterInstanceJson.CharacteristicJson { diff --git a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/model/campaign/factory/CampaignJsonFactory.kt b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/model/campaign/factory/CampaignJsonFactory.kt index a142acd..1c7b203 100644 --- a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/model/campaign/factory/CampaignJsonFactory.kt +++ b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/model/campaign/factory/CampaignJsonFactory.kt @@ -79,6 +79,7 @@ class CampaignJsonFactory( characteristic = data.characteristic .map { char -> convertToJson(characteristic = char.key) to char.value } .toMap(), + alterations = data.alterations, diminished = data.diminished, ) } diff --git a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/model/campaign/factory/CampaignJsonV1Factory.kt b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/model/campaign/factory/CampaignJsonV1Factory.kt index 02fb187..887d7f6 100644 --- a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/model/campaign/factory/CampaignJsonV1Factory.kt +++ b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/model/campaign/factory/CampaignJsonV1Factory.kt @@ -49,6 +49,7 @@ class CampaignJsonV1Factory { characteristic = characterInstanceJson.characteristic .mapKeys { convertFromV1(characteristicJson = it.key) } .toMap(), + alterations = characterInstanceJson.alterations ?: emptyList(), diminished = characterInstanceJson.diminished ?: 0, ) } diff --git a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/websocket/CampaignMessage.kt b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/websocket/CampaignMessage.kt index 3648abe..d3acc73 100644 --- a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/websocket/CampaignMessage.kt +++ b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/websocket/CampaignMessage.kt @@ -28,4 +28,14 @@ sealed interface CampaignMessage : SocketMessage, CharacterInstanceIdMessage { override val instanceId: Int, val diminished: Int, ) : CampaignMessage + + @Serializable + data class ToggleActiveAlteration( + override val timestamp: Long, + override val characterSheetId: String, + override val prefix: Char, + override val instanceId: Int, + val alterationId: String, + val active: Boolean, + ) : CampaignMessage, CharacterInstanceIdMessage } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/websocket/ToggleActiveAlteration.kt b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/websocket/ToggleActiveAlteration.kt deleted file mode 100644 index 23fbb45..0000000 --- a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/websocket/ToggleActiveAlteration.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.pixelized.shared.lwa.protocol.websocket - -import kotlinx.serialization.Serializable - -@Serializable -data class ToggleActiveAlteration( - override val timestamp: Long, - override val characterSheetId: String, - override val prefix: Char, - override val instanceId: Int, - val alterationId: String, - val active: Boolean, -) : SocketMessage, CharacterInstanceIdMessage \ No newline at end of file