Skip to content

Commit

Permalink
Added frame handling, added functional WS sending, added apiVersion t…
Browse files Browse the repository at this point in the history
…o server feature set
  • Loading branch information
CrsiX committed May 4, 2023
1 parent 1fdb8d0 commit 9888bed
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 4 deletions.
4 changes: 2 additions & 2 deletions core/src/com/unciv/logic/multiplayer/OldOnlineMultiplayer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ private val FILE_UPDATE_THROTTLE_PERIOD = Duration.ofSeconds(60)
class OldOnlineMultiplayer {
private val files = UncivGame.Current.files
private val multiplayerFiles = OnlineMultiplayerFiles()
private var featureSet = ServerFeatureSet()
private var featureSet = ServerFeatureSet(apiVersion = 0)

private val savedGames: MutableMap<FileHandle, OnlineMultiplayerGame> = Collections.synchronizedMap(mutableMapOf())

Expand Down Expand Up @@ -344,7 +344,7 @@ class OldOnlineMultiplayer {
json().fromJson(ServerFeatureSet::class.java, result)
} catch (ex: Exception) {
Log.error("${UncivGame.Current.settings.multiplayer.server} does not support server feature set")
ServerFeatureSet()
ServerFeatureSet(apiVersion = 1)
}
}
}
Expand Down
78 changes: 76 additions & 2 deletions core/src/com/unciv/logic/multiplayer/OnlineMultiplayer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import com.unciv.logic.event.EventBus
import com.unciv.logic.multiplayer.api.Api
import com.unciv.logic.multiplayer.api.ApiErrorResponse
import com.unciv.logic.multiplayer.api.ApiStatusCode
import com.unciv.logic.multiplayer.api.WebSocketMessage
import com.unciv.logic.multiplayer.api.WebSocketMessageSerializer
import com.unciv.logic.multiplayer.api.WebSocketMessageType
import com.unciv.logic.multiplayer.storage.FileStorageRateLimitReached
import com.unciv.logic.multiplayer.storage.MultiplayerAuthException
import com.unciv.logic.multiplayer.storage.MultiplayerFileNotFoundException
Expand All @@ -36,6 +38,7 @@ import java.time.Duration
import java.time.Instant
import java.util.*
import java.util.concurrent.atomic.AtomicReference
import java.util.logging.Level

/**
* How often files can be checked for new multiplayer games (could be that the user modified their file system directly). More checks within this time period
Expand All @@ -53,7 +56,7 @@ class OnlineMultiplayer {

private val files = UncivGame.Current.files
private val multiplayerFiles = OnlineMultiplayerFiles()
private var featureSet = ServerFeatureSet()
private var featureSet = ServerFeatureSet(apiVersion = 2)

private val savedGames: MutableMap<FileHandle, OnlineMultiplayerGame> = Collections.synchronizedMap(mutableMapOf())

Expand All @@ -67,6 +70,7 @@ class OnlineMultiplayer {
private val api = Api(UncivGame.Current.settings.multiplayer.server)

init {
logger.level = Level.FINER // for debugging
var password = UncivGame.Current.settings.multiplayer.passwords[UncivGame.Current.settings.multiplayer.server]
if (password == null) {
password = "SomePasswordForThoseFolksWhoDoNotHaveAnyStrongPasswordYet!" // TODO: Obviously, replace this password
Expand Down Expand Up @@ -136,7 +140,77 @@ class OnlineMultiplayer {
return true
}

/**
* Handle incoming WebSocket messages
*/
private fun handleIncomingWSMessage(msg: WebSocketMessage) {
when (msg.type) {
WebSocketMessageType.InvalidMessage -> {
logger.warning("Received invalid message from WebSocket connection")
}
WebSocketMessageType.FinishedTurn -> {
// This message type is not meant to be received from the server
logger.warning("Received FinishedTurn message from WebSocket connection")
}
WebSocketMessageType.UpdateGameData -> {
// TODO: The body of this message contains a whole game state, so we need to unpack and use it here
}
WebSocketMessageType.ClientDisconnected -> {
logger.info("Received ClientDisconnected message from WebSocket connection")
// TODO: Implement client connectivity handling
}
WebSocketMessageType.ClientReconnected -> {
logger.info("Received ClientReconnected message from WebSocket connection")
// TODO: Implement client connectivity handling
}
WebSocketMessageType.IncomingChatMessage -> {
logger.info("Received IncomingChatMessage message from WebSocket connection")
// TODO: Implement chat message handling
}
}
}

/**
* Handle a newly established WebSocket connection
*/
private suspend fun handleWS(session: ClientWebSocketSession) {
sendChannel?.close()
sendChannel = session.outgoing

try {
while (true) {
val incomingFrame = session.incoming.receive()
when (incomingFrame.frameType) {
FrameType.CLOSE, FrameType.PING, FrameType.PONG -> {
// This handler won't handle control frames
logger.info("Received CLOSE, PING or PONG as message")
}
FrameType.BINARY -> {
logger.warning("Received binary packet which can't be parsed at the moment")
}
FrameType.TEXT -> {
try {
logger.fine("Incoming text message: $incomingFrame")
val text = (incomingFrame as Frame.Text).readText()
logger.fine("Message text: $text")
val msg = Json.decodeFromString(WebSocketMessageSerializer(), text)
logger.fine("Message type: ${msg::class.java.canonicalName}")
logger.fine("Deserialized: $msg")
handleIncomingWSMessage(msg)
} catch (e: Throwable) {
logger.severe(e.localizedMessage)
logger.severe(e.stackTraceToString())
}
}
}
}
} catch (e: ClosedReceiveChannelException) {
logger.warning("The WebSocket channel was closed: $e")
} catch (e: Throwable) {
logger.severe(e.localizedMessage)
logger.severe(e.stackTraceToString())
throw e
}
}

private fun getCurrentGame(): OnlineMultiplayerGame? {
Expand Down Expand Up @@ -409,7 +483,7 @@ class OnlineMultiplayer {
json().fromJson(ServerFeatureSet::class.java, result)
} catch (ex: Exception) {
Log.error("${UncivGame.Current.settings.multiplayer.server} does not support server feature set")
ServerFeatureSet()
ServerFeatureSet(apiVersion = 0)
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions core/src/com/unciv/logic/multiplayer/ServerFeatureSet.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@ package com.unciv.logic.multiplayer
*
* Everything is optional, so if a feature is not present, it is assumed to be 0.
* Dropbox does not support anything of this, so it will always be 0.
*
* The API developed to replace dropbox which uses preview files and
* polling via HTTP is referred to as API v1, [apiVersion] = 1.
* It may or may not support auth. The new WebSocket-based and extended
* API is referred to as API v2, [apiVersion] = 2. It's not directly a
* feature set, but rather another refined interface.
*/
data class ServerFeatureSet(
val authVersion: Int = 0,
val apiVersion: Int
)

0 comments on commit 9888bed

Please sign in to comment.