为服务器统一数据访问连接 (#91)

* 重做 WebSocket 监听;为服务器统一了多种数据连接访问时的处理;统一编码为 UTF-8

* ModelManager已更名并移动到工具命名空间中

* 完成 WebSocket 消息处理系统

* 添加Socket异步接收数据流;修复TaskUtility阻塞的问题;优化心跳、房间、模组

* 添加枚举

* 删除多余字符

* 添加监听器的名称

* 修改了命名
This commit is contained in:
milimoe 2024-10-02 15:00:34 +08:00 committed by GitHub
parent 2de1e57e0c
commit 940f8397f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 858 additions and 695 deletions

View File

@ -13,9 +13,9 @@ namespace Milimoe.FunGame.Core.Api.Factory
return RoomFactory.Create(); return RoomFactory.Create();
} }
public static Room Create(long Id = 0, string Roomid = "-1", DateTime? CreateTime = null, User? RoomMaster = null, RoomType RoomType = RoomType.All, string GameModule = "", string GameMap = "", RoomState RoomState = RoomState.Created, bool IsRank = false, string Password = "") public static Room Create(long id = 0, string roomid = "-1", DateTime? createTime = null, User? roomMaster = null, RoomType roomType = RoomType.All, string gameModule = "", string gameMap = "", RoomState roomState = RoomState.Created, bool isRank = false, string password = "", int maxUsers = 4)
{ {
return new Room(Id, Roomid, CreateTime, RoomMaster, RoomType, GameModule, GameMap, RoomState, IsRank, Password); return new Room(id, roomid, createTime, roomMaster, roomType, gameModule, gameMap, roomState, isRank, password, maxUsers);
} }
} }
} }

View File

