fix bug with player not getting updated on disconnect (related to issue #24)

jint version downgraded for better stability (also locked the engine instance as it's not thread safe)
updated readme
remove vpn detection from application configuration as it's now in a seperate plugin
defaulted webfront bind URl to all interfaces
readd the custom say name
added visibility percentage to AC
This commit is contained in:
RaidMax 2018-09-04 12:40:29 -05:00
parent 672d45df7c
commit cfbacabb4a
22 changed files with 1126 additions and 313 deletions

View File

@ -21,4 +21,11 @@ xcopy /Y "%SolutionDir%BUILD\Plugins" "%SolutionDir%Publish\WindowsPrerelease\Pl
echo Copying script plugins for publish echo Copying script plugins for publish
xcopy /Y "%SolutionDir%Plugins\ScriptPlugins" "%SolutionDir%Publish\Windows\Plugins\" xcopy /Y "%SolutionDir%Plugins\ScriptPlugins" "%SolutionDir%Publish\Windows\Plugins\"
xcopy /Y "%SolutionDir%Plugins\ScriptPlugins" "%SolutionDir%Publish\WindowsPrerelease\Plugins\" xcopy /Y "%SolutionDir%Plugins\ScriptPlugins" "%SolutionDir%Publish\WindowsPrerelease\Plugins\"
echo Copying GSC files for publish
xcopy /Y "%SolutionDir%_customcallbacks.gsc" "%SolutionDir%Publish\Windows\userraw\scripts\"
xcopy /Y "%SolutionDir%_customcallbacks.gsc" "%SolutionDir%Publish\WindowsPrerelease\userraw\scripts\"
xcopy /Y "%SolutionDir%_commands.gsc" "%SolutionDir%Publish\Windows\userraw\scripts\"
xcopy /Y "%SolutionDir%_commands.gsc" "%SolutionDir%Publish\WindowsPrerelease\userraw\scripts\"

View File

@ -1,12 +1,9 @@
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Events; using SharedLibraryCore.Events;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
namespace IW4MAdmin.Application namespace IW4MAdmin.Application
{ {
@ -22,59 +19,57 @@ namespace IW4MAdmin.Application
OutOfOrderEvents = new SortedList<long, GameEvent>(); OutOfOrderEvents = new SortedList<long, GameEvent>();
} }
public bool AddEvent(GameEvent gameEvent) public void AddEvent(GameEvent gameEvent)
{ {
#if DEBUG #if DEBUG
Manager.GetLogger().WriteDebug($"Got new event of type {gameEvent.Type} for {gameEvent.Owner} with id {gameEvent.Id}"); Manager.GetLogger().WriteDebug($"Got new event of type {gameEvent.Type} for {gameEvent.Owner} with id {gameEvent.Id}");
#endif #endif
// GameEvent sortedEvent = null; GameEvent sortedEvent = null;
// lock (OutOfOrderEvents) lock (OutOfOrderEvents)
// { {
// sortedEvent = OutOfOrderEvents.Values.FirstOrDefault(); sortedEvent = OutOfOrderEvents.Values.FirstOrDefault();
// while (sortedEvent?.Id == Interlocked.Read(ref NextEventId)) while (sortedEvent?.Id == Interlocked.Read(ref NextEventId))
// { {
// if (OutOfOrderEvents.Count > 0) if (OutOfOrderEvents.Count > 0)
// { {
// OutOfOrderEvents.RemoveAt(0); OutOfOrderEvents.RemoveAt(0);
// } }
// AddEvent(sortedEvent); AddEvent(sortedEvent);
// sortedEvent = OutOfOrderEvents.Values.FirstOrDefault(); sortedEvent = OutOfOrderEvents.Values.FirstOrDefault();
// } }
// } }
// // both the gameEvent Id and the LastEventId are thread safe because we want to synchronize when the // both the gameEvent Id and the LastEventId are thread safe because we want to synchronize when the
// // event occurs // event occurs
// if (gameEvent.Id == Interlocked.Read(ref NextEventId)) if (gameEvent.Id == Interlocked.Read(ref NextEventId))
// { {
//#if DEBUG == true #if DEBUG == true
// Manager.GetLogger().WriteDebug($"sent event with id {gameEvent.Id} to be processed"); Manager.GetLogger().WriteDebug($"sent event with id {gameEvent.Id} to be processed");
//#endif #endif
((Manager as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent)); ((Manager as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent));
return true; Interlocked.Increment(ref NextEventId);
// Interlocked.Increment(ref NextEventId); }
// }
// // a "newer" event has been added before and "older" one has been added (due to threads and context switching) // a "newer" event has been added before and "older" one has been added (due to threads and context switching)
// // so me must wait until the next expected one arrives // so me must wait until the next expected one arrives
// else else
// { {
//#if DEBUG == true #if DEBUG == true
// Manager.GetLogger().WriteWarning("Incoming event is out of order"); Manager.GetLogger().WriteWarning("Incoming event is out of order");
// Manager.GetLogger().WriteDebug($"Expected event Id is {Interlocked.Read(ref NextEventId)}, but got {gameEvent.Id} instead"); Manager.GetLogger().WriteDebug($"Expected event Id is {Interlocked.Read(ref NextEventId)}, but got {gameEvent.Id} instead");
//#endif #endif
// // this prevents multiple threads from adding simultaneously // this prevents multiple threads from adding simultaneously
// lock (OutOfOrderEvents) lock (OutOfOrderEvents)
// { {
// if (!OutOfOrderEvents.TryGetValue(gameEvent.Id, out GameEvent discard)) if (!OutOfOrderEvents.TryGetValue(gameEvent.Id, out GameEvent discard))
// { {
// OutOfOrderEvents.Add(gameEvent.Id, gameEvent); OutOfOrderEvents.Add(gameEvent.Id, gameEvent);
// } }
// } }
// } }
// return true;
} }
} }
} }

View File

