// =========================================================================== // FILE: lib/services/multiplayer_service.dart // =========================================================================== import 'dart:math'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:share_plus/share_plus.dart'; class MultiplayerService { final FirebaseFirestore _firestore = FirebaseFirestore.instance; final FirebaseAuth _auth = FirebaseAuth.instance; CollectionReference get _gamesCollection => _firestore.collection('games'); Future createGameRoom(int boardRadius, String hostName, String shapeName, bool isTimeMode, {bool isPublic = true}) async { 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, 'hostUid': _auth.currentUser?.uid, // NUOVO: Salviamo l'ID univoco del creatore 'guestName': '', 'shape': shapeName, 'timeMode': isTimeMode, 'isPublic': isPublic, 'p1_reaction': null, 'p2_reaction': null, 'p1_rematch': false, 'p2_rematch': false, }); return roomCode; } Future?> 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; } return null; } Stream getPublicRooms() { return _gamesCollection .where('status', isEqualTo: 'waiting') .where('isPublic', isEqualTo: true) .snapshots(); } void shareInviteLink(String roomCode) { String message = "Ehi! Giochiamo a TetraQ? 🎮\nCopia questo intero messaggio e apri l'app per entrare direttamente, oppure inserisci manualmente il codice: $roomCode"; Share.share(message); } Stream 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 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 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 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"); } } }