Introduce api types and some changes.

This commit is contained in:
Marco 2023-06-02 23:28:40 +02:00
parent b0d6f4002c
commit 605743c0d2
10 changed files with 124 additions and 60 deletions

32
lib/api/move.dart Normal file
View File

@ -0,0 +1,32 @@
class ApiMove {
final ApiCoordinate startSquare;
final ApiCoordinate endSquare;
const ApiMove({
required this.startSquare,
required this.endSquare,
});
factory ApiMove.fromJson(Map<String, dynamic> json) {
final startSquare = ApiCoordinate.fromJson(json['startSquare']);
final endSquare = ApiCoordinate.fromJson(json['endSquare']);
return ApiMove(startSquare: startSquare, endSquare: endSquare);
}
Map<String, dynamic> toJson() =>
{'startSquare': startSquare, 'endSquare': endSquare};
}
class ApiCoordinate {
final int col;
final int row;
const ApiCoordinate({required this.col, required this.row});
factory ApiCoordinate.fromJson(Map<String, dynamic> json) {
return ApiCoordinate(col: json['col'], row: json['row']);
}
Map<String, dynamic> toJson() => {'col': col, 'row': row};
}

30
lib/api/register.dart Normal file
View File

@ -0,0 +1,30 @@
import 'package:uuid/uuid.dart';
class ResponseFromRegisteringGame {
final UuidValue playerID;
final UuidValue lobbyID;
const ResponseFromRegisteringGame({
required this.playerID,
required this.lobbyID,
});
factory ResponseFromRegisteringGame.fromJson(Map<String, dynamic> json) {
final playerid = UuidValue(json['playerID']);
final lobbyid = UuidValue(json['lobbyID']);
return ResponseFromRegisteringGame(playerID: playerid, lobbyID: lobbyid);
}
}
class WebsocketMessageIdentifyPlayer {
final String playerID;
final String lobbyID;
const WebsocketMessageIdentifyPlayer({
required this.playerID,
required this.lobbyID,
});
Map<String, dynamic> toJson() => {'lobbyID': lobbyID, 'playerID': playerID};
}

View File

@ -1,4 +1,7 @@
import 'dart:convert';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:mchess/api/move.dart';
import 'package:mchess/chess_bloc/chess_events.dart'; import 'package:mchess/chess_bloc/chess_events.dart';
import 'package:mchess/chess_bloc/chess_position.dart'; import 'package:mchess/chess_bloc/chess_position.dart';
import 'package:mchess/connection/ws_connection.dart'; import 'package:mchess/connection/ws_connection.dart';
@ -62,8 +65,13 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
void ownMoveHandler(OwnPieceMoved event, Emitter<ChessBoardState> emit) { void ownMoveHandler(OwnPieceMoved event, Emitter<ChessBoardState> emit) {
ChessPosition.getInstance().recordMove(event.startSquare, event.endSquare); ChessPosition.getInstance().recordMove(event.startSquare, event.endSquare);
ServerConnection.getInstance().send( var start = ApiCoordinate(
'mv ${event.startSquare.toString()} ${event.endSquare.toString()}'); col: event.startSquare.column, row: event.startSquare.row);
var end =
ApiCoordinate(col: event.endSquare.column, row: event.endSquare.row);
var move = ApiMove(startSquare: start, endSquare: end);
ServerConnection.getInstance().send(jsonEncode(move));
} }
} }

View File

