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

View File

@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace IW4MAdmin.Application.IO
{
@ -22,7 +23,7 @@ namespace IW4MAdmin.Application.IO
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
List<string> logLines = new List<string>();
@ -30,6 +31,7 @@ namespace IW4MAdmin.Application.IO
// open the file as a stream
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
rd.BaseStream.Seek(-fileSizeDiff, SeekOrigin.End);
// the difference should be in the range of a int :P

View File

@ -5,6 +5,7 @@ using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using static SharedLibraryCore.Utilities;
namespace IW4MAdmin.Application.IO
@ -29,18 +30,19 @@ namespace IW4MAdmin.Application.IO
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
server.Logger.WriteDebug($"Begin reading {fileSizeDiff} from http log");
#endif
var events = new List<GameEvent>();
string b64Path = server.LogPath.ToBase64UrlSafeString();
var response = Api.Log(b64Path).Result;
var response = await Api.Log(b64Path);
if (!response.Success)
{
server.Logger.WriteError($"Could not get log server info of {LogFile}/{b64Path} ({server.LogPath})");
return events;
}
// 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");
// hack: don't do anything with the event because the target is invalid
e.Origin = null;
e.Type = GameEvent.EventType.Unknown;
}
}
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);
Logger.WriteDebug($"There are {workerThreads - availableThreads} active threading tasks");
#endif
#if DEBUG
await Task.Delay(10000);
#else
await Task.Delay(ConfigHandler.Configuration().RConPollRate);
#endif
}
// trigger the event processing loop to end

View File

@ -26,6 +26,7 @@ namespace IW4MAdmin
{
private static readonly Index loc = Utilities.CurrentLocalization.LocalizationIndex;
private GameLogEventDetection LogEvent;
public int Id { get; private set; }
public IW4MServer(IManager mgr, ServerConfiguration cfg) : base(mgr, cfg)
{
@ -33,30 +34,28 @@ namespace IW4MAdmin
public override int GetHashCode()
{
if (GameName == Game.IW4)
if ($"{IP}:{Port.ToString()}" == "66.150.121.184:28965")
{
// todo: make this better with collisions
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;
return 886229536;
}
else
if ($"{IP}:{Port.ToString()}" == "66.150.121.184:28960")
{
int id = HashCode.Combine(IP, Port);
return id < 0 ? Math.Abs(id) : id;
return 1645744423;
}
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)
@ -560,11 +559,13 @@ namespace IW4MAdmin
if (E.Type == GameEvent.EventType.Broadcast)
{
#if DEBUG == false
// this is a little ugly but I don't want to change the abstract class
if (E.Data != null)
{
await E.Owner.ExecuteCommandAsync(E.Data);
}
#endif
}
while (ChatHistory.Count > Math.Ceiling((double)ClientNum / 2))
@ -661,7 +662,7 @@ namespace IW4MAdmin
waiterList.Add(e);
}
// 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();
// this are our new connecting clients
@ -686,7 +687,7 @@ namespace IW4MAdmin
}
// 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)
{
@ -700,7 +701,7 @@ namespace IW4MAdmin
catch (NetworkException e)
{
ConnectionErrors++;
if (ConnectionErrors == 1)
if (ConnectionErrors == 3)
{
Logger.WriteError($"{e.Message} {IP}:{Port}, {loc["SERVER_ERROR_POLLING"]}");
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

@ -17,33 +17,39 @@ namespace IW4ScriptCommands.Commands
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)
.Select(c => c.Split(','))
.Select(c => new TeamAssignment()
{
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,
Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(client.CurrentServer.Players.FirstOrDefault(p => p.ClientNumber == Int32.Parse(c[0])).ClientId, client.CurrentServer.GetHashCode())
Num = server.GetPlayersAsList().FirstOrDefault(p => p.ClientNumber == Int32.Parse(c[0]))?.ClientNumber ?? -1,
Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(server.Players.FirstOrDefault(p => p.ClientNumber == Int32.Parse(c[0])).ClientId, server.GetHashCode())
})
.ToList();
// 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)
|| scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies) >= 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(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;
}
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,
Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, client.CurrentServer.GetHashCode()),
CurrentTeam = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, client.CurrentServer.GetHashCode()).Team
Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, server.GetHashCode()),
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 => c.CurrentTeam != scriptClientTeams.FirstOrDefault(p => p.Num == c.Num)?.CurrentTeam)

View File

@ -35,14 +35,18 @@ namespace WebfrontCore.Controllers.API
}
[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()
.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;
string assignments = Balance.GetTeamAssignments(client, teams);
string assignments = Balance.GetTeamAssignments(client, isDisconnect, server, teams);
return Content(assignments);
}

View File

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

View File

@ -265,7 +265,7 @@ namespace SharedLibraryCore.Commands
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);
return Task.CompletedTask;

View File

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

View File

