using Fleck; using System.Text.Json; namespace ExtinctionOnline.Server { /// /// Extinction Online のサーバー。 /// public class Server { /// /// すべてのクライアントのリスト /// internal static readonly List s_clients = new(); /// /// Lobbyに居るクライアントのリスト /// internal static readonly List s_lobby = new(); /// /// それぞれのRoomのIDとRoomのリスト /// internal static readonly Dictionary s_rooms = new(); /// /// メッセージ処理の辞書。呼び出し用 /// static readonly Dictionary> s_messageTypes = new() { { "SYSTEM", Commands.SystemCommand }, { "GAME", Commands.Game }, }; /// /// サーバーのメインメソッド。 /// /// /// 外部からの呼び出し非推奨 /// コマンド実行時の引数 public static void Main(string[] args) { Console.WriteLine(Logo.LogoStr); // TODO:ポート変更を可能に var server = new WebSocketServer("ws://0.0.0.0:1234"); server.Start(socket => { socket.OnOpen = () => OnOpen(socket); socket.OnClose = () => OnClose(socket); socket.OnMessage = message => OnMessage(socket, message); }); // 標準入力を受付 string? data = ""; while (data == null || !data.StartsWith("exit")) { data = Console.ReadLine(); } foreach (var c in new List(s_clients)) { c.Socket.Close(); } server.Dispose(); } /// /// クライアントとの接続が開いたとき用のHook。 /// /// 外部からの呼び出し非推奨 /// クライアントとのソケット static void OnOpen(IWebSocketConnection socket) { // Guidを生成してクライアントに送信、記録 Guid clientGuid = Guid.NewGuid(); s_clients.Add(new ClientInfo(clientGuid.ToString(), socket)); socket.Send(JsonSerializer.Serialize(new MessageData { MessageType = "SYSTEM", DeliveryTo = new DeliveryTo { Type = "CLIENT", ClientId = clientGuid.ToString() } }, JsonUtil.GetJsonOptions())); Console.WriteLine($"Open connection: {socket.ConnectionInfo.ClientIpAddress}:{socket.ConnectionInfo.ClientPort}"); } /// /// クライアントとの接続が閉じたとき用のHook。 /// /// 外部からの呼び出し非推奨 /// クライアントとのソケット static void OnClose(IWebSocketConnection socket) { var client = s_clients.Find(it => it.Socket.GetHashCode() == socket.GetHashCode()); if (client != null) { if (client.RoomId != null) { string roomId = client.RoomId; lock (((System.Collections.IDictionary)s_rooms).SyncRoot) { s_rooms[roomId].RemoveClient(client); if (s_rooms[roomId]._clients.Count <= 0) s_rooms.Remove(roomId); } } else { s_lobby.Remove(client); } lock (((System.Collections.ICollection)s_clients).SyncRoot) { s_clients.Remove(client); } } Console.WriteLine($"Close connection: {socket.ConnectionInfo.ClientIpAddress}:{socket.ConnectionInfo.ClientPort}"); } /// /// クライアントからメッセージを受け取ったとき用のHook。 /// /// 外部からの呼び出し非推奨 /// クライアントとのソケット /// 受け取ったメッセージ static void OnMessage(IWebSocketConnection socket, string message) { Console.WriteLine(socket.GetHashCode()); Console.WriteLine(message); try { MessageData? messageData = JsonSerializer.Deserialize(message, JsonUtil.GetJsonOptions()); if (messageData == null) throw new NullReferenceException("messageData(Json body) is needed."); if (messageData.From == null) throw new NullReferenceException("from is needed."); if (messageData.MessageType == null) throw new NullReferenceException("messageType is needed."); var client = s_clients.Find(it => it.Socket.GetHashCode() == socket.GetHashCode()); if (client == null) throw new NullReferenceException("client is null. Not found in client list."); if (client.ClientId != messageData.From) throw new Exception("ClientId does not match."); s_messageTypes[messageData.MessageType].Invoke(messageData, client, message); } catch (JsonException) { socket.Send("Invalid message. It must be JSON. Close connection."); socket.Close(); } catch (Exception e) { socket.Send($"Invalid message. {e.Message}; Close connection."); socket.Close(); } } /* /// /// すべてのクライアントにメッセージを送信する。 /// /// 送信するメッセージ本文 internal static void SendMessageToAll(string data) { foreach (var c in s_clients) { c.Socket.Send(data); } } /// /// 指定したRoom内の指定したクライアント以外にメッセージを送信する。 /// /// RoomのID /// 送信するメッセージ本文 /// 除外するクライアントのリスト internal static void SendMessageToWithout(string roomId, string data, List without) { foreach (var c in s_rooms[roomId]) { if (!without.Contains(c)) c.Socket.Send(data); } } /// /// 指定したクライアントにメッセージを送信する。 /// /// 送信するメッセージ本文 /// クライアントのリスト internal static void SendMessageToListClient(string data, List clients) { foreach (var c in clients) { c.Socket.Send(data); } } /// /// クライアントを指定したRoomに参加させる。 /// /// RoomのID /// 参加するClient /// 存在しないRoomを作成するか public static void JoinClientToRoom(string roomId, ClientInfo client, bool doCreate) { if (s_rooms.ContainsKey(roomId)) { s_rooms[roomId].Add(client); } else { if (doCreate) { s_rooms.Add(roomId, new List() { client }); } else { throw new Exception("The specified Room does not exist."); } } client.RoomId = roomId; client.Socket.Send(JsonSerializer.Serialize(new MessageData { MessageType = "Room", RoomData = new RoomMessageData() { RequestType = "Joined", RoomId = roomId } }, JsonUtil.GetJsonOptions())); if (s_rooms[roomId].Count > 1) { List clients = new List(); foreach (var existingClient in s_rooms[roomId]) { if (client.ClientId != existingClient.ClientId) clients.Add(new ClientMessageData() { ClientId = existingClient.ClientId }); } client.Socket.Send(JsonSerializer.Serialize(new MessageData { MessageType = "Room", RoomData = new RoomMessageData() { RequestType = "ClientsInfoNotification", RoomId = roomId }, ClientsInfo = clients, })); } SendMessageToWithout(roomId, JsonSerializer.Serialize(new MessageData { MessageType = "Room", ClientId = client.ClientId, RoomData = new RoomMessageData() { RequestType = "ClientJoined", RoomId = roomId } }, JsonUtil.GetJsonOptions()), new() { client }); }*/ } }