Skip to content

Commit 5ed425b

Browse files
committed
feat(network): add JoinRoomRequest to join room by id
This is required to join a specific prepared game without reservations.
1 parent 0c3fe0d commit 5ed425b

File tree

11 files changed

+153
-63
lines changed

11 files changed

+153
-63
lines changed

plugin/src/client/sc/plugin2021/AbstractClient.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@ import org.slf4j.LoggerFactory
44
import sc.api.plugins.IGameState
55
import sc.framework.plugins.protocol.MoveRequest
66
import sc.networking.clients.AbstractLobbyClientListener
7-
import sc.networking.clients.IControllableGame
87
import sc.networking.clients.ILobbyClientListener
98
import sc.networking.clients.LobbyClient
109
import sc.protocol.room.RoomMessage
11-
import sc.protocol.responses.GamePreparedResponse
1210
import sc.protocol.room.ErrorMessage
1311
import sc.shared.GameResult
1412
import sc.shared.WelcomeMessage
@@ -96,7 +94,7 @@ abstract class AbstractClient(
9694
/** [start] and join any game with the appropriate [gameType]. */
9795
fun joinAnyGame() {
9896
start()
99-
client.joinRoomRequest(GamePlugin.PLUGIN_ID)
97+
client.joinGame(GamePlugin.PLUGIN_ID)
10098
}
10199

102100
override fun onGameLeft(roomId: String) {
@@ -112,6 +110,6 @@ abstract class AbstractClient(
112110

113111
fun joinPreparedGame(reservation: String) {
114112
start()
115-
client.joinPreparedGame(reservation)
113+
client.joinGameWithReservation(reservation)
116114
}
117115
}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,16 @@ public void sendMessageToRoom(String roomId, RoomMessage o) {
162162
send(new RoomPacket(roomId, o));
163163
}
164164

165-
public void joinPreparedGame(String reservation) {
165+
public void joinGameWithReservation(String reservation) {
166166
send(new JoinPreparedRoomRequest(reservation));
167167
}
168168

169-
public void joinRoomRequest(String gameType) {
170-
send(new JoinRoomRequest(gameType));
169+
public void joinGameRoom(String roomId) {
170+
send(new JoinRoomRequest(roomId));
171+
}
172+
173+
public void joinGame(String gameType) {
174+
send(new JoinGameRequest(gameType));
171175
}
172176

173177
public void addListener(ILobbyClientListener listener) {

sdk/src/server-api/sc/protocol/LobbyProtocol.kt

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,67 @@ import sc.protocol.room.RoomPacket
1111
import sc.shared.*
1212

1313
object LobbyProtocol {
14-
14+
1515
@JvmStatic
1616
fun registerMessages(xStream: XStream): XStream {
17-
registerAdditionalMessages(xStream, listOf(ErrorPacket::class.java, ErrorMessage::class.java, GamePaused::class.java, JoinedRoomResponse::class.java, RemovedFromGame::class.java, MementoMessage::class.java, GamePreparedResponse::class.java, ObservationResponse::class.java, RoomPacket::class.java, WelcomeMessage::class.java))
18-
19-
registerAdditionalMessages(xStream, listOf(AuthenticateRequest::class.java, CancelRequest::class.java, JoinPreparedRoomRequest::class.java, JoinRoomRequest::class.java, ObservationRequest::class.java, PauseGameRequest::class.java, PrepareGameRequest::class.java, StepRequest::class.java, PlayerScoreRequest::class.java, TestModeRequest::class.java, PlayerScoreResponse::class.java, TestModeResponse::class.java, RoomWasJoinedEvent::class.java))
20-
21-
registerAdditionalMessages(xStream, listOf(GameResult::class.java, PlayerScore::class.java, ScoreAggregation::class.java, ITeam::class.java, ScoreCause::class.java, ScoreDefinition::class.java, ScoreFragment::class.java, WinCondition::class.java, SlotDescriptor::class.java, Score::class.java, ScoreValue::class.java))
22-
17+
// Messages
18+
registerAdditionalMessages(xStream, listOf(
19+
ErrorPacket::class.java,
20+
ErrorMessage::class.java,
21+
GamePaused::class.java,
22+
JoinedRoomResponse::class.java,
23+
RemovedFromGame::class.java,
24+
MementoMessage::class.java,
25+
GamePreparedResponse::class.java,
26+
ObservationResponse::class.java,
27+
RoomPacket::class.java,
28+
WelcomeMessage::class.java,
29+
PlayerScoreResponse::class.java,
30+
TestModeResponse::class.java,
31+
RoomWasJoinedEvent::class.java,
32+
))
33+
34+
// Requests
35+
registerAdditionalMessages(xStream, listOf(
36+
AuthenticateRequest::class.java,
37+
CancelRequest::class.java,
38+
JoinPreparedRoomRequest::class.java,
39+
JoinGameRequest::class.java,
40+
JoinRoomRequest::class.java,
41+
ObservationRequest::class.java,
42+
PauseGameRequest::class.java,
43+
PrepareGameRequest::class.java,
44+
StepRequest::class.java,
45+
PlayerScoreRequest::class.java,
46+
TestModeRequest::class.java,
47+
))
48+
49+
// Data
50+
registerAdditionalMessages(xStream, listOf(
51+
GameResult::class.java,
52+
PlayerScore::class.java,
53+
ScoreAggregation::class.java,
54+
ITeam::class.java,
55+
ScoreCause::class.java,
56+
ScoreDefinition::class.java,
57+
ScoreFragment::class.java,
58+
WinCondition::class.java,
59+
SlotDescriptor::class.java,
60+
Score::class.java,
61+
ScoreValue::class.java,
62+
))
63+
2364
return xStream
2465
}
25-
66+
2667
@JvmStatic
2768
fun registerAdditionalMessages(xStream: XStream, protocolClasses: Collection<Class<*>>?): XStream {
28-
if(protocolClasses != null) {
29-
for(clazz in protocolClasses) {
69+
if (protocolClasses != null) {
70+
for (clazz in protocolClasses) {
3071
xStream.processAnnotations(clazz)
3172
}
3273
}
3374
return xStream
3475
}
35-
76+
3677
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package sc.protocol.requests
2+
3+
import com.thoughtworks.xstream.annotations.XStreamAlias
4+
import com.thoughtworks.xstream.annotations.XStreamAsAttribute
5+
6+
/**
7+
* Join a game by [gameType].
8+
* Creates a new gameRoom if no open gameRoom of the specified gameType exists.
9+
*/
10+
@XStreamAlias("join")
11+
data class JoinGameRequest(
12+
@XStreamAsAttribute
13+
val gameType: String
14+
): ILobbyRequest

sdk/src/server-api/sc/protocol/requests/JoinRoomRequest.kt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@ package sc.protocol.requests
33
import com.thoughtworks.xstream.annotations.XStreamAlias
44
import com.thoughtworks.xstream.annotations.XStreamAsAttribute
55

6-
/**
7-
* Send by client to join game by gameType.
8-
* Creates a new gameRoom if no open gameRoom of the specified gameType exists.
9-
*/
10-
@XStreamAlias("join")
6+
/** Join a prepared GameRoom without reservation. */
7+
@XStreamAlias("joinRoom")
118
data class JoinRoomRequest(
129
@XStreamAsAttribute
13-
val gameType: String
10+
val roomId: String
1411
): ILobbyRequest

server/src/sc/server/Lobby.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package sc.server
22

33
import org.slf4j.LoggerFactory
4+
import sc.api.plugins.exceptions.GameRoomException
45
import sc.api.plugins.exceptions.RescuableClientException
56
import sc.protocol.requests.*
67
import sc.protocol.responses.PlayerScoreResponse
@@ -9,7 +10,6 @@ import sc.protocol.room.RoomPacket
910
import sc.server.gaming.GameRoomManager
1011
import sc.server.gaming.ReservationManager
1112
import sc.server.network.*
12-
import sc.shared.InvalidGameStateException
1313
import sc.shared.Score
1414
import java.io.Closeable
1515
import java.io.IOException
@@ -26,8 +26,10 @@ class Lobby: GameRoomManager(), Closeable, IClientRequestListener {
2626
clientManager.start()
2727
}
2828

29-
/** Handle requests or moves of clients. */
30-
@Throws(RescuableClientException::class, InvalidGameStateException::class)
29+
/** Handle requests or moves of clients.
30+
* @throws RescuableClientException if something goes wrong.
31+
* Usually results in termination of the connection to the offending client. */
32+
@Throws(RescuableClientException::class)
3133
override fun onRequest(source: Client, callback: PacketCallback) {
3234
when (val packet = callback.packet) {
3335
is RoomPacket -> {
@@ -37,7 +39,10 @@ class Lobby: GameRoomManager(), Closeable, IClientRequestListener {
3739
}
3840
is JoinPreparedRoomRequest ->
3941
ReservationManager.redeemReservationCode(source, packet.reservationCode)
40-
is JoinRoomRequest -> {
42+
is JoinRoomRequest ->
43+
if(!this.findRoom(packet.roomId).join(source))
44+
throw GameRoomException("Room ${packet.roomId} is already full!")
45+
is JoinGameRequest -> {
4146
val gameRoomMessage = this.joinOrCreateGame(source, packet.gameType)
4247
// null is returned if join was unsuccessful
4348
if (gameRoomMessage != null) {

server/test/sc/server/network/ConnectionTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import org.junit.jupiter.api.Disabled;
44
import org.junit.jupiter.api.Test;
55
import sc.networking.clients.XStreamClient.DisconnectCause;
6-
import sc.protocol.requests.JoinRoomRequest;
6+
import sc.protocol.requests.JoinGameRequest;
77
import sc.protocol.ProtocolPacket;
88
import sc.server.helpers.TestHelper;
99
import sc.server.plugins.TestPlugin;
@@ -22,7 +22,7 @@ public void connectionTest() {
2222
TestTcpClient client = connectClient();
2323
waitForConnect(1);
2424

25-
client.send(new JoinRoomRequest(TestPlugin.TEST_PLUGIN_UUID));
25+
client.send(new JoinGameRequest(TestPlugin.TEST_PLUGIN_UUID));
2626
TestHelper.INSTANCE.assertEqualsWithTimeout(1, () -> getLobby().getGames().size(), 1, TimeUnit.SECONDS);
2727
}
2828

server/test/sc/server/network/LobbyRequestTest.kt

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import sc.protocol.requests.JoinPreparedRoomRequest
1616
import sc.protocol.requests.PrepareGameRequest
1717
import sc.protocol.responses.ErrorPacket
1818
import sc.protocol.responses.GamePreparedResponse
19-
import sc.protocol.responses.ObservationResponse
2019
import sc.protocol.room.ErrorMessage
2120
import sc.server.client.MessageListener
2221
import sc.server.client.PlayerListener
@@ -25,6 +24,7 @@ import sc.server.helpers.TestTeam
2524
import sc.server.plugins.TestGame
2625
import sc.server.plugins.TestMove
2726
import sc.server.plugins.TestPlugin
27+
import sc.shared.SlotDescriptor
2828
import kotlin.time.Duration
2929
import kotlin.time.ExperimentalTime
3030
import kotlin.time.milliseconds
@@ -43,21 +43,24 @@ class LobbyRequestTest: WordSpec({
4343
val adminListener = MessageListener<ResponsePacket>()
4444
val lobbyClient = LobbyClient("localhost", testLobby.serverPort)
4545
val admin = lobbyClient.authenticate(PASSWORD, adminListener::addMessage)
46+
fun prepareGame(request: PrepareGameRequest): GamePreparedResponse {
47+
admin.prepareGame(request)
48+
val prepared = adminListener.waitForMessage(GamePreparedResponse::class)
49+
lobby.games shouldHaveSize 1
50+
return prepared
51+
}
4652

4753
val players = Array(2) { testLobby.connectClient() }
4854
await("Clients connected") { lobby.clientManager.clients.size shouldBe 3 }
4955
"a player joined" should {
50-
players[0].joinRoomRequest(TestPlugin.TEST_PLUGIN_UUID)
56+
players[0].joinGame(TestPlugin.TEST_PLUGIN_UUID)
5157
"create a room for it" {
5258
await("Room opened") { lobby.games.size shouldBe 1 }
5359
lobby.games.single().clients shouldHaveSize 1
5460
}
5561
}
5662
"a game is prepared paused" should {
57-
admin.prepareGame(PrepareGameRequest(TestPlugin.TEST_PLUGIN_UUID, pause = true))
58-
val prepared = adminListener.waitForMessage(GamePreparedResponse::class)
59-
lobby.games shouldHaveSize 1
60-
63+
val prepared = prepareGame(PrepareGameRequest(TestPlugin.TEST_PLUGIN_UUID, pause = true))
6164
val roomId = prepared.roomId
6265
val room = lobby.findRoom(roomId)
6366
withClue("GameRoom is empty and paused") {
@@ -66,15 +69,15 @@ class LobbyRequestTest: WordSpec({
6669
}
6770

6871
val reservations = prepared.reservations
69-
players[0].joinPreparedGame(reservations[0])
72+
players[0].joinGameWithReservation(reservations[0])
7073
await("First player joined") { room.clients shouldHaveSize 1 }
7174
"not accept a reservation twice" {
7275
lobbyClient.send(JoinPreparedRoomRequest(reservations[0]))
7376
adminListener.waitForMessage(ErrorPacket::class)
7477
room.clients shouldHaveSize 1
7578
lobby.games shouldHaveSize 1
7679
}
77-
players[1].joinPreparedGame(reservations[1])
80+
players[1].joinGameWithReservation(reservations[1])
7881
await("Players join, Game start") { room.status shouldBe GameRoom.GameStatus.ACTIVE }
7982

8083
val playerListeners = room.slots.map { slot ->
@@ -110,5 +113,36 @@ class LobbyRequestTest: WordSpec({
110113
}
111114
}
112115
}
116+
"a game is prepared with descriptors" should {
117+
val descriptors = arrayOf(
118+
SlotDescriptor("supreme", reserved = true),
119+
SlotDescriptor("human", canTimeout = false, reserved = false),
120+
)
121+
val prepared = prepareGame(PrepareGameRequest(TestPlugin.TEST_PLUGIN_UUID, descriptors, pause = false))
122+
val room = lobby.findRoom(prepared.roomId)
123+
"return a single reservation" {
124+
prepared.reservations shouldHaveSize 1
125+
}
126+
"create appropriate slots" {
127+
room.slots shouldHaveSize 2
128+
room.slots.forEachIndexed { index, slot ->
129+
val descriptor = descriptors[index]
130+
slot.player.displayName shouldBe descriptor.displayName
131+
slot.player.canTimeout shouldBe descriptor.canTimeout
132+
slot.isReserved shouldBe descriptor.reserved
133+
}
134+
}
135+
players[0].joinGameRoom(prepared.roomId)
136+
"join player into nonreserved slot" {
137+
await { room.clients shouldHaveSize 1 }
138+
room.slots[0].isEmpty shouldBe true
139+
room.slots[0].isFree shouldBe false
140+
room.slots[1].isEmpty shouldBe false
141+
}
142+
players[1].joinGameWithReservation(prepared.reservations.single())
143+
await("start game when two players joined") {
144+
room.status shouldBe GameRoom.GameStatus.ACTIVE
145+
}
146+
}
113147
}
114148
})

server/test/sc/server/network/LobbyTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ class LobbyTest: RealServerTest() {
1717
val player1 = connectClient("localhost", serverPort)
1818
val player2 = connectClient("localhost", serverPort)
1919

20-
player1.joinRoomRequest(TestPlugin.TEST_PLUGIN_UUID)
21-
player2.joinRoomRequest(TestPlugin.TEST_PLUGIN_UUID)
20+
player1.joinGame(TestPlugin.TEST_PLUGIN_UUID)
21+
player2.joinGame(TestPlugin.TEST_PLUGIN_UUID)
2222

2323
await("Game created") { lobby.games.size == 1 }
2424
await("Game started") { lobby.games.single().status == GameRoom.GameStatus.ACTIVE }
@@ -35,9 +35,9 @@ class LobbyTest: RealServerTest() {
3535
val listener = TestLobbyClientListener()
3636
player1.addListener(listener)
3737

38-
player1.joinRoomRequest(TestPlugin.TEST_PLUGIN_UUID)
38+
player1.joinGame(TestPlugin.TEST_PLUGIN_UUID)
3939
await { listener.gameJoinedReceived }
40-
player2.joinRoomRequest(TestPlugin.TEST_PLUGIN_UUID)
40+
player2.joinGame(TestPlugin.TEST_PLUGIN_UUID)
4141

4242
await("Game started") { lobby.games.single().status == GameRoom.GameStatus.ACTIVE }
4343

0 commit comments

Comments
 (0)