From ad05ac8741ee37c0cc86aa80c0fca206b77515e7 Mon Sep 17 00:00:00 2001 From: MrOkiDoki <0mrokidoki@gmail.com> Date: Sun, 20 Aug 2023 10:39:00 +0300 Subject: [PATCH] Squad implementation --- BattleBitAPI/Common/Conts.cs | 2 + .../Networking/NetworkCommuncation.cs | 1 + BattleBitAPI/Pooling/ItemPooling.cs | 47 ++ BattleBitAPI/Server/GameServer.cs | 481 +++++++++++++++++- BattleBitAPI/Server/Internal/Squad.cs | 58 +++ BattleBitAPI/Server/Player.cs | 32 +- BattleBitAPI/Server/ServerListener.cs | 145 +++++- CommunityServerAPI.csproj | 21 +- CommunityServerAPI.sln | 8 +- Program.cs | 112 ---- 10 files changed, 756 insertions(+), 151 deletions(-) create mode 100644 BattleBitAPI/Pooling/ItemPooling.cs create mode 100644 BattleBitAPI/Server/Internal/Squad.cs delete mode 100644 Program.cs diff --git a/BattleBitAPI/Common/Conts.cs b/BattleBitAPI/Common/Conts.cs index 4abba28..e339257 100644 --- a/BattleBitAPI/Common/Conts.cs +++ b/BattleBitAPI/Common/Conts.cs @@ -2,6 +2,8 @@ { public static class Const { + public static string Version = "1.0.1v"; + // ---- Networking ---- /// /// Maximum data size for a single package. 4MB is default. diff --git a/BattleBitAPI/Networking/NetworkCommuncation.cs b/BattleBitAPI/Networking/NetworkCommuncation.cs index 1199b5a..69f4304 100644 --- a/BattleBitAPI/Networking/NetworkCommuncation.cs +++ b/BattleBitAPI/Networking/NetworkCommuncation.cs @@ -39,5 +39,6 @@ GameTick = 69, OnPlayerGivenUp = 70, OnPlayerRevivedAnother = 71, + OnSquadPointsChanged = 72, } } diff --git a/BattleBitAPI/Pooling/ItemPooling.cs b/BattleBitAPI/Pooling/ItemPooling.cs new file mode 100644 index 0000000..4b1bce0 --- /dev/null +++ b/BattleBitAPI/Pooling/ItemPooling.cs @@ -0,0 +1,47 @@ +namespace BattleBitAPI.Pooling +{ + public class ItemPooling + { + private Queue.List> mPool; + private int mDefaultCount; + public ItemPooling(int defaultCount) + { + this.mPool = new Queue.List>(6); + this.mDefaultCount = defaultCount; + } + + public ItemPooling.List Get() + { + lock (mPool) + { + if (mPool.Count > 0) + return mPool.Dequeue(); + } + return new ItemPooling.List(this, mDefaultCount); + } + public void Post(ItemPooling.List item) + { + lock (mPool) + mPool.Enqueue(item); + } + + public class List : IDisposable + { + private ItemPooling mParent; + public List ListItems; + + public List(ItemPooling parent, int count) + { + this.mParent = parent; + this.ListItems = new List(count); + } + + + public void Dispose() + { + ListItems.Clear(); + mParent.Post(this); + } + } + } +} diff --git a/BattleBitAPI/Server/GameServer.cs b/BattleBitAPI/Server/GameServer.cs index 34cda73..c437f95 100644 --- a/BattleBitAPI/Server/GameServer.cs +++ b/BattleBitAPI/Server/GameServer.cs @@ -1,4 +1,6 @@ -using System.Diagnostics; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Linq; using System.Net; using System.Net.Sockets; using System.Numerics; @@ -6,6 +8,7 @@ using System.Text; using BattleBitAPI.Common; using BattleBitAPI.Common.Extentions; using BattleBitAPI.Networking; +using BattleBitAPI.Pooling; using CommunityServerAPI.BattleBitAPI; namespace BattleBitAPI.Server @@ -36,6 +39,32 @@ namespace BattleBitAPI.Server public RoundSettings RoundSettings => mInternal.RoundSettings; public string TerminationReason => mInternal.TerminationReason; public bool ReconnectFlag => mInternal.ReconnectFlag; + public IEnumerable> TeamASquads + { + get + { + for (int i = 1; i < this.mInternal.TeamASquads.Length; i++) + yield return this.mInternal.TeamASquads[i]; + } + } + public IEnumerable> TeamBSquads + { + get + { + for (int i = 1; i < this.mInternal.TeamBSquads.Length; i++) + yield return this.mInternal.TeamBSquads[i]; + } + } + public IEnumerable> AllSquads + { + get + { + for (int i = 1; i < this.mInternal.TeamASquads.Length; i++) + yield return this.mInternal.TeamASquads[i]; + for (int i = 1; i < this.mInternal.TeamBSquads.Length; i++) + yield return this.mInternal.TeamBSquads[i]; + } + } // ---- Private Variables ---- private Internal mInternal; @@ -240,15 +269,123 @@ namespace BattleBitAPI.Server { get { - var list = new List(this.mInternal.Players.Values.Count); - lock (this.mInternal.Players) + using (var list = this.mInternal.PlayerPool.Get()) { - foreach (var item in this.mInternal.Players.Values) - list.Add((TPlayer)item); + //Get A copy of players to our list + lock (this.mInternal.Players) + list.ListItems.AddRange(this.mInternal.Players.Values); + + //Iterate our list. + for (int i = 0; i < list.ListItems.Count; i++) + yield return (TPlayer)list.ListItems[i]; } - return list; } } + public IEnumerable AllTeamAPlayers + { + get + { + using (var list = this.mInternal.PlayerPool.Get()) + { + //Get A copy of players to our list + lock (this.mInternal.Players) + list.ListItems.AddRange(this.mInternal.Players.Values); + + //Iterate our list. + for (int i = 0; i < list.ListItems.Count; i++) + { + var item = list.ListItems[i]; + if (item.Team == Team.TeamA) + yield return (TPlayer)item; + } + } + } + } + public IEnumerable AllTeamBPlayers + { + get + { + using (var list = this.mInternal.PlayerPool.Get()) + { + //Get A copy of players to our list + lock (this.mInternal.Players) + list.ListItems.AddRange(this.mInternal.Players.Values); + + //Iterate our list. + for (int i = 0; i < list.ListItems.Count; i++) + { + var item = list.ListItems[i]; + if (item.Team == Team.TeamB) + yield return (TPlayer)item; + } + } + } + } + public IEnumerable PlayersOf(Team team) + { + using (var list = this.mInternal.PlayerPool.Get()) + { + //Get A copy of players to our list + lock (this.mInternal.Players) + list.ListItems.AddRange(this.mInternal.Players.Values); + + //Iterate our list. + for (int i = 0; i < list.ListItems.Count; i++) + { + var item = list.ListItems[i]; + if (item.Team == team) + yield return (TPlayer)item; + } + } + } + public IEnumerable SearchPlayerByName(string keyword) + { + keyword = keyword.ToLower().Replace(" ", ""); + + using (var list = this.mInternal.PlayerPool.Get()) + { + //Get A copy of players to our list + lock (this.mInternal.Players) + list.ListItems.AddRange(this.mInternal.Players.Values); + + //Iterate our list. + for (int i = 0; i < list.ListItems.Count; i++) + { + var item = list.ListItems[i]; + if (item.Name.ToLower().Replace(" ", "").Contains(keyword)) + yield return (TPlayer)item; + } + } + } + public IEnumerable SearchPlayerByName(params string[] keywords) + { + for (int i = 0; i < keywords.Length; i++) + keywords[i] = keywords[i].ToLower().Replace(" ", ""); + + using (var list = this.mInternal.PlayerPool.Get()) + { + //Get A copy of players to our list + lock (this.mInternal.Players) + list.ListItems.AddRange(this.mInternal.Players.Values); + + //Iterate our list. + for (int i = 0; i < list.ListItems.Count; i++) + { + var item = list.ListItems[i]; + var lowerName = item.Name.ToLower().Replace(" ", ""); + + for (int x = 0; x < keywords.Length; x++) + { + if (lowerName.Contains(keywords[x])) + { + yield return (TPlayer)item; + break; + } + } + } + } + } + public bool TryGetPlayer(ulong steamID, out TPlayer player) { lock (this.mInternal.Players) @@ -312,17 +449,21 @@ namespace BattleBitAPI.Server { } - public virtual async Task OnPlayerJoinedSquad(TPlayer player, Squads squad) + public virtual async Task OnPlayerJoinedSquad(TPlayer player, Squad squad) { } - public virtual async Task OnPlayerLeftSquad(TPlayer player, Squads squad) + public virtual async Task OnPlayerLeftSquad(TPlayer player, Squad squad) { } public virtual async Task OnPlayerChangeTeam(TPlayer player, Team team) { + } + public virtual async Task OnSquadPointsChanged(Squad squad, int newPoints) + { + } public virtual async Task OnPlayerSpawning(TPlayer player, OnPlayerSpawnArguments request) { @@ -584,6 +725,10 @@ namespace BattleBitAPI.Server { Heal(player.SteamID, heal); } + public void SetSquadPointsOf(Team team, Squads squad, int points) + { + ExecuteCommand("setsquadpoints " + ((int)(team)) + " " + ((int)squad) + " " + points); + } public void SetPrimaryWeapon(ulong steamID, WeaponItem item, int extraMagazines, bool clear = false) { @@ -694,6 +839,31 @@ namespace BattleBitAPI.Server SetThrowable(player.SteamID, tool, extra, clear); } + // ---- Squads ---- + public IEnumerable IterateMembersOf(Squad squad) + { + using (var list = this.mInternal.PlayerPool.Get()) + { + var rsquad = this.mInternal.GetSquadInternal(squad); + + //Get A copy of players to our list + lock (rsquad.Members) + list.ListItems.AddRange(rsquad.Members); + + //Iterate our list. + for (int i = 0; i < list.ListItems.Count; i++) + yield return (TPlayer)list.ListItems[i]; + } + } + public Squad GetSquad(Team team, Squads name) + { + if (team == Team.TeamA) + return this.mInternal.TeamASquads[(int)name]; + if (team == Team.TeamB) + return this.mInternal.TeamBSquads[(int)name]; + return null; + } + // ---- Closing ---- public void CloseConnection(string additionInfo = "") { @@ -764,6 +934,11 @@ namespace BattleBitAPI.Server public RoundSettings RoundSettings; public string TerminationReason; public bool ReconnectFlag; + public Squad.Internal[] TeamASquadInternals; + public Squad.Internal[] TeamBSquadInternals; + public Squad[] TeamASquads; + public Squad[] TeamBSquads; + public ItemPooling> PlayerPool; // ---- Private Variables ---- public byte[] mKeepAliveBuffer; @@ -776,7 +951,7 @@ namespace BattleBitAPI.Server public StringBuilder mBuilder; public Queue<(ulong steamID, PlayerModifications.mPlayerModifications)> mChangedModifications; - public Internal() + public Internal(GameServer server) { this.TerminationReason = string.Empty; this.mWriteStream = new Common.Serialization.Stream() @@ -806,6 +981,281 @@ namespace BattleBitAPI.Server this.GamemodeRotation = new GamemodeRotation(this); this.RoundSettings = new RoundSettings(this); this.mChangedModifications = new Queue<(ulong steamID, PlayerModifications.mPlayerModifications)>(254); + + this.TeamASquadInternals = new Squad.Internal[] + { + null, + new Squad.Internal(server,Team.TeamA, Squads.Alpha), + new Squad.Internal(server,Team.TeamA, Squads.Bravo), + new Squad.Internal(server,Team.TeamA, Squads.Charlie), + new Squad.Internal(server,Team.TeamA, Squads.Delta ), + new Squad.Internal(server,Team.TeamA, Squads.Echo ), + new Squad.Internal(server,Team.TeamA, Squads.Foxtrot ), + new Squad.Internal(server,Team.TeamA, Squads.Golf ), + new Squad.Internal(server,Team.TeamA, Squads.Hotel ), + new Squad.Internal(server,Team.TeamA, Squads.India), + new Squad.Internal(server,Team.TeamA, Squads.Juliett ), + new Squad.Internal(server,Team.TeamA, Squads.Kilo ), + new Squad.Internal(server,Team.TeamA, Squads.Lima ), + new Squad.Internal(server,Team.TeamA, Squads.Mike ), + new Squad.Internal(server,Team.TeamA, Squads.November), + new Squad.Internal(server,Team.TeamA, Squads.Oscar ), + new Squad.Internal(server,Team.TeamA, Squads.Papa ), + new Squad.Internal(server,Team.TeamA, Squads.Quebec), + new Squad.Internal(server,Team.TeamA, Squads.Romeo ), + new Squad.Internal(server,Team.TeamA, Squads.Sierra), + new Squad.Internal(server,Team.TeamA, Squads.Tango ), + new Squad.Internal(server,Team.TeamA, Squads.Uniform ), + new Squad.Internal(server,Team.TeamA, Squads.Whiskey ), + new Squad.Internal(server,Team.TeamA, Squads.Xray ), + new Squad.Internal(server,Team.TeamA, Squads.Yankee ), + new Squad.Internal(server,Team.TeamA, Squads.Zulu ), + new Squad.Internal(server,Team.TeamA, Squads.Ash ), + new Squad.Internal(server,Team.TeamA, Squads.Baker ), + new Squad.Internal(server,Team.TeamA, Squads.Cast ), + new Squad.Internal(server,Team.TeamA, Squads.Diver), + new Squad.Internal(server,Team.TeamA, Squads.Eagle), + new Squad.Internal(server,Team.TeamA, Squads.Fisher), + new Squad.Internal(server,Team.TeamA, Squads.George), + new Squad.Internal(server,Team.TeamA, Squads.Hanover), + new Squad.Internal(server,Team.TeamA, Squads.Ice ), + new Squad.Internal(server,Team.TeamA, Squads.Jake), + new Squad.Internal(server,Team.TeamA, Squads.King), + new Squad.Internal(server,Team.TeamA, Squads.Lash), + new Squad.Internal(server,Team.TeamA, Squads.Mule), + new Squad.Internal(server,Team.TeamA, Squads.Neptune ), + new Squad.Internal(server,Team.TeamA, Squads.Ostend), + new Squad.Internal(server,Team.TeamA, Squads.Page ), + new Squad.Internal(server,Team.TeamA, Squads.Quail ), + new Squad.Internal(server,Team.TeamA, Squads.Raft ), + new Squad.Internal(server,Team.TeamA, Squads.Scout ), + new Squad.Internal(server,Team.TeamA, Squads.Tare ), + new Squad.Internal(server,Team.TeamA, Squads.Unit ), + new Squad.Internal(server,Team.TeamA, Squads.William ), + new Squad.Internal(server,Team.TeamA, Squads.Xaintrie ), + new Squad.Internal(server,Team.TeamA, Squads.Yoke ), + new Squad.Internal(server,Team.TeamA, Squads.Zebra ), + new Squad.Internal(server,Team.TeamA, Squads.Ace ), + new Squad.Internal(server,Team.TeamA, Squads.Beer ), + new Squad.Internal(server,Team.TeamA, Squads.Cast2 ), + new Squad.Internal(server,Team.TeamA, Squads.Duff ), + new Squad.Internal(server,Team.TeamA, Squads.Edward ), + new Squad.Internal(server,Team.TeamA, Squads.Freddy), + new Squad.Internal(server,Team.TeamA, Squads.Gustav), + new Squad.Internal(server,Team.TeamA, Squads.Henry ), + new Squad.Internal(server,Team.TeamA, Squads.Ivar ), + new Squad.Internal(server,Team.TeamA, Squads.Jazz ), + new Squad.Internal(server,Team.TeamA, Squads.Key ), + new Squad.Internal(server,Team.TeamA, Squads.Lincoln ), + new Squad.Internal(server,Team.TeamA, Squads.Mary ), + new Squad.Internal(server,Team.TeamA, Squads.Nora ), + }; + this.TeamBSquadInternals = new Squad.Internal[] + { + null, + new Squad.Internal(server,Team.TeamB, Squads.Alpha), + new Squad.Internal(server,Team.TeamB, Squads.Bravo), + new Squad.Internal(server,Team.TeamB, Squads.Charlie), + new Squad.Internal(server,Team.TeamB, Squads.Delta ), + new Squad.Internal(server,Team.TeamB, Squads.Echo ), + new Squad.Internal(server,Team.TeamB, Squads.Foxtrot ), + new Squad.Internal(server,Team.TeamB, Squads.Golf ), + new Squad.Internal(server,Team.TeamB, Squads.Hotel ), + new Squad.Internal(server,Team.TeamB, Squads.India), + new Squad.Internal(server,Team.TeamB, Squads.Juliett ), + new Squad.Internal(server,Team.TeamB, Squads.Kilo ), + new Squad.Internal(server,Team.TeamB, Squads.Lima ), + new Squad.Internal(server,Team.TeamB, Squads.Mike ), + new Squad.Internal(server,Team.TeamB, Squads.November), + new Squad.Internal(server,Team.TeamB, Squads.Oscar ), + new Squad.Internal(server,Team.TeamB, Squads.Papa ), + new Squad.Internal(server,Team.TeamB, Squads.Quebec), + new Squad.Internal(server,Team.TeamB, Squads.Romeo ), + new Squad.Internal(server,Team.TeamB, Squads.Sierra), + new Squad.Internal(server,Team.TeamB, Squads.Tango ), + new Squad.Internal(server,Team.TeamB, Squads.Uniform ), + new Squad.Internal(server,Team.TeamB, Squads.Whiskey ), + new Squad.Internal(server,Team.TeamB, Squads.Xray ), + new Squad.Internal(server,Team.TeamB, Squads.Yankee ), + new Squad.Internal(server,Team.TeamB, Squads.Zulu ), + new Squad.Internal(server,Team.TeamB, Squads.Ash ), + new Squad.Internal(server,Team.TeamB, Squads.Baker ), + new Squad.Internal(server,Team.TeamB, Squads.Cast ), + new Squad.Internal(server,Team.TeamB, Squads.Diver), + new Squad.Internal(server,Team.TeamB, Squads.Eagle), + new Squad.Internal(server,Team.TeamB, Squads.Fisher), + new Squad.Internal(server,Team.TeamB, Squads.George), + new Squad.Internal(server,Team.TeamB, Squads.Hanover), + new Squad.Internal(server,Team.TeamB, Squads.Ice ), + new Squad.Internal(server,Team.TeamB, Squads.Jake), + new Squad.Internal(server,Team.TeamB, Squads.King), + new Squad.Internal(server,Team.TeamB, Squads.Lash), + new Squad.Internal(server,Team.TeamB, Squads.Mule), + new Squad.Internal(server,Team.TeamB, Squads.Neptune ), + new Squad.Internal(server,Team.TeamB, Squads.Ostend), + new Squad.Internal(server,Team.TeamB, Squads.Page ), + new Squad.Internal(server,Team.TeamB, Squads.Quail ), + new Squad.Internal(server,Team.TeamB, Squads.Raft ), + new Squad.Internal(server,Team.TeamB, Squads.Scout ), + new Squad.Internal(server,Team.TeamB, Squads.Tare ), + new Squad.Internal(server,Team.TeamB, Squads.Unit ), + new Squad.Internal(server,Team.TeamB, Squads.William ), + new Squad.Internal(server,Team.TeamB, Squads.Xaintrie ), + new Squad.Internal(server,Team.TeamB, Squads.Yoke ), + new Squad.Internal(server,Team.TeamB, Squads.Zebra ), + new Squad.Internal(server,Team.TeamB, Squads.Ace ), + new Squad.Internal(server,Team.TeamB, Squads.Beer ), + new Squad.Internal(server,Team.TeamB, Squads.Cast2 ), + new Squad.Internal(server,Team.TeamB, Squads.Duff ), + new Squad.Internal(server,Team.TeamB, Squads.Edward ), + new Squad.Internal(server,Team.TeamB, Squads.Freddy), + new Squad.Internal(server,Team.TeamB, Squads.Gustav), + new Squad.Internal(server,Team.TeamB, Squads.Henry ), + new Squad.Internal(server,Team.TeamB, Squads.Ivar ), + new Squad.Internal(server,Team.TeamB, Squads.Jazz ), + new Squad.Internal(server,Team.TeamB, Squads.Key ), + new Squad.Internal(server,Team.TeamB, Squads.Lincoln ), + new Squad.Internal(server,Team.TeamB, Squads.Mary ), + new Squad.Internal(server,Team.TeamB, Squads.Nora ), + }; + + this.TeamASquads = new Squad[] + { + null, + new Squad(this.TeamASquadInternals[01]), + new Squad(this.TeamASquadInternals[02]), + new Squad(this.TeamASquadInternals[03]), + new Squad(this.TeamASquadInternals[04]), + new Squad(this.TeamASquadInternals[05]), + new Squad(this.TeamASquadInternals[06]), + new Squad(this.TeamASquadInternals[07]), + new Squad(this.TeamASquadInternals[08]), + new Squad(this.TeamASquadInternals[09]), + new Squad(this.TeamASquadInternals[10]), + new Squad(this.TeamASquadInternals[11]), + new Squad(this.TeamASquadInternals[12]), + new Squad(this.TeamASquadInternals[13]), + new Squad(this.TeamASquadInternals[14]), + new Squad(this.TeamASquadInternals[15]), + new Squad(this.TeamASquadInternals[16]), + new Squad(this.TeamASquadInternals[17]), + new Squad(this.TeamASquadInternals[18]), + new Squad(this.TeamASquadInternals[19]), + new Squad(this.TeamASquadInternals[20]), + new Squad(this.TeamASquadInternals[21]), + new Squad(this.TeamASquadInternals[22]), + new Squad(this.TeamASquadInternals[23]), + new Squad(this.TeamASquadInternals[24]), + new Squad(this.TeamASquadInternals[25]), + new Squad(this.TeamASquadInternals[26]), + new Squad(this.TeamASquadInternals[27]), + new Squad(this.TeamASquadInternals[28]), + new Squad(this.TeamASquadInternals[29]), + new Squad(this.TeamASquadInternals[30]), + new Squad(this.TeamASquadInternals[31]), + new Squad(this.TeamASquadInternals[32]), + new Squad(this.TeamASquadInternals[33]), + new Squad(this.TeamASquadInternals[34]), + new Squad(this.TeamASquadInternals[35]), + new Squad(this.TeamASquadInternals[36]), + new Squad(this.TeamASquadInternals[37]), + new Squad(this.TeamASquadInternals[38]), + new Squad(this.TeamASquadInternals[39]), + new Squad(this.TeamASquadInternals[40]), + new Squad(this.TeamASquadInternals[41]), + new Squad(this.TeamASquadInternals[42]), + new Squad(this.TeamASquadInternals[43]), + new Squad(this.TeamASquadInternals[44]), + new Squad(this.TeamASquadInternals[45]), + new Squad(this.TeamASquadInternals[46]), + new Squad(this.TeamASquadInternals[47]), + new Squad(this.TeamASquadInternals[48]), + new Squad(this.TeamASquadInternals[49]), + new Squad(this.TeamASquadInternals[50]), + new Squad(this.TeamASquadInternals[51]), + new Squad(this.TeamASquadInternals[52]), + new Squad(this.TeamASquadInternals[53]), + new Squad(this.TeamASquadInternals[54]), + new Squad(this.TeamASquadInternals[55]), + new Squad(this.TeamASquadInternals[56]), + new Squad(this.TeamASquadInternals[57]), + new Squad(this.TeamASquadInternals[58]), + new Squad(this.TeamASquadInternals[59]), + new Squad(this.TeamASquadInternals[60]), + new Squad(this.TeamASquadInternals[61]), + new Squad(this.TeamASquadInternals[62]), + new Squad(this.TeamASquadInternals[63]), + new Squad(this.TeamASquadInternals[64]), + }; + this.TeamBSquads = new Squad[] + { + null, + new Squad(this.TeamBSquadInternals[01]), + new Squad(this.TeamBSquadInternals[02]), + new Squad(this.TeamBSquadInternals[03]), + new Squad(this.TeamBSquadInternals[04]), + new Squad(this.TeamBSquadInternals[05]), + new Squad(this.TeamBSquadInternals[06]), + new Squad(this.TeamBSquadInternals[07]), + new Squad(this.TeamBSquadInternals[08]), + new Squad(this.TeamBSquadInternals[09]), + new Squad(this.TeamBSquadInternals[10]), + new Squad(this.TeamBSquadInternals[11]), + new Squad(this.TeamBSquadInternals[12]), + new Squad(this.TeamBSquadInternals[13]), + new Squad(this.TeamBSquadInternals[14]), + new Squad(this.TeamBSquadInternals[15]), + new Squad(this.TeamBSquadInternals[16]), + new Squad(this.TeamBSquadInternals[17]), + new Squad(this.TeamBSquadInternals[18]), + new Squad(this.TeamBSquadInternals[19]), + new Squad(this.TeamBSquadInternals[20]), + new Squad(this.TeamBSquadInternals[21]), + new Squad(this.TeamBSquadInternals[22]), + new Squad(this.TeamBSquadInternals[23]), + new Squad(this.TeamBSquadInternals[24]), + new Squad(this.TeamBSquadInternals[25]), + new Squad(this.TeamBSquadInternals[26]), + new Squad(this.TeamBSquadInternals[27]), + new Squad(this.TeamBSquadInternals[28]), + new Squad(this.TeamBSquadInternals[29]), + new Squad(this.TeamBSquadInternals[30]), + new Squad(this.TeamBSquadInternals[31]), + new Squad(this.TeamBSquadInternals[32]), + new Squad(this.TeamBSquadInternals[33]), + new Squad(this.TeamBSquadInternals[34]), + new Squad(this.TeamBSquadInternals[35]), + new Squad(this.TeamBSquadInternals[36]), + new Squad(this.TeamBSquadInternals[37]), + new Squad(this.TeamBSquadInternals[38]), + new Squad(this.TeamBSquadInternals[39]), + new Squad(this.TeamBSquadInternals[40]), + new Squad(this.TeamBSquadInternals[41]), + new Squad(this.TeamBSquadInternals[42]), + new Squad(this.TeamBSquadInternals[43]), + new Squad(this.TeamBSquadInternals[44]), + new Squad(this.TeamBSquadInternals[45]), + new Squad(this.TeamBSquadInternals[46]), + new Squad(this.TeamBSquadInternals[47]), + new Squad(this.TeamBSquadInternals[48]), + new Squad(this.TeamBSquadInternals[49]), + new Squad(this.TeamBSquadInternals[50]), + new Squad(this.TeamBSquadInternals[51]), + new Squad(this.TeamBSquadInternals[52]), + new Squad(this.TeamBSquadInternals[53]), + new Squad(this.TeamBSquadInternals[54]), + new Squad(this.TeamBSquadInternals[55]), + new Squad(this.TeamBSquadInternals[56]), + new Squad(this.TeamBSquadInternals[57]), + new Squad(this.TeamBSquadInternals[58]), + new Squad(this.TeamBSquadInternals[59]), + new Squad(this.TeamBSquadInternals[60]), + new Squad(this.TeamBSquadInternals[61]), + new Squad(this.TeamBSquadInternals[62]), + new Squad(this.TeamBSquadInternals[63]), + new Squad(this.TeamBSquadInternals[64]), + }; + this.PlayerPool = new ItemPooling>(254); } // ---- Players In Room ---- @@ -894,6 +1344,7 @@ namespace BattleBitAPI.Server this.mBuilder.Clear(); this.mChangedModifications.Clear(); } + public void AddPlayer(Player player) { lock (Players) @@ -912,6 +1363,18 @@ namespace BattleBitAPI.Server lock (Players) return Players.TryGetValue(steamID, out result); } + public Squad.Internal GetSquadInternal(Team team, Squads squad) + { + if (team == Team.TeamA) + return this.TeamASquadInternals[(int)squad]; + if (team == Team.TeamB) + return this.TeamBSquadInternals[(int)squad]; + return null; + } + public Squad.Internal GetSquadInternal(Squad squad) + { + return GetSquadInternal(squad.Team, squad.Name); + } } } } diff --git a/BattleBitAPI/Server/Internal/Squad.cs b/BattleBitAPI/Server/Internal/Squad.cs new file mode 100644 index 0000000..b29583d --- /dev/null +++ b/BattleBitAPI/Server/Internal/Squad.cs @@ -0,0 +1,58 @@ +using BattleBitAPI.Common; + +namespace BattleBitAPI.Server +{ + public class Squad where TPlayer : Player + { + public Team Team => @internal.Team; + public Squads Name => @internal.Name; + public GameServer Server => @internal.Server; + public int NumberOfMembers => @internal.Members.Count; + public bool IsEmpty => NumberOfMembers == 0; + public IEnumerable Members => @internal.Server.IterateMembersOf(this); + public int SquadPoints + { + get => @internal.SquadPoints; + + set + { + @internal.SquadPoints = value; + Server.SetSquadPointsOf(@internal.Team, @internal.Name, value); + } + } + + private Internal @internal; + public Squad(Internal @internal) + { + this.@internal = @internal; + } + + public override string ToString() + { + return Team + " : " + Name; + } + + // ---- Internal ---- + public class Internal + { + public readonly Team Team; + public readonly Squads Name; + public int SquadPoints; + public GameServer Server; + public HashSet Members; + + public Internal(GameServer server, Team team, Squads squads) + { + this.Team = team; + this.Name = squads; + this.Server = server; + this.Members = new HashSet(8); + } + + public void Reset() + { + + } + } + } +} diff --git a/BattleBitAPI/Server/Player.cs b/BattleBitAPI/Server/Player.cs index dabe927..4bea9c5 100644 --- a/BattleBitAPI/Server/Player.cs +++ b/BattleBitAPI/Server/Player.cs @@ -33,12 +33,12 @@ namespace BattleBitAPI ChangeTeam(value); } } - public Squads Squad + public Squads SquadName { - get => mInternal.Squad; + get => mInternal.SquadName; set { - if (value == mInternal.Squad) + if (value == mInternal.SquadName) return; if (value == Squads.NoSquad) KickFromSquad(); @@ -46,7 +46,25 @@ namespace BattleBitAPI JoinSquad(value); } } - public bool InSquad => mInternal.Squad != Squads.NoSquad; + public Squad Squad + { + get => GameServer.GetSquad(mInternal.Team, mInternal.SquadName); + set + { + if (value == Squad) + return; + + if (value == null) + KickFromSquad(); + else + { + if(value.Team != this.Team) + ChangeTeam(value.Team); + JoinSquad(value.Name); + } + } + } + public bool InSquad => mInternal.SquadName != Squads.NoSquad; public int PingMs => mInternal.PingMs; public float HP @@ -127,11 +145,11 @@ namespace BattleBitAPI { } - public virtual async Task OnJoinedSquad(Squads newSquad) + public virtual async Task OnJoinedSquad(Squad newSquad) { } - public virtual async Task OnLeftSquad(Squads oldSquad) + public virtual async Task OnLeftSquad(Squad oldSquad) { } @@ -255,7 +273,7 @@ namespace BattleBitAPI public GameServer GameServer; public GameRole Role; public Team Team; - public Squads Squad; + public Squads SquadName; public int PingMs = 999; public bool IsAlive; diff --git a/BattleBitAPI/Server/ServerListener.cs b/BattleBitAPI/Server/ServerListener.cs index 1790e4a..6bdd385 100644 --- a/BattleBitAPI/Server/ServerListener.cs +++ b/BattleBitAPI/Server/ServerListener.cs @@ -79,9 +79,10 @@ namespace BattleBitAPI.Server /// /// /// - /// GameServer: Game server that has been just created.
+ /// IPAddress: Game server's IP.
+ /// ushort: Game server's Port.
///
- public Func OnCreatingGameServerInstance { get; set; } + public Func OnCreatingGameServerInstance { get; set; } /// /// Fired when a new instance of player instance created. @@ -89,8 +90,9 @@ namespace BattleBitAPI.Server /// /// /// TPlayer: The player instance that was created
+ /// ulong: The steamID of the player
///
- public Func OnCreatingPlayerInstance { get; set; } + public Func OnCreatingPlayerInstance { get; set; } // --- Private --- private TcpListener mSocket; @@ -190,7 +192,7 @@ namespace BattleBitAPI.Server throw new Exception("Incoming package wasn't hail."); } - //Read the server name + //Read the server token string token; { readStream.Reset(); @@ -208,6 +210,27 @@ namespace BattleBitAPI.Server token = readStream.ReadString(stringSize); } + //Read the server version + string version; + { + readStream.Reset(); + if (!await networkStream.TryRead(readStream, 2, source.Token)) + throw new Exception("Unable to read the version size"); + + int stringSize = readStream.ReadUInt16(); + if (stringSize > 32) + throw new Exception("Invalid version size"); + + readStream.Reset(); + if (!await networkStream.TryRead(readStream, stringSize, source.Token)) + throw new Exception("Unable to read the version"); + + 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; { @@ -382,7 +405,7 @@ namespace BattleBitAPI.Server } var hash = ((ulong)gamePort << 32) | (ulong)ip.ToUInt(); - server = this.mInstanceDatabase.GetServerInstance(hash, out resources, this.OnCreatingGameServerInstance); + server = this.mInstanceDatabase.GetServerInstance(hash, out resources, this.OnCreatingGameServerInstance, ip, (ushort)gamePort); resources.Set( this.mExecutePackage, this.mGetPlayerInternals, @@ -563,7 +586,7 @@ namespace BattleBitAPI.Server playerInternal.IP = new IPAddress(ipHash); playerInternal.GameServer = (GameServer)server; playerInternal.Team = team; - playerInternal.Squad = squad; + playerInternal.SquadName = squad; playerInternal.Role = role; playerInternal.IsAlive = isAlive; playerInternal.CurrentLoadout = loadout; @@ -585,6 +608,42 @@ namespace BattleBitAPI.Server resources.AddPlayer(player); } + //Squads + { + readStream.Reset(); + if (!await networkStream.TryRead(readStream, 4, source.Token)) + throw new Exception("Unable to read the Squad size"); + int squadsDataSize = (int)readStream.ReadUInt32(); + + readStream.Reset(); + if (!await networkStream.TryRead(readStream, squadsDataSize, source.Token)) + throw new Exception("Unable to read the Squads"); + + for (int i = 1; i < resources.TeamASquadInternals.Length; i++) + { + var item = resources.TeamASquadInternals[i]; + item.SquadPoints = readStream.ReadInt32(); + } + for (int i = 1; i < resources.TeamBSquadInternals.Length; i++) + { + var item = resources.TeamBSquadInternals[i]; + item.SquadPoints = readStream.ReadInt32(); + } + } + + // --- Finished Reading --- + + //Assing each player to their squad. + foreach (var item in resources.Players.Values) + { + if (item.InSquad) + { + var @squad = resources.GetSquadInternal(item.Squad); + lock (@squad.Members) + @squad.Members.Add((TPlayer)item); + } + } + //Send accepted notification. networkStream.WriteByte((byte)NetworkCommuncation.Accepted); } @@ -711,7 +770,7 @@ namespace BattleBitAPI.Server playerInternal.GameServer = (GameServer)server; playerInternal.Team = team; - playerInternal.Squad = squad; + playerInternal.SquadName = squad; playerInternal.Role = role; //Start from default. @@ -745,6 +804,19 @@ namespace BattleBitAPI.Server server.OnPlayerDied((TPlayer)player); } + if (@internal.SquadName != Squads.NoSquad) + { + var msquad = server.GetSquad(@internal.Team, @internal.SquadName); + var rsquad = resources.GetSquadInternal(msquad); + + @internal.SquadName = Squads.NoSquad; + lock (rsquad.Members) + rsquad.Members.Remove((TPlayer)player); + + player.OnLeftSquad(msquad); + server.OnPlayerLeftSquad((TPlayer)player, msquad); + } + player.OnDisconnected(); server.OnPlayerDisconnected((TPlayer)player); } @@ -916,10 +988,15 @@ namespace BattleBitAPI.Server if (resources.TryGetPlayer(steamID, out var client)) { var @internal = mInstanceDatabase.GetPlayerInternals(steamID); - @internal.Squad = squad; + @internal.SquadName = squad; - client.OnJoinedSquad(squad); - server.OnPlayerJoinedSquad((TPlayer)client, squad); + var msquad = server.GetSquad(client.Team, squad); + var rsquad = resources.GetSquadInternal(msquad); + lock (rsquad.Members) + rsquad.Members.Add((TPlayer)client); + + client.OnJoinedSquad(msquad); + server.OnPlayerJoinedSquad((TPlayer)client, msquad); } } break; @@ -934,13 +1011,20 @@ namespace BattleBitAPI.Server { var @internal = mInstanceDatabase.GetPlayerInternals(steamID); - var oldSquad = client.Squad; + var oldSquad = client.SquadName; var oldRole = client.Role; - @internal.Squad = Squads.NoSquad; + @internal.SquadName = Squads.NoSquad; @internal.Role = GameRole.Assault; - client.OnLeftSquad(oldSquad); - server.OnPlayerLeftSquad((TPlayer)client, oldSquad); + var msquad = server.GetSquad(client.Team, oldSquad); + var rsquad = resources.GetSquadInternal(msquad); + + @internal.SquadName = Squads.NoSquad; + lock (rsquad.Members) + rsquad.Members.Remove((TPlayer)client); + + client.OnLeftSquad(msquad); + server.OnPlayerLeftSquad((TPlayer)client, msquad); if (oldRole != GameRole.Assault) { @@ -1228,6 +1312,25 @@ namespace BattleBitAPI.Server } break; } + case NetworkCommuncation.OnSquadPointsChanged: + { + if (stream.CanRead(1 + 1 + 4)) + { + Team team = (Team)stream.ReadInt8(); + Squads squad = (Squads)stream.ReadInt8(); + int points = stream.ReadInt32(); + + var msquad = server.GetSquad(team, squad); + var rsquad = resources.GetSquadInternal(msquad); + + if (rsquad.SquadPoints != points) + { + rsquad.SquadPoints = points; + server.OnSquadPointsChanged(msquad, points); + } + } + break; + } } } @@ -1291,7 +1394,7 @@ namespace BattleBitAPI.Server this.mPlayerInstances = new Dictionary.Internal)>(1024 * 16); } - public TGameServer GetServerInstance(ulong hash, out GameServer.Internal @internal, Func createFunc) + public TGameServer GetServerInstance(ulong hash, out GameServer.Internal @internal, Func createFunc, IPAddress ip, ushort port) { lock (mGameServerInstances) { @@ -1301,13 +1404,13 @@ namespace BattleBitAPI.Server return data.Item1; } - @internal = new GameServer.Internal(); GameServer server; - if (createFunc != null) - server = createFunc(); + server = createFunc(ip, port); else - server = Activator.CreateInstance>(); + server = Activator.CreateInstance(); + + @internal = new GameServer.Internal(server); GameServer.SetInstance(server, @internal); @@ -1315,7 +1418,7 @@ namespace BattleBitAPI.Server return (TGameServer)server; } } - public TPlayer GetPlayerInstance(ulong steamID, out Player.Internal @internal, Func createFunc) + public TPlayer GetPlayerInstance(ulong steamID, out Player.Internal @internal, Func createFunc) { lock (this.mPlayerInstances) { @@ -1330,7 +1433,7 @@ namespace BattleBitAPI.Server Player pplayer; if (createFunc != null) - pplayer = createFunc(); + pplayer = createFunc(steamID); else pplayer = Activator.CreateInstance(); Player.SetInstance((TPlayer)pplayer, @internal); diff --git a/CommunityServerAPI.csproj b/CommunityServerAPI.csproj index 2fb5483..10c18d1 100644 --- a/CommunityServerAPI.csproj +++ b/CommunityServerAPI.csproj @@ -1,11 +1,30 @@ - Exe + Library net6.0 enable disable True + True + BattleBitRemastered.CommunityAPI.Core + SgtOkiDoki + OkigamesLimited + BattleBit Remastered + API to customize game servers for BattleBit Remastered. + + MIT + README.md + https://github.com/MrOkiDoki/BattleBit-Community-Server-API + https://github.com/MrOkiDoki/BattleBit-Community-Server-API + BattleBit + + + True + \ + + + diff --git a/CommunityServerAPI.sln b/CommunityServerAPI.sln index 1a7e5a7..2ba073e 100644 --- a/CommunityServerAPI.sln +++ b/CommunityServerAPI.sln @@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.4.33205.214 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityServerAPI", "CommunityServerAPI.csproj", "{787887A1-8AEE-43E4-AF9B-A3883DE04486}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityServerAPI", "CommunityServerAPI.csproj", "{787887A1-8AEE-43E4-AF9B-A3883DE04486}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BattleBitAPI.Program", "..\BattleBitAPI.Program\BattleBitAPI.Program.csproj", "{96D55E1B-2711-47E2-9DA1-41B645F4FCF2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +17,10 @@ Global {787887A1-8AEE-43E4-AF9B-A3883DE04486}.Debug|Any CPU.Build.0 = Debug|Any CPU {787887A1-8AEE-43E4-AF9B-A3883DE04486}.Release|Any CPU.ActiveCfg = Release|Any CPU {787887A1-8AEE-43E4-AF9B-A3883DE04486}.Release|Any CPU.Build.0 = Release|Any CPU + {96D55E1B-2711-47E2-9DA1-41B645F4FCF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {96D55E1B-2711-47E2-9DA1-41B645F4FCF2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {96D55E1B-2711-47E2-9DA1-41B645F4FCF2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {96D55E1B-2711-47E2-9DA1-41B645F4FCF2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Program.cs b/Program.cs deleted file mode 100644 index d4aec4f..0000000 --- a/Program.cs +++ /dev/null @@ -1,112 +0,0 @@ -using BattleBitAPI; -using BattleBitAPI.Common; -using BattleBitAPI.Server; -using System.Linq.Expressions; -using System.Net; -using System.Numerics; -using System.Threading.Channels; -using System.Xml; - -class Program -{ - static void Main(string[] args) - { - var listener = new ServerListener(); - listener.OnCreatingGameServerInstance += OnCreatingGameServerInstance; - listener.OnCreatingPlayerInstance += OnCreatingPlayerInstance; - listener.Start(29294); - - Thread.Sleep(-1); - } - - private static MyPlayer OnCreatingPlayerInstance() - { - return new MyPlayer("asdasd"); - } - - private static MyGameServer OnCreatingGameServerInstance() - { - return new MyGameServer("mysecretDBpass"); - } -} -class MyPlayer : Player -{ - private string mydb; - public MyPlayer(string mydb) - { - this.mydb = mydb; - } - - public override async Task OnSpawned() - { - } -} -class MyGameServer : GameServer -{ - private string myDbConnection; - public MyGameServer(string mySecretDBConnection) - { - this.myDbConnection = mySecretDBConnection; - } - - public override async Task OnConnected() - { - ForceStartGame(); - ServerSettings.PlayerCollision = true; - } - public override async Task OnTick() - { - foreach (var item in AllPlayers) - { - item.GameServer.ForceStartGame(); - } - } - public override async Task OnPlayerSpawning(MyPlayer player, OnPlayerSpawnArguments request) - { - return null; - - request.Wearings.Eye = "Eye_Zombie_01"; - request.Wearings.Face = "Face_Zombie_01"; - request.Wearings.Face = "Hair_Zombie_01"; - request.Wearings.Skin = "Zombie_01"; - request.Wearings.Uniform = "ANY_NU_Uniform_Zombie_01"; - request.Wearings.Head = "ANV2_Universal_Zombie_Helmet_00_A_Z"; - request.Wearings.Belt = "ANV2_Universal_All_Belt_Null"; - request.Wearings.Backbag = "ANV2_Universal_All_Backpack_Null"; - request.Wearings.Chest = "ANV2_Universal_All_Armor_Null"; - - return request; - } - - public override async Task OnPlayerConnected(MyPlayer player) - { - await Console.Out.WriteLineAsync("Connected: " + player); - player.Modifications.CanSpectate = true; - player.Modifications.CanDeploy = false; - - } - public override async Task OnPlayerSpawned(MyPlayer player) - { - await Console.Out.WriteLineAsync("Spawned: " + player); - } - public override async Task OnAPlayerDownedAnotherPlayer(OnPlayerKillArguments args) - { - await Console.Out.WriteLineAsync("Downed: " + args.Victim); - } - public override async Task OnPlayerGivenUp(MyPlayer player) - { - await Console.Out.WriteLineAsync("Giveup: " + player); - } - public override async Task OnPlayerDied(MyPlayer player) - { - await Console.Out.WriteLineAsync("Died: " + player); - } - public override async Task OnAPlayerRevivedAnotherPlayer(MyPlayer from, MyPlayer to) - { - await Console.Out.WriteLineAsync(from + " revived " + to); - } - public override async Task OnPlayerDisconnected(MyPlayer player) - { - await Console.Out.WriteLineAsync("Disconnected: " + player); - } -}