Consistent player - gameserver instances.

This commit is contained in:
MrOkiDoki 2023-08-11 11:04:47 +03:00
parent 9f5f0d5259
commit 1326cd85e4
25 changed files with 532 additions and 533 deletions

View File

@ -2,7 +2,7 @@
namespace BattleBitAPI.Common
{
public struct OnPlayerKillArguments<TPlayer> where TPlayer : Player
public struct OnPlayerKillArguments<TPlayer> where TPlayer : Player<TPlayer>
{
public TPlayer Killer;
public Vector3 KillerPosition;

View File

@ -5,12 +5,12 @@ using System.Numerics;
namespace BattleBitAPI
{
public class Player
public class Player<TPlayer> where TPlayer : Player<TPlayer>
{
public ulong SteamID { get; internal set; }
public string Name { get; internal set; }
public IPAddress IP { get; internal set; }
public GameServer GameServer { get; internal set; }
public GameServer<TPlayer> GameServer { get; internal set; }
public GameRole Role { get; internal set; }
public Team Team { get; internal set; }
public Squads Squad { get; internal set; }
@ -18,15 +18,23 @@ namespace BattleBitAPI
public PlayerLoadout CurrentLoadout { get; internal set; } = new PlayerLoadout();
public PlayerWearings CurrentWearings { get; internal set; } = new PlayerWearings();
internal virtual async Task OnInitialized()
public virtual void OnCreated()
{
}
internal virtual async Task OnSpawned()
public virtual async Task OnConnected()
{
}
internal virtual async Task OnDied()
public virtual async Task OnSpawned()
{
}
public virtual async Task OnDied()
{
}
public virtual async Task OnDisconnected()
{
}

View File

@ -1,6 +1,7 @@
using System.Net;
using System.Net.Sockets;
using System.Numerics;
using System.Resources;
using System.Text;
using BattleBitAPI.Common;
using BattleBitAPI.Common.Extentions;
@ -9,112 +10,40 @@ using CommunityServerAPI.BattleBitAPI;
namespace BattleBitAPI.Server
{
public class GameServer : IDisposable
public class GameServer<TPlayer> : System.IDisposable where TPlayer : Player<TPlayer>
{
// ---- Public Variables ----
public TcpClient Socket { get; private set; }
public ulong ServerHash => mInternal.ServerHash;
public bool IsConnected => mInternal.IsConnected;
public IPAddress GameIP => mInternal.GameIP;
public int GamePort => mInternal.GamePort;
public ulong ServerHash { get; private set; }
/// <summary>
/// Is game server connected to our server?
/// </summary>
public bool IsConnected { get; private set; }
public IPAddress GameIP { get; private set; }
public int GamePort { get; private set; }
public bool IsPasswordProtected { get; private set; }
public string ServerName { get; private set; }
public string Gamemode { get; private set; }
public string Map { get; private set; }
public MapSize MapSize { get; private set; }
public MapDayNight DayNight { get; private set; }
public int CurrentPlayers { get; private set; }
public int InQueuePlayers { get; private set; }
public int MaxPlayers { get; private set; }
public string LoadingScreenText { get; private set; }
public string ServerRulesText { get; private set; }
public ServerSettings Settings { get; private set; }
public MapRotation MapRotation { get; private set; }
public GamemodeRotation GamemodeRotation { get; private set; }
/// <summary>
/// Reason why connection was terminated.
/// </summary>
public string TerminationReason { get; private set; }
internal bool ReconnectFlag { get; set; }
public TcpClient Socket => mInternal.Socket;
public bool IsPasswordProtected => mInternal.IsPasswordProtected;
public string ServerName => mInternal.ServerName;
public string Gamemode => mInternal.Gamemode;
public string Map => mInternal.Map;
public MapSize MapSize => mInternal.MapSize;
public MapDayNight DayNight => mInternal.DayNight;
public int CurrentPlayers => mInternal.CurrentPlayers;
public int InQueuePlayers => mInternal.InQueuePlayers;
public int MaxPlayers => mInternal.MaxPlayers;
public string LoadingScreenText => mInternal.LoadingScreenText;
public string ServerRulesText => mInternal.ServerRulesText;
public ServerSettings<TPlayer> Settings => mInternal.Settings;
public MapRotation<TPlayer> MapRotation => mInternal.MapRotation;
public GamemodeRotation<TPlayer> GamemodeRotation => mInternal.GamemodeRotation;
public string TerminationReason => mInternal.TerminationReason;
public bool ReconnectFlag => mInternal.ReconnectFlag;
// ---- Private Variables ----
private byte[] mKeepAliveBuffer;
private Func<GameServer, mInternalResources, Common.Serialization.Stream, Task> mExecutionFunc;
private Common.Serialization.Stream mWriteStream;
private Common.Serialization.Stream mReadStream;
private uint mReadPackageSize;
private long mLastPackageReceived;
private long mLastPackageSent;
private bool mIsDisposed;
private mInternalResources mInternal;
private StringBuilder mBuilder;
private bool mWantsToCloseConnection;
// ---- Construction ----
public GameServer(TcpClient socket, mInternalResources resources, Func<GameServer, mInternalResources, Common.Serialization.Stream, Task> func, IPAddress iP, int port, bool isPasswordProtected, string serverName, string gamemode, string map, MapSize mapSize, MapDayNight dayNight, int currentPlayers, int inQueuePlayers, int maxPlayers, string loadingScreenText, string serverRulesText)
{
this.IsConnected = true;
this.Socket = socket;
this.mInternal = resources;
this.mExecutionFunc = func;
this.mBuilder = new StringBuilder(1024);
this.GameIP = iP;
this.GamePort = port;
this.IsPasswordProtected = isPasswordProtected;
this.ServerName = serverName;
this.Gamemode = gamemode;
this.Map = map;
this.MapSize = mapSize;
this.DayNight = dayNight;
this.CurrentPlayers = currentPlayers;
this.InQueuePlayers = inQueuePlayers;
this.MaxPlayers = maxPlayers;
this.LoadingScreenText = loadingScreenText;
this.ServerRulesText = serverRulesText;
this.TerminationReason = string.Empty;
this.mWriteStream = new Common.Serialization.Stream()
{
Buffer = new byte[Const.MaxNetworkPackageSize],
InPool = false,
ReadPosition = 0,
WritePosition = 0,
};
this.mReadStream = new Common.Serialization.Stream()
{
Buffer = new byte[Const.MaxNetworkPackageSize],
InPool = false,
ReadPosition = 0,
WritePosition = 0,
};
this.mKeepAliveBuffer = new byte[4]
{
0,0,0,0,
};
this.mLastPackageReceived = Extentions.TickCount;
this.mLastPackageSent = Extentions.TickCount;
this.ServerHash = ((ulong)port << 32) | (ulong)iP.ToUInt();
this.Settings = new ServerSettings(resources);
this.MapRotation = new MapRotation(resources);
this.GamemodeRotation = new GamemodeRotation(resources);
}
private Internal mInternal;
// ---- Tick ----
public async Task Tick()
{
if (!this.IsConnected)
return;
if (this.mIsDisposed)
return;
if (this.mInternal.IsDirtySettings)
{
@ -124,37 +53,39 @@ namespace BattleBitAPI.Server
using (var pck = Common.Serialization.Stream.Get())
{
pck.Write((byte)NetworkCommuncation.SetNewRoomSettings);
this.mInternal.Settings.Write(pck);
this.mInternal._Settings.Write(pck);
WriteToSocket(pck);
}
}
if (this.mInternal.MapRotationDirty)
{
this.mInternal.MapRotationDirty = false;
this.mBuilder.Clear();
this.mInternal.mBuilder.Clear();
this.mBuilder.Append("setmaprotation ");
lock (this.mInternal.MapRotation)
foreach (var map in this.mInternal.MapRotation)
this.mInternal.mBuilder.Append("setmaprotation ");
lock (this.mInternal._MapRotation)
foreach (var map in this.mInternal._MapRotation)
{
this.mBuilder.Append(map);
this.mBuilder.Append(',');
this.mInternal.mBuilder.Append(map);
this.mInternal.mBuilder.Append(',');
}
this.ExecuteCommand(this.mBuilder.ToString());
this.ExecuteCommand(this.mInternal.mBuilder.ToString());
}
if (this.mInternal.GamemodeRotationDirty)
{
this.mInternal.GamemodeRotationDirty = false;
this.mBuilder.Clear();
this.mInternal.mBuilder.Clear();
this.mBuilder.Append("setgamemoderotation ");
lock (this.mInternal.GamemodeRotation)
foreach (var gamemode in this.mInternal.GamemodeRotation)
this.mInternal.mBuilder.Append("setgamemoderotation ");
lock (this.mInternal._GamemodeRotation)
{
foreach (var gamemode in this.mInternal._GamemodeRotation)
{
this.mBuilder.Append(gamemode);
this.mBuilder.Append(',');
this.mInternal.mBuilder.Append(gamemode);
this.mInternal.mBuilder.Append(',');
}
this.ExecuteCommand(this.mBuilder.ToString());
}
this.ExecuteCommand(this.mInternal.mBuilder.ToString());
}
try
@ -167,7 +98,7 @@ namespace BattleBitAPI.Server
}
//Did user requested to close connection?
if (this.mWantsToCloseConnection)
if (this.mInternal.mWantsToCloseConnection)
{
mClose(this.TerminationReason);
return;
@ -178,82 +109,82 @@ namespace BattleBitAPI.Server
//Read network packages.
while (Socket.Available > 0)
{
this.mLastPackageReceived = Extentions.TickCount;
this.mInternal.mLastPackageReceived = Extentions.TickCount;
//Do we know the package size?
if (this.mReadPackageSize == 0)
if (this.mInternal.mReadPackageSize == 0)
{
const int sizeToRead = 4;
int leftSizeToRead = sizeToRead - this.mReadStream.WritePosition;
int leftSizeToRead = sizeToRead - this.mInternal.mReadStream.WritePosition;
int read = await networkStream.ReadAsync(this.mReadStream.Buffer, this.mReadStream.WritePosition, leftSizeToRead);
int read = await networkStream.ReadAsync(this.mInternal.mReadStream.Buffer, this.mInternal.mReadStream.WritePosition, leftSizeToRead);
if (read <= 0)
throw new Exception("Connection was terminated.");
this.mReadStream.WritePosition += read;
this.mInternal.mReadStream.WritePosition += read;
//Did we receive the package?
if (this.mReadStream.WritePosition >= 4)
if (this.mInternal.mReadStream.WritePosition >= 4)
{
//Read the package size
this.mReadPackageSize = this.mReadStream.ReadUInt32();
this.mInternal.mReadPackageSize = this.mInternal.mReadStream.ReadUInt32();
if (this.mReadPackageSize > Const.MaxNetworkPackageSize)
if (this.mInternal.mReadPackageSize > Const.MaxNetworkPackageSize)
throw new Exception("Incoming package was larger than 'Conts.MaxNetworkPackageSize'");
this.mReadStream.Reset();
this.mInternal.mReadStream.Reset();
}
}
else
{
int sizeToRead = (int)mReadPackageSize;
int leftSizeToRead = sizeToRead - this.mReadStream.WritePosition;
int sizeToRead = (int)this.mInternal.mReadPackageSize;
int leftSizeToRead = sizeToRead - this.mInternal.mReadStream.WritePosition;
int read = await networkStream.ReadAsync(this.mReadStream.Buffer, this.mReadStream.WritePosition, leftSizeToRead);
int read = await networkStream.ReadAsync(this.mInternal.mReadStream.Buffer, this.mInternal.mReadStream.WritePosition, leftSizeToRead);
if (read <= 0)
throw new Exception("Connection was terminated.");
this.mReadStream.WritePosition += read;
this.mInternal.mReadStream.WritePosition += read;
//Do we have the package?
if (this.mReadStream.WritePosition >= mReadPackageSize)
if (this.mInternal.mReadStream.WritePosition >= this.mInternal.mReadPackageSize)
{
this.mReadPackageSize = 0;
this.mInternal.mReadPackageSize = 0;
await this.mExecutionFunc(this, this.mInternal, this.mReadStream);
await this.mInternal.mExecutionFunc(this, this.mInternal, this.mInternal.mReadStream);
//Reset
this.mReadStream.Reset();
this.mInternal.mReadStream.Reset();
}
}
}
//Send the network packages.
if (this.mWriteStream.WritePosition > 0)
if (this.mInternal.mWriteStream.WritePosition > 0)
{
lock (this.mWriteStream)
lock (this.mInternal.mWriteStream)
{
if (this.mWriteStream.WritePosition > 0)
if (this.mInternal.mWriteStream.WritePosition > 0)
{
networkStream.WriteAsync(this.mWriteStream.Buffer, 0, this.mWriteStream.WritePosition);
this.mWriteStream.WritePosition = 0;
networkStream.WriteAsync(this.mInternal.mWriteStream.Buffer, 0, this.mInternal.mWriteStream.WritePosition);
this.mInternal.mWriteStream.WritePosition = 0;
this.mLastPackageSent = Extentions.TickCount;
this.mInternal.mLastPackageSent = Extentions.TickCount;
}
}
}
//Are we timed out?
if ((Extentions.TickCount - this.mLastPackageReceived) > Const.NetworkTimeout)
if ((Extentions.TickCount - this.mInternal.mLastPackageReceived) > Const.NetworkTimeout)
throw new Exception("Timedout.");
//Send keep alive if needed
if ((Extentions.TickCount - this.mLastPackageSent) > Const.NetworkKeepAlive)
if ((Extentions.TickCount - this.mInternal.mLastPackageSent) > Const.NetworkKeepAlive)
{
//Send keep alive.
await networkStream.WriteAsync(this.mKeepAliveBuffer, 0, 4);
await networkStream.WriteAsync(this.mInternal.mKeepAliveBuffer, 0, 4);
await networkStream.FlushAsync();
this.mLastPackageSent = Extentions.TickCount;
this.mInternal.mLastPackageSent = Extentions.TickCount;
}
}
catch (Exception e)
@ -263,20 +194,100 @@ namespace BattleBitAPI.Server
}
// ---- Team ----
public IEnumerable<Player> GetAllPlayers()
public IEnumerable<TPlayer> GetAllPlayers()
{
var list = new List<Player>(254);
list.AddRange(this.mInternal.Players.Values);
var list = new List<TPlayer>(this.mInternal.Players.Values.Count);
foreach (var item in this.mInternal.Players.Values)
list.Add((TPlayer)item);
return list;
}
// ---- Virtual ----
public virtual async Task OnConnected()
{
}
public virtual async Task OnTick()
{
}
public virtual async Task OnReconnected()
{
}
public virtual async Task OnDisconnected()
{
}
public virtual async Task OnPlayerConnected(TPlayer player)
{
}
public virtual async Task OnPlayerDisconnected(TPlayer player)
{
}
public virtual async Task<bool> OnPlayerTypedMessage(TPlayer player, ChatChannel channel, string msg)
{
return true;
}
public virtual async Task<PlayerStats> OnGetPlayerStats(ulong steamID, PlayerStats officialStats)
{
return officialStats;
}
public virtual async Task OnSavePlayerStats(ulong steamID, PlayerStats stats)
{
}
public virtual async Task<bool> OnPlayerRequestingToChangeRole(TPlayer player, GameRole role)
{
return true;
}
public virtual async Task OnPlayerChangedRole(TPlayer player, GameRole role)
{
}
public virtual async Task OnPlayerJoinedSquad(TPlayer player, Squads squad)
{
}
public virtual async Task OnPlayerLeftSquad(TPlayer player, Squads squad)
{
}
public virtual async Task OnPlayerChangeTeam(TPlayer player, Team team)
{
}
public virtual async Task<PlayerSpawnRequest> OnPlayerSpawning(TPlayer player, PlayerSpawnRequest request)
{
return request;
}
public virtual async Task OnPlayerSpawned(TPlayer player)
{
}
public virtual async Task OnPlayerDied(TPlayer player)
{
}
public virtual async Task OnAPlayerKilledAnotherPlayer(OnPlayerKillArguments<TPlayer> args)
{
}
public virtual async Task OnPlayerReported(TPlayer from, TPlayer to, ReportReason reason, string additional)
{
}
// ---- Functions ----
public void WriteToSocket(Common.Serialization.Stream pck)
{
lock (mWriteStream)
lock (this.mInternal.mWriteStream)
{
mWriteStream.Write((uint)pck.WritePosition);
mWriteStream.Write(pck.Buffer, 0, pck.WritePosition);
this.mInternal.mWriteStream.Write((uint)pck.WritePosition);
this.mInternal.mWriteStream.Write(pck.Buffer, 0, pck.WritePosition);
}
}
public void ExecuteCommand(string cmd)
@ -285,11 +296,11 @@ namespace BattleBitAPI.Server
return;
int bytesLong = System.Text.Encoding.UTF8.GetByteCount(cmd);
lock (mWriteStream)
lock (this.mInternal.mWriteStream)
{
mWriteStream.Write((uint)(1 + 2 + bytesLong));
mWriteStream.Write((byte)NetworkCommuncation.ExecuteCommand);
mWriteStream.Write(cmd);
this.mInternal.mWriteStream.Write((uint)(1 + 2 + bytesLong));
this.mInternal.mWriteStream.Write((byte)NetworkCommuncation.ExecuteCommand);
this.mInternal.mWriteStream.Write(cmd);
}
}
@ -342,7 +353,7 @@ namespace BattleBitAPI.Server
{
ExecuteCommand("kick " + steamID + " " + reason);
}
public void Kick(Player player, string reason)
public void Kick(Player<TPlayer> player, string reason)
{
Kick(player.SteamID, reason);
}
@ -350,7 +361,7 @@ namespace BattleBitAPI.Server
{
ExecuteCommand("kill " + steamID);
}
public void Kill(Player player)
public void Kill(Player<TPlayer> player)
{
Kill(player.SteamID);
}
@ -358,7 +369,7 @@ namespace BattleBitAPI.Server
{
ExecuteCommand("changeteam " + steamID);
}
public void ChangeTeam(Player player)
public void ChangeTeam(Player<TPlayer> player)
{
ChangeTeam(player.SteamID);
}
@ -366,7 +377,7 @@ namespace BattleBitAPI.Server
{
ExecuteCommand("squadkick " + steamID);
}
public void KickFromSquad(Player player)
public void KickFromSquad(Player<TPlayer> player)
{
KickFromSquad(player.SteamID);
}
@ -374,7 +385,7 @@ namespace BattleBitAPI.Server
{
ExecuteCommand("squaddisband " + steamID);
}
public void DisbandPlayerCurrentSquad(Player player)
public void DisbandPlayerCurrentSquad(Player<TPlayer> player)
{
DisbandPlayerSSquad(player.SteamID);
}
@ -382,7 +393,7 @@ namespace BattleBitAPI.Server
{
ExecuteCommand("squadpromote " + steamID);
}
public void PromoteSquadLeader(Player player)
public void PromoteSquadLeader(Player<TPlayer> player)
{
PromoteSquadLeader(player.SteamID);
}
@ -390,7 +401,7 @@ namespace BattleBitAPI.Server
{
ExecuteCommand("warn " + steamID + " " + msg);
}
public void WarnPlayer(Player player, string msg)
public void WarnPlayer(Player<TPlayer> player, string msg)
{
WarnPlayer(player.SteamID, msg);
}
@ -398,7 +409,7 @@ namespace BattleBitAPI.Server
{
ExecuteCommand("msg " + steamID + " " + msg);
}
public void MessageToPlayer(Player player, string msg)
public void MessageToPlayer(Player<TPlayer> player, string msg)
{
MessageToPlayer(player.SteamID, msg);
}
@ -406,7 +417,7 @@ namespace BattleBitAPI.Server
{
ExecuteCommand("setrole " + steamID + " " + role);
}
public void SetRoleTo(Player player, GameRole role)
public void SetRoleTo(Player<TPlayer> player, GameRole role)
{
SetRoleTo(player.SteamID, role);
}
@ -434,52 +445,36 @@ namespace BattleBitAPI.Server
WriteToSocket(response);
}
}
public void SpawnPlayer(Player player, PlayerLoadout loadout, PlayerWearings wearings, Vector3 position, Vector3 lookDirection, PlayerStand stand, float spawnProtection)
public void SpawnPlayer(Player<TPlayer> player, PlayerLoadout loadout, PlayerWearings wearings, Vector3 position, Vector3 lookDirection, PlayerStand stand, float spawnProtection)
{
SpawnPlayer(player.SteamID, loadout, wearings, position, lookDirection, stand, spawnProtection);
}
// ---- Closing ----
public void CloseConnection(string additionInfo = string.Empty)
public void CloseConnection(string additionInfo = "")
{
if (string.IsNullOrWhiteSpace(additionInfo))
this.TerminationReason = additionInfo;
this.mInternal.TerminationReason = additionInfo;
else
this.TerminationReason = "User requested to terminate the connection";
this.mWantsToCloseConnection = true;
this.mInternal.TerminationReason = "User requested to terminate the connection";
this.mInternal.mWantsToCloseConnection = true;
}
private void mClose(string reason)
{
if (this.IsConnected)
{
this.TerminationReason = reason;
this.IsConnected = false;
this.mInternal.TerminationReason = reason;
this.mInternal.IsConnected = false;
}
}
// ---- Disposing ----
public void Dispose()
{
if (this.mIsDisposed)
return;
this.mIsDisposed = true;
if (this.mWriteStream != null)
if (this.mInternal.Socket != null)
{
this.mWriteStream.Dispose();
this.mWriteStream = null;
}
if (this.mReadStream != null)
{
this.mReadStream.Dispose();
this.mReadStream = null;
}
if (this.Socket != null)
{
this.Socket.SafeClose();
this.Socket = null;
this.mInternal.Socket.SafeClose();
this.mInternal.Socket = null;
}
}
@ -491,23 +486,141 @@ namespace BattleBitAPI.Server
this.ServerName;
}
// ---- Static ----
public static TGameServer CreateInstance<TGameServer>(Internal @internal) where TGameServer : GameServer<TPlayer>
{
TGameServer gameServer = (TGameServer)Activator.CreateInstance(typeof(TGameServer));
gameServer.mInternal = @internal;
return gameServer;
}
// ---- Internal ----
public class mInternalResources
public class Internal
{
public Dictionary<ulong, Player> Players = new Dictionary<ulong, Player>(254);
// ---- Variables ----
public ulong ServerHash;
public bool IsConnected;
public IPAddress GameIP;
public int GamePort;
public TcpClient Socket;
public Func<GameServer<TPlayer>, Internal, Common.Serialization.Stream, Task> mExecutionFunc;
public bool IsPasswordProtected;
public string ServerName;
public string Gamemode;
public string Map;
public MapSize MapSize;
public MapDayNight DayNight;
public int CurrentPlayers;
public int InQueuePlayers;
public int MaxPlayers;
public string LoadingScreenText;
public string ServerRulesText;
public ServerSettings<TPlayer> Settings;
public MapRotation<TPlayer> MapRotation;
public GamemodeRotation<TPlayer> GamemodeRotation;
public string TerminationReason;
public bool ReconnectFlag;
public mRoomSettings Settings = new mRoomSettings();
// ---- Private Variables ----
public byte[] mKeepAliveBuffer;
public Common.Serialization.Stream mWriteStream;
public Common.Serialization.Stream mReadStream;
public uint mReadPackageSize;
public long mLastPackageReceived;
public long mLastPackageSent;
public bool mWantsToCloseConnection;
public StringBuilder mBuilder;
public Internal()
{
this.TerminationReason = string.Empty;
this.mWriteStream = new Common.Serialization.Stream()
{
Buffer = new byte[Const.MaxNetworkPackageSize],
InPool = false,
ReadPosition = 0,
WritePosition = 0,
};
this.mReadStream = new Common.Serialization.Stream()
{
Buffer = new byte[Const.MaxNetworkPackageSize],
InPool = false,
ReadPosition = 0,
WritePosition = 0,
};
this.mKeepAliveBuffer = new byte[4]
{
0,0,0,0,
};
this.mLastPackageReceived = Extentions.TickCount;
this.mLastPackageSent = Extentions.TickCount;
this.mBuilder = new StringBuilder(4096);
this.Settings = new ServerSettings<TPlayer>(this);
this.MapRotation = new MapRotation<TPlayer>(this);
this.GamemodeRotation = new GamemodeRotation<TPlayer>(this);
}
// ---- Players In Room ----
public Dictionary<ulong, Player<TPlayer>> Players = new Dictionary<ulong, Player<TPlayer>>(254);
// ---- Room Settings ----
public mRoomSettings _Settings = new mRoomSettings();
public bool IsDirtySettings;
public HashSet<string> MapRotation = new HashSet<string>(8);
// ---- Map Rotation ----
public HashSet<string> _MapRotation = new HashSet<string>(8);
public bool MapRotationDirty = false;
public HashSet<string> GamemodeRotation = new HashSet<string>(8);
// ---- Gamemode Rotation ----
public HashSet<string> _GamemodeRotation = new HashSet<string>(8);
public bool GamemodeRotationDirty = false;
public void AddPlayer<TPlayer>(TPlayer player) where TPlayer : Player
// ---- Public Functions ----
public void Set(Func<GameServer<TPlayer>, Internal, Common.Serialization.Stream, Task> func, TcpClient socket, IPAddress iP, int port, bool isPasswordProtected, string serverName, string gamemode, string map, MapSize mapSize, MapDayNight dayNight, int currentPlayers, int inQueuePlayers, int maxPlayers, string loadingScreenText, string serverRulesText)
{
this.ServerHash = ((ulong)port << 32) | (ulong)iP.ToUInt();
this.IsConnected = true;
this.GameIP = iP;
this.GamePort = port;
this.Socket = socket;
this.mExecutionFunc = func;
this.IsPasswordProtected = isPasswordProtected;
this.ServerName = serverName;
this.Gamemode = gamemode;
this.Map = map;
this.MapSize = mapSize;
this.DayNight = dayNight;
this.CurrentPlayers = currentPlayers;
this.InQueuePlayers = inQueuePlayers;
this.MaxPlayers = maxPlayers;
this.LoadingScreenText = loadingScreenText;
this.ServerRulesText = serverRulesText;
this.Settings.Reset();
this._Settings.Reset();
this.IsDirtySettings = false;
this.MapRotation.Reset();
this._MapRotation.Clear();
this.MapRotationDirty = false;
this.GamemodeRotation.Reset();
this._GamemodeRotation.Clear();
this.GamemodeRotationDirty = false;
this.TerminationReason = string.Empty;
this.ReconnectFlag = false;
this.mWriteStream.Reset();
this.mReadStream.Reset();
this.mReadPackageSize = 0;
this.mLastPackageReceived = Extentions.TickCount;
this.mLastPackageSent = Extentions.TickCount;
this.mWantsToCloseConnection = false;
this.mBuilder.Clear();
}
public void AddPlayer(Player<TPlayer> player)
{
lock (Players)
{
@ -515,12 +628,12 @@ namespace BattleBitAPI.Server
Players.Add(player.SteamID, player);
}
}
public void RemovePlayer<TPlayer>(TPlayer player) where TPlayer : Player
public void RemovePlayer<TPlayer>(TPlayer player) where TPlayer : Player<TPlayer>
{
lock (Players)
Players.Remove(player.SteamID);
}
public bool TryGetPlayer(ulong steamID, out Player result)
public bool TryGetPlayer(ulong steamID, out Player<TPlayer> result)
{
lock (Players)
return Players.TryGetValue(steamID, out result);
@ -576,6 +689,23 @@ namespace BattleBitAPI.Server
this.SupportLimitPerSquad = ser.ReadInt8();
this.ReconLimitPerSquad = ser.ReadInt8();
}
public void Reset()
{
this.DamageMultiplier = 1.0f;
this.BleedingEnabled = true;
this.StamineEnabled = false;
this.FriendlyFireEnabled = false;
this.HideMapVotes = true;
this.OnlyWinnerTeamCanVote = false;
this.HitMarkersEnabled = true;
this.PointLogEnabled = true;
this.SpectatorEnabled = true;
this.MedicLimitPerSquad = 8;
this.EngineerLimitPerSquad = 8;
this.SupportLimitPerSquad = 8;
this.ReconLimitPerSquad = 8;
}
}
}
}

