using System; using System.Collections.Generic; using System.Reflection; using System.Threading.Tasks; using Cysharp.Threading.Tasks; using Plugins.JNGame.Sync.Frame.game; using Plugins.JNGame.System; using Plugins.JNGame.Util; using UnityEngine; using UnityEngine.SceneManagement; namespace Plugins.JNGame.Sync.Frame { //帧同步 通知 public interface JNSyncFrameEvent { static string CLEAR = "JNSyncFrameEvent_CLEAR"; static string CREATE = "JNSyncFrameEvent_CREATE"; static string FRAME = "JNSyncFrameEvent_FRAME"; } public abstract class JNSyncFrame : SystemBase { //同步时间 (和服务器保持一致) private int _nSyncTime = 67; //大于多少帧进行追帧 private int _nMaxFrameBan = 4; //大于多少帧进行快速追帧 private int _nMaxFrameLoopBan = 18; //将服务器帧数进行平分 private int _nDivideFrame = 3; //帧队列 private Queue _nFrameQueue = new(); //本地已执行帧数 private int _nLocalRunFrame = 0; //本地帧数 private int _nLocalFrame = 0; //服务器的帧 private int _nServerFrame = 0; public int NLocalRunFrame => _nLocalRunFrame; public int NLocalFrame => _nLocalFrame; public int NServerFrame => _nServerFrame; //暂存帧列表 private Dictionary _nFrameTempQueue = new(); //需要同步的Actor private List _nSyncActors = new(); //ID 每添加 JNSyncFrameComponent + 1 public Func nSyncID = RandomUtil.Next(0); //随机数 public Func nRandom = RandomUtil.SyncRandom(); //随机数double public Func nRandomFloat = RandomUtil.SyncRandomFloat(); //随机数整数 public Func nRandomInt = RandomUtil.SyncRandomInt(); //是否开始同步 Boolean _isStart = false; //是否请求后台数据 private Boolean _isRequestServerData = false; public bool IsRequestServerData => _isRequestServerData; //帧更新 int dtTotal = 0; //输入更新 int dtInputTotal = 0; public override Task OnInit() { Physics.autoSimulation = false; Physics.autoSyncTransforms = false; this.OnReset(); return Task.CompletedTask; } //开始 public void OnStart(){ this._isStart = true; } //重置数据 public void OnReset() { EventDispatcher.Event.Dispatch(JNSyncFrameEvent.CLEAR); this._isStart = false; this.nSyncID = RandomUtil.Next(0); this.nRandom = RandomUtil.SyncRandom(); this.nRandomFloat = RandomUtil.SyncRandomFloat(); this.nRandomInt = RandomUtil.SyncRandomInt(); this._nSyncActors = new(); this._nFrameQueue = new(); this._nFrameTempQueue = new(); this._nLocalFrame = 0; this._nServerFrame = 0; this._nLocalRunFrame = 0; Physics.SyncTransforms(); EventDispatcher.Event.Dispatch(JNSyncFrameEvent.CREATE); } //更新同步 public void Update(int dt) { if(!_isStart) return; dtTotal += dt; dtInputTotal += dt; int nSyncTime = this.DyTime(); if(nSyncTime > 0){ while(nSyncTime != 0 && this.dtTotal > nSyncTime){ this.onUpdate(); this.dtTotal -= nSyncTime; nSyncTime = this.DyTime(); } }else{ //追帧运行 保持前端 15 帧 刷新 long endTime = (new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds()) + 66; while(this.DyTime() == 0 && (new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds()) < endTime){ this.onUpdate(); } dtTotal = 0; } } //运行帧 public void onUpdate() { if(!(_nFrameQueue.TryDequeue(out var frame))) return; if(frame.Index != 0) this._nLocalRunFrame = frame.Index; int dt = this._nSyncTime / this._nDivideFrame; //拆出输入 Dictionary inputs = new(); foreach (var message in frame.Messages) { inputs.Add(message.NId,message); } //更新帧 this._nSyncActors.ForEach(child => { MethodInfo OnSyncUpdate = child.GetType().GetMethod("OnSyncUpdate"); MethodInfo Decoder = child.GetType().GetMethod("Decoder"); if (inputs.ContainsKey(child.NID)) { OnSyncUpdate.Invoke(child,new []{dt,frame,Decoder.Invoke(child,new object[]{ inputs[child.NID].Input.ToByteArray() })}); } else { OnSyncUpdate.Invoke(child,new []{ dt,frame,(object)null }); } }); //执行下一帧物理 Physics.Simulate((float)dt / 1000); Physics.SyncTransforms(); } //自适应间隔时间 public int DyTime(){ int dt = this._nSyncTime / this._nDivideFrame; int loop = 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; } /** * 接收帧数据 * @param frame 帧数据 * @param isLatestData 是否最新的帧数据 */ public void AddInput(JNFrameInfo frame,Boolean isLatestData = true) { if (!_isStart) return; if(isLatestData) { this._nServerFrame = frame.Index; // //如果推的帧小于本地帧 则 重开 // if(frame.Index < _nLocalFrame){ // OnReset(); // OnStart(); // return; // } } //我需要的下一帧 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.AddInput(frameInfo,false); } that._isRequestServerData = false; }).Forget(); } return; } else { //如果有则覆盖 frame = tamp; } } //删除临时帧 _nFrameTempQueue.Remove(frame.Index); _nLocalFrame = index; //分帧插入 _nFrameQueue.Enqueue(frame); for (var i = 0; i < this._nDivideFrame - 1; i++) { _nFrameQueue.Enqueue(new JNFrameInfo()); } } //添加同步组件 public void AddSyncActor(IJNSyncFrameComponent sync){ if(!this._nSyncActors.Contains(sync)){ this._nSyncActors.Add(sync); } } //销毁同步组件 public void DelSyncActor(IJNSyncFrameComponent sync){ this._nSyncActors.Remove(sync); } //发送帧数据 protected abstract void OnSendInput(JNFrameInputs inputs); //获取帧数据 protected abstract UniTask OnServerData(int start,int end); } }