IW4M-Admin/SharedLibrary/Server.cs
RaidMax 979b1f2310 fixed initialization error when no map set exists in config
fixed discord link showing when no invite specified
OpenGraph image set to absolute url
more changes to killcallback and logging
fixed some angle conversion stuff
2018-03-25 23:51:25 -05:00

329 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using SharedLibrary.Network;
using SharedLibrary.Commands;
using System.Threading.Tasks;
using SharedLibrary.Helpers;
using SharedLibrary.Objects;
using SharedLibrary.Dtos;
using SharedLibrary.Configuration;
namespace SharedLibrary
{
public abstract class Server
{
public enum Game
{
UKN,
IW3,
IW4,
IW5,
T4,
T5,
T5M,
}
public Server(Interfaces.IManager mgr, ServerConfiguration config)
{
Password = config.Password;
IP = config.IPAddress;
Port = config.Port;
Manager = mgr;
Logger = Manager.GetLogger();
ServerConfig = config;
Players = new List<Player>(new Player[18]);
Reports = new List<Report>();
PlayerHistory = new Queue<PlayerHistory>();
ChatHistory = new List<ChatInfo>();
NextMessage = 0;
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<Player> GetPlayersAsList()
{
return Players.FindAll(x => x != null);
}
/// <summary>
/// Add a player to the server's player list
/// </summary>
/// <param name="P">Player pulled from memory reading</param>
/// <returns>True if player added sucessfully, false otherwise</returns>
abstract public Task<bool> AddPlayer(Player P);
/// <summary>
/// Remove player by client number
/// </summary>
/// <param name="cNum">Client ID of player to be removed</param>
/// <returns>true if removal succeded, false otherwise</returns>
abstract public Task RemovePlayer(int cNum);
/// <summary>
/// Get the player from the server's list by line from game long
/// </summary>
/// <param name="L">Game log line containing event</param>
/// <param name="cIDPos">Position in the line where the cliet ID is written</param>
/// <returns>Matching player if found</returns>
abstract public Player ParseClientFromString(String[] L, int cIDPos);
/// <summary>
/// Get a player by name
/// </summary>
/// <param name="pName">Player name to search for</param>
/// <returns>Matching player if found</returns>
public List<Player> 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();
}
/// <summary>
/// Process requested command correlating to an event
/// </summary>
/// <param name="E">Event parameter</param>
/// <param name="C">Command requested from the event</param>
/// <returns></returns>
abstract public Task<Command> ValidateCommand(Event E);
virtual public Task<bool> ProcessUpdatesAsync(CancellationToken cts)
{
return null;
}
/// <summary>
/// Process any server event
/// </summary>
/// <param name="E">Event</param>
/// <returns>True on sucess</returns>
abstract protected Task ProcessEvent(Event E);
abstract public Task ExecuteEvent(Event E);
/// <summary>
/// Send a message to all players
/// </summary>
/// <param name="Message">Message to be sent to all players</param>
public async Task Broadcast(String Message)
{
string sayCommand = (GameName == Game.IW4) ? "sayraw" : "say";
#if !DEBUG
await this.ExecuteCommandAsync($"{sayCommand} {Message}");
#else
Logger.WriteVerbose(Message.StripColors());
#endif
}
/// <summary>
/// Send a message to a particular players
/// </summary>
/// <param name="Message">Message to send</param>
/// <param name="Target">Player to send message to</param>
public async Task Tell(String Message, Player Target)
{
string tellCommand = (GameName == Game.IW4) ? "tellraw" : "tell";
#if !DEBUG
if (Target.ClientNumber > -1 && Message.Length > 0 && Target.Level != Player.Permission.Console)
await this.ExecuteCommandAsync($"{tellCommand} {Target.ClientNumber} {Message}^7");
#else
Logger.WriteVerbose($"{Target.ClientNumber}->{Message.StripColors()}");
#endif
if (Target.Level == Player.Permission.Console)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(Utilities.StripColors(Message));
Console.ForegroundColor = ConsoleColor.Gray;
}
if (CommandResult.Count > 15)
CommandResult.RemoveAt(0);
CommandResult.Add(new CommandResponseInfo()
{
Response = Utilities.StripColors(Message),
ClientId = Target.ClientId
});
}
/// <summary>
/// Send a message to all admins on the server
/// </summary>
/// <param name="message">Message to send out</param>
public async Task ToAdmins(String message)
{
foreach (Player P in Players)
{
if (P == null)
continue;
if (P.Level > Player.Permission.Flagged)
await P.Tell(message);
}
}
/// <summary>
/// Kick a player from the server
/// </summary>
/// <param name="Reason">Reason for kicking</param>
/// <param name="Target">Player to kick</param>
abstract public Task Kick(String Reason, Player Target, Player Origin);
/// <summary>
/// Temporarily ban a player ( default 1 hour ) from the server
/// </summary>
/// <param name="Reason">Reason for banning the player</param>
/// <param name="Target">The player to ban</param>
abstract public Task TempBan(String Reason, TimeSpan length, Player Target, Player Origin);
/// <summary>
/// Perm ban a player from the server
/// </summary>
/// <param name="Reason">The reason for the ban</param>
/// <param name="Target">The person to ban</param>
/// <param name="Origin">The person who banned the target</param>
abstract public Task Ban(String Reason, Player Target, Player Origin);
abstract public Task Warn(String Reason, Player Target, Player Origin);
/// <summary>
/// Unban a player by npID / GUID
/// </summary>
/// <param name="npID">npID of the player</param>
/// <param name="Target">I don't remember what this is for</param>
/// <returns></returns>
abstract public Task Unban(string reason, Player Target, Player Origin);
/// <summary>
/// Change the current searver map
/// </summary>
/// <param name="mapName">Non-localized map name</param>
public async Task LoadMap(string mapName)
{
await this.ExecuteCommandAsync($"map {mapName}");
}
public async Task LoadMap(Map newMap)
{
await this.ExecuteCommandAsync($"map {newMap.Name}");
}
/// <summary>
/// Initalize the macro variables
/// </summary>
abstract public void InitializeTokens();
/// <summary>
/// Read the map configuration
/// </summary>
protected void InitializeMaps()
{
Maps = new List<Map>();
var gameMaps = Manager.GetApplicationSettings().Configuration().Maps.FirstOrDefault(m => m.Game == GameName);
if (gameMaps != null)
Maps.AddRange(gameMaps.Maps);
}
/// <summary>
/// Initialize the messages to be broadcasted
/// todo: this needs to be a serialized file
/// </summary>
protected void InitializeAutoMessages()
{
BroadcastMessages = new List<String>();
if(ServerConfig.AutoMessages != null)
BroadcastMessages.AddRange(ServerConfig.AutoMessages);
BroadcastMessages.AddRange(Manager.GetApplicationSettings().Configuration().AutoMessages);
}
public override string ToString()
{
return $"{IP}_{Port}";
}
protected async Task<bool> ScriptLoaded()
{
try
{
return (await this.GetDvarAsync<string>("sv_customcallbacks")).Value == "1";
}
catch (Exceptions.DvarException)
{
return false;
}
}
// Objects
public Interfaces.IManager Manager { get; protected set; }
public Interfaces.ILogger Logger { get; private set; }
public ServerConfiguration ServerConfig { get; private set; }
public List<Map> Maps { get; protected set; }
public List<Report> Reports { get; set; }
public List<ChatInfo> ChatHistory { get; protected set; }
public Queue<PlayerHistory> 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; protected set; }
public Map CurrentMap { get; protected set; }
public int ClientNum
{
get
{
return Players.Where(p => p != null).Count();
}
}
public int MaxClients { get; protected set; }
public List<Player> 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; }
// Internal
protected string IP;
protected int Port;
protected string FSGame;
protected int NextMessage;
protected int ConnectionErrors;
protected List<string> BroadcastMessages;
protected TimeSpan LastMessage;
protected IFile LogFile;
protected DateTime LastPoll;
//Remote
public IList<CommandResponseInfo> CommandResult = new List<CommandResponseInfo>();
}
}