2023-12-09 17:03:37 +08:00

632 lines
23 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections;
using System.Data;
using Milimoe.FunGame.Core.Api.Transmittal;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Common.Network;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Library.SQLScript.Entity;
using Milimoe.FunGame.Server.Controller;
using Milimoe.FunGame.Server.Others;
using Milimoe.FunGame.Server.Utility;
namespace Milimoe.FunGame.Server.Model
{
public class ServerModel : IServerModel
{
/**
* Public
*/
public bool Running => _Running;
public ClientSocket? Socket => _Socket;
public string ClientName => _ClientName;
public User User => _User;
public Room Room
{
get => _Room;
set => _Room = value;
}
public MySQLHelper? SQLHelper { get; }
public MailSender? MailSender { get; }
public bool IsDebugMode { get; }
/**
* Private
*/
private GameModeServer? NowGamingServer = null;
private ClientSocket? _Socket = null;
private bool _Running = false;
private User _User = General.UnknownUserInstance;
private Room _Room = General.HallInstance;
private string _ClientName = "";
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;
_Socket = socket;
_Running = running;
Token = socket.Token;
this.IsDebugMode = isDebugMode;
if (Config.SQLMode) SQLHelper = new(this);
MailSender = SmtpHelper.GetMailSender();
DataRequestController = new(this);
}
public bool Read(ClientSocket socket)
{
// 接收客户端消息
try
{
// 禁止GameModeServer调用
if ((IServerModel)this is GameModeServer) throw new NotSupportedException("请勿在GameModeServer类中调用此方法");
SocketObject SocketObject = socket.Receive();
SocketMessageType type = SocketObject.SocketType;
Guid token = SocketObject.Token;
object[] args = SocketObject.Parameters;
string msg = "";
// 验证Token
if (type != SocketMessageType.HeartBeat && token != Token)
{
ServerHelper.WriteLine(GetClientName() + " 使用了非法方式传输消息,服务器拒绝回应 -> [" + ServerSocket.GetTypeString(type) + "] ");
return false;
}
if (type == SocketMessageType.EndGame)
{
NowGamingServer = null;
return true;
}
if (type == SocketMessageType.DataRequest)
{
return DataRequestHandler(socket, SocketObject);
}
if (type == SocketMessageType.Gaming)
{
return GamingMessageHandler(socket, SocketObject);
}
if (type == SocketMessageType.HeartBeat)
{
return HeartBeat(socket);
}
switch (type)
{
case SocketMessageType.Disconnect:
ServerHelper.WriteLine("[" + ServerSocket.GetTypeString(SocketMessageType.DataRequest) + "] " + GetClientName() + " -> Disconnect", InvokeMessageType.Core);
msg = "你已成功断开与服务器的连接: " + Config.ServerName + "。 ";
break;
}
return Send(socket, type, msg);
}
catch (Exception e)
{
ServerHelper.WriteLine(GetClientName() + " 没有回应。");
ServerHelper.Error(e);
return false;
}
}
public bool DataRequestHandler(ClientSocket socket, SocketObject SocketObject)
{
if (SQLHelper != null)
{
Hashtable result = [];
DataRequestType type = DataRequestType.UnKnown;
if (SocketObject.Parameters.Length > 0)
{
try
{
type = SocketObject.GetParam<DataRequestType>(0);
Hashtable data = SocketObject.GetParam<Hashtable>(1) ?? [];
SQLHelper.NewTransaction();
result = DataRequestController.GetResultData(type, data);
SQLHelper.Commit();
}
catch (Exception e)
{
ServerHelper.Error(e);
SQLHelper.Rollback();
return Send(socket, SocketMessageType.DataRequest, type, result);
}
}
return Send(socket, SocketMessageType.DataRequest, type, result);
}
return false;
}
public bool GamingMessageHandler(ClientSocket socket, SocketObject SocketObject)
{
if (NowGamingServer != null)
{
Hashtable result = [];
GamingType type = GamingType.None;
if (SocketObject.Parameters.Length > 0)
{
try
{
type = SocketObject.GetParam<GamingType>(0);
Hashtable data = SocketObject.GetParam<Hashtable>(1) ?? [];
result = NowGamingServer.GamingMessageHandler(UserName, type, data);
}
catch (Exception e)
{
ServerHelper.Error(e);
return Send(socket, SocketMessageType.Gaming, type, result);
}
}
return Send(socket, SocketMessageType.Gaming, type, result);
}
return false;
}
public bool Send(ClientSocket socket, SocketMessageType type, params object[] objs)
{
// 发送消息给客户端
try
{
if (socket.Send(type, objs) == SocketResult.Success)
{
switch (type)
{
case SocketMessageType.ForceLogout:
RemoveUser();
break;
case SocketMessageType.Disconnect:
RemoveUser();
Close();
break;
case SocketMessageType.Chat:
return true;
}
object obj = objs[0];
if (obj.GetType() == typeof(string) && (string)obj != "")
ServerHelper.WriteLine("[" + ServerSocket.GetTypeString(type) + "] " + GetClientName() + " <- " + obj, InvokeMessageType.Core);
return true;
}
throw new CanNotSendToClientException();
}
catch (Exception e)
{
ServerHelper.WriteLine(GetClientName() + " 没有回应。");
ServerHelper.Error(e);
return false;
}
}
public void Start()
{
if ((IServerModel)this is GameModeServer) throw new NotSupportedException("请勿在GameModeServer类中调用此方法"); // 禁止GameModeServer调用
Task StreamReader = Task.Factory.StartNew(CreateStreamReader);
Task PeriodicalQuerier = Task.Factory.StartNew(CreatePeriodicalQuerier);
}
public void SetClientName(string ClientName)
{
_ClientName = ClientName;
// 添加客户端到列表中
Server.AddClient(_ClientName, this);
Config.OnlinePlayerCount++;
GetUsersCount();
}
public string GetClientName()
{
return ServerHelper.MakeClientName(ClientName, User);
}
public void PreLogin(DataSet dsuser, string username, Guid checkloginkey)
{
DsUser = dsuser;
UserName = username;
CheckLoginKey = checkloginkey;
}
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)
{
bool result;
Config.RoomList.CancelReady(roomid, User);
Config.RoomList.QuitRoom(roomid, User);
Room Room = Config.RoomList[roomid] ?? General.HallInstance;
// 是否是房主
if (isMaster)
{
List<User> users = Config.RoomList.GetPlayerList(roomid);
if (users.Count > 0) // 如果此时房间还有人,更新房主
{
User NewMaster = users[0];
Room.RoomMaster = NewMaster;
SQLHelper?.Execute(RoomQuery.Update_QuitRoom(roomid, User.Id, NewMaster.Id));
this.Room = General.HallInstance;
UpdateRoomMaster(Room, true);
result = true;
}
else // 没人了就解散房间
{
Config.RoomList.RemoveRoom(roomid);
SQLHelper?.Execute(RoomQuery.Delete_QuitRoom(roomid, User.Id));
this.Room = General.HallInstance;
ServerHelper.WriteLine("[ " + GetClientName() + " ] 解散了房间 " + roomid);
result = true;
}
}
// 不是房主直接退出房间
else
{
this.Room = General.HallInstance;
UpdateRoomMaster(Room);
result = true;
}
return result;
}
public void Kick(string msg, string clientname = "")
{
// 将客户端踢出服务器
ServerModel serverTask = (ServerModel)Server.GetClient(clientname == "" ? ClientName : clientname);
if (serverTask.Socket != null)
{
serverTask.Room = General.HallInstance;
foreach (Room room in Config.RoomList.Cast<Room>())
{
QuitRoom(room.Roomid, room.RoomMaster.Id == User.Id);
}
RemoveUser();
serverTask.Send(serverTask.Socket, SocketMessageType.Disconnect, msg);
}
Close();
}
public void Chat(string msg)
{
ServerHelper.WriteLine(msg);
foreach (ServerModel Client in Server.ClientList.Cast<ServerModel>())
{
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)
{
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.GameModeLoader != null && Config.GameModeLoader.ServerModes.ContainsKey(room.GameMode))
{
NowGamingServer = Config.GameModeLoader.GetServerMode(room.GameMode);
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.GameMode, 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(string roomtype_string, User user)
{
IsMatching = true;
ServerHelper.WriteLine(GetClientName() + " 开始匹配。类型:" + roomtype_string);
TaskUtility.NewTask(async () =>
{
if (IsMatching)
{
RoomType roomtype = RoomSet.GetRoomType(roomtype_string);
Room room = await MatchingRoom(roomtype, 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)
{
string user = User.Username;
if (Server.ContainsUser(user))
{
ServerHelper.WriteLine("OnlinePlayers: 玩家 " + user + " 重复登录!");
ForceLogOut("您的账号在别处登录,已强制下线。");
}
}
}
private bool AddUser()
{
if (User.Id != 0 && this != null)
{
Server.AddUser(User.Username, this);
UserName = User.Username;
ServerHelper.WriteLine("OnlinePlayers: 玩家 " + User.Username + " 已添加");
return true;
}
return false;
}
private bool RemoveUser()
{
if (User.Id != 0 && this != null)
{
LogoutTime = DateTime.Now.Ticks;
int TotalMinutes = Convert.ToInt32((new DateTime(LogoutTime) - new DateTime(LoginTime)).TotalMinutes);
SQLHelper?.Execute(UserQuery.Update_GameTime(User.Username, TotalMinutes));
if (SQLHelper?.Result == SQLResult.Success)
{
ServerHelper.WriteLine("OnlinePlayers: 玩家 " + User.Username + " 本次已游玩" + TotalMinutes + "分钟");
}
else ServerHelper.WriteLine("OnlinePlayers: 无法更新玩家 " + User.Username + " 的游戏时长");
if (Server.RemoveUser(User.Username))
{
ServerHelper.WriteLine("OnlinePlayers: 玩家 " + User.Username + " 已移除");
_User = General.UnknownUserInstance;
return true;
}
else ServerHelper.WriteLine("OnlinePlayers: 移除玩家 " + User.Username + " 失败");
}
return false;
}
private void GetUsersCount()
{
ServerHelper.WriteLine($"目前在线客户端数量: {Server.ClientCount}(已登录的玩家数量:{Server.UserCount}");
}
private void CreateStreamReader()
{
Thread.Sleep(100);
ServerHelper.WriteLine("Creating: StreamReader -> " + GetClientName() + " ...OK");
while (Running)
{
if (Socket != null)
{
if (!Read(Socket))
{
FailedTimes++;
if (FailedTimes >= Config.MaxConnectionFaileds)
{
RemoveUser();
Close();
ServerHelper.WriteLine(GetClientName() + " Error -> Too Many Faileds.");
ServerHelper.WriteLine(GetClientName() + " Close -> StreamReader is Closed.");
break;
}
}
else if (FailedTimes - 1 >= 0) FailedTimes--;
}
else
{
RemoveUser();
Close();
ServerHelper.WriteLine(GetClientName() + " Error -> Socket is Closed.");
ServerHelper.WriteLine(GetClientName() + " Close -> StringStream is Closed.");
break;
}
}
}
private void CreatePeriodicalQuerier()
{
Thread.Sleep(100);
ServerHelper.WriteLine("Creating: PeriodicalQuerier -> " + GetClientName() + " ...OK");
while (Running)
{
// 每两小时触发一次SQL服务器的心跳查询防止SQL服务器掉线
Thread.Sleep(2 * 1000 * 3600);
SQLHelper?.ExecuteDataSet(UserQuery.Select_IsExistUsername(UserName));
}
}
private void Close()
{
try
{
SQLHelper?.Close();
MailSender?.Dispose();
Socket?.Close();
_Socket = null;
_Running = false;
Server.RemoveClient(ClientName);
Config.OnlinePlayerCount--;
GetUsersCount();
}
catch (Exception e)
{
ServerHelper.Error(e);
}
}
}
}