feat: initial commit - HJHA game server full source

6 major server modules (PdkFriendServer/GlobalSever/ServerCore/GameModule/GameNetModule) +
game logic (GameFix/GameDAL/ServerData) +
network layer (NetWorkMessage) +
data layer (ObjectModel) +
utilities (MrWu/Core/Config/CloudAPI/dll) +
adapters (zyxAdapter/base)

.NET 8.0 C# solution, 16 projects, 958 source files
This commit is contained in:
2026-07-07 12:02:15 +08:00
commit e9616125ce
958 changed files with 158203 additions and 0 deletions

View File

@ -0,0 +1,49 @@

using System.Threading.Tasks;
using MrWu.Debug;
using WebSocketSharp;
using WebSocketSharp.Server;
namespace Server.Net
{
public abstract class BaseWChannel : WebSocketBehavior
{
public bool Active
{
get { return ReadyState == WebSocketState.Open; }
}
public string RemoteEndPointIP {
get
{
string ipAddress = null;
if (this.Headers["X-Forwarded-For"] != null)
{
ipAddress = this.Headers["X-Forwarded-For"];
}else if (this.Headers["X-Real-IP"] != null)
{
ipAddress = this.Headers["X-Real-IP"];
}
if (ipAddress == null)
{
return this.UserEndPoint.Address.ToString();
}
if (ipAddress.IndexOf(',') >= 0)
{
string[] ss = ipAddress.Split(',');
ipAddress = ss[0];
}
return ipAddress.Trim();
}
}
public void BeginDisconnect()
{
Task.Factory.StartNew(Close);
}
}
}

View File

@ -0,0 +1,262 @@
using System;
using MrWu.Debug;
using Server;
using WebSocketSharp.Server;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using GameMessage;
using MessagePack;
using MrWu;
using Server.Core;
namespace Server.Net
{
public class GameNetManager : SingleInstance<GameNetManager>, IDisposable
{
public static bool IsStart
{
get;
private set;
}
public bool IsDispose { get; private set; }
private WebSocketServer _webSocketServer;
/// <summary>
/// 会话集合
/// </summary>
private ConcurrentDictionary<long, Session> sessionDic = new ConcurrentDictionary<long, Session>();
/// <summary>
/// SessionIds 队列
/// </summary>
private readonly ConcurrentQueue<long> sessionIds = new ConcurrentQueue<long>();
/// <summary>
/// 当前的链接数
/// </summary>
public int ConnectCount
{
get { return sessionDic.Count; }
}
/// <summary>
/// 当前接收的数量
/// </summary>
public long ReceiveCount;
public const int MaxMessageSize = 1024 * 1024;
public event Action<Session> ConnectedEvt;
public event Action<Session> DisConnectedEvt;
public event Action<Session, byte[]> ReceiveEvt;
/// <summary>
/// 启动网络
/// </summary>
public void Start(string webPath, int webPort)
{
if (!string.IsNullOrEmpty(webPath) && webPort > 0)
{
//webSocket 服务 启动
GameWChannel.OnConnectedEvt += WConnected;
GameWChannel.OnDisconnectedEvt += WDisconnected;
GameWChannel.OnReceivedEvt += WReceived;
GameWChannel.OnErrorEvt += WError;
_webSocketServer = new WebSocketServer(webPort);
_webSocketServer.AddWebSocketService<GameWChannel>(webPath);
_webSocketServer.Start();
//webSocket 服务 结束
IsStart = true;
SessionUUIDManager.Instance.Init();
}
}
public void Dispose()
{
IsDispose = true;
WebSocketStop();
}
private void WebSocketStop()
{
Task.Factory.StartNew(() => { _webSocketServer?.Stop(); });
}
#region WebSocket
private void WConnected(GameWChannel channel)
{
Session session = new Session(channel);
Interlocked.Exchange(ref session.ReadWriteTime, TimeInfo.Instance.FrameTime);
long id = session.Id;
channel.SessionId = id;
if (!sessionDic.TryAdd(id, session))
{
Debug.Error("添加 webSocket 链接到 字典失败!!!");
}
else
{
sessionIds.Enqueue(id);
try
{
ConnectedEvt?.Invoke(session);
}
catch (Exception e)
{
Debug.Error($"链接成功事件执行报错:{e.Message}");
}
}
}
private void WDisconnected(GameWChannel channel)
{
long sessionId = channel.SessionId;
if (sessionDic.TryRemove(sessionId, out Session session))
{
lock (session.DisConnectLock)
{
try
{
DisConnectedEvt?.Invoke(session);
}
catch (Exception e)
{
Debug.Error($"断开连接事件执行报错:{e.Message}");
}
session.OnDisConnected();
}
}
else
{
if (!_forceDisConnectedSessionIds.TryRemove(sessionId, out Session _))
{
Debug.Error("webSocket断开连接竟然未找到 session");
}
}
}
private ConcurrentDictionary<long,Session> _forceDisConnectedSessionIds = new ConcurrentDictionary<long, Session>();
public void ForceDisconnected(long sessionId)
{
if (sessionDic.TryRemove(sessionId, out Session session))
{
if (!_forceDisConnectedSessionIds.TryAdd(sessionId, session))
{
Debug.Error("不存在说添加不进去吧???");
}
lock (session.DisConnectLock)
{
try
{
DisConnectedEvt?.Invoke(session);
}
catch (Exception e)
{
Debug.Error($"断开连接事件执行报错:{e.Message}");
}
session.OnDisConnected();
}
}
else
{
Debug.Error($"ForceDisconnected 断开连接,竟然未找到 session {sessionId}");
}
}
private void WReceived(GameWChannel channel, byte[] data)
{
long sessionId = channel.SessionId;
if (sessionDic.TryGetValue(sessionId, out Session session))
{
//因为没去掉头
Received(session, data, 0);
}
else
{
Debug.Error("接收到webSocket包未找到会话!");
}
}
private void WError(GameWChannel channel, Exception e)
{
if (e == null)
{
Debug.Error($"Web Socket Error: e is null!");
}else
Debug.Error("Web Socket Error:" + e.Message);
}
#endregion
/// <summary>
/// 接受到数据
/// </summary>
/// <param name="session"></param>
/// <param name="data"></param>
/// <param name="offset"></param>
private void Received(Session session, byte[] data, int offset)
{
try
{
ReceiveEvt?.Invoke(session, data);
}
catch (Exception e)
{
Debug.Error($"收包解析报错:{e.Message}");
}
}
public void Update()
{
// 清理超时,和失效的链接
long timeNow = TimeInfo.Instance.FrameTime;
const int MaxCheckNum = 10;
int n = this.sessionIds.Count;
if (n > MaxCheckNum)
{
n = MaxCheckNum;
}
for (int i = 0; i < n; i++)
{
if (this.sessionIds.TryDequeue(out long sessionId))
{
if (this.sessionDic.TryGetValue(sessionId, out Session session))
{
if (timeNow - session.ReadWriteTime > 20 * 1000)
{
Debug.Info($"超时断线! {session.IsDisConnected}");
//var key = Debug.StartTiming();
session.DisConnected();
// double runtime = Debug.GetRunTime(key);
// if (runtime > 20)
// {
// Debug.Warning($"NetManager runtime:{runtime},n:{n}");
// }
//超时断线
continue;
}
this.sessionIds.Enqueue(sessionId);
}
}
else
{
break;
}
}
}
}
}

