提交 2b191c8b 编写于 作者: T tanghai

1.服务端消息大部分可以热更,把能热更的消息移动到hotfix层,需要保存状态的消息例如FrameMessage需要手写放到ETModel层

2.一部分服务端逻辑移动到hotfix层
上级 9235acce
......@@ -44,18 +44,6 @@ message Actor_CreateUnits // IActorMessage
repeated UnitInfo Units = 1;
}
message OneFrameMessage // IActorMessage
{
required uint16 Op = 1;
required bytes AMessage = 2;
}
message FrameMessage // IActorMessage
{
required int32 Frame = 1;
repeated OneFrameMessage Messages = 2;
}
message Frame_ClickMap // IFrameMessage
{
required int32 X = 1;
......
......@@ -36,8 +36,6 @@ namespace ETHotfix
{
AppType appType = self.Entity.GetComponent<StartConfigComponent>().StartConfig.AppType;
Log.Info("apptype: " + appType);
self.ActorMessageHandlers.Clear();
self.ActorTypeHandlers.Clear();
......
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using ETModel;
namespace ETHotfix
{
[ObjectSystem]
public class ActorProxyAwakeSystem : AwakeSystem<ActorProxy>
{
public override void Awake(ActorProxy self)
{
self.Awake();
}
}
[ObjectSystem]
public class ActorProxyStartSystem : StartSystem<ActorProxy>
{
public override async void Start(ActorProxy self)
{
int appId = await Game.Scene.GetComponent<LocationProxyComponent>().Get(self.Id);
self.Address = Game.Scene.GetComponent<StartConfigComponent>().Get(appId).GetComponent<InnerConfig>().IPEndPoint;
self.UpdateAsync();
}
}
[ObjectSystem]
public class ActorProxyDestroySystem : DestroySystem<ActorProxy>
{
public override void Destroy(ActorProxy self)
{
if (self.IsDisposed)
{
return;
}
self.LastSendTime = 0;
self.Address = null;
while (self.WaitingTasks.Count > 0)
{
ActorTask actorTask = self.WaitingTasks.Dequeue();
actorTask.RunFail(ErrorCode.ERR_NotFoundActor);
}
self.failTimes = 0;
var t = self.tcs;
self.tcs = null;
t?.SetResult(new ActorTask());
}
}
public static class ActorProxyEx
{
public static void Awake(this ActorProxy self)
{
self.LastSendTime = TimeHelper.Now();
self.tcs = null;
self.CancellationTokenSource = new CancellationTokenSource();
}
private static void Add(this ActorProxy self, ActorTask task)
{
if (self.IsDisposed)
{
throw new Exception("ActorProxy Disposed! dont hold actorproxy");
}
self.WaitingTasks.Enqueue(task);
// failtimes > 0表示正在重试,这时候不能加到正在发送队列
if (self.failTimes == 0)
{
self.AllowGet();
}
}
private static void AllowGet(this ActorProxy self)
{
if (self.tcs == null || self.WaitingTasks.Count <= 0)
{
return;
}
ActorTask task = self.WaitingTasks.Peek();
var t = self.tcs;
self.tcs = null;
t.SetResult(task);
}
private static Task<ActorTask> GetAsync(this ActorProxy self)
{
if (self.WaitingTasks.Count > 0)
{
ActorTask task = self.WaitingTasks.Peek();
return Task.FromResult(task);
}
self.tcs = new TaskCompletionSource<ActorTask>();
return self.tcs.Task;
}
public static async void UpdateAsync(this ActorProxy self)
{
while (true)
{
ActorTask actorTask = await self.GetAsync();
if (self.IsDisposed)
{
return;
}
try
{
await self.RunTask(actorTask);
}
catch (Exception e)
{
Log.Error(e);
return;
}
}
}
private static async Task RunTask(this ActorProxy self, ActorTask task)
{
try
{
IResponse response = await task.Run();
// 如果没找到Actor,重试
if (response.Error == ErrorCode.ERR_NotFoundActor)
{
self.CancellationTokenSource.Cancel();
++self.failTimes;
// 失败10次则清空actor发送队列,返回失败
if (self.failTimes > 10)
{
// 失败直接删除actorproxy
Log.Info($"actor send message fail, actorid: {self.Id}");
Game.Scene.GetComponent<ActorProxyComponent>().Remove(self.Id);
return;
}
// 等待1s再发送
await Game.Scene.GetComponent<TimerComponent>().WaitAsync(1000);
int appId = await Game.Scene.GetComponent<LocationProxyComponent>().Get(self.Id);
self.Address = Game.Scene.GetComponent<StartConfigComponent>().Get(appId).GetComponent<InnerConfig>().IPEndPoint;
self.CancellationTokenSource = new CancellationTokenSource();
self.AllowGet();
return;
}
// 发送成功
self.LastSendTime = TimeHelper.Now();
self.failTimes = 0;
self.WaitingTasks.Dequeue();
}
catch (Exception e)
{
Log.Error(e);
}
}
public static void Send(this ActorProxy self, IActorMessage message)
{
ActorTask task = new ActorTask { message = message, proxy = self };
self.Add(task);
}
public static Task<IResponse> Call(this ActorProxy self, IActorRequest request)
{
ActorTask task = new ActorTask { message = request, proxy = self, Tcs = new TaskCompletionSource<IResponse>() };
self.Add(task);
return task.Tcs.Task;
}
public static string DebugQueue(this ActorProxy self, Queue<ActorTask> tasks)
{
string s = "";
foreach (ActorTask task in tasks)
{
s += $" {task.message.GetType().Name}";
}
return s;
}
}
}
\ No newline at end of file
using System;
using System.Net;
using System.Threading.Tasks;
using ETModel;
namespace ETModel
namespace ETHotfix
{
[ObjectSystem]
public class BenchmarkComponentSystem : AwakeSystem<BenchmarkComponent, IPEndPoint>
......@@ -13,20 +14,16 @@ namespace ETModel
}
}
public class BenchmarkComponent: Component
public static class BenchmarkComponentEx
{
private int k;
private long time1 = TimeHelper.ClientNow();
public void Awake(IPEndPoint ipEndPoint)
public static void Awake(this BenchmarkComponent self, IPEndPoint ipEndPoint)
{
try
{
NetOuterComponent networkComponent = Game.Scene.GetComponent<NetOuterComponent>();
for (int i = 0; i < 1000; i++)
{
this.TestAsync(networkComponent, ipEndPoint, i);
self.TestAsync(networkComponent, ipEndPoint, i);
}
}
catch (Exception e)
......@@ -35,7 +32,7 @@ namespace ETModel
}
}
public async void TestAsync(NetOuterComponent networkComponent, IPEndPoint ipEndPoint, int j)
public static async void TestAsync(this BenchmarkComponent self, NetOuterComponent networkComponent, IPEndPoint ipEndPoint, int j)
{
try
{
......@@ -45,7 +42,7 @@ namespace ETModel
while (i < 100000000)
{
++i;
await this.Send(session, j);
await self.Send(session, j);
}
}
}
......@@ -59,37 +56,27 @@ namespace ETModel
}
}
public async Task Send(Session session, int j)
public static async Task Send(this BenchmarkComponent self, Session session, int j)
{
try
{
await session.Call(new C2R_Ping());
++this.k;
++self.k;
if (this.k % 100000 != 0)
if (self.k % 100000 != 0)
{
return;
}
long time2 = TimeHelper.ClientNow();
long time = time2 - this.time1;
this.time1 = time2;
Log.Info($"Benchmark k: {this.k} 每10W次耗时: {time} ms");
long time = time2 - self.time1;
self.time1 = time2;
Log.Info($"Benchmark k: {self.k} 每10W次耗时: {time} ms");
}
catch (Exception e)
{
Log.Error(e);
}
}
public override void Dispose()
{
if (this.IsDisposed)
{
return;
}
base.Dispose();
}
}
}
\ No newline at end of file
......@@ -2,16 +2,10 @@
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using ETModel;
namespace ETModel
namespace ETHotfix
{
public enum LockStatus
{
LockedNot,
LockRequesting,
Locked,
}
[ObjectSystem]
public class LockComponentAwakeSystem : AwakeSystem<LockComponent, IPEndPoint>
{
......@@ -24,92 +18,87 @@ namespace ETModel
/// <summary>
/// 分布式锁组件,Unit对象可能在不同进程上有镜像,访问该对象的时候需要对他加锁
/// </summary>
public class LockComponent: Component
public static class LockComponentEx
{
private LockStatus status = LockStatus.LockedNot;
private IPEndPoint address;
private int lockCount;
private readonly Queue<TaskCompletionSource<bool>> queue = new Queue<TaskCompletionSource<bool>>();
public void Awake(IPEndPoint addr)
public static void Awake(this LockComponent self, IPEndPoint addr)
{
this.address = addr;
self.address = addr;
}
public async Task Lock()
public static async Task Lock(this LockComponent self)
{
++this.lockCount;
++self.lockCount;
if (this.status == LockStatus.Locked)
if (self.status == LockStatus.Locked)
{
return;
}
if (this.status == LockStatus.LockRequesting)
if (self.status == LockStatus.LockRequesting)
{
await WaitLock();
await self.WaitLock();
return;
}
this.status = LockStatus.LockRequesting;
self.status = LockStatus.LockRequesting;
// 真身直接本地请求锁,镜像需要调用Rpc获取锁
MasterComponent masterComponent = this.Entity.GetComponent<MasterComponent>();
MasterComponent masterComponent = self.Entity.GetComponent<MasterComponent>();
if (masterComponent != null)
{
await masterComponent.Lock(this.address);
await masterComponent.Lock(self.address);
}
else
{
RequestLock();
await WaitLock();
self.RequestLock();
await self.WaitLock();
}
}
private Task<bool> WaitLock()
private static Task<bool> WaitLock(this LockComponent self)
{
if (this.status == LockStatus.Locked)
if (self.status == LockStatus.Locked)
{
return Task.FromResult(true);
}
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
this.queue.Enqueue(tcs);
self.queue.Enqueue(tcs);
return tcs.Task;
}
private async void RequestLock()
private static async void RequestLock(this LockComponent self)
{
try
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(this.address);
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(self.address);
string serverAddress = Game.Scene.GetComponent<StartConfigComponent>().StartConfig.ServerIP;
G2G_LockRequest request = new G2G_LockRequest { Id = this.Entity.Id, Address = serverAddress };
G2G_LockRequest request = new G2G_LockRequest { Id = self.Entity.Id, Address = serverAddress };
await session.Call(request);
this.status = LockStatus.Locked;
self.status = LockStatus.Locked;
foreach (TaskCompletionSource<bool> taskCompletionSource in this.queue)
foreach (TaskCompletionSource<bool> taskCompletionSource in self.queue)
{
taskCompletionSource.SetResult(true);
}
this.queue.Clear();
self.queue.Clear();
}
catch (Exception e)
{
Log.Error($"获取锁失败: {this.address} {this.Entity.Id} {e}");
Log.Error($"获取锁失败: {self.address} {self.Entity.Id} {e}");
}
}
public async Task Release()
public static async Task Release(this LockComponent self)
{
--this.lockCount;
if (this.lockCount != 0)
--self.lockCount;
if (self.lockCount != 0)
{
return;
}
this.status = LockStatus.LockedNot;
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(this.address);
self.status = LockStatus.LockedNot;
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(self.address);
G2G_LockReleaseRequest request = new G2G_LockReleaseRequest();
await session.Call(request);
}
......
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using ETModel;
namespace ETHotfix
{
public static class MasterComponentEx
{
public static void AddGhost(this MasterComponent self, IPEndPoint address)
{
self.ghostsAddress.Add(address);
}
public static void RemoveGhost(this MasterComponent self, IPEndPoint address)
{
self.ghostsAddress.Remove(address);
}
public static Task<bool> Lock(this MasterComponent self, IPEndPoint address)
{
if (self.lockedAddress == null)
{
self.lockedAddress = address;
return Task.FromResult(true);
}
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
LockInfo lockInfo = new LockInfo(address, tcs);
self.queue.Enqueue(lockInfo);
return tcs.Task;
}
public static void Release(this MasterComponent self, IPEndPoint address)
{
if (!self.lockedAddress.Equals(address))
{
Log.Error($"解锁地址与锁地址不匹配! {self.lockedAddress} {address}");
return;
}
if (self.queue.Count == 0)
{
self.lockedAddress = null;
return;
}
LockInfo lockInfo = self.queue.Dequeue();
self.lockedAddress = lockInfo.Address;
lockInfo.Tcs.SetResult(true);
}
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using ETModel;
namespace ETHotfix
{
[ObjectSystem]
public class DbProxyComponentSystem : AwakeSystem<DBProxyComponent>
{
public override void Awake(DBProxyComponent self)
{
self.Awake();
}
}
/// <summary>
/// 用来与数据库操作代理
/// </summary>
public static class DBProxyComponentEx
{
public static void Awake(this DBProxyComponent self)
{
StartConfig dbStartConfig = Game.Scene.GetComponent<StartConfigComponent>().DBConfig;
self.dbAddress = dbStartConfig.GetComponent<InnerConfig>().IPEndPoint;
}
public static async Task Save(this DBProxyComponent self, ComponentWithId component, bool needCache = true)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(self.dbAddress);
await session.Call(new DBSaveRequest { Component = component, NeedCache = needCache});
}
public static async Task SaveBatch(this DBProxyComponent self, List<ComponentWithId> components, bool needCache = true)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(self.dbAddress);
await session.Call(new DBSaveBatchRequest { Components = components, NeedCache = needCache});
}
public static async Task Save(this DBProxyComponent self, ComponentWithId component, bool needCache, CancellationToken cancellationToken)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(self.dbAddress);
await session.Call(new DBSaveRequest { Component = component, NeedCache = needCache}, cancellationToken);
}
public static async void SaveLog(this DBProxyComponent self, ComponentWithId component)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(self.dbAddress);
await session.Call(new DBSaveRequest { Component = component, NeedCache = false, CollectionName = "Log" });
}
public static async Task<T> Query<T>(this DBProxyComponent self, long id, bool needCache = true) where T: ComponentWithId
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(self.dbAddress);
DBQueryResponse dbQueryResponse = (DBQueryResponse)await session.Call(new DBQueryRequest { CollectionName = typeof(T).Name, Id = id, NeedCache = needCache });
return (T)dbQueryResponse.Component;
}
public static async Task<List<T>> QueryBatch<T>(this DBProxyComponent self, List<long> ids, bool needCache = true) where T : ComponentWithId
{
List<T> list = new List<T>();
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(self.dbAddress);
DBQueryBatchResponse dbQueryBatchResponse = (DBQueryBatchResponse)await session.Call(new DBQueryBatchRequest { CollectionName = typeof(T).Name, IdList = ids, NeedCache = needCache});
foreach (ComponentWithId component in dbQueryBatchResponse.Components)
{
list.Add((T)component);
}
return list;
}
public static async Task<List<T>> QueryJson<T>(this DBProxyComponent self, string json) where T : ComponentWithId
{
List<T> list = new List<T>();
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(self.dbAddress);
DBQueryJsonResponse dbQueryJsonResponse = (DBQueryJsonResponse)await session.Call(new DBQueryJsonRequest { CollectionName = typeof(T).Name, Json = json });
foreach (ComponentWithId component in dbQueryJsonResponse.Components)
{
list.Add((T)component);
}
return list;
}
}
}
\ No newline at end of file
using System.Threading.Tasks;
using ETModel;
namespace ETHotfix
{
[ObjectSystem]
public class LocationProxyComponentSystem : AwakeSystem<LocationProxyComponent>
{
public override void Awake(LocationProxyComponent self)
{
self.Awake();
}
}
public static class LocationProxyComponentEx
{
public static void Awake(this LocationProxyComponent self)
{
StartConfigComponent startConfigComponent = Game.Scene.GetComponent<StartConfigComponent>();
self.AppId = startConfigComponent.StartConfig.AppId;
StartConfig startConfig = startConfigComponent.LocationConfig;
self.LocationAddress = startConfig.GetComponent<InnerConfig>().IPEndPoint;
}
public static async Task Add(this LocationProxyComponent self, long key)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(self.LocationAddress);
await session.Call(new ObjectAddRequest() { Key = key, AppId = self.AppId });
}
public static async Task Lock(this LocationProxyComponent self, long key, int time = 1000)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(self.LocationAddress);
await session.Call(new ObjectLockRequest() { Key = key, LockAppId = self.AppId, Time = time });
}
public static async Task UnLock(this LocationProxyComponent self, long key, int value)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(self.LocationAddress);
await session.Call(new ObjectUnLockRequest() { Key = key, UnLockAppId = self.AppId, AppId = value});
}
public static async Task Remove(this LocationProxyComponent self, long key)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(self.LocationAddress);
await session.Call(new ObjectRemoveRequest() { Key = key });
}
public static async Task<int> Get(this LocationProxyComponent self, long key)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(self.LocationAddress);
ObjectGetResponse response = (ObjectGetResponse)await session.Call(new ObjectGetRequest() { Key = key });
return response.AppId;
}
}
}
\ No newline at end of file
......@@ -2,7 +2,7 @@ using ProtoBuf;
using ETModel;
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
namespace ETModel
namespace ETHotfix
{
[Message(HotfixOpcode.C2R_Login)]
[ProtoContract]
......
......@@ -2,7 +2,7 @@ using ProtoBuf;
using ETModel;
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
namespace ETModel
namespace ETHotfix
{
/// <summary>
/// 传送unit
......
......@@ -2,7 +2,7 @@ using ProtoBuf;
using ETModel;
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
namespace ETModel
namespace ETHotfix
{
[Message(OuterOpcode.Actor_Test)]
[ProtoContract]
......@@ -142,42 +142,6 @@ namespace ETModel
}
[Message(OuterOpcode.OneFrameMessage)]
[ProtoContract]
public partial class OneFrameMessage: IActorMessage
{
[ProtoMember(90, IsRequired = true)]
public int RpcId { get; set; }
[ProtoMember(93, IsRequired = true)]
public long ActorId { get; set; }
[ProtoMember(1, IsRequired = true)]
public ushort Op;
[ProtoMember(2, IsRequired = true)]
public byte[] AMessage;
}
[Message(OuterOpcode.FrameMessage)]
[ProtoContract]
public partial class FrameMessage: IActorMessage
{
[ProtoMember(90, IsRequired = true)]
public int RpcId { get; set; }
[ProtoMember(93, IsRequired = true)]
public long ActorId { get; set; }
[ProtoMember(1, IsRequired = true)]
public int Frame;
[ProtoMember(2)]
public List<OneFrameMessage> Messages = new List<OneFrameMessage>();
}
[Message(OuterOpcode.Frame_ClickMap)]
[ProtoContract]
public partial class Frame_ClickMap: IFrameMessage
......
......@@ -11,12 +11,10 @@ namespace ETModel
public const ushort G2C_EnterMap = 107;
public const ushort UnitInfo = 108;
public const ushort Actor_CreateUnits = 109;
public const ushort OneFrameMessage = 110;
public const ushort FrameMessage = 111;
public const ushort Frame_ClickMap = 112;
public const ushort C2M_Reload = 113;
public const ushort M2C_Reload = 114;
public const ushort C2R_Ping = 115;
public const ushort R2C_Ping = 116;
public const ushort Frame_ClickMap = 110;
public const ushort C2M_Reload = 111;
public const ushort M2C_Reload = 112;
public const ushort C2R_Ping = 113;
public const ushort R2C_Ping = 114;
}
}
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyName>Hotfix</AssemblyName>
<RootNamespace>ETHotfix</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;DEBUG;NETCOREAPP2_0;SERVER</DefineConstants>
<OutputPath>..\..\Bin\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE;RELEASE;NETCOREAPP2_0;SERVER</DefineConstants>
<OutputPath>..\..\Bin\</OutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Other\**" />
<EmbeddedResource Remove="Other\**" />
<None Remove="Other\**" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Model\Server.Model.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Module\Http\" />
</ItemGroup>
</Project>
</Project>
\ No newline at end of file
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
namespace ETModel
{
public class LockInfo
{
public IPEndPoint Address;
public TaskCompletionSource<bool> Tcs;
public LockInfo(IPEndPoint address, TaskCompletionSource<bool> tcs)
{
this.Address = address;
this.Tcs = tcs;
}
}
public class MasterComponent : Component
{
/// 镜像的地址
private readonly List<IPEndPoint> ghostsAddress = new List<IPEndPoint>();
/// 当前获取锁的进程地址
private IPEndPoint lockedAddress;
/// 请求锁的队列
private readonly Queue<LockInfo> queue = new Queue<LockInfo>();
public void AddGhost(IPEndPoint address)
{
this.ghostsAddress.Add(address);
}
public void RemoveGhost(IPEndPoint address)
{
this.ghostsAddress.Remove(address);
}
public Task<bool> Lock(IPEndPoint address)
{
if (this.lockedAddress == null)
{
this.lockedAddress = address;
return Task.FromResult(true);
}
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
LockInfo lockInfo = new LockInfo(address, tcs);
this.queue.Enqueue(lockInfo);
return tcs.Task;
}
public void Release(IPEndPoint address)
{
if (!this.lockedAddress.Equals(address))
{
Log.Error($"解锁地址与锁地址不匹配! {this.lockedAddress} {address}");
return;
}
if (this.queue.Count == 0)
{
this.lockedAddress = null;
return;
}
LockInfo lockInfo = this.queue.Dequeue();
this.lockedAddress = lockInfo.Address;
lockInfo.Tcs.SetResult(true);
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace ETModel
{
[ObjectSystem]
public class ActorProxyAwakeSystem : AwakeSystem<ActorProxy>
{
public override void Awake(ActorProxy self)
{
self.Awake();
}
}
[ObjectSystem]
public class ActorProxyStartSystem : StartSystem<ActorProxy>
{
public override async void Start(ActorProxy self)
{
int appId = await Game.Scene.GetComponent<LocationProxyComponent>().Get(self.Id);
self.Address = Game.Scene.GetComponent<StartConfigComponent>().Get(appId).GetComponent<InnerConfig>().IPEndPoint;
self.UpdateAsync();
}
}
public sealed class ActorProxy : ComponentWithId
{
// actor的地址
......@@ -38,173 +16,10 @@ namespace ETModel
// 最近发送消息的时间
public long LastSendTime;
private TaskCompletionSource<ActorTask> tcs;
public TaskCompletionSource<ActorTask> tcs;
public CancellationTokenSource CancellationTokenSource;
private int failTimes;
public void Awake()
{
this.LastSendTime = TimeHelper.Now();
this.tcs = null;
this.CancellationTokenSource = new CancellationTokenSource();
}
public override void Dispose()
{
if (this.IsDisposed)
{
return;
}
base.Dispose();
this.LastSendTime = 0;
this.Address = null;
while (this.WaitingTasks.Count > 0)
{
ActorTask actorTask = this.WaitingTasks.Dequeue();
actorTask.RunFail(ErrorCode.ERR_NotFoundActor);
}
this.failTimes = 0;
var t = this.tcs;
this.tcs = null;
t?.SetResult(new ActorTask());
}
private void Add(ActorTask task)
{
if (this.IsDisposed)
{
throw new Exception("ActorProxy Disposed! dont hold actorproxy");
}
this.WaitingTasks.Enqueue(task);
// failtimes > 0表示正在重试,这时候不能加到正在发送队列
if (this.failTimes == 0)
{
this.AllowGet();
}
}
private void AllowGet()
{
if (this.tcs == null || this.WaitingTasks.Count <= 0)
{
return;
}
ActorTask task = this.WaitingTasks.Peek();
var t = this.tcs;
this.tcs = null;
t.SetResult(task);
}
private Task<ActorTask> GetAsync()
{
if (this.WaitingTasks.Count > 0)
{
ActorTask task = this.WaitingTasks.Peek();
return Task.FromResult(task);
}
this.tcs = new TaskCompletionSource<ActorTask>();
return this.tcs.Task;
}
public async void UpdateAsync()
{
while (true)
{
ActorTask actorTask = await this.GetAsync();
if (this.IsDisposed)
{
return;
}
try
{
await this.RunTask(actorTask);
}
catch (Exception e)
{
Log.Error(e);
return;
}
}
}
private async Task RunTask(ActorTask task)
{
try
{
IResponse response = await task.Run();
// 如果没找到Actor,重试
if (response.Error == ErrorCode.ERR_NotFoundActor)
{
this.CancellationTokenSource.Cancel();
++this.failTimes;
// 失败10次则清空actor发送队列,返回失败
if (this.failTimes > 10)
{
// 失败直接删除actorproxy
Log.Info($"actor send message fail, actorid: {this.Id}");
Game.Scene.GetComponent<ActorProxyComponent>().Remove(this.Id);
return;
}
// 等待1s再发送
await Game.Scene.GetComponent<TimerComponent>().WaitAsync(1000);
int appId = await Game.Scene.GetComponent<LocationProxyComponent>().Get(this.Id);
this.Address = Game.Scene.GetComponent<StartConfigComponent>().Get(appId).GetComponent<InnerConfig>().IPEndPoint;
this.CancellationTokenSource = new CancellationTokenSource();
this.AllowGet();
return;
}
// 发送成功
this.LastSendTime = TimeHelper.Now();
this.failTimes = 0;
this.WaitingTasks.Dequeue();
}
catch (Exception e)
{
Log.Error(e);
}
}
public void Send(IActorMessage message)
{
ActorTask task = new ActorTask
{
message = message,
proxy = this
};
this.Add(task);
}
public Task<IResponse> Call(IActorRequest request)
{
ActorTask task = new ActorTask
{
message = request,
proxy = this,
Tcs = new TaskCompletionSource<IResponse>()
};
this.Add(task);
return task.Tcs.Task;
}
public string DebugQueue(Queue<ActorTask> tasks)
{
string s = "";
foreach (ActorTask task in tasks)
{
s += $" {task.message.GetType().Name}";
}
return s;
}
public int failTimes;
}
}
\ No newline at end of file
using System;
using System.Net;
using System.Threading.Tasks;
namespace ETModel
{
public class BenchmarkComponent: Component
{
public int k;
public long time1 = TimeHelper.ClientNow();
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
namespace ETModel
{
public enum LockStatus
{
LockedNot,
LockRequesting,
Locked,
}
/// <summary>
/// 分布式锁组件,Unit对象可能在不同进程上有镜像,访问该对象的时候需要对他加锁
/// </summary>
public class LockComponent: Component
{
public LockStatus status = LockStatus.LockedNot;
public IPEndPoint address;
public int lockCount;
public readonly Queue<TaskCompletionSource<bool>> queue = new Queue<TaskCompletionSource<bool>>();
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
namespace ETModel
{
public class LockInfo
{
public IPEndPoint Address;
public TaskCompletionSource<bool> Tcs;
public LockInfo(IPEndPoint address, TaskCompletionSource<bool> tcs)
{
this.Address = address;
this.Tcs = tcs;
}
}
public class MasterComponent : Component
{
/// 镜像的地址
public readonly List<IPEndPoint> ghostsAddress = new List<IPEndPoint>();
/// 当前获取锁的进程地址
public IPEndPoint lockedAddress;
/// 请求锁的队列
public readonly Queue<LockInfo> queue = new Queue<LockInfo>();
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
namespace ETModel
{
[ObjectSystem]
public class DbProxyComponentSystem : AwakeSystem<DBProxyComponent>
{
public override void Awake(DBProxyComponent self)
{
self.Awake();
}
}
/// <summary>
/// 用来与数据库操作代理
/// </summary>
public class DBProxyComponent : Component
public class DBProxyComponent: Component
{
public IPEndPoint dbAddress;
public void Awake()
{
StartConfig dbStartConfig = Game.Scene.GetComponent<StartConfigComponent>().DBConfig;
dbAddress = dbStartConfig.GetComponent<InnerConfig>().IPEndPoint;
}
public async Task Save(ComponentWithId component, bool needCache = true)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(dbAddress);
await session.Call(new DBSaveRequest { Component = component, NeedCache = needCache});
}
public async Task SaveBatch(List<ComponentWithId> components, bool needCache = true)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(dbAddress);
await session.Call(new DBSaveBatchRequest { Components = components, NeedCache = needCache});
}
public async Task Save(ComponentWithId component, bool needCache, CancellationToken cancellationToken)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(dbAddress);
await session.Call(new DBSaveRequest { Component = component, NeedCache = needCache}, cancellationToken);
}
public async void SaveLog(ComponentWithId component)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(dbAddress);
await session.Call(new DBSaveRequest { Component = component, NeedCache = false, CollectionName = "Log" });
}
public async Task<T> Query<T>(long id, bool needCache = true) where T: ComponentWithId
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(dbAddress);
DBQueryResponse dbQueryResponse = (DBQueryResponse)await session.Call(new DBQueryRequest { CollectionName = typeof(T).Name, Id = id, NeedCache = needCache });
return (T)dbQueryResponse.Component;
}
public async Task<List<T>> QueryBatch<T>(List<long> ids, bool needCache = true) where T : ComponentWithId
{
List<T> list = new List<T>();
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(dbAddress);
DBQueryBatchResponse dbQueryBatchResponse = (DBQueryBatchResponse)await session.Call(new DBQueryBatchRequest { CollectionName = typeof(T).Name, IdList = ids, NeedCache = needCache});
foreach (ComponentWithId component in dbQueryBatchResponse.Components)
{
list.Add((T)component);
}
return list;
}
public async Task<List<T>> QueryJson<T>(string json) where T : ComponentWithId
{
List<T> list = new List<T>();
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(dbAddress);
DBQueryJsonResponse dbQueryJsonResponse = (DBQueryJsonResponse)await session.Call(new DBQueryJsonRequest { CollectionName = typeof(T).Name, Json = json });
foreach (ComponentWithId component in dbQueryJsonResponse.Components)
{
list.Add((T)component);
}
return list;
}
}
}
\ No newline at end of file
......@@ -3,68 +3,10 @@ using System.Threading.Tasks;
namespace ETModel
{
[ObjectSystem]
public class LocationProxyComponentSystem : AwakeSystem<LocationProxyComponent>
{
public override void Awake(LocationProxyComponent self)
{
self.Awake();
}
}
public class LocationProxyComponent : Component
{
public IPEndPoint LocationAddress;
public int AppId;
public void Awake()
{
StartConfigComponent startConfigComponent = Game.Scene.GetComponent<StartConfigComponent>();
this.AppId = startConfigComponent.StartConfig.AppId;
StartConfig startConfig = startConfigComponent.LocationConfig;
this.LocationAddress = startConfig.GetComponent<InnerConfig>().IPEndPoint;
}
public async Task Add(long key)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(this.LocationAddress);
await session.Call(new ObjectAddRequest() { Key = key, AppId = this.AppId });
}
public async Task Lock(long key, int time = 1000)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(this.LocationAddress);
await session.Call(new ObjectLockRequest() { Key = key, LockAppId = this.AppId, Time = time });
}
public async Task UnLock(long key, int value)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(this.LocationAddress);
await session.Call(new ObjectUnLockRequest() { Key = key, UnLockAppId = this.AppId, AppId = value});
}
public async Task Remove(long key)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(this.LocationAddress);
await session.Call(new ObjectRemoveRequest() { Key = key });
}
public async Task<int> Get(long key)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(this.LocationAddress);
ObjectGetResponse response = (ObjectGetResponse)await session.Call(new ObjectGetRequest() { Key = key });
return response.AppId;
}
public override void Dispose()
{
if (this.IsDisposed)
{
return;
}
base.Dispose();
}
}
}
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyName>Model</AssemblyName>
<RootNamespace>Model</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;DEBUG;NETCOREAPP2_0;SERVER</DefineConstants>
<OutputPath>..\..\Bin\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE;RELEASE;NETCOREAPP2_0;SERVER</DefineConstants>
<OutputPath>..\..\Bin\</OutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\Unity\Assets\Scripts\Base\DoubleMap.cs" Link="Base\DoubleMap.cs" />
<Compile Include="..\..\Unity\Assets\Scripts\Base\Event\Env.cs" Link="Base\Event\Env.cs" />
......@@ -69,6 +64,7 @@
<Compile Include="..\..\Unity\Assets\Scripts\Module\Config\ConfigAttribute.cs" Link="Module\Config\ConfigAttribute.cs" />
<Compile Include="..\..\Unity\Assets\Scripts\Module\Config\ConfigComponent.cs" Link="Module\Config\ConfigComponent.cs" />
<Compile Include="..\..\Unity\Assets\Scripts\Module\Config\IConfig.cs" Link="Module\Config\IConfig.cs" />
<Compile Include="..\..\Unity\Assets\Scripts\Module\FrameSync\FrameMessage.cs" Link="Module\FrameSync\FrameMessage.cs" />
<Compile Include="..\..\Unity\Assets\Scripts\Module\Message\AMHandler.cs" Link="Module\Message\AMHandler.cs" />
<Compile Include="..\..\Unity\Assets\Scripts\Module\Message\ErrorCode.cs" Link="Module\Message\ErrorCode.cs" />
<Compile Include="..\..\Unity\Assets\Scripts\Module\Message\IActorMessage.cs" Link="Module\Message\IActorMessage.cs" />
......@@ -104,24 +100,22 @@
<Compile Include="..\..\Unity\Assets\Scripts\Module\Numeric\NumericWatcherComponent.cs" Link="Module\Numeric\NumericWatcherComponent.cs" />
<Compile Include="..\..\Unity\Assets\Scripts\Other\AppType.cs" Link="Other\AppType.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.2.1" />
<PackageReference Include="NLog" Version="4.5.0-rc04" />
<PackageReference Include="protobuf-net" Version="2.3.4" />
<PackageReference Include="SharpZipLib" Version="1.0.0-alpha2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ThirdParty\MongodbDriver\DotNetCoreDriver\MongoDB.Bson\MongoDB.Bson.csproj" />
<ProjectReference Include="..\ThirdParty\MongodbDriver\DotNetCoreDriver\MongoDB.Driver.Core\MongoDB.Driver.Core.csproj" />
<ProjectReference Include="..\ThirdParty\MongodbDriver\DotNetCoreDriver\MongoDB.Driver\MongoDB.Driver.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Component\Config\" />
<Folder Include="Component\Unit" />
<Folder Include="Module\Message\Network\KCP\" />
<Folder Include="Module\Message\Network\TCP\" />
<Folder Include="Module\Numeric\" />
</ItemGroup>
</Project>
</Project>
\ No newline at end of file
......@@ -24,7 +24,7 @@ namespace ETEditor
public class Proto2CSEditor : EditorWindow
{
private const string protoPath = "../Proto/";
private const string serverMessagePath = "../Server/Model/Module/Message/";
private const string serverMessagePath = "../Server/Hotfix/Module/Message/";
private const string clientMessagePath = "Assets/Scripts/Module/Message/";
private const string hotfixMessagePath = "Hotfix/Module/Message/";
private static readonly char[] splitChars = { ' ', '\t' };
......@@ -38,7 +38,7 @@ namespace ETEditor
Proto2CS("ETModel", "OuterMessage.proto", clientMessagePath, "OuterOpcode", 100, HeadFlag.Proto);
GenerateOpcode("OuterOpcode", clientMessagePath);
Proto2CS("ETModel", "OuterMessage.proto", serverMessagePath, "OuterOpcode", 100, HeadFlag.Proto | HeadFlag.Bson, false);
Proto2CS("ETHotfix", "OuterMessage.proto", serverMessagePath, "OuterOpcode", 100, HeadFlag.Proto | HeadFlag.Bson, false);
GenerateOpcode("OuterOpcode", serverMessagePath);
msgOpcode.Clear();
......@@ -46,11 +46,11 @@ namespace ETEditor
GenerateOpcode("HotfixOpcode", hotfixMessagePath);
msgOpcode.Clear();
Proto2CS("ETModel", "HotfixMessage.proto", serverMessagePath, "HotfixOpcode", 10000, HeadFlag.Bson, false);
Proto2CS("ETHotfix", "HotfixMessage.proto", serverMessagePath, "HotfixOpcode", 10000, HeadFlag.Bson, false);
GenerateOpcode("HotfixOpcode", serverMessagePath);
msgOpcode.Clear();
Proto2CS("ETModel", "InnerMessage.proto", serverMessagePath, "InnerOpcode", 1000, HeadFlag.Bson, false);
Proto2CS("ETHotfix", "InnerMessage.proto", serverMessagePath, "InnerOpcode", 1000, HeadFlag.Bson, false);
GenerateOpcode("InnerOpcode", serverMessagePath);
AssetDatabase.Refresh();
......
using System.Collections.Generic;
using ProtoBuf;
namespace ETModel
{
[Message(Opcode.OneFrameMessage)]
[ProtoContract]
public partial class OneFrameMessage: IActorMessage
{
[ProtoMember(90, IsRequired = true)]
public int RpcId { get; set; }
[ProtoMember(93, IsRequired = true)]
public long ActorId { get; set; }
[ProtoMember(1, IsRequired = true)]
public ushort Op;
[ProtoMember(2, IsRequired = true)]
public byte[] AMessage;
}
[Message(Opcode.FrameMessage)]
[ProtoContract]
public partial class FrameMessage: IActorMessage
{
[ProtoMember(90, IsRequired = true)]
public int RpcId { get; set; }
[ProtoMember(93, IsRequired = true)]
public long ActorId { get; set; }
[ProtoMember(1, IsRequired = true)]
public int Frame;
[ProtoMember(2)]
public List<OneFrameMessage> Messages = new List<OneFrameMessage>();
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 6a109203eeeb90d46ab73f54fd2a82a5
timeCreated: 1524623132
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
......@@ -3,5 +3,7 @@ namespace ETModel
public static partial class Opcode
{
public const ushort ActorResponse = 1;
public const ushort FrameMessage = 2;
public const ushort OneFrameMessage = 3;
}
}
......@@ -142,42 +142,6 @@ namespace ETModel
}
[Message(OuterOpcode.OneFrameMessage)]
[ProtoContract]
public partial class OneFrameMessage: IActorMessage
{
[ProtoMember(90, IsRequired = true)]
public int RpcId { get; set; }
[ProtoMember(93, IsRequired = true)]
public long ActorId { get; set; }
[ProtoMember(1, IsRequired = true)]
public ushort Op;
[ProtoMember(2, IsRequired = true)]
public byte[] AMessage;
}
[Message(OuterOpcode.FrameMessage)]
[ProtoContract]
public partial class FrameMessage: IActorMessage
{
[ProtoMember(90, IsRequired = true)]
public int RpcId { get; set; }
[ProtoMember(93, IsRequired = true)]
public long ActorId { get; set; }
[ProtoMember(1, IsRequired = true)]
public int Frame;
[ProtoMember(2, TypeName = "ETModel.OneFrameMessage")]
public List<OneFrameMessage> Messages = new List<OneFrameMessage>();
}
[Message(OuterOpcode.Frame_ClickMap)]
[ProtoContract]
public partial class Frame_ClickMap: IFrameMessage
......
......@@ -11,12 +11,10 @@ namespace ETModel
public const ushort G2C_EnterMap = 107;
public const ushort UnitInfo = 108;
public const ushort Actor_CreateUnits = 109;
public const ushort OneFrameMessage = 110;
public const ushort FrameMessage = 111;
public const ushort Frame_ClickMap = 112;
public const ushort C2M_Reload = 113;
public const ushort M2C_Reload = 114;
public const ushort C2R_Ping = 115;
public const ushort R2C_Ping = 116;
public const ushort Frame_ClickMap = 110;
public const ushort C2M_Reload = 111;
public const ushort M2C_Reload = 112;
public const ushort C2R_Ping = 113;
public const ushort R2C_Ping = 114;
}
}
......@@ -23,7 +23,7 @@ namespace ETHotfix
{
ReferenceCollector rc = this.GetParent<UI>().GameObject.GetComponent<ReferenceCollector>();
GameObject sendBtn = rc.Get<GameObject>("Send");
GameObject sendRpcBtn = rc.Get<GameObject>("SendRpc");
GameObject sendRpcBtn = rc.Get<GameObject>("" + "SendRpc");
sendBtn.GetComponent<Button>().onClick.Add(this.OnSend);
sendRpcBtn.GetComponent<Button>().onClick.Add(this.OnSendRpc);
......
......@@ -12,13 +12,16 @@
<ProjectTypeGuids>{E097FAD1-6243-4DAD-9C02-E9B9EFC3FFC1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TargetFrameworkIdentifier>.NETFramework</TargetFrameworkIdentifier>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<TargetFrameworkProfile></TargetFrameworkProfile>
<CompilerResponseFile></CompilerResponseFile>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<CompilerResponseFile>
</CompilerResponseFile>
<UnityProjectGenerator>VSTU</UnityProjectGenerator>
<UnityProjectType>Game:1</UnityProjectType>
<UnityBuildTarget>StandaloneWindows:5</UnityBuildTarget>
<UnityVersion>2017.1.3p2</UnityVersion>
<RootNamespace></RootNamespace>
<RootNamespace>
</RootNamespace>
<LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup>
......@@ -266,6 +269,7 @@
<Compile Include="Assets\Scripts\Module\FrameSync\AnimatorComponent.cs" />
<Compile Include="Assets\Scripts\Module\FrameSync\CameraComponent.cs" />
<Compile Include="Assets\Scripts\Module\FrameSync\ClientFrameComponent.cs" />
<Compile Include="Assets\Scripts\Module\FrameSync\FrameMessage.cs" />
<Compile Include="Assets\Scripts\Module\FrameSync\MoveComponent.cs" />
<Compile Include="Assets\Scripts\Module\FrameSync\Player.cs" />
<Compile Include="Assets\Scripts\Module\FrameSync\PlayerComponent.cs" />
......@@ -774,4 +778,4 @@
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="GenerateTargetFrameworkMonikerAttribute" />
</Project>
</Project>
\ No newline at end of file

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27428.2015
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.Plugins", "Unity.Plugins.csproj", "{D1FDB199-0FB7-099D-3771-C6A942E4E326}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity", "Unity.csproj", "{CF118143-7E37-744F-BE45-3F55345FEC40}"
......@@ -43,7 +41,4 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {300FBDB7-5336-420B-B6BE-7E5F863E4F8B}
EndGlobalSection
EndGlobal
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册