JisolGame/JNFrame2/Assets/JNGame/Sync/App/Frame/JNSyncFrameService.cs
PC-20230316NUNE\Administrator 894100ae37 提交Unity 联机Pro
2024-08-17 14:27:18 +08:00

268 lines
7.8 KiB
C#

using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using JNGame.Sync.Frame.Service;
using JNGame.Sync.System.View;
using UnityEngine;
namespace JNGame.Sync.Frame
{
public abstract class JNSyncFrameService : JNSyncDefaultService
{
public override int DeltaTime => _nSyncTime / _nDivideFrame;
//同步时间 (和服务器保持一致)
private int _nSyncTime = 67;
public int NSyncTime => _nSyncTime;
//大于多少帧进行追帧
private int _nMaxFrameBan = 4;
public int NMaxFrameBan => _nMaxFrameBan;
//大于多少帧进行快速追帧
private int _nMaxFrameLoopBan = 20;
public int NMaxFrameLoopBan => _nMaxFrameLoopBan;
//将服务器帧数进行平分
private int _nDivideFrame = 2;
public int NDivideFrame => _nDivideFrame;
//本地帧数
private int _nLocalFrame = -1;
public int NLocalFrame => _nLocalFrame;
//帧队列
private Queue<JNFrameInfo> _nFrameQueue = new();
public Queue<JNFrameInfo> NFrameQueue => _nFrameQueue;
//暂存帧列表
private Dictionary<int,JNFrameInfo> _nFrameTempQueue = new();
//是否请求后台数据
private bool _isRequestServerData = false;
//帧更新
int dtTotal = 0;
//输入更新
int dtInputTotal = 0;
//是否开始同步
private bool _isStart = false;
public bool IsStart => _isStart;
//是否在追帧
private bool _isLoop = false;
public bool IsLoop => _isLoop;
protected JNSyncFrameService() : base()
{
}
public override void Initialize()
{
base.Initialize();
_isStart = true;
}
/// <summary>
/// 视图帧更新
/// </summary>
public override void Execute()
{
#if (!ENTITAS_DISABLE_VISUAL_DEBUGGING && UNITY_EDITOR)
if (paused) return;
#endif
if (!IsStart) return;
base.Execute();
int deltaTime = (int)(Time.deltaTime * 1000f);
dtTotal += deltaTime;
dtInputTotal += deltaTime;
try
{
int nSyncTime = this.DyTime();
if (nSyncTime > 0)
{
this._isLoop = false;
if (dtTotal > nSyncTime && _nFrameQueue.Count > 0)
{
this.OnRunSimulate();
dtTotal -= nSyncTime;
}
if (_nFrameQueue.Count <= 0)
{
dtTotal = 0;
}
}
else
{
this._isLoop = true;
//追帧运行 保持前端 15 帧 刷新
long endTime = (new DateTimeOffset(DateTime.UtcNow)).ToUnixTimeMilliseconds() + 100;
while (this.DyTime() == 0 && ((new DateTimeOffset(DateTime.UtcNow)).ToUnixTimeMilliseconds()) < endTime)
{
this.OnRunSimulate();
}
dtTotal = 0;
}
//更新输入
if (dtInputTotal > (_nSyncTime / _nDivideFrame))
{
dtInputTotal = 0;
OnRunInput();
}
}
catch (Exception e)
{
Debug.LogError(e.Message);
}
}
/// <summary>
/// 运行输入
/// </summary>
public void OnRunInput()
{
var inputs = GetInputs();
if (inputs.Inputs.Count > 0)
{
OnSendInput(inputs);
}
}
/// <summary>
/// 自适应间隔时间
/// </summary>
/// <returns></returns>
public int DyTime(){
int dt = this._nSyncTime / this._nDivideFrame;
int loop = dt;
// return dt;
//大于nMaxFrameBan 进行 追帧
if(this._nFrameQueue.Count > this._nMaxFrameBan) {
//计算超过的帧数
int exceed = this._nFrameQueue.Count - this._nMaxFrameBan;
int most = this._nMaxFrameLoopBan - this._nMaxFrameBan;
int ldt = ((most - exceed) / most) * dt;
//自适应追帧算法
if(exceed <= this._nMaxFrameLoopBan){
loop = ldt;
}else{
loop = 0;
}
}else{
loop = dt;
}
return loop;
}
/// <summary>
/// 接受帧数
/// </summary>
public void AddFrame(JNFrameInfo frame,bool isLatestData = true)
{
//我需要的下一帧
int index = _nLocalFrame + 1;
//判断接受的帧是否下一帧 如果不是则加入未列入
if (frame.Index != index){
_nFrameTempQueue.TryAdd(frame.Index,frame);
//在未列入中拿到需要的帧
JNFrameInfo tamp = null;
if ((tamp = _nFrameTempQueue.GetValueOrDefault(index,null)) == null)
{
var that = this;
//如果没有则向服务器请求我需要的帧数
if (!that._isRequestServerData)
{
that._isRequestServerData = true;
//请求
that.OnServerData(this._nLocalFrame, 0).ContinueWith(infos =>
{
foreach (var frameInfo in infos.Frames)
{
if(frameInfo.Index >= this._nLocalFrame)
that.AddFrame(frameInfo,false);
}
that._isRequestServerData = false;
}).Forget();
}
return;
}
else
{
//如果有则覆盖
frame = tamp;
}
}
//删除临时帧
_nFrameTempQueue.Remove(frame.Index);
_nLocalFrame = index;
//分帧插入
_nFrameQueue.Enqueue(frame);
for (var i = 0; i < _nDivideFrame - 1; i++) {
_nFrameQueue.Enqueue(new JNFrameInfo());
}
}
/// <summary>
/// 获取输入
/// </summary>
/// <returns></returns>
protected abstract JNFrameInputs GetInputs();
/// <summary>
/// 运行帧
/// </summary>
protected virtual void OnRunSimulate()
{
Debug.Log("Run OnRunSimulate");
if (!(NFrameQueue.TryDequeue(out var frame))) return;
Simulate();
}
/// <summary>
/// 发送帧数据
/// </summary>
/// <param name="inputs"></param>
protected abstract void OnSendInput(JNFrameInputs inputs);
/// <summary>
/// 获取帧数据
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <returns></returns>
protected abstract UniTask<JNFrameInfos> OnServerData(int start,int end);
}
}