tetraq/lib/services/multiplayer_service.dart

158 lines
4.8 KiB
Dart
Raw Normal View History

2026-02-27 23:35:54 +01:00
// ===========================================================================
// FILE: lib/services/multiplayer_service.dart
// ===========================================================================
import 'dart:math';
import 'package:cloud_firestore/cloud_firestore.dart';
2026-03-11 23:00:01 +01:00
import 'package:firebase_auth/firebase_auth.dart';
2026-02-27 23:35:54 +01:00
import 'package:flutter/material.dart';
import 'package:share_plus/share_plus.dart';
class MultiplayerService {
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
2026-03-11 23:00:01 +01:00
final FirebaseAuth _auth = FirebaseAuth.instance;
2026-02-27 23:35:54 +01:00
CollectionReference get _gamesCollection => _firestore.collection('games');
2026-03-15 13:00:00 +01:00
CollectionReference get _invitesCollection => _firestore.collection('invites');
2026-02-27 23:35:54 +01:00
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,
2026-03-15 13:00:00 +01:00
'hostUid': _auth.currentUser?.uid,
2026-02-27 23:35:54 +01:00
'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
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) {
2026-03-15 17:00:01 +01:00
// ECCO IL TUO SMART LINK FIREBASE!
String smartLink = "https://tetraq-32a4a.web.app";
2026-03-12 21:00:08 +01:00
String message = "Ehi! Giochiamo a TetraQ? 🎮\n\n"
2026-03-15 17:00:01 +01:00
"Apri l'app e inserisci il codice stanza:\n"
"👉 $roomCode\n\n"
"Oppure clicca qui se il tuo telefono lo supporta:\n"
2026-03-12 21:00:08 +01:00
"tetraq://join?code=$roomCode\n\n"
2026-03-15 17:00:01 +01:00
"Non hai ancora il gioco? Scaricalo da qui:\n"
"$smartLink";
2026-02-27 23:35:54 +01:00
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");
}
}
2026-03-15 13:00:00 +01:00
Future<void> sendInvite(String targetUid, String roomCode, String hostName) async {
try {
await _invitesCollection.add({
'targetUid': targetUid,
'hostName': hostName,
'roomCode': roomCode,
'timestamp': FieldValue.serverTimestamp(),
});
} catch(e) {
debugPrint("Errore invio invito: $e");
}
}
Stream<QuerySnapshot> listenForInvites(String myUid) {
return _invitesCollection.where('targetUid', isEqualTo: myUid).snapshots();
}
Future<void> deleteInvite(String inviteId) async {
try {
await _invitesCollection.doc(inviteId).delete();
} catch(e) {
debugPrint("Errore cancellazione invito: $e");
}
}
2026-02-27 23:35:54 +01:00
}