// 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()