protected override void Execute(List <GameStateEntity> entities)
        {
            var currentTick = _gameStateContext.tick.value;


            //Register the tick for which a snapshot is created
            _snapshotIndexService.AddIndex(currentTick);

            var actors   = _actorContext.GetEntities(ActorMatcher.Id);
            var gameEnts = _activeEntities.GetEntities();

            foreach (var actor in actors)
            {
                var shadowActor = _actorContext.CreateEntity();

                //LocalId is primary index => don't copy
                foreach (var index in actor.GetComponentIndices().Except(new[] { ActorComponentsLookup.Id }))
                {
                    var component1 = actor.GetComponent(index);
                    var component2 = shadowActor.CreateComponent(index, component1.GetType());
                    component1.CopyPublicMemberValues(component2);
                    shadowActor.AddComponent(index, component2);
                }

                shadowActor.AddBackup(actor.id.value, currentTick);
            }

            foreach (var e in gameEnts)
            {
                var shadowEntity = _gameContext.CreateEntity();

                _services.Get <IDebugService>().Register(currentTick, e.localId.value, e.position.value);

                //LocalId is primary index => don't copy; id+actorId should be readonly and stay the same throughout the game
                foreach (var index in e.GetComponentIndices().Except(new[] { GameComponentsLookup.LocalId, GameComponentsLookup.Id, GameComponentsLookup.ActorId }))
                {
                    var component1 = e.GetComponent(index);
                    var component2 = shadowEntity.CreateComponent(index, component1.GetType());
                    component1.CopyPublicMemberValues(component2);
                    shadowEntity.AddComponent(index, component2);
                }

                shadowEntity.AddBackup(e.localId.value, currentTick);
            }

            _services.Get <ILogService>().Warn("New backup for " + currentTick + "(" + actors.Length + " actors, " + gameEnts.Length + " entities)");
        }
