Просмотр исходного кода

服务端不等待客户端消息,如果客户端消息晚到了,服务端会拿客户端上一帧的输入发送出去

tanghai 2 лет назад
Родитель
Сommit
91f90624cb

+ 19 - 6
Unity/Assets/Scripts/Hotfix/Client/LockStep/OneFrameMessagesHandler.cs

@@ -10,22 +10,35 @@ namespace ET.Client
             Room room = session.DomainScene().GetComponent<Room>();
             FrameBuffer frameBuffer = room.FrameBuffer;
             
-            if (message.Frame != frameBuffer.RealFrame + 1)
+            if (message.Frame != room.RealFrame + 1)
             {
-                throw new Exception($"recv oneframeMessage frame error: {message.Frame} {frameBuffer.RealFrame}");
+                throw new Exception($"recv oneframeMessage frame error: {message.Frame} {room.RealFrame}");
             }
 
-            // 服务端返回来的消息,跟预测消息对比
-            OneFrameMessages predictionMessage = frameBuffer.GetFrame(message.Frame);
+            ++room.RealFrame;
+            // 服务端返回的消息比预测的还早
+            if (room.RealFrame > room.PredictionFrame)
+            {
+                OneFrameMessages realFrame = frameBuffer[room.RealFrame];
+                message.CopyTo(realFrame);
+                return;
+            }
             
-            ++frameBuffer.RealFrame;
+            // 服务端返回来的消息,跟预测消息对比
+            OneFrameMessages predictionMessage = frameBuffer[message.Frame];
+            // 对比失败有两种可能,
+            // 1是别人的输入预测失败,这种很正常,
+            // 2 自己的输入对比失败,这种情况是自己发送的消息比服务器晚到了,服务器使用了你的上一次输入
+            // 回滚重新预测的时候,自己的输入不用变化
             if (message != predictionMessage)
             {
                 message.CopyTo(predictionMessage);
                 // 回滚到frameBuffer.RealFrame
-                LSHelper.Rollback(room, frameBuffer.RealFrame);
+                LSHelper.Rollback(room, room.RealFrame);
             }
 
+            // 回收消息,减少GC
+            NetServices.Instance.RecycleMessage(message);
             await ETTask.CompletedTask;
         }
     }

+ 1 - 1
Unity/Assets/Scripts/Hotfix/Client/LockStep/Room2C_AdjustUpdateTimeHandler.cs

@@ -18,7 +18,7 @@ namespace ET.Client
                 newInterval = 66;
             }
             
-            room.FixedTimeCounter.ChangeInterval(newInterval, room.FrameBuffer.PredictionFrame);
+            room.FixedTimeCounter.ChangeInterval(newInterval, room.PredictionFrame);
             await ETTask.CompletedTask;
         }
     }

+ 21 - 18
Unity/Assets/Scripts/Hotfix/Client/LockStep/RoomClientUpdaterSystem.cs

@@ -1,6 +1,5 @@
 using System;
 using System.IO;
-using MongoDB.Bson;
 
 namespace ET.Client
 {
@@ -16,7 +15,6 @@ namespace ET.Client
             }
         }
         
