mirror of
https://github.com/project-redbud/FunGame-Server.git
synced 2025-04-23 12:39:36 +08:00
为服务器统一数据访问连接 (#37)
* 添加 Web API 和 RESTful API 模式; * 添加 SQLite 模式; * 添加 ISocketMessageProcessor 和 ISocketListener<> 接口,用于统一数据访问; * 重做了 ISocketModel; * 完善了 WebSocket 的连接模式。
This commit is contained in:
parent
cea0f393f7
commit
14ff58f4f4
@ -1,7 +1,7 @@
|
|||||||
using Milimoe.FunGame.Core.Api.Transmittal;
|
using Milimoe.FunGame.Core.Api.Transmittal;
|
||||||
using Milimoe.FunGame.Core.Api.Utility;
|
using Milimoe.FunGame.Core.Api.Utility;
|
||||||
|
using Milimoe.FunGame.Core.Interface.Base;
|
||||||
using Milimoe.FunGame.Core.Library.Constant;
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
using Milimoe.FunGame.Server.Model;
|
|
||||||
|
|
||||||
namespace Milimoe.FunGame.Server.Controller
|
namespace Milimoe.FunGame.Server.Controller
|
||||||
{
|
{
|
||||||
@ -9,11 +9,11 @@ namespace Milimoe.FunGame.Server.Controller
|
|||||||
{
|
{
|
||||||
public TwoFactorAuthenticator Login2FA = new();
|
public TwoFactorAuthenticator Login2FA = new();
|
||||||
|
|
||||||
private readonly ServerModel Server;
|
private readonly IServerModel Server;
|
||||||
private readonly SQLHelper SQLHelper;
|
private readonly SQLHelper SQLHelper;
|
||||||
private readonly MailSender? MailSender;
|
private readonly MailSender? MailSender;
|
||||||
|
|
||||||
public Authenticator(ServerModel Server, SQLHelper SQLHelper, MailSender? MailSender) : base(SQLHelper)
|
public Authenticator(IServerModel Server, SQLHelper SQLHelper, MailSender? MailSender) : base(SQLHelper)
|
||||||
{
|
{
|
||||||
this.Server = Server;
|
this.Server = Server;
|
||||||
this.SQLHelper = SQLHelper;
|
this.SQLHelper = SQLHelper;
|
||||||
|
134
FunGame.Server/Controllers/ConnectController.cs
Normal file
134
FunGame.Server/Controllers/ConnectController.cs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
using Milimoe.FunGame.Core.Interface.Base;
|
||||||
|
using Milimoe.FunGame.Core.Library.Common.Network;
|
||||||
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
|
using Milimoe.FunGame.Server.Others;
|
||||||
|
using Milimoe.FunGame.Server.Utility;
|
||||||
|
|
||||||
|
namespace Milimoe.FunGame.Server.Controller
|
||||||
|
{
|
||||||
|
public class ConnectController
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 因为异步函数无法使用 ref 变量,因此使用元组返回
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="listener"></param>
|
||||||
|
/// <param name="socket"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <param name="clientip"></param>
|
||||||
|
/// <param name="objs"></param>
|
||||||
|
/// <returns>[0]isConnected;[1]isDebugMode</returns>
|
||||||
|
public static async Task<(bool, bool)> Connect<T>(ISocketListener<T> listener, ISocketMessageProcessor socket, Guid token, string clientip, IEnumerable<SocketObject> objs) where T : ISocketMessageProcessor
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool isConnected = false;
|
||||||
|
bool isDebugMode = false;
|
||||||
|
foreach (SocketObject obj in objs)
|
||||||
|
{
|
||||||
|
if (obj.SocketType == SocketMessageType.Connect)
|
||||||
|
{
|
||||||
|
if (Config.ConnectingPlayerCount + Config.OnlinePlayerCount > Config.MaxPlayers)
|
||||||
|
{
|
||||||
|
await SendRefuseConnect(socket, "服务器可接受的连接数量已上限!");
|
||||||
|
ServerHelper.WriteLine("服务器可接受的连接数量已上限!", InvokeMessageType.Core);
|
||||||
|
return (isConnected, isDebugMode);
|
||||||
|
}
|
||||||
|
ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 正在连接服务器 . . .", InvokeMessageType.Core);
|
||||||
|
if (IsIPBanned(listener, clientip))
|
||||||
|
{
|
||||||
|
await SendRefuseConnect(socket, "服务器已拒绝黑名单用户连接。");
|
||||||
|
ServerHelper.WriteLine("检测到 " + ServerHelper.MakeClientName(clientip) + " 为黑名单用户,已禁止其连接!", InvokeMessageType.Core);
|
||||||
|
return (isConnected, isDebugMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerHelper.WriteLine("[" + SocketSet.GetTypeString(obj.SocketType) + "] " + ServerHelper.MakeClientName(socket.ClientIP), InvokeMessageType.Core);
|
||||||
|
|
||||||
|
// 读取参数
|
||||||
|
// 参数1:客户端的游戏模组列表,没有服务器的需要拒绝
|
||||||
|
string[] modes = obj.GetParam<string[]>(0) ?? [];
|
||||||
|
// 参数2:客户端是否开启了开发者模式,开启开发者模式部分功能不可用
|
||||||
|
isDebugMode = obj.GetParam<bool>(1);
|
||||||
|
if (isDebugMode) ServerHelper.WriteLine("客户端已开启开发者模式");
|
||||||
|
|
||||||
|
string msg = "";
|
||||||
|
List<string> ClientDontHave = [];
|
||||||
|
string strDontHave = string.Join("\r\n", Config.GameModuleSupported.Where(mode => !modes.Contains(mode)));
|
||||||
|
if (strDontHave != "")
|
||||||
|
{
|
||||||
|
strDontHave = "客户端缺少服务器所需的模组:" + strDontHave;
|
||||||
|
ServerHelper.WriteLine(strDontHave, InvokeMessageType.Core);
|
||||||
|
msg += strDontHave;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg == "" && await socket.SendAsync(SocketMessageType.Connect, true, msg, token, Config.ServerName, Config.ServerNotice) == SocketResult.Success)
|
||||||
|
{
|
||||||
|
isConnected = true;
|
||||||
|
ServerHelper.WriteLine(ServerHelper.MakeClientName(socket.ClientIP) + " <- " + "已确认连接", InvokeMessageType.Core);
|
||||||
|
return (isConnected, isDebugMode);
|
||||||
|
}
|
||||||
|
else if (msg != "" && await socket.SendAsync(SocketMessageType.Connect, false, msg) == SocketResult.Success)
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine(ServerHelper.MakeClientName(socket.ClientIP) + " <- " + "拒绝连接", InvokeMessageType.Core);
|
||||||
|
return (isConnected, isDebugMode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine("无法传输数据,与客户端的连接可能丢失。", InvokeMessageType.Core);
|
||||||
|
return (isConnected, isDebugMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await SendRefuseConnect(socket, "服务器已拒绝连接。");
|
||||||
|
ServerHelper.WriteLine("客户端发送了不符合FunGame规定的字符,拒绝连接。", InvokeMessageType.Core);
|
||||||
|
return (isConnected, isDebugMode);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ServerHelper.Error(e);
|
||||||
|
await SendRefuseConnect(socket, "请勿发送错误的数据,请保持FunGame版本为最新。");
|
||||||
|
throw new SocketWrongInfoException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 回复拒绝连接消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="socket"></param>
|
||||||
|
/// <param name="msg"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static async Task<bool> SendRefuseConnect(ISocketMessageProcessor socket, string msg)
|
||||||
|
{
|
||||||
|
// 发送消息给客户端
|
||||||
|
msg = "连接被拒绝,如有疑问请联系服务器管理员:" + msg;
|
||||||
|
if (await socket.SendAsync(SocketMessageType.Connect, false, msg) == SocketResult.Success)
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine(ServerHelper.MakeClientName(socket.ClientIP) + " <- " + "已拒绝连接", InvokeMessageType.Core);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine("无法传输数据,与客户端的连接可能丢失。", InvokeMessageType.Core);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断是否是黑名单里的IP
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="server"></param>
|
||||||
|
/// <param name="ip"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static bool IsIPBanned<T>(ISocketListener<T> server, string ip) where T : ISocketMessageProcessor
|
||||||
|
{
|
||||||
|
string[] strs = ip.Split(":");
|
||||||
|
if (strs.Length == 2 && server.BannedList.Contains(strs[0]))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -16,20 +16,34 @@
|
|||||||
<FileVersion>1.0</FileVersion>
|
<FileVersion>1.0</FileVersion>
|
||||||
<AssemblyName>FunGameServer</AssemblyName>
|
<AssemblyName>FunGameServer</AssemblyName>
|
||||||
<RootNamespace>Milimoe.$(MSBuildProjectName.Replace(" ", "_"))</RootNamespace>
|
<RootNamespace>Milimoe.$(MSBuildProjectName.Replace(" ", "_"))</RootNamespace>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DebugType>embedded</DebugType>
|
<DebugType>embedded</DebugType>
|
||||||
|
<NoWarn>1701;1702;IDE0130</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
<DebugType>embedded</DebugType>
|
<DebugType>embedded</DebugType>
|
||||||
|
<NoWarn>1701;1702;IDE0130</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="Libraries\**" />
|
||||||
|
<EmbeddedResource Remove="Libraries\**" />
|
||||||
|
<None Remove="Libraries\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="logo.ico" />
|
<Content Include="logo.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.8" />
|
||||||
|
<PackageReference Include="MySql.Data" Version="9.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\FunGame.Implement\FunGame.Implement.csproj" />
|
<ProjectReference Include="..\FunGame.Implement\FunGame.Implement.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@ -38,15 +52,6 @@
|
|||||||
<Reference Include="FunGame.Core">
|
<Reference Include="FunGame.Core">
|
||||||
<HintPath>..\..\FunGame.Core\bin\Debug\net8.0\FunGame.Core.dll</HintPath>
|
<HintPath>..\..\FunGame.Core\bin\Debug\net8.0\FunGame.Core.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="MySql.Data">
|
|
||||||
<HintPath>..\bin\Debug\net7.0\MySql.Data.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System.Configuration.ConfigurationManager">
|
|
||||||
<HintPath>..\bin\Debug\net7.0\System.Configuration.ConfigurationManager.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System.Security.Permissions">
|
|
||||||
<HintPath>..\bin\Debug\net7.0\System.Security.Permissions.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
using System.Collections;
|
using Milimoe.FunGame;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Milimoe.FunGame;
|
|
||||||
using Milimoe.FunGame.Core.Api.Utility;
|
using Milimoe.FunGame.Core.Api.Utility;
|
||||||
using Milimoe.FunGame.Core.Library.Common.Addon;
|
|
||||||
using Milimoe.FunGame.Core.Library.Common.Network;
|
using Milimoe.FunGame.Core.Library.Common.Network;
|
||||||
using Milimoe.FunGame.Core.Library.Constant;
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
|
using Milimoe.FunGame.Server.Controller;
|
||||||
using Milimoe.FunGame.Server.Model;
|
using Milimoe.FunGame.Server.Model;
|
||||||
using Milimoe.FunGame.Server.Others;
|
using Milimoe.FunGame.Server.Others;
|
||||||
using Milimoe.FunGame.Server.Utility;
|
using Milimoe.FunGame.Server.Utility;
|
||||||
@ -13,7 +11,8 @@ Console.Title = Config.ServerName;
|
|||||||
Console.WriteLine(FunGameInfo.GetInfo(Config.FunGameType));
|
Console.WriteLine(FunGameInfo.GetInfo(Config.FunGameType));
|
||||||
|
|
||||||
bool Running = true;
|
bool Running = true;
|
||||||
ServerSocket? ListeningSocket = null;
|
SocketListener? SocketListener = null;
|
||||||
|
HTTPListener? WebSocketListener = null;
|
||||||
|
|
||||||
StartServer();
|
StartServer();
|
||||||
|
|
||||||
@ -32,7 +31,7 @@ while (Running)
|
|||||||
Running = false;
|
Running = false;
|
||||||
break;
|
break;
|
||||||
case OrderDictionary.Restart:
|
case OrderDictionary.Restart:
|
||||||
if (ListeningSocket == null)
|
if (SocketListener is null || WebSocketListener is null)
|
||||||
{
|
{
|
||||||
ServerHelper.WriteLine("重启服务器");
|
ServerHelper.WriteLine("重启服务器");
|
||||||
StartServer();
|
StartServer();
|
||||||
@ -40,7 +39,14 @@ while (Running)
|
|||||||
else ServerHelper.WriteLine("服务器正在运行,拒绝重启!");
|
else ServerHelper.WriteLine("服务器正在运行,拒绝重启!");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ConsoleModel.Order(ListeningSocket, order);
|
if (SocketListener != null)
|
||||||
|
{
|
||||||
|
await ConsoleModel.Order(SocketListener, order);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await ConsoleModel.Order(WebSocketListener, order);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,7 +57,7 @@ Console.ReadKey();
|
|||||||
|
|
||||||
void StartServer()
|
void StartServer()
|
||||||
{
|
{
|
||||||
Task t = Task.Factory.StartNew(() =>
|
TaskUtility.NewTask(async () =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -60,7 +66,7 @@ void StartServer()
|
|||||||
ServerHelper.InitOrderList();
|
ServerHelper.InitOrderList();
|
||||||
|
|
||||||
// 读取游戏模组
|
// 读取游戏模组
|
||||||
if (!GetGameModuleList())
|
if (!Config.GetGameModuleList())
|
||||||
{
|
{
|
||||||
ServerHelper.WriteLine("服务器似乎未安装任何游戏模组,请检查是否正确安装它们。");
|
ServerHelper.WriteLine("服务器似乎未安装任何游戏模组,请检查是否正确安装它们。");
|
||||||
}
|
}
|
||||||
@ -83,11 +89,17 @@ void StartServer()
|
|||||||
// 创建全局SQLHelper
|
// 创建全局SQLHelper
|
||||||
Config.InitSQLHelper();
|
Config.InitSQLHelper();
|
||||||
|
|
||||||
|
// 使用Socket还是WebSocket
|
||||||
|
bool useWebSocket = Config.UseWebSocket;
|
||||||
|
|
||||||
|
if (!useWebSocket)
|
||||||
|
{
|
||||||
// 创建监听
|
// 创建监听
|
||||||
ListeningSocket = ServerSocket.StartListening();
|
SocketListener listener = SocketListener.StartListening(Config.ServerPort, Config.MaxPlayers);
|
||||||
|
SocketListener = listener;
|
||||||
|
|
||||||
// 开始监听连接
|
// 开始监听连接
|
||||||
AddBannedList(ListeningSocket);
|
listener.BannedList.AddRange(Config.ServerBannedList);
|
||||||
ServerHelper.WriteLine("Listen -> " + Config.ServerPort);
|
ServerHelper.WriteLine("Listen -> " + Config.ServerPort);
|
||||||
ServerHelper.WriteLine("服务器启动成功,开始监听 . . .");
|
ServerHelper.WriteLine("服务器启动成功,开始监听 . . .");
|
||||||
|
|
||||||
@ -98,36 +110,114 @@ void StartServer()
|
|||||||
|
|
||||||
while (Running)
|
while (Running)
|
||||||
{
|
{
|
||||||
ClientSocket socket;
|
ServerSocket socket;
|
||||||
string clientip = "";
|
string clientip = "";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Guid token = Guid.NewGuid();
|
Guid token = Guid.NewGuid();
|
||||||
socket = ListeningSocket.Accept(token);
|
socket = listener.Accept(token);
|
||||||
|
|
||||||
|
TaskUtility.NewTask(async () =>
|
||||||
|
{
|
||||||
clientip = socket.ClientIP;
|
clientip = socket.ClientIP;
|
||||||
Config.ConnectingPlayerCount++;
|
Config.ConnectingPlayerCount++;
|
||||||
// 开始处理客户端连接请求
|
bool isConnected = false;
|
||||||
bool isDebugMode = false;
|
bool isDebugMode = false;
|
||||||
if (Connect(socket, token, clientip, ref isDebugMode))
|
|
||||||
|
// 开始处理客户端连接请求
|
||||||
|
SocketObject[] objs = socket.Receive();
|
||||||
|
(isConnected, isDebugMode) = await ConnectController.Connect(listener, socket, token, clientip, objs);
|
||||||
|
if (isConnected)
|
||||||
{
|
{
|
||||||
ServerModel ClientModel = new(ListeningSocket, socket, Running, isDebugMode);
|
ServerModel<ServerSocket> ClientModel = new(listener, socket, isDebugMode);
|
||||||
Task t = Task.Factory.StartNew(() =>
|
|
||||||
{
|
|
||||||
ClientModel.Start();
|
|
||||||
});
|
|
||||||
ClientModel.SetClientName(clientip);
|
ClientModel.SetClientName(clientip);
|
||||||
|
Task t = Task.Run(ClientModel.Start);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 连接失败。", InvokeMessageType.Core);
|
ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 连接失败。", InvokeMessageType.Core);
|
||||||
}
|
}
|
||||||
Config.ConnectingPlayerCount--;
|
Config.ConnectingPlayerCount--;
|
||||||
}
|
}).OnError(e =>
|
||||||
catch (Exception e)
|
|
||||||
{
|
{
|
||||||
if (--Config.ConnectingPlayerCount < 0) Config.ConnectingPlayerCount = 0;
|
if (--Config.ConnectingPlayerCount < 0) Config.ConnectingPlayerCount = 0;
|
||||||
ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 中断连接!", InvokeMessageType.Core);
|
ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 中断连接!", InvokeMessageType.Core);
|
||||||
ServerHelper.Error(e);
|
ServerHelper.Error(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ServerHelper.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Config.WebSocketAddress == "*")
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine("WebSocket 监听 * 地址要求权限提升,如果提示拒绝访问请以管理员身份运行服务器。", InvokeMessageType.Warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建监听
|
||||||
|
HTTPListener listener = HTTPListener.StartListening(Config.WebSocketAddress, Config.WebSocketPort, Config.WebSocketSubUrl, Config.WebSocketSSL);
|
||||||
|
WebSocketListener = listener;
|
||||||
|
|
||||||
|
// 开始监听连接
|
||||||
|
listener.BannedList.AddRange(Config.ServerBannedList);
|
||||||
|
ServerHelper.WriteLine("Listen -> " + listener.Instance.Prefixes.First());
|
||||||
|
ServerHelper.WriteLine("服务器启动成功,开始监听 . . .");
|
||||||
|
|
||||||
|
if (Config.ServerNotice != "")
|
||||||
|
ServerHelper.WriteLine("\n\n********** 服务器公告 **********\n\n" + Config.ServerNotice + "\n");
|
||||||
|
else
|
||||||
|
ServerHelper.WriteLine("无法读取服务器公告");
|
||||||
|
|
||||||
|
while (Running)
|
||||||
|
{
|
||||||
|
ServerWebSocket socket;
|
||||||
|
string clientip = "";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Guid token = Guid.NewGuid();
|
||||||
|
socket = await listener.Accept(token);
|
||||||
|
|
||||||
|
TaskUtility.NewTask(async () =>
|
||||||
|
{
|
||||||
|
clientip = socket.ClientIP;
|
||||||
|
Config.ConnectingPlayerCount++;
|
||||||
|
bool isConnected = false;
|
||||||
|
bool isDebugMode = false;
|
||||||
|
|
||||||
|
// 开始处理客户端连接请求
|
||||||
|
IEnumerable<SocketObject> objs = [];
|
||||||
|
while (!objs.Any(o => o.SocketType == SocketMessageType.Connect))
|
||||||
|
{
|
||||||
|
objs = objs.Union(await socket.ReceiveAsync());
|
||||||
|
}
|
||||||
|
(isConnected, isDebugMode) = await ConnectController.Connect(listener, socket, token, clientip, objs.Where(o => o.SocketType == SocketMessageType.Connect));
|
||||||
|
if (isConnected)
|
||||||
|
{
|
||||||
|
ServerModel<ServerWebSocket> ClientModel = new(listener, socket, isDebugMode);
|
||||||
|
ClientModel.SetClientName(clientip);
|
||||||
|
Task t = Task.Run(ClientModel.Start);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 连接失败。", InvokeMessageType.Core);
|
||||||
|
await socket.CloseAsync();
|
||||||
|
}
|
||||||
|
Config.ConnectingPlayerCount--;
|
||||||
|
}).OnError(e =>
|
||||||
|
{
|
||||||
|
if (--Config.ConnectingPlayerCount < 0) Config.ConnectingPlayerCount = 0;
|
||||||
|
ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 中断连接!", InvokeMessageType.Core);
|
||||||
|
ServerHelper.Error(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ServerHelper.Error(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,158 +225,31 @@ void StartServer()
|
|||||||
{
|
{
|
||||||
if (e.Message.Equals(new ServerErrorException().Message))
|
if (e.Message.Equals(new ServerErrorException().Message))
|
||||||
{
|
{
|
||||||
if (ListeningSocket != null)
|
if (SocketListener != null)
|
||||||
{
|
{
|
||||||
ListeningSocket.Close();
|
SocketListener.Close();
|
||||||
ListeningSocket = null;
|
SocketListener = null;
|
||||||
|
}
|
||||||
|
if (WebSocketListener != null)
|
||||||
|
{
|
||||||
|
WebSocketListener.Close();
|
||||||
|
WebSocketListener = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ServerHelper.Error(e);
|
ServerHelper.Error(e);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (ListeningSocket != null)
|
if (SocketListener != null)
|
||||||
{
|
{
|
||||||
ListeningSocket.Close();
|
SocketListener.Close();
|
||||||
ListeningSocket = null;
|
SocketListener = null;
|
||||||
|
}
|
||||||
|
if (WebSocketListener != null)
|
||||||
|
{
|
||||||
|
WebSocketListener.Close();
|
||||||
|
WebSocketListener = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetGameModuleList()
|
|
||||||
{
|
|
||||||
List<string> supported = [];
|
|
||||||
// 构建AddonController
|
|
||||||
Hashtable delegates = [];
|
|
||||||
delegates.Add("WriteLine", new Action<string>(msg => ServerHelper.WriteLine(msg, InvokeMessageType.GameModule)));
|
|
||||||
delegates.Add("Error", new Action<Exception>(ServerHelper.Error));
|
|
||||||
// 读取modules目录下的模组
|
|
||||||
Config.GameModuleLoader = GameModuleLoader.LoadGameModules(Config.FunGameType, delegates);
|
|
||||||
foreach (GameModule module in Config.GameModuleLoader.AssociatedServers.Keys)
|
|
||||||
{
|
|
||||||
bool check = true;
|
|
||||||
// 检查模组是否存在对应的模组服务器
|
|
||||||
if (Config.GameModuleLoader.AssociatedServers[module] is null)
|
|
||||||
{
|
|
||||||
ServerHelper.WriteLine("[GameModule] Load Failed: " + module.Name + " 缺少模组服务器");
|
|
||||||
check = false;
|
|
||||||
}
|
|
||||||
// 检查模组是否有相对应的地图
|
|
||||||
if (!Config.GameModuleLoader.Maps.ContainsKey(module.DefaultMap))
|
|
||||||
{
|
|
||||||
ServerHelper.WriteLine("[GameModule] Load Failed: " + module + " 没有找到相对应的地图,加载失败");
|
|
||||||
check = false;
|
|
||||||
}
|
|
||||||
if (check)
|
|
||||||
{
|
|
||||||
supported.Add(module.Name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 设置全局
|
|
||||||
Config.GameModuleSupported = supported.Distinct().ToArray();
|
|
||||||
foreach (string modename in Config.GameModuleSupported)
|
|
||||||
{
|
|
||||||
ServerHelper.WriteLine("[GameModule] Loaded: " + modename);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Config.GameModuleSupported.Length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Connect(ClientSocket socket, Guid token, string clientip, ref bool isDebugMode)
|
|
||||||
{
|
|
||||||
// 接收客户端消息
|
|
||||||
foreach (SocketObject read in socket.Receive())
|
|
||||||
{
|
|
||||||
if (read.SocketType == SocketMessageType.Connect)
|
|
||||||
{
|
|
||||||
if (Config.ConnectingPlayerCount + Config.OnlinePlayerCount > Config.MaxPlayers)
|
|
||||||
{
|
|
||||||
SendRefuseConnect(socket, "服务器可接受的连接数量已上限!");
|
|
||||||
ServerHelper.WriteLine("服务器可接受的连接数量已上限!", InvokeMessageType.Core);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 正在连接服务器 . . .", InvokeMessageType.Core);
|
|
||||||
if (IsIPBanned(ListeningSocket, clientip))
|
|
||||||
{
|
|
||||||
SendRefuseConnect(socket, "服务器已拒绝黑名单用户连接。");
|
|
||||||
ServerHelper.WriteLine("检测到 " + ServerHelper.MakeClientName(clientip) + " 为黑名单用户,已禁止其连接!", InvokeMessageType.Core);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerHelper.WriteLine("[" + SocketSet.GetTypeString(read.SocketType) + "] " + ServerHelper.MakeClientName(socket.ClientIP), InvokeMessageType.Core);
|
|
||||||
|
|
||||||
// 读取参数
|
|
||||||
// 参数1:客户端的游戏模组列表,没有服务器的需要拒绝
|
|
||||||
string[] modes = read.GetParam<string[]>(0) ?? [];
|
|
||||||
// 参数2:客户端是否开启了开发者模式,开启开发者模式部分功能不可用
|
|
||||||
isDebugMode = read.GetParam<bool>(1);
|
|
||||||
if (isDebugMode) ServerHelper.WriteLine("客户端已开启开发者模式");
|
|
||||||
|
|
||||||
string msg = "";
|
|
||||||
List<string> ClientDontHave = [];
|
|
||||||
string strDontHave = string.Join("\r\n", Config.GameModuleSupported.Where(mode => !modes.Contains(mode)));
|
|
||||||
if (strDontHave != "")
|
|
||||||
{
|
|
||||||
strDontHave = "客户端缺少服务器所需的模组:" + strDontHave;
|
|
||||||
ServerHelper.WriteLine(strDontHave, InvokeMessageType.Core);
|
|
||||||
msg += strDontHave;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg == "" && socket.Send(SocketMessageType.Connect, true, msg, token, Config.ServerName, Config.ServerNotice) == SocketResult.Success)
|
|
||||||
{
|
|
||||||
ServerHelper.WriteLine(ServerHelper.MakeClientName(socket.ClientIP) + " <- " + "已确认连接", InvokeMessageType.Core);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (msg != "" && socket.Send(SocketMessageType.Connect, false, msg) == SocketResult.Success)
|
|
||||||
{
|
|
||||||
ServerHelper.WriteLine(ServerHelper.MakeClientName(socket.ClientIP) + " <- " + "拒绝连接", InvokeMessageType.Core);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ServerHelper.WriteLine("无法传输数据,与客户端的连接可能丢失。", InvokeMessageType.Core);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SendRefuseConnect(socket, "服务器已拒绝连接。");
|
|
||||||
ServerHelper.WriteLine("客户端发送了不符合FunGame规定的字符,拒绝连接。", InvokeMessageType.Core);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SendRefuseConnect(ClientSocket socket, string msg)
|
|
||||||
{
|
|
||||||
// 发送消息给客户端
|
|
||||||
msg = "连接被拒绝,如有疑问请联系服务器管理员:" + msg;
|
|
||||||
if (socket.Send(SocketMessageType.Connect, false, msg) == SocketResult.Success)
|
|
||||||
{
|
|
||||||
ServerHelper.WriteLine(ServerHelper.MakeClientName(socket.ClientIP) + " <- " + "已拒绝连接", InvokeMessageType.Core);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ServerHelper.WriteLine("无法传输数据,与客户端的连接可能丢失。", InvokeMessageType.Core);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddBannedList(ServerSocket server)
|
|
||||||
{
|
|
||||||
string[] bans = Config.ServerBannedList.Split(',');
|
|
||||||
foreach (string banned in bans)
|
|
||||||
{
|
|
||||||
server.BannedList.Add(banned.Trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsIPBanned(ServerSocket server, string ip)
|
|
||||||
{
|
|
||||||
string[] strs = ip.Split(":");
|
|
||||||
if (strs.Length == 2 && server.BannedList.Contains(strs[0]))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
@ -1,5 +1,4 @@
|
|||||||
using Milimoe.FunGame.Core.Interface.Base;
|
using Milimoe.FunGame.Core.Interface.Base;
|
||||||
using Milimoe.FunGame.Core.Library.Common.Network;
|
|
||||||
using Milimoe.FunGame.Server.Others;
|
using Milimoe.FunGame.Server.Others;
|
||||||
using Milimoe.FunGame.Server.Utility;
|
using Milimoe.FunGame.Server.Utility;
|
||||||
|
|
||||||
@ -7,7 +6,7 @@ namespace Milimoe.FunGame.Server.Model
|
|||||||
{
|
{
|
||||||
public class ConsoleModel
|
public class ConsoleModel
|
||||||
{
|
{
|
||||||
public static void Order(ServerSocket? server, string order)
|
public static async Task Order<T>(ISocketListener<T>? server, string order) where T : ISocketMessageProcessor
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -19,7 +18,7 @@ namespace Milimoe.FunGame.Server.Model
|
|||||||
string client = Console.ReadLine() ?? "";
|
string client = Console.ReadLine() ?? "";
|
||||||
if (client != "" && server != null)
|
if (client != "" && server != null)
|
||||||
{
|
{
|
||||||
((ServerModel)server.GetClient(client))?.Kick("您已被服务器管理员踢出此服务器。");
|
await Kick((ServerModel<T>)server.ClientList[client]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -29,7 +28,7 @@ namespace Milimoe.FunGame.Server.Model
|
|||||||
string user = Console.ReadLine() ?? "";
|
string user = Console.ReadLine() ?? "";
|
||||||
if (user != "" && server != null)
|
if (user != "" && server != null)
|
||||||
{
|
{
|
||||||
((ServerModel)server.GetUser(user))?.ForceLogOut("您已被服务器管理员强制下线。");
|
await ForceLogOut((ServerModel<T>)server.UserList[user]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -46,7 +45,7 @@ namespace Milimoe.FunGame.Server.Model
|
|||||||
ShowUsers(server);
|
ShowUsers(server);
|
||||||
break;
|
break;
|
||||||
case OrderDictionary.Help:
|
case OrderDictionary.Help:
|
||||||
ServerHelper.WriteLine("Milimoe -> 帮助");
|
ShowHelp();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,7 +55,17 @@ namespace Milimoe.FunGame.Server.Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ShowClients(ServerSocket? server)
|
public static async Task Kick<T>(ServerModel<T> clientModel) where T : ISocketMessageProcessor
|
||||||
|
{
|
||||||
|
await clientModel.Kick("您已被服务器管理员踢出此服务器。");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ForceLogOut<T>(ServerModel<T> clientModel) where T : ISocketMessageProcessor
|
||||||
|
{
|
||||||
|
await clientModel.ForceLogOut("您已被服务器管理员强制下线。");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ShowClients<T>(ISocketListener<T>? server) where T : ISocketMessageProcessor
|
||||||
{
|
{
|
||||||
if (server != null)
|
if (server != null)
|
||||||
{
|
{
|
||||||
@ -69,7 +78,7 @@ namespace Milimoe.FunGame.Server.Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ShowUsers(ServerSocket? server)
|
public static void ShowUsers<T>(ISocketListener<T>? server) where T : ISocketMessageProcessor
|
||||||
{
|
{
|
||||||
if (server != null)
|
if (server != null)
|
||||||
{
|
{
|
||||||
@ -81,5 +90,10 @@ namespace Milimoe.FunGame.Server.Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ShowHelp()
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine("Milimoe -> 帮助");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System.Collections;
|
using System.Data;
|
||||||
using System.Data;
|
|
||||||
using Milimoe.FunGame.Core.Api.Transmittal;
|
using Milimoe.FunGame.Core.Api.Transmittal;
|
||||||
using Milimoe.FunGame.Core.Api.Utility;
|
using Milimoe.FunGame.Core.Api.Utility;
|
||||||
using Milimoe.FunGame.Core.Entity;
|
using Milimoe.FunGame.Core.Entity;
|
||||||
@ -14,88 +13,67 @@ using Milimoe.FunGame.Server.Utility;
|
|||||||
|
|
||||||
namespace Milimoe.FunGame.Server.Model
|
namespace Milimoe.FunGame.Server.Model
|
||||||
{
|
{
|
||||||
public class ServerModel : IServerModel
|
public class ServerModel<T> : IServerModel where T : ISocketMessageProcessor
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Public
|
* Public
|
||||||
*/
|
*/
|
||||||
public bool Running => _Running;
|
public bool Running => _running;
|
||||||
public ClientSocket? Socket => _Socket;
|
public ISocketMessageProcessor Socket { get; }
|
||||||
public string ClientName => _ClientName;
|
public ISocketListener<T> Listener { get; }
|
||||||
public User User => _User;
|
public DataRequestController<T> DataRequestController { get; }
|
||||||
public Room Room
|
public Guid Token => Socket?.Token ?? Guid.Empty;
|
||||||
{
|
public string ClientName => _clientName;
|
||||||
get => _Room;
|
public User User { get; set; } = General.UnknownUserInstance;
|
||||||
set => _Room = value;
|
public Room InRoom { get; set; } = General.HallInstance;
|
||||||
}
|
public SQLHelper? SQLHelper => _sqlHelper;
|
||||||
public MySQLHelper? SQLHelper => _SQLHelper;
|
public MailSender? MailSender => _mailer;
|
||||||
public MailSender? MailSender => _MailSender;
|
|
||||||
public bool IsDebugMode { get; }
|
public bool IsDebugMode { get; }
|
||||||
|
public GameModuleServer? NowGamingServer { get; set; } = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private
|
* protected
|
||||||
*/
|
*/
|
||||||
private GameModuleServer? NowGamingServer = null;
|
protected bool _running = true;
|
||||||
|
protected int _failedTimes = 0; // 超过一定次数断开连接
|
||||||
|
protected string _clientName = "";
|
||||||
|
protected SQLHelper? _sqlHelper = null;
|
||||||
|
protected MailSender? _mailer = null;
|
||||||
|
protected string _username = "";
|
||||||
|
protected long _loginTime = 0;
|
||||||
|
protected long _logoutTime = 0;
|
||||||
|
|
||||||
private ClientSocket? _Socket = null;
|
public ServerModel(ISocketListener<T> server, ISocketMessageProcessor socket, bool isDebugMode)
|
||||||
private bool _Running = false;
|
|
||||||
private User _User = General.UnknownUserInstance;
|
|
||||||
private Room _Room = General.HallInstance;
|
|
||||||
private string _ClientName = "";
|
|
||||||
public MySQLHelper? _SQLHelper = null;
|
|
||||||
public MailSender? _MailSender = null;
|
|
||||||
|
|
||||||
private Guid CheckLoginKey = Guid.Empty;
|
|
||||||
private int FailedTimes = 0; // 超过一定次数断开连接
|
|
||||||
private string UserName = "";
|
|
||||||
private DataSet DsUser = new();
|
|
||||||
private readonly Guid Token;
|
|
||||||
private readonly ServerSocket Server;
|
|
||||||
private readonly DataRequestController DataRequestController;
|
|
||||||
private long LoginTime;
|
|
||||||
private long LogoutTime;
|
|
||||||
private bool IsMatching;
|
|
||||||
|
|
||||||
public ServerModel(ServerSocket server, ClientSocket socket, bool running, bool isDebugMode)
|
|
||||||
{
|
{
|
||||||
Server = server;
|
Listener = server;
|
||||||
_Socket = socket;
|
Socket = socket;
|
||||||
_Running = running;
|
|
||||||
Token = socket.Token;
|
|
||||||
this.IsDebugMode = isDebugMode;
|
|
||||||
if (Config.SQLMode) _SQLHelper = new(this);
|
|
||||||
_MailSender = SmtpHelper.GetMailSender();
|
|
||||||
DataRequestController = new(this);
|
DataRequestController = new(this);
|
||||||
|
IsDebugMode = isDebugMode;
|
||||||
|
if (Config.SQLMode == SQLMode.MySQL) _sqlHelper = new MySQLHelper(this);
|
||||||
|
else if (Config.SQLMode == SQLMode.SQLite) _sqlHelper = Config.SQLHelper;
|
||||||
|
else ServerHelper.WriteLine("SQL 服务处于关闭状态", InvokeMessageType.Warning);
|
||||||
|
_mailer = SmtpHelper.GetMailSender();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Read(ClientSocket socket)
|
public virtual async Task<bool> SocketMessageHandler(ISocketMessageProcessor socket, SocketObject obj)
|
||||||
{
|
{
|
||||||
// 接收客户端消息
|
// 读取收到的消息
|
||||||
try
|
SocketMessageType type = obj.SocketType;
|
||||||
{
|
Guid token = obj.Token;
|
||||||
// 禁止GameModuleServer调用
|
|
||||||
if ((IServerModel)this is GameModuleServer) throw new NotSupportedException("请勿在GameModuleServer类中调用此方法");
|
|
||||||
|
|
||||||
SocketObject[] SocketObjects = socket.Receive();
|
|
||||||
if (SocketObjects.Length == 0)
|
|
||||||
{
|
|
||||||
ServerHelper.WriteLine(GetClientName() + " 发送了空信息。");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SocketObject SocketObject = SocketObjects[0];
|
|
||||||
|
|
||||||
SocketMessageType type = SocketObject.SocketType;
|
|
||||||
Guid token = SocketObject.Token;
|
|
||||||
object[] args = SocketObject.Parameters;
|
|
||||||
string msg = "";
|
string msg = "";
|
||||||
|
|
||||||
// 验证Token
|
// 验证Token
|
||||||
if (type != SocketMessageType.HeartBeat && token != Token)
|
if (type != SocketMessageType.HeartBeat && token != socket.Token)
|
||||||
{
|
{
|
||||||
ServerHelper.WriteLine(GetClientName() + " 使用了非法方式传输消息,服务器拒绝回应 -> [" + SocketSet.GetTypeString(type) + "]");
|
ServerHelper.WriteLine(GetClientName() + " 使用了非法方式传输消息,服务器拒绝回应 -> [" + SocketSet.GetTypeString(type) + "]");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == SocketMessageType.HeartBeat)
|
||||||
|
{
|
||||||
|
return await HeartBeat();
|
||||||
|
}
|
||||||
|
|
||||||
if (type == SocketMessageType.EndGame)
|
if (type == SocketMessageType.EndGame)
|
||||||
{
|
{
|
||||||
NowGamingServer = null;
|
NowGamingServer = null;
|
||||||
@ -104,17 +82,17 @@ namespace Milimoe.FunGame.Server.Model
|
|||||||
|
|
||||||
if (type == SocketMessageType.DataRequest)
|
if (type == SocketMessageType.DataRequest)
|
||||||
{
|
{
|
||||||
return DataRequestHandler(socket, SocketObject);
|
return await DataRequestHandler(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == SocketMessageType.GamingRequest)
|
||||||
|
{
|
||||||
|
return await GamingRequestHandler(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == SocketMessageType.Gaming)
|
if (type == SocketMessageType.Gaming)
|
||||||
{
|
{
|
||||||
return GamingMessageHandler(socket, SocketObject);
|
return await GamingMessageHandler(obj);
|
||||||
}
|
|
||||||
|
|
||||||
if (type == SocketMessageType.HeartBeat)
|
|
||||||
{
|
|
||||||
return HeartBeat(socket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
@ -124,81 +102,136 @@ namespace Milimoe.FunGame.Server.Model
|
|||||||
msg = "你已成功断开与服务器的连接: " + Config.ServerName + "。 ";
|
msg = "你已成功断开与服务器的连接: " + Config.ServerName + "。 ";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return Send(socket, type, msg);
|
|
||||||
}
|
return await Send(type, msg);
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
ServerHelper.WriteLine(GetClientName() + " 没有回应。");
|
|
||||||
ServerHelper.Error(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DataRequestHandler(ClientSocket socket, SocketObject SocketObject)
|
public async Task<bool> HeartBeat()
|
||||||
|
{
|
||||||
|
bool result = await Send(SocketMessageType.HeartBeat);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine("[ " + _username + " ] " + nameof(HeartBeat) + ": " + result, InvokeMessageType.Error);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<bool> DataRequestHandler(SocketObject obj)
|
||||||
{
|
{
|
||||||
if (SQLHelper != null)
|
if (SQLHelper != null)
|
||||||
{
|
{
|
||||||
Hashtable result = [];
|
Dictionary<string, object> result = [];
|
||||||
|
Guid requestID = Guid.Empty;
|
||||||
DataRequestType type = DataRequestType.UnKnown;
|
DataRequestType type = DataRequestType.UnKnown;
|
||||||
|
|
||||||
if (SocketObject.Parameters.Length > 0)
|
if (obj.Parameters.Length > 0)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
type = SocketObject.GetParam<DataRequestType>(0);
|
type = obj.GetParam<DataRequestType>(0);
|
||||||
Hashtable data = SocketObject.GetParam<Hashtable>(1) ?? [];
|
requestID = obj.GetParam<Guid>(1);
|
||||||
|
Dictionary<string, object> data = obj.GetParam<Dictionary<string, object>>(2) ?? [];
|
||||||
|
|
||||||
result = DataRequestController.GetResultData(type, data);
|
result = await DataRequestController.GetResultData(type, data);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
ServerHelper.Error(e);
|
ServerHelper.Error(e);
|
||||||
SQLHelper.Rollback();
|
SQLHelper.Rollback();
|
||||||
return Send(socket, SocketMessageType.DataRequest, type, result);
|
return await Send(SocketMessageType.DataRequest, type, requestID, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Send(socket, SocketMessageType.DataRequest, type, result);
|
bool sendResult = await Send(SocketMessageType.DataRequest, type, requestID, result);
|
||||||
|
if (!sendResult)
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine("[ " + _username + " ] " + nameof(DataRequestHandler) + ": " + sendResult, InvokeMessageType.Error);
|
||||||
|
}
|
||||||
|
return sendResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ServerHelper.WriteLine("[ " + _username + " ] " + nameof(DataRequestHandler) + ": " + false, InvokeMessageType.Error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool GamingMessageHandler(ClientSocket socket, SocketObject SocketObject)
|
protected async Task<bool> GamingRequestHandler(SocketObject obj)
|
||||||
{
|
{
|
||||||
if (NowGamingServer != null)
|
if (NowGamingServer != null)
|
||||||
{
|
{
|
||||||
Hashtable result = [];
|
Dictionary<string, object> result = [];
|
||||||
|
Guid requestID = Guid.Empty;
|
||||||
GamingType type = GamingType.None;
|
GamingType type = GamingType.None;
|
||||||
|
|
||||||
if (SocketObject.Parameters.Length > 0)
|
if (obj.Parameters.Length > 0)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
type = SocketObject.GetParam<GamingType>(0);
|
type = obj.GetParam<GamingType>(0);
|
||||||
Hashtable data = SocketObject.GetParam<Hashtable>(1) ?? [];
|
requestID = obj.GetParam<Guid>(1);
|
||||||
|
Dictionary<string, object> data = obj.GetParam<Dictionary<string, object>>(2) ?? [];
|
||||||
|
|
||||||
result = NowGamingServer.GamingMessageHandler(UserName, type, data);
|
result = await NowGamingServer.GamingMessageHandler(_username, type, data);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
ServerHelper.Error(e);
|
ServerHelper.Error(e);
|
||||||
return Send(socket, SocketMessageType.Gaming, type, result);
|
return await Send(SocketMessageType.GamingRequest, type, requestID, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Send(socket, SocketMessageType.Gaming, type, result);
|
bool sendResult = await Send(SocketMessageType.GamingRequest, type, requestID, result);
|
||||||
|
if (!sendResult)
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine("[ " + _username + " ] " + nameof(GamingRequestHandler) + ": " + sendResult, InvokeMessageType.Error);
|
||||||
|
}
|
||||||
|
return sendResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ServerHelper.WriteLine("[ " + _username + " ] " + nameof(GamingRequestHandler) + ": " + false, InvokeMessageType.Error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Send(ClientSocket socket, SocketMessageType type, params object[] objs)
|
protected async Task<bool> GamingMessageHandler(SocketObject obj)
|
||||||
|
{
|
||||||
|
if (NowGamingServer != null)
|
||||||
|
{
|
||||||
|
Dictionary<string, object> result = [];
|
||||||
|
GamingType type = GamingType.None;
|
||||||
|
|
||||||
|
if (obj.Parameters.Length > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
type = obj.GetParam<GamingType>(0);
|
||||||
|
Dictionary<string, object> data = obj.GetParam<Dictionary<string, object>>(1) ?? [];
|
||||||
|
|
||||||
|
result = await NowGamingServer.GamingMessageHandler(_username, type, data);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ServerHelper.Error(e);
|
||||||
|
return await Send(SocketMessageType.Gaming, type, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sendResult = await Send(SocketMessageType.Gaming, type, result);
|
||||||
|
if (!sendResult)
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine("[ " + _username + " ] " + nameof(GamingMessageHandler) + ": " + sendResult, InvokeMessageType.Error);
|
||||||
|
}
|
||||||
|
return sendResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerHelper.WriteLine("[ " + _username + " ] " + nameof(GamingMessageHandler) + ": " + false, InvokeMessageType.Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task<bool> Send(SocketMessageType type, params object[] objs)
|
||||||
{
|
{
|
||||||
// 发送消息给客户端
|
// 发送消息给客户端
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (socket.Send(type, objs) == SocketResult.Success)
|
if (await Socket.SendAsync(type, objs) == SocketResult.Success)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
@ -207,14 +240,15 @@ namespace Milimoe.FunGame.Server.Model
|
|||||||
break;
|
break;
|
||||||
case SocketMessageType.Disconnect:
|
case SocketMessageType.Disconnect:
|
||||||
RemoveUser();
|
RemoveUser();
|
||||||
Close();
|
await Close();
|
||||||
break;
|
break;
|
||||||
case SocketMessageType.Chat:
|
case SocketMessageType.Chat:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
object obj = objs[0];
|
if (objs.Length > 0 && objs[0] is string str && str != "")
|
||||||
if (obj.GetType() == typeof(string) && (string)obj != "")
|
{
|
||||||
ServerHelper.WriteLine("[" + SocketSet.GetTypeString(type) + "] " + GetClientName() + " <- " + obj, InvokeMessageType.Core);
|
ServerHelper.WriteLine("[" + SocketSet.GetTypeString(type) + "] " + GetClientName() + " <- " + str, InvokeMessageType.Core);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
throw new CanNotSendToClientException();
|
throw new CanNotSendToClientException();
|
||||||
@ -227,18 +261,36 @@ namespace Milimoe.FunGame.Server.Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
public async Task SendClients(IEnumerable<IServerModel> clients, SocketMessageType type, params object[] objs)
|
||||||
{
|
{
|
||||||
if ((IServerModel)this is GameModuleServer) throw new NotSupportedException("请勿在GameModuleServer类中调用此方法"); // 禁止GameModuleServer调用
|
// 发送消息给多个客户端
|
||||||
Task StreamReader = Task.Factory.StartNew(CreateStreamReader);
|
try
|
||||||
Task PeriodicalQuerier = Task.Factory.StartNew(CreatePeriodicalQuerier);
|
{
|
||||||
|
foreach (IServerModel client in clients)
|
||||||
|
{
|
||||||
|
if (client.Socket != null)
|
||||||
|
{
|
||||||
|
await client.Socket.SendAsync(type, objs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ServerHelper.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Start()
|
||||||
|
{
|
||||||
|
TaskUtility.NewTask(CreatePeriodicalQuerier);
|
||||||
|
await CreateStreamReader();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetClientName(string ClientName)
|
public void SetClientName(string ClientName)
|
||||||
{
|
{
|
||||||
_ClientName = ClientName;
|
_clientName = ClientName;
|
||||||
// 添加客户端到列表中
|
// 添加客户端到列表中
|
||||||
Server.AddClient(_ClientName, this);
|
Listener.ClientList.Add(_clientName, this);
|
||||||
Config.OnlinePlayerCount++;
|
Config.OnlinePlayerCount++;
|
||||||
GetUsersCount();
|
GetUsersCount();
|
||||||
}
|
}
|
||||||
@ -248,55 +300,18 @@ namespace Milimoe.FunGame.Server.Model
|
|||||||
return ServerHelper.MakeClientName(ClientName, User);
|
return ServerHelper.MakeClientName(ClientName, User);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PreLogin(DataSet dsuser, string username, Guid checkloginkey)
|
public void SendSystemMessage(ShowMessageType showtype, string msg, string title, int autoclose, params string[] usernames)
|
||||||
{
|
{
|
||||||
DsUser = dsuser;
|
foreach (IServerModel serverTask in Listener.UserList.Where(model => usernames.Length > 0 && usernames.Contains(model.User.Username)))
|
||||||
UserName = username;
|
{
|
||||||
CheckLoginKey = checkloginkey;
|
if (serverTask != null && serverTask.Socket != null)
|
||||||
|
{
|
||||||
|
serverTask.Send(SocketMessageType.System, showtype, msg, title, autoclose);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CheckLogin()
|
|
||||||
{
|
|
||||||
// 创建User对象
|
|
||||||
_User = Factory.GetUser(DsUser);
|
|
||||||
// 检查有没有重复登录的情况
|
|
||||||
KickUser();
|
|
||||||
// 添加至玩家列表
|
|
||||||
AddUser();
|
|
||||||
GetUsersCount();
|
|
||||||
// CheckLogin
|
|
||||||
LoginTime = DateTime.Now.Ticks;
|
|
||||||
SQLHelper?.Execute(UserQuery.Update_CheckLogin(UserName, _Socket?.ClientIP.Split(':')[0] ?? "127.0.0.1"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsLoginKey(Guid key)
|
|
||||||
{
|
|
||||||
return key == CheckLoginKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LogOut()
|
|
||||||
{
|
|
||||||
// 从玩家列表移除
|
|
||||||
RemoveUser();
|
|
||||||
GetUsersCount();
|
|
||||||
CheckLoginKey = Guid.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ForceLogOut(string msg, string username = "")
|
|
||||||
{
|
|
||||||
ServerModel serverTask = (ServerModel)Server.GetUser(username == "" ? UserName : username);
|
|
||||||
if (serverTask.Socket != null)
|
|
||||||
{
|
|
||||||
serverTask.Room = General.HallInstance;
|
|
||||||
foreach (Room room in Config.RoomList.Cast<Room>())
|
|
||||||
{
|
|
||||||
QuitRoom(room.Roomid, room.RoomMaster.Id == User.Id);
|
|
||||||
}
|
|
||||||
serverTask.Send(serverTask.Socket, SocketMessageType.ForceLogout, msg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool QuitRoom(string roomid, bool isMaster)
|
public async Task<bool> QuitRoom(string roomid, bool isMaster)
|
||||||
{
|
{
|
||||||
bool result;
|
bool result;
|
||||||
|
|
||||||
@ -306,21 +321,21 @@ namespace Milimoe.FunGame.Server.Model
|
|||||||
// 是否是房主
|
// 是否是房主
|
||||||
if (isMaster)
|
if (isMaster)
|
||||||
{
|
{
|
||||||
List<User> users = Config.RoomList.GetPlayerList(roomid);
|
List<User> users = [.. Config.RoomList[roomid].UserAndIsReady.Keys];
|
||||||
if (users.Count > 0) // 如果此时房间还有人,更新房主
|
if (users.Count > 0) // 如果此时房间还有人,更新房主
|
||||||
{
|
{
|
||||||
User NewMaster = users[0];
|
User NewMaster = users[0];
|
||||||
Room.RoomMaster = NewMaster;
|
Room.RoomMaster = NewMaster;
|
||||||
SQLHelper?.Execute(RoomQuery.Update_QuitRoom(roomid, User.Id, NewMaster.Id));
|
SQLHelper?.Execute(RoomQuery.Update_QuitRoom(roomid, User.Id, NewMaster.Id));
|
||||||
this.Room = General.HallInstance;
|
this.InRoom = General.HallInstance;
|
||||||
UpdateRoomMaster(Room, true);
|
await UpdateRoomMaster(Room, true);
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
else // 没人了就解散房间
|
else // 没人了就解散房间
|
||||||
{
|
{
|
||||||
Config.RoomList.RemoveRoom(roomid);
|
Config.RoomList.RemoveRoom(roomid);
|
||||||
SQLHelper?.Execute(RoomQuery.Delete_QuitRoom(roomid, User.Id));
|
SQLHelper?.Execute(RoomQuery.Delete_QuitRoom(roomid, User.Id));
|
||||||
this.Room = General.HallInstance;
|
this.InRoom = General.HallInstance;
|
||||||
ServerHelper.WriteLine("[ " + GetClientName() + " ] 解散了房间 " + roomid);
|
ServerHelper.WriteLine("[ " + GetClientName() + " ] 解散了房间 " + roomid);
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
@ -328,236 +343,86 @@ namespace Milimoe.FunGame.Server.Model
|
|||||||
// 不是房主直接退出房间
|
// 不是房主直接退出房间
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.Room = General.HallInstance;
|
this.InRoom = General.HallInstance;
|
||||||
UpdateRoomMaster(Room);
|
await UpdateRoomMaster(Room);
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Kick(string msg, string clientname = "")
|
public async Task UpdateRoomMaster(Room room, bool isUpdateRoomMaster = false)
|
||||||
{
|
{
|
||||||
// 将客户端踢出服务器
|
foreach (IServerModel Client in Listener.ClientList.Where(c => c != null && c.User.Id != 0 && room.Roomid == c.InRoom?.Roomid))
|
||||||
ServerModel serverTask = (ServerModel)Server.GetClient(clientname == "" ? ClientName : clientname);
|
|
||||||
if (serverTask.Socket != null)
|
|
||||||
{
|
{
|
||||||
serverTask.Room = General.HallInstance;
|
await Client.Send(SocketMessageType.Chat, User.Username, DateTimeUtility.GetNowShortTime() + " [ " + User.Username + " ] 离开了房间。");
|
||||||
foreach (Room room in Config.RoomList.Cast<Room>())
|
if (isUpdateRoomMaster && room.RoomMaster?.Id != 0 && room.Roomid != "-1")
|
||||||
{
|
{
|
||||||
QuitRoom(room.Roomid, room.RoomMaster.Id == User.Id);
|
await Client.Send(SocketMessageType.UpdateRoomMaster, room);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Kick(string msg)
|
||||||
|
{
|
||||||
|
await QuitRoom(InRoom.Roomid, InRoom.RoomMaster.Id == User.Id);
|
||||||
RemoveUser();
|
RemoveUser();
|
||||||
serverTask.Send(serverTask.Socket, SocketMessageType.Disconnect, msg);
|
InRoom = General.HallInstance;
|
||||||
}
|
await Send(SocketMessageType.Disconnect, msg);
|
||||||
Close();
|
await Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Chat(string msg)
|
public async Task ForceLogOut(string msg)
|
||||||
{
|
{
|
||||||
ServerHelper.WriteLine(msg);
|
await QuitRoom(InRoom.Roomid, InRoom.RoomMaster.Id == User.Id);
|
||||||
foreach (ServerModel Client in Server.ClientList.Cast<ServerModel>())
|
InRoom = General.HallInstance;
|
||||||
{
|
await Send(SocketMessageType.ForceLogout, msg);
|
||||||
if (Room.Roomid == Client.Room.Roomid)
|
|
||||||
{
|
|
||||||
if (Client != null && User.Id != 0)
|
|
||||||
{
|
|
||||||
Client.Send(Client.Socket!, SocketMessageType.Chat, User.Username, DateTimeUtility.GetNowShortTime() + msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendSystemMessage(ShowMessageType showtype, string msg, string title, int autoclose, params string[] usernames)
|
public async Task ForceLogOutDuplicateLogonUser()
|
||||||
{
|
|
||||||
foreach (ServerModel serverTask in Server.UserList.Cast<ServerModel>().Where(model => usernames.Length > 0 && usernames.Contains(model.UserName)))
|
|
||||||
{
|
|
||||||
if (serverTask != null && serverTask.Socket != null)
|
|
||||||
{
|
|
||||||
serverTask.Send(serverTask.Socket, SocketMessageType.System, showtype, msg, title, autoclose);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StartGame(string roomid, List<User> users, params string[] usernames)
|
|
||||||
{
|
|
||||||
Room room = General.HallInstance;
|
|
||||||
if (roomid != "-1")
|
|
||||||
{
|
|
||||||
room = Config.RoomList[roomid];
|
|
||||||
}
|
|
||||||
if (room.Roomid == "-1") return;
|
|
||||||
// 启动服务器
|
|
||||||
TaskUtility.NewTask(() =>
|
|
||||||
{
|
|
||||||
if (Config.GameModuleLoader != null && Config.GameModuleLoader.ServerModules.ContainsKey(room.GameModule))
|
|
||||||
{
|
|
||||||
NowGamingServer = Config.GameModuleLoader.GetServerMode(room.GameModule);
|
|
||||||
Dictionary<string, IServerModel> others = Server.UserList.Cast<IServerModel>().Where(model => usernames.Contains(model.User.Username) && model.User.Username != UserName).ToDictionary(k => k.User.Username, v => v);
|
|
||||||
if (NowGamingServer.StartServer(room.GameModule, room, users, this, others))
|
|
||||||
{
|
|
||||||
foreach (ServerModel serverTask in Server.UserList.Cast<ServerModel>().Where(model => usernames.Contains(model.User.Username)))
|
|
||||||
{
|
|
||||||
if (serverTask != null && serverTask.Socket != null)
|
|
||||||
{
|
|
||||||
Config.RoomList.CancelReady(roomid, serverTask.User);
|
|
||||||
serverTask.Send(serverTask.Socket, SocketMessageType.StartGame, room, users);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void IntoRoom(string roomid)
|
|
||||||
{
|
|
||||||
Room = Config.RoomList[roomid];
|
|
||||||
foreach (ServerModel Client in Server.ClientList.Cast<ServerModel>().Where(c => c != null && c.Socket != null && roomid == c.Room.Roomid))
|
|
||||||
{
|
|
||||||
if (User.Id != 0)
|
|
||||||
{
|
|
||||||
Client.Send(Client.Socket!, SocketMessageType.Chat, User.Username, DateTimeUtility.GetNowShortTime() + " [ " + User.Username + " ] 进入了房间。");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateRoomMaster(Room Room, bool bolIsUpdateRoomMaster = false)
|
|
||||||
{
|
|
||||||
foreach (ServerModel Client in Server.ClientList.Cast<ServerModel>().Where(c => c != null && c.Socket != null && Room.Roomid == c.Room.Roomid))
|
|
||||||
{
|
|
||||||
if (User.Id != 0)
|
|
||||||
{
|
|
||||||
Client.Send(Client.Socket!, SocketMessageType.Chat, User.Username, DateTimeUtility.GetNowShortTime() + " [ " + User.Username + " ] 离开了房间。");
|
|
||||||
if (bolIsUpdateRoomMaster && Room.RoomMaster?.Id != 0 && Room.Roomid != "-1")
|
|
||||||
{
|
|
||||||
Client.Send(Client.Socket!, SocketMessageType.UpdateRoomMaster, Room);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HeartBeat(ClientSocket socket)
|
|
||||||
{
|
|
||||||
return Send(socket, SocketMessageType.HeartBeat, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StartMatching(RoomType type, User user)
|
|
||||||
{
|
|
||||||
IsMatching = true;
|
|
||||||
ServerHelper.WriteLine(GetClientName() + " 开始匹配。类型:" + RoomSet.GetTypeString(type));
|
|
||||||
TaskUtility.NewTask(async () =>
|
|
||||||
{
|
|
||||||
if (IsMatching)
|
|
||||||
{
|
|
||||||
Room room = await MatchingRoom(type, user);
|
|
||||||
if (IsMatching && Socket != null)
|
|
||||||
{
|
|
||||||
Send(Socket, SocketMessageType.MatchRoom, room);
|
|
||||||
}
|
|
||||||
IsMatching = false;
|
|
||||||
}
|
|
||||||
}).OnError(e =>
|
|
||||||
{
|
|
||||||
ServerHelper.Error(e);
|
|
||||||
IsMatching = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StopMatching()
|
|
||||||
{
|
|
||||||
if (IsMatching)
|
|
||||||
{
|
|
||||||
ServerHelper.WriteLine(GetClientName() + " 取消了匹配。");
|
|
||||||
IsMatching = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<Room> MatchingRoom(RoomType roomtype, User user)
|
|
||||||
{
|
|
||||||
int i = 1;
|
|
||||||
int time = 0;
|
|
||||||
while (IsMatching)
|
|
||||||
{
|
|
||||||
// 先列出符合条件的房间
|
|
||||||
List<Room> targets = Config.RoomList.ListRoom.Where(r => r.RoomType == roomtype).ToList();
|
|
||||||
|
|
||||||
// 匹配Elo
|
|
||||||
foreach (Room room in targets)
|
|
||||||
{
|
|
||||||
// 计算房间平均Elo
|
|
||||||
List<User> players = Config.RoomList.GetPlayerList(room.Roomid);
|
|
||||||
if (players.Count > 0)
|
|
||||||
{
|
|
||||||
decimal avgelo = players.Sum(u => u.Statistics.EloStats.ContainsKey(0) ? u.Statistics.EloStats[0] : 0M) / players.Count;
|
|
||||||
decimal userelo = user.Statistics.EloStats.ContainsKey(0) ? user.Statistics.EloStats[0] : 0M;
|
|
||||||
if (userelo >= avgelo - (300 * i) && userelo <= avgelo + (300 * i))
|
|
||||||
{
|
|
||||||
return room;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IsMatching) break;
|
|
||||||
|
|
||||||
// 等待10秒
|
|
||||||
await Task.Delay(10 * 1000);
|
|
||||||
time += 10 * 1000;
|
|
||||||
if (time >= 50 * 1000)
|
|
||||||
{
|
|
||||||
// 50秒后不再匹配Elo,直接返回第一个房间
|
|
||||||
if (targets.Count > 0)
|
|
||||||
{
|
|
||||||
return targets[0];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return General.HallInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void KickUser()
|
|
||||||
{
|
{
|
||||||
if (User.Id != 0)
|
if (User.Id != 0)
|
||||||
{
|
{
|
||||||
string user = User.Username;
|
string user = User.Username;
|
||||||
if (Server.ContainsUser(user))
|
if (Listener.UserList.ContainsKey(user))
|
||||||
{
|
{
|
||||||
ServerHelper.WriteLine("OnlinePlayers: 玩家 " + user + " 重复登录!");
|
ServerHelper.WriteLine("OnlinePlayers: 玩家 " + user + " 重复登录!");
|
||||||
ForceLogOut("您的账号在别处登录,已强制下线。");
|
await ForceLogOut("您的账号在别处登录,已强制下线。");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool AddUser()
|
public bool AddUser()
|
||||||
{
|
{
|
||||||
if (User.Id != 0 && this != null)
|
if (User.Id != 0 && this != null)
|
||||||
{
|
{
|
||||||
Server.AddUser(User.Username, this);
|
Listener.UserList.Add(User.Username, this);
|
||||||
UserName = User.Username;
|
_username = User.Username;
|
||||||
ServerHelper.WriteLine("OnlinePlayers: 玩家 " + User.Username + " 已添加");
|
ServerHelper.WriteLine("OnlinePlayers: 玩家 " + User.Username + " 已添加");
|
||||||
|
// 更新最后登录时间、IP地址
|
||||||
|
_loginTime = DateTime.Now.Ticks;
|
||||||
|
SQLHelper?.Execute(UserQuery.Update_CheckLogin(_username, Socket?.ClientIP.Split(':')[0] ?? "127.0.0.1"));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool RemoveUser()
|
public bool RemoveUser()
|
||||||
{
|
{
|
||||||
if (User.Id != 0 && this != null)
|
if (User.Id != 0 && this != null)
|
||||||
{
|
{
|
||||||
LogoutTime = DateTime.Now.Ticks;
|
_logoutTime = DateTime.Now.Ticks;
|
||||||
int TotalMinutes = Convert.ToInt32((new DateTime(LogoutTime) - new DateTime(LoginTime)).TotalMinutes);
|
int TotalMinutes = Convert.ToInt32((new DateTime(_logoutTime) - new DateTime(_loginTime)).TotalMinutes);
|
||||||
SQLHelper?.Execute(UserQuery.Update_GameTime(User.Username, TotalMinutes));
|
SQLHelper?.Execute(UserQuery.Update_GameTime(User.Username, TotalMinutes));
|
||||||
if (SQLHelper?.Result == SQLResult.Success)
|
if (SQLHelper != null && SQLHelper.Result == SQLResult.Success)
|
||||||
{
|
{
|
||||||
ServerHelper.WriteLine("OnlinePlayers: 玩家 " + User.Username + " 本次已游玩" + TotalMinutes + "分钟");
|
ServerHelper.WriteLine("OnlinePlayers: 玩家 " + User.Username + " 本次已游玩" + TotalMinutes + "分钟");
|
||||||
}
|
}
|
||||||
else ServerHelper.WriteLine("OnlinePlayers: 无法更新玩家 " + User.Username + " 的游戏时长");
|
else ServerHelper.WriteLine("OnlinePlayers: 无法更新玩家 " + User.Username + " 的游戏时长");
|
||||||
if (Server.RemoveUser(User.Username))
|
if (Listener.UserList.Remove(User.Username))
|
||||||
{
|
{
|
||||||
ServerHelper.WriteLine("OnlinePlayers: 玩家 " + User.Username + " 已移除");
|
ServerHelper.WriteLine("OnlinePlayers: 玩家 " + User.Username + " 已移除");
|
||||||
_User = General.UnknownUserInstance;
|
User = General.UnknownUserInstance;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else ServerHelper.WriteLine("OnlinePlayers: 移除玩家 " + User.Username + " 失败");
|
else ServerHelper.WriteLine("OnlinePlayers: 移除玩家 " + User.Username + " 失败");
|
||||||
@ -565,37 +430,65 @@ namespace Milimoe.FunGame.Server.Model
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetUsersCount()
|
public void GetUsersCount()
|
||||||
{
|
{
|
||||||
ServerHelper.WriteLine($"目前在线客户端数量: {Server.ClientCount}(已登录的玩家数量:{Server.UserCount})");
|
ServerHelper.WriteLine($"{Listener.Name} 的目前在线客户端数量: {Listener.ClientList.Count}(已登录的玩家数量:{Listener.UserList.Count})");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateStreamReader()
|
protected virtual async Task<bool> Read(ISocketMessageProcessor socket)
|
||||||
{
|
{
|
||||||
Thread.Sleep(20);
|
// 接收客户端消息
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SocketObject[] objs = await socket.ReceiveAsync();
|
||||||
|
|
||||||
|
if (objs.Length == 0)
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine(GetClientName() + " 发送了空信息。");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (SocketObject obj in objs)
|
||||||
|
{
|
||||||
|
await SocketMessageHandler(socket, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine(GetClientName() + " 没有回应。");
|
||||||
|
ServerHelper.Error(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task CreateStreamReader()
|
||||||
|
{
|
||||||
|
await Task.Delay(20);
|
||||||
ServerHelper.WriteLine("Creating: StreamReader -> " + GetClientName() + " ...OK");
|
ServerHelper.WriteLine("Creating: StreamReader -> " + GetClientName() + " ...OK");
|
||||||
while (Running)
|
while (Running)
|
||||||
{
|
{
|
||||||
if (Socket != null)
|
if (Socket != null)
|
||||||
{
|
{
|
||||||
if (!Read(Socket))
|
if (!await Read(Socket))
|
||||||
{
|
{
|
||||||
FailedTimes++;
|
_failedTimes++;
|
||||||
if (FailedTimes >= Config.MaxConnectionFaileds)
|
if (_failedTimes >= Config.MaxConnectionFaileds)
|
||||||
{
|
{
|
||||||
RemoveUser();
|
RemoveUser();
|
||||||
Close();
|
await Close();
|
||||||
ServerHelper.WriteLine(GetClientName() + " Error -> Too Many Faileds.");
|
ServerHelper.WriteLine(GetClientName() + " Error -> Too Many Faileds.");
|
||||||
ServerHelper.WriteLine(GetClientName() + " Close -> StreamReader is Closed.");
|
ServerHelper.WriteLine(GetClientName() + " Close -> StreamReader is Closed.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (FailedTimes - 1 >= 0) FailedTimes--;
|
else if (_failedTimes - 1 >= 0) _failedTimes--;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
RemoveUser();
|
RemoveUser();
|
||||||
Close();
|
await Close();
|
||||||
ServerHelper.WriteLine(GetClientName() + " Error -> Socket is Closed.");
|
ServerHelper.WriteLine(GetClientName() + " Error -> Socket is Closed.");
|
||||||
ServerHelper.WriteLine(GetClientName() + " Close -> StringStream is Closed.");
|
ServerHelper.WriteLine(GetClientName() + " Close -> StringStream is Closed.");
|
||||||
break;
|
break;
|
||||||
@ -603,41 +496,40 @@ namespace Milimoe.FunGame.Server.Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreatePeriodicalQuerier()
|
protected async Task CreatePeriodicalQuerier()
|
||||||
{
|
{
|
||||||
Thread.Sleep(20);
|
await Task.Delay(20);
|
||||||
ServerHelper.WriteLine("Creating: PeriodicalQuerier -> " + GetClientName() + " ...OK");
|
ServerHelper.WriteLine("Creating: PeriodicalQuerier -> " + GetClientName() + " ...OK");
|
||||||
while (Running)
|
while (Running)
|
||||||
{
|
{
|
||||||
// 每两小时触发一次SQL服务器的心跳查询,防止SQL服务器掉线
|
// 每两小时触发一次SQL服务器的心跳查询,防止SQL服务器掉线
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Thread.Sleep(2 * 1000 * 3600);
|
await Task.Delay(2 * 1000 * 3600);
|
||||||
SQLHelper?.ExecuteDataSet(UserQuery.Select_IsExistUsername(UserName));
|
SQLHelper?.ExecuteDataSet(UserQuery.Select_IsExistUsername(_username));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
ServerHelper.Error(e);
|
ServerHelper.Error(e);
|
||||||
RemoveUser();
|
RemoveUser();
|
||||||
Close();
|
await Close();
|
||||||
ServerHelper.WriteLine(GetClientName() + " Error -> Socket is Closed.");
|
ServerHelper.WriteLine(GetClientName() + " Error -> Socket is Closed.");
|
||||||
ServerHelper.WriteLine(GetClientName() + " Close -> StringStream is Closed.");
|
ServerHelper.WriteLine(GetClientName() + " Close -> StringStream is Closed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Close()
|
protected async Task Close()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SQLHelper?.Close();
|
SQLHelper?.Close();
|
||||||
_SQLHelper = null;
|
_sqlHelper = null;
|
||||||
MailSender?.Dispose();
|
MailSender?.Dispose();
|
||||||
_MailSender = null;
|
_mailer = null;
|
||||||
Socket?.Close();
|
await Socket.CloseAsync();
|
||||||
_Socket = null;
|
_running = false;
|
||||||
_Running = false;
|
Listener.ClientList.Remove(ClientName);
|
||||||
Server.RemoveClient(ClientName);
|
|
||||||
Config.OnlinePlayerCount--;
|
Config.OnlinePlayerCount--;
|
||||||
GetUsersCount();
|
GetUsersCount();
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Milimoe.FunGame.Core.Api.Transmittal;
|
using Milimoe.FunGame.Core.Api.Transmittal;
|
||||||
using Milimoe.FunGame.Core.Api.Utility;
|
using Milimoe.FunGame.Core.Api.Utility;
|
||||||
|
using Milimoe.FunGame.Core.Library.Common.Addon;
|
||||||
using Milimoe.FunGame.Core.Library.Constant;
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
using Milimoe.FunGame.Core.Library.SQLScript.Common;
|
using Milimoe.FunGame.Core.Library.SQLScript.Common;
|
||||||
using Milimoe.FunGame.Core.Library.SQLScript.Entity;
|
using Milimoe.FunGame.Core.Library.SQLScript.Entity;
|
||||||
using Milimoe.FunGame.Core.Model;
|
using Milimoe.FunGame.Core.Model;
|
||||||
using Milimoe.FunGame.Server.Utility;
|
using Milimoe.FunGame.Server.Utility;
|
||||||
|
using Milimoe.FunGame.Server.Utility.DataUtility;
|
||||||
|
|
||||||
namespace Milimoe.FunGame.Server.Others
|
namespace Milimoe.FunGame.Server.Others
|
||||||
{
|
{
|
||||||
@ -18,10 +20,35 @@ namespace Milimoe.FunGame.Server.Others
|
|||||||
public static string ServerName { get; set; } = "FunGame Server";
|
public static string ServerName { get; set; } = "FunGame Server";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 默认端口
|
/// Socket 端口
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static int ServerPort { get; set; } = 22222;
|
public static int ServerPort { get; set; } = 22222;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 使用 WebSocket
|
||||||
|
/// </summary>
|
||||||
|
public static bool UseWebSocket { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WebSocket 监听地址
|
||||||
|
/// </summary>
|
||||||
|
public static string WebSocketAddress { get; set; } = "localhost";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WebSocket 端口
|
||||||
|
/// </summary>
|
||||||
|
public static int WebSocketPort { get; set; } = 22222;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WebSocket 监听子路径
|
||||||
|
/// </summary>
|
||||||
|
public static string WebSocketSubUrl { get; set; } = "ws";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WebSocket 开启 SSL
|
||||||
|
/// </summary>
|
||||||
|
public static bool WebSocketSSL { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 默认状态:1可连接 0不可连接 -1不可用
|
/// 默认状态:1可连接 0不可连接 -1不可用
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -50,7 +77,7 @@ namespace Milimoe.FunGame.Server.Others
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 禁止连接的黑名单
|
/// 禁止连接的黑名单
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string ServerBannedList { get; set; } = "";
|
public static List<string> ServerBannedList { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 最多接受连接的玩家数量
|
/// 最多接受连接的玩家数量
|
||||||
@ -95,7 +122,7 @@ namespace Milimoe.FunGame.Server.Others
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否运行数据库模式
|
/// 是否运行数据库模式
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool SQLMode { get; set; } = false;
|
public static SQLMode SQLMode { get; set; } = SQLMode.None;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Server实际安装的模组
|
/// Server实际安装的模组
|
||||||
@ -114,7 +141,7 @@ namespace Milimoe.FunGame.Server.Others
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_SQLHelper is null) throw new MySQLConfigException();
|
if (_SQLHelper is null) throw new SQLServiceException();
|
||||||
return _SQLHelper;
|
return _SQLHelper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,27 +154,78 @@ namespace Milimoe.FunGame.Server.Others
|
|||||||
public static void InitSQLHelper()
|
public static void InitSQLHelper()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
{
|
||||||
|
if (INIHelper.ExistINIFile())
|
||||||
|
{
|
||||||
|
if (INIHelper.ReadINI("MySQL", "UseMySQL").Trim() == "true")
|
||||||
{
|
{
|
||||||
_SQLHelper = new MySQLHelper("", false);
|
_SQLHelper = new MySQLHelper("", false);
|
||||||
if (((MySQLHelper)_SQLHelper).Connection != null)
|
if (((MySQLHelper)_SQLHelper).Connection != null)
|
||||||
{
|
{
|
||||||
SQLMode = true;
|
SQLMode = _SQLHelper.Mode;
|
||||||
ServerLogin();
|
ServerLogin();
|
||||||
ClearRoomList();
|
ClearRoomList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (INIHelper.ReadINI("SQLite", "UseSQLite").Trim() == "true")
|
||||||
|
{
|
||||||
|
_SQLHelper = new SQLiteHelper();
|
||||||
|
SQLMode = _SQLHelper.Mode;
|
||||||
|
ServerLogin();
|
||||||
|
ClearRoomList();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SQLMode = SQLMode.None;
|
||||||
|
ServerHelper.WriteLine("未开启 SQL 服务,某些请求将无法处理。", InvokeMessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
ServerHelper.Error(e);
|
ServerHelper.Error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool GetGameModuleList()
|
||||||
|
{
|
||||||
|
List<string> supported = [];
|
||||||
|
// 构建AddonController
|
||||||
|
Hashtable delegates = [];
|
||||||
|
delegates.Add("WriteLine", new Action<string>(msg => ServerHelper.WriteLine(msg, InvokeMessageType.GameModule)));
|
||||||
|
delegates.Add("Error", new Action<Exception>(ServerHelper.Error));
|
||||||
|
// 读取modules目录下的模组
|
||||||
|
GameModuleLoader = GameModuleLoader.LoadGameModules(FunGameType, delegates);
|
||||||
|
foreach (GameModuleServer module in GameModuleLoader.ModuleServers.Values)
|
||||||
|
{
|
||||||
|
bool check = true;
|
||||||
|
// 检查模组是否有相对应的地图
|
||||||
|
if (!GameModuleLoader.Maps.ContainsKey(module.DefaultMap))
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine("GameModule Load Failed: " + module + " 没有找到相对应的地图,加载失败", InvokeMessageType.Error);
|
||||||
|
check = false;
|
||||||
|
}
|
||||||
|
if (check)
|
||||||
|
{
|
||||||
|
supported.Add(module.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 设置全局
|
||||||
|
GameModuleSupported = supported.Distinct().ToArray();
|
||||||
|
foreach (string modename in GameModuleSupported)
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine("Loaded: " + modename, InvokeMessageType.GameModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GameModuleSupported.Length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 服务器启动登记
|
/// 服务器启动登记
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void ServerLogin()
|
public static void ServerLogin()
|
||||||
{
|
{
|
||||||
if (SQLMode)
|
if (SQLMode != SQLMode.None)
|
||||||
{
|
{
|
||||||
SQLHelper.Execute(ServerLoginLogs.Insert_ServerLoginLogs(ServerName, ServerKey));
|
SQLHelper.Execute(ServerLoginLogs.Insert_ServerLoginLogs(ServerName, ServerKey));
|
||||||
}
|
}
|
||||||
@ -158,7 +236,7 @@ namespace Milimoe.FunGame.Server.Others
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static void ClearRoomList()
|
public static void ClearRoomList()
|
||||||
{
|
{
|
||||||
if (SQLMode)
|
if (SQLMode != SQLMode.None)
|
||||||
{
|
{
|
||||||
SQLHelper.Execute(RoomQuery.Delete_Rooms());
|
SQLHelper.Execute(RoomQuery.Delete_Rooms());
|
||||||
}
|
}
|
||||||
|
52
FunGame.Server/Utilities/ConnectProperties.cs
Normal file
52
FunGame.Server/Utilities/ConnectProperties.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using Milimoe.FunGame.Core.Api.Utility;
|
||||||
|
|
||||||
|
namespace Milimoe.FunGame.Server.Utility
|
||||||
|
{
|
||||||
|
public class ConnectProperties
|
||||||
|
{
|
||||||
|
public static string Name { get; set; } = "";
|
||||||
|
public static string DataSource { get; set; } = "";
|
||||||
|
public static string Port { get; set; } = "";
|
||||||
|
public static string DataBase { get; set; } = "";
|
||||||
|
public static string User { get; set; } = "";
|
||||||
|
public static string Password { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 读取MySQL服务器配置文件
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetConnectPropertiesForMySQL()
|
||||||
|
{
|
||||||
|
if (Name == "" && DataSource == "" && Port == "" && DataBase == "" && User == "" && Password == "")
|
||||||
|
{
|
||||||
|
if (INIHelper.ExistINIFile())
|
||||||
|
{
|
||||||
|
DataSource = INIHelper.ReadINI("MySQL", "DBServer");
|
||||||
|
Port = INIHelper.ReadINI("MySQL", "DBPort");
|
||||||
|
DataBase = INIHelper.ReadINI("MySQL", "DBName");
|
||||||
|
User = INIHelper.ReadINI("MySQL", "DBUser");
|
||||||
|
Password = INIHelper.ReadINI("MySQL", "DBPassword");
|
||||||
|
}
|
||||||
|
else ServerHelper.Error(new MySQLConfigException());
|
||||||
|
}
|
||||||
|
return "data source = " + DataSource + "; port = " + Port + "; database = " + DataBase + "; user = " + User + "; password = " + Password + "; charset = utf8mb4;";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 读取SQLite服务器配置文件
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetConnectPropertiesForSQLite()
|
||||||
|
{
|
||||||
|
if (DataSource == "")
|
||||||
|
{
|
||||||
|
if (INIHelper.ExistINIFile())
|
||||||
|
{
|
||||||
|
DataSource = INIHelper.ReadINI("SQLite", "DataSource");
|
||||||
|
}
|
||||||
|
else ServerHelper.Error(new SQLServiceException());
|
||||||
|
}
|
||||||
|
return "data source=" + DataSource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -26,6 +26,10 @@ namespace Milimoe.FunGame.Server.Utility
|
|||||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||||
prefix = "[Api] ";
|
prefix = "[Api] ";
|
||||||
break;
|
break;
|
||||||
|
case InvokeMessageType.Warning:
|
||||||
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||||
|
prefix = "[Warning] ";
|
||||||
|
break;
|
||||||
case InvokeMessageType.Interface:
|
case InvokeMessageType.Interface:
|
||||||
Console.ForegroundColor = ConsoleColor.Magenta;
|
Console.ForegroundColor = ConsoleColor.Magenta;
|
||||||
prefix = "[Interface] ";
|
prefix = "[Interface] ";
|
||||||
@ -62,15 +66,18 @@ namespace Milimoe.FunGame.Server.Utility
|
|||||||
public static void Write(string msg, InvokeMessageType type = InvokeMessageType.System)
|
public static void Write(string msg, InvokeMessageType type = InvokeMessageType.System)
|
||||||
{
|
{
|
||||||
if (msg.Trim() != "") Console.Write("\r" + GetPrefix(type) + msg + "> ");
|
if (msg.Trim() != "") Console.Write("\r" + GetPrefix(type) + msg + "> ");
|
||||||
|
Console.ResetColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void WriteLine(string msg, InvokeMessageType type = InvokeMessageType.System)
|
public static void WriteLine(string msg, InvokeMessageType type = InvokeMessageType.System)
|
||||||
{
|
{
|
||||||
if (msg.Trim() != "") Console.Write("\r" + GetPrefix(type) + msg + "\n\r> ");
|
if (msg.Trim() != "") Console.Write("\r" + GetPrefix(type) + msg + "\n\r> ");
|
||||||
|
Console.ResetColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Type()
|
public static void Type()
|
||||||
{
|
{
|
||||||
|
Console.ResetColor();
|
||||||
Console.Write("\r> ");
|
Console.Write("\r> ");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +93,7 @@ namespace Milimoe.FunGame.Server.Utility
|
|||||||
|
|
||||||
private static Hashtable GetServerSettingHashtable()
|
private static Hashtable GetServerSettingHashtable()
|
||||||
{
|
{
|
||||||
Hashtable settings = new();
|
Hashtable settings = [];
|
||||||
if (INIHelper.ExistINIFile())
|
if (INIHelper.ExistINIFile())
|
||||||
{
|
{
|
||||||
settings.Add("Name", INIHelper.ReadINI("Server", "Name"));
|
settings.Add("Name", INIHelper.ReadINI("Server", "Name"));
|
||||||
@ -99,6 +106,11 @@ namespace Milimoe.FunGame.Server.Utility
|
|||||||
settings.Add("OfficialMail", INIHelper.ReadINI("ServerMail", "OfficialMail"));
|
settings.Add("OfficialMail", INIHelper.ReadINI("ServerMail", "OfficialMail"));
|
||||||
settings.Add("SupportMail", INIHelper.ReadINI("ServerMail", "SupportMail"));
|
settings.Add("SupportMail", INIHelper.ReadINI("ServerMail", "SupportMail"));
|
||||||
settings.Add("Port", Convert.ToInt32(INIHelper.ReadINI("Socket", "Port")));
|
settings.Add("Port", Convert.ToInt32(INIHelper.ReadINI("Socket", "Port")));
|
||||||
|
settings.Add("UseWebSocket", Convert.ToBoolean(INIHelper.ReadINI("Socket", "UseWebSocket")));
|
||||||
|
settings.Add("WebSocketAddress", Convert.ToString(INIHelper.ReadINI("Socket", "WebSocketAddress")));
|
||||||
|
settings.Add("WebSocketPort", Convert.ToInt32(INIHelper.ReadINI("Socket", "WebSocketPort")));
|
||||||
|
settings.Add("WebSocketSubUrl", Convert.ToString(INIHelper.ReadINI("Socket", "WebSocketSubUrl")));
|
||||||
|
settings.Add("WebSocketSSL", Convert.ToBoolean(INIHelper.ReadINI("Socket", "WebSocketSSL")));
|
||||||
settings.Add("MaxPlayer", Convert.ToInt32(INIHelper.ReadINI("Socket", "MaxPlayer")));
|
settings.Add("MaxPlayer", Convert.ToInt32(INIHelper.ReadINI("Socket", "MaxPlayer")));
|
||||||
settings.Add("MaxConnectFailed", Convert.ToInt32(INIHelper.ReadINI("Socket", "MaxConnectFailed")));
|
settings.Add("MaxConnectFailed", Convert.ToInt32(INIHelper.ReadINI("Socket", "MaxConnectFailed")));
|
||||||
}
|
}
|
||||||
@ -124,7 +136,7 @@ namespace Milimoe.FunGame.Server.Utility
|
|||||||
if (Describe != null) Config.ServerDescription = Describe;
|
if (Describe != null) Config.ServerDescription = Describe;
|
||||||
if (Notice != null) Config.ServerNotice = Notice;
|
if (Notice != null) Config.ServerNotice = Notice;
|
||||||
if (Key != null) Config.ServerKey = Key;
|
if (Key != null) Config.ServerKey = Key;
|
||||||
if (BannedList != null) Config.ServerBannedList = BannedList;
|
if (BannedList != null) Config.ServerBannedList = BannedList.Split(',').Select(s => s.Trim()).ToList();
|
||||||
|
|
||||||
string? OfficialMail = (string?)settings["OfficialMail"];
|
string? OfficialMail = (string?)settings["OfficialMail"];
|
||||||
string? SupportMail = (string?)settings["SupportMail"];
|
string? SupportMail = (string?)settings["SupportMail"];
|
||||||
@ -134,18 +146,28 @@ namespace Milimoe.FunGame.Server.Utility
|
|||||||
|
|
||||||
int? Status = (int?)settings["Status"];
|
int? Status = (int?)settings["Status"];
|
||||||
int? Port = (int?)settings["Port"];
|
int? Port = (int?)settings["Port"];
|
||||||
|
bool? UseWebSocket = (bool?)settings["UseWebSocket"];
|
||||||
|
string? WebSocketAddress = (string?)settings["WebSocketAddress"];
|
||||||
|
int? WebSocketPort = (int?)settings["WebSocketPort"];
|
||||||
|
string? WebSocketSubUrl = (string?)settings["WebSocketSubUrl"];
|
||||||
|
bool? WebSocketSSL = (bool?)settings["WebSocketSSL"];
|
||||||
int? MaxPlayer = (int?)settings["MaxPlayer"];
|
int? MaxPlayer = (int?)settings["MaxPlayer"];
|
||||||
int? MaxConnectFailed = (int?)settings["MaxConnectFailed"];
|
int? MaxConnectFailed = (int?)settings["MaxConnectFailed"];
|
||||||
|
|
||||||
if (Status != null) Config.ServerStatus = (int)Status;
|
if (Status != null) Config.ServerStatus = (int)Status;
|
||||||
if (Port != null) Config.ServerPort = (int)Port;
|
if (Port != null) Config.ServerPort = (int)Port;
|
||||||
|
if (UseWebSocket != null) Config.UseWebSocket = (bool)UseWebSocket;
|
||||||
|
if (WebSocketAddress != null) Config.WebSocketAddress = WebSocketAddress;
|
||||||
|
if (WebSocketPort != null) Config.WebSocketPort = (int)WebSocketPort;
|
||||||
|
if (WebSocketSubUrl != null) Config.WebSocketSubUrl = WebSocketSubUrl;
|
||||||
|
if (WebSocketSSL != null) Config.WebSocketSSL = (bool)WebSocketSSL;
|
||||||
if (MaxPlayer != null) Config.MaxPlayers = (int)MaxPlayer;
|
if (MaxPlayer != null) Config.MaxPlayers = (int)MaxPlayer;
|
||||||
if (MaxConnectFailed != null) Config.MaxConnectionFaileds = (int)MaxConnectFailed;
|
if (MaxConnectFailed != null) Config.MaxConnectionFaileds = (int)MaxConnectFailed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
ServerHelper.WriteLine(e.StackTrace ?? "");
|
Error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +214,7 @@ namespace Milimoe.FunGame.Server.Utility
|
|||||||
if (SmtpPort > 0) return new MailSender(SenderMailAddress, SenderName, SenderPassword, SmtpHost, SmtpPort, OpenSSL);
|
if (SmtpPort > 0) return new MailSender(SenderMailAddress, SenderName, SenderPassword, SmtpHost, SmtpPort, OpenSSL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ServerHelper.WriteLine("Smtp服务处于关闭状态");
|
ServerHelper.WriteLine("SMTP 服务处于关闭状态", InvokeMessageType.Warning);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
throw new SmtpHelperException();
|
throw new SmtpHelperException();
|
||||||
|
@ -1,41 +1,8 @@
|
|||||||
using Milimoe.FunGame.Core.Api.Utility;
|
using Milimoe.FunGame.Core.Model;
|
||||||
using Milimoe.FunGame.Core.Model;
|
|
||||||
using MySql.Data.MySqlClient;
|
using MySql.Data.MySqlClient;
|
||||||
|
|
||||||
namespace Milimoe.FunGame.Server.Utility.DataUtility
|
namespace Milimoe.FunGame.Server.Utility.DataUtility
|
||||||
{
|
{
|
||||||
public class ConnectProperties
|
|
||||||
{
|
|
||||||
public static string Name { get; set; } = "";
|
|
||||||
public static string DataSource { get; set; } = "";
|
|
||||||
public static string Port { get; set; } = "";
|
|
||||||
public static string DataBase { get; set; } = "";
|
|
||||||
public static string User { get; set; } = "";
|
|
||||||
public static string Password { get; set; } = "";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 读取MySQL服务器配置文件
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string GetConnectProperties()
|
|
||||||
{
|
|
||||||
if (Name == "" && DataSource == "" && Port == "" && DataBase == "" && User == "" && Password == "")
|
|
||||||
{
|
|
||||||
if (INIHelper.ExistINIFile())
|
|
||||||
{
|
|
||||||
DataSource = INIHelper.ReadINI("MySQL", "DBServer");
|
|
||||||
Port = INIHelper.ReadINI("MySQL", "DBPort");
|
|
||||||
DataBase = INIHelper.ReadINI("MySQL", "DBName");
|
|
||||||
User = INIHelper.ReadINI("MySQL", "DBUser");
|
|
||||||
Password = INIHelper.ReadINI("MySQL", "DBPassword");
|
|
||||||
return "data source = " + DataSource + "; port = " + Port + "; database = " + DataBase + "; user = " + User + "; password = " + Password + "; charset = utf8mb4;";
|
|
||||||
}
|
|
||||||
else ServerHelper.Error(new MySQLConfigException());
|
|
||||||
}
|
|
||||||
return "data source = " + DataSource + "; port = " + Port + "; database = " + DataBase + "; user = " + User + "; password = " + Password + "; charset = utf8mb4;";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MySQLConnection
|
public class MySQLConnection
|
||||||
{
|
{
|
||||||
public MySqlConnection? Connection
|
public MySqlConnection? Connection
|
||||||
@ -87,15 +54,15 @@ namespace Milimoe.FunGame.Server.Utility.DataUtility
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string _GetConnection = ConnectProperties.GetConnectProperties();
|
string connectionString = ConnectProperties.GetConnectPropertiesForMySQL();
|
||||||
if (_GetConnection != null)
|
if (connectionString != null)
|
||||||
{
|
{
|
||||||
string[] DataSetting = _GetConnection.Split(";");
|
string[] strings = connectionString.Split(";");
|
||||||
if (DataSetting.Length > 1 && DataSetting[0].Length > 14 && DataSetting[1].Length > 8)
|
if (strings.Length > 1 && strings[0].Length > 14 && strings[1].Length > 8)
|
||||||
{
|
{
|
||||||
ServerHelper.WriteLine("Connect -> MySQL://" + DataSetting[0][14..] + ":" + DataSetting[1][8..]);
|
ServerHelper.WriteLine("Connect -> MySQL://" + strings[0][14..] + ":" + strings[1][8..]);
|
||||||
}
|
}
|
||||||
_Connection = new MySqlConnection(_GetConnection);
|
_Connection = new MySqlConnection(connectionString);
|
||||||
_Connection.Open();
|
_Connection.Open();
|
||||||
if (_Connection.State == System.Data.ConnectionState.Open)
|
if (_Connection.State == System.Data.ConnectionState.Open)
|
||||||
{
|
{
|
@ -1,8 +1,8 @@
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
using Milimoe.FunGame.Core.Api.Transmittal;
|
using Milimoe.FunGame.Core.Api.Transmittal;
|
||||||
|
using Milimoe.FunGame.Core.Interface.Base;
|
||||||
using Milimoe.FunGame.Core.Library.Constant;
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
using Milimoe.FunGame.Core.Model;
|
using Milimoe.FunGame.Core.Model;
|
||||||
using Milimoe.FunGame.Server.Model;
|
|
||||||
using Milimoe.FunGame.Server.Others;
|
using Milimoe.FunGame.Server.Others;
|
||||||
using Milimoe.FunGame.Server.Utility.DataUtility;
|
using Milimoe.FunGame.Server.Utility.DataUtility;
|
||||||
using MySql.Data.MySqlClient;
|
using MySql.Data.MySqlClient;
|
||||||
@ -12,6 +12,7 @@ namespace Milimoe.FunGame.Server.Utility
|
|||||||
public class MySQLHelper : SQLHelper
|
public class MySQLHelper : SQLHelper
|
||||||
{
|
{
|
||||||
public override FunGameInfo.FunGame FunGameType => Config.FunGameType;
|
public override FunGameInfo.FunGame FunGameType => Config.FunGameType;
|
||||||
|
public override SQLMode Mode => SQLMode.MySQL;
|
||||||
public override string Script { get; set; } = "";
|
public override string Script { get; set; } = "";
|
||||||
public override CommandType CommandType { get; set; } = CommandType.Text;
|
public override CommandType CommandType { get; set; } = CommandType.Text;
|
||||||
public override SQLResult Result => _Result;
|
public override SQLResult Result => _Result;
|
||||||
@ -27,7 +28,7 @@ namespace Milimoe.FunGame.Server.Utility
|
|||||||
private DataSet _DataSet = new();
|
private DataSet _DataSet = new();
|
||||||
private MySQLConnection? _Connection;
|
private MySQLConnection? _Connection;
|
||||||
private MySqlTransaction? _Transaction;
|
private MySqlTransaction? _Transaction;
|
||||||
private readonly ServerModel? ServerModel;
|
private readonly IServerModel? ServerModel;
|
||||||
private readonly bool _IsOneTime = false;
|
private readonly bool _IsOneTime = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -123,7 +124,7 @@ namespace Milimoe.FunGame.Server.Utility
|
|||||||
/// 创建为SocketModel服务的SQLHelper
|
/// 创建为SocketModel服务的SQLHelper
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ServerModel">SocketModel</param>
|
/// <param name="ServerModel">SocketModel</param>
|
||||||
public MySQLHelper(ServerModel ServerModel)
|
public MySQLHelper(IServerModel ServerModel)
|
||||||
{
|
{
|
||||||
this.ServerModel = ServerModel;
|
this.ServerModel = ServerModel;
|
||||||
Script = "";
|
Script = "";
|
229
FunGame.Server/Utilities/SQLite/SQLiteHelper.cs
Normal file
229
FunGame.Server/Utilities/SQLite/SQLiteHelper.cs
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
using System.Data;
|
||||||
|
using Microsoft.Data.Sqlite;
|
||||||
|
using Milimoe.FunGame.Core.Api.Transmittal;
|
||||||
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
|
using Milimoe.FunGame.Core.Model;
|
||||||
|
|
||||||
|
namespace Milimoe.FunGame.Server.Utility.DataUtility
|
||||||
|
{
|
||||||
|
public class SQLiteHelper : SQLHelper
|
||||||
|
{
|
||||||
|
public override FunGameInfo.FunGame FunGameType { get; } = FunGameInfo.FunGame.FunGame_Server;
|
||||||
|
public override SQLMode Mode { get; } = SQLMode.SQLite;
|
||||||
|
public override string Script { get; set; } = "";
|
||||||
|
public override CommandType CommandType { get; set; } = CommandType.Text;
|
||||||
|
public override SQLResult Result => _result;
|
||||||
|
public override SQLServerInfo ServerInfo => _serverInfo ?? SQLServerInfo.Create();
|
||||||
|
public override int UpdateRows => _updateRows;
|
||||||
|
public override DataSet DataSet => _dataSet;
|
||||||
|
|
||||||
|
private readonly SqliteConnection _connection;
|
||||||
|
private SqliteTransaction? _transaction;
|
||||||
|
private DataSet _dataSet = new();
|
||||||
|
private SQLResult _result = SQLResult.NotFound;
|
||||||
|
private readonly SQLServerInfo? _serverInfo;
|
||||||
|
private int _updateRows = 0;
|
||||||
|
private readonly string _connectionString = "";
|
||||||
|
|
||||||
|
public SQLiteHelper(string script = "", CommandType type = CommandType.Text)
|
||||||
|
{
|
||||||
|
Script = script;
|
||||||
|
CommandType = type;
|
||||||
|
_connectionString = ConnectProperties.GetConnectPropertiesForSQLite();
|
||||||
|
string[] strings = _connectionString.Split("=");
|
||||||
|
if (strings.Length > 1)
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine("Connect -> SQLite://" + strings[1]);
|
||||||
|
_serverInfo = SQLServerInfo.Create(database: strings[1]);
|
||||||
|
}
|
||||||
|
_connection = new SqliteConnection(_connectionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 打开数据库连接
|
||||||
|
/// </summary>
|
||||||
|
private void OpenConnection()
|
||||||
|
{
|
||||||
|
if (_connection.State != ConnectionState.Open)
|
||||||
|
{
|
||||||
|
_connection.Open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关闭数据库连接
|
||||||
|
/// </summary>
|
||||||
|
public override void Close()
|
||||||
|
{
|
||||||
|
_transaction?.Dispose();
|
||||||
|
if (_connection.State != ConnectionState.Closed)
|
||||||
|
{
|
||||||
|
_connection.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行一个命令
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override int Execute()
|
||||||
|
{
|
||||||
|
return Execute(Script);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行一个指定的命令
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="script"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="Exception"></exception>
|
||||||
|
public override int Execute(string script)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OpenConnection();
|
||||||
|
ServerHelper.WriteLine("SQLQuery -> " + script, InvokeMessageType.Api);
|
||||||
|
using SqliteCommand command = new(script, _connection);
|
||||||
|
command.CommandType = CommandType;
|
||||||
|
if (_transaction != null)
|
||||||
|
{
|
||||||
|
command.Transaction = _transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateRows = command.ExecuteNonQuery();
|
||||||
|
_result = SQLResult.Success;
|
||||||
|
Close();
|
||||||
|
return UpdateRows;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_result = SQLResult.Fail;
|
||||||
|
throw new Exception($"SQL execution failed: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询DataSet
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override DataSet ExecuteDataSet()
|
||||||
|
{
|
||||||
|
return ExecuteDataSet(Script);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行指定的命令查询DataSet
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="script"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="Exception"></exception>
|
||||||
|
public override DataSet ExecuteDataSet(string script)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OpenConnection();
|
||||||
|
ServerHelper.WriteLine("SQLQuery -> " + script, InvokeMessageType.Api);
|
||||||
|
using SqliteCommand command = new(script, _connection)
|
||||||
|
{
|
||||||
|
CommandType = CommandType
|
||||||
|
};
|
||||||
|
using SqliteDataReader reader = command.ExecuteReader();
|
||||||
|
_dataSet = new();
|
||||||
|
DataTable table = new();
|
||||||
|
table.Load(reader);
|
||||||
|
_dataSet.Tables.Add(table);
|
||||||
|
Close();
|
||||||
|
return _dataSet;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_result = SQLResult.Fail;
|
||||||
|
throw new Exception($"SQL execution failed: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行指定的命令查询DataRow
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override DataRow? ExecuteDataRow()
|
||||||
|
{
|
||||||
|
return ExecuteDataRow(Script);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行指定的命令查询DataRow
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="script"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override DataRow? ExecuteDataRow(string script)
|
||||||
|
{
|
||||||
|
OpenConnection();
|
||||||
|
ServerHelper.WriteLine("SQLQuery -> " + script, InvokeMessageType.Api);
|
||||||
|
DataSet dataSet = ExecuteDataSet(script);
|
||||||
|
if (dataSet.Tables.Count > 0 && dataSet.Tables[0].Rows.Count > 0)
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
return dataSet.Tables[0].Rows[0];
|
||||||
|
}
|
||||||
|
Close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建一个SQL事务
|
||||||
|
/// </summary>
|
||||||
|
public override void NewTransaction()
|
||||||
|
{
|
||||||
|
OpenConnection();
|
||||||
|
_transaction = _connection.BeginTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 提交事务
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="Exception"></exception>
|
||||||
|
public override void Commit()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_transaction?.Commit();
|
||||||
|
Close();
|
||||||
|
_result = SQLResult.Success;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_result = SQLResult.Fail;
|
||||||
|
throw new Exception($"Transaction commit failed: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 回滚事务
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="Exception"></exception>
|
||||||
|
public override void Rollback()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_transaction?.Rollback();
|
||||||
|
Close();
|
||||||
|
_result = SQLResult.Success;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_result = SQLResult.Fail;
|
||||||
|
throw new Exception($"Transaction rollback failed: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源清理
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_transaction?.Dispose();
|
||||||
|
_connection.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
79
FunGame.Server/app.manifest
Normal file
79
FunGame.Server/app.manifest
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="FunGameServer"/>
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<!-- UAC 清单选项
|
||||||
|
如果想要更改 Windows 用户帐户控制级别,请使用
|
||||||
|
以下节点之一替换 requestedExecutionLevel 节点。
|
||||||
|
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||||
|
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||||
|
|
||||||
|
指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
|
||||||
|
如果你的应用程序需要此虚拟化来实现向后兼容性,则移除此
|
||||||
|
元素。
|
||||||
|
-->
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的
|
||||||
|
Windows 版本的列表。取消评论适当的元素,
|
||||||
|
Windows 将自动选择最兼容的环境。 -->
|
||||||
|
|
||||||
|
<!-- Windows Vista -->
|
||||||
|
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 7 -->
|
||||||
|
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 8 -->
|
||||||
|
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 8.1 -->
|
||||||
|
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 10 -->
|
||||||
|
<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
|
||||||
|
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
|
||||||
|
<!-- 指示该应用程序可感知 DPI 且 Windows 在 DPI 较高时将不会对其进行
|
||||||
|
自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI,无需
|
||||||
|
选择加入。选择加入此设置的 Windows 窗体应用程序(面向 .NET Framework 4.6)还应
|
||||||
|
在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。
|
||||||
|
|
||||||
|
将应用程序设为感知长路径。请参阅 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
|
||||||
|
<!--
|
||||||
|
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<windowsSettings>
|
||||||
|
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||||
|
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
||||||
|
</windowsSettings>
|
||||||
|
</application>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->
|
||||||
|
<!--
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity
|
||||||
|
type="win32"
|
||||||
|
name="Microsoft.Windows.Common-Controls"
|
||||||
|
version="6.0.0.0"
|
||||||
|
processorArchitecture="*"
|
||||||
|
publicKeyToken="6595b64144ccf1df"
|
||||||
|
language="*"
|
||||||
|
/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
-->
|
||||||
|
|
||||||
|
</assembly>
|
49
FunGame.WebAPI/Architecture/RESTfulAPI.cs
Normal file
49
FunGame.WebAPI/Architecture/RESTfulAPI.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using Milimoe.FunGame.Core.Interface.Base;
|
||||||
|
using Milimoe.FunGame.Core.Library.Common.Network;
|
||||||
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
|
|
||||||
|
namespace Milimoe.FunGame.WebAPI.Architecture
|
||||||
|
{
|
||||||
|
public class RESTfulAPI(Guid token, string clientip, string clientname) : ISocketMessageProcessor
|
||||||
|
{
|
||||||
|
public Type InstanceType => typeof(RESTfulAPI);
|
||||||
|
|
||||||
|
public Guid Token { get; init; } = token;
|
||||||
|
|
||||||
|
public string ClientIP { get; init; } = clientip;
|
||||||
|
|
||||||
|
public string ClientName { get; init; } = clientname;
|
||||||
|
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CloseAsync()
|
||||||
|
{
|
||||||
|
await Task.Delay(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SocketObject[] Receive()
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SocketObject[]> ReceiveAsync()
|
||||||
|
{
|
||||||
|
await Task.Delay(100);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public SocketResult Send(SocketMessageType type, params object[] objs)
|
||||||
|
{
|
||||||
|
return SocketResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SocketResult> SendAsync(SocketMessageType type, params object[] objs)
|
||||||
|
{
|
||||||
|
await Task.Delay(100);
|
||||||
|
return SocketResult.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
FunGame.WebAPI/Architecture/RESTfulAPIListener.cs
Normal file
21
FunGame.WebAPI/Architecture/RESTfulAPIListener.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using Milimoe.FunGame.Core.Api.Utility;
|
||||||
|
using Milimoe.FunGame.Core.Interface.Base;
|
||||||
|
|
||||||
|
namespace Milimoe.FunGame.WebAPI.Architecture
|
||||||
|
{
|
||||||
|
public class RESTfulAPIListener : ISocketListener<RESTfulAPI>
|
||||||
|
{
|
||||||
|
public string Name => "RESTfulAPIListener";
|
||||||
|
|
||||||
|
public ConcurrentModelList<IServerModel> ClientList { get; } = [];
|
||||||
|
|
||||||
|
public ConcurrentModelList<IServerModel> UserList { get; } = [];
|
||||||
|
|
||||||
|
public List<string> BannedList { get; } = [];
|
||||||
|
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
FunGame.WebAPI/Architecture/WebAPIListener.cs
Normal file
22
FunGame.WebAPI/Architecture/WebAPIListener.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using Milimoe.FunGame.Core.Api.Utility;
|
||||||
|
using Milimoe.FunGame.Core.Interface.Base;
|
||||||
|
using Milimoe.FunGame.Core.Library.Common.Network;
|
||||||
|
|
||||||
|
namespace Milimoe.FunGame.WebAPI.Architecture
|
||||||
|
{
|
||||||
|
public class WebAPIListener : ISocketListener<ServerWebSocket>
|
||||||
|
{
|
||||||
|
public string Name => "WebAPIListener";
|
||||||
|
|
||||||
|
public ConcurrentModelList<IServerModel> ClientList { get; } = [];
|
||||||
|
|
||||||
|
public ConcurrentModelList<IServerModel> UserList { get; } = [];
|
||||||
|
|
||||||
|
public List<string> BannedList { get; } = [];
|
||||||
|
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
FunGame.WebAPI/Controllers/PostDataController.cs
Normal file
58
FunGame.WebAPI/Controllers/PostDataController.cs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Milimoe.FunGame.Core.Api.Utility;
|
||||||
|
using Milimoe.FunGame.Core.Library.Common.Network;
|
||||||
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
|
using Milimoe.FunGame.WebAPI.Architecture;
|
||||||
|
using Milimoe.FunGame.WebAPI.Models;
|
||||||
|
|
||||||
|
namespace Milimoe.FunGame.WebAPI.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("[controller]")]
|
||||||
|
[Authorize]
|
||||||
|
public class PostDataController(ILogger<PostDataController> logger) : ControllerBase
|
||||||
|
{
|
||||||
|
public static Dictionary<Guid, SocketObject> ResultDatas { get; } = [];
|
||||||
|
|
||||||
|
private readonly ILogger<PostDataController> _logger = logger;
|
||||||
|
|
||||||
|
[HttpPost("{username}", Name = "username")]
|
||||||
|
public async Task<IActionResult> Post(string username, [FromBody] SocketObject obj)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RESTfulAPIListener? apiListener = Singleton.Get<RESTfulAPIListener>();
|
||||||
|
if (apiListener != null && apiListener.UserList.ContainsKey(username))
|
||||||
|
{
|
||||||
|
RESTfulAPIModel model = (RESTfulAPIModel)apiListener.UserList[username];
|
||||||
|
if (model.LastRequestID == Guid.Empty)
|
||||||
|
{
|
||||||
|
Guid uid = Guid.NewGuid();
|
||||||
|
model.LastRequestID = uid;
|
||||||
|
await model.SocketMessageHandler(model.Socket, obj);
|
||||||
|
model.LastRequestID = Guid.Empty;
|
||||||
|
if (ResultDatas.TryGetValue(uid, out SocketObject list))
|
||||||
|
{
|
||||||
|
return Ok(list);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Ok(new SocketObject(SocketMessageType.System, model.Token, "请求未执行完毕,请等待!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogError(e, "Error during post data");
|
||||||
|
return StatusCode(500, "服务器内部错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
FunGame.WebAPI/Controllers/UserController.cs
Normal file
74
FunGame.WebAPI/Controllers/UserController.cs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Milimoe.FunGame.Core.Api.Utility;
|
||||||
|
using Milimoe.FunGame.Core.Library.SQLScript.Entity;
|
||||||
|
using Milimoe.FunGame.Server.Others;
|
||||||
|
using Milimoe.FunGame.Server.Utility;
|
||||||
|
using Milimoe.FunGame.WebAPI.Architecture;
|
||||||
|
using Milimoe.FunGame.WebAPI.Models;
|
||||||
|
using Milimoe.FunGame.WebAPI.Services;
|
||||||
|
|
||||||
|
namespace Milimoe.FunGame.WebAPI.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("[controller]")]
|
||||||
|
public class UserController(JWTService jwtTokenService) : ControllerBase
|
||||||
|
{
|
||||||
|
[HttpPost("login")]
|
||||||
|
public async Task<IActionResult> Login([FromBody] LoginModel loginModel)
|
||||||
|
{
|
||||||
|
string msg = "用户名或密码不正确。";
|
||||||
|
string clientip = HttpContext.Connection.RemoteIpAddress?.ToString() + ":" + HttpContext.Connection.RemotePort;
|
||||||
|
ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 通过 RESTful API 连接至服务器,正在登录 . . .", Core.Library.Constant.InvokeMessageType.Core);
|
||||||
|
string username = loginModel.Username;
|
||||||
|
string password = loginModel.Password;
|
||||||
|
RESTfulAPIListener? apiListener = Singleton.Get<RESTfulAPIListener>();
|
||||||
|
if (apiListener != null)
|
||||||
|
{
|
||||||
|
// 移除旧模型
|
||||||
|
if (apiListener.UserList.ContainsKey(username))
|
||||||
|
{
|
||||||
|
await apiListener.UserList[username].Send(Core.Library.Constant.SocketMessageType.Disconnect);
|
||||||
|
}
|
||||||
|
// 创建新模型
|
||||||
|
if (!apiListener.UserList.ContainsKey(username))
|
||||||
|
{
|
||||||
|
Config.ConnectingPlayerCount++;
|
||||||
|
RESTfulAPIModel model = new(apiListener, clientip);
|
||||||
|
model.SetClientName(clientip);
|
||||||
|
// 创建User对象
|
||||||
|
if (model.SQLHelper != null)
|
||||||
|
{
|
||||||
|
model.SQLHelper.ExecuteDataSet(UserQuery.Select_Users_LoginQuery(username, password));
|
||||||
|
Core.Entity.User user = Factory.GetUser(model.SQLHelper?.DataSet ?? new());
|
||||||
|
if (user.Id != 0)
|
||||||
|
{
|
||||||
|
model.User = user;
|
||||||
|
// 检查有没有重复登录的情况
|
||||||
|
await model.ForceLogOutDuplicateLogonUser();
|
||||||
|
// 添加至玩家列表
|
||||||
|
model.AddUser();
|
||||||
|
model.GetUsersCount();
|
||||||
|
string token = jwtTokenService.GenerateToken(username);
|
||||||
|
Config.ConnectingPlayerCount--;
|
||||||
|
return Ok(new { BearerToken = token, InternalToken = model.Token });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else msg = "服务器暂时无法处理登录请求。";
|
||||||
|
await model.Send(Core.Library.Constant.SocketMessageType.Disconnect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.ConnectingPlayerCount--;
|
||||||
|
ServerHelper.WriteLine(msg, Core.Library.Constant.InvokeMessageType.Core);
|
||||||
|
return Unauthorized(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("refresh")]
|
||||||
|
[Authorize]
|
||||||
|
public IActionResult Refresh([FromBody] LoginModel request)
|
||||||
|
{
|
||||||
|
return Ok(jwtTokenService.GenerateToken(request.Username));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
FunGame.WebAPI/FunGame.WebAPI.csproj
Normal file
48
FunGame.WebAPI/FunGame.WebAPI.csproj
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<RootNamespace>Milimoe.$(MSBuildProjectName.Replace(" ", "_"))</RootNamespace>
|
||||||
|
<BaseOutputPath>..\bin\</BaseOutputPath>
|
||||||
|
<AssemblyName>FunGameWebAPI</AssemblyName>
|
||||||
|
<ApplicationIcon>Images\logo.ico</ApplicationIcon>
|
||||||
|
<Authors>Milimoe</Authors>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<DebugType>embedded</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<DebugType>embedded</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Images\logo.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\FunGame.Server\FunGame.Server.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="FunGame.Core">
|
||||||
|
<HintPath>..\..\FunGame.Core\bin\Debug\net8.0\FunGame.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="Images\logo.ico">
|
||||||
|
<PackagePath>\</PackagePath>
|
||||||
|
<Pack>True</Pack>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
6
FunGame.WebAPI/FunGame.WebAPI.http
Normal file
6
FunGame.WebAPI/FunGame.WebAPI.http
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
@FunGame.WebAPI_HostAddress = http://localhost:5117
|
||||||
|
|
||||||
|
GET {{FunGame.WebAPI_HostAddress}}/weatherforecast/
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
###
|
BIN
FunGame.WebAPI/Images/logo.ico
Normal file
BIN
FunGame.WebAPI/Images/logo.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
8
FunGame.WebAPI/Models/LoginModel.cs
Normal file
8
FunGame.WebAPI/Models/LoginModel.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Milimoe.FunGame.WebAPI.Models
|
||||||
|
{
|
||||||
|
public class LoginModel(string username, string password)
|
||||||
|
{
|
||||||
|
public string Username { get; set; } = username;
|
||||||
|
public string Password { get; set; } = password;
|
||||||
|
}
|
||||||
|
}
|
72
FunGame.WebAPI/Models/RESTfulAPIModel.cs
Normal file
72
FunGame.WebAPI/Models/RESTfulAPIModel.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
using Milimoe.FunGame.Core.Interface.Base;
|
||||||
|
using Milimoe.FunGame.Core.Library.Common.Network;
|
||||||
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
|
using Milimoe.FunGame.Server.Model;
|
||||||
|
using Milimoe.FunGame.Server.Utility;
|
||||||
|
using Milimoe.FunGame.WebAPI.Architecture;
|
||||||
|
using Milimoe.FunGame.WebAPI.Controllers;
|
||||||
|
|
||||||
|
namespace Milimoe.FunGame.WebAPI.Models
|
||||||
|
{
|
||||||
|
public class RESTfulAPIModel(ISocketListener<RESTfulAPI> server, string clientip) : ServerModel<RESTfulAPI>(server, new RESTfulAPI(Guid.NewGuid(), clientip, clientip), false)
|
||||||
|
{
|
||||||
|
public Guid LastRequestID { get; set; } = Guid.Empty;
|
||||||
|
public List<SocketObject> ToBeSent { get; set; } = [];
|
||||||
|
|
||||||
|
public override async Task<bool> Send(SocketMessageType type, params object[] objs)
|
||||||
|
{
|
||||||
|
if (type == SocketMessageType.Disconnect || type == SocketMessageType.ForceLogout)
|
||||||
|
{
|
||||||
|
RemoveUser();
|
||||||
|
await Close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (type != SocketMessageType.HeartBeat)
|
||||||
|
{
|
||||||
|
SocketObject obj = new(type, Token, objs);
|
||||||
|
if (LastRequestID != Guid.Empty)
|
||||||
|
{
|
||||||
|
return PostDataController.ResultDatas.TryAdd(LastRequestID, obj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ToBeSent.Add(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<bool> SocketMessageHandler(ISocketMessageProcessor socket, SocketObject obj)
|
||||||
|
{
|
||||||
|
// 读取收到的消息
|
||||||
|
SocketMessageType type = obj.SocketType;
|
||||||
|
Guid token = obj.Token;
|
||||||
|
string msg = "";
|
||||||
|
|
||||||
|
// 验证Token
|
||||||
|
if (type != SocketMessageType.HeartBeat && token != socket.Token)
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine(GetClientName() + " 使用了非法方式传输消息,服务器拒绝回应 -> [" + SocketSet.GetTypeString(type) + "]");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == SocketMessageType.DataRequest)
|
||||||
|
{
|
||||||
|
return await DataRequestHandler(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == SocketMessageType.GamingRequest)
|
||||||
|
{
|
||||||
|
return await GamingRequestHandler(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == SocketMessageType.Gaming)
|
||||||
|
{
|
||||||
|
return await GamingMessageHandler(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await Send(type, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
267
FunGame.WebAPI/Program.cs
Normal file
267
FunGame.WebAPI/Program.cs
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Text.Unicode;
|
||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
using Microsoft.AspNetCore.Diagnostics;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Milimoe.FunGame.Core.Api.Utility;
|
||||||
|
using Milimoe.FunGame.Core.Library.Common.JsonConverter;
|
||||||
|
using Milimoe.FunGame.Core.Library.Common.Network;
|
||||||
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
|
using Milimoe.FunGame.Server.Controller;
|
||||||
|
using Milimoe.FunGame.Server.Model;
|
||||||
|
using Milimoe.FunGame.Server.Others;
|
||||||
|
using Milimoe.FunGame.Server.Utility;
|
||||||
|
using Milimoe.FunGame.WebAPI.Architecture;
|
||||||
|
using Milimoe.FunGame.WebAPI.Services;
|
||||||
|
|
||||||
|
WebAPIListener listener = new();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.Title = Config.ServerName;
|
||||||
|
Console.WriteLine(FunGameInfo.GetInfo(Config.FunGameType));
|
||||||
|
|
||||||
|
ServerHelper.WriteLine("正在读取配置文件并初始化服务 . . .");
|
||||||
|
// 初始化命令菜单
|
||||||
|
ServerHelper.InitOrderList();
|
||||||
|
|
||||||
|
// 读取游戏模组
|
||||||
|
if (!Config.GetGameModuleList())
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine("服务器似乎未安装任何游戏模组,请检查是否正确安装它们。");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否存在配置文件
|
||||||
|
if (!INIHelper.ExistINIFile())
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine("未检测到配置文件,将自动创建配置文件 . . .");
|
||||||
|
INIHelper.Init(Config.FunGameType);
|
||||||
|
ServerHelper.WriteLine("配置文件FunGame.ini创建成功,请修改该配置文件,然后重启服务器。");
|
||||||
|
Console.ReadKey();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ServerHelper.GetServerSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建单例
|
||||||
|
RESTfulAPIListener apiListener = new();
|
||||||
|
Singleton.Add(apiListener);
|
||||||
|
|
||||||
|
ServerHelper.WriteLine("请输入 help 来获取帮助,输入 quit 关闭服务器。");
|
||||||
|
|
||||||
|
// 创建全局SQLHelper
|
||||||
|
Config.InitSQLHelper();
|
||||||
|
|
||||||
|
ServerHelper.WriteLine("正在启动 Web API 监听 . . .");
|
||||||
|
|
||||||
|
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
|
builder.Services.AddControllers().AddJsonOptions(options =>
|
||||||
|
{
|
||||||
|
options.JsonSerializerOptions.WriteIndented = true;
|
||||||
|
options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
|
||||||
|
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new DataTableConverter());
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new DataSetConverter());
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new UserConverter());
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new RoomConverter());
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new CharacterConverter());
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new MagicResistanceConverter());
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new EquipSlotConverter());
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new SkillConverter());
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new EffectConverter());
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new ItemConverter());
|
||||||
|
});
|
||||||
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen(options =>
|
||||||
|
{
|
||||||
|
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Name = "Authorization",
|
||||||
|
Type = SecuritySchemeType.Http,
|
||||||
|
Scheme = "Bearer",
|
||||||
|
BearerFormat = "JWT",
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
Description = "输入 Auth 返回的 BearerToken",
|
||||||
|
});
|
||||||
|
options.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||||
|
{
|
||||||
|
{
|
||||||
|
new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Reference = new OpenApiReference
|
||||||
|
{
|
||||||
|
Type = ReferenceType.SecurityScheme,
|
||||||
|
Id = "Bearer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Array.Empty<string>()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// 添加 CORS 服务
|
||||||
|
builder.Services.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy("AllowSpecificOrigin", policy =>
|
||||||
|
{
|
||||||
|
policy.AllowAnyOrigin()
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.AllowAnyMethod();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// 添加 JWT 认证
|
||||||
|
builder.Services.AddScoped<JWTService>();
|
||||||
|
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
|
||||||
|
{
|
||||||
|
options.TokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuer = true,
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidateLifetime = true,
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
ValidIssuer = builder.Configuration["Jwt:Issuer"],
|
||||||
|
ValidAudience = builder.Configuration["Jwt:Audience"],
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"] ?? "undefined"))
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
WebApplication app = builder.Build();
|
||||||
|
|
||||||
|
// 启用 CORS
|
||||||
|
app.UseCors("AllowSpecificOrigin");
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
if (app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
|
app.UseExceptionHandler(errorApp =>
|
||||||
|
{
|
||||||
|
errorApp.Run(async context =>
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 500;
|
||||||
|
context.Response.ContentType = "application/json";
|
||||||
|
IExceptionHandlerFeature? contextFeature = context.Features.Get<IExceptionHandlerFeature>();
|
||||||
|
if (contextFeature != null)
|
||||||
|
{
|
||||||
|
await context.Response.WriteAsync(new
|
||||||
|
{
|
||||||
|
context.Response.StatusCode,
|
||||||
|
Message = "Internal Server Error.",
|
||||||
|
Detailed = contextFeature.Error.Message
|
||||||
|
}.ToString() ?? "");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 启用 WebSockets 中间件
|
||||||
|
WebSocketOptions webSocketOptions = new()
|
||||||
|
{
|
||||||
|
KeepAliveInterval = TimeSpan.FromMinutes(2) // 设置 WebSocket 的保活间隔
|
||||||
|
};
|
||||||
|
app.UseWebSockets(webSocketOptions);
|
||||||
|
|
||||||
|
// 路由到 WebSocket 处理器
|
||||||
|
app.Map("/ws", WebSocketConnectionHandler);
|
||||||
|
|
||||||
|
// 开始监听连接
|
||||||
|
listener.BannedList.AddRange(Config.ServerBannedList);
|
||||||
|
|
||||||
|
if (Config.ServerNotice != "")
|
||||||
|
Console.WriteLine("\n\n********** 服务器公告 **********\n\n" + Config.ServerNotice + "\n");
|
||||||
|
else
|
||||||
|
Console.WriteLine("无法读取服务器公告");
|
||||||
|
|
||||||
|
Task order = Task.Factory.StartNew(GetConsoleOrder);
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ServerHelper.Error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task GetConsoleOrder()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
string order = Console.ReadLine() ?? "";
|
||||||
|
ServerHelper.Type();
|
||||||
|
if (order != "")
|
||||||
|
{
|
||||||
|
order = order.ToLower();
|
||||||
|
switch (order)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
await ConsoleModel.Order(listener, order);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task WebSocketConnectionHandler(HttpContext context)
|
||||||
|
{
|
||||||
|
string clientip = "";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (context.WebSockets.IsWebSocketRequest)
|
||||||
|
{
|
||||||
|
WebSocket instance = await context.WebSockets.AcceptWebSocketAsync();
|
||||||
|
clientip = context.Connection.RemoteIpAddress?.ToString() + ":" + context.Connection.RemotePort;
|
||||||
|
|
||||||
|
Guid token = Guid.NewGuid();
|
||||||
|
ServerWebSocket socket = new(listener, instance, clientip, clientip, token);
|
||||||
|
Config.ConnectingPlayerCount++;
|
||||||
|
bool isConnected = false;
|
||||||
|
bool isDebugMode = false;
|
||||||
|
|
||||||
|
// 开始处理客户端连接请求
|
||||||
|
IEnumerable<SocketObject> objs = [];
|
||||||
|
while (!objs.Any(o => o.SocketType == SocketMessageType.Connect))
|
||||||
|
{
|
||||||
|
objs = objs.Union(await socket.ReceiveAsync());
|
||||||
|
}
|
||||||
|
(isConnected, isDebugMode) = await ConnectController.Connect(listener, socket, token, clientip, objs.Where(o => o.SocketType == SocketMessageType.Connect));
|
||||||
|
if (isConnected)
|
||||||
|
{
|
||||||
|
ServerModel<ServerWebSocket> ClientModel = new(listener, socket, isDebugMode);
|
||||||
|
ClientModel.SetClientName(clientip);
|
||||||
|
await ClientModel.Start();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 连接失败。", InvokeMessageType.Core);
|
||||||
|
await socket.CloseAsync();
|
||||||
|
}
|
||||||
|
Config.ConnectingPlayerCount--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (--Config.ConnectingPlayerCount < 0) Config.ConnectingPlayerCount = 0;
|
||||||
|
ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 中断连接!", InvokeMessageType.Core);
|
||||||
|
ServerHelper.Error(e);
|
||||||
|
}
|
||||||
|
}
|
41
FunGame.WebAPI/Properties/launchSettings.json
Normal file
41
FunGame.WebAPI/Properties/launchSettings.json
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:45590",
|
||||||
|
"sslPort": 44356
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"applicationUrl": "http://localhost:5117",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"applicationUrl": "https://localhost:7162;http://localhost:5117",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
FunGame.WebAPI/Services/JWTService.cs
Normal file
33
FunGame.WebAPI/Services/JWTService.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
|
|
||||||
|
namespace Milimoe.FunGame.WebAPI.Services
|
||||||
|
{
|
||||||
|
public class JWTService(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
public string GenerateToken(string username)
|
||||||
|
{
|
||||||
|
// 创建一个包含用户信息的声明
|
||||||
|
Claim[] claims = [
|
||||||
|
new Claim(JwtRegisteredClaimNames.Sub, username),
|
||||||
|
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
|
||||||
|
];
|
||||||
|
|
||||||
|
// 获取密钥和发行者
|
||||||
|
SymmetricSecurityKey key = new(General.DefaultEncoding.GetBytes(configuration["Jwt:Key"] ?? "undefined"));
|
||||||
|
SigningCredentials creds = new(key, SecurityAlgorithms.HmacSha256);
|
||||||
|
|
||||||
|
JwtSecurityToken token = new(
|
||||||
|
issuer: configuration["Jwt:Issuer"],
|
||||||
|
audience: configuration["Jwt:Audience"],
|
||||||
|
claims: claims,
|
||||||
|
expires: DateTime.Now.AddMinutes(30), // 设置过期时间
|
||||||
|
signingCredentials: creds
|
||||||
|
);
|
||||||
|
|
||||||
|
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
FunGame.WebAPI/appsettings.Development.json
Normal file
8
FunGame.WebAPI/appsettings.Development.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
FunGame.WebAPI/appsettings.json
Normal file
19
FunGame.WebAPI/appsettings.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*",
|
||||||
|
"Kestrel": {
|
||||||
|
"EndpointDefaults": {
|
||||||
|
"Protocols": "Http1AndHttp2AndHttp3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Jwt": {
|
||||||
|
"Key": "166afec8ff6e0c3a647c7230294ea10be39d5d217f37aa5195f795017403da730ce6313790335b4975d7387c14aaa06c52d1cd90b5ef47d1831b6d7d524a12bf",
|
||||||
|
"Issuer": "FunGame",
|
||||||
|
"Audience": "FunGame Web API"
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunGame.Server", "FunGame.S
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunGame.Implement", "FunGame.Implement\FunGame.Implement.csproj", "{F5BACA36-3DE2-450A-8518-E5DC29991875}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunGame.Implement", "FunGame.Implement\FunGame.Implement.csproj", "{F5BACA36-3DE2-450A-8518-E5DC29991875}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunGame.WebAPI", "FunGame.WebAPI\FunGame.WebAPI.csproj", "{F4C6F3E8-84EA-49B4-99B7-A886C56C44F2}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -21,6 +23,10 @@ Global
|
|||||||
{F5BACA36-3DE2-450A-8518-E5DC29991875}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{F5BACA36-3DE2-450A-8518-E5DC29991875}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{F5BACA36-3DE2-450A-8518-E5DC29991875}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{F5BACA36-3DE2-450A-8518-E5DC29991875}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{F5BACA36-3DE2-450A-8518-E5DC29991875}.Release|Any CPU.Build.0 = Release|Any CPU
|
{F5BACA36-3DE2-450A-8518-E5DC29991875}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{F4C6F3E8-84EA-49B4-99B7-A886C56C44F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F4C6F3E8-84EA-49B4-99B7-A886C56C44F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F4C6F3E8-84EA-49B4-99B7-A886C56C44F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F4C6F3E8-84EA-49B4-99B7-A886C56C44F2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
Loading…
x
Reference in New Issue
Block a user