more work on skill based team balance.

added on player disconnect to custom callbacks
This commit is contained in:
RaidMax 2018-10-25 08:14:39 -05:00
parent d50e6c8030
commit 1779bf821d
19 changed files with 315 additions and 110 deletions

View File

@ -37,7 +37,7 @@ namespace IW4MAdmin.Application.IO
Server = server; Server = server;
} }
public void PollForChanges() public async Task PollForChanges()
{ {
while (!Server.Manager.ShutdownRequested()) while (!Server.Manager.ShutdownRequested())
{ {
@ -45,7 +45,7 @@ namespace IW4MAdmin.Application.IO
{ {
try try
{ {
UpdateLogEvents(); await UpdateLogEvents();
} }
catch (Exception e) catch (Exception e)
@ -59,7 +59,7 @@ namespace IW4MAdmin.Application.IO
} }
} }
private void UpdateLogEvents() private async Task UpdateLogEvents()
{ {
long fileSize = Reader.Length; long fileSize = Reader.Length;
@ -74,7 +74,7 @@ namespace IW4MAdmin.Application.IO
PreviousFileSize = fileSize; PreviousFileSize = fileSize;
var events = Reader.ReadEventsFromLog(Server, fileDiff, 0); var events = await Reader.ReadEventsFromLog(Server, fileDiff, 0);
foreach (var ev in events) foreach (var ev in events)
{ {

View File

@ -4,6 +4,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks;
namespace IW4MAdmin.Application.IO namespace IW4MAdmin.Application.IO
{ {
@ -22,7 +23,7 @@ namespace IW4MAdmin.Application.IO
Parser = parser; Parser = parser;
} }
public ICollection<GameEvent> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition) public async Task<ICollection<GameEvent>> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
{ {
// allocate the bytes for the new log lines // allocate the bytes for the new log lines
List<string> logLines = new List<string>(); List<string> logLines = new List<string>();
@ -30,6 +31,7 @@ namespace IW4MAdmin.Application.IO
// open the file as a stream // open the file as a stream
using (var rd = new StreamReader(new FileStream(LogFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Utilities.EncodingType)) using (var rd = new StreamReader(new FileStream(LogFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Utilities.EncodingType))
{ {
// todo: max async
// take the old start position and go back the number of new characters // take the old start position and go back the number of new characters
rd.BaseStream.Seek(-fileSizeDiff, SeekOrigin.End); rd.BaseStream.Seek(-fileSizeDiff, SeekOrigin.End);
// the difference should be in the range of a int :P // the difference should be in the range of a int :P

View File

@ -5,6 +5,7 @@ using SharedLibraryCore.Interfaces;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using static SharedLibraryCore.Utilities; using static SharedLibraryCore.Utilities;
namespace IW4MAdmin.Application.IO namespace IW4MAdmin.Application.IO
@ -29,18 +30,19 @@ namespace IW4MAdmin.Application.IO
public int UpdateInterval => 1000; public int UpdateInterval => 1000;
public ICollection<GameEvent> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition) public async Task<ICollection<GameEvent>> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
{ {
#if DEBUG == true #if DEBUG == true
server.Logger.WriteDebug($"Begin reading {fileSizeDiff} from http log"); server.Logger.WriteDebug($"Begin reading {fileSizeDiff} from http log");
#endif #endif
var events = new List<GameEvent>(); var events = new List<GameEvent>();
string b64Path = server.LogPath.ToBase64UrlSafeString(); string b64Path = server.LogPath.ToBase64UrlSafeString();
var response = Api.Log(b64Path).Result; var response = await Api.Log(b64Path);
if (!response.Success) if (!response.Success)
{ {
server.Logger.WriteError($"Could not get log server info of {LogFile}/{b64Path} ({server.LogPath})"); server.Logger.WriteError($"Could not get log server info of {LogFile}/{b64Path} ({server.LogPath})");
return events;
} }
// parse each line // parse each line

View File

@ -151,7 +151,9 @@ namespace IW4MAdmin.Application
{ {
Logger.WriteWarning($"Delayed event for {e.Origin} was ignored because the target has left"); Logger.WriteWarning($"Delayed event for {e.Origin} was ignored because the target has left");
// hack: don't do anything with the event because the target is invalid // hack: don't do anything with the event because the target is invalid
e.Origin = null;
e.Type = GameEvent.EventType.Unknown; e.Type = GameEvent.EventType.Unknown;
} }
} }
Logger.WriteDebug($"Adding delayed event of type {e.Type} for {e.Origin} back for processing"); Logger.WriteDebug($"Adding delayed event of type {e.Type} for {e.Origin} back for processing");
@ -265,11 +267,7 @@ namespace IW4MAdmin.Application
ThreadPool.GetAvailableThreads(out int availableThreads, out int m); ThreadPool.GetAvailableThreads(out int availableThreads, out int m);
Logger.WriteDebug($"There are {workerThreads - availableThreads} active threading tasks"); Logger.WriteDebug($"There are {workerThreads - availableThreads} active threading tasks");
#endif #endif
#if DEBUG
await Task.Delay(10000);
#else
await Task.Delay(ConfigHandler.Configuration().RConPollRate); await Task.Delay(ConfigHandler.Configuration().RConPollRate);
#endif
} }
// trigger the event processing loop to end // trigger the event processing loop to end

View File

@ -26,6 +26,7 @@ namespace IW4MAdmin
{ {
private static readonly Index loc = Utilities.CurrentLocalization.LocalizationIndex; private static readonly Index loc = Utilities.CurrentLocalization.LocalizationIndex;
private GameLogEventDetection LogEvent; private GameLogEventDetection LogEvent;
public int Id { get; private set; }
public IW4MServer(IManager mgr, ServerConfiguration cfg) : base(mgr, cfg) public IW4MServer(IManager mgr, ServerConfiguration cfg) : base(mgr, cfg)
{ {
@ -33,30 +34,28 @@ namespace IW4MAdmin
public override int GetHashCode() public override int GetHashCode()
{ {
if (GameName == Game.IW4) if ($"{IP}:{Port.ToString()}" == "66.150.121.184:28965")
{ {
// todo: make this better with collisions return 886229536;
int id = Math.Abs($"{IP}:{Port.ToString()}".Select(a => (int)a).Sum());
// hack: this is a nasty fix for get hashcode being changed
switch (id)
{
case 765:
return 886229536;
case 760:
return 1645744423;
case 761:
return 1645809959;
}
return id;
} }
else if ($"{IP}:{Port.ToString()}" == "66.150.121.184:28960")
{ {
int id = HashCode.Combine(IP, Port); return 1645744423;
return id < 0 ? Math.Abs(id) : id;
} }
if ($"{IP}:{Port.ToString()}" == "66.150.121.184:28970")
{
return 1645809959;
}
if (Id == 0)
{
Id = HashCode.Combine(IP, Port);
Id = Id < 0 ? Math.Abs(Id) : Id;
}
return Id;
} }
public async Task OnPlayerJoined(Player logClient) public async Task OnPlayerJoined(Player logClient)
@ -560,11 +559,13 @@ namespace IW4MAdmin
if (E.Type == GameEvent.EventType.Broadcast) if (E.Type == GameEvent.EventType.Broadcast)
{ {
#if DEBUG == false
// this is a little ugly but I don't want to change the abstract class // this is a little ugly but I don't want to change the abstract class
if (E.Data != null) if (E.Data != null)
{ {
await E.Owner.ExecuteCommandAsync(E.Data); await E.Owner.ExecuteCommandAsync(E.Data);
} }
#endif
} }
while (ChatHistory.Count > Math.Ceiling((double)ClientNum / 2)) while (ChatHistory.Count > Math.Ceiling((double)ClientNum / 2))
@ -661,7 +662,7 @@ namespace IW4MAdmin
waiterList.Add(e); waiterList.Add(e);
} }
// wait for all the disconnect tasks to finish // wait for all the disconnect tasks to finish
await Task.WhenAll(waiterList.Select(e => e.WaitAsync())); await Task.WhenAll(waiterList.Select(e => e.WaitAsync(10 * 1000)));
waiterList.Clear(); waiterList.Clear();
// this are our new connecting clients // this are our new connecting clients
@ -686,7 +687,7 @@ namespace IW4MAdmin
} }
// wait for all the connect tasks to finish // wait for all the connect tasks to finish
await Task.WhenAll(waiterList.Select(e => e.WaitAsync())); await Task.WhenAll(waiterList.Select(e => e.WaitAsync(10 * 1000)));
if (ConnectionErrors > 0) if (ConnectionErrors > 0)
{ {
@ -700,7 +701,7 @@ namespace IW4MAdmin
catch (NetworkException e) catch (NetworkException e)
{ {
ConnectionErrors++; ConnectionErrors++;
if (ConnectionErrors == 1) if (ConnectionErrors == 3)
{ {
Logger.WriteError($"{e.Message} {IP}:{Port}, {loc["SERVER_ERROR_POLLING"]}"); Logger.WriteError($"{e.Message} {IP}:{Port}, {loc["SERVER_ERROR_POLLING"]}");
Logger.WriteDebug($"Internal Exception: {e.Data["internal_exception"]}"); Logger.WriteDebug($"Internal Exception: {e.Data["internal_exception"]}");

View File

@ -0,0 +1,31 @@
var plugin = {
author: 'RaidMax',
version: 1.1,
name: 'Shared GUID Kicker Plugin',
onEventAsync: function (gameEvent, server) {
// make sure we only check for IW4(x)
if (server.GameName !== 2) {
return false;
}
// connect or join event
if (gameEvent.Type === 3) {
// this GUID seems to have been packed in a IW4 torrent and results in an unreasonable amount of people using the same GUID
if (gameEvent.Origin.NetworkId === -805366929435212061) {
gameEvent.Origin.Kick('Your GUID is generic. Delete players/guids.dat and rejoin', _IW4MAdminClient);
}
}
},
onLoadAsync: function (manager) {
},
onUnloadAsync: function () {
},
onTickAsync: function (server) {
}
};

View File

@ -0,0 +1,62 @@
var plugin = {
author: 'RaidMax',
version: 1.0,
name: 'VPN Detection Plugin',
manager: null,
logger: null,
vpnExceptionIds: [],
checkForVpn: function (origin) {
var exempt = false;
// prevent players that are exempt from being kicked
this.vpnExceptionIds.forEach(function (id) {
if (id === origin.ClientId) {
exempt = true;
return false;
}
});
if (exempt) {
return;
}
var usingVPN = false;
try {
var cl = new System.Net.Http.HttpClient();
var re = cl.GetAsync('https://api.xdefcon.com/proxy/check/?ip=' + origin.IPAddressString).Result;
var co = re.Content;
var parsedJSON = JSON.parse(co.ReadAsStringAsync().Result);
co.Dispose();
re.Dispose();
cl.Dispose();
usingVPN = parsedJSON.success && parsedJSON.proxy;
} catch (e) {
this.logger.WriteError(e.message);
}
if (usingVPN) {
this.logger.WriteInfo(origin + ' is using a VPN (' + origin.IPAddressString + ')');
origin.Kick(_localization.LocalizationIndex["SERVER_KICK_VPNS_NOTALLOWED"], _IW4MAdminClient);
}
},
onEventAsync: function (gameEvent, server) {
// connect event
if (gameEvent.Type === 3) {
this.checkForVpn(gameEvent.Origin);
}
},
onLoadAsync: function (manager) {
this.manager = manager;
this.logger = manager.GetLogger(0);
},
onUnloadAsync: function () {
},
onTickAsync: function (server) {
}
};

View File

@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace IW4ScriptCommands.Commands namespace IW4ScriptCommands.Commands
{ {
class Balance class Balance
{ {
private class TeamAssignment private class TeamAssignment
{ {
public IW4MAdmin.Plugins.Stats.IW4Info.Team CurrentTeam { get; set; } public IW4MAdmin.Plugins.Stats.IW4Info.Team CurrentTeam { get; set; }
@ -17,33 +17,39 @@ namespace IW4ScriptCommands.Commands
public IW4MAdmin.Plugins.Stats.Models.EFClientStatistics Stats { get; set; } public IW4MAdmin.Plugins.Stats.Models.EFClientStatistics Stats { get; set; }
} }
public static string GetTeamAssignments(Player client, string teamsString = "") public static string GetTeamAssignments(Player client, bool isDisconnect, Server server, string teamsString = "")
{ {
var scriptClientTeams = teamsString.Split(';', StringSplitOptions.RemoveEmptyEntries) var scriptClientTeams = teamsString.Split(';', StringSplitOptions.RemoveEmptyEntries)
.Select(c => c.Split(',')) .Select(c => c.Split(','))
.Select(c => new TeamAssignment() .Select(c => new TeamAssignment()
{ {
CurrentTeam = (IW4MAdmin.Plugins.Stats.IW4Info.Team)Enum.Parse(typeof(IW4MAdmin.Plugins.Stats.IW4Info.Team), c[1]), CurrentTeam = (IW4MAdmin.Plugins.Stats.IW4Info.Team)Enum.Parse(typeof(IW4MAdmin.Plugins.Stats.IW4Info.Team), c[1]),
Num = client.CurrentServer.GetPlayersAsList().FirstOrDefault(p => p.ClientNumber== Int32.Parse(c[0]))?.ClientNumber ?? -1, Num = server.GetPlayersAsList().FirstOrDefault(p => p.ClientNumber == Int32.Parse(c[0]))?.ClientNumber ?? -1,
Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(client.CurrentServer.Players.FirstOrDefault(p => p.ClientNumber == Int32.Parse(c[0])).ClientId, client.CurrentServer.GetHashCode()) Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(server.Players.FirstOrDefault(p => p.ClientNumber == Int32.Parse(c[0])).ClientId, server.GetHashCode())
}) })
.ToList(); .ToList();
// at least one team is full so we can't balance // at least one team is full so we can't balance
if (scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis) >= Math.Floor(client.CurrentServer.MaxClients / 2.0) if (scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis) >= Math.Floor(server.MaxClients / 2.0)
|| scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies) >= Math.Floor(client.CurrentServer.MaxClients / 2.0)) || scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies) >= Math.Floor(server.MaxClients / 2.0))
{ {
// E.Origin?.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL"]); // E.Origin?.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL"]);
return string.Empty; return string.Empty;
} }
List<string> teamAssignments = new List<string>(); List<string> teamAssignments = new List<string>();
var activeClients = client.CurrentServer.GetPlayersAsList().Select(c => new TeamAssignment() var _c = server.GetPlayersAsList();
if (isDisconnect && client != null)
{
_c = _c.Where(c => c.ClientNumber != client.ClientNumber).ToList();
}
var activeClients = _c.Select(c => new TeamAssignment()
{ {
Num = c.ClientNumber, Num = c.ClientNumber,
Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, client.CurrentServer.GetHashCode()), Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, server.GetHashCode()),
CurrentTeam = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, client.CurrentServer.GetHashCode()).Team CurrentTeam = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, server.GetHashCode()).Team
}) })
.Where(c => scriptClientTeams.FirstOrDefault(sc => sc.Num == c.Num)?.CurrentTeam != IW4MAdmin.Plugins.Stats.IW4Info.Team.Spectator) .Where(c => scriptClientTeams.FirstOrDefault(sc => sc.Num == c.Num)?.CurrentTeam != IW4MAdmin.Plugins.Stats.IW4Info.Team.Spectator)
.Where(c => c.CurrentTeam != scriptClientTeams.FirstOrDefault(p => p.Num == c.Num)?.CurrentTeam) .Where(c => c.CurrentTeam != scriptClientTeams.FirstOrDefault(p => p.Num == c.Num)?.CurrentTeam)

View File

@ -35,14 +35,18 @@ namespace WebfrontCore.Controllers.API
} }
[HttpGet("{networkId}")] [HttpGet("{networkId}")]
public IActionResult GetTeamAssignments(string networkId, string teams = "") public IActionResult GetTeamAssignments(string networkId, int serverId, string teams = "", bool isDisconnect = false)
{ {
return Unauthorized();
var client = Manager.GetActiveClients() var client = Manager.GetActiveClients()
.First(c => c.NetworkId == networkId.ConvertLong()); .FirstOrDefault(c => c.NetworkId == networkId.ConvertLong());
var server = Manager.GetServers().First(c => c.GetHashCode() == serverId);
teams = teams ?? string.Empty; teams = teams ?? string.Empty;
string assignments = Balance.GetTeamAssignments(client, teams); string assignments = Balance.GetTeamAssignments(client, isDisconnect, server, teams);
return Content(assignments); return Content(assignments);
} }

View File

@ -17,15 +17,10 @@ namespace IW4ScriptCommands
public Task OnEventAsync(GameEvent E, Server S) public Task OnEventAsync(GameEvent E, Server S)
{ {
//if (E.Type == GameEvent.EventType.JoinTeam || E.Type == GameEvent.EventType.Disconnect) if (E.Type == GameEvent.EventType.Start)
//{ {
// E.Origin = new SharedLibraryCore.Objects.Player() return S.SetDvarAsync("sv_iw4madmin_serverid", S.GetHashCode());
// { }
// ClientId = 1,
// CurrentServer = E.Owner
// };
// return new Commands.Balance().ExecuteAsync(E);
//}
if (E.Type == GameEvent.EventType.Warn) if (E.Type == GameEvent.EventType.Warn)
{ {

View File

@ -265,7 +265,7 @@ namespace SharedLibraryCore.Commands
public override Task ExecuteAsync(GameEvent E) public override Task ExecuteAsync(GameEvent E)
{ {
String You = String.Format("{0} [^3#{1}^7] {2} [^3@{3}^7] [{4}^7] IP: {5}", E.Origin.Name, E.Origin.ClientNumber, E.Origin.NetworkId, E.Origin.ClientId, Utilities.ConvertLevelToColor(E.Origin.Level, E.Origin.ClientPermission.Name), E.Origin.IPAddressString); String You = String.Format("{0} [^3#{1}^7] {2} ^7[^3@{3}^7] ^7[{4}^7] IP: {5}", E.Origin.Name, E.Origin.ClientNumber, E.Origin.NetworkId, E.Origin.ClientId, Utilities.ConvertLevelToColor(E.Origin.Level, E.Origin.ClientPermission.Name), E.Origin.IPAddressString);
E.Origin.Tell(You); E.Origin.Tell(You);
return Task.CompletedTask; return Task.CompletedTask;

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Threading.Tasks;
namespace SharedLibraryCore.Interfaces namespace SharedLibraryCore.Interfaces
{ {
@ -16,7 +17,7 @@ namespace SharedLibraryCore.Interfaces
/// <param name="fileSizeDiff"></param> /// <param name="fileSizeDiff"></param>
/// <param name="startPosition"></param> /// <param name="startPosition"></param>
/// <returns></returns> /// <returns></returns>
ICollection<GameEvent> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition); Task<ICollection<GameEvent>> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition);
/// <summary> /// <summary>
/// how long the log file is /// how long the log file is
/// </summary> /// </summary>

View File

@ -89,41 +89,43 @@ namespace SharedLibraryCore.RCon
byte[] response = null; byte[] response = null;
retrySend: retrySend:
connectionState.SendEventArgs.UserToken = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp) using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
{ {
DontFragment = true, //DontFragment = true,
Ttl = 42, Ttl = 100,
ExclusiveAddressUse = true, ExclusiveAddressUse = true,
}; })
{
connectionState.OnSentData.Reset(); connectionState.SendEventArgs.UserToken = socket;
connectionState.OnReceivedData.Reset(); connectionState.OnSentData.Reset();
connectionState.ConnectionAttempts++; connectionState.OnReceivedData.Reset();
connectionState.ConnectionAttempts++;
#if DEBUG == true #if DEBUG == true
Log.WriteDebug($"Sending {payload.Length} bytes to [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})"); Log.WriteDebug($"Sending {payload.Length} bytes to [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})");
#endif #endif
try try
{
response = await SendPayloadAsync(payload, waitForResponse);
connectionState.OnComplete.Release(1);
connectionState.ConnectionAttempts = 0;
}
catch/* (Exception ex)*/
{
if (connectionState.ConnectionAttempts < StaticHelpers.AllowedConnectionFails)
{ {
// Log.WriteWarning($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})"); response = await SendPayloadAsync(payload, waitForResponse);
await Task.Delay(StaticHelpers.FloodProtectionInterval); connectionState.OnComplete.Release(1);
goto retrySend; connectionState.ConnectionAttempts = 0;
} }
connectionState.OnComplete.Release(1); catch/* (Exception ex)*/
//Log.WriteDebug(ex.GetExceptionInfo()); {
throw new NetworkException($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}]"); if (connectionState.ConnectionAttempts < StaticHelpers.AllowedConnectionFails)
{
// Log.WriteWarning($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})");
await Task.Delay(StaticHelpers.FloodProtectionInterval);
goto retrySend;
}
connectionState.OnComplete.Release(1);
//Log.WriteDebug(ex.GetExceptionInfo());
throw new NetworkException($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}]");
}
} }
string responseString = Utilities.EncodingType.GetString(response, 0, response.Length).TrimEnd('\0') + '\n'; string responseString = Utilities.EncodingType.GetString(response, 0, response.Length) + '\n';
if (responseString.Contains("Invalid password")) if (responseString.Contains("Invalid password"))
{ {
@ -135,9 +137,12 @@ namespace SharedLibraryCore.RCon
throw new NetworkException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_RCON_NOTSET"]); throw new NetworkException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_RCON_NOTSET"]);
} }
Log.WriteInfo(responseString);
string[] splitResponse = responseString.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries) string[] splitResponse = responseString.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Trim()).ToArray(); .Select(line => line.Trim()).ToArray();
return splitResponse; return splitResponse;
} }
private async Task<byte[]> SendPayloadAsync(byte[] payload, bool waitForResponse) private async Task<byte[]> SendPayloadAsync(byte[] payload, bool waitForResponse)

View File

@ -117,19 +117,14 @@ namespace SharedLibraryCore
/// <param name="message">Message to be sent to all players</param> /// <param name="message">Message to be sent to all players</param>
public GameEvent Broadcast(string message, Player sender = null) public GameEvent Broadcast(string message, Player sender = null)
{ {
#if DEBUG == false
string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Say, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{message}"); string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Say, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{message}");
#else
Logger.WriteVerbose(message.StripColors()); //Logger.WriteVerbose(message.StripColors());
#endif
var e = new GameEvent() var e = new GameEvent()
{ {
Type = GameEvent.EventType.Broadcast, Type = GameEvent.EventType.Broadcast,
#if DEBUG == true
Data = message,
#else
Data = formattedMessage, Data = formattedMessage,
#endif
Owner = this, Owner = this,
Origin = sender, Origin = sender,
}; };

View File

@ -145,7 +145,7 @@ namespace SharedLibraryCore.Services
PunisherId = penalty.PunisherId, PunisherId = penalty.PunisherId,
Offense = penalty.Offense, Offense = penalty.Offense,
Type = penalty.Type.ToString(), Type = penalty.Type.ToString(),
TimeRemaining = now > penalty.Expires ? "" : penalty.Expires.ToString(), TimeRemaining = penalty.Expires.HasValue ? (now > penalty.Expires ? "" : penalty.Expires.ToString()) : DateTime.MaxValue.ToString(),
AutomatedOffense = penalty.AutomatedOffense AutomatedOffense = penalty.AutomatedOffense
}, },
When = penalty.When, When = penalty.When,
@ -160,7 +160,7 @@ namespace SharedLibraryCore.Services
((PenaltyInfo)p.Value).Type = ((Penalty.PenaltyType)Convert.ToInt32(((PenaltyInfo)p.Value).Type)).ToString(); ((PenaltyInfo)p.Value).Type = ((Penalty.PenaltyType)Convert.ToInt32(((PenaltyInfo)p.Value).Type)).ToString();
var pi = ((PenaltyInfo)p.Value); var pi = ((PenaltyInfo)p.Value);
if (pi.TimeRemaining.Length > 0) if (pi.TimeRemaining?.Length > 0)
pi.TimeRemaining = (DateTime.Parse(((PenaltyInfo)p.Value).TimeRemaining) - now).TimeSpanText(); pi.TimeRemaining = (DateTime.Parse(((PenaltyInfo)p.Value).TimeRemaining) - now).TimeSpanText();
}); });