-        [FriendOf(typeof (Room))]
         public class UpdateSystem: UpdateSystem<RoomClientUpdater>
         {
             protected override void Update(RoomClientUpdater self)
@@ -28,17 +26,23 @@ namespace ET.Client
         private static void Update(this RoomClientUpdater self)
         {
             Room room = self.GetParent<Room>();
-            FrameBuffer frameBuffer = room.FrameBuffer;
             long timeNow = TimeHelper.ServerFrameTime();
             Scene clientScene = room.GetParent<Scene>();
             
-            if (timeNow < room.FixedTimeCounter.FrameTime(frameBuffer.PredictionFrame + 1))
+            if (timeNow < room.FixedTimeCounter.FrameTime(room.PredictionFrame + 1))
             {
                 return;
             }
-            ++frameBuffer.PredictionFrame;
-            OneFrameMessages oneFrameMessages = GetOneFrameMessages(self, frameBuffer.PredictionFrame);
-            room.Update(oneFrameMessages, frameBuffer.PredictionFrame);
+
+            // 最多只预测5帧
+            if (room.PredictionFrame - room.RealFrame > 5)
+            {
+                return;
+            }
+            
+            ++room.PredictionFrame;
+            OneFrameMessages oneFrameMessages = self.GetOneFrameMessages(room.PredictionFrame);
+            room.Update(oneFrameMessages, room.PredictionFrame);
             
 
             FrameMessage frameMessage = NetServices.Instance.FetchMessage<FrameMessage>();
@@ -52,21 +56,20 @@ namespace ET.Client
             Room room = self.GetParent<Room>();
             FrameBuffer frameBuffer = room.FrameBuffer;
             
-            if (frame <= frameBuffer.RealFrame)
+            if (frame <= room.RealFrame)
             {
-                return frameBuffer.GetFrame(frame);
+                return frameBuffer[frame];
             }
             
             // predict
-            return GetPredictionOneFrameMessage(self, frame);
-        }
-
-        // 获取预测一帧的消息
-        private static OneFrameMessages GetPredictionOneFrameMessage(this RoomClientUpdater self, int frame)
-        {
-            Room room = self.GetParent<Room>();
-            OneFrameMessages predictionFrame = room.FrameBuffer.GetFrame(frame);
-            OneFrameMessages realFrame = room.FrameBuffer.GetFrame(room.FrameBuffer.RealFrame);
+            OneFrameMessages predictionFrame = frameBuffer[frame];
+            if (predictionFrame == null)
+            {
+                throw new Exception($"get frame is null: {frame}, max frame: {frameBuffer.MaxFrame}");
+            }
+            
+            frameBuffer.MoveForward(frame);
+            OneFrameMessages realFrame = frameBuffer[room.RealFrame];
             realFrame?.CopyTo(predictionFrame);
             predictionFrame.Frame = frame;
             predictionFrame.Inputs[self.MyId] = self.Input;

+ 18 - 8
Unity/Assets/Scripts/Hotfix/Server/LockStep/Map/FrameMessageHandler.cs

@@ -1,3 +1,4 @@
+using System;
 using System.Collections.Generic;
 
 namespace ET.Server
@@ -8,22 +9,31 @@ namespace ET.Server
         protected override async ETTask Run(Room room, FrameMessage message)
         {
             FrameBuffer frameBuffer = room.FrameBuffer;
-            if (message.Frame < frameBuffer.RealFrame)  // 小于RealFrame,丢弃
+            
+            if (message.Frame % (1000 / LSConstValue.UpdateInterval) == 0)
+            {
+                long nowFrameTime = room.FixedTimeCounter.FrameTime(message.Frame);
+                int diffTime = (int)(nowFrameTime - TimeHelper.ServerFrameTime());
+
+                ActorLocationSenderComponent.Instance.Get(LocationType.GateSession).Send(message.PlayerId, new Room2C_AdjustUpdateTime() {DiffTime = diffTime});
+            }
+
+            if (message.Frame < room.RealFrame)  // 小于RealFrame,丢弃
             {
                 Log.Warning($"FrameMessage discard: {message}");
                 return;
             }
-            OneFrameMessages oneFrameMessages = frameBuffer.GetFrame(message.Frame);
+            
+            OneFrameMessages oneFrameMessages = frameBuffer[message.Frame];
+            if (oneFrameMessages == null)
+            {
+                Log.Error($"FrameMessageHandler get frame is null: {message.Frame}, max frame: {frameBuffer.MaxFrame}");
+                return;
+            }
             oneFrameMessages.Frame = message.Frame;
             oneFrameMessages.Inputs[message.PlayerId] = message.Input;
 
-            if (message.Frame % (1000 / LSConstValue.UpdateInterval) == 0)
-            {
-                long nowFrameTime = room.FixedTimeCounter.FrameTime(message.Frame);
-                int diffTime = (int)(nowFrameTime - TimeHelper.ServerFrameTime());
 
-                ActorLocationSenderComponent.Instance.Get(LocationType.GateSession).Send(message.PlayerId, new Room2C_AdjustUpdateTime() {DiffTime = diffTime});
-            }
             await ETTask.CompletedTask;
         }
     }

+ 47 - 12
Unity/Assets/Scripts/Hotfix/Server/LockStep/Room/RoomServerUpdaterSystem.cs

@@ -18,31 +18,66 @@ namespace ET.Server
         private static void Update(this RoomServerUpdater self)
         {
             Room room = self.GetParent<Room>();
-            FrameBuffer frameBuffer = room.FrameBuffer;
             long timeNow = TimeHelper.ServerFrameTime();
             
             
-            int frame = frameBuffer.RealFrame + 1;
+            int frame = room.RealFrame + 1;
             if (timeNow < room.FixedTimeCounter.FrameTime(frame))
             {
                 return;
             }
+
+            OneFrameMessages oneFrameMessages = self.GetOneFrameMessage(frame);
+            ++room.RealFrame;
+
+            OneFrameMessages sendMessage = new();
+            oneFrameMessages.CopyTo(sendMessage);
+
+            RoomMessageHelper.BroadCast(room, sendMessage);
             
-            OneFrameMessages oneFrameMessages = frameBuffer.GetFrame(frame);
-            oneFrameMessages.Frame = frame;
-            if (oneFrameMessages.Inputs.Count != LSConstValue.MatchCount)
+            room.Update(oneFrameMessages, frame);
+        }
+
+        private static OneFrameMessages GetOneFrameMessage(this RoomServerUpdater self, int frame)
+        {
+            Room room = self.GetParent<Room>();
+            FrameBuffer frameBuffer = room.FrameBuffer;
+            OneFrameMessages oneFrameMessages = frameBuffer[frame];
+            if (oneFrameMessages == null)
             {
-                return;
+                throw new Exception($"get frame is null: {frame}, max frame: {frameBuffer.MaxFrame}");
             }
-            ++frameBuffer.RealFrame;
             
-            OneFrameMessages sendMessage = NetServices.Instance.FetchMessage<OneFrameMessages>();
+            frameBuffer.MoveForward(frame);
             
-            oneFrameMessages.CopyTo(sendMessage);
-            oneFrameMessages.Inputs.Clear();
-            oneFrameMessages.Frame = 0;
+            oneFrameMessages.Frame = frame;
+            if (oneFrameMessages.Inputs.Count == LSConstValue.MatchCount)
+            {
+                return oneFrameMessages;
+            }
+
+            OneFrameMessages preFrameMessages = frameBuffer[frame - 1];
             
-            RoomMessageHelper.BroadCast(room, sendMessage);
+            // 有人输入的消息没过来,给他使用上一帧的操作
+            foreach (long playerId in room.PlayerIds)
+            {
+                if (oneFrameMessages.Inputs.ContainsKey(playerId))
+                {
+                    continue;
+                }
+
+                if (preFrameMessages != null && preFrameMessages.Inputs.TryGetValue(playerId, out LSInput input))
+                {
+                    // 使用上一帧的输入
+                    oneFrameMessages.Inputs[playerId] = input;
+                }
+                else
+                {
+                    oneFrameMessages.Inputs[playerId] = new LSInput();
+                }
+            }
+
+            return oneFrameMessages;
         }
     }
 }