View File

@ -0,0 +1,91 @@
using System;
using System.Collections.Specialized;
using System.IO;
using System.Threading.Tasks;
using MrWu.Debug;
using WebSocketSharp;
using WebSocketSharp.Server;
using ErrorEventArgs = WebSocketSharp.ErrorEventArgs;
namespace Server.Net
{
public class GameWChannel : BaseWChannel
{
public long SessionId { get; set; }
/// <summary>
/// 链接成功事件
/// </summary>
public static event Action<GameWChannel> OnConnectedEvt;
/// <summary>
/// 收到包事件
/// </summary>
public static event Action<GameWChannel, byte[]> OnReceivedEvt;
/// <summary>
/// 断开连接事件
/// </summary>
public static event Action<GameWChannel> OnDisconnectedEvt;
/// <summary>
/// 发送错误事件
/// </summary>
public static event Action<GameWChannel, Exception> OnErrorEvt;
//未收齐的字节,写入内存字节
private MemoryStream _memoryStream = new MemoryStream();
private bool waitReceiveHead = true;
private int waitReceiveLength;
public NameValueCollection WHeaders
{
get { return this.Headers; }
}
protected override void OnMessage(MessageEventArgs e)
{
base.OnMessage(e);
// 收到包了!
lock (_memoryStream)
{
_memoryStream.Write(e.RawData, 0, e.RawData.Length);
_memoryStream.Seek(0, SeekOrigin.Begin);
//Debug.Info($"OnMessage,{e.RawData.Length},{_memoryStream.Position},{_memoryStream.Length}");
long byteLength = _memoryStream.Length - _memoryStream.Position;
byte[] bodyData = new byte[byteLength];
_memoryStream.Read(bodyData, 0, (int)byteLength);
//Debug.Info($"收到包体:{_memoryStream.Position},{_memoryStream.Length},bodyLength:{waitReceiveLength}");
waitReceiveHead = true;
OnReceivedEvt?.Invoke(this, bodyData);
_memoryStream.Seek(0, SeekOrigin.Begin);
_memoryStream.SetLength(0);
}
}
protected override void OnClose(CloseEventArgs e)
{
base.OnClose(e);
OnDisconnectedEvt?.Invoke(this);
}
protected override void OnError(ErrorEventArgs e)
{
base.OnError(e);
OnErrorEvt?.Invoke(this, e.Exception);
}
protected override void OnOpen()
{
base.OnOpen();
OnConnectedEvt?.Invoke(this);
}
}
}

View File

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.IO;
using MrWu;
using MrWu.Debug;
using Server.Core;
using WebSocketSharp.Net;
using WebSocketSharp.Server;
namespace Server.Net
{
public class HttpNetManager<T> : SingleInstance<T>,IDisposable where T : SingleInstance,new()
{
protected HttpServer _httpServer;
public virtual void Start(int port,string docPath = null)
{
if (_httpServer != null)
{
Debug.Warning("HttpServer is not null!");
return;
}
_httpServer = new HttpServer($"http://0.0.0.0:{port}");
if (!string.IsNullOrEmpty(docPath))
{
_httpServer.DocumentRootPath = docPath;
}
_httpServer.OnPost += OnPost;
_httpServer.OnGet += OnGet;
_httpServer.Start();
}
protected virtual void OnGet(object sender,HttpRequestEventArgs args)
{
Debug.Log("OnGet---");
var res = args.Response;
res.StatusCode = (int)HttpStatusCode.NotModified;
}
protected virtual void OnPost(object sender, HttpRequestEventArgs args)
{
Debug.Log("OnPost---");
var res = args.Response;
res.StatusCode = (int)HttpStatusCode.NotModified;
}
public virtual void Dispose()
{
_httpServer.Stop();
}
protected string ReadRequest(HttpListenerRequest request)
{
using (StreamReader streamReader = new StreamReader(request.InputStream))
{
return streamReader.ReadToEnd();
}
}
}
}

View File

@ -0,0 +1,46 @@
using System;
using GameMessage;
namespace Server.Net
{
public interface IUserSession
{
long Id { get; }
/// <summary>
/// 实例ID
/// </summary>
long InstanceId { get; }
/// <summary>
/// 是否断开连接
/// </summary>
bool IsDisConnected { get; }
/// <summary>
/// 登录验证码
/// </summary>
string VerificationCode { get; set; }
/// <summary>
/// 用户数据
/// </summary>
SessionUserData UserData { get; }
/// <summary>
/// 远端ip地址
/// </summary>
string RemoteIPAddress
{
get;
}
void Send(byte[] buffer, Type messageType);
void Send(MessageData messageData);
void BindUser(int userid, string uuid, int clientVer, int plat,string deviceInfo,bool isNewUser,string storeType, bool reset = false);
void DisConnected();
}
}

View File

@ -0,0 +1,218 @@
using System;
using MrWu.Debug;
using Server;
using WebSocketSharp.Server;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using GameMessage;
using MessagePack;
using MrWu;
using Server.Core;
namespace Server.Net
{
public class NetManager : SingleInstance<NetManager>, IDisposable
{
public static bool isStart
{
get;
private set;
}
public bool IsDispose { get; private set; }
private WebSocketServer _webSocketServer;
/// <summary>
/// 会话集合
/// </summary>
private ConcurrentDictionary<long, Session> sessionDic = new ConcurrentDictionary<long, Session>();
/// <summary>
/// SessionIds 队列
/// </summary>
private readonly ConcurrentQueue<long> sessionIds = new ConcurrentQueue<long>();
/// <summary>
/// 当前的链接数
/// </summary>
public int ConnectCount
{
get
{
return sessionDic.Count;
}
}
/// <summary>
/// 当前接收的数量
/// </summary>
public long ReceiveCount;
public const int MaxMessageSize = 1024*1024;
/// <summary>
/// 启动网络
/// </summary>
public void Start(string webPath,int webPort)
{
if (!string.IsNullOrEmpty(webPath) && webPort > 0)
{
//webSocket 服务 启动
WChannel.OnConnectedEvt += WConnected;
WChannel.OnDisconnectedEvt += WDisconnected;
WChannel.OnReceivedEvt += WReceived;
WChannel.OnErrorEvt += WError;
_webSocketServer = new WebSocketServer(webPort);
_webSocketServer.AddWebSocketService<WChannel>(webPath);
_webSocketServer.Start();
//webSocket 服务 结束
isStart = true;
}
SessionUUIDManager.Instance.Init();
}
public void Dispose()
{
IsDispose = true;
WebSocketStop();
}
private void WebSocketStop()
{
Task.Factory.StartNew(() =>
{
_webSocketServer?.Stop();
});
}
#region WebSocket
private void WConnected(WChannel channel)
{
Session session = new Session(channel);
Interlocked.Exchange(ref session.ReadWriteTime,TimeInfo.Instance.FrameTime);
long id = session.Id;
channel.SessionId = id;
if (!sessionDic.TryAdd(id, session))
{
Debug.Error("添加 webSocket 链接到 字典失败!!!");
}
else
{
sessionIds.Enqueue(id);
}
}
private void WDisconnected(WChannel channel)
{
long sessionId = channel.SessionId;
if (sessionDic.TryRemove(sessionId,out Session session))
{
session.OnDisConnected();
}
else
{
Debug.Error("webSocket断开连接竟然未找到 session!");
}
}
private void WReceived(WChannel channel, byte[] data)
{
long sessionId = channel.SessionId;
if (sessionDic.TryGetValue(sessionId, out Session session))
{
//因为没去掉头
Received(session,data,0);
}
else
{
Debug.Error("接收到webSocket包未找到会话!");
}
}
private void WError(WChannel channel, Exception e)
{
Debug.Error("Web Socket Error:" + e.Message);
}
#endregion
/// <summary>
/// 接受到数据
/// </summary>
/// <param name="session"></param>
/// <param name="data"></param>
/// <param name="offset"></param>
private void Received(Session session, byte[] data,int offset)
{
try
{
//opcode
int opcode = BitConverter.ToInt32(data, offset);
offset += 4;
//4个备份字节
offset += 4;
//后面是包内容
//Debug.Info($"接收到数据{opcode},{data.Length}");
Type messageType = MessageManager.GetType(opcode);
MessageData messageData = (MessageData)MessagePackSerializer.Deserialize(messageType,
new ReadOnlyMemory<byte>(data, offset, data.Length - offset));
MessageDispatcher.Instance.AddPack(GamePacket.Create(opcode, messageData, session));
Interlocked.Exchange(ref session.ReadWriteTime, TimeInfo.Instance.FrameTime);
Interlocked.Add(ref ReceiveCount, 1);
}
catch (Exception e)
{
Debug.Error($"收包解析报错:{e.Message}");
}
}
public void Update()
{
// 清理超时,和失效的链接
long timeNow = TimeInfo.Instance.FrameTime;
const int MaxCheckNum = 10;
int n = sessionIds.Count;
if (n > MaxCheckNum)
{
n = MaxCheckNum;
}
for (int i = 0; i < n; i++)
{
if (this.sessionIds.TryDequeue(out long sessionId))
{
if (this.sessionDic.TryGetValue(sessionId, out Session session))
{
if (timeNow - session.ReadWriteTime > 20 * 1000)
{
Debug.Info($"超时断线! {session.IsDisConnected}");
//var key = Debug.StartTiming();
session.DisConnected();
// double runtime = Debug.GetRunTime(key);
// if (runtime > 20)
// {
// Debug.Warning($"NetManager runtime:{runtime},n:{n}");
// }
//超时断线
continue;
}
this.sessionIds.Enqueue(sessionId);
}
}
else
{
break;
}
}
}
}
}

