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 });
}*/
}
}