Files
farolero/lib/servicios/servicio_nearby.dart

879 lines
25 KiB
Dart

import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:nearby_connections/nearby_connections.dart';
import '../modelos/inicio_partida_multijugador.dart';
import '../modelos/sala_multijugador.dart';
import '../modelos/usuario.dart';
/// Tipos de mensajes en el protocolo P2P.
enum TipoMensaje {
salaInfo,
partidaInicio,
fase,
votacionResultado,
partidaFin,
unirse,
voto,
listo,
ping,
jugadorDesconectado,
clienteRegistrado,
estadoSala,
crearUsuario,
seleccionarUsuario,
liberarUsuario,
eliminarUsuario,
errorOperacion,
usuarioNuevo,
// Compatibilidad con versiones previas del protocolo.
usuarioEliminado,
usuariosActualizados,
}
/// Mensaje del protocolo P2P entre dispositivos.
class MensajeP2P {
final TipoMensaje tipo;
final Map<String, dynamic> datos;
MensajeP2P({required this.tipo, required this.datos});
String toJson() => json.encode({'tipo': tipo.name, 'datos': datos});
factory MensajeP2P.fromJson(String jsonStr) {
final mapa = json.decode(jsonStr) as Map<String, dynamic>;
return MensajeP2P(
tipo: TipoMensaje.values.firstWhere((t) => t.name == mapa['tipo']),
datos: mapa['datos'] as Map<String, dynamic>,
);
}
Uint8List toBytes() => Uint8List.fromList(utf8.encode(toJson()));
}
/// Info de un dispositivo conectado. El nombre identifica al cliente/dispositivo,
/// no necesariamente a un jugador de la partida.
class JugadorConectado {
final String endpointId;
final String nombre;
bool listo;
JugadorConectado({
required this.endpointId,
required this.nombre,
this.listo = false,
});
}
typedef OnMensajeCallback =
void Function(String endpointId, MensajeP2P mensaje);
/// Servicio para conexiones P2P usando Google Nearby Connections API.
class ServicioNearby extends ChangeNotifier {
static const _serviceId = 'es.freetimelab.farolero';
static const _hostClientId = 'host';
bool _esHost = false;
bool _conectado = false;
bool _buscando = false;
bool _anunciando = false;
String? _miEndpointId;
String? _hostEndpointId;
String? _roomId;
String? _miClientId;
String? _nombreSala;
String? _miNombre;
final Map<String, JugadorConectado> _jugadores = {};
final List<OnMensajeCallback> _listeners = [];
final Map<String, String> _hostsEncontrados = {};
final Map<String, Usuario> _usuariosPool = {};
String? _palabraRecibida;
bool? _soyImpostor;
String? _faseActual;
Map<String, dynamic>? _datosPartida;
EstadoSalaMultijugador? _estadoSala;
bool get esHost => _esHost;
bool get conectado => _conectado;
bool get buscando => _buscando;
bool get anunciando => _anunciando;
String? get miEndpointId => _miEndpointId;
String? get hostEndpointId => _hostEndpointId;
String? get roomId => _roomId;
String? get miClientId => _miClientId;
String? get nombreSala => _nombreSala;
String? get miNombre => _miNombre;
String? get palabraRecibida => _palabraRecibida;
bool? get soyImpostor => _soyImpostor;
String? get faseActual => _faseActual;
Map<String, dynamic>? get datosPartida => _datosPartida;
EstadoSalaMultijugador? get estadoSala => _estadoSala;
List<JugadorConectado> get jugadores => _jugadores.values.toList();
int get numJugadoresConectados => _jugadores.length;
Map<String, String> get hostsEncontrados =>
Map.unmodifiable(_hostsEncontrados);
List<Usuario> get usuarios =>
(_estadoSala?.usuarios.values ?? _usuariosPool.values).toList();
List<Usuario> get misUsuariosSeleccionados {
final clientId = _miClientId;
final sala = _estadoSala;
if (clientId == null || sala == null) return [];
return sala.usuariosPorCliente(clientId);
}
void onMensaje(OnMensajeCallback callback) {
_listeners.add(callback);
}
void removeMensajeListener(OnMensajeCallback callback) {
_listeners.remove(callback);
}
void _notificarMensaje(String endpointId, MensajeP2P mensaje) {
for (final listener in _listeners) {
listener(endpointId, mensaje);
}
}
// ==================== USER POOL / SALA ====================
void agregarUsuario(Usuario usuario) {
_usuariosPool[usuario.id] = usuario;
_estadoSala?.usuarios[usuario.id] = usuario;
notifyListeners();
}
void eliminarUsuario(String usuarioId) {
_usuariosPool.remove(usuarioId);
_estadoSala?.usuarios.remove(usuarioId);
notifyListeners();
}
Usuario? getUsuario(String usuarioId) {
return _estadoSala?.usuarios[usuarioId] ?? _usuariosPool[usuarioId];
}
void sincronizarUsuarios(List<Usuario> usuarios) {
_usuariosPool.clear();
for (final usuario in usuarios) {
_usuariosPool[usuario.id] = usuario;
}
notifyListeners();
}
void _sincronizarSala(EstadoSalaMultijugador sala) {
_estadoSala = sala;
_roomId = sala.roomId;
_usuariosPool
..clear()
..addEntries(sala.usuarios.entries);
}
void _sincronizarPoolDesdeSala() {
final sala = _estadoSala;
if (sala == null) return;
_usuariosPool
..clear()
..addEntries(sala.usuarios.entries);
}
Future<void> _broadcastEstadoSala() async {
final sala = _estadoSala;
if (sala == null) return;
_sincronizarPoolDesdeSala();
if (_esHost) {
await enviarATodos(
MensajeP2P(tipo: TipoMensaje.estadoSala, datos: sala.toJson()),
);
}
notifyListeners();
}
JugadorConectado? getJugadorLocal() {
if (_miNombre == null) return null;
return JugadorConectado(
endpointId: _miEndpointId ?? '',
nombre: _miNombre!,
listo: true,
);
}
// ==================== HOST ====================
Future<bool> iniciarHost(String nombreSala, String miNombre) async {
_nombreSala = nombreSala;
_miNombre = miNombre;
_roomId = DateTime.now().microsecondsSinceEpoch.toString();
_miClientId = _hostClientId;
_estadoSala = EstadoSalaMultijugador.crear(
roomId: _roomId!,
nombreSala: nombreSala,
hostClientId: _hostClientId,
hostNombre: miNombre,
);
// Compatibilidad con el flujo actual: el nombre con el que se crea la sala
// arranca como usuario seleccionado por el host. Luego puede crear/seleccionar
// más usuarios en el lobby.
final usuarioHost = Usuario(
id: 'u-${_roomId!}-host',
nombre: miNombre,
creadoPorClienteId: _hostClientId,
clienteIdSeleccionado: _hostClientId,
);
_estadoSala!.crearUsuario(usuarioHost);
_sincronizarPoolDesdeSala();
try {
final resultado = await Nearby().startAdvertising(
miNombre,
Strategy.P2P_STAR,
onConnectionInitiated: _onConexionIniciada,
onConnectionResult: _onResultadoConexion,
onDisconnected: _onDesconexion,
serviceId: _serviceId,
);
if (resultado) {
_esHost = true;
_anunciando = true;
_conectado = true;
notifyListeners();
return true;
}
return false;
} catch (e) {
debugPrint('Error iniciando host: $e');
return false;
}
}
// ==================== CLIENTE ====================
Future<bool> buscarHosts(String miNombre) async {
_miNombre = miNombre;
try {
final resultado = await Nearby().startDiscovery(
miNombre,
Strategy.P2P_STAR,
onEndpointFound: _onEndpointEncontrado,
onEndpointLost: _onEndpointPerdido,
serviceId: _serviceId,
);
if (resultado) {
_buscando = true;
notifyListeners();
return true;
}
return false;
} catch (e) {
debugPrint('Error buscando hosts: $e');
return false;
}
}
Future<bool> conectarAHost(String endpointId, String miNombre) async {
try {
await Nearby().requestConnection(
miNombre,
endpointId,
onConnectionInitiated: _onConexionIniciada,
onConnectionResult: _onResultadoConexion,
onDisconnected: _onDesconexion,
);
return true;
} catch (e) {
debugPrint('Error conectando a host: $e');
return false;
}
}
// ==================== CALLBACKS NEARBY ====================
void _onConexionIniciada(String endpointId, ConnectionInfo info) {
debugPrint('Conexion iniciada con $endpointId: ${info.endpointName}');
Nearby().acceptConnection(
endpointId,
onPayLoadRecieved: _onPayloadRecibido,
onPayloadTransferUpdate: _onPayloadUpdate,
);
}
void _onResultadoConexion(String endpointId, Status status) {
debugPrint('Resultado conexion $endpointId: $status');
if (status == Status.CONNECTED) {
if (_esHost) {
debugPrint('Cliente conectado: $endpointId');
} else {
_hostEndpointId = endpointId;
_conectado = true;
enviarMensaje(
endpointId,
MensajeP2P(
tipo: TipoMensaje.unirse,
datos: {'nombre': _miNombre ?? 'Jugador'},
),
);
}
notifyListeners();
} else {
debugPrint('Conexion fallida con $endpointId');
}
}
void _onDesconexion(String endpointId) {
debugPrint('Desconexion: $endpointId');
if (_esHost) {
final jugador = _jugadores.remove(endpointId);
final cliente = _estadoSala?.clientePorEndpoint(endpointId);
if (cliente != null) {
_estadoSala?.desconectarCliente(cliente.clientId);
_broadcastEstadoSala();
}
if (jugador != null) {
enviarATodos(
MensajeP2P(
tipo: TipoMensaje.jugadorDesconectado,
datos: {'nombre': jugador.nombre, 'endpointId': endpointId},
),
);
}
} else {
_conectado = false;
_hostEndpointId = null;
}
notifyListeners();
}
void _onEndpointEncontrado(
String endpointId,
String endpointName,
String serviceId,
) {
debugPrint('Host encontrado: $endpointName ($endpointId)');
_hostsEncontrados[endpointId] = endpointName;
notifyListeners();
}
void _onEndpointPerdido(String? endpointId) {
debugPrint('Endpoint perdido: $endpointId');
if (endpointId != null) {
_hostsEncontrados.remove(endpointId);
notifyListeners();
}
}
Future<void> pararBusqueda() async {
try {
await Nearby().stopDiscovery();
} catch (_) {}
_buscando = false;
_hostsEncontrados.clear();
notifyListeners();
}
void _onPayloadRecibido(String endpointId, Payload payload) {
if (payload.type == PayloadType.BYTES && payload.bytes != null) {
try {
final jsonStr = utf8.decode(payload.bytes!);
final mensaje = MensajeP2P.fromJson(jsonStr);
_procesarMensaje(endpointId, mensaje);
} catch (e) {
debugPrint('Error procesando payload: $e');
}
}
}
void _onPayloadUpdate(String endpointId, PayloadTransferUpdate update) {}
// ==================== PROCESAMIENTO DE MENSAJES ====================
void _procesarMensaje(String endpointId, MensajeP2P mensaje) {
debugPrint('Mensaje de $endpointId: ${mensaje.tipo.name}');
if (_esHost) {
_procesarMensajeHost(endpointId, mensaje);
} else {
_procesarMensajeCliente(endpointId, mensaje);
}
_notificarMensaje(endpointId, mensaje);
}
void _procesarMensajeHost(String endpointId, MensajeP2P mensaje) {
switch (mensaje.tipo) {
case TipoMensaje.unirse:
_registrarClienteRemoto(endpointId, mensaje);
break;
case TipoMensaje.voto:
_notificarMensaje(endpointId, mensaje);
break;
case TipoMensaje.listo:
final jugador = _jugadores[endpointId];
if (jugador != null) {
jugador.listo = true;
notifyListeners();
}
break;
case TipoMensaje.crearUsuario:
_handleCrearUsuario(endpointId, mensaje);
break;
case TipoMensaje.seleccionarUsuario:
_handleSeleccionarUsuario(endpointId, mensaje);
break;
case TipoMensaje.liberarUsuario:
_handleLiberarUsuario(endpointId, mensaje);
break;
case TipoMensaje.eliminarUsuario:
case TipoMensaje.usuarioEliminado:
_handleEliminarUsuario(endpointId, mensaje);
break;
case TipoMensaje.usuarioNuevo:
_handleUsuarioNuevo(mensaje);
break;
case TipoMensaje.usuariosActualizados:
_handleUsuariosActualizados(mensaje);
break;
default:
break;
}
}
void _registrarClienteRemoto(String endpointId, MensajeP2P mensaje) {
final nombre = mensaje.datos['nombre'] as String? ?? 'Jugador';
final clientId = endpointId;
_jugadores[endpointId] = JugadorConectado(
endpointId: endpointId,
nombre: nombre,
);
_estadoSala?.registrarCliente(
ClienteSala(clientId: clientId, endpointId: endpointId, nombre: nombre),
);
enviarMensaje(
endpointId,
MensajeP2P(
tipo: TipoMensaje.clienteRegistrado,
datos: {
'clientId': clientId,
'sala': _nombreSala,
'roomId': _roomId,
'jugadores': _jugadores.values
.map((j) => {'nombre': j.nombre, 'endpointId': j.endpointId})
.toList(),
'usuarios': _usuariosPool.values.map((u) => u.toJson()).toList(),
if (_estadoSala != null) 'estadoSala': _estadoSala!.toJson(),
},
),
);
_broadcastEstadoSala();
notifyListeners();
}
void _handleUsuarioNuevo(MensajeP2P mensaje) {
final usuarioJson = mensaje.datos['usuario'] as Map<String, dynamic>?;
if (usuarioJson != null) {
final nuevoUsuario = Usuario.fromJson(usuarioJson);
_usuariosPool[nuevoUsuario.id] = nuevoUsuario;
_estadoSala?.usuarios[nuevoUsuario.id] = nuevoUsuario;
if (_esHost) {
_broadcastEstadoSala();
}
notifyListeners();
}
}
void _handleUsuariosActualizados(MensajeP2P mensaje) {
final usuariosJson = mensaje.datos['usuarios'] as List<dynamic>?;
if (usuariosJson != null) {
_usuariosPool.clear();
for (final u in usuariosJson) {
final usuario = Usuario.fromJson(u as Map<String, dynamic>);
_usuariosPool[usuario.id] = usuario;
}
notifyListeners();
}
}
String _clientIdParaEndpoint(String endpointId, MensajeP2P mensaje) {
return mensaje.datos['clientId'] as String? ??
_estadoSala?.clientePorEndpoint(endpointId)?.clientId ??
endpointId;
}
Future<void> _enviarErrorOperacion(
String endpointId,
ResultadoOperacionSala resultado,
) async {
if (resultado.exitoso) return;
await enviarMensaje(
endpointId,
MensajeP2P(tipo: TipoMensaje.errorOperacion, datos: resultado.toJson()),
);
}
void _handleCrearUsuario(String endpointId, MensajeP2P mensaje) {
final sala = _estadoSala;
final usuarioJson = mensaje.datos['usuario'] as Map<String, dynamic>?;
if (sala == null || usuarioJson == null) return;
final clientId = _clientIdParaEndpoint(endpointId, mensaje);
final usuario = Usuario.fromJson(usuarioJson).copiar(
creadoPorClienteId: clientId,
liberarSeleccion: true,
);
final resultadoCrear = sala.crearUsuario(usuario);
if (!resultadoCrear.exitoso) {
_enviarErrorOperacion(endpointId, resultadoCrear);
return;
}
if (mensaje.datos['seleccionar'] == true) {
final resultadoSeleccion = sala.seleccionarUsuario(
usuarioId: usuario.id,
clienteId: clientId,
);
_enviarErrorOperacion(endpointId, resultadoSeleccion);
}
_broadcastEstadoSala();
}
void _handleSeleccionarUsuario(String endpointId, MensajeP2P mensaje) {
final sala = _estadoSala;
final usuarioId = mensaje.datos['usuarioId'] as String?;
if (sala == null || usuarioId == null) return;
final resultado = sala.seleccionarUsuario(
usuarioId: usuarioId,
clienteId: _clientIdParaEndpoint(endpointId, mensaje),
);
_enviarErrorOperacion(endpointId, resultado);
_broadcastEstadoSala();
}
void _handleLiberarUsuario(String endpointId, MensajeP2P mensaje) {
final sala = _estadoSala;
final usuarioId = mensaje.datos['usuarioId'] as String?;
if (sala == null || usuarioId == null) return;
final resultado = sala.liberarUsuario(
usuarioId: usuarioId,
solicitanteClientId: _clientIdParaEndpoint(endpointId, mensaje),
);
_enviarErrorOperacion(endpointId, resultado);
_broadcastEstadoSala();
}
void _handleEliminarUsuario(String endpointId, MensajeP2P mensaje) {
final sala = _estadoSala;
final usuarioId = mensaje.datos['usuarioId'] as String?;
if (sala == null || usuarioId == null) return;
final resultado = sala.eliminarUsuario(
usuarioId: usuarioId,
solicitanteClientId: _clientIdParaEndpoint(endpointId, mensaje),
);
_enviarErrorOperacion(endpointId, resultado);
_broadcastEstadoSala();
}
void _procesarMensajeCliente(String endpointId, MensajeP2P mensaje) {
switch (mensaje.tipo) {
case TipoMensaje.salaInfo:
_datosPartida = mensaje.datos;
final usuariosData = mensaje.datos['usuarios'] as List<dynamic>?;
if (usuariosData != null) {
_usuariosPool.clear();
for (final u in usuariosData) {
final usuario = Usuario.fromJson(u as Map<String, dynamic>);
_usuariosPool[usuario.id] = usuario;
}
}
notifyListeners();
break;
case TipoMensaje.clienteRegistrado:
_miClientId = mensaje.datos['clientId'] as String?;
_datosPartida = mensaje.datos;
final estadoSalaJson =
mensaje.datos['estadoSala'] as Map<String, dynamic>?;
if (estadoSalaJson != null) {
_sincronizarSala(EstadoSalaMultijugador.fromJson(estadoSalaJson));
}
notifyListeners();
break;
case TipoMensaje.estadoSala:
_sincronizarSala(EstadoSalaMultijugador.fromJson(mensaje.datos));
notifyListeners();
break;
case TipoMensaje.partidaInicio:
final jugadoresInicio = mensaje.datos['jugadores'] as List<dynamic>?;
if (jugadoresInicio != null && jugadoresInicio.isNotEmpty) {
final primerJugador = jugadoresInicio.first as Map<String, dynamic>;
_palabraRecibida = primerJugador['palabra'] as String?;
_soyImpostor = primerJugador['esImpostor'] as bool? ?? false;
} else {
_palabraRecibida = mensaje.datos['palabra'] as String?;
_soyImpostor = mensaje.datos['esImpostor'] as bool? ?? false;
}
_datosPartida = mensaje.datos;
notifyListeners();
break;
case TipoMensaje.fase:
_faseActual = mensaje.datos['fase'] as String?;
_datosPartida = mensaje.datos;
notifyListeners();
break;
case TipoMensaje.votacionResultado:
case TipoMensaje.partidaFin:
case TipoMensaje.errorOperacion:
_datosPartida = mensaje.datos;
notifyListeners();
break;
case TipoMensaje.jugadorDesconectado:
notifyListeners();
break;
default:
break;
}
}
// ==================== ENVIO ====================
Future<void> enviarMensaje(String endpointId, MensajeP2P mensaje) async {
try {
await Nearby().sendBytesPayload(endpointId, mensaje.toBytes());
} catch (e) {
debugPrint('Error enviando a $endpointId: $e');
}
}
Future<void> enviarATodos(MensajeP2P mensaje) async {
for (final id in _jugadores.keys) {
await enviarMensaje(id, mensaje);
}
}
Future<void> crearUsuarioSala(String nombre, {bool seleccionar = true}) async {
final nombreLimpio = nombre.trim();
if (nombreLimpio.isEmpty) return;
final clientId = _miClientId;
final usuario = Usuario(
id: 'u-${DateTime.now().microsecondsSinceEpoch}',
nombre: nombreLimpio,
creadoPorClienteId: clientId,
);
if (_esHost && _estadoSala != null && clientId != null) {
final resultado = _estadoSala!.crearUsuario(usuario);
if (resultado.exitoso && seleccionar) {
_estadoSala!.seleccionarUsuario(
usuarioId: usuario.id,
clienteId: clientId,
);
}
await _broadcastEstadoSala();
return;
}
final hostId = _hostEndpointId;
if (hostId == null) return;
await enviarMensaje(
hostId,
MensajeP2P(
tipo: TipoMensaje.crearUsuario,
datos: {
'clientId': clientId,
'seleccionar': seleccionar,
'usuario': usuario.toJson(),
},
),
);
}
Future<void> seleccionarUsuarioSala(String usuarioId) async {
final clientId = _miClientId;
if (_esHost && _estadoSala != null && clientId != null) {
_estadoSala!.seleccionarUsuario(usuarioId: usuarioId, clienteId: clientId);
await _broadcastEstadoSala();
return;
}
final hostId = _hostEndpointId;
if (hostId == null) return;
await enviarMensaje(
hostId,
MensajeP2P(
tipo: TipoMensaje.seleccionarUsuario,
datos: {'clientId': clientId, 'usuarioId': usuarioId},
),
);
}
Future<void> liberarUsuarioSala(String usuarioId) async {
final clientId = _miClientId;
if (_esHost && _estadoSala != null && clientId != null) {
_estadoSala!.liberarUsuario(
usuarioId: usuarioId,
solicitanteClientId: clientId,
);
await _broadcastEstadoSala();
return;
}
final hostId = _hostEndpointId;
if (hostId == null) return;
await enviarMensaje(
hostId,
MensajeP2P(
tipo: TipoMensaje.liberarUsuario,
datos: {'clientId': clientId, 'usuarioId': usuarioId},
),
);
}
Future<void> eliminarUsuarioSala(String usuarioId) async {
final clientId = _miClientId;
if (_esHost && _estadoSala != null && clientId != null) {
_estadoSala!.eliminarUsuario(
usuarioId: usuarioId,
solicitanteClientId: clientId,
);
await _broadcastEstadoSala();
return;
}
final hostId = _hostEndpointId;
if (hostId == null) return;
await enviarMensaje(
hostId,
MensajeP2P(
tipo: TipoMensaje.eliminarUsuario,
datos: {'clientId': clientId, 'usuarioId': usuarioId},
),
);
}
// ==================== HOST: ACCIONES DE JUEGO ====================
Future<void> enviarInicioPartida({
required String palabraSecreta,
required String categoria,
required Map<String, bool> impostores,
}) async {
for (final entry in _jugadores.entries) {
final esImpostor = impostores[entry.key] ?? false;
await enviarMensaje(
entry.key,
MensajeP2P(
tipo: TipoMensaje.partidaInicio,
datos: {
'palabra': esImpostor ? null : palabraSecreta,
'esImpostor': esImpostor,
'categoria': categoria,
'numJugadores': _jugadores.length + 1,
},
),
);
}
}
Future<void> enviarInicioPartidaMulti({
required List<AsignacionJugador> asignaciones,
required String palabraSecreta,
required String categoria,
required Map<String, bool> impostoresPorJugadorId,
required List<Map<String, dynamic>> jugadoresTodos,
}) async {
final payloads = InicioPartidaMultijugador.crearPayloadsPorCliente(
asignaciones: asignaciones,
palabraSecreta: palabraSecreta,
categoria: categoria,
impostoresPorJugadorId: impostoresPorJugadorId,
);
for (final payload in payloads.values) {
final endpointId = payload.endpointId;
if (endpointId == null) continue;
final datos = payload.toJson();
datos['jugadoresTodos'] = jugadoresTodos;
await enviarMensaje(
endpointId,
MensajeP2P(tipo: TipoMensaje.partidaInicio, datos: datos),
);
}
}
Future<void> enviarCambioFase(
String fase, [
Map<String, dynamic>? extra,
]) async {
final datos = {'fase': fase, ...?extra};
await enviarATodos(MensajeP2P(tipo: TipoMensaje.fase, datos: datos));
}
Future<void> enviarResultadoVotacion(Map<String, dynamic> resultado) async {
await enviarATodos(
MensajeP2P(tipo: TipoMensaje.votacionResultado, datos: resultado),
);
}
Future<void> enviarFinPartida(Map<String, dynamic> resultado) async {
await enviarATodos(
MensajeP2P(tipo: TipoMensaje.partidaFin, datos: resultado),
);
}
// ==================== LIMPIEZA ====================
Future<void> desconectar() async {
try {
await Nearby().stopAllEndpoints();
if (_anunciando) await Nearby().stopAdvertising();
if (_buscando) await Nearby().stopDiscovery();
} catch (e) {
debugPrint('Error desconectando: $e');
}
_esHost = false;
_conectado = false;
_buscando = false;
_anunciando = false;
_miEndpointId = null;
_hostEndpointId = null;
_roomId = null;
_miClientId = null;
_nombreSala = null;
_miNombre = null;
_palabraRecibida = null;
_soyImpostor = null;
_faseActual = null;
_datosPartida = null;
_estadoSala = null;
_jugadores.clear();
_hostsEncontrados.clear();
_usuariosPool.clear();
notifyListeners();
}
String generarDatosQR(String nombreSala) {
return json.encode({
'app': 'farolero',
'sala': nombreSala,
'host': _miNombre,
'roomId': _roomId,
});
}
static Map<String, dynamic>? parsearQR(String datos) {
try {
final mapa = json.decode(datos) as Map<String, dynamic>;
if (mapa['app'] == 'farolero') return mapa;
return null;
} catch (_) {
return null;
}
}
@override
void dispose() {
desconectar();
super.dispose();
}
}