@ -258,7 +258,6 @@ namespace IW4MAdmin
{ {
Player Leaving = Players[cNum]; Player Leaving = Players[cNum];
Logger.WriteInfo($"Client {Leaving}, state {Leaving.State.ToString()} disconnecting..."); Logger.WriteInfo($"Client {Leaving}, state {Leaving.State.ToString()} disconnecting...");
Leaving.State = Player.ClientState.Disconnecting;
// occurs when the player disconnects via log before being authenticated by RCon // occurs when the player disconnects via log before being authenticated by RCon
if (Leaving.State != Player.ClientState.Connected) if (Leaving.State != Player.ClientState.Connected)
@ -268,6 +267,7 @@ namespace IW4MAdmin
else else
{ {
Leaving.State = Player.ClientState.Disconnecting;
Leaving.TotalConnectionTime += (int)(DateTime.UtcNow - Leaving.ConnectionTime).TotalSeconds; Leaving.TotalConnectionTime += (int)(DateTime.UtcNow - Leaving.ConnectionTime).TotalSeconds;
Leaving.LastConnection = DateTime.UtcNow; Leaving.LastConnection = DateTime.UtcNow;
await Manager.GetClientService().Update(Leaving); await Manager.GetClientService().Update(Leaving);
@ -822,7 +822,7 @@ namespace IW4MAdmin
Logger.WriteInfo($"Log file is {logPath}"); Logger.WriteInfo($"Log file is {logPath}");
Task.Run(() => LogEvent.PollForChanges()); _ = Task.Run(() => LogEvent.PollForChanges());
#if !DEBUG #if !DEBUG
await Broadcast(loc["BROADCAST_ONLINE"]); await Broadcast(loc["BROADCAST_ONLINE"]);
#endif #endif

View File

@ -1,197 +1,197 @@
//using SharedLibraryCore; using SharedLibraryCore;
//using SharedLibraryCore.Objects; using SharedLibraryCore.Objects;
//using System; using System;
//using System.Collections.Generic; using System.Collections.Generic;
//using System.Linq; using System.Linq;
//using System.Text; using System.Text;
//using System.Threading.Tasks; using System.Threading.Tasks;
//namespace IW4ScriptCommands.Commands namespace IW4ScriptCommands.Commands
//{ {
// class Balance : Command class Balance : Command
// { {
// private class TeamAssignment private class TeamAssignment
// { {
// public IW4MAdmin.Plugins.Stats.IW4Info.Team CurrentTeam { get; set; } public IW4MAdmin.Plugins.Stats.IW4Info.Team CurrentTeam { get; set; }
// public int Num { get; set; } public int Num { get; set; }
// public IW4MAdmin.Plugins.Stats.Models.EFClientStatistics Stats { get; set; } public IW4MAdmin.Plugins.Stats.Models.EFClientStatistics Stats { get; set; }
// } }
// public Balance() : base("balance", "balance teams", "bal", Player.Permission.Trusted, false, null) public Balance() : base("balance", "balance teams", "bal", Player.Permission.Trusted, false, null)
// { {
// } }
// public override async Task ExecuteAsync(GameEvent E) public override async Task ExecuteAsync(GameEvent E)
// { {
// string teamsString = (await E.Owner.GetDvarAsync<string>("sv_iw4madmin_teams")).Value; string teamsString = (await E.Owner.GetDvarAsync<string>("sv_iw4madmin_teams")).Value;
// 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 = E.Owner.Players.FirstOrDefault(p => p?.NetworkId == c[0].ConvertLong())?.ClientNumber ?? -1, Num = E.Owner.Players.FirstOrDefault(p => p?.NetworkId == c[0].ConvertLong())?.ClientNumber ?? -1,
// Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(E.Owner.Players.FirstOrDefault(p => p?.NetworkId == c[0].ConvertLong()).ClientId, E.Owner.GetHashCode()) Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(E.Owner.Players.FirstOrDefault(p => p?.NetworkId == c[0].ConvertLong()).ClientId, E.Owner.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(E.Owner.MaxClients / 2.0) if (scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis) >= Math.Floor(E.Owner.MaxClients / 2.0)
// || scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies) >= Math.Floor(E.Owner.MaxClients / 2.0)) || scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies) >= Math.Floor(E.Owner.MaxClients / 2.0))
// { {
// await E.Origin?.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL"]); await E.Origin?.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL"]);
// return; return;
// } }
// List<string> teamAssignments = new List<string>(); List<string> teamAssignments = new List<string>();
// var activeClients = E.Owner.GetPlayersAsList().Select(c => new TeamAssignment() var activeClients = E.Owner.GetPlayersAsList().Select(c => new TeamAssignment()
// { {
// Num = c.ClientNumber, Num = c.ClientNumber,
// Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, E.Owner.GetHashCode()), Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, E.Owner.GetHashCode()),
// CurrentTeam = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, E.Owner.GetHashCode()).Team CurrentTeam = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, E.Owner.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)
// .OrderByDescending(c => c.Stats.Performance) .OrderByDescending(c => c.Stats.Performance)
// .ToList(); .ToList();
// var alliesTeam = scriptClientTeams var alliesTeam = scriptClientTeams
// .Where(c => c.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies) .Where(c => c.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies)
// .Where(c => activeClients.Count(t => t.Num == c.Num) == 0) .Where(c => activeClients.Count(t => t.Num == c.Num) == 0)
// .ToList(); .ToList();
// var axisTeam = scriptClientTeams var axisTeam = scriptClientTeams
// .Where(c => c.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis) .Where(c => c.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis)
// .Where(c => activeClients.Count(t => t.Num == c.Num) == 0) .Where(c => activeClients.Count(t => t.Num == c.Num) == 0)
// .ToList(); .ToList();
// while (activeClients.Count() > 0) while (activeClients.Count() > 0)
// { {
// int teamSizeDifference = alliesTeam.Count - axisTeam.Count; int teamSizeDifference = alliesTeam.Count - axisTeam.Count;
// double performanceDisparity = alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0 - double performanceDisparity = alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0 -
// axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0; axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0;
// if (teamSizeDifference == 0) if (teamSizeDifference == 0)
// { {
// if (performanceDisparity == 0) if (performanceDisparity == 0)
// { {
// alliesTeam.Add(activeClients.First()); alliesTeam.Add(activeClients.First());
// activeClients.RemoveAt(0); activeClients.RemoveAt(0);
// } }
// else else
// { {
// if (performanceDisparity > 0) if (performanceDisparity > 0)
// { {
// axisTeam.Add(activeClients.First()); axisTeam.Add(activeClients.First());
// activeClients.RemoveAt(0); activeClients.RemoveAt(0);
// } }
// else else
// { {
// alliesTeam.Add(activeClients.First()); alliesTeam.Add(activeClients.First());
// activeClients.RemoveAt(0); activeClients.RemoveAt(0);
// } }
// } }
// } }
// else if (teamSizeDifference > 0) else if (teamSizeDifference > 0)
// { {
// if (performanceDisparity > 0) if (performanceDisparity > 0)
// { {
// axisTeam.Add(activeClients.First()); axisTeam.Add(activeClients.First());
// activeClients.RemoveAt(0); activeClients.RemoveAt(0);
// } }
// else else
// { {
// axisTeam.Add(activeClients.Last()); axisTeam.Add(activeClients.Last());
// activeClients.RemoveAt(activeClients.Count - 1); activeClients.RemoveAt(activeClients.Count - 1);
// } }
// } }
// else else
// { {
// if (performanceDisparity > 0) if (performanceDisparity > 0)
// { {
// alliesTeam.Add(activeClients.First()); alliesTeam.Add(activeClients.First());
// activeClients.RemoveAt(0); activeClients.RemoveAt(0);
// } }
// else else
// { {
// alliesTeam.Add(activeClients.Last()); alliesTeam.Add(activeClients.Last());
// activeClients.RemoveAt(activeClients.Count - 1); activeClients.RemoveAt(activeClients.Count - 1);
// } }
// } }
// } }
// alliesTeam = alliesTeam.OrderByDescending(t => t.Stats.Performance) alliesTeam = alliesTeam.OrderByDescending(t => t.Stats.Performance)
// .ToList(); .ToList();
// axisTeam = axisTeam.OrderByDescending(t => t.Stats.Performance) axisTeam = axisTeam.OrderByDescending(t => t.Stats.Performance)
// .ToList(); .ToList();
// while (Math.Abs(alliesTeam.Count - axisTeam.Count) > 1) while (Math.Abs(alliesTeam.Count - axisTeam.Count) > 1)
// { {
// int teamSizeDifference = alliesTeam.Count - axisTeam.Count; int teamSizeDifference = alliesTeam.Count - axisTeam.Count;
// double performanceDisparity = alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0 - double performanceDisparity = alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0 -
// axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0; axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0;
// if (teamSizeDifference > 0) if (teamSizeDifference > 0)
// { {
// if (performanceDisparity > 0) if (performanceDisparity > 0)
// { {
// axisTeam.Add(alliesTeam.First()); axisTeam.Add(alliesTeam.First());
// alliesTeam.RemoveAt(0); alliesTeam.RemoveAt(0);
// } }
// else else
// { {
// axisTeam.Add(alliesTeam.Last()); axisTeam.Add(alliesTeam.Last());
// alliesTeam.RemoveAt(axisTeam.Count - 1); alliesTeam.RemoveAt(axisTeam.Count - 1);
// } }
// } }
// else else
// { {
// if (performanceDisparity > 0) if (performanceDisparity > 0)
// { {
// alliesTeam.Add(axisTeam.Last()); alliesTeam.Add(axisTeam.Last());
// axisTeam.RemoveAt(axisTeam.Count - 1); axisTeam.RemoveAt(axisTeam.Count - 1);
// } }
// else else
// { {
// alliesTeam.Add(axisTeam.First()); alliesTeam.Add(axisTeam.First());
// axisTeam.RemoveAt(0); axisTeam.RemoveAt(0);
// } }
// } }
// } }
// foreach (var assignment in alliesTeam) foreach (var assignment in alliesTeam)
// { {
// teamAssignments.Add($"{assignment.Num},2"); teamAssignments.Add($"{assignment.Num},2");
// assignment.Stats.Team = IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies; assignment.Stats.Team = IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies;
// } }
// foreach (var assignment in axisTeam) foreach (var assignment in axisTeam)
// { {
// teamAssignments.Add($"{assignment.Num},3"); teamAssignments.Add($"{assignment.Num},3");
// assignment.Stats.Team = IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis; assignment.Stats.Team = IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis;
// } }
// if (alliesTeam.Count(ac => scriptClientTeams.First(sc => sc.Num == ac.Num).CurrentTeam != ac.CurrentTeam) == 0 && if (alliesTeam.Count(ac => scriptClientTeams.First(sc => sc.Num == ac.Num).CurrentTeam != ac.CurrentTeam) == 0 &&
// axisTeam.Count(ac => scriptClientTeams.First(sc => sc.Num == ac.Num).CurrentTeam != ac.CurrentTeam) == 0) axisTeam.Count(ac => scriptClientTeams.First(sc => sc.Num == ac.Num).CurrentTeam != ac.CurrentTeam) == 0)
// { {
// await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL_BALANCED"]); await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL_BALANCED"]);
// return; return;
// } }
// if (E.Origin?.Level > Player.Permission.Administrator) if (E.Origin?.Level > Player.Permission.Administrator)
// { {
// await E.Origin.Tell($"Allies Elo: {(alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0)}"); await E.Origin.Tell($"Allies Elo: {(alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0)}");
// await E.Origin.Tell($"Axis Elo: {(axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0)}"); await E.Origin.Tell($"Axis Elo: {(axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0)}");
// } }
// string args = string.Join(",", teamAssignments); string args = string.Join(",", teamAssignments);
// await E.Owner.ExecuteCommandAsync($"sv_iw4madmin_command \"balance:{args}\""); await E.Owner.ExecuteCommandAsync($"sv_iw4madmin_command \"balance:{args}\"");
// await E.Origin.Tell("Balance command sent"); await E.Origin.Tell("Balance command sent");
// } }
// } }
//} }

View File

@ -1,6 +1,7 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Configuration; using SharedLibraryCore.Configuration;
@ -36,6 +37,15 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
var objectionalWords = Settings.Configuration().OffensiveWords; var objectionalWords = Settings.Configuration().OffensiveWords;
bool containsObjectionalWord = objectionalWords.FirstOrDefault(w => E.Origin.Name.ToLower().Contains(w)) != null; bool containsObjectionalWord = objectionalWords.FirstOrDefault(w => E.Origin.Name.ToLower().Contains(w)) != null;
// we want to run regex against it just incase
if (!containsObjectionalWord)
{
foreach (string word in objectionalWords)
{
containsObjectionalWord |= Regex.IsMatch(E.Origin.Name.ToLower(), word);
}
}
if (containsObjectionalWord) if (containsObjectionalWord)
{ {
await E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, new Player() await E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, new Player()
@ -56,7 +66,18 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
if (E.Type == GameEvent.EventType.Say) if (E.Type == GameEvent.EventType.Say)
{ {
var objectionalWords = Settings.Configuration().OffensiveWords; var objectionalWords = Settings.Configuration().OffensiveWords;
bool containsObjectionalWord = objectionalWords.FirstOrDefault(w => E.Data.ToLower().Contains(w)) != null; bool containsObjectionalWord = false;
foreach (string word in objectionalWords)
{
containsObjectionalWord |= Regex.IsMatch(E.Origin.Name.ToLower(), word);
// break out early because there's at least one objectional word
if (containsObjectionalWord)
{
break;
}
}
if (containsObjectionalWord) if (containsObjectionalWord)
{ {

View File

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

View File

@ -43,15 +43,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
context.ChangeTracker.AutoDetectChangesEnabled = false; context.ChangeTracker.AutoDetectChangesEnabled = false;
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var thirtyDaysAgo = DateTime.UtcNow.AddMonths(-1); var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15);
var iqClientRatings = (from rating in context.Set<EFRating>() var iqClientRatings = (from rating in context.Set<EFRating>()
#if DEBUG == false
where rating.ActivityAmount >= Plugin.Config.Configuration().TopPlayersMinPlayTime
#endif
where rating.RatingHistory.Client.Level != Player.Permission.Banned
where rating.RatingHistory.Client.LastConnection > thirtyDaysAgo
where rating.Newest
where rating.ServerId == null where rating.ServerId == null
where rating.RatingHistory.Client.LastConnection > fifteenDaysAgo
where rating.RatingHistory.Client.Level != Player.Permission.Banned
where rating.Newest
where rating.ActivityAmount >= Plugin.Config.Configuration().TopPlayersMinPlayTime
orderby rating.Performance descending orderby rating.Performance descending
select new select new
{ {
@ -67,7 +65,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
var clientRatings = await iqClientRatings.ToListAsync(); var clientRatings = await iqClientRatings.ToListAsync();
var clientIds = clientRatings.GroupBy(r => r.ClientId).Select(r => r.First().ClientId).ToList(); var clientIds = clientRatings
.GroupBy(r => r.ClientId)
.Select(r => r.First().ClientId)
.ToList();
var iqStatsInfo = (from stat in context.Set<EFClientStatistics>() var iqStatsInfo = (from stat in context.Set<EFClientStatistics>()
where clientIds.Contains(stat.ClientId) where clientIds.Contains(stat.ClientId)
@ -101,8 +102,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
Performance = Math.Round(clientRatingsDict[s.ClientId].Performance, 2), Performance = Math.Round(clientRatingsDict[s.ClientId].Performance, 2),
RatingChange = clientRatingsDict[s.ClientId].Ratings.First().Ranking - clientRatingsDict[s.ClientId].Ratings.Last().Ranking, RatingChange = clientRatingsDict[s.ClientId].Ratings.First().Ranking - clientRatingsDict[s.ClientId].Ratings.Last().Ranking,
PerformanceHistory = clientRatingsDict[s.ClientId].Ratings.Count() > 1 ? PerformanceHistory = clientRatingsDict[s.ClientId].Ratings.Count() > 1 ?
clientRatingsDict[s.ClientId].Ratings.Select(r => r.Performance).ToList() : clientRatingsDict[s.ClientId].Ratings.Select(r => r.Performance).ToList() :
new List<double>() { clientRatingsDict[s.ClientId].Performance, clientRatingsDict[s.ClientId].Performance }, new List<double>() { clientRatingsDict[s.ClientId].Performance, clientRatingsDict[s.ClientId].Performance },
TimePlayed = Math.Round(clientRatingsDict[s.ClientId].TotalConnectionTime / 3600.0, 1).ToString("#,##0"), TimePlayed = Math.Round(clientRatingsDict[s.ClientId].TotalConnectionTime / 3600.0, 1).ToString("#,##0"),
}) })
.OrderByDescending(r => r.Performance) .OrderByDescending(r => r.Performance)
@ -324,7 +325,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public async Task AddScriptHit(bool isDamage, DateTime time, Player attacker, Player victim, int serverId, string map, string hitLoc, string type, public async Task AddScriptHit(bool isDamage, DateTime time, Player attacker, Player victim, int serverId, string map, string hitLoc, string type,
string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads, string fraction, string snapAngles) string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads,
string fraction, string visibilityPercentage, string snapAngles)
{ {
var statsSvc = ContextThreads[serverId]; var statsSvc = ContextThreads[serverId];
Vector3 vDeathOrigin = null; Vector3 vDeathOrigin = null;
@ -381,6 +383,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
IsKillstreakKill = isKillstreakKill[0] != '0', IsKillstreakKill = isKillstreakKill[0] != '0',
AdsPercent = float.Parse(Ads), AdsPercent = float.Parse(Ads),
Fraction = double.Parse(fraction), Fraction = double.Parse(fraction),
VisibilityPercentage = double.Parse(visibilityPercentage),
IsKill = !isDamage, IsKill = !isDamage,
AnglesList = snapshotAngles AnglesList = snapshotAngles
}; };
@ -569,9 +572,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
} }
// update their performance // update their performance
//#if !DEBUG #if !DEBUG
if ((DateTime.UtcNow - attackerStats.LastStatHistoryUpdate).TotalMinutes >= 2.5) if ((DateTime.UtcNow - attackerStats.LastStatHistoryUpdate).TotalMinutes >= 2.5)
//#endif #endif
{ {
await UpdateStatHistory(attacker, attackerStats); await UpdateStatHistory(attacker, attackerStats);
attackerStats.LastStatHistoryUpdate = DateTime.UtcNow; attackerStats.LastStatHistoryUpdate = DateTime.UtcNow;
@ -631,13 +634,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
ctx.Update(clientHistory); ctx.Update(clientHistory);
} }
var thirtyDaysAgo = DateTime.UtcNow.AddMonths(-1); var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15);
// get the client ranking for the current server // get the client ranking for the current server
int individualClientRanking = await ctx.Set<EFRating>() int individualClientRanking = await ctx.Set<EFRating>()
.Where(c => c.ServerId == clientStats.ServerId) .Where(c => c.ServerId == clientStats.ServerId)
.Where(r => r.RatingHistory.Client.LastConnection > fifteenDaysAgo)
.Where(r => r.RatingHistory.Client.Level != Player.Permission.Banned) .Where(r => r.RatingHistory.Client.Level != Player.Permission.Banned)
.Where(r => r.ActivityAmount > Plugin.Config.Configuration().TopPlayersMinPlayTime) .Where(r => r.ActivityAmount > Plugin.Config.Configuration().TopPlayersMinPlayTime)
.Where(r => r.RatingHistory.Client.LastConnection > thirtyDaysAgo)
.Where(c => c.RatingHistory.ClientId != client.ClientId) .Where(c => c.RatingHistory.ClientId != client.ClientId)
.Where(r => r.Newest) .Where(r => r.Newest)
.Where(c => c.Performance > clientStats.Performance) .Where(c => c.Performance > clientStats.Performance)
@ -689,11 +692,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
} }
int overallClientRanking = await ctx.Set<EFRating>() int overallClientRanking = await ctx.Set<EFRating>()
.Where(r => r.RatingHistory.Client.Level != Player.Permission.Banned) .Where(r => r.ServerId == null)
.Where(r => r.RatingHistory.ClientId != client.ClientId)
.Where(r => r.RatingHistory.Client.LastConnection > fifteenDaysAgo)
.Where(r => r.RatingHistory.Client.Level != Player.Permission.Banned)
.Where(r => r.ActivityAmount > Plugin.Config.Configuration().TopPlayersMinPlayTime) .Where(r => r.ActivityAmount > Plugin.Config.Configuration().TopPlayersMinPlayTime)
.Where(r => r.RatingHistory.Client.LastConnection > thirtyDaysAgo)
.Where(r => r.RatingHistory.ClientId != client.ClientId)
.Where(r => r.ServerId == null)
.Where(r => r.Newest) .Where(r => r.Newest)
.Where(r => r.Performance > performanceAverage) .Where(r => r.Performance > performanceAverage)
.CountAsync() + 1; .CountAsync() + 1;
@ -730,9 +733,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
{ {
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
} }
catch (DbUpdateConcurrencyException e) // this can happen when the client disconnects without any stat changes
catch (DbUpdateConcurrencyException)
{ {
} }
} }
} }

View File

@ -31,6 +31,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
public DateTime When { get; set; } public DateTime When { get; set; }
public double Fraction { get; set; } public double Fraction { get; set; }
public bool IsKill { get; set; } public bool IsKill { get; set; }
public double VisibilityPercentage { get; set; }
// http://wiki.modsrepository.com/index.php?title=Call_of_Duty_5:_Gameplay_standards for conversion to meters // http://wiki.modsrepository.com/index.php?title=Call_of_Duty_5:_Gameplay_standards for conversion to meters
[NotMapped] [NotMapped]
public double Distance => Vector3.Distance(KillOrigin, DeathOrigin) * 0.0254; public double Distance => Vector3.Distance(KillOrigin, DeathOrigin) * 0.0254;

View File

@ -76,7 +76,7 @@ namespace IW4MAdmin.Plugins.Stats
if (killInfo.Length >= 14) if (killInfo.Length >= 14)
{ {
await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8], await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14]); killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14], killInfo[15]);
} }
break; break;
case GameEvent.EventType.Kill: case GameEvent.EventType.Kill:
@ -92,7 +92,7 @@ namespace IW4MAdmin.Plugins.Stats
if (killInfo.Length >= 14) if (killInfo.Length >= 14)
{ {
await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8], await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14]); killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14], killInfo[15]);
} }
break; break;
} }