View File

@ -1,38 +1,42 @@
namespace BattleBitAPI.Server
{
public class GamemodeRotation
public class GamemodeRotation<TPlayer> where TPlayer : Player<TPlayer>
{
private GameServer.mInternalResources mResources;
public GamemodeRotation(GameServer.mInternalResources resources)
private GameServer<TPlayer>.Internal mResources;
public GamemodeRotation(GameServer<TPlayer>.Internal resources)
{
mResources = resources;
}
public IEnumerable<string> GetGamemodeRotation()
{
lock (mResources.GamemodeRotation)
return new List<string>(mResources.GamemodeRotation);
lock (mResources._GamemodeRotation)
return new List<string>(mResources._GamemodeRotation);
}
public bool InRotation(string gamemode)
{
lock (mResources.GamemodeRotation)
return mResources.GamemodeRotation.Contains(gamemode);
lock (mResources._GamemodeRotation)
return mResources._GamemodeRotation.Contains(gamemode);
}
public bool RemoveFromRotation(string gamemode)
{
lock (mResources.GamemodeRotation)
if (!mResources.GamemodeRotation.Remove(gamemode))
lock (mResources._GamemodeRotation)
if (!mResources._GamemodeRotation.Remove(gamemode))
return false;
mResources.GamemodeRotationDirty = true;
return true;
}
public bool AddToRotation(string gamemode)
{
lock (mResources.GamemodeRotation)
if (!mResources.GamemodeRotation.Add(gamemode))
lock (mResources._GamemodeRotation)
if (!mResources._GamemodeRotation.Add(gamemode))
return false;
mResources.GamemodeRotationDirty = true;
return true;
}
public void Reset()
{
}
}
}