View File

@ -0,0 +1,129 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using GameMessage;
using K4os.Compression.LZ4;
using MessagePack;
using MrWu.Debug;
using Server.Core;
namespace Server.Net
{
public static class PacketUtility
{
private static int MaxSendLength = 0;
private const int LogMinLength = 1024 * 100;
public static int MessageParseDataV1(byte[] data, Type messageType, MemoryStream memoryStream)
{
int opcode = MessageManager.GetOpcode(messageType);
memoryStream.Seek(5, SeekOrigin.Begin);
//写入数据
memoryStream.Write(data,0,data.Length);
int length = (int)memoryStream.Position;
memoryStream.GetBuffer()[0] = 0; //设置为不压缩
memoryStream.GetBuffer().WriteTo(1,opcode);
memoryStream.Seek(0, SeekOrigin.Begin);
return length;
}
public static int MessageParseDataV1(MessageData messageData,MemoryStream memoryStream,MemoryStream compressStream)
{
Type messageType = messageData.GetType();
int opcode = MessageManager.GetOpcode(messageType);
compressStream.Seek(0, SeekOrigin.Begin);
MessagePackSerializer.Serialize(messageType,compressStream, messageData);
memoryStream.Seek(5, SeekOrigin.Begin);
byte compressFlag = 0;
if (compressStream.Position < 2048) //2k 一下不压缩
{
memoryStream.Write(compressStream.GetBuffer(),0,(int)compressStream.Position); //把包内容全部写入
}
else
{
//压缩处理
byte[] compressData = LZ4Pickler.Pickle(compressStream.GetBuffer(), 0, (int)compressStream.Position);
memoryStream.WriteInt((int)compressStream.Position); //写入压缩前的长度
memoryStream.Write(compressData,0,compressData.Length);
compressFlag = 1;
//Debug.Info($"得到数据:{compressData.Length} {compressData[0]} {compressData[compressData.Length - 1]}");
}
int length = (int)memoryStream.Position;
memoryStream.GetBuffer()[0] = compressFlag;
memoryStream.GetBuffer().WriteTo(1,opcode);
memoryStream.Seek(0, SeekOrigin.Begin);
return length;
}
public static byte[] MessageParseData(Type messageType,byte[] data)
{
int opcode = MessageManager.GetOpcode(messageType);
int len = 4 + 4 + data.Length;
//包长度
byte[] lenData = BitConverter.GetBytes(len);
//包类型
byte[] opcodeData = BitConverter.GetBytes(opcode);
//备用 4个字节
byte[] bakData = BitConverter.GetBytes(0);
//发送的数据
byte[] sendData = new byte[len + 4];
if (len > MaxSendLength)
{
MaxSendLength = len;
if (MaxSendLength > LogMinLength)
{
Debug.ImportantLog($"当前最大发送数据长度:{MaxSendLength},opcode:{opcode}");
}
}
Buffer.BlockCopy(lenData, 0, sendData, 0, 4);
Buffer.BlockCopy(opcodeData, 0, sendData, 4, 4);
Buffer.BlockCopy(bakData, 0, sendData, 8, 4);
Buffer.BlockCopy(data, 0, sendData, 12, data.Length);
return sendData;
}
public static byte[] MessageParseData(MessageData messageData)
{
Type messageType = messageData.GetType();
int opcode = MessageManager.GetOpcode(messageType);
byte[] data = MessagePackSerializer.Serialize(messageType, messageData);
int len = 4 + 4 + data.Length;
//包长度
byte[] lenData = BitConverter.GetBytes(len);
//包类型
byte[] opcodeData = BitConverter.GetBytes(opcode);
//备用 4个字节
byte[] bakData = BitConverter.GetBytes(0);
//发送的数据
byte[] sendData = new byte[len + 4];
if (len > MaxSendLength)
{
MaxSendLength = len;
if (MaxSendLength > LogMinLength)
{
Debug.ImportantLog($"当前最大发送数据长度:{MaxSendLength},opcode:{opcode}");
}
}
Buffer.BlockCopy(lenData, 0, sendData, 0, 4);
Buffer.BlockCopy(opcodeData, 0, sendData, 4, 4);
Buffer.BlockCopy(bakData, 0, sendData, 8, 4);
Buffer.BlockCopy(data, 0, sendData, 12, data.Length);
return sendData;
}
}
}

View File