View File

@ -0,0 +1,55 @@
using IW4MAdmin.Application;
using SharedLibraryCore;
using SharedLibraryCore.Objects;
using System;
using System.Collections.Generic;
using System.Text;
using Xunit;
namespace Tests
{
[Collection("ManagerCollection")]
public class PluginTests
{
readonly ApplicationManager Manager;
public PluginTests(ManagerFixture fixture)
{
Manager = fixture.Manager;
}
[Fact]
public void ClientSayObjectionalWordShouldWarn()
{
var e = new GameEvent()
{
Type = GameEvent.EventType.Connect,
Origin = new Player()
{
Name = $"Player1",
NetworkId = 1,
ClientNumber = 1
},
Owner = Manager.GetServers()[0]
};
Manager.GetEventHandler().AddEvent(e);
e.OnProcessed.Wait();
var client = Manager.GetServers()[0].Players[0];
e = new GameEvent()
{
Type = GameEvent.EventType.Say,
Origin = client,
Data = "nigger",
Owner = client.CurrentServer
};
Manager.GetEventHandler().AddEvent(e);
e.OnProcessed.Wait();
Assert.True(client.Warnings == 1, "client wasn't warned for objectional language");
}
}
}

View File

@ -11,6 +11,9 @@ Latest binary builds are always available at https://raidmax.org/IW4MAdmin
* [.NET Core 2.1 Runtime](https://www.microsoft.com/net/download) *or newer* * [.NET Core 2.1 Runtime](https://www.microsoft.com/net/download) *or newer*
1. Extract `IW4MAdmin-<version>.zip` 1. Extract `IW4MAdmin-<version>.zip`
2. Run `StartIW4MAdmin.cmd` 2. Run `StartIW4MAdmin.cmd`
### Help
Feel free to join the **IW4MAdmin** [Discord](https://discord.gg/ZZFK5p3)
If you come across an issue, bug, or feature request please post an [issue](https://github.com/RaidMax/IW4M-Admin/issues)
___ ___
### Configuration ### Configuration
@ -30,10 +33,6 @@ When **IW4MAdmin** is launched for the _first time_, you will be prompted to set
* Shows a prefix to every message send by **IW4MAdmin** -- `[Admin] message` * Shows a prefix to every message send by **IW4MAdmin** -- `[Admin] message`
* _This feature requires you specify a custom say name_ * _This feature requires you specify a custom say name_
`Enable client VPNs`
* Allow clients to use a [VPN](https://en.wikipedia.org/wiki/Virtual_private_network)
* _This feature requires an active api key on [iphub.info](https://iphub.info/)_
`Enable social link` `Enable social link`
* Shows a link to your community's social media/website on the webfront * Shows a link to your community's social media/website on the webfront
@ -82,9 +81,6 @@ If you wish to further customize your experience of **IW4MAdmin**, the following
`RConPollRate` `RConPollRate`
* Specifies (in milliseconds) how often to poll each server for updates * Specifies (in milliseconds) how often to poll each server for updates
`VpnExceptionIds`
* Specifies the list of `Client IDs` exempt from the VPN check (if enabled)
`Servers` `Servers`
* Specifies the list of servers **IW4MAdmin** will monitor * Specifies the list of servers **IW4MAdmin** will monitor
* `IPAddress` * `IPAddress`
@ -228,6 +224,9 @@ ___
#### Stats #### Stats
- This plugin calculates basic player performance, skill approximation, and kill/death ratio - This plugin calculates basic player performance, skill approximation, and kill/death ratio
- Skill is an number derived from an algorithmic processing of a player's Kill Death Ratio (KDR) and Score per Minute (SPM).
- Elo Rating is based off of the number of encounters a player wins.
- Performance is the average of Skill + Elo Rating
**Commands added by this plugin** **Commands added by this plugin**
@ -238,7 +237,7 @@ ___
|topstats|ts|view the top 5 players on this server|False|!ts |User| |topstats|ts|view the top 5 players on this server|False|!ts |User|
|mostplayed|mp|view the top 5 dedicated players on the server|False|!mp |User| |mostplayed|mp|view the top 5 dedicated players on the server|False|!mp |User|
- To qualify for top stats, a client must have played for at least `1 hour` and connected within the past `30 days`. - To qualify for top stats, a client must have played for at least `3 hours` and connected within the past `15 days`.
#### Login #### Login
- This plugin deters GUID spoofing by requiring privileged users to login with their password before executing commands - This plugin deters GUID spoofing by requiring privileged users to login with their password before executing commands
@ -254,6 +253,14 @@ ___
- This plugin warns and kicks players for using profanity - This plugin warns and kicks players for using profanity
- Profane words and warning message can be specified in `ProfanityDetermentSettings.json` - Profane words and warning message can be specified in `ProfanityDetermentSettings.json`
- If a client's name contains a word listed in the settings, they will immediately be kicked - If a client's name contains a word listed in the settings, they will immediately be kicked
####IW4 Script Commands
- This plugin provides additional integration to IW4x
- In order to take advantage of it, copy the `userraw` folder into your IW4x server directory
####VPN Detection [Script Plugin]
- This plugin detects if a client is using a VPN and kicks them if they are
- To disable this plugin, delete `Plugins\VPNDetection.js`
___ ___
### Webfront ### Webfront
`Home` `Home`
@ -274,34 +281,35 @@ ___
`Web Console` `Web Console`
* Allows logged in privileged users to execute commands as if they are in-game * Allows logged in privileged users to execute commands as if they are in-game
--- ---
### Extending Plugins ### Extending Plugins
#### Code #### Code
IW4Madmin functionality can be extended by writing additional plugins in C#. IW4Madmin functionality can be extended by writing additional plugins in C#.
Each class library must implement the `IPlugin` interface.
See the existing plugins for examples.
#### JavaScript #### JavaScript
IW4MAdmin functionality can be extended using JavaScript. IW4MAdmin functionality can be extended using JavaScript.
The JavaScript parser supports [some](https://github.com/sebastienros/jint/issues/343) of ECMAScript 6's new features. The JavaScript parser supports [ECMA 5.1](https://ecma-international.org/ecma-262/5.1/) standards.
#### Plugin Object Template #### Plugin Object Template
In order to be properly parsed by the JavaScript engine, every plugin must conform to the following template. In order to be properly parsed by the JavaScript engine, every plugin must conform to the following template.
```js ```js
const plugin = { var plugin = {
author: 'YourHandle', author: 'YourHandle',
version: 1.0, version: 1.0,
name: 'Sample JavaScript Plugin', name: 'Sample JavaScript Plugin',
onEventAsync(gameEvent, server) { onEventAsync: function (gameEvent, server) {
}, },
onLoadAsync(manager) { onLoadAsync: function (manager) {
}, },
onUnloadAsync() { onUnloadAsync: function () {
}, },
onTickAsync(server) { onTickAsync: function (server) {
} }
} };
``` ```
#### Required Properties #### Required Properties
- `author` &mdash; [string] Author of the plugin (usually your name or online name/alias) - `author` &mdash; [string] Author of the plugin (usually your name or online name/alias)
@ -333,9 +341,9 @@ setuptools>=39.0.1
urllib3>=1.23 urllib3>=1.23
``` ```
#### Configuration Options #### Configuration Options
- `IW4MAdminUrl` &mdash; Base url corresponding to your IW4MAdmin `WebfrontBindUrl`. - `IW4MAdminUrl` &mdash; Base url corresponding to your IW4MAdmin `WebfrontBindUrl`.
Example http://127.0.0.1 Example http://127.0.0.1
- `DiscordWebhookNotificationUrl` &mdash; [required] Discord generated URL to send notifications/alerts to; this includes **Reports** and **Bans** - `DiscordWebhookNotificationUrl` &mdash; [required] Discord generated URL to send notifications/alerts to; this includes **Reports** and **Bans**
Example https://discordapp.com/api/webhooks/id/token Example https://discordapp.com/api/webhooks/id/token
- `DiscordWebhookInformationUrl` &mdash; [optional] Discord generated URL to send information to; this includes information such as player messages - `DiscordWebhookInformationUrl` &mdash; [optional] Discord generated URL to send information to; this includes information such as player messages
- `NotifyRoleIds` &mdash; [optional] List of [discord role ids](https://discordhelp.net/role-id) to mention when notification hook is sent - `NotifyRoleIds` &mdash; [optional] List of [discord role ids](https://discordhelp.net/role-id) to mention when notification hook is sent

View File

@ -10,19 +10,16 @@ namespace SharedLibraryCore.Configuration
public bool EnableWebFront { get; set; } public bool EnableWebFront { get; set; }
public bool EnableMultipleOwners { get; set; } public bool EnableMultipleOwners { get; set; }
public bool EnableSteppedHierarchy { get; set; } public bool EnableSteppedHierarchy { get; set; }
public bool EnableClientVPNs { get; set; }
public bool EnableSocialLink { get; set; } public bool EnableSocialLink { get; set; }
public bool EnableCustomSayName { get; set; } public bool EnableCustomSayName { get; set; }
public string CustomSayName { get; set; } public string CustomSayName { get; set; }
public string SocialLinkAddress { get; set; } public string SocialLinkAddress { get; set; }
public string SocialLinkTitle { get; set; } public string SocialLinkTitle { get; set; }
public string IPHubAPIKey { get; set; }
public string WebfrontBindUrl { get; set; } public string WebfrontBindUrl { get; set; }
public string CustomParserEncoding { get; set; } public string CustomParserEncoding { get; set; }
public string CustomLocale { get; set; } public string CustomLocale { get; set; }
public string ConnectionString { get; set; } public string ConnectionString { get; set; }
public int RConPollRate { get; set; } = 5000; public int RConPollRate { get; set; } = 5000;
public List<int> VpnExceptionIds { get; set; }
public string Id { get; set; } public string Id { get; set; }
public List<ServerConfiguration> Servers { get; set; } public List<ServerConfiguration> Servers { get; set; }
public int AutoMessagePeriod { get; set; } public int AutoMessagePeriod { get; set; }
@ -43,16 +40,11 @@ namespace SharedLibraryCore.Configuration
bool useCustomParserEncoding = Utilities.PromptBool(loc["SETUP_USE_CUSTOMENCODING"]); bool useCustomParserEncoding = Utilities.PromptBool(loc["SETUP_USE_CUSTOMENCODING"]);
CustomParserEncoding = useCustomParserEncoding ? Utilities.PromptString(loc["SETUP_ENCODING_STRING"]) : "windows-1252"; CustomParserEncoding = useCustomParserEncoding ? Utilities.PromptString(loc["SETUP_ENCODING_STRING"]) : "windows-1252";
WebfrontBindUrl = "http://127.0.0.1:1624"; WebfrontBindUrl = "http://0.0.0.0:1624";
if (EnableCustomSayName) if (EnableCustomSayName)
CustomSayName = Utilities.PromptString(loc["SETUP_SAY_NAME"]); CustomSayName = Utilities.PromptString(loc["SETUP_SAY_NAME"]);
EnableClientVPNs = Utilities.PromptBool(loc["SETUP_ENABLE_VPNS"]);
if (!EnableClientVPNs)
IPHubAPIKey = Utilities.PromptString(loc["SETUP_IPHUB_KEY"]);
EnableSocialLink = Utilities.PromptBool(loc["SETUP_DISPLAY_SOCIAL"]); EnableSocialLink = Utilities.PromptBool(loc["SETUP_DISPLAY_SOCIAL"]);
if (EnableSocialLink) if (EnableSocialLink)
@ -60,7 +52,7 @@ namespace SharedLibraryCore.Configuration
SocialLinkTitle = Utilities.PromptString(loc["SETUP_SOCIAL_TITLE"]); SocialLinkTitle = Utilities.PromptString(loc["SETUP_SOCIAL_TITLE"]);
SocialLinkAddress = Utilities.PromptString(loc["SETUP_SOCIAL_LINK"]); SocialLinkAddress = Utilities.PromptString(loc["SETUP_SOCIAL_LINK"]);
} }
VpnExceptionIds = new List<int>();
RConPollRate = 5000; RConPollRate = 5000;
return this; return this;

View File

@ -13,6 +13,6 @@ namespace SharedLibraryCore.Interfaces
/// Add a game event event to the queue to be processed /// Add a game event event to the queue to be processed
/// </summary> /// </summary>
/// <param name="gameEvent">Game event</param> /// <param name="gameEvent">Game event</param>
bool AddEvent(GameEvent gameEvent); void AddEvent(GameEvent gameEvent);
} }
} }

View File

@ -0,0 +1,665 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using SharedLibraryCore.Database;
namespace SharedLibraryCore.Migrations
{
[DbContext(typeof(DatabaseContext))]
[Migration("20180904154622_AddVisibilityPercentage")]
partial class AddVisibilityPercentage
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.2-rtm-30932");
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
{
b.Property<int>("SnapshotId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ClientId");
b.Property<int>("CurrentSessionLength");
b.Property<double>("CurrentStrain");
b.Property<int?>("CurrentViewAngleVector3Id");
b.Property<int>("Deaths");
b.Property<double>("Distance");
b.Property<double>("EloRating");
b.Property<int?>("HitDestinationVector3Id");
b.Property<int>("HitLocation");
b.Property<int?>("HitOriginVector3Id");
b.Property<int>("HitType");
b.Property<int>("Hits");
b.Property<int>("Kills");
b.Property<int?>("LastStrainAngleVector3Id");
b.Property<double>("SessionAngleOffset");
b.Property<double>("SessionSPM");
b.Property<int>("SessionScore");
b.Property<double>("StrainAngleBetween");
b.Property<int>("TimeSinceLastEvent");
b.Property<int>("WeaponId");
b.Property<DateTime>("When");
b.HasKey("SnapshotId");
b.HasIndex("ClientId");
b.HasIndex("CurrentViewAngleVector3Id");
b.HasIndex("HitDestinationVector3Id");
b.HasIndex("HitOriginVector3Id");
b.HasIndex("LastStrainAngleVector3Id");
b.ToTable("EFACSnapshot");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
{
b.Property<long>("KillId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("AttackerId");
b.Property<int>("Damage");
b.Property<int?>("DeathOriginVector3Id");
b.Property<int>("DeathType");
b.Property<double>("Fraction");
b.Property<int>("HitLoc");
b.Property<bool>("IsKill");
b.Property<int?>("KillOriginVector3Id");
b.Property<int>("Map");
b.Property<int>("ServerId");
b.Property<int>("VictimId");
b.Property<int?>("ViewAnglesVector3Id");
b.Property<double>("VisibilityPercentage");
b.Property<int>("Weapon");
b.Property<DateTime>("When");
b.HasKey("KillId");
b.HasIndex("AttackerId");
b.HasIndex("DeathOriginVector3Id");
b.HasIndex("KillOriginVector3Id");
b.HasIndex("ServerId");
b.HasIndex("VictimId");
b.HasIndex("ViewAnglesVector3Id");
b.ToTable("EFClientKills");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
{
b.Property<long>("MessageId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ClientId");
b.Property<string>("Message");
b.Property<int>("ServerId");
b.Property<DateTime>("TimeSent");
b.HasKey("MessageId");
b.HasIndex("ClientId");
b.HasIndex("ServerId");
b.ToTable("EFClientMessages");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
{
b.Property<int>("RatingHistoryId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ClientId");
b.HasKey("RatingHistoryId");
b.HasIndex("ClientId");
b.ToTable("EFClientRatingHistory");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
{
b.Property<int>("ClientId");
b.Property<int>("ServerId");
b.Property<bool>("Active");
b.Property<int>("Deaths");
b.Property<double>("EloRating");
b.Property<int>("Kills");
b.Property<double>("MaxStrain");
b.Property<double>("RollingWeightedKDR");
b.Property<double>("SPM");
b.Property<double>("Skill");
b.Property<int>("TimePlayed");
b.HasKey("ClientId", "ServerId");
b.HasIndex("ServerId");
b.ToTable("EFClientStatistics");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
{
b.Property<int>("HitLocationCountId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ClientId")
.HasColumnName("EFClientStatistics_ClientId");
b.Property<int>("HitCount");
b.Property<float>("HitOffsetAverage");
b.Property<int>("Location");
b.Property<float>("MaxAngleDistance");
b.Property<int>("ServerId")
.HasColumnName("EFClientStatistics_ServerId");
b.HasKey("HitLocationCountId");
b.HasIndex("ServerId");
b.HasIndex("ClientId", "ServerId");
b.ToTable("EFHitLocationCounts");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
{
b.Property<int>("RatingId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ActivityAmount");
b.Property<bool>("Newest");
b.Property<double>("Performance");
b.Property<int>("Ranking");
b.Property<int>("RatingHistoryId");
b.Property<int?>("ServerId");
b.HasKey("RatingId");
b.HasIndex("RatingHistoryId");
b.HasIndex("ServerId");
b.ToTable("EFRating");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b =>
{
b.Property<int>("ServerId");
b.Property<bool>("Active");
b.Property<int>("Port");
b.HasKey("ServerId");
b.ToTable("EFServers");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
{
b.Property<int>("StatisticId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ServerId");
b.Property<long>("TotalKills");
b.Property<long>("TotalPlayTime");
b.HasKey("StatisticId");
b.HasIndex("ServerId");
b.ToTable("EFServerStatistics");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
{
b.Property<int>("AliasId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<DateTime>("DateAdded");
b.Property<int>("IPAddress");
b.Property<int>("LinkId");
b.Property<string>("Name")
.IsRequired();
b.HasKey("AliasId");
b.HasIndex("IPAddress");
b.HasIndex("LinkId");
b.ToTable("EFAlias");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b =>
{
b.Property<int>("AliasLinkId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.HasKey("AliasLinkId");
b.ToTable("EFAliasLinks");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b =>
{
b.Property<int>("ChangeHistoryId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<string>("Comment")
.HasMaxLength(128);
b.Property<int>("OriginEntityId");
b.Property<int>("TargetEntityId");
b.Property<DateTime>("TimeChanged");
b.Property<int>("TypeOfChange");
b.HasKey("ChangeHistoryId");
b.ToTable("EFChangeHistory");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
{
b.Property<int>("ClientId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("AliasLinkId");
b.Property<int>("Connections");
b.Property<int>("CurrentAliasId");
b.Property<DateTime>("FirstConnection");
b.Property<DateTime>("LastConnection");
b.Property<int>("Level");
b.Property<bool>("Masked");
b.Property<long>("NetworkId");
b.Property<string>("Password");
b.Property<string>("PasswordSalt");
b.Property<int>("TotalConnectionTime");
b.HasKey("ClientId");
b.HasIndex("AliasLinkId");
b.HasIndex("CurrentAliasId");
b.HasIndex("NetworkId")
.IsUnique();
b.ToTable("EFClients");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
{
b.Property<int>("MetaId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ClientId");
b.Property<DateTime>("Created");
b.Property<string>("Extra");
b.Property<string>("Key")
.IsRequired();
b.Property<DateTime>("Updated");
b.Property<string>("Value")
.IsRequired();
b.HasKey("MetaId");
b.HasIndex("ClientId");
b.ToTable("EFMeta");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
{
b.Property<int>("PenaltyId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<string>("AutomatedOffense");
b.Property<DateTime>("Expires");
b.Property<int>("LinkId");
b.Property<int>("OffenderId");
b.Property<string>("Offense")
.IsRequired();
b.Property<int>("PunisherId");
b.Property<int>("Type");
b.Property<DateTime>("When");
b.HasKey("PenaltyId");
b.HasIndex("LinkId");
b.HasIndex("OffenderId");
b.HasIndex("PunisherId");
b.ToTable("EFPenalties");
});
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
{
b.Property<int>("Vector3Id")
.ValueGeneratedOnAdd();
b.Property<int?>("EFACSnapshotSnapshotId");
b.Property<float>("X");
b.Property<float>("Y");
b.Property<float>("Z");
b.HasKey("Vector3Id");
b.ToTable("Vector3");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany()
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Helpers.Vector3", "CurrentViewAngle")
.WithMany()
.HasForeignKey("CurrentViewAngleVector3Id");
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination")
.WithMany()
.HasForeignKey("HitDestinationVector3Id");
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin")
.WithMany()
.HasForeignKey("HitOriginVector3Id");
b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle")
.WithMany()
.HasForeignKey("LastStrainAngleVector3Id");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker")
.WithMany()
.HasForeignKey("AttackerId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Helpers.Vector3", "DeathOrigin")
.WithMany()
.HasForeignKey("DeathOriginVector3Id");
b.HasOne("SharedLibraryCore.Helpers.Vector3", "KillOrigin")
.WithMany()
.HasForeignKey("KillOriginVector3Id");
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim")
.WithMany()
.HasForeignKey("VictimId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Helpers.Vector3", "ViewAngles")
.WithMany()
.HasForeignKey("ViewAnglesVector3Id");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany()
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany()
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany()
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany()
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics")
.WithMany("HitLocations")
.HasForeignKey("ClientId", "ServerId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
{
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", "RatingHistory")
.WithMany("Ratings")
.HasForeignKey("RatingHistoryId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
{
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
.WithMany("Children")
.HasForeignKey("LinkId")
.OnDelete(DeleteBehavior.Restrict);
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink")
.WithMany()
.HasForeignKey("AliasLinkId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias")
.WithMany()
.HasForeignKey("CurrentAliasId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany("Meta")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
.WithMany("ReceivedPenalties")
.HasForeignKey("LinkId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender")
.WithMany("ReceivedPenalties")
.HasForeignKey("OffenderId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher")
.WithMany("AdministeredPenalties")
.HasForeignKey("PunisherId")
.OnDelete(DeleteBehavior.Restrict);
});
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
{
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot")
.WithMany("PredictedViewAngles")
.HasForeignKey("EFACSnapshotSnapshotId");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,23 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace SharedLibraryCore.Migrations
{
public partial class AddVisibilityPercentage : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<double>(
name: "VisibilityPercentage",
table: "EFClientKills",
nullable: false,
defaultValue: 0.0);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "VisibilityPercentage",
table: "EFClientKills");
}
}
}

View File

@ -111,6 +111,8 @@ namespace SharedLibraryCore.Migrations
b.Property<int?>("ViewAnglesVector3Id"); b.Property<int?>("ViewAnglesVector3Id");
b.Property<double>("VisibilityPercentage");
b.Property<int>("Weapon"); b.Property<int>("Weapon");
b.Property<DateTime>("When"); b.Property<DateTime>("When");

View File

@ -17,19 +17,14 @@ namespace SharedLibraryCore.RCon
public int BufferSize { get; private set; } public int BufferSize { get; private set; }
public byte[] Buffer { get; private set; } public byte[] Buffer { get; private set; }
private readonly StringBuilder sb; public StringBuilder ResponseString { get; }
public StringBuilder ResponseString
{
get => sb;
}
public ConnectionState(Socket cl) public ConnectionState(Socket cl)
{ {
BufferSize = 8192; BufferSize = 8192;
Buffer = new byte[BufferSize]; Buffer = new byte[BufferSize];
Client = cl; Client = cl;
sb = new StringBuilder(); ResponseString = new StringBuilder();
} }
} }

View File

@ -13,6 +13,6 @@ namespace SharedLibraryCore.RCon
} }
public static char SeperatorChar = (char)int.Parse("0a", System.Globalization.NumberStyles.AllowHexSpecifier); public static char SeperatorChar = (char)int.Parse("0a", System.Globalization.NumberStyles.AllowHexSpecifier);
public static readonly TimeSpan SocketTimeout = new TimeSpan(0, 0, 5); public static readonly TimeSpan SocketTimeout = new TimeSpan(0, 0, 10);
} }
} }

View File

@ -58,8 +58,8 @@ namespace SharedLibraryCore
Manager = mgr; Manager = mgr;
string script = File.ReadAllText(FileName); string script = File.ReadAllText(FileName);
ScriptEngine = new Jint.Engine(cfg => ScriptEngine = new Jint.Engine(cfg =>
cfg.AllowClr(new[] cfg.AllowClr(new[]
{ {
typeof(System.Net.Http.HttpClient).Assembly, typeof(System.Net.Http.HttpClient).Assembly,
typeof(Objects.Player).Assembly, typeof(Objects.Player).Assembly,
@ -82,9 +82,12 @@ namespace SharedLibraryCore
public Task OnEventAsync(GameEvent E, Server S) public Task OnEventAsync(GameEvent E, Server S)
{ {
ScriptEngine.SetValue("_gameEvent", E); lock (ScriptEngine)
ScriptEngine.SetValue("_server", S); {
return Task.FromResult(ScriptEngine.Execute("plugin.onEventAsync(_gameEvent, _server)").GetCompletionValue()); ScriptEngine.SetValue("_gameEvent", E);
ScriptEngine.SetValue("_server", S);
return Task.FromResult(ScriptEngine.Execute("plugin.onEventAsync(_gameEvent, _server)").GetCompletionValue());
}
} }
public Task OnLoadAsync(IManager manager) public Task OnLoadAsync(IManager manager)

View File

@ -117,7 +117,7 @@ namespace SharedLibraryCore
public async Task Broadcast(String Message) public async Task Broadcast(String Message)
{ {
#if DEBUG == false #if DEBUG == false
string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Say, Message); string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Say, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{Message}");
#else #else
Logger.WriteVerbose(Message.StripColors()); Logger.WriteVerbose(Message.StripColors());
#endif #endif
@ -145,7 +145,7 @@ namespace SharedLibraryCore
public async Task Tell(String Message, Player Target) public async Task Tell(String Message, Player Target)
{ {
#if !DEBUG #if !DEBUG
string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Tell, Target.ClientNumber, Message); string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Tell, Target.ClientNumber, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{Message}");
if (Target.ClientNumber > -1 && Message.Length > 0 && Target.Level != Player.Permission.Console) if (Target.ClientNumber > -1 && Message.Length > 0 && Target.Level != Player.Permission.Console)
await this.ExecuteCommandAsync(formattedMessage); await this.ExecuteCommandAsync(formattedMessage);
#else #else
@ -160,9 +160,11 @@ namespace SharedLibraryCore
Console.ForegroundColor = ConsoleColor.Gray; Console.ForegroundColor = ConsoleColor.Gray;
} }
// prevent this from queueing up too many command responses
if (CommandResult.Count > 15) if (CommandResult.Count > 15)
CommandResult.RemoveAt(0); CommandResult.RemoveAt(0);
// it was a remote command so we need to add it to the command result queue
if (Target.ClientNumber < 0) if (Target.ClientNumber < 0)
{ {
CommandResult.Add(new CommandResponseInfo() CommandResult.Add(new CommandResponseInfo()

View File

@ -17,7 +17,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Jint" Version="3.0.0-beta-1249" /> <PackageReference Include="Jint" Version="2.11.58" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.2" />

View File

@ -12,6 +12,26 @@ init()
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.playerTags = [];
level.playerTags[0] = "j_head";
level.playerTags[1] = "j_neck";
level.playerTags[2] = "j_spineupper";
level.playerTags[3] = "j_spinelower";
level.playerTags[4] = "j_shoulder_ri";
level.playerTags[5] = "j_shoulder_le";
level.playerTags[6] = "j_elbow_ri";
level.playerTags[7] = "j_spineupper";
level.playerTags[8] = "j_spineupper";
level.playerTags[9] = "j_elbow_le";
level.playerTags[10] = "j_wrist_ri";
level.playerTags[11] = "j_wrist_le";
level.playerTags[12] = "j_hip_ri";
level.playerTags[13] = "j_hip_le";
level.playerTags[14] = "j_knee_ri";
level.playerTags[15] = "j_knee_le";
level.playerTags[16] = "j_ankle_ri";
level.playerTags[17] = "j_ankle_le";
level.playerTags[18] = "j_helmet";
} }
@ -112,6 +132,21 @@ waitForAdditionalAngles(logString)
logPrint(logString + ";" + anglesStr + "\n"); logPrint(logString + ";" + anglesStr + "\n");
} }
runVisibilityCheck(attacker, victim)
{
start = attacker getTagOrigin("tag_eye");
traceSucceedCount = 0;
for (i = 0; i < 19; i++)
{
if (sightTracePassed(start, victim getTagOrigin(level.playerTags[i]), false, attacker))
{
traceSucceedCount += 1;
}
}
return traceSucceedCount / 20;
}
vectorScale(vector, scale) vectorScale(vector, scale)
{ {
return (vector[0] * scale, vector[1] * scale, vector[2] * scale); return (vector[0] * scale, vector[1] * scale, vector[2] * scale);
@ -134,7 +169,9 @@ Process_Hit(type, attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon)
end = location; end = location;
trace = bulletTrace(start, end, true, _attacker); trace = bulletTrace(start, end, true, _attacker);
logLine = "Script" + type + ";" + _attacker.guid + ";" + victim.guid + ";" + _attacker GetTagOrigin("tag_eye") + ";" + location + ";" + iDamage + ";" + sWeapon + ";" + sHitLoc + ";" + sMeansOfDeath + ";" + _attacker getPlayerAngles() + ";" + gettime() + ";" + isKillstreakKill + ";" + _attacker playerADS() + ";" + trace["fraction"]; playerVisibilityPercentage = runVisibilityCheck(_attacker, victim);
logLine = "Script" + type + ";" + _attacker.guid + ";" + victim.guid + ";" + _attacker GetTagOrigin("tag_eye") + ";" + location + ";" + iDamage + ";" + sWeapon + ";" + sHitLoc + ";" + sMeansOfDeath + ";" + _attacker getPlayerAngles() + ";" + gettime() + ";" + isKillstreakKill + ";" + _attacker playerADS() + ";" + trace["fraction"] + ";" + playerVisibilityPercentage;
attacker thread waitForAdditionalAngles(logLine); attacker thread waitForAdditionalAngles(logLine);
} }