+ 5 - 4
Unity/Assets/Scripts/Hotfix/Share/LockStep/LSHelper.cs

@@ -13,16 +13,16 @@ namespace ET
             
             // 回滚
             room.LSWorld = frameBuffer.GetLSWorld(frame);
-            OneFrameMessages realFrameMessage = frameBuffer.GetFrame(frame);
+            OneFrameMessages realFrameMessage = frameBuffer[frame];
             // 执行RealFrame
             room.Update(realFrameMessage, frame);
 
             
             // 重新执行预测的帧
-            for (int i = frameBuffer.RealFrame + 1; i <= frameBuffer.PredictionFrame; ++i)
+            for (int i = room.RealFrame + 1; i <= room.PredictionFrame; ++i)
             {
-                OneFrameMessages oneFrameMessages = frameBuffer.GetFrame(i);
-                CopyOtherInputsTo(room, realFrameMessage, oneFrameMessages); // 重新预测剩下预测过的消息
+                OneFrameMessages oneFrameMessages = frameBuffer[i];
+                CopyOtherInputsTo(room, realFrameMessage, oneFrameMessages); // 重新预测消息
                 room.Update(oneFrameMessages, i);
             }
             
@@ -31,6 +31,7 @@ namespace ET
             Log.Debug($"roll back finish {frame}");
         }
 
