Add map management to the server (REST+WS)

This commit is contained in:
Thomas Andres Gomez 2025-12-07 10:18:57 +01:00
parent 3485b8a9fd
commit 03dbd7aad6
62 changed files with 1226 additions and 144 deletions

View file

@ -11,6 +11,8 @@ import com.pixelized.shared.lwa.model.inventory.factory.InventoryJsonFactory
import com.pixelized.shared.lwa.model.inventory.factory.InventoryJsonFactoryV1
import com.pixelized.shared.lwa.model.item.factory.ItemJsonFactory
import com.pixelized.shared.lwa.model.item.factory.ItemJsonFactoryV1
import com.pixelized.shared.lwa.model.map.factory.MapJsonFactory
import com.pixelized.shared.lwa.model.map.factory.MapJsonFactoryV1
import com.pixelized.shared.lwa.model.tag.TagJsonFactory
import com.pixelized.shared.lwa.parser.dice.DiceParser
import com.pixelized.shared.lwa.parser.expression.ExpressionParser
@ -58,6 +60,8 @@ val factoryDependencies
factoryOf(::ItemJsonFactoryV1)
factoryOf(::InventoryJsonFactory)
factoryOf(::InventoryJsonFactoryV1)
factoryOf(::MapJsonFactory)
factoryOf(::MapJsonFactoryV1)
}
val parserDependencies

View file

@ -1,13 +1,25 @@
package com.pixelized.shared.lwa.model.campaign
import com.pixelized.shared.lwa.model.map.Map
data class Campaign(
val characters: Set<String>,
val npcs: Set<String>,
val map: MiniMap?,
val scene: Scene,
val options: Options,
) {
val instances = characters + npcs
data class MiniMap(
val id: String,
val camera: Map.Camera?,
) {
companion object {
fun empty(): MiniMap? = null
}
}
data class Scene(
val name: String,
) {
@ -34,6 +46,7 @@ data class Campaign(
fun empty() = Campaign(
characters = emptySet(),
npcs = emptySet(),
map = null,
scene = Scene.empty(),
options = Options.empty(),
)

View file

@ -12,6 +12,9 @@ sealed interface CampaignJson {
sealed interface CharacteristicJson
}
@Serializable
sealed interface MiniMap
@Serializable
sealed interface SceneJson

View file

@ -6,10 +6,24 @@ import kotlinx.serialization.Serializable
data class CampaignJsonV2(
val characters: Set<String>,
val npcs: Set<String>,
val map: MiniMapJson?,
val scene: SceneJsonV2?,
val options: OptionsJsonV2?,
) : CampaignJson {
@Serializable
data class MiniMapJson(
val id: String,
val camera: CameraJson?,
): CampaignJson.MiniMap {
@Serializable
data class CameraJson(
val zoom: Float,
val offsetX: Int,
val offsetY: Int,
)
}
@Serializable
data class SceneJsonV2(
val name: String,

View file

@ -27,6 +27,14 @@ class CampaignJsonFactory(
}
}
fun convertFromJson(
json: CampaignJson.MiniMap
): Campaign.MiniMap {
return when (json) {
is CampaignJsonV2.MiniMapJson -> v2.convertFromJson(mapJson = json)
}
}
// Json conversion.
fun createScene(
@ -43,6 +51,18 @@ class CampaignJsonFactory(
return CampaignJsonV2(
characters = campaign.characters,
npcs = campaign.npcs,
map = campaign.map?.let { campaign ->
CampaignJsonV2.MiniMapJson(
id = campaign.id,
camera = campaign.camera?.let { camera ->
CampaignJsonV2.MiniMapJson.CameraJson(
zoom = camera.zoom,
offsetX = camera.offsetX,
offsetY = camera.offsetY,
)
}
)
},
scene = CampaignJsonV2.SceneJsonV2(
name = campaign.scene.name,
),

View file

@ -14,6 +14,7 @@ class CampaignJsonV1Factory {
return Campaign(
characters = emptySet(),
npcs = emptySet(),
map = null,
scene = campaignJson.scene
?.let { convertFromJson(it) }
?: Campaign.Scene.empty(),

View file

@ -2,6 +2,7 @@ package com.pixelized.shared.lwa.model.campaign.factory
import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.model.campaign.CampaignJsonV2
import com.pixelized.shared.lwa.model.map.Map
class CampaignJsonV2Factory {
@ -11,15 +12,33 @@ class CampaignJsonV2Factory {
return Campaign(
characters = campaignJson.characters,
npcs = campaignJson.npcs,
map = campaignJson.map
?.let { convertFromJson(mapJson = it) }
?: Campaign.MiniMap.empty(),
scene = campaignJson.scene
?.let { convertFromJson(it) }
?.let { convertFromJson(sceneJson = it) }
?: Campaign.Scene.empty(),
options = campaignJson.options
?.let { convertFromJson(it) }
?.let { convertFromJson(optionsJson = it) }
?: Campaign.Options.empty()
)
}
fun convertFromJson(
mapJson: CampaignJsonV2.MiniMapJson,
): Campaign.MiniMap {
return Campaign.MiniMap(
id = mapJson.id,
camera = mapJson.camera?.let {
Map.Camera(
zoom = it.zoom,
offsetX = it.offsetX,
offsetY = it.offsetY,
)
}
)
}
fun convertFromJson(
sceneJson: CampaignJsonV2.SceneJsonV2,
): Campaign.Scene {

View file

@ -0,0 +1,26 @@
package com.pixelized.shared.lwa.model.map
data class Map(
val id: String,
val name : String,
val camera: Camera,
val size: Size,
val resources: List<Resource>,
) {
data class Camera(
val zoom: Float,
val offsetX: Int,
val offsetY: Int,
)
data class Size(
val width: Int,
val height: Int,
)
data class Resource(
val id: String,
val name: String,
val uri: String,
)
}

View file

@ -0,0 +1,8 @@
package com.pixelized.shared.lwa.model.map
import kotlinx.serialization.Serializable
@Serializable
sealed interface MapJson {
val id: String
}

View file

@ -0,0 +1,32 @@
package com.pixelized.shared.lwa.model.map
import kotlinx.serialization.Serializable
@Serializable
data class MapJsonV1(
override val id: String,
val name: String,
val camera: Camera,
val size: Size,
val resources: List<Resource>,
) : MapJson {
@Serializable
data class Camera(
val zoom: Float,
val offsetX: Int,
val offsetY: Int,
)
@Serializable
data class Size(
val width: Int,
val height: Int,
)
@Serializable
data class Resource(
val id: String,
val name: String,
val uri: String,
)
}

View file

@ -0,0 +1,43 @@
package com.pixelized.shared.lwa.model.map.factory
import com.pixelized.shared.lwa.model.map.Map
import com.pixelized.shared.lwa.model.map.MapJson
import com.pixelized.shared.lwa.model.map.MapJsonV1
class MapJsonFactory(
private val v1: MapJsonFactoryV1,
) {
fun convertFromJson(
json: MapJson,
): Map {
return when (json) {
is MapJsonV1 -> v1.convertFromJson(json = json)
}
}
fun convertToJson(
item: Map,
): MapJson {
return MapJsonV1(
id = item.id,
name = item.name,
camera = MapJsonV1.Camera(
zoom = item.camera.zoom,
offsetX = item.camera.offsetX,
offsetY = item.camera.offsetY,
),
size = MapJsonV1.Size(
width = item.size.width,
height = item.size.height,
),
resources = item.resources.map {
MapJsonV1.Resource(
id = it.id,
name = it.name,
uri = it.uri,
)
},
)
}
}

View file

@ -0,0 +1,31 @@
package com.pixelized.shared.lwa.model.map.factory
import com.pixelized.shared.lwa.model.map.Map
import com.pixelized.shared.lwa.model.map.MapJsonV1
class MapJsonFactoryV1 {
fun convertFromJson(
json: MapJsonV1,
): Map {
return Map(
id = json.id,
name = json.name,
camera = Map.Camera(
zoom = json.camera.zoom,
offsetX = json.camera.offsetX,
offsetY = json.camera.offsetY,
),
size = Map.Size(
width = json.size.width,
height = json.size.height,
),
resources = json.resources.map {
Map.Resource(
id = it.id,
name = it.name,
uri = it.uri,
)
},
)
}
}

View file

@ -74,4 +74,19 @@ sealed interface ApiSynchronisation : SocketMessage {
override val timestamp: Long,
override val characterSheetId: String,
) : InventoryApiSynchronisation
@Serializable
sealed interface MapApiSynchronisation : ApiSynchronisation
@Serializable
data class MapUpdate(
override val timestamp: Long,
val id: String,
) : MapApiSynchronisation
@Serializable
data class MapDelete(
override val timestamp: Long,
val id: String,
) : MapApiSynchronisation
}

View file

@ -1,5 +1,6 @@
package com.pixelized.shared.lwa.protocol.websocket
import com.pixelized.shared.lwa.model.campaign.CampaignJson
import kotlinx.serialization.Serializable
@Serializable
@ -7,7 +8,7 @@ sealed interface CampaignEvent : SocketMessage {
// TODO Will probably be moved when the scene become more complex, (should already actually)
@Serializable
data class UpdateScene(
data class SceneUpdated(
override val timestamp: Long,
val name: String,
) : CampaignEvent
@ -35,4 +36,15 @@ sealed interface CampaignEvent : SocketMessage {
override val timestamp: Long,
override val characterSheetId: String,
) : CampaignEvent, CharacterSheetIdMessage
@Serializable
data class MapUpdated(
override val timestamp: Long,
val map: CampaignJson.MiniMap,
): CampaignEvent
@Serializable
data class MapDeleted(
override val timestamp: Long,
): CampaignEvent
}

View file

@ -83,4 +83,14 @@ class PathProvider(
OperatingSystem.Macintosh -> "${storePath(os = os, app = app)}inventory/"
}
}
fun mapPath(
os: OperatingSystem = this.operatingSystem,
app: String = this.appName,
): String {
return when (os) {
OperatingSystem.Windows -> "${storePath(os = os, app = app)}maps\\"
OperatingSystem.Macintosh -> "${storePath(os = os, app = app)}maps/"
}
}
}