View File

@ -52,7 +52,7 @@ namespace WebfrontCore.Controllers
PunisherId = p.PunisherId, PunisherId = p.PunisherId,
Type = p.Type.ToString(), Type = p.Type.ToString(),
TimePunished = p.When.ToString(), TimePunished = p.When.ToString(),
TimeRemaining = p.Expires.ToString(), TimeRemaining = "",
AutomatedOffense = p.AutomatedOffense AutomatedOffense = p.AutomatedOffense
}).ToList(); }).ToList();

View File

@ -30,7 +30,7 @@ namespace WebfrontCore.ViewComponents
Type = p.Type.ToString(), Type = p.Type.ToString(),
TimePunished = Utilities.GetTimePassed(p.When, false), TimePunished = Utilities.GetTimePassed(p.When, false),
// show time passed if ban // show time passed if ban
TimeRemaining = DateTime.UtcNow > p.Expires ? "" : $"{(p.Expires.Value.Year == DateTime.MaxValue.Year ? Utilities.GetTimePassed(p.When, true) : Utilities.TimeSpanText(p.Expires.Value - DateTime.UtcNow))}", TimeRemaining = DateTime.UtcNow > p.Expires ? "" : $"{((p.Expires ?? DateTime.MaxValue).Year == DateTime.MaxValue.Year ? Utilities.GetTimePassed(p.When, true) : Utilities.TimeSpanText((p.Expires ?? DateTime.MaxValue) - DateTime.UtcNow))}",
Sensitive = p.Type == Penalty.PenaltyType.Flag, Sensitive = p.Type == Penalty.PenaltyType.Flag,
AutomatedOffense = p.AutomatedOffense AutomatedOffense = p.AutomatedOffense
}); });