@ -64,7 +64,10 @@ class ChessPosition {
} }
ChessPositionType get currentPosition => position; ChessPositionType get currentPosition => position;
ChessMove get lastMove => history.last; ChessMove? get lastMove {
if (history.isEmpty) return null;
return history.last;
}
void recordMove(ChessCoordinate from, ChessCoordinate to) { void recordMove(ChessCoordinate from, ChessCoordinate to) {
position[to] = position[from] ?? const ChessPiece.none(); position[to] = position[from] ?? const ChessPiece.none();

View File

@ -1,8 +1,11 @@
import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:mchess/api/move.dart';
import 'package:mchess/chess_bloc/chess_bloc.dart'; import 'package:mchess/chess_bloc/chess_bloc.dart';
import 'package:mchess/chess_bloc/chess_events.dart'; import 'package:mchess/chess_bloc/chess_events.dart';
import 'package:mchess/chess_bloc/chess_position.dart'; import 'package:mchess/chess_bloc/chess_position.dart';
import 'package:mchess/api/register.dart';
import 'package:mchess/utils/chess_utils.dart'; import 'package:mchess/utils/chess_utils.dart';
import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:web_socket_channel/web_socket_channel.dart';
@ -31,7 +34,7 @@ class ServerConnection {
counter++; counter++;
} }
void connect(String playerID) { void connect(String playerID, lobbyID) {
if (wasConnected) channel.sink.close(); if (wasConnected) channel.sink.close();
if (kDebugMode) { if (kDebugMode) {
@ -41,7 +44,8 @@ class ServerConnection {
channel = channel =
WebSocketChannel.connect(Uri.parse('wss://chess.sw-gross.de:8080')); WebSocketChannel.connect(Uri.parse('wss://chess.sw-gross.de:8080'));
} }
send(playerID); send(jsonEncode(WebsocketMessageIdentifyPlayer(
playerID: (playerID), lobbyID: (lobbyID))));
log(channel.closeCode.toString()); log(channel.closeCode.toString());
@ -49,39 +53,27 @@ class ServerConnection {
broadcast = channel.stream.asBroadcastStream(); broadcast = channel.stream.asBroadcastStream();
broadcast.listen((data) { broadcast.listen(handleIncomingData);
log("Data received:"); }
log(data);
var receivedString = data.toString(); void handleIncomingData(dynamic data) {
var splitString = receivedString.split(' '); log("Data received:");
log(data);
var apiMove = ApiMove.fromJson(jsonDecode(data));
if (splitString[0] == "cl") { //Todo: Implement status messages
ChessColor onBottom = // e.g. to tell the client that both players connected, who is playing which color and so on
splitString[1] == "white" ? ChessColor.white : ChessColor.black;
ChessBloc.getInstance().add(ColorDetermined(myColor: onBottom));
return;
}
if (splitString[0] == "bd") { var move = ChessMove.fromApiMove(apiMove);
if (splitString[1] == "init") ChessBloc.getInstance().add(InitBoard()); if (move == ChessPosition.getInstance().lastMove) {
} //This is our own move that got resent by the server. Do not process.
} else {
log('lastMove: from: ${ChessPosition.getInstance().lastMove?.from} to: ${ChessPosition.getInstance().lastMove?.to}');
log('constructed move: from: ${move.from} to: ${move.to}');
log('Move received : ${move.from.toAlphabetical()}->${move.to.toAlphabetical()}');
if (splitString[0] == ('mv')) { ChessBloc.getInstance()
var startSquare = ChessCoordinate.fromString(splitString[1]); .add(OpponentPieceMoved(startSquare: move.from, endSquare: move.to));
var endSquare = ChessCoordinate.fromString(splitString[2]); }
if (ChessMove(from: startSquare, to: endSquare) ==
ChessPosition.getInstance().lastMove) {
//This is our own move that got resent by the server. Do not process.
} else {
log('lastMove: from: ${ChessPosition.getInstance().lastMove.from} to: ${ChessPosition.getInstance().lastMove.to}');
log('constructed move: from: $startSquare to: $endSquare)}');
log('Move received : ${startSquare.toAlphabetical()}:${endSquare.toAlphabetical()}');
ChessBloc.getInstance().add(OpponentPieceMoved(
startSquare: startSquare, endSquare: endSquare));
}
}
});
} }
} }

View File

@ -15,8 +15,8 @@ class ConnectionCubit extends Cubit<ConnectionCubitState> {
return _instance; return _instance;
} }
void connect(String playerID) { void connect(String playerID, lobbyID) {
ServerConnection.getInstance().connect(playerID); ServerConnection.getInstance().connect(playerID, lobbyID);
emit(ConnectionCubitState(true)); emit(ConnectionCubitState(true));
} }
} }

View File

