nao sei
This commit is contained in:
157
SYNC_CHANGES_SUMMARY.md
Normal file
157
SYNC_CHANGES_SUMMARY.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# Resumo das Mudanças - Sincronização de Jogo em Tempo Real
|
||||
|
||||
## 1. lib/controllers/placar_controller.dart
|
||||
|
||||
### Adicionado ao constructor:
|
||||
```dart
|
||||
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:
|
||||
```dart
|
||||
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:**
|
||||
```dart
|
||||
_dispatchSyncAction('commit_stat', {
|
||||
'action': action,
|
||||
'player_data': playerData,
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. lib/pages/PlacarPage.dart
|
||||
|
||||
### Adicionado ao state:
|
||||
```dart
|
||||
String? _lastAppliedSyncEventId; // ← NOVO - deduplicação de eventos
|
||||
```
|
||||
|
||||
### Constructor do controller:
|
||||
```dart
|
||||
_controller = PlacarController(
|
||||
gameId: widget.gameId,
|
||||
myTeam: widget.myTeam,
|
||||
opponentTeam: widget.opponentTeam,
|
||||
onSyncAction: _onLocalControllerSync, // ← CONECTADO
|
||||
);
|
||||
```
|
||||
|
||||
### Adicionado novo método _onLocalControllerSync:
|
||||
```dart
|
||||
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):
|
||||
```dart
|
||||
_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):
|
||||
```dart
|
||||
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
|
||||
|
||||
1. **Ação Local** → `commitStat()` no controller
|
||||
2. **Controller emite** → `_dispatchSyncAction('commit_stat', {action, player_data, remaining_seconds, is_running})`
|
||||
3. **PlacarPage escuta** → `_onLocalControllerSync()` recebe o evento
|
||||
4. **Envia ao Supabase** → `sendSyncEvent()` armazena em `game_sync_events`
|
||||
5. **Parceiro recebe** → `listenToGameSyncOthers()` retorna o evento
|
||||
6. **Aplica remotamente** → `_applyRemoteSyncEvent()` executa a ação no parceiro
|
||||
7. **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
|
||||
Reference in New Issue
Block a user