@ -1,14 +1,14 @@
using System.Collections; using System.Collections;
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace Milimoe.FunGame.Core.Service namespace Milimoe.FunGame.Core.Api.Utility
{ {
internal class ModelManager<T> : IEnumerable<T> public class ConcurrentModelList<T> : IEnumerable<T>
{ {
/// <summary> /// <summary>
/// 目前的Model数量 /// 目前的Model数量
/// </summary> /// </summary>
internal int Count => Models.Count; public int Count => Models.Count;
/// <summary> /// <summary>
/// 最大接受的Model数量 /// 最大接受的Model数量
@ -24,7 +24,7 @@ namespace Milimoe.FunGame.Core.Service
/// Init ModelManager /// Init ModelManager
/// </summary> /// </summary>
/// <param name="MaxModel">MaxModel</param> /// <param name="MaxModel">MaxModel</param>
internal ModelManager(int MaxModel = 0) public ConcurrentModelList(int MaxModel = 0)
{ {
if (MaxModel <= 0) if (MaxModel <= 0)
this.MaxModel = Library.Constant.General.MaxTask_2C2G; this.MaxModel = Library.Constant.General.MaxTask_2C2G;
@ -39,7 +39,7 @@ namespace Milimoe.FunGame.Core.Service
/// </summary> /// </summary>
/// <param name="name">Model的Key</param> /// <param name="name">Model的Key</param>
/// <returns>Model对象</returns> /// <returns>Model对象</returns>
internal T this[string name] => Models[name]; public T this[string name] => Models[name];
/// <summary> /// <summary>
/// 向Model管理器中添加Model /// 向Model管理器中添加Model
@ -47,7 +47,7 @@ namespace Milimoe.FunGame.Core.Service
/// <param name="name">Model的Key</param> /// <param name="name">Model的Key</param>
/// <param name="t">Model对象</param> /// <param name="t">Model对象</param>
/// <returns>True操作成功</returns> /// <returns>True操作成功</returns>
internal bool Add(string name, T t) public bool Add(string name, T t)
{ {
if (Models.Count + 1 > MaxModel) return false; if (Models.Count + 1 > MaxModel) return false;
return Models.TryAdd(name, t); return Models.TryAdd(name, t);
@ -58,7 +58,7 @@ namespace Milimoe.FunGame.Core.Service
/// </summary> /// </summary>
/// <param name="name">Model的Key</param> /// <param name="name">Model的Key</param>
/// <returns>True操作成功</returns> /// <returns>True操作成功</returns>
internal bool Remove(string name) public bool Remove(string name)
{ {
return Models.TryRemove(name, out _); return Models.TryRemove(name, out _);
} }
@ -69,7 +69,7 @@ namespace Milimoe.FunGame.Core.Service
/// <param name="name">Model的Key</param> /// <param name="name">Model的Key</param>
/// <param name="t">Model对象</param> /// <param name="t">Model对象</param>
/// <returns>被移除的Model</returns> /// <returns>被移除的Model</returns>
internal bool Remove(string name, ref T? t) public bool Remove(string name, ref T? t)
{ {
return Models.TryRemove(name, out t); return Models.TryRemove(name, out t);
} }
@ -79,13 +79,13 @@ namespace Milimoe.FunGame.Core.Service
/// </summary> /// </summary>
/// <param name="name">Model的Key</param> /// <param name="name">Model的Key</param>
/// <returns>被移除的Model</returns> /// <returns>被移除的Model</returns>
internal T? RemoveAndGet(string name) public T? RemoveAndGet(string name)
{ {
Models.TryRemove(name, out T? result); Models.TryRemove(name, out T? result);
return result; return result;
} }
internal bool ContainsKey(string name) public bool ContainsKey(string name)
{ {
return Models.ContainsKey(name); return Models.ContainsKey(name);
} }
@ -93,7 +93,7 @@ namespace Milimoe.FunGame.Core.Service
/// <summary> /// <summary>
/// 清空Model管理器 /// 清空Model管理器
/// </summary> /// </summary>
internal void Clear() public void Clear()
{ {
Models.Clear(); Models.Clear();
} }
@ -102,7 +102,7 @@ namespace Milimoe.FunGame.Core.Service
/// <summary> /// <summary>
/// 获取Model对象的列表 /// 获取Model对象的列表
/// </summary> /// </summary>
internal List<T> GetList() public List<T> GetList()
{ {
return [.. Models.Values]; return [.. Models.Values];
} }

View File

@ -64,44 +64,46 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <summary> /// <summary>
/// 获取房间实例 /// 获取房间实例
/// </summary> /// </summary>
/// <param name="Id">房间内部序列号</param> /// <param name="id">房间内部序列号</param>
/// <param name="Roomid">房间号</param> /// <param name="roomid">房间号</param>
/// <param name="CreateTime">创建时间</param> /// <param name="createTime">创建时间</param>
/// <param name="RoomMaster">房主</param> /// <param name="roomMaster">房主</param>
/// <param name="RoomType">房间类型</param> /// <param name="roomType">房间类型</param>
/// <param name="GameModule">游戏模组</param> /// <param name="gameModule">游戏模组</param>
/// <param name="GameMap"></param> /// <param name="gameMap"></param>
/// <param name="RoomState">房间状态</param> /// <param name="roomState">房间状态</param>
/// <param name="IsRank"></param> /// <param name="isRank"></param>
/// <param name="Password">房间密码</param> /// <param name="password">房间密码</param>
/// <param name="maxUsers">人数上限</param>
/// <returns></returns> /// <returns></returns>
public static Room GetRoom(long Id = 0, string Roomid = "-1", DateTime? CreateTime = null, User? RoomMaster = null, RoomType RoomType = RoomType.All, string GameModule = "", string GameMap = "", RoomState RoomState = RoomState.Created, bool IsRank = false, string Password = "") public static Room GetRoom(long id = 0, string roomid = "-1", DateTime? createTime = null, User? roomMaster = null, RoomType roomType = RoomType.All, string gameModule = "", string gameMap = "", RoomState roomState = RoomState.Created, bool isRank = false, string password = "", int maxUsers = 4)
{ {
return RoomFactory.Create(Id, Roomid, CreateTime, RoomMaster, RoomType, GameModule, GameMap, RoomState, IsRank, Password); return RoomFactory.Create(id, roomid, createTime, roomMaster, roomType, gameModule, gameMap, roomState, isRank, password, maxUsers);
} }
/// <summary> /// <summary>
/// 通过DataSet获取房间实例 /// 通过DataSet获取房间实例
/// </summary> /// </summary>
/// <param name="DrRoom"></param> /// <param name="drRoom"></param>
/// <param name="User"></param> /// <param name="user"></param>
/// <returns></returns> /// <returns></returns>
public static Room GetRoom(DataRow DrRoom, User User) public static Room GetRoom(DataRow drRoom, User user)
{ {
Room room = General.HallInstance; Room room = General.HallInstance;
if (DrRoom != null) if (drRoom != null)
{ {
long Id = (long)DrRoom[RoomQuery.Column_ID]; long id = (long)drRoom[RoomQuery.Column_ID];
string Roomid = (string)DrRoom[RoomQuery.Column_RoomID]; string roomid = (string)drRoom[RoomQuery.Column_RoomID];
DateTime CreateTime = (DateTime)DrRoom[RoomQuery.Column_CreateTime]; DateTime createTime = (DateTime)drRoom[RoomQuery.Column_CreateTime];
User RoomMaster = User; User roomMaster = user;
RoomType RoomType = (RoomType)Convert.ToInt32(DrRoom[RoomQuery.Column_RoomType]); RoomType roomType = (RoomType)Convert.ToInt32(drRoom[RoomQuery.Column_RoomType]);
string GameModule = (string)DrRoom[RoomQuery.Column_GameModule]; string gameModule = (string)drRoom[RoomQuery.Column_GameModule];
string GameMap = (string)DrRoom[RoomQuery.Column_GameMap]; string gameMap = (string)drRoom[RoomQuery.Column_GameMap];
RoomState RoomState = (RoomState)Convert.ToInt32(DrRoom[RoomQuery.Column_RoomState]); RoomState roomState = (RoomState)Convert.ToInt32(drRoom[RoomQuery.Column_RoomState]);
bool IsRank = Convert.ToInt32(DrRoom[RoomQuery.Column_IsRank]) == 1; bool isRank = Convert.ToInt32(drRoom[RoomQuery.Column_IsRank]) == 1;
string Password = (string)DrRoom[RoomQuery.Column_Password]; string password = (string)drRoom[RoomQuery.Column_Password];
room = GetRoom(Id, Roomid, CreateTime, RoomMaster, RoomType, GameModule, GameMap, RoomState, IsRank, Password); int maxUsers = (int)drRoom[RoomQuery.Column_MaxUsers];
room = GetRoom(id, roomid, createTime, roomMaster, roomType, gameModule, gameMap, roomState, isRank, password, maxUsers);
} }
return room; return room;
} }
@ -109,38 +111,38 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <summary> /// <summary>
/// 通过DataSet获取房间列表 /// 通过DataSet获取房间列表
/// </summary> /// </summary>
/// <param name="DsRoom"></param> /// <param name="dsRoom"></param>
/// <param name="DsUser"></param> /// <param name="dsUser"></param>
/// <returns></returns> /// <returns></returns>
public static List<Room> GetRooms(DataSet DsRoom, DataSet DsUser) public static List<Room> GetRooms(DataSet dsRoom, DataSet dsUser)
{ {
List<Room> list = List<Room> list =
[ [
General.HallInstance General.HallInstance
]; ];
if (DsRoom != null && DsRoom.Tables[0].Rows.Count > 0) if (dsRoom != null && dsRoom.Tables[0].Rows.Count > 0)
{ {
foreach (DataRow DrRoom in DsRoom.Tables[0].Rows) foreach (DataRow drRoom in dsRoom.Tables[0].Rows)
{ {
long Id = (long)DrRoom[RoomQuery.Column_ID]; long Id = (long)drRoom[RoomQuery.Column_ID];
string Roomid = (string)DrRoom[RoomQuery.Column_RoomID]; string Roomid = (string)drRoom[RoomQuery.Column_RoomID];
DateTime CreateTime = (DateTime)DrRoom[RoomQuery.Column_CreateTime]; DateTime createTime = (DateTime)drRoom[RoomQuery.Column_CreateTime];
User RoomMaster = General.UnknownUserInstance; User roomMaster = General.UnknownUserInstance;
if (DsUser != null && DsUser.Tables.Count > 0) if (dsUser != null && dsUser.Tables.Count > 0)
{ {
DataRow[] rows = DsUser.Tables[0].Select($"{UserQuery.Column_UID} = {(long)DrRoom[RoomQuery.Column_RoomMaster]}"); DataRow[] rows = dsUser.Tables[0].Select($"{UserQuery.Column_UID} = {(long)drRoom[RoomQuery.Column_RoomMaster]}");
if (rows.Length > 0) if (rows.Length > 0)
{ {
RoomMaster = GetUser(rows[0]); roomMaster = GetUser(rows[0]);
} }
} }
RoomType RoomType = (RoomType)Convert.ToInt32(DrRoom[RoomQuery.Column_RoomType]); RoomType roomType = (RoomType)Convert.ToInt32(drRoom[RoomQuery.Column_RoomType]);
string GameModule = (string)DrRoom[RoomQuery.Column_GameModule]; string gameModule = (string)drRoom[RoomQuery.Column_GameModule];
string GameMap = (string)DrRoom[RoomQuery.Column_GameMap]; string gameMap = (string)drRoom[RoomQuery.Column_GameMap];
RoomState RoomState = (RoomState)Convert.ToInt32(DrRoom[RoomQuery.Column_RoomState]); RoomState roomState = (RoomState)Convert.ToInt32(drRoom[RoomQuery.Column_RoomState]);
bool IsRank = Convert.ToInt32(DrRoom[RoomQuery.Column_IsRank]) == 1; bool isRank = Convert.ToInt32(drRoom[RoomQuery.Column_IsRank]) == 1;
string Password = (string)DrRoom[RoomQuery.Column_Password]; string password = (string)drRoom[RoomQuery.Column_Password];
list.Add(GetRoom(Id, Roomid, CreateTime, RoomMaster, RoomType, GameModule, GameMap, RoomState, IsRank, Password)); list.Add(GetRoom(Id, Roomid, createTime, roomMaster, roomType, gameModule, gameMap, roomState, isRank, password));
} }
} }
return list; return list;

View File

@ -2,7 +2,6 @@
using System.Net; using System.Net;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Milimoe.FunGame.Core.Library.Common.Architecture; using Milimoe.FunGame.Core.Library.Common.Architecture;
@ -282,7 +281,7 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <returns></returns> /// <returns></returns>
public static async Task<T?> HttpPost<T>(string url, string json) public static async Task<T?> HttpPost<T>(string url, string json)
{ {
HttpContent content = new StringContent(json, Encoding.UTF8, "application/json"); HttpContent content = new StringContent(json, General.DefaultEncoding, "application/json");
HttpResponseMessage response = await client.PostAsync(url, content); HttpResponseMessage response = await client.PostAsync(url, content);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
@ -431,7 +430,7 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <returns></returns> /// <returns></returns>
public static string RSAEncrypt(string plain_text, string plublic_key) public static string RSAEncrypt(string plain_text, string plublic_key)
{ {
byte[] plain = Encoding.UTF8.GetBytes(plain_text); byte[] plain = General.DefaultEncoding.GetBytes(plain_text);
using RSACryptoServiceProvider rsa = new(); using RSACryptoServiceProvider rsa = new();
rsa.FromXmlString(plublic_key); rsa.FromXmlString(plublic_key);
byte[] encrypted = rsa.Encrypt(plain, false); byte[] encrypted = rsa.Encrypt(plain, false);
@ -450,7 +449,7 @@ namespace Milimoe.FunGame.Core.Api.Utility
using RSACryptoServiceProvider rsa = new(); using RSACryptoServiceProvider rsa = new();
rsa.FromXmlString(private_key); rsa.FromXmlString(private_key);
byte[] decrypted = rsa.Decrypt(secret, false); byte[] decrypted = rsa.Decrypt(secret, false);
return Encoding.UTF8.GetString(decrypted); return General.DefaultEncoding.GetString(decrypted);
} }
} }

View File

@ -57,15 +57,12 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// </summary> /// </summary>
public static void Init(FunGameInfo.FunGame FunGameType) public static void Init(FunGameInfo.FunGame FunGameType)
{ {
StreamWriter writer = new(DefaultFileName, false, General.DefaultEncoding);
switch (FunGameType) switch (FunGameType)
{ {
case FunGameInfo.FunGame.FunGame_Core: case FunGameInfo.FunGame.FunGame_Core:
case FunGameInfo.FunGame.FunGame_Core_Api: case FunGameInfo.FunGame.FunGame_Core_Api:
case FunGameInfo.FunGame.FunGame_Console: case FunGameInfo.FunGame.FunGame_Console:
case FunGameInfo.FunGame.FunGame_Desktop: case FunGameInfo.FunGame.FunGame_Desktop:
writer.Write("[Config]");
writer.Close();
/** /**
* Config * Config
*/ */
@ -79,8 +76,6 @@ namespace Milimoe.FunGame.Core.Api.Utility
WriteINI("Account", "AutoKey", ""); WriteINI("Account", "AutoKey", "");
break; break;
case FunGameInfo.FunGame.FunGame_Server: case FunGameInfo.FunGame.FunGame_Server:
writer.Write("[Server]");
writer.Close();
/** /**
* Server * Server
*/ */
@ -100,6 +95,11 @@ namespace Milimoe.FunGame.Core.Api.Utility
* Socket * Socket
*/ */
WriteINI("Socket", "Port", "22222"); WriteINI("Socket", "Port", "22222");
WriteINI("Socket", "UseWebSocket", "false");
WriteINI("Socket", "WebSocketAddress", "*");
WriteINI("Socket", "WebSocketPort", "22223");
WriteINI("Socket", "WebSocketSubUrl", "ws");
WriteINI("Socket", "WebSocketSSL", "false");
WriteINI("Socket", "MaxPlayer", "20"); WriteINI("Socket", "MaxPlayer", "20");
WriteINI("Socket", "MaxConnectFailed", "0"); WriteINI("Socket", "MaxConnectFailed", "0");
/** /**

View File

@ -1,7 +1,6 @@
using Milimoe.FunGame.Core.Interface.Sockets; using Milimoe.FunGame.Core.Interface.Sockets;
using Milimoe.FunGame.Core.Library.Common.Architecture; using Milimoe.FunGame.Core.Library.Common.Architecture;
using Milimoe.FunGame.Core.Library.Common.Network; using Milimoe.FunGame.Core.Library.Common.Network;
using Milimoe.FunGame.Core.Service;
namespace Milimoe.FunGame.Core.Controller namespace Milimoe.FunGame.Core.Controller
{ {
@ -37,7 +36,7 @@ namespace Milimoe.FunGame.Core.Controller
if (socket != null) if (socket != null)
{ {
_Socket = socket; _Socket = socket;
socket.BindEvent(new SocketManager.SocketReceiveHandler(SocketHandler)); socket.AddSocketObjectHandler(SocketHandler);
} }
else throw new SocketCreateReceivingException(); else throw new SocketCreateReceivingException();
} }
@ -51,7 +50,7 @@ namespace Milimoe.FunGame.Core.Controller
if (websocket != null) if (websocket != null)
{ {
_WebSocket = websocket; _WebSocket = websocket;
websocket.BindEvent(new SocketManager.SocketReceiveHandler(SocketHandler)); websocket.AddSocketObjectHandler(SocketHandler);
} }
else throw new SocketCreateReceivingException(); else throw new SocketCreateReceivingException();
} }
@ -90,8 +89,8 @@ namespace Milimoe.FunGame.Core.Controller
{ {
if (Disposing) if (Disposing)
{ {
_Socket?.BindEvent(new SocketManager.SocketReceiveHandler(SocketHandler), true); _Socket?.RemoveSocketObjectHandler(SocketHandler);
_WebSocket?.BindEvent(new SocketManager.SocketReceiveHandler(SocketHandler), true); _WebSocket?.RemoveSocketObjectHandler(SocketHandler);
} }
} }
IsDisposed = true; IsDisposed = true;

View File

@ -8,15 +8,15 @@ namespace Milimoe.FunGame.Core.Entity
{ {
public long Id => User.Id; public long Id => User.Id;
public User User { get; } public User User { get; }
public Dictionary<long, double> DamageStats { get; } = new(); public Dictionary<long, double> DamageStats { get; } = [];
public Dictionary<long, double> PhysicalDamageStats { get; } = new(); public Dictionary<long, double> PhysicalDamageStats { get; } = [];
public Dictionary<long, double> MagicDamageStats { get; } = new(); public Dictionary<long, double> MagicDamageStats { get; } = [];
public Dictionary<long, double> RealDamageStats { get; } = new(); public Dictionary<long, double> RealDamageStats { get; } = [];
public Dictionary<long, double> AvgDamageStats public Dictionary<long, double> AvgDamageStats
{ {
get get
{ {
Dictionary<long, double> avgdamage = new(); Dictionary<long, double> avgdamage = [];
foreach (long key in Plays.Keys) foreach (long key in Plays.Keys)
{ {
long plays = Plays[key]; long plays = Plays[key];
@ -34,7 +34,7 @@ namespace Milimoe.FunGame.Core.Entity
{ {
get get
{ {
Dictionary<long, double> avgdamage = new(); Dictionary<long, double> avgdamage = [];
foreach (long key in Plays.Keys) foreach (long key in Plays.Keys)
{ {
long plays = Plays[key]; long plays = Plays[key];
@ -52,7 +52,7 @@ namespace Milimoe.FunGame.Core.Entity
{ {
get get
{ {
Dictionary<long, double> avgdamage = new(); Dictionary<long, double> avgdamage = [];
foreach (long key in Plays.Keys) foreach (long key in Plays.Keys)
{ {
long plays = Plays[key]; long plays = Plays[key];
@ -70,7 +70,7 @@ namespace Milimoe.FunGame.Core.Entity
{ {
get get
{ {
Dictionary<long, double> avgdamage = new(); Dictionary<long, double> avgdamage = [];
foreach (long key in Plays.Keys) foreach (long key in Plays.Keys)
{ {
long plays = Plays[key]; long plays = Plays[key];
@ -84,17 +84,17 @@ namespace Milimoe.FunGame.Core.Entity
return avgdamage; return avgdamage;
} }
} }
public Dictionary<long, long> Kills { get; } = new(); public Dictionary<long, long> Kills { get; } = [];
public Dictionary<long, long> Deaths { get; } = new(); public Dictionary<long, long> Deaths { get; } = [];
public Dictionary<long, long> Assists { get; } = new(); public Dictionary<long, long> Assists { get; } = [];
public Dictionary<long, long> Plays { get; } = new(); public Dictionary<long, long> Plays { get; } = [];
public Dictionary<long, long> Wins { get; } = new(); public Dictionary<long, long> Wins { get; } = [];
public Dictionary<long, long> Loses { get; } = new(); public Dictionary<long, long> Loses { get; } = [];
public Dictionary<long, double> Winrates public Dictionary<long, double> Winrates
{ {
get get
{ {
Dictionary<long, double> winrates = new(); Dictionary<long, double> winrates = [];
foreach (long key in Plays.Keys) foreach (long key in Plays.Keys)
{ {
long plays = Plays[key]; long plays = Plays[key];
@ -108,15 +108,15 @@ namespace Milimoe.FunGame.Core.Entity
return winrates; return winrates;
} }
} }
public Dictionary<long, double> RatingStats { get; } = new(); public Dictionary<long, double> RatingStats { get; } = [];
public Dictionary<long, double> EloStats { get; } = new(); public Dictionary<long, double> EloStats { get; } = [];
public Dictionary<long, string> RankStats { get; } = new(); public Dictionary<long, string> RankStats { get; } = [];
public string GetWinrate(long season) public string GetWinrate(long season)
{ {
if (Winrates.ContainsKey(season)) if (Winrates.TryGetValue(season, out double value))
{ {
return Winrates[season].ToString("0.##%"); return value.ToString("0.##%");
} }
return "0%"; return "0%";
} }

View File

@ -17,6 +17,8 @@ namespace Milimoe.FunGame.Core.Entity
public bool IsRank { get; set; } = false; public bool IsRank { get; set; } = false;
public bool HasPass => Password.Trim() != ""; public bool HasPass => Password.Trim() != "";
public string Password { get; set; } = ""; public string Password { get; set; } = "";
public int MaxUsers { get; set; } = 0;
public Dictionary<User, bool> UserAndIsReady { get; } = [];
public GameStatistics Statistics { get; set; } public GameStatistics Statistics { get; set; }
internal Room() internal Room()
@ -24,18 +26,19 @@ namespace Milimoe.FunGame.Core.Entity
Statistics = new(this); Statistics = new(this);
} }
internal Room(long Id = 0, string Roomid = "-1", DateTime? CreateTime = null, User? RoomMaster = null, RoomType RoomType = RoomType.All, string GameModule = "", string GameMap = "", RoomState RoomState = RoomState.Created, bool IsRank = false, string Password = "") internal Room(long id = 0, string roomid = "-1", DateTime? createTime = null, User? roomMaster = null, RoomType roomType = RoomType.All, string gameModule = "", string gameMap = "", RoomState roomState = RoomState.Created, bool isRank = false, string password = "", int maxUsers = 4)
{ {
this.Id = Id; Id = id;
this.Roomid = Roomid; Roomid = roomid;
this.CreateTime = CreateTime ?? General.DefaultTime; CreateTime = createTime ?? General.DefaultTime;
this.RoomMaster = RoomMaster ?? General.UnknownUserInstance; RoomMaster = roomMaster ?? General.UnknownUserInstance;
this.RoomType = RoomType; RoomType = roomType;
this.GameModule = GameModule; GameModule = gameModule;
this.GameMap = GameMap; GameMap = gameMap;
this.RoomState = RoomState; RoomState = roomState;
this.IsRank = IsRank; IsRank = isRank;
this.Password = Password; Password = password;
MaxUsers = maxUsers;
Statistics = new(this); Statistics = new(this);
} }

9
Entity/System/Season.cs Normal file
View File

@ -0,0 +1,9 @@
namespace Milimoe.FunGame.Core.Entity.System
{
public class Season(long id, string name, string description)
{
public long Id { get; } = id;
public string Name { get; } = name;
public string Description { get; } = description;
}
}

View File

@ -8,6 +8,6 @@ namespace Milimoe.FunGame.Core.Interface.Addons
{ {
public bool StartServer(string GameModule, Room Room, List<User> Users, IServerModel RoomMasterServerModel, Dictionary<string, IServerModel> ServerModels, params object[] args); public bool StartServer(string GameModule, Room Room, List<User> Users, IServerModel RoomMasterServerModel, Dictionary<string, IServerModel> ServerModels, params object[] args);
public Dictionary<string, object> GamingMessageHandler(string username, GamingType type, Dictionary<string, object> data); public Task<Dictionary<string, object>> GamingMessageHandler(string username, GamingType type, Dictionary<string, object> data);
} }
} }

View File

@ -6,8 +6,10 @@ namespace Milimoe.FunGame.Core.Interface.HTTP
{ {
public interface IHTTPClient : IBaseSocket public interface IHTTPClient : IBaseSocket
{ {
public bool Receiving { get; }
public Task Receive();
public Task<SocketResult> Send(SocketMessageType type, params object[] objs); public Task<SocketResult> Send(SocketMessageType type, params object[] objs);
public SocketObject SocketObject_Handler(SocketObject objs); public void AddSocketObjectHandler(Action<SocketObject> method);
public void BindEvent(Delegate method, bool remove = false); public void RemoveSocketObjectHandler(Action<SocketObject> method);
} }
} }

View File

@ -1,6 +1,6 @@
using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Api.Transmittal;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Common.Addon; using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Common.Network;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Core.Interface.Base namespace Milimoe.FunGame.Core.Interface.Base
@ -10,28 +10,43 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// <summary> /// <summary>
/// 服务器实例是否在运行 /// 服务器实例是否在运行
/// </summary> /// </summary>
public abstract bool Running { get; } public bool Running { get; }
/// <summary> /// <summary>
/// 客户端的套接字实例 /// 客户端的套接字实例
/// </summary> /// </summary>
public abstract ISocketMessageProcessor? Socket { get; } public ISocketMessageProcessor? Socket { get; }
/// <summary>
/// 客户端的数据库连接实例
/// </summary>
public SQLHelper? SQLHelper { get; }
/// <summary>
/// 客户端的邮件服务实例
/// </summary>
public MailSender? MailSender { get; }
/// <summary> /// <summary>
/// 客户端的用户实例,在用户登录后有效 /// 客户端的用户实例,在用户登录后有效
/// </summary> /// </summary>
public abstract User User { get; } public User User { get; }
/// <summary> /// <summary>
/// 客户端的名称默认是客户端的IP地址 /// 客户端的名称默认是客户端的IP地址
/// </summary> /// </summary>
public abstract string ClientName { get; } public string ClientName { get; }
/// <summary> /// <summary>
/// 客户端是否启动了开发者模式 /// 客户端是否启动了开发者模式
/// </summary> /// </summary>
public bool IsDebugMode { get; } public bool IsDebugMode { get; }
/// <summary>
/// 客户端所在的房间
/// </summary>
public Room InRoom { get; set; }
/// <summary> /// <summary>
/// 客户端的游戏模组服务器 /// 客户端的游戏模组服务器
/// </summary> /// </summary>
@ -40,11 +55,10 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// <summary> /// <summary>
/// 向客户端发送消息 /// 向客户端发送消息
/// </summary> /// </summary>
/// <param name="socket"></param>
/// <param name="type"></param> /// <param name="type"></param>
/// <param name="objs"></param> /// <param name="objs"></param>
/// <returns></returns> /// <returns></returns>
public bool Send(ISocketMessageProcessor socket, SocketMessageType type, params object[] objs); public Task<bool> Send(SocketMessageType type, params object[] objs);
/// <summary> /// <summary>
/// 向客户端发送系统消息 /// 向客户端发送系统消息
@ -61,19 +75,5 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public string GetClientName(); public string GetClientName();
/// <summary>
/// 开始接收客户端消息
/// <para>请勿在 <see cref="GameModuleServer"/> 中调用此方法</para>
/// </summary>
/// <param name="socket"></param>
/// <returns></returns>
public bool Read(ISocketMessageProcessor socket);
/// <summary>
/// 启动对客户端的监听
/// <para>请勿在 <see cref="GameModuleServer"/> 中调用此方法</para>
/// </summary>
public void Start();
} }
} }

View File

@ -0,0 +1,32 @@
using Milimoe.FunGame.Core.Api.Utility;
namespace Milimoe.FunGame.Core.Interface.Base
{
public interface ISocketListener<T> where T : ISocketMessageProcessor
{
/// <summary>
/// 监听器的名称
/// </summary>
public string Name { get; }
/// <summary>
/// 已连接的客户端列表
/// </summary>
public ConcurrentModelList<IServerModel> ClientList { get; }
/// <summary>
/// 已登录的用户列表
/// </summary>
public ConcurrentModelList<IServerModel> UserList { get; }
/// <summary>
/// 黑名单IP地址列表
/// </summary>
public List<string> BannedList { get; }
/// <summary>
/// 关闭监听
/// </summary>
public void Close();
}
}

View File

@ -1,15 +1,20 @@
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Common.Network;
using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Core.Interface.Base namespace Milimoe.FunGame.Core.Interface.Base
{ {
public interface ISocketMessageProcessor public interface ISocketMessageProcessor
{ {
public Type InstanceType { get; } public Type InstanceType { get; }
public Guid Token { get; }
public string ClientIP { get; } public string ClientIP { get; }
public string ClientName { get; } public string ClientName { get; }
public SocketObject[] Receive();
public Task<SocketObject[]> ReceiveAsync();
public SocketResult Send(SocketMessageType type, params object[] objs); public SocketResult Send(SocketMessageType type, params object[] objs);
public Task<SocketResult> SendAsync(SocketMessageType type, params object[] objs); public Task<SocketResult> SendAsync(SocketMessageType type, params object[] objs);
public void Close(); public void Close();
public Task CloseAsync();
} }
} }

View File

@ -1,4 +1,5 @@
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Common.Network;
using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Core.Interface.Sockets namespace Milimoe.FunGame.Core.Interface.Sockets
{ {
@ -6,8 +7,10 @@ namespace Milimoe.FunGame.Core.Interface.Sockets
{ {
public bool Receiving { get; } public bool Receiving { get; }
public void StartReceiving(Task t); public void StartReceiving(Task t);
public void StopReceiving();
public SocketResult Send(SocketMessageType type, params object[] objs); public SocketResult Send(SocketMessageType type, params object[] objs);
public Library.Common.Network.SocketObject[] Receive(); public SocketObject[] Receive();
public void BindEvent(Delegate method, bool remove = false); public void AddSocketObjectHandler(Action<SocketObject> method);
public void RemoveSocketObjectHandler(Action<SocketObject> method);
} }
} }

View File

@ -0,0 +1,16 @@
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Core.Interface.Sockets
{
public interface IClientWebSocket : IBaseSocket
{
public System.Net.WebSockets.WebSocket Instance { get; }
public bool Connected => Instance != null && Instance.State == System.Net.WebSockets.WebSocketState.Open;
public bool Receiving { get; }
public void StartReceiving(Task t);
public void StopReceiving();
public Task<Library.Common.Network.SocketObject[]> ReceiveAsync();
public Task<SocketResult> SendAsync(SocketMessageType type, params object[] objs);
}
}

View File

@ -1,10 +0,0 @@
using Milimoe.FunGame.Core.Interface.Base;
namespace Milimoe.FunGame.Core.Interface.Sockets
{
public interface IWebSocket : IBaseSocket
{
public System.Net.WebSockets.WebSocket Instance { get; }
public bool Connected => Instance != null && Instance.State == System.Net.WebSockets.WebSocketState.Open;
}
}

View File

@ -48,6 +48,8 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
public override RoomType RoomType => RoomType.Mix; public override RoomType RoomType => RoomType.Mix;
public override int MaxUsers => 8;
public override bool HideMain => false; public override bool HideMain => false;
public ExampleGameModule() public ExampleGameModule()
@ -175,7 +177,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
UserData[username].Add("connect_token", token); UserData[username].Add("connect_token", token);
} }
} }
SendGamingMessage(All.Values, GamingType.UpdateInfo, data); await SendGamingMessage(All.Values, GamingType.UpdateInfo, data);
// 新建一个线程等待所有玩家确认 // 新建一个线程等待所有玩家确认
while (true) while (true)
@ -187,7 +189,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
Controller.WriteLine("所有玩家都已经连接。"); Controller.WriteLine("所有玩家都已经连接。");
} }
public override Dictionary<string, object> GamingMessageHandler(string username, GamingType type, Dictionary<string, object> data) public override async Task<Dictionary<string, object>> GamingMessageHandler(string username, GamingType type, Dictionary<string, object> data)
{ {
Dictionary<string, object> result = []; Dictionary<string, object> result = [];
@ -205,6 +207,9 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
} }
else Controller.WriteLine(username + " 确认连接失败!"); else Controller.WriteLine(username + " 确认连接失败!");
break; break;
default:
await Task.Delay(1);
break;
} }
return result; return result;

View File

@ -44,6 +44,11 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
/// </summary> /// </summary>
public abstract RoomType RoomType { get; } public abstract RoomType RoomType { get; }
/// <summary>
/// 模组的容纳人数
/// </summary>
public abstract int MaxUsers { get; }
/// <summary> /// <summary>
/// 是否隐藏主界面 /// 是否隐藏主界面
/// </summary> /// </summary>

View File

@ -72,7 +72,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
/// <param name="type">消息类型</param> /// <param name="type">消息类型</param>
/// <param name="data">消息参数</param> /// <param name="data">消息参数</param>
/// <returns>底层会将字典中的数据发送给客户端</returns> /// <returns>底层会将字典中的数据发送给客户端</returns>
public abstract Dictionary<string, object> GamingMessageHandler(string username, GamingType type, Dictionary<string, object> data); public abstract Task<Dictionary<string, object>> GamingMessageHandler(string username, GamingType type, Dictionary<string, object> data);
/// <summary> /// <summary>
/// 加载标记 /// 加载标记
@ -120,15 +120,12 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
/// <param name="clients"></param> /// <param name="clients"></param>
/// <param name="type"></param> /// <param name="type"></param>
/// <param name="data"></param> /// <param name="data"></param>
protected virtual void SendGamingMessage(IEnumerable<IServerModel> clients, GamingType type, Dictionary<string, object> data) protected virtual async Task SendGamingMessage(IEnumerable<IServerModel> clients, GamingType type, Dictionary<string, object> data)
{ {
// 发送局内消息 // 发送局内消息
foreach (IServerModel s in clients) foreach (IServerModel s in clients)
{ {
if (s != null && s.Socket != null) await s.Send(SocketMessageType.Gaming, type, data);
{
s.Send(s.Socket, SocketMessageType.Gaming, type, data);
}
} }
} }
@ -138,15 +135,12 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
/// <param name="clients"></param> /// <param name="clients"></param>
/// <param name="type"></param> /// <param name="type"></param>
/// <param name="args"></param> /// <param name="args"></param>
protected virtual void Send(IEnumerable<IServerModel> clients, SocketMessageType type, params object[] args) protected virtual async Task Send(IEnumerable<IServerModel> clients, SocketMessageType type, params object[] args)
{ {
// 发送消息 // 发送消息
foreach (IServerModel s in clients) foreach (IServerModel s in clients)
{ {
if (s != null && s.Socket != null) await s.Send(type, args);
{
s.Send(s.Socket, type, args);
}
} }
} }
} }

View File

@ -12,6 +12,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture
private Task? SendingHeartBeatTask; private Task? SendingHeartBeatTask;
private bool _SendingHeartBeat = false; private bool _SendingHeartBeat = false;
private bool _LastHeartbeatReceived = false;
private int _HeartBeatFaileds = 0; private int _HeartBeatFaileds = 0;
private readonly Socket? _Socket = null; private readonly Socket? _Socket = null;
@ -20,13 +21,13 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture
public HeartBeat(Socket socket) public HeartBeat(Socket socket)
{ {
_Socket = socket; _Socket = socket;
this.TransmittalType = TransmittalType.Socket; TransmittalType = TransmittalType.Socket;
} }
public HeartBeat(HTTPClient client) public HeartBeat(HTTPClient client)
{ {
_HTTPClient = client; _HTTPClient = client;
this.TransmittalType = TransmittalType.WebSocket; TransmittalType = TransmittalType.WebSocket;
} }
public void StartSendingHeartBeat() public void StartSendingHeartBeat()
@ -34,6 +35,8 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture
if (!FunGameInfo.FunGame_DebugMode) if (!FunGameInfo.FunGame_DebugMode)
{ {
_SendingHeartBeat = true; _SendingHeartBeat = true;
_Socket?.AddSocketObjectHandler(SocketObject_Handler);
_HTTPClient?.AddSocketObjectHandler(SocketObject_Handler);
SendingHeartBeatTask = Task.Factory.StartNew(SendHeartBeat); SendingHeartBeatTask = Task.Factory.StartNew(SendHeartBeat);
} }
} }
@ -43,6 +46,8 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture
_SendingHeartBeat = false; _SendingHeartBeat = false;
SendingHeartBeatTask?.Wait(1); SendingHeartBeatTask?.Wait(1);
SendingHeartBeatTask = null; SendingHeartBeatTask = null;
_Socket?.RemoveSocketObjectHandler(SocketObject_Handler);
_HTTPClient?.RemoveSocketObjectHandler(SocketObject_Handler);
} }
private async Task SendHeartBeat() private async Task SendHeartBeat()
@ -54,13 +59,14 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture
{ {
if (!SendingHeartBeat) _SendingHeartBeat = true; if (!SendingHeartBeat) _SendingHeartBeat = true;
// 发送心跳包 // 发送心跳包
_LastHeartbeatReceived = false;
if (_Socket.Send(SocketMessageType.HeartBeat) == SocketResult.Success) if (_Socket.Send(SocketMessageType.HeartBeat) == SocketResult.Success)
{ {
await Task.Delay(4 * 000); await Task.Delay(4 * 1000);
AddHeartBeatFaileds(); if (!_LastHeartbeatReceived) AddHeartBeatFaileds();
} }
else AddHeartBeatFaileds(); else AddHeartBeatFaileds();
await Task.Delay(20 * 000); await Task.Delay(20 * 1000);
} }
} }
else if (_HTTPClient != null) else if (_HTTPClient != null)
@ -71,11 +77,11 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture
// 发送心跳包 // 发送心跳包
if (await _HTTPClient.Send(SocketMessageType.HeartBeat) == SocketResult.Success) if (await _HTTPClient.Send(SocketMessageType.HeartBeat) == SocketResult.Success)
{ {
await Task.Delay(4 * 000); await Task.Delay(4 * 1000);
AddHeartBeatFaileds(); AddHeartBeatFaileds();
} }
else AddHeartBeatFaileds(); else AddHeartBeatFaileds();
await Task.Delay(20 * 000); await Task.Delay(20 * 1000);
} }
} }
_SendingHeartBeat = false; _SendingHeartBeat = false;
@ -85,7 +91,20 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture
{ {
// 超过三次没回应心跳,服务器连接失败。 // 超过三次没回应心跳,服务器连接失败。
if (_HeartBeatFaileds++ >= 3) if (_HeartBeatFaileds++ >= 3)
{
_Socket?.Close();
_HTTPClient?.Close();
throw new LostConnectException(); throw new LostConnectException();
}
}
private void SocketObject_Handler(SocketObject obj)
{
if (obj.SocketType == SocketMessageType.HeartBeat)
{
_LastHeartbeatReceived = true;
_HeartBeatFaileds = 0;
}
} }
} }
} }

View File

@ -22,7 +22,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Event
RoomType = type; RoomType = type;
RoomTypeString = RoomSet.GetTypeString(type); RoomTypeString = RoomSet.GetTypeString(type);
Password = password; Password = password;
Room = Factory.GetRoom(RoomType: RoomType, Password: Password); Room = Factory.GetRoom(roomType: RoomType, password: Password);
} }
public RoomEventArgs(Room room) public RoomEventArgs(Room room)

View File

@ -1,93 +0,0 @@
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Interface.Sockets;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Service;
namespace Milimoe.FunGame.Core.Library.Common.Network
{
public class ClientSocket(System.Net.Sockets.Socket instance, string clientIP, string clientName, Guid token) : IClientSocket, ISocketMessageProcessor
{
public System.Net.Sockets.Socket Instance { get; } = instance;
public SocketRuntimeType Runtime => SocketRuntimeType.Server;
public Guid Token { get; } = token;
public string ClientIP { get; } = clientIP;
public string ClientName => _ClientName;
public bool Connected => Instance != null && Instance.Connected;
public bool Receiving => _Receiving;
public Type InstanceType => typeof(ClientSocket);
private Task? ReceivingTask;
private bool _Receiving;
private readonly string _ClientName = clientName;
public void Close()
{
StopReceiving();
Instance?.Close();
}
public SocketObject[] Receive()
{
try
{
return SocketManager.Receive(Instance);
}
catch
{
throw new SocketWrongInfoException();
}
}
public SocketResult Send(SocketMessageType type, params object[] objs)
{
if (Instance != null)
{
if (SocketManager.Send(Instance, new(type, Token, objs)) == SocketResult.Success)
{
return SocketResult.Success;
}
else return SocketResult.Fail;
}
return SocketResult.NotSent;
}
public async Task<SocketResult> SendAsync(SocketMessageType type, params object[] objs)
{
if (Instance != null)
{
if (await SocketManager.SendAsync(Instance, new(type, Token, objs)) == SocketResult.Success)
{
return SocketResult.Success;
}
else return SocketResult.Fail;
}
return SocketResult.NotSent;
}
public void BindEvent(Delegate method, bool remove = false)
{
if (!remove)
{
SocketManager.SocketReceive += (SocketManager.SocketReceiveHandler)method;
}
else
{
SocketManager.SocketReceive -= (SocketManager.SocketReceiveHandler)method;
}
}
public void StartReceiving(Task t)
{
_Receiving = true;
ReceivingTask = t;
}
public void StopReceiving()
{
_Receiving = false;
ReceivingTask?.Wait(1);
ReceivingTask = null;
}
}
}

View File

@ -1,43 +0,0 @@
using System.Net.WebSockets;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Interface.Sockets;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Service;
namespace Milimoe.FunGame.Core.Library.Common.Network
{
public class ClientWebSocket(HTTPListener listener, WebSocket instance, string clientIP, string clientName, Guid token) : IWebSocket, ISocketMessageProcessor
{
public HTTPListener Listener => listener;
public WebSocket Instance => instance;
public SocketRuntimeType Runtime => SocketRuntimeType.Server;
public Guid Token => token;
public string ClientIP => clientIP;
public string ClientName => clientName;
public Type InstanceType => typeof(ClientWebSocket);
public void Close()
{
TaskUtility.NewTask(async () =>
{
if (Instance.State == WebSocketState.Open)
{
// 安全关闭 WebSocket 连接
await Instance.CloseAsync(WebSocketCloseStatus.NormalClosure, "服务器正在关闭,断开连接!", CancellationToken.None);
Listener.ClientSockets.Remove(Token);
}
});
}
public SocketResult Send(SocketMessageType type, params object[] objs)
{
throw new AsyncSendException();
}
public async Task<SocketResult> SendAsync(SocketMessageType type, params object[] objs)
{
return await HTTPManager.Send(Instance, new(type, token, objs));
}
}
}

View File

@ -11,53 +11,44 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
{ {
public System.Net.WebSockets.ClientWebSocket? Instance { get; } = null; public System.Net.WebSockets.ClientWebSocket? Instance { get; } = null;
public SocketRuntimeType Runtime => SocketRuntimeType.Client; public SocketRuntimeType Runtime => SocketRuntimeType.Client;
public Guid Token { get; } = Guid.Empty; public Guid Token { get; set; } = Guid.Empty;
public string ServerAddress { get; } = ""; public string ServerAddress { get; } = "";
public int ServerPort { get; } = 0; public int ServerPort { get; } = 0;
public string ServerName { get; } = ""; public string ServerName { get; } = "";
public string ServerNotice { get; } = ""; public string ServerNotice { get; } = "";
public bool Connected => Instance != null && Instance.State == WebSocketState.Open;
public bool Receiving => _receiving;
private HeartBeat HeartBeat { get; }
private bool _Listening = false; private bool _receiving = false;
private readonly HeartBeat HeartBeat; private readonly HashSet<Action<SocketObject>> _boundEvents = [];
private HTTPClient(System.Net.WebSockets.ClientWebSocket Instance, string ServerAddress, int ServerPort, params object[] args) private HTTPClient(System.Net.WebSockets.ClientWebSocket instance, string serverAddress, int serverPort, params object[] args)
{ {
this.Instance = Instance; Instance = instance;
this.ServerAddress = ServerAddress; ServerAddress = serverAddress;
this.ServerPort = ServerPort; ServerPort = serverPort;
HeartBeat = new(this); HeartBeat = new(this);
HeartBeat.StartSendingHeartBeat(); HeartBeat.StartSendingHeartBeat();
Task.Factory.StartNew(async () => await StartListening(args)); Task.Factory.StartNew(async () => await StartListening(args));
} }
public static async Task<HTTPClient> Connect(string ServerAddress, int ServerPort, bool SSL, string SubDirectory = "", params object[] args) public static async Task<HTTPClient> Connect(string serverAddress, int serverPort, bool ssl, string subUrl = "", params object[] args)
{ {
string ServerIP = Api.Utility.NetworkUtility.GetIPAddress(ServerAddress); string ServerIP = Api.Utility.NetworkUtility.GetIPAddress(serverAddress);
Uri uri = new((SSL ? "wss://" : "ws://") + ServerIP + ":" + ServerPort + "/" + SubDirectory); Uri uri = new((ssl ? "wss://" : "ws://") + ServerIP + ":" + serverPort + "/" + subUrl.Trim('/') + "/");
System.Net.WebSockets.ClientWebSocket? socket = await HTTPManager.Connect(uri); System.Net.WebSockets.ClientWebSocket? socket = await HTTPManager.Connect(uri);
if (socket != null && socket.State == WebSocketState.Open) if (socket != null && socket.State == WebSocketState.Open)
{ {
HTTPClient client = new(socket, ServerAddress, ServerPort, args); HTTPClient client = new(socket, serverAddress, serverPort, args);
return client; return client;
} }
throw new CanNotConnectException(); throw new CanNotConnectException();
} }
private async Task StartListening(params object[] args)
{
if (Instance != null && Instance.State == WebSocketState.Open)
{
if (await HTTPManager.Send(Instance, new(SocketMessageType.Connect, Guid.Empty, args)) == SocketResult.Success && await HTTPManager.ReceiveMessage(this))
{
_Listening = true;
await Receive();
}
}
}
public async Task Receive() public async Task Receive()
{ {
while (_Listening) while (_receiving)
{ {
try try
{ {
@ -81,29 +72,42 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
return SocketResult.NotSent; return SocketResult.NotSent;
} }
public virtual SocketObject SocketObject_Handler(SocketObject objs) public void AddSocketObjectHandler(Action<SocketObject> method)
{ {
return new(SocketMessageType.Unknown, Guid.Empty); if (_boundEvents.Add(method))
{
SocketManager.SocketReceive += new SocketManager.SocketReceiveHandler(method);
}
} }
public void BindEvent(Delegate method, bool remove = false) public void RemoveSocketObjectHandler(Action<SocketObject> method)
{ {
if (!remove) _boundEvents.Remove(method);
{ SocketManager.SocketReceive -= new SocketManager.SocketReceiveHandler(method);
SocketManager.SocketReceive += (SocketManager.SocketReceiveHandler)method;
}
else
{
SocketManager.SocketReceive -= (SocketManager.SocketReceiveHandler)method;
}
} }
public void Close() public void Close()
{ {
_Listening = false; _receiving = false;
HeartBeat.StopSendingHeartBeat(); HeartBeat.StopSendingHeartBeat();
Instance?.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); Instance?.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
Instance?.Dispose(); Instance?.Dispose();
foreach (Action<SocketObject> method in _boundEvents.ToList())
{
RemoveSocketObjectHandler(method);
}
}
private async Task StartListening(params object[] args)
{
if (Instance != null && Instance.State == WebSocketState.Open)
{
if (await HTTPManager.Send(Instance, new(SocketMessageType.Connect, Guid.Empty, args)) == SocketResult.Success && await HTTPManager.ReceiveMessage(this))
{
_receiving = true;
await Receive();
}
}
} }
} }
} }

View File

@ -1,18 +1,22 @@
using System.Net; using System.Net;
using System.Net.WebSockets; using System.Net.WebSockets;
using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Interface.HTTP; using Milimoe.FunGame.Core.Interface.HTTP;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Service; using Milimoe.FunGame.Core.Service;
namespace Milimoe.FunGame.Core.Library.Common.Network namespace Milimoe.FunGame.Core.Library.Common.Network
{ {
public class HTTPListener : IHTTPListener public class HTTPListener : IHTTPListener, ISocketListener<ServerWebSocket>
{ {
public HttpListener Instance { get; } public HttpListener Instance { get; }
public string Name => "HTTPListener";
public SocketRuntimeType Runtime => SocketRuntimeType.Server; public SocketRuntimeType Runtime => SocketRuntimeType.Server;
public Guid Token { get; } = Guid.Empty; public Guid Token { get; } = Guid.Empty;
public Dictionary<Guid, WebSocket> ClientSockets { get; } = []; public ConcurrentModelList<IServerModel> ClientList { get; } = [];
public ConcurrentModelList<IServerModel> UserList { get; } = [];
public List<string> BannedList { get; } = [];
private HTTPListener(HttpListener instance) private HTTPListener(HttpListener instance)
{ {
@ -20,24 +24,27 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
Token = Guid.NewGuid(); Token = Guid.NewGuid();
} }
public static HTTPListener StartListening(int port, bool ssl = false) public static HTTPListener StartListening(string address = "*", int port = 22223, string subUrl = "ws", bool ssl = false)
{ {
HttpListener? socket = HTTPManager.StartListening(port, ssl); HttpListener? socket = HTTPManager.StartListening(address, port, subUrl, ssl);
if (socket != null) if (socket != null)
{ {
HTTPListener instance = new(socket); HTTPListener instance = new(socket);
Task t = Task.Run(async () => await HTTPManager.Receiving(instance));
return instance; return instance;
} }
else throw new SocketCreateListenException(); else throw new SocketCreateListenException();
} }
public async Task SendMessage(Guid token, SocketObject obj) public async Task<ServerWebSocket> Accept(Guid token)
{ {
if (ClientSockets.TryGetValue(token, out WebSocket? socket) && socket != null) object[] result = await HTTPManager.Accept();
if (result != null && result.Length == 2)
{ {
await HTTPManager.Send(socket, obj); string clientIP = (string)result[0];
WebSocket client = (WebSocket)result[1];
return new ServerWebSocket(this, client, clientIP, clientIP, token);
} }
throw new SocketGetClientException();
} }
public virtual bool CheckClientConnection(SocketObject objs) public virtual bool CheckClientConnection(SocketObject objs)
@ -52,27 +59,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
public void Close() public void Close()
{ {
bool closing = true; Instance?.Close();
TaskUtility.NewTask(async () =>
{
// 关闭所有 WebSocket 连接
foreach (WebSocket socket in ClientSockets.Values)
{
if (socket.State == WebSocketState.Open)
{
// 安全关闭 WebSocket 连接
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "服务器正在关闭,断开连接!", CancellationToken.None);
}
}
ClientSockets.Clear();
Instance?.Close();
closing = true;
});
while (closing)
{
if (!closing) break;
Thread.Sleep(100);
}
} }
} }
} }

View File

@ -5,109 +5,109 @@ using Milimoe.FunGame.Core.Service;
namespace Milimoe.FunGame.Core.Library.Common.Network namespace Milimoe.FunGame.Core.Library.Common.Network
{ {
public class ServerSocket : ISocket public class ServerSocket(SocketListener listener, System.Net.Sockets.Socket instance, string clientIP, string clientName, Guid token) : IClientSocket, ISocketMessageProcessor
{ {
public System.Net.Sockets.Socket Instance { get; } public SocketListener Listener { get; } = listener;
public System.Net.Sockets.Socket Instance { get; } = instance;
public SocketRuntimeType Runtime => SocketRuntimeType.Server; public SocketRuntimeType Runtime => SocketRuntimeType.Server;
public Guid Token { get; } = Guid.Empty; public Guid Token { get; } = token;
public string ClientIP { get; } = clientIP;
public string ClientName => clientName;
public bool Connected => Instance != null && Instance.Connected; public bool Connected => Instance != null && Instance.Connected;
public List<IServerModel> ClientList => OnlineClients.GetList(); public bool Receiving => _receiving;
public List<IServerModel> UserList => OnlineUsers.GetList(); public Type InstanceType => typeof(ServerSocket);
public List<string> BannedList { get; } = [];
public int ClientCount => OnlineClients.Count;
public int UserCount => OnlineUsers.Count;
public int BannedCount => BannedList.Count;
private readonly ModelManager<IServerModel> OnlineClients; private Task? _receivingTask;
private readonly ModelManager<IServerModel> OnlineUsers; private bool _receiving;
private readonly HashSet<Action<SocketObject>> _boundEvents = [];
private ServerSocket(System.Net.Sockets.Socket instance, int maxConnection = 0)
{
Token = Guid.NewGuid();
Instance = instance;
if (maxConnection <= 0)
{
OnlineClients = [];
OnlineUsers = [];
}
else
{
OnlineClients = new(maxConnection);
OnlineUsers = new(maxConnection);
}
}
public static ServerSocket StartListening(int port = 22222, int maxConnection = 0)
{
if (maxConnection <= 0) maxConnection = SocketSet.MaxConnection_2C2G;
System.Net.Sockets.Socket? socket = SocketManager.StartListening(port, maxConnection);
if (socket != null) return new ServerSocket(socket, port);
else throw new SocketCreateListenException();
}
public static ClientSocket Accept(Guid token)
{
object[] result = SocketManager.Accept();
if (result != null && result.Length == 2)
{
string clientIP = (string)result[0];
System.Net.Sockets.Socket client = (System.Net.Sockets.Socket)result[1];
return new ClientSocket(client, clientIP, clientIP, token);
}
throw new SocketGetClientException();
}
public bool AddClient(string name, IServerModel t)
{
name = name.ToLower();
return OnlineClients.Add(name, t);
}
public bool RemoveClient(string name)
{
name = name.ToLower();
return OnlineClients.Remove(name);
}
public bool ContainsClient(string name)
{
name = name.ToLower();
return OnlineClients.ContainsKey(name);
}
public IServerModel GetClient(string name)
{
name = name.ToLower();
return OnlineClients[name];
}
public bool AddUser(string name, IServerModel t)
{
name = name.ToLower();
return OnlineUsers.Add(name, t);
}
public bool RemoveUser(string name)
{
name = name.ToLower();
return OnlineUsers.Remove(name);
}
public bool ContainsUser(string name)
{
name = name.ToLower();
return OnlineUsers.ContainsKey(name);
}
public IServerModel GetUser(string name)
{
name = name.ToLower();
return OnlineUsers[name];
}
public void Close() public void Close()
{ {
Instance?.Close(); StopReceiving();
Instance.Close();
}
public async Task CloseAsync()
{
StopReceiving();
await Task.Run(() => Instance?.Close());
}
public SocketObject[] Receive()
{
try
{
return SocketManager.Receive(Instance);
}
catch
{
throw new SocketWrongInfoException();
}
}
public async Task<SocketObject[]> ReceiveAsync()
{
try
{
return await SocketManager.ReceiveAsync(Instance);
}
catch
{
throw new SocketWrongInfoException();
}
}
public SocketResult Send(SocketMessageType type, params object[] objs)
{
if (Instance != null)
{
if (SocketManager.Send(Instance, new(type, Token, objs)) == SocketResult.Success)
{
return SocketResult.Success;
}
else return SocketResult.Fail;
}
return SocketResult.NotSent;
}
public async Task<SocketResult> SendAsync(SocketMessageType type, params object[] objs)
{
if (Instance != null)
{
if (await SocketManager.SendAsync(Instance, new(type, Token, objs)) == SocketResult.Success)
{
return SocketResult.Success;
}
else return SocketResult.Fail;
}
return SocketResult.NotSent;
}
public void AddSocketObjectHandler(Action<SocketObject> method)
{
if (_boundEvents.Add(method))
{
SocketManager.SocketReceive += new SocketManager.SocketReceiveHandler(method);
}
}
public void RemoveSocketObjectHandler(Action<SocketObject> method)
{
_boundEvents.Remove(method);
SocketManager.SocketReceive -= new SocketManager.SocketReceiveHandler(method);
}
public void StartReceiving(Task t)
{
_receiving = true;
_receivingTask = t;
}
public void StopReceiving()
{
_receiving = false;
_receivingTask?.Wait(1);
_receivingTask = null;
} }
} }
} }

View File

@ -0,0 +1,77 @@
using System.Net.WebSockets;
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Interface.Sockets;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Service;
namespace Milimoe.FunGame.Core.Library.Common.Network
{
public class ServerWebSocket(ISocketListener<ServerWebSocket> listener, WebSocket instance, string clientIP, string clientName, Guid token) : IClientWebSocket, ISocketMessageProcessor
{
public ISocketListener<ServerWebSocket> Listener => listener;
public WebSocket Instance => instance;
public SocketRuntimeType Runtime => SocketRuntimeType.Server;
public Guid Token => token;
public string ClientIP => clientIP;
public string ClientName => clientName;
public Type InstanceType => typeof(ServerWebSocket);
public bool Receiving => _receiving;
private Task? _receivingTask;
private bool _receiving = false;
public void Close()
{
throw new AsyncSendException();
}
public async Task CloseAsync()
{
if (Instance.State == WebSocketState.Open)
{
// 安全关闭 WebSocket 连接
await Instance.CloseAsync(WebSocketCloseStatus.NormalClosure, "服务器正在关闭,断开连接!", CancellationToken.None);
}
}
public SocketObject[] Receive()
{
throw new AsyncReadException();
}
public async Task<SocketObject[]> ReceiveAsync()
{
try
{
return await HTTPManager.Receive(Instance);
}
catch
{
throw new SocketWrongInfoException();
}
}
public SocketResult Send(SocketMessageType type, params object[] objs)
{
throw new AsyncSendException();
}
public async Task<SocketResult> SendAsync(SocketMessageType type, params object[] objs)
{
return await HTTPManager.Send(Instance, new(type, token, objs));
}
public void StartReceiving(Task t)
{
_receiving = true;
_receivingTask = t;
}
public void StopReceiving()
{
_receiving = false;
_receivingTask?.Wait(1);
_receivingTask = null;
}
}
}

View File

@ -16,11 +16,12 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
public string ServerName { get; } = ""; public string ServerName { get; } = "";
public string ServerNotice { get; } = ""; public string ServerNotice { get; } = "";
public bool Connected => Instance != null && Instance.Connected; public bool Connected => Instance != null && Instance.Connected;
public bool Receiving => _Receiving; public bool Receiving => _receiving;
private HeartBeat HeartBeat { get; }
private Task? ReceivingTask; private Task? _receivingTask;
private readonly HeartBeat HeartBeat; private bool _receiving = false;
private bool _Receiving = false; private readonly HashSet<Action<SocketObject>> _boundEvents = [];
private Socket(System.Net.Sockets.Socket instance, string serverAddress, int serverPort) private Socket(System.Net.Sockets.Socket instance, string serverAddress, int serverPort)
{ {
@ -65,36 +66,42 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
} }
} }
public void BindEvent(Delegate method, bool remove = false) public void AddSocketObjectHandler(Action<SocketObject> method)
{ {
if (!remove) if (_boundEvents.Add(method))
{ {
SocketManager.SocketReceive += (SocketManager.SocketReceiveHandler)method; SocketManager.SocketReceive += new SocketManager.SocketReceiveHandler(method);
}
else
{
SocketManager.SocketReceive -= (SocketManager.SocketReceiveHandler)method;
} }
} }
public void RemoveSocketObjectHandler(Action<SocketObject> method)
{
_boundEvents.Remove(method);
SocketManager.SocketReceive -= new SocketManager.SocketReceiveHandler(method);
}
public void Close() public void Close()
{ {
HeartBeat.StopSendingHeartBeat(); HeartBeat.StopSendingHeartBeat();
StopReceiving(); StopReceiving();
Instance?.Close(); Instance?.Close();
foreach (Action<SocketObject> method in _boundEvents.ToList())
{
RemoveSocketObjectHandler(method);
}
} }
public void StartReceiving(Task t) public void StartReceiving(Task t)
{ {
_Receiving = true; _receiving = true;
ReceivingTask = t; _receivingTask = t;
} }
private void StopReceiving() public void StopReceiving()
{ {
_Receiving = false; _receiving = false;
ReceivingTask?.Wait(1); _receivingTask?.Wait(1);
ReceivingTask = null; _receivingTask = null;
} }
} }
} }

View File

@ -0,0 +1,51 @@
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Interface.Sockets;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Service;
namespace Milimoe.FunGame.Core.Library.Common.Network
{
public class SocketListener : ISocket, ISocketListener<ServerSocket>
{
public System.Net.Sockets.Socket Instance { get; }
public string Name => "SocketListener";
public SocketRuntimeType Runtime => SocketRuntimeType.Server;
public Guid Token { get; } = Guid.Empty;
public bool Connected => Instance != null && Instance.Connected;
public ConcurrentModelList<IServerModel> ClientList { get; } = [];
public ConcurrentModelList<IServerModel> UserList { get; } = [];
public List<string> BannedList { get; } = [];
private SocketListener(System.Net.Sockets.Socket instance)
{
Token = Guid.NewGuid();
Instance = instance;
}
public static SocketListener StartListening(int port = 22222, int maxConnection = 0)
{
if (maxConnection <= 0) maxConnection = SocketSet.MaxConnection_2C2G;
System.Net.Sockets.Socket? socket = SocketManager.StartListening(port, maxConnection);
if (socket != null) return new SocketListener(socket);
else throw new SocketCreateListenException();
}
public ServerSocket Accept(Guid token)
{
object[] result = SocketManager.Accept();
if (result != null && result.Length == 2)
{
string clientIP = (string)result[0];
System.Net.Sockets.Socket client = (System.Net.Sockets.Socket)result[1];
return new ServerSocket(this, client, clientIP, clientIP, token);
}
throw new SocketGetClientException();
}
public void Close()
{
Instance?.Close();
}
}
}

View File

@ -27,11 +27,11 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
} }
[JsonConstructor] [JsonConstructor]
public SocketObject(SocketMessageType SocketType, Guid Token, params object[] Parameters) public SocketObject(SocketMessageType type, Guid token, params object[] args)
{ {
this.SocketType = SocketType; SocketType = type;
this.Token = Token; Token = token;
if (Parameters != null && Parameters.Length > 0) this.Parameters = Parameters; if (args != null && args.Length > 0) Parameters = args;
} }
/// <summary> /// <summary>

View File

@ -102,7 +102,7 @@ namespace Milimoe.FunGame.Core.Library.Constant
/** /**
* Register * Register
*/ */
public const string Reg_GetRegVerifyCode = "Reg::GetRegVerifyCode"; public const string Reg_Reg = "Reg::Reg";
/** /**
* Login * Login
*/ */
@ -140,7 +140,7 @@ namespace Milimoe.FunGame.Core.Library.Constant
DataRequestType.Main_Ready => Main_Ready, DataRequestType.Main_Ready => Main_Ready,
DataRequestType.Main_CancelReady => Main_CancelReady, DataRequestType.Main_CancelReady => Main_CancelReady,
DataRequestType.Main_StartGame => Main_StartGame, DataRequestType.Main_StartGame => Main_StartGame,
DataRequestType.Reg_GetRegVerifyCode => Reg_GetRegVerifyCode, DataRequestType.Reg_Reg => Reg_Reg,
DataRequestType.Login_Login => Login_Login, DataRequestType.Login_Login => Login_Login,
DataRequestType.Login_GetFindPasswordVerifyCode => Login_GetFindPasswordVerifyCode, DataRequestType.Login_GetFindPasswordVerifyCode => Login_GetFindPasswordVerifyCode,
DataRequestType.Login_UpdatePassword => Login_UpdatePassword, DataRequestType.Login_UpdatePassword => Login_UpdatePassword,

View File

@ -38,7 +38,7 @@ namespace Milimoe.FunGame.Core.Library.Constant
/// <summary> /// <summary>
/// 默认的字符编码 /// 默认的字符编码
/// </summary> /// </summary>
public static Encoding DefaultEncoding => Encoding.Unicode; public static Encoding DefaultEncoding => Encoding.UTF8;
/// <summary> /// <summary>
/// 默认的时间格式 yyyy-MM-dd HH:mm:ss.fff /// 默认的时间格式 yyyy-MM-dd HH:mm:ss.fff

View File

@ -97,7 +97,7 @@ namespace Milimoe.FunGame.Core.Library.Constant
Main_Ready, Main_Ready,
Main_CancelReady, Main_CancelReady,
Main_StartGame, Main_StartGame,
Reg_GetRegVerifyCode, Reg_Reg,
Login_Login, Login_Login,
Login_GetFindPasswordVerifyCode, Login_GetFindPasswordVerifyCode,
Login_UpdatePassword, Login_UpdatePassword,
@ -843,7 +843,8 @@ namespace Milimoe.FunGame.Core.Library.Constant
Interface, Interface,
DataRequest, DataRequest,
Plugin, Plugin,
GameModule GameModule,
Warning
} }
public enum SQLMode public enum SQLMode

View File

@ -165,13 +165,18 @@
public override string Message => "试图在不支持的类中创建数据请求 (#10033)"; public override string Message => "试图在不支持的类中创建数据请求 (#10033)";
} }
public class AsyncSendException : Exception
{
public override string Message => "必须以异步方式发送数据 (#10034)";
}
public class SQLServiceException : Exception public class SQLServiceException : Exception
{ {
public override string Message => "SQL服务遇到错误请检查SQL配置 (#10035)"; public override string Message => "SQL服务遇到错误请检查SQL配置 (#10034)";
}
public class AsyncSendException : Exception
{
public override string Message => "必须以异步方式发送数据 (#10035)";
}
public class AsyncReadException : Exception
{
public override string Message => "必须以异步方式读取数据 (#10036)";
} }
} }

View File

@ -15,16 +15,17 @@
public const string Column_IsRank = "IsRank"; public const string Column_IsRank = "IsRank";
public const string Column_HasPass = "HasPass"; public const string Column_HasPass = "HasPass";
public const string Column_Password = "Password"; public const string Column_Password = "Password";
public const string Column_MaxUsers = "MaxUsers";
public const string Select_Rooms = $"{Command_Select} {TableName}.{Command_All}, {UserQuery.TableName}.{UserQuery.Column_Username} {Command_As} {Column_RoomMasterName} " + public const string Select_Rooms = $"{Command_Select} {TableName}.{Command_All}, {UserQuery.TableName}.{UserQuery.Column_Username} {Command_As} {Column_RoomMasterName} " +
$"{Command_From} {TableName} {Command_LeftJoin} {UserQuery.TableName} {Command_On} {UserQuery.TableName}.{UserQuery.Column_UID} = {TableName}.{Column_RoomMaster}"; $"{Command_From} {TableName} {Command_LeftJoin} {UserQuery.TableName} {Command_On} {UserQuery.TableName}.{UserQuery.Column_UID} = {TableName}.{Column_RoomMaster}";
public static string Insert_CreateRoom(string RoomID, long RoomMaster, Library.Constant.RoomType RoomType, string GameModule, string GameMap, bool IsRank, string Password) public static string Insert_CreateRoom(string roomid, long roomMaster, Library.Constant.RoomType roomType, string gameModule, string gameMap, bool isRank, string password, int maxUsers)
{ {
Library.Constant.RoomState RoomState = Library.Constant.RoomState.Created; Library.Constant.RoomState RoomState = Library.Constant.RoomState.Created;
DateTime NowTime = DateTime.Now; DateTime NowTime = DateTime.Now;
bool HasPass = Password.Trim() != ""; bool HasPass = password.Trim() != "";
return $"{Command_Insert} {Command_Into} {TableName} ({Column_RoomID}, {Column_CreateTime}, {Column_RoomMaster}, {Column_RoomType}, {Column_GameModule}, {Column_GameMap}, {Column_RoomState}, {Column_IsRank}, {Column_HasPass}, {Column_Password})" + return $"{Command_Insert} {Command_Into} {TableName} ({Column_RoomID}, {Column_CreateTime}, {Column_RoomMaster}, {Column_RoomType}, {Column_GameModule}, {Column_GameMap}, {Column_RoomState}, {Column_IsRank}, {Column_HasPass}, {Column_Password}, {Column_MaxUsers})" +
$" {Command_Values} ('{RoomID}', '{NowTime}', {RoomMaster}, {(int)RoomType}, '{GameModule}', '{GameMap}', {(int)RoomState}, {(IsRank ? 1 : 0)}, {(HasPass ? 1 : 0)}, '{Password}')"; $" {Command_Values} ('{roomid}', '{NowTime}', {roomMaster}, {(int)roomType}, '{gameModule}', '{gameMap}', {(int)RoomState}, {(isRank ? 1 : 0)}, {(HasPass ? 1 : 0)}, '{password}', {maxUsers})";
} }
public static string Delete_Rooms(params string[] roomids) public static string Delete_Rooms(params string[] roomids)
@ -37,19 +38,19 @@
return $"{Command_Delete} {Command_From} {TableName}"; return $"{Command_Delete} {Command_From} {TableName}";
} }
public static string Delete_QuitRoom(string RoomID, long RoomMaster) public static string Delete_QuitRoom(string roomID, long roomMaster)
{ {
return $"{Command_Delete} {Command_From} {TableName} {Command_Where} {Column_RoomID} = '{RoomID}' {Command_And} {Column_RoomMaster} = {RoomMaster}"; return $"{Command_Delete} {Command_From} {TableName} {Command_Where} {Column_RoomID} = '{roomID}' {Command_And} {Column_RoomMaster} = {roomMaster}";
} }
public static string Update_QuitRoom(string RoomID, long OldRoomMaster, long NewRoomMaster) public static string Update_QuitRoom(string roomid, long oldRoomMaster, long newRoomMaster)
{ {
return $"{Command_Update} {TableName} {Command_Set} {Column_RoomMaster} = {NewRoomMaster} {Command_Where} {Column_RoomID} = '{RoomID}' {Command_And} {Column_RoomMaster} = {OldRoomMaster}"; return $"{Command_Update} {TableName} {Command_Set} {Column_RoomMaster} = {newRoomMaster} {Command_Where} {Column_RoomID} = '{roomid}' {Command_And} {Column_RoomMaster} = {oldRoomMaster}";
} }
public static string Select_IsExistRoom(string RoomID) public static string Select_IsExistRoom(string roomid)
{ {
return $"{Command_Select} {Command_All} {Command_From} {TableName} {Command_Where} {Column_RoomID} = '{RoomID}'"; return $"{Command_Select} {Command_All} {Command_From} {TableName} {Command_Where} {Column_RoomID} = '{roomid}'";
} }
} }
} }

View File

@ -42,7 +42,7 @@
public static string Select_Users_Where(string Where) public static string Select_Users_Where(string Where)
{ {
return $"{Select_Users} {Command_Where} {Where}'"; return $"{Select_Users} {Command_Where} {Where}";
} }
public static string Select_CheckAutoKey(string Username, string AutoKey) public static string Select_CheckAutoKey(string Username, string AutoKey)

View File

@ -7,39 +7,41 @@ namespace Milimoe.FunGame.Core.Model
public class RoomList : IEnumerable<Room> public class RoomList : IEnumerable<Room>
{ {
private readonly Dictionary<string, Room> _List = []; private readonly Dictionary<string, Room> _List = [];
private readonly Dictionary<string, List<User>> _PlayerList = []; private readonly Dictionary<string, List<User>> _UserList = [];
private readonly Dictionary<string, List<User>> _ReadyPlayerList = []; private readonly Dictionary<string, List<User>> _ReadyUserList = [];
public Room this[string roomid] => GetRoom(roomid); public Room this[string roomid] => GetRoom(roomid);
public int Count => _List.Count; public int Count => _List.Count;
public int GetPlayerCount(string roomid) => GetPlayerList(roomid).Count; public int GetUserCount(string roomid) => this[roomid].UserAndIsReady.Count;
public int GetReadyPlayerCount(string roomid) => GetReadyPlayerList(roomid).Count; public int GetReadyUserCount(string roomid) => GetReadyUserList(roomid).Count;
public List<Room> ListRoom => [.. _List.Values]; public List<Room> ListRoom => [.. _List.Values];
public List<string> ListRoomID => [.. _List.Keys]; public List<string> ListRoomID => [.. _List.Keys];
public List<User> GetPlayerList(string roomid) => _PlayerList.TryGetValue(roomid, out List<User>? user) ? user : []; public User GetRoomMaster(string roomid) => this[roomid].RoomMaster;
public List<User> GetReadyPlayerList(string roomid) => _ReadyPlayerList.TryGetValue(roomid, out List<User>? user) ? user : []; public List<User> GetUsers(string roomid) => [.. this[roomid].UserAndIsReady.Keys];
public List<User> GetNotReadyPlayerList(string roomid) => _PlayerList.TryGetValue(roomid, out List<User>? user) ? user.Except(GetReadyPlayerList(roomid)).Except([this[roomid].RoomMaster]).ToList() : []; public List<User> GetReadyUserList(string roomid) => this[roomid].UserAndIsReady.Where(kv => kv.Value && kv.Key.Id != GetRoomMaster(roomid).Id).Select(kv => kv.Key).ToList();
public List<User> GetNotReadyUserList(string roomid) => this[roomid].UserAndIsReady.Where(kv => !kv.Value && kv.Key.Id != GetRoomMaster(roomid).Id).Select(kv => kv.Key).ToList();
public void Clear() public void Clear()
{ {
_List.Clear(); _List.Clear();
_PlayerList.Clear(); _UserList.Clear();
_ReadyPlayerList.Clear(); _ReadyUserList.Clear();
} }
public void AddRoom(Room room) public void AddRoom(Room room)
{ {
_List.Add(room.Roomid, room); _List.Add(room.Roomid, room);
_PlayerList.Add(room.Roomid, []); _UserList.Add(room.Roomid, []);
_ReadyPlayerList.Add(room.Roomid, []); _ReadyUserList.Add(room.Roomid, []);
} }
public void AddRooms(List<Room> rooms) public void AddRooms(List<Room> rooms)
@ -53,8 +55,8 @@ namespace Milimoe.FunGame.Core.Model
public void RemoveRoom(string roomid) public void RemoveRoom(string roomid)
{ {
_List.Remove(roomid); _List.Remove(roomid);
_PlayerList.Remove(roomid); _UserList.Remove(roomid);
_ReadyPlayerList.Remove(roomid); _ReadyUserList.Remove(roomid);
} }
public void RemoveRoom(Room room) => RemoveRoom(room.Roomid); public void RemoveRoom(Room room) => RemoveRoom(room.Roomid);
@ -63,7 +65,10 @@ namespace Milimoe.FunGame.Core.Model
{ {
if (roomid != "-1" && user.Id != 0) if (roomid != "-1" && user.Id != 0)
{ {
GetPlayerList(roomid).Add(user); if (!this[roomid].UserAndIsReady.TryAdd(user, false))
{
this[roomid].UserAndIsReady[user] = false;
}
} }
} }
@ -71,23 +76,23 @@ namespace Milimoe.FunGame.Core.Model
{ {
if (roomid != "-1" && user.Id != 0) if (roomid != "-1" && user.Id != 0)
{ {
GetPlayerList(roomid).Remove(user); this[roomid].UserAndIsReady.Remove(user);
} }
} }
public void SetReady(string roomid, User user) public void SetReady(string roomid, User user)
{ {
if (roomid != "-1" && user.Id != 0) if (roomid != "-1" && user.Id != 0 && this[roomid].UserAndIsReady.ContainsKey(user))
{ {
GetReadyPlayerList(roomid).Add(user); this[roomid].UserAndIsReady[user] = true;
} }
} }
public void CancelReady(string roomid, User user) public void CancelReady(string roomid, User user)
{ {
if (roomid != "-1" && user.Id != 0) if (roomid != "-1" && user.Id != 0 && this[roomid].UserAndIsReady.ContainsKey(user))
{ {
GetReadyPlayerList(roomid).Remove(user); this[roomid].UserAndIsReady[user] = false;
} }
} }
@ -95,15 +100,6 @@ namespace Milimoe.FunGame.Core.Model
public bool IsExist(string roomid) => _List.ContainsKey(roomid); public bool IsExist(string roomid) => _List.ContainsKey(roomid);
public User GetRoomMaster(string roomid)
{
foreach (Room room in ListRoom.Where(r => r.Roomid == roomid && r.RoomMaster != null))
{
return room.RoomMaster;
}
return General.UnknownUserInstance;
}
public void SetRoomMaster(string roomid, User user) public void SetRoomMaster(string roomid, User user)
{ {
if (roomid != "-1" && user.Id != 0) if (roomid != "-1" && user.Id != 0)

View File

@ -10,17 +10,34 @@ namespace Milimoe.FunGame.Core.Service
{ {
internal class HTTPManager internal class HTTPManager
{ {
internal static HttpListener? ServerSocket => _ServerSocket; /// <summary>
private static HttpListener? _ServerSocket = null; /// 实际的 <see cref="System.Net.HttpListener"/> 监听实例 [ 单例 ]
/// </summary>
internal static HttpListener? HttpListener => _HttpListener;
private static HttpListener? _HttpListener = null;
internal static HttpListener StartListening(int port = 22227, bool ssl = false) /// <summary>
/// 开始监听
/// 当 <paramref name="address"/> = "*" 时,需要管理员权限
/// </summary>
/// <param name="address"></param>
/// <param name="port"></param>
/// <param name="subUrl"></param>
/// <param name="ssl"></param>
/// <returns></returns>
internal static HttpListener StartListening(string address = "*", int port = 22223, string subUrl = "ws", bool ssl = false)
{ {
HttpListener listener = new(); _HttpListener = new();
listener.Prefixes.Add((ssl ? "https://" : "http://") + "localhost:" + port + "/ws"); _HttpListener.Prefixes.Add((ssl ? "https://" : "http://") + address + ":" + port + "/" + subUrl.Trim('/') + "/");
listener.Start(); _HttpListener.Start();
return listener; return _HttpListener;
} }
/// <summary>
/// 客户端连接远程 WebSocket 服务器
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
internal static async Task<System.Net.WebSockets.ClientWebSocket?> Connect(Uri uri) internal static async Task<System.Net.WebSockets.ClientWebSocket?> Connect(Uri uri)
{ {
System.Net.WebSockets.ClientWebSocket socket = new(); System.Net.WebSockets.ClientWebSocket socket = new();
@ -32,6 +49,12 @@ namespace Milimoe.FunGame.Core.Service
return null; return null;
} }
/// <summary>
/// 客户端向服务器发送消息
/// </summary>
/// <param name="socket"></param>
/// <param name="obj"></param>
/// <returns></returns>
internal static async Task<SocketResult> Send(System.Net.WebSockets.ClientWebSocket socket, SocketObject obj) internal static async Task<SocketResult> Send(System.Net.WebSockets.ClientWebSocket socket, SocketObject obj)
{ {
if (socket != null) if (socket != null)
@ -50,6 +73,12 @@ namespace Milimoe.FunGame.Core.Service
return SocketResult.NotSent; return SocketResult.NotSent;
} }
/// <summary>
/// 服务器向客户端发送消息
/// </summary>
/// <param name="socket"></param>
/// <param name="obj"></param>
/// <returns></returns>
internal static async Task<SocketResult> Send(WebSocket socket, SocketObject obj) internal static async Task<SocketResult> Send(WebSocket socket, SocketObject obj)
{ {
if (socket != null) if (socket != null)
@ -68,45 +97,95 @@ namespace Milimoe.FunGame.Core.Service
return SocketResult.NotSent; return SocketResult.NotSent;
} }
internal static async Task Receiving(HTTPListener listener) /// <summary>
/// 服务器接受一个 HTTP 的 WebSocket 升级请求
/// </summary>
/// <returns>[0]客户端IP地址[1]客户端的WebSocket实例</returns>
internal static async Task<object[]> Accept()
{ {
if (ServerSocket != null) if (HttpListener is null) return [];
try
{ {
try HttpListenerContext context = await HttpListener.GetContextAsync();
if (context.Request.IsWebSocketRequest)
{ {
while (true) HttpListenerWebSocketContext socketContext = await context.AcceptWebSocketAsync(null);
WebSocket socket = socketContext.WebSocket;
string ip = context.Request.RemoteEndPoint.ToString();
return [ip, socket];
}
else
{
context.Response.StatusCode = 400;
context.Response.Close();
}
}
catch
{
HttpListener?.Close();
}
return [];
}
/// <summary>
/// 服务器接收客户端消息
/// </summary>
/// <param name="socket"></param>
/// <returns></returns>
internal static async Task<SocketObject[]> Receive(WebSocket socket)
{
try
{
List<SocketObject> objs = [];
if (socket != null)
{
byte[] buffer = new byte[General.SocketByteSize];
WebSocketReceiveResult result;
StringBuilder builder = new();
do
{ {
HttpListenerContext context = await ServerSocket.GetContextAsync(); result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (context.Request.IsWebSocketRequest) builder.Append(General.DefaultEncoding.GetString(buffer, 0, result.Count).Replace("\0", "").Trim());
}
while (!result.EndOfMessage);
string msg = builder.ToString();
if (JsonManager.IsCompleteJson<SocketObject>(msg))
{
foreach (SocketObject obj in JsonManager.GetObjects<SocketObject>(msg))
{ {
TaskUtility.NewTask(async () => await AddClientWebSocket(listener, context)); objs.Add(obj);
}
else
{
context.Response.StatusCode = 400;
context.Response.Close();
} }
} }
} }
catch return [.. objs];
{ }
_ServerSocket = null; catch (Exception e)
} {
TXTHelper.AppendErrorLog(e.GetErrorInfo());
return [];
} }
} }
/// <summary>
/// 客户端接收服务器消息
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
internal static async Task<bool> ReceiveMessage(HTTPClient client) internal static async Task<bool> ReceiveMessage(HTTPClient client)
{ {
if (client.Instance is null) return false; if (client.Instance is null) return false;
byte[] buffer = new byte[General.SocketByteSize]; byte[] buffer = new byte[General.SocketByteSize];
WebSocketReceiveResult result = await client.Instance.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); WebSocketReceiveResult result = await client.Instance.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
string msg = Encoding.UTF8.GetString(buffer).Replace("\0", "").Trim(); string msg = General.DefaultEncoding.GetString(buffer).Replace("\0", "").Trim();
SocketObject[] objs = await GetSocketObjects(client.Instance, result, msg); SocketObject[] objs = await GetSocketObjects(client.Instance, result, msg);
foreach (SocketObject obj in objs) foreach (SocketObject obj in objs)
{ {
SocketObject sendobject = client.SocketObject_Handler(obj); SocketManager.OnSocketReceive(obj);
if (obj.SocketType == SocketMessageType.Connect) if (obj.SocketType == SocketMessageType.Connect)
{ {
return true; return true;
@ -116,81 +195,18 @@ namespace Milimoe.FunGame.Core.Service
await client.Instance.CloseAsync(result.CloseStatus ?? WebSocketCloseStatus.NormalClosure, result.CloseStatusDescription, CancellationToken.None); await client.Instance.CloseAsync(result.CloseStatus ?? WebSocketCloseStatus.NormalClosure, result.CloseStatusDescription, CancellationToken.None);
return true; return true;
} }
await Send(client.Instance, sendobject);
} }
return true; return true;
} }
private static async Task AddClientWebSocket(HTTPListener listener, HttpListenerContext context) /// <summary>
{ /// 将收到的消息反序列为 <see cref="SocketObject"/>
HttpListenerWebSocketContext socketContext = await context.AcceptWebSocketAsync(null); /// </summary>
WebSocket socket = socketContext.WebSocket; /// <param name="socket"></param>
/// <param name="result"></param>
byte[] buffer = new byte[General.SocketByteSize]; /// <param name="msg"></param>
WebSocketReceiveResult result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); /// <returns></returns>
string msg = Encoding.UTF8.GetString(buffer).Replace("\0", "").Trim();
SocketObject sendobject = new(SocketMessageType.Unknown, Guid.Empty);
SocketObject[] objs = await GetSocketObjects(socket, result, msg);
bool isConnect = false;
foreach (SocketObject obj in objs)
{
if (obj.SocketType == SocketMessageType.Connect)
{
isConnect = listener.CheckClientConnection(obj);
}
else if (listener.ClientSockets.ContainsKey(obj.Token))
{
sendobject = listener.SocketObject_Handler(obj);
isConnect = true;
}
}
if (isConnect)
{
Guid token = Guid.NewGuid();
listener.ClientSockets.TryAdd(token, socket);
await Send(socket, sendobject);
while (socket.State == WebSocketState.Open)
{
try
{
buffer = new byte[General.SocketByteSize];
result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.CloseStatus.HasValue)
{
await socket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
return;
}
msg = Encoding.UTF8.GetString(buffer).Replace("\0", "").Trim();
objs = await GetSocketObjects(socket, result, msg);
foreach (SocketObject obj in objs)
{
sendobject = listener.SocketObject_Handler(obj);
if (obj.SocketType == SocketMessageType.Disconnect)
{
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Disconnect received", CancellationToken.None);
return;
}
await Send(socket, sendobject);
}
}
catch (Exception e)
{
// 处理其他异常
TXTHelper.AppendErrorLog(e.GetErrorInfo());
await socket.CloseAsync(WebSocketCloseStatus.InternalServerError, "Server Error", CancellationToken.None);
return;
}
}
}
}
private static async Task<SocketObject[]> GetSocketObjects(WebSocket socket, WebSocketReceiveResult result, string msg) private static async Task<SocketObject[]> GetSocketObjects(WebSocket socket, WebSocketReceiveResult result, string msg)
{ {
List<SocketObject> objs = []; List<SocketObject> objs = [];

View File

@ -67,10 +67,10 @@ namespace Milimoe.FunGame.Core.Service
MailMessage Msg = new() MailMessage Msg = new()
{ {
Subject = Mail.Subject, Subject = Mail.Subject,
SubjectEncoding = System.Text.Encoding.UTF8, SubjectEncoding = General.DefaultEncoding,
Body = Mail.Body, Body = Mail.Body,
BodyEncoding = System.Text.Encoding.UTF8, BodyEncoding = General.DefaultEncoding,
From = new MailAddress(Mail.Sender, Mail.SenderName, System.Text.Encoding.UTF8), From = new MailAddress(Mail.Sender, Mail.SenderName, General.DefaultEncoding),
IsBodyHtml = Mail.HTML, IsBodyHtml = Mail.HTML,
Priority = Mail.Priority Priority = Mail.Priority
}; };

View File

@ -30,18 +30,18 @@ namespace Milimoe.FunGame.Core.Service
/// <summary> /// <summary>
/// 创建服务器监听Socket /// 创建服务器监听Socket
/// </summary> /// </summary>
/// <param name="Port">监听端口号</param> /// <param name="port">监听端口号</param>
/// <param name="MaxConnection">最大连接数量</param> /// <param name="maxConnection">最大连接数量</param>
/// <returns>服务器端专用Socket</returns> /// <returns>服务器端专用Socket</returns>
internal static Socket? StartListening(int Port = 22222, int MaxConnection = 0) internal static Socket? StartListening(int port = 22222, int maxConnection = 0)
{ {
if (MaxConnection <= 0) MaxConnection = SocketSet.MaxConnection_2C2G; if (maxConnection <= 0) maxConnection = SocketSet.MaxConnection_2C2G;
try try
{ {
_ServerSocket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _ServerSocket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ServerEndPoint = new(IPAddress.Any, Port); IPEndPoint ServerEndPoint = new(IPAddress.Any, port);
_ServerSocket.Bind(ServerEndPoint); _ServerSocket.Bind(ServerEndPoint);
_ServerSocket.Listen(MaxConnection); _ServerSocket.Listen(maxConnection);
_ServerSocket.NoDelay = true; _ServerSocket.NoDelay = true;
return _ServerSocket; return _ServerSocket;
} }
@ -65,8 +65,7 @@ namespace Milimoe.FunGame.Core.Service
{ {
Client = ServerSocket.Accept(); Client = ServerSocket.Accept();
Client.NoDelay = true; Client.NoDelay = true;
IPEndPoint? ClientIPEndPoint = (IPEndPoint?)Client.RemoteEndPoint; ClientIP = Client.RemoteEndPoint?.ToString() ?? "Unknown";
ClientIP = (ClientIPEndPoint != null) ? ClientIPEndPoint.ToString() : "Unknown";
return [ClientIP, Client]; return [ClientIP, Client];
} }
catch catch
@ -79,17 +78,17 @@ namespace Milimoe.FunGame.Core.Service
/// <summary> /// <summary>
/// 创建客户端Socket /// 创建客户端Socket
/// </summary> /// </summary>
/// <param name="Address">服务器IP地址</param> /// <param name="address">服务器IP地址</param>
/// <param name="Port">服务器监听端口</param> /// <param name="port">服务器监听端口</param>
/// <returns>客户端专用Socket</returns> /// <returns>客户端专用Socket</returns>
internal static Socket? Connect(string Address, int Port = 22222) internal static Socket? Connect(string address, int port = 22222)
{ {
Socket? ClientSocket; Socket? ClientSocket;
EndPoint ServerEndPoint; EndPoint ServerEndPoint;
try try
{ {
string IP = Api.Utility.NetworkUtility.GetIPAddress(Address); string IP = NetworkUtility.GetIPAddress(address);
ServerEndPoint = new IPEndPoint(IPAddress.Parse(IP), Port); ServerEndPoint = new IPEndPoint(IPAddress.Parse(IP), port);
if (ServerEndPoint != null) if (ServerEndPoint != null)
{ {
ClientSocket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); ClientSocket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
@ -131,14 +130,14 @@ namespace Milimoe.FunGame.Core.Service
/// <summary> /// <summary>
/// 用于服务器端向客户端Socket发送信息 /// 用于服务器端向客户端Socket发送信息
/// </summary> /// </summary>
/// <param name="ClientSocket">客户端Socket</param> /// <param name="clientSocket">客户端Socket</param>
/// <param name="SocketObject">Socket信息容器</param> /// <param name="obj">Socket信息容器</param>
/// <returns>通信结果</returns> /// <returns>通信结果</returns>
internal static SocketResult Send(Socket ClientSocket, Library.Common.Network.SocketObject SocketObject) internal static SocketResult Send(Socket clientSocket, Library.Common.Network.SocketObject obj)
{ {
if (ClientSocket != null) if (clientSocket != null)
{ {
if (ClientSocket.Send(General.DefaultEncoding.GetBytes(JsonManager.GetString(SocketObject))) > 0) if (clientSocket.Send(General.DefaultEncoding.GetBytes(JsonManager.GetString(obj))) > 0)
{ {
return SocketResult.Success; return SocketResult.Success;
} }
@ -150,14 +149,14 @@ namespace Milimoe.FunGame.Core.Service
/// <summary> /// <summary>
/// 用于服务器端向客户端Socket发送信息 [ 异步版 ] /// 用于服务器端向客户端Socket发送信息 [ 异步版 ]
/// </summary> /// </summary>
/// <param name="ClientSocket">客户端Socket</param> /// <param name="clientSocket">客户端Socket</param>
/// <param name="SocketObject">Socket信息容器</param> /// <param name="obj">Socket信息容器</param>
/// <returns>通信结果</returns> /// <returns>通信结果</returns>
internal static async Task<SocketResult> SendAsync(Socket ClientSocket, Library.Common.Network.SocketObject SocketObject) internal static async Task<SocketResult> SendAsync(Socket clientSocket, Library.Common.Network.SocketObject obj)
{ {
if (ClientSocket != null) if (clientSocket != null)
{ {
if (await ClientSocket.SendAsync(General.DefaultEncoding.GetBytes(JsonManager.GetString(SocketObject))) > 0) if (await clientSocket.SendAsync(General.DefaultEncoding.GetBytes(JsonManager.GetString(obj))) > 0)
{ {
return SocketResult.Success; return SocketResult.Success;
} }
@ -169,13 +168,13 @@ namespace Milimoe.FunGame.Core.Service
/// <summary> /// <summary>
/// 用于客户端向服务器Socket发送信息 /// 用于客户端向服务器Socket发送信息
/// </summary> /// </summary>
/// <param name="SocketObject">Socket信息容器</param> /// <param name="obj">Socket信息容器</param>
/// <returns>通信结果</returns> /// <returns>通信结果</returns>
internal static SocketResult Send(Library.Common.Network.SocketObject SocketObject) internal static SocketResult Send(Library.Common.Network.SocketObject obj)
{ {
if (Socket != null) if (Socket != null)
{ {
if (Socket.Send(General.DefaultEncoding.GetBytes(JsonManager.GetString(SocketObject))) > 0) if (Socket.Send(General.DefaultEncoding.GetBytes(JsonManager.GetString(obj))) > 0)
{ {
return SocketResult.Success; return SocketResult.Success;
} }
@ -187,13 +186,13 @@ namespace Milimoe.FunGame.Core.Service
/// <summary> /// <summary>
/// 用于客户端向服务器Socket发送信息 [ 异步版 ] /// 用于客户端向服务器Socket发送信息 [ 异步版 ]
/// </summary> /// </summary>
/// <param name="SocketObject">Socket信息容器</param> /// <param name="obj">Socket信息容器</param>
/// <returns>通信结果</returns> /// <returns>通信结果</returns>
internal static async Task<SocketResult> SendAsync(Library.Common.Network.SocketObject SocketObject) internal static async Task<SocketResult> SendAsync(Library.Common.Network.SocketObject obj)
{ {
if (Socket != null) if (Socket != null)
{ {
if (await Socket.SendAsync(General.DefaultEncoding.GetBytes(JsonManager.GetString(SocketObject))) > 0) if (await Socket.SendAsync(General.DefaultEncoding.GetBytes(JsonManager.GetString(obj))) > 0)
{ {
return SocketResult.Success; return SocketResult.Success;
} }
@ -204,16 +203,16 @@ namespace Milimoe.FunGame.Core.Service
/// <summary> /// <summary>
/// 接收数据流中的信息 /// 接收数据流中的信息
/// <para/>如果是服务器接收信息需要传入客户端Socket <paramref name="ClientSocket"/> /// <para/>如果是服务器接收信息需要传入客户端Socket <paramref name="clientSocket"/>
/// </summary> /// </summary>
/// <param name="ClientSocket">如果是服务器接收信息需要传入客户端Socket</param> /// <param name="clientSocket">如果是服务器接收信息需要传入客户端Socket</param>
/// <returns>SocketObjects</returns> /// <returns>SocketObjects</returns>
internal static Library.Common.Network.SocketObject[] Receive(Socket? ClientSocket = null) internal static Library.Common.Network.SocketObject[] Receive(Socket? clientSocket = null)
{ {
try try
{ {
List<Library.Common.Network.SocketObject> result = []; List<Library.Common.Network.SocketObject> result = [];
Socket? tempSocket = ClientSocket is null ? Socket : ClientSocket; Socket? tempSocket = clientSocket is null ? Socket : clientSocket;
if (tempSocket != null) if (tempSocket != null)
{ {
// 从服务器接收消息 // 从服务器接收消息
@ -229,7 +228,7 @@ namespace Milimoe.FunGame.Core.Service
{ {
result.Add(obj); result.Add(obj);
// 客户端接收消息广播ScoketObject到每个UIModel // 客户端接收消息广播ScoketObject到每个UIModel
if (ClientSocket is null) OnSocketReceive(obj); if (clientSocket is null) OnSocketReceive(obj);
} }
return [.. result]; return [.. result];
} }
@ -256,7 +255,73 @@ namespace Milimoe.FunGame.Core.Service
{ {
result.Add(obj); result.Add(obj);
// 客户端接收消息广播ScoketObject到每个UIModel // 客户端接收消息广播ScoketObject到每个UIModel
if (ClientSocket is null) OnSocketReceive(obj); if (clientSocket is null) OnSocketReceive(obj);
}
}
return [.. result];
}
catch (Exception e)
{
TXTHelper.AppendErrorLog(e.GetErrorInfo());
return [];
}
}
/// <summary>
/// 接收数据流中的信息 [ 异步版 ]
/// <para/>如果是服务器接收信息需要传入客户端Socket <paramref name="clientSocket"/>
/// </summary>
/// <param name="clientSocket">如果是服务器接收信息需要传入客户端Socket</param>
/// <returns>SocketObjects</returns>
internal static async Task<Library.Common.Network.SocketObject[]> ReceiveAsync(Socket? clientSocket = null)
{
try
{
List<Library.Common.Network.SocketObject> result = [];
Socket? tempSocket = clientSocket is null ? Socket : clientSocket;
if (tempSocket != null)
{
// 从服务器接收消息
byte[] buffer = new byte[General.SocketByteSize];
int length = await tempSocket.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
string msg = "";
if (length > 0)
{
msg = General.DefaultEncoding.GetString(buffer, 0, length);
if (JsonManager.IsCompleteJson<Library.Common.Network.SocketObject>(msg))
{
foreach (Library.Common.Network.SocketObject obj in JsonManager.GetObjects<Library.Common.Network.SocketObject>(msg))
{
result.Add(obj);
// 客户端接收消息广播ScoketObject到每个UIModel
if (clientSocket is null) OnSocketReceive(obj);
}
return [.. result];
}
else
{
Thread.Sleep(20);
while (true)
{
if (tempSocket.Available > 0)
{
length = tempSocket.Receive(buffer, buffer.Length, SocketFlags.None);
msg += General.DefaultEncoding.GetString(buffer, 0, length);
if (JsonManager.IsCompleteJson<Library.Common.Network.SocketObject>(msg))
{
break;
}
Thread.Sleep(20);
}
else break;
}
}
}
foreach (Library.Common.Network.SocketObject obj in JsonManager.GetObjects<Library.Common.Network.SocketObject>(msg))
{
result.Add(obj);
// 客户端接收消息广播ScoketObject到每个UIModel
if (clientSocket is null) OnSocketReceive(obj);
} }
} }
return [.. result]; return [.. result];
@ -275,8 +340,8 @@ namespace Milimoe.FunGame.Core.Service
/// <summary> /// <summary>
/// 监听事件的委托 /// 监听事件的委托
/// </summary> /// </summary>
/// <param name="SocketObject">SocketObject</param> /// <param name="obj">SocketObject</param>
internal delegate void SocketReceiveHandler(Library.Common.Network.SocketObject SocketObject); internal delegate void SocketReceiveHandler(Library.Common.Network.SocketObject obj);
/// <summary> /// <summary>
/// 监听事件 /// 监听事件
@ -286,10 +351,10 @@ namespace Milimoe.FunGame.Core.Service
/// <summary> /// <summary>
/// 触发异步监听事件 /// 触发异步监听事件
/// </summary> /// </summary>
/// <param name="SocketObject">SocketObject</param> /// <param name="obj">SocketObject</param>
internal static void OnSocketReceive(Library.Common.Network.SocketObject SocketObject) internal static void OnSocketReceive(Library.Common.Network.SocketObject obj)
{ {
SocketReceive?.Invoke(SocketObject); SocketReceive?.Invoke(obj);
} }
#endregion #endregion

View File

@ -34,9 +34,9 @@ namespace Milimoe.FunGame.Core.Service
private bool _IsCompleted = false; private bool _IsCompleted = false;
private Exception _Exception = new(); private Exception _Exception = new();
internal TaskAwaiter(Action action) => _ = Worker(action); internal TaskAwaiter(Action action) => Worker(action);
internal TaskAwaiter(Func<Task> function) => _ = Worker(function); internal TaskAwaiter(Func<Task> function) => Worker(function);
/// <summary> /// <summary>
/// 返回ITaskAwaiter可以进一步调用方法<para/> /// 返回ITaskAwaiter可以进一步调用方法<para/>
@ -63,34 +63,40 @@ namespace Milimoe.FunGame.Core.Service
return this; return this;
} }
private async Task Worker(Action action) private void Worker(Action action)
{ {
try Task.Run(async () =>
{ {
await Task.Run(action); try
_IsCompleted = true; {
Completed?.Invoke(); await Task.Run(action);
} _IsCompleted = true;
catch (Exception e) Completed?.Invoke();
{ }
_Exception = e; catch (Exception e)
Error?.Invoke(e); {
} _Exception = e;
Error?.Invoke(e);
}
});
} }
private async Task Worker(Func<Task> function) private void Worker(Func<Task> function)
{ {
try Task.Run(async () =>
{ {
await function(); try
_IsCompleted = true; {
Completed?.Invoke(); await function();
} _IsCompleted = true;
catch (Exception e) Completed?.Invoke();
{ }
_Exception = e; catch (Exception e)
Error?.Invoke(e); {
} _Exception = e;
Error?.Invoke(e);
}
});
} }
} }
} }