@ -1,17 +0,0 @@
import 'package:uuid/uuid.dart';
class ResponseFromRegisteringGame {
final UuidValue playerID;
const ResponseFromRegisteringGame({
required this.playerID,
});
factory ResponseFromRegisteringGame.fromJson(Map<String, dynamic> json) {
final uuid = UuidValue(json['playerID']);
return ResponseFromRegisteringGame(
playerID: uuid,
);
}
}

View File

@ -14,7 +14,8 @@ import 'package:uuid/uuid.dart';
class ChessGame extends StatefulWidget { class ChessGame extends StatefulWidget {
final UuidValue playerID; final UuidValue playerID;
const ChessGame({required this.playerID, super.key}); final UuidValue lobbyID;
const ChessGame({required this.playerID, required this.lobbyID, super.key});
@override @override
State<ChessGame> createState() => _ChessGameState(); State<ChessGame> createState() => _ChessGameState();
@ -24,7 +25,8 @@ class _ChessGameState extends State<ChessGame> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
ConnectionCubit.getInstance().connect(widget.playerID.uuid); ConnectionCubit.getInstance()
.connect(widget.playerID.uuid, widget.lobbyID.uuid);
} }
@override @override

View File

@ -1,7 +1,7 @@
import 'dart:developer'; import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mchess/models/models.dart'; import 'package:mchess/api/register.dart';
import 'package:mchess/pages/chess_game.dart'; import 'package:mchess/pages/chess_game.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'dart:convert'; import 'dart:convert';
@ -18,6 +18,7 @@ class PrepareChessGameWidget extends StatelessWidget {
log('future done ${snapshot.data?.playerID}'); log('future done ${snapshot.data?.playerID}');
return ChessGame( return ChessGame(
playerID: snapshot.data!.playerID, playerID: snapshot.data!.playerID,
lobbyID: snapshot.data!.lobbyID,
); );
} }
return const CircularProgressIndicator(); return const CircularProgressIndicator();
@ -26,8 +27,9 @@ class PrepareChessGameWidget extends StatelessWidget {
} }
Future<ResponseFromRegisteringGame> registerForRandomGame() async { Future<ResponseFromRegisteringGame> registerForRandomGame() async {
final response = final response = await http.get(
await http.get(Uri.parse('http://localhost:8080/api/random')); Uri.parse('http://localhost:8080/api/random'),
headers: {"Accept": "application/json"});
if (response.statusCode == 200) { if (response.statusCode == 200) {
log(response.body); log(response.body);
@ -35,7 +37,7 @@ class PrepareChessGameWidget extends StatelessWidget {
} else { } else {
// If the server did not return a 200 OK response, // If the server did not return a 200 OK response,
// then throw an exception. // then throw an exception.
throw Exception('Failed to load album'); throw Exception('Failed to register for random game');
} }
} }
} }

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:mchess/api/move.dart';
import 'package:quiver/core.dart'; import 'package:quiver/core.dart';
enum ChessPieceName { enum ChessPieceName {
@ -65,6 +66,10 @@ class ChessCoordinate {
return ChessCoordinate(column, row); return ChessCoordinate(column, row);
} }
factory ChessCoordinate.fromApiCoordinate(ApiCoordinate apiCoordinate) {
return ChessCoordinate(apiCoordinate.col, apiCoordinate.row);
}
ChessCoordinate.copyFrom(ChessCoordinate original) ChessCoordinate.copyFrom(ChessCoordinate original)
: column = original.column, : column = original.column,
row = original.row; row = original.row;
@ -159,6 +164,13 @@ class ChessMove {
ChessMove({required this.from, required this.to}); ChessMove({required this.from, required this.to});
factory ChessMove.fromApiMove(ApiMove apiMove) {
final start = ChessCoordinate.fromApiCoordinate(apiMove.startSquare);
final end = ChessCoordinate.fromApiCoordinate(apiMove.endSquare);
return ChessMove(from: start, to: end);
}
@override @override
operator ==(other) { operator ==(other) {
return other is ChessMove && other.from == from && other.to == to; return other is ChessMove && other.from == from && other.to == to;