View File

@ -1,31 +1,31 @@
namespace BattleBitAPI.Server
{
public class MapRotation
public class MapRotation<TPlayer> where TPlayer : Player<TPlayer>
{
private GameServer.mInternalResources mResources;
public MapRotation(GameServer.mInternalResources resources)
private GameServer<TPlayer>.Internal mResources;
public MapRotation(GameServer<TPlayer>.Internal resources)
{
mResources = resources;
}
public IEnumerable<string> GetMapRotation()
{
lock (mResources.MapRotation)
return new List<string>(mResources.MapRotation);
lock (mResources._MapRotation)
return new List<string>(mResources._MapRotation);
}
public bool InRotation(string map)
{
map = map.ToUpperInvariant();
lock (mResources.MapRotation)
return mResources.MapRotation.Contains(map);
lock (mResources._MapRotation)
return mResources._MapRotation.Contains(map);
}
public bool RemoveFromRotation(string map)
{
map = map.ToUpperInvariant();
lock (mResources.MapRotation)
if (!mResources.MapRotation.Remove(map))
lock (mResources._MapRotation)
if (!mResources._MapRotation.Remove(map))
return false;
mResources.MapRotationDirty = true;
return true;
@ -34,11 +34,15 @@
{
map = map.ToUpperInvariant();
lock (mResources.MapRotation)
if (!mResources.MapRotation.Add(map))
lock (mResources._MapRotation)
if (!mResources._MapRotation.Add(map))
return false;
mResources.MapRotationDirty = true;
return true;
}
public void Reset()
{
}
}
}

View File

@ -1,86 +1,89 @@
namespace BattleBitAPI.Server
{
public class ServerSettings
public class ServerSettings<TPlayer> where TPlayer : Player<TPlayer>
{
private GameServer.mInternalResources mResources;
public ServerSettings(GameServer.mInternalResources resources)
private GameServer<TPlayer>.Internal mResources;
public ServerSettings(GameServer<TPlayer>.Internal resources)
{
mResources = resources;
}
public float DamageMultiplier
{
get => mResources.Settings.DamageMultiplier;
get => mResources._Settings.DamageMultiplier;
set
{
mResources.Settings.DamageMultiplier = value;
mResources._Settings.DamageMultiplier = value;
mResources.IsDirtySettings = true;
}
}
public bool BleedingEnabled
{
get => mResources.Settings.BleedingEnabled;
get => mResources._Settings.BleedingEnabled;
set
{
mResources.Settings.BleedingEnabled = value;
mResources._Settings.BleedingEnabled = value;
mResources.IsDirtySettings = true;
}
}
public bool StamineEnabled
{
get => mResources.Settings.StamineEnabled;
get => mResources._Settings.StamineEnabled;
set
{
mResources.Settings.StamineEnabled = value;
mResources._Settings.StamineEnabled = value;
mResources.IsDirtySettings = true;
}
}
public bool FriendlyFireEnabled
{
get => mResources.Settings.FriendlyFireEnabled;
get => mResources._Settings.FriendlyFireEnabled;
set
{
mResources.Settings.FriendlyFireEnabled = value;
mResources._Settings.FriendlyFireEnabled = value;
mResources.IsDirtySettings = true;
}
}
public bool OnlyWinnerTeamCanVote
{
get => mResources.Settings.OnlyWinnerTeamCanVote;
get => mResources._Settings.OnlyWinnerTeamCanVote;
set
{
mResources.Settings.OnlyWinnerTeamCanVote = value;
mResources._Settings.OnlyWinnerTeamCanVote = value;
mResources.IsDirtySettings = true;
}
}
public bool HitMarkersEnabled
{
get => mResources.Settings.HitMarkersEnabled;
get => mResources._Settings.HitMarkersEnabled;
set
{
mResources.Settings.HitMarkersEnabled = value;
mResources._Settings.HitMarkersEnabled = value;
mResources.IsDirtySettings = true;
}
}
public bool PointLogEnabled
{
get => mResources.Settings.PointLogEnabled;
get => mResources._Settings.PointLogEnabled;
set
{
mResources.Settings.PointLogEnabled = value;
mResources._Settings.PointLogEnabled = value;
mResources.IsDirtySettings = true;
}
}
public bool SpectatorEnabled
{
get => mResources.Settings.SpectatorEnabled;
get => mResources._Settings.SpectatorEnabled;
set
{
mResources.Settings.SpectatorEnabled = value;
mResources._Settings.SpectatorEnabled = value;
mResources.IsDirtySettings = true;
}
}
public void Reset()
{
}
}
}

View File

@ -1,4 +1,5 @@
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Numerics;
using BattleBitAPI.Common;
@ -9,7 +10,7 @@ using CommunityServerAPI.BattleBitAPI;
namespace BattleBitAPI.Server
{
public class ServerListener<TPlayer> : IDisposable where TPlayer : Player
public class ServerListener<TPlayer, TGameServer> : IDisposable where TPlayer : Player<TPlayer> where TGameServer : GameServer<TPlayer>
{
// --- Public ---
public bool IsListening { get; private set; }
@ -17,11 +18,6 @@ namespace BattleBitAPI.Server
public int ListeningPort { get; private set; }
// --- Events ---
/// <summary>
/// Fired when game server is ticking (~100hz)<br/>
/// </summary>
public Func<GameServer, Task> OnGameServerTick { get; set; }
/// <summary>
/// Fired when an attempt made to connect to the server.<br/>
/// Default, any connection attempt will be accepted
@ -36,206 +32,16 @@ namespace BattleBitAPI.Server
/// </value>
public Func<IPAddress, Task<bool>> OnGameServerConnecting { get; set; }
/// <summary>
/// Fired when a game server connects.
/// </summary>
///
/// <remarks>
/// GameServer: Game server that is connecting.<br/>
/// </remarks>
public Func<GameServer, Task> OnGameServerConnected { get; set; }
/// <summary>
/// Fired when a game server reconnects. (When game server connects while a socket is already open)
/// </summary>
///
/// <remarks>
/// GameServer: Game server that is reconnecting.<br/>
/// </remarks>
public Func<GameServer, Task> OnGameServerReconnected { get; set; }
/// <summary>
/// Fired when a game server disconnects. Check (GameServer.TerminationReason) to see the reason.
/// </summary>
///
/// <remarks>
/// GameServer: Game server that disconnected.<br/>
/// </remarks>
public Func<GameServer, Task> OnGameServerDisconnected { get; set; }
/// <summary>
/// Fired when a player connects to a server.<br/>
/// Check player.GameServer get the server that player joined.
/// </summary>
///
/// <remarks>
/// Player: The player that connected to the server<br/>
/// </remarks>
public Func<TPlayer, Task> OnPlayerConnected { get; set; }
/// <summary>
/// Fired when a player disconnects from a server.<br/>
/// Check player.GameServer get the server that player left.
/// </summary>
///
/// <remarks>
/// Player: The player that disconnected from the server<br/>
/// </remarks>
public Func<TPlayer, Task> OnPlayerDisconnected { get; set; }
/// <summary>
/// Fired when a player types a message to text chat.<br/>
/// </summary>
///
/// <remarks>
/// Player: The player that typed the message <br/>
/// ChatChannel: The channel the message was sent <br/>
/// string - Message: The message<br/>
/// </remarks>
/// <value>
/// Returns: True if you let the message broadcasted, false if you don't it to be broadcasted.
/// </value>
public Func<TPlayer, ChatChannel, string, Task<bool>> OnPlayerTypedMessage { get; set; }
/// <summary>
/// Fired when a player kills another player.
/// </summary>
///
/// <remarks>
/// OnPlayerKillArguments: Details about the kill<br/>
/// </remarks>
public Func<OnPlayerKillArguments<TPlayer>, Task> OnAPlayerKilledAnotherPlayer { get; set; }
/// <summary>
/// Fired when game server requests the stats of a player, this function should return in 3000ms or player will not able to join to server.
/// </summary>
///
/// <remarks>
/// ulong - SteamID of the player<br/>
/// PlayerStats - The official stats of the player<br/>
/// </remarks>
/// <value>
/// Returns: The modified stats of the player.
/// </value>
public Func<ulong, PlayerStats, Task<PlayerStats>> OnGetPlayerStats { get; set; }
/// <summary>
/// Fired when game server requests to save the stats of a player.
/// </summary>
///
/// <remarks>
/// ulong - SteamID of the player<br/>
/// PlayerStats - Stats of the player<br/>
/// </remarks>
/// <value>
/// Returns: The stats of the player.
/// </value>
public Func<ulong, PlayerStats, Task> OnSavePlayerStats { get; set; }
/// <summary>
/// Fired when a player requests server to change role.
/// </summary>
///
/// <remarks>
/// TPlayer - The player requesting<br/>
/// GameRole - The role the player asking to change<br/>
/// </remarks>
/// <value>
/// Returns: True if you accept if, false if you don't.
/// </value>
public Func<TPlayer, GameRole, Task<bool>> OnPlayerRequestingToChangeRole { get; set; }
/// <summary>
/// Fired when a player changes their game role.
/// </summary>
///
/// <remarks>
/// TPlayer - The player<br/>
/// GameRole - The new role of the player<br/>
/// </remarks>
public Func<TPlayer, GameRole, Task> OnPlayerChangedRole { get; set; }
/// <summary>
/// Fired when a player joins a squad.
/// </summary>
///
/// <remarks>
/// TPlayer - The player<br/>
/// Squads - The squad player joined<br/>
/// </remarks>
public Func<TPlayer, Squads, Task> OnPlayerJoinedASquad { get; set; }
/// <summary>
/// Fired when a player leaves their squad.
/// </summary>
///
/// <remarks>
/// TPlayer - The player<br/>
/// Squads - The squad that player left<br/>
/// </remarks>
public Func<TPlayer, Squads, Task> OnPlayerLeftSquad { get; set; }
/// <summary>
/// Fired when a player changes team.
/// </summary>
///
/// <remarks>
/// TPlayer - The player<br/>
/// Team - The new team that player joined<br/>
/// </remarks>
public Func<TPlayer, Team, Task> OnPlayerChangedTeam { get; set; }
/// <summary>
/// Fired when a player is spawning.
/// </summary>
///
/// <remarks>
/// TPlayer - The player<br/>
/// PlayerSpawnRequest - The request<br/>
/// </remarks>
/// <value>
/// Returns: The new spawn response
/// </value>
public Func<TPlayer, PlayerSpawnRequest, Task<PlayerSpawnRequest>> OnPlayerSpawning { get; set; }
/// <summary>
/// Fired when a player is spawns
/// </summary>
///
/// <remarks>
/// TPlayer - The player<br/>
/// </remarks>
public Func<TPlayer, Task> OnPlayerSpawned { get; set; }
/// <summary>
/// Fired when a player dies
/// </summary>
///
/// <remarks>
/// TPlayer - The player<br/>
/// </remarks>
public Func<TPlayer, Task> OnPlayerDied { get; set; }
/// <summary>
/// Fired when a player reports another player.
/// </summary>
///
/// <remarks>
/// TPlayer - The reporter player<br/>
/// TPlayer - The reported player<br/>
/// ReportReason - The reason of report<br/>
/// String - Additional detail<br/>
/// </remarks>
public Func<TPlayer, TPlayer, ReportReason, string, Task> OnPlayerReported { get; set; }
// --- Private ---
private TcpListener mSocket;
private Dictionary<ulong, GameServer> mActiveConnections;
private Dictionary<ulong, (TGameServer server, GameServer<TPlayer>.Internal resources)> mActiveConnections;
private mInstances<TPlayer, TGameServer> mInstanceDatabase;
// --- Construction ---
public ServerListener()
{
this.mActiveConnections = new Dictionary<ulong, GameServer>(16);
this.mActiveConnections = new Dictionary<ulong, (TGameServer, GameServer<TPlayer>.Internal)>(16);
this.mInstanceDatabase = new mInstances<TPlayer, TGameServer>();
}
// --- Starting ---
@ -304,8 +110,8 @@ namespace BattleBitAPI.Server
return;
}
GameServer server = null;
GameServer.mInternalResources resources;
TGameServer server = null;
GameServer<TPlayer>.Internal resources;
try
{
using (CancellationTokenSource source = new CancellationTokenSource(Const.HailConnectTimeout))
@ -491,8 +297,25 @@ namespace BattleBitAPI.Server
}
}
resources = new GameServer.mInternalResources();
server = new GameServer(client, resources, mExecutePackage, ip, gamePort, isPasswordProtected, serverName, gameMode, gamemap, size, dayNight, currentPlayers, queuePlayers, maxPlayers, loadingScreenText, serverRulesText);
var hash = ((ulong)gamePort << 32) | (ulong)ip.ToUInt();
server = this.mInstanceDatabase.GetServerInstance(hash, out resources);
resources.Set(
this.mExecutePackage,
client,
ip,
gamePort,
isPasswordProtected,
serverName,
gameMode,
gamemap,
size,
dayNight,
currentPlayers,
queuePlayers,
maxPlayers,
loadingScreenText,
serverRulesText
);
//Room settings
{
@ -504,7 +327,7 @@ namespace BattleBitAPI.Server
readStream.Reset();
if (!await networkStream.TryRead(readStream, roomSize, source.Token))
throw new Exception("Unable to read the room");
resources.Settings.Read(readStream);
resources._Settings.Read(readStream);
}
//Map&gamemode rotation
@ -523,7 +346,7 @@ namespace BattleBitAPI.Server
{
count--;
if (readStream.TryReadString(out var item))
resources.MapRotation.Add(item.ToUpperInvariant());
resources._MapRotation.Add(item.ToUpperInvariant());
}
count = readStream.ReadUInt32();
@ -531,7 +354,7 @@ namespace BattleBitAPI.Server
{
count--;
if (readStream.TryReadString(out var item))
resources.GamemodeRotation.Add(item);
resources._GamemodeRotation.Add(item);
}
}
@ -640,11 +463,11 @@ namespace BattleBitAPI.Server
wearings.Read(readStream);
}
TPlayer player = Activator.CreateInstance<TPlayer>();
TPlayer player = mInstanceDatabase.GetPlayerInstance(steamid);
player.SteamID = steamid;
player.Name = username;
player.IP = new IPAddress(ipHash);
player.GameServer = server;
player.GameServer = (GameServer<TPlayer>)server;
player.Team = team;
player.Squad = squad;
player.Role = role;
@ -652,8 +475,6 @@ namespace BattleBitAPI.Server
player.CurrentLoadout = loadout;
player.CurrentWearings = wearings;
await player.OnInitialized();
resources.AddPlayer(player);
}
@ -679,12 +500,6 @@ namespace BattleBitAPI.Server
}
catch { }
if (server != null)
{
server.Dispose();
server = null;
}
client.SafeClose();
return;
}
@ -697,25 +512,23 @@ namespace BattleBitAPI.Server
//An old connection exist with same IP + Port?
if (connectionExist = this.mActiveConnections.TryGetValue(server.ServerHash, out var oldServer))
{
oldServer.ReconnectFlag = true;
oldServer.resources.ReconnectFlag = true;
this.mActiveConnections.Remove(server.ServerHash);
}
this.mActiveConnections.Add(server.ServerHash, server);
this.mActiveConnections.Add(server.ServerHash, (server, resources));
}
//Call the callback.
if (!connectionExist)
{
//New connection!
if (OnGameServerConnected != null)
await OnGameServerConnected.Invoke(server);
await server.OnConnected();
}
else
{
//Reconnection
if (OnGameServerReconnected != null)
await OnGameServerReconnected.Invoke(server);
await server.OnReconnected();
}
//Set the buffer sizes.
@ -725,20 +538,19 @@ namespace BattleBitAPI.Server
//Join to main server loop.
await mHandleGameServer(server);
}
private async Task mHandleGameServer(GameServer server)
private async Task mHandleGameServer(TGameServer server)
{
using (server)
{
while (server.IsConnected)
{
if (OnGameServerTick != null)
await OnGameServerTick(server);
await server.OnTick();
await server.Tick();
await Task.Delay(10);
}
if (OnGameServerDisconnected != null && !server.ReconnectFlag)
await OnGameServerDisconnected.Invoke(server);
if (!server.ReconnectFlag)
await server.OnDisconnected();
}
//Remove from list.
@ -748,7 +560,7 @@ namespace BattleBitAPI.Server
}
// --- Logic Executing ---
private async Task mExecutePackage(GameServer server, GameServer.mInternalResources resources, Common.Serialization.Stream stream)
private async Task mExecutePackage(GameServer<TPlayer> server, GameServer<TPlayer>.Internal resources, Common.Serialization.Stream stream)
{
var communcation = (NetworkCommuncation)stream.ReadInt8();
switch (communcation)
@ -765,21 +577,19 @@ namespace BattleBitAPI.Server
Squads squad = (Squads)stream.ReadInt8();
GameRole role = (GameRole)stream.ReadInt8();
TPlayer player = Activator.CreateInstance<TPlayer>();
TPlayer player = mInstanceDatabase.GetPlayerInstance(steamID);
player.SteamID = steamID;
player.Name = username;
player.IP = new IPAddress(ip);
player.GameServer = server;
player.GameServer = (GameServer<TPlayer>)server;
player.Team = team;
player.Squad = squad;
player.Role = role;
await player.OnInitialized();
resources.AddPlayer(player);
if (OnPlayerConnected != null)
await OnPlayerConnected.Invoke(player);
await server.OnPlayerConnected(player);
await player.OnConnected();
}
}
break;
@ -790,14 +600,14 @@ namespace BattleBitAPI.Server
{
ulong steamID = stream.ReadUInt64();
bool exist;
Player player;
Player<TPlayer> player;
lock (resources.Players)
exist = resources.Players.Remove(steamID, out player);
if (exist)
{
if (OnPlayerDisconnected != null)
await OnPlayerDisconnected.Invoke((TPlayer)player);
await server.OnPlayerDisconnected((TPlayer)player);
await player.OnDisconnected();
}
}
break;
@ -814,9 +624,7 @@ namespace BattleBitAPI.Server
ChatChannel chat = (ChatChannel)stream.ReadInt8();
if (stream.TryReadString(out var msg))
{
bool pass = true;
if (OnPlayerTypedMessage != null)
pass = await OnPlayerTypedMessage.Invoke((TPlayer)player, chat, msg);
var pass = await server.OnPlayerTypedMessage((TPlayer)player, chat, msg);
//Respond back.
using (var response = Common.Serialization.Stream.Get())
@ -850,22 +658,17 @@ namespace BattleBitAPI.Server
{
if (resources.TryGetPlayer(victim, out var victimClient))
{
if (OnAPlayerKilledAnotherPlayer != null)
var args = new OnPlayerKillArguments<TPlayer>()
{
var args = new OnPlayerKillArguments<TPlayer>()
{
Killer = (TPlayer)killerClient,
KillerPosition = killerPos,
Victim = (TPlayer)victimClient,
VictimPosition = victimPos,
BodyPart = body,
SourceOfDamage = source,
KillerTool = tool,
};
await OnAPlayerKilledAnotherPlayer.Invoke(args);
}
Killer = (TPlayer)killerClient,
KillerPosition = killerPos,
Victim = (TPlayer)victimClient,
VictimPosition = victimPos,
BodyPart = body,
SourceOfDamage = source,
KillerTool = tool,
};
await server.OnAPlayerKilledAnotherPlayer(args);
}
}
}
@ -881,9 +684,7 @@ namespace BattleBitAPI.Server
var stats = new PlayerStats();
stats.Read(stream);
if (OnGetPlayerStats != null)
stats = await OnGetPlayerStats.Invoke(steamID, stats);
stats = await server.OnGetPlayerStats(steamID, stats);
using (var response = Common.Serialization.Stream.Get())
{
@ -903,8 +704,7 @@ namespace BattleBitAPI.Server
PlayerStats stats = new PlayerStats();
stats.Read(stream);
if (OnSavePlayerStats != null)
await OnSavePlayerStats.Invoke(steamID, stats);
await server.OnSavePlayerStats(steamID, stats);
}
break;
}
@ -919,8 +719,7 @@ namespace BattleBitAPI.Server
{
bool accepted = true;
if (OnPlayerRequestingToChangeRole != null)
accepted = await OnPlayerRequestingToChangeRole.Invoke((TPlayer)client, role);
accepted = await server.OnPlayerRequestingToChangeRole((TPlayer)client, role);
if (accepted)
server.SetRoleTo(steamID, role);
@ -938,8 +737,7 @@ namespace BattleBitAPI.Server
if (resources.TryGetPlayer(steamID, out var client))
{
client.Role = role;
if (OnPlayerChangedRole != null)
await OnPlayerChangedRole.Invoke((TPlayer)client, role);
await server.OnPlayerChangedRole((TPlayer)client, role);
}
}
break;
@ -954,8 +752,7 @@ namespace BattleBitAPI.Server
if (resources.TryGetPlayer(steamID, out var client))
{
client.Squad = squad;
if (OnPlayerJoinedASquad != null)
await OnPlayerJoinedASquad.Invoke((TPlayer)client, squad);
await server.OnPlayerJoinedSquad((TPlayer)client, squad);
}
}
break;
@ -973,12 +770,10 @@ namespace BattleBitAPI.Server
client.Squad = Squads.NoSquad;
client.Role = GameRole.Assault;
if (OnPlayerLeftSquad != null)
await OnPlayerLeftSquad.Invoke((TPlayer)client, oldSquad);
await server.OnPlayerLeftSquad((TPlayer)client, oldSquad);
if (oldRole != GameRole.Assault)
if (OnPlayerChangedRole != null)
await OnPlayerChangedRole.Invoke((TPlayer)client, GameRole.Assault);
await server.OnPlayerChangedRole((TPlayer)client, GameRole.Assault);
}
}
break;
@ -993,8 +788,7 @@ namespace BattleBitAPI.Server
if (resources.TryGetPlayer(steamID, out var client))
{
client.Team = team;
if (OnPlayerChangedTeam != null)
await OnPlayerChangedTeam.Invoke((TPlayer)client, team);
await server.OnPlayerChangeTeam((TPlayer)client, team);
}
}
break;
@ -1010,8 +804,7 @@ namespace BattleBitAPI.Server
ushort vehicleID = stream.ReadUInt16();
if (resources.TryGetPlayer(steamID, out var client))
if (this.OnPlayerSpawning != null)
request = await OnPlayerSpawning.Invoke((TPlayer)client, request);
request = await server.OnPlayerSpawning((TPlayer)client, request);
//Respond back.
using (var response = Common.Serialization.Stream.Get())
@ -1038,8 +831,7 @@ namespace BattleBitAPI.Server
{
if (resources.TryGetPlayer(reported, out var reportedClient))
{
if (OnPlayerReported != null)
await OnPlayerReported.Invoke((TPlayer)reporterClient, (TPlayer)reportedClient, reason, additionalInfo);
await server.OnPlayerReported((TPlayer)reporterClient, (TPlayer)reportedClient, reason, additionalInfo);
}
}
}
@ -1063,9 +855,7 @@ namespace BattleBitAPI.Server
client.IsAlive = true;
await client.OnSpawned();
if (OnPlayerSpawned != null)
await OnPlayerSpawned.Invoke((TPlayer)client);
await server.OnPlayerSpawned((TPlayer)client);
}
}
break;
@ -1083,8 +873,7 @@ namespace BattleBitAPI.Server
await client.OnDied();
if (OnPlayerDied != null)
await OnPlayerDied.Invoke((TPlayer)client);
await server.OnPlayerDied((TPlayer)client);
}
}
break;
@ -1094,14 +883,14 @@ namespace BattleBitAPI.Server
if (stream.CanRead(4))
{
uint count = stream.ReadUInt32();
lock (resources.MapRotation)
lock (resources._MapRotation)
{
resources.MapRotation.Clear();
resources._MapRotation.Clear();
while (count > 0)
{
count--;
if (stream.TryReadString(out var map))
resources.MapRotation.Add(map.ToUpperInvariant());
resources._MapRotation.Add(map.ToUpperInvariant());
}
}
}
@ -1112,14 +901,14 @@ namespace BattleBitAPI.Server
if (stream.CanRead(4))
{
uint count = stream.ReadUInt32();
lock (resources.GamemodeRotation)
lock (resources._GamemodeRotation)
{
resources.GamemodeRotation.Clear();
resources._GamemodeRotation.Clear();
while (count > 0)
{
count--;
if (stream.TryReadString(out var map))
resources.GamemodeRotation.Add(map);
resources._GamemodeRotation.Add(map);
}
}
}
@ -1139,5 +928,49 @@ namespace BattleBitAPI.Server
if (IsListening)
Stop();
}
// --- Classes ---
private class mInstances<TPlayer, TGameServer> where TPlayer : Player<TPlayer> where TGameServer : GameServer<TPlayer>
{
private Dictionary<ulong, (TGameServer, GameServer<TPlayer>.Internal)> mGameServerInstances;
private Dictionary<ulong, TPlayer> mPlayerInstances;
public mInstances()
{
this.mGameServerInstances = new Dictionary<ulong, (TGameServer, GameServer<TPlayer>.Internal)>(64);
this.mPlayerInstances = new Dictionary<ulong, TPlayer>(1024 * 16);
}
public TGameServer GetServerInstance(ulong hash, out GameServer<TPlayer>.Internal @internal)
{
lock (mGameServerInstances)
{
if (mGameServerInstances.TryGetValue(hash, out var data))
{
@internal = data.Item2;
return data.Item1;
}
@internal = new GameServer<TPlayer>.Internal();
TGameServer gameServer = GameServer<TPlayer>.CreateInstance<TGameServer>(@internal);
mGameServerInstances.Add(hash, (gameServer, @internal));
return gameServer;
}
}
public TPlayer GetPlayerInstance(ulong steamID)
{
lock (this.mPlayerInstances)
{
if (this.mPlayerInstances.TryGetValue(steamID, out var player))
return player;
player = Activator.CreateInstance<TPlayer>();
player.OnCreated();
mPlayerInstances.Add(steamID, player);
return player;
}
}
}
}
}

View File

@ -1,22 +1,39 @@
using BattleBitAPI;
using BattleBitAPI.Common;
using BattleBitAPI.Server;
using System.Diagnostics;
using System.Net;
using System.Text;
class Program
{
static void Main(string[] args)
{
var listener = new ServerListener<MyPlayer>();
var listener = new ServerListener<MyPlayer, MyGameServer>();
listener.Start(29294);
listener.OnGameServerConnecting += OnGameServerConnecting;
Thread.Sleep(-1);
}
private static async Task<bool> OnGameServerConnecting(IPAddress ip)
{
return true;
}
}
class MyPlayer : Player
class MyPlayer : Player<MyPlayer>
{
public int NumberOfSpawns = 0;
}
public override async Task OnSpawned()
{
this.NumberOfSpawns++;
base.GameServer.CloseConnection();
await Console.Out.WriteLineAsync("Spawn: " + this.NumberOfSpawns);
}
}
class MyGameServer : GameServer<MyPlayer>
{
public override async Task OnConnected()
{
Console.WriteLine(base.GameIP + " connected");
}
}