@ -0,0 +1,167 @@
using System;
using System.Diagnostics;
using System.Threading;
using GameDAL.Game;
using GameMessage;
using Server.Core;
using Debug = MrWu.Debug.Debug;
namespace Server.Net
{
/// <summary>
/// 用在这个代理Session的原因是 源Session可以被复用
/// </summary>
public class ProxySession : Reference, IUserSession
{
/// <summary>
/// 是否已经被释放
/// </summary>
private bool IsDisposed { get; set; }
/// <summary>
/// 实例Id
/// </summary>
public long InstanceId { get; private set; }
/// <summary>
/// 注意这个ID 与 普通 Session 会有相同的情况
/// </summary>
public long Id
{
get { return SourceSession.Id; }
}
/// <summary>
/// 是否断开连接
/// </summary>
public bool IsDisConnected
{
get
{
//Debug.Info($"IsDisConnected: {SourceSession == null} {InstanceId} {SourceSession?.InstanceId}");
return SourceSession == null || SourceSession.IsDisConnected || InstanceId != SourceSession.InstanceId;
}
}
private SessionUserData _userData;
public SessionUserData UserData {
get
{
CheckDisposed();
return _userData;
}
private set
{
CheckDisposed();
_userData = value;
}
}
public string RemoteIPAddress { get; private set; }
/// <summary>
/// 登录验证码
/// </summary>
public string VerificationCode
{
get
{
if (SourceSession == null)
{
Debug.Fatal("VerificationCode Get Error, SourceSession is null!");
return null;
}
return SourceSession.VerificationCode;
}
set
{
if (SourceSession == null)
{
Debug.Fatal("VerificationCode Set Error, SourceSession is null!");
return;
}
SourceSession.VerificationCode = value;
}
}
/// <summary>
/// 源会话 -- 使用者不能拿这个用,因为这个是对象池中的,随时会变
/// </summary>
public UserSession SourceSession { get; private set; }
public void BindUser(int userid, string uuid, int clientVer, int plat, string deviceInfo,bool isNewUser,string storeType, bool reset = false)
{
if (!IsDisConnected)
{
SourceSession.BindUser(userid, uuid, clientVer, plat, deviceInfo, isNewUser,storeType, reset);
UserData = SourceSession.UserData;
}
CheckDisposed();
}
public void Send(byte[] buffer, Type messageType)
{
if (!IsDisConnected)
{
SourceSession.Send(buffer, messageType);
}
}
public void Send(MessageData messageData)
{
//Debug.Info($"发送消息:{IsDisConnected}");
if (!IsDisConnected)
{
SourceSession.Send(messageData);
}
CheckDisposed();
}
public static ProxySession Create(UserSession session)
{
ProxySession proxySession = ReferencePool.Fetch<ProxySession>();
proxySession.IsDisposed = false;
proxySession.InstanceId = session.InstanceId;
proxySession.UserData = session.UserData;
proxySession.RemoteIPAddress = session.RemoteIPAddress;
proxySession.SourceSession = session;
return proxySession;
}
public override void Dispose()
{
if (this.IsFromPool)
{
this.SourceSession = null;
this.UserData = null;
ReferencePool.Recycle(this);
this.IsDisposed = true;
}
}
public void DisConnected()
{
if (!IsDisConnected)
{
SourceSession.DisConnected();
}
CheckDisposed();
}
private void CheckDisposed()
{
if (this.IsDisposed)
{
//打印堆栈信息
StackTrace st = new StackTrace();
Debug.Fatal($"ProxySession is Disposed! {st.ToString()}");
}
}
}
}

View File

@ -0,0 +1,186 @@
using System;
using System.Collections.Concurrent;
using System.Text;
using System.Threading;
using GameMessage;
using MessagePack;
using MrWu;
using MrWu.Debug;
using Server.Core;
using Server.Core.Extend;
namespace Server.Net
{
/// <summary>
/// 会话
/// </summary>
public class Session : IUserSession
{
/// <summary>
/// 唯一id
/// </summary>
public long Id { get; private set; }
public long InstanceId {
get
{
return Id;
}
}
public BaseWChannel WChannel { get; private set; }
/// <summary>
/// 读写时间
/// </summary>
public long ReadWriteTime;
/// <summary>
/// 登录验证码
/// </summary>
public string VerificationCode { get; set; }
/// <summary>
/// 是否已经断开连接
/// </summary>
public bool IsDisConnected { get; private set; }
/// <summary>
/// 绑定的用户数据 -- 老的用户数据
/// </summary>
public SessionUserData UserData { get; private set; }
/// <summary>
/// 断线锁
/// </summary>
public readonly object DisConnectLock = new object();
public string RemoteIPAddress
{
get { return WChannel.RemoteEndPointIP; }
}
public Session(BaseWChannel wChannel)
{
this.WChannel = wChannel;
Id = NetServices.CreateAcceptChannelId();
}
public virtual void Send(MessageData messageData)
{
if (messageData == null)
{
Debug.Error("不能发送空数据!");
return;
}
byte[] data = PacketUtility.MessageParseData(messageData);
Send(data);
}
public void Send(byte[] data,Type messageType)
{
data = PacketUtility.MessageParseData(messageType, data);
Send(data);
}
/// <summary>
/// 发送数据
/// </summary>
/// <param name="data"></param>
public virtual void Send(byte[] data)
{
if (IsDisConnected)
{
Debug.Info("发送失败,链接已经断开!");
return;
}
if (!WChannel.Active)
{
Debug.Info("链接已经断开,发送失败!");
return;
}
Interlocked.Exchange(ref ReadWriteTime, TimeInfo.Instance.FrameTime);
// 组件 data 数据
// 分类发送
WChannel.Send(data);
}
public virtual void Send(string message)
{
if (IsDisConnected)
{
Debug.Info("发送失败,链接已经断开!");
return;
}
if (!WChannel.Active)
{
Debug.Info("链接已经断开,发送失败!");
return;
}
Interlocked.Exchange(ref ReadWriteTime, TimeInfo.Instance.FrameTime);
WChannel.Send(message);
}
public void DisConnected()
{
WChannel.BeginDisconnect();
}
/// <summary>
/// 绑定用户
/// </summary>
public void BindUser(int userid, string uuid, int clientVer, int plat, string deviceInfo, bool isNewUser,string storeType, bool reset = false)
{
if (IsDisConnected)
{
return;
}
if (!reset && UserData != null)
{
Debug.Error("该会话已经绑定了用户数据!");
return;
}
ClearUserData();
UserData = SessionUserData.Create(userid, uuid, clientVer, plat, deviceInfo,isNewUser, storeType);
SessionUUIDManager.Instance.AddSessionUUID(uuid, UserData);
GameNetDispatcher.Instance.Dispatch(GameNetMsgId.SessionBindUser, this);
}
public void ClearUserData()
{
if (UserData != null)
{
GameNetDispatcher.Instance.Dispatch(GameNetMsgId.SessionUnBindUser, this);
SessionUUIDManager.Instance.RemoveLocalUUID(UserData.Uuid, UserData);
UserData = null;
}
}
/// <summary>
/// 断开连接事件
/// </summary>
public virtual void OnDisConnected()
{
if (IsDisConnected)
{
return;
}
IsDisConnected = true;
GameNetDispatcher.Instance.DispatchNextFrame(GameNetMsgId.SessionDisConnected, this);
ClearUserData();
Clear();
}
public virtual void Clear()
{
}
}
}

