using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using SharedLibraryCore.Helpers; using SharedLibraryCore.Objects; using SharedLibraryCore.Dtos; using SharedLibraryCore.Configuration; using SharedLibraryCore.Interfaces; namespace SharedLibraryCore { public abstract class Server { public enum Game { UKN, IW3, IW4, IW5, T4, T5, T5M, T6M, } public Server(IManager mgr, ServerConfiguration config) { Password = config.Password; IP = config.IPAddress; Port = config.Port; Manager = mgr; Logger = Manager.GetLogger(this.GetHashCode()); Logger.WriteInfo(this.ToString()); ServerConfig = config; RemoteConnection = new RCon.Connection(IP, Port, Password, Logger); Players = new List(new Player[18]); Reports = new List(); PlayerHistory = new Queue(); ChatHistory = new List(); NextMessage = 0; CustomSayEnabled = Manager.GetApplicationSettings().Configuration().EnableCustomSayName; CustomSayName = Manager.GetApplicationSettings().Configuration().CustomSayName; InitializeTokens(); InitializeAutoMessages(); } //Returns current server IP set by `net_ip` -- *STRING* public String GetIP() { return IP; } //Returns current server port set by `net_port` -- *INT* public int GetPort() { return Port; } //Returns list of all current players public List GetPlayersAsList() { return Players.FindAll(x => x != null); } /// /// Add a player to the server's player list /// /// Player pulled from memory reading /// True if player added sucessfully, false otherwise abstract public Task AddPlayer(Player P); /// /// Remove player by client number /// /// Client ID of player to be removed /// true if removal succeded, false otherwise abstract public Task RemovePlayer(int cNum); /// /// Get a player by name /// /// Player name to search for /// Matching player if found public List GetClientByName(String pName) { string[] QuoteSplit = pName.Split('"'); bool literal = false; if (QuoteSplit.Length > 1) { pName = QuoteSplit[1]; literal = true; } if (literal) return Players.Where(p => p != null && p.Name.ToLower().Equals(pName.ToLower())).ToList(); return Players.Where(p => p != null && p.Name.ToLower().Contains(pName.ToLower())).ToList(); } virtual public Task ProcessUpdatesAsync(CancellationToken cts) => (Task)Task.CompletedTask; /// /// Process any server event /// /// Event /// True on sucess abstract protected Task ProcessEvent(GameEvent E); abstract public Task ExecuteEvent(GameEvent E); /// /// Send a message to all players /// /// Message to be sent to all players public GameEvent Broadcast(string message, Player sender = null) { #if DEBUG == false string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Say, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{message}"); #else Logger.WriteVerbose(message.StripColors()); #endif var e = new GameEvent() { Type = GameEvent.EventType.Broadcast, #if DEBUG == true Data = message, #else Data = formattedMessage, #endif Owner = this, Origin = sender, }; Manager.GetEventHandler().AddEvent(e); return e; } /// /// Send a message to a particular players /// /// Message to send /// Player to send message to protected async Task Tell(String Message, Player Target) { #if !DEBUG string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Tell, Target.ClientNumber, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{Message}"); if (Target.ClientNumber > -1 && Message.Length > 0 && Target.Level != Player.Permission.Console) await this.ExecuteCommandAsync(formattedMessage); #else Logger.WriteVerbose($"{Target.ClientNumber}->{Message.StripColors()}"); await Task.CompletedTask; #endif if (Target.Level == Player.Permission.Console) { Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine(Message.StripColors()); Console.ForegroundColor = ConsoleColor.Gray; } // prevent this from queueing up too many command responses if (CommandResult.Count > 15) CommandResult.RemoveAt(0); // it was a remote command so we need to add it to the command result queue if (Target.ClientNumber < 0) { CommandResult.Add(new CommandResponseInfo() { Response = Message.StripColors(), ClientId = Target.ClientId }); } } /// /// Send a message to all admins on the server /// /// Message to send out public void ToAdmins(String message) { foreach (var client in GetPlayersAsList().Where(c => c.Level > Player.Permission.Flagged)) { client.Tell(message); } } /// /// Kick a player from the server /// /// Reason for kicking /// Player to kick abstract protected Task Kick(String Reason, Player Target, Player Origin); /// /// Temporarily ban a player ( default 1 hour ) from the server /// /// Reason for banning the player /// The player to ban abstract protected Task TempBan(String Reason, TimeSpan length, Player Target, Player Origin); /// /// Perm ban a player from the server /// /// The reason for the ban /// The person to ban /// The person who banned the target abstract protected Task Ban(String Reason, Player Target, Player Origin); abstract protected Task Warn(String Reason, Player Target, Player Origin); /// /// Unban a player by npID / GUID /// /// npID of the player /// I don't remember what this is for /// abstract public Task Unban(string reason, Player Target, Player Origin); /// /// Change the current searver map /// /// Non-localized map name public async Task LoadMap(string mapName) { await this.ExecuteCommandAsync($"map {mapName}"); } public async Task LoadMap(Map newMap) { await this.ExecuteCommandAsync($"map {newMap.Name}"); } /// /// Initalize the macro variables /// abstract public void InitializeTokens(); /// /// Read the map configuration /// protected void InitializeMaps() { Maps = new List(); var gameMaps = Manager.GetApplicationSettings().Configuration().Maps.FirstOrDefault(m => m.Game == GameName); if (gameMaps != null) Maps.AddRange(gameMaps.Maps); } /// /// Initialize the messages to be broadcasted /// protected void InitializeAutoMessages() { BroadcastMessages = new List(); if (ServerConfig.AutoMessages != null) BroadcastMessages.AddRange(ServerConfig.AutoMessages); BroadcastMessages.AddRange(Manager.GetApplicationSettings().Configuration().AutoMessages); } public override string ToString() { return $"{IP}-{Port}"; } protected async Task ScriptLoaded() { try { return (await this.GetDvarAsync("sv_customcallbacks")).Value == "1"; } catch (Exceptions.DvarException) { return false; } } // Objects public IManager Manager { get; protected set; } public ILogger Logger { get; private set; } public ServerConfiguration ServerConfig { get; private set; } public List Maps { get; protected set; } public List Reports { get; set; } public List ChatHistory { get; protected set; } public Queue PlayerHistory { get; private set; } public Game GameName { get; protected set; } // Info public string Hostname { get; protected set; } public string Website { get; protected set; } public string Gametype { get; set; } public Map CurrentMap { get; set; } public int ClientNum { get { return Players.Where(p => p != null).Count(); } } public int MaxClients { get; protected set; } public List Players { get; protected set; } public string Password { get; private set; } public bool Throttled { get; protected set; } public bool CustomCallback { get; protected set; } public string WorkingDirectory { get; protected set; } public RCon.Connection RemoteConnection { get; protected set; } public IRConParser RconParser { get; protected set; } public IEventParser EventParser { get; set; } public string LogPath { get; protected set; } // Internal protected string IP; protected int Port; protected string FSGame; protected int NextMessage; protected int ConnectionErrors; protected List BroadcastMessages; protected TimeSpan LastMessage; protected DateTime LastPoll; protected ManualResetEventSlim OnRemoteCommandResponse; // only here for performance private readonly bool CustomSayEnabled; private readonly string CustomSayName; //Remote public IList CommandResult = new List(); } }