Marco
13bcfb1131
Show checkmate screen, once the server sends the 'gameEnded' message. Additionally, show an icon that lets users copy the passphrase to the clipboard.
158 lines
4.7 KiB
Dart
158 lines
4.7 KiB
Dart
import 'dart:convert';
|
|
import 'dart:developer';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
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_router.dart';
|
|
import 'package:mchess/utils/chess_utils.dart';
|
|
import 'package:mchess/utils/config.dart' as config;
|
|
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) {
|
|
channel = WebSocketChannel.connect(Uri.parse(config.getWebsocketURL()));
|
|
|
|
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);
|
|
case MessageType.gameEnded:
|
|
handleGameEndedMessage(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)),
|
|
),
|
|
);
|
|
}
|
|
|
|
void handleGameEndedMessage(ApiWebsocketMessage apiMessage) {
|
|
showDialog(
|
|
context: navigatorKey.currentContext!,
|
|
builder: (context) {
|
|
String msg = '';
|
|
if (apiMessage.reason == 'whiteIsCheckmated') {
|
|
msg = 'Black won! White is checkmated';
|
|
} else if (apiMessage.reason == 'blackIsCheckmated') {
|
|
msg = 'White won! Black is checkmated';
|
|
}
|
|
return AlertDialog(
|
|
title: const Text('Game ended'),
|
|
content: Text(msg),
|
|
actions: <Widget>[
|
|
TextButton(
|
|
child: const Text('Home'),
|
|
onPressed: () {
|
|
navigatorKey.currentContext!.goNamed('lobbySelector');
|
|
},
|
|
),
|
|
]);
|
|
},
|
|
);
|
|
}
|
|
}
|