diff --git a/src/CommandHandlers/ChatMessageHandler.cs b/src/CommandHandlers/ChatMessageHandler.cs index 4c03b53..cd61b84 100644 --- a/src/CommandHandlers/ChatMessageHandler.cs +++ b/src/CommandHandlers/ChatMessageHandler.cs @@ -35,11 +35,7 @@ class ChatMessageHandler : ICommandHandler { cmd.Add("p", data); NetworkPacket packet = NetworkObject.WrapObject(1, 13, cmd).Serialize(); - foreach (var roomClient in client.Room.Clients) { - if (roomClient != client) { - roomClient.Send(packet); - } - } + client.Room.Send(packet, client); cmd = new(); data = new(); diff --git a/src/CommandHandlers/ExitHandler.cs b/src/CommandHandlers/ExitHandler.cs index c3be2c6..caee1f7 100644 --- a/src/CommandHandlers/ExitHandler.cs +++ b/src/CommandHandlers/ExitHandler.cs @@ -7,6 +7,6 @@ namespace sodoffmmo.CommandHandlers; [CommandHandler(26)] class ExitHandler : ICommandHandler { public void Handle(Client client, NetworkObject receivedObject) { - client.LeaveRoom(); + client.SetRoom(null); } } diff --git a/src/CommandHandlers/GauntletHandlers.cs b/src/CommandHandlers/GauntletHandlers.cs index d337159..c90a583 100644 --- a/src/CommandHandlers/GauntletHandlers.cs +++ b/src/CommandHandlers/GauntletHandlers.cs @@ -39,10 +39,7 @@ class GauntletPlayAgainHandler : ICommandHandler client.PlayerData.Uid }, "msg", room.Id); - foreach (var roomClient in room.Clients) { - if (roomClient != client) - roomClient.Send(packet); - } + room.Send(packet, client); room.SendPA(client); } } @@ -61,9 +58,7 @@ class GauntletLobbyUserReadyHandler : ICommandHandler client.PlayerData.Uid }, "msg", room.Id); - foreach (var roomClient in room.Clients) { - roomClient.Send(packet); - } + room.Send(packet); if (room.GetReadyCount() > 1) { packet = Utils.ArrNetworkPacket(new string[] { @@ -72,9 +67,7 @@ class GauntletLobbyUserReadyHandler : ICommandHandler client.PlayerData.Uid }, "msg", room.Id); - foreach (var roomClient in room.Clients) { - roomClient.Send(packet); - } + room.Send(packet); } } } @@ -93,9 +86,7 @@ class GauntletLobbyUserNotReadyHandler : ICommandHandler client.PlayerData.Uid }, "msg", room.Id); - foreach (var roomClient in room.Clients) { - roomClient.Send(packet); - } + room.Send(packet); } } @@ -114,10 +105,7 @@ class GauntletLevelLoadHandler : ICommandHandler p.Get("1"), p.Get("2") // TODO use size of p.fields - 1 }, "msg", room.Id); - foreach (var roomClient in room.Clients) { - roomClient.Send(packet); - } - + room.Send(packet); } } @@ -139,9 +127,7 @@ class GauntletLevelLoadedHandler : ICommandHandler room.Id.ToString(), (--counter).ToString() }, "msg", room.Id); - foreach (var roomClient in room.Clients) { - roomClient.Send(packet); - } + room.Send(packet); timer = new System.Timers.Timer(1500); timer.AutoReset = true; @@ -169,9 +155,7 @@ class GauntletLevelLoadedHandler : ICommandHandler timer!.Close(); timer = null; } - foreach (var roomClient in room.Clients) { - roomClient.Send(packet); - } + room.Send(packet); } } @@ -191,10 +175,7 @@ class GauntletRelayGameDataHandler : ICommandHandler p.Get("0"), p.Get("1") }, "msg", room.Id); - foreach (var roomClient in room.Clients) { - if (roomClient != client) - roomClient.Send(packet); - } + room.Send(packet, client); } } diff --git a/src/CommandHandlers/GenericMessageHandler.cs b/src/CommandHandlers/GenericMessageHandler.cs index 7257cde..bb6c41f 100644 --- a/src/CommandHandlers/GenericMessageHandler.cs +++ b/src/CommandHandlers/GenericMessageHandler.cs @@ -8,7 +8,6 @@ namespace sodoffmmo.CommandHandlers; class GenericMessageHandler : ICommandHandler { public void Handle(Client client, NetworkObject receivedObject) { NetworkPacket packet = NetworkObject.WrapObject(0, 7, receivedObject).Serialize(); - foreach (var roomClient in client.Room.Clients) - roomClient.Send(packet); + client.Room.Send(packet); } } diff --git a/src/CommandHandlers/JoinRoomHandler.cs b/src/CommandHandlers/JoinRoomHandler.cs index 8f7c1d7..b9f7ab0 100644 --- a/src/CommandHandlers/JoinRoomHandler.cs +++ b/src/CommandHandlers/JoinRoomHandler.cs @@ -11,7 +11,6 @@ class JoinRoomHandler : ICommandHandler { string roomName = receivedObject.Get("p").Get("rn"); Room room = Room.GetOrAdd(roomName); - Console.WriteLine($"Join Room: {roomName} RoomID: {room.Id} IID: {client.ClientID}"); - client.JoinRoom(room); + client.SetRoom(room); } } diff --git a/src/CommandHandlers/PublicMessageHandlers.cs b/src/CommandHandlers/PublicMessageHandlers.cs index c09133d..86f1257 100644 --- a/src/CommandHandlers/PublicMessageHandlers.cs +++ b/src/CommandHandlers/PublicMessageHandlers.cs @@ -24,8 +24,6 @@ class RacingPMHandler : ICommandHandler cmd.Add("p", p); NetworkPacket packet = NetworkObject.WrapObject(1, 13, cmd).Serialize(); - foreach (var roomClient in client.Room.Clients) { - roomClient.Send(packet); - } + client.Room.Send(packet); } } diff --git a/src/CommandHandlers/RacingHandlers.cs b/src/CommandHandlers/RacingHandlers.cs index 5861d18..d0ee9e3 100644 --- a/src/CommandHandlers/RacingHandlers.cs +++ b/src/CommandHandlers/RacingHandlers.cs @@ -58,9 +58,7 @@ class RacingARACKHandler : ICommandHandler if (room.GetPlayersCount(RacingPlayerState.RaceReady2) == room.ClientsCount) { NetworkPacket packet = room.GetSTAPacket(); - foreach (var roomClient in room.Clients) { - roomClient.Send(packet); - } + room.Send(packet); Console.WriteLine($"STA"); } } diff --git a/src/CommandHandlers/SetPositionVariablesHandler.cs b/src/CommandHandlers/SetPositionVariablesHandler.cs index 17c3efe..446c385 100644 --- a/src/CommandHandlers/SetPositionVariablesHandler.cs +++ b/src/CommandHandlers/SetPositionVariablesHandler.cs @@ -59,11 +59,7 @@ class SetPositionVariablesHandler : ICommandHandler { cmd.Add("c", "SPV"); cmd.Add("p", obj); - NetworkPacket packet = NetworkObject.WrapObject(1, 13, cmd).Serialize(); - foreach (var roomClient in client.Room.Clients) { - if (roomClient != client) - roomClient.Send(packet); - } + client.Room.Send(packet, client); } } diff --git a/src/CommandHandlers/SetUserVariablesHandler.cs b/src/CommandHandlers/SetUserVariablesHandler.cs index c745c99..15a35b2 100644 --- a/src/CommandHandlers/SetUserVariablesHandler.cs +++ b/src/CommandHandlers/SetUserVariablesHandler.cs @@ -11,6 +11,12 @@ class SetUserVariablesHandler : ICommandHandler { Client client; string? uid; public void Handle(Client client, NetworkObject receivedObject) { + if (client.Room == null) { + Console.WriteLine($"SUV Missing Room IID: {client.ClientID}"); + client.Send(NetworkObject.WrapObject(0, 1006, new NetworkObject()).Serialize()); + client.SheduleDisconnect(); + return; + } this.client = client; suvData = receivedObject.Get("p"); uid = suvData.Get("UID"); @@ -90,8 +96,7 @@ class SetUserVariablesHandler : ICommandHandler { data.Add("vl", vl); NetworkPacket packet = NetworkObject.WrapObject(0, 12, data).Serialize(); - foreach (var roomClient in client.Room.Clients) - roomClient.Send(packet); + client.Room.Send(packet); NetworkObject cmd = new(); cmd.Add("c", "SUV"); @@ -105,10 +110,7 @@ class SetUserVariablesHandler : ICommandHandler { container.Add("arr", arr); cmd.Add("p", container); packet = NetworkObject.WrapObject(1, 13, cmd).Serialize(); - foreach (var roomClient in client.Room.Clients) { - if (roomClient != client) - roomClient.Send(packet); - } + client.Room.Send(packet, client); } private void UpdatePlayersInRoom() { @@ -129,10 +131,7 @@ class SetUserVariablesHandler : ICommandHandler { NetworkPacket packet = NetworkObject.WrapObject(0, 1000, data).Serialize(); packet.Compress(); - foreach (var roomClient in client.Room.Clients) { - if (roomClient != client) - roomClient.Send(packet); - } + client.Room.Send(packet, client); } private void SendSUVToPlayerInRoom() { @@ -144,9 +143,6 @@ class SetUserVariablesHandler : ICommandHandler { cmd.Add("p", obj); NetworkPacket packet = NetworkObject.WrapObject(1, 13, cmd).Serialize(); - foreach (var roomClient in client.Room.Clients) { - if (roomClient != client) - roomClient.Send(packet); - } + client.Room.Send(packet, client); } } diff --git a/src/CommandHandlers/WorldEventHandlers.cs b/src/CommandHandlers/WorldEventHandlers.cs index dd85db8..88fe73e 100644 --- a/src/CommandHandlers/WorldEventHandlers.cs +++ b/src/CommandHandlers/WorldEventHandlers.cs @@ -37,9 +37,7 @@ class WorldEventHealthHandler : ICommandHandler { health.ToString("0.0#####", CultureInfo.GetCultureInfo("en-US")) + "," + DateTime.UtcNow.ToString("ddd MMM dd HH:mm:ss UTC yyyy", CultureInfo.GetCultureInfo("en-US")), WorldEvent.Get().GetRoom().Id ); - foreach (var roomClient in WorldEvent.Get().GetRoom().Clients) { - roomClient.Send(packet); - } + WorldEvent.Get().GetRoom().Send(packet); } } } @@ -59,9 +57,7 @@ class WorldEventFlareHandler : ICommandHandler { p.Get("oh") + "," + p.Get("ts"), WorldEvent.Get().GetRoom().Id ); - foreach (var roomClient in WorldEvent.Get().GetRoom().Clients) { - roomClient.Send(packet); - } + WorldEvent.Get().GetRoom().Send(packet); } } @@ -82,9 +78,7 @@ class WorldEventMissileHandler : ICommandHandler { p.Get("tID"), p.Get("objID") }); - foreach (var roomClient in WorldEvent.Get().GetRoom().Clients) { - roomClient.Send(packet); - } + WorldEvent.Get().GetRoom().Send(packet); } } diff --git a/src/Core/Client.cs b/src/Core/Client.cs index f736b91..357e497 100644 --- a/src/Core/Client.cs +++ b/src/Core/Client.cs @@ -1,6 +1,7 @@ using sodoffmmo.Data; using System; using System.Net.Sockets; +using System.Runtime.CompilerServices; namespace sodoffmmo.Core; public class Client { @@ -9,12 +10,12 @@ public class Client { public int ClientID { get; private set; } public PlayerData PlayerData { get; set; } = new(); - public Room Room { get; private set; } + public Room? Room { get; private set; } private readonly Socket socket; SocketBuffer socketBuffer = new(); private volatile bool scheduledDisconnect = false; - private object ClientLock = new(); + private readonly object clientLock = new(); public Client(Socket clientSocket) { socket = clientSocket; @@ -39,36 +40,36 @@ public class Client { try { socket.Send(packet.SendData); } catch (SocketException) { - LeaveRoom(); SheduleDisconnect(); } } - public void LeaveRoom() { - if (Room != null) { - Console.WriteLine($"Leave room {Room.Name} IID: {ClientID}"); - Room.RemoveClient(this); - NetworkObject data = new(); - data.Add("r", Room.Id); - data.Add("u", ClientID); - - NetworkPacket packet = NetworkObject.WrapObject(0, 1004, data).Serialize(); - foreach (var roomClient in Room.Clients) { - roomClient.Send(packet); - } - Room = null; - } - } - - public void JoinRoom(Room room) { - lock(ClientLock) { - LeaveRoom(); + public void SetRoom(Room? room) { + lock(clientLock) { + // set variable player data as not valid, but do not reset all player data PlayerData.IsValid = false; + + if (Room != null) { + Console.WriteLine($"Leave room: {Room.Name} (id={Room.Id}, size={Room.ClientsCount}) IID: {ClientID}"); + Room.RemoveClient(this); + + NetworkObject data = new(); + data.Add("r", Room.Id); + data.Add("u", ClientID); + Room.Send(NetworkObject.WrapObject(0, 1004, data).Serialize()); + } + + // set new room (null when SetRoom is used as LeaveRoom) Room = room; - Room.AddClient(this); + + if (Room != null) { + Console.WriteLine($"Join room: {Room.Name} RoomID (id={Room.Id}, size={Room.ClientsCount}) IID: {ClientID}"); + Room.AddClient(this); + + Send(Room.SubscribeRoom()); + UpdatePlayerUserVariables(); + } } - Send(Room.SubscribeRoom()); - UpdatePlayerUserVariables(); } private void UpdatePlayerUserVariables() { @@ -91,6 +92,12 @@ public class Client { } public void SheduleDisconnect() { + if (Room != null) { + // quiet remove from room (to avoid issues in Room.Send) + // - do not change Room value here + // - full remove will be will take place Server.HandleClient (before real disconnected) + Room.RemoveClient(this); + } scheduledDisconnect = true; } diff --git a/src/Core/GauntletRoom.cs b/src/Core/GauntletRoom.cs index 19c6dfd..1f6fb4d 100644 --- a/src/Core/GauntletRoom.cs +++ b/src/Core/GauntletRoom.cs @@ -65,9 +65,7 @@ public class GauntletRoom : Room { } NetworkPacket packet = Utils.ArrNetworkPacket(info.ToArray(), "msg", base.Id); - foreach(var player in players) { - player.Key.Send(packet); - } + Send(packet); } public void SendPA(Client client) { @@ -112,10 +110,7 @@ public class GauntletRoom : Room { } NetworkPacket packet = Utils.ArrNetworkPacket(info.ToArray(), "msg", base.Id); - foreach(var player in players) { - player.Key.Send(packet); - } - + Send(packet); return true; } } @@ -127,8 +122,8 @@ public class GauntletRoom : Room { if (room is null) room = GauntletRoom.Get(); - room.AddPlayer(client); // must be call before JoinRoom (before InvalidatePlayerData) - we need uid - client.JoinRoom(room); + client.SetRoom(room); + room.AddPlayer(client); // client will be not removed from GauntletRoom.players ... after remove all client from room whole GauntletRoom.players will be removed room.SendUJR(); } } diff --git a/src/Core/Racing.cs b/src/Core/Racing.cs index c6057d5..843d784 100644 --- a/src/Core/Racing.cs +++ b/src/Core/Racing.cs @@ -94,9 +94,7 @@ public class RacingRoom : Room { } NetworkPacket packet = Utils.ArrNetworkPacket(info.ToArray(), "", Id); - foreach (var roomClient in Clients) { - roomClient.Send(packet); - } + Send(packet); } } @@ -124,8 +122,7 @@ public class RacingRoom : Room { private void SendJoin(Object? source, ElapsedEventArgs e) { foreach(var player in players) { - Console.WriteLine($"Join Racing Room: {Name} RoomID: {Id} IID: {player.Key.ClientID}"); - player.Key.JoinRoom(this); + player.Key.SetRoom(this); } SetTimer(1, CountDown, true); } @@ -142,9 +139,7 @@ public class RacingRoom : Room { "LT", (--counter).ToString() }, "", Id); - foreach (var roomClient in Clients) { - roomClient.Send(packet); - } + Send(packet); } } } @@ -167,9 +162,7 @@ public class RacingRoom : Room { "", "ST" }, "", Id); - foreach (var roomClient in Clients) { - roomClient.Send(packet); - } + Send(packet); } // TODO StratTimer → kick out to main lobby players without RacingPlayerState.RaceReady1 after timeout, next kick out players without RacingPlayerState.RaceReady2 after timeout2 diff --git a/src/Core/Room.cs b/src/Core/Room.cs index 39748eb..741ff29 100644 --- a/src/Core/Room.cs +++ b/src/Core/Room.cs @@ -66,6 +66,14 @@ public class Room { } } + public void Send(NetworkPacket packet, Client? skip = null) { + foreach (var roomClient in clients) { + if (roomClient != skip) { + roomClient.Send(packet); + } + } + } + public static bool Exists(string name) => rooms.ContainsKey(name); public static Room Get(string name) => rooms[name]; @@ -99,7 +107,7 @@ public class Room { roomInfo.Add((short)0); // max spectator count NetworkArray userList = new(); - foreach (Client player in Clients) { + foreach (Client player in clients) { if (player.PlayerData.Uid != "") userList.Add(player.PlayerData.GetNetworkData(player.ClientID, out _)); } diff --git a/src/Core/WorldEvent.cs b/src/Core/WorldEvent.cs index 81c6da2..4640ce0 100644 --- a/src/Core/WorldEvent.cs +++ b/src/Core/WorldEvent.cs @@ -160,9 +160,7 @@ class WorldEvent { lastResults, room.Id ); - foreach (var roomClient in room.Clients) { - roomClient.Send(packet); - } + room.Send(packet); NetworkArray vl = new(); vl.Add(NetworkArray.VlElement("WE__AI", NetworkArray.NULL)); @@ -171,9 +169,7 @@ class WorldEvent { vl.Add(NetworkArray.VlElement("WEF_" + t.Key, NetworkArray.NULL)); } packet = Utils.VlNetworkPacket(vl, room.Id); - foreach (var roomClient in room.Clients) { - roomClient.Send(packet); - } + room.Send(packet); Console.WriteLine($"Event {uid} sent _End"); @@ -206,9 +202,7 @@ class WorldEvent { Console.WriteLine($"Event {uid} send event notification (WE_ = {(WE ? startTimeString : WE)} WEN_ = {(WEN ? nextStartTimeString : WEN)}, room = {room.Id}) to all clients"); NetworkPacket packet = Utils.VlNetworkPacket(EventInfoArray(WE, WEN), room.Id); foreach (var r in Room.AllRooms()) { - foreach (var roomClient in r.Clients) { - roomClient.Send(packet); - } + r.Send(packet); } } diff --git a/src/Server.cs b/src/Server.cs index 2001576..0d7a1dd 100644 --- a/src/Server.cs +++ b/src/Server.cs @@ -53,7 +53,7 @@ public class Server { } } finally { try { - client.LeaveRoom(); + client.SetRoom(null); } catch (Exception) { } client.Disconnect(); Console.WriteLine("Socket disconnected IID: " + client.ClientID);