From adf8c86692badbe274cb86cc018a7e5ce8d155dd Mon Sep 17 00:00:00 2001 From: Marco Date: Mon, 20 May 2024 15:34:20 +0200 Subject: [PATCH] Make many changes 1. A game is only identified by a passphrase (not a lobby id) 2. We can store multiple passphrase/playerID combinations --- lib/api/game_info.dart | 48 +++------------ lib/connection/ws_connection.dart | 9 +-- lib/connection_cubit/connection_cubit.dart | 8 ++- lib/pages/create_game_widget.dart | 44 +++++--------- lib/pages/join_game_handle_widget.dart | 69 ++++++++-------------- lib/pages/lobby_selector.dart | 8 ++- lib/utils/chess_router.dart | 6 +- lib/utils/config.dart | 6 +- 8 files changed, 68 insertions(+), 130 deletions(-) diff --git a/lib/api/game_info.dart b/lib/api/game_info.dart index c34adc7..9247df4 100644 --- a/lib/api/game_info.dart +++ b/lib/api/game_info.dart @@ -3,43 +3,33 @@ import 'package:uuid/uuid.dart'; class GameInfo { final UuidValue? playerID; - final UuidValue? lobbyID; final String? passphrase; const GameInfo({ required this.playerID, - required this.lobbyID, required this.passphrase, }); factory GameInfo.empty() { - return const GameInfo(playerID: null, lobbyID: null, passphrase: null); + return const GameInfo(playerID: null, passphrase: null); } factory GameInfo.fromJson(Map json) { final playerid = UuidValue.fromString(json['playerID']); - final lobbyid = UuidValue.fromString(json['lobbyID']); final passphrase = json['passphrase']; - return GameInfo( - playerID: playerid, lobbyID: lobbyid, passphrase: passphrase); + return GameInfo(playerID: playerid, passphrase: passphrase); } Map toJson() { String? pid; - String? lid; if (playerID != null) { pid = playerID.toString(); } - if (lobbyID != null) { - lid = lobbyID.toString(); - } - return { 'playerID': pid, - 'lobbyID': lid, 'passphrase': passphrase, }; } @@ -47,51 +37,29 @@ class GameInfo { void store() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); - await prefs.setBool("contains", true); - await prefs.setString("playerID", playerID.toString()); - await prefs.setString("lobbyID", lobbyID.toString()); - await prefs.setString("passphrase", passphrase.toString()); + await prefs.setString(passphrase!, playerID.toString()); } - void delete() async { + static Future get(String phrase) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); + var playerID = prefs.getString(phrase); - await prefs.setBool("contains", false); - } - - static Future get() async { - final SharedPreferences prefs = await SharedPreferences.getInstance(); - var contains = prefs.getBool("contains"); - var playerID = prefs.getString("playerID"); - var lobbyID = prefs.getString("lobbyID"); - var passphrase = prefs.getString("passphrase"); - - if (contains == null || - !contains || - playerID == null || - lobbyID == null || - passphrase == null) { - return null; - } + if (playerID == null) return null; return GameInfo( - playerID: UuidValue.fromString(playerID), - lobbyID: UuidValue.fromString(lobbyID), - passphrase: passphrase); + playerID: UuidValue.fromString(playerID!), passphrase: phrase); } } class WebsocketMessageIdentifyPlayer { final String playerID; - final String lobbyID; final String? passphrase; const WebsocketMessageIdentifyPlayer({ required this.playerID, - required this.lobbyID, required this.passphrase, }); Map toJson() => - {'lobbyID': lobbyID, 'playerID': playerID, 'passphrase': passphrase}; + {'playerID': playerID, 'passphrase': passphrase}; } diff --git a/lib/connection/ws_connection.dart b/lib/connection/ws_connection.dart index 33eaac8..85dcd99 100644 --- a/lib/connection/ws_connection.dart +++ b/lib/connection/ws_connection.dart @@ -40,15 +40,15 @@ class ServerConnection { channel!.sink.add(message); } - void connect(String playerID, lobbyID, String? passphrase) { - disconnectExistingConnection(); + void connect(String playerID, String? passphrase) { + if (channel != null) return; + channel = WebSocketChannel.connect(Uri.parse(config.getWebsocketURL())); send( jsonEncode( WebsocketMessageIdentifyPlayer( playerID: (playerID), - lobbyID: (lobbyID), passphrase: (passphrase), ), ), @@ -61,8 +61,9 @@ class ServerConnection { void disconnectExistingConnection() { if (channel == null) return; - channel!.sink.close(); + channel!.sink.close(); + channel = null; broadcast = const Stream.empty(); } diff --git a/lib/connection_cubit/connection_cubit.dart b/lib/connection_cubit/connection_cubit.dart index 9c9c3e2..3ef0c5e 100644 --- a/lib/connection_cubit/connection_cubit.dart +++ b/lib/connection_cubit/connection_cubit.dart @@ -15,8 +15,12 @@ class ConnectionCubit extends Cubit { return _instance; } - void connect(String playerID, lobbyID, String? passphrase) { - ServerConnection.getInstance().connect(playerID, lobbyID, passphrase); + void connect(String playerID, String? passphrase) { + ServerConnection.getInstance().connect(playerID, passphrase); + } + + void disonnect() { + ServerConnection.getInstance().disconnectExistingConnection(); } void opponentConnected() { diff --git a/lib/pages/create_game_widget.dart b/lib/pages/create_game_widget.dart index 5db7430..d005bba 100644 --- a/lib/pages/create_game_widget.dart +++ b/lib/pages/create_game_widget.dart @@ -28,31 +28,9 @@ class _CreateGameWidgetState extends State { @override void initState() { - registerResponse = hostPrivateGame(); - - registerResponse.then((args) { - if (args == null) return; - - chessGameArgs = ChessGameArguments( - lobbyID: args.lobbyID!, - playerID: args.playerID!, - passphrase: args.passphrase); - }); - - connectToWebsocket(registerResponse); super.initState(); - } - - void connectToWebsocket(Future resp) { - resp.then((value) { - if (value == null) return; - - ConnectionCubit.getInstance().connect( - value.playerID!.uuid, - value.lobbyID!.uuid, - value.passphrase, - ); - }); + ConnectionCubit().disonnect(); + registerResponse = createPrivateGame(); } @override @@ -82,15 +60,21 @@ class _CreateGameWidgetState extends State { child: CircularProgressIndicator(), ); } else { - String passphrase = snapshot.data?.passphrase ?? "no passphrase"; + var passphrase = snapshot.data?.passphrase ?? "no passphrase"; + + ConnectionCubit().connect( + snapshot.data!.playerID.toString(), + snapshot.data!.passphrase, + ); + return BlocListener( listener: (context, state) { // We wait for our opponent to connect if (state.opponentConnected) { //TODO: is goNamed the correct way to navigate? - context.goNamed('game', - pathParameters: {'phrase': passphrase.toURL()}, - extra: chessGameArgs); + context.goNamed('game', pathParameters: { + 'phrase': passphrase.toURL(), + }); } }, child: Column( @@ -130,11 +114,11 @@ class _CreateGameWidgetState extends State { ); } - Future hostPrivateGame() async { + Future createPrivateGame() async { Response response; try { - response = await http.get(Uri.parse(config.getHostURL()), + response = await http.get(Uri.parse(config.getCreateGameURL()), headers: {"Accept": "application/json"}); } catch (e) { log('Exception: ${e.toString()}'); diff --git a/lib/pages/join_game_handle_widget.dart b/lib/pages/join_game_handle_widget.dart index 4d865c5..9c0dfbc 100644 --- a/lib/pages/join_game_handle_widget.dart +++ b/lib/pages/join_game_handle_widget.dart @@ -3,7 +3,6 @@ import 'dart:developer'; import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; import 'package:mchess/api/game_info.dart'; import 'package:mchess/connection_cubit/connection_cubit.dart'; @@ -23,65 +22,48 @@ class _JoinGameHandleWidgetState extends State { @override void initState() { - joinGameFuture = joinPrivateGame(widget.passphrase); - joinGameFuture.then( - (value) { - if (value != null) { - switchToGame(value); - } - }, - ); super.initState(); + ConnectionCubit().disonnect(); + joinGameFuture = joinPrivateGame(widget.passphrase); } @override Widget build(BuildContext context) { - return const ChessGame(); - } - - void switchToGame(GameInfo info) { - var chessGameArgs = ChessGameArguments( - lobbyID: info.lobbyID!, - playerID: info.playerID!, - passphrase: info.passphrase); - - ConnectionCubit.getInstance().connect( - info.playerID!.uuid, - info.lobbyID!.uuid, - info.passphrase, - ); - - if (!chessGameArgs.isValid()) { - context.goNamed('lobbySelector'); - const snackBar = SnackBar( - backgroundColor: Colors.amberAccent, - content: Text("Game information is corrupted"), - ); - ScaffoldMessenger.of(context).clearSnackBars(); - ScaffoldMessenger.of(context).showSnackBar(snackBar); - - return; - } + return FutureBuilder( + future: joinGameFuture, + builder: (context, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return const SizedBox( + height: 100, + width: 100, + child: CircularProgressIndicator(), + ); + } else { + ConnectionCubit.getInstance().connect( + snapshot.data!.playerID!.uuid, + snapshot.data!.passphrase, + ); + return const ChessGame(); + } + }); } Future joinPrivateGame(String phrase) async { http.Response response; - var existingInfo = await GameInfo.get(); - log('lobbyID: ${existingInfo?.lobbyID} and playerID: ${existingInfo?.playerID} and passphrase: "${existingInfo?.passphrase}"'); + + var existingInfo = await GameInfo.get(phrase); + log('playerID: ${existingInfo?.playerID} and passphrase: "${existingInfo?.passphrase}"'); GameInfo info; if (existingInfo?.passphrase == phrase) { // We have player info for this exact passphrase - info = GameInfo( - playerID: existingInfo?.playerID, - lobbyID: existingInfo?.lobbyID, - passphrase: phrase); + info = GameInfo(playerID: existingInfo?.playerID, passphrase: phrase); } else { - info = GameInfo(playerID: null, lobbyID: null, passphrase: phrase); + info = GameInfo(playerID: null, passphrase: phrase); } try { - response = await http.post(Uri.parse(config.getJoinURL()), + response = await http.post(Uri.parse(config.getJoinGameURL()), body: jsonEncode(info), headers: {"Accept": "application/json"}); } catch (e) { log(e.toString()); @@ -114,7 +96,6 @@ class _JoinGameHandleWidgetState extends State { var info = GameInfo.fromJson(jsonDecode(response.body)); info.store(); log('Player info received from server: '); - log('lobbyID: ${info.lobbyID}'); log('playerID: ${info.playerID}'); log('passphrase: ${info.passphrase}'); diff --git a/lib/pages/lobby_selector.dart b/lib/pages/lobby_selector.dart index 353d73b..c6e10b2 100644 --- a/lib/pages/lobby_selector.dart +++ b/lib/pages/lobby_selector.dart @@ -20,7 +20,9 @@ class _LobbySelectorState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( - onPressed: () => context.goNamed('createGame'), + onPressed: () { + context.goNamed('createGame'); + }, child: const Row( mainAxisSize: MainAxisSize.min, children: [ @@ -34,7 +36,9 @@ class _LobbySelectorState extends State { ), const SizedBox(height: 20), ElevatedButton( - onPressed: () => buildEnterPassphraseDialog(context), + onPressed: () { + buildEnterPassphraseDialog(context); + }, child: const Row( mainAxisSize: MainAxisSize.min, children: [ diff --git a/lib/utils/chess_router.dart b/lib/utils/chess_router.dart index aab6974..6507b9a 100644 --- a/lib/utils/chess_router.dart +++ b/lib/utils/chess_router.dart @@ -2,7 +2,6 @@ import 'dart:developer'; import 'package:flutter/widgets.dart'; import 'package:go_router/go_router.dart'; -import 'package:mchess/connection/ws_connection.dart'; import 'package:mchess/pages/join_game_handle_widget.dart'; import 'package:mchess/pages/lobby_selector.dart'; import 'package:mchess/pages/create_game_widget.dart'; @@ -40,8 +39,6 @@ class ChessAppRouter { path: 'game/:phrase', name: 'game', builder: (context, state) { - ServerConnection.getInstance().disconnectExistingConnection(); - var urlPhrase = state.pathParameters['phrase']; if (urlPhrase == null) { log('in /game route builder: url phrase null'); @@ -49,8 +46,7 @@ class ChessAppRouter { } return JoinGameHandleWidget( - passphrase: urlPhrase.toPhraseWithSpaces(), - ); + passphrase: urlPhrase.toPhraseWithSpaces()); }, ) ], diff --git a/lib/utils/config.dart b/lib/utils/config.dart index 863dedb..c80f6ae 100644 --- a/lib/utils/config.dart +++ b/lib/utils/config.dart @@ -1,9 +1,9 @@ const prodURL = 'chess.sw-gross.de:9999'; const debugURL = 'localhost:8080'; -const useDbgUrl = false; +const useDbgUrl = true; -String getHostURL() { +String getCreateGameURL() { var prot = 'https'; var domain = prodURL; if (useDbgUrl) { @@ -13,7 +13,7 @@ String getHostURL() { return '$prot://$domain/api/hostPrivate'; } -String getJoinURL() { +String getJoinGameURL() { var prot = 'https'; var domain = prodURL; if (useDbgUrl) {