tweaked rcon throttle rate/made async
increased cutoff for server overview messages dont print message if timed out
This commit is contained in:
parent
1bdf4e63fc
commit
2952e307b2
@ -255,8 +255,8 @@ namespace StatsPlugin.Helpers
|
|||||||
await statsSvc.ClientStatSvc.SaveChangesAsync();
|
await statsSvc.ClientStatSvc.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
statsSvc.KillStatsSvc.Insert(kill);
|
//statsSvc.KillStatsSvc.Insert(kill);
|
||||||
await statsSvc.KillStatsSvc.SaveChangesAsync();
|
//await statsSvc.KillStatsSvc.SaveChangesAsync();
|
||||||
|
|
||||||
if (Plugin.Config.Configuration().EnableAntiCheat)
|
if (Plugin.Config.Configuration().EnableAntiCheat)
|
||||||
{
|
{
|
||||||
@ -331,12 +331,9 @@ namespace StatsPlugin.Helpers
|
|||||||
if (streakMessage != string.Empty)
|
if (streakMessage != string.Empty)
|
||||||
await attacker.Tell(streakMessage);
|
await attacker.Tell(streakMessage);
|
||||||
|
|
||||||
// immediately write changes in debug
|
// todo: do we want to save this immediately?
|
||||||
//#if DEBUG
|
|
||||||
var statsSvc = ContextThreads[serverId];
|
var statsSvc = ContextThreads[serverId];
|
||||||
statsSvc.ClientStatSvc.SaveChanges();
|
statsSvc.ClientStatSvc.SaveChanges();
|
||||||
//statsSvc.ServerStatsSvc.SaveChanges();
|
|
||||||
//#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -151,6 +151,7 @@
|
|||||||
<ProjectReference Include="..\..\SharedLibrary\SharedLibrary.csproj">
|
<ProjectReference Include="..\..\SharedLibrary\SharedLibrary.csproj">
|
||||||
<Project>{d51eeceb-438a-47da-870f-7d7b41bc24d6}</Project>
|
<Project>{d51eeceb-438a-47da-870f-7d7b41bc24d6}</Project>
|
||||||
<Name>SharedLibrary</Name>
|
<Name>SharedLibrary</Name>
|
||||||
|
<Private>False</Private>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using SharedLibrary;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using SharedLibrary.Interfaces;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using SharedLibrary.Network;
|
using SharedLibrary;
|
||||||
|
using SharedLibrary.Interfaces;
|
||||||
using SharedLibrary.Objects;
|
using SharedLibrary.Objects;
|
||||||
using SharedLibrary.Helpers;
|
|
||||||
using SharedLibrary.Configuration;
|
using SharedLibrary.Configuration;
|
||||||
|
|
||||||
namespace Welcome_Plugin
|
namespace Welcome_Plugin
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
using System;
|
using SharedLibrary.Database;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using SharedLibrary.Network;
|
|
||||||
using SharedLibrary.Helpers;
|
|
||||||
using SharedLibrary.Objects;
|
|
||||||
using SharedLibrary.Database;
|
|
||||||
using System.Data.Entity;
|
|
||||||
using SharedLibrary.Database.Models;
|
using SharedLibrary.Database.Models;
|
||||||
using SharedLibrary.Services;
|
|
||||||
using SharedLibrary.Exceptions;
|
using SharedLibrary.Exceptions;
|
||||||
|
using SharedLibrary.Objects;
|
||||||
|
using SharedLibrary.Services;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data.Entity;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace SharedLibrary.Commands
|
namespace SharedLibrary.Commands
|
||||||
{
|
{
|
||||||
@ -420,9 +417,6 @@ namespace SharedLibrary.Commands
|
|||||||
|
|
||||||
Player.Permission newPerm = Utilities.MatchPermission(E.Data);
|
Player.Permission newPerm = Utilities.MatchPermission(E.Data);
|
||||||
|
|
||||||
if (newPerm == Player.Permission.Owner && E.Origin.Level != Player.Permission.Console)
|
|
||||||
newPerm = Player.Permission.Banned;
|
|
||||||
|
|
||||||
if (newPerm == Player.Permission.Owner &&
|
if (newPerm == Player.Permission.Owner &&
|
||||||
!E.Owner.Manager.GetApplicationSettings().Configuration().EnableMultipleOwners)
|
!E.Owner.Manager.GetApplicationSettings().Configuration().EnableMultipleOwners)
|
||||||
{
|
{
|
||||||
@ -981,16 +975,16 @@ namespace SharedLibrary.Commands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CRestartServer : Command
|
public class CKillServer : Command
|
||||||
{
|
{
|
||||||
public CRestartServer() : base("restartserver", "restart the server", "restart", Player.Permission.Administrator, false)
|
public CKillServer() : base("killserver", "kill the game server", "kill", Player.Permission.Administrator, false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task ExecuteAsync(Event E)
|
public override async Task ExecuteAsync(Event E)
|
||||||
{
|
{
|
||||||
var gameserverProcesses = System.Diagnostics.Process.GetProcessesByName("iw4x");
|
var gameserverProcesses = System.Diagnostics.Process.GetProcessesByName("iw4x");
|
||||||
var currentProcess = gameserverProcesses.FirstOrDefault(g => g.GetCommandLine().Contains($"+set net_port {E.Owner.GetPort()}"));
|
var currentProcess = gameserverProcesses.FirstOrDefault(g => g.MainWindowTitle.Contains(E.Owner.Hostname));
|
||||||
|
|
||||||
if (currentProcess == null)
|
if (currentProcess == null)
|
||||||
{
|
{
|
||||||
@ -999,7 +993,6 @@ namespace SharedLibrary.Commands
|
|||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var commandLine = currentProcess.GetCommandLine();
|
|
||||||
// attempt to kill it natively
|
// attempt to kill it natively
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -1021,51 +1014,17 @@ namespace SharedLibrary.Commands
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
currentProcess.Kill();
|
currentProcess.Kill();
|
||||||
|
await E.Origin.Tell("Successfully killed server process");
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
await E.Origin.Tell("Could not kill IW4x process");
|
await E.Origin.Tell("Could not kill server process");
|
||||||
E.Owner.Logger.WriteDebug("Unable to kill process");
|
E.Owner.Logger.WriteDebug("Unable to kill process");
|
||||||
E.Owner.Logger.WriteDebug($"Exception: {e.Message}");
|
E.Owner.Logger.WriteDebug($"Exception: {e.Message}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
System.Diagnostics.Process process = new System.Diagnostics.Process();
|
|
||||||
process.StartInfo.UseShellExecute = false;
|
|
||||||
#if !DEBUG
|
|
||||||
process.StartInfo.WorkingDirectory = E.Owner.WorkingDirectory;
|
|
||||||
#else
|
|
||||||
process.StartInfo.WorkingDirectory = @"C:\Users\User\Desktop\MW2";
|
|
||||||
#endif
|
|
||||||
process.StartInfo.FileName = $"{process.StartInfo.WorkingDirectory}\\iw4x.exe";
|
|
||||||
process.StartInfo.Arguments = commandLine.Substring(6);
|
|
||||||
|
|
||||||
/*process.StartInfo.UserName = E.Owner.ServerConfig.RestartUsername;
|
|
||||||
|
|
||||||
var pw = new System.Security.SecureString();
|
|
||||||
foreach (char c in E.Owner.ServerConfig.RestartPassword)
|
|
||||||
pw.AppendChar(c);
|
|
||||||
|
|
||||||
process.StartInfo.Password = pw;
|
|
||||||
*/
|
|
||||||
process.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
await E.Origin.Tell("Could not start the IW4x process");
|
|
||||||
E.Owner.Logger.WriteDebug("Unable to start process");
|
|
||||||
E.Owner.Logger.WriteDebug($"Exception: {e.Message}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ namespace SharedLibrary.Dtos
|
|||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public int ClientId { get; set; }
|
public int ClientId { get; set; }
|
||||||
public string Level { get; set; }
|
public string Level { get; set; }
|
||||||
|
public int LevelInt { get; set; }
|
||||||
public string IPAddress { get; set; }
|
public string IPAddress { get; set; }
|
||||||
public long NetworkId { get; set; }
|
public long NetworkId { get; set; }
|
||||||
public List<string> Aliases { get; set; }
|
public List<string> Aliases { get; set; }
|
||||||
|
@ -1,163 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
|
|
||||||
using SharedLibrary.Objects;
|
|
||||||
|
|
||||||
namespace SharedLibrary.Network
|
|
||||||
{
|
|
||||||
public static class RCON
|
|
||||||
{
|
|
||||||
enum QueryType
|
|
||||||
{
|
|
||||||
GET_STATUS,
|
|
||||||
GET_INFO,
|
|
||||||
DVAR,
|
|
||||||
COMMAND,
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DateTime LastQuery;
|
|
||||||
|
|
||||||
static string[] SendQuery(QueryType Type, Server QueryServer, string Parameters = "")
|
|
||||||
{
|
|
||||||
using (var ServerOOBConnection = new UdpClient())
|
|
||||||
{
|
|
||||||
// prevent flooding
|
|
||||||
if ((DateTime.Now - LastQuery).TotalMilliseconds < 100)
|
|
||||||
Task.Delay(100).Wait();
|
|
||||||
LastQuery = DateTime.Now;
|
|
||||||
|
|
||||||
ServerOOBConnection.Client.SendTimeout = 1000;
|
|
||||||
ServerOOBConnection.Client.ReceiveTimeout = 1000;
|
|
||||||
var Endpoint = new IPEndPoint(IPAddress.Parse(QueryServer.GetIP()), QueryServer.GetPort());
|
|
||||||
|
|
||||||
string QueryString = String.Empty;
|
|
||||||
|
|
||||||
switch (Type)
|
|
||||||
{
|
|
||||||
case QueryType.DVAR:
|
|
||||||
case QueryType.COMMAND:
|
|
||||||
QueryString = $"ÿÿÿÿrcon {QueryServer.Password} {Parameters}";
|
|
||||||
break;
|
|
||||||
case QueryType.GET_STATUS:
|
|
||||||
QueryString = "ÿÿÿÿ getstatus";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] Payload = GetRequestBytes(QueryString);
|
|
||||||
|
|
||||||
int attempts = 0;
|
|
||||||
retry:
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ServerOOBConnection.Connect(Endpoint);
|
|
||||||
ServerOOBConnection.Send(Payload, Payload.Length);
|
|
||||||
|
|
||||||
byte[] ReceiveBuffer = new byte[8192];
|
|
||||||
StringBuilder QueryResponseString = new StringBuilder();
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
ReceiveBuffer = ServerOOBConnection.Receive(ref Endpoint);
|
|
||||||
QueryResponseString.Append(Encoding.UTF7.GetString(ReceiveBuffer).TrimEnd('\0'));
|
|
||||||
} while (ServerOOBConnection.Available > 0 && ServerOOBConnection.Client.Connected);
|
|
||||||
|
|
||||||
if (QueryResponseString.ToString().Contains("Invalid password"))
|
|
||||||
throw new Exceptions.NetworkException("RCON password is invalid");
|
|
||||||
if (QueryResponseString.ToString().Contains("rcon_password"))
|
|
||||||
throw new Exceptions.NetworkException("RCON password has not been set");
|
|
||||||
|
|
||||||
int num = int.Parse("0a", System.Globalization.NumberStyles.AllowHexSpecifier);
|
|
||||||
string[] SplitResponse = QueryResponseString.ToString().Split(new char[] { (char)num }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
return SplitResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (Exceptions.NetworkException e)
|
|
||||||
{
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
attempts++;
|
|
||||||
if (attempts > 2)
|
|
||||||
{
|
|
||||||
var ne = new Exceptions.NetworkException("Could not communicate with the server");
|
|
||||||
ne.Data["internal_exception"] = e.Message;
|
|
||||||
ne.Data["server_address"] = ServerOOBConnection.Client.RemoteEndPoint.ToString();
|
|
||||||
throw ne;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread.Sleep(1000);
|
|
||||||
goto retry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<DVAR<T>> GetDvarAsync<T>(this Server server, string dvarName)
|
|
||||||
{
|
|
||||||
string[] LineSplit = await Task.FromResult(SendQuery(QueryType.DVAR, server, dvarName));
|
|
||||||
|
|
||||||
if (LineSplit.Length != 3)
|
|
||||||
{
|
|
||||||
var e = new Exceptions.DvarException($"DVAR \"{dvarName}\" does not exist");
|
|
||||||
e.Data["dvar_name"] = dvarName;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
string[] ValueSplit = LineSplit[1].Split(new char[] { '"' }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
|
||||||
if (ValueSplit.Length != 5)
|
|
||||||
{
|
|
||||||
var e = new Exceptions.DvarException($"DVAR \"{dvarName}\" does not exist");
|
|
||||||
e.Data["dvar_name"] = dvarName;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
string DvarName = Regex.Replace(ValueSplit[0], @"\^[0-9]", "");
|
|
||||||
string DvarCurrentValue = Regex.Replace(ValueSplit[2], @"\^[0-9]", "");
|
|
||||||
string DvarDefaultValue = Regex.Replace(ValueSplit[4], @"\^[0-9]", "");
|
|
||||||
|
|
||||||
return new DVAR<T>(DvarName) { Value = (T)Convert.ChangeType(DvarCurrentValue, typeof(T)) };
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task SetDvarAsync(this Server server, string dvarName, object dvarValue)
|
|
||||||
{
|
|
||||||
await Task.FromResult(SendQuery(QueryType.DVAR, server, $"set {dvarName} {dvarValue}"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<string[]> ExecuteCommandAsync(this Server server, string commandName)
|
|
||||||
{
|
|
||||||
return await Task.FromResult(SendQuery(QueryType.COMMAND, server, commandName).Skip(1).ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<List<Player>> GetStatusAsync(this Server server)
|
|
||||||
{
|
|
||||||
#if DEBUG && DEBUG_PLAYERS
|
|
||||||
string[] response = await Task.Run(() => System.IO.File.ReadAllLines("players.txt"));
|
|
||||||
#else
|
|
||||||
string[] response = await Task.FromResult(SendQuery(QueryType.DVAR, server, "status"));
|
|
||||||
#endif
|
|
||||||
return Utilities.PlayersFromStatus(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
static byte[] GetRequestBytes(string Request)
|
|
||||||
{
|
|
||||||
Byte[] initialRequestBytes = Encoding.Unicode.GetBytes(Request);
|
|
||||||
Byte[] fixedRequest = new Byte[initialRequestBytes.Length / 2];
|
|
||||||
|
|
||||||
for (int i = 0; i < initialRequestBytes.Length; i++)
|
|
||||||
if (initialRequestBytes[i] != 0)
|
|
||||||
fixedRequest[i / 2] = initialRequestBytes[i];
|
|
||||||
|
|
||||||
return fixedRequest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
226
SharedLibrary/RCon/Connection.cs
Normal file
226
SharedLibrary/RCon/Connection.cs
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
using SharedLibrary.Exceptions;
|
||||||
|
using SharedLibrary.Interfaces;
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SharedLibrary.RCon
|
||||||
|
{
|
||||||
|
class ConnectionState
|
||||||
|
{
|
||||||
|
public Socket Client { get; set; }
|
||||||
|
public const int BufferSize = 8192;
|
||||||
|
public byte[] Buffer = new byte[BufferSize];
|
||||||
|
public StringBuilder ResponseString { get; set; }
|
||||||
|
|
||||||
|
public ConnectionState()
|
||||||
|
{
|
||||||
|
ResponseString = new StringBuilder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Connection
|
||||||
|
{
|
||||||
|
IPEndPoint Endpoint;
|
||||||
|
string RConPassword;
|
||||||
|
Socket ServerConnection;
|
||||||
|
ILogger Log;
|
||||||
|
int FailedConnections;
|
||||||
|
DateTime LastQuery;
|
||||||
|
string Response;
|
||||||
|
|
||||||
|
ManualResetEvent OnConnected;
|
||||||
|
ManualResetEvent OnSent;
|
||||||
|
ManualResetEvent OnReceived;
|
||||||
|
|
||||||
|
public Connection(string ipAddress, int port, string password, ILogger log)
|
||||||
|
{
|
||||||
|
Endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
|
||||||
|
RConPassword = password;
|
||||||
|
Log = log;
|
||||||
|
|
||||||
|
OnConnected = new ManualResetEvent(false);
|
||||||
|
OnSent = new ManualResetEvent(false);
|
||||||
|
OnReceived = new ManualResetEvent(false);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ServerConnection = new Socket(Endpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
|
||||||
|
ServerConnection.BeginConnect(Endpoint, new AsyncCallback(OnConnectedCallback), ServerConnection);
|
||||||
|
if (!OnConnected.WaitOne(StaticHelpers.SocketTimeout))
|
||||||
|
throw new SocketException((int)SocketError.TimedOut);
|
||||||
|
FailedConnections = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (SocketException e)
|
||||||
|
{
|
||||||
|
throw new NetworkException(e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~Connection()
|
||||||
|
{
|
||||||
|
ServerConnection.Shutdown(SocketShutdown.Both);
|
||||||
|
ServerConnection.Close();
|
||||||
|
ServerConnection.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnConnectedCallback(IAsyncResult ar)
|
||||||
|
{
|
||||||
|
var serverSocket = (Socket)ar.AsyncState;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
serverSocket.EndConnect(ar);
|
||||||
|
#if DEBUG
|
||||||
|
Log.WriteDebug($"Successfully initialized socket to {serverSocket.RemoteEndPoint}");
|
||||||
|
#endif
|
||||||
|
OnConnected.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (SocketException e)
|
||||||
|
{
|
||||||
|
throw new NetworkException($"Could not connect to RCon - {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSentCallback(IAsyncResult ar)
|
||||||
|
{
|
||||||
|
Socket serverConnection = (Socket)ar.AsyncState;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int sentByteNum = serverConnection.EndSend(ar);
|
||||||
|
FailedConnections = 0;
|
||||||
|
#if DEBUG
|
||||||
|
Log.WriteDebug($"Sent {sentByteNum} bytes to server");
|
||||||
|
#endif
|
||||||
|
OnSent.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
FailedConnections++;
|
||||||
|
if (FailedConnections < 1)
|
||||||
|
Log.WriteWarning($"Could not send RCon data to server - {e.Message}");
|
||||||
|
//throw new NetworkException($"Could not send RCon message to server - {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnReceivedCallback(IAsyncResult ar)
|
||||||
|
{
|
||||||
|
var connectionState = (ConnectionState)ar.AsyncState;
|
||||||
|
var serverConnection = connectionState.Client;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int bytesRead = serverConnection.EndReceive(ar);
|
||||||
|
FailedConnections = 0;
|
||||||
|
|
||||||
|
if (bytesRead > 0)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Log.WriteDebug($"Received {bytesRead} bytes from server");
|
||||||
|
#endif
|
||||||
|
connectionState.ResponseString.Append(Encoding.UTF7.GetString(connectionState.Buffer, 0, bytesRead).TrimEnd('\0'));
|
||||||
|
|
||||||
|
if (serverConnection.Available > 0)
|
||||||
|
{
|
||||||
|
ServerConnection.BeginReceive(connectionState.Buffer, 0, connectionState.Buffer.Length, 0,
|
||||||
|
new AsyncCallback(OnReceivedCallback), connectionState);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Response = connectionState.ResponseString.ToString();
|
||||||
|
OnReceived.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnReceived.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
FailedConnections++;
|
||||||
|
if (FailedConnections < 1)
|
||||||
|
Log.WriteWarning($"Could not receive data from server - {e.Message}");
|
||||||
|
//throw new NetworkException($"Could not recieve message from server - {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "")
|
||||||
|
{
|
||||||
|
if ((DateTime.Now - LastQuery).TotalMilliseconds < 150)
|
||||||
|
{
|
||||||
|
await Task.Delay(150);
|
||||||
|
LastQuery = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnSent.Reset();
|
||||||
|
OnReceived.Reset();
|
||||||
|
string queryString = "";
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case StaticHelpers.QueryType.DVAR:
|
||||||
|
case StaticHelpers.QueryType.COMMAND:
|
||||||
|
queryString = $"ÿÿÿÿrcon {RConPassword} {parameters}";
|
||||||
|
break;
|
||||||
|
case StaticHelpers.QueryType.GET_STATUS:
|
||||||
|
queryString = "ÿÿÿÿgetstatus";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] payload = Encoding.Default.GetBytes(queryString);
|
||||||
|
retrySend:
|
||||||
|
ServerConnection.BeginSend(payload, 0, payload.Length, 0, new AsyncCallback(OnSentCallback), ServerConnection);
|
||||||
|
bool success = await Task.FromResult(OnSent.WaitOne(StaticHelpers.SocketTimeout));
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
FailedConnections++;
|
||||||
|
if (FailedConnections < 4)
|
||||||
|
goto retrySend;
|
||||||
|
else
|
||||||
|
throw new NetworkException($"Could not send data to server - {new SocketException((int)SocketError.TimedOut).Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var connectionState = new ConnectionState
|
||||||
|
{
|
||||||
|
Client = ServerConnection
|
||||||
|
};
|
||||||
|
|
||||||
|
retryReceive:
|
||||||
|
ServerConnection.BeginReceive(connectionState.Buffer, 0, connectionState.Buffer.Length, 0,
|
||||||
|
new AsyncCallback(OnReceivedCallback), connectionState);
|
||||||
|
success = await Task.FromResult(OnReceived.WaitOne(StaticHelpers.SocketTimeout));
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
FailedConnections++;
|
||||||
|
if (FailedConnections < 4)
|
||||||
|
goto retryReceive;
|
||||||
|
else
|
||||||
|
throw new NetworkException($"Could not send data to server - {new SocketException((int)SocketError.TimedOut).Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
string queryResponse = Response;//connectionState.ResponseString.ToString();
|
||||||
|
|
||||||
|
if (queryResponse.Contains("Invalid password"))
|
||||||
|
throw new NetworkException("RCON password is invalid");
|
||||||
|
if (queryResponse.ToString().Contains("rcon_password"))
|
||||||
|
throw new NetworkException("RCON password has not been set");
|
||||||
|
|
||||||
|
string[] splitResponse = queryResponse.Split(new char[]
|
||||||
|
{
|
||||||
|
StaticHelpers.SeperatorChar
|
||||||
|
}, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
return splitResponse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
SharedLibrary/RCon/StaticHelpers.cs
Normal file
24
SharedLibrary/RCon/StaticHelpers.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using SharedLibrary.Objects;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SharedLibrary.RCon
|
||||||
|
{
|
||||||
|
public static class StaticHelpers
|
||||||
|
{
|
||||||
|
public enum QueryType
|
||||||
|
{
|
||||||
|
GET_STATUS,
|
||||||
|
GET_INFO,
|
||||||
|
DVAR,
|
||||||
|
COMMAND,
|
||||||
|
}
|
||||||
|
|
||||||
|
public static char SeperatorChar = (char)int.Parse("0a", System.Globalization.NumberStyles.AllowHexSpecifier);
|
||||||
|
public static readonly TimeSpan SocketTimeout = new TimeSpan(0, 0, 1);
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ using System.Runtime.InteropServices;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
using SharedLibrary.Network;
|
using SharedLibrary.RCon;
|
||||||
using SharedLibrary.Commands;
|
using SharedLibrary.Commands;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using SharedLibrary.Helpers;
|
using SharedLibrary.Helpers;
|
||||||
@ -36,6 +36,7 @@ namespace SharedLibrary
|
|||||||
Manager = mgr;
|
Manager = mgr;
|
||||||
Logger = Manager.GetLogger();
|
Logger = Manager.GetLogger();
|
||||||
ServerConfig = config;
|
ServerConfig = config;
|
||||||
|
RemoteConnection = new RCon.Connection(IP, Port, Password, Logger);
|
||||||
|
|
||||||
Players = new List<Player>(new Player[18]);
|
Players = new List<Player>(new Player[18]);
|
||||||
Reports = new List<Report>();
|
Reports = new List<Report>();
|
||||||
@ -312,6 +313,7 @@ namespace SharedLibrary
|
|||||||
public bool Throttled { get; protected set; }
|
public bool Throttled { get; protected set; }
|
||||||
public bool CustomCallback { get; protected set; }
|
public bool CustomCallback { get; protected set; }
|
||||||
public string WorkingDirectory { get; protected set; }
|
public string WorkingDirectory { get; protected set; }
|
||||||
|
public RCon.Connection RemoteConnection { get; protected set; }
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
protected string IP;
|
protected string IP;
|
||||||
|
@ -196,7 +196,8 @@
|
|||||||
<Compile Include="Objects\Report.cs" />
|
<Compile Include="Objects\Report.cs" />
|
||||||
<Compile Include="PluginImporter.cs" />
|
<Compile Include="PluginImporter.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="RCON.cs" />
|
<Compile Include="RCon\Connection.cs" />
|
||||||
|
<Compile Include="RCon\StaticHelpers.cs" />
|
||||||
<Compile Include="Server.cs" />
|
<Compile Include="Server.cs" />
|
||||||
<Compile Include="Configuration\ApplicationConfiguration.cs" />
|
<Compile Include="Configuration\ApplicationConfiguration.cs" />
|
||||||
<Compile Include="Services\AliasService.cs" />
|
<Compile Include="Services\AliasService.cs" />
|
||||||
|
@ -11,6 +11,9 @@ using static SharedLibrary.Server;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using static SharedLibrary.RCon.StaticHelpers;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace SharedLibrary
|
namespace SharedLibrary
|
||||||
{
|
{
|
||||||
@ -359,36 +362,6 @@ namespace SharedLibrary
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/*https://stackoverflow.com/questions/2633628/can-i-get-command-line-arguments-of-other-processes-from-net-c*/
|
|
||||||
// Define an extension method for type System.Process that returns the command
|
|
||||||
// line via WMI.
|
|
||||||
public static string GetCommandLine(this Process process)
|
|
||||||
{
|
|
||||||
string cmdLine = null;
|
|
||||||
using (var searcher = new ManagementObjectSearcher(
|
|
||||||
$"SELECT CommandLine FROM Win32_Process WHERE ProcessId = {process.Id}"))
|
|
||||||
{
|
|
||||||
// By definition, the query returns at most 1 match, because the process
|
|
||||||
// is looked up by ID (which is unique by definition).
|
|
||||||
var matchEnum = searcher.Get().GetEnumerator();
|
|
||||||
if (matchEnum.MoveNext()) // Move to the 1st item.
|
|
||||||
{
|
|
||||||
cmdLine = matchEnum.Current["CommandLine"]?.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cmdLine == null)
|
|
||||||
{
|
|
||||||
// Not having found a command line implies 1 of 2 exceptions, which the
|
|
||||||
// WMI query masked:
|
|
||||||
// An "Access denied" exception due to lack of privileges.
|
|
||||||
// A "Cannot process request because the process (<pid>) has exited."
|
|
||||||
// exception due to the process having terminated.
|
|
||||||
// We provoke the same exception again simply by accessing process.MainModule.
|
|
||||||
var dummy = process.MainModule; // Provoke exception.
|
|
||||||
}
|
|
||||||
return cmdLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsPrivileged(this Player p) => p.Level > Player.Permission.User;
|
public static bool IsPrivileged(this Player p) => p.Level > Player.Permission.User;
|
||||||
|
|
||||||
public static bool PromptBool(string question)
|
public static bool PromptBool(string question)
|
||||||
@ -409,5 +382,52 @@ namespace SharedLibrary
|
|||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<DVAR<T>> GetDvarAsync<T>(this Server server, string dvarName)
|
||||||
|
{
|
||||||
|
string[] LineSplit = await server.RemoteConnection.SendQueryAsync(QueryType.DVAR, dvarName);
|
||||||
|
|
||||||
|
if (LineSplit.Length != 3)
|
||||||
|
{
|
||||||
|
var e = new Exceptions.DvarException($"DVAR \"{dvarName}\" does not exist");
|
||||||
|
e.Data["dvar_name"] = dvarName;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] ValueSplit = LineSplit[1].Split(new char[] { '"' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if (ValueSplit.Length != 5)
|
||||||
|
{
|
||||||
|
var e = new Exceptions.DvarException($"DVAR \"{dvarName}\" does not exist");
|
||||||
|
e.Data["dvar_name"] = dvarName;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
string DvarName = Regex.Replace(ValueSplit[0], @"\^[0-9]", "");
|
||||||
|
string DvarCurrentValue = Regex.Replace(ValueSplit[2], @"\^[0-9]", "");
|
||||||
|
string DvarDefaultValue = Regex.Replace(ValueSplit[4], @"\^[0-9]", "");
|
||||||
|
|
||||||
|
return new DVAR<T>(DvarName) { Value = (T)Convert.ChangeType(DvarCurrentValue, typeof(T)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task SetDvarAsync(this Server server, string dvarName, object dvarValue)
|
||||||
|
{
|
||||||
|
await server.RemoteConnection.SendQueryAsync(QueryType.DVAR, $"set {dvarName} {dvarValue}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<string[]> ExecuteCommandAsync(this Server server, string commandName)
|
||||||
|
{
|
||||||
|
return (await server.RemoteConnection.SendQueryAsync(QueryType.COMMAND, commandName)).Skip(1).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<List<Player>> GetStatusAsync(this Server server)
|
||||||
|
{
|
||||||
|
#if DEBUG && DEBUG_PLAYERS
|
||||||
|
string[] response = await Task.Run(() => System.IO.File.ReadAllLines("players.txt"));
|
||||||
|
#else
|
||||||
|
string[] response = await server.RemoteConnection.SendQueryAsync(QueryType.DVAR, "status");
|
||||||
|
#endif
|
||||||
|
return Utilities.PlayersFromStatus(response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ namespace IW4MAdmin
|
|||||||
{
|
{
|
||||||
private List<Server> _servers;
|
private List<Server> _servers;
|
||||||
public List<Server> Servers => _servers.OrderByDescending(s => s.ClientNum).ToList();
|
public List<Server> Servers => _servers.OrderByDescending(s => s.ClientNum).ToList();
|
||||||
public Dictionary<int, int> PrivilegedClients { get; set; }
|
public Dictionary<int, Player> PrivilegedClients { get; set; }
|
||||||
public ILogger Logger { get; private set; }
|
public ILogger Logger { get; private set; }
|
||||||
public bool Running { get; private set; }
|
public bool Running { get; private set; }
|
||||||
public EventHandler<Event> ServerEventOccurred { get; private set; }
|
public EventHandler<Event> ServerEventOccurred { get; private set; }
|
||||||
@ -54,7 +54,7 @@ namespace IW4MAdmin
|
|||||||
ClientSvc = new ClientService();
|
ClientSvc = new ClientService();
|
||||||
AliasSvc = new AliasService();
|
AliasSvc = new AliasService();
|
||||||
PenaltySvc = new PenaltyService();
|
PenaltySvc = new PenaltyService();
|
||||||
PrivilegedClients = new Dictionary<int, int>();
|
PrivilegedClients = new Dictionary<int, Player>();
|
||||||
ServerEventOccurred += EventAPI.OnServerEventOccurred;
|
ServerEventOccurred += EventAPI.OnServerEventOccurred;
|
||||||
ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings");
|
ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings");
|
||||||
}
|
}
|
||||||
@ -78,13 +78,17 @@ namespace IW4MAdmin
|
|||||||
{
|
{
|
||||||
#region DATABASE
|
#region DATABASE
|
||||||
var ipList = (await ClientSvc.Find(c => c.Level > Player.Permission.Trusted))
|
var ipList = (await ClientSvc.Find(c => c.Level > Player.Permission.Trusted))
|
||||||
.Select(c => new { c.IPAddress, c.ClientId });
|
.Select(c => new { c.IPAddress, c.ClientId, c.Level });
|
||||||
|
|
||||||
foreach (var a in ipList)
|
foreach (var a in ipList)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PrivilegedClients.Add(a.IPAddress, a.ClientId);
|
PrivilegedClients.Add(a.IPAddress, new Player()
|
||||||
|
{
|
||||||
|
ClientId = a.ClientId,
|
||||||
|
Level = a.Level
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (ArgumentException)
|
catch (ArgumentException)
|
||||||
@ -208,7 +212,7 @@ namespace IW4MAdmin
|
|||||||
Commands.Add(new CIP());
|
Commands.Add(new CIP());
|
||||||
Commands.Add(new CMask());
|
Commands.Add(new CMask());
|
||||||
Commands.Add(new CPruneAdmins());
|
Commands.Add(new CPruneAdmins());
|
||||||
Commands.Add(new CRestartServer());
|
Commands.Add(new CKillServer());
|
||||||
|
|
||||||
foreach (Command C in SharedLibrary.Plugins.PluginImporter.ActiveCommands)
|
foreach (Command C in SharedLibrary.Plugins.PluginImporter.ActiveCommands)
|
||||||
Commands.Add(C);
|
Commands.Add(C);
|
||||||
@ -238,7 +242,7 @@ namespace IW4MAdmin
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Status.Update(new Task<bool>(() => { return (Status.Dependant as Server).ProcessUpdatesAsync(Status.GetToken()).Result; }));
|
Status.Update(new Task<bool>(() => { return (Status.Dependant as Server).ProcessUpdatesAsync(Status.GetToken()).Result; }));
|
||||||
if (Status.RunAverage > 1000 + UPDATE_FREQUENCY)
|
if (Status.RunAverage > 1000 + UPDATE_FREQUENCY && !(Status.Dependant as Server).Throttled)
|
||||||
Logger.WriteWarning($"Update task average execution is longer than desired for {(Status.Dependant as Server)} [{Status.RunAverage}ms]");
|
Logger.WriteWarning($"Update task average execution is longer than desired for {(Status.Dependant as Server)} [{Status.RunAverage}ms]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,18 +4,18 @@ using System.Threading;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
using SharedLibrary;
|
using SharedLibrary;
|
||||||
using SharedLibrary.Network;
|
|
||||||
using SharedLibrary.Interfaces;
|
using SharedLibrary.Interfaces;
|
||||||
using SharedLibrary.Objects;
|
using SharedLibrary.Objects;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using SharedLibrary.Services;
|
using SharedLibrary.Services;
|
||||||
using SharedLibrary.Database.Models;
|
using SharedLibrary.Database.Models;
|
||||||
using SharedLibrary.Dtos;
|
using SharedLibrary.Dtos;
|
||||||
using WebfrontCore.Application.Misc;
|
|
||||||
using SharedLibrary.Configuration;
|
using SharedLibrary.Configuration;
|
||||||
|
|
||||||
|
using WebfrontCore.Application.Misc;
|
||||||
|
|
||||||
namespace IW4MAdmin
|
namespace IW4MAdmin
|
||||||
{
|
{
|
||||||
public class IW4MServer : Server
|
public class IW4MServer : Server
|
||||||
@ -386,7 +386,19 @@ namespace IW4MAdmin
|
|||||||
async Task<int> PollPlayersAsync()
|
async Task<int> PollPlayersAsync()
|
||||||
{
|
{
|
||||||
var now = DateTime.Now;
|
var now = DateTime.Now;
|
||||||
var CurrentPlayers = await this.GetStatusAsync();
|
|
||||||
|
List<Player> CurrentPlayers = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CurrentPlayers = await this.GetStatusAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// when the server has lost connection
|
||||||
|
catch (SharedLibrary.Exceptions.NetworkException)
|
||||||
|
{
|
||||||
|
Throttled = true;
|
||||||
|
return ClientNum;
|
||||||
|
}
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Logger.WriteInfo($"Polling players took {(DateTime.Now - now).TotalMilliseconds}ms");
|
Logger.WriteInfo($"Polling players took {(DateTime.Now - now).TotalMilliseconds}ms");
|
||||||
#endif
|
#endif
|
||||||
@ -659,7 +671,7 @@ namespace IW4MAdmin
|
|||||||
//#else
|
//#else
|
||||||
}
|
}
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
LogFile = new RemoteFile("https://raidmax.org/IW4MAdmin/getlog.php");
|
//LogFile = new RemoteFile("https://raidmax.org/IW4MAdmin/getlog.php");
|
||||||
#endif
|
#endif
|
||||||
Logger.WriteInfo($"Log file is {logPath}");
|
Logger.WriteInfo($"Log file is {logPath}");
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
@ -776,16 +788,20 @@ namespace IW4MAdmin
|
|||||||
CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map() { Alias = mapname, Name = mapname };
|
CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map() { Alias = mapname, Name = mapname };
|
||||||
|
|
||||||
// todo: make this more efficient
|
// todo: make this more efficient
|
||||||
((ApplicationManager)(Manager)).PrivilegedClients = new Dictionary<int, int>();
|
((ApplicationManager)(Manager)).PrivilegedClients = new Dictionary<int, Player>();
|
||||||
var ClientSvc = new ClientService();
|
var ClientSvc = new ClientService();
|
||||||
var ipList = (await ClientSvc.Find(c => c.Level > Player.Permission.Trusted))
|
var ipList = (await ClientSvc.Find(c => c.Level > Player.Permission.Trusted))
|
||||||
.Select(c => new { c.IPAddress, c.ClientId });
|
.Select(c => new { c.IPAddress, c.ClientId, c.Level });
|
||||||
|
|
||||||
foreach (var a in ipList)
|
foreach (var a in ipList)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
((ApplicationManager)(Manager)).PrivilegedClients.Add(a.IPAddress, a.ClientId);
|
((ApplicationManager)(Manager)).PrivilegedClients.Add(a.IPAddress, new Player()
|
||||||
|
{
|
||||||
|
ClientId = a.ClientId,
|
||||||
|
Level = a.Level
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (ArgumentException)
|
catch (ArgumentException)
|
||||||
|
@ -24,7 +24,9 @@ namespace WebfrontCore.Controllers
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
User.ClientId = Manager.PrivilegedClients[context.HttpContext.Connection.RemoteIpAddress.ToString().ConvertToIP()];
|
var client = Manager.PrivilegedClients[context.HttpContext.Connection.RemoteIpAddress.ToString().ConvertToIP()];
|
||||||
|
User.ClientId = client.ClientId;
|
||||||
|
User.Level = client.Level;
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (KeyNotFoundException)
|
catch (KeyNotFoundException)
|
||||||
@ -36,11 +38,16 @@ namespace WebfrontCore.Controllers
|
|||||||
User.ClientId >= 0;
|
User.ClientId >= 0;
|
||||||
ViewBag.Authorized = Authorized;
|
ViewBag.Authorized = Authorized;
|
||||||
ViewBag.Url = Startup.Configuration["Web:Address"];
|
ViewBag.Url = Startup.Configuration["Web:Address"];
|
||||||
|
ViewBag.User = User;
|
||||||
|
|
||||||
|
if (Manager.GetApplicationSettings().Configuration().EnableDiscordLink)
|
||||||
|
{
|
||||||
string inviteLink = Manager.GetApplicationSettings().Configuration().DiscordInviteCode;
|
string inviteLink = Manager.GetApplicationSettings().Configuration().DiscordInviteCode;
|
||||||
if (inviteLink != null)
|
if (inviteLink != null)
|
||||||
ViewBag.DiscordLink = inviteLink.Contains("https") ? inviteLink : $"https://discordapp.com/invite/{inviteLink}";
|
ViewBag.DiscordLink = inviteLink.Contains("https") ? inviteLink : $"https://discordapp.com/invite/{inviteLink}";
|
||||||
else
|
else
|
||||||
ViewBag.DiscordLink = "";
|
ViewBag.DiscordLink = "";
|
||||||
|
}
|
||||||
base.OnActionExecuting(context);
|
base.OnActionExecuting(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ namespace WebfrontCore.Controllers
|
|||||||
{
|
{
|
||||||
Name = client.Name,
|
Name = client.Name,
|
||||||
Level = client.Level.ToString(),
|
Level = client.Level.ToString(),
|
||||||
|
LevelInt = (int)client.Level,
|
||||||
ClientId = client.ClientId,
|
ClientId = client.ClientId,
|
||||||
IPAddress = client.IPAddressString,
|
IPAddress = client.IPAddressString,
|
||||||
NetworkId = client.NetworkId,
|
NetworkId = client.NetworkId,
|
||||||
@ -54,6 +55,17 @@ namespace WebfrontCore.Controllers
|
|||||||
When = DateTime.MinValue
|
When = DateTime.MinValue
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (Authorized)
|
||||||
|
{
|
||||||
|
clientDto.Meta.AddRange(client.AliasLink.Children.Select(a => new ProfileMeta()
|
||||||
|
{
|
||||||
|
Key = "AliasEvent",
|
||||||
|
Value = $"Connected with name {a.Name}",
|
||||||
|
Sensitive = true,
|
||||||
|
When = a.DateAdded
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
clientDto.Meta.AddRange(Authorized ? meta : meta.Where(m => !m.Sensitive));
|
clientDto.Meta.AddRange(Authorized ? meta : meta.Where(m => !m.Sensitive));
|
||||||
clientDto.Meta.AddRange(Authorized ? penaltyMeta : penaltyMeta.Where(m => !m.Sensitive));
|
clientDto.Meta.AddRange(Authorized ? penaltyMeta : penaltyMeta.Where(m => !m.Sensitive));
|
||||||
clientDto.Meta.AddRange(Authorized ? administeredPenaltiesMeta : administeredPenaltiesMeta.Where(m => !m.Sensitive));
|
clientDto.Meta.AddRange(Authorized ? administeredPenaltiesMeta : administeredPenaltiesMeta.Where(m => !m.Sensitive));
|
||||||
@ -105,6 +117,7 @@ namespace WebfrontCore.Controllers
|
|||||||
{
|
{
|
||||||
Name = c.Name,
|
Name = c.Name,
|
||||||
Level = c.Level.ToString(),
|
Level = c.Level.ToString(),
|
||||||
|
LevelInt = (int)c.Level,
|
||||||
ClientId = c.ClientId,
|
ClientId = c.ClientId,
|
||||||
LastSeen = Utilities.GetTimePassed(c.LastConnection, false)
|
LastSeen = Utilities.GetTimePassed(c.LastConnection, false)
|
||||||
})
|
})
|
||||||
|
@ -31,7 +31,8 @@ namespace WebfrontCore.Controllers
|
|||||||
{
|
{
|
||||||
Name = p.Name,
|
Name = p.Name,
|
||||||
ClientId = p.ClientId,
|
ClientId = p.ClientId,
|
||||||
Level = p.Level.ToString()
|
Level = p.Level.ToString(),
|
||||||
|
LevelInt = (int)p.Level
|
||||||
}).ToList(),
|
}).ToList(),
|
||||||
ChatHistory = s.ChatHistory.OrderBy(c => c.Time).Take((int)Math.Ceiling(s.ClientNum / 2.0)).ToArray(),
|
ChatHistory = s.ChatHistory.OrderBy(c => c.Time).Take((int)Math.Ceiling(s.ClientNum / 2.0)).ToArray(),
|
||||||
PlayerHistory = s.PlayerHistory.ToArray()
|
PlayerHistory = s.PlayerHistory.ToArray()
|
||||||
|
@ -27,7 +27,8 @@ namespace WebfrontCore.ViewComponents
|
|||||||
{
|
{
|
||||||
Name = p.Name,
|
Name = p.Name,
|
||||||
ClientId = p.ClientId,
|
ClientId = p.ClientId,
|
||||||
Level = p.Level.ToString()
|
Level = p.Level.ToString(),
|
||||||
|
LevelInt = (int)p.Level
|
||||||
}).ToList(),
|
}).ToList(),
|
||||||
ChatHistory = s.ChatHistory.ToArray()
|
ChatHistory = s.ChatHistory.ToArray()
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
@{
|
@{
|
||||||
string match = System.Text.RegularExpressions.Regex.Match(Model.Name.ToUpper(), "[A-Z]").Value;
|
string match = System.Text.RegularExpressions.Regex.Match(Model.Name.ToUpper(), "[A-Z]").Value;
|
||||||
string shortCode = match == string.Empty ? "?" : match;
|
string shortCode = match == string.Empty ? "?" : match;
|
||||||
string marginClass = Model.Aliases.Count > 0 ? "mr-4" : "";
|
|
||||||
}
|
}
|
||||||
<div id="profile_wrapper" class="row d-flex d-sm-inline-flex justify-content-center justify-content-left pb-3">
|
<div id="profile_wrapper" class="row d-flex d-sm-inline-flex justify-content-center justify-content-left pb-3">
|
||||||
<div class="mr-auto ml-auto ml-sm-0 mr-sm-0">
|
<div class="mr-auto ml-auto ml-sm-0 mr-sm-0">
|
||||||
@ -12,33 +11,29 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="profile_info" class="text-center text-sm-left pr-3 pl-3">
|
<div id="profile_info" class="text-center text-sm-left pr-3 pl-3">
|
||||||
<div id="profile_name">
|
<div id="profile_name">
|
||||||
<h1>
|
<div class="client-name h1 d-flex d-inline-flex">
|
||||||
<span class="client-name @marginClass">
|
|
||||||
@Model.Name
|
@Model.Name
|
||||||
@if (Model.Aliases.Count > 0 || ViewBag.Authorized)
|
</div>
|
||||||
{
|
|
||||||
<span id="profile_aliases_btn" class="oi oi-caret-bottom pl-2"></span>
|
|
||||||
}
|
|
||||||
|
|
||||||
@{
|
@{
|
||||||
if (ViewBag.Authorized)
|
if (ViewBag.Authorized)
|
||||||
{
|
{
|
||||||
if (Model.Level == SharedLibrary.Objects.Player.Permission.User.ToString())
|
<div class="d-flex d-md-inline-flex justify-content-center order-1">
|
||||||
|
<div id="profile_aliases_btn" class="oi oi-caret-bottom h3 ml-0 ml-md-2"></div>
|
||||||
|
|
||||||
|
@if (Model.LevelInt < (int)ViewBag.User.Level &&
|
||||||
|
(SharedLibrary.Objects.Player.Permission)Model.LevelInt != SharedLibrary.Objects.Player.Permission.Banned)
|
||||||
{
|
{
|
||||||
|
<div id="profile_action_ban_btn" class="profile-action oi oi-ban text-danger h3 ml-2" title="Ban Client" data-action="ban" aria-hidden="true"></div>
|
||||||
<span id="profile_action_ban_btn" class="profile-action oi oi-ban text-danger" title="Ban Client" data-action="ban" aria-hidden="true"></span>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Model.Level == SharedLibrary.Objects.Player.Permission.Banned.ToString())
|
@if (Model.LevelInt < (int)ViewBag.User.Level &&
|
||||||
|
(SharedLibrary.Objects.Player.Permission)Model.LevelInt == SharedLibrary.Objects.Player.Permission.Banned)
|
||||||
{
|
{
|
||||||
<span id="profile_action_unban_btn" class="profile-action oi oi-action-undo text-success" title="carriage return" data-action="unban" aria-hidden="true"></span>
|
<div id="profile_action_unban_btn" class="profile-action oi oi-action-undo text-success h3 ml-2" title="Unban Client" data-action="unban" aria-hidden="true"></div>
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
</span>
|
<div id="profile_aliases" class="pr-0 pr-sm-4 pb-2 mb-2 text-muted order-0">
|
||||||
</h1>
|
|
||||||
<div id="profile_aliases" class="pr-0 pr-sm-4 pb-2 mb-2 text-muted">
|
|
||||||
@{
|
@{
|
||||||
foreach (string alias in Model.Aliases)
|
foreach (string alias in Model.Aliases)
|
||||||
{
|
{
|
||||||
@ -51,10 +46,11 @@
|
|||||||
{
|
{
|
||||||
<a class="ip-locate-link" href="#" data-ip="@ip">@ip</a><br />
|
<a class="ip-locate-link" href="#" data-ip="@ip">@ip</a><br />
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<div id="profile_level" class="text-muted mb-2">
|
<div id="profile_level" class="text-muted mb-2">
|
||||||
<h5><span class="level-color-@Model.Level.ToLower()"><strong>@Model.Level</strong></span></h5>
|
<h5><span class="level-color-@Model.Level.ToLower()"><strong>@Model.Level</strong></span></h5>
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
}
|
}
|
||||||
if (Model.ChatHistory[i].Message != "CONNECTED" && Model.ChatHistory[i].Message != "DISCONNECTED")
|
if (Model.ChatHistory[i].Message != "CONNECTED" && Model.ChatHistory[i].Message != "DISCONNECTED")
|
||||||
{
|
{
|
||||||
<span class="text-light">@Model.ChatHistory[i].Name</span><span> — @message.Substring(0, Math.Min(50, message.Length)) </span><br />
|
<span class="text-light">@Model.ChatHistory[i].Name</span><span> — @message.Substring(0, Math.Min(65, message.Length)) </span><br />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,9 +96,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#profile_aliases_btn {
|
#profile_aliases_btn {
|
||||||
position: relative;
|
|
||||||
top: -2px;
|
|
||||||
font-size: 0.5em;
|
|
||||||
color: rgb(0, 122, 204);
|
color: rgb(0, 122, 204);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ $(document).ready(function () {
|
|||||||
const aliases = $('#profile_aliases').text().trim();
|
const aliases = $('#profile_aliases').text().trim();
|
||||||
if (aliases && aliases.length !== 0) {
|
if (aliases && aliases.length !== 0) {
|
||||||
$('#profile_aliases').slideToggle(150);
|
$('#profile_aliases').slideToggle(150);
|
||||||
|
$(this).toggleClass('oi-caret-top');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -177,6 +178,9 @@ function loadMeta(meta) {
|
|||||||
eventString = `<div><span class="penalties-color-${meta.value.type.toLowerCase()}">${penaltyToName(meta.value.type)} </span> <span class="text-highlight"><a class="link-inverse" href="${meta.value.offenderId}"> ${meta.value.offenderName}</a></span > for <span style="color: white; ">${meta.value.offense}</span></div>`;
|
eventString = `<div><span class="penalties-color-${meta.value.type.toLowerCase()}">${penaltyToName(meta.value.type)} </span> <span class="text-highlight"><a class="link-inverse" href="${meta.value.offenderId}"> ${meta.value.offenderName}</a></span > for <span style="color: white; ">${meta.value.offense}</span></div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (meta.key.includes("Alias")) {
|
||||||
|
eventString = `<div><span class="text-primary">${meta.value}</span></div>`;
|
||||||
|
}
|
||||||
// it's a message
|
// it's a message
|
||||||
else if (meta.key.includes("Event")) {
|
else if (meta.key.includes("Event")) {
|
||||||
eventString = `<div><span style="color:white;">></span><span class="text-muted"> ${meta.value}</span></div>`;
|
eventString = `<div><span style="color:white;">></span><span class="text-muted"> ${meta.value}</span></div>`;
|
||||||
|
Loading…
Reference in New Issue
Block a user