import 'dart:convert'; import 'dart:developer'; import 'package:mchess/api/move.dart'; import 'package:mchess/api/websocket_message.dart'; import 'package:mchess/chess_bloc/chess_bloc.dart'; import 'package:mchess/chess_bloc/chess_events.dart'; import 'package:mchess/api/register.dart'; import 'package:mchess/chess_bloc/chess_position.dart'; import 'package:mchess/connection_cubit/connection_cubit.dart'; import 'package:mchess/utils/chess_utils.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; class ServerConnection { WebSocketChannel? channel; late bool wasConnected = false; Stream broadcast = const Stream.empty(); static final ServerConnection _instance = ServerConnection._internal(); ServerConnection._internal() { log("ServerConnection._internal constructor is called"); } factory ServerConnection() { return _instance; } factory ServerConnection.getInstance() { return ServerConnection(); } void send(String message) { if (channel == null) { log("Sending on channel without initializing"); return; } channel!.sink.add(message); } void connect(String playerID, lobbyID, String? passphrase) { String url; url = 'wss://chess.sw-gross.de:9999/api/ws'; channel = WebSocketChannel.connect(Uri.parse(url)); send( jsonEncode( WebsocketMessageIdentifyPlayer( playerID: (playerID), lobbyID: (lobbyID), passphrase: (passphrase), ), ), ); log(channel!.closeCode.toString()); broadcast = channel!.stream.asBroadcastStream(); broadcast.listen(handleIncomingData); } void disconnectExistingConnection() { if (channel == null) return; channel!.sink.close(); } void handleIncomingData(dynamic data) { log('${DateTime.now()}: Data received:'); log(data); var apiMessage = ApiWebsocketMessage.fromJson(jsonDecode(data)); switch (apiMessage.type) { case MessageType.boardState: handleBoardStateMessage(apiMessage); break; case MessageType.colorDetermined: handleIncomingColorDeterminedMessage(apiMessage); break; case MessageType.move: log('ERROR: move message received'); break; case MessageType.invalidMove: handleInvalidMoveMessage(apiMessage); } } void handleBoardStateMessage(ApiWebsocketMessage apiMessage) { ChessMove? move; if (apiMessage.move != null) { move = ChessMove.fromApiMove(apiMessage.move!); } if (apiMessage.position != null) { ChessBloc.getInstance().add( ReceivedBoardState( startSquare: move?.from, endSquare: move?.to, position: ChessPositionManager.getInstance() .fromPGNString(apiMessage.position!), squareInCheck: ChessCoordinate.fromApiCoordinate( apiMessage.squareInCheck ?? const ApiCoordinate(col: 0, row: 0)), turnColor: ChessColor.fromApiColor(apiMessage.turnColor!)), ); } else { log('Error: no position received'); } } void handleIncomingColorDeterminedMessage(ApiWebsocketMessage apiMessage) { ConnectionCubit.getInstance().opponentConnected(); ChessBloc.getInstance().add(InitBoard()); ChessBloc.getInstance().add(ColorDetermined( myColor: ChessColor.fromApiColor(apiMessage.playerColor!))); } void handleInvalidMoveMessage(ApiWebsocketMessage apiMessage) { log("invalid move message received, with move: ${apiMessage.move.toString()}"); ChessBloc.getInstance().add( InvalidMovePlayed( move: ChessMove.fromApiMove(apiMessage.move!), squareInCheck: ChessCoordinate.fromApiCoordinate( apiMessage.squareInCheck ?? const ApiCoordinate(col: 0, row: 0)), ), ); } }