Compare commits
55 Commits
2.2
...
2.3-prerel
Author | SHA1 | Date | |
---|---|---|---|
5d41059641 | |||
2e6889d9bb | |||
9c4d23f0b4 | |||
b4e3e8526a | |||
e3944fb8c2 | |||
40f1697c97 | |||
2bbf2988da | |||
5e36bf4316 | |||
a362caebac | |||
2260d8974d | |||
5e04274da6 | |||
6d2e6aee4f | |||
9e74c42246 | |||
dea5b3f954 | |||
7c6419a16a | |||
0194196a33 | |||
044991272f | |||
f3290cf066 | |||
29eedea093 | |||
ce02f5dd68 | |||
e6bfa408f8 | |||
0a1dc46760 | |||
97ba6aae2e | |||
a456fab0e5 | |||
3e5282df87 | |||
59e0072744 | |||
f1dd4f7c7f | |||
760d3026ce | |||
07df6dbf79 | |||
ca535019c6 | |||
e6154822f6 | |||
7a6dccc26a | |||
08c883e0ff | |||
aaf9eb09b6 | |||
7b75c35c9b | |||
07ec5cf52f | |||
cf5ee8765d | |||
9494a17997 | |||
5f4171ccf4 | |||
a10746d5ff | |||
8dca05a442 | |||
8aa0d204f4 | |||
12cf2e8247 | |||
b77bdbe793 | |||
4522992c0e | |||
9d6cbee69c | |||
abf0609e2e | |||
5ac8a55c72 | |||
9bdd7d1b8a | |||
ed83c4c011 | |||
d9d548ea18 | |||
1779bf821d | |||
d50e6c8030 | |||
a58726d872 | |||
dded60a6ef |
5
.gitignore
vendored
5
.gitignore
vendored
@ -229,4 +229,7 @@ bootstrap-custom.min.css
|
|||||||
/DiscordWebhook/env
|
/DiscordWebhook/env
|
||||||
/DiscordWebhook/config.dev.json
|
/DiscordWebhook/config.dev.json
|
||||||
/GameLogServer/env
|
/GameLogServer/env
|
||||||
launchSettings.json
|
launchSettings.json
|
||||||
|
/VpnDetectionPrivate.js
|
||||||
|
/Plugins/ScriptPlugins/VpnDetectionPrivate.js
|
||||||
|
**/Master/env_master
|
@ -8,9 +8,13 @@ namespace IW4MAdmin.Application.API.Master
|
|||||||
public class ApiServer
|
public class ApiServer
|
||||||
{
|
{
|
||||||
[JsonProperty("id")]
|
[JsonProperty("id")]
|
||||||
public int Id { get; set; }
|
public long Id { get; set; }
|
||||||
|
[JsonProperty("ip")]
|
||||||
|
public string IPAddress { get; set; }
|
||||||
[JsonProperty("port")]
|
[JsonProperty("port")]
|
||||||
public short Port { get; set; }
|
public short Port { get; set; }
|
||||||
|
[JsonProperty("version")]
|
||||||
|
public string Version { get; set; }
|
||||||
[JsonProperty("gametype")]
|
[JsonProperty("gametype")]
|
||||||
public string Gametype { get; set; }
|
public string Gametype { get; set; }
|
||||||
[JsonProperty("map")]
|
[JsonProperty("map")]
|
||||||
|
@ -39,12 +39,14 @@ namespace IW4MAdmin.Application.API.Master
|
|||||||
{
|
{
|
||||||
ClientNum = s.ClientNum,
|
ClientNum = s.ClientNum,
|
||||||
Game = s.GameName.ToString(),
|
Game = s.GameName.ToString(),
|
||||||
|
Version = s.Version,
|
||||||
Gametype = s.Gametype,
|
Gametype = s.Gametype,
|
||||||
Hostname = s.Hostname,
|
Hostname = s.Hostname,
|
||||||
Map = s.CurrentMap.Name,
|
Map = s.CurrentMap.Name,
|
||||||
MaxClientNum = s.MaxClients,
|
MaxClientNum = s.MaxClients,
|
||||||
Id = s.GetHashCode(),
|
Id = s.EndPoint,
|
||||||
Port = (short)s.GetPort()
|
Port = (short)s.GetPort(),
|
||||||
|
IPAddress = s.IP
|
||||||
}).ToList()
|
}).ToList()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
|
<RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion>
|
||||||
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
|
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
|
||||||
<PackageId>RaidMax.IW4MAdmin.Application</PackageId>
|
<PackageId>RaidMax.IW4MAdmin.Application</PackageId>
|
||||||
<Version>2.2</Version>
|
<Version>2.2.5.3</Version>
|
||||||
<Authors>RaidMax</Authors>
|
<Authors>RaidMax</Authors>
|
||||||
<Company>Forever None</Company>
|
<Company>Forever None</Company>
|
||||||
<Product>IW4MAdmin</Product>
|
<Product>IW4MAdmin</Product>
|
||||||
<Description>IW4MAdmin is a complete server administration tool for IW4x and most Call of Duty® dedicated server</Description>
|
<Description>IW4MAdmin is a complete server administration tool for IW4x and most Call of Duty® dedicated servers</Description>
|
||||||
<Copyright>2018</Copyright>
|
<Copyright>2019</Copyright>
|
||||||
<PackageLicenseUrl>https://github.com/RaidMax/IW4M-Admin/blob/master/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/RaidMax/IW4M-Admin/blob/master/LICENSE</PackageLicenseUrl>
|
||||||
<PackageProjectUrl>https://raidmax.org/IW4MAdmin</PackageProjectUrl>
|
<PackageProjectUrl>https://raidmax.org/IW4MAdmin</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://github.com/RaidMax/IW4M-Admin</RepositoryUrl>
|
<RepositoryUrl>https://github.com/RaidMax/IW4M-Admin</RepositoryUrl>
|
||||||
@ -25,14 +25,14 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="RestEase" Version="1.4.7" />
|
<PackageReference Include="RestEase" Version="1.4.7" />
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.0" />
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||||
<TieredCompilation>true</TieredCompilation>
|
<TieredCompilation>true</TieredCompilation>
|
||||||
<AssemblyVersion>2.2.0.0</AssemblyVersion>
|
<AssemblyVersion>2.2.5.3</AssemblyVersion>
|
||||||
<FileVersion>2.2.0.0</FileVersion>
|
<FileVersion>2.2.5.3</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -45,21 +45,6 @@
|
|||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Update="Properties\IW4MAdmin.en-US.Designer.cs">
|
|
||||||
<DesignTime>True</DesignTime>
|
|
||||||
<AutoGen>True</AutoGen>
|
|
||||||
<DependentUpon>IW4MAdmin.en-US.resx</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<EmbeddedResource Update="Properties\IW4MAdmin.en-US.resx">
|
|
||||||
<Generator>ResXFileCodeGenerator</Generator>
|
|
||||||
<LastGenOutput>IW4MAdmin.en-US.Designer.cs</LastGenOutput>
|
|
||||||
</EmbeddedResource>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="DefaultSettings.json">
|
<None Update="DefaultSettings.json">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
@ -79,7 +64,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
|
<PackageReference Update="Microsoft.NETCore.App" Version="2.2.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
using System;
|
using IW4MAdmin.Application.API.Master;
|
||||||
using System.Collections.Generic;
|
using IW4MAdmin.Application.EventParsers;
|
||||||
using System.Linq;
|
using IW4MAdmin.Application.Misc;
|
||||||
using System.Threading;
|
using IW4MAdmin.Application.RconParsers;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Text;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using SharedLibraryCore.Interfaces;
|
|
||||||
using SharedLibraryCore.Commands;
|
using SharedLibraryCore.Commands;
|
||||||
using SharedLibraryCore.Helpers;
|
|
||||||
using SharedLibraryCore.Exceptions;
|
|
||||||
using SharedLibraryCore.Objects;
|
|
||||||
using SharedLibraryCore.Services;
|
|
||||||
using SharedLibraryCore.Configuration;
|
using SharedLibraryCore.Configuration;
|
||||||
using SharedLibraryCore.Database;
|
using SharedLibraryCore.Database;
|
||||||
|
using SharedLibraryCore.Database.Models;
|
||||||
using SharedLibraryCore.Events;
|
using SharedLibraryCore.Events;
|
||||||
|
using SharedLibraryCore.Exceptions;
|
||||||
using IW4MAdmin.Application.API.Master;
|
using SharedLibraryCore.Helpers;
|
||||||
using IW4MAdmin.Application.Migration;
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using SharedLibraryCore.Services;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application
|
namespace IW4MAdmin.Application
|
||||||
{
|
{
|
||||||
@ -26,7 +26,7 @@ namespace IW4MAdmin.Application
|
|||||||
{
|
{
|
||||||
private List<Server> _servers;
|
private List<Server> _servers;
|
||||||
public List<Server> Servers => _servers.OrderByDescending(s => s.ClientNum).ToList();
|
public List<Server> Servers => _servers.OrderByDescending(s => s.ClientNum).ToList();
|
||||||
public Dictionary<int, Player> PrivilegedClients { get; set; }
|
public Dictionary<int, EFClient> PrivilegedClients { get; set; }
|
||||||
public ILogger Logger => GetLogger(0);
|
public ILogger Logger => GetLogger(0);
|
||||||
public bool Running { get; private set; }
|
public bool Running { get; private set; }
|
||||||
public bool IsInitialized { get; private set; }
|
public bool IsInitialized { get; private set; }
|
||||||
@ -37,6 +37,13 @@ namespace IW4MAdmin.Application
|
|||||||
public DateTime StartTime { get; private set; }
|
public DateTime StartTime { get; private set; }
|
||||||
public string Version => Assembly.GetEntryAssembly().GetName().Version.ToString();
|
public string Version => Assembly.GetEntryAssembly().GetName().Version.ToString();
|
||||||
|
|
||||||
|
public IList<IRConParser> AdditionalRConParsers { get; }
|
||||||
|
public IList<IEventParser> AdditionalEventParsers { get; }
|
||||||
|
|
||||||
|
public ITokenAuthentication TokenAuthenticator => Authenticator;
|
||||||
|
|
||||||
|
public ITokenAuthentication Authenticator => _authenticator;
|
||||||
|
|
||||||
static ApplicationManager Instance;
|
static ApplicationManager Instance;
|
||||||
readonly List<AsyncStatus> TaskStatuses;
|
readonly List<AsyncStatus> TaskStatuses;
|
||||||
List<Command> Commands;
|
List<Command> Commands;
|
||||||
@ -49,7 +56,8 @@ namespace IW4MAdmin.Application
|
|||||||
ManualResetEventSlim OnQuit;
|
ManualResetEventSlim OnQuit;
|
||||||
readonly IPageList PageList;
|
readonly IPageList PageList;
|
||||||
readonly SemaphoreSlim ProcessingEvent = new SemaphoreSlim(1, 1);
|
readonly SemaphoreSlim ProcessingEvent = new SemaphoreSlim(1, 1);
|
||||||
readonly Dictionary<int, ILogger> Loggers = new Dictionary<int, ILogger>();
|
readonly Dictionary<long, ILogger> Loggers = new Dictionary<long, ILogger>();
|
||||||
|
readonly ITokenAuthentication _authenticator;
|
||||||
|
|
||||||
private ApplicationManager()
|
private ApplicationManager()
|
||||||
{
|
{
|
||||||
@ -60,13 +68,15 @@ namespace IW4MAdmin.Application
|
|||||||
ClientSvc = new ClientService();
|
ClientSvc = new ClientService();
|
||||||
AliasSvc = new AliasService();
|
AliasSvc = new AliasService();
|
||||||
PenaltySvc = new PenaltyService();
|
PenaltySvc = new PenaltyService();
|
||||||
PrivilegedClients = new Dictionary<int, Player>();
|
|
||||||
ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings");
|
ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings");
|
||||||
StartTime = DateTime.UtcNow;
|
StartTime = DateTime.UtcNow;
|
||||||
OnQuit = new ManualResetEventSlim();
|
OnQuit = new ManualResetEventSlim();
|
||||||
PageList = new PageList();
|
PageList = new PageList();
|
||||||
|
AdditionalEventParsers = new List<IEventParser>();
|
||||||
|
AdditionalRConParsers = new List<IRConParser>();
|
||||||
OnServerEvent += OnGameEvent;
|
OnServerEvent += OnGameEvent;
|
||||||
OnServerEvent += EventApi.OnGameEvent;
|
OnServerEvent += EventApi.OnGameEvent;
|
||||||
|
_authenticator = new TokenAuthentication();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnGameEvent(object sender, GameEventArgs args)
|
private async void OnGameEvent(object sender, GameEventArgs args)
|
||||||
@ -85,80 +95,11 @@ namespace IW4MAdmin.Application
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// if the origin client is not in an authorized state (detected by RCon) don't execute the event
|
await newEvent.Owner.ExecuteEvent(newEvent);
|
||||||
if (GameEvent.ShouldOriginEventBeDelayed(newEvent))
|
|
||||||
{
|
|
||||||
Logger.WriteDebug($"Delaying origin execution of event type {newEvent.Type} for {newEvent.Origin} because they are not authed");
|
|
||||||
if (newEvent.Type == GameEvent.EventType.Command)
|
|
||||||
{
|
|
||||||
newEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["SERVER_DELAYED_EVENT_WAIT"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// offload it to the player to keep
|
// save the event info to the database
|
||||||
newEvent.Origin.DelayedEvents.Enqueue(newEvent);
|
var changeHistorySvc = new ChangeHistoryService();
|
||||||
}
|
await changeHistorySvc.Add(args.Event);
|
||||||
|
|
||||||
// if the target client is not in an authorized state (detected by RCon) don't execute the event
|
|
||||||
else if (GameEvent.ShouldTargetEventBeDelayed(newEvent))
|
|
||||||
{
|
|
||||||
Logger.WriteDebug($"Delaying target execution of event type {newEvent.Type} for {newEvent.Target} because they are not authed");
|
|
||||||
// offload it to the player to keep
|
|
||||||
newEvent.Target.DelayedEvents.Enqueue(newEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
await newEvent.Owner.ExecuteEvent(newEvent);
|
|
||||||
|
|
||||||
// save the event info to the database
|
|
||||||
var changeHistorySvc = new ChangeHistoryService();
|
|
||||||
await changeHistorySvc.Add(args.Event);
|
|
||||||
|
|
||||||
// todo: this is a hacky mess
|
|
||||||
if (newEvent.Origin?.DelayedEvents.Count > 0 &&
|
|
||||||
(//newEvent.Origin?.State == Player.ClientState.Connected ||
|
|
||||||
newEvent.Type == GameEvent.EventType.Connect))
|
|
||||||
{
|
|
||||||
var events = newEvent.Origin.DelayedEvents;
|
|
||||||
|
|
||||||
// add the delayed event to the queue
|
|
||||||
while (events.Count > 0)
|
|
||||||
{
|
|
||||||
var oldEvent = events.Dequeue();
|
|
||||||
|
|
||||||
var e = new GameEvent()
|
|
||||||
{
|
|
||||||
Type = oldEvent.Type,
|
|
||||||
Origin = newEvent.Origin,
|
|
||||||
Data = oldEvent.Data,
|
|
||||||
Extra = oldEvent.Extra,
|
|
||||||
Owner = oldEvent.Owner,
|
|
||||||
Message = oldEvent.Message,
|
|
||||||
Target = oldEvent.Target,
|
|
||||||
Remote = oldEvent.Remote
|
|
||||||
};
|
|
||||||
|
|
||||||
e.Origin = newEvent.Origin;
|
|
||||||
// check if the target was assigned
|
|
||||||
if (e.Target != null)
|
|
||||||
{
|
|
||||||
// update the target incase they left or have newer info
|
|
||||||
e.Target = newEvent.Owner.GetPlayersAsList()
|
|
||||||
.FirstOrDefault(p => p.NetworkId == e.Target.NetworkId);
|
|
||||||
// we have to throw out the event because they left
|
|
||||||
if (e.Target == null)
|
|
||||||
{
|
|
||||||
Logger.WriteWarning($"Delayed event for {e.Origin} was ignored because the target has left");
|
|
||||||
// hack: don't do anything with the event because the target is invalid
|
|
||||||
e.Type = GameEvent.EventType.Unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Logger.WriteDebug($"Adding delayed event of type {e.Type} for {e.Origin} back for processing");
|
|
||||||
this.GetEventHandler().AddEvent(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Logger.WriteDebug($"Processed event with id {newEvent.Id}");
|
Logger.WriteDebug($"Processed event with id {newEvent.Id}");
|
||||||
@ -192,7 +133,7 @@ namespace IW4MAdmin.Application
|
|||||||
Logger.WriteDebug(ex.GetExceptionInfo());
|
Logger.WriteDebug(ex.GetExceptionInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
skip:
|
skip:
|
||||||
|
|
||||||
// tell anyone waiting for the output that we're done
|
// tell anyone waiting for the output that we're done
|
||||||
newEvent.OnProcessed.Set();
|
newEvent.OnProcessed.Set();
|
||||||
@ -216,7 +157,7 @@ namespace IW4MAdmin.Application
|
|||||||
public async Task UpdateServerStates()
|
public async Task UpdateServerStates()
|
||||||
{
|
{
|
||||||
// store the server hash code and task for it
|
// store the server hash code and task for it
|
||||||
var runningUpdateTasks = new Dictionary<int, Task>();
|
var runningUpdateTasks = new Dictionary<long, Task>();
|
||||||
|
|
||||||
while (Running)
|
while (Running)
|
||||||
{
|
{
|
||||||
@ -236,16 +177,16 @@ namespace IW4MAdmin.Application
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove the update tasks as they have completd
|
// remove the update tasks as they have completd
|
||||||
foreach (int serverId in serverTasksToRemove)
|
foreach (long serverId in serverTasksToRemove)
|
||||||
{
|
{
|
||||||
runningUpdateTasks.Remove(serverId);
|
runningUpdateTasks.Remove(serverId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// select the servers where the tasks have completed
|
// select the servers where the tasks have completed
|
||||||
var serverIds = Servers.Select(s => s.GetHashCode()).Except(runningUpdateTasks.Select(r => r.Key)).ToList();
|
var serverIds = Servers.Select(s => s.EndPoint).Except(runningUpdateTasks.Select(r => r.Key)).ToList();
|
||||||
foreach (var server in Servers.Where(s => serverIds.Contains(s.GetHashCode())))
|
foreach (var server in Servers.Where(s => serverIds.Contains(s.EndPoint)))
|
||||||
{
|
{
|
||||||
runningUpdateTasks.Add(server.GetHashCode(), Task.Run(async () =>
|
runningUpdateTasks.Add(server.EndPoint, Task.Run(async () =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -265,11 +206,7 @@ namespace IW4MAdmin.Application
|
|||||||
ThreadPool.GetAvailableThreads(out int availableThreads, out int m);
|
ThreadPool.GetAvailableThreads(out int availableThreads, out int m);
|
||||||
Logger.WriteDebug($"There are {workerThreads - availableThreads} active threading tasks");
|
Logger.WriteDebug($"There are {workerThreads - availableThreads} active threading tasks");
|
||||||
#endif
|
#endif
|
||||||
#if DEBUG
|
|
||||||
await Task.Delay(10000);
|
|
||||||
#else
|
|
||||||
await Task.Delay(ConfigHandler.Configuration().RConPollRate);
|
await Task.Delay(ConfigHandler.Configuration().RConPollRate);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// trigger the event processing loop to end
|
// trigger the event processing loop to end
|
||||||
@ -280,40 +217,21 @@ namespace IW4MAdmin.Application
|
|||||||
{
|
{
|
||||||
Running = true;
|
Running = true;
|
||||||
|
|
||||||
#region DATABASE
|
|
||||||
using (var db = new DatabaseContext(GetApplicationSettings().Configuration()?.ConnectionString, GetApplicationSettings().Configuration()?.DatabaseProvider))
|
|
||||||
{
|
|
||||||
await new ContextSeed(db).Seed();
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: optimize this (or replace it)
|
#region PLUGINS
|
||||||
var ipList = (await ClientSvc.Find(c => c.Level > Player.Permission.Trusted))
|
SharedLibraryCore.Plugins.PluginImporter.Load(this);
|
||||||
.Select(c => new
|
|
||||||
{
|
|
||||||
c.Password,
|
|
||||||
c.PasswordSalt,
|
|
||||||
c.ClientId,
|
|
||||||
c.Level,
|
|
||||||
c.Name
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach (var a in ipList)
|
foreach (var Plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PrivilegedClients.Add(a.ClientId, new Player()
|
await Plugin.OnLoadAsync(this);
|
||||||
{
|
|
||||||
Name = a.Name,
|
|
||||||
ClientId = a.ClientId,
|
|
||||||
Level = a.Level,
|
|
||||||
PasswordSalt = a.PasswordSalt,
|
|
||||||
Password = a.Password
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (ArgumentException)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
continue;
|
Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_PLUGIN"]} {Plugin.Name}");
|
||||||
|
Logger.WriteDebug(ex.GetExceptionInfo());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
@ -340,7 +258,18 @@ namespace IW4MAdmin.Application
|
|||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
newConfig.Servers.Add((ServerConfiguration)new ServerConfiguration().Generate());
|
var serverConfig = new ServerConfiguration();
|
||||||
|
foreach (var parser in AdditionalRConParsers)
|
||||||
|
{
|
||||||
|
serverConfig.AddRConParser(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var parser in AdditionalEventParsers)
|
||||||
|
{
|
||||||
|
serverConfig.AddEventParser(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
newConfig.Servers.Add((ServerConfiguration)serverConfig.Generate());
|
||||||
} while (Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationIndex["SETUP_SERVER_SAVE"]));
|
} while (Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationIndex["SETUP_SERVER_SAVE"]));
|
||||||
|
|
||||||
config = newConfig;
|
config = newConfig;
|
||||||
@ -361,36 +290,54 @@ namespace IW4MAdmin.Application
|
|||||||
config.WebfrontBindUrl = "http://0.0.0.0:1624";
|
config.WebfrontBindUrl = "http://0.0.0.0:1624";
|
||||||
await ConfigHandler.Save();
|
await ConfigHandler.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var serverConfig in config.Servers)
|
||||||
|
{
|
||||||
|
Migration.ConfigurationMigration.ModifyLogPath020919(serverConfig);
|
||||||
|
|
||||||
|
if (serverConfig.RConParserVersion == null || serverConfig.EventParserVersion == null)
|
||||||
|
{
|
||||||
|
foreach (var parser in AdditionalRConParsers)
|
||||||
|
{
|
||||||
|
serverConfig.AddRConParser(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var parser in AdditionalEventParsers)
|
||||||
|
{
|
||||||
|
serverConfig.AddEventParser(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
serverConfig.ModifyParsers();
|
||||||
|
}
|
||||||
|
await ConfigHandler.Save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (config.Servers.Count == 0)
|
else if (config.Servers.Count == 0)
|
||||||
|
{
|
||||||
throw new ServerException("A server configuration in IW4MAdminSettings.json is invalid");
|
throw new ServerException("A server configuration in IW4MAdminSettings.json is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||||
Utilities.EncodingType = Encoding.GetEncoding(!string.IsNullOrEmpty(config.CustomParserEncoding) ? config.CustomParserEncoding : "windows-1252");
|
Utilities.EncodingType = Encoding.GetEncoding(!string.IsNullOrEmpty(config.CustomParserEncoding) ? config.CustomParserEncoding : "windows-1252");
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
#region PLUGINS
|
|
||||||
SharedLibraryCore.Plugins.PluginImporter.Load(this);
|
|
||||||
|
|
||||||
foreach (var Plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
|
#region DATABASE
|
||||||
|
using (var db = new DatabaseContext(GetApplicationSettings().Configuration()?.ConnectionString,
|
||||||
|
GetApplicationSettings().Configuration()?.DatabaseProvider))
|
||||||
{
|
{
|
||||||
try
|
await new ContextSeed(db).Seed();
|
||||||
{
|
|
||||||
await Plugin.OnLoadAsync(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_PLUGIN"]} {Plugin.Name}");
|
|
||||||
Logger.WriteDebug(ex.GetExceptionInfo());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PrivilegedClients = (await ClientSvc.GetPrivilegedClients()).ToDictionary(_client => _client.ClientId);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region COMMANDS
|
#region COMMANDS
|
||||||
if (ClientSvc.GetOwners().Result.Count == 0)
|
if (ClientSvc.GetOwners().Result.Count == 0)
|
||||||
|
{
|
||||||
Commands.Add(new COwner());
|
Commands.Add(new COwner());
|
||||||
|
}
|
||||||
|
|
||||||
Commands.Add(new CQuit());
|
Commands.Add(new CQuit());
|
||||||
Commands.Add(new CKick());
|
Commands.Add(new CKick());
|
||||||
@ -429,9 +376,12 @@ namespace IW4MAdmin.Application
|
|||||||
Commands.Add(new CPing());
|
Commands.Add(new CPing());
|
||||||
Commands.Add(new CSetGravatar());
|
Commands.Add(new CSetGravatar());
|
||||||
Commands.Add(new CNextMap());
|
Commands.Add(new CNextMap());
|
||||||
|
Commands.Add(new RequestTokenCommand());
|
||||||
|
|
||||||
foreach (Command C in SharedLibraryCore.Plugins.PluginImporter.ActiveCommands)
|
foreach (Command C in SharedLibraryCore.Plugins.PluginImporter.ActiveCommands)
|
||||||
|
{
|
||||||
Commands.Add(C);
|
Commands.Add(C);
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region INIT
|
#region INIT
|
||||||
@ -466,7 +416,9 @@ namespace IW4MAdmin.Application
|
|||||||
{
|
{
|
||||||
Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_UNFIXABLE"]} [{Conf.IPAddress}:{Conf.Port}]");
|
Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_UNFIXABLE"]} [{Conf.IPAddress}:{Conf.Port}]");
|
||||||
if (e.GetType() == typeof(DvarException))
|
if (e.GetType() == typeof(DvarException))
|
||||||
|
{
|
||||||
Logger.WriteDebug($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_DVAR"]} {(e as DvarException).Data["dvar_name"]} ({Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_DVAR_HELP"]})");
|
Logger.WriteDebug($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_DVAR"]} {(e as DvarException).Data["dvar_name"]} ({Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_DVAR_HELP"]})");
|
||||||
|
}
|
||||||
else if (e.GetType() == typeof(NetworkException))
|
else if (e.GetType() == typeof(NetworkException))
|
||||||
{
|
{
|
||||||
Logger.WriteDebug(e.Message);
|
Logger.WriteDebug(e.Message);
|
||||||
@ -565,7 +517,7 @@ namespace IW4MAdmin.Application
|
|||||||
Running = false;
|
Running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ILogger GetLogger(int serverId)
|
public ILogger GetLogger(long serverId)
|
||||||
{
|
{
|
||||||
if (Loggers.ContainsKey(serverId))
|
if (Loggers.ContainsKey(serverId))
|
||||||
{
|
{
|
||||||
@ -595,23 +547,69 @@ namespace IW4MAdmin.Application
|
|||||||
return MessageTokens;
|
return MessageTokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList<Player> GetActiveClients() => _servers.SelectMany(s => s.Players).Where(p => p != null).ToList();
|
public IList<EFClient> GetActiveClients()
|
||||||
|
{
|
||||||
|
return _servers.SelectMany(s => s.Clients).Where(p => p != null).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
public ClientService GetClientService() => ClientSvc;
|
public ClientService GetClientService()
|
||||||
public AliasService GetAliasService() => AliasSvc;
|
{
|
||||||
public PenaltyService GetPenaltyService() => PenaltySvc;
|
return ClientSvc;
|
||||||
public IConfigurationHandler<ApplicationConfiguration> GetApplicationSettings() => ConfigHandler;
|
}
|
||||||
public IDictionary<int, Player> GetPrivilegedClients() => PrivilegedClients;
|
|
||||||
public bool ShutdownRequested() => !Running;
|
public AliasService GetAliasService()
|
||||||
public IEventHandler GetEventHandler() => Handler;
|
{
|
||||||
|
return AliasSvc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PenaltyService GetPenaltyService()
|
||||||
|
{
|
||||||
|
return PenaltySvc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IConfigurationHandler<ApplicationConfiguration> GetApplicationSettings()
|
||||||
|
{
|
||||||
|
return ConfigHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDictionary<int, EFClient> GetPrivilegedClients()
|
||||||
|
{
|
||||||
|
return PrivilegedClients;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShutdownRequested()
|
||||||
|
{
|
||||||
|
return !Running;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEventHandler GetEventHandler()
|
||||||
|
{
|
||||||
|
return Handler;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetHasEvent()
|
public void SetHasEvent()
|
||||||
{
|
{
|
||||||
OnQuit.Set();
|
OnQuit.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList<Assembly> GetPluginAssemblies() => SharedLibraryCore.Plugins.PluginImporter.PluginAssemblies;
|
public IList<Assembly> GetPluginAssemblies()
|
||||||
|
{
|
||||||
|
return SharedLibraryCore.Plugins.PluginImporter.PluginAssemblies.Union(SharedLibraryCore.Plugins.PluginImporter.Assemblies).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
public IPageList GetPageList() => PageList;
|
public IPageList GetPageList()
|
||||||
|
{
|
||||||
|
return PageList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IRConParser GenerateDynamicRConParser()
|
||||||
|
{
|
||||||
|
return new DynamicRConParser();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEventParser GenerateDynamicEventParser()
|
||||||
|
{
|
||||||
|
return new DynamicEventParser();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,82 +0,0 @@
|
|||||||
using SharedLibraryCore.Interfaces;
|
|
||||||
using SharedLibraryCore.Objects;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.Core
|
|
||||||
{
|
|
||||||
class ClientAuthentication : IClientAuthentication
|
|
||||||
{
|
|
||||||
private Queue<Player> ClientAuthenticationQueue;
|
|
||||||
private Dictionary<long, Player> AuthenticatedClients;
|
|
||||||
|
|
||||||
public ClientAuthentication()
|
|
||||||
{
|
|
||||||
ClientAuthenticationQueue = new Queue<Player>();
|
|
||||||
AuthenticatedClients = new Dictionary<long, Player>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AuthenticateClients(IList<Player> clients)
|
|
||||||
{
|
|
||||||
// we need to un-auth all the clients that have disconnected
|
|
||||||
var clientNetworkIds = clients.Select(c => c.NetworkId);
|
|
||||||
var clientsToRemove = AuthenticatedClients.Keys.Where(c => !clientNetworkIds.Contains(c));
|
|
||||||
// remove them
|
|
||||||
foreach (long Id in clientsToRemove.ToList())
|
|
||||||
{
|
|
||||||
AuthenticatedClients.Remove(Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// loop through the polled clients to see if they've been authenticated yet
|
|
||||||
foreach (var client in clients)
|
|
||||||
{
|
|
||||||
// they've not been authenticated
|
|
||||||
if (!AuthenticatedClients.TryGetValue(client.NetworkId, out Player value))
|
|
||||||
{
|
|
||||||
// authenticate them
|
|
||||||
client.State = Player.ClientState.Authenticated;
|
|
||||||
AuthenticatedClients.Add(client.NetworkId, client);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// this update their ping
|
|
||||||
// todo: this seems kinda hacky
|
|
||||||
value.Ping = client.Ping;
|
|
||||||
value.Score = client.Score;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// empty out the queue of clients detected through log
|
|
||||||
while (ClientAuthenticationQueue.Count > 0)
|
|
||||||
{
|
|
||||||
// grab each client that's connected via log
|
|
||||||
var clientToAuthenticate = ClientAuthenticationQueue.Dequeue();
|
|
||||||
// if they're not already authed, auth them
|
|
||||||
if (!AuthenticatedClients.TryGetValue(clientToAuthenticate.NetworkId, out Player value))
|
|
||||||
{
|
|
||||||
// authenticate them
|
|
||||||
clientToAuthenticate.State = Player.ClientState.Authenticated;
|
|
||||||
AuthenticatedClients.Add(clientToAuthenticate.NetworkId, clientToAuthenticate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IList<Player> GetAuthenticatedClients()
|
|
||||||
{
|
|
||||||
if (AuthenticatedClients.Values.Count > 18)
|
|
||||||
{
|
|
||||||
Program.ServerManager.GetLogger(0).WriteError($"auth client count is {AuthenticatedClients.Values.Count}, this is bad");
|
|
||||||
return AuthenticatedClients.Values.Take(18).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
return AuthenticatedClients.Values.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RequestClientAuthentication(Player client)
|
|
||||||
{
|
|
||||||
ClientAuthenticationQueue.Enqueue(client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -242,12 +242,12 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
"Name": "Village",
|
"Name": "Village",
|
||||||
"Alias": "co_hunted"
|
"Alias": "co_hunted"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Game": "T6M",
|
"Game": "T6",
|
||||||
"Maps": [
|
"Maps": [
|
||||||
{
|
{
|
||||||
"Alias": "Aftermath",
|
"Alias": "Aftermath",
|
||||||
@ -402,6 +402,95 @@
|
|||||||
"Name": "zm_transit"
|
"Name": "zm_transit"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Game": "IW3",
|
||||||
|
"Maps": [
|
||||||
|
{
|
||||||
|
"Alias": "Ambush",
|
||||||
|
"Name": "mp_convoy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Backlot",
|
||||||
|
"Name": "mp_backlot"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Bloc",
|
||||||
|
"Name": "mp_bloc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Bog",
|
||||||
|
"Name": "mp_bog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Countdown",
|
||||||
|
"Name": "mp_countdown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Crash",
|
||||||
|
"Name": "mp_crash"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Crossfire",
|
||||||
|
"Name": "mp_crossfire"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "District",
|
||||||
|
"Name": "mp_citystreets"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Downpour",
|
||||||
|
"Name": "mp_farm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Overgrown",
|
||||||
|
"Name": "mp_overgrown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Pipeline",
|
||||||
|
"Name": "mp_pipeline"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Shipment",
|
||||||
|
"Name": "mp_shipment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Showdown",
|
||||||
|
"Name": "mp_showdown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Strike",
|
||||||
|
"Name": "mp_strike"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Vacant",
|
||||||
|
"Name": "mp_vacant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Wet Work",
|
||||||
|
"Name": "mp_cargoship"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Winter Crash",
|
||||||
|
"Name": "mp_crash_snow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Broadcast",
|
||||||
|
"Name": "mp_broadcast"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Creek",
|
||||||
|
"Name": "mp_creek"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Chinatown",
|
||||||
|
"Name": "mp_carentan"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Killhouse",
|
||||||
|
"Name": "mp_killhouse"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
306
Application/EventParsers/BaseEventParser.cs
Normal file
306
Application/EventParsers/BaseEventParser.cs
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Database.Models;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using static SharedLibraryCore.Server;
|
||||||
|
|
||||||
|
namespace IW4MAdmin.Application.EventParsers
|
||||||
|
{
|
||||||
|
class BaseEventParser : IEventParser
|
||||||
|
{
|
||||||
|
public BaseEventParser()
|
||||||
|
{
|
||||||
|
Configuration = new DynamicEventParserConfiguration()
|
||||||
|
{
|
||||||
|
GameDirectory = "main",
|
||||||
|
};
|
||||||
|
|
||||||
|
Configuration.Say.Pattern = @"^(say|sayteam);(-?[A-Fa-f0-9_]{8,32});([0-9]+);(.+);(.*)$";
|
||||||
|
Configuration.Say.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||||
|
Configuration.Say.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2);
|
||||||
|
Configuration.Say.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
|
||||||
|
Configuration.Say.AddMapping(ParserRegex.GroupType.OriginName, 4);
|
||||||
|
Configuration.Say.AddMapping(ParserRegex.GroupType.Message, 5);
|
||||||
|
|
||||||
|
Configuration.Quit.Pattern = @"^(Q);(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+);([0-9]+);(.*)$";
|
||||||
|
Configuration.Quit.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||||
|
Configuration.Quit.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2);
|
||||||
|
Configuration.Quit.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
|
||||||
|
Configuration.Quit.AddMapping(ParserRegex.GroupType.OriginName, 4);
|
||||||
|
|
||||||
|
Configuration.Join.Pattern = @"^(J);(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+);([0-9]+);(.*)$";
|
||||||
|
Configuration.Join.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||||
|
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2);
|
||||||
|
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
|
||||||
|
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginName, 4);
|
||||||
|
|
||||||
|
Configuration.Damage.Pattern = @"^(D);(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world);(.{1,24});(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world);(.{1,24})?;((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
|
||||||
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||||
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2);
|
||||||
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3);
|
||||||
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetTeam, 4);
|
||||||
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetName, 5);
|
||||||
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.OriginNetworkId, 6);
|
||||||
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.OriginClientNumber, 7);
|
||||||
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.OriginTeam, 8);
|
||||||
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.OriginName, 9);
|
||||||
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.Weapon, 10);
|
||||||
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.Damage, 11);
|
||||||
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.MeansOfDeath, 12);
|
||||||
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.HitLocation, 13);
|
||||||
|
|
||||||
|
Configuration.Kill.Pattern = @"^(K);(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world);(.{1,24});(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world);(.{1,24})?;((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
|
||||||
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||||
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2);
|
||||||
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3);
|
||||||
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetTeam, 4);
|
||||||
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetName, 5);
|
||||||
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.OriginNetworkId, 6);
|
||||||
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.OriginClientNumber, 7);
|
||||||
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.OriginTeam, 8);
|
||||||
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.OriginName, 9);
|
||||||
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.Weapon, 10);
|
||||||
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.Damage, 11);
|
||||||
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.MeansOfDeath, 12);
|
||||||
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.HitLocation, 13);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEventParserConfiguration Configuration { get; set; }
|
||||||
|
|
||||||
|
public string Version { get; set; } = "CoD";
|
||||||
|
|
||||||
|
public Game GameName { get; set; } = Game.COD;
|
||||||
|
|
||||||
|
public virtual GameEvent GetEvent(Server server, string logLine)
|
||||||
|
{
|
||||||
|
logLine = Regex.Replace(logLine, @"([0-9]+:[0-9]+ |^[0-9]+ )", "").Trim();
|
||||||
|
string[] lineSplit = logLine.Split(';');
|
||||||
|
string eventType = lineSplit[0];
|
||||||
|
|
||||||
|
if (eventType == "JoinTeam")
|
||||||
|
{
|
||||||
|
var origin = server.GetClientsAsList()
|
||||||
|
.FirstOrDefault(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||||
|
|
||||||
|
return new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.JoinTeam,
|
||||||
|
Data = logLine,
|
||||||
|
Origin = origin,
|
||||||
|
Owner = server
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType == "say" || eventType == "sayteam")
|
||||||
|
{
|
||||||
|
var matchResult = Regex.Match(logLine, Configuration.Say.Pattern);
|
||||||
|
|
||||||
|
if (matchResult.Success)
|
||||||
|
{
|
||||||
|
string message = matchResult
|
||||||
|
.Groups[Configuration.Say.GroupMapping[ParserRegex.GroupType.Message]]
|
||||||
|
.ToString()
|
||||||
|
.Replace("\x15", "")
|
||||||
|
.Trim();
|
||||||
|
|
||||||
|
var origin = server.GetClientsAsList()
|
||||||
|
.First(c => c.NetworkId == matchResult.Groups[Configuration.Say.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertLong());
|
||||||
|
|
||||||
|
if (message[0] == '!' || message[0] == '@')
|
||||||
|
{
|
||||||
|
return new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.Command,
|
||||||
|
Data = message,
|
||||||
|
Origin = origin,
|
||||||
|
Owner = server,
|
||||||
|
Message = message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.Say,
|
||||||
|
Data = message,
|
||||||
|
Origin = origin,
|
||||||
|
Owner = server,
|
||||||
|
Message = message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType == "K")
|
||||||
|
{
|
||||||
|
if (!server.CustomCallback)
|
||||||
|
{
|
||||||
|
var match = Regex.Match(logLine, Configuration.Kill.Pattern);
|
||||||
|
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
var origin = server.GetClientsAsList()
|
||||||
|
.First(c => c.NetworkId == match.Groups[Configuration.Kill.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertLong());
|
||||||
|
var target = server.GetClientsAsList()
|
||||||
|
.First(c => c.NetworkId == match.Groups[Configuration.Kill.GroupMapping[ParserRegex.GroupType.TargetNetworkId]].ToString().ConvertLong());
|
||||||
|
|
||||||
|
|
||||||
|
return new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.Kill,
|
||||||
|
Data = logLine,
|
||||||
|
Origin = origin,
|
||||||
|
Target = target,
|
||||||
|
Owner = server
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType == "ScriptKill")
|
||||||
|
{
|
||||||
|
var origin = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||||
|
var target = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong());
|
||||||
|
return new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.ScriptKill,
|
||||||
|
Data = logLine,
|
||||||
|
Origin = origin,
|
||||||
|
Target = target,
|
||||||
|
Owner = server
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType == "ScriptDamage")
|
||||||
|
{
|
||||||
|
var origin = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||||
|
var target = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong());
|
||||||
|
|
||||||
|
return new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.ScriptDamage,
|
||||||
|
Data = logLine,
|
||||||
|
Origin = origin,
|
||||||
|
Target = target,
|
||||||
|
Owner = server
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// damage
|
||||||
|
if (eventType == "D")
|
||||||
|
{
|
||||||
|
if (!server.CustomCallback)
|
||||||
|
{
|
||||||
|
var regexMatch = Regex.Match(logLine, Configuration.Damage.Pattern);
|
||||||
|
|
||||||
|
if (regexMatch.Success)
|
||||||
|
{
|
||||||
|
var origin = server.GetClientsAsList()
|
||||||
|
.First(c => c.NetworkId == regexMatch.Groups[Configuration.Damage.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertLong());
|
||||||
|
var target = server.GetClientsAsList()
|
||||||
|
.First(c => c.NetworkId == regexMatch.Groups[Configuration.Damage.GroupMapping[ParserRegex.GroupType.TargetNetworkId]].ToString().ConvertLong());
|
||||||
|
|
||||||
|
return new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.Damage,
|
||||||
|
Data = logLine,
|
||||||
|
Origin = origin,
|
||||||
|
Target = target,
|
||||||
|
Owner = server
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// join
|
||||||
|
if (eventType == "J")
|
||||||
|
{
|
||||||
|
var regexMatch = Regex.Match(logLine, Configuration.Join.Pattern);
|
||||||
|
if (regexMatch.Success)
|
||||||
|
{
|
||||||
|
bool isBot = regexMatch.Groups[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().Contains("bot");
|
||||||
|
|
||||||
|
return new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.PreConnect,
|
||||||
|
Data = logLine,
|
||||||
|
Owner = server,
|
||||||
|
Origin = new EFClient()
|
||||||
|
{
|
||||||
|
CurrentAlias = new EFAlias()
|
||||||
|
{
|
||||||
|
Active = false,
|
||||||
|
Name = regexMatch.Groups[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginName]].ToString().StripColors(),
|
||||||
|
},
|
||||||
|
NetworkId = regexMatch.Groups[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertLong(),
|
||||||
|
ClientNumber = Convert.ToInt32(regexMatch.Groups[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginClientNumber]].ToString()),
|
||||||
|
State = EFClient.ClientState.Connecting,
|
||||||
|
CurrentServer = server,
|
||||||
|
IsBot = isBot
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType == "Q")
|
||||||
|
{
|
||||||
|
var regexMatch = Regex.Match(logLine, Configuration.Quit.Pattern);
|
||||||
|
if (regexMatch.Success)
|
||||||
|
{
|
||||||
|
return new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.PreDisconnect,
|
||||||
|
Data = logLine,
|
||||||
|
Owner = server,
|
||||||
|
Origin = new EFClient()
|
||||||
|
{
|
||||||
|
CurrentAlias = new EFAlias()
|
||||||
|
{
|
||||||
|
Active = false,
|
||||||
|
Name = regexMatch.Groups[Configuration.Quit.GroupMapping[ParserRegex.GroupType.OriginName]].ToString().StripColors()
|
||||||
|
},
|
||||||
|
NetworkId = regexMatch.Groups[Configuration.Quit.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertLong(),
|
||||||
|
ClientNumber = Convert.ToInt32(regexMatch.Groups[Configuration.Quit.GroupMapping[ParserRegex.GroupType.OriginClientNumber]].ToString()),
|
||||||
|
State = EFClient.ClientState.Disconnecting
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType.Contains("ExitLevel"))
|
||||||
|
{
|
||||||
|
return new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.MapEnd,
|
||||||
|
Data = lineSplit[0],
|
||||||
|
Origin = Utilities.IW4MAdminClient(server),
|
||||||
|
Target = Utilities.IW4MAdminClient(server),
|
||||||
|
Owner = server
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType.Contains("InitGame"))
|
||||||
|
{
|
||||||
|
string dump = eventType.Replace("InitGame: ", "");
|
||||||
|
|
||||||
|
return new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.MapChange,
|
||||||
|
Data = lineSplit[0],
|
||||||
|
Origin = Utilities.IW4MAdminClient(server),
|
||||||
|
Target = Utilities.IW4MAdminClient(server),
|
||||||
|
Owner = server,
|
||||||
|
Extra = dump.DictionaryFromKeyValue()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.Unknown,
|
||||||
|
Origin = Utilities.IW4MAdminClient(server),
|
||||||
|
Target = Utilities.IW4MAdminClient(server),
|
||||||
|
Owner = server
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
Application/EventParsers/DynamicEventParser.cs
Normal file
15
Application/EventParsers/DynamicEventParser.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using static SharedLibraryCore.Server;
|
||||||
|
|
||||||
|
namespace IW4MAdmin.Application.EventParsers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// empty generic implementation of the IEventParserConfiguration
|
||||||
|
/// allows script plugins to generate dynamic event parsers
|
||||||
|
/// </summary>
|
||||||
|
sealed internal class DynamicEventParser : BaseEventParser
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
19
Application/EventParsers/DynamicEventParserConfiguration.cs
Normal file
19
Application/EventParsers/DynamicEventParserConfiguration.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
|
||||||
|
namespace IW4MAdmin.Application.EventParsers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// generic implementation of the IEventParserConfiguration
|
||||||
|
/// allows script plugins to generate dynamic configurations
|
||||||
|
/// </summary>
|
||||||
|
sealed internal class DynamicEventParserConfiguration : IEventParserConfiguration
|
||||||
|
{
|
||||||
|
public string GameDirectory { get; set; }
|
||||||
|
public ParserRegex Say { get; set; } = new ParserRegex();
|
||||||
|
public ParserRegex Join { get; set; } = new ParserRegex();
|
||||||
|
public ParserRegex Quit { get; set; } = new ParserRegex();
|
||||||
|
public ParserRegex Kill { get; set; } = new ParserRegex();
|
||||||
|
public ParserRegex Damage { get; set; } = new ParserRegex();
|
||||||
|
public ParserRegex Action { get; set; } = new ParserRegex();
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.EventParsers
|
|
||||||
{
|
|
||||||
class IW3EventParser : IW4EventParser
|
|
||||||
{
|
|
||||||
public override string GetGameDir() => "main";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,239 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using SharedLibraryCore;
|
|
||||||
using SharedLibraryCore.Interfaces;
|
|
||||||
using SharedLibraryCore.Objects;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.EventParsers
|
|
||||||
{
|
|
||||||
class IW4EventParser : IEventParser
|
|
||||||
{
|
|
||||||
private const string SayRegex = @"(say|sayteam);(.{1,32});([0-9]+)(.*);(.*)";
|
|
||||||
|
|
||||||
public virtual GameEvent GetEvent(Server server, string logLine)
|
|
||||||
{
|
|
||||||
logLine = Regex.Replace(logLine, @"([0-9]+:[0-9]+ |^[0-9]+ )", "").Trim();
|
|
||||||
string[] lineSplit = logLine.Split(';');
|
|
||||||
string eventType = lineSplit[0];
|
|
||||||
|
|
||||||
if (eventType == "JoinTeam")
|
|
||||||
{
|
|
||||||
var origin = server.GetPlayersAsList().FirstOrDefault(c => c.NetworkId == lineSplit[1].ConvertLong());
|
|
||||||
|
|
||||||
return new GameEvent()
|
|
||||||
{
|
|
||||||
Type = GameEvent.EventType.JoinTeam,
|
|
||||||
Data = eventType,
|
|
||||||
Origin = origin,
|
|
||||||
Owner = server
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventType == "say" || eventType == "sayteam")
|
|
||||||
{
|
|
||||||
var matchResult = Regex.Match(logLine, SayRegex);
|
|
||||||
|
|
||||||
if (matchResult.Success)
|
|
||||||
{
|
|
||||||
string message = matchResult.Groups[5].ToString()
|
|
||||||
.Replace("\x15", "")
|
|
||||||
.Trim();
|
|
||||||
|
|
||||||
var origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2));
|
|
||||||
|
|
||||||
if (message[0] == '!' || message[0] == '@')
|
|
||||||
{
|
|
||||||
return new GameEvent()
|
|
||||||
{
|
|
||||||
Type = GameEvent.EventType.Command,
|
|
||||||
Data = message,
|
|
||||||
Origin = origin,
|
|
||||||
Owner = server,
|
|
||||||
Message = message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return new GameEvent()
|
|
||||||
{
|
|
||||||
Type = GameEvent.EventType.Say,
|
|
||||||
Data = message,
|
|
||||||
Origin = origin,
|
|
||||||
Owner = server,
|
|
||||||
Message = message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventType == "K")
|
|
||||||
{
|
|
||||||
if (!server.CustomCallback)
|
|
||||||
{
|
|
||||||
var origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 6));
|
|
||||||
var target = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2));
|
|
||||||
|
|
||||||
return new GameEvent()
|
|
||||||
{
|
|
||||||
Type = GameEvent.EventType.Kill,
|
|
||||||
Data = logLine,
|
|
||||||
Origin = origin,
|
|
||||||
Target = target,
|
|
||||||
Owner = server
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventType == "ScriptKill")
|
|
||||||
{
|
|
||||||
var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
|
||||||
var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong());
|
|
||||||
return new GameEvent()
|
|
||||||
{
|
|
||||||
Type = GameEvent.EventType.ScriptKill,
|
|
||||||
Data = logLine,
|
|
||||||
Origin = origin,
|
|
||||||
Target = target,
|
|
||||||
Owner = server
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventType == "ScriptDamage")
|
|
||||||
{
|
|
||||||
var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
|
||||||
var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong());
|
|
||||||
|
|
||||||
return new GameEvent()
|
|
||||||
{
|
|
||||||
Type = GameEvent.EventType.ScriptDamage,
|
|
||||||
Data = logLine,
|
|
||||||
Origin = origin,
|
|
||||||
Target = target,
|
|
||||||
Owner = server
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// damage
|
|
||||||
if (eventType == "D")
|
|
||||||
{
|
|
||||||
if (!server.CustomCallback)
|
|
||||||
{
|
|
||||||
if (Regex.Match(eventType, @"^(D);((?:bot[0-9]+)|(?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$").Success)
|
|
||||||
{
|
|
||||||
var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong());
|
|
||||||
var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
|
||||||
|
|
||||||
return new GameEvent()
|
|
||||||
{
|
|
||||||
Type = GameEvent.EventType.Damage,
|
|
||||||
Data = eventType,
|
|
||||||
Origin = origin,
|
|
||||||
Target = target,
|
|
||||||
Owner = server
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// join
|
|
||||||
if (eventType == "J")
|
|
||||||
{
|
|
||||||
var regexMatch = Regex.Match(logLine, @"^(J;)(.{1,32});([0-9]+);(.*)$");
|
|
||||||
if (regexMatch.Success)
|
|
||||||
{
|
|
||||||
return new GameEvent()
|
|
||||||
{
|
|
||||||
Type = GameEvent.EventType.Join,
|
|
||||||
Data = logLine,
|
|
||||||
Owner = server,
|
|
||||||
Origin = new Player()
|
|
||||||
{
|
|
||||||
Name = regexMatch.Groups[4].ToString().StripColors(),
|
|
||||||
NetworkId = regexMatch.Groups[2].ToString().ConvertLong(),
|
|
||||||
ClientNumber = Convert.ToInt32(regexMatch.Groups[3].ToString()),
|
|
||||||
State = Player.ClientState.Connecting,
|
|
||||||
CurrentServer = server
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//if (eventType == "Q")
|
|
||||||
//{
|
|
||||||
// var regexMatch = Regex.Match(logLine, @"^(Q;)(.{1,32});([0-9]+);(.*)$");
|
|
||||||
// if (regexMatch.Success)
|
|
||||||
// {
|
|
||||||
// return new GameEvent()
|
|
||||||
// {
|
|
||||||
// Type = GameEvent.EventType.Quit,
|
|
||||||
// Data = logLine,
|
|
||||||
// Owner = server,
|
|
||||||
// Origin = new Player()
|
|
||||||
// {
|
|
||||||
// Name = regexMatch.Groups[4].ToString().StripColors(),
|
|
||||||
// NetworkId = regexMatch.Groups[2].ToString().ConvertLong(),
|
|
||||||
// ClientNumber = Convert.ToInt32(regexMatch.Groups[3].ToString()),
|
|
||||||
// State = Player.ClientState.Connecting
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
if (eventType.Contains("ExitLevel"))
|
|
||||||
{
|
|
||||||
return new GameEvent()
|
|
||||||
{
|
|
||||||
Type = GameEvent.EventType.MapEnd,
|
|
||||||
Data = lineSplit[0],
|
|
||||||
Origin = new Player()
|
|
||||||
{
|
|
||||||
ClientId = 1
|
|
||||||
},
|
|
||||||
Target = new Player()
|
|
||||||
{
|
|
||||||
ClientId = 1
|
|
||||||
},
|
|
||||||
Owner = server
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventType.Contains("InitGame"))
|
|
||||||
{
|
|
||||||
string dump = eventType.Replace("InitGame: ", "");
|
|
||||||
|
|
||||||
return new GameEvent()
|
|
||||||
{
|
|
||||||
Type = GameEvent.EventType.MapChange,
|
|
||||||
Data = lineSplit[0],
|
|
||||||
Origin = new Player()
|
|
||||||
{
|
|
||||||
ClientId = 1
|
|
||||||
},
|
|
||||||
Target = new Player()
|
|
||||||
{
|
|
||||||
ClientId = 1
|
|
||||||
},
|
|
||||||
Owner = server,
|
|
||||||
Extra = dump.DictionaryFromKeyValue()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return new GameEvent()
|
|
||||||
{
|
|
||||||
Type = GameEvent.EventType.Unknown,
|
|
||||||
Origin = new Player()
|
|
||||||
{
|
|
||||||
ClientId = 1
|
|
||||||
},
|
|
||||||
Target = new Player()
|
|
||||||
{
|
|
||||||
ClientId = 1
|
|
||||||
},
|
|
||||||
Owner = server
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// other parsers can derive from this parser so we make it virtual
|
|
||||||
public virtual string GetGameDir() => "userraw";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
using SharedLibraryCore;
|
|
||||||
using SharedLibraryCore.Interfaces;
|
|
||||||
using SharedLibraryCore.Objects;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.EventParsers
|
|
||||||
{
|
|
||||||
class IW5EventParser : IW4EventParser
|
|
||||||
{
|
|
||||||
public override string GetGameDir() => "logs";
|
|
||||||
|
|
||||||
public override GameEvent GetEvent(Server server, string logLine)
|
|
||||||
{
|
|
||||||
string cleanedEventLine = Regex.Replace(logLine, @"[0-9]+:[0-9]+\ ", "").Trim();
|
|
||||||
|
|
||||||
if (cleanedEventLine.Contains("J;"))
|
|
||||||
{
|
|
||||||
string[] lineSplit = cleanedEventLine.Split(';');
|
|
||||||
|
|
||||||
int clientNum = Int32.Parse(lineSplit[2]);
|
|
||||||
|
|
||||||
var player = new Player()
|
|
||||||
{
|
|
||||||
NetworkId = lineSplit[1].ConvertLong(),
|
|
||||||
ClientNumber = clientNum,
|
|
||||||
Name = lineSplit[3]
|
|
||||||
};
|
|
||||||
|
|
||||||
return new GameEvent()
|
|
||||||
{
|
|
||||||
Type = GameEvent.EventType.Join,
|
|
||||||
Origin = new Player()
|
|
||||||
{
|
|
||||||
ClientId = 1
|
|
||||||
},
|
|
||||||
Target = new Player()
|
|
||||||
{
|
|
||||||
ClientId = 1
|
|
||||||
},
|
|
||||||
Owner = server,
|
|
||||||
Extra = player
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
return base.GetEvent(server, logLine);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.EventParsers
|
|
||||||
{
|
|
||||||
class T5MEventParser : IW4EventParser
|
|
||||||
{
|
|
||||||
public override string GetGameDir() => "v2";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using SharedLibraryCore;
|
|
||||||
using SharedLibraryCore.Interfaces;
|
|
||||||
using SharedLibraryCore.Objects;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.EventParsers
|
|
||||||
{
|
|
||||||
class T6MEventParser : IW4EventParser
|
|
||||||
{
|
|
||||||
public override string GetGameDir() => $"t6r{Path.DirectorySeparatorChar}data";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,6 @@
|
|||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -21,23 +19,14 @@ namespace IW4MAdmin.Application.IO
|
|||||||
public string ServerId { get; set; }
|
public string ServerId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameLogEventDetection(Server server, string gameLogPath, string gameLogName)
|
public GameLogEventDetection(Server server, string gameLogPath, Uri gameLogServerUri)
|
||||||
{
|
{
|
||||||
GameLogFile = gameLogPath;
|
GameLogFile = gameLogPath;
|
||||||
// todo: abtract this more
|
Reader = gameLogServerUri != null ? new GameLogReaderHttp(gameLogServerUri, gameLogPath, server.EventParser) : Reader = new GameLogReader(gameLogPath, server.EventParser);
|
||||||
if (gameLogPath.StartsWith("http"))
|
|
||||||
{
|
|
||||||
Reader = new GameLogReaderHttp(gameLogPath, server.EventParser);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Reader = new GameLogReader(gameLogPath, server.EventParser);
|
|
||||||
}
|
|
||||||
|
|
||||||
Server = server;
|
Server = server;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PollForChanges()
|
public async Task PollForChanges()
|
||||||
{
|
{
|
||||||
while (!Server.Manager.ShutdownRequested())
|
while (!Server.Manager.ShutdownRequested())
|
||||||
{
|
{
|
||||||
@ -45,12 +34,12 @@ namespace IW4MAdmin.Application.IO
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
UpdateLogEvents();
|
await UpdateLogEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Server.Logger.WriteWarning($"Failed to update log event for {Server.GetHashCode()}");
|
Server.Logger.WriteWarning($"Failed to update log event for {Server.EndPoint}");
|
||||||
Server.Logger.WriteDebug($"Exception: {e.Message}");
|
Server.Logger.WriteDebug($"Exception: {e.Message}");
|
||||||
Server.Logger.WriteDebug($"StackTrace: {e.StackTrace}");
|
Server.Logger.WriteDebug($"StackTrace: {e.StackTrace}");
|
||||||
}
|
}
|
||||||
@ -59,7 +48,7 @@ namespace IW4MAdmin.Application.IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateLogEvents()
|
private async Task UpdateLogEvents()
|
||||||
{
|
{
|
||||||
long fileSize = Reader.Length;
|
long fileSize = Reader.Length;
|
||||||
|
|
||||||
@ -74,11 +63,12 @@ namespace IW4MAdmin.Application.IO
|
|||||||
|
|
||||||
PreviousFileSize = fileSize;
|
PreviousFileSize = fileSize;
|
||||||
|
|
||||||
var events = Reader.ReadEventsFromLog(Server, fileDiff, 0);
|
var events = await Reader.ReadEventsFromLog(Server, fileDiff, 0);
|
||||||
|
|
||||||
foreach (var ev in events)
|
foreach (var ev in events)
|
||||||
{
|
{
|
||||||
Server.Manager.GetEventHandler().AddEvent(ev);
|
Server.Manager.GetEventHandler().AddEvent(ev);
|
||||||
|
await ev.WaitAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
PreviousFileSize = fileSize;
|
PreviousFileSize = fileSize;
|
||||||
|
@ -4,6 +4,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.IO
|
namespace IW4MAdmin.Application.IO
|
||||||
{
|
{
|
||||||
@ -22,7 +23,7 @@ namespace IW4MAdmin.Application.IO
|
|||||||
Parser = parser;
|
Parser = parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICollection<GameEvent> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
|
public async Task<ICollection<GameEvent>> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
|
||||||
{
|
{
|
||||||
// allocate the bytes for the new log lines
|
// allocate the bytes for the new log lines
|
||||||
List<string> logLines = new List<string>();
|
List<string> logLines = new List<string>();
|
||||||
@ -30,6 +31,7 @@ namespace IW4MAdmin.Application.IO
|
|||||||
// open the file as a stream
|
// open the file as a stream
|
||||||
using (var rd = new StreamReader(new FileStream(LogFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Utilities.EncodingType))
|
using (var rd = new StreamReader(new FileStream(LogFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Utilities.EncodingType))
|
||||||
{
|
{
|
||||||
|
// todo: max async
|
||||||
// take the old start position and go back the number of new characters
|
// take the old start position and go back the number of new characters
|
||||||
rd.BaseStream.Seek(-fileSizeDiff, SeekOrigin.End);
|
rd.BaseStream.Seek(-fileSizeDiff, SeekOrigin.End);
|
||||||
// the difference should be in the range of a int :P
|
// the difference should be in the range of a int :P
|
||||||
|
@ -5,6 +5,7 @@ using SharedLibraryCore.Interfaces;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using static SharedLibraryCore.Utilities;
|
using static SharedLibraryCore.Utilities;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.IO
|
namespace IW4MAdmin.Application.IO
|
||||||
@ -16,31 +17,32 @@ namespace IW4MAdmin.Application.IO
|
|||||||
{
|
{
|
||||||
readonly IEventParser Parser;
|
readonly IEventParser Parser;
|
||||||
readonly IGameLogServer Api;
|
readonly IGameLogServer Api;
|
||||||
readonly string LogFile;
|
readonly string logPath;
|
||||||
|
|
||||||
public GameLogReaderHttp(string logFile, IEventParser parser)
|
public GameLogReaderHttp(Uri gameLogServerUri, string logPath, IEventParser parser)
|
||||||
{
|
{
|
||||||
LogFile = logFile;
|
this.logPath = logPath.ToBase64UrlSafeString(); ;
|
||||||
Parser = parser;
|
Parser = parser;
|
||||||
Api = RestClient.For<IGameLogServer>(logFile);
|
Api = RestClient.For<IGameLogServer>(gameLogServerUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long Length => -1;
|
public long Length => -1;
|
||||||
|
|
||||||
public int UpdateInterval => 1000;
|
public int UpdateInterval => 350;
|
||||||
|
|
||||||
public ICollection<GameEvent> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
|
public async Task<ICollection<GameEvent>> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
|
||||||
{
|
{
|
||||||
#if DEBUG == true
|
#if DEBUG == true
|
||||||
server.Logger.WriteDebug($"Begin reading {fileSizeDiff} from http log");
|
server.Logger.WriteDebug($"Begin reading from http log");
|
||||||
#endif
|
#endif
|
||||||
var events = new List<GameEvent>();
|
var events = new List<GameEvent>();
|
||||||
string b64Path = server.LogPath.ToBase64UrlSafeString();
|
string b64Path = logPath;
|
||||||
var response = Api.Log(b64Path).Result;
|
var response = await Api.Log(b64Path);
|
||||||
|
|
||||||
if (!response.Success)
|
if (!response.Success)
|
||||||
{
|
{
|
||||||
server.Logger.WriteError($"Could not get log server info of {LogFile}/{b64Path} ({server.LogPath})");
|
server.Logger.WriteError($"Could not get log server info of {logPath}/{b64Path} ({server.LogPath})");
|
||||||
|
return events;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse each line
|
// parse each line
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,7 @@ namespace IW4MAdmin.Application.Localization
|
|||||||
{
|
{
|
||||||
public class Configure
|
public class Configure
|
||||||
{
|
{
|
||||||
public static void Initialize(string customLocale)
|
public static void Initialize(string customLocale = null)
|
||||||
{
|
{
|
||||||
string currentLocale = string.IsNullOrEmpty(customLocale) ? CultureInfo.CurrentCulture.Name : customLocale;
|
string currentLocale = string.IsNullOrEmpty(customLocale) ? CultureInfo.CurrentCulture.Name : customLocale;
|
||||||
string[] localizationFiles = Directory.GetFiles(Path.Join(Utilities.OperatingDirectory, "Localization"), $"*.{currentLocale}.json");
|
string[] localizationFiles = Directory.GetFiles(Path.Join(Utilities.OperatingDirectory, "Localization"), $"*.{currentLocale}.json");
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
using System;
|
using IW4MAdmin.Application.Migration;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.IO;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using SharedLibraryCore.Objects;
|
using SharedLibraryCore.Localization;
|
||||||
using SharedLibraryCore.Database;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Collections.Generic;
|
using System.Threading.Tasks;
|
||||||
using SharedLibraryCore.Localization;
|
|
||||||
using IW4MAdmin.Application.Migration;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Application
|
namespace IW4MAdmin.Application
|
||||||
{
|
{
|
||||||
@ -23,7 +18,6 @@ namespace IW4MAdmin.Application
|
|||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
AppDomain.CurrentDomain.SetData("DataDirectory", Utilities.OperatingDirectory);
|
AppDomain.CurrentDomain.SetData("DataDirectory", Utilities.OperatingDirectory);
|
||||||
|
|
||||||
Console.OutputEncoding = Encoding.UTF8;
|
Console.OutputEncoding = Encoding.UTF8;
|
||||||
Console.ForegroundColor = ConsoleColor.Gray;
|
Console.ForegroundColor = ConsoleColor.Gray;
|
||||||
|
|
||||||
@ -39,6 +33,18 @@ namespace IW4MAdmin.Application
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
ServerManager = ApplicationManager.GetInstance();
|
||||||
|
|
||||||
|
if (ServerManager.GetApplicationSettings().Configuration() != null)
|
||||||
|
{
|
||||||
|
Localization.Configure.Initialize(ServerManager.GetApplicationSettings().Configuration().CustomLocale);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Localization.Configure.Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
loc = Utilities.CurrentLocalization.LocalizationIndex;
|
loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||||
Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKey);
|
Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKey);
|
||||||
|
|
||||||
@ -47,10 +53,6 @@ namespace IW4MAdmin.Application
|
|||||||
// todo: move out
|
// todo: move out
|
||||||
ConfigurationMigration.MoveConfigFolder10518(null);
|
ConfigurationMigration.MoveConfigFolder10518(null);
|
||||||
|
|
||||||
ServerManager = ApplicationManager.GetInstance();
|
|
||||||
Localization.Configure.Initialize(ServerManager.GetApplicationSettings().Configuration()?.CustomLocale);
|
|
||||||
|
|
||||||
|
|
||||||
ServerManager.Logger.WriteInfo($"Version is {Version}");
|
ServerManager.Logger.WriteInfo($"Version is {Version}");
|
||||||
|
|
||||||
var api = API.Master.Endpoint.Get();
|
var api = API.Master.Endpoint.Get();
|
||||||
@ -111,15 +113,17 @@ namespace IW4MAdmin.Application
|
|||||||
|
|
||||||
var consoleTask = Task.Run(async () =>
|
var consoleTask = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
String userInput;
|
string userInput;
|
||||||
Player Origin = Utilities.IW4MAdminClient(ServerManager.Servers[0]);
|
var Origin = Utilities.IW4MAdminClient(ServerManager.Servers[0]);
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
userInput = Console.ReadLine();
|
userInput = Console.ReadLine();
|
||||||
|
|
||||||
if (userInput?.ToLower() == "quit")
|
if (userInput?.ToLower() == "quit")
|
||||||
|
{
|
||||||
ServerManager.Stop();
|
ServerManager.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
if (ServerManager.Servers.Count == 0)
|
if (ServerManager.Servers.Count == 0)
|
||||||
{
|
{
|
||||||
@ -148,13 +152,16 @@ namespace IW4MAdmin.Application
|
|||||||
|
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Console.WriteLine(loc["MANAGER_INIT_FAIL"]);
|
string failMessage = loc == null ? "Failed to initalize IW4MAdmin" : loc["MANAGER_INIT_FAIL"];
|
||||||
|
string exitMessage = loc == null ? "Press any key to exit..." : loc["MANAGER_EXIT"];
|
||||||
|
|
||||||
|
Console.WriteLine(failMessage);
|
||||||
while (e.InnerException != null)
|
while (e.InnerException != null)
|
||||||
{
|
{
|
||||||
e = e.InnerException;
|
e = e.InnerException;
|
||||||
}
|
}
|
||||||
Console.WriteLine(e.Message);
|
Console.WriteLine(e.Message);
|
||||||
Console.WriteLine(loc["MANAGER_EXIT"]);
|
Console.WriteLine(exitMessage);
|
||||||
Console.ReadKey();
|
Console.ReadKey();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -56,5 +56,14 @@ namespace IW4MAdmin.Application.Migration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ModifyLogPath020919(SharedLibraryCore.Configuration.ServerConfiguration config)
|
||||||
|
{
|
||||||
|
if (config.ManualLogPath.IsRemoteLog())
|
||||||
|
{
|
||||||
|
config.GameLogServerUrl = new Uri(config.ManualLogPath);
|
||||||
|
config.ManualLogPath = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
100
Application/Misc/TokenAuthentication.cs
Normal file
100
Application/Misc/TokenAuthentication.cs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
using SharedLibraryCore.Helpers;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace IW4MAdmin.Application.Misc
|
||||||
|
{
|
||||||
|
class TokenAuthentication : ITokenAuthentication
|
||||||
|
{
|
||||||
|
private readonly ConcurrentDictionary<long, TokenState> _tokens;
|
||||||
|
private readonly RNGCryptoServiceProvider _random;
|
||||||
|
private readonly static TimeSpan _timeoutPeriod = new TimeSpan(0, 0, 120);
|
||||||
|
private const short TOKEN_LENGTH = 4;
|
||||||
|
|
||||||
|
public TokenAuthentication()
|
||||||
|
{
|
||||||
|
_tokens = new ConcurrentDictionary<long, TokenState>();
|
||||||
|
_random = new RNGCryptoServiceProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AuthorizeToken(long networkId, string token)
|
||||||
|
{
|
||||||
|
bool authorizeSuccessful = _tokens.ContainsKey(networkId) && _tokens[networkId].Token == token;
|
||||||
|
|
||||||
|
if (authorizeSuccessful)
|
||||||
|
{
|
||||||
|
_tokens.TryRemove(networkId, out TokenState _);
|
||||||
|
}
|
||||||
|
|
||||||
|
return authorizeSuccessful;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenState GenerateNextToken(long networkId)
|
||||||
|
{
|
||||||
|
TokenState state = null;
|
||||||
|
|
||||||
|
if (_tokens.ContainsKey(networkId))
|
||||||
|
{
|
||||||
|
state = _tokens[networkId];
|
||||||
|
|
||||||
|
if ((DateTime.Now - state.RequestTime) > _timeoutPeriod)
|
||||||
|
{
|
||||||
|
_tokens.TryRemove(networkId, out TokenState _);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state = new TokenState()
|
||||||
|
{
|
||||||
|
NetworkId = networkId,
|
||||||
|
Token = _generateToken(),
|
||||||
|
TokenDuration = _timeoutPeriod
|
||||||
|
};
|
||||||
|
|
||||||
|
_tokens.TryAdd(networkId, state);
|
||||||
|
|
||||||
|
// perform some housekeeping so we don't have built up tokens if they're not ever used
|
||||||
|
foreach (var (key, value) in _tokens)
|
||||||
|
{
|
||||||
|
if ((DateTime.Now - value.RequestTime) > _timeoutPeriod)
|
||||||
|
{
|
||||||
|
_tokens.TryRemove(key, out TokenState _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string _generateToken()
|
||||||
|
{
|
||||||
|
bool validCharacter(char c)
|
||||||
|
{
|
||||||
|
// this ensure that the characters are 0-9, A-Z, a-z
|
||||||
|
return (c > 47 && c < 58) || (c > 64 && c < 91) || (c > 96 && c < 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder token = new StringBuilder();
|
||||||
|
|
||||||
|
while (token.Length < TOKEN_LENGTH)
|
||||||
|
{
|
||||||
|
byte[] charSet = new byte[1];
|
||||||
|
_random.GetBytes(charSet);
|
||||||
|
|
||||||
|
if (validCharacter((char)charSet[0]))
|
||||||
|
{
|
||||||
|
token.Append((char)charSet[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_random.Dispose();
|
||||||
|
return token.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
180
Application/RconParsers/BaseRConParser.cs
Normal file
180
Application/RconParsers/BaseRConParser.cs
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Database.Models;
|
||||||
|
using SharedLibraryCore.Exceptions;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using SharedLibraryCore.RCon;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using static SharedLibraryCore.Server;
|
||||||
|
|
||||||
|
namespace IW4MAdmin.Application.RconParsers
|
||||||
|
{
|
||||||
|
class BaseRConParser : IRConParser
|
||||||
|
{
|
||||||
|
public BaseRConParser()
|
||||||
|
{
|
||||||
|
Configuration = new DynamicRConParserConfiguration()
|
||||||
|
{
|
||||||
|
CommandPrefixes = new CommandPrefix()
|
||||||
|
{
|
||||||
|
Tell = "tell {0} {1}",
|
||||||
|
Say = "say {0}",
|
||||||
|
Kick = "clientkick {0} \"{1}\"",
|
||||||
|
Ban = "clientkick {0} \"{1}\"",
|
||||||
|
TempBan = "tempbanclient {0} \"{1}\"",
|
||||||
|
RConCommand = "ÿÿÿÿrcon {0} {1}",
|
||||||
|
RConGetDvar = "ÿÿÿÿrcon {0} {1}",
|
||||||
|
RConSetDvar = "ÿÿÿÿrcon {0} set {1}",
|
||||||
|
RConGetStatus = "ÿÿÿÿgetstatus",
|
||||||
|
RConGetInfo = "ÿÿÿÿgetinfo",
|
||||||
|
RConResponse = "ÿÿÿÿprint",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Configuration.Status.Pattern = @"^ *([0-9]+) +-?([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){8,32}|(?:[a-z]|[0-9]){8,32}|bot[0-9]+|(?:[0-9]+)) +(.{0,32}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback) +(-*[0-9]+) +([0-9]+) *$";
|
||||||
|
Configuration.Status.AddMapping(ParserRegex.GroupType.RConClientNumber, 1);
|
||||||
|
Configuration.Status.AddMapping(ParserRegex.GroupType.RConScore, 2);
|
||||||
|
Configuration.Status.AddMapping(ParserRegex.GroupType.RConPing, 3);
|
||||||
|
Configuration.Status.AddMapping(ParserRegex.GroupType.RConNetworkId, 4);
|
||||||
|
Configuration.Status.AddMapping(ParserRegex.GroupType.RConName, 5);
|
||||||
|
Configuration.Status.AddMapping(ParserRegex.GroupType.RConIpAddress, 7);
|
||||||
|
|
||||||
|
Configuration.Dvar.Pattern = "^\"(.+)\" is: \"(.+)?\" default: \"(.+)?\"\n(?:latched: \"(.+)?\"\n)? *(.+)$";
|
||||||
|
Configuration.Dvar.AddMapping(ParserRegex.GroupType.RConDvarName, 1);
|
||||||
|
Configuration.Dvar.AddMapping(ParserRegex.GroupType.RConDvarValue, 2);
|
||||||
|
Configuration.Dvar.AddMapping(ParserRegex.GroupType.RConDvarDefaultValue, 3);
|
||||||
|
Configuration.Dvar.AddMapping(ParserRegex.GroupType.RConDvarLatchedValue, 4);
|
||||||
|
Configuration.Dvar.AddMapping(ParserRegex.GroupType.RConDvarDomain, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IRConParserConfiguration Configuration { get; set; }
|
||||||
|
|
||||||
|
public string Version { get; set; } = "CoD";
|
||||||
|
public Game GameName { get; set; } = Game.COD;
|
||||||
|
public bool CanGenerateLogPath { get; set; } = true;
|
||||||
|
|
||||||
|
public async Task<string[]> ExecuteCommandAsync(Connection connection, string command)
|
||||||
|
{
|
||||||
|
var response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, command);
|
||||||
|
return response.Skip(1).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Dvar<T>> GetDvarAsync<T>(Connection connection, string dvarName)
|
||||||
|
{
|
||||||
|
string[] lineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.GET_DVAR, dvarName);
|
||||||
|
string response = string.Join('\n', lineSplit.Skip(1));
|
||||||
|
|
||||||
|
if (!lineSplit[0].Contains(Configuration.CommandPrefixes.RConResponse))
|
||||||
|
{
|
||||||
|
throw new DvarException($"Could not retrieve DVAR \"{dvarName}\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.Contains("Unknown command"))
|
||||||
|
{
|
||||||
|
throw new DvarException($"DVAR \"{dvarName}\" does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
var match = Regex.Match(response, Configuration.Dvar.Pattern);
|
||||||
|
|
||||||
|
if (!match.Success)
|
||||||
|
{
|
||||||
|
throw new DvarException($"Could not retrieve DVAR \"{dvarName}\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
string value = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarValue]].Value.StripColors();
|
||||||
|
string defaultValue = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarDefaultValue]].Value.StripColors();
|
||||||
|
string latchedValue = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarLatchedValue]].Value.StripColors();
|
||||||
|
|
||||||
|
return new Dvar<T>()
|
||||||
|
{
|
||||||
|
Name = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarName]].Value.StripColors(),
|
||||||
|
Value = string.IsNullOrEmpty(value) ? default(T) : (T)Convert.ChangeType(value, typeof(T)),
|
||||||
|
DefaultValue = string.IsNullOrEmpty(defaultValue) ? default(T) : (T)Convert.ChangeType(defaultValue, typeof(T)),
|
||||||
|
LatchedValue = string.IsNullOrEmpty(latchedValue) ? default(T) : (T)Convert.ChangeType(latchedValue, typeof(T)),
|
||||||
|
Domain = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarDomain]].Value.StripColors()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<EFClient>> GetStatusAsync(Connection connection)
|
||||||
|
{
|
||||||
|
string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND_STATUS);
|
||||||
|
return ClientsFromStatus(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SetDvarAsync(Connection connection, string dvarName, object dvarValue)
|
||||||
|
{
|
||||||
|
return (await connection.SendQueryAsync(StaticHelpers.QueryType.SET_DVAR, $"{dvarName} {dvarValue}")).Length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<EFClient> ClientsFromStatus(string[] Status)
|
||||||
|
{
|
||||||
|
List<EFClient> StatusPlayers = new List<EFClient>();
|
||||||
|
|
||||||
|
if (Status.Length < 4)
|
||||||
|
{
|
||||||
|
throw new ServerException("Unexpected status response received");
|
||||||
|
}
|
||||||
|
|
||||||
|
int validMatches = 0;
|
||||||
|
foreach (string S in Status)
|
||||||
|
{
|
||||||
|
string responseLine = S.Trim();
|
||||||
|
|
||||||
|
var regex = Regex.Match(responseLine, Configuration.Status.Pattern, RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
if (regex.Success)
|
||||||
|
{
|
||||||
|
validMatches++;
|
||||||
|
int clientNumber = int.Parse(regex.Groups[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConClientNumber]].Value);
|
||||||
|
int score = int.Parse(regex.Groups[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConScore]].Value);
|
||||||
|
|
||||||
|
int ping = 999;
|
||||||
|
|
||||||
|
// their state can be CNCT, ZMBI etc
|
||||||
|
if (regex.Groups[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConPing]].Value.Length <= 3)
|
||||||
|
{
|
||||||
|
ping = int.Parse(regex.Groups[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConPing]].Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
long networkId = regex.Groups[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConNetworkId]].Value.ConvertLong();
|
||||||
|
string name = regex.Groups[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConName]].Value.StripColors().Trim();
|
||||||
|
int? ip = regex.Groups[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConIpAddress]].Value.Split(':')[0].ConvertToIP();
|
||||||
|
|
||||||
|
var client = new EFClient()
|
||||||
|
{
|
||||||
|
CurrentAlias = new EFAlias()
|
||||||
|
{
|
||||||
|
Name = name
|
||||||
|
},
|
||||||
|
NetworkId = networkId,
|
||||||
|
ClientNumber = clientNumber,
|
||||||
|
IPAddress = ip,
|
||||||
|
Ping = ping,
|
||||||
|
Score = score,
|
||||||
|
IsBot = ip == null,
|
||||||
|
State = EFClient.ClientState.Connecting
|
||||||
|
};
|
||||||
|
|
||||||
|
// they've not fully connected yet
|
||||||
|
if (!client.IsBot && ping == 999)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusPlayers.Add(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this happens if status is requested while map is rotating
|
||||||
|
if (Status.Length > 5 && validMatches == 0)
|
||||||
|
{
|
||||||
|
throw new ServerException("Server is rotating map");
|
||||||
|
}
|
||||||
|
|
||||||
|
return StatusPlayers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
Application/RconParsers/DynamicRConParser.cs
Normal file
10
Application/RconParsers/DynamicRConParser.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace IW4MAdmin.Application.RconParsers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// empty implementation of the IW4RConParser
|
||||||
|
/// allows script plugins to generate dynamic RCon parsers
|
||||||
|
/// </summary>
|
||||||
|
sealed internal class DynamicRConParser : BaseRConParser
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
18
Application/RconParsers/DynamicRConParserConfiguration.cs
Normal file
18
Application/RconParsers/DynamicRConParserConfiguration.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using SharedLibraryCore.RCon;
|
||||||
|
|
||||||
|
namespace IW4MAdmin.Application.RconParsers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// generic implementation of the IRConParserConfiguration
|
||||||
|
/// allows script plugins to generate dynamic RCon configurations
|
||||||
|
/// </summary>
|
||||||
|
sealed internal class DynamicRConParserConfiguration : IRConParserConfiguration
|
||||||
|
{
|
||||||
|
public CommandPrefix CommandPrefixes { get; set; }
|
||||||
|
public ParserRegex Status { get; set; } = new ParserRegex();
|
||||||
|
public ParserRegex Dvar { get; set; } = new ParserRegex();
|
||||||
|
public bool WaitForResponse { get; set; } = true;
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
using SharedLibraryCore.RCon;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.RconParsers
|
|
||||||
{
|
|
||||||
class IW3RConParser : IW4RConParser
|
|
||||||
{
|
|
||||||
private static readonly CommandPrefix Prefixes = new CommandPrefix()
|
|
||||||
{
|
|
||||||
Tell = "tell {0} {1}",
|
|
||||||
Say = "say {0}",
|
|
||||||
Kick = "clientkick {0} \"{1}\"",
|
|
||||||
Ban = "clientkick {0} \"{1}\"",
|
|
||||||
TempBan = "tempbanclient {0} \"{1}\""
|
|
||||||
};
|
|
||||||
|
|
||||||
public override CommandPrefix GetCommandPrefixes() => Prefixes;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,133 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using SharedLibraryCore.Interfaces;
|
|
||||||
using SharedLibraryCore.Objects;
|
|
||||||
using SharedLibraryCore;
|
|
||||||
using SharedLibraryCore.RCon;
|
|
||||||
using SharedLibraryCore.Exceptions;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.RconParsers
|
|
||||||
{
|
|
||||||
class IW4RConParser : IRConParser
|
|
||||||
{
|
|
||||||
private static readonly CommandPrefix Prefixes = new CommandPrefix()
|
|
||||||
{
|
|
||||||
Tell = "tellraw {0} {1}",
|
|
||||||
Say = "sayraw {0}",
|
|
||||||
Kick = "clientkick {0} \"{1}\"",
|
|
||||||
Ban = "clientkick {0} \"{1}\"",
|
|
||||||
TempBan = "tempbanclient {0} \"{1}\""
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly string StatusRegex = @"^( *[0-9]+) +-*([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){16}|(?:[a-z]|[0-9]){32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback) +(-*[0-9]+) +([0-9]+) *$";
|
|
||||||
|
|
||||||
public async Task<string[]> ExecuteCommandAsync(Connection connection, string command)
|
|
||||||
{
|
|
||||||
var response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, command);
|
|
||||||
return response.Skip(1).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Dvar<T>> GetDvarAsync<T>(Connection connection, string dvarName)
|
|
||||||
{
|
|
||||||
string[] LineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.DVAR, dvarName);
|
|
||||||
|
|
||||||
if (LineSplit.Length < 3)
|
|
||||||
{
|
|
||||||
var e = new DvarException($"DVAR \"{dvarName}\" does not exist");
|
|
||||||
e.Data["dvar_name"] = dvarName;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
string[] ValueSplit = LineSplit[1].Split(new char[] { '"' }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
|
||||||
if (ValueSplit.Length < 5)
|
|
||||||
{
|
|
||||||
var e = new DvarException($"DVAR \"{dvarName}\" does not exist");
|
|
||||||
e.Data["dvar_name"] = dvarName;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
string DvarName = Regex.Replace(ValueSplit[0], @"\^[0-9]", "");
|
|
||||||
string DvarCurrentValue = Regex.Replace(ValueSplit[2], @"\^[0-9]", "");
|
|
||||||
string DvarDefaultValue = Regex.Replace(ValueSplit[4], @"\^[0-9]", "");
|
|
||||||
|
|
||||||
return new Dvar<T>(DvarName)
|
|
||||||
{
|
|
||||||
Value = (T)Convert.ChangeType(DvarCurrentValue, typeof(T))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<Player>> GetStatusAsync(Connection connection)
|
|
||||||
{
|
|
||||||
string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, "status");
|
|
||||||
return ClientsFromStatus(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> SetDvarAsync(Connection connection, string dvarName, object dvarValue)
|
|
||||||
{
|
|
||||||
return (await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, $"set {dvarName} {dvarValue}")).Length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual CommandPrefix GetCommandPrefixes() => Prefixes;
|
|
||||||
|
|
||||||
private List<Player> ClientsFromStatus(string[] Status)
|
|
||||||
{
|
|
||||||
List<Player> StatusPlayers = new List<Player>();
|
|
||||||
|
|
||||||
if (Status.Length < 4)
|
|
||||||
throw new ServerException("Unexpected status response received");
|
|
||||||
|
|
||||||
int validMatches = 0;
|
|
||||||
foreach (String S in Status)
|
|
||||||
{
|
|
||||||
String responseLine = S.Trim();
|
|
||||||
|
|
||||||
var regex = Regex.Match(responseLine, StatusRegex, RegexOptions.IgnoreCase);
|
|
||||||
|
|
||||||
if (regex.Success)
|
|
||||||
{
|
|
||||||
validMatches++;
|
|
||||||
int clientNumber = int.Parse(regex.Groups[1].Value);
|
|
||||||
int score = int.Parse(regex.Groups[2].Value);
|
|
||||||
|
|
||||||
int ping = 999;
|
|
||||||
|
|
||||||
// their state can be CNCT, ZMBI etc
|
|
||||||
if (regex.Groups[3].Value.Length <= 3)
|
|
||||||
{
|
|
||||||
ping = int.Parse(regex.Groups[3].Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
long networkId = regex.Groups[4].Value.ConvertLong();
|
|
||||||
string name = regex.Groups[5].Value.StripColors().Trim();
|
|
||||||
int ip = regex.Groups[7].Value.Split(':')[0].ConvertToIP();
|
|
||||||
|
|
||||||
Player P = new Player()
|
|
||||||
{
|
|
||||||
Name = name,
|
|
||||||
NetworkId = networkId,
|
|
||||||
ClientNumber = clientNumber,
|
|
||||||
IPAddress = ip == 0 ? int.MinValue : ip,
|
|
||||||
Ping = ping,
|
|
||||||
Score = score,
|
|
||||||
IsBot = ip == 0,
|
|
||||||
State = Player.ClientState.Connecting
|
|
||||||
};
|
|
||||||
StatusPlayers.Add(P);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this happens if status is requested while map is rotating
|
|
||||||
if (Status.Length > 5 && validMatches == 0)
|
|
||||||
{
|
|
||||||
throw new ServerException("Server is rotating map");
|
|
||||||
}
|
|
||||||
|
|
||||||
return StatusPlayers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,170 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
using SharedLibraryCore;
|
|
||||||
using SharedLibraryCore.Interfaces;
|
|
||||||
using SharedLibraryCore.Objects;
|
|
||||||
using SharedLibraryCore.RCon;
|
|
||||||
using SharedLibraryCore.Exceptions;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.RconParsers
|
|
||||||
{
|
|
||||||
public class IW5MRConParser : IRConParser
|
|
||||||
{
|
|
||||||
private static readonly CommandPrefix Prefixes = new CommandPrefix()
|
|
||||||
{
|
|
||||||
Tell = "tell {0} {1}",
|
|
||||||
Say = "say {0}",
|
|
||||||
Kick = "dropClient {0} \"{1}\"",
|
|
||||||
Ban = "dropClient {0} \"{1}\"",
|
|
||||||
TempBan = "dropClient {0} \"{1}\""
|
|
||||||
};
|
|
||||||
|
|
||||||
public CommandPrefix GetCommandPrefixes() => Prefixes;
|
|
||||||
|
|
||||||
public async Task<string[]> ExecuteCommandAsync(Connection connection, string command)
|
|
||||||
{
|
|
||||||
await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, command, false);
|
|
||||||
return new string[] { "Command Executed" };
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Dvar<T>> GetDvarAsync<T>(Connection connection, string dvarName)
|
|
||||||
{
|
|
||||||
// why can't this be real :(
|
|
||||||
if (dvarName == "version")
|
|
||||||
return new Dvar<T>(dvarName)
|
|
||||||
{
|
|
||||||
Value = (T)Convert.ChangeType("IW5 MP 1.9 build 461 Fri Sep 14 00:04:28 2012 win-x86", typeof(T))
|
|
||||||
};
|
|
||||||
|
|
||||||
if (dvarName == "shortversion")
|
|
||||||
return new Dvar<T>(dvarName)
|
|
||||||
{
|
|
||||||
Value = (T)Convert.ChangeType("1.9", typeof(T))
|
|
||||||
};
|
|
||||||
|
|
||||||
if (dvarName == "mapname")
|
|
||||||
return new Dvar<T>(dvarName)
|
|
||||||
{
|
|
||||||
Value = (T)Convert.ChangeType("Unknown", typeof(T))
|
|
||||||
};
|
|
||||||
|
|
||||||
if (dvarName == "g_gametype")
|
|
||||||
return new Dvar<T>(dvarName)
|
|
||||||
{
|
|
||||||
Value = (T)Convert.ChangeType("Unknown", typeof(T))
|
|
||||||
};
|
|
||||||
|
|
||||||
if (dvarName == "fs_game")
|
|
||||||
return new Dvar<T>(dvarName)
|
|
||||||
{
|
|
||||||
Value = (T)Convert.ChangeType("", typeof(T))
|
|
||||||
};
|
|
||||||
|
|
||||||
if (dvarName == "g_logsync")
|
|
||||||
return new Dvar<T>(dvarName)
|
|
||||||
{
|
|
||||||
Value = (T)Convert.ChangeType(1, typeof(T))
|
|
||||||
};
|
|
||||||
|
|
||||||
if (dvarName == "fs_basepath")
|
|
||||||
return new Dvar<T>(dvarName)
|
|
||||||
{
|
|
||||||
Value = (T)Convert.ChangeType("", typeof(T))
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
string[] LineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.DVAR, dvarName);
|
|
||||||
|
|
||||||
if (LineSplit.Length < 4)
|
|
||||||
{
|
|
||||||
var e = new DvarException($"DVAR \"{dvarName}\" does not exist");
|
|
||||||
e.Data["dvar_name"] = dvarName;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
string[] ValueSplit = LineSplit[1].Split(new char[] { '"' });
|
|
||||||
|
|
||||||
if (ValueSplit.Length == 0)
|
|
||||||
{
|
|
||||||
var e = new DvarException($"DVAR \"{dvarName}\" does not exist");
|
|
||||||
e.Data["dvar_name"] = dvarName;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
string DvarName = dvarName;
|
|
||||||
string DvarCurrentValue = Regex.Replace(ValueSplit[3].StripColors(), @"\^[0-9]", "");
|
|
||||||
|
|
||||||
return new Dvar<T>(DvarName)
|
|
||||||
{
|
|
||||||
Value = (T)Convert.ChangeType(DvarCurrentValue, typeof(T))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<Player>> GetStatusAsync(Connection connection)
|
|
||||||
{
|
|
||||||
string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, "status");
|
|
||||||
return ClientsFromStatus(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> SetDvarAsync(Connection connection, string dvarName, object dvarValue)
|
|
||||||
{
|
|
||||||
// T6M doesn't respond with anything when a value is set, so we can only hope for the best :c
|
|
||||||
await connection.SendQueryAsync(StaticHelpers.QueryType.DVAR, $"set {dvarName} {dvarValue}", false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Player> ClientsFromStatus(string[] status)
|
|
||||||
{
|
|
||||||
List<Player> StatusPlayers = new List<Player>();
|
|
||||||
|
|
||||||
foreach (string statusLine in status)
|
|
||||||
{
|
|
||||||
String responseLine = statusLine;
|
|
||||||
|
|
||||||
if (Regex.Matches(responseLine, @"^ *\d+", RegexOptions.IgnoreCase).Count > 0) // its a client line!
|
|
||||||
{
|
|
||||||
String[] playerInfo = responseLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
|
||||||
// this happens when the client is in a zombie state
|
|
||||||
if (playerInfo.Length < 5)
|
|
||||||
continue;
|
|
||||||
int clientId = -1;
|
|
||||||
int Ping = -1;
|
|
||||||
|
|
||||||
Int32.TryParse(playerInfo[2], out Ping);
|
|
||||||
string name = Encoding.UTF8.GetString(Encoding.Convert(Utilities.EncodingType, Encoding.UTF8, Utilities.EncodingType.GetBytes(responseLine.Substring(23, 15).StripColors().Trim())));
|
|
||||||
long networkId = 0;//playerInfo[4].ConvertLong();
|
|
||||||
int.TryParse(playerInfo[0], out clientId);
|
|
||||||
var regex = Regex.Match(responseLine, @"\d+\.\d+\.\d+.\d+\:\d{1,5}");
|
|
||||||
int ipAddress = regex.Value.Split(':')[0].ConvertToIP();
|
|
||||||
regex = Regex.Match(responseLine, @" +(\d+ +){3}");
|
|
||||||
int score = Int32.Parse(regex.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries)[0]);
|
|
||||||
|
|
||||||
var p = new Player()
|
|
||||||
{
|
|
||||||
Name = name,
|
|
||||||
NetworkId = networkId,
|
|
||||||
ClientNumber = clientId,
|
|
||||||
IPAddress = ipAddress,
|
|
||||||
Ping = Ping,
|
|
||||||
Score = score,
|
|
||||||
IsBot = false,
|
|
||||||
State = Player.ClientState.Connecting
|
|
||||||
};
|
|
||||||
|
|
||||||
StatusPlayers.Add(p);
|
|
||||||
|
|
||||||
if (p.IsBot)
|
|
||||||
p.NetworkId = -p.ClientNumber;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return StatusPlayers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,123 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using SharedLibraryCore;
|
|
||||||
using SharedLibraryCore.Interfaces;
|
|
||||||
using SharedLibraryCore.Objects;
|
|
||||||
using SharedLibraryCore.RCon;
|
|
||||||
using SharedLibraryCore.Exceptions;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.RconParsers
|
|
||||||
{
|
|
||||||
public class T6MRConParser : IRConParser
|
|
||||||
{
|
|
||||||
private static readonly CommandPrefix Prefixes = new CommandPrefix()
|
|
||||||
{
|
|
||||||
Tell = "tell {0} {1}",
|
|
||||||
Say = "say {0}",
|
|
||||||
Kick = "clientKick {0}",
|
|
||||||
Ban = "clientKick {0}",
|
|
||||||
TempBan = "clientKick {0}"
|
|
||||||
};
|
|
||||||
|
|
||||||
public CommandPrefix GetCommandPrefixes() => Prefixes;
|
|
||||||
|
|
||||||
public async Task<string[]> ExecuteCommandAsync(Connection connection, string command)
|
|
||||||
{
|
|
||||||
await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, command, false);
|
|
||||||
return new string[] { "Command Executed" };
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Dvar<T>> GetDvarAsync<T>(Connection connection, string dvarName)
|
|
||||||
{
|
|
||||||
string[] LineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, $"get {dvarName}");
|
|
||||||
|
|
||||||
if (LineSplit.Length < 2)
|
|
||||||
{
|
|
||||||
var e = new DvarException($"DVAR \"{dvarName}\" does not exist");
|
|
||||||
e.Data["dvar_name"] = dvarName;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
string[] ValueSplit = LineSplit[1].Split(new char[] { '"' });
|
|
||||||
|
|
||||||
if (ValueSplit.Length == 0)
|
|
||||||
{
|
|
||||||
var e = new DvarException($"DVAR \"{dvarName}\" does not exist");
|
|
||||||
e.Data["dvar_name"] = dvarName;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
string DvarName = dvarName;
|
|
||||||
string DvarCurrentValue = Regex.Replace(ValueSplit[1], @"\^[0-9]", "");
|
|
||||||
|
|
||||||
return new Dvar<T>(DvarName)
|
|
||||||
{
|
|
||||||
Value = (T)Convert.ChangeType(DvarCurrentValue, typeof(T))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<Player>> GetStatusAsync(Connection connection)
|
|
||||||
{
|
|
||||||
string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, "status");
|
|
||||||
return ClientsFromStatus(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> SetDvarAsync(Connection connection, string dvarName, object dvarValue)
|
|
||||||
{
|
|
||||||
// T6M doesn't respond with anything when a value is set, so we can only hope for the best :c
|
|
||||||
await connection.SendQueryAsync(StaticHelpers.QueryType.DVAR, $"set {dvarName} {dvarValue}", false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Player> ClientsFromStatus(string[] status)
|
|
||||||
{
|
|
||||||
List<Player> StatusPlayers = new List<Player>();
|
|
||||||
|
|
||||||
foreach (string statusLine in status)
|
|
||||||
{
|
|
||||||
String responseLine = statusLine;
|
|
||||||
|
|
||||||
if (Regex.Matches(responseLine, @"^ *\d+", RegexOptions.IgnoreCase).Count > 0) // its a client line!
|
|
||||||
{
|
|
||||||
String[] playerInfo = responseLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
int clientId = -1;
|
|
||||||
int Ping = -1;
|
|
||||||
|
|
||||||
Int32.TryParse(playerInfo[3], out Ping);
|
|
||||||
var regex = Regex.Match(responseLine, @"\^7.*\ +0 ");
|
|
||||||
string name = Encoding.UTF8.GetString(Encoding.Convert(Utilities.EncodingType, Encoding.UTF8, Utilities.EncodingType.GetBytes(regex.Value.Substring(0, regex.Value.Length - 2).StripColors().Trim())));
|
|
||||||
long networkId = playerInfo[4].ConvertLong();
|
|
||||||
int.TryParse(playerInfo[0], out clientId);
|
|
||||||
regex = Regex.Match(responseLine, @"\d+\.\d+\.\d+.\d+\:\d{1,5}");
|
|
||||||
#if DEBUG
|
|
||||||
Ping = 1;
|
|
||||||
#endif
|
|
||||||
int ipAddress = regex.Value.Split(':')[0].ConvertToIP();
|
|
||||||
regex = Regex.Match(responseLine, @"[0-9]{1,2}\s+[0-9]+\s+");
|
|
||||||
var p = new Player()
|
|
||||||
{
|
|
||||||
Name = name,
|
|
||||||
NetworkId = networkId,
|
|
||||||
ClientNumber = clientId,
|
|
||||||
IPAddress = ipAddress,
|
|
||||||
Ping = Ping,
|
|
||||||
Score = 0,
|
|
||||||
State = Player.ClientState.Connecting,
|
|
||||||
IsBot = networkId == 0
|
|
||||||
};
|
|
||||||
|
|
||||||
if (p.IsBot)
|
|
||||||
p.NetworkId = -p.ClientNumber;
|
|
||||||
|
|
||||||
StatusPlayers.Add(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return StatusPlayers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -27,6 +27,11 @@
|
|||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Prerelease' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||||
|
<OutputPath>bin\Prerelease\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="DiscordWebhook.py" />
|
<Compile Include="DiscordWebhook.py" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@ -42,7 +47,6 @@
|
|||||||
</Interpreter>
|
</Interpreter>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="config.dev.json" />
|
|
||||||
<Content Include="config.json">
|
<Content Include="config.json">
|
||||||
<Publish>True</Publish>
|
<Publish>True</Publish>
|
||||||
</Content>
|
</Content>
|
||||||
@ -50,7 +54,7 @@
|
|||||||
<Publish>True</Publish>
|
<Publish>True</Publish>
|
||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.Web.targets" />
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.Web.targets" />
|
||||||
<!-- Uncomment the CoreCompile target to enable the Build command in
|
<!-- Uncomment the CoreCompile target to enable the Build command in
|
||||||
Visual Studio and specify your pre- and post-build commands in
|
Visual Studio and specify your pre- and post-build commands in
|
||||||
the BeforeBuild and AfterBuild targets below. -->
|
the BeforeBuild and AfterBuild targets below. -->
|
||||||
@ -59,7 +63,7 @@
|
|||||||
</Target>
|
</Target>
|
||||||
<Target Name="AfterBuild">
|
<Target Name="AfterBuild">
|
||||||
</Target>
|
</Target>
|
||||||
<ProjectExtensions>
|
<ProjectExtensions>
|
||||||
<VisualStudio>
|
<VisualStudio>
|
||||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||||
<WebProjectProperties>
|
<WebProjectProperties>
|
||||||
|
@ -11,13 +11,15 @@
|
|||||||
<SearchPath>
|
<SearchPath>
|
||||||
</SearchPath>
|
</SearchPath>
|
||||||
<WorkingDirectory>.</WorkingDirectory>
|
<WorkingDirectory>.</WorkingDirectory>
|
||||||
<LaunchProvider>Web launcher</LaunchProvider>
|
<LaunchProvider>Standard Python launcher</LaunchProvider>
|
||||||
<WebBrowserUrl>http://localhost</WebBrowserUrl>
|
<WebBrowserUrl>http://localhost</WebBrowserUrl>
|
||||||
<OutputPath>.</OutputPath>
|
<OutputPath>.</OutputPath>
|
||||||
<SuppressCollectPythonCloudServiceFiles>true</SuppressCollectPythonCloudServiceFiles>
|
<SuppressCollectPythonCloudServiceFiles>true</SuppressCollectPythonCloudServiceFiles>
|
||||||
<Name>GameLogServer</Name>
|
<Name>GameLogServer</Name>
|
||||||
<RootNamespace>GameLogServer</RootNamespace>
|
<RootNamespace>GameLogServer</RootNamespace>
|
||||||
<InterpreterId>MSBuild|env|$(MSBuildProjectFullPath)</InterpreterId>
|
<InterpreterId>MSBuild|env|$(MSBuildProjectFullPath)</InterpreterId>
|
||||||
|
<EnableNativeCodeDebugging>False</EnableNativeCodeDebugging>
|
||||||
|
<Environment>DEBUG=True</Environment>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@ -27,10 +29,16 @@
|
|||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Prerelease' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||||
|
<OutputPath>bin\Prerelease\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="GameLogServer\log_reader.py">
|
<Compile Include="GameLogServer\log_reader.py">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="GameLogServer\restart_resource.py" />
|
||||||
<Compile Include="GameLogServer\server.py">
|
<Compile Include="GameLogServer\server.py">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@ -42,8 +50,8 @@
|
|||||||
<Folder Include="GameLogServer\" />
|
<Folder Include="GameLogServer\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="FolderProfile.pubxml" />
|
|
||||||
<Content Include="requirements.txt" />
|
<Content Include="requirements.txt" />
|
||||||
|
<None Include="Stable.pubxml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Interpreter Include="env\">
|
<Interpreter Include="env\">
|
||||||
|
@ -5,26 +5,27 @@ import time
|
|||||||
class LogReader(object):
|
class LogReader(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.log_file_sizes = {}
|
self.log_file_sizes = {}
|
||||||
# (if the file changes more than this, ignore ) - 1 MB
|
# (if the file changes more than this, ignore ) - 0.125 MB
|
||||||
self.max_file_size_change = 1000000
|
self.max_file_size_change = 125000
|
||||||
# (if the time between checks is greater, ignore ) - 5 minutes
|
# (if the time between checks is greater, ignore ) - 5 minutes
|
||||||
self.max_file_time_change = 1000
|
self.max_file_time_change = 60
|
||||||
|
|
||||||
def read_file(self, path):
|
def read_file(self, path):
|
||||||
# prevent traversing directories
|
# prevent traversing directories
|
||||||
if re.search('r^.+\.\.\\.+$', path):
|
if re.search('r^.+\.\.\\.+$', path):
|
||||||
return False
|
return False
|
||||||
# must be a valid log path and log file
|
# must be a valid log path and log file
|
||||||
if not re.search(r'^.+[\\|\/](userraw|mods)[\\|\/].+.log$', path):
|
if not re.search(r'^.+[\\|\/](.+)[\\|\/].+.log$', path):
|
||||||
return False
|
return False
|
||||||
# set the initialze size to the current file size
|
# set the initialze size to the current file size
|
||||||
file_size = 0
|
file_size = 0
|
||||||
|
|
||||||
if path not in self.log_file_sizes:
|
if path not in self.log_file_sizes:
|
||||||
self.log_file_sizes[path] = {
|
self.log_file_sizes[path] = {
|
||||||
'length' : self.file_length(path),
|
'length' : self.file_length(path),
|
||||||
'read': time.time()
|
'read': time.time()
|
||||||
}
|
}
|
||||||
return ''
|
return True
|
||||||
|
|
||||||
# grab the previous values
|
# grab the previous values
|
||||||
last_length = self.log_file_sizes[path]['length']
|
last_length = self.log_file_sizes[path]['length']
|
||||||
@ -50,9 +51,9 @@ class LogReader(object):
|
|||||||
|
|
||||||
# if it's been too long since we read and the amount changed is too great, discard it
|
# if it's been too long since we read and the amount changed is too great, discard it
|
||||||
# todo: do we really want old events? maybe make this an "or"
|
# todo: do we really want old events? maybe make this an "or"
|
||||||
if file_size_difference > self.max_file_size_change and time_difference > self.max_file_time_change:
|
if file_size_difference > self.max_file_size_change or time_difference > self.max_file_time_change:
|
||||||
return ''
|
return True
|
||||||
|
|
||||||
new_log_info = self.get_file_lines(path, file_size_difference)
|
new_log_info = self.get_file_lines(path, file_size_difference)
|
||||||
return new_log_info
|
return new_log_info
|
||||||
|
|
||||||
|
@ -9,9 +9,11 @@ class LogResource(Resource):
|
|||||||
|
|
||||||
if log_info is False:
|
if log_info is False:
|
||||||
print('could not read log file ' + path)
|
print('could not read log file ' + path)
|
||||||
|
|
||||||
|
empty_read = (log_info == False) or (log_info == True)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'success' : log_info is not False,
|
'success' : log_info is not False,
|
||||||
'length': -1 if log_info is False else len(log_info),
|
'length': -1 if empty_read else len(log_info),
|
||||||
'data': log_info
|
'data': log_info
|
||||||
}
|
}
|
||||||
|
29
GameLogServer/GameLogServer/restart_resource.py
Normal file
29
GameLogServer/GameLogServer/restart_resource.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from flask_restful import Resource
|
||||||
|
from flask import request
|
||||||
|
import requests
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import re
|
||||||
|
|
||||||
|
def get_pid_of_server_windows(port):
|
||||||
|
process = subprocess.Popen('netstat -aon', shell=True, stdout=subprocess.PIPE)
|
||||||
|
output = process.communicate()[0]
|
||||||
|
matches = re.search(' *(UDP) +([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}):'+ str(port) + ' +[^\w]*([0-9]+)', output.decode('utf-8'))
|
||||||
|
if matches is not None:
|
||||||
|
return matches.group(3)
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
class RestartResource(Resource):
|
||||||
|
def get(self):
|
||||||
|
try:
|
||||||
|
response = requests.get('http://' + request.remote_addr + ':1624/api/restartapproved')
|
||||||
|
if response.status_code == 200:
|
||||||
|
pid = get_pid_of_server_windows(response.json()['port'])
|
||||||
|
subprocess.check_output("Taskkill /PID %s /F" % pid)
|
||||||
|
else:
|
||||||
|
return {}, 400
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return {}, 500
|
||||||
|
return {}, 200
|
@ -1,9 +1,11 @@
|
|||||||
from flask import Flask
|
from flask import Flask
|
||||||
from flask_restful import Api
|
from flask_restful import Api
|
||||||
from .log_resource import LogResource
|
from .log_resource import LogResource
|
||||||
|
from .restart_resource import RestartResource
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
api = Api(app)
|
api = Api(app)
|
||||||
api.add_resource(LogResource, '/log/<string:path>')
|
api.add_resource(LogResource, '/log/<string:path>')
|
||||||
|
api.add_resource(RestartResource, '/restart')
|
||||||
|
@ -1,12 +1,26 @@
|
|||||||
Flask==1.0.2
|
|
||||||
aniso8601==3.0.2
|
aniso8601==3.0.2
|
||||||
|
APScheduler==3.5.3
|
||||||
|
certifi==2018.10.15
|
||||||
|
chardet==3.0.4
|
||||||
click==6.7
|
click==6.7
|
||||||
|
Flask==1.0.2
|
||||||
|
Flask-JWT==0.3.2
|
||||||
|
Flask-JWT-Extended==3.8.1
|
||||||
Flask-RESTful==0.3.6
|
Flask-RESTful==0.3.6
|
||||||
|
idna==2.7
|
||||||
itsdangerous==0.24
|
itsdangerous==0.24
|
||||||
Jinja2==2.10
|
Jinja2==2.10
|
||||||
MarkupSafe==1.0
|
MarkupSafe==1.0
|
||||||
|
marshmallow==3.0.0b8
|
||||||
pip==9.0.3
|
pip==9.0.3
|
||||||
pytz==2018.5
|
psutil==5.4.8
|
||||||
setuptools==39.0.1
|
pygal==2.4.0
|
||||||
|
PyJWT==1.4.2
|
||||||
|
pytz==2018.7
|
||||||
|
requests==2.20.0
|
||||||
|
setuptools==40.5.0
|
||||||
six==1.11.0
|
six==1.11.0
|
||||||
|
timeago==1.0.8
|
||||||
|
tzlocal==1.5.1
|
||||||
|
urllib3==1.24
|
||||||
Werkzeug==0.14.1
|
Werkzeug==0.14.1
|
||||||
|
@ -12,4 +12,4 @@ if __name__ == '__main__':
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
PORT = 5555
|
PORT = 5555
|
||||||
init()
|
init()
|
||||||
app.run(HOST, PORT, debug=True)
|
app.run('0.0.0.0', PORT, debug=False)
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio 15
|
||||||
VisualStudioVersion = 15.0.26730.16
|
VisualStudioVersion = 15.0.26730.16
|
||||||
@ -39,12 +38,22 @@ Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "DiscordWebhook", "DiscordWe
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ScriptPlugins", "ScriptPlugins", "{3F9ACC27-26DB-49FA-BCD2-50C54A49C9FA}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ScriptPlugins", "ScriptPlugins", "{3F9ACC27-26DB-49FA-BCD2-50C54A49C9FA}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
Plugins\ScriptPlugins\ParserCoD4x.js = Plugins\ScriptPlugins\ParserCoD4x.js
|
||||||
|
Plugins\ScriptPlugins\ParserIW4x.js = Plugins\ScriptPlugins\ParserIW4x.js
|
||||||
|
Plugins\ScriptPlugins\ParserPT6.js = Plugins\ScriptPlugins\ParserPT6.js
|
||||||
|
Plugins\ScriptPlugins\ParserTeknoMW3.js = Plugins\ScriptPlugins\ParserTeknoMW3.js
|
||||||
Plugins\ScriptPlugins\SharedGUIDKick.js = Plugins\ScriptPlugins\SharedGUIDKick.js
|
Plugins\ScriptPlugins\SharedGUIDKick.js = Plugins\ScriptPlugins\SharedGUIDKick.js
|
||||||
Plugins\ScriptPlugins\VPNDetection.js = Plugins\ScriptPlugins\VPNDetection.js
|
Plugins\ScriptPlugins\VPNDetection.js = Plugins\ScriptPlugins\VPNDetection.js
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "GameLogServer", "GameLogServer\GameLogServer.pyproj", "{42EFDA12-10D3-4C40-A210-9483520116BC}"
|
Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "GameLogServer", "GameLogServer\GameLogServer.pyproj", "{42EFDA12-10D3-4C40-A210-9483520116BC}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{A848FCF1-8527-4AA8-A1AA-50D29695C678}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StatsWeb", "Plugins\Web\StatsWeb\StatsWeb.csproj", "{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutomessageFeed", "Plugins\AutomessageFeed\AutomessageFeed.csproj", "{F5815359-CFC7-44B4-9A3B-C04BACAD5836}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -283,8 +292,8 @@ Global
|
|||||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Debug|x64.Build.0 = Debug|Any CPU
|
{6C706CE5-A206-4E46-8712-F8C48D526091}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Debug|x86.ActiveCfg = Debug|Any CPU
|
{6C706CE5-A206-4E46-8712-F8C48D526091}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Debug|x86.Build.0 = Debug|Any CPU
|
{6C706CE5-A206-4E46-8712-F8C48D526091}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|Any CPU.ActiveCfg = Debug|Any CPU
|
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU
|
||||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|Any CPU.Build.0 = Debug|Any CPU
|
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|Any CPU.Build.0 = Prerelease|Any CPU
|
||||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|Mixed Platforms.Build.0 = Debug|Any CPU
|
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|x64.ActiveCfg = Debug|Any CPU
|
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|x64.ActiveCfg = Debug|Any CPU
|
||||||
@ -303,7 +312,7 @@ Global
|
|||||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|Any CPU.ActiveCfg = Release|Any CPU
|
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU
|
||||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|Mixed Platforms.ActiveCfg = Release|Any CPU
|
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|x64.ActiveCfg = Release|Any CPU
|
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|x64.ActiveCfg = Release|Any CPU
|
||||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|x86.ActiveCfg = Release|Any CPU
|
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|x86.ActiveCfg = Release|Any CPU
|
||||||
@ -312,15 +321,13 @@ Global
|
|||||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Release|x64.ActiveCfg = Release|Any CPU
|
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Release|x86.ActiveCfg = Release|Any CPU
|
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|x64.ActiveCfg = Debug|Any CPU
|
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|x64.Build.0 = Debug|Any CPU
|
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|x86.ActiveCfg = Debug|Any CPU
|
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|x86.Build.0 = Debug|Any CPU
|
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Prerelease|Any CPU.ActiveCfg = Release|Any CPU
|
{42EFDA12-10D3-4C40-A210-9483520116BC}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU
|
||||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Prerelease|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Prerelease|Mixed Platforms.ActiveCfg = Release|Any CPU
|
{42EFDA12-10D3-4C40-A210-9483520116BC}.Prerelease|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Prerelease|Mixed Platforms.Build.0 = Release|Any CPU
|
{42EFDA12-10D3-4C40-A210-9483520116BC}.Prerelease|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Prerelease|x64.ActiveCfg = Release|Any CPU
|
{42EFDA12-10D3-4C40-A210-9483520116BC}.Prerelease|x64.ActiveCfg = Release|Any CPU
|
||||||
@ -335,6 +342,54 @@ Global
|
|||||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Release|x64.Build.0 = Release|Any CPU
|
{42EFDA12-10D3-4C40-A210-9483520116BC}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Release|x86.ActiveCfg = Release|Any CPU
|
{42EFDA12-10D3-4C40-A210-9483520116BC}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Release|x86.Build.0 = Release|Any CPU
|
{42EFDA12-10D3-4C40-A210-9483520116BC}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|Any CPU.Build.0 = Prerelease|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|x64.Build.0 = Debug|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|x86.Build.0 = Debug|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Prerelease|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Prerelease|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Prerelease|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Prerelease|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Prerelease|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Prerelease|x64.Build.0 = Debug|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Prerelease|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Prerelease|x86.Build.0 = Debug|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -347,6 +402,9 @@ Global
|
|||||||
{B72DEBFB-9D48-4076-8FF5-1FD72A830845} = {26E8B310-269E-46D4-A612-24601F16065F}
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845} = {26E8B310-269E-46D4-A612-24601F16065F}
|
||||||
{6C706CE5-A206-4E46-8712-F8C48D526091} = {26E8B310-269E-46D4-A612-24601F16065F}
|
{6C706CE5-A206-4E46-8712-F8C48D526091} = {26E8B310-269E-46D4-A612-24601F16065F}
|
||||||
{3F9ACC27-26DB-49FA-BCD2-50C54A49C9FA} = {26E8B310-269E-46D4-A612-24601F16065F}
|
{3F9ACC27-26DB-49FA-BCD2-50C54A49C9FA} = {26E8B310-269E-46D4-A612-24601F16065F}
|
||||||
|
{A848FCF1-8527-4AA8-A1AA-50D29695C678} = {26E8B310-269E-46D4-A612-24601F16065F}
|
||||||
|
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B} = {A848FCF1-8527-4AA8-A1AA-50D29695C678}
|
||||||
|
{F5815359-CFC7-44B4-9A3B-C04BACAD5836} = {26E8B310-269E-46D4-A612-24601F16065F}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {84F8F8E0-1F73-41E0-BD8D-BB6676E2EE87}
|
SolutionGuid = {84F8F8E0-1F73-41E0-BD8D-BB6676E2EE87}
|
||||||
|
@ -11,13 +11,13 @@
|
|||||||
<SearchPath>
|
<SearchPath>
|
||||||
</SearchPath>
|
</SearchPath>
|
||||||
<WorkingDirectory>.</WorkingDirectory>
|
<WorkingDirectory>.</WorkingDirectory>
|
||||||
<LaunchProvider>Web launcher</LaunchProvider>
|
<LaunchProvider>Standard Python launcher</LaunchProvider>
|
||||||
<WebBrowserUrl>http://localhost</WebBrowserUrl>
|
<WebBrowserUrl>http://localhost</WebBrowserUrl>
|
||||||
<OutputPath>.</OutputPath>
|
<OutputPath>.</OutputPath>
|
||||||
<SuppressCollectPythonCloudServiceFiles>true</SuppressCollectPythonCloudServiceFiles>
|
<SuppressCollectPythonCloudServiceFiles>true</SuppressCollectPythonCloudServiceFiles>
|
||||||
<Name>Master</Name>
|
<Name>Master</Name>
|
||||||
<RootNamespace>Master</RootNamespace>
|
<RootNamespace>Master</RootNamespace>
|
||||||
<InterpreterId>MSBuild|dev_env|$(MSBuildProjectFullPath)</InterpreterId>
|
<InterpreterId>MSBuild|env_master|$(MSBuildProjectFullPath)</InterpreterId>
|
||||||
<IsWindowsApplication>False</IsWindowsApplication>
|
<IsWindowsApplication>False</IsWindowsApplication>
|
||||||
<PythonRunWebServerCommand>
|
<PythonRunWebServerCommand>
|
||||||
</PythonRunWebServerCommand>
|
</PythonRunWebServerCommand>
|
||||||
@ -25,6 +25,7 @@
|
|||||||
</PythonDebugWebServerCommand>
|
</PythonDebugWebServerCommand>
|
||||||
<PythonRunWebServerCommandType>script</PythonRunWebServerCommandType>
|
<PythonRunWebServerCommandType>script</PythonRunWebServerCommandType>
|
||||||
<PythonDebugWebServerCommandType>script</PythonDebugWebServerCommandType>
|
<PythonDebugWebServerCommandType>script</PythonDebugWebServerCommandType>
|
||||||
|
<EnableNativeCodeDebugging>False</EnableNativeCodeDebugging>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@ -111,18 +112,18 @@
|
|||||||
<Folder Include="master\templates\" />
|
<Folder Include="master\templates\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="FolderProfile.pubxml" />
|
|
||||||
<Content Include="master\config\master.json" />
|
<Content Include="master\config\master.json" />
|
||||||
<Content Include="master\templates\serverlist.html" />
|
<Content Include="master\templates\serverlist.html" />
|
||||||
|
<None Include="Release.pubxml" />
|
||||||
<Content Include="requirements.txt" />
|
<Content Include="requirements.txt" />
|
||||||
<Content Include="master\templates\index.html" />
|
<Content Include="master\templates\index.html" />
|
||||||
<Content Include="master\templates\layout.html" />
|
<Content Include="master\templates\layout.html" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Interpreter Include="dev_env\">
|
<Interpreter Include="env_master\">
|
||||||
<Id>dev_env</Id>
|
<Id>env_master</Id>
|
||||||
<Version>3.6</Version>
|
<Version>3.6</Version>
|
||||||
<Description>dev_env (Python 3.6 (64-bit))</Description>
|
<Description>env_master (Python 3.6 (64-bit))</Description>
|
||||||
<InterpreterPath>Scripts\python.exe</InterpreterPath>
|
<InterpreterPath>Scripts\python.exe</InterpreterPath>
|
||||||
<WindowsInterpreterPath>Scripts\pythonw.exe</WindowsInterpreterPath>
|
<WindowsInterpreterPath>Scripts\pythonw.exe</WindowsInterpreterPath>
|
||||||
<PathEnvironmentVariable>PYTHONPATH</PathEnvironmentVariable>
|
<PathEnvironmentVariable>PYTHONPATH</PathEnvironmentVariable>
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
|
||||||
class ServerModel(object):
|
class ServerModel(object):
|
||||||
def __init__(self, id, port, game, hostname, clientnum, maxclientnum, map, gametype, ip):
|
def __init__(self, id, port, game, hostname, clientnum, maxclientnum, map, gametype, ip, version):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.port = port
|
self.port = port
|
||||||
|
self.version = version
|
||||||
self.game = game
|
self.game = game
|
||||||
self.hostname = hostname
|
self.hostname = hostname
|
||||||
self.clientnum = clientnum
|
self.clientnum = clientnum
|
||||||
|
@ -5,6 +5,7 @@ from marshmallow import ValidationError
|
|||||||
from master.schema.instanceschema import InstanceSchema
|
from master.schema.instanceschema import InstanceSchema
|
||||||
from master import ctx
|
from master import ctx
|
||||||
import json
|
import json
|
||||||
|
from netaddr import IPAddress
|
||||||
|
|
||||||
class Instance(Resource):
|
class Instance(Resource):
|
||||||
def get(self, id=None):
|
def get(self, id=None):
|
||||||
@ -23,7 +24,10 @@ class Instance(Resource):
|
|||||||
def put(self, id):
|
def put(self, id):
|
||||||
try:
|
try:
|
||||||
for server in request.json['servers']:
|
for server in request.json['servers']:
|
||||||
server['ip'] = request.remote_addr
|
if 'ip' not in server or IPAddress(server['ip']).is_private() or IPAddress(server['ip']).is_loopback():
|
||||||
|
server['ip'] = request.remote_addr
|
||||||
|
if 'version' not in server:
|
||||||
|
server['version'] = 'Unknown'
|
||||||
instance = InstanceSchema().load(request.json)
|
instance = InstanceSchema().load(request.json)
|
||||||
except ValidationError as err:
|
except ValidationError as err:
|
||||||
return {'message' : err.messages }, 400
|
return {'message' : err.messages }, 400
|
||||||
@ -34,7 +38,10 @@ class Instance(Resource):
|
|||||||
def post(self):
|
def post(self):
|
||||||
try:
|
try:
|
||||||
for server in request.json['servers']:
|
for server in request.json['servers']:
|
||||||
server['ip'] = request.remote_addr
|
if 'ip' not in server or server['ip'] == 'localhost':
|
||||||
|
server['ip'] = request.remote_addr
|
||||||
|
if 'version' not in server:
|
||||||
|
server['version'] = 'Unknown'
|
||||||
instance = InstanceSchema().load(request.json)
|
instance = InstanceSchema().load(request.json)
|
||||||
except ValidationError as err:
|
except ValidationError as err:
|
||||||
return {'message' : err.messages }, 400
|
return {'message' : err.messages }, 400
|
||||||
|
@ -4,7 +4,7 @@ from master.models.servermodel import ServerModel
|
|||||||
class ServerSchema(Schema):
|
class ServerSchema(Schema):
|
||||||
id = fields.Int(
|
id = fields.Int(
|
||||||
required=True,
|
required=True,
|
||||||
validate=validate.Range(1, 2147483647, 'invalid id')
|
validate=validate.Range(0, 25525525525565535, 'invalid id')
|
||||||
)
|
)
|
||||||
ip = fields.Str(
|
ip = fields.Str(
|
||||||
required=True
|
required=True
|
||||||
@ -13,6 +13,10 @@ class ServerSchema(Schema):
|
|||||||
required=True,
|
required=True,
|
||||||
validate=validate.Range(1, 65535, 'invalid port')
|
validate=validate.Range(1, 65535, 'invalid port')
|
||||||
)
|
)
|
||||||
|
version = fields.String(
|
||||||
|
required=False,
|
||||||
|
validate=validate.Length(0, 128, 'invalid server version')
|
||||||
|
)
|
||||||
game = fields.String(
|
game = fields.String(
|
||||||
required=True,
|
required=True,
|
||||||
validate=validate.Length(1, 5, 'invalid game name')
|
validate=validate.Length(1, 5, 'invalid game name')
|
||||||
@ -31,7 +35,7 @@ class ServerSchema(Schema):
|
|||||||
)
|
)
|
||||||
map = fields.String(
|
map = fields.String(
|
||||||
required=True,
|
required=True,
|
||||||
validate=validate.Length(1, 32, 'invalid map name')
|
validate=validate.Length(0, 64, 'invalid map name')
|
||||||
)
|
)
|
||||||
gametype = fields.String(
|
gametype = fields.String(
|
||||||
required=True,
|
required=True,
|
||||||
|
@ -1,16 +1,26 @@
|
|||||||
aniso8601==3.0.0
|
aniso8601==3.0.2
|
||||||
|
APScheduler==3.5.3
|
||||||
|
certifi==2018.10.15
|
||||||
|
chardet==3.0.4
|
||||||
click==6.7
|
click==6.7
|
||||||
Flask==0.12.2
|
Flask==1.0.2
|
||||||
Flask-JWT==0.3.2
|
Flask-JWT==0.3.2
|
||||||
Flask-JWT-Extended==3.8.1
|
Flask-JWT-Extended==3.8.1
|
||||||
Flask-RESTful==0.3.6
|
Flask-RESTful==0.3.6
|
||||||
|
idna==2.7
|
||||||
itsdangerous==0.24
|
itsdangerous==0.24
|
||||||
Jinja2==2.10
|
Jinja2==2.10
|
||||||
MarkupSafe==1.0
|
MarkupSafe==1.0
|
||||||
marshmallow==3.0.0b8
|
marshmallow==3.0.0b8
|
||||||
pip==9.0.1
|
pip==9.0.3
|
||||||
|
psutil==5.4.8
|
||||||
|
pygal==2.4.0
|
||||||
PyJWT==1.4.2
|
PyJWT==1.4.2
|
||||||
pytz==2018.4
|
pytz==2018.7
|
||||||
setuptools==39.0.1
|
requests==2.20.0
|
||||||
|
setuptools==40.5.0
|
||||||
six==1.11.0
|
six==1.11.0
|
||||||
|
timeago==1.0.8
|
||||||
|
tzlocal==1.5.1
|
||||||
|
urllib3==1.24
|
||||||
Werkzeug==0.14.1
|
Werkzeug==0.14.1
|
||||||
|
21
Plugins/AutomessageFeed/AutomessageFeed.csproj
Normal file
21
Plugins/AutomessageFeed/AutomessageFeed.csproj
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\SharedLibraryCore\SharedLibraryCore.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
|
<Exec Command="copy "$(TargetPath)" "$(SolutionDir)BUILD\Plugins""/>
|
||||||
|
<Exec Command="copy "$(TargetDir)Microsoft.SyndicationFeed.ReaderWriter.dll" "$(SolutionDir)BUILD\Plugins""/>
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
</Project>
|
24
Plugins/AutomessageFeed/Configuration.cs
Normal file
24
Plugins/AutomessageFeed/Configuration.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace AutomessageFeed
|
||||||
|
{
|
||||||
|
class Configuration : IBaseConfiguration
|
||||||
|
{
|
||||||
|
public bool EnableFeed { get; set; }
|
||||||
|
public string FeedUrl { get; set; }
|
||||||
|
|
||||||
|
public IBaseConfiguration Generate()
|
||||||
|
{
|
||||||
|
EnableFeed = Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_AUTOMESSAGEFEED_PROMPT_ENABLE"]);
|
||||||
|
FeedUrl = Utilities.PromptString(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_AUTOMESSAGEFEED_URL"]);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name() => "AutomessageFeedConfiguration";
|
||||||
|
}
|
||||||
|
}
|
85
Plugins/AutomessageFeed/Plugin.cs
Normal file
85
Plugins/AutomessageFeed/Plugin.cs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SyndicationFeed.Rss;
|
||||||
|
using SharedLibraryCore.Configuration;
|
||||||
|
using System.Xml;
|
||||||
|
using Microsoft.SyndicationFeed;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using SharedLibraryCore.Helpers;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace AutomessageFeed
|
||||||
|
{
|
||||||
|
public class Plugin : IPlugin
|
||||||
|
{
|
||||||
|
public string Name => "Automessage Feed";
|
||||||
|
|
||||||
|
public float Version => (float)Utilities.GetVersionAsDouble();
|
||||||
|
|
||||||
|
public string Author => "RaidMax";
|
||||||
|
|
||||||
|
private Configuration _configuration;
|
||||||
|
private int _currentFeedItem;
|
||||||
|
|
||||||
|
private async Task<object> GetNextFeedItem(Server server)
|
||||||
|
{
|
||||||
|
var items = new List<string>();
|
||||||
|
|
||||||
|
using (var reader = XmlReader.Create(_configuration.FeedUrl, new XmlReaderSettings() { Async = true }))
|
||||||
|
{
|
||||||
|
var feedReader = new RssFeedReader(reader);
|
||||||
|
|
||||||
|
while (await feedReader.Read())
|
||||||
|
{
|
||||||
|
switch (feedReader.ElementType)
|
||||||
|
{
|
||||||
|
case SyndicationElementType.Item:
|
||||||
|
var item = await feedReader.ReadItem();
|
||||||
|
items.Add(Regex.Replace(item.Title, @"\<.+\>.*\</.+\>", ""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentFeedItem < items.Count)
|
||||||
|
{
|
||||||
|
_currentFeedItem++;
|
||||||
|
return items[_currentFeedItem - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentFeedItem = 0;
|
||||||
|
return Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_AUTOMESSAGEFEED_NO_ITEMS"];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task OnEventAsync(GameEvent E, Server S)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnLoadAsync(IManager manager)
|
||||||
|
{
|
||||||
|
var cfg = new BaseConfigurationHandler<Configuration>("AutomessageFeedPluginSettings");
|
||||||
|
if (cfg.Configuration() == null)
|
||||||
|
{
|
||||||
|
cfg.Set((Configuration)new Configuration().Generate());
|
||||||
|
await cfg.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
_configuration = cfg.Configuration();
|
||||||
|
|
||||||
|
manager.GetMessageTokens().Add(new MessageToken("FEED", GetNextFeedItem));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task OnTickAsync(Server S)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task OnUnloadAsync()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,197 +1,200 @@
|
|||||||
//using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
//using SharedLibraryCore.Objects;
|
using SharedLibraryCore.Database.Models;
|
||||||
//using System;
|
using SharedLibraryCore.Objects;
|
||||||
//using System.Collections.Generic;
|
using System;
|
||||||
//using System.Linq;
|
using System.Collections.Generic;
|
||||||
//using System.Text;
|
using System.Linq;
|
||||||
//using System.Threading.Tasks;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
//namespace IW4ScriptCommands.Commands
|
namespace IW4ScriptCommands.Commands
|
||||||
//{
|
{
|
||||||
// class Balance : Command
|
class Balance
|
||||||
// {
|
{
|
||||||
// private class TeamAssignment
|
private class TeamAssignment
|
||||||
// {
|
{
|
||||||
// public IW4MAdmin.Plugins.Stats.IW4Info.Team CurrentTeam { get; set; }
|
public IW4MAdmin.Plugins.Stats.IW4Info.Team CurrentTeam { get; set; }
|
||||||
// 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 override async Task ExecuteAsync(GameEvent E)
|
public static string GetTeamAssignments(EFClient client, bool isDisconnect, Server server, string teamsString = "")
|
||||||
// {
|
{
|
||||||
// string teamsString = (await E.Owner.GetDvarAsync<string>("sv_iw4madmin_teams")).Value;
|
var scriptClientTeams = teamsString.Split(';', StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(c => c.Split(','))
|
||||||
|
.Select(c => new TeamAssignment()
|
||||||
|
{
|
||||||
|
CurrentTeam = (IW4MAdmin.Plugins.Stats.IW4Info.Team)Enum.Parse(typeof(IW4MAdmin.Plugins.Stats.IW4Info.Team), c[1]),
|
||||||
|
Num = server.GetClientsAsList().FirstOrDefault(p => p.ClientNumber == Int32.Parse(c[0]))?.ClientNumber ?? -1,
|
||||||
|
Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(server.Clients.FirstOrDefault(p => p.ClientNumber == Int32.Parse(c[0])).ClientId, server.EndPoint)
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
// var scriptClientTeams = teamsString.Split(';', StringSplitOptions.RemoveEmptyEntries)
|
// at least one team is full so we can't balance
|
||||||
// .Select(c => c.Split(','))
|
if (scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis) >= Math.Floor(server.MaxClients / 2.0)
|
||||||
// .Select(c => new TeamAssignment()
|
|| scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies) >= Math.Floor(server.MaxClients / 2.0))
|
||||||
// {
|
{
|
||||||
// CurrentTeam = (IW4MAdmin.Plugins.Stats.IW4Info.Team)Enum.Parse(typeof(IW4MAdmin.Plugins.Stats.IW4Info.Team), c[1]),
|
// E.Origin?.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL"]);
|
||||||
// Num = E.Owner.Players.FirstOrDefault(p => p?.NetworkId == c[0].ConvertLong())?.ClientNumber ?? -1,
|
return string.Empty;
|
||||||
// Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(E.Owner.Players.FirstOrDefault(p => p?.NetworkId == c[0].ConvertLong()).ClientId, E.Owner.GetHashCode())
|
}
|
||||||
// })
|
|
||||||
// .ToList();
|
|
||||||
|
|
||||||
// // at least one team is full so we can't balance
|
List<string> teamAssignments = new List<string>();
|
||||||
// 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))
|
|
||||||
// {
|
|
||||||
// await E.Origin?.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL"]);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// List<string> teamAssignments = new List<string>();
|
var _c = server.GetClientsAsList();
|
||||||
|
if (isDisconnect && client != null)
|
||||||
|
{
|
||||||
|
_c = _c.Where(c => c.ClientNumber != client.ClientNumber).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
// var activeClients = E.Owner.GetPlayersAsList().Select(c => new TeamAssignment()
|
var activeClients = _c.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, server.EndPoint),
|
||||||
// CurrentTeam = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, E.Owner.GetHashCode()).Team
|
CurrentTeam = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, server.EndPoint).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)
|
|
||||||
// {
|
|
||||||
// teamAssignments.Add($"{assignment.Num},3");
|
|
||||||
// assignment.Stats.Team = IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (alliesTeam.Count(ac => scriptClientTeams.First(sc => sc.Num == ac.Num).CurrentTeam != ac.CurrentTeam) == 0 &&
|
foreach (var assignment in axisTeam)
|
||||||
// axisTeam.Count(ac => scriptClientTeams.First(sc => sc.Num == ac.Num).CurrentTeam != ac.CurrentTeam) == 0)
|
{
|
||||||
// {
|
teamAssignments.Add($"{assignment.Num},3");
|
||||||
// await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL_BALANCED"]);
|
assignment.Stats.Team = IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis;
|
||||||
// return;
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// if (E.Origin?.Level > Player.Permission.Administrator)
|
//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)
|
||||||
// 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)}");
|
// //E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL_BALANCED"]);
|
||||||
// }
|
// return string.Empty;
|
||||||
|
//}
|
||||||
|
|
||||||
// string args = string.Join(",", teamAssignments);
|
//if (E.Origin?.Level > Player.Permission.Administrator)
|
||||||
// await E.Owner.ExecuteCommandAsync($"sv_iw4madmin_command \"balance:{args}\"");
|
//{
|
||||||
// await E.Origin.Tell("Balance command sent");
|
// E.Origin.Tell($"Allies Elo: {(alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0)}");
|
||||||
// }
|
// E.Origin.Tell($"Axis Elo: {(axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0)}");
|
||||||
// }
|
//}
|
||||||
//}
|
|
||||||
|
//E.Origin.Tell("Balance command sent");
|
||||||
|
string args = string.Join(",", teamAssignments);
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
54
Plugins/IW4ScriptCommands/GscApiController.cs
Normal file
54
Plugins/IW4ScriptCommands/GscApiController.cs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
using IW4ScriptCommands.Commands;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Objects;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace WebfrontCore.Controllers.API
|
||||||
|
{
|
||||||
|
[Route("api/gsc/[action]")]
|
||||||
|
public class GscApiController : ApiController
|
||||||
|
{
|
||||||
|
[HttpGet("{networkId}")]
|
||||||
|
public IActionResult ClientInfo(string networkId)
|
||||||
|
{
|
||||||
|
var clientInfo = Manager.GetActiveClients()
|
||||||
|
.FirstOrDefault(c => c.NetworkId == networkId.ConvertLong());
|
||||||
|
|
||||||
|
if (clientInfo != null)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine($"admin={clientInfo.IsPrivileged()}");
|
||||||
|
sb.AppendLine($"level={(int)clientInfo.Level}");
|
||||||
|
sb.AppendLine($"levelstring={clientInfo.Level.ToLocalizedLevelName()}");
|
||||||
|
sb.AppendLine($"connections={clientInfo.Connections}");
|
||||||
|
sb.AppendLine($"authenticated={clientInfo.GetAdditionalProperty<bool>("IsLoggedIn") == true}");
|
||||||
|
|
||||||
|
return Content(sb.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Content("");
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{networkId}")]
|
||||||
|
public IActionResult GetTeamAssignments(string networkId, int serverId, string teams = "", bool isDisconnect = false)
|
||||||
|
{
|
||||||
|
return Unauthorized();
|
||||||
|
|
||||||
|
var client = Manager.GetActiveClients()
|
||||||
|
.FirstOrDefault(c => c.NetworkId == networkId.ConvertLong());
|
||||||
|
|
||||||
|
var server = Manager.GetServers().First(c => c.EndPoint == serverId);
|
||||||
|
|
||||||
|
teams = teams ?? string.Empty;
|
||||||
|
|
||||||
|
string assignments = Balance.GetTeamAssignments(client, isDisconnect, server, teams);
|
||||||
|
|
||||||
|
return Content(assignments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
|
<RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion>
|
||||||
<ApplicationIcon />
|
<ApplicationIcon />
|
||||||
<StartupObject />
|
<StartupObject />
|
||||||
|
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
@ -18,7 +19,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
|
<PackageReference Update="Microsoft.NETCore.App" Version="2.2.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -17,15 +17,10 @@ namespace IW4ScriptCommands
|
|||||||
|
|
||||||
public Task OnEventAsync(GameEvent E, Server S)
|
public Task OnEventAsync(GameEvent E, Server S)
|
||||||
{
|
{
|
||||||
//if (E.Type == GameEvent.EventType.JoinTeam || E.Type == GameEvent.EventType.Disconnect)
|
if (E.Type == GameEvent.EventType.Start)
|
||||||
//{
|
{
|
||||||
// E.Origin = new SharedLibraryCore.Objects.Player()
|
return S.SetDvarAsync("sv_iw4madmin_serverid", S.EndPoint);
|
||||||
// {
|
}
|
||||||
// ClientId = 1,
|
|
||||||
// CurrentServer = E.Owner
|
|
||||||
// };
|
|
||||||
// return new Commands.Balance().ExecuteAsync(E);
|
|
||||||
//}
|
|
||||||
|
|
||||||
if (E.Type == GameEvent.EventType.Warn)
|
if (E.Type == GameEvent.EventType.Warn)
|
||||||
{
|
{
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using SharedLibraryCore.Objects;
|
using SharedLibraryCore.Database.Models;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Login.Commands
|
namespace IW4MAdmin.Plugins.Login.Commands
|
||||||
{
|
{
|
||||||
public class CLogin : Command
|
public class CLogin : Command
|
||||||
{
|
{
|
||||||
public CLogin() : base("login", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_DESC"], "li", Player.Permission.Trusted, false, new CommandArgument[]
|
public CLogin() : base("login", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_DESC"], "li", EFClient.Permission.Trusted, false, new CommandArgument[]
|
||||||
{
|
{
|
||||||
new CommandArgument()
|
new CommandArgument()
|
||||||
{
|
{
|
||||||
@ -21,18 +19,22 @@ namespace IW4MAdmin.Plugins.Login.Commands
|
|||||||
public override async Task ExecuteAsync(GameEvent E)
|
public override async Task ExecuteAsync(GameEvent E)
|
||||||
{
|
{
|
||||||
var client = E.Owner.Manager.GetPrivilegedClients()[E.Origin.ClientId];
|
var client = E.Owner.Manager.GetPrivilegedClients()[E.Origin.ClientId];
|
||||||
string[] hashedPassword = await Task.FromResult(SharedLibraryCore.Helpers.Hashing.Hash(E.Data, client.PasswordSalt));
|
bool success = E.Owner.Manager.TokenAuthenticator.AuthorizeToken(E.Origin.NetworkId, E.Data);
|
||||||
|
|
||||||
if (hashedPassword[0] == client.Password)
|
if (!success)
|
||||||
{
|
{
|
||||||
Plugin.AuthorizedClients[E.Origin.ClientId] = true;
|
string[] hashedPassword = await Task.FromResult(SharedLibraryCore.Helpers.Hashing.Hash(E.Data, client.PasswordSalt));
|
||||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS"]);
|
|
||||||
|
if (hashedPassword[0] == client.Password)
|
||||||
|
{
|
||||||
|
success = true;
|
||||||
|
Plugin.AuthorizedClients[E.Origin.ClientId] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
_ = success ?
|
||||||
{
|
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS"]) :
|
||||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_FAIL"]);
|
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_FAIL"]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
|
<RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion>
|
||||||
<ApplicationIcon />
|
<ApplicationIcon />
|
||||||
<StartupObject />
|
<StartupObject />
|
||||||
<PackageId>RaidMax.IW4MAdmin.Plugins.Login</PackageId>
|
<PackageId>RaidMax.IW4MAdmin.Plugins.Login</PackageId>
|
||||||
@ -22,7 +22,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
|
<PackageReference Update="Microsoft.NETCore.App" Version="2.2.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
|
@ -3,9 +3,9 @@ using System.Reflection;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using SharedLibraryCore.Configuration;
|
using SharedLibraryCore.Configuration;
|
||||||
|
using SharedLibraryCore.Database.Models;
|
||||||
using SharedLibraryCore.Exceptions;
|
using SharedLibraryCore.Exceptions;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using SharedLibraryCore.Objects;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Login
|
namespace IW4MAdmin.Plugins.Login
|
||||||
{
|
{
|
||||||
@ -22,12 +22,13 @@ namespace IW4MAdmin.Plugins.Login
|
|||||||
|
|
||||||
public Task OnEventAsync(GameEvent E, Server S)
|
public Task OnEventAsync(GameEvent E, Server S)
|
||||||
{
|
{
|
||||||
if (E.Remote || Config.RequirePrivilegedClientLogin == false)
|
if (E.IsRemote || Config.RequirePrivilegedClientLogin == false)
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
if (E.Type == GameEvent.EventType.Connect)
|
if (E.Type == GameEvent.EventType.Connect)
|
||||||
{
|
{
|
||||||
AuthorizedClients.TryAdd(E.Origin.ClientId, false);
|
AuthorizedClients.TryAdd(E.Origin.ClientId, false);
|
||||||
|
E.Origin.SetAdditionalProperty("IsLoggedIn", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (E.Type == GameEvent.EventType.Disconnect)
|
if (E.Type == GameEvent.EventType.Disconnect)
|
||||||
@ -37,11 +38,11 @@ namespace IW4MAdmin.Plugins.Login
|
|||||||
|
|
||||||
if (E.Type == GameEvent.EventType.Command)
|
if (E.Type == GameEvent.EventType.Command)
|
||||||
{
|
{
|
||||||
if (E.Origin.Level < Player.Permission.Moderator ||
|
if (E.Origin.Level < EFClient.Permission.Moderator ||
|
||||||
E.Origin.Level == Player.Permission.Console)
|
E.Origin.Level == EFClient.Permission.Console)
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
E.Owner.Manager.GetPrivilegedClients().TryGetValue(E.Origin.ClientId, out Player client);
|
E.Owner.Manager.GetPrivilegedClients().TryGetValue(E.Origin.ClientId, out EFClient client);
|
||||||
|
|
||||||
if (((Command)E.Extra).Name == new SharedLibraryCore.Commands.CSetPassword().Name &&
|
if (((Command)E.Extra).Name == new SharedLibraryCore.Commands.CSetPassword().Name &&
|
||||||
client?.Password == null)
|
client?.Password == null)
|
||||||
@ -51,7 +52,14 @@ namespace IW4MAdmin.Plugins.Login
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
if (!AuthorizedClients[E.Origin.ClientId])
|
if (!AuthorizedClients[E.Origin.ClientId])
|
||||||
|
{
|
||||||
throw new AuthorizationException(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_AUTH"]);
|
throw new AuthorizationException(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_AUTH"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
E.Origin.SetAdditionalProperty("IsLoggedIn", true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
@ -5,6 +5,7 @@ using System.Text.RegularExpressions;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using SharedLibraryCore.Configuration;
|
using SharedLibraryCore.Configuration;
|
||||||
|
using SharedLibraryCore.Database.Models;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using SharedLibraryCore.Objects;
|
using SharedLibraryCore.Objects;
|
||||||
|
|
||||||
@ -48,7 +49,7 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
|||||||
|
|
||||||
if (containsObjectionalWord)
|
if (containsObjectionalWord)
|
||||||
{
|
{
|
||||||
E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, new Player()
|
E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, new EFClient()
|
||||||
{
|
{
|
||||||
ClientId = 1,
|
ClientId = 1,
|
||||||
CurrentServer = E.Owner
|
CurrentServer = E.Owner
|
||||||
@ -85,22 +86,14 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
|||||||
var clientProfanity = ProfanityCounts[E.Origin.ClientId];
|
var clientProfanity = ProfanityCounts[E.Origin.ClientId];
|
||||||
if (clientProfanity.Infringements >= Settings.Configuration().KickAfterInfringementCount)
|
if (clientProfanity.Infringements >= Settings.Configuration().KickAfterInfringementCount)
|
||||||
{
|
{
|
||||||
clientProfanity.Client.Kick(Settings.Configuration().ProfanityKickMessage, new Player()
|
clientProfanity.Client.Kick(Settings.Configuration().ProfanityKickMessage, Utilities.IW4MAdminClient(E.Owner));
|
||||||
{
|
|
||||||
ClientId = 1,
|
|
||||||
CurrentServer = E.Owner
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (clientProfanity.Infringements < Settings.Configuration().KickAfterInfringementCount)
|
else if (clientProfanity.Infringements < Settings.Configuration().KickAfterInfringementCount)
|
||||||
{
|
{
|
||||||
clientProfanity.Infringements++;
|
clientProfanity.Infringements++;
|
||||||
|
|
||||||
clientProfanity.Client.Warn(Settings.Configuration().ProfanityWarningMessage, new Player()
|
clientProfanity.Client.Warn(Settings.Configuration().ProfanityWarningMessage, Utilities.IW4MAdminClient(E.Owner));
|
||||||
{
|
|
||||||
ClientId = 1,
|
|
||||||
CurrentServer = E.Owner
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
|
<RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion>
|
||||||
<ApplicationIcon />
|
<ApplicationIcon />
|
||||||
<StartupObject />
|
<StartupObject />
|
||||||
<PackageId>RaidMax.IW4MAdmin.Plugins.ProfanityDeterment</PackageId>
|
<PackageId>RaidMax.IW4MAdmin.Plugins.ProfanityDeterment</PackageId>
|
||||||
@ -20,7 +20,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
|
<PackageReference Update="Microsoft.NETCore.App" Version="2.2.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using SharedLibraryCore.Database.Models;
|
||||||
using SharedLibraryCore.Objects;
|
using SharedLibraryCore.Objects;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.ProfanityDeterment
|
namespace IW4MAdmin.Plugins.ProfanityDeterment
|
||||||
{
|
{
|
||||||
class Tracking
|
class Tracking
|
||||||
{
|
{
|
||||||
public Player Client { get; private set; }
|
public EFClient Client { get; private set; }
|
||||||
public int Infringements { get; set; }
|
public int Infringements { get; set; }
|
||||||
|
|
||||||
public Tracking(Player client)
|
public Tracking(EFClient client)
|
||||||
{
|
{
|
||||||
Client = client;
|
Client = client;
|
||||||
Infringements = 0;
|
Infringements = 0;
|
||||||
|
37
Plugins/ScriptPlugins/ParserCoD4x.js
Normal file
37
Plugins/ScriptPlugins/ParserCoD4x.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
var rconParser;
|
||||||
|
var eventParser;
|
||||||
|
|
||||||
|
var plugin = {
|
||||||
|
author: 'FrenchFry, RaidMax',
|
||||||
|
version: 0.2,
|
||||||
|
name: 'CoD4x Parser',
|
||||||
|
isParser: true,
|
||||||
|
|
||||||
|
onEventAsync: function (gameEvent, server) {
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadAsync: function (manager) {
|
||||||
|
rconParser = manager.GenerateDynamicRConParser();
|
||||||
|
eventParser = manager.GenerateDynamicEventParser();
|
||||||
|
|
||||||
|
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +-?([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){16}|(?:[a-z]|[0-9]){32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +([0-9]+) +(\\d+\\.\\d+\\.\\d+.\\d+\\:-*\\d{1,5}|0+.0+:-*\\d{1,5}|loopback) +(-*[0-9]+) +([0-9]+) *$'
|
||||||
|
rconParser.Configuration.Status.AddMapping(104, 6); // RConName
|
||||||
|
rconParser.Configuration.Status.AddMapping(105, 8); // RConIPAddress
|
||||||
|
|
||||||
|
rconParser.Configuration.Dvar.Pattern = '^"(.+)" is: "(.+)?" default: "(.+)?" info: "(.+)?"$';
|
||||||
|
rconParser.Configuration.Dvar.AddMapping(109, 2); // DVAR latched value
|
||||||
|
rconParser.Configuration.Dvar.AddMapping(110, 4); // dvar info
|
||||||
|
rconParser.Version = 'CoD4 X 1.8 win_mingw-x86 build 2055 May 2 2017';
|
||||||
|
rconParser.GameName = 1; // IW3
|
||||||
|
|
||||||
|
eventParser.Configuration.GameDirectory = 'main';
|
||||||
|
eventParser.Version = 'CoD4 X 1.8 win_mingw-x86 build 2055 May 2 2017';
|
||||||
|
eventParser.GameName = 1; // IW3
|
||||||
|
},
|
||||||
|
|
||||||
|
onUnloadAsync: function () {
|
||||||
|
},
|
||||||
|
|
||||||
|
onTickAsync: function (server) {
|
||||||
|
}
|
||||||
|
};
|
35
Plugins/ScriptPlugins/ParserIW4x.js
Normal file
35
Plugins/ScriptPlugins/ParserIW4x.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
var rconParser;
|
||||||
|
var eventParser;
|
||||||
|
|
||||||
|
var plugin = {
|
||||||
|
author: 'RaidMax',
|
||||||
|
version: 0.2,
|
||||||
|
name: 'IW3 Parser',
|
||||||
|
isParser: true,
|
||||||
|
|
||||||
|
onEventAsync: function (gameEvent, server) {
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadAsync: function (manager) {
|
||||||
|
rconParser = manager.GenerateDynamicRConParser();
|
||||||
|
eventParser = manager.GenerateDynamicEventParser();
|
||||||
|
|
||||||
|
rconParser.Configuration.CommandPrefixes.Tell = 'tellraw {0} {1}';
|
||||||
|
rconParser.Configuration.CommandPrefixes.Say = 'sayraw {0}';
|
||||||
|
rconParser.Configuration.CommandPrefixes.Kick = 'clientkick {0} "{1}"';
|
||||||
|
rconParser.Configuration.CommandPrefixes.Ban = 'clientkick {0} "{1}"';
|
||||||
|
rconParser.Configuration.CommandPrefixes.TempBan = 'tempbanclient {0} "{1}"';
|
||||||
|
eventParser.Configuration.GameDirectory = 'userraw';
|
||||||
|
|
||||||
|
rconParser.Version = 'IW4x (v0.6.0)';
|
||||||
|
rconParser.GameName = 2; // IW4x
|
||||||
|
eventParser.Version = 'IW4x (v0.6.0)';
|
||||||
|
eventParser.GameName = 2; // IW4x
|
||||||
|
},
|
||||||
|
|
||||||
|
onUnloadAsync: function () {
|
||||||
|
},
|
||||||
|
|
||||||
|
onTickAsync: function (server) {
|
||||||
|
}
|
||||||
|
};
|
50
Plugins/ScriptPlugins/ParserPT6.js
Normal file
50
Plugins/ScriptPlugins/ParserPT6.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
var rconParser;
|
||||||
|
var eventParser;
|
||||||
|
|
||||||
|
var plugin = {
|
||||||
|
author: 'RaidMax',
|
||||||
|
version: 0.2,
|
||||||
|
name: 'Plutoniun T6 Parser',
|
||||||
|
isParser: true,
|
||||||
|
|
||||||
|
onEventAsync: function (gameEvent, server) {
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadAsync: function (manager) {
|
||||||
|
rconParser = manager.GenerateDynamicRConParser();
|
||||||
|
eventParser = manager.GenerateDynamicEventParser();
|
||||||
|
|
||||||
|
rconParser.Configuration.CommandPrefixes.Tell = 'tell {0} {1}';
|
||||||
|
rconParser.Configuration.CommandPrefixes.Say = 'say {0}';
|
||||||
|
rconParser.Configuration.CommandPrefixes.Kick = 'clientkick_for_reason {0} "{1}"';
|
||||||
|
rconParser.Configuration.CommandPrefixes.Ban = 'clientkick_for_reason {0} "{1}"';
|
||||||
|
rconParser.Configuration.CommandPrefixes.TempBan = 'clientkick_for_reason {0} "{1}"';
|
||||||
|
rconParser.Configuration.CommandPrefixes.RConGetDvar = '\xff\xff\xff\xffrcon {0} get {1}';
|
||||||
|
|
||||||
|
rconParser.Configuration.Dvar.Pattern = '^(.+) is "(.+)?"$';
|
||||||
|
rconParser.Configuration.Dvar.AddMapping(106, 1);
|
||||||
|
rconParser.Configuration.Dvar.AddMapping(107, 2);
|
||||||
|
rconParser.Configuration.WaitForResponse = false;
|
||||||
|
|
||||||
|
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +([0-9]+) +(.+) +((?:[A-Z]+|[0-9]+)) +((?:[A-Z]|[0-9]){8,16}) +(.{0,16}) +([0-9]+) +(\\d+\\.\\d+\\.\\d+\\.\\d+\\:-?\\d{1,5}|0+\\.0+:-?\\d{1,5}|loopback) +(-?[0-9]+) +([0-9]+) *$'
|
||||||
|
rconParser.Configuration.Status.AddMapping(100, 1);
|
||||||
|
rconParser.Configuration.Status.AddMapping(101, 2);
|
||||||
|
rconParser.Configuration.Status.AddMapping(102, 4);
|
||||||
|
rconParser.Configuration.Status.AddMapping(103, 5);
|
||||||
|
rconParser.Configuration.Status.AddMapping(104, 6);
|
||||||
|
rconParser.Configuration.Status.AddMapping(105, 8);
|
||||||
|
|
||||||
|
eventParser.Configuration.GameDirectory = 't6r\\data';
|
||||||
|
|
||||||
|
rconParser.Version = 'Call of Duty Multiplayer - Ship COD_T6_S MP build 1.0.44 CL(1759941) CODPCAB2 CEG Fri May 9 19:19:19 2014 win-x86 813e66d5';
|
||||||
|
rconParser.GameName = 7; // T6
|
||||||
|
eventParser.Version = 'Call of Duty Multiplayer - Ship COD_T6_S MP build 1.0.44 CL(1759941) CODPCAB2 CEG Fri May 9 19:19:19 2014 win-x86 813e66d5';
|
||||||
|
eventParser.GameName = 7; // T6
|
||||||
|
},
|
||||||
|
|
||||||
|
onUnloadAsync: function () {
|
||||||
|
},
|
||||||
|
|
||||||
|
onTickAsync: function (server) {
|
||||||
|
}
|
||||||
|
};
|
43
Plugins/ScriptPlugins/ParserTeknoMW3.js
Normal file
43
Plugins/ScriptPlugins/ParserTeknoMW3.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
var rconParser;
|
||||||
|
var eventParser;
|
||||||
|
|
||||||
|
var plugin = {
|
||||||
|
author: 'RaidMax',
|
||||||
|
version: 0.2,
|
||||||
|
name: 'Tekno MW3 Parser',
|
||||||
|
isParser: true,
|
||||||
|
|
||||||
|
onEventAsync: function (gameEvent, server) {
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadAsync: function (manager) {
|
||||||
|
rconParser = manager.GenerateDynamicRConParser();
|
||||||
|
eventParser = manager.GenerateDynamicEventParser();
|
||||||
|
|
||||||
|
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[A-Z]|[0-9]){16,32})\t +(.{0,16}) +([0-9]+) +(\\d+\\.\\d+\\.\\d+\\.\\d+\\:-?\\d{1,5}|0+\\.0+\\:-?\\d{1,5}|loopback) *$';
|
||||||
|
rconParser.Configuration.Status.AddMapping(104, 5); // RConName
|
||||||
|
rconParser.Configuration.Status.AddMapping(103, 4); // RConNetworkId
|
||||||
|
rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined;
|
||||||
|
rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xff';
|
||||||
|
rconParser.Configuration.CommandPrefixes.Tell = 'tell {0} {1}';
|
||||||
|
rconParser.Configuration.CommandPrefixes.Say = 'say {0}';
|
||||||
|
rconParser.Configuration.CommandPrefixes.Kick = 'dropclient {0} "{1}"';
|
||||||
|
rconParser.Configuration.CommandPrefixes.Ban = 'dropclient {0} "{1}"';
|
||||||
|
rconParser.Configuration.CommandPrefixes.TempBan = 'tempbanclient {0} "{1}"';
|
||||||
|
rconParser.Configuration.Dvar.AddMapping(107, 1); // RCon DvarValue
|
||||||
|
rconParser.Configuration.Dvar.Pattern = '^(.*)$';
|
||||||
|
rconParser.Version = 'IW5 MP 1.4 build 382 latest Thu Jan 19 2012 11:09:49AM win-x86';
|
||||||
|
rconParser.GameName = 3; // IW5
|
||||||
|
rconParser.CanGenerateLogPath = false;
|
||||||
|
|
||||||
|
eventParser.Configuration.GameDirectory = 'scripts';
|
||||||
|
eventParser.Version = 'IW5 MP 1.4 build 382 latest Thu Jan 19 2012 11:09:49AM win-x86';
|
||||||
|
eventParser.GameName = 3; // IW5
|
||||||
|
},
|
||||||
|
|
||||||
|
onUnloadAsync: function () {
|
||||||
|
},
|
||||||
|
|
||||||
|
onTickAsync: function (server) {
|
||||||
|
}
|
||||||
|
};
|
@ -1,7 +1,7 @@
|
|||||||
using SharedLibraryCore.Helpers;
|
using IW4MAdmin.Plugins.Stats.Models;
|
||||||
|
using SharedLibraryCore.Helpers;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using SharedLibraryCore.Objects;
|
using SharedLibraryCore.Objects;
|
||||||
using IW4MAdmin.Plugins.Stats.Models;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -20,7 +20,9 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
};
|
};
|
||||||
|
|
||||||
public ChangeTracking<EFACSnapshot> Tracker { get; private set; }
|
public ChangeTracking<EFACSnapshot> Tracker { get; private set; }
|
||||||
|
public const int QUEUE_COUNT = 10;
|
||||||
|
|
||||||
|
public List<EFClientKill> QueuedHits { get; set; }
|
||||||
int Kills;
|
int Kills;
|
||||||
int HitCount;
|
int HitCount;
|
||||||
Dictionary<IW4Info.HitLocation, int> HitLocationCount;
|
Dictionary<IW4Info.HitLocation, int> HitLocationCount;
|
||||||
@ -37,49 +39,55 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
Log = log;
|
Log = log;
|
||||||
HitLocationCount = new Dictionary<IW4Info.HitLocation, int>();
|
HitLocationCount = new Dictionary<IW4Info.HitLocation, int>();
|
||||||
foreach (var loc in Enum.GetValues(typeof(IW4Info.HitLocation)))
|
foreach (var loc in Enum.GetValues(typeof(IW4Info.HitLocation)))
|
||||||
|
{
|
||||||
HitLocationCount.Add((IW4Info.HitLocation)loc, 0);
|
HitLocationCount.Add((IW4Info.HitLocation)loc, 0);
|
||||||
|
}
|
||||||
|
|
||||||
ClientStats = clientStats;
|
ClientStats = clientStats;
|
||||||
Strain = new Strain();
|
Strain = new Strain();
|
||||||
Tracker = new ChangeTracking<EFACSnapshot>();
|
Tracker = new ChangeTracking<EFACSnapshot>();
|
||||||
|
QueuedHits = new List<EFClientKill>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Analyze kill and see if performed by a cheater
|
/// Analyze kill and see if performed by a cheater
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="kill">kill performed by the player</param>
|
/// <param name="hit">kill performed by the player</param>
|
||||||
/// <returns>true if detection reached thresholds, false otherwise</returns>
|
/// <returns>true if detection reached thresholds, false otherwise</returns>
|
||||||
public DetectionPenaltyResult ProcessKill(EFClientKill kill, bool isDamage)
|
public DetectionPenaltyResult ProcessHit(EFClientKill hit, bool isDamage)
|
||||||
{
|
{
|
||||||
if ((kill.DeathType != IW4Info.MeansOfDeath.MOD_PISTOL_BULLET &&
|
if ((hit.DeathType != IW4Info.MeansOfDeath.MOD_PISTOL_BULLET &&
|
||||||
kill.DeathType != IW4Info.MeansOfDeath.MOD_RIFLE_BULLET &&
|
hit.DeathType != IW4Info.MeansOfDeath.MOD_RIFLE_BULLET &&
|
||||||
kill.DeathType != IW4Info.MeansOfDeath.MOD_HEAD_SHOT) ||
|
hit.DeathType != IW4Info.MeansOfDeath.MOD_HEAD_SHOT) ||
|
||||||
kill.HitLoc == IW4Info.HitLocation.none || kill.TimeOffset - LastOffset < 0 ||
|
hit.HitLoc == IW4Info.HitLocation.none || hit.TimeOffset - LastOffset < 0 ||
|
||||||
// hack: prevents false positives
|
// hack: prevents false positives
|
||||||
(LastWeapon != kill.Weapon && (kill.TimeOffset - LastOffset) == 50))
|
(LastWeapon != hit.Weapon && (hit.TimeOffset - LastOffset) == 50))
|
||||||
|
{
|
||||||
return new DetectionPenaltyResult()
|
return new DetectionPenaltyResult()
|
||||||
{
|
{
|
||||||
ClientPenalty = Penalty.PenaltyType.Any,
|
ClientPenalty = Penalty.PenaltyType.Any,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
DetectionPenaltyResult result = null;
|
DetectionPenaltyResult result = null;
|
||||||
LastWeapon = kill.Weapon;
|
LastWeapon = hit.Weapon;
|
||||||
|
|
||||||
|
HitLocationCount[hit.HitLoc]++;
|
||||||
|
HitCount++;
|
||||||
|
|
||||||
HitLocationCount[kill.HitLoc]++;
|
|
||||||
if (!isDamage)
|
if (!isDamage)
|
||||||
{
|
{
|
||||||
Kills++;
|
Kills++;
|
||||||
}
|
}
|
||||||
|
|
||||||
HitCount++;
|
|
||||||
|
|
||||||
#region VIEWANGLES
|
#region VIEWANGLES
|
||||||
if (kill.AnglesList.Count >= 2)
|
if (hit.AnglesList.Count >= 2)
|
||||||
{
|
{
|
||||||
double realAgainstPredict = Vector3.ViewAngleDistance(kill.AnglesList[0], kill.AnglesList[1], kill.ViewAngles);
|
double realAgainstPredict = Vector3.ViewAngleDistance(hit.AnglesList[0], hit.AnglesList[1], hit.ViewAngles);
|
||||||
|
|
||||||
// LIFETIME
|
// LIFETIME
|
||||||
var hitLoc = ClientStats.HitLocations
|
var hitLoc = ClientStats.HitLocations
|
||||||
.First(hl => hl.Location == kill.HitLoc);
|
.First(hl => hl.Location == hit.HitLoc);
|
||||||
|
|
||||||
float previousAverage = hitLoc.HitOffsetAverage;
|
float previousAverage = hitLoc.HitOffsetAverage;
|
||||||
double newAverage = (previousAverage * (hitLoc.HitCount - 1) + realAgainstPredict) / hitLoc.HitCount;
|
double newAverage = (previousAverage * (hitLoc.HitCount - 1) + realAgainstPredict) / hitLoc.HitCount;
|
||||||
@ -88,11 +96,11 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
if (hitLoc.HitOffsetAverage > Thresholds.MaxOffset(hitLoc.HitCount) &&
|
if (hitLoc.HitOffsetAverage > Thresholds.MaxOffset(hitLoc.HitCount) &&
|
||||||
hitLoc.HitCount > 100)
|
hitLoc.HitCount > 100)
|
||||||
{
|
{
|
||||||
Log.WriteDebug("*** Reached Max Lifetime Average for Angle Difference ***");
|
//Log.WriteDebug("*** Reached Max Lifetime Average for Angle Difference ***");
|
||||||
Log.WriteDebug($"Lifetime Average = {newAverage}");
|
//Log.WriteDebug($"Lifetime Average = {newAverage}");
|
||||||
Log.WriteDebug($"Bone = {hitLoc.Location}");
|
//Log.WriteDebug($"Bone = {hitLoc.Location}");
|
||||||
Log.WriteDebug($"HitCount = {hitLoc.HitCount}");
|
//Log.WriteDebug($"HitCount = {hitLoc.HitCount}");
|
||||||
Log.WriteDebug($"ID = {kill.AttackerId}");
|
//Log.WriteDebug($"ID = {hit.AttackerId}");
|
||||||
|
|
||||||
result = new DetectionPenaltyResult()
|
result = new DetectionPenaltyResult()
|
||||||
{
|
{
|
||||||
@ -110,10 +118,10 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
if (sessAverage > Thresholds.MaxOffset(HitCount) &&
|
if (sessAverage > Thresholds.MaxOffset(HitCount) &&
|
||||||
HitCount > 30)
|
HitCount > 30)
|
||||||
{
|
{
|
||||||
Log.WriteDebug("*** Reached Max Session Average for Angle Difference ***");
|
//Log.WriteDebug("*** Reached Max Session Average for Angle Difference ***");
|
||||||
Log.WriteDebug($"Session Average = {sessAverage}");
|
//Log.WriteDebug($"Session Average = {sessAverage}");
|
||||||
Log.WriteDebug($"HitCount = {HitCount}");
|
//Log.WriteDebug($"HitCount = {HitCount}");
|
||||||
Log.WriteDebug($"ID = {kill.AttackerId}");
|
//Log.WriteDebug($"ID = {hit.AttackerId}");
|
||||||
|
|
||||||
result = new DetectionPenaltyResult()
|
result = new DetectionPenaltyResult()
|
||||||
{
|
{
|
||||||
@ -130,8 +138,11 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
double currentStrain = Strain.GetStrain(isDamage, kill.Damage, kill.Distance / 0.0254, kill.ViewAngles, Math.Max(50, kill.TimeOffset - LastOffset));
|
double currentStrain = Strain.GetStrain(isDamage, hit.Damage, hit.Distance / 0.0254, hit.ViewAngles, Math.Max(50, hit.TimeOffset - LastOffset));
|
||||||
LastOffset = kill.TimeOffset;
|
#if DEBUG == true
|
||||||
|
Log.WriteDebug($"Current Strain: {currentStrain}");
|
||||||
|
#endif
|
||||||
|
LastOffset = hit.TimeOffset;
|
||||||
|
|
||||||
if (currentStrain > ClientStats.MaxStrain)
|
if (currentStrain > ClientStats.MaxStrain)
|
||||||
{
|
{
|
||||||
@ -139,8 +150,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
}
|
}
|
||||||
|
|
||||||
// flag
|
// flag
|
||||||
if (currentStrain > Thresholds.MaxStrainFlag &&
|
if (currentStrain > Thresholds.MaxStrainFlag)
|
||||||
HitCount >= 10)
|
|
||||||
{
|
{
|
||||||
result = new DetectionPenaltyResult()
|
result = new DetectionPenaltyResult()
|
||||||
{
|
{
|
||||||
@ -153,7 +163,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
|
|
||||||
// ban
|
// ban
|
||||||
if (currentStrain > Thresholds.MaxStrainBan &&
|
if (currentStrain > Thresholds.MaxStrainBan &&
|
||||||
HitCount >= 15)
|
HitCount >= 5)
|
||||||
{
|
{
|
||||||
result = new DetectionPenaltyResult()
|
result = new DetectionPenaltyResult()
|
||||||
{
|
{
|
||||||
@ -163,11 +173,6 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
Type = DetectionType.Strain
|
Type = DetectionType.Strain
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
Log.WriteDebug($"Current Strain: {currentStrain}");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region SESSION_RATIOS
|
#region SESSION_RATIOS
|
||||||
@ -197,13 +202,16 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
if (currentHeadshotRatio > maxHeadshotLerpValueForBan)
|
if (currentHeadshotRatio > maxHeadshotLerpValueForBan)
|
||||||
{
|
{
|
||||||
Log.WriteDebug("**Maximum Headshot Ratio Reached For Ban**");
|
Log.WriteDebug("**Maximum Headshot Ratio Reached For Ban**");
|
||||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
Log.WriteDebug($"ClientId: {hit.AttackerId}");
|
||||||
Log.WriteDebug($"**HitCount: {HitCount}");
|
Log.WriteDebug($"**HitCount: {HitCount}");
|
||||||
Log.WriteDebug($"**Ratio {currentHeadshotRatio}");
|
Log.WriteDebug($"**Ratio {currentHeadshotRatio}");
|
||||||
Log.WriteDebug($"**MaxRatio {maxHeadshotLerpValueForFlag}");
|
Log.WriteDebug($"**MaxRatio {maxHeadshotLerpValueForFlag}");
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
foreach (var kvp in HitLocationCount)
|
foreach (var kvp in HitLocationCount)
|
||||||
|
{
|
||||||
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
Log.WriteDebug(sb.ToString());
|
Log.WriteDebug(sb.ToString());
|
||||||
|
|
||||||
result = new DetectionPenaltyResult()
|
result = new DetectionPenaltyResult()
|
||||||
@ -218,13 +226,16 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log.WriteDebug("**Maximum Headshot Ratio Reached For Flag**");
|
Log.WriteDebug("**Maximum Headshot Ratio Reached For Flag**");
|
||||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
Log.WriteDebug($"ClientId: {hit.AttackerId}");
|
||||||
Log.WriteDebug($"**HitCount: {HitCount}");
|
Log.WriteDebug($"**HitCount: {HitCount}");
|
||||||
Log.WriteDebug($"**Ratio {currentHeadshotRatio}");
|
Log.WriteDebug($"**Ratio {currentHeadshotRatio}");
|
||||||
Log.WriteDebug($"**MaxRatio {maxHeadshotLerpValueForFlag}");
|
Log.WriteDebug($"**MaxRatio {maxHeadshotLerpValueForFlag}");
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
foreach (var kvp in HitLocationCount)
|
foreach (var kvp in HitLocationCount)
|
||||||
|
{
|
||||||
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
Log.WriteDebug(sb.ToString());
|
Log.WriteDebug(sb.ToString());
|
||||||
|
|
||||||
result = new DetectionPenaltyResult()
|
result = new DetectionPenaltyResult()
|
||||||
@ -247,13 +258,16 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
if (currentMaxBoneRatio > maxBoneRatioLerpValueForBan)
|
if (currentMaxBoneRatio > maxBoneRatioLerpValueForBan)
|
||||||
{
|
{
|
||||||
Log.WriteDebug("**Maximum Bone Ratio Reached For Ban**");
|
Log.WriteDebug("**Maximum Bone Ratio Reached For Ban**");
|
||||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
Log.WriteDebug($"ClientId: {hit.AttackerId}");
|
||||||
Log.WriteDebug($"**HitCount: {HitCount}");
|
Log.WriteDebug($"**HitCount: {HitCount}");
|
||||||
Log.WriteDebug($"**Ratio {currentMaxBoneRatio}");
|
Log.WriteDebug($"**Ratio {currentMaxBoneRatio}");
|
||||||
Log.WriteDebug($"**MaxRatio {maxBoneRatioLerpValueForBan}");
|
Log.WriteDebug($"**MaxRatio {maxBoneRatioLerpValueForBan}");
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
foreach (var kvp in HitLocationCount)
|
foreach (var kvp in HitLocationCount)
|
||||||
|
{
|
||||||
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
Log.WriteDebug(sb.ToString());
|
Log.WriteDebug(sb.ToString());
|
||||||
|
|
||||||
result = new DetectionPenaltyResult()
|
result = new DetectionPenaltyResult()
|
||||||
@ -268,13 +282,16 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log.WriteDebug("**Maximum Bone Ratio Reached For Flag**");
|
Log.WriteDebug("**Maximum Bone Ratio Reached For Flag**");
|
||||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
Log.WriteDebug($"ClientId: {hit.AttackerId}");
|
||||||
Log.WriteDebug($"**HitCount: {HitCount}");
|
Log.WriteDebug($"**HitCount: {HitCount}");
|
||||||
Log.WriteDebug($"**Ratio {currentMaxBoneRatio}");
|
Log.WriteDebug($"**Ratio {currentMaxBoneRatio}");
|
||||||
Log.WriteDebug($"**MaxRatio {maxBoneRatioLerpValueForFlag}");
|
Log.WriteDebug($"**MaxRatio {maxBoneRatioLerpValueForFlag}");
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
foreach (var kvp in HitLocationCount)
|
foreach (var kvp in HitLocationCount)
|
||||||
|
{
|
||||||
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
Log.WriteDebug(sb.ToString());
|
Log.WriteDebug(sb.ToString());
|
||||||
|
|
||||||
result = new DetectionPenaltyResult()
|
result = new DetectionPenaltyResult()
|
||||||
@ -308,15 +325,15 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
|
|
||||||
if (currentChestAbdomenRatio > chestAbdomenLerpValueForBan && chestHits >= Thresholds.MediumSampleMinKills + 30)
|
if (currentChestAbdomenRatio > chestAbdomenLerpValueForBan && chestHits >= Thresholds.MediumSampleMinKills + 30)
|
||||||
{
|
{
|
||||||
Log.WriteDebug("**Maximum Chest/Abdomen Ratio Reached For Ban**");
|
//Log.WriteDebug("**Maximum Chest/Abdomen Ratio Reached For Ban**");
|
||||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
//Log.WriteDebug($"ClientId: {hit.AttackerId}");
|
||||||
Log.WriteDebug($"**Chest Hits: {chestHits}");
|
//Log.WriteDebug($"**Chest Hits: {chestHits}");
|
||||||
Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
//Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
||||||
Log.WriteDebug($"**MaxRatio {chestAbdomenLerpValueForBan}");
|
//Log.WriteDebug($"**MaxRatio {chestAbdomenLerpValueForBan}");
|
||||||
var sb = new StringBuilder();
|
//var sb = new StringBuilder();
|
||||||
foreach (var kvp in HitLocationCount)
|
//foreach (var kvp in HitLocationCount)
|
||||||
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
// sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
||||||
Log.WriteDebug(sb.ToString());
|
//Log.WriteDebug(sb.ToString());
|
||||||
|
|
||||||
result = new DetectionPenaltyResult()
|
result = new DetectionPenaltyResult()
|
||||||
{
|
{
|
||||||
@ -329,16 +346,15 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log.WriteDebug("**Maximum Chest/Abdomen Ratio Reached For Flag**");
|
//Log.WriteDebug("**Maximum Chest/Abdomen Ratio Reached For Flag**");
|
||||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
//Log.WriteDebug($"ClientId: {hit.AttackerId}");
|
||||||
Log.WriteDebug($"**Chest Hits: {chestHits}");
|
//Log.WriteDebug($"**Chest Hits: {chestHits}");
|
||||||
Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
//Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
||||||
Log.WriteDebug($"**MaxRatio {chestAbdomenRatioLerpValueForFlag}");
|
//Log.WriteDebug($"**MaxRatio {chestAbdomenRatioLerpValueForFlag}");
|
||||||
var sb = new StringBuilder();
|
//var sb = new StringBuilder();
|
||||||
foreach (var kvp in HitLocationCount)
|
//foreach (var kvp in HitLocationCount)
|
||||||
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
// sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
||||||
Log.WriteDebug(sb.ToString());
|
//Log.WriteDebug(sb.ToString());
|
||||||
// Log.WriteDebug($"ThresholdReached: {AboveThresholdCount}");
|
|
||||||
|
|
||||||
result = new DetectionPenaltyResult()
|
result = new DetectionPenaltyResult()
|
||||||
{
|
{
|
||||||
@ -356,31 +372,31 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
|
|
||||||
Tracker.OnChange(new EFACSnapshot()
|
Tracker.OnChange(new EFACSnapshot()
|
||||||
{
|
{
|
||||||
When = kill.When,
|
When = hit.When,
|
||||||
ClientId = ClientStats.ClientId,
|
ClientId = ClientStats.ClientId,
|
||||||
SessionAngleOffset = AngleDifferenceAverage,
|
SessionAngleOffset = AngleDifferenceAverage,
|
||||||
CurrentSessionLength = (int)(DateTime.UtcNow - ConnectionTime).TotalSeconds,
|
CurrentSessionLength = (int)(DateTime.UtcNow - ConnectionTime).TotalSeconds,
|
||||||
CurrentStrain = currentStrain,
|
CurrentStrain = currentStrain,
|
||||||
CurrentViewAngle = kill.ViewAngles,
|
CurrentViewAngle = hit.ViewAngles,
|
||||||
Hits = HitCount,
|
Hits = HitCount,
|
||||||
Kills = Kills,
|
Kills = Kills,
|
||||||
Deaths = ClientStats.SessionDeaths,
|
Deaths = ClientStats.SessionDeaths,
|
||||||
HitDestinationId = kill.DeathOrigin.Vector3Id,
|
HitDestinationId = hit.DeathOrigin.Vector3Id,
|
||||||
HitDestination = kill.DeathOrigin,
|
HitDestination = hit.DeathOrigin,
|
||||||
HitOriginId = kill.KillOrigin.Vector3Id,
|
HitOriginId = hit.KillOrigin.Vector3Id,
|
||||||
HitOrigin = kill.KillOrigin,
|
HitOrigin = hit.KillOrigin,
|
||||||
EloRating = ClientStats.EloRating,
|
EloRating = ClientStats.EloRating,
|
||||||
HitLocation = kill.HitLoc,
|
HitLocation = hit.HitLoc,
|
||||||
LastStrainAngle = Strain.LastAngle,
|
LastStrainAngle = Strain.LastAngle,
|
||||||
PredictedViewAngles = kill.AnglesList,
|
PredictedViewAngles = hit.AnglesList,
|
||||||
// this is in "meters"
|
// this is in "meters"
|
||||||
Distance = kill.Distance,
|
Distance = hit.Distance,
|
||||||
SessionScore = ClientStats.SessionScore,
|
SessionScore = ClientStats.SessionScore,
|
||||||
HitType = kill.DeathType,
|
HitType = hit.DeathType,
|
||||||
SessionSPM = ClientStats.SessionSPM,
|
SessionSPM = ClientStats.SessionSPM,
|
||||||
StrainAngleBetween = Strain.LastDistance,
|
StrainAngleBetween = Strain.LastDistance,
|
||||||
TimeSinceLastEvent = (int)Strain.LastDeltaTime,
|
TimeSinceLastEvent = (int)Strain.LastDeltaTime,
|
||||||
WeaponId = kill.Weapon
|
WeaponId = hit.Weapon
|
||||||
});
|
});
|
||||||
|
|
||||||
return result ?? new DetectionPenaltyResult()
|
return result ?? new DetectionPenaltyResult()
|
||||||
@ -409,15 +425,15 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
|
|
||||||
if (currentChestAbdomenRatio > chestAbdomenLerpValueForBan)
|
if (currentChestAbdomenRatio > chestAbdomenLerpValueForBan)
|
||||||
{
|
{
|
||||||
Log.WriteDebug("**Maximum Lifetime Chest/Abdomen Ratio Reached For Ban**");
|
//Log.WriteDebug("**Maximum Lifetime Chest/Abdomen Ratio Reached For Ban**");
|
||||||
Log.WriteDebug($"ClientId: {stats.ClientId}");
|
//Log.WriteDebug($"ClientId: {stats.ClientId}");
|
||||||
Log.WriteDebug($"**Total Chest Hits: {totalChestHits}");
|
//Log.WriteDebug($"**Total Chest Hits: {totalChestHits}");
|
||||||
Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
//Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
||||||
Log.WriteDebug($"**MaxRatio {chestAbdomenLerpValueForBan}");
|
//Log.WriteDebug($"**MaxRatio {chestAbdomenLerpValueForBan}");
|
||||||
var sb = new StringBuilder();
|
//var sb = new StringBuilder();
|
||||||
foreach (var location in stats.HitLocations)
|
//foreach (var location in stats.HitLocations)
|
||||||
sb.Append($"HitLocation: {location.Location} -> {location.HitCount}\r\n");
|
// sb.Append($"HitLocation: {location.Location} -> {location.HitCount}\r\n");
|
||||||
Log.WriteDebug(sb.ToString());
|
//Log.WriteDebug(sb.ToString());
|
||||||
|
|
||||||
return new DetectionPenaltyResult()
|
return new DetectionPenaltyResult()
|
||||||
{
|
{
|
||||||
@ -430,15 +446,15 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log.WriteDebug("**Maximum Lifetime Chest/Abdomen Ratio Reached For Flag**");
|
//Log.WriteDebug("**Maximum Lifetime Chest/Abdomen Ratio Reached For Flag**");
|
||||||
Log.WriteDebug($"ClientId: {stats.ClientId}");
|
//Log.WriteDebug($"ClientId: {stats.ClientId}");
|
||||||
Log.WriteDebug($"**Total Chest Hits: {totalChestHits}");
|
//Log.WriteDebug($"**Total Chest Hits: {totalChestHits}");
|
||||||
Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
//Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
||||||
Log.WriteDebug($"**MaxRatio {chestAbdomenRatioLerpValueForFlag}");
|
//Log.WriteDebug($"**MaxRatio {chestAbdomenRatioLerpValueForFlag}");
|
||||||
var sb = new StringBuilder();
|
//var sb = new StringBuilder();
|
||||||
foreach (var location in stats.HitLocations)
|
//foreach (var location in stats.HitLocations)
|
||||||
sb.Append($"HitLocation: {location.Location} -> {location.HitCount}\r\n");
|
// sb.Append($"HitLocation: {location.Location} -> {location.HitCount}\r\n");
|
||||||
Log.WriteDebug(sb.ToString());
|
//Log.WriteDebug(sb.ToString());
|
||||||
|
|
||||||
return new DetectionPenaltyResult()
|
return new DetectionPenaltyResult()
|
||||||
{
|
{
|
||||||
|
@ -14,8 +14,6 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
public Vector3 LastAngle { get; private set; }
|
public Vector3 LastAngle { get; private set; }
|
||||||
public double LastDeltaTime { get; private set; }
|
public double LastDeltaTime { get; private set; }
|
||||||
|
|
||||||
public int TimesReachedMaxStrain { get; private set; }
|
|
||||||
|
|
||||||
public double GetStrain(bool isDamage, int damage, double killDistance, Vector3 newAngle, double deltaTime)
|
public double GetStrain(bool isDamage, int damage, double killDistance, Vector3 newAngle, double deltaTime)
|
||||||
{
|
{
|
||||||
if (LastAngle == null)
|
if (LastAngle == null)
|
||||||
@ -25,14 +23,15 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
|
|
||||||
double decayFactor = GetDecay(deltaTime);
|
double decayFactor = GetDecay(deltaTime);
|
||||||
CurrentStrain *= decayFactor;
|
CurrentStrain *= decayFactor;
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
Console.WriteLine($"Decay Factor = {decayFactor} ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
double[] distance = Helpers.Extensions.AngleStuff(newAngle, LastAngle);
|
double[] distance = Helpers.Extensions.AngleStuff(newAngle, LastAngle);
|
||||||
LastDistance = distance[0] + distance[1];
|
LastDistance = distance[0] + distance[1];
|
||||||
|
|
||||||
|
#if DEBUG == true
|
||||||
|
Console.WriteLine($"Angle Between = {LastDistance}");
|
||||||
|
Console.WriteLine($"Distance From Target = {killDistance}");
|
||||||
|
Console.WriteLine($"Time Offset = {deltaTime}");
|
||||||
|
Console.WriteLine($"Decay Factor = {decayFactor} ");
|
||||||
|
#endif
|
||||||
// this happens on first kill
|
// this happens on first kill
|
||||||
if ((distance[0] == 0 && distance[1] == 0) ||
|
if ((distance[0] == 0 && distance[1] == 0) ||
|
||||||
deltaTime == 0 ||
|
deltaTime == 0 ||
|
||||||
@ -41,14 +40,11 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
return CurrentStrain;
|
return CurrentStrain;
|
||||||
}
|
}
|
||||||
|
|
||||||
double newStrain = Math.Pow(distance[0] + distance[1], 0.99) / deltaTime;
|
double newStrain = Math.Pow(LastDistance, 0.99) / deltaTime;
|
||||||
newStrain *= killDistance / 1000.0;
|
newStrain *= killDistance / 1000.0;
|
||||||
|
|
||||||
CurrentStrain += newStrain;
|
CurrentStrain += newStrain;
|
||||||
|
|
||||||
if (CurrentStrain > Thresholds.MaxStrainBan)
|
|
||||||
TimesReachedMaxStrain++;
|
|
||||||
|
|
||||||
LastAngle = newAngle;
|
LastAngle = newAngle;
|
||||||
return CurrentStrain;
|
return CurrentStrain;
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,8 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
public const int HighSampleMinKills = 100;
|
public const int HighSampleMinKills = 100;
|
||||||
public const double KillTimeThreshold = 0.2;
|
public const double KillTimeThreshold = 0.2;
|
||||||
|
|
||||||
public const double MaxStrainBan = 1.12;
|
public const double MaxStrainBan = 0.9;
|
||||||
|
|
||||||
//=exp((MAX(-3.07+(-3.07/sqrt(J20)),-3.07-(-3.07/sqrt(J20))))+(4*(0.869)))
|
|
||||||
public static double MaxOffset(int sampleSize) => Math.Exp(Math.Max(-3.07 + (-3.07 / Math.Sqrt(sampleSize)), -3.07 - (-3.07 / Math.Sqrt(sampleSize))) + 4 * (0.869));
|
public static double MaxOffset(int sampleSize) => Math.Exp(Math.Max(-3.07 + (-3.07 / Math.Sqrt(sampleSize)), -3.07 - (-3.07 / Math.Sqrt(sampleSize))) + 4 * (0.869));
|
||||||
public const double MaxStrainFlag = 0.36;
|
public const double MaxStrainFlag = 0.36;
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ using SharedLibraryCore.Objects;
|
|||||||
using IW4MAdmin.Plugins.Stats.Models;
|
using IW4MAdmin.Plugins.Stats.Models;
|
||||||
using SharedLibraryCore.Database;
|
using SharedLibraryCore.Database;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using SharedLibraryCore.Database.Models;
|
||||||
|
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Stats.Commands
|
namespace IW4MAdmin.Plugins.Stats.Commands
|
||||||
{
|
{
|
||||||
@ -15,7 +17,8 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
{
|
{
|
||||||
public static async Task<List<string>> GetMostPlayed(Server s)
|
public static async Task<List<string>> GetMostPlayed(Server s)
|
||||||
{
|
{
|
||||||
int serverId = s.GetHashCode();
|
long serverId = await StatManager.GetIdForServer(s);
|
||||||
|
|
||||||
List<string> mostPlayed = new List<string>()
|
List<string> mostPlayed = new List<string>()
|
||||||
{
|
{
|
||||||
$"^5--{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_MOSTPLAYED_TEXT"]}--"
|
$"^5--{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_MOSTPLAYED_TEXT"]}--"
|
||||||
@ -34,7 +37,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
join alias in db.Aliases
|
join alias in db.Aliases
|
||||||
on client.CurrentAliasId equals alias.AliasId
|
on client.CurrentAliasId equals alias.AliasId
|
||||||
where stats.ServerId == serverId
|
where stats.ServerId == serverId
|
||||||
where client.Level != Player.Permission.Banned
|
where client.Level != EFClient.Permission.Banned
|
||||||
where client.LastConnection >= thirtyDaysAgo
|
where client.LastConnection >= thirtyDaysAgo
|
||||||
orderby stats.Kills descending
|
orderby stats.Kills descending
|
||||||
select new
|
select new
|
||||||
@ -55,7 +58,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
return mostPlayed;
|
return mostPlayed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MostPlayed() : base("mostplayed", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_MOSTPLAYED_DESC"], "mp", Player.Permission.User, false) { }
|
public MostPlayed() : base("mostplayed", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_MOSTPLAYED_DESC"], "mp", EFClient.Permission.User, false) { }
|
||||||
|
|
||||||
public override async Task ExecuteAsync(GameEvent E)
|
public override async Task ExecuteAsync(GameEvent E)
|
||||||
{
|
{
|
||||||
|
@ -1,32 +1,30 @@
|
|||||||
using SharedLibraryCore;
|
using IW4MAdmin.Plugins.Stats.Models;
|
||||||
using SharedLibraryCore.Objects;
|
|
||||||
using IW4MAdmin.Plugins.Stats.Models;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using SharedLibraryCore.Database;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Database;
|
||||||
|
using SharedLibraryCore.Database.Models;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Stats.Commands
|
namespace IW4MAdmin.Plugins.Stats.Commands
|
||||||
{
|
{
|
||||||
public class ResetStats : Command
|
public class ResetStats : Command
|
||||||
{
|
{
|
||||||
public ResetStats() : base("resetstats", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_RESET_DESC"], "rs", Player.Permission.User, false) { }
|
public ResetStats() : base("resetstats", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_RESET_DESC"], "rs", EFClient.Permission.User, false) { }
|
||||||
|
|
||||||
public override async Task ExecuteAsync(GameEvent E)
|
public override async Task ExecuteAsync(GameEvent E)
|
||||||
{
|
{
|
||||||
if (E.Origin.ClientNumber >= 0)
|
if (E.Origin.ClientNumber >= 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
int serverId = E.Owner.GetHashCode();
|
long serverId = await Helpers.StatManager.GetIdForServer(E.Owner);
|
||||||
|
|
||||||
EFClientStatistics clientStats;
|
EFClientStatistics clientStats;
|
||||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||||
{
|
{
|
||||||
clientStats = await ctx.Set<EFClientStatistics>()
|
clientStats = await ctx.Set<EFClientStatistics>()
|
||||||
.Where(s => s.ClientId == E.Origin.ClientId && s.ServerId == serverId)
|
.Where(s => s.ClientId == E.Origin.ClientId)
|
||||||
|
.Where(s => s.ServerId == serverId)
|
||||||
.FirstAsync();
|
.FirstAsync();
|
||||||
|
|
||||||
clientStats.Deaths = 0;
|
clientStats.Deaths = 0;
|
||||||
@ -38,7 +36,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
clientStats.EloRating = 200.0;
|
clientStats.EloRating = 200.0;
|
||||||
|
|
||||||
// reset the cached version
|
// reset the cached version
|
||||||
Plugin.Manager.ResetStats(E.Origin.ClientId, E.Owner.GetHashCode());
|
Plugin.Manager.ResetStats(E.Origin.ClientId, serverId);
|
||||||
|
|
||||||
// fixme: this doesn't work properly when another context exists
|
// fixme: this doesn't work properly when another context exists
|
||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
|
@ -9,15 +9,16 @@ using SharedLibraryCore.Services;
|
|||||||
using IW4MAdmin.Plugins.Stats.Models;
|
using IW4MAdmin.Plugins.Stats.Models;
|
||||||
using SharedLibraryCore.Database;
|
using SharedLibraryCore.Database;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using SharedLibraryCore.Database.Models;
|
||||||
|
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Stats.Commands
|
namespace IW4MAdmin.Plugins.Stats.Commands
|
||||||
{
|
{
|
||||||
class TopStats : Command
|
class TopStats : Command
|
||||||
{
|
{
|
||||||
|
|
||||||
public static async Task<List<string>> GetTopStats(Server s)
|
public static async Task<List<string>> GetTopStats(Server s)
|
||||||
{
|
{
|
||||||
int serverId = s.GetHashCode();
|
long serverId = await StatManager.GetIdForServer(s);
|
||||||
List<string> topStatsText = new List<string>()
|
List<string> topStatsText = new List<string>()
|
||||||
{
|
{
|
||||||
$"^5--{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_TOP_TEXT"]}--"
|
$"^5--{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_TOP_TEXT"]}--"
|
||||||
@ -34,7 +35,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
on client.CurrentAliasId equals alias.AliasId
|
on client.CurrentAliasId equals alias.AliasId
|
||||||
where stats.ServerId == serverId
|
where stats.ServerId == serverId
|
||||||
where stats.TimePlayed >= Plugin.Config.Configuration().TopPlayersMinPlayTime
|
where stats.TimePlayed >= Plugin.Config.Configuration().TopPlayersMinPlayTime
|
||||||
where client.Level != Player.Permission.Banned
|
where client.Level != EFClient.Permission.Banned
|
||||||
where client.LastConnection >= fifteenDaysAgo
|
where client.LastConnection >= fifteenDaysAgo
|
||||||
orderby stats.Performance descending
|
orderby stats.Performance descending
|
||||||
select new
|
select new
|
||||||
@ -67,7 +68,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
return topStatsText;
|
return topStatsText;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TopStats() : base("topstats", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_TOP_DESC"], "ts", Player.Permission.User, false) { }
|
public TopStats() : base("topstats", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_TOP_DESC"], "ts", EFClient.Permission.User, false) { }
|
||||||
|
|
||||||
public override async Task ExecuteAsync(GameEvent E)
|
public override async Task ExecuteAsync(GameEvent E)
|
||||||
{
|
{
|
||||||
|
@ -9,12 +9,14 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using SharedLibraryCore.Database;
|
using SharedLibraryCore.Database;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||||
|
using SharedLibraryCore.Database.Models;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Stats.Commands
|
namespace IW4MAdmin.Plugins.Stats.Commands
|
||||||
{
|
{
|
||||||
public class CViewStats : Command
|
public class CViewStats : Command
|
||||||
{
|
{
|
||||||
public CViewStats() : base("stats", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_VIEW_DESC"], "xlrstats", Player.Permission.User, false, new CommandArgument[]
|
public CViewStats() : base("stats", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_VIEW_DESC"], "xlrstats", EFClient.Permission.User, false, new CommandArgument[]
|
||||||
{
|
{
|
||||||
new CommandArgument()
|
new CommandArgument()
|
||||||
{
|
{
|
||||||
@ -41,20 +43,26 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int serverId = E.Owner.GetHashCode();
|
long serverId = await StatManager.GetIdForServer(E.Owner);
|
||||||
|
|
||||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||||
{
|
{
|
||||||
if (E.Target != null)
|
if (E.Target != null)
|
||||||
{
|
{
|
||||||
|
int performanceRanking = await StatManager.GetClientOverallRanking(E.Target.ClientId);
|
||||||
|
string performanceRankingString = performanceRanking == 0 ? loc["WEBFRONT_STATS_INDEX_UNRANKED"] : $"{loc["WEBFRONT_STATS_INDEX_RANKED"]} #{performanceRanking}";
|
||||||
|
|
||||||
pStats = (await ctx.Set<EFClientStatistics>().FirstAsync(c => c.ServerId == serverId && c.ClientId == E.Target.ClientId));
|
pStats = (await ctx.Set<EFClientStatistics>().FirstAsync(c => c.ServerId == serverId && c.ClientId == E.Target.ClientId));
|
||||||
statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()}";
|
statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()} | {performanceRankingString}";
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
int performanceRanking = await StatManager.GetClientOverallRanking(E.Origin.ClientId);
|
||||||
|
string performanceRankingString = performanceRanking == 0 ? loc["WEBFRONT_STATS_INDEX_UNRANKED"] : $"{loc["WEBFRONT_STATS_INDEX_RANKED"]} #{performanceRanking}";
|
||||||
|
|
||||||
pStats = (await ctx.Set<EFClientStatistics>().FirstAsync((c => c.ServerId == serverId && c.ClientId == E.Origin.ClientId)));
|
pStats = (await ctx.Set<EFClientStatistics>().FirstAsync((c => c.ServerId == serverId && c.ClientId == E.Origin.ClientId)));
|
||||||
statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()}";
|
statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()} | {performanceRankingString}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,53 +1,53 @@
|
|||||||
using System;
|
using IW4MAdmin.Plugins.Stats.Cheat;
|
||||||
using System.Collections.Concurrent;
|
using IW4MAdmin.Plugins.Stats.Models;
|
||||||
using System.Collections.Generic;
|
using IW4MAdmin.Plugins.Stats.Web.Dtos;
|
||||||
using System.Linq;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Database;
|
||||||
|
using SharedLibraryCore.Database.Models;
|
||||||
using SharedLibraryCore.Helpers;
|
using SharedLibraryCore.Helpers;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using SharedLibraryCore.Objects;
|
using SharedLibraryCore.Objects;
|
||||||
using SharedLibraryCore.Commands;
|
using System;
|
||||||
using IW4MAdmin.Plugins.Stats.Models;
|
using System.Collections.Concurrent;
|
||||||
using System.Text.RegularExpressions;
|
using System.Collections.Generic;
|
||||||
using IW4MAdmin.Plugins.Stats.Web.Dtos;
|
using System.Linq;
|
||||||
using SharedLibraryCore.Database;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using SharedLibraryCore.Database.Models;
|
|
||||||
using SharedLibraryCore.Services;
|
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Stats.Helpers
|
namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||||
{
|
{
|
||||||
public class StatManager
|
public class StatManager
|
||||||
{
|
{
|
||||||
private ConcurrentDictionary<int, ServerStats> Servers;
|
private ConcurrentDictionary<long, ServerStats> Servers;
|
||||||
private ILogger Log;
|
private ILogger Log;
|
||||||
private readonly IManager Manager;
|
private readonly IManager Manager;
|
||||||
|
|
||||||
|
|
||||||
private readonly SemaphoreSlim OnProcessingPenalty;
|
private readonly SemaphoreSlim OnProcessingPenalty;
|
||||||
private readonly SemaphoreSlim OnProcessingSensitive;
|
private readonly SemaphoreSlim OnProcessingSensitive;
|
||||||
|
|
||||||
public StatManager(IManager mgr)
|
public StatManager(IManager mgr)
|
||||||
{
|
{
|
||||||
Servers = new ConcurrentDictionary<int, ServerStats>();
|
Servers = new ConcurrentDictionary<long, ServerStats>();
|
||||||
Log = mgr.GetLogger(0);
|
Log = mgr.GetLogger(0);
|
||||||
Manager = mgr;
|
Manager = mgr;
|
||||||
OnProcessingPenalty = new SemaphoreSlim(1, 1);
|
OnProcessingPenalty = new SemaphoreSlim(1, 1);
|
||||||
OnProcessingSensitive = new SemaphoreSlim(1, 1);
|
OnProcessingSensitive = new SemaphoreSlim(1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EFClientStatistics GetClientStats(int clientId, int serverId) => Servers[serverId].PlayerStats[clientId];
|
public EFClientStatistics GetClientStats(int clientId, long serverId)
|
||||||
|
{
|
||||||
|
return Servers[serverId].PlayerStats[clientId];
|
||||||
|
}
|
||||||
|
|
||||||
public static Expression<Func<EFRating, bool>> GetRankingFunc(int? serverId = null)
|
public static Expression<Func<EFRating, bool>> GetRankingFunc(long? serverId = null)
|
||||||
{
|
{
|
||||||
var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15);
|
var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15);
|
||||||
return (r) => r.ServerId == serverId &&
|
return (r) => r.ServerId == serverId &&
|
||||||
r.When > fifteenDaysAgo &&
|
r.When > fifteenDaysAgo &&
|
||||||
r.RatingHistory.Client.Level != Player.Permission.Banned &&
|
r.RatingHistory.Client.Level != EFClient.Permission.Banned &&
|
||||||
r.Newest &&
|
r.Newest &&
|
||||||
r.ActivityAmount >= Plugin.Config.Configuration().TopPlayersMinPlayTime;
|
r.ActivityAmount >= Plugin.Config.Configuration().TopPlayersMinPlayTime;
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="clientId">client id of the player</param>
|
/// <param name="clientId">client id of the player</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<int> GetClientOverallRanking(int clientId)
|
public static async Task<int> GetClientOverallRanking(int clientId)
|
||||||
{
|
{
|
||||||
using (var context = new DatabaseContext(true))
|
using (var context = new DatabaseContext(true))
|
||||||
{
|
{
|
||||||
@ -191,21 +191,36 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
// insert the server if it does not exist
|
// insert the server if it does not exist
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int serverId = sv.GetHashCode();
|
long serverId = GetIdForServer(sv).Result;
|
||||||
EFServer server;
|
EFServer server;
|
||||||
|
|
||||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||||
{
|
{
|
||||||
var serverSet = ctx.Set<EFServer>();
|
var serverSet = ctx.Set<EFServer>();
|
||||||
// get the server from the database if it exists, otherwise create and insert a new one
|
// get the server from the database if it exists, otherwise create and insert a new one
|
||||||
server = serverSet.FirstOrDefault(c => c.ServerId == serverId);
|
server = serverSet.FirstOrDefault(s => s.ServerId == serverId);
|
||||||
|
|
||||||
|
// the server might be using legacy server id
|
||||||
|
if (server == null)
|
||||||
|
{
|
||||||
|
server = serverSet.FirstOrDefault(s => s.EndPoint == sv.ToString());
|
||||||
|
|
||||||
|
if (server != null)
|
||||||
|
{
|
||||||
|
// this provides a way to identify legacy server entries
|
||||||
|
server.EndPoint = sv.ToString();
|
||||||
|
ctx.Update(server);
|
||||||
|
ctx.SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// server has never been added before
|
||||||
if (server == null)
|
if (server == null)
|
||||||
{
|
{
|
||||||
server = new EFServer()
|
server = new EFServer()
|
||||||
{
|
{
|
||||||
Port = sv.GetPort(),
|
Port = sv.GetPort(),
|
||||||
Active = true,
|
EndPoint = sv.ToString(),
|
||||||
ServerId = serverId
|
ServerId = serverId
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -216,7 +231,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check to see if the stats have ever been initialized
|
// check to see if the stats have ever been initialized
|
||||||
var serverStats = InitializeServerStats(sv);
|
var serverStats = InitializeServerStats(server.ServerId);
|
||||||
|
|
||||||
Servers.TryAdd(serverId, new ServerStats(server, serverStats)
|
Servers.TryAdd(serverId, new ServerStats(server, serverStats)
|
||||||
{
|
{
|
||||||
@ -227,6 +242,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_ERROR_ADD"]} - {e.Message}");
|
Log.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_ERROR_ADD"]} - {e.Message}");
|
||||||
|
Log.WriteDebug(e.GetExceptionInfo());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,14 +251,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pl">Player to add/retrieve stats for</param>
|
/// <param name="pl">Player to add/retrieve stats for</param>
|
||||||
/// <returns>EFClientStatistic of specified player</returns>
|
/// <returns>EFClientStatistic of specified player</returns>
|
||||||
public async Task<EFClientStatistics> AddPlayer(Player pl)
|
public async Task<EFClientStatistics> AddPlayer(EFClient pl)
|
||||||
{
|
{
|
||||||
await OnProcessingSensitive.WaitAsync();
|
await OnProcessingSensitive.WaitAsync();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int serverId = pl.CurrentServer.GetHashCode();
|
long serverId = await GetIdForServer(pl.CurrentServer);
|
||||||
|
|
||||||
|
|
||||||
if (!Servers.ContainsKey(serverId))
|
if (!Servers.ContainsKey(serverId))
|
||||||
{
|
{
|
||||||
@ -299,7 +314,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
{
|
{
|
||||||
Log.WriteWarning("Adding new client to stats failed");
|
Log.WriteWarning("Adding new client to stats failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
@ -348,7 +362,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
Log.WriteWarning("Could not add client to detection");
|
Log.WriteWarning("Could not add client to detection");
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.WriteInfo($"Adding {pl} to stats");
|
pl.CurrentServer.Logger.WriteInfo($"Adding {pl} to stats");
|
||||||
}
|
}
|
||||||
|
|
||||||
return clientStats;
|
return clientStats;
|
||||||
@ -373,21 +387,21 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pl">Disconnecting client</param>
|
/// <param name="pl">Disconnecting client</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task RemovePlayer(Player pl)
|
public async Task RemovePlayer(EFClient pl)
|
||||||
{
|
{
|
||||||
Log.WriteInfo($"Removing {pl} from stats");
|
pl.CurrentServer.Logger.WriteInfo($"Removing {pl} from stats");
|
||||||
|
|
||||||
int serverId = pl.CurrentServer.GetHashCode();
|
long serverId = await GetIdForServer(pl.CurrentServer);
|
||||||
var playerStats = Servers[serverId].PlayerStats;
|
var playerStats = Servers[serverId].PlayerStats;
|
||||||
var detectionStats = Servers[serverId].PlayerDetections;
|
var detectionStats = Servers[serverId].PlayerDetections;
|
||||||
var serverStats = Servers[serverId].ServerStatistics;
|
var serverStats = Servers[serverId].ServerStatistics;
|
||||||
|
|
||||||
if (!playerStats.ContainsKey(pl.ClientId))
|
if (!playerStats.ContainsKey(pl.ClientId))
|
||||||
{
|
{
|
||||||
Log.WriteWarning($"Client disconnecting not in stats {pl}");
|
pl.CurrentServer.Logger.WriteWarning($"Client disconnecting not in stats {pl}");
|
||||||
// remove the client from the stats dictionary as they're leaving
|
// remove the client from the stats dictionary as they're leaving
|
||||||
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue1);
|
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue1);
|
||||||
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue2);
|
detectionStats.TryRemove(pl.ClientId, out Detection removedValue2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,7 +410,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
|
|
||||||
// remove the client from the stats dictionary as they're leaving
|
// remove the client from the stats dictionary as they're leaving
|
||||||
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue3);
|
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue3);
|
||||||
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue4);
|
detectionStats.TryRemove(pl.ClientId, out Detection removedValue4);
|
||||||
|
|
||||||
// sync their stats before they leave
|
// sync their stats before they leave
|
||||||
clientStats = UpdateStats(clientStats);
|
clientStats = UpdateStats(clientStats);
|
||||||
@ -408,31 +422,32 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
}
|
}
|
||||||
|
|
||||||
// increment the total play time
|
// increment the total play time
|
||||||
serverStats.TotalPlayTime += (int)(DateTime.UtcNow - pl.LastConnection).TotalSeconds;
|
serverStats.TotalPlayTime += pl.ConnectionLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddDamageEvent(string eventLine, int attackerClientId, int victimClientId, int serverId)
|
public void AddDamageEvent(string eventLine, int attackerClientId, int victimClientId, long serverId)
|
||||||
{
|
{
|
||||||
string regex = @"^(D);(.+);([0-9]+);(allies|axis);(.+);([0-9]+);(allies|axis);(.+);(.+);([0-9]+);(.+);(.+)$";
|
// todo: maybe do something with this
|
||||||
var match = Regex.Match(eventLine, regex, RegexOptions.IgnoreCase);
|
//string regex = @"^(D);(.+);([0-9]+);(allies|axis);(.+);([0-9]+);(allies|axis);(.+);(.+);([0-9]+);(.+);(.+)$";
|
||||||
|
//var match = Regex.Match(eventLine, regex, RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
if (match.Success)
|
//if (match.Success)
|
||||||
{
|
//{
|
||||||
// this gives us what team the player is on
|
// // this gives us what team the player is on
|
||||||
var attackerStats = Servers[serverId].PlayerStats[attackerClientId];
|
// var attackerStats = Servers[serverId].PlayerStats[attackerClientId];
|
||||||
var victimStats = Servers[serverId].PlayerStats[victimClientId];
|
// var victimStats = Servers[serverId].PlayerStats[victimClientId];
|
||||||
IW4Info.Team victimTeam = (IW4Info.Team)Enum.Parse(typeof(IW4Info.Team), match.Groups[4].ToString());
|
// IW4Info.Team victimTeam = (IW4Info.Team)Enum.Parse(typeof(IW4Info.Team), match.Groups[4].ToString(), true);
|
||||||
IW4Info.Team attackerTeam = (IW4Info.Team)Enum.Parse(typeof(IW4Info.Team), match.Groups[7].ToString());
|
// IW4Info.Team attackerTeam = (IW4Info.Team)Enum.Parse(typeof(IW4Info.Team), match.Groups[7].ToString(), true);
|
||||||
attackerStats.Team = attackerTeam;
|
// attackerStats.Team = attackerTeam;
|
||||||
victimStats.Team = victimTeam;
|
// victimStats.Team = victimTeam;
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Process stats for kill event
|
/// Process stats for kill event
|
||||||
/// </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, EFClient attacker, EFClient victim, long serverId, string map, string hitLoc, string type,
|
||||||
string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads,
|
string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads,
|
||||||
string fraction, string visibilityPercentage, string snapAngles)
|
string fraction, string visibilityPercentage, string snapAngles)
|
||||||
{
|
{
|
||||||
@ -548,7 +563,22 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
|
|
||||||
if (Plugin.Config.Configuration().EnableAntiCheat)
|
if (Plugin.Config.Configuration().EnableAntiCheat)
|
||||||
{
|
{
|
||||||
ApplyPenalty(clientDetection.ProcessKill(hit, isDamage), clientDetection, attacker, ctx);
|
if (clientDetection.QueuedHits.Count > Detection.QUEUE_COUNT)
|
||||||
|
{
|
||||||
|
while (clientDetection.QueuedHits.Count > 0)
|
||||||
|
{
|
||||||
|
clientDetection.QueuedHits = clientDetection.QueuedHits.OrderBy(_hits => _hits.TimeOffset).ToList();
|
||||||
|
var oldestHit = clientDetection.QueuedHits.First();
|
||||||
|
clientDetection.QueuedHits.RemoveAt(0);
|
||||||
|
ApplyPenalty(clientDetection.ProcessHit(oldestHit, isDamage), clientDetection, attacker, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clientDetection.QueuedHits.Add(hit);
|
||||||
|
}
|
||||||
|
|
||||||
ApplyPenalty(clientDetection.ProcessTotalRatio(clientStats), clientDetection, attacker, ctx);
|
ApplyPenalty(clientDetection.ProcessTotalRatio(clientStats), clientDetection, attacker, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -567,16 +597,16 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplyPenalty(Cheat.DetectionPenaltyResult penalty, Cheat.Detection clientDetection, Player attacker, DatabaseContext ctx)
|
void ApplyPenalty(DetectionPenaltyResult penalty, Detection clientDetection, EFClient attacker, DatabaseContext ctx)
|
||||||
{
|
{
|
||||||
switch (penalty.ClientPenalty)
|
switch (penalty.ClientPenalty)
|
||||||
{
|
{
|
||||||
case Penalty.PenaltyType.Ban:
|
case Penalty.PenaltyType.Ban:
|
||||||
if (attacker.Level == Player.Permission.Banned)
|
if (attacker.Level == EFClient.Permission.Banned)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
attacker.Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], new Player()
|
attacker.Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], new EFClient()
|
||||||
{
|
{
|
||||||
ClientId = 1,
|
ClientId = 1,
|
||||||
AdministeredPenalties = new List<EFPenalty>()
|
AdministeredPenalties = new List<EFPenalty>()
|
||||||
@ -588,16 +618,17 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
$"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}",
|
$"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Level = Player.Permission.Console,
|
Level = EFClient.Permission.Console,
|
||||||
CurrentServer = attacker.CurrentServer
|
CurrentServer = attacker.CurrentServer,
|
||||||
});
|
|
||||||
|
}, false);
|
||||||
if (clientDetection.Tracker.HasChanges)
|
if (clientDetection.Tracker.HasChanges)
|
||||||
{
|
{
|
||||||
SaveTrackedSnapshots(clientDetection, ctx);
|
SaveTrackedSnapshots(clientDetection, ctx);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Penalty.PenaltyType.Flag:
|
case Penalty.PenaltyType.Flag:
|
||||||
if (attacker.Level != Player.Permission.User)
|
if (attacker.Level != EFClient.Permission.User)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -606,10 +637,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
$"{penalty.Type}-{(int)penalty.Location}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}" :
|
$"{penalty.Type}-{(int)penalty.Location}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}" :
|
||||||
$"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}";
|
$"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}";
|
||||||
|
|
||||||
attacker.Flag(flagReason, new Player()
|
attacker.Flag(flagReason, new EFClient()
|
||||||
{
|
{
|
||||||
ClientId = 1,
|
ClientId = 1,
|
||||||
Level = Player.Permission.Console,
|
Level = EFClient.Permission.Console,
|
||||||
CurrentServer = attacker.CurrentServer,
|
CurrentServer = attacker.CurrentServer,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -677,9 +708,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddStandardKill(Player attacker, Player victim)
|
public async Task AddStandardKill(EFClient attacker, EFClient victim)
|
||||||
{
|
{
|
||||||
int serverId = attacker.CurrentServer.GetHashCode();
|
long serverId = await GetIdForServer(attacker.CurrentServer);
|
||||||
|
|
||||||
EFClientStatistics attackerStats = null;
|
EFClientStatistics attackerStats = null;
|
||||||
if (!Servers[serverId].PlayerStats.ContainsKey(attacker.ClientId))
|
if (!Servers[serverId].PlayerStats.ContainsKey(attacker.ClientId))
|
||||||
@ -709,13 +740,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
|
|
||||||
// update the total stats
|
// update the total stats
|
||||||
Servers[serverId].ServerStatistics.TotalKills += 1;
|
Servers[serverId].ServerStatistics.TotalKills += 1;
|
||||||
|
await Sync(attacker.CurrentServer);
|
||||||
|
|
||||||
// this happens when the round has changed
|
// this happens when the round has changed
|
||||||
if (attackerStats.SessionScore == 0)
|
if (attackerStats.SessionScore == 0)
|
||||||
|
{
|
||||||
attackerStats.LastScore = 0;
|
attackerStats.LastScore = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (victimStats.SessionScore == 0)
|
if (victimStats.SessionScore == 0)
|
||||||
|
{
|
||||||
victimStats.LastScore = 0;
|
victimStats.LastScore = 0;
|
||||||
|
}
|
||||||
|
|
||||||
attackerStats.SessionScore = attacker.Score;
|
attackerStats.SessionScore = attacker.Score;
|
||||||
victimStats.SessionScore = victim.Score;
|
victimStats.SessionScore = victim.Score;
|
||||||
@ -768,8 +804,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
{
|
{
|
||||||
var clientStatsSet = ctx.Set<EFClientStatistics>();
|
var clientStatsSet = ctx.Set<EFClientStatistics>();
|
||||||
|
|
||||||
clientStatsSet.Update(attackerStats);
|
clientStatsSet.Attach(attackerStats).State = EntityState.Modified;
|
||||||
clientStatsSet.Update(victimStats);
|
clientStatsSet.Attach(victimStats).State = EntityState.Modified;
|
||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -780,7 +816,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
/// <param name="client">client to update</param>
|
/// <param name="client">client to update</param>
|
||||||
/// <param name="clientStats">stats of client that is being updated</param>
|
/// <param name="clientStats">stats of client that is being updated</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private async Task UpdateStatHistory(Player client, EFClientStatistics clientStats)
|
private async Task UpdateStatHistory(EFClient client, EFClientStatistics clientStats)
|
||||||
{
|
{
|
||||||
int currentSessionTime = (int)(DateTime.UtcNow - client.LastConnection).TotalSeconds;
|
int currentSessionTime = (int)(DateTime.UtcNow - client.LastConnection).TotalSeconds;
|
||||||
|
|
||||||
@ -979,6 +1015,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
// calulate elo
|
// calulate elo
|
||||||
if (Servers[attackerStats.ServerId].PlayerStats.Count > 1)
|
if (Servers[attackerStats.ServerId].PlayerStats.Count > 1)
|
||||||
{
|
{
|
||||||
|
#region DEPRECATED
|
||||||
/* var validAttackerLobbyRatings = Servers[attackerStats.ServerId].PlayerStats
|
/* var validAttackerLobbyRatings = Servers[attackerStats.ServerId].PlayerStats
|
||||||
.Where(cs => cs.Value.ClientId != attackerStats.ClientId)
|
.Where(cs => cs.Value.ClientId != attackerStats.ClientId)
|
||||||
.Where(cs =>
|
.Where(cs =>
|
||||||
@ -1002,6 +1039,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
double victimLobbyRating = validVictimLobbyRatings.Count() > 0 ?
|
double victimLobbyRating = validVictimLobbyRatings.Count() > 0 ?
|
||||||
validVictimLobbyRatings.Average(cs => cs.Value.EloRating) :
|
validVictimLobbyRatings.Average(cs => cs.Value.EloRating) :
|
||||||
victimStats.EloRating;*/
|
victimStats.EloRating;*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
double attackerEloDifference = Math.Log(Math.Max(1, victimStats.EloRating)) - Math.Log(Math.Max(1, attackerStats.EloRating));
|
double attackerEloDifference = Math.Log(Math.Max(1, victimStats.EloRating)) - Math.Log(Math.Max(1, attackerStats.EloRating));
|
||||||
double winPercentage = 1.0 / (1 + Math.Pow(10, attackerEloDifference / Math.E));
|
double winPercentage = 1.0 / (1 + Math.Pow(10, attackerEloDifference / Math.E));
|
||||||
@ -1104,9 +1142,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
return clientStats;
|
return clientStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EFServerStatistics InitializeServerStats(Server sv)
|
public EFServerStatistics InitializeServerStats(long serverId)
|
||||||
{
|
{
|
||||||
int serverId = sv.GetHashCode();
|
|
||||||
EFServerStatistics serverStats;
|
EFServerStatistics serverStats;
|
||||||
|
|
||||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||||
@ -1116,7 +1153,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
|
|
||||||
if (serverStats == null)
|
if (serverStats == null)
|
||||||
{
|
{
|
||||||
Log.WriteDebug($"Initializing server stats for {sv}");
|
Log.WriteDebug($"Initializing server stats for {serverId}");
|
||||||
// server stats have never been generated before
|
// server stats have never been generated before
|
||||||
serverStats = new EFServerStatistics()
|
serverStats = new EFServerStatistics()
|
||||||
{
|
{
|
||||||
@ -1133,7 +1170,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
return serverStats;
|
return serverStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetKillstreaks(int serverId)
|
public void ResetKillstreaks(long serverId)
|
||||||
{
|
{
|
||||||
var serverStats = Servers[serverId];
|
var serverStats = Servers[serverId];
|
||||||
|
|
||||||
@ -1143,7 +1180,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetStats(int clientId, int serverId)
|
public void ResetStats(int clientId, long serverId)
|
||||||
{
|
{
|
||||||
var stats = Servers[serverId].PlayerStats[clientId];
|
var stats = Servers[serverId].PlayerStats[clientId];
|
||||||
stats.Kills = 0;
|
stats.Kills = 0;
|
||||||
@ -1154,11 +1191,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
stats.EloRating = 200;
|
stats.EloRating = 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddMessageAsync(int clientId, int serverId, string message)
|
public async Task AddMessageAsync(int clientId, long serverId, string message)
|
||||||
{
|
{
|
||||||
// the web users can have no account
|
// the web users can have no account
|
||||||
if (clientId < 1)
|
if (clientId < 1)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||||
{
|
{
|
||||||
@ -1176,19 +1215,64 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
|
|
||||||
public async Task Sync(Server sv)
|
public async Task Sync(Server sv)
|
||||||
{
|
{
|
||||||
int serverId = sv.GetHashCode();
|
long serverId = await GetIdForServer(sv);
|
||||||
|
|
||||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||||
{
|
{
|
||||||
var serverSet = ctx.Set<EFServer>();
|
var serverSet = ctx.Set<EFServer>();
|
||||||
serverSet.Update(Servers[serverId].Server);
|
serverSet.Update(Servers[serverId].Server);
|
||||||
|
|
||||||
|
var serverStatsSet = ctx.Set<EFServerStatistics>();
|
||||||
|
serverStatsSet.Update(Servers[serverId].ServerStatistics);
|
||||||
|
|
||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetTeamBased(int serverId, bool isTeamBased)
|
public void SetTeamBased(long serverId, bool isTeamBased)
|
||||||
{
|
{
|
||||||
Servers[serverId].IsTeamBased = isTeamBased;
|
Servers[serverId].IsTeamBased = isTeamBased;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<long> GetIdForServer(Server server)
|
||||||
|
{
|
||||||
|
// hack: my laziness
|
||||||
|
if ($"{server.IP}:{server.GetPort().ToString()}" == "66.150.121.184:28965")
|
||||||
|
{
|
||||||
|
return 886229536;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ($"{server.IP}:{server.GetPort().ToString()}" == "66.150.121.184:28960")
|
||||||
|
{
|
||||||
|
return 1645744423;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ($"{server.IP}:{server.GetPort().ToString()}" == "66.150.121.184:28970")
|
||||||
|
{
|
||||||
|
return 1645809959;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
long id = HashCode.Combine(server.IP, server.GetPort());
|
||||||
|
id = id < 0 ? Math.Abs(id) : id;
|
||||||
|
long? serverId;
|
||||||
|
|
||||||
|
// todo: cache this eventually, as it shouldn't change
|
||||||
|
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||||
|
{
|
||||||
|
serverId = (await ctx.Set<EFServer>().FirstOrDefaultAsync(_server => _server.ServerId == server.EndPoint ||
|
||||||
|
_server.EndPoint == server.ToString() ||
|
||||||
|
_server.ServerId == id))?.ServerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!serverId.HasValue)
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return serverId.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
|||||||
public int AttackerId { get; set; }
|
public int AttackerId { get; set; }
|
||||||
[ForeignKey("AttackerId")]
|
[ForeignKey("AttackerId")]
|
||||||
public virtual EFClient Attacker { get; set; }
|
public virtual EFClient Attacker { get; set; }
|
||||||
public int ServerId { get; set; }
|
public long ServerId { get; set; }
|
||||||
[ForeignKey("ServerId")]
|
[ForeignKey("ServerId")]
|
||||||
public virtual EFServer Server { get; set; }
|
public virtual EFServer Server { get; set; }
|
||||||
public IW4Info.HitLocation HitLoc { get; set; }
|
public IW4Info.HitLocation HitLoc { get; set; }
|
||||||
|
@ -13,7 +13,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
|||||||
{
|
{
|
||||||
[Key]
|
[Key]
|
||||||
public long MessageId { get; set; }
|
public long MessageId { get; set; }
|
||||||
public int ServerId { get; set; }
|
public long ServerId { get; set; }
|
||||||
[ForeignKey("ServerId")]
|
[ForeignKey("ServerId")]
|
||||||
public virtual EFServer Server { get; set; }
|
public virtual EFServer Server { get; set; }
|
||||||
public int ClientId { get; set; }
|
public int ClientId { get; set; }
|
||||||
|
@ -15,7 +15,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
|||||||
public int ClientId { get; set; }
|
public int ClientId { get; set; }
|
||||||
[ForeignKey("ClientId")]
|
[ForeignKey("ClientId")]
|
||||||
public virtual EFClient Client { get; set; }
|
public virtual EFClient Client { get; set; }
|
||||||
public int ServerId { get; set; }
|
public long ServerId { get; set; }
|
||||||
[ForeignKey("ServerId")]
|
[ForeignKey("ServerId")]
|
||||||
public virtual EFServer Server { get; set; }
|
public virtual EFServer Server { get; set; }
|
||||||
[Required]
|
[Required]
|
||||||
|
@ -20,7 +20,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
|||||||
public int ClientId { get; set; }
|
public int ClientId { get; set; }
|
||||||
[ForeignKey("ClientId"), Column(Order = 0 )]
|
[ForeignKey("ClientId"), Column(Order = 0 )]
|
||||||
public EFClient Client { get; set; }
|
public EFClient Client { get; set; }
|
||||||
public int ServerId { get; set; }
|
public long ServerId { get; set; }
|
||||||
[ForeignKey("ServerId"), Column(Order = 1)]
|
[ForeignKey("ServerId"), Column(Order = 1)]
|
||||||
public EFServer Server { get; set; }
|
public EFServer Server { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
|||||||
[ForeignKey("RatingHistoryId")]
|
[ForeignKey("RatingHistoryId")]
|
||||||
public virtual EFClientRatingHistory RatingHistory { get; set; }
|
public virtual EFClientRatingHistory RatingHistory { get; set; }
|
||||||
// if null, indicates that the rating is an average rating
|
// if null, indicates that the rating is an average rating
|
||||||
public int? ServerId { get; set; }
|
public long? ServerId { get; set; }
|
||||||
// [ForeignKey("ServerId")] can't make this nullable if this annotation is set
|
// [ForeignKey("ServerId")] can't make this nullable if this annotation is set
|
||||||
public virtual EFServer Server { get; set; }
|
public virtual EFServer Server { get; set; }
|
||||||
[Required]
|
[Required]
|
||||||
|
@ -9,8 +9,9 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
|||||||
{
|
{
|
||||||
[Key]
|
[Key]
|
||||||
[DatabaseGenerated(DatabaseGeneratedOption.None)]
|
[DatabaseGenerated(DatabaseGeneratedOption.None)]
|
||||||
public int ServerId { get; set; }
|
public long ServerId { get; set; }
|
||||||
[Required]
|
[Required]
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
|
public string EndPoint { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
|||||||
{
|
{
|
||||||
[Key]
|
[Key]
|
||||||
public int StatisticId { get; set; }
|
public int StatisticId { get; set; }
|
||||||
public int ServerId { get; set; }
|
public long ServerId { get; set; }
|
||||||
[ForeignKey("ServerId")]
|
[ForeignKey("ServerId")]
|
||||||
public virtual EFServer Server { get; set; }
|
public virtual EFServer Server { get; set; }
|
||||||
public long TotalKills { get; set; }
|
public long TotalKills { get; set; }
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using IW4MAdmin.Plugins.Stats.Models;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using IW4MAdmin.Plugins.Stats.Models;
|
|
||||||
|
|
||||||
namespace Stats.Models
|
namespace Stats.Models
|
||||||
{
|
{
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
using System;
|
using IW4MAdmin.Plugins.Stats.Config;
|
||||||
using System.Collections.Generic;
|
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||||
using System.Linq;
|
using IW4MAdmin.Plugins.Stats.Models;
|
||||||
using System.Threading.Tasks;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using SharedLibraryCore.Configuration;
|
using SharedLibraryCore.Configuration;
|
||||||
|
using SharedLibraryCore.Database;
|
||||||
using SharedLibraryCore.Dtos;
|
using SharedLibraryCore.Dtos;
|
||||||
using SharedLibraryCore.Helpers;
|
using SharedLibraryCore.Helpers;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using SharedLibraryCore.Services;
|
using SharedLibraryCore.Services;
|
||||||
using IW4MAdmin.Plugins.Stats.Config;
|
using System;
|
||||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
using System.Collections.Generic;
|
||||||
using IW4MAdmin.Plugins.Stats.Models;
|
using System.Linq;
|
||||||
using SharedLibraryCore.Database;
|
using System.Reflection;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Stats
|
namespace IW4MAdmin.Plugins.Stats
|
||||||
{
|
{
|
||||||
@ -44,16 +43,19 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
break;
|
break;
|
||||||
case GameEvent.EventType.Disconnect:
|
case GameEvent.EventType.Disconnect:
|
||||||
await Manager.RemovePlayer(E.Origin);
|
await Manager.RemovePlayer(E.Origin);
|
||||||
|
await Manager.Sync(S);
|
||||||
break;
|
break;
|
||||||
case GameEvent.EventType.Say:
|
case GameEvent.EventType.Say:
|
||||||
if (!string.IsNullOrEmpty(E.Data) &&
|
if (!string.IsNullOrEmpty(E.Data) &&
|
||||||
E.Origin.ClientId > 1)
|
E.Origin.ClientId > 1)
|
||||||
await Manager.AddMessageAsync(E.Origin.ClientId, E.Owner.GetHashCode(), E.Data);
|
{
|
||||||
|
await Manager.AddMessageAsync(E.Origin.ClientId, await StatManager.GetIdForServer(E.Owner), E.Data);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case GameEvent.EventType.MapChange:
|
case GameEvent.EventType.MapChange:
|
||||||
Manager.SetTeamBased(E.Owner.GetHashCode(), E.Owner.Gametype != "dm");
|
Manager.SetTeamBased(await StatManager.GetIdForServer(E.Owner), E.Owner.Gametype != "dm");
|
||||||
Manager.ResetKillstreaks(S.GetHashCode());
|
Manager.ResetKillstreaks(await StatManager.GetIdForServer(E.Owner));
|
||||||
await Manager.Sync(S);
|
|
||||||
break;
|
break;
|
||||||
case GameEvent.EventType.MapEnd:
|
case GameEvent.EventType.MapEnd:
|
||||||
break;
|
break;
|
||||||
@ -77,7 +79,7 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
||||||
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, await StatManager.GetIdForServer(E.Owner), 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[15]);
|
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14], killInfo[15]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -90,14 +92,14 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
case GameEvent.EventType.Damage:
|
case GameEvent.EventType.Damage:
|
||||||
if (!E.Owner.CustomCallback)
|
if (!E.Owner.CustomCallback)
|
||||||
{
|
{
|
||||||
Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, E.Owner.GetHashCode());
|
Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, await StatManager.GetIdForServer(E.Owner));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GameEvent.EventType.ScriptDamage:
|
case GameEvent.EventType.ScriptDamage:
|
||||||
killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
||||||
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, await StatManager.GetIdForServer(E.Owner), 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[15]);
|
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14], killInfo[15]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -143,7 +145,7 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
new ProfileMeta()
|
new ProfileMeta()
|
||||||
{
|
{
|
||||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_RANKING"],
|
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_RANKING"],
|
||||||
Value = "#" + await Manager.GetClientOverallRanking(clientId),
|
Value = "#" + await StatManager.GetClientOverallRanking(clientId),
|
||||||
},
|
},
|
||||||
new ProfileMeta()
|
new ProfileMeta()
|
||||||
{
|
{
|
||||||
@ -289,32 +291,32 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
|
|
||||||
MetaService.AddMeta(getMessages);
|
MetaService.AddMeta(getMessages);
|
||||||
|
|
||||||
string totalKills(Server server)
|
async Task<object> totalKills(Server server)
|
||||||
{
|
{
|
||||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||||
{
|
{
|
||||||
long kills = ctx.Set<EFServerStatistics>().Where(s => s.Active).Sum(s => s.TotalKills);
|
long kills = await ctx.Set<EFServerStatistics>().Where(s => s.Active).SumAsync(s => s.TotalKills);
|
||||||
return kills.ToString("#,##0");
|
return kills.ToString("#,##0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string totalPlayTime(Server server)
|
async Task<object> totalPlayTime(Server server)
|
||||||
{
|
{
|
||||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||||
{
|
{
|
||||||
long playTime = ctx.Set<EFServerStatistics>().Where(s => s.Active).Sum(s => s.TotalPlayTime);
|
long playTime = await ctx.Set<EFServerStatistics>().Where(s => s.Active).SumAsync(s => s.TotalPlayTime);
|
||||||
return (playTime / 3600.0).ToString("#,##0");
|
return (playTime / 3600.0).ToString("#,##0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string topStats(Server s)
|
async Task<object> topStats(Server s)
|
||||||
{
|
{
|
||||||
return String.Join(Environment.NewLine, Commands.TopStats.GetTopStats(s).Result);
|
return String.Join(Environment.NewLine, await Commands.TopStats.GetTopStats(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
string mostPlayed(Server s)
|
async Task<object> mostPlayed(Server s)
|
||||||
{
|
{
|
||||||
return String.Join(Environment.NewLine, Commands.MostPlayed.GetMostPlayed(s).Result);
|
return String.Join(Environment.NewLine, await Commands.MostPlayed.GetMostPlayed(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.GetMessageTokens().Add(new MessageToken("TOTALKILLS", totalKills));
|
manager.GetMessageTokens().Add(new MessageToken("TOTALKILLS", totalKills));
|
||||||
@ -327,12 +329,17 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
Manager = new StatManager(manager);
|
Manager = new StatManager(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task OnTickAsync(Server S) => Task.CompletedTask;
|
public Task OnTickAsync(Server S)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task OnUnloadAsync()
|
public async Task OnUnloadAsync()
|
||||||
{
|
{
|
||||||
foreach (var sv in ServerManager.GetServers())
|
foreach (var sv in ServerManager.GetServers())
|
||||||
|
{
|
||||||
await Manager.Sync(sv);
|
await Manager.Sync(sv);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
|
<RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion>
|
||||||
<ApplicationIcon />
|
<ApplicationIcon />
|
||||||
<StartupObject />
|
<StartupObject />
|
||||||
<PackageId>RaidMax.IW4MAdmin.Plugins.Stats</PackageId>
|
<PackageId>RaidMax.IW4MAdmin.Plugins.Stats</PackageId>
|
||||||
@ -15,27 +15,16 @@
|
|||||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="Web\Views\Stats\_MessageContext.cshtml">
|
|
||||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\SharedLibraryCore\SharedLibraryCore.csproj" />
|
<ProjectReference Include="..\..\SharedLibraryCore\SharedLibraryCore.csproj" />
|
||||||
<ProjectReference Include="..\..\WebfrontCore\WebfrontCore.csproj" />
|
<ProjectReference Include="..\..\WebfrontCore\WebfrontCore.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
|
<PackageReference Update="Microsoft.NETCore.App" Version="2.2.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
<Exec Command="copy "$(TargetPath)" "$(SolutionDir)BUILD\Plugins"" />
|
<Exec Command="copy "$(TargetPath)" "$(SolutionDir)BUILD\Plugins"" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
|
||||||
<Exec Command="xcopy /E /K /Y /C /I "$(ProjectDir)Web\Views" "$(SolutionDir)WebfrontCore\Views\Plugins"
xcopy /E /K /Y /C /I "$(ProjectDir)Web\wwwroot\images" "$(SolutionDir)WebfrontCore\wwwroot\images"" />
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user