| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- import 'dart:async';
- import 'dart:ui';
- import 'package:fixnum/fixnum.dart';
- import 'package:get/get.dart';
- import 'package:grpc/grpc.dart';
- import 'package:trackoffical_app/model/game_map.dart';
- import 'package:trackoffical_app/service/api.dart';
- import 'package:trackoffical_app/service/app_map.dart';
- import 'package:trackoffical_app/service/game/game_instance_std/plug_sport_wear.dart';
- import 'plug_location.dart';
- import 'plug_orientation.dart';
- import 'package:wakelock/wakelock.dart';
- import '../../../model.dart';
- import '../rule.dart';
- import '../rule_in_order.dart';
- import 'package:trackoffical_app/utils.dart';
- import '../../../logger.dart';
- import '../../../model/game_person_data.dart';
- import '../../app.dart';
- import '../../database.dart';
- import '../game_instance.dart';
- import 'package:trackoffical_app/pb.dart' as pb;
- export '../../../model.dart';
- import 'package:sensor/sensor.dart' as sensor;
- class GameInstanceStd extends GameInstance {
- GameInstanceStd({
- required GameState gameState,
- }) {
- model.gameSrcState.value = gameState;
- }
- void gameGiveUp() {
- _isGiveUp = true;
- stop();
- }
- _setStartAt(DateTime? time) {
- if (time != null) {
- gameState.pbGameSave.startAt = time.toPb();
- } else {
- gameState.pbGameSave.clearStartAt();
- }
- model.gameSrcState.refresh();
- }
- _setEndAt(DateTime? time) {
- if (time != null) {
- gameState.pbGameSave.stopAt = time.toPb();
- } else {
- gameState.pbGameSave.clearStopAt();
- }
- model.gameSrcState.refresh();
- }
- void _gameStopSetValues({DateTime? now}) {
- if(endAt!=null){
- return;
- }
- info('游戏结束');
- if (now != null) {
- _setEndAt(now);
- } else {
- _setEndAt(this.now);
- }
- }
- void _updateCheckHistory() {
- gameState.pbGameSave.checkedSortedList.clear();
- gameState.pbGameSave.checkedSortedList
- .addAll(checkedPointsHistory.map((e) => e.toPbSave()).toList());
- }
- Future<void> _saveToDatabase() async {
- await DatabaseService.to.saveGameState(gameState);
- }
- Future<void> _saveToServer() async {
- info('进度上传开始');
- await _api.gameSaveUpload(
- pb.GameSaveUploadRequest()
- ..gameSave= gameState.pbGameSave);
- info('进度上传完成');
- }
- // 上报游戏结果
- Future<void> _settlement() async {
- info('结算开始');
- for(final one in plugs){
- await one.join();
- info('[${one.runtimeType}]已卸载');
- }
- while (true) {
- try {
- await _saveToServer();
- break;
- } on GrpcError catch (e) {
- if (e.code == StatusCode.unavailable) {
- errorMsg.add('无法连接至网络,正在重试');
- } else {
- break;
- }
- error(e);
- } catch (e) {
- error(e);
- break;
- }
- }
- while (true) {
- try {
- finishData = await _api.gameFinish(gameState.pbGameData.gameId, _isGiveUp);
- info('结算完成');
- break;
- } on GrpcError catch (e) {
- if (e.code == StatusCode.unavailable) {
- errorMsg.add('无法连接至网络,正在重试');
- } else {
- break;
- }
- error(e);
- Future.delayed(500.milliseconds);
- } catch (e) {
- error(e);
- break;
- }
- }
- }
- void _gameLoadCheckedCP() {
- final controlPointWantIdValueMap = <Int64, MControlPoint>{};
- final controlPointAllIdValueMap = <Int64, pb.ControlPointSimple>{};
- for (var one in model.controlPointWantSequence) {
- controlPointWantIdValueMap[one.intId] = one;
- }
- for (var one in gameState.pbGameData.controlPointAll) {
- controlPointAllIdValueMap[one.id] = one;
- }
- for (var i = 0; i < gameState.pbGameSave.checkedSortedList.length; i++) {
- final one = gameState.pbGameSave.checkedSortedList[i];
- final his = one.toModel();
- final hisInfo1 = controlPointWantIdValueMap[his.intId];
- final hisInfo2 = controlPointAllIdValueMap[his.intId];
- if (hisInfo2 != null) {
- his.updateBySimple(hisInfo2);
- }
- if (hisInfo1 != null) {
- his.updateBy(hisInfo1);
- his.type = hisInfo1.type;
- }
- checkedPointsHistory.add(his);
- }
- for (var i = 0; i < checkedPointsHistory.length; i++) {
- final his = checkedPointsHistory[i];
- if (i > 0) {
- final last = checkedPointsHistory[i - 1];
- his.checkAfterPrev = his.checkAfterStart - last.checkAfterStart;
- his.checkDistanceAfterPrev = his.checkDistanceAfterStart - last.checkDistanceAfterStart;
- } else {
- his.checkAfterPrev = his.checkAfterStart;
- his.checkDistanceAfterPrev = his.checkDistanceAfterStart;
- }
- }
- }
- bool _isCheckTooFast(Int64 id) {
- final last = lastCheckedPoint;
- if(last==null){
- return false;
- }
- return last.intId == id &&
- now.difference((startAt??DateTime(2000)).add(last.checkAfterStart)) <= 10.seconds;
- }
- void _updateNewCPState(MControlPoint point, {DateTime? now}) {
- now = now ?? this.now;
- point.checkAfterStart = now.difference(startAt ?? now);
- point.checkAfterPrev = point.checkAfterStart;
- point.checkDistanceAfterStart = model.myPositionHistoryLen.value;
- point.checkDistanceAfterPrev = point.checkDistanceAfterStart;
- final last = lastCheckedPoint;
- if (last != null) {
- point.checkAfterPrev = point.checkAfterStart - last.checkAfterStart;
- point.checkDistanceAfterPrev = point.checkDistanceAfterStart - last.checkDistanceAfterStart;
- }
- }
- void save() {
- _updateCheckHistory();
- _saveToDatabase().then((value) => info('本地存档完成'));
- _saveToServer().then((value) => info('上传存档完成'));
- }
- checkPointNFC(
- String identifier,
- void Function(MControlPoint cp) onChecked,
- Future<bool> Function () onConfirmFinish,
- void Function(pb.ControlPointSimple point) onProjectPoint,
- VoidCallback onNoPoint,
- ) {
- final checkedPoint = model.findCPWantByNfcId(identifier);
- if (checkedPoint != null) {
- checkPoint(checkedPoint, onChecked, onConfirmFinish);
- } else {
- final point = model.findCPInProjectByNfcId(identifier);
- if (point != null) {
- if (_isCheckTooFast(point.id)) {
- return;
- }
- final result = point.toModel();
- _updateNewCPState(result);
- result.isSuccess = false;
- model.checkHistoryAdd(result);
- save();
- onProjectPoint(point);
- } else {
- onNoPoint();
- }
- }
- }
- checkPointGps(
- void Function(MControlPoint cp) onChecked,
- Future<bool> Function () onConfirmFinish,
- ) {
- if(model.isInPlanControlPointArea){
- MControlPoint? checkedPoint;
- final point = model.nextPlanPoint;
- if (point != null) {
- checkedPoint = model.findCPInRoute(point);
- }
- checkPoint(checkedPoint!, onChecked, onConfirmFinish);
- }else if (model.isInWantControlPointArea) {
- MControlPoint? checkedPoint;
- final point = model.nextWantPoint;
- if (point != null) {
- checkedPoint = model.findCPInRoute(point);
- }
- checkPoint(checkedPoint!, onChecked, onConfirmFinish);
- }
- }
- Future<void> checkPoint(
- MControlPoint cp,
- void Function (MControlPoint cp) onChecked,
- /// 打了结束点,询问是否结束
- Future<bool> Function () onConfirmFinish,
- ) async{
- if (rule.checkNeedReturn(cp)){
- return;
- }
- final now = this.now;
- final result = rule.checkPoint(cp);
- result.isUnchecked=false;
- var isFinish = result.isSuccess && result.isFinish;
- if(!result.isSuccess && result.isFinish){
- isFinish = await onConfirmFinish();
- if(!isFinish){
- return;
- }
- }
- _updateNewCPState(result, now: now);
- if(result.isStart && result.isSuccess){
- _setStartAt(now);
- }
- if(isFinish){
- _gameStopSetValues(now: now);
- }
- model.checkHistoryAdd(result);
- if(startAt!=null){
- gameState.pbGameSave.duration = now.difference(startAt!).toPb();
- if(endAt != null){
- gameState.pbGameSave.duration = endAt!.difference(startAt!).toPb();
- }
- }
- model.gameSrcState.refresh();
- if(isFinish){
- _isGiveUp=false;
- stop();
- return;
- }else{
- save();
- }
- rule.recordLastPoint(cp);
- onChecked(result);
- model.controlPointWantSequence.refresh();
- }
- void _jobDuration() {
- final startAt = model.startAt;
- if(startAt!= null){
- final endAt = model.endAt?? now;
- model.duration.value = endAt.difference(startAt);
- }
- }
- // 达到强制结束时间
- void _jobCheckMaxForcedEnd() {
- if (now.difference(createTime) > maxForcedEndDuration) {
- stop();
- }
- }
- // 保存进度
- void _workAutoSave() async {
- while (isActive) {
- await Future.delayed(500.milliseconds);
- if (model.gameSrcState.value.pbGameData.gameId > 0 &&
- model.isStarted &&
- !model.isFinish) {
- await _saveToDatabase();
- }
- }
- }
- void _jobTrace() {
- final duration =
- _userProfile.gameSettingsTrackLengthSeconds.value.seconds;
- var out = <Offset>[];
- for (var i = model.myPositionHistory.length - 1; i >= 0; i--) {
- var one = model.myPositionHistory[i];
- if (now.difference(one.timestamp) > duration) {
- break;
- }
- if(i < model.myPositionOnMapHistory.length){
- out.add(model.myPositionOnMapHistory[i]);
- }
- }
- myTrace.value= out.reversed.toList();
- }
- void _jobPace() {
- final startDuration = model.duration.value;
- if (startDuration.inMilliseconds == 0) {
- return;
- }
- model.paceSecondKm.value =
- pacePerKm(model.myPositionHistoryLen.value, startDuration);
- if (checkedPointsHistory.isEmpty) {
- model.paceSecondKmFromLastCP.value = model.paceSecondKm.value;
- } else {
- final cp = checkedPointsHistory.last;
- model.paceSecondKmFromLastCP.value = pacePerKm(
- model.myPositionHistoryLenFromLastCP,
- startDuration - cp.checkAfterStart);
- }
- }
- @override
- Future<void> onClose() async {
- _gameStopSetValues();
- _updateCheckHistory();
- await Future.wait([_saveToDatabase(), _settlement()]);
- if (await Wakelock.enabled) {
- Wakelock.disable();
- }
- }
- @override
- Future<void> onInit() async {
- info('载入项目[GameId]:$id');
- await _saveToDatabase();
- // ---- 载入服务器游戏设置 ----
- _app.userProfile.cleanGameSettingsLock();
- _app.userProfile.gameSettingsLoadLock(gameState.pbGameData.ruleList);
- // ---- 载入服务器游戏设置 ----
- // 游戏规则
- rule = RuleInOrder(model);
- loadProgress.value= _progressApi;
- var progress = _progressApi;
- gameMapData = gameState.pbGameData.mapZip.toGameMap();
- await gameMapData.loadMemory(onReceiveProgress: (c, a) {
- if (a > 0) {
- var p = c.toDouble() / a;
- p = p * _progressMap + progress;
- loadProgress.value= (p);
- }
- });
- progress +=_progressMap;
- await model.initControlPointWantSequence(gameMapData, loadPic: true,
- onProgress: (c, a) {
- if (a > 0) {
- var p = c / a;
- p = p * _progressCP + progress;
- loadProgress.value= (p);
- }
- });
- _gameLoadCheckedCP();
- Wakelock.enable();
- addTimer(1.seconds, _jobCheckMaxForcedEnd);
- addTimer(1.seconds, _jobPace);
- addTimer(1.seconds, _jobTrace);
- addTimer(200.milliseconds, _jobDuration);
- _workAutoSave();
- plugs.addAll([
- PlugLocation(this),
- PlugOrientation(this),
- PlugSportWear(this)
- ]);
- }
- static const _progressMap = 0.4;
- static const _progressApi = 0.1;
- static const _progressCP = 0.3;
- bool _isGiveUp = false;
- final errorMsg = StreamController<String>.broadcast();
- final App _app = Get.find();
- final _userProfile = App.to.userProfile;
- final ApiService _api = Get.find();
- Rule rule = RuleMock();
- var gameMapData = GameMap();
- DateTime get now => _app.now;
- final model = GamePersonData();
- GameState get gameState => model.gameSrcState.value;
- // 创建时间
- DateTime get createTime => gameState.createTime;
- // 关门时间
- Duration get maxPassDuration => gameState.pbGameData.maxDuration.toDuration();
- // 强制结束时间
- Duration get maxForcedEndDuration =>
- gameState.pbGameData.maxForcedEndDuration.toDuration();
- // 打开始点的时间
- DateTime? get startAt => model.startAt;
- DateTime? get endAt => model.endAt;
- List<MControlPoint> get checkedPointsHistory => model.checkedPointsHistory;
- /// 指北针弧度,移动时通过GPS判断
- final compassRadiansFused = 0.0.obs;
- final compassRadiansSrc = 0.0.obs;
- final orientation = sensor.Orientation().obs;
- var accelerometerEvent = sensor.Orientation();
- bool get isPersonMoving {
- return gpsSpeedPerSecond > 0.5.meter && isDeviceMoving;
- }
- bool get isDeviceMoving {
- return sensorSpeedPerSecond> 0.1.meter;
- }
- Distance get gpsSpeedPerSecond{
- final p = model.myPosition;
- if(p!= null){
- return p.speed.meter;
- }
- return 0.meter;
- }
- var sensorSpeedPerSecond = 0.meter;
- @override
- int get id => gameState.pbGameData.gameId.toInt();
- var finishData=pb.GameDetailReply();
- final myTrace = <Offset>[].obs;
- MControlPoint? get lastCheckedPoint {
- if (checkedPointsHistory.isNotEmpty) {
- return checkedPointsHistory.last;
- } else {
- return null;
- }
- }
- }
|