View File

@ -0,0 +1,92 @@
using Server.Core;
namespace Server.Net
{
/// <summary>
/// 会话用户数据 -- 这个还不能使用线程池,找不准释放的机会
/// </summary>
public class SessionUserData
{
/// <summary>
/// 玩家的Userid;
/// </summary>
public int Userid
{
get;
private set;
}
/// <summary>
/// 会话Uuid
/// </summary>
public string Uuid
{
get;
private set;
}
/// <summary>
/// 客户端版本
/// </summary>
public int ClientVer
{
get;
private set;
}
/// <summary>
/// 0桌面模式 1安卓平台 2苹果平台 3小游戏 4WebGl
/// </summary>
public int Plat
{
get;
private set;
}
/// <summary>
/// 设备信息
/// </summary>
public string DeviceInfo
{
get;
private set;
}
/// <summary>
/// 是否是新用户
/// </summary>
public bool IsNewUser
{
get;
private set;
}
/// <summary>
/// 商店类型
/// </summary>
public string StoreType
{
get;
private set;
}
public static SessionUserData Create(int userid,string uuid,int clientVer,int plat,string deviceInfo,bool isNewUser,string storeType)
{
SessionUserData data = new SessionUserData();
data.Userid = userid;
data.Uuid = uuid;
data.ClientVer = clientVer;
data.Plat = plat;
data.DeviceInfo = deviceInfo;
data.IsNewUser = isNewUser;
data.StoreType = storeType;
return data;
}
// public override void Dispose()
// {
// Uuid = null;
// DeviceInfo = null;
// }
}
}

View File

@ -0,0 +1,68 @@
// using System;
// using System.Collections.Concurrent;
// using System.Collections.Generic;
// using System.Linq;
// using System.Net;
// using System.Net.Sockets;
// using MrWu.Debug;
// using Sodao.FastSocket.Server;
// using Sodao.FastSocket.Server.Messaging;
// using Sodao.FastSocket.SocketBase;
//
// namespace Server.Net
// {
// public sealed class TService : AbsSocketService<ThriftMessage>
// {
// /// <summary>
// /// 链接成功事件
// /// </summary>
// public event Action<IConnection> OnConnectedEvt;
//
// /// <summary>
// /// 收到包事件
// /// </summary>
// public event Action<IConnection, byte[]> OnReceivedEvt;
//
// /// <summary>
// /// 断开连接事件
// /// </summary>
// public event Action<IConnection> OnDisconnectedEvt;
//
// /// <summary>
// /// 发送错误事件
// /// </summary>
// public event Action<IConnection, Exception> OnErrorEvt;
//
// public override void OnConnected(IConnection connection)
// {
// base.OnConnected(connection);
// connection.BeginReceive();
//
// OnConnectedEvt?.Invoke(connection);
// }
//
// public override void OnDisconnected(IConnection connection, Exception ex)
// {
// base.OnDisconnected(connection, ex);
// OnDisconnectedEvt?.Invoke(connection);
// }
//
// public override void OnException(IConnection connection, Exception ex)
// {
// base.OnException(connection, ex);
// OnErrorEvt?.Invoke(connection,ex);
// }
//
// public override void OnReceived(IConnection connection, ThriftMessage message)
// {
// base.OnReceived(connection, message);
// OnReceivedEvt?.Invoke(connection,message.Payload);
// }
//
// public override void OnSendCallback(IConnection connection, Sodao.FastSocket.SocketBase.Packet packet, bool isSuccess)
// {
// base.OnSendCallback(connection, packet, isSuccess);
// }
//
// }
// }

View File

