From d0981e6f6ff19b9b222971a7efcf3de911134deb Mon Sep 17 00:00:00 2001 From: MrOkiDoki <0mrokidoki@gmail.com> Date: Sun, 20 Aug 2023 11:21:22 +0300 Subject: [PATCH 1/3] namespace fix. --- BattleBitAPI/Common/Conts.cs | 2 +- BattleBitAPI/Server/GameServer.cs | 6 +----- BattleBitAPI/Server/ServerListener.cs | 1 - CommunityServerAPI.csproj | 1 + 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/BattleBitAPI/Common/Conts.cs b/BattleBitAPI/Common/Conts.cs index e339257..122f18d 100644 --- a/BattleBitAPI/Common/Conts.cs +++ b/BattleBitAPI/Common/Conts.cs @@ -1,4 +1,4 @@ -namespace CommunityServerAPI.BattleBitAPI +namespace BattleBitAPI { public static class Const { diff --git a/BattleBitAPI/Server/GameServer.cs b/BattleBitAPI/Server/GameServer.cs index c437f95..e9c9916 100644 --- a/BattleBitAPI/Server/GameServer.cs +++ b/BattleBitAPI/Server/GameServer.cs @@ -1,7 +1,4 @@ -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; -using System.Linq; -using System.Net; +using System.Net; using System.Net.Sockets; using System.Numerics; using System.Text; @@ -9,7 +6,6 @@ using BattleBitAPI.Common; using BattleBitAPI.Common.Extentions; using BattleBitAPI.Networking; using BattleBitAPI.Pooling; -using CommunityServerAPI.BattleBitAPI; namespace BattleBitAPI.Server { diff --git a/BattleBitAPI/Server/ServerListener.cs b/BattleBitAPI/Server/ServerListener.cs index 6bdd385..c7ad39d 100644 --- a/BattleBitAPI/Server/ServerListener.cs +++ b/BattleBitAPI/Server/ServerListener.cs @@ -5,7 +5,6 @@ using System.Runtime.CompilerServices; using BattleBitAPI.Common; using BattleBitAPI.Common.Extentions; using BattleBitAPI.Networking; -using CommunityServerAPI.BattleBitAPI; namespace BattleBitAPI.Server { diff --git a/CommunityServerAPI.csproj b/CommunityServerAPI.csproj index 10c18d1..52a520b 100644 --- a/CommunityServerAPI.csproj +++ b/CommunityServerAPI.csproj @@ -18,6 +18,7 @@ https://github.com/MrOkiDoki/BattleBit-Community-Server-API https://github.com/MrOkiDoki/BattleBit-Community-Server-API BattleBit + 1.0.1 From 0ee058910159db2404631876e3460339716de2c1 Mon Sep 17 00:00:00 2001 From: MrOkiDoki <0mrokidoki@gmail.com> Date: Sun, 20 Aug 2023 14:53:28 +0300 Subject: [PATCH 2/3] Callbacks for session changes. --- BattleBitAPI/Common/Extentions/Extentions.cs | 6 + .../Networking/NetworkCommuncation.cs | 1 + BattleBitAPI/Server/GameServer.cs | 39 ++- BattleBitAPI/Server/Player.cs | 84 +++-- BattleBitAPI/Server/ServerListener.cs | 293 +++++++++++++----- 5 files changed, 305 insertions(+), 118 deletions(-) diff --git a/BattleBitAPI/Common/Extentions/Extentions.cs b/BattleBitAPI/Common/Extentions/Extentions.cs index 3ef94dd..ae5b7ec 100644 --- a/BattleBitAPI/Common/Extentions/Extentions.cs +++ b/BattleBitAPI/Common/Extentions/Extentions.cs @@ -25,6 +25,12 @@ namespace BattleBitAPI.Common.Extentions #endif } + public static void Replace(this Dictionary dic, TKey key, TValue value) + { + dic.Remove(key); + dic.Add(key, value); + } + public static void SafeClose(this TcpClient client) { try { client.Close(); } catch { } diff --git a/BattleBitAPI/Networking/NetworkCommuncation.cs b/BattleBitAPI/Networking/NetworkCommuncation.cs index 69f4304..f30cbe0 100644 --- a/BattleBitAPI/Networking/NetworkCommuncation.cs +++ b/BattleBitAPI/Networking/NetworkCommuncation.cs @@ -40,5 +40,6 @@ OnPlayerGivenUp = 70, OnPlayerRevivedAnother = 71, OnSquadPointsChanged = 72, + NotifyNewRoundID = 73, } } diff --git a/BattleBitAPI/Server/GameServer.cs b/BattleBitAPI/Server/GameServer.cs index e9c9916..ff45b59 100644 --- a/BattleBitAPI/Server/GameServer.cs +++ b/BattleBitAPI/Server/GameServer.cs @@ -17,7 +17,6 @@ namespace BattleBitAPI.Server public IPAddress GameIP => mInternal.GameIP; public int GamePort => mInternal.GamePort; - public TcpClient Socket => mInternal.Socket; public bool IsPasswordProtected => mInternal.IsPasswordProtected; public string ServerName => mInternal.ServerName; public string Gamemode => mInternal.Gamemode; @@ -29,6 +28,8 @@ namespace BattleBitAPI.Server public int MaxPlayerCount => mInternal.MaxPlayerCount; public string LoadingScreenText => mInternal.LoadingScreenText; public string ServerRulesText => mInternal.ServerRulesText; + public uint RoundIndex => mInternal.RoundIndex; + public long SessionID => mInternal.SessionID; public ServerSettings ServerSettings => mInternal.ServerSettings; public MapRotation MapRotation => mInternal.MapRotation; public GamemodeRotation GamemodeRotation => mInternal.GamemodeRotation; @@ -158,7 +159,7 @@ namespace BattleBitAPI.Server try { //Are we still connected on socket level? - if (!Socket.Connected) + if (mInternal.Socket == null || !mInternal.Socket.Connected) { mClose("Connection was terminated."); return; @@ -171,10 +172,10 @@ namespace BattleBitAPI.Server return; } - var networkStream = Socket.GetStream(); + var networkStream = mInternal.Socket.GetStream(); //Read network packages. - while (Socket.Available > 0) + while (mInternal.Socket.Available > 0) { this.mInternal.mLastPackageReceived = Extentions.TickCount; @@ -405,10 +406,6 @@ namespace BattleBitAPI.Server public virtual async Task OnTick() { - } - public virtual async Task OnReconnected() - { - } public virtual async Task OnDisconnected() { @@ -500,6 +497,10 @@ namespace BattleBitAPI.Server public virtual async Task OnRoundEnded() { + } + public virtual async Task OnSessionChanged(long oldSessionID, long newSessionID) + { + } // ---- Functions ---- @@ -553,10 +554,18 @@ namespace BattleBitAPI.Server { ExecuteCommand("endgame"); } - public void SayToChat(string msg) + public void SayToAllChat(string msg) { ExecuteCommand("say " + msg); } + public void SayToChat(string msg, ulong steamID) + { + ExecuteCommand("sayto " + steamID + " " + msg); + } + public void SayToChat(string msg, Player player) + { + SayToChat(msg, player.SteamID); + } public void StopServer() { @@ -897,7 +906,7 @@ namespace BattleBitAPI.Server } // ---- Static ---- - public static void SetInstance(GameServer server, Internal @internal) + internal static void SetInstance(GameServer server, Internal @internal) { server.mInternal = @internal; } @@ -908,6 +917,7 @@ namespace BattleBitAPI.Server // ---- Variables ---- public ulong ServerHash; public bool IsConnected; + public bool HasActiveConnectionSession; public IPAddress GameIP; public int GamePort; public TcpClient Socket; @@ -924,6 +934,8 @@ namespace BattleBitAPI.Server public int MaxPlayerCount; public string LoadingScreenText; public string ServerRulesText; + public uint RoundIndex; + public long SessionID; public ServerSettings ServerSettings; public MapRotation MapRotation; public GamemodeRotation GamemodeRotation; @@ -944,6 +956,7 @@ namespace BattleBitAPI.Server public long mLastPackageReceived; public long mLastPackageSent; public bool mWantsToCloseConnection; + public long mPreviousSessionID; public StringBuilder mBuilder; public Queue<(ulong steamID, PlayerModifications.mPlayerModifications)> mChangedModifications; @@ -1290,7 +1303,9 @@ namespace BattleBitAPI.Server int inQueuePlayers, int maxPlayers, string loadingScreenText, - string serverRulesText + string serverRulesText, + uint roundIndex, + long sessionID ) { this.ServerHash = ((ulong)port << 32) | (ulong)iP.ToUInt(); @@ -1311,6 +1326,8 @@ namespace BattleBitAPI.Server this.MaxPlayerCount = maxPlayers; this.LoadingScreenText = loadingScreenText; this.ServerRulesText = serverRulesText; + this.RoundIndex = roundIndex; + this.SessionID = sessionID; this.ServerSettings.Reset(); this._RoomSettings.Reset(); diff --git a/BattleBitAPI/Server/Player.cs b/BattleBitAPI/Server/Player.cs index 4bea9c5..880548f 100644 --- a/BattleBitAPI/Server/Player.cs +++ b/BattleBitAPI/Server/Player.cs @@ -58,7 +58,7 @@ namespace BattleBitAPI KickFromSquad(); else { - if(value.Team != this.Team) + if (value.Team != this.Team) ChangeTeam(value.Team); JoinSquad(value.Name); } @@ -66,6 +66,8 @@ namespace BattleBitAPI } public bool InSquad => mInternal.SquadName != Squads.NoSquad; public int PingMs => mInternal.PingMs; + public long CurrentSessionID => mInternal.SessionID; + public bool IsConnected => mInternal.SessionID != 0; public float HP { @@ -156,56 +158,78 @@ namespace BattleBitAPI public virtual async Task OnDisconnected() { + } + public virtual async Task OnSessionChanged(long oldSessionID, long newSessionID) + { + } // ---- Functions ---- public void Kick(string reason = "") { - GameServer.Kick(this, reason); + if (IsConnected) + GameServer.Kick(this, reason); } public void Kill() { - GameServer.Kill(this); + if (IsConnected) + GameServer.Kill(this); } public void ChangeTeam() { - GameServer.ChangeTeam(this); + if (IsConnected) + GameServer.ChangeTeam(this); } public void ChangeTeam(Team team) { - GameServer.ChangeTeam(this, team); + if (IsConnected) + GameServer.ChangeTeam(this, team); } public void KickFromSquad() { - GameServer.KickFromSquad(this); + if (IsConnected) + GameServer.KickFromSquad(this); } public void JoinSquad(Squads targetSquad) { - GameServer.JoinSquad(this, targetSquad); + if (IsConnected) + GameServer.JoinSquad(this, targetSquad); } public void DisbandTheSquad() { - GameServer.DisbandPlayerCurrentSquad(this); + if (IsConnected) + GameServer.DisbandPlayerCurrentSquad(this); } public void PromoteToSquadLeader() { - GameServer.PromoteSquadLeader(this); + if (IsConnected) + GameServer.PromoteSquadLeader(this); } public void WarnPlayer(string msg) { - GameServer.WarnPlayer(this, msg); + if (IsConnected) + GameServer.WarnPlayer(this, msg); } public void Message(string msg) { - GameServer.MessageToPlayer(this, msg); + if (IsConnected) + GameServer.MessageToPlayer(this, msg); } + public void SayToChat(string msg) + { + if (IsConnected) + GameServer.SayToChat(msg, this); + } + public void Message(string msg, float fadeoutTime) { - GameServer.MessageToPlayer(this, msg, fadeoutTime); + if (IsConnected) + GameServer.MessageToPlayer(this, msg, fadeoutTime); } public void SetNewRole(GameRole role) { - GameServer.SetRoleTo(this, role); + if (IsConnected) + GameServer.SetRoleTo(this, role); } public void Teleport(Vector3 target) { @@ -213,47 +237,57 @@ namespace BattleBitAPI } public void SpawnPlayer(PlayerLoadout loadout, PlayerWearings wearings, Vector3 position, Vector3 lookDirection, PlayerStand stand, float spawnProtection) { - GameServer.SpawnPlayer(this, loadout, wearings, position, lookDirection, stand, spawnProtection); + if (IsConnected) + GameServer.SpawnPlayer(this, loadout, wearings, position, lookDirection, stand, spawnProtection); } public void SetHP(float newHP) { - GameServer.SetHP(this, newHP); + if (IsConnected) + GameServer.SetHP(this, newHP); } public void GiveDamage(float damage) { - GameServer.GiveDamage(this, damage); + if (IsConnected) + GameServer.GiveDamage(this, damage); } public void Heal(float hp) { - GameServer.Heal(this, hp); + if (IsConnected) + GameServer.Heal(this, hp); } public void SetPrimaryWeapon(WeaponItem item, int extraMagazines, bool clear = false) { - GameServer.SetPrimaryWeapon(this, item, extraMagazines, clear); + if (IsConnected) + GameServer.SetPrimaryWeapon(this, item, extraMagazines, clear); } public void SetSecondaryWeapon(WeaponItem item, int extraMagazines, bool clear = false) { - GameServer.SetSecondaryWeapon(this, item, extraMagazines, clear); + if (IsConnected) + GameServer.SetSecondaryWeapon(this, item, extraMagazines, clear); } public void SetFirstAidGadget(string item, int extra, bool clear = false) { - GameServer.SetFirstAid(this, item, extra, clear); + if (IsConnected) + GameServer.SetFirstAid(this, item, extra, clear); } public void SetLightGadget(string item, int extra, bool clear = false) { - GameServer.SetLightGadget(this, item, extra, clear); + if (IsConnected) + GameServer.SetLightGadget(this, item, extra, clear); } public void SetHeavyGadget(string item, int extra, bool clear = false) { - GameServer.SetHeavyGadget(this, item, extra, clear); + if (IsConnected) + GameServer.SetHeavyGadget(this, item, extra, clear); } public void SetThrowable(string item, int extra, bool clear = false) { - GameServer.SetThrowable(this, item, extra, clear); + if (IsConnected) + GameServer.SetThrowable(this, item, extra, clear); } // ---- Static ---- - public static void SetInstance(TPlayer player, Player.Internal @internal) + internal static void SetInstance(TPlayer player, Player.Internal @internal) { player.mInternal = @internal; } @@ -275,6 +309,8 @@ namespace BattleBitAPI public Team Team; public Squads SquadName; public int PingMs = 999; + public long PreviousSessionID = 0; + public long SessionID = 0; public bool IsAlive; public float HP; diff --git a/BattleBitAPI/Server/ServerListener.cs b/BattleBitAPI/Server/ServerListener.cs index c7ad39d..b75c3b3 100644 --- a/BattleBitAPI/Server/ServerListener.cs +++ b/BattleBitAPI/Server/ServerListener.cs @@ -1,10 +1,11 @@ using System.Net; using System.Net.Sockets; using System.Numerics; -using System.Runtime.CompilerServices; +using System.Resources; using BattleBitAPI.Common; using BattleBitAPI.Common.Extentions; using BattleBitAPI.Networking; +using BattleBitAPI.Pooling; namespace BattleBitAPI.Server { @@ -55,15 +56,6 @@ namespace BattleBitAPI.Server /// public Func, Task> OnGameServerConnected { get; set; } - /// - /// Fired when a game server reconnects. (When game server connects while a socket is already open) - /// - /// - /// - /// GameServer: Game server that is reconnecting.
- ///
- public Func, Task> OnGameServerReconnected { get; set; } - /// /// Fired when a game server disconnects. Check (GameServer.TerminationReason) to see the reason. /// @@ -97,12 +89,14 @@ namespace BattleBitAPI.Server private TcpListener mSocket; private Dictionary.Internal resources)> mActiveConnections; private mInstances mInstanceDatabase; + private ItemPooling> mGameServerPool; // --- Construction --- public ServerListener() { this.mActiveConnections = new Dictionary.Internal)>(16); this.mInstanceDatabase = new mInstances(); + this.mGameServerPool = new ItemPooling>(64); } // --- Starting --- @@ -160,10 +154,12 @@ namespace BattleBitAPI.Server { var ip = (client.Client.RemoteEndPoint as IPEndPoint).Address; + //Is this IP allowed? bool allow = true; if (OnGameServerConnecting != null) allow = await OnGameServerConnecting(ip); + //Close connection if it was not allowed. if (!allow) { //Connection is not allowed from this IP. @@ -171,11 +167,13 @@ namespace BattleBitAPI.Server return; } - TGameServer server = null; - GameServer.Internal resources; + //Read port,token,version + string token; + string version; + int gamePort; try { - using (CancellationTokenSource source = new CancellationTokenSource(Const.HailConnectTimeout)) + using (var source = new CancellationTokenSource(2000)) { using (var readStream = Common.Serialization.Stream.Get()) { @@ -186,13 +184,13 @@ namespace BattleBitAPI.Server readStream.Reset(); if (!await networkStream.TryRead(readStream, 1, source.Token)) throw new Exception("Unable to read the package type"); + NetworkCommuncation type = (NetworkCommuncation)readStream.ReadInt8(); if (type != NetworkCommuncation.Hail) throw new Exception("Incoming package wasn't hail."); } //Read the server token - string token; { readStream.Reset(); if (!await networkStream.TryRead(readStream, 2, source.Token)) @@ -210,7 +208,6 @@ namespace BattleBitAPI.Server } //Read the server version - string version; { readStream.Reset(); if (!await networkStream.TryRead(readStream, 2, source.Token)) @@ -227,23 +224,59 @@ namespace BattleBitAPI.Server version = readStream.ReadString(stringSize); } - if (version != Const.Version) - throw new Exception("Incoming server's version `" + version + "` does not match with current API version `" + Const.Version + "`"); - //Read port - int gamePort; { readStream.Reset(); if (!await networkStream.TryRead(readStream, 2, source.Token)) throw new Exception("Unable to read the Port"); gamePort = readStream.ReadUInt16(); } + } + } + } + catch { client.SafeClose(); return; } - if (OnValidateGameServerToken != null) - allow = await OnValidateGameServerToken(ip, (ushort)gamePort, token); + var hash = ((ulong)gamePort << 32) | (ulong)ip.ToUInt(); + TGameServer server = null; + GameServer.Internal resources = null; + try + { + //Does versions match? + if (version != Const.Version) + throw new Exception("Incoming server's version `" + version + "` does not match with current API version `" + Const.Version + "`"); - if (!allow) - throw new Exception("Token was not valid!"); + //Is valid token? + if (OnValidateGameServerToken != null) + { + if (!await OnValidateGameServerToken(ip, (ushort)gamePort, token)) + throw new Exception("Token was not valid!"); + } + + //Are there any connections with same IP and port? + { + bool sessionExist = false; + (TGameServer server, GameServer.Internal resources) oldSession; + + //Any sessions with this IP:Port? + lock (this.mActiveConnections) + sessionExist = this.mActiveConnections.TryGetValue(hash, out oldSession); + + if (sessionExist) + { + //Close old session. + oldSession.server.CloseConnection("Reconnecting."); + + //Wait until session is fully closed. + while (oldSession.resources.HasActiveConnectionSession) + await Task.Delay(1); + } + } + + using (var source = new CancellationTokenSource(Const.HailConnectTimeout)) + { + using (var readStream = Common.Serialization.Stream.Get()) + { + var networkStream = client.GetStream(); //Read is server protected bool isPasswordProtected; @@ -403,7 +436,28 @@ namespace BattleBitAPI.Server } } - var hash = ((ulong)gamePort << 32) | (ulong)ip.ToUInt(); + //Round index + uint roundIndex; + { + readStream.Reset(); + if (!await networkStream.TryRead(readStream, 4, source.Token)) + throw new Exception("Unable to read the Server Round Index"); + roundIndex = readStream.ReadUInt32(); + } + + //Round index + long sessionID; + { + readStream.Reset(); + if (!await networkStream.TryRead(readStream, 8, source.Token)) + throw new Exception("Unable to read the Server Round ID"); + sessionID = readStream.ReadInt64(); + } + + + + + server = this.mInstanceDatabase.GetServerInstance(hash, out resources, this.OnCreatingGameServerInstance, ip, (ushort)gamePort); resources.Set( this.mExecutePackage, @@ -421,7 +475,9 @@ namespace BattleBitAPI.Server queuePlayers, maxPlayers, loadingScreenText, - serverRulesText + serverRulesText, + roundIndex, + sessionID ); //Room settings @@ -583,7 +639,6 @@ namespace BattleBitAPI.Server playerInternal.SteamID = steamid; playerInternal.Name = username; playerInternal.IP = new IPAddress(ipHash); - playerInternal.GameServer = (GameServer)server; playerInternal.Team = team; playerInternal.SquadName = squad; playerInternal.Role = role; @@ -604,6 +659,9 @@ namespace BattleBitAPI.Server playerInternal._Modifications.Read(readStream); } + playerInternal.GameServer = (GameServer)server; + playerInternal.SessionID = server.SessionID; + resources.AddPlayer(player); } @@ -669,37 +727,6 @@ namespace BattleBitAPI.Server return; } - bool connectionExist = false; - - //Track the connection - lock (this.mActiveConnections) - { - //An old connection exist with same IP + Port? - if (connectionExist = this.mActiveConnections.TryGetValue(server.ServerHash, out var oldServer)) - { - oldServer.resources.ReconnectFlag = true; - this.mActiveConnections.Remove(server.ServerHash); - } - - this.mActiveConnections.Add(server.ServerHash, (server, resources)); - } - - //Call the callback. - if (!connectionExist) - { - //New connection! - server.OnConnected(); - if (this.OnGameServerConnected != null) - this.OnGameServerConnected(server); - } - else - { - //Reconnection - server.OnReconnected(); - if (this.OnGameServerReconnected != null) - this.OnGameServerReconnected(server); - } - //Set the buffer sizes. client.ReceiveBufferSize = Const.MaxNetworkPackageSize; client.SendBufferSize = Const.MaxNetworkPackageSize; @@ -709,39 +736,77 @@ namespace BattleBitAPI.Server } private async Task mHandleGameServer(TGameServer server, GameServer.Internal @internal) { - bool isTicking = false; - - using (server) + @internal.HasActiveConnectionSession = true; { - async Task mTickAsync() + // ---- Connected ---- { - isTicking = true; - await server.OnTick(); - isTicking = false; + lock (this.mActiveConnections) + this.mActiveConnections.Replace(server.ServerHash, (server, @internal)); + + server.OnConnected(); + if (this.OnGameServerConnected != null) + this.OnGameServerConnected(server); } - while (server.IsConnected) + //Update sessions { - if (!isTicking) - mTickAsync(); + if (@internal.mPreviousSessionID != @internal.SessionID) + { + var oldSession = @internal.mPreviousSessionID; + @internal.mPreviousSessionID = @internal.SessionID; - await server.Tick(); - await Task.Delay(10); + if (oldSession != 0) + server.OnSessionChanged(oldSession, @internal.SessionID); + } + + foreach (var item in @internal.Players) + { + var @player_internal = mInstanceDatabase.GetPlayerInternals(item.Key); + if (@player_internal.PreviousSessionID != @player_internal.SessionID) + { + var previousID = @player_internal.PreviousSessionID; + @player_internal.PreviousSessionID = @player_internal.SessionID; + + if (previousID != 0) + item.Value.OnSessionChanged(previousID, @player_internal.SessionID); + } + } } - if (!server.ReconnectFlag) + // ---- Ticking ---- + using (server) { + var isTicking = false; + async Task mTickAsync() + { + isTicking = true; + await server.OnTick(); + isTicking = false; + } + + while (server.IsConnected) + { + if (!isTicking) + mTickAsync(); + + await server.Tick(); + await Task.Delay(10); + } + } + + // ---- Disconnected ---- + { + mCleanup(server, @internal); + + lock (this.mActiveConnections) + this.mActiveConnections.Remove(server.ServerHash); + server.OnDisconnected(); - if (this.OnGameServerDisconnected != null) this.OnGameServerDisconnected(server); } } - - //Remove from list. - if (!server.ReconnectFlag) - lock (this.mActiveConnections) - this.mActiveConnections.Remove(server.ServerHash); + @internal.HasActiveConnectionSession = false; } // --- Logic Executing --- @@ -766,8 +831,6 @@ namespace BattleBitAPI.Server playerInternal.SteamID = steamID; playerInternal.Name = username; playerInternal.IP = new IPAddress(ip); - playerInternal.GameServer = (GameServer)server; - playerInternal.Team = team; playerInternal.SquadName = squad; playerInternal.Role = role; @@ -775,9 +838,21 @@ namespace BattleBitAPI.Server //Start from default. playerInternal._Modifications.Reset(); + playerInternal.GameServer = (GameServer)server; + playerInternal.SessionID = server.SessionID; + resources.AddPlayer(player); player.OnConnected(); server.OnPlayerConnected(player); + + if (playerInternal.PreviousSessionID != playerInternal.SessionID) + { + var previousID = playerInternal.PreviousSessionID; + playerInternal.PreviousSessionID = playerInternal.SessionID; + + if (previousID != 0) + player.OnSessionChanged(previousID, playerInternal.SessionID); + } } } break; @@ -816,6 +891,9 @@ namespace BattleBitAPI.Server server.OnPlayerLeftSquad((TPlayer)player, msquad); } + @internal.SessionID = 0; + @internal.GameServer = null; + player.OnDisconnected(); server.OnPlayerDisconnected((TPlayer)player); } @@ -1330,10 +1408,55 @@ namespace BattleBitAPI.Server } break; } + case NetworkCommuncation.NotifyNewRoundID: + { + if (stream.CanRead(4 + 8)) + { + resources.RoundIndex = stream.ReadUInt32(); + resources.SessionID = stream.ReadInt64(); + + if (resources.mPreviousSessionID != resources.SessionID) + { + var oldSession = resources.mPreviousSessionID; + resources.mPreviousSessionID = resources.SessionID; + + if (oldSession != 0) + server.OnSessionChanged(oldSession, resources.SessionID); + } + + foreach (var item in resources.Players) + { + var @player_internal = mInstanceDatabase.GetPlayerInternals(item.Key); + @player_internal.SessionID = resources.SessionID; + + if (@player_internal.PreviousSessionID != @player_internal.SessionID) + { + var previousID = @player_internal.PreviousSessionID; + @player_internal.PreviousSessionID = @player_internal.SessionID; + + if (previousID != 0) + item.Value.OnSessionChanged(previousID, @player_internal.SessionID); + } + } + } + break; + } } } // --- Private --- + private void mCleanup(GameServer server, GameServer.Internal @internal) + { + lock (@internal.Players) + { + foreach (var item in @internal.Players) + { + var @player_internal = mInstanceDatabase.GetPlayerInternals(item.Key); + @player_internal.SessionID = 0; + @player_internal.GameServer = null; + } + } + } private Player.Internal mGetPlayerInternals(ulong steamID) { return mInstanceDatabase.GetPlayerInternals(steamID); @@ -1344,13 +1467,17 @@ namespace BattleBitAPI.Server { get { - var list = new List(mActiveConnections.Count); - lock (mActiveConnections) + using (var list = this.mGameServerPool.Get()) { - foreach (var item in mActiveConnections.Values) - list.Add(item.server); + //Get a copy + lock (mActiveConnections) + foreach (var item in mActiveConnections.Values) + list.ListItems.Add(item.server); + + //Iterate + for (int i = 0; i < list.ListItems.Count; i++) + yield return (TGameServer)list.ListItems[i]; } - return list; } } public bool TryGetGameServer(IPAddress ip, ushort port, out TGameServer server) From c603b80d26128aa12761c998665a59d91434804d Mon Sep 17 00:00:00 2001 From: MrOkiDoki <0mrokidoki@gmail.com> Date: Sun, 20 Aug 2023 16:00:51 +0300 Subject: [PATCH 3/3] Logging added. --- BattleBitAPI/Common/Enums/LogLevel.cs | 53 +++++ .../Networking/NetworkCommuncation.cs | 1 + BattleBitAPI/Server/Internal/Squad.cs | 2 +- BattleBitAPI/Server/ServerListener.cs | 197 +++++++++++++++--- CommunityServerAPI.csproj | 2 +- 5 files changed, 220 insertions(+), 35 deletions(-) create mode 100644 BattleBitAPI/Common/Enums/LogLevel.cs diff --git a/BattleBitAPI/Common/Enums/LogLevel.cs b/BattleBitAPI/Common/Enums/LogLevel.cs new file mode 100644 index 0000000..5ed5849 --- /dev/null +++ b/BattleBitAPI/Common/Enums/LogLevel.cs @@ -0,0 +1,53 @@ +namespace BattleBitAPI.Common +{ + [System.Flags] + public enum LogLevel : ulong + { + None = 0, + + /// + /// Output logs from low level sockets. + /// + Sockets = 1 << 0, + + /// + /// Output logs from remote game server (Highly recommended) + /// + GameServerErrors = 1 << 1, + + /// + /// Output logs of game server connects, reconnects. + /// + GameServers = 1 << 2, + + /// + /// Output logs of player connects, disconnects + /// + Players = 1 << 3, + + /// + /// Output logs of squad changes (someone joining, leaving etc) + /// + Squads = 1 << 4, + + /// + /// Output logs of kills/giveups/revives. + /// + KillsAndSpawns = 1 << 5, + + /// + /// Output logs of role changes (player changing role to medic, support etc). + /// + Roles = 1 << 6, + + /// + /// Output logs player's healt changes. (When received damage or healed) + /// + HealtChanges = 1 << 7, + + /// + /// Output everything. + /// + All = ulong.MaxValue, + } +} diff --git a/BattleBitAPI/Networking/NetworkCommuncation.cs b/BattleBitAPI/Networking/NetworkCommuncation.cs index f30cbe0..f9c1be8 100644 --- a/BattleBitAPI/Networking/NetworkCommuncation.cs +++ b/BattleBitAPI/Networking/NetworkCommuncation.cs @@ -41,5 +41,6 @@ OnPlayerRevivedAnother = 71, OnSquadPointsChanged = 72, NotifyNewRoundID = 73, + Log = 74, } } diff --git a/BattleBitAPI/Server/Internal/Squad.cs b/BattleBitAPI/Server/Internal/Squad.cs index b29583d..c21089c 100644 --- a/BattleBitAPI/Server/Internal/Squad.cs +++ b/BattleBitAPI/Server/Internal/Squad.cs @@ -29,7 +29,7 @@ namespace BattleBitAPI.Server public override string ToString() { - return Team + " : " + Name; + return "Squad " + Name; } // ---- Internal ---- diff --git a/BattleBitAPI/Server/ServerListener.cs b/BattleBitAPI/Server/ServerListener.cs index b75c3b3..8845126 100644 --- a/BattleBitAPI/Server/ServerListener.cs +++ b/BattleBitAPI/Server/ServerListener.cs @@ -1,4 +1,5 @@ -using System.Net; +using System.Data; +using System.Net; using System.Net.Sockets; using System.Numerics; using System.Resources; @@ -15,6 +16,7 @@ namespace BattleBitAPI.Server public bool IsListening { get; private set; } public bool IsDisposed { get; private set; } public int ListeningPort { get; private set; } + public LogLevel LogLevel { get; set; } = LogLevel.None; // --- Events --- /// @@ -85,6 +87,17 @@ namespace BattleBitAPI.Server /// public Func OnCreatingPlayerInstance { get; set; } + /// + /// Fired on log + /// + /// + /// + /// LogLevel: The level of log
+ /// string: The message
+ /// object: The object that will be carried on log
+ ///
+ public Action OnLog { get; set; } + // --- Private --- private TcpListener mSocket; private Dictionary.Internal resources)> mActiveConnections; @@ -115,6 +128,9 @@ namespace BattleBitAPI.Server this.ListeningPort = port; this.IsListening = true; + if (this.LogLevel.HasFlag(LogLevel.Sockets)) + OnLog(LogLevel.Sockets, $"Listening TCP connections on port " + port, null); + mMainLoop(); } public void Start(int port) @@ -136,6 +152,9 @@ namespace BattleBitAPI.Server } catch { } + if (this.LogLevel.HasFlag(LogLevel.Sockets)) + OnLog(LogLevel.Sockets, $"Stopped listening TCP connection.", null); + this.mSocket = null; this.ListeningPort = 0; this.IsListening = true; @@ -154,6 +173,9 @@ namespace BattleBitAPI.Server { var ip = (client.Client.RemoteEndPoint as IPEndPoint).Address; + if (this.LogLevel.HasFlag(LogLevel.Sockets)) + OnLog(LogLevel.Sockets, $"Incoming TCP connection from {ip}", client); + //Is this IP allowed? bool allow = true; if (OnGameServerConnecting != null) @@ -162,6 +184,9 @@ namespace BattleBitAPI.Server //Close connection if it was not allowed. if (!allow) { + if (this.LogLevel.HasFlag(LogLevel.Sockets)) + OnLog(LogLevel.Sockets, $"Incoming connection from {ip} was denied", client); + //Connection is not allowed from this IP. client.SafeClose(); return; @@ -234,7 +259,14 @@ namespace BattleBitAPI.Server } } } - catch { client.SafeClose(); return; } + catch (Exception e) + { + if (this.LogLevel.HasFlag(LogLevel.Sockets)) + OnLog(LogLevel.Sockets, $"{ip} failed to connected because " + e.Message, client); + + client.SafeClose(); + return; + } var hash = ((ulong)gamePort << 32) | (ulong)ip.ToUInt(); TGameServer server = null; @@ -723,6 +755,9 @@ namespace BattleBitAPI.Server } catch { } + if (this.LogLevel.HasFlag(LogLevel.Sockets)) + OnLog(LogLevel.Sockets, $"{ip} failed to connected because " + e.Message, client); + client.SafeClose(); return; } @@ -731,6 +766,9 @@ namespace BattleBitAPI.Server client.ReceiveBufferSize = Const.MaxNetworkPackageSize; client.SendBufferSize = Const.MaxNetworkPackageSize; + if (this.LogLevel.HasFlag(LogLevel.Sockets)) + OnLog(LogLevel.Sockets, $"Incoming game server from {ip}:{gamePort} accepted.", client); + //Join to main server loop. await mHandleGameServer(server, resources); } @@ -773,6 +811,9 @@ namespace BattleBitAPI.Server } } + if (this.LogLevel.HasFlag(LogLevel.GameServers)) + OnLog(LogLevel.GameServers, $"{server} has connected", server); + // ---- Ticking ---- using (server) { @@ -805,6 +846,9 @@ namespace BattleBitAPI.Server if (this.OnGameServerDisconnected != null) this.OnGameServerDisconnected(server); } + + if (this.LogLevel.HasFlag(LogLevel.GameServers)) + OnLog(LogLevel.GameServers, $"{server} has disconnected", server); } @internal.HasActiveConnectionSession = false; } @@ -853,6 +897,9 @@ namespace BattleBitAPI.Server if (previousID != 0) player.OnSessionChanged(previousID, playerInternal.SessionID); } + + if (this.LogLevel.HasFlag(LogLevel.Players)) + OnLog(LogLevel.Players, $"{player} has connected", player); } } break; @@ -896,6 +943,9 @@ namespace BattleBitAPI.Server player.OnDisconnected(); server.OnPlayerDisconnected((TPlayer)player); + + if (this.LogLevel.HasFlag(LogLevel.Players)) + OnLog(LogLevel.Players, $"{player} has disconnected", player); } } break; @@ -965,6 +1015,9 @@ namespace BattleBitAPI.Server victimClient.OnDowned(); server.OnAPlayerDownedAnotherPlayer(args); + + if (this.LogLevel.HasFlag(LogLevel.KillsAndSpawns)) + OnLog(LogLevel.KillsAndSpawns, $"{killer} downed {victim} in {(Vector3.Distance(killerPos, victimPos))} meters", null); } } } @@ -1023,13 +1076,19 @@ namespace BattleBitAPI.Server ulong steamID = stream.ReadUInt64(); GameRole role = (GameRole)stream.ReadInt8(); - if (resources.TryGetPlayer(steamID, out var client)) + if (resources.TryGetPlayer(steamID, out var player)) { async Task mHandle() { - bool accepted = await server.OnPlayerRequestingToChangeRole((TPlayer)client, role); + if (this.LogLevel.HasFlag(LogLevel.Roles)) + OnLog(LogLevel.Roles, $"{player} asking to change role to {role}", player); + + bool accepted = await server.OnPlayerRequestingToChangeRole((TPlayer)player, role); if (accepted) server.SetRoleTo(steamID, role); + + if (this.LogLevel.HasFlag(LogLevel.Roles)) + OnLog(LogLevel.Roles, $"{player}'s request to change role to {role} was {(accepted ? "accepted" : "denied")}", player); } mHandle(); @@ -1044,13 +1103,16 @@ namespace BattleBitAPI.Server ulong steamID = stream.ReadUInt64(); GameRole role = (GameRole)stream.ReadInt8(); - if (resources.TryGetPlayer(steamID, out var client)) + if (resources.TryGetPlayer(steamID, out var player)) { var @internal = mInstanceDatabase.GetPlayerInternals(steamID); @internal.Role = role; - client.OnChangedRole(role); - server.OnPlayerChangedRole((TPlayer)client, role); + player.OnChangedRole(role); + server.OnPlayerChangedRole((TPlayer)player, role); + + if (this.LogLevel.HasFlag(LogLevel.Roles)) + OnLog(LogLevel.Roles, $"{player} changed role to {role}", player); } } break; @@ -1062,18 +1124,21 @@ namespace BattleBitAPI.Server ulong steamID = stream.ReadUInt64(); Squads squad = (Squads)stream.ReadInt8(); - if (resources.TryGetPlayer(steamID, out var client)) + if (resources.TryGetPlayer(steamID, out var player)) { var @internal = mInstanceDatabase.GetPlayerInternals(steamID); @internal.SquadName = squad; - var msquad = server.GetSquad(client.Team, squad); + var msquad = server.GetSquad(player.Team, squad); var rsquad = resources.GetSquadInternal(msquad); lock (rsquad.Members) - rsquad.Members.Add((TPlayer)client); + rsquad.Members.Add((TPlayer)player); - client.OnJoinedSquad(msquad); - server.OnPlayerJoinedSquad((TPlayer)client, msquad); + player.OnJoinedSquad(msquad); + server.OnPlayerJoinedSquad((TPlayer)player, msquad); + + if (this.LogLevel.HasFlag(LogLevel.Squads)) + OnLog(LogLevel.Squads, $"{player} has joined to {msquad}", msquad); } } break; @@ -1084,30 +1149,33 @@ namespace BattleBitAPI.Server { ulong steamID = stream.ReadUInt64(); - if (resources.TryGetPlayer(steamID, out var client)) + if (resources.TryGetPlayer(steamID, out var player)) { var @internal = mInstanceDatabase.GetPlayerInternals(steamID); - var oldSquad = client.SquadName; - var oldRole = client.Role; + var oldSquad = player.SquadName; + var oldRole = player.Role; @internal.SquadName = Squads.NoSquad; @internal.Role = GameRole.Assault; - var msquad = server.GetSquad(client.Team, oldSquad); + var msquad = server.GetSquad(player.Team, oldSquad); var rsquad = resources.GetSquadInternal(msquad); @internal.SquadName = Squads.NoSquad; lock (rsquad.Members) - rsquad.Members.Remove((TPlayer)client); + rsquad.Members.Remove((TPlayer)player); - client.OnLeftSquad(msquad); - server.OnPlayerLeftSquad((TPlayer)client, msquad); + player.OnLeftSquad(msquad); + server.OnPlayerLeftSquad((TPlayer)player, msquad); if (oldRole != GameRole.Assault) { - client.OnChangedRole(GameRole.Assault); - server.OnPlayerChangedRole((TPlayer)client, GameRole.Assault); + player.OnChangedRole(GameRole.Assault); + server.OnPlayerChangedRole((TPlayer)player, GameRole.Assault); } + + if (this.LogLevel.HasFlag(LogLevel.Squads)) + OnLog(LogLevel.Squads, $"{player} has left the {msquad}", msquad); } } break; @@ -1141,11 +1209,14 @@ namespace BattleBitAPI.Server request.Read(stream); ushort vehicleID = stream.ReadUInt16(); - if (resources.TryGetPlayer(steamID, out var client)) + if (resources.TryGetPlayer(steamID, out var player)) { async Task mHandle() { - var responseSpawn = await server.OnPlayerSpawning((TPlayer)client, request); + if (this.LogLevel.HasFlag(LogLevel.KillsAndSpawns)) + OnLog(LogLevel.KillsAndSpawns, $"{player} asking to spawn at {request.SpawnPosition} ({request.RequestedPoint})", player); + + var responseSpawn = await server.OnPlayerSpawning((TPlayer)player, request); //Respond back. using (var response = Common.Serialization.Stream.Get()) @@ -1166,6 +1237,15 @@ namespace BattleBitAPI.Server server.WriteToSocket(response); } + + if (this.LogLevel.HasFlag(LogLevel.KillsAndSpawns)) + { + if (responseSpawn == null) + OnLog(LogLevel.KillsAndSpawns, $"{player}'s spawn request was denied", player); + else + OnLog(LogLevel.KillsAndSpawns, $"{player}'s spawn request was accepted at {responseSpawn.Value.SpawnPosition}", player); + } + } mHandle(); @@ -1197,7 +1277,7 @@ namespace BattleBitAPI.Server if (stream.CanRead(8 + 2)) { ulong steamID = stream.ReadUInt64(); - if (resources.TryGetPlayer(steamID, out var client)) + if (resources.TryGetPlayer(steamID, out var player)) { var @internal = mInstanceDatabase.GetPlayerInternals(steamID); @@ -1209,10 +1289,21 @@ namespace BattleBitAPI.Server wearings.Read(stream); @internal.CurrentWearings = wearings; + Vector3 position = new Vector3() + { + X = stream.ReadFloat(), + Y = stream.ReadFloat(), + Z = stream.ReadFloat(), + }; + + @internal.Position = position; @internal.IsAlive = true; - client.OnSpawned(); - server.OnPlayerSpawned((TPlayer)client); + player.OnSpawned(); + server.OnPlayerSpawned((TPlayer)player); + + if (this.LogLevel.HasFlag(LogLevel.KillsAndSpawns)) + OnLog(LogLevel.KillsAndSpawns, $"{player} has spawned at {player.Position}", player); } } break; @@ -1222,13 +1313,16 @@ namespace BattleBitAPI.Server if (stream.CanRead(8)) { ulong steamid = stream.ReadUInt64(); - if (resources.TryGetPlayer(steamid, out var client)) + if (resources.TryGetPlayer(steamid, out var player)) { var @internal = mInstanceDatabase.GetPlayerInternals(steamid); @internal.OnDie(); - client.OnDied(); - server.OnPlayerDied((TPlayer)client); + player.OnDied(); + server.OnPlayerDied((TPlayer)player); + + if (this.LogLevel.HasFlag(LogLevel.KillsAndSpawns)) + OnLog(LogLevel.KillsAndSpawns, $"{player} has died", player); } } break; @@ -1339,19 +1433,38 @@ namespace BattleBitAPI.Server var @internal = mInstanceDatabase.GetPlayerInternals(steamID); if (@internal.IsAlive) { + float newHP = (com_healt * 0.5f) - 1f; + + if (this.LogLevel.HasFlag(LogLevel.HealtChanges)) + { + var player = resources.Players[steamID]; + float dtHP = newHP - @internal.HP; + if (dtHP > 0) + { + //Heal + OnLog(LogLevel.HealtChanges, $"{player} was healed by {dtHP} HP (new HP is {newHP} HP)", player); + } + else if(dtHP < 0) + { + //Damage + OnLog(LogLevel.HealtChanges, $"{player} was damaged by {(-dtHP)} HP (new HP is {newHP} HP)", player); + } + } + @internal.Position = new Vector3() { X = com_posX * decompressX, Y = com_posY * decompressY, Z = com_posZ * decompressZ, }; - @internal.HP = (com_healt * 0.5f) - 1f; + @internal.HP = newHP; @internal.Standing = standing; @internal.Leaning = side; @internal.CurrentLoadoutIndex = loadoutIndex; @internal.InVehicle = inSeat; @internal.IsBleeding = isBleeding; @internal.PingMs = ping; + } } } @@ -1362,10 +1475,13 @@ namespace BattleBitAPI.Server if (stream.CanRead(8)) { ulong steamID = stream.ReadUInt64(); - if (resources.TryGetPlayer(steamID, out var client)) + if (resources.TryGetPlayer(steamID, out var player)) { - client.OnGivenUp(); - server.OnPlayerGivenUp((TPlayer)client); + player.OnGivenUp(); + server.OnPlayerGivenUp((TPlayer)player); + + if (this.LogLevel.HasFlag(LogLevel.KillsAndSpawns)) + OnLog(LogLevel.KillsAndSpawns, $"{player} has givenup", player); } } break; @@ -1384,6 +1500,9 @@ namespace BattleBitAPI.Server { fromClient.OnRevivedAnotherPlayer(); server.OnAPlayerRevivedAnotherPlayer((TPlayer)fromClient, (TPlayer)toClient); + + if (this.LogLevel.HasFlag(LogLevel.KillsAndSpawns)) + OnLog(LogLevel.KillsAndSpawns, $"{fromClient} revived {toClient}", null); } } } @@ -1405,6 +1524,9 @@ namespace BattleBitAPI.Server rsquad.SquadPoints = points; server.OnSquadPointsChanged(msquad, points); } + + if (this.LogLevel.HasFlag(LogLevel.Squads)) + OnLog(LogLevel.Squads, $"{msquad} now has {points} points", msquad); } break; } @@ -1441,6 +1563,15 @@ namespace BattleBitAPI.Server } break; } + case NetworkCommuncation.Log: + { + if (this.LogLevel.HasFlag(LogLevel.GameServerErrors)) + { + if (stream.TryReadString(out var log)) + OnLog(LogLevel.GameServerErrors, log, server); + } + break; + } } } diff --git a/CommunityServerAPI.csproj b/CommunityServerAPI.csproj index 52a520b..6c2fbc5 100644 --- a/CommunityServerAPI.csproj +++ b/CommunityServerAPI.csproj @@ -18,7 +18,7 @@ https://github.com/MrOkiDoki/BattleBit-Community-Server-API https://github.com/MrOkiDoki/BattleBit-Community-Server-API BattleBit - 1.0.1 + 1.0.2