View File

@ -1,10 +1,65 @@
#include common_scripts\utility;
#include maps\mp\_utility; #include maps\mp\_utility;
#include maps\mp\gametypes\_hud_util; #include maps\mp\gametypes\_hud_util;
#include common_scripts\utility; #include maps\mp\gametypes\_playerlogic;
init() init()
{ {
level thread WaitForCommand(); SetDvarIfUninitialized("sv_team_balance_assignments", "");
SetDvarIfUninitialized("sv_iw4madmin_serverid", 0);
SetDvarIfUninitialized("sv_iw4madmin_apiurl", "http://127.0.0.1:1624/api/gsc/");
level.apiUrl = GetDvar("sv_iw4madmin_apiurl");
//level thread WaitForCommand();
level thread onPlayerConnect();
level thread onPlayerDisconnect();
}
onPlayerConnect()
{
for(;;)
{
level waittill( "connected", player );
player thread onJoinedTeam();
}
}
onPlayerDisconnect()
{
for(;;)
{
level waittill( "disconnected", player );
logPrint("player disconnected\n");
level.players[0] SetTeamBalanceAssignments(true);
}
}
onJoinedTeam()
{
self endon("disconnect");
for(;;)
{
self waittill( "joined_team" );
self SetTeamBalanceAssignments(false);
}
}
SetTeamBalanceAssignments(isDisconnect)
{
assignments = GetDvar("sv_team_balance_assignments");
dc = "";
if (isDisconnect)
{
dc = "&isDisconnect=true";
}
url = level.apiUrl + "GetTeamAssignments/" + self.guid + "/?teams=" + assignments + dc + "&serverId=" + GetDvar("sv_iw4madmin_serverid");
newAssignments = GetHttpString(url);
SetDvar("sv_team_balance_assignments", newAssignments.data);
if (newAssignments.success)
{
BalanceTeams(strtok(newAssignments.data, ","));
}
} }
WaitForCommand() WaitForCommand()
@ -33,41 +88,79 @@ WaitForCommand()
SendAlert(clientId, alertType, sound, message) SendAlert(clientId, alertType, sound, message)
{ {
client = playerForClientId(clientId); client = getPlayerFromClientNum(clientId);
client thread playLeaderDialogOnPlayer(sound, client.team); client thread playLeaderDialogOnPlayer(sound, client.team);
client playLocalSound(sound); client playLocalSound(sound);
client iPrintLnBold("^1" + alertType + ": ^3" + message); client iPrintLnBold("^1" + alertType + ": ^3" + message);
} }
GetHttpString(url)
{
response = spawnStruct();
response.success = false;
response.data = undefined;
logPrint("Making request to " + url + "\n");
request = httpGet(url);
request waittill("done", success, data);
if(success != 0){
logPrint("Request succeeded\n");
response.success = true;
response.data = data;
}
else
{
logPrint("Request failed\n");
}
return response;
}
BalanceTeams(commandArgs) BalanceTeams(commandArgs)
{ {
if (isRoundBased()) if (level.teamBased)
{ {
iPrintLnBold("Balancing Teams.."); printOnPlayers("^5Balancing Teams...");
for (i = 0; i < commandArgs.size; i+= 2) for (i = 0; i < commandArgs.size; i+= 2)
{ {
teamNum = commandArgs[i+1]; teamNum = int(commandArgs[i+1]);
clientNum = commandArgs[i]; clientNum = int(commandArgs[i]);
if (teamNum == "0")
//printOnPlayers("[" + teamNum + "," + clientNum + "]");
if (teamNum == 2)
{
newTeam = "allies"; newTeam = "allies";
}
else else
{
newTeam = "axis"; newTeam = "axis";
player = level.players[clientNum]; }
player = getPlayerFromClientNum(clientNum);
if (!isPlayer(player)) //if (!isPlayer(player))
continue; // continue;
iPrintLnBold(player.name + " " + teamNum);
switch (newTeam) switch (newTeam)
{ {
case "axis": case "axis":
player[[level.axis]](); if (player.team != "axis")
{
//printOnPlayers("moving " + player.name + " to axis");
player[[level.axis]]();
}
break; break;
case "allies": case "allies":
player[[level.allies]](); if (player.team != "allies")
{
//printOnPlayers("moving " + player.name + " to allies");
player[[level.allies]]();
}
break; break;
} }
} }

View File

@ -8,10 +8,14 @@ init()
SetDvarIfUninitialized("sv_framewaittime", 0.05); SetDvarIfUninitialized("sv_framewaittime", 0.05);
SetDvarIfUninitialized("sv_additionalwaittime", 0.05); SetDvarIfUninitialized("sv_additionalwaittime", 0.05);
SetDvarIfUninitialized("sv_maxstoredframes", 3); SetDvarIfUninitialized("sv_maxstoredframes", 3);
level thread onPlayerConnect(); level thread onPlayerConnect();
level waittill("prematch_over"); level waittill("prematch_over");
level.callbackPlayerKilled = ::Callback_PlayerKilled; level.callbackPlayerKilled = ::Callback_PlayerKilled;
level.callbackPlayerDamage = ::Callback_PlayerDamage; level.callbackPlayerDamage = ::Callback_PlayerDamage;
level.callbackPlayerDisconnect = ::Callback_PlayerDisconnect;
level.playerTags = []; level.playerTags = [];
level.playerTags[0] = "j_head"; level.playerTags[0] = "j_head";
level.playerTags[1] = "j_neck"; level.playerTags[1] = "j_neck";
@ -240,4 +244,10 @@ Callback_PlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vD
{ {
Process_Hit("Kill", attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon); Process_Hit("Kill", attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon);
self maps\mp\gametypes\_damage::Callback_PlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration ); self maps\mp\gametypes\_damage::Callback_PlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration );
}
Callback_PlayerDisconnect()
{
level notify("disconnected", self);
self maps\mp\gametypes\_playerlogic::Callback_PlayerDisconnect();
} }