import 'backend.dart'; /// 서버 시각 — 단말 시계와 서버 시계의 차이를 한 번 재어 앱 전체가 같이 쓴다. /// /// 타임어택 창(`quests.activeFrom`~`activeTo`)은 서버 시각이다. 단말 시계로 세면 시계를 /// 돌린 단말에서 마감이 늘어나 보인다. 판정은 서버가 다시 하므로 이것은 표시용이다. class ServerClock { ServerClock._(); /// 서버 − 단말. 재지 못했으면 0 이다(단말 시계 그대로). static Duration offset = Duration.zero; static Future? _synced; static DateTime now() => DateTime.now().add(offset); /// 세션당 한 번 `fnServerTime` 을 부른다. 실패하면 단말 시계로 두고 넘어간다. static Future ensure(Backend backend) => _synced ??= _sync(backend); static Future _sync(Backend backend) async { try { final sent = DateTime.now(); final reply = await backend.call('fnServerTime'); final received = DateTime.now(); final server = reply['now']; if (server is! num) return; // 왕복의 가운데를 서버가 답한 시각으로 본다. final middle = sent.add(received.difference(sent) ~/ 2); offset = DateTime.fromMillisecondsSinceEpoch(server.round()).difference(middle); } catch (_) { _synced = null; // 다음 기회에 다시 잰다. } } /// 테스트가 회차마다 처음 상태로 돌린다. static void reset() { offset = Duration.zero; _synced = null; } }