Пример #2
0
        protected override void Execute(List <GameStateEntity> entities)
        {
            var currentTick = _gameStateContext.tick.value;

            //Debug.Log($"Create snapshort {currentTick}");

            //Register the tick for which a snapshot is created
            _snapshotContext.CreateEntity().AddTick(currentTick);

            foreach (var e in _activeEntities)
            {
                var shadowEntity = _gameContext.CreateEntity();

                //LocalId is primary index => don't copy; everything else has to be copied in case a destroyed entity has to be recovered
                foreach (var index in e.GetComponentIndices().Except(new[] { GameComponentsLookup.LocalId }))
                {
                    var component1 = e.GetComponent(index);
                    var component2 = shadowEntity.CreateComponent(index, component1.GetType());
                    component1.CopyPublicMemberValues(component2);
                    shadowEntity.AddComponent(index, component2);
                }

                shadowEntity.AddBackup(e.localId.value, currentTick);
            }

            foreach (var actor in _activeActors)
            {
                var shadowActor = _actorContext.CreateEntity();

                //Id is primary index => don't copy
                foreach (var index in actor.GetComponentIndices().Except(new[] { ActorComponentsLookup.Id }))
                {
                    var actorComponent  = actor.GetComponent(index);
                    var backupComponent = shadowActor.CreateComponent(index, actorComponent.GetType());
                    actorComponent.CopyPublicMemberValues(backupComponent);
                    shadowActor.AddComponent(index, backupComponent);
                }

                shadowActor.AddBackup(actor.id.value, currentTick);
            }

            Log.Trace(this, "New snapshot for " + currentTick + "(" + _activeActors.count + " actors, " + _activeEntities.count + " entities)");
        }
        protected override void Execute(List <GameStateEntity> entities)
        {
            var currentTick = _gameStateContext.tick.value;

            //Debug.Log($"Create snapshort {currentTick}");

            //Register the tick for which a snapshot is created
            _snapshotContext.CreateEntity().AddTick(currentTick);

            foreach (var entity in _activeEntities)
            {
                var shadowEntity = _gameContext.CreateEntity();

                //LocalId is primary index => don't copy; everything else has to be copied in case a destroyed entity has to be recovered
                foreach (var index in entity.GetComponentIndices().Except(new[] { GameComponentsLookup.LocalId }))
                {
                    entity.CopyTo(shadowEntity, index);
                }

                shadowEntity.AddBackup(entity.localId.value, currentTick);
            }

            foreach (var entity in _activeActors)
            {
                var shadowEntity = _actorContext.CreateEntity();

                //Id is primary index => don't copy
                foreach (var index in entity.GetComponentIndices().Except(new[] { ActorComponentsLookup.Id }))
                {
                    entity.CopyTo(shadowEntity, index);
                }

                shadowEntity.AddBackup(entity.id.value, currentTick);
            }

            Log.Trace(this, "New snapshot for " + currentTick + "(" + _activeActors.count + " actors, " + _activeEntities.count + " entities)");
        }
        protected override void DoRollbackTo(int tick, int missFrameTick, bool isNeedClear = true)
        {
            var snapshotIndices = _snapshotContext.GetEntities(SnapshotMatcher.Tick)
                                  .Where(entity => entity.tick.value <= tick).Select(entity => entity.tick.value).ToList();

            if (snapshotIndices.Count <= 0)
            {
                return;
            }
            snapshotIndices.Sort();
            Logging.Debug.Assert(snapshotIndices.Count > 0 && snapshotIndices[0] <= tick,
                                 $"Error! no correct history frame to revert minTick{(snapshotIndices.Count > 0 ? snapshotIndices[0] : 0)} targetTick {tick}");
            int i = snapshotIndices.Count - 1;

            for (; i >= 0; i--)
            {
                if (snapshotIndices[i] <= tick)
                {
                    break;
                }
            }

            var resultTick = snapshotIndices[i];

            if (resultTick == Tick)
            {
                Logging.Debug.Log("SelfTick should not rollback");
                return;
            }

            var snaps = "";

            foreach (var idx in snapshotIndices)
            {
                snaps += idx + " ";
            }

            Logging.Debug.Log(
                $"Rolling back {Tick}->{tick} :final from {resultTick} to {_gameStateContext.tick.value}  " +
                $"missTick:{missFrameTick} total:{Tick - resultTick} ");

            /*
             * ====================== Revert actors ======================
             * most importantly: the entity-count per actor gets reverted so the composite key (Id + ActorId) of GameEntities stays in sync
             */

            var backedUpActors =
                _actorContext.GetEntities(ActorMatcher.Backup).Where(e => e.backup.tick == resultTick);

            foreach (var backedUpActor in backedUpActors)
            {
                var target = _actorContext.GetEntityWithActorId(backedUpActor.backup.actorId);
                if (target == null)
                {
                    target = _actorContext.CreateEntity();
                    target.AddActorId(backedUpActor.backup.actorId);
                }

                //CopyTo does NOT remove additional existing components, so remove them first
                var additionalComponentIndices = target.GetComponentIndices().Except(
                    backedUpActor.GetComponentIndices()
                    .Except(new[] { ActorComponentsLookup.Backup })
                    .Concat(new[] { ActorComponentsLookup.ActorId }));

                foreach (var index in additionalComponentIndices)
                {
                    target.RemoveComponent(index);
                }

                backedUpActor.CopyTo(target, true,
                                     backedUpActor.GetComponentIndices().Except(new[] { ActorComponentsLookup.Backup }).ToArray());
            }

            /*
             * ====================== Revert game-entities ======================
             */
            var currentEntities = _gameContext.GetEntities(GameMatcher.EntityId);
            var backupEntities  = _gameContext.GetEntities(GameMatcher.Backup).Where(e => e.backup.tick == resultTick)
                                  .ToList();
            var backupEntityIds = backupEntities.Select(entity => entity.backup.entityId);

            //Entities that were created in the prediction have to be destroyed
            var invalidEntities = currentEntities.Where(entity => !backupEntityIds.Contains(entity.entityId.value))
                                  .ToList();

            foreach (var invalidEntity in invalidEntities)
            {
                invalidEntity.isDestroyed = true;
            }


            //Copy old state to the entity
            foreach (var backupEntity in backupEntities)
            {
                var target = _gameContext.GetEntityWithEntityId(backupEntity.backup.entityId);
                if (target == null)
                {
                    target = _gameContext.CreateEntity();
                    target.AddEntityId(backupEntity.backup.entityId);
                }

                //CopyTo does NOT remove additional existing components, so remove them first
                var additionalComponentIndices = target.GetComponentIndices().Except(
                    backupEntity.GetComponentIndices()
                    .Except(new[] { GameComponentsLookup.Backup })
                    .Concat(new[] { GameComponentsLookup.EntityId }));

                foreach (var index in additionalComponentIndices)
                {
                    target.RemoveComponent(index);
                }

                backupEntity.CopyTo(target, true,
                                    backupEntity.GetComponentIndices().Except(new[] { GameComponentsLookup.Backup }).ToArray());
            }

            //将太后 和太前的snapshot 删除掉
            if (isNeedClear)
            {
                foreach (var invalidBackupEntity in _actorContext.GetEntities(ActorMatcher.Backup)
                         .Where(e => e.backup.tick != resultTick))
                {
                    invalidBackupEntity.Destroy();
                }

                foreach (var invalidBackupEntity in _gameContext.GetEntities(GameMatcher.Backup)
                         .Where(e => e.backup.tick != resultTick))
                {
                    invalidBackupEntity.Destroy();
                }

                foreach (var snapshotEntity in _snapshotContext.GetEntities(SnapshotMatcher.Tick)
                         .Where(e => e.tick.value != resultTick))
                {
                    snapshotEntity.Destroy();
                }
            }

            //TODO: restore locally destroyed entities

            //Cleanup game-entities that are marked as destroyed
            _systems.Cleanup();
            _gameStateContext.ReplaceTick(resultTick);
            _timeMachineService.RollbackTo(resultTick);
        }