2026-02-27 23:35:54 +01:00
// ===========================================================================
// FILE: lib/services/multiplayer_service.dart
// ===========================================================================
import ' dart:math ' ;
import ' package:cloud_firestore/cloud_firestore.dart ' ;
import ' package:flutter/material.dart ' ;
import ' package:share_plus/share_plus.dart ' ;
class MultiplayerService {
final FirebaseFirestore _firestore = FirebaseFirestore . instance ;
CollectionReference get _gamesCollection = > _firestore . collection ( ' games ' ) ;
2026-03-11 22:00:01 +01:00
Future < String > createGameRoom ( int boardRadius , String hostName , String shapeName , bool isTimeMode , { bool isPublic = true } ) async {
2026-02-27 23:35:54 +01:00
String roomCode = _generateRoomCode ( ) ;
int randomSeed = Random ( ) . nextInt ( 1000000 ) ;
await _gamesCollection . doc ( roomCode ) . set ( {
' status ' : ' waiting ' ,
' radius ' : boardRadius ,
' createdAt ' : FieldValue . serverTimestamp ( ) ,
' players ' : [ ' host ' ] ,
' turn ' : ' host ' ,
' moves ' : [ ] ,
' seed ' : randomSeed ,
' hostName ' : hostName ,
' guestName ' : ' ' ,
' shape ' : shapeName ,
' timeMode ' : isTimeMode ,
2026-03-11 22:00:01 +01:00
' isPublic ' : isPublic ,
2026-02-27 23:35:54 +01:00
' p1_reaction ' : null ,
' p2_reaction ' : null ,
' p1_rematch ' : false ,
' p2_rematch ' : false ,
} ) ;
return roomCode ;
}
Future < Map < String , dynamic > ? > joinGameRoom ( String roomCode , String guestName ) async {
DocumentSnapshot doc = await _gamesCollection . doc ( roomCode ) . get ( ) ;
if ( doc . exists & & doc [ ' status ' ] = = ' waiting ' ) {
await _gamesCollection . doc ( roomCode ) . update ( {
' status ' : ' playing ' ,
' players ' : FieldValue . arrayUnion ( [ ' guest ' ] ) ,
' guestName ' : guestName ,
} ) ;
return doc . data ( ) as Map < String , dynamic > ;
}
return null ;
}
2026-03-11 22:00:01 +01:00
// QUERY BLINDATA: Chiede a Firebase solo le stanze waiting E pubbliche
Stream < QuerySnapshot > getPublicRooms ( ) {
return _gamesCollection
. where ( ' status ' , isEqualTo: ' waiting ' )
. where ( ' isPublic ' , isEqualTo: true )
. snapshots ( ) ;
}
2026-02-27 23:35:54 +01:00
void shareInviteLink ( String roomCode ) {
String message = " Ehi! Giochiamo a TetraQ? 🎮 \n Copia questo intero messaggio e apri l'app per entrare direttamente, oppure inserisci manualmente il codice: $ roomCode " ;
Share . share ( message ) ;
}
Stream < DocumentSnapshot > listenToRoom ( String roomCode ) {
return _gamesCollection . doc ( roomCode ) . snapshots ( ) ;
}
String _generateRoomCode ( ) {
const chars = ' ACDEFGHJKLMNPQRSTUVWXYZ2345679 ' ;
final random = Random ( ) ;
return String . fromCharCodes ( Iterable . generate (
5 , ( _ ) = > chars . codeUnitAt ( random . nextInt ( chars . length ) ) ,
) ) ;
}
Future < void > sendReaction ( String roomCode , bool isHost , String reaction ) async {
try {
String prefix = isHost ? ' p1 ' : ' p2 ' ;
await _gamesCollection . doc ( roomCode ) . update ( {
' ${ prefix } _reaction ' : reaction ,
' ${ prefix } _reaction_time ' : FieldValue . serverTimestamp ( ) ,
} ) ;
} catch ( e ) {
debugPrint ( " Errore invio reazione: $ e " ) ;
}
}
Future < void > requestRematch ( String roomCode , bool isHost ) async {
try {
String prefix = isHost ? ' p1 ' : ' p2 ' ;
await _gamesCollection . doc ( roomCode ) . update ( {
' ${ prefix } _rematch ' : true ,
} ) ;
} catch ( e ) {
debugPrint ( " Errore richiesta rivincita: $ e " ) ;
}
}
Future < void > resetMatch ( String roomCode , int newRadius , String newShape , int newSeed ) async {
try {
await _gamesCollection . doc ( roomCode ) . update ( {
' status ' : ' playing ' ,
' moves ' : [ ] ,
' seed ' : newSeed ,
' radius ' : newRadius ,
' shape ' : newShape ,
' p1_rematch ' : false ,
' p2_rematch ' : false ,
' p1_reaction ' : null ,
' p2_reaction ' : null ,
} ) ;
} catch ( e ) {
debugPrint ( " Errore reset partita: $ e " ) ;
}
}
}