+        // 重新调整预测消息,只需要调整其他玩家的输入
         private static void CopyOtherInputsTo(Room room, OneFrameMessages from, OneFrameMessages to)
         {
             long myId = room.GetComponent<RoomClientUpdater>().MyId;

+ 1 - 0
Unity/Assets/Scripts/Hotfix/Share/LockStep/RoomSystem.cs

@@ -16,6 +16,7 @@ namespace ET
             {
                 LockStepUnitInfo unitInfo = room2CStart.UnitInfo[i];
                 LSUnitFactory.Init(self.LSWorld, unitInfo);
+                self.PlayerIds.Add(unitInfo.PlayerId);
             }
         }
 

+ 23 - 19
Unity/Assets/Scripts/HotfixView/Client/LockStep/LSUnitViewComponentSystem.cs

@@ -14,23 +14,23 @@ namespace ET.Client
             }
         }
         
-        public class RollbackSystem: RollbackSystem<LSUnitViewComponent>
-        {
-            protected override void Rollback(LSUnitViewComponent self)
-            {
-                LSWorld lsWorld = self.GetParent<Room>().LSWorld;
-                foreach (LSUnitView child in self.Children.Values)
-                {
-                    LSUnit unit = lsWorld.Get(child.Id) as LSUnit;
-
-                    Vector3 pos = child.Transform.position;
-                    Vector3 to = unit.Position.ToVector();
-                    float t = (to - pos).magnitude / 9f;
-
-                    child.Transform.position = pos;
-                }
-            }
-        }
+        //public class RollbackSystem: RollbackSystem<LSUnitViewComponent>
+        //{
+        //    protected override void Rollback(LSUnitViewComponent self)
+        //    {
+        //        LSWorld lsWorld = self.GetParent<Room>().LSWorld;
+        //        foreach (LSUnitView child in self.Children.Values)
+        //        {
+        //            LSUnit unit = lsWorld.Get(child.Id) as LSUnit;
+//
+        //            Vector3 pos = child.Transform.position;
+        //            Vector3 to = unit.Position.ToVector();
+        //            float t = (to - pos).magnitude / 9f;
+//
+        //            child.Transform.position = pos;
+        //        }
+        //    }
+        //}
         
         public class UpdateSystem: UpdateSystem<LSUnitViewComponent>
         {
@@ -43,8 +43,12 @@ namespace ET.Client
 
                     Vector3 pos = child.Transform.position;
                     Vector3 to = unit.Position.ToVector();
-                    float t = (to - pos).magnitude / 9f;
-                    
+                    float distance = (to - pos).magnitude;
+                    if (distance < 0.5)
+                    {
+                        continue;
+                    }
+                    float t = distance / 9f;
                     child.Transform.position = Vector3.Lerp(pos, unit.Position.ToVector(), Time.deltaTime / t);
                 }
             }

+ 44 - 17
Unity/Assets/Scripts/Model/Share/LockStep/FrameBuffer.cs

