WIP ws messages, TODO backend receiving frontend messages

This commit is contained in:
2025-02-19 09:28:13 -07:00
parent b37862a321
commit d2e378b4a3
12 changed files with 104 additions and 153 deletions
+3 -51
View File
@@ -4,55 +4,6 @@ import 'dart:isolate';
import 'package:logging/logging.dart';
import 'package:shared_models/room.dart';
// class GameRoomManager {
// GameRoomManager({required SendPort this.socketManagerSendPort}) {
// managerReceivePort = ReceivePort();
// gamePorts = {};
// receiveSubscription = managerReceivePort.listen((message) {
// if (message is GameRoomManagerMessage) {
// handleMessage(message);
// } else if (message is GameRoomMessage) {
// final gameUuid = message.gameUuid;
// final gamePort = gamePorts[gameUuid];
// if (gamePort == null) {
// _logger.warning('Received GameRoomMessage for empty gamePort');
// return;
// }
// gamePort.send(message);
// } else {
// _logger.warning('Received unknown message: $message');
// }
// });
// }
// late final Map<String, SendPort> gamePorts;
// late final ReceivePort managerReceivePort;
// late final StreamSubscription<dynamic> receiveSubscription;
// final SendPort socketManagerSendPort;
// final Logger _logger = Logger('GameRoomManager');
// void close() {
// receiveSubscription.cancel();
// //TODO remove connections
// }
// Future<void> createRoom({required String roomUuid}) async {
// // receivePort
// final wsSendPort = SocketManager().createWsSendPort(roomUuid);
// await Isolate.spawn(LiveGameRoom.spawn, LiveGameRoomData(roomUuid: roomUuid, wsSendPort: wsSendPort, gameManagerSendPort: ));
// // first message from new isolate will be its SendPort
// gamePorts[roomUuid] = await roomReceivePort.first as SendPort;
// }
// void routePlayerToRoom(String roomCode, PlayerConnection player) {
// gamePorts[roomCode]?.addPlayer(player);
// }
// void handleMessage(message) {
// throw UnimplementedError();
// }
// }
class LiveGameRoomData {
LiveGameRoomData({
required this.wsSendPort,
@@ -102,15 +53,16 @@ class LiveGameRoom {
}
void start() {
gameLoop = Timer.periodic(const Duration(milliseconds: 750), update);
gameLoop = Timer.periodic(const Duration(milliseconds: 3500), update);
}
void update(Timer timer) {
logger.finest('Room $roomUuid tick: ${timer.tick}');
wsSendPort.send(
RoomPingMessage(
PingMessage.now(
roomUuid,
dest: PingDestination.client,
userUuid: '',
),
);
}
+43 -32
View File
@@ -25,7 +25,8 @@ class SocketManager {
final _connections = <String, Map<String, WebSocketChannel>>{};
// Store isolate port and stream subscription to said port
final _gameRoomSendPorts = <String, SendPort>{};
final _gameRoomSpSubs = <String, StreamSubscription<dynamic>>{};
final _gameRoomPortSubs = <String, StreamSubscription<dynamic>>{};
final _gameRoomUserWsSubs = <String, StreamSubscription<dynamic>>{};
// Add a new connection
Future<Status> addConnection(
@@ -34,7 +35,7 @@ class SocketManager {
required String userUuid,
}) async {
_logger.finer('Adding connection to socket manager for user $userUuid in room $roomUuid');
if (!_gameRoomSpSubs.containsKey(roomUuid)) {
if (!_gameRoomPortSubs.containsKey(roomUuid)) {
final status = await spawnGameRoomIsolate(roomUuid);
if (status == Status.failure) {
return status;
@@ -42,6 +43,11 @@ class SocketManager {
}
_connections.putIfAbsent(roomUuid, () => <String, WebSocketChannel>{});
_connections[roomUuid]![userUuid] = connection;
_logger.fine('Listening to websocket for messages');
// ignore: cancel_subscriptions
final sub = connection.stream.listen(_gameRoomMessageListener);
_gameRoomUserWsSubs[userUuid] = sub;
return Status.success;
}
@@ -67,6 +73,9 @@ class SocketManager {
void broadcastToRoom(String roomUuid, GameRoomMessage message, {String? excludeUserUuid}) {
_logger.fine('Broadcasting ${message.type} to room $roomUuid');
_connections[roomUuid]?.forEach((userUuid, connection) {
if (message is PingMessage) {
message.userUuid = userUuid;
}
if (userUuid != excludeUserUuid) {
connection.sink.add(jsonEncode(message.toJson()));
}
@@ -92,47 +101,49 @@ class SocketManager {
final completer = Completer<SendPort>();
// ignore: cancel_subscriptions
final sub = receivePort.listen((message) {
// first message from new isolate will be its SendPort
if (!completer.isCompleted) {
completer.complete(message as SendPort);
return;
}
if (message is GameRoomMessage) {
switch (message) {
case RoomPingMessage():
if (message.dest != PingDestination.client) {
_logger.warning('Got room ping meant for server');
return;
}
broadcastToRoom(
message.roomUuid,
message,
);
final sub = receivePort.listen((message) => _gameRoomPortListener(message, completer));
case PlayerVoteMessage():
// TODO: Handle this case.
throw UnimplementedError();
case PingMessage():
// TODO: Handle this case.
throw UnimplementedError();
}
} else {
_logger.info('Unknown message: $message');
}
});
_gameRoomSpSubs[roomUuid] = sub;
_gameRoomPortSubs[roomUuid] = sub;
_gameRoomSendPorts[roomUuid] = await completer.future;
_logger.info('Spawned new game room $roomUuid, listening to messages from room.');
return Status.success;
} catch (e) {
_logger.severe('Failed to spawn game room isolate', e, StackTrace.current);
_gameRoomSendPorts.remove(roomUuid);
final sub = _gameRoomSpSubs.remove(roomUuid);
final sub = _gameRoomPortSubs.remove(roomUuid);
await sub?.cancel();
}
return Status.failure;
}
void _gameRoomPortListener(dynamic message, Completer<SendPort> completer) {
// first message from new isolate will be its SendPort
if (!completer.isCompleted) {
completer.complete(message as SendPort);
return;
}
if (message is GameRoomMessage) {
switch (message) {
case PlayerVoteMessage():
// TODO: Handle this case.
throw UnimplementedError();
case PingMessage():
if (message.dest != PingDestination.client) {
_logger.warning('Got room ping meant for server');
return;
}
broadcastToRoom(
message.roomUuid,
message,
);
}
} else {
_logger.info('Unknown message: $message');
}
}
void _gameRoomMessageListener(dynamic message) {}
}
enum Status { success, failure }
+4 -3
View File
@@ -16,17 +16,18 @@ Future<Response> onRequest(RequestContext context) async {
try {
// Parse the request body
final body = await context.request.json();
final createUserReq = CreateUserRequest.fromJson(body as Map<String, dynamic>);
final joinRoomRequest = JoinRoomRequest.fromJson(body as Map<String, dynamic>);
// Generate token
final authenticator = context.read<Authenticator>();
final (token, user) = await authenticator.generateToken(createUserReq);
final (token, user) = await authenticator.generateToken(joinRoomRequest);
if (token == null || user == null) {
final body = CreateUserResponse(
success: false,
token: null,
error: user == null ? 'Room ${createUserReq.roomCode} requested is not available' : 'Unexpected error occurred',
error:
user == null ? 'Room ${joinRoomRequest.roomCode} requested is not available' : 'Unexpected error occurred',
uuid: null,
).toJson();
return Response.json(
-1
View File
@@ -10,7 +10,6 @@ Future<Response> onRequest(RequestContext context, String roomCode) async {
final handler = webSocketHandler(protocols: ['game.room.v1'], (channel, protocol) async {
try {
channel.sink.add('test');
logger.finest(protocol);
final room = await Db.getRoomByCode(roomCode);
if (room == null) {