public bool IsPredictionCorrect(SnapshotWithLastInputId correctServerSnapshot, ushort playerEntityId) { PredictedSnapshot predictedSnapshot = predictedSnapshotsStorage .GetByInputId(correctServerSnapshot.lastProcessedInputId); if (predictedSnapshot == null) { string mes = $"Не найдено предсказанное состояние. lastProcessedInputId = {correctServerSnapshot.lastProcessedInputId}"; throw new Exception(mes); } bool timeIsSame = correctServerSnapshot.lastProcessedInputId == predictedSnapshot.lastInputId; if (!timeIsSame) { string message = "Время снимков не совпадает. " + $" inputId тика с сервера = {correctServerSnapshot.lastProcessedInputId} " + $" inputId предсказанного тика = {predictedSnapshot.lastInputId}"; throw new ArgumentException(message); } bool isPredictionCorrect = playerEntityComparer .IsSame(predictedSnapshot, correctServerSnapshot, playerEntityId); return(isPredictionCorrect); }
public void Execute() { try { int newestTickNumber = snapshotBuffer.GetNewestTickNumber(); //Пришла новая информация if (lastSavedTickNumber < newestTickNumber) { //Обновить локальный счётчик lastSavedTickNumber = newestTickNumber; SnapshotWithLastInputId newest = snapshotBuffer.GetNewestSnapshot(); ushort playerEntityId = PlayerIdStorage.PlayerEntityId; if (playerEntityId == 0) { //todo изменить порядок установки playerEntityId throw new Exception("PlayerEntityId не установлен"); } //проверить, что игрок правильно предсказан или пересоздать текущее состояние predictionManager.Reconcile(newest, playerEntityId); } else { //новый тик от сервера не пришёл } } catch (Exception e) { log.Error(e.FullMessage()); } }
public void SetNewTransforms(TransformPackMessage message) { SnapshotWithLastInputId snapshot = new SnapshotWithLastInputId(message); snapshotBuffer.Add(snapshot); timeUpdater.NewSnapshotReceived(message.TickNumber, message.TickStartTimeSec); }
public void Resimulate(SnapshotWithLastInputId correctServerSnapshot, ushort playerEntityId) { log.Info($"Пересчёт тика inputId = {correctServerSnapshot.lastProcessedInputId}."); ReplaceWrongSnapshot(correctServerSnapshot); ResimulateSnapshots(correctServerSnapshot, playerEntityId); }
private void ReplaceWrongSnapshot(SnapshotWithLastInputId correctServerSnapshot) { PredictedSnapshot wrongSnapshot = predictedSnapshotsStorage .GetByInputId(correctServerSnapshot.lastProcessedInputId); //изменение ошибочного снимка // ReSharper disable once PossibleNullReferenceException wrongSnapshot.Clear(); wrongSnapshot.Modify(correctServerSnapshot); //todo это можно не делать predictedSnapshotsStorage.PutCorrect(wrongSnapshot); }
public void Execute() { int newestTickNumber = snapshotBuffer.GetNewestTickNumber(); if (newestTickNumber == lastShowedTickNumber) { return; } lastShowedTickNumber = newestTickNumber; SnapshotWithLastInputId newestGameState = snapshotBuffer.GetNewestSnapshot(); HashSet <ushort> needDelete = new HashSet <ushort>(dictionary.Keys); foreach (var pair in newestGameState.transforms) { ushort entityId = pair.Key; var viewTransform = pair.Value; Vector3 position = viewTransform.GetPosition(); Quaternion rotation = Quaternion.AngleAxis(viewTransform.Angle, Vector3.up); if (!vectorValidator.TryValidate(position)) { log.Debug("Проблема позиции"); continue; } needDelete.Remove(entityId); //Такой обьект уже есть на сцене if (dictionary.TryGetValue(entityId, out var gameObject)) { //Обновить gameObject.transform.position = position; gameObject.transform.rotation = rotation; } else { //Создать GameObject prefab = clientPrefabsStorage.GetPrefab(viewTransform.viewTypeEnum); GameObject go = physicsSpawner.Spawn(prefab, position, rotation); dictionary.Add(entityId, go); } } //Удалить лишние объекты foreach (ushort entityId in needDelete) { Object.Destroy(dictionary[entityId]); } }
public void Add(SnapshotWithLastInputId snapshotArg) { lock (lockObj) { // log.Info($"Добавление нового тика № = {snapshotArg.tickNumber} time = {snapshotArg.tickTime}"); if (history.TryGetValue(snapshotArg.tickNumber, out var snapshot)) { snapshot.Modify(snapshotArg); } else { history.Add(snapshotArg.tickNumber, snapshotArg); } } }
private void ResimulateSnapshots(SnapshotWithLastInputId correctServerSnapshot, ushort playerEntityId) { //достать все вводы после заменённого снимка var allInputs = clientInputMessagesHistory .GetAllFromId(correctServerSnapshot.lastProcessedInputId); List <AverageInputMessageModel> averageInputs = averageInputManager.GetAverageInputs(allInputs); log.Debug($"Кол-во вводов {allInputs.Count}. Кол-во тиков физики = {averageInputs.Count}"); DateTime startTime = DateTime.UtcNow; //вызвать перегенерцию положения игрока для каждого ввода foreach (var averageInput in averageInputs) { //todo взять суммарную длительность тиков float physicsSimulationDuration = predictedSnapshotUtil .GetTotalDuration(averageInput.replacedInputsIds, predictedSnapshotsStorage); log.Debug("Длительность симуляции " + physicsSimulationDuration); var baseSnapshot = predictedSnapshotsStorage.GetByInputId(averageInput.inputId - 1) ?? throw new NullReferenceException(); if (baseSnapshot.lastInputId != averageInput.inputId - 1) { throw new Exception("Не совпадает lastInputId"); } log.Debug($"Пересчёт снимка {averageInput.inputId}"); Snapshot snapshot = playerPredictor.Predict(baseSnapshot, playerEntityId, averageInput.inputMessageModel, physicsSimulationDuration); foreach (uint inputId in averageInput.replacedInputsIds) { var wrongPredictedSnapshot = predictedSnapshotsStorage.GetByInputId(inputId) ?? throw new NullReferenceException(); wrongPredictedSnapshot.Clear(); wrongPredictedSnapshot.Modify(snapshot); } } DateTime finishTime = DateTime.UtcNow; int reconcileTime = (finishTime - startTime).Milliseconds; log.Debug($"reconcileTime = {reconcileTime}"); }
public void Reconcile(SnapshotWithLastInputId correctServerSnapshot, ushort playerEntityId) { uint lastProcessedInputId = correctServerSnapshot.lastProcessedInputId; if (lastProcessedInputId == 0) { log.Debug("С сервера пришёл тик с пустым lastProcessedInputId"); return; } if (!predictionChecker.IsPredictionCorrect(correctServerSnapshot, playerEntityId)) { simulationCorrector.Resimulate(correctServerSnapshot, playerEntityId); } else { // log.Info($"Правильное предсказание inputId = {lastProcessedInputId}"); } }