@ -0,0 +1,226 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Threading;
using ActorCore;
using GameMessage;
using MessagePack.Formatters;
using NetWorkMessage;
using Server.Core.Extend;
using Debug = MrWu.Debug.Debug;
namespace Server.Net
{
/// <summary>
/// 这个是路由过来的用户Session用来做对象池
/// </summary>
public class UserSession : IUserSession
{
/// <summary>
/// 实例Id
/// </summary>
public long Id { get; private set; }
/// <summary>
/// 实例ID 如果Id 改变就不能用了,不再是之前的用户
/// </summary>
public long InstanceId { get; private set; }
/// <summary>
/// 登录验证码
/// </summary>
public string VerificationCode { get; set; }
public string RemoteIPAddress { get; set; }
public bool IsDisConnected { get; set; }
public SessionUserData UserData { get; private set; }
/// <summary>
/// UserData 设置的时候需要锁
/// </summary>
public readonly object DisConnectLock = new object();
// 收到链接包 -> State = Using
// 收到断开包 然后就回复断开成功的包 Session设置为断开
//UserSession 被 ProxySession 包装使用, 代理持有UserSession 那么RefCount + 1 当ProxySession 销毁那么RefCount - 1
public UserSession(long id, long instanceId)
{
this.Id = id;
this.InstanceId = instanceId;
}
public void BindUser(int userid, string uuid, int clientVer, int plat, string deviceInfo,bool isNewUser,string storeType, bool reset = false)
{
if (IsDisConnected)
{
return;
}
if (!reset && UserData != null)
{
Debug.Error("该会话已经绑定了用户数据!");
return;
}
ClearUserData();
UserData = SessionUserData.Create(userid, uuid, clientVer, plat, deviceInfo,isNewUser, storeType);
SessionUUIDManager.Instance.AddSessionUUID(uuid, UserData);
GameNetDispatcher.Instance.Dispatch(GameNetMsgId.SessionBindUser, this);
}
private const int WarningCnt = 30 * 8 * 1024;
/// <summary>
/// 发送数据给客户端
/// </summary>
/// <param name="buffer"></param>
/// <param name="messageType"></param>
public void Send(byte[] buffer, Type messageType)
{
if (buffer == null)
{
Debug.Error("不能发送空数据!");
return;
}
if (IsDisConnected)
{
Debug.Info($"链接并未在使用,不能发送! {Id} {this.InstanceId}");
return;
}
//发送给客户端
Server2ClientMessage message = Server2ClientMessage.Create();
int len = PacketUtility.MessageParseDataV1(buffer, messageType, message.MemoryStream);
byte[] data = new byte[len];
int count = message.MemoryStream.Read(data, 0, len);
if (count != len)
{
Debug.Error($"怎么会读取的数据跟要读取的长度不一致! {len} {count}");
}
if (buffer.Length > WarningCnt)
{
Debug.ImportantLog($"还有包大于240K? {messageType}");
}
message.Data = data;
Send2Router(message);
}
/// <summary>
/// 这个只能用来发送给客户端
/// </summary>
/// <param name="messageData"></param>
public void Send(MessageData messageData)
{
if (messageData == null)
{
Debug.Error("不能发送空数据!");
return;
}
if (IsDisConnected)
{
Debug.Info($"链接并未在使用,不能发送! - {Id} {this.InstanceId}");
return;
}
//发送给客户端
Server2ClientMessage message = Server2ClientMessage.Create();
int len = PacketUtility.MessageParseDataV1(messageData, message.MemoryStream, message.CompressStream);
byte[] data = new byte[len];
int count = message.MemoryStream.Read(data, 0, len);
if (count != len)
{
Debug.Error($"怎么会读取的数据跟要读取的长度不一致! {len} {count}");
}
//Debug.Info($"压缩标记:{data[0]},opcode:{BitConverter.ToInt32(data,1)}");
message.Data = data;
Send2Router(message);
}
private IActor MainActor;
private IActor UerNetActor;
private void GetActor()
{
if (MainActor == null)
{
MainActor = ActorManager.Instance.Get(ActorTypeId.Main);
}
if (UerNetActor == null)
{
UerNetActor = ActorManager.Instance.Get(ActorTypeId.UserNet);
}
}
private void Send2Router(MessageObject message)
{
GetActor();
UserNetSendMessage userNetSendMessage = new UserNetSendMessage();
userNetSendMessage.Id = Id;
userNetSendMessage.InstanceId = InstanceId;
userNetSendMessage.MessageObject = message;
MainActor.Send(UerNetActor.ActorId,userNetSendMessage);
}
/// <summary>
/// 断开连接
/// </summary>
public void DisConnected()
{
GetActor();
Debug.Info("UserSession 强制断开链接");
UserNetForceFinMessage userNetForceFinMessage = new UserNetForceFinMessage();
userNetForceFinMessage.Id = Id;
userNetForceFinMessage.InstanceId = InstanceId;
userNetForceFinMessage.FinCode = 0;
MainActor.Send(UerNetActor.ActorId,userNetForceFinMessage);
//提前处理用户断开
UserSessionManager.Instance.UserFin(this);
}
public void ClearUserData()
{
if (UserData != null)
{
Debug.Info($"注销绑定用户数据 Session:{Id} {UserData.Userid}");
GameNetDispatcher.Instance.Dispatch(GameNetMsgId.SessionUnBindUser, this);
SessionUUIDManager.Instance.RemoveLocalUUID(UserData.Uuid, UserData);
UserData = null;
}
}
/// <summary>
/// 强制下线强制下线也要等待1帧因为线程不一样
/// </summary>
/// <param name="finCode"></param>
/// <param name="userId"></param>
public void ForceDiscConnect(int finCode,int userId)
{
GetActor();
UserNetForceFinMessage userNetForceFinMessage = new UserNetForceFinMessage();
userNetForceFinMessage.Id = Id;
userNetForceFinMessage.InstanceId = InstanceId;
userNetForceFinMessage.FinCode = finCode;
MainActor.Send(UerNetActor.ActorId,userNetForceFinMessage);
//提前处理用户断开
UserSessionManager.Instance.UserFin(this);
}
}
}

View File

@ -0,0 +1,15 @@
using GameMessage;
using ObjectMessage;
namespace Server.Net
{
public static class UserSessionExt
{
public static void SendTipCode(this IUserSession session, int code)
{
TipCodeResponse tcResponse = new TipCodeResponse();
tcResponse.SetCode(code);
session.Send(tcResponse);
}
}
}

View File

@ -0,0 +1,151 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using MrWu.Debug;
using WebSocketSharp;
using WebSocketSharp.Server;
using ErrorEventArgs = WebSocketSharp.ErrorEventArgs;
namespace Server.Net
{
/// <summary>
/// WebSocket 单个链接的通道
/// </summary>
public class WChannel: BaseWChannel
{
public long SessionId { get; set; }
/// <summary>
/// 链接成功事件
/// </summary>
public static event Action<WChannel> OnConnectedEvt;
/// <summary>
/// 收到包事件
/// </summary>
public static event Action<WChannel, byte[]> OnReceivedEvt;
/// <summary>
/// 断开连接事件
/// </summary>
public static event Action<WChannel> OnDisconnectedEvt;
/// <summary>
/// 发送错误事件
/// </summary>
public static event Action<WChannel, Exception> OnErrorEvt;
//未收齐的字节,写入内存字节
private MemoryStream _memoryStream = new MemoryStream();
private byte[] lengthBytes = new byte[4];
private bool waitReceiveHead = true;
private int waitReceiveLength;
public NameValueCollection WHeaders {
get
{
return this.Headers;
}
}
protected override void OnMessage(MessageEventArgs e)
{
base.OnMessage(e);
lock (_memoryStream)
{
_memoryStream.Write(e.RawData,0,e.RawData.Length);
_memoryStream.Seek(0, SeekOrigin.Begin);
//Debug.Info($"OnMessage,{e.RawData.Length},{_memoryStream.Position},{_memoryStream.Length}");
while (true)
{
long byteLength = _memoryStream.Length - _memoryStream.Position;
//Debug.Log("缓存区长度:" + _memoryStream.Length);
if (waitReceiveHead)
{
// 没收到 包头
if (byteLength < 4)
{
break;
}
else
{
//收到了包头
_memoryStream.Read(lengthBytes, 0, 4);
waitReceiveLength = BitConverter.ToInt32(lengthBytes, 0);
waitReceiveHead = false;
if (waitReceiveLength > GameNetManager.MaxMessageSize)
{
Debug.Error("包体过大:" + waitReceiveLength);
throw new Exception("Message Size :" + waitReceiveLength);
}
}
}
else
{
//还没有收齐,不处理
if (byteLength < waitReceiveLength)
{
break;
}
else
{
byte[] bodyData = new byte[waitReceiveLength];
_memoryStream.Read(bodyData, 0, waitReceiveLength);
//Debug.Info($"收到包体:{_memoryStream.Position},{_memoryStream.Length},bodyLength:{waitReceiveLength}");
waitReceiveHead = true;
OnReceivedEvt?.Invoke(this, bodyData);
}
}
}
//未读取的长度
int noReadLength = (int)(_memoryStream.Length - _memoryStream.Position);
//Debug.Info($"剩余长度:{noReadLength}");
if (noReadLength > 0)
{
byte[] noReadBytes = new byte[noReadLength];
_memoryStream.Read(noReadBytes, 0, noReadLength);
_memoryStream.Seek(0, SeekOrigin.Begin);
_memoryStream.SetLength(0);
_memoryStream.Write(noReadBytes, 0, noReadLength);
}
else
{
_memoryStream.Seek(0, SeekOrigin.Begin);
_memoryStream.SetLength(0);
}
//Debug.Info($"重置收包:{_memoryStream.Position},{_memoryStream.Length}");
}
}
protected override void OnClose(CloseEventArgs e)
{
base.OnClose(e);
OnDisconnectedEvt?.Invoke(this);
}
protected override void OnError(ErrorEventArgs e)
{
base.OnError(e);
OnErrorEvt?.Invoke(this, e.Exception);
}
protected override void OnOpen()
{
base.OnOpen();
OnConnectedEvt?.Invoke(this);
}
}
}

View File

@ -0,0 +1,103 @@
using System.Collections.Concurrent;
using System.Threading.Tasks;
using GameMessage;
using MrWu.Debug;
using Server.Core;
using Server.Core.Extend;
namespace Server.Net
{
/// <summary>
/// 游戏包
/// </summary>
public class GamePacket : Reference
{
/// <summary>
/// 消息Code
/// </summary>
public int OpCode { get; private set; }
/// <summary>
/// 消息数据
/// </summary>
public MessageData MessageData { get; private set; }
/// <summary>
/// 谁发来的 - 包处理完会释放
/// </summary>
public IUserSession Session { get; set; }
/// <summary>
/// 任务
/// </summary>
private ConcurrentQueue<Task> Tasks = new ConcurrentQueue<Task>();
public static GamePacket Create(int opCode, MessageData messageData)
{
GamePacket packet = ReferencePool.Fetch<GamePacket>();
packet.OpCode = opCode;
packet.MessageData = messageData;
#if DEBUG
if (!packet.Tasks.IsEmpty)
{
Debug.Fatal("Packet has task!");
}
#endif
return packet;
}
public static GamePacket Create(int opCode, MessageData messageData,IUserSession session)
{
GamePacket packet = ReferencePool.Fetch<GamePacket>();
packet.OpCode = opCode;
packet.MessageData = messageData;
packet.Session = session;
#if DEBUG
if (!packet.Tasks.IsEmpty)
{
Debug.Fatal("Packet has task!");
}
#endif
return packet;
}
public void BeginTask(Task task)
{
if (task == null)
{
return;
}
Tasks.Enqueue(task);
}
public Task Wait()
{
if (Tasks.IsEmpty)
{
return Task.CompletedTask;
}
//等待所有任务完成
return Task.WhenAll(Tasks);
}
public override void Dispose()
{
if (IsFromPool)
{
MessageData = null;
if (Session is ProxySession proxySession)
{
proxySession.Dispose();
}
Tasks.Clear();
Session = null;
ReferencePool.Recycle(this);
}
}
}
}