@@ -6,45 +6,72 @@ namespace ET
 {
     public class FrameBuffer
     {
-        public int PredictionFrame { get; set; } = -1;
+        public int MaxFrame { get; private set; }
+        private readonly List<OneFrameMessages> messageBuffer;
+        private readonly List<MemoryBuffer> dataBuffer;
 
-        public int RealFrame { get; set; } = -1;
-        
-        private const int TotalFrameCount = 128;
-        
-        private readonly List<OneFrameMessages> messageBuffer = new(TotalFrameCount);
-        private readonly List<MemoryBuffer> dataBuffer = new(TotalFrameCount);
-
-        public FrameBuffer()
+        public FrameBuffer(int capacity = LSConstValue.FrameCountPerSecond * 10)
         {
+            this.MaxFrame = capacity - 1;
+            this.messageBuffer = new List<OneFrameMessages>(capacity);
+            this.dataBuffer = new List<MemoryBuffer>(capacity);
+            
             for (int i = 0; i < this.dataBuffer.Capacity; ++i)
             {
                 this.messageBuffer.Add(new OneFrameMessages());
-                this.dataBuffer.Add(new MemoryBuffer(10240));
+                MemoryBuffer memoryBuffer = new(10240);
+                memoryBuffer.SetLength(0);
+                memoryBuffer.Seek(0, SeekOrigin.Begin);
+                this.dataBuffer.Add(memoryBuffer);
             }
         }
         
-        public OneFrameMessages GetFrame(int frame)
+        public OneFrameMessages this[int frame]
+        {
+            get
+            {
+                if (frame < 0)
+                {
+                    return null;
+                }
+
+                if (frame > this.MaxFrame)
+                {
+                    return null;
+                }
+                OneFrameMessages oneFrameMessages = this.messageBuffer[frame % this.messageBuffer.Capacity];
+                return oneFrameMessages;
+            }
+        }
+
+        public void MoveForward(int frame)
         {
-            if (frame < 0)
+            if (this.MaxFrame - frame > LSConstValue.FrameCountPerSecond) // 至少留出1秒的空间
             {
-                return null;
+                return;
             }
-            OneFrameMessages oneFrameMessages = this.messageBuffer[frame % TotalFrameCount];
-            return oneFrameMessages;
+            
+            ++this.MaxFrame;
+            
+            Log.Debug($"framebuffer move forward: {this.MaxFrame}");
+            
+            OneFrameMessages oneFrameMessages = this[this.MaxFrame];
+            oneFrameMessages.Inputs.Clear();
+            oneFrameMessages.Frame = 0;
         }
 
         public LSWorld GetLSWorld(int frame)
         {
-            MemoryBuffer memoryBuffer = this.dataBuffer[frame % TotalFrameCount];
+            MemoryBuffer memoryBuffer = this.dataBuffer[frame % this.dataBuffer.Capacity];
             return MongoHelper.Deserialize(typeof (LSWorld), memoryBuffer) as LSWorld;
         }
 
         public void SaveLSWorld(int frame, LSWorld lsWorld)
         {
-            MemoryBuffer memoryBuffer = this.dataBuffer[frame % TotalFrameCount];
+            MemoryBuffer memoryBuffer = this.dataBuffer[frame % this.dataBuffer.Capacity];
             memoryBuffer.Seek(0, SeekOrigin.Begin);
             memoryBuffer.SetLength(0);
+            
             MongoHelper.Serialize(lsWorld, memoryBuffer);
             memoryBuffer.Seek(0, SeekOrigin.Begin);
         }

+ 1 - 0
Unity/Assets/Scripts/Model/Share/LockStep/LSConstValue.cs

@@ -4,5 +4,6 @@ namespace ET
     {
         public const int MatchCount = 2;
         public const int UpdateInterval = 50;
+        public const int FrameCountPerSecond = 1000 / UpdateInterval;
     }
 }

+ 6 - 0
Unity/Assets/Scripts/Model/Share/LockStep/Room.cs

@@ -30,5 +30,11 @@ namespace ET
         public FrameBuffer FrameBuffer { get; } = new();
 
         public FixedTimeCounter FixedTimeCounter { get; set; }
+
+        public List<long> PlayerIds { get; } = new(LSConstValue.MatchCount);
+        
+        public int PredictionFrame { get; set; } = -1;
+
+        public int RealFrame { get; set; } = -1;
     }
 }