Merge pull request 'Handle board status message' (#2) from handle-status-reconnects into master
Reviewed-on: #2
This commit is contained in:
commit
4a9047fd67
@ -12,8 +12,8 @@ class PlayerInfo {
|
||||
});
|
||||
|
||||
factory PlayerInfo.fromJson(Map<String, dynamic> json) {
|
||||
final playerid = UuidValue(json['playerID']);
|
||||
final lobbyid = UuidValue(json['lobbyID']);
|
||||
final playerid = UuidValue.fromString(json['playerID']);
|
||||
final lobbyid = UuidValue.fromString(json['lobbyID']);
|
||||
final passphrase = json['passphrase'];
|
||||
|
||||
return PlayerInfo(
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:mchess/api/move.dart';
|
||||
|
||||
enum MessageType {
|
||||
boardState,
|
||||
move,
|
||||
invalidMove,
|
||||
colorDetermined;
|
||||
@ -20,7 +21,8 @@ enum ApiColor {
|
||||
class ApiWebsocketMessage {
|
||||
final MessageType type;
|
||||
final ApiMove? move;
|
||||
final ApiColor? color;
|
||||
final ApiColor? turnColor;
|
||||
final ApiColor? playerColor;
|
||||
final String? reason;
|
||||
final String? position;
|
||||
final ApiCoordinate? squareInCheck;
|
||||
@ -28,7 +30,8 @@ class ApiWebsocketMessage {
|
||||
ApiWebsocketMessage({
|
||||
required this.type,
|
||||
required this.move,
|
||||
required this.color,
|
||||
required this.turnColor,
|
||||
required this.playerColor,
|
||||
required this.reason,
|
||||
required this.position,
|
||||
required this.squareInCheck,
|
||||
@ -38,34 +41,46 @@ class ApiWebsocketMessage {
|
||||
final type = MessageType.fromJson(json['messageType']);
|
||||
ApiWebsocketMessage ret;
|
||||
switch (type) {
|
||||
case MessageType.boardState:
|
||||
ret = ApiWebsocketMessage(
|
||||
type: type,
|
||||
move: null,
|
||||
turnColor: ApiColor.fromJson(json['turnColor']),
|
||||
reason: null,
|
||||
position: json['position'],
|
||||
squareInCheck: null,
|
||||
playerColor: ApiColor.fromJson(json['playerColor']),
|
||||
);
|
||||
case MessageType.colorDetermined:
|
||||
ret = ApiWebsocketMessage(
|
||||
type: type,
|
||||
move: null,
|
||||
color: ApiColor.fromJson(json['color']),
|
||||
turnColor: null,
|
||||
reason: null,
|
||||
position: null,
|
||||
squareInCheck: null,
|
||||
playerColor: ApiColor.fromJson(json['playerColor']),
|
||||
);
|
||||
break;
|
||||
case MessageType.move:
|
||||
ret = ApiWebsocketMessage(
|
||||
type: type,
|
||||
move: ApiMove.fromJson(json['move']),
|
||||
color: null,
|
||||
reason: null,
|
||||
position: json['position'],
|
||||
squareInCheck: json['squareInCheck']
|
||||
);
|
||||
type: type,
|
||||
move: ApiMove.fromJson(json['move']),
|
||||
turnColor: null,
|
||||
reason: null,
|
||||
position: json['position'],
|
||||
squareInCheck: json['squareInCheck'],
|
||||
playerColor: null);
|
||||
break;
|
||||
case MessageType.invalidMove:
|
||||
ret = ApiWebsocketMessage(
|
||||
type: type,
|
||||
move: ApiMove.fromJson(json['move']),
|
||||
color: null,
|
||||
turnColor: null,
|
||||
reason: json['reason'],
|
||||
position: null,
|
||||
squareInCheck: json['squareInCheck'],
|
||||
playerColor: null,
|
||||
);
|
||||
}
|
||||
return ret;
|
||||
@ -74,6 +89,6 @@ class ApiWebsocketMessage {
|
||||
Map<String, dynamic> toJson() => {
|
||||
'messageType': type,
|
||||
'move': move,
|
||||
'color': color,
|
||||
'color': turnColor,
|
||||
};
|
||||
}
|
||||
|
@ -68,9 +68,10 @@ class _ChessSquareState extends State<ChessSquare> {
|
||||
return true;
|
||||
},
|
||||
listener: (context, state) {
|
||||
setState(() {
|
||||
squareColor = Colors.red;
|
||||
});},
|
||||
setState(() {
|
||||
squareColor = Colors.red;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
color: widget.color,
|
||||
child: ChessSquareOuterDragTarget(
|
||||
|
@ -20,10 +20,11 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||
ChessBloc._internal() : super(ChessBoardState.init()) {
|
||||
on<InitBoard>(initBoard);
|
||||
on<ColorDetermined>(flipBoard);
|
||||
on<ReceivedMove>(moveAndPositionHandler);
|
||||
on<ReceivedBoardState>(moveAndPositionHandler);
|
||||
on<OwnPieceMoved>(ownMoveHandler);
|
||||
on<OwnPromotionPlayed>(ownPromotionHandler);
|
||||
on<InvalidMovePlayed>(invalidMoveHandler);
|
||||
on<BoardStatusReceived>(boardStatusHandler);
|
||||
}
|
||||
|
||||
factory ChessBloc.getInstance() {
|
||||
@ -65,21 +66,24 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||
}
|
||||
|
||||
void moveAndPositionHandler(
|
||||
ReceivedMove event,
|
||||
Emitter<ChessBoardState> emit,
|
||||
) {
|
||||
ChessPositionManager.getInstance()
|
||||
.recordMove(event.startSquare, event.endSquare, event.position);
|
||||
ReceivedBoardState event, Emitter<ChessBoardState> emit) {
|
||||
turnColor = state.newTurnColor == ChessColor.white
|
||||
? ChessColor.black
|
||||
: ChessColor.white;
|
||||
|
||||
ChessMove? move;
|
||||
if (event.startSquare != null && event.endSquare != null) {
|
||||
move = ChessMove(from: event.startSquare!, to: event.endSquare!);
|
||||
ChessPositionManager.getInstance()
|
||||
.recordMove(event.startSquare, event.endSquare, event.position);
|
||||
}
|
||||
|
||||
emit(
|
||||
ChessBoardState(
|
||||
state.bottomColor,
|
||||
turnColor,
|
||||
event.position,
|
||||
ChessMove(from: event.startSquare, to: event.endSquare),
|
||||
move,
|
||||
true,
|
||||
event.squareInCheck,
|
||||
),
|
||||
@ -93,10 +97,11 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||
var apiMessage = ApiWebsocketMessage(
|
||||
type: MessageType.move,
|
||||
move: apiMove,
|
||||
color: null,
|
||||
turnColor: null,
|
||||
reason: null,
|
||||
position: null,
|
||||
squareInCheck: null,
|
||||
playerColor: null,
|
||||
);
|
||||
|
||||
ServerConnection.getInstance().send(jsonEncode(apiMessage));
|
||||
@ -122,10 +127,11 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||
var message = ApiWebsocketMessage(
|
||||
type: MessageType.move,
|
||||
move: apiMove,
|
||||
color: null,
|
||||
turnColor: null,
|
||||
reason: null,
|
||||
position: null,
|
||||
squareInCheck: null,
|
||||
playerColor: null,
|
||||
);
|
||||
log(jsonEncode(message));
|
||||
ServerConnection.getInstance().send(jsonEncode(message));
|
||||
@ -144,6 +150,20 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void boardStatusHandler(
|
||||
BoardStatusReceived event, Emitter<ChessBoardState> emit) {
|
||||
emit(
|
||||
ChessBoardState(
|
||||
event.myColor,
|
||||
event.whoseTurn,
|
||||
event.pos,
|
||||
ChessMove.none(),
|
||||
false,
|
||||
ChessCoordinate.none(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChessBoardState {
|
||||
@ -167,12 +187,12 @@ class ChessBoardState {
|
||||
ChessColor bottomColor,
|
||||
ChessColor turnColor,
|
||||
ChessPosition position,
|
||||
ChessMove lastMove,
|
||||
ChessMove? lastMove,
|
||||
bool positionAckd,
|
||||
ChessCoordinate squareInCheck,
|
||||
) {
|
||||
return ChessBoardState._(bottomColor, turnColor, position, lastMove,
|
||||
positionAckd, squareInCheck);
|
||||
return ChessBoardState._(bottomColor, turnColor, position,
|
||||
lastMove ?? ChessMove.none(), positionAckd, squareInCheck);
|
||||
}
|
||||
|
||||
factory ChessBoardState.init() {
|
||||
|
@ -3,13 +3,13 @@ import 'package:mchess/utils/chess_utils.dart';
|
||||
|
||||
abstract class ChessEvent {}
|
||||
|
||||
class ReceivedMove extends ChessEvent {
|
||||
final ChessCoordinate startSquare;
|
||||
final ChessCoordinate endSquare;
|
||||
class ReceivedBoardState extends ChessEvent {
|
||||
final ChessCoordinate? startSquare;
|
||||
final ChessCoordinate? endSquare;
|
||||
final ChessPosition position;
|
||||
final ChessCoordinate squareInCheck;
|
||||
|
||||
ReceivedMove({
|
||||
ReceivedBoardState({
|
||||
required this.startSquare,
|
||||
required this.endSquare,
|
||||
required this.position,
|
||||
@ -54,3 +54,15 @@ class InvalidMovePlayed extends ChessEvent {
|
||||
required this.squareInCheck,
|
||||
});
|
||||
}
|
||||
|
||||
class BoardStatusReceived extends ChessEvent {
|
||||
final ChessPosition pos;
|
||||
final ChessColor myColor;
|
||||
final ChessColor whoseTurn;
|
||||
|
||||
BoardStatusReceived({
|
||||
required this.pos,
|
||||
required this.myColor,
|
||||
required this.whoseTurn,
|
||||
});
|
||||
}
|
||||
|
@ -71,10 +71,16 @@ class ChessPositionManager {
|
||||
log(logString);
|
||||
}
|
||||
|
||||
void recordMove(ChessCoordinate from, ChessCoordinate to, ChessPosition pos) {
|
||||
void recordMove(
|
||||
ChessCoordinate? from, ChessCoordinate? to, ChessPosition pos) {
|
||||
position = pos;
|
||||
|
||||
history.add(ChessMove(from: from, to: to));
|
||||
history.add(
|
||||
ChessMove(
|
||||
from: from ?? ChessCoordinate.none(),
|
||||
to: to ?? ChessCoordinate.none(),
|
||||
),
|
||||
);
|
||||
|
||||
logPosition(position);
|
||||
logHistory(history);
|
||||
|
@ -12,9 +12,8 @@ import 'package:mchess/utils/chess_utils.dart';
|
||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||
|
||||
class ServerConnection {
|
||||
late WebSocketChannel channel;
|
||||
WebSocketChannel? channel;
|
||||
late bool wasConnected = false;
|
||||
late int counter = 0;
|
||||
Stream broadcast = const Stream.empty();
|
||||
|
||||
static final ServerConnection _instance = ServerConnection._internal();
|
||||
@ -32,18 +31,22 @@ class ServerConnection {
|
||||
}
|
||||
|
||||
void send(String message) {
|
||||
channel.sink.add(message);
|
||||
counter++;
|
||||
if (channel == null) {
|
||||
log("Sending on channel without initializing");
|
||||
return;
|
||||
}
|
||||
channel!.sink.add(message);
|
||||
}
|
||||
|
||||
void connect(String playerID, lobbyID, String? passphrase) {
|
||||
String url;
|
||||
if (kDebugMode) {
|
||||
channel =
|
||||
WebSocketChannel.connect(Uri.parse('ws://localhost:8080/api/ws'));
|
||||
url = 'ws://localhost:8080/api/ws';
|
||||
} else {
|
||||
channel = WebSocketChannel.connect(
|
||||
Uri.parse('wss://chess.sw-gross.de:9999/api/ws'));
|
||||
url = 'wss://chess.sw-gross.de:9999/api/ws';
|
||||
}
|
||||
channel = WebSocketChannel.connect(Uri.parse(url));
|
||||
|
||||
send(
|
||||
jsonEncode(
|
||||
WebsocketMessageIdentifyPlayer(
|
||||
@ -54,17 +57,26 @@ class ServerConnection {
|
||||
),
|
||||
);
|
||||
|
||||
log(channel.closeCode.toString());
|
||||
broadcast = channel.stream.asBroadcastStream();
|
||||
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("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;
|
||||
@ -78,25 +90,46 @@ class ServerConnection {
|
||||
}
|
||||
}
|
||||
|
||||
void handleIncomingColorDeterminedMessage(ApiWebsocketMessage apiMessage) {
|
||||
ConnectionCubit.getInstance().opponentConnected();
|
||||
ChessBloc.getInstance().add(InitBoard());
|
||||
ChessBloc.getInstance().add(
|
||||
ColorDetermined(myColor: ChessColor.fromApiColor(apiMessage.color!)));
|
||||
}
|
||||
|
||||
void handleIncomingMoveMessage(ApiWebsocketMessage apiMessage) {
|
||||
var move = ChessMove.fromApiMove(apiMessage.move!);
|
||||
void handleBoardStateMessage(ApiWebsocketMessage apiMessage) {
|
||||
ChessMove? move;
|
||||
if (apiMessage.move != null) {
|
||||
move = ChessMove.fromApiMove(apiMessage.move!);
|
||||
}
|
||||
|
||||
if (apiMessage.position != null) {
|
||||
ChessBloc.getInstance().add(
|
||||
ReceivedMove(
|
||||
ReceivedBoardState(
|
||||
startSquare: move?.from,
|
||||
endSquare: move?.to,
|
||||
position: ChessPositionManager.getInstance()
|
||||
.fromPGNString(apiMessage.position!),
|
||||
squareInCheck: ChessCoordinate.fromApiCoordinate(
|
||||
apiMessage.squareInCheck ?? const ApiCoordinate(col: 0, row: 0)),
|
||||
),
|
||||
);
|
||||
} 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 handleIncomingMoveMessage(ApiWebsocketMessage apiMessage) {
|
||||
if (apiMessage.position != null) {
|
||||
var move = ChessMove.fromApiMove(apiMessage.move!);
|
||||
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)),
|
||||
squareInCheck: ChessCoordinate.fromApiCoordinate(
|
||||
apiMessage.squareInCheck ?? const ApiCoordinate(col: 0, row: 0)),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
@ -109,8 +142,8 @@ class ServerConnection {
|
||||
ChessBloc.getInstance().add(
|
||||
InvalidMovePlayed(
|
||||
move: ChessMove.fromApiMove(apiMessage.move!),
|
||||
squareInCheck:
|
||||
ChessCoordinate.fromApiCoordinate(apiMessage.squareInCheck!),
|
||||
squareInCheck: ChessCoordinate.fromApiCoordinate(
|
||||
apiMessage.squareInCheck ?? const ApiCoordinate(col: 0, row: 0)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import 'package:mchess/chess_bloc/chess_bloc.dart';
|
||||
|
||||
import 'package:mchess/chess/chess_board.dart';
|
||||
import 'package:mchess/chess_bloc/promotion_bloc.dart';
|
||||
import 'package:mchess/connection/ws_connection.dart';
|
||||
import 'package:mchess/utils/chess_utils.dart';
|
||||
import 'package:mchess/utils/widgets/promotion_dialog.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
@ -53,6 +54,7 @@ class _ChessGameState extends State<ChessGame> {
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {
|
||||
ServerConnection.getInstance().disconnectExistingConnection();
|
||||
context.push('/');
|
||||
},
|
||||
child: const Icon(Icons.cancel),
|
||||
|
@ -19,7 +19,9 @@ class ChessAppRouter {
|
||||
GoRoute(
|
||||
path: '/',
|
||||
name: 'lobbySelector',
|
||||
builder: (context, state) => const LobbySelector(),
|
||||
builder: (context, state) {
|
||||
return const LobbySelector();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/prepareRandom',
|
||||
|
@ -1,53 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:mchess/chess_bloc/chess_bloc.dart';
|
||||
import 'package:mchess/chess_bloc/chess_position.dart';
|
||||
import 'package:mchess/utils/chess_utils.dart';
|
||||
|
||||
class MoveHistory extends StatefulWidget {
|
||||
const MoveHistory({super.key});
|
||||
|
||||
@override
|
||||
State<MoveHistory> createState() => _MoveHistoryState();
|
||||
}
|
||||
|
||||
class _MoveHistoryState extends State<MoveHistory> {
|
||||
late List<String> entries;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
entries = [];
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocListener<ChessBloc, ChessBoardState>(
|
||||
listener: (context, state) {
|
||||
List<String> newEntries = [];
|
||||
var positionManager = ChessPositionManager.getInstance();
|
||||
var allMoves = positionManager.allMoves;
|
||||
|
||||
for (ChessMove move in allMoves) {
|
||||
var movedPiece = positionManager.getPieceAt(move.to);
|
||||
var char = pieceCharacter[ChessPieceAssetKey(
|
||||
pieceClass: movedPiece!.pieceClass,
|
||||
color: movedPiece.color.getOpposite())];
|
||||
|
||||
if (movedPiece.color == ChessColor.white) {
|
||||
newEntries.add("$char ${move.to.toAlphabetical()}");
|
||||
} else {
|
||||
newEntries.last =
|
||||
"${newEntries.last}\t\t$char${move.to.toAlphabetical()}";
|
||||
}
|
||||
}
|
||||
setState(() {
|
||||
entries = newEntries;
|
||||
});
|
||||
},
|
||||
child: ListView(children: [
|
||||
for (var entry in entries) Text(entry),
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
20
pubspec.lock
20
pubspec.lock
@ -124,18 +124,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: go_router
|
||||
sha256: c247a4f76071c3b97bb5ae8912968870d5565644801c5e09f3bc961b4d874895
|
||||
sha256: c5fa45fa502ee880839e3b2152d987c44abae26d064a2376d4aad434cf0f7b15
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "12.1.1"
|
||||
version: "12.1.3"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
|
||||
sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.1.2"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -212,10 +212,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
sha256: eeb2d1428ee7f4170e2bd498827296a18d4e7fc462b71727d111c0ac7707cfa6
|
||||
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.1"
|
||||
version: "6.0.2"
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -361,10 +361,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556
|
||||
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.4.2"
|
||||
version: "6.5.0"
|
||||
sdks:
|
||||
dart: ">=3.2.0-194.0.dev <4.0.0"
|
||||
flutter: ">=3.7.0"
|
||||
dart: ">=3.2.0 <4.0.0"
|
||||
flutter: ">=3.10.0"
|
||||
|
@ -13,9 +13,9 @@ import 'package:uuid/uuid.dart';
|
||||
void main() {
|
||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(const ChessGame(
|
||||
playerID: UuidValue("test"),
|
||||
lobbyID: UuidValue("testLobbyId"),
|
||||
await tester.pumpWidget(ChessGame(
|
||||
playerID: UuidValue.fromString("test"),
|
||||
lobbyID: UuidValue.fromString("testLobbyId"),
|
||||
passphrase: 'test',
|
||||
));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user