Exemplo n.º 1
0
        /// 清理无效的快照
        public void CleanUselessSnapshot(int checkedTick)
        {
            if (checkedTick < 2)
            {
                return;
            }
            //_timeMachineService.Clean(checkedTick-1);
            var snapshotIndices = _snapshotContext.GetEntities(SnapshotMatcher.Tick)
                                  .Where(entity => entity.tick.value <= checkedTick).Select(entity => entity.tick.value).ToList();

            if (snapshotIndices.Count == 0)
            {
                return;
            }
            snapshotIndices.Sort();
            int i = snapshotIndices.Count - 1;

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

            if (i < 0)
            {
                return;
            }
            var resultTick = snapshotIndices[i];

            //将太后 和太前的snapshot 删除掉
            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();
            }

            _systems.Cleanup();
        }
Exemplo n.º 2
0
 public void Initialize()
 {
     foreach (var actor in _actorContext.GetEntities())
     {
         actor.AddEntityCount(0);
     }
 }
        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)");
        }
Exemplo n.º 4
0
        /// <summary>
        /// Revert all changes that were done during or after the given tick
        /// </summary>
        /// <param name="tick"></param>
        public void RevertToTick(uint tick)
        {
            Services.Get <ILogService>().Trace("Rollback to " + tick);
            //Get the actual tick that we have a snapshot for
            var resultTick = Services.Get <ISnapshotIndexService>().GetFirstIndexBefore(tick);

            /*
             * ====================== 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)
            {
                backedUpActor.CopyTo(
                    _actorContext.GetEntityWithId(backedUpActor.backup.actorId),                                    //Current Actor
                    true,                                                                                           //Replace components
                    backedUpActor.GetComponentIndices().Except(new [] { ActorComponentsLookup.Backup }).ToArray()); //Copy everything except the backup-component
            }


            /*
             * ====================== Revert game-entities ======================
             */

            var currentEntities = _gameContext.GetEntities(GameMatcher.LocalId);
            var backupEntities  = _gameContext.GetEntities(GameMatcher.Backup).Where(e => e.backup.tick == resultTick).ToList();
            var backupEntityIds = backupEntities.Select(entity => entity.backup.localEntityId);

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

            foreach (var invalidEntity in invalidEntities)
            {
                //Here we have the actual entities, we can safely refer to them via the internal id
                _view.DeleteView(invalidEntity.localId.value);
                invalidEntity.Destroy();
            }

            foreach (var invalidBackupEntity in _gameContext.GetEntities(GameMatcher.Backup).Where(e => e.backup.tick > resultTick))
            {
                Services.Get <ISnapshotIndexService>().RemoveIndex(invalidBackupEntity.backup.tick);
                invalidBackupEntity.Destroy();
            }

            //Copy old state to the entity
            foreach (var backupEntity in backupEntities)
            {
                var target = _gameContext.GetEntityWithLocalId(backupEntity.backup.localEntityId);
                var additionalComponentIndices = target.GetComponentIndices().Except(
                    backupEntity
                    .GetComponentIndices()
                    .Except(new[] { GameComponentsLookup.Backup })
                    .Concat(new[] { GameComponentsLookup.Id, GameComponentsLookup.ActorId, GameComponentsLookup.LocalId }))
                ;
                foreach (var index in additionalComponentIndices)
                {
                    target.RemoveComponent(index);
                }

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


                if (!Services.Get <IDebugService>().Validate(resultTick, backupEntity.backup.localEntityId, target.position.value))
                {
                    throw new Exception();
                }
            }

            //TODO: restore locally destroyed entities

            Contexts.gameState.ReplaceTick(resultTick);
        }
        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);
        }