Skip to content

Commit 72962c0

Browse files
committed
feat: implement replay loading via simple GameLoaderClient
1 parent ddc6a2b commit 72962c0

File tree

6 files changed

+116
-168
lines changed

6 files changed

+116
-168
lines changed

sdk/src/server-api/sc/api/plugins/host/GameLoader.java

Lines changed: 0 additions & 71 deletions
This file was deleted.

sdk/src/server-api/sc/networking/clients/GameLoaderClient.java

Lines changed: 0 additions & 47 deletions
This file was deleted.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package sc.networking.clients
2+
3+
import org.slf4j.LoggerFactory
4+
import sc.api.plugins.IGameState
5+
import sc.networking.FileSystemInterface
6+
import sc.protocol.ProtocolPacket
7+
import sc.protocol.room.MementoMessage
8+
import sc.protocol.room.RoomPacket
9+
import sc.shared.GameResult
10+
import java.io.File
11+
import java.io.IOException
12+
import java.io.InputStream
13+
14+
/**
15+
* This client serves the purpose to load a game state from
16+
* any XML file (a replay for example).
17+
* It is used to load a given board to play on.
18+
*/
19+
class GameLoaderClient(inputStream: InputStream): XStreamClient(FileSystemInterface(inputStream)) {
20+
constructor(file: File): this(file.inputStream())
21+
22+
private val history: MutableList<IGameState> = ArrayList(50)
23+
var result: GameResult? = null
24+
private set
25+
26+
override fun onObject(message: ProtocolPacket) {
27+
logger.trace("Adding packet to replay: {}", message)
28+
if (message !is RoomPacket)
29+
throw IOException("Can't extract replay from $message")
30+
when (val msg = message.data) {
31+
is MementoMessage -> history.add(msg.state)
32+
is GameResult -> result = msg
33+
else -> logger.debug("Unknown message in replay: {}", msg)
34+
}
35+
}
36+
37+
fun getHistory(): List<IGameState> {
38+
start()
39+
while (!isClosed)
40+
Thread.sleep(100)
41+
return history
42+
}
43+
44+
fun getTurn(turn: Int) =
45+
getHistory().first {
46+
it.turn >= turn
47+
}
48+
49+
companion object {
50+
private val logger = LoggerFactory.getLogger(this::class.java)
51+
}
52+
}

server/src/sc/server/gaming/GameRoomManager.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
import sc.api.plugins.IGameState;
88
import sc.api.plugins.exceptions.GameRoomException;
99
import sc.api.plugins.exceptions.RescuableClientException;
10-
import sc.api.plugins.host.GameLoader;
1110
import sc.networking.InvalidScoreDefinitionException;
11+
import sc.networking.clients.GameLoaderClient;
1212
import sc.protocol.requests.PrepareGameRequest;
1313
import sc.protocol.responses.GamePreparedResponse;
1414
import sc.protocol.responses.RoomWasJoinedEvent;
@@ -83,14 +83,8 @@ public synchronized GameRoom createGameRoom(String gameType) throws RescuableCli
8383
} catch(NumberFormatException ignored) {
8484
}
8585

86-
// TODO test this
8786
logger.info("Loading game from file '{}' at turn {}", gameFile, turn);
88-
try {
89-
game = plugin.createGameFromState(new GameLoader().loadGame(gameFile, turn));
90-
} catch(IOException e) {
91-
logger.error("Failed to load game from file", e);
92-
game = plugin.createGame();
93-
}
87+
game = plugin.createGameFromState(new GameLoaderClient(gameFile).getTurn(turn));
9488
} else {
9589
game = plugin.createGame();
9690
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package sc.server.gaming
2+
3+
import io.kotest.assertions.throwables.shouldThrow
4+
import io.kotest.core.spec.style.FunSpec
5+
import io.kotest.matchers.shouldBe
6+
import sc.networking.clients.GameLoaderClient
7+
import sc.server.plugins.TestGameState
8+
9+
class GameLoaderTest: FunSpec({
10+
test("GameLoaderClient loads replay") {
11+
val client = GameLoaderClient(minimalReplay.byteInputStream())
12+
val state = TestGameState()
13+
client.getHistory() shouldBe listOf(state)
14+
client.getTurn(0) shouldBe state
15+
client.getTurn(-1) shouldBe state
16+
shouldThrow<NoSuchElementException> {
17+
client.getTurn(1)
18+
}
19+
}
20+
})

server/test/sc/server/gaming/GameRoomTest.kt

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,47 @@ import sc.shared.ScoreCause
1616
import sc.shared.SlotDescriptor
1717
import java.io.StringWriter
1818

19+
val minimalReplay = """<protocol>
20+
<room roomId="some-id">
21+
<data class="memento">
22+
<state class="sc.server.plugins.TestGameState">
23+
<turn>0</turn>
24+
<state>0</state>
25+
<currentPlayer>RED</currentPlayer>
26+
<startPlayer>RED</startPlayer>
27+
<red displayName="">
28+
<color class="sc.server.helpers.TestTeam">RED</color>
29+
</red>
30+
<blue displayName="">
31+
<color class="sc.server.helpers.TestTeam">BLUE</color>
32+
</blue>
33+
</state>
34+
</data>
35+
</room>
36+
<room roomId="some-id">
37+
<data class="result">
38+
<definition>
39+
<fragment name="winner">
40+
<aggregation>SUM</aggregation>
41+
<relevantForRanking>true</relevantForRanking>
42+
</fragment>
43+
</definition>
44+
<score cause="REGULAR" reason="Game terminated">
45+
<part>0</part>
46+
</score>
47+
<score cause="REGULAR" reason="Game terminated">
48+
<part>0</part>
49+
</score>
50+
<winner displayName="">
51+
<color class="sc.server.helpers.TestTeam">RED</color>
52+
</winner>
53+
<winner displayName="">
54+
<color class="sc.server.helpers.TestTeam">BLUE</color>
55+
</winner>
56+
</data>
57+
</room>
58+
</protocol>"""
59+
1960
class GameRoomTest: WordSpec({
2061
isolationMode = IsolationMode.SingleInstance
2162
val client = Client(StringNetworkInterface("")).apply { start() }
@@ -40,48 +81,7 @@ class GameRoomTest: WordSpec({
4081
"save a correct replay" {
4182
val replayWriter = StringWriter()
4283
room.saveReplay(replayWriter)
43-
replayWriter.toString() shouldBe """
44-
<protocol>
45-
<room roomId="${room.id}">
46-
<data class="memento">
47-
<state class="sc.server.plugins.TestGameState">
48-
<turn>0</turn>
49-
<state>0</state>
50-
<currentPlayer>RED</currentPlayer>
51-
<startPlayer>RED</startPlayer>
52-
<red displayName="">
53-
<color class="sc.server.helpers.TestTeam">RED</color>
54-
</red>
55-
<blue displayName="">
56-
<color class="sc.server.helpers.TestTeam">BLUE</color>
57-
</blue>
58-
</state>
59-
</data>
60-
</room>
61-
<room roomId="${room.id}">
62-
<data class="result">
63-
<definition>
64-
<fragment name="winner">
65-
<aggregation>SUM</aggregation>
66-
<relevantForRanking>true</relevantForRanking>
67-
</fragment>
68-
</definition>
69-
<score cause="REGULAR" reason="Game terminated">
70-
<part>0</part>
71-
</score>
72-
<score cause="REGULAR" reason="Game terminated">
73-
<part>0</part>
74-
</score>
75-
<winner displayName="">
76-
<color class="sc.server.helpers.TestTeam">RED</color>
77-
</winner>
78-
<winner displayName="">
79-
<color class="sc.server.helpers.TestTeam">BLUE</color>
80-
</winner>
81-
</data>
82-
</room>
83-
</protocol>
84-
""".trimIndent()
84+
replayWriter.toString() shouldBe minimalReplay.replace("some-id", room.id)
8585
}
8686
}
8787
"A GameRoom with prepared reservations" should {

0 commit comments

Comments
 (0)