@ -89,41 +89,43 @@ namespace SharedLibraryCore.RCon
byte[] response = null;
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,
Ttl = 42,
//DontFragment = true,
Ttl = 100,
ExclusiveAddressUse = true,
};
connectionState.OnSentData.Reset();
connectionState.OnReceivedData.Reset();
connectionState.ConnectionAttempts++;
})
{
connectionState.SendEventArgs.UserToken = socket;
connectionState.OnSentData.Reset();
connectionState.OnReceivedData.Reset();
connectionState.ConnectionAttempts++;
#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
try
{
response = await SendPayloadAsync(payload, waitForResponse);
connectionState.OnComplete.Release(1);
connectionState.ConnectionAttempts = 0;
}
catch/* (Exception ex)*/
{
if (connectionState.ConnectionAttempts < StaticHelpers.AllowedConnectionFails)
try
{
// Log.WriteWarning($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})");
await Task.Delay(StaticHelpers.FloodProtectionInterval);
goto retrySend;
response = await SendPayloadAsync(payload, waitForResponse);
connectionState.OnComplete.Release(1);
connectionState.ConnectionAttempts = 0;
}
connectionState.OnComplete.Release(1);
//Log.WriteDebug(ex.GetExceptionInfo());
throw new NetworkException($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}]");
catch/* (Exception ex)*/
{
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"))
{
@ -135,9 +137,12 @@ namespace SharedLibraryCore.RCon
throw new NetworkException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_RCON_NOTSET"]);
}
Log.WriteInfo(responseString);
string[] splitResponse = responseString.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Trim()).ToArray();
return splitResponse;
}
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>
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
//Logger.WriteVerbose(message.StripColors());
var e = new GameEvent()
{
Type = GameEvent.EventType.Broadcast,
#if DEBUG == true
Data = message,
#else
Data = formattedMessage,
#endif
Owner = this,
Origin = sender,
};

View File

@ -145,7 +145,7 @@ namespace SharedLibraryCore.Services
PunisherId = penalty.PunisherId,
Offense = penalty.Offense,
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
},
When = penalty.When,
@ -160,7 +160,7 @@ namespace SharedLibraryCore.Services
((PenaltyInfo)p.Value).Type = ((Penalty.PenaltyType)Convert.ToInt32(((PenaltyInfo)p.Value).Type)).ToString();
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();
});

View File

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

View File

@ -30,7 +30,7 @@ namespace WebfrontCore.ViewComponents
Type = p.Type.ToString(),
TimePunished = Utilities.GetTimePassed(p.When, false),
// 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,
AutomatedOffense = p.AutomatedOffense
});

View File

@ -1,10 +1,65 @@
#include common_scripts\utility;
#include maps\mp\_utility;
#include maps\mp\gametypes\_hud_util;
#include common_scripts\utility;
#include maps\mp\gametypes\_playerlogic;
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()
@ -33,41 +88,79 @@ WaitForCommand()
SendAlert(clientId, alertType, sound, message)
{
client = playerForClientId(clientId);
client = getPlayerFromClientNum(clientId);
client thread playLeaderDialogOnPlayer(sound, client.team);
client playLocalSound(sound);
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)
{
if (isRoundBased())
if (level.teamBased)
{
iPrintLnBold("Balancing Teams..");
printOnPlayers("^5Balancing Teams...");
for (i = 0; i < commandArgs.size; i+= 2)
{
teamNum = commandArgs[i+1];
clientNum = commandArgs[i];
if (teamNum == "0")
teamNum = int(commandArgs[i+1]);
clientNum = int(commandArgs[i]);
//printOnPlayers("[" + teamNum + "," + clientNum + "]");
if (teamNum == 2)
{
newTeam = "allies";
}
else
{
newTeam = "axis";
player = level.players[clientNum];
}
if (!isPlayer(player))
continue;
player = getPlayerFromClientNum(clientNum);
iPrintLnBold(player.name + " " + teamNum);
//if (!isPlayer(player))
// continue;
switch (newTeam)
{
case "axis":
player[[level.axis]]();
if (player.team != "axis")
{
//printOnPlayers("moving " + player.name + " to axis");
player[[level.axis]]();
}
break;
case "allies":
player[[level.allies]]();
if (player.team != "allies")
{
//printOnPlayers("moving " + player.name + " to allies");
player[[level.allies]]();
}
break;
}
}

View File

@ -8,10 +8,14 @@ init()
SetDvarIfUninitialized("sv_framewaittime", 0.05);
SetDvarIfUninitialized("sv_additionalwaittime", 0.05);
SetDvarIfUninitialized("sv_maxstoredframes", 3);
level thread onPlayerConnect();
level waittill("prematch_over");
level.callbackPlayerKilled = ::Callback_PlayerKilled;
level.callbackPlayerDamage = ::Callback_PlayerDamage;
level.callbackPlayerDisconnect = ::Callback_PlayerDisconnect;
level.playerTags = [];
level.playerTags[0] = "j_head";
level.playerTags[1] = "j_neck";
@ -241,3 +245,9 @@ Callback_PlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vD
Process_Hit("Kill", attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon);
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();
}