4.7 KiB
4.7 KiB
Resumo das Mudanças - Sincronização de Jogo em Tempo Real
1. lib/controllers/placar_controller.dart
Adicionado ao constructor:
final void Function(String actionType, Map<String, dynamic> actionData)? onSyncAction;
PlacarController({
required this.gameId,
required this.myTeam,
required this.opponentTeam,
this.onSyncAction, // ← NOVO
});
Adicionado método _dispatchSyncAction:
void _dispatchSyncAction(String actionType, Map<String, dynamic> actionData) {
if (onSyncAction != null) {
final enrichedActionData = Map<String, dynamic>.from(actionData)
..['remaining_seconds'] = durationNotifier.value.inSeconds
..['is_running'] = isRunning;
onSyncAction!(actionType, enrichedActionData);
}
}
Adicionado em 5 métodos (chamada _dispatchSyncAction):
useTimeout()→ dispatch'use_timeout'handleSubbing()→ dispatch'subbing'swapCourtPlayers()→ dispatch'swap_players'registerFoul()→ dispatch'register_foul'commitStat()→ dispatch'commit_stat'
Exemplo em commitStat:
_dispatchSyncAction('commit_stat', {
'action': action,
'player_data': playerData,
});
2. lib/pages/PlacarPage.dart
Adicionado ao state:
String? _lastAppliedSyncEventId; // ← NOVO - deduplicação de eventos
Constructor do controller:
_controller = PlacarController(
gameId: widget.gameId,
myTeam: widget.myTeam,
opponentTeam: widget.opponentTeam,
onSyncAction: _onLocalControllerSync, // ← CONECTADO
);
Adicionado novo método _onLocalControllerSync:
void _onLocalControllerSync(String actionType, Map<String, dynamic> actionData) {
if (_sessionId == null || _isApplyingRemoteSync) return;
print("📤 Enviando sync action local: $actionType -> $actionData");
_sharingController.sendSyncEvent(_sessionId!, actionType, actionData);
}
Atualizado _setupSyncListener (deduplicação):
_syncSubscription = _sharingController.listenToGameSyncOthers(_sessionId!).listen(
(dynamic event) {
Map<String, dynamic>? record;
if (event is List && event.isNotEmpty) {
for (final item in event) {
final row = item as Map<String, dynamic>?;
if (row == null) continue;
final rowId = row['id']?.toString();
if (rowId != null && rowId != _lastAppliedSyncEventId) {
record = row;
break; // ← para no primeiro evento novo
}
}
} else if (event is Map<String, dynamic>) {
record = Map<String, dynamic>.from(event);
}
if (record != null) {
final recordId = record['id']?.toString();
if (recordId != null && recordId == _lastAppliedSyncEventId) return;
if (recordId != null) _lastAppliedSyncEventId = recordId;
_applyRemoteSyncEvent(record);
}
},
);
Atualizado _applyRemoteSyncEvent (aplicar estado remoto):
void _applyRemoteSyncEvent(Map<String, dynamic> record) {
final actionType = record['action_type']?.toString();
final actionData = Map<String, dynamic>.from(record['action_data'] ?? {});
// ← NOVO: aplicar timer remotamente em TODAS as ações
final remoteSeconds = int.tryParse(actionData['remaining_seconds']?.toString() ?? '');
final remoteIsRunning = actionData['is_running'] == true;
if (remoteSeconds != null) {
_controller.durationNotifier.value = Duration(seconds: remoteSeconds);
}
if (remoteIsRunning != _controller.isRunning) {
_isApplyingRemoteSync = true;
_controller.toggleTimer(context);
_isApplyingRemoteSync = false;
}
// Aplicar ações específicas
if (actionType == 'toggle_timer') {
setState(() {});
} else if (actionType == 'commit_stat') {
// aplicar pontos/faltas
} else if (actionType == 'register_foul') {
// aplicar falta
} else if (actionType == 'subbing') {
// aplicar substituição
} else if (actionType == 'swap_players') {
// trocar posição
} else if (actionType == 'use_timeout') {
// usar timeout
}
}
Fluxo Completo
- Ação Local →
commitStat()no controller - Controller emite →
_dispatchSyncAction('commit_stat', {action, player_data, remaining_seconds, is_running}) - PlacarPage escuta →
_onLocalControllerSync()recebe o evento - Envia ao Supabase →
sendSyncEvent()armazena emgame_sync_events - Parceiro recebe →
listenToGameSyncOthers()retorna o evento - Aplica remotamente →
_applyRemoteSyncEvent()executa a ação no parceiro - Estado sincronizado → Ambos têm timer, pontos, faltas idênticos
Resultado
✅ Timer não reseta ao marcar ponto ✅ Pontos sincronizam entre os dois lados ✅ Faltas sincronizam ✅ Timeouts sincronizam ✅ Substituições sincronizam ✅ Posições de jogadores sincronizam