public void Execute() { foreach (var input in _spawnInputs.GetEntities().Where(entity => entity.tick.value == _gameStateContext.tick.value)) { var actor = _actorContext.GetEntityWithId(input.actorId.value); var nextEntityId = actor.entityCount.value; var e = _gameContext.CreateEntity(); e.isNew = true; _services.Get <ILogService>().Trace(actor.id.value + " -> " + nextEntityId); //composite primary key e.AddId(nextEntityId); e.AddActorId(input.actorId.value); //unique id for internal usage e.AddLocalId(_localIdCounter); //some default components that every game-entity must have e.AddVelocity(Vector2.Zero); e.AddPosition(input.coordinate.value); _viewService.LoadView(e, input.entityConfigId.value); actor.ReplaceEntityCount(nextEntityId + 1); _localIdCounter += 1; } }
public void CreatePlayer(byte actorId, int type){ var bornPos = _gameConstStateService.playerBornPoss[actorId % _gameConstStateService.playerBornPoss.Count]; var createPos = bornPos + base._gameConfigService.TankBornOffset; _gameEffectService.ShowBornEffect(createPos); _gameAudioService.PlayClipBorn(); EDir dir = EDir.Up; DelayCall(base._gameConfigService.TankBornDelay, () => { var entity = CreateUnit(createPos, _gameConfigService.playerPrefabs, type, dir); var actor = _actorContext.GetEntityWithId(actorId); if (actor != null) { actor.ReplaceGameLocalId(entity.localId.value); entity.ReplaceActorId(actorId); } else { Debug.LogError( $"can not find a actor after create a game player actorId:{actorId} localId{entity.localId.value}"); } }); }
public void Execute() { foreach (var input in _spawnInputs.GetEntities().Where(entity => entity.tick.value == _gameStateContext.tick.value)) { var actor = _actorContext.GetEntityWithId(input.actorId.value); var nextEntityId = actor.entityCount.value; var e = _gameContext.CreateEntity(); Log.Trace(this, actor.id.value + " -> " + nextEntityId); //composite primary key e.AddId(nextEntityId); e.AddActorId(input.actorId.value); //unique id for internal usage e.AddLocalId(_localIdCounter); //some default components that every game-entity must have e.AddVelocity(Vector2.Zero); e.AddPosition(input.coordinate.value); _viewService.LoadView(e, input.entityConfigId.value); if (e.isNavigable) { //TODO: factory method to create entity? //Default agent settings e.AddRadius(F64.C1); e.AddMaxSpeed(F64.C2); e.AddRvoAgentSettings(Vector2.Zero, 5, new List <KeyValuePair <Fix64, uint> >()); } actor.ReplaceEntityCount(nextEntityId + 1); _localIdCounter += 1; } }
/// <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); }
/// <summary> /// Reverts all changes that were done during or after the given tick /// </summary> public void RollbackTo(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(); UnityEngine.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.GetEntityWithId(backedUpActor.backup.actorId); if (target == null) { target = _actorContext.CreateEntity(); target.AddId(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.Id })); 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.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) { invalidEntity.isDestroyed = true; } //将太后 和太前的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(); } } //Copy old state to the entity foreach (var backupEntity in backupEntities) { var target = _gameContext.GetEntityWithLocalId(backupEntity.backup.localEntityId); if (target == null) { target = _gameContext.CreateEntity(); target.AddLocalId(backupEntity.backup.localEntityId); } //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.LocalId })); foreach (var index in additionalComponentIndices) { target.RemoveComponent(index); } backupEntity.CopyTo(target, true, backupEntity.GetComponentIndices().Except(new[] { GameComponentsLookup.Backup }).ToArray()); } //TODO: restore locally destroyed entities //Cleanup game-entities that are marked as destroyed _systems.Cleanup(); _gameStateContext.ReplaceTick(resultTick); _timeMachineService.RollbackTo(resultTick); _timeMachineService.CurTick = resultTick; }