NUEVA GESTIÓN DE USUARIOS Y PARTIDAS
This commit is contained in:
@@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../modelos/partida.dart';
|
||||
import '../modelos/snapshot_partida_online.dart';
|
||||
|
||||
class ResultadoPartidaGuardado {
|
||||
final String id;
|
||||
@@ -42,6 +43,25 @@ class ResultadoPartidaGuardado {
|
||||
);
|
||||
}
|
||||
|
||||
factory ResultadoPartidaGuardado.desdeSnapshotOnline(
|
||||
SnapshotPartidaOnline snapshot,
|
||||
) {
|
||||
final impostores = snapshot.impostores.isNotEmpty
|
||||
? snapshot.impostores.length
|
||||
: snapshot.jugadores.where((jugador) => jugador.esImpostor).length;
|
||||
return ResultadoPartidaGuardado(
|
||||
id: 'online-${snapshot.roomId ?? DateTime.now().microsecondsSinceEpoch}',
|
||||
fecha: DateTime.now(),
|
||||
modoMultimovil: true,
|
||||
jugadores: snapshot.jugadores.length,
|
||||
impostores: impostores,
|
||||
rondas: snapshot.ronda,
|
||||
ganador: snapshot.ganador ?? 'sin_resultado',
|
||||
palabra: snapshot.palabraSecreta ?? '',
|
||||
categoria: snapshot.categoria,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'fecha': fecha.toIso8601String(),
|
||||
@@ -99,6 +119,16 @@ class ServicioHistorialPartidas extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> guardarSnapshotOnline(SnapshotPartidaOnline snapshot) async {
|
||||
if (snapshot.ganador == null || snapshot.palabraSecreta == null) return;
|
||||
final guardado = ResultadoPartidaGuardado.desdeSnapshotOnline(snapshot);
|
||||
if (_partidas.any((partida) => partida.id == guardado.id)) return;
|
||||
_partidas.insert(0, guardado);
|
||||
if (_partidas.length > 100) _partidas.removeRange(100, _partidas.length);
|
||||
await _persistir();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> limpiar() async {
|
||||
_partidas.clear();
|
||||
await _persistir();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:nearby_connections/nearby_connections.dart';
|
||||
import '../modelos/inicio_partida_multijugador.dart';
|
||||
@@ -82,11 +83,14 @@ class ServicioNearby extends ChangeNotifier {
|
||||
String? _miClientId;
|
||||
String? _nombreSala;
|
||||
String? _miNombre;
|
||||
String? _miNick;
|
||||
String? _miAvatar;
|
||||
|
||||
final Map<String, JugadorConectado> _jugadores = {};
|
||||
final List<OnMensajeCallback> _listeners = [];
|
||||
final Map<String, String> _hostsEncontrados = {};
|
||||
final Map<String, Usuario> _usuariosPool = {};
|
||||
Timer? _heartbeatTimer;
|
||||
|
||||
String? _palabraRecibida;
|
||||
bool? _soyImpostor;
|
||||
@@ -273,8 +277,14 @@ class ServicioNearby extends ChangeNotifier {
|
||||
|
||||
// ==================== CLIENTE ====================
|
||||
|
||||
Future<bool> buscarHosts(String miNombre) async {
|
||||
Future<bool> buscarHosts(
|
||||
String miNombre, {
|
||||
String? miNick,
|
||||
String? miAvatar,
|
||||
}) async {
|
||||
_miNombre = miNombre;
|
||||
_miNick = miNick;
|
||||
_miAvatar = miAvatar;
|
||||
|
||||
try {
|
||||
final resultado = await Nearby().startDiscovery(
|
||||
@@ -297,7 +307,15 @@ class ServicioNearby extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> conectarAHost(String endpointId, String miNombre) async {
|
||||
Future<bool> conectarAHost(
|
||||
String endpointId,
|
||||
String miNombre, {
|
||||
String? miNick,
|
||||
String? miAvatar,
|
||||
}) async {
|
||||
_miNombre = miNombre;
|
||||
_miNick = miNick;
|
||||
_miAvatar = miAvatar;
|
||||
try {
|
||||
await Nearby().requestConnection(
|
||||
miNombre,
|
||||
@@ -332,11 +350,16 @@ class ServicioNearby extends ChangeNotifier {
|
||||
} else {
|
||||
_hostEndpointId = endpointId;
|
||||
_conectado = true;
|
||||
_iniciarHeartbeatCliente();
|
||||
enviarMensaje(
|
||||
endpointId,
|
||||
MensajeP2P(
|
||||
tipo: TipoMensaje.unirse,
|
||||
datos: {'nombre': _miNombre ?? 'Jugador'},
|
||||
datos: {
|
||||
'nombre': _miNombre ?? 'Jugador',
|
||||
if (_miNick != null) 'nick': _miNick,
|
||||
if (_miAvatar != null) 'avatar': _miAvatar,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -366,10 +389,30 @@ class ServicioNearby extends ChangeNotifier {
|
||||
} else {
|
||||
_conectado = false;
|
||||
_hostEndpointId = null;
|
||||
_heartbeatTimer?.cancel();
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
||||
void _iniciarHeartbeatCliente() {
|
||||
_heartbeatTimer?.cancel();
|
||||
_heartbeatTimer = Timer.periodic(const Duration(seconds: 5), (_) {
|
||||
final hostId = _hostEndpointId;
|
||||
if (!_esHost && _conectado && hostId != null) {
|
||||
enviarMensaje(
|
||||
hostId,
|
||||
MensajeP2P(
|
||||
tipo: TipoMensaje.ping,
|
||||
datos: {
|
||||
'heartbeat': true,
|
||||
if (_miClientId != null) 'clientId': _miClientId,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
void _onEndpointEncontrado(
|
||||
String endpointId,
|
||||
String endpointName,
|
||||
@@ -426,6 +469,10 @@ class ServicioNearby extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void _procesarMensajeHost(String endpointId, MensajeP2P mensaje) {
|
||||
final cliente = _estadoSala?.clientePorEndpoint(endpointId);
|
||||
if (cliente != null) {
|
||||
_estadoSala?.registrarActividadCliente(cliente.clientId);
|
||||
}
|
||||
switch (mensaje.tipo) {
|
||||
case TipoMensaje.unirse:
|
||||
_registrarClienteRemoto(endpointId, mensaje);
|
||||
@@ -466,6 +513,8 @@ class ServicioNearby extends ChangeNotifier {
|
||||
|
||||
void _registrarClienteRemoto(String endpointId, MensajeP2P mensaje) {
|
||||
final nombre = mensaje.datos['nombre'] as String? ?? 'Jugador';
|
||||
final nick = mensaje.datos['nick'] as String?;
|
||||
final avatar = mensaje.datos['avatar'] as String?;
|
||||
final clientId = endpointId;
|
||||
_jugadores[endpointId] = JugadorConectado(
|
||||
endpointId: endpointId,
|
||||
@@ -474,6 +523,12 @@ class ServicioNearby extends ChangeNotifier {
|
||||
_estadoSala?.registrarCliente(
|
||||
ClienteSala(clientId: clientId, endpointId: endpointId, nombre: nombre),
|
||||
);
|
||||
_crearUsuarioAutomaticoCliente(
|
||||
clientId: clientId,
|
||||
nombre: nombre,
|
||||
nick: nick,
|
||||
avatar: avatar,
|
||||
);
|
||||
|
||||
enviarMensaje(
|
||||
endpointId,
|
||||
@@ -495,6 +550,42 @@ class ServicioNearby extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _crearUsuarioAutomaticoCliente({
|
||||
required String clientId,
|
||||
required String nombre,
|
||||
String? nick,
|
||||
String? avatar,
|
||||
}) {
|
||||
final sala = _estadoSala;
|
||||
if (sala == null || sala.fase != FaseSalaMultijugador.lobby) return;
|
||||
|
||||
final yaTieneUsuario = sala.usuariosPorCliente(clientId).isNotEmpty;
|
||||
if (yaTieneUsuario) return;
|
||||
|
||||
final base = nombre.trim().isEmpty ? 'Jugador' : nombre.trim();
|
||||
var nombreFinal = base;
|
||||
var intento = 2;
|
||||
while (sala.usuarios.values.any(
|
||||
(u) => u.nombre.trim().toLowerCase() == nombreFinal.toLowerCase(),
|
||||
)) {
|
||||
nombreFinal = '$base ($intento)';
|
||||
intento++;
|
||||
}
|
||||
|
||||
final usuario = Usuario(
|
||||
id: 'u-${_roomId ?? DateTime.now().microsecondsSinceEpoch}-$clientId',
|
||||
nombre: nombreFinal,
|
||||
nick: nick,
|
||||
avatar: avatar,
|
||||
foto: avatar,
|
||||
creadoPorClienteId: clientId,
|
||||
);
|
||||
final resultadoCrear = sala.crearUsuario(usuario);
|
||||
if (!resultadoCrear.exitoso) return;
|
||||
sala.seleccionarUsuario(usuarioId: usuario.id, clienteId: clientId);
|
||||
_sincronizarPoolDesdeSala();
|
||||
}
|
||||
|
||||
void _handleUsuarioNuevo(MensajeP2P mensaje) {
|
||||
final usuarioJson = mensaje.datos['usuario'] as Map<String, dynamic>?;
|
||||
if (usuarioJson != null) {
|
||||
@@ -776,6 +867,22 @@ class ServicioNearby extends ChangeNotifier {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> asumirUsuariosDesconectados() async {
|
||||
final sala = _estadoSala;
|
||||
if (!_esHost || sala == null) return;
|
||||
var huboCambios = false;
|
||||
for (final cliente in sala.clientesDesconectados) {
|
||||
final reasignados = sala.reasignarUsuariosDeCliente(
|
||||
clientIdOrigen: cliente.clientId,
|
||||
clientIdDestino: sala.hostClientId,
|
||||
);
|
||||
huboCambios = huboCambios || reasignados > 0;
|
||||
}
|
||||
if (huboCambios) {
|
||||
await _broadcastEstadoSala();
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== HOST: ACCIONES DE JUEGO ====================
|
||||
|
||||
Future<void> enviarInicioPartida({
|
||||
@@ -851,6 +958,7 @@ class ServicioNearby extends ChangeNotifier {
|
||||
// ==================== LIMPIEZA ====================
|
||||
|
||||
Future<void> desconectar() async {
|
||||
_heartbeatTimer?.cancel();
|
||||
try {
|
||||
await Nearby().stopAllEndpoints();
|
||||
if (_anunciando) await Nearby().stopAdvertising();
|
||||
@@ -869,6 +977,8 @@ class ServicioNearby extends ChangeNotifier {
|
||||
_miClientId = null;
|
||||
_nombreSala = null;
|
||||
_miNombre = null;
|
||||
_miNick = null;
|
||||
_miAvatar = null;
|
||||
_palabraRecibida = null;
|
||||
_soyImpostor = null;
|
||||
_faseActual = null;
|
||||
@@ -877,6 +987,7 @@ class ServicioNearby extends ChangeNotifier {
|
||||
_jugadores.clear();
|
||||
_hostsEncontrados.clear();
|
||||
_usuariosPool.clear();
|
||||
_heartbeatTimer = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@@ -901,6 +1012,7 @@ class ServicioNearby extends ChangeNotifier {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_heartbeatTimer?.cancel();
|
||||
desconectar();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user