players;
+ public int ID;
}
[Serializable]
@@ -825,6 +882,7 @@ namespace IW4MAdmin
public string penaltyType;
public string penaltyReason;
public string penaltyTime;
+ public string Expires;
}
[Serializable]
diff --git a/Admin/lib/SharedLibrary.dll b/Admin/lib/SharedLibrary.dll
index b819a852d..52329072a 100644
Binary files a/Admin/lib/SharedLibrary.dll and b/Admin/lib/SharedLibrary.dll differ
diff --git a/Admin/version.txt b/Admin/version.txt
index 9a0030d4d..dca5454e6 100644
--- a/Admin/version.txt
+++ b/Admin/version.txt
@@ -1,4 +1,11 @@
-VERSION 1.4
+Versino 1.5
+CHANGELOG:
+-added back player history graphs (past 12 hours every 15 minutes)
+-fixed issue with configurationmanager files and threading
+-servers on webfront listed in descending player count
+-fixed resolution of tempban times from console feedback
+
+VERSION 1.4
CHANGELOG:
-works: with COD, WaW, MW3, BO1 (preliminary without extensive testing)
-fixed the issue with webfront chat history
diff --git a/Admin/webfront/graph.html b/Admin/webfront/graph.html
deleted file mode 100644
index a1d26a701..000000000
--- a/Admin/webfront/graph.html
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-{{GRAPH}}
diff --git a/Admin/webfront/header.html b/Admin/webfront/header.html
index 989efcc04..aca2ff546 100644
--- a/Admin/webfront/header.html
+++ b/Admin/webfront/header.html
@@ -8,6 +8,7 @@
+
diff --git a/Admin/webfront/main.css b/Admin/webfront/main.css
index 111990e20..1020dbed8 100644
--- a/Admin/webfront/main.css
+++ b/Admin/webfront/main.css
@@ -15,7 +15,7 @@ div#header #navContainer .navEntry a { padding: 1.2vw; width: 4vw; }
div#header #navContainer .navEntry:hover { background-color: rgb(34, 34, 34); }
div#content { margin: 3em 10%; }
-div#content .serverContainer { background-color: #191919; margin: 2em 0; font-size: 1.25vw; }
+div#content .serverContainer { background-color: #191919; margin-top 0; margin-bottom: 0; font-size: 1.25vw; padding-bottom: 100px; }
div#content hr { border-width: 0; height: 0.25em; background-color: #007ACC; }
div#content .serverInfo { width: 100%; }
div#content .serverInfo .tableCell { padding: 0 0.5em; }
@@ -31,13 +31,15 @@ div#content .chatPlayerName { font-weight: bold; font-size: 1.1vw; color:#fff; p
div#content .chatPlayerMessage {font-size: 1.1vw; color: #fff; opacity: 1; }
div#content .playerPenalty, div#content .playerInfo { margin: 0 auto; padding: 1em 10px; background-color: #181818; width: calc(100% - 20px); }
-div#content .penaltyName { width: 14.28%; }
+div#content .penaltyName { width: 15%; }
div#content .penaltyName a:link, div#content .penaltyName a:visited, div#content .playerInfo a:link, div#content .playerInfo a:visited { color: rgb(0, 122, 204) !important; }
div#content .penaltyName a:hover, div#content .playerInfo a:hover { color: rgb(255, 255, 255) !important; opacity: 0.75; }
-div#content .penaltyTime { text-align: right; width: 12.5%; }
+div#content .penaltyTime { text-align: left; width:8%; }
+div#content .penaltyOrigin {width: 12%;}
+div#content .penaltyRemaining { text-align: right; width: 10%:}
div#content .playerPenalty .penaltyTime { opacity: 0.5; }
-div#content .penaltyType { width: 12.5%; }
-div#content .penaltyReason { width: 50%; }
+div#content .penaltyType { width: 10%; }
+div#content .penaltyReason { width: 45%; }
div#content .playerPenalty .tableCell { }
div#content .penaltyHeader, div#content .contentHeader { width: calc(100% - 20px); background-color: #007ACC; font-size: 15pt; padding: 0.5em 10px; }
div#content .alternate_1 { background-color: rgb(34, 34, 34); }
@@ -199,3 +201,5 @@ div#footer { position: fixed; bottom: 0.5em; right: 0.5em; opacity: 0.5; }
.admin-name a { font-size: 14pt; color: #007ACC !important; }
.admin-name a:hover { color: #fff !important; }
.clients { margin: 0.5em; }
+.canvasjs-chart-credit { display: none; }
+.player-history { margin-top: -100px; height: 100px; }
\ No newline at end of file
diff --git a/Admin/webfront/main.html b/Admin/webfront/main.html
index d17cdf0ae..cc58c003b 100644
--- a/Admin/webfront/main.html
+++ b/Admin/webfront/main.html
@@ -1,31 +1,101 @@
diff --git a/Admin/webfront/penalties.html b/Admin/webfront/penalties.html
index ffcedb978..4a7ffa75f 100644
--- a/Admin/webfront/penalties.html
+++ b/Admin/webfront/penalties.html
@@ -34,6 +34,7 @@ function getPenalties(from)
"+ penalty['penaltyReason'] + "
\
"+ getColorForLevel(penalty['adminLevel'], penalty['adminName']) + "
\
"+ penalty['penaltyTime'] + "
\
+ " + penalty['Expires'] + "
\
"
)
});
@@ -51,6 +52,7 @@ $( document ).ready(function() {
Reason
Admin
Time
+ Remaining
diff --git a/IW4MAdmin.sln b/IW4MAdmin.sln
index 16ba56c85..70259908c 100644
--- a/IW4MAdmin.sln
+++ b/IW4MAdmin.sln
@@ -39,6 +39,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.md = README.md
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Plugins\Tests\Tests.csproj", "{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -303,6 +305,38 @@ Global
{1479DE87-ACB5-4046-81C8-A0BA5041227D}.Release-Stable|x64.Build.0 = Release|Any CPU
{1479DE87-ACB5-4046-81C8-A0BA5041227D}.Release-Stable|x86.ActiveCfg = Release|Any CPU
{1479DE87-ACB5-4046-81C8-A0BA5041227D}.Release-Stable|x86.Build.0 = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Debug|x64.Build.0 = Debug|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Debug|x86.Build.0 = Debug|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release|x64.ActiveCfg = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release|x64.Build.0 = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release|x86.ActiveCfg = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release|x86.Build.0 = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release-Nightly|Any CPU.ActiveCfg = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release-Nightly|Any CPU.Build.0 = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release-Nightly|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release-Nightly|Mixed Platforms.Build.0 = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release-Nightly|x64.ActiveCfg = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release-Nightly|x64.Build.0 = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release-Nightly|x86.ActiveCfg = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release-Nightly|x86.Build.0 = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release-Stable|Any CPU.ActiveCfg = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release-Stable|Any CPU.Build.0 = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release-Stable|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release-Stable|Mixed Platforms.Build.0 = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release-Stable|x64.ActiveCfg = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release-Stable|x64.Build.0 = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release-Stable|x86.ActiveCfg = Release|Any CPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release-Stable|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -314,5 +348,6 @@ Global
{E46C85BD-A99C-484E-BCCE-0F1831C5925E} = {26E8B310-269E-46D4-A612-24601F16065F}
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650} = {26E8B310-269E-46D4-A612-24601F16065F}
{1479DE87-ACB5-4046-81C8-A0BA5041227D} = {26E8B310-269E-46D4-A612-24601F16065F}
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1} = {26E8B310-269E-46D4-A612-24601F16065F}
EndGlobalSection
EndGlobal
diff --git a/Plugins/Tests/Plugin.cs b/Plugins/Tests/Plugin.cs
new file mode 100644
index 000000000..2585566d7
--- /dev/null
+++ b/Plugins/Tests/Plugin.cs
@@ -0,0 +1,78 @@
+#if DEBUG
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using SharedLibrary;
+using SharedLibrary.Interfaces;
+using SharedLibrary.Helpers;
+
+namespace IW4MAdmin.Plugins
+{
+ public class Tests : IPlugin
+ {
+ public string Name => "Dev Tests";
+
+ public float Version => 0.1f;
+
+ public string Author => "RaidMax";
+
+ private static DateTime Interval;
+
+ public async Task OnEventAsync(Event E, Server S)
+ {
+ if (E.Type == Event.GType.Start)
+ {
+ #region PLAYER_HISTORY
+ var rand = new Random(GetHashCode());
+ var time = DateTime.UtcNow;
+
+ await Task.Run(() =>
+ {
+ if (S.PlayerHistory.Count > 0)
+ return;
+
+ while (S.PlayerHistory.Count < 48)
+ {
+ S.PlayerHistory.Enqueue(new PlayerHistory(time, rand.Next(7, 18)));
+ time = time.AddMinutes(15);
+ }
+ });
+ #endregion
+ }
+ }
+
+ public async Task OnLoadAsync()
+ {
+ Interval = DateTime.Now;
+ }
+
+ public async Task OnTickAsync(Server S)
+ {
+ if ((DateTime.Now - Interval).TotalSeconds > 5)
+ {
+ var rand = new Random();
+ int index = rand.Next(0, 17);
+ var p = new Player($"Test_{index}", "_test", index, (int)Player.Permission.User)
+ {
+ Ping = 1
+ };
+
+ p.SetIP("127.0.0.1");
+
+ if (S.Players.ElementAt(index) != null)
+ await S.RemovePlayer(index);
+ await S.AddPlayer(p);
+
+ Interval = DateTime.Now;
+ }
+ }
+
+ public async Task OnUnloadAsync()
+ {
+
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Plugins/Tests/Properties/AssemblyInfo.cs b/Plugins/Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..1d901a309
--- /dev/null
+++ b/Plugins/Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Tests")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("b8c2a759-8663-4f6f-9ba4-19595f5e12c1")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Plugins/Tests/Tests.csproj b/Plugins/Tests/Tests.csproj
new file mode 100644
index 000000000..3d11308fb
--- /dev/null
+++ b/Plugins/Tests/Tests.csproj
@@ -0,0 +1,56 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {B8C2A759-8663-4F6F-9BA4-19595F5E12C1}
+ Library
+ Properties
+ Tests
+ Tests
+ v4.5
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {d51eeceb-438a-47da-870f-7d7b41bc24d6}
+ SharedLibrary
+
+
+
+
+ copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)BUILD\plugins\"
+
+
\ No newline at end of file
diff --git a/SharedLibrary/Helpers/ConfigurationManager.cs b/SharedLibrary/Helpers/ConfigurationManager.cs
index 60c1500d8..fa111d1ec 100644
--- a/SharedLibrary/Helpers/ConfigurationManager.cs
+++ b/SharedLibrary/Helpers/ConfigurationManager.cs
@@ -1,35 +1,38 @@
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
namespace SharedLibrary.Helpers
{
public class ConfigurationManager
{
- Dictionary> ConfigurationSet;
+ ConcurrentDictionary> ConfigurationSet;
Type PluginType;
public ConfigurationManager(Type PluginType)
{
- ConfigurationSet = new Dictionary>();
+ ConfigurationSet = new ConcurrentDictionary>();
this.PluginType = PluginType;
}
public void AddConfiguration(Server S)
{
+ /* if (ConfigurationSet.ContainsKey(S.ToString()))
+ {
+ S.Logger.WriteWarning($"not adding server configuration for {S} as it already exists");
+ return;
+ }*/
+
try
{
var Config = Interfaces.Serialize>.Read($"config/{PluginType.ToString()}_{S.ToString()}.cfg");
- lock (ConfigurationSet)
- {
- ConfigurationSet.Add(S.ToString(), Config);
- }
+ ConfigurationSet.TryAdd(S.ToString(), Config);
}
catch (Exceptions.SerializeException)
{
- ConfigurationSet.Add(S.ToString(), new Dictionary());
+ ConfigurationSet.TryAdd(S.ToString(), new Dictionary());
}
-
}
public void AddProperty(Server S, KeyValuePair Property)
diff --git a/SharedLibrary/Helpers/PlayerHistory.cs b/SharedLibrary/Helpers/PlayerHistory.cs
index 427f4a119..7a5da5f19 100644
--- a/SharedLibrary/Helpers/PlayerHistory.cs
+++ b/SharedLibrary/Helpers/PlayerHistory.cs
@@ -4,12 +4,45 @@ namespace SharedLibrary.Helpers
{
public class PlayerHistory
{
- public PlayerHistory(DateTime w, int cNum)
+ public PlayerHistory(int cNum)
{
- When = w;
- Players = cNum;
+ DateTime t = DateTime.UtcNow;
+ When = new DateTime(t.Year, t.Month, t.Day, t.Hour, 5 * (int)Math.Round(t.Minute / 5.0), 0);
+ PlayerCount = cNum;
+ }
+
+#if DEBUG
+ public PlayerHistory(DateTime t, int cNum)
+ {
+ When = new DateTime(t.Year, t.Month, t.Day, t.Hour, 15 * (int)Math.Round(t.Minute / 15.0), 0);
+ PlayerCount = cNum;
+ }
+#endif
+
+ private DateTime When;
+ private int PlayerCount;
+
+ ///
+ /// Used by CanvasJS as a point on the x axis
+ ///
+ public double x
+ {
+ get
+ {
+ return (When - DateTime.MinValue).TotalSeconds;
+ }
+ }
+
+
+ ///
+ /// Used by CanvasJS as a point on the y axis
+ ///
+ public int y
+ {
+ get
+ {
+ return PlayerCount;
+ }
}
- public DateTime When { get; private set; }
- public int Players { get; private set; }
}
}
diff --git a/SharedLibrary/Interfaces/IManager.cs b/SharedLibrary/Interfaces/IManager.cs
index 99913c335..ad359af8f 100644
--- a/SharedLibrary/Interfaces/IManager.cs
+++ b/SharedLibrary/Interfaces/IManager.cs
@@ -17,5 +17,6 @@ namespace SharedLibrary.Interfaces
IList GetActiveClients();
IList GetAliasClients(Player player);
IList GetAliases(Player player);
+ IList GetPrivilegedClients();
}
}
diff --git a/SharedLibrary/RCON.cs b/SharedLibrary/RCON.cs
index b47e1480e..4abf57987 100644
--- a/SharedLibrary/RCON.cs
+++ b/SharedLibrary/RCON.cs
@@ -98,7 +98,7 @@ namespace SharedLibrary.Network
if (LineSplit.Length != 3)
{
- var e = new Exceptions.DvarException("DVAR does not exist");
+ var e = new Exceptions.DvarException($"DVAR \"{dvarName}\" does not exist");
e.Data["dvar_name"] = dvarName;
throw e;
}
diff --git a/SharedLibrary/Utilities.cs b/SharedLibrary/Utilities.cs
index 7048a55d7..c57ffcd58 100644
--- a/SharedLibrary/Utilities.cs
+++ b/SharedLibrary/Utilities.cs
@@ -293,16 +293,18 @@ namespace SharedLibrary
public static string TimeSpanText(this TimeSpan span)
{
- if (span.TotalMinutes < 6)
- return $"{span.Minutes} minutes";
- else if (span.TotalHours < 24)
- return $"{span.Hours} hours";
- else if (span.TotalDays < 7)
- return $"{span.Days} days";
- else if (span.TotalDays > 7 && span.TotalDays < 365)
- return $"{Math.Ceiling(span.Days / 7.0)} weeks";
- else if (span.TotalDays >= 365)
- return $"{Math.Ceiling(span.Days / 365.0)} years";
+ if (span.TotalMinutes < 60)
+ return $"{span.Minutes} minute(s)";
+ else if (span.Hours >= 1 && span.TotalHours < 24)
+ return $"{span.Hours} hour(s)";
+ else if (span.TotalDays >= 1 && span.TotalDays < 7)
+ return $"{span.Days} day(s)";
+ else if (span.TotalDays >= 7 && span.TotalDays < 365)
+ return $"{Math.Ceiling(span.Days / 7.0)} week(s)";
+ else if (span.TotalDays >= 365 && span.TotalDays < 36500)
+ return $"{Math.Ceiling(span.Days / 365.0)} year(s)";
+ else if (span.TotalDays >= 36500)
+ return "Forever";
return "1 hour";
}