diff --git a/Application/Application.csproj b/Application/Application.csproj
index 9c322e34b..e46d20c11 100644
--- a/Application/Application.csproj
+++ b/Application/Application.csproj
@@ -30,6 +30,7 @@
true
true
+ 2.1.9.3
diff --git a/Application/Server.cs b/Application/Server.cs
index d520eada5..211ec1d26 100644
--- a/Application/Server.cs
+++ b/Application/Server.cs
@@ -432,6 +432,11 @@ namespace IW4MAdmin
await Kick(E.Data, E.Target, E.Origin);
}
+ else if (E.Type == GameEvent.EventType.Warn)
+ {
+ await Warn(E.Data, E.Target, E.Origin);
+ }
+
else if (E.Type == GameEvent.EventType.Quit)
{
var origin = Players.FirstOrDefault(p => p != null && p.NetworkId == E.Origin.NetworkId);
@@ -888,7 +893,7 @@ namespace IW4MAdmin
#endif
}
- public override async Task Warn(String Reason, Player Target, Player Origin)
+ protected override async Task Warn(String Reason, Player Target, Player Origin)
{
// ensure player gets warned if command not performed on them in game
if (Target.ClientNumber < 0)
diff --git a/Plugins/ProfanityDeterment/Plugin.cs b/Plugins/ProfanityDeterment/Plugin.cs
index 08e230b2e..6e058bece 100644
--- a/Plugins/ProfanityDeterment/Plugin.cs
+++ b/Plugins/ProfanityDeterment/Plugin.cs
@@ -25,7 +25,7 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
public Task OnEventAsync(GameEvent E, Server S)
{
if (!Settings.Configuration().EnableProfanityDeterment)
- return Task.CompletedTask; ;
+ return Task.CompletedTask;
if (E.Type == GameEvent.EventType.Connect)
{
@@ -50,7 +50,8 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
{
E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, new Player()
{
- ClientId = 1
+ ClientId = 1,
+ CurrentServer = E.Owner
});
};
}
@@ -86,7 +87,8 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
{
clientProfanity.Client.Kick(Settings.Configuration().ProfanityKickMessage, new Player()
{
- ClientId = 1
+ ClientId = 1,
+ CurrentServer = E.Owner
});
}
@@ -96,7 +98,8 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
clientProfanity.Client.Warn(Settings.Configuration().ProfanityWarningMessage, new Player()
{
- ClientId = 1
+ ClientId = 1,
+ CurrentServer = E.Owner
});
}
}
diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs
index 1449e6607..3966672ac 100644
--- a/Plugins/Stats/Helpers/StatManager.cs
+++ b/Plugins/Stats/Helpers/StatManager.cs
@@ -194,8 +194,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
var statsSvc = new ThreadSafeStatsService();
ContextThreads.TryAdd(serverId, statsSvc);
+ var serverSvc = statsSvc.ServerSvc;
+
// get the server from the database if it exists, otherwise create and insert a new one
var server = statsSvc.ServerSvc.Find(c => c.ServerId == serverId).FirstOrDefault();
+
if (server == null)
{
server = new EFServer()
@@ -205,11 +208,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
ServerId = serverId
};
- statsSvc.ServerSvc.Insert(server);
+ serverSvc.Insert(server);
}
// this doesn't need to be async as it's during initialization
- statsSvc.ServerSvc.SaveChanges();
+ serverSvc.SaveChanges();
// check to see if the stats have ever been initialized
InitializeServerStats(sv);
statsSvc.ServerStatsSvc.SaveChanges();
@@ -278,7 +281,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
};
// insert if they've not been added
- clientStats = clientStatsSvc.Insert(clientStats);
+ clientStatsSvc.Insert(clientStats);
await clientStatsSvc.SaveChangesAsync();
}
@@ -470,6 +473,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
var clientStats = Servers[serverId].PlayerStats[attacker.ClientId];
var clientStatsSvc = statsSvc.ClientStatSvc;
clientStatsSvc.Update(clientStats);
+
// increment their hit count
if (hit.DeathType == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET ||
@@ -496,13 +500,25 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
await ApplyPenalty(clientDetection.ProcessTotalRatio(clientStats), clientDetection, attacker, ctx);
}
- await clientStatsSvc.SaveChangesAsync();
+ ctx.Set().UpdateRange(clientStats.HitLocations);
+
await ctx.SaveChangesAsync();
}
catch (Exception ex)
{
- Log.WriteError("AC ERROR");
+ Log.WriteError("Could not save hit or AC info");
+ Log.WriteDebug(ex.GetExceptionInfo());
+ }
+
+ try
+ {
+ await clientStatsSvc.SaveChangesAsync();
+ }
+
+ catch (Exception ex)
+ {
+ Log.WriteError("Could save save client stats");
Log.WriteDebug(ex.GetExceptionInfo());
}
@@ -1116,10 +1132,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
{
int serverId = sv.GetHashCode();
var statsSvc = ContextThreads[serverId];
+ var serverSvc = statsSvc.ServerSvc;
+
+ serverSvc.Update(Servers[serverId].Server);
+ await serverSvc.SaveChangesAsync();
- // Log.WriteDebug("Syncing stats contexts");
- await statsSvc.ServerStatsSvc.SaveChangesAsync();
- //await statsSvc.ClientStatSvc.SaveChangesAsync();
await statsSvc.KillStatsSvc.SaveChangesAsync();
await statsSvc.ServerSvc.SaveChangesAsync();
diff --git a/Plugins/Stats/Helpers/ThreadSafeStatsService.cs b/Plugins/Stats/Helpers/ThreadSafeStatsService.cs
index 1863794c4..64cfb0074 100644
--- a/Plugins/Stats/Helpers/ThreadSafeStatsService.cs
+++ b/Plugins/Stats/Helpers/ThreadSafeStatsService.cs
@@ -14,10 +14,16 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
{
get
{
- return new GenericRepository(true);
+ return new GenericRepository(false);
+ }
+ }
+ public GenericRepository ServerSvc
+ {
+ get
+ {
+ return new GenericRepository(false);
}
}
- public GenericRepository ServerSvc { get; private set; }
public GenericRepository KillStatsSvc { get; private set; }
public GenericRepository ServerStatsSvc { get; private set; }
public GenericRepository MessageSvc
@@ -30,7 +36,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
public ThreadSafeStatsService()
{
- ServerSvc = new GenericRepository();
KillStatsSvc = new GenericRepository();
ServerStatsSvc = new GenericRepository();
}
diff --git a/Plugins/Stats/Models/EFHitLocationCount.cs b/Plugins/Stats/Models/EFHitLocationCount.cs
index 4d7fa192b..e5fea2f78 100644
--- a/Plugins/Stats/Models/EFHitLocationCount.cs
+++ b/Plugins/Stats/Models/EFHitLocationCount.cs
@@ -23,6 +23,5 @@ namespace IW4MAdmin.Plugins.Stats.Models
public int ServerId { get; set; }
[ForeignKey("ServerId"), Column(Order = 1)]
public EFServer Server { get; set; }
-
}
}
diff --git a/Plugins/Tests/ClientTests.cs b/Plugins/Tests/ClientTests.cs
index 30737a704..089dd512e 100644
--- a/Plugins/Tests/ClientTests.cs
+++ b/Plugins/Tests/ClientTests.cs
@@ -53,13 +53,15 @@ namespace Tests
Assert.False(client == null, "no client found to warn");
var warnEvent = client.Warn("test warn", new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer });
- warnEvent.OnProcessed.Wait(TestTimeout);
+ warnEvent.OnProcessed.Wait();
- Assert.True(client.Warnings == 1 ||
- warnEvent.Failed, "warning did not get applied");
+ Assert.True((client.Warnings == 1 ||
+ warnEvent.Failed) &&
+ Manager.GetPenaltyService().GetClientPenaltiesAsync(client.ClientId).Result.Count(p => p.Type == Penalty.PenaltyType.Warning) == 1,
+ "warning did not get applied");
warnEvent = client.Warn("test warn", new Player() { ClientId = 1, Level = Player.Permission.Banned, CurrentServer = client.CurrentServer });
- warnEvent.OnProcessed.Wait(TestTimeout);
+ warnEvent.OnProcessed.Wait();
Assert.True(warnEvent.FailReason == GameEvent.EventFailReason.Permission &&
client.Warnings == 1, "warning was applied without proper permissions");
@@ -86,8 +88,17 @@ namespace Tests
var client = Manager.Servers.First().GetPlayersAsList().FirstOrDefault();
Assert.False(client == null, "no client found to report");
+ // fail
+ var player = new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer };
+ player.SetAdditionalProperty("_reportCount", 3);
+ var reportEvent = client.Report("test report", player);
+ reportEvent.OnProcessed.Wait(TestTimeout);
+
+ Assert.True(reportEvent.FailReason == GameEvent.EventFailReason.Throttle &
+ client.CurrentServer.Reports.Count(r => r.Target.NetworkId == client.NetworkId) == 0, $"too many reports were applied [{reportEvent.FailReason.ToString()}]");
+
// succeed
- var reportEvent = client.Report("test report", new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer });
+ reportEvent = client.Report("test report", new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer });
reportEvent.OnProcessed.Wait(TestTimeout);
Assert.True(!reportEvent.Failed &&
diff --git a/SharedLibraryCore/Commands/NativeCommands.cs b/SharedLibraryCore/Commands/NativeCommands.cs
index 42f179dab..99094f46c 100644
--- a/SharedLibraryCore/Commands/NativeCommands.cs
+++ b/SharedLibraryCore/Commands/NativeCommands.cs
@@ -773,12 +773,12 @@ namespace SharedLibraryCore.Commands
else if (unflagEvent.FailReason == GameEvent.EventFailReason.Invalid)
{
- E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"]} ^5{E.Target.Name}");
+ E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_NOTFLAGGED"]);
}
else
{
- E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_NOTFLAGGED"]);
+ E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"]} ^5{E.Target.Name}");
}
return Task.CompletedTask;
@@ -825,6 +825,11 @@ namespace SharedLibraryCore.Commands
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_SELF"]);
}
+ else if (reportEvent.FailReason == GameEvent.EventFailReason.Throttle)
+ {
+ commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_TOOMANY"]);
+ }
+
else if (reportEvent.Failed)
{
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_DUPLICATE"]);
@@ -1324,6 +1329,7 @@ namespace SharedLibraryCore.Commands
var nextMapMatch = currentMap.First().Index != lastMap.Index ?
regexMatches[regexMatches.IndexOf(currentMap.First()) + 1] :
regexMatches.First();
+
nextMap = s.Maps.FirstOrDefault(m => m.Name == nextMapMatch.Groups[3].ToString()) ?? nextMap;
string nextGametype = nextMapMatch.Groups[2].ToString().Length == 0 ?
Utilities.GetLocalizedGametype(s.Gametype) :
diff --git a/SharedLibraryCore/Events/GameEvent.cs b/SharedLibraryCore/Events/GameEvent.cs
index 5edd1683c..3cc639957 100644
--- a/SharedLibraryCore/Events/GameEvent.cs
+++ b/SharedLibraryCore/Events/GameEvent.cs
@@ -26,7 +26,11 @@ namespace SharedLibraryCore
///
/// executing the event would cause an invalid state
///
- Invalid
+ Invalid,
+ ///
+ /// client is doing too much of something
+ ///
+ Throttle
}
public enum EventType
diff --git a/SharedLibraryCore/Objects/Player.cs b/SharedLibraryCore/Objects/Player.cs
index 43fb55e58..568f26839 100644
--- a/SharedLibraryCore/Objects/Player.cs
+++ b/SharedLibraryCore/Objects/Player.cs
@@ -81,7 +81,10 @@ namespace SharedLibraryCore.Objects
ConnectionTime = DateTime.UtcNow;
ClientNumber = -1;
DelayedEvents = new Queue();
- _additionalProperties = new Dictionary();
+ _additionalProperties = new Dictionary
+ {
+ { "_reportCount", 0 }
+ };
}
public override string ToString() => $"{Name}::{NetworkId}";
@@ -116,19 +119,22 @@ namespace SharedLibraryCore.Objects
{
Type = GameEvent.EventType.Warn,
Message = warnReason,
+ Data = warnReason,
Origin = sender,
Target = this,
Owner = sender.CurrentServer
};
// enforce level restrictions
- if (sender.Level <= this.Level)
+ if (this.Level > sender.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
- return e;
}
- this.Warnings++;
+ else
+ {
+ this.Warnings++;
+ }
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
@@ -181,6 +187,8 @@ namespace SharedLibraryCore.Objects
Owner = sender.CurrentServer
};
+ int reportCount = sender.GetAdditionalProperty("_reportCount");
+
if (this.Level > sender.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
@@ -191,12 +199,18 @@ namespace SharedLibraryCore.Objects
e.FailReason = GameEvent.EventFailReason.Invalid;
}
+ else if (reportCount > 2)
+ {
+ e.FailReason = GameEvent.EventFailReason.Throttle;
+ }
+
else if (CurrentServer.Reports.Count(report => (report.Origin.NetworkId == sender.NetworkId &&
report.Target.NetworkId == this.NetworkId)) > 0)
{
e.FailReason = GameEvent.EventFailReason.Exception;
}
+ sender.SetAdditionalProperty("_reportCount", reportCount + 1);
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
@@ -391,7 +405,18 @@ namespace SharedLibraryCore.Objects
[NotMapped]
Dictionary _additionalProperties;
public T GetAdditionalProperty(string name) => (T)_additionalProperties[name];
- public void SetAdditionalProperty(string name, object value) => _additionalProperties.Add(name, value);
+ public void SetAdditionalProperty(string name, object value)
+ {
+ if (_additionalProperties.ContainsKey(name))
+ {
+ _additionalProperties[name] = value;
+ }
+ else
+ {
+ _additionalProperties.Add(name, value);
+ }
+ }
+
[NotMapped]
public int ClientNumber { get; set; }
[NotMapped]
diff --git a/SharedLibraryCore/Server.cs b/SharedLibraryCore/Server.cs
index 7c7fe04c5..c61d2eaf7 100644
--- a/SharedLibraryCore/Server.cs
+++ b/SharedLibraryCore/Server.cs
@@ -209,7 +209,7 @@ namespace SharedLibraryCore
/// The person who banned the target
abstract protected Task Ban(String Reason, Player Target, Player Origin);
- abstract public Task Warn(String Reason, Player Target, Player Origin);
+ abstract protected Task Warn(String Reason, Player Target, Player Origin);
///
/// Unban a player by npID / GUID
diff --git a/SharedLibraryCore/Services/GenericRepository.cs b/SharedLibraryCore/Services/GenericRepository.cs
index 6ae7727bc..0fe272484 100644
--- a/SharedLibraryCore/Services/GenericRepository.cs
+++ b/SharedLibraryCore/Services/GenericRepository.cs
@@ -28,7 +28,7 @@ namespace SharedLibraryCore.Services
{
if (_context == null)
{
- _context = new DatabaseContext(ShouldTrack);
+ _context = new DatabaseContext(!ShouldTrack);
}
return _context;