diff --git a/ConnectionMessage.cs b/ConnectionMessage.cs index 9f397d6..3edf1d3 100644 --- a/ConnectionMessage.cs +++ b/ConnectionMessage.cs @@ -1,5 +1,5 @@ using System.Text.RegularExpressions; - +using System; namespace Phi_MGUS { public static class ConnectionMessage @@ -45,20 +45,50 @@ public class JoinServerSuccess : Message } /// - /// Room add failed message | 房间新建失败消息 + /// Room new failed message | 房间新建失败消息 /// - public class AddRoomFailed : Message + public class NewRoomFailed : Message { - public new readonly string action = "addRoomFailed"; + public new readonly string action = "newRoomFailed"; public string reason = "unknown"; } /// - /// Room success message | 房间新建成功消息 + /// Room new success message | 房间新建成功消息 + /// + public class NewRoomSuccess : Message + { + public new readonly string action = "newRoomSuccess"; + } + /// + /// Room join failed message | 房间加入成功消息 + /// + public class JoinRoomFailed : Message + { + public new readonly string action = "joinRoomFaild"; + public string reason = "unknown"; + } + /// + /// Room join success message | 房间加入成功消息 + /// + public class JoinRoomSuccess : Message + { + public new readonly string action = "joinRoomSuccess"; + } + /// + /// Leave room failed message | 离开房间失败消息 /// - public class AddRoomSuccess : Message + public class LeaveRoomFailed : Message { - public new readonly string action = "addRoomSuccess"; + public new readonly string action = "leaveRoomFailed"; + public string reason = "unknown"; + } + /// + /// Leave room success message | 离开房间成功消息 + /// + public class LeaveRoomSuccess : Message + { + public new readonly string action = "leaveRoomSuccess"; } } @@ -107,7 +137,7 @@ public class NewRoom : Message public Data data = new Data { //RoomID is a random string, length is 16 | 房间ID是随机字符串,长度为16 - roomID = Guid.NewGuid().ToString().Substring(0, 16) + roomID = Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16) }; public class Data @@ -135,6 +165,25 @@ public string roomID }// Only English or numbers can be used, and cannot exceed 32 digits | 只能使用英文或数字,且不超过32位 } + public NewRoom(int? maxUser = 8, string roomID = null) + { + if (roomID == null) + { + roomID = Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16); + } + else + { + data.roomID = roomID; + } + if (maxUser == null) + { + maxUser = 8; + } + else + { + data.maxUser = maxUser.Value; + } + } } /// @@ -149,6 +198,11 @@ public class Data { public string roomID = ""; } + + public JoinRoom(string roomID) + { + data.roomID = roomID; + } } /// /// Leave room | 离开房间 diff --git a/GameManager.cs b/GameManager.cs index aba3d5c..b462013 100644 --- a/GameManager.cs +++ b/GameManager.cs @@ -10,22 +10,51 @@ public static class GameManager /// public static class RoomManager { - private static readonly List roomList = new(); + public static readonly List roomList = new(); public static void AddRoom(User user, string roomID) { roomList.Add(new Room(user, roomID)); + user.userStatus = User.Status.InRoom; } - public static void RemoveRoom(Room instance) + /// + /// Dissolve the room | 解散房间 + /// + /// 房间 + public static void RemoveRoom(Room room) + { + for(var i = roomList.Count - 1; i >= 0; i--) + { + if(roomList[i] == room) + { + roomList.RemoveAt(i); + } + } + } + /// + /// Remove room by roomID | 根据房间ID删除房间 + /// + /// 房间ID + public static void RemoveRoom(string roomID) { - for(int i = roomList.Count - 1; i >= 0; i--) + for(var i = roomList.Count - 1; i >= 0; i--) { - if(roomList[i] == instance) + if(roomList[i].roomID == roomID) { roomList.RemoveAt(i); } } } + + /// + /// Get room by roomID | 根据房间ID获取房间 + /// + /// 房间ID + /// Room | 房间 + public static Room? GetRoom(string roomID) + { + return roomList.FirstOrDefault(x => x.roomID == roomID); + } } /// @@ -48,7 +77,7 @@ public static void Drop(IWebSocketConnection socket) /// /// Remove user | 移除用户 /// - /// + /// 用户对应Socket public static void RemoveUser(IWebSocketConnection socket) { for(int i = userList.Count - 1; i >= 0; i--) @@ -73,7 +102,6 @@ public static User GetUser(IWebSocketConnection socket) { return userList.First(x => x.userSocket == socket); } - } /// @@ -87,22 +115,24 @@ public class Room /// /// Create room | 创建房间 /// - /// - /// + /// 房间所有者 + /// 房间ID /// Illegal room ID | 非法房间ID public Room(User owner, string roomID) { if (roomID.Length > 32) { - throw new ArgumentException("RoomIdentifier cannot exceed 32 digits."); + throw new ArgumentException("RoomIdentifier cannot exceed 32 digits."); // 房间ID长度不能超过32位 } - if (!Regex.IsMatch(this.roomID, @"^[a-zA-Z0-9]+$")) + if (!Regex.IsMatch(roomID, @"^[a-zA-Z0-9]+$")) { - throw new ArgumentException("RoomIdentifier can only use pure English or numbers."); + throw new ArgumentException("RoomIdentifier can only use English or numbers.");// 房间ID只能使用英文或数字 } this.owner = owner; + owner.userRoom = this; this.roomID = roomID; - userList = new List(); + userList = new(); + LogManager.WriteLog($"New room created: {roomID} by {owner.userName}"); } /// /// User join room | 用户加入房间 @@ -111,6 +141,7 @@ public Room(User owner, string roomID) public void Join(User user) { userList.Add(user); + user.userRoom = this; } /// /// User leave room | 用户离开房间 @@ -119,6 +150,11 @@ public void Join(User user) public void Leave(User user) { userList.Remove(user); + if (userList.Count == 0) + { + RoomManager.RemoveRoom(this); + LogManager.WriteLog($"{roomID} user all left, room removed."); + } } /// /// Broadcast message to room | 广播消息到房间 @@ -145,11 +181,8 @@ public void Broadcast(string message,User? exceptUser) /// 索引 public User this[int index] { - get { return userList[index]; } - set - { - userList[index] = value; - } + get => userList[index]; + set => userList[index] = value; } } @@ -213,9 +246,21 @@ public void LeaveRoom() userStatus = Status.AFK; } + /// + /// New Room | 创建房间 + /// + /// 房间ID + /// 房间已存在 public void CreateRoom(string roomID) { - RoomManager.AddRoom(this, roomID); + if (RoomManager.GetRoom(roomID) != null) + { + throw new ArgumentException("RoomID already exists."); + } + else + { + RoomManager.AddRoom(this, roomID); + } } public void Remove() diff --git a/Program.cs b/Program.cs index 3cb0d34..1a64047 100644 --- a/Program.cs +++ b/Program.cs @@ -12,10 +12,6 @@ public class Program isDebug = true }; - //private static GameManager.ClientList clients = new(); - //private static GameManager.UserList users = new(); - - private static void Main(string[] args) { //Load config | 读取配置 @@ -47,6 +43,8 @@ private static void Main(string[] args) { // set cert wssserver.Certificate = new X509Certificate2(config.certPath, config.certPassword); + wssserver.EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls12 | + System.Security.Authentication.SslProtocols.Tls13; } catch (Exception e) { @@ -86,6 +84,7 @@ private static void Main(string[] args) }; }); } + if (config.ws) { wsserver.Start(socket => @@ -117,18 +116,20 @@ private static void Main(string[] args) LogManager.WriteLog( "Is it expected that both secure and insecure connections will be enabled simultaneously?", LogManager.LogLevel.Warning - ); + ); } - + LogManager.WriteLog("The server has been started"); if (config.ws) { LogManager.WriteLog($"The server is listening on ws://{config.wsOptions.Host}:{config.wsOptions.Port}"); } + if (config.wss) { LogManager.WriteLog($"The server is listening on wss://{config.wssOptions.Host}:{config.wssOptions.Port}"); } + while (true) { var command = Console.ReadLine(); @@ -142,6 +143,14 @@ private static void Main(string[] args) LogManager.WriteLog($"{user.userName} Joined at {user.joinTime.ToString("yyyy-mm-dd hh:mm:ss")}"); } } + else if (command == "roomlist") + { + LogManager.WriteLog("Room List:"); + foreach (var room in GameManager.RoomManager.roomList) + { + LogManager.WriteLog($"{room.roomID} Owner: {room.owner.userName}"); + } + } } } @@ -156,7 +165,8 @@ private static async Task ServerOnMessage(string message, IWebSocketConnection s { LogManager.WriteLog("illegal data received, Drop.", LogManager.LogLevel.Warning); //Drop | 丢弃并断开连接 - GameManager.UserManager.RemoveUser(socket); + if (GameManager.UserManager.Contains(socket)) + GameManager.UserManager.RemoveUser(socket); socket.Close(); return; } @@ -171,11 +181,14 @@ private static async Task ServerOnMessage(string message, IWebSocketConnection s { //illegal client, Drop it | 非法客户端,丢弃并断开连接 LogManager.WriteLog("illegal client, Drop it.", LogManager.LogLevel.Warning); - await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.JoinServerFailed() + await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.JoinServerFailed { - reason = "The client is already connected to the server.\nYour connection will be closed.", + reason = + "The client is already connected to the server.\nYour connection will be closed." //客户端已经连接服务器,您的连接将被关闭。 })); - GameManager.UserManager.Drop(socket); + LogManager.WriteLog( + $"There are illegal clients attempting to pass metadata multiple times: {GameManager.UserManager.GetUser(socket).userName}"); + GameManager.UserManager.RemoveUser(socket); socket.Close(); return; } @@ -185,17 +198,20 @@ await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.JoinS { if (string.IsNullOrEmpty(data.password) || data.password != config.Password) { - await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.JoinServerFailed() + await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.JoinServerFailed { reason = - "The server is private, but no password was provided or the password is invalid.\nYour connection will be closed.", + "The server is private, but no password was provided or the password is invalid.\nYour connection will be closed." //服务器是私有的,但没有提供密码或密码无效。您的连接将被关闭。 })); + LogManager.WriteLog( + $"The client attempted to connect to the server, but authentication failed: {data.userName}"); //Authentication failed, Drop and disconnect | 认证失败,丢弃并断开连接 - GameManager.UserManager.Drop(socket); + GameManager.UserManager.RemoveUser(socket); socket.Close(); return; } } + // Add user | 添加用户 LogManager.WriteLog($"User {clientMetaData.data.userName} connected."); LogManager.WriteLog($"Raw Message: {message}", LogManager.LogLevel.Debug); @@ -209,7 +225,7 @@ await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.JoinS await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.JoinServerSuccess())); return; } - + // Check if the user has completed the return of metadata | 检查用户是否已经返回元数据 if (!GameManager.UserManager.Contains(socket)) { @@ -221,26 +237,99 @@ await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.JoinS if (msg.action == "newRoom") { // To ConnectionMessage.Client.NewRoom | 将客户端发送的消息转换为 ConnectionMessage.Client.NewRoom - ConnectionMessage.Client.NewRoom newRoom = JsonConvert.DeserializeObject(message)!; + ConnectionMessage.Client.NewRoom newRoom = + JsonConvert.DeserializeObject(message)!; // TODO: User creates room logic | 创建房间逻辑 var user = GameManager.UserManager.GetUser(socket); if (user.userStatus == GameManager.User.Status.InRoom) { - await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.AddRoomFailed + await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.NewRoomFailed { reason = "You are already in a room." // 你已经在房间里了。 })); + LogManager.WriteLog($"User {user.userName} tried to create a room while in a room."); // 用户尝试在房间里创建房间。 return; } + + var room = GameManager.RoomManager.GetRoom(newRoom.data.roomID); + if (room != null) + { + await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.NewRoomFailed + { + reason = "The room already exists." // 房间已存在。 + })); + LogManager.WriteLog( + $"User {user.userName} tried to create a room with the same name."); // 用户尝试使用相同的名称创建房间。 + return; + } + GameManager.UserManager.GetUser(socket).CreateRoom(newRoom.data.roomID); + await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.NewRoomSuccess())); + LogManager.WriteLog($"User {user.userName} has created a new room {user.userRoom!.roomID}."); + } + + if (msg.action == "joinRoom") + { + // To ConnectionMessage.Client.JoinRoom | 将客户端发送的消息转换为 ConnectionMessage.Client.JoinRoom + ConnectionMessage.Client.JoinRoom joinRoom = + JsonConvert.DeserializeObject(message)!; + var user = GameManager.UserManager.GetUser(socket); + if (user.userStatus == GameManager.User.Status.InRoom) + { + await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.JoinRoomFailed + { + reason = "You are already in a room." // 你已经在房间里了。 + })); + LogManager.WriteLog($"User {user.userName} tried to join a room while in a room."); // 用户尝试在房间里加入房间。 + return; + } + + //Find the room | 查找房间 + var room = GameManager.RoomManager.GetRoom(joinRoom.data.roomID); + if (room != null) + { + await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.JoinRoomFailed + { + reason = "You are already in a room." // 你已经在房间里了。 + })); + LogManager.WriteLog($"User {user.userName} tried to join a room while in a room."); // 用户尝试在房间里加入房间。 + } + else + { + await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.JoinRoomFailed + { + reason = "The room does not exist." // 房间不存在。 + })); + LogManager.WriteLog($"User {user.userName} tried to join a room that does not exist.");// 用户尝试加入不存在的房间。 + } + } + + if (msg.action == "leaveRoom") + { + var user = GameManager.UserManager.GetUser(socket); + if (user.userStatus == GameManager.User.Status.InRoom) + { + user.LeaveRoom(); + LogManager.WriteLog($"User {user.userName} has left the {user.userRoom!.roomID} room."); + await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.LeaveRoomSuccess())); + } + else + { + await socket.Send(JsonConvert.SerializeObject(new ConnectionMessage.Server.LeaveRoomFailed + { + reason = "You are not in a room." // 你不在房间里。 + })); + LogManager.WriteLog($"User {user.userName} tried to leave a room while not in a room.");// 用户尝试离开房间,但未在房间里。 + } } } private static async Task ServerOnOpen(IWebSocketConnection socket) { + // Send GetData message, get metadata. | 发送 GetData 消息,获取元数据 await socket.Send( JsonConvert.SerializeObject( - new ConnectionMessage.Server.GetData() + new ConnectionMessage.Server.GetData { needPassword = config.isPrivate } @@ -251,18 +340,19 @@ await socket.Send( private static async Task ServerOnError(Exception e, IWebSocketConnection socket) { LogManager.WriteLog(e.Message, LogManager.LogLevel.Debug); - //WriteLog | 输出日志 - LogManager.WriteLog("Socket has been drop, because of an error: " + e.Message, LogManager.LogLevel.Error); - + //WriteLog, Socket has been drop | 输出日志,连接被丢弃 + LogManager.WriteLog("Socket has been drop, because of an error: " + e.Message, LogManager.LogLevel.Debug); + LogManager.WriteLog($"User {GameManager.UserManager.GetUser(socket).userName} unexpectedly disconnected", + LogManager.LogLevel.Warning); //Drop and disconnect | 丢弃并断开连接 - GameManager.UserManager.Drop(socket); + GameManager.UserManager.RemoveUser(socket); socket.Close(); } - + private static async Task ServerOnClose(IWebSocketConnection socket) { - LogManager.WriteLog("Client disconnected."); + LogManager.WriteLog($"user{GameManager.UserManager.GetUser(socket).userName} disconnected."); GameManager.UserManager.RemoveUser(socket); } @@ -277,16 +367,19 @@ public class Config public string Password = ""; public bool ws = true; public bool wss = false; + public dynamic wsOptions = new { Host = "0.0.0.0", Port = 14156 }; + public dynamic wssOptions = new { Host = "0.0.0.0", Port = 14157 }; + public string certPath = "path/to/your/certificate.pfx"; public string certPassword = "your_certificate_password"; public string userDefauletAvatarUrl = "";