[+] 接入ET8服务端

[+] 接入ET8服务端
This commit is contained in:
ALEXTANG
2023-07-13 12:23:48 +08:00
parent e0be062006
commit 336d4b2eb9
1316 changed files with 130657 additions and 626 deletions

View File

@@ -0,0 +1,11 @@
namespace ET.Client
{
[MessageHandler(SceneType.LockStep)]
public class Match2G_NotifyMatchSuccessHandler: MessageHandler<Match2G_NotifyMatchSuccess>
{
protected override async ETTask Run(Session session, Match2G_NotifyMatchSuccess message)
{
await LSSceneChangeHelper.SceneChangeTo(session.Root(), "Map1", message.ActorId.InstanceId);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cbf8b88a563eb46bfbaf987167d6a451
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,12 @@
namespace ET.Client
{
[MessageHandler(SceneType.LockStep)]
public class G2C_ReconnectHandler: MessageHandler<G2C_Reconnect>
{
protected override async ETTask Run(Session session, G2C_Reconnect message)
{
await LSSceneChangeHelper.SceneChangeToReconnect(session.Root(), message);
await ETTask.CompletedTask;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 41d6bacdbd4ca1843a732fc0144dc8fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,126 @@
using System.IO;
namespace ET.Client
{
public static partial class LSClientHelper
{
public static void RunLSRollbackSystem(Entity entity)
{
if (entity is LSEntity)
{
return;
}
LSEntitySystemSingleton.Instance.LSRollback(entity);
if (entity.ComponentsCount() > 0)
{
foreach (var kv in entity.Components)
{
RunLSRollbackSystem(kv.Value);
}
}
if (entity.ChildrenCount() > 0)
{
foreach (var kv in entity.Children)
{
RunLSRollbackSystem(kv.Value);
}
}
}
// 回滚
public static void Rollback(Room room, int frame)
{
room.LSWorld.Dispose();
FrameBuffer frameBuffer = room.FrameBuffer;
// 回滚
room.LSWorld = room.GetLSWorld(SceneType.LockStepClient, frame);
OneFrameInputs authorityFrameInput = frameBuffer.FrameInputs(frame);
// 执行AuthorityFrame
room.Update(authorityFrameInput);
room.SendHash(frame);
// 重新执行预测的帧
for (int i = room.AuthorityFrame + 1; i <= room.PredictionFrame; ++i)
{
OneFrameInputs oneFrameInputs = frameBuffer.FrameInputs(i);
LSClientHelper.CopyOtherInputsTo(room, authorityFrameInput, oneFrameInputs); // 重新预测消息
room.Update(oneFrameInputs);
}
RunLSRollbackSystem(room);
}
public static void SendHash(this Room self, int frame)
{
if (frame > self.AuthorityFrame)
{
return;
}
long hash = self.FrameBuffer.GetHash(frame);
C2Room_CheckHash c2RoomCheckHash = C2Room_CheckHash.Create();
c2RoomCheckHash.Frame = frame;
c2RoomCheckHash.Hash = hash;
self.Root().GetComponent<ClientSenderCompnent>().Send(c2RoomCheckHash);
}
// 重新调整预测消息,只需要调整其他玩家的输入
public static void CopyOtherInputsTo(Room room, OneFrameInputs from, OneFrameInputs to)
{
long myId = room.GetComponent<LSClientUpdater>().MyId;
foreach (var kv in from.Inputs)
{
if (kv.Key == myId)
{
continue;
}
to.Inputs[kv.Key] = kv.Value;
}
}
public static void SaveReplay(Room room, string path)
{
if (room.IsReplay)
{
return;
}
Log.Debug($"save replay: {path} frame: {room.Replay.FrameInputs.Count}");
byte[] bytes = MemoryPackHelper.Serialize(room.Replay);
File.WriteAllBytes(path, bytes);
}
public static void JumpReplay(Room room, int frame)
{
if (!room.IsReplay)
{
return;
}
if (frame >= room.Replay.FrameInputs.Count)
{
frame = room.Replay.FrameInputs.Count - 1;
}
int snapshotIndex = frame / LSConstValue.SaveLSWorldFrameCount;
Log.Debug($"jump replay start {room.AuthorityFrame} {frame} {snapshotIndex}");
if (snapshotIndex != room.AuthorityFrame / LSConstValue.SaveLSWorldFrameCount || frame < room.AuthorityFrame)
{
room.LSWorld.Dispose();
// 回滚
byte[] memoryBuffer = room.Replay.Snapshots[snapshotIndex];
LSWorld lsWorld = MongoHelper.Deserialize(typeof (LSWorld), memoryBuffer, 0, memoryBuffer.Length) as LSWorld;
room.LSWorld = lsWorld;
room.AuthorityFrame = snapshotIndex * LSConstValue.SaveLSWorldFrameCount;
RunLSRollbackSystem(room);
}
room.FixedTimeCounter.Reset(room.Fiber.TimeInfo.ServerFrameTime() - frame * LSConstValue.UpdateInterval, 0);
Log.Debug($"jump replay finish {frame}");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 915aaa51beb994638bd10e0d2ed9d8a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,83 @@
using System;
using System.IO;
namespace ET.Client
{
[EntitySystemOf(typeof(LSClientUpdater))]
[FriendOf(typeof (LSClientUpdater))]
public static partial class LSClientUpdaterSystem
{
[EntitySystem]
private static void Awake(this LSClientUpdater self)
{
Room room = self.GetParent<Room>();
self.MyId = room.Root().GetComponent<PlayerComponent>().MyId;
}
[EntitySystem]
private static void Update(this LSClientUpdater self)
{
Room room = self.GetParent<Room>();
long timeNow = self.Fiber().TimeInfo.ServerNow();
Scene root = room.Root();
int i = 0;
while (true)
{
if (timeNow < room.FixedTimeCounter.FrameTime(room.PredictionFrame + 1))
{
return;
}
// 最多只预测5帧
if (room.PredictionFrame - room.AuthorityFrame > 5)
{
return;
}
++room.PredictionFrame;
OneFrameInputs oneFrameInputs = self.GetOneFrameMessages(room.PredictionFrame);
room.Update(oneFrameInputs);
room.SendHash(room.PredictionFrame);
room.SpeedMultiply = ++i;
FrameMessage frameMessage = FrameMessage.Create();
frameMessage.Frame = room.PredictionFrame;
frameMessage.Input = self.Input;
root.GetComponent<ClientSenderCompnent>().Send(frameMessage);
long timeNow2 = self.Fiber().TimeInfo.ServerNow();
if (timeNow2 - timeNow > 5)
{
break;
}
}
}
private static OneFrameInputs GetOneFrameMessages(this LSClientUpdater self, int frame)
{
Room room = self.GetParent<Room>();
FrameBuffer frameBuffer = room.FrameBuffer;
if (frame <= room.AuthorityFrame)
{
return frameBuffer.FrameInputs(frame);
}
// predict
OneFrameInputs predictionFrame = frameBuffer.FrameInputs(frame);
frameBuffer.MoveForward(frame);
if (frameBuffer.CheckFrame(room.AuthorityFrame))
{
OneFrameInputs authorityFrame = frameBuffer.FrameInputs(room.AuthorityFrame);
authorityFrame.CopyTo(predictionFrame);
}
predictionFrame.Inputs[self.MyId] = self.Input;
return predictionFrame;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 46f47e16ad6bc417391e3cdee1b5a851
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,67 @@
using System;
namespace ET.Client
{
[EntitySystemOf(typeof(LSReplayUpdater))]
[FriendOf(typeof(LSReplayUpdater))]
public static partial class LSReplayUpdaterSystem
{
[EntitySystem]
private static void Awake(this LSReplayUpdater self)
{
}
[EntitySystem]
private static void Update(this LSReplayUpdater self)
{
Room room = self.GetParent<Room>();
Fiber fiber = self.Fiber();
long timeNow = fiber.TimeInfo.ServerNow();
int i = 0;
while (true)
{
if (room.AuthorityFrame + 1 >= room.Replay.FrameInputs.Count)
{
break;
}
if (timeNow < room.FixedTimeCounter.FrameTime(room.AuthorityFrame + 1))
{
break;
}
++room.AuthorityFrame;
OneFrameInputs oneFrameInputs = room.Replay.FrameInputs[room.AuthorityFrame];
room.Update(oneFrameInputs);
room.SpeedMultiply = ++i;
long timeNow2 = fiber.TimeInfo.ServerNow();
if (timeNow2 - timeNow > 5)
{
break;
}
}
}
public static void ChangeReplaySpeed(this LSReplayUpdater self)
{
Room room = self.Room();
LSReplayUpdater lsReplayUpdater = room.GetComponent<LSReplayUpdater>();
if (lsReplayUpdater.ReplaySpeed == 8)
{
lsReplayUpdater.ReplaySpeed = 1;
}
else
{
lsReplayUpdater.ReplaySpeed *= 2;
}
int updateInterval = LSConstValue.UpdateInterval / lsReplayUpdater.ReplaySpeed;
room.FixedTimeCounter.ChangeInterval(updateInterval, room.AuthorityFrame);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1f2dc3825dd2f4641bce36a7b8b7708e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,72 @@
namespace ET.Client
{
public static partial class LSSceneChangeHelper
{
// 场景切换协程
public static async ETTask SceneChangeTo(Scene root, string sceneName, long sceneInstanceId)
{
root.RemoveComponent<Room>();
Room room = root.AddComponentWithId<Room>(sceneInstanceId);
room.Name = sceneName;
// 等待表现层订阅的事件完成
await EventSystem.Instance.PublishAsync(root, new EventType.LSSceneChangeStart() {Room = room});
root.GetComponent<ClientSenderCompnent>().Send(new C2Room_ChangeSceneFinish());
// 等待Room2C_EnterMap消息
WaitType.Wait_Room2C_Start waitRoom2CStart = await root.GetComponent<ObjectWait>().Wait<WaitType.Wait_Room2C_Start>();
room.LSWorld = new LSWorld(SceneType.LockStepClient);
room.Init(waitRoom2CStart.Message.UnitInfo, waitRoom2CStart.Message.StartTime);
room.AddComponent<LSClientUpdater>();
// 这个事件中可以订阅取消loading
EventSystem.Instance.Publish(root, new EventType.LSSceneInitFinish());
}
// 场景切换协程
public static async ETTask SceneChangeToReplay(Scene root, Replay replay)
{
root.RemoveComponent<Room>();
Room room = root.AddComponent<Room>();
room.Name = "Map1";
room.IsReplay = true;
room.Replay = replay;
room.LSWorld = new LSWorld(SceneType.LockStepClient);
room.Init(replay.UnitInfos, root.Fiber().TimeInfo.ServerFrameTime());
// 等待表现层订阅的事件完成
await EventSystem.Instance.PublishAsync(root, new EventType.LSSceneChangeStart() {Room = room});
room.AddComponent<LSReplayUpdater>();
// 这个事件中可以订阅取消loading
EventSystem.Instance.Publish(root, new EventType.LSSceneInitFinish());
}
// 场景切换协程
public static async ETTask SceneChangeToReconnect(Scene root, G2C_Reconnect message)
{
root.RemoveComponent<Room>();
Room room = root.AddComponent<Room>();
room.Name = "Map1";
room.LSWorld = new LSWorld(SceneType.LockStepClient);
room.Init(message.UnitInfos, message.StartTime, message.Frame);
// 等待表现层订阅的事件完成
await EventSystem.Instance.PublishAsync(root, new EventType.LSSceneChangeStart() {Room = room});
room.AddComponent<LSClientUpdater>();
// 这个事件中可以订阅取消loading
EventSystem.Instance.Publish(root, new EventType.LSSceneInitFinish());
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6b38bd6590f604a9e9bc0360a81fe2b3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,51 @@
using System;
namespace ET.Client
{
[MessageHandler(SceneType.LockStep)]
public class OneFrameInputsHandler: MessageHandler<OneFrameInputs>
{
protected override async ETTask Run(Session session, OneFrameInputs input)
{
using var _ = input ; // 方法结束时回收消息
Room room = session.Scene().GetComponent<Room>();
Log.Debug($"OneFrameInputs: {room.AuthorityFrame + 1} {input.ToJson()}");
FrameBuffer frameBuffer = room.FrameBuffer;
++room.AuthorityFrame;
// 服务端返回的消息比预测的还早
if (room.AuthorityFrame > room.PredictionFrame)
{
OneFrameInputs authorityFrame = frameBuffer.FrameInputs(room.AuthorityFrame);
input.CopyTo(authorityFrame);
}
else
{
// 服务端返回来的消息,跟预测消息对比
OneFrameInputs predictionInput = frameBuffer.FrameInputs(room.AuthorityFrame);
// 对比失败有两种可能,
// 1是别人的输入预测失败这种很正常
// 2 自己的输入对比失败,这种情况是自己发送的消息比服务器晚到了,服务器使用了你的上一次输入
// 回滚重新预测的时候,自己的输入不用变化
if (input != predictionInput)
{
Log.Debug($"frame diff: {predictionInput} {input}");
input.CopyTo(predictionInput);
// 回滚到frameBuffer.AuthorityFrame
Log.Debug($"roll back start {room.AuthorityFrame}");
LSClientHelper.Rollback(room, room.AuthorityFrame);
Log.Debug($"roll back finish {room.AuthorityFrame}");
}
else // 对比成功
{
room.Record(room.AuthorityFrame);
room.SendHash(room.AuthorityFrame);
}
}
await ETTask.CompletedTask;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ce16289caea864b01988193d7ea2e08a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,25 @@
namespace ET.Client
{
[ActorMessageHandler(SceneType.LockStep)]
public class Room2C_AdjustUpdateTimeHandler: ActorMessageHandler<Scene, Room2C_AdjustUpdateTime>
{
protected override async ETTask Run(Scene root, Room2C_AdjustUpdateTime message)
{
Room room = root.GetComponent<Room>();
int newInterval = (1000 + (message.DiffTime - LSConstValue.UpdateInterval)) * LSConstValue.UpdateInterval / 1000;
if (newInterval < 40)
{
newInterval = 40;
}
if (newInterval > 66)
{
newInterval = 66;
}
room.FixedTimeCounter.ChangeInterval(newInterval, room.PredictionFrame);
await ETTask.CompletedTask;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e4df743a2d294ae59bf4844174591716
timeCreated: 1682414000

View File

@@ -0,0 +1,25 @@
namespace ET.Client
{
[ActorMessageHandler(SceneType.LockStep)]
public class Room2C_CheckHashFailHandler: ActorMessageHandler<Scene, Room2C_CheckHashFail>
{
protected override async ETTask Run(Scene root, Room2C_CheckHashFail message)
{
LSWorld serverWorld = MongoHelper.Deserialize(typeof(LSWorld), message.LSWorldBytes, 0, message.LSWorldBytes.Length) as LSWorld;
using (root.AddChild(serverWorld))
{
Log.Debug($"check hash fail, server: {message.Frame} {serverWorld.ToJson()}");
}
Room room = root.GetComponent<Room>();
LSWorld clientWorld = room.GetLSWorld(SceneType.LockStepClient, message.Frame);
using (root.AddChild(clientWorld))
{
Log.Debug($"check hash fail, client: {message.Frame} {clientWorld.ToJson()}");
}
message.Dispose();
await ETTask.CompletedTask;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bd750474e6e5d437e8f1ac8279a7489d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,12 @@
namespace ET.Client
{
[ActorMessageHandler(SceneType.LockStep)]
public class Room2C_EnterMapHandler: ActorMessageHandler<Scene, Room2C_Start>
{
protected override async ETTask Run(Scene root, Room2C_Start message)
{
root.GetComponent<ObjectWait>().Notify(new WaitType.Wait_Room2C_Start() {Message = message});
await ETTask.CompletedTask;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c61c8470e59f14b9eb2ace115446c1c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: