Exit Full View

Games Cupboard / gamescupboard-common / src / commonMain / kotlin / Messages.kt

// NOTE. Messages do NOT have a package name. This is deliberate to save a few bytes for every message.
// Not a huge saving, but every little helps ;-)
// I've also made the field names short for the same reason.

import com.soywiz.klock.DateTime
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import uk.co.nickthecoder.gamescupboard.common.*

private val json = Json

/**
 * All [Message]s are wrapped inside a [Packet], because at some point, we may want to do sanity checks.
 * e.g. If a message is badly delayed, then show a warning that the connection is too laggy.
 */
@Serializable
data class Packet(
    val m: Message,
    val time: Long = DateTime.nowUnixLong()
)

@Serializable
sealed class Message {
    fun toJsonPacket() = json.encodeToString(Packet(this))
}

// ==== GAME INITIALISATION ====

@Serializable
object NotInvited : Message()

@Serializable
class GameInfo(
    val p: Player,
    val seatCount: Int,
    val otherPlayers: List<Player>,
    val gameTypeLabel: String,
    val variationLabel: String,
    val gameLabel: String,
    val bgColor: String,
    val grids: List<Grid>,
    val specialAreas: List<SpecialArea>,
    val specialPoints: List<SpecialPoint>,
    val commands: List<CommandInfo>,
    val commandPrototypes: List<CommandPrototype>,
    val backgroundObjects: List<GameObject>,
    val playingObjects: List<GameObject>,
    val foregroundObjects: List<GameObject>,
    val rules: String,
    val buttons: List<CommandButton>,
    /**
     * If not zero, then all GameObjects are flipped about the line Y=mirrorY.
     * Therefore, all y values in messages MoveObject, DragObject, AddObject etc.
     * should be transformed when received and sent.
     */
    val mirrorX: Int,
    val mirrorY: Int,
    val playerColors: List<String>,
    val scoreSheetHasBidColumn: Boolean
) : Message()

/**
 * Remove all game objects, and (re)create those in [gameObjects].
 */
@Serializable
data class ResetGame(val removeIds: List<Int>, val gameObjects: List<GameObject>) : Message()

// ==== PLAYER ====

/**
 * When a new client connects to the server, all other clients are sent this message.
 * NOTE, it is likely that an "Avatar" will be sent at the same time, but that will
 * be a separate message (after this one).
 */
@Serializable
data class PlayerJoined(val p: Player) : Message()

/**
 * When a client closes their connected, then all other clients are sent this message.
 */
@Serializable
data class PlayerLeft(val id: Int) : Message()

@Serializable
data class RenamePlayer(val id: Int, val name: String) : Message()

// ==== GAME OBJECT ====

@Serializable
data class AddObjects(val gameObjects: List<GameObject>) : Message()

@Serializable
data class RemoveObjects(val ids: List<Int>, val saveToBin : Boolean = false) : Message()

@Serializable
data class ScaleObject(val id: Int, val scale: Double) : Message()

@Serializable
data class CycleText(val id: Int, val cyclicIndex: Int) : Message()

@Serializable
/**
 * For MultipleImageObjects only.
 */
data class ChangeImage(val id: Int, val imageNumber: Int) : Message()

@Serializable
enum class ChangeZOrder { BOTTOM, NO_CHANGE, TOP }

/**
 */
@Serializable
data class MoveObject(val id: Int, val x: Int, val y: Int, val z: ChangeZOrder = ChangeZOrder.TOP) : Message() {
    constructor(id: Int, x: Double, y: Double, changeZOrder: ChangeZOrder = ChangeZOrder.TOP) : this(id, x.toInt(), y.toInt(), changeZOrder)
}

/**
 * Movements of objects while the playing moving it is still dragging.
 * When the mouse is released [MoveObject] will be sent.
 */
@Serializable
data class DragObject(val id: Int, val x: Int, val y: Int, val forceFaceDown: Boolean) : Message() {
    constructor(id: Int, x: Double, y: Double, forceFaceDown: Boolean) : this(id, x.toInt(), y.toInt(), forceFaceDown)
}

@Serializable
data class ChangePrivacy(val id: Int, val privateToPlayerId: Int?) : Message()

@Serializable
data class FaceUpOrDown(val id: Int, val isFaceUp: Boolean) : Message()

// ==== COMMANDS ====

@Serializable
class RunCommand(val name: String, val parameters: List<String>) : Message()

@Serializable
object CommandOk : Message()

@Serializable
class CommandError(val message: String) : Message()

// ==== MISC ====

@Serializable
data class ChatMessage(val fromId: Int, val str: String, val toId: Int? = null) : Message()

@Serializable
data class ScoreSheetData(val scores: List<List<Double>>) : Message()

@Serializable
/**
 * [on] True to turn the highlight on. Null to keep it at its current state.
 */
data class MouseHighlight(val id: Int, val x: Int, val y: Int, val on: Boolean?)

/**
 * [state] : 0 Mouse down (start highlighting) 1 : Move, 2 Mouse up (stop highlighting)
 */
@Serializable
data class HighlightMouse(val playerId: Int, val x: Int, val y: Int, val state: Boolean?) : Message()

@Serializable
object AddBin : Message()

/**
 * Removes the object (similar to [RemoveObjects] message), but also remembers
 * the game object so that it may later be restored using [RestoreFromBin].
 * NOTE. Currently, only 1 game object is remembered, as it is designed to allow users to correct
 * when an object is added to the bin by accident.
 */
@Serializable
class AddToBin(val id: Int) : Message()

/**
 * If the `Game` has a `Bin`, and it has remembered a deleted GameObject,
 * then that game object is restored (with a new id).
 */
@Serializable
object RestoreFromBin : Message()