Line data Source code
1 : import 'dart:convert';
2 :
3 : import 'package:fast_immutable_collections/fast_immutable_collections.dart';
4 : import 'package:web_socket_channel/web_socket_channel.dart';
5 : import 'package:ws_game_server_types/ws_game_server_types.dart';
6 :
7 : /// All of the user connections are kept by the [ClientConnectionsService] object,
8 : /// which keeps a map of [WebSocketChannel]s to userIds.
9 : ///
10 : /// When a user connection is added or removed the [OtherPlayerIds] is broadcast.
11 : class ClientConnectionsService {
12 : // We can constructor inject the message handler function used by shelf_web_socket
13 0 : ClientConnectionsService([Function(WebSocketChannel)? messageHandler]) {
14 0 : _messageHandler = messageHandler ?? defaultMessageHandler;
15 : }
16 :
17 : final presenceMap = <WebSocketChannel, String>{};
18 :
19 : // We keep the handler function as a member so that different handlers can
20 : // be constructor injected.
21 : late final Function(WebSocketChannel) _messageHandler;
22 :
23 : // The default function that [_messageHandler] is set to.
24 0 : void defaultMessageHandler(WebSocketChannel webSocket) {
25 : // Now attach a listener to the websocket that will perform the ongoing logic
26 0 : webSocket.stream.listen(
27 0 : (jsonString) {
28 0 : final jsonData = jsonDecode(jsonString);
29 : // If a user is announcing their presence, store the webSocket against the
30 : // userId and broadcast the current connections
31 0 : if (jsonData['type'] == PresentMessage.jsonType) {
32 0 : print(
33 0 : 'server received: $jsonString \nAdding user & broadcasting other player list');
34 0 : _addAndBroadcast(webSocket, jsonData['userId'] as String);
35 0 : } else if (jsonData['type'] == OtherPlayerIdsMessage.jsonType) {
36 0 : print('server received: $jsonString, broadcasting other player ids');
37 0 : _broadcastOtherPlayerIds();
38 0 : } else if (jsonData['type'] == PlayerPathMessage.jsonType) {
39 0 : print('server received: $jsonString, broadcasting');
40 0 : _broadcastPlayerPath(jsonData);
41 : } else {
42 0 : throw Exception('Unknown json type in websocket stream: $jsonData');
43 : }
44 : },
45 0 : onError: (error) {
46 0 : print(error);
47 0 : webSocket.sink.add('$error');
48 : },
49 0 : onDone: () {
50 0 : _removeAndBroadcast(webSocket);
51 : },
52 : );
53 : }
54 :
55 0 : Function(WebSocketChannel) get messageHandler => _messageHandler;
56 :
57 0 : void _addAndBroadcast(WebSocketChannel ws, String userId) {
58 0 : presenceMap[ws] = userId;
59 0 : _broadcastOtherPlayerIds();
60 : }
61 :
62 0 : void _removeAndBroadcast(WebSocketChannel ws) {
63 0 : presenceMap.remove(ws);
64 0 : _broadcastOtherPlayerIds();
65 : }
66 :
67 0 : void _broadcastOtherPlayerIds() {
68 0 : for (final ws in presenceMap.keys) {
69 : // make the "other players" list for this player and send
70 0 : var otherIdsList = presenceMap.values.toISet().remove(presenceMap[ws]!);
71 : final message =
72 0 : jsonEncode(OtherPlayerIdsMessage(ids: otherIdsList).toJson());
73 0 : ws.sink.add(message);
74 : }
75 : }
76 :
77 0 : void _broadcastPlayerPath(JsonMap jsonData) {
78 0 : var message = PlayerPathMessage.fromJson(jsonData);
79 0 : for (final ws in presenceMap.keys) {
80 0 : if (presenceMap[ws]! != message.userId) {
81 0 : ws.sink.add(jsonEncode(message.toJson()));
82 : }
83 : }
84 : }
85 :
86 0 : void _broadcast(String message) {
87 0 : for (final ws in presenceMap.keys) {
88 0 : ws.sink.add(message);
89 : }
90 : }
91 : }
|