gameJoinRoom
Join the hosted game in the room named gameId over rooms — the game-per-room analogue of gameJoin.
rooms is this player's session-mux Loom (a us.tractat.kuilt.core.MuxClientLoom over one base connection). Joining several games through the same loom multiplexes them all over that single connection — the channel name is the (gameId) envelope the server routes on. The room channel is wrapped in the star-relay layer (starOverlay) so this player speaks the same relay envelope as the hosting side, then gameJoin runs on it unchanged.
Parameters are forwarded to gameJoin unchanged; see gameJoin for their semantics.
Parameters
Must seat AuthoritySeating.SessionPeers (see gameJoin). To ride a server-core game as a learner use gameNodeRoom with ConsensusPlacement.serverCore.
Samples
runTest(StandardTestDispatcher(), timeout = TEST_WEDGE_BACKSTOP) {
val dispatcher = requireNotNull(coroutineContext[kotlin.coroutines.ContinuationInterceptor])
val fabric = InMemoryRoomFabric(backgroundScope, dispatcher, random = Random(0))
val clock = { Instant.fromEpochMilliseconds(0) }
fun config(seed: Long) = RaftConfig(expectVirtualTime = true, random = Random(seed))
// Room "casual": appoint-the-host — the server hosts, alice takes a voter seat.
val casualHost = async {
backgroundScope.gameHostedRoom(
fabric.serverLoom, "casual", peerCount = 2,
raftConfig = config(1), random = Random(11), clock = clock,
)
}
val casualAlice = async {
backgroundScope.gameJoinRoom(
fabric.clientLoom(PeerId("alice"), Random(21)), "casual",
raftConfig = config(2), random = Random(12), clock = clock,
)
}
// Room "ranked": server-core — every voter seat is the server's; bob rides as a learner.
val core = setOf(NodeId("server"))
val placement = ConsensusPlacement.serverCore(core)
backgroundScope.gameNodeRoom(
fabric.serverLoom, "ranked", voterIds = core,
raftConfig = config(3), random = Random(13), clock = clock, placement = placement,
)
val rankedBob = backgroundScope.gameNodeRoom(
fabric.clientLoom(PeerId("bob"), Random(22)), "ranked", voterIds = core,
raftConfig = config(4), random = Random(14), clock = clock, placement = placement,
)
// Both games converge independently — same TurnSequencer code, different authority.
val casualMove = TurnSequencer(casualAlice.await().node, Int.serializer()).propose(1)
val rankedMove = TurnSequencer(rankedBob.node, Int.serializer()).propose(2)
assertEquals(1, casualMove.action)
assertEquals(2, rankedMove.action)
casualHost.await().close()
}