Compare commits
40 Commits
2.3-prerel
...
2.3-prerel
Author | SHA1 | Date | |
---|---|---|---|
b9c4a1b5f6 | |||
f0fd4c66e9 | |||
52fe8fc847 | |||
9f8c35dbed | |||
9d9be7f8af | |||
dd82a5e3fa | |||
8667532d24 | |||
863ba8b096 | |||
8ab89e113d | |||
00634780d4 | |||
6f80f1edbb | |||
9393b35c39 | |||
37d3f4f90d | |||
8521df85f5 | |||
7b8126d57e | |||
1e2e2218e3 | |||
11e3235d5d | |||
cae6d8389e | |||
1056c7c335 | |||
95e4ee672e | |||
bf0a0befc6 | |||
53c3ff6ce3 | |||
c8ec0eefa9 | |||
82bae772f0 | |||
af3aea77bc | |||
b3e5f468a1 | |||
c21bf2ebf1 | |||
d318a57830 | |||
61f1436faf | |||
0c527a5f65 | |||
1457b843e2 | |||
de69bed792 | |||
4b1f44cc2a | |||
74cdf8e885 | |||
5d41059641 | |||
2e6889d9bb | |||
9c4d23f0b4 | |||
b4e3e8526a | |||
e3944fb8c2 | |||
40f1697c97 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -223,7 +223,7 @@ global.min.js
|
|||||||
bootstrap-custom.min.css
|
bootstrap-custom.min.css
|
||||||
**/Master/static
|
**/Master/static
|
||||||
**/Master/dev_env
|
**/Master/dev_env
|
||||||
/WebfrontCore/Views/Plugins/Stats
|
/WebfrontCore/Views/Plugins/*
|
||||||
/WebfrontCore/wwwroot/**/dds
|
/WebfrontCore/wwwroot/**/dds
|
||||||
|
|
||||||
/DiscordWebhook/env
|
/DiscordWebhook/env
|
||||||
@ -233,3 +233,4 @@ launchSettings.json
|
|||||||
/VpnDetectionPrivate.js
|
/VpnDetectionPrivate.js
|
||||||
/Plugins/ScriptPlugins/VpnDetectionPrivate.js
|
/Plugins/ScriptPlugins/VpnDetectionPrivate.js
|
||||||
**/Master/env_master
|
**/Master/env_master
|
||||||
|
/GameLogServer/log_env
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using RestEase;
|
using RestEase;
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
@ -11,6 +12,7 @@ namespace IW4MAdmin.Application.API.Master
|
|||||||
public class HeartbeatState
|
public class HeartbeatState
|
||||||
{
|
{
|
||||||
public bool Connected { get; set; }
|
public bool Connected { get; set; }
|
||||||
|
public CancellationToken Token { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Heartbeat
|
public class Heartbeat
|
||||||
|
@ -6,11 +6,11 @@
|
|||||||
<RuntimeFrameworkVersion>2.2.2</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.5.1</Version>
|
<Version>2.2.6.1</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>2019</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>
|
||||||
@ -31,8 +31,8 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||||
<TieredCompilation>true</TieredCompilation>
|
<TieredCompilation>true</TieredCompilation>
|
||||||
<AssemblyVersion>2.2.5.1</AssemblyVersion>
|
<AssemblyVersion>2.2.6.1</AssemblyVersion>
|
||||||
<FileVersion>2.2.5.1</FileVersion>
|
<FileVersion>2.2.6.1</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>
|
||||||
|
@ -7,10 +7,12 @@ using SharedLibraryCore.Commands;
|
|||||||
using SharedLibraryCore.Configuration;
|
using SharedLibraryCore.Configuration;
|
||||||
using SharedLibraryCore.Database;
|
using SharedLibraryCore.Database;
|
||||||
using SharedLibraryCore.Database.Models;
|
using SharedLibraryCore.Database.Models;
|
||||||
|
using SharedLibraryCore.Dtos;
|
||||||
using SharedLibraryCore.Events;
|
using SharedLibraryCore.Events;
|
||||||
using SharedLibraryCore.Exceptions;
|
using SharedLibraryCore.Exceptions;
|
||||||
using SharedLibraryCore.Helpers;
|
using SharedLibraryCore.Helpers;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using SharedLibraryCore.Objects;
|
||||||
using SharedLibraryCore.Services;
|
using SharedLibraryCore.Services;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -39,10 +41,9 @@ namespace IW4MAdmin.Application
|
|||||||
|
|
||||||
public IList<IRConParser> AdditionalRConParsers { get; }
|
public IList<IRConParser> AdditionalRConParsers { get; }
|
||||||
public IList<IEventParser> AdditionalEventParsers { get; }
|
public IList<IEventParser> AdditionalEventParsers { get; }
|
||||||
|
|
||||||
public ITokenAuthentication TokenAuthenticator => Authenticator;
|
public ITokenAuthentication TokenAuthenticator => Authenticator;
|
||||||
|
|
||||||
public ITokenAuthentication Authenticator => _authenticator;
|
public ITokenAuthentication Authenticator => _authenticator;
|
||||||
|
public string ExternalIPAddress { get; private set; }
|
||||||
|
|
||||||
static ApplicationManager Instance;
|
static ApplicationManager Instance;
|
||||||
readonly List<AsyncStatus> TaskStatuses;
|
readonly List<AsyncStatus> TaskStatuses;
|
||||||
@ -58,6 +59,7 @@ namespace IW4MAdmin.Application
|
|||||||
readonly SemaphoreSlim ProcessingEvent = new SemaphoreSlim(1, 1);
|
readonly SemaphoreSlim ProcessingEvent = new SemaphoreSlim(1, 1);
|
||||||
readonly Dictionary<long, ILogger> Loggers = new Dictionary<long, ILogger>();
|
readonly Dictionary<long, ILogger> Loggers = new Dictionary<long, ILogger>();
|
||||||
readonly ITokenAuthentication _authenticator;
|
readonly ITokenAuthentication _authenticator;
|
||||||
|
private readonly MetaService _metaService;
|
||||||
|
|
||||||
private ApplicationManager()
|
private ApplicationManager()
|
||||||
{
|
{
|
||||||
@ -77,6 +79,7 @@ namespace IW4MAdmin.Application
|
|||||||
OnServerEvent += OnGameEvent;
|
OnServerEvent += OnGameEvent;
|
||||||
OnServerEvent += EventApi.OnGameEvent;
|
OnServerEvent += EventApi.OnGameEvent;
|
||||||
_authenticator = new TokenAuthentication();
|
_authenticator = new TokenAuthentication();
|
||||||
|
_metaService = new MetaService();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnGameEvent(object sender, GameEventArgs args)
|
private async void OnGameEvent(object sender, GameEventArgs args)
|
||||||
@ -154,7 +157,7 @@ namespace IW4MAdmin.Application
|
|||||||
return Instance ?? (Instance = new ApplicationManager());
|
return Instance ?? (Instance = new ApplicationManager());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateServerStates()
|
public async Task UpdateServerStates(CancellationToken token)
|
||||||
{
|
{
|
||||||
// store the server hash code and task for it
|
// store the server hash code and task for it
|
||||||
var runningUpdateTasks = new Dictionary<long, Task>();
|
var runningUpdateTasks = new Dictionary<long, Task>();
|
||||||
@ -198,6 +201,11 @@ namespace IW4MAdmin.Application
|
|||||||
Logger.WriteWarning($"Failed to update status for {server}");
|
Logger.WriteWarning($"Failed to update status for {server}");
|
||||||
Logger.WriteDebug(e.GetExceptionInfo());
|
Logger.WriteDebug(e.GetExceptionInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
server.IsInitialized = true;
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
@ -206,17 +214,19 @@ 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
|
||||||
await Task.Delay(ConfigHandler.Configuration().RConPollRate);
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(ConfigHandler.Configuration().RConPollRate, token);
|
||||||
|
}
|
||||||
|
// if a cancellation is received, we want to return immediately
|
||||||
|
catch { break; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// trigger the event processing loop to end
|
|
||||||
SetHasEvent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Init()
|
public async Task Init()
|
||||||
{
|
{
|
||||||
Running = true;
|
Running = true;
|
||||||
|
ExternalIPAddress = await Utilities.GetExternalIP();
|
||||||
|
|
||||||
#region PLUGINS
|
#region PLUGINS
|
||||||
SharedLibraryCore.Plugins.PluginImporter.Load(this);
|
SharedLibraryCore.Plugins.PluginImporter.Load(this);
|
||||||
@ -246,10 +256,11 @@ namespace IW4MAdmin.Application
|
|||||||
ConfigHandler.Set((ApplicationConfiguration)new ApplicationConfiguration().Generate());
|
ConfigHandler.Set((ApplicationConfiguration)new ApplicationConfiguration().Generate());
|
||||||
var newConfig = ConfigHandler.Configuration();
|
var newConfig = ConfigHandler.Configuration();
|
||||||
|
|
||||||
newConfig.AutoMessagePeriod = defaultConfig.AutoMessagePeriod;
|
|
||||||
newConfig.AutoMessages = defaultConfig.AutoMessages;
|
newConfig.AutoMessages = defaultConfig.AutoMessages;
|
||||||
newConfig.GlobalRules = defaultConfig.GlobalRules;
|
newConfig.GlobalRules = defaultConfig.GlobalRules;
|
||||||
newConfig.Maps = defaultConfig.Maps;
|
newConfig.Maps = defaultConfig.Maps;
|
||||||
|
newConfig.DisallowedClientNames = defaultConfig.DisallowedClientNames;
|
||||||
|
newConfig.QuickMessages = defaultConfig.QuickMessages;
|
||||||
|
|
||||||
if (newConfig.Servers == null)
|
if (newConfig.Servers == null)
|
||||||
{
|
{
|
||||||
@ -277,7 +288,7 @@ namespace IW4MAdmin.Application
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (config != null)
|
else
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(config.Id))
|
if (string.IsNullOrEmpty(config.Id))
|
||||||
{
|
{
|
||||||
@ -313,7 +324,7 @@ namespace IW4MAdmin.Application
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (config.Servers.Count == 0)
|
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");
|
||||||
}
|
}
|
||||||
@ -376,7 +387,7 @@ 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 GenerateTokenCommand());
|
Commands.Add(new RequestTokenCommand());
|
||||||
|
|
||||||
foreach (Command C in SharedLibraryCore.Plugins.PluginImporter.ActiveCommands)
|
foreach (Command C in SharedLibraryCore.Plugins.PluginImporter.ActiveCommands)
|
||||||
{
|
{
|
||||||
@ -384,11 +395,139 @@ namespace IW4MAdmin.Application
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region META
|
||||||
|
async Task<List<ProfileMeta>> getProfileMeta(int clientId, int offset, int count, DateTime? startAt)
|
||||||
|
{
|
||||||
|
var metaList = new List<ProfileMeta>();
|
||||||
|
|
||||||
|
// we don't want to return anything because it means we're trying to retrieve paged meta data
|
||||||
|
if (count > 1)
|
||||||
|
{
|
||||||
|
return metaList;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastMapMeta = await _metaService.GetPersistentMeta("LastMapPlayed", new EFClient() { ClientId = clientId });
|
||||||
|
|
||||||
|
if (lastMapMeta != null)
|
||||||
|
{
|
||||||
|
metaList.Add(new ProfileMeta()
|
||||||
|
{
|
||||||
|
Id = lastMapMeta.MetaId,
|
||||||
|
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_LAST_MAP"],
|
||||||
|
Value = lastMapMeta.Value,
|
||||||
|
Show = true,
|
||||||
|
Type = ProfileMeta.MetaType.Information,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastServerMeta = await _metaService.GetPersistentMeta("LastServerPlayed", new EFClient() { ClientId = clientId });
|
||||||
|
|
||||||
|
if (lastServerMeta != null)
|
||||||
|
{
|
||||||
|
metaList.Add(new ProfileMeta()
|
||||||
|
{
|
||||||
|
Id = lastServerMeta.MetaId,
|
||||||
|
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_LAST_SERVER"],
|
||||||
|
Value = lastServerMeta.Value,
|
||||||
|
Show = true,
|
||||||
|
Type = ProfileMeta.MetaType.Information
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var client = await GetClientService().Get(clientId);
|
||||||
|
|
||||||
|
metaList.Add(new ProfileMeta()
|
||||||
|
{
|
||||||
|
Id = client.ClientId,
|
||||||
|
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["GLOBAL_TIME_HOURS"]} {Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_PLAYER"]}",
|
||||||
|
Value = Math.Round(client.TotalConnectionTime / 3600.0, 1).ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||||
|
Show = true,
|
||||||
|
Column = 1,
|
||||||
|
Order = 0,
|
||||||
|
Type = ProfileMeta.MetaType.Information
|
||||||
|
});
|
||||||
|
|
||||||
|
metaList.Add(new ProfileMeta()
|
||||||
|
{
|
||||||
|
Id = client.ClientId,
|
||||||
|
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_FSEEN"],
|
||||||
|
Value = Utilities.GetTimePassed(client.FirstConnection, false),
|
||||||
|
Show = true,
|
||||||
|
Column = 1,
|
||||||
|
Order = 1,
|
||||||
|
Type = ProfileMeta.MetaType.Information
|
||||||
|
});
|
||||||
|
|
||||||
|
metaList.Add(new ProfileMeta()
|
||||||
|
{
|
||||||
|
Id = client.ClientId,
|
||||||
|
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_LSEEN"],
|
||||||
|
Value = Utilities.GetTimePassed(client.LastConnection, false),
|
||||||
|
Show = true,
|
||||||
|
Column = 1,
|
||||||
|
Order = 2,
|
||||||
|
Type = ProfileMeta.MetaType.Information
|
||||||
|
});
|
||||||
|
|
||||||
|
metaList.Add(new ProfileMeta()
|
||||||
|
{
|
||||||
|
Id = client.ClientId,
|
||||||
|
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_CONNECTIONS"],
|
||||||
|
Value = client.Connections.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||||
|
Show = true,
|
||||||
|
Column = 1,
|
||||||
|
Order = 3,
|
||||||
|
Type = ProfileMeta.MetaType.Information
|
||||||
|
});
|
||||||
|
|
||||||
|
metaList.Add(new ProfileMeta()
|
||||||
|
{
|
||||||
|
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_MASKED"],
|
||||||
|
Value = client.Masked ? Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_TRUE"] : Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_FALSE"],
|
||||||
|
Sensitive = true,
|
||||||
|
Column = 1,
|
||||||
|
Order = 4,
|
||||||
|
Type = ProfileMeta.MetaType.Information
|
||||||
|
});
|
||||||
|
|
||||||
|
return metaList;
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<List<ProfileMeta>> getPenaltyMeta(int clientId, int offset, int count, DateTime? startAt)
|
||||||
|
{
|
||||||
|
if (count <= 1)
|
||||||
|
{
|
||||||
|
return new List<ProfileMeta>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var penalties = await GetPenaltyService().GetClientPenaltyForMetaAsync(clientId, count, offset, startAt);
|
||||||
|
|
||||||
|
return penalties.Select(_penalty => new ProfileMeta()
|
||||||
|
{
|
||||||
|
Id = _penalty.Id,
|
||||||
|
Type = _penalty.PunisherId == clientId ? ProfileMeta.MetaType.Penalized : ProfileMeta.MetaType.ReceivedPenalty,
|
||||||
|
Value = _penalty,
|
||||||
|
When = _penalty.TimePunished,
|
||||||
|
Sensitive = _penalty.Sensitive
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaService.AddRuntimeMeta(getProfileMeta);
|
||||||
|
MetaService.AddRuntimeMeta(getPenaltyMeta);
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region INIT
|
#region INIT
|
||||||
|
int failedServers = 0;
|
||||||
|
int successServers = 0;
|
||||||
|
Exception lastException = null;
|
||||||
|
|
||||||
async Task Init(ServerConfiguration Conf)
|
async Task Init(ServerConfiguration Conf)
|
||||||
{
|
{
|
||||||
// setup the event handler after the class is initialized
|
// setup the event handler after the class is initialized
|
||||||
|
|
||||||
Handler = new GameEventHandler(this);
|
Handler = new GameEventHandler(this);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var ServerInstance = new IW4MServer(this, Conf);
|
var ServerInstance = new IW4MServer(this, Conf);
|
||||||
@ -399,7 +538,7 @@ namespace IW4MAdmin.Application
|
|||||||
_servers.Add(ServerInstance);
|
_servers.Add(ServerInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.WriteVerbose($"{Utilities.CurrentLocalization.LocalizationIndex["MANAGER_MONITORING_TEXT"]} {ServerInstance.Hostname}");
|
Logger.WriteVerbose(Utilities.CurrentLocalization.LocalizationIndex["MANAGER_MONITORING_TEXT"].FormatExt(ServerInstance.Hostname));
|
||||||
// add the start event for this server
|
// add the start event for this server
|
||||||
|
|
||||||
var e = new GameEvent()
|
var e = new GameEvent()
|
||||||
@ -410,26 +549,37 @@ namespace IW4MAdmin.Application
|
|||||||
};
|
};
|
||||||
|
|
||||||
Handler.AddEvent(e);
|
Handler.AddEvent(e);
|
||||||
|
successServers++;
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (ServerException e)
|
catch (ServerException e)
|
||||||
{
|
{
|
||||||
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"].FormatExt((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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// throw the exception to the main method to stop before instantly exiting
|
failedServers++;
|
||||||
throw e;
|
lastException = e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.WhenAll(config.Servers.Select(c => Init(c)).ToArray());
|
await Task.WhenAll(config.Servers.Select(c => Init(c)).ToArray());
|
||||||
|
|
||||||
|
if (failedServers > 0 && successServers > 0)
|
||||||
|
{
|
||||||
|
if (!Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationIndex["MANAGER_START_WITH_ERRORS"]))
|
||||||
|
{
|
||||||
|
throw lastException;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -494,27 +644,34 @@ namespace IW4MAdmin.Application
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
await Task.Delay(30000);
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(30000, heartbeatState.Token);
|
||||||
|
}
|
||||||
|
catch { break; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
|
var tokenSource = new CancellationTokenSource();
|
||||||
// this needs to be run seperately from the main thread
|
// this needs to be run seperately from the main thread
|
||||||
var _ = Task.Run(() => SendHeartbeat(new HeartbeatState()));
|
_ = Task.Run(() => SendHeartbeat(new HeartbeatState() { Token = tokenSource.Token }));
|
||||||
_ = Task.Run(() => UpdateServerStates());
|
_ = Task.Run(() => UpdateServerStates(tokenSource.Token));
|
||||||
|
|
||||||
while (Running)
|
while (Running)
|
||||||
{
|
{
|
||||||
OnQuit.Wait();
|
OnQuit.Wait();
|
||||||
|
tokenSource.Cancel();
|
||||||
OnQuit.Reset();
|
OnQuit.Reset();
|
||||||
}
|
}
|
||||||
_servers.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
Running = false;
|
Running = false;
|
||||||
|
OnQuit.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ILogger GetLogger(long serverId)
|
public ILogger GetLogger(long serverId)
|
||||||
@ -589,12 +746,12 @@ namespace IW4MAdmin.Application
|
|||||||
|
|
||||||
public void SetHasEvent()
|
public void SetHasEvent()
|
||||||
{
|
{
|
||||||
OnQuit.Set();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList<Assembly> GetPluginAssemblies()
|
public IList<Assembly> GetPluginAssemblies()
|
||||||
{
|
{
|
||||||
return SharedLibraryCore.Plugins.PluginImporter.PluginAssemblies;
|
return SharedLibraryCore.Plugins.PluginImporter.PluginAssemblies.Union(SharedLibraryCore.Plugins.PluginImporter.Assemblies).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPageList GetPageList()
|
public IPageList GetPageList()
|
||||||
|
@ -98,10 +98,8 @@ if "%CurrentConfiguration%" == "Release" (
|
|||||||
)
|
)
|
||||||
|
|
||||||
echo making start scripts
|
echo making start scripts
|
||||||
@(echo dotnet Lib/IW4MAdmin.dll && echo pause) > "%SolutionDir%Publish\WindowsPrerelease\StartIW4MAdmin.cmd"
|
@(echo @echo off && echo @title IW4MAdmin && echo dotnet Lib\IW4MAdmin.dll && echo pause) > "%SolutionDir%Publish\WindowsPrerelease\StartIW4MAdmin.cmd"
|
||||||
@(echo dotnet Lib/IW4MAdmin.dll && echo pause) > "%SolutionDir%Publish\Windows\StartIW4MAdmin.cmd"
|
@(echo @echo off && echo @title IW4MAdmin && echo dotnet Lib\IW4MAdmin.dll && echo pause) > "%SolutionDir%Publish\Windows\StartIW4MAdmin.cmd"
|
||||||
|
|
||||||
@(echo #!/bin/bash && echo dotnet Lib\IW4MAdmin.dll) > "%SolutionDir%Publish\WindowsPrerelease\StartIW4MAdmin.sh"
|
@(echo #!/bin/bash && echo dotnet Lib/IW4MAdmin.dll) > "%SolutionDir%Publish\WindowsPrerelease\StartIW4MAdmin.sh"
|
||||||
@(echo #!/bin/bash && echo dotnet Lib\IW4MAdmin.dll) > "%SolutionDir%Publish\Windows\StartIW4MAdmin.sh"
|
@(echo #!/bin/bash && echo dotnet Lib/IW4MAdmin.dll) > "%SolutionDir%Publish\Windows\StartIW4MAdmin.sh"
|
||||||
|
|
||||||
eCHO "%CurrentConfiguration%"
|
|
||||||
|
@ -16,7 +16,231 @@
|
|||||||
"Keep grenade launcher use to a minimum",
|
"Keep grenade launcher use to a minimum",
|
||||||
"Balance teams at ALL times"
|
"Balance teams at ALL times"
|
||||||
],
|
],
|
||||||
|
"DisallowedClientNames": ["Unknown Soldier", "VickNet", "UnknownSoldier", "CHEATER"],
|
||||||
|
"QuickMessages": [
|
||||||
|
{
|
||||||
|
"Game": "IW4",
|
||||||
|
"Messages": {
|
||||||
|
"QUICKMESSAGE_AREA_SECURE": "Area secure!",
|
||||||
|
"QUICKMESSAGE_ARE_YOU_CRAZY": "Are you crazy?",
|
||||||
|
"QUICKMESSAGE_ATTACK_LEFT_FLANK": "Attack left flank!",
|
||||||
|
"QUICKMESSAGE_ATTACK_RIGHT_FLANK": "Attack right flank!",
|
||||||
|
"QUICKMESSAGE_COME_ON": "Come on.",
|
||||||
|
"QUICKMESSAGE_ENEMIES_SPOTTED": "Multiple contacts!",
|
||||||
|
"QUICKMESSAGE_ENEMY_DOWN": "Enemy down!",
|
||||||
|
"QUICKMESSAGE_ENEMY_GRENADE": "Enemy grenade!",
|
||||||
|
"QUICKMESSAGE_ENEMY_SPOTTED": "Contact!",
|
||||||
|
"QUICKMESSAGE_FALL_BACK": "Fall back!",
|
||||||
|
"QUICKMESSAGE_FOLLOW_ME": "On me!",
|
||||||
|
"QUICKMESSAGE_GREAT_SHOT": "Nice shot!",
|
||||||
|
"QUICKMESSAGE_GRENADE": "Grenade!",
|
||||||
|
"QUICKMESSAGE_HOLD_THIS_POSITION": "Hold this position!",
|
||||||
|
"QUICKMESSAGE_HOLD_YOUR_FIRE": "Hold your fire!",
|
||||||
|
"QUICKMESSAGE_IM_IN_POSITION": "In position.",
|
||||||
|
"QUICKMESSAGE_IM_ON_MY_WAY": "Moving.",
|
||||||
|
"QUICKMESSAGE_MOVE_IN": "Move in!",
|
||||||
|
"QUICKMESSAGE_NEED_REINFORCEMENTS": "Need reinforcements!",
|
||||||
|
"QUICKMESSAGE_NO_SIR": "Negative.",
|
||||||
|
"QUICKMESSAGE_ON_MY_WAY": "On my way.",
|
||||||
|
"QUICKMESSAGE_REGROUP": "Regroup!",
|
||||||
|
"QUICKMESSAGE_SNIPER": "Sniper!",
|
||||||
|
"QUICKMESSAGE_SORRY": "Sorry.",
|
||||||
|
"QUICKMESSAGE_SQUAD_ATTACK_LEFT_FLANK": "Squad, attack left flank!",
|
||||||
|
"QUICKMESSAGE_SQUAD_ATTACK_RIGHT_FLANK": "Squad, attack right flank!",
|
||||||
|
"QUICKMESSAGE_SQUAD_HOLD_THIS_POSITION": "Squad, hold this position!",
|
||||||
|
"QUICKMESSAGE_SQUAD_REGROUP": "Squad, regroup!",
|
||||||
|
"QUICKMESSAGE_SQUAD_STICK_TOGETHER": "Squad, stick together!",
|
||||||
|
"QUICKMESSAGE_STICK_TOGETHER": "Stick together!",
|
||||||
|
"QUICKMESSAGE_SUPPRESSING_FIRE": "Base of fire!",
|
||||||
|
"QUICKMESSAGE_TOOK_LONG_ENOUGH": "Took long enough!",
|
||||||
|
"QUICKMESSAGE_TOOK_YOU_LONG_ENOUGH": "Took you long enough!",
|
||||||
|
"QUICKMESSAGE_WATCH_SIX": "Watch your six!",
|
||||||
|
"QUICKMESSAGE_YES_SIR": "Roger.",
|
||||||
|
"QUICKMESSAGE_YOURE_CRAZY": "You're crazy!",
|
||||||
|
"QUICKMESSAGE_YOURE_NUTS": "You're nuts!",
|
||||||
|
"QUICKMESSAGE_YOU_OUTTA_YOUR_MIND": "You outta your mind?"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"Maps": [
|
"Maps": [
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Game": "T4",
|
||||||
|
"Maps": [
|
||||||
|
{
|
||||||
|
"Alias": "Airfield",
|
||||||
|
"Name": "mp_airfield"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Asylum",
|
||||||
|
"Name": "mp_asylum"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Castle",
|
||||||
|
"Name": "mp_castle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Cliffside",
|
||||||
|
"Name": "mp_shrine"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Courtyard",
|
||||||
|
"Name": "mp_courtyard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Dome",
|
||||||
|
"Name": "mp_dome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Downfall",
|
||||||
|
"Name": "mp_downfall"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Hanger",
|
||||||
|
"Name": "mp_hangar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Makin",
|
||||||
|
"Name": "mp_makin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Outskirts",
|
||||||
|
"Name": "mp_outskirts"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Roundhouse",
|
||||||
|
"Name": "mp_roundhouse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Upheaval",
|
||||||
|
"Name": "mp_suburban"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Knee Deep",
|
||||||
|
"Name": "mp_kneedeep"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Nightfire",
|
||||||
|
"Name": "mp_nachtfeuer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Station",
|
||||||
|
"Name": "mp_subway"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Banzai",
|
||||||
|
"Name": "mp_kwai"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Corrosion",
|
||||||
|
"Name": "mp_stalingrad"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Sub Pens",
|
||||||
|
"Name": "mp_docks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Battery",
|
||||||
|
"Name": "mp_drum"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Breach",
|
||||||
|
"Name": "mp_bgate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Revolution",
|
||||||
|
"Name": "mp_vodka"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Game": "IW4",
|
"Game": "IW4",
|
||||||
"Maps": [
|
"Maps": [
|
||||||
@ -246,6 +470,260 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Game": "T5",
|
||||||
|
"Maps": [
|
||||||
|
{
|
||||||
|
"Alias": "Array",
|
||||||
|
"Name": "mp_array"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Berlin Wall",
|
||||||
|
"Name": "mp_berlinwall2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Convoy",
|
||||||
|
"Name": "mp_gridlock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Cracked",
|
||||||
|
"Name": "mp_cracked"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Crisis",
|
||||||
|
"Name": "mp_crisis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Discovery",
|
||||||
|
"Name": "mp_discovery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Drive-In",
|
||||||
|
"Name": "mp_drivein"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Firing Range",
|
||||||
|
"Name": "mp_firingrange"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Grid",
|
||||||
|
"Name": "mp_duga"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Hangar 18",
|
||||||
|
"Name": "mp_area51"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Hanoi",
|
||||||
|
"Name": "mp_hanoi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Hazard",
|
||||||
|
"Name": "mp_golfcourse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Hotel",
|
||||||
|
"Name": "mp_hotel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Jungle",
|
||||||
|
"Name": "mp_havoc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Kowloon",
|
||||||
|
"Name": "mp_kowloon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Launch",
|
||||||
|
"Name": "mp_cosmodrome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Nuketown",
|
||||||
|
"Name": "mp_nuked"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Radiation",
|
||||||
|
"Name": "mp_radiation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Silo",
|
||||||
|
"Name": "mp_silo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Stadium",
|
||||||
|
"Name": "mp_stadium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Stockpile",
|
||||||
|
"Name": "mp_outskirts"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Summit",
|
||||||
|
"Name": "mp_mountain"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Villa",
|
||||||
|
"Name": "mp_villa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "WMD",
|
||||||
|
"Name": "mp_russianbase"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Zoo",
|
||||||
|
"Name": "mp_zoo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Game": "IW5",
|
||||||
|
"Maps": [
|
||||||
|
{
|
||||||
|
"Alias": "Seatown",
|
||||||
|
"Name": "mp_seatown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Lockdown",
|
||||||
|
"Name": "mp_alpha"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Mission",
|
||||||
|
"Name": "mp_bravo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Carbon",
|
||||||
|
"Name": "mp_carbon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Dome",
|
||||||
|
"Name": "mp_dome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Arkaden",
|
||||||
|
"Name": "mp_plaza2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Downturn",
|
||||||
|
"Name": "mp_exchange"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Bootleg",
|
||||||
|
"Name": "mp_bootleg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Hardhat",
|
||||||
|
"Name": "mp_hardhat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Interchange",
|
||||||
|
"Name": "mp_interchange"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Fallen",
|
||||||
|
"Name": "mp_lambeth"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Outpost",
|
||||||
|
"Name": "mp_radar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Bakaara",
|
||||||
|
"Name": "mp_mogadishu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Resistance",
|
||||||
|
"Name": "mp_paris"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Underground",
|
||||||
|
"Name": "mp_underground"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Village",
|
||||||
|
"Name": "mp_village"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Aground",
|
||||||
|
"Name": "mp_aground_ss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Boardwalk",
|
||||||
|
"Name": "mp_boardwalk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "U-turn",
|
||||||
|
"Name": "mp_burn_ss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Foundation",
|
||||||
|
"Name": "mp_cement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Erosion",
|
||||||
|
"Name": "mp_courtyard_ss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Intersection",
|
||||||
|
"Name": "mp_crosswalk_ss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Getaway",
|
||||||
|
"Name": "mp_hillside_ss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Piazza",
|
||||||
|
"Name": "mp_italy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Sanctuary",
|
||||||
|
"Name": "mp_meteora"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Gulch",
|
||||||
|
"Name": "mp_moab"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Black Box",
|
||||||
|
"Name": "mp_morningwood"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Parish",
|
||||||
|
"Name": "mp_nola"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Overwatch",
|
||||||
|
"Name": "mp_overwatch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Liberation",
|
||||||
|
"Name": "mp_park"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Oasis",
|
||||||
|
"Name": "mp_qadeem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Lookout",
|
||||||
|
"Name": "mp_restrepo_ss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Off Shore",
|
||||||
|
"Name": "mp_roughneck"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Decommission",
|
||||||
|
"Name": "mp_shipbreaker"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Vortex",
|
||||||
|
"Name": "mp_six_ss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Terminal",
|
||||||
|
"Name": "mp_terminal_cls"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Game": "T6",
|
"Game": "T6",
|
||||||
"Maps": [
|
"Maps": [
|
||||||
@ -402,95 +880,6 @@
|
|||||||
"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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,8 @@ namespace IW4MAdmin.Application.EventParsers
|
|||||||
|
|
||||||
public Game GameName { get; set; } = Game.COD;
|
public Game GameName { get; set; } = Game.COD;
|
||||||
|
|
||||||
|
public string URLProtocolFormat { get; set; } = "CoD://{{ip}}:{{port}}";
|
||||||
|
|
||||||
public virtual GameEvent GetEvent(Server server, string logLine)
|
public virtual GameEvent GetEvent(Server server, string logLine)
|
||||||
{
|
{
|
||||||
logLine = Regex.Replace(logLine, @"([0-9]+:[0-9]+ |^[0-9]+ )", "").Trim();
|
logLine = Regex.Replace(logLine, @"([0-9]+:[0-9]+ |^[0-9]+ )", "").Trim();
|
||||||
@ -87,7 +89,7 @@ namespace IW4MAdmin.Application.EventParsers
|
|||||||
return new GameEvent()
|
return new GameEvent()
|
||||||
{
|
{
|
||||||
Type = GameEvent.EventType.JoinTeam,
|
Type = GameEvent.EventType.JoinTeam,
|
||||||
Data = eventType,
|
Data = logLine,
|
||||||
Origin = origin,
|
Origin = origin,
|
||||||
Owner = server
|
Owner = server
|
||||||
};
|
};
|
||||||
@ -139,11 +141,16 @@ namespace IW4MAdmin.Application.EventParsers
|
|||||||
|
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
{
|
{
|
||||||
var origin = server.GetClientsAsList()
|
string originId = match.Groups[Configuration.Kill.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].Value.ToString();
|
||||||
.First(c => c.NetworkId == match.Groups[Configuration.Kill.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertLong());
|
string targetId = match.Groups[Configuration.Kill.GroupMapping[ParserRegex.GroupType.TargetNetworkId]].Value.ToString();
|
||||||
var target = server.GetClientsAsList()
|
|
||||||
.First(c => c.NetworkId == match.Groups[Configuration.Kill.GroupMapping[ParserRegex.GroupType.TargetNetworkId]].ToString().ConvertLong());
|
|
||||||
|
|
||||||
|
var origin = !string.IsNullOrEmpty(originId) ? server.GetClientsAsList()
|
||||||
|
.First(c => c.NetworkId == originId.ConvertLong()) :
|
||||||
|
Utilities.IW4MAdminClient(server);
|
||||||
|
|
||||||
|
var target = !string.IsNullOrEmpty(targetId) ? server.GetClientsAsList()
|
||||||
|
.First(c => c.NetworkId == targetId.ConvertLong()) :
|
||||||
|
Utilities.IW4MAdminClient(server);
|
||||||
|
|
||||||
return new GameEvent()
|
return new GameEvent()
|
||||||
{
|
{
|
||||||
@ -159,8 +166,14 @@ namespace IW4MAdmin.Application.EventParsers
|
|||||||
|
|
||||||
if (eventType == "ScriptKill")
|
if (eventType == "ScriptKill")
|
||||||
{
|
{
|
||||||
var origin = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
long originId = lineSplit[1].ConvertLong();
|
||||||
var target = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong());
|
long targetId = lineSplit[2].ConvertLong();
|
||||||
|
|
||||||
|
var origin = originId == long.MinValue ? Utilities.IW4MAdminClient(server) :
|
||||||
|
server.GetClientsAsList().First(c => c.NetworkId == originId);
|
||||||
|
var target = targetId == long.MinValue ? Utilities.IW4MAdminClient(server) :
|
||||||
|
server.GetClientsAsList().FirstOrDefault(c => c.NetworkId == targetId) ?? Utilities.IW4MAdminClient(server);
|
||||||
|
|
||||||
return new GameEvent()
|
return new GameEvent()
|
||||||
{
|
{
|
||||||
Type = GameEvent.EventType.ScriptKill,
|
Type = GameEvent.EventType.ScriptKill,
|
||||||
@ -173,8 +186,13 @@ namespace IW4MAdmin.Application.EventParsers
|
|||||||
|
|
||||||
if (eventType == "ScriptDamage")
|
if (eventType == "ScriptDamage")
|
||||||
{
|
{
|
||||||
var origin = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
long originId = lineSplit[1].ConvertLong();
|
||||||
var target = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong());
|
long targetId = lineSplit[2].ConvertLong();
|
||||||
|
|
||||||
|
var origin = originId == long.MinValue ? Utilities.IW4MAdminClient(server) :
|
||||||
|
server.GetClientsAsList().First(c => c.NetworkId == originId);
|
||||||
|
var target = targetId == long.MinValue ? Utilities.IW4MAdminClient(server) :
|
||||||
|
server.GetClientsAsList().FirstOrDefault(c => c.NetworkId == targetId) ?? Utilities.IW4MAdminClient(server);
|
||||||
|
|
||||||
return new GameEvent()
|
return new GameEvent()
|
||||||
{
|
{
|
||||||
@ -195,15 +213,21 @@ namespace IW4MAdmin.Application.EventParsers
|
|||||||
|
|
||||||
if (regexMatch.Success)
|
if (regexMatch.Success)
|
||||||
{
|
{
|
||||||
var origin = server.GetClientsAsList()
|
string originId = regexMatch.Groups[Configuration.Damage.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString();
|
||||||
.First(c => c.NetworkId == regexMatch.Groups[Configuration.Damage.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertLong());
|
string targetId = regexMatch.Groups[Configuration.Damage.GroupMapping[ParserRegex.GroupType.TargetNetworkId]].ToString();
|
||||||
var target = server.GetClientsAsList()
|
|
||||||
.First(c => c.NetworkId == regexMatch.Groups[Configuration.Damage.GroupMapping[ParserRegex.GroupType.TargetNetworkId]].ToString().ConvertLong());
|
var origin = !string.IsNullOrEmpty(originId) ? server.GetClientsAsList()
|
||||||
|
.First(c => c.NetworkId == originId.ConvertLong()) :
|
||||||
|
Utilities.IW4MAdminClient(server);
|
||||||
|
|
||||||
|
var target = !string.IsNullOrEmpty(targetId) ? server.GetClientsAsList()
|
||||||
|
.First(c => c.NetworkId == targetId.ConvertLong()) :
|
||||||
|
Utilities.IW4MAdminClient(server);
|
||||||
|
|
||||||
return new GameEvent()
|
return new GameEvent()
|
||||||
{
|
{
|
||||||
Type = GameEvent.EventType.Damage,
|
Type = GameEvent.EventType.Damage,
|
||||||
Data = eventType,
|
Data = logLine,
|
||||||
Origin = origin,
|
Origin = origin,
|
||||||
Target = target,
|
Target = target,
|
||||||
Owner = server
|
Owner = server
|
||||||
@ -229,7 +253,6 @@ namespace IW4MAdmin.Application.EventParsers
|
|||||||
{
|
{
|
||||||
CurrentAlias = new EFAlias()
|
CurrentAlias = new EFAlias()
|
||||||
{
|
{
|
||||||
Active = false,
|
|
||||||
Name = regexMatch.Groups[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginName]].ToString().StripColors(),
|
Name = regexMatch.Groups[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginName]].ToString().StripColors(),
|
||||||
},
|
},
|
||||||
NetworkId = regexMatch.Groups[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertLong(),
|
NetworkId = regexMatch.Groups[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertLong(),
|
||||||
@ -256,7 +279,6 @@ namespace IW4MAdmin.Application.EventParsers
|
|||||||
{
|
{
|
||||||
CurrentAlias = new EFAlias()
|
CurrentAlias = new EFAlias()
|
||||||
{
|
{
|
||||||
Active = false,
|
|
||||||
Name = regexMatch.Groups[Configuration.Quit.GroupMapping[ParserRegex.GroupType.OriginName]].ToString().StripColors()
|
Name = regexMatch.Groups[Configuration.Quit.GroupMapping[ParserRegex.GroupType.OriginName]].ToString().StripColors()
|
||||||
},
|
},
|
||||||
NetworkId = regexMatch.Groups[Configuration.Quit.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertLong(),
|
NetworkId = regexMatch.Groups[Configuration.Quit.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertLong(),
|
||||||
|
@ -30,7 +30,7 @@ namespace IW4MAdmin.Application.IO
|
|||||||
{
|
{
|
||||||
while (!Server.Manager.ShutdownRequested())
|
while (!Server.Manager.ShutdownRequested())
|
||||||
{
|
{
|
||||||
if ((Server.Manager as ApplicationManager).IsInitialized)
|
if (Server.IsInitialized)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -34,9 +34,9 @@ namespace IW4MAdmin.Application.IO
|
|||||||
// todo: max async
|
// 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
|
|
||||||
string newLine;
|
string newLine;
|
||||||
while (!String.IsNullOrEmpty(newLine = rd.ReadLine()))
|
while (!string.IsNullOrEmpty(newLine = await rd.ReadLineAsync()))
|
||||||
{
|
{
|
||||||
logLines.Add(newLine);
|
logLines.Add(newLine);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ using SharedLibraryCore.Exceptions;
|
|||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using SharedLibraryCore.Localization;
|
using SharedLibraryCore.Localization;
|
||||||
using SharedLibraryCore.Objects;
|
using SharedLibraryCore.Objects;
|
||||||
|
using SharedLibraryCore.Services;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@ -17,6 +18,7 @@ using System.Runtime.InteropServices;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using static SharedLibraryCore.Database.Models.EFClient;
|
||||||
|
|
||||||
namespace IW4MAdmin
|
namespace IW4MAdmin
|
||||||
{
|
{
|
||||||
@ -34,7 +36,6 @@ namespace IW4MAdmin
|
|||||||
override public async Task OnClientConnected(EFClient clientFromLog)
|
override public async Task OnClientConnected(EFClient clientFromLog)
|
||||||
{
|
{
|
||||||
Logger.WriteDebug($"Client slot #{clientFromLog.ClientNumber} now reserved");
|
Logger.WriteDebug($"Client slot #{clientFromLog.ClientNumber} now reserved");
|
||||||
Clients[clientFromLog.ClientNumber] = new EFClient();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -44,19 +45,16 @@ namespace IW4MAdmin
|
|||||||
if (client == null)
|
if (client == null)
|
||||||
{
|
{
|
||||||
Logger.WriteDebug($"Client {clientFromLog} first time connecting");
|
Logger.WriteDebug($"Client {clientFromLog} first time connecting");
|
||||||
|
clientFromLog.CurrentServer = this;
|
||||||
client = await Manager.GetClientService().Create(clientFromLog);
|
client = await Manager.GetClientService().Create(clientFromLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
// client has connected in the past
|
/// this is only a temporary version until the IPAddress is transmitted
|
||||||
else
|
client.CurrentAlias = new EFAlias()
|
||||||
{
|
|
||||||
// this is only a temporary version until the IPAddress is transmitted
|
|
||||||
client.CurrentAlias = new EFAlias
|
|
||||||
{
|
{
|
||||||
Name = clientFromLog.Name,
|
Name = clientFromLog.Name,
|
||||||
IPAddress = clientFromLog.IPAddress
|
IPAddress = clientFromLog.IPAddress
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
Logger.WriteInfo($"Client {client} connected...");
|
Logger.WriteInfo($"Client {client} connected...");
|
||||||
|
|
||||||
@ -68,8 +66,6 @@ namespace IW4MAdmin
|
|||||||
client.CurrentServer = this;
|
client.CurrentServer = this;
|
||||||
|
|
||||||
Clients[client.ClientNumber] = client;
|
Clients[client.ClientNumber] = client;
|
||||||
|
|
||||||
client.State = EFClient.ClientState.Connected;
|
|
||||||
#if DEBUG == true
|
#if DEBUG == true
|
||||||
Logger.WriteDebug($"End PreConnect for {client}");
|
Logger.WriteDebug($"End PreConnect for {client}");
|
||||||
#endif
|
#endif
|
||||||
@ -81,11 +77,8 @@ namespace IW4MAdmin
|
|||||||
};
|
};
|
||||||
|
|
||||||
Manager.GetEventHandler().AddEvent(e);
|
Manager.GetEventHandler().AddEvent(e);
|
||||||
|
|
||||||
if (client.IPAddress != null)
|
|
||||||
{
|
|
||||||
await client.OnJoin(client.IPAddress);
|
await client.OnJoin(client.IPAddress);
|
||||||
}
|
client.State = ClientState.Connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -97,12 +90,14 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
override public async Task OnClientDisconnected(EFClient client)
|
override public async Task OnClientDisconnected(EFClient client)
|
||||||
{
|
{
|
||||||
Logger.WriteInfo($"Client {client} [{client.State.ToString().ToLower()}] disconnecting...");
|
|
||||||
await client.OnDisconnect();
|
|
||||||
Clients[client.ClientNumber] = null;
|
|
||||||
#if DEBUG == true
|
#if DEBUG == true
|
||||||
Logger.WriteDebug($"End PreDisconnect for {client}");
|
if (client.ClientNumber >= 0)
|
||||||
|
{
|
||||||
#endif
|
#endif
|
||||||
|
Logger.WriteInfo($"Client {client} [{client.State.ToString().ToLower()}] disconnecting...");
|
||||||
|
Clients[client.ClientNumber] = null;
|
||||||
|
await client.OnDisconnect();
|
||||||
|
|
||||||
var e = new GameEvent()
|
var e = new GameEvent()
|
||||||
{
|
{
|
||||||
Origin = client,
|
Origin = client,
|
||||||
@ -111,6 +106,9 @@ namespace IW4MAdmin
|
|||||||
};
|
};
|
||||||
|
|
||||||
Manager.GetEventHandler().AddEvent(e);
|
Manager.GetEventHandler().AddEvent(e);
|
||||||
|
#if DEBUG == true
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task ExecuteEvent(GameEvent E)
|
public override async Task ExecuteEvent(GameEvent E)
|
||||||
@ -178,7 +176,9 @@ namespace IW4MAdmin
|
|||||||
{
|
{
|
||||||
if (E.Type == GameEvent.EventType.ChangePermission)
|
if (E.Type == GameEvent.EventType.ChangePermission)
|
||||||
{
|
{
|
||||||
if (!E.Target.IsPrivileged())
|
var newPermission = (Permission)E.Extra;
|
||||||
|
|
||||||
|
if (newPermission < Permission.Moderator)
|
||||||
{
|
{
|
||||||
// remove banned or demoted privileged user
|
// remove banned or demoted privileged user
|
||||||
Manager.GetPrivilegedClients().Remove(E.Target.ClientId);
|
Manager.GetPrivilegedClients().Remove(E.Target.ClientId);
|
||||||
@ -188,15 +188,36 @@ namespace IW4MAdmin
|
|||||||
{
|
{
|
||||||
Manager.GetPrivilegedClients()[E.Target.ClientId] = E.Target;
|
Manager.GetPrivilegedClients()[E.Target.ClientId] = E.Target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.WriteInfo($"{E.Origin} is setting {E.Target} to permission level {newPermission}");
|
||||||
|
await Manager.GetClientService().UpdateLevel(newPermission, E.Target, E.Origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (E.Type == GameEvent.EventType.PreConnect)
|
else if (E.Type == GameEvent.EventType.PreConnect)
|
||||||
{
|
{
|
||||||
|
// we don't want to track bots in the database at all if ignore bots is requested
|
||||||
|
if (E.Origin.IsBot && Manager.GetApplicationSettings().Configuration().IgnoreBots)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingClient = GetClientsAsList().FirstOrDefault(_client => _client.Equals(E.Origin));
|
||||||
|
|
||||||
|
// they're already connected
|
||||||
|
if (existingClient != null)
|
||||||
|
{
|
||||||
|
Logger.WriteWarning($"detected preconnect for {E.Origin}, but they are already connected");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CONNECT:
|
||||||
if (Clients[E.Origin.ClientNumber] == null)
|
if (Clients[E.Origin.ClientNumber] == null)
|
||||||
{
|
{
|
||||||
#if DEBUG == true
|
#if DEBUG == true
|
||||||
Logger.WriteDebug($"Begin PreConnect for {E.Origin}");
|
Logger.WriteDebug($"Begin PreConnect for {E.Origin}");
|
||||||
#endif
|
#endif
|
||||||
|
// we can go ahead and put them in so that they don't get re added
|
||||||
|
Clients[E.Origin.ClientNumber] = E.Origin;
|
||||||
await OnClientConnected(E.Origin);
|
await OnClientConnected(E.Origin);
|
||||||
|
|
||||||
ChatHistory.Add(new ChatInfo()
|
ChatHistory.Add(new ChatInfo()
|
||||||
@ -212,9 +233,12 @@ namespace IW4MAdmin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for some reason there's still a client in the spot
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return false;
|
Logger.WriteWarning($"{E.Origin} is connecteding but {Clients[E.Origin.ClientNumber]} is currently in that client slot");
|
||||||
|
await OnClientDisconnected(Clients[E.Origin.ClientNumber]);
|
||||||
|
goto CONNECT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,17 +257,29 @@ namespace IW4MAdmin
|
|||||||
};
|
};
|
||||||
|
|
||||||
var addedPenalty = await Manager.GetPenaltyService().Create(newPenalty);
|
var addedPenalty = await Manager.GetPenaltyService().Create(newPenalty);
|
||||||
await Manager.GetClientService().Update(E.Target);
|
E.Target.SetLevel(Permission.Flagged, E.Origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (E.Type == GameEvent.EventType.Unflag)
|
else if (E.Type == GameEvent.EventType.Unflag)
|
||||||
{
|
{
|
||||||
await Manager.GetClientService().Update(E.Target);
|
var unflagPenalty = new Penalty()
|
||||||
|
{
|
||||||
|
Type = Penalty.PenaltyType.Unflag,
|
||||||
|
Expires = DateTime.UtcNow,
|
||||||
|
Offender = E.Target,
|
||||||
|
Offense = E.Data,
|
||||||
|
Punisher = E.Origin,
|
||||||
|
When = DateTime.UtcNow,
|
||||||
|
Link = E.Target.AliasLink
|
||||||
|
};
|
||||||
|
|
||||||
|
await Manager.GetPenaltyService().Create(unflagPenalty);
|
||||||
|
E.Target.SetLevel(Permission.User, E.Origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (E.Type == GameEvent.EventType.Report)
|
else if (E.Type == GameEvent.EventType.Report)
|
||||||
{
|
{
|
||||||
this.Reports.Add(new Report()
|
Reports.Add(new Report()
|
||||||
{
|
{
|
||||||
Origin = E.Origin,
|
Origin = E.Origin,
|
||||||
Target = E.Target,
|
Target = E.Target,
|
||||||
@ -277,37 +313,21 @@ namespace IW4MAdmin
|
|||||||
await Warn(E.Data, E.Target, E.Origin);
|
await Warn(E.Data, E.Target, E.Origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (E.Type == GameEvent.EventType.Quit)
|
else if (E.Type == GameEvent.EventType.Disconnect)
|
||||||
{
|
{
|
||||||
var origin = GetClientsAsList().FirstOrDefault(_client => _client.NetworkId.Equals(E.Origin));
|
ChatHistory.Add(new ChatInfo()
|
||||||
|
{
|
||||||
|
Name = E.Origin.Name,
|
||||||
|
Message = "DISCONNECTED",
|
||||||
|
Time = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
|
||||||
if (origin != null)
|
await new MetaService().AddPersistentMeta("LastMapPlayed", CurrentMap.Alias, E.Origin);
|
||||||
{
|
await new MetaService().AddPersistentMeta("LastServerPlayed", E.Owner.Hostname, E.Origin);
|
||||||
var e = new GameEvent()
|
|
||||||
{
|
|
||||||
Type = GameEvent.EventType.Disconnect,
|
|
||||||
Origin = origin,
|
|
||||||
Owner = this
|
|
||||||
};
|
|
||||||
|
|
||||||
Manager.GetEventHandler().AddEvent(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (E.Type == GameEvent.EventType.PreDisconnect)
|
else if (E.Type == GameEvent.EventType.PreDisconnect)
|
||||||
{
|
{
|
||||||
if ((DateTime.UtcNow - SessionStart).TotalSeconds < 30)
|
|
||||||
{
|
|
||||||
Logger.WriteInfo($"Cancelling pre disconnect for {E.Origin} as it occured too close to map end");
|
|
||||||
E.FailReason = GameEvent.EventFailReason.Invalid;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// predisconnect comes from minimal rcon polled players and minimal log players
|
// predisconnect comes from minimal rcon polled players and minimal log players
|
||||||
// so we need to disconnect the "full" version of the client
|
// so we need to disconnect the "full" version of the client
|
||||||
var client = GetClientsAsList().FirstOrDefault(_client => _client.Equals(E.Origin));
|
var client = GetClientsAsList().FirstOrDefault(_client => _client.Equals(E.Origin));
|
||||||
@ -317,18 +337,16 @@ namespace IW4MAdmin
|
|||||||
#if DEBUG == true
|
#if DEBUG == true
|
||||||
Logger.WriteDebug($"Begin PreDisconnect for {client}");
|
Logger.WriteDebug($"Begin PreDisconnect for {client}");
|
||||||
#endif
|
#endif
|
||||||
ChatHistory.Add(new ChatInfo()
|
|
||||||
{
|
|
||||||
Name = client.Name,
|
|
||||||
Message = "DISCONNECTED",
|
|
||||||
Time = DateTime.UtcNow
|
|
||||||
});
|
|
||||||
|
|
||||||
await OnClientDisconnected(client);
|
await OnClientDisconnected(client);
|
||||||
|
#if DEBUG == true
|
||||||
|
Logger.WriteDebug($"End PreDisconnect for {client}");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else if (client?.State != ClientState.Disconnecting)
|
||||||
{
|
{
|
||||||
|
Logger.WriteWarning($"Client {E.Origin} detected as disconnecting, but could not find them in the player list");
|
||||||
|
Logger.WriteDebug($"Expected {E.Origin} but found {GetClientsAsList().FirstOrDefault(_client => _client.ClientNumber == E.Origin.ClientNumber)}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -389,7 +407,7 @@ namespace IW4MAdmin
|
|||||||
var dict = (Dictionary<string, string>)E.Extra;
|
var dict = (Dictionary<string, string>)E.Extra;
|
||||||
Gametype = dict["g_gametype"].StripColors();
|
Gametype = dict["g_gametype"].StripColors();
|
||||||
Hostname = dict["sv_hostname"].StripColors();
|
Hostname = dict["sv_hostname"].StripColors();
|
||||||
MaxClients = Int32.Parse(dict["sv_maxclients"]);
|
MaxClients = int.Parse(dict["sv_maxclients"]);
|
||||||
|
|
||||||
string mapname = dict["mapname"].StripColors();
|
string mapname = dict["mapname"].StripColors();
|
||||||
CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map()
|
CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map()
|
||||||
@ -440,9 +458,9 @@ namespace IW4MAdmin
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task OnClientUpdate(EFClient origin)
|
private async Task OnClientUpdate(EFClient origin)
|
||||||
{
|
{
|
||||||
var client = Clients[origin.ClientNumber];
|
var client = GetClientsAsList().FirstOrDefault(_client => _client.Equals(origin));
|
||||||
|
|
||||||
if (client != null)
|
if (client != null)
|
||||||
{
|
{
|
||||||
@ -450,13 +468,22 @@ namespace IW4MAdmin
|
|||||||
client.Score = origin.Score;
|
client.Score = origin.Score;
|
||||||
|
|
||||||
// update their IP if it hasn't been set yet
|
// update their IP if it hasn't been set yet
|
||||||
if (client.IPAddress == null && !client.IsBot)
|
if (client.IPAddress == null &&
|
||||||
|
!client.IsBot &&
|
||||||
|
client.State == ClientState.Connected)
|
||||||
{
|
{
|
||||||
return client.OnJoin(origin.IPAddress);
|
try
|
||||||
}
|
{
|
||||||
|
await client.OnJoin(origin.IPAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
origin.CurrentServer.Logger.WriteWarning($"Could not execute on join for {origin}");
|
||||||
|
origin.CurrentServer.Logger.WriteDebug(e.GetExceptionInfo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -472,6 +499,7 @@ namespace IW4MAdmin
|
|||||||
#endif
|
#endif
|
||||||
var currentClients = GetClientsAsList();
|
var currentClients = GetClientsAsList();
|
||||||
var polledClients = (await this.GetStatusAsync()).AsEnumerable();
|
var polledClients = (await this.GetStatusAsync()).AsEnumerable();
|
||||||
|
|
||||||
if (Manager.GetApplicationSettings().Configuration().IgnoreBots)
|
if (Manager.GetApplicationSettings().Configuration().IgnoreBots)
|
||||||
{
|
{
|
||||||
polledClients = polledClients.Where(c => !c.IsBot);
|
polledClients = polledClients.Where(c => !c.IsBot);
|
||||||
@ -533,7 +561,7 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
foreach (var disconnectingClient in polledClients[1])
|
foreach (var disconnectingClient in polledClients[1])
|
||||||
{
|
{
|
||||||
if (disconnectingClient.State == EFClient.ClientState.Disconnecting)
|
if (disconnectingClient.State == ClientState.Disconnecting)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -557,6 +585,12 @@ namespace IW4MAdmin
|
|||||||
// this are our new connecting clients
|
// this are our new connecting clients
|
||||||
foreach (var client in polledClients[0])
|
foreach (var client in polledClients[0])
|
||||||
{
|
{
|
||||||
|
// note: this prevents players in ZMBI state from being registered with no name
|
||||||
|
if (string.IsNullOrEmpty(client.Name))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var e = new GameEvent()
|
var e = new GameEvent()
|
||||||
{
|
{
|
||||||
Type = GameEvent.EventType.PreConnect,
|
Type = GameEvent.EventType.PreConnect,
|
||||||
@ -590,7 +624,18 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
if (ConnectionErrors > 0)
|
if (ConnectionErrors > 0)
|
||||||
{
|
{
|
||||||
Logger.WriteVerbose($"{loc["MANAGER_CONNECTION_REST"]} {IP}:{Port}");
|
Logger.WriteVerbose(loc["MANAGER_CONNECTION_REST"].FormatExt($"{IP}:{Port}"));
|
||||||
|
|
||||||
|
var _event = new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.ConnectionRestored,
|
||||||
|
Owner = this,
|
||||||
|
Origin = Utilities.IW4MAdminClient(this),
|
||||||
|
Target = Utilities.IW4MAdminClient(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
Manager.GetEventHandler().AddEvent(_event);
|
||||||
|
|
||||||
Throttled = false;
|
Throttled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -605,6 +650,19 @@ namespace IW4MAdmin
|
|||||||
{
|
{
|
||||||
Logger.WriteError($"{e.Message} {IP}:{Port}, {loc["SERVER_ERROR_POLLING"]}");
|
Logger.WriteError($"{e.Message} {IP}:{Port}, {loc["SERVER_ERROR_POLLING"]}");
|
||||||
Logger.WriteDebug($"Internal Exception: {e.Data["internal_exception"]}");
|
Logger.WriteDebug($"Internal Exception: {e.Data["internal_exception"]}");
|
||||||
|
|
||||||
|
var _event = new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.ConnectionLost,
|
||||||
|
Owner = this,
|
||||||
|
Origin = Utilities.IW4MAdminClient(this),
|
||||||
|
Target = Utilities.IW4MAdminClient(this),
|
||||||
|
Extra = e,
|
||||||
|
Data = ConnectionErrors.ToString()
|
||||||
|
};
|
||||||
|
|
||||||
|
Manager.GetEventHandler().AddEvent(_event);
|
||||||
|
|
||||||
Throttled = true;
|
Throttled = true;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -630,7 +688,7 @@ namespace IW4MAdmin
|
|||||||
&& BroadcastMessages.Count > 0
|
&& BroadcastMessages.Count > 0
|
||||||
&& ClientNum > 0)
|
&& ClientNum > 0)
|
||||||
{
|
{
|
||||||
string[] messages = this.ProcessMessageToken(Manager.GetMessageTokens(), BroadcastMessages[NextMessage]).Split(Environment.NewLine);
|
string[] messages = (await this.ProcessMessageToken(Manager.GetMessageTokens(), BroadcastMessages[NextMessage])).Split(Environment.NewLine);
|
||||||
|
|
||||||
foreach (string message in messages)
|
foreach (string message in messages)
|
||||||
{
|
{
|
||||||
@ -774,8 +832,8 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
if (!File.Exists(LogPath) && ServerConfig.GameLogServerUrl == null)
|
if (!File.Exists(LogPath) && ServerConfig.GameLogServerUrl == null)
|
||||||
{
|
{
|
||||||
Logger.WriteError($"{LogPath} {loc["SERVER_ERROR_DNE"]}");
|
Logger.WriteError(loc["SERVER_ERROR_DNE"].FormatExt(LogPath));
|
||||||
throw new ServerException($"{loc["SERVER_ERROR_LOG"]} {LogPath}");
|
throw new ServerException(loc["SERVER_ERROR_DNE"].FormatExt(LogPath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -925,11 +983,9 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// this is set only because they're still in the server.
|
|
||||||
targetClient.Level = EFClient.Permission.Banned;
|
|
||||||
|
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
string formattedString = String.Format(RconParser.Configuration.CommandPrefixes.Kick, targetClient.ClientNumber, $"{loc["SERVER_BAN_TEXT"]} - ^5{reason} ^7({loc["SERVER_BAN_APPEAL"]} {Website})^7");
|
string formattedString = String.Format(RconParser.Configuration.CommandPrefixes.Kick, targetClient.ClientNumber, $"{loc["SERVER_BAN_TEXT"]} - ^5{reason} ^7{loc["SERVER_BAN_APPEAL"].FormatExt(Website)}^7");
|
||||||
await targetClient.CurrentServer.ExecuteCommandAsync(formattedString);
|
await targetClient.CurrentServer.ExecuteCommandAsync(formattedString);
|
||||||
#else
|
#else
|
||||||
await targetClient.CurrentServer.OnClientDisconnected(targetClient);
|
await targetClient.CurrentServer.OnClientDisconnected(targetClient);
|
||||||
@ -949,6 +1005,7 @@ namespace IW4MAdmin
|
|||||||
};
|
};
|
||||||
|
|
||||||
await Manager.GetPenaltyService().Create(newPenalty);
|
await Manager.GetPenaltyService().Create(newPenalty);
|
||||||
|
targetClient.SetLevel(Permission.Banned, originClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
override public async Task Unban(string reason, EFClient Target, EFClient Origin)
|
override public async Task Unban(string reason, EFClient Target, EFClient Origin)
|
||||||
@ -965,16 +1022,17 @@ namespace IW4MAdmin
|
|||||||
Link = Target.AliasLink
|
Link = Target.AliasLink
|
||||||
};
|
};
|
||||||
|
|
||||||
await Manager.GetPenaltyService().RemoveActivePenalties(Target.AliasLink.AliasLinkId);
|
await Manager.GetPenaltyService().RemoveActivePenalties(Target.AliasLink.AliasLinkId, Origin);
|
||||||
await Manager.GetPenaltyService().Create(unbanPenalty);
|
await Manager.GetPenaltyService().Create(unbanPenalty);
|
||||||
|
Target.SetLevel(Permission.User, Origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
override public void InitializeTokens()
|
override public void InitializeTokens()
|
||||||
{
|
{
|
||||||
Manager.GetMessageTokens().Add(new SharedLibraryCore.Helpers.MessageToken("TOTALPLAYERS", (Server s) => Manager.GetClientService().GetTotalClientsAsync().Result.ToString()));
|
Manager.GetMessageTokens().Add(new SharedLibraryCore.Helpers.MessageToken("TOTALPLAYERS", (Server s) => Task.Run(async () => (await Manager.GetClientService().GetTotalClientsAsync()).ToString())));
|
||||||
Manager.GetMessageTokens().Add(new SharedLibraryCore.Helpers.MessageToken("VERSION", (Server s) => Application.Program.Version.ToString()));
|
Manager.GetMessageTokens().Add(new SharedLibraryCore.Helpers.MessageToken("VERSION", (Server s) => Task.FromResult(Application.Program.Version.ToString())));
|
||||||
Manager.GetMessageTokens().Add(new SharedLibraryCore.Helpers.MessageToken("NEXTMAP", (Server s) => SharedLibraryCore.Commands.CNextMap.GetNextMap(s).Result));
|
Manager.GetMessageTokens().Add(new SharedLibraryCore.Helpers.MessageToken("NEXTMAP", (Server s) => SharedLibraryCore.Commands.CNextMap.GetNextMap(s)));
|
||||||
Manager.GetMessageTokens().Add(new SharedLibraryCore.Helpers.MessageToken("ADMINS", (Server s) => SharedLibraryCore.Commands.CListAdmins.OnlineAdmins(s)));
|
Manager.GetMessageTokens().Add(new SharedLibraryCore.Helpers.MessageToken("ADMINS", (Server s) => Task.FromResult(SharedLibraryCore.Commands.CListAdmins.OnlineAdmins(s))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ namespace IW4MAdmin.Application.Localization
|
|||||||
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");
|
||||||
|
|
||||||
|
if (!Program.ServerManager.GetApplicationSettings()?.Configuration()?.UseLocalTranslations ?? false)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var api = Endpoint.Get();
|
var api = Endpoint.Get();
|
||||||
@ -29,6 +31,7 @@ namespace IW4MAdmin.Application.Localization
|
|||||||
{
|
{
|
||||||
// the online localization failed so will default to local files
|
// the online localization failed so will default to local files
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// culture doesn't exist so we just want language
|
// culture doesn't exist so we just want language
|
||||||
if (localizationFiles.Length == 0)
|
if (localizationFiles.Length == 0)
|
||||||
|
@ -90,7 +90,7 @@ namespace IW4MAdmin.Application
|
|||||||
{
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||||
Console.WriteLine($"IW4MAdmin {loc["MANAGER_VERSION_UPDATE"]} [v{version.CurrentVersionStable.ToString("0.0")}]");
|
Console.WriteLine($"IW4MAdmin {loc["MANAGER_VERSION_UPDATE"]} [v{version.CurrentVersionStable.ToString("0.0")}]");
|
||||||
Console.WriteLine($"{loc["MANAGER_VERSION_CURRENT"]} [v{Version.ToString("0.0")}]");
|
Console.WriteLine(loc["MANAGER_VERSION_CURRENT"].FormatExt($"[v{Version.ToString("0.0")}]"));
|
||||||
Console.ForegroundColor = ConsoleColor.Gray;
|
Console.ForegroundColor = ConsoleColor.Gray;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -98,7 +98,7 @@ namespace IW4MAdmin.Application
|
|||||||
{
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||||
Console.WriteLine($"IW4MAdmin-Prerelease {loc["MANAGER_VERSION_UPDATE"]} [v{version.CurrentVersionPrerelease.ToString("0.0")}-pr]");
|
Console.WriteLine($"IW4MAdmin-Prerelease {loc["MANAGER_VERSION_UPDATE"]} [v{version.CurrentVersionPrerelease.ToString("0.0")}-pr]");
|
||||||
Console.WriteLine($"{loc["MANAGER_VERSION_CURRENT"]} [v{Version.ToString("0.0")}-pr]");
|
Console.WriteLine(loc["MANAGER_VERSION_CURRENT"].FormatExt($"[v{Version.ToString("0.0")}-pr]"));
|
||||||
Console.ForegroundColor = ConsoleColor.Gray;
|
Console.ForegroundColor = ConsoleColor.Gray;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Helpers;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
@ -10,16 +11,9 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<long, TokenState> _tokens;
|
private readonly ConcurrentDictionary<long, TokenState> _tokens;
|
||||||
private readonly RNGCryptoServiceProvider _random;
|
private readonly RNGCryptoServiceProvider _random;
|
||||||
private readonly static TimeSpan _timeoutPeriod = new TimeSpan(0, 0, 30);
|
private readonly static TimeSpan _timeoutPeriod = new TimeSpan(0, 0, 120);
|
||||||
private const short TOKEN_LENGTH = 4;
|
private const short TOKEN_LENGTH = 4;
|
||||||
|
|
||||||
private class TokenState
|
|
||||||
{
|
|
||||||
public long NetworkId { get; set; }
|
|
||||||
public DateTime RequestTime { get; set; } = DateTime.Now;
|
|
||||||
public string Token { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public TokenAuthentication()
|
public TokenAuthentication()
|
||||||
{
|
{
|
||||||
_tokens = new ConcurrentDictionary<long, TokenState>();
|
_tokens = new ConcurrentDictionary<long, TokenState>();
|
||||||
@ -38,32 +32,44 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
return authorizeSuccessful;
|
return authorizeSuccessful;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GenerateNextToken(long networkId)
|
public TokenState GenerateNextToken(long networkId)
|
||||||
{
|
{
|
||||||
TokenState state = null;
|
TokenState state = null;
|
||||||
|
|
||||||
if (_tokens.ContainsKey(networkId))
|
if (_tokens.ContainsKey(networkId))
|
||||||
{
|
{
|
||||||
state = _tokens[networkId];
|
state = _tokens[networkId];
|
||||||
|
|
||||||
if ((DateTime.Now - state.RequestTime) < _timeoutPeriod)
|
if ((DateTime.Now - state.RequestTime) > _timeoutPeriod)
|
||||||
{
|
{
|
||||||
return null;
|
_tokens.TryRemove(networkId, out TokenState _);
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_tokens.TryRemove(networkId, out TokenState _);
|
return state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state = new TokenState()
|
state = new TokenState()
|
||||||
{
|
{
|
||||||
NetworkId = networkId,
|
NetworkId = networkId,
|
||||||
Token = _generateToken()
|
Token = _generateToken(),
|
||||||
|
TokenDuration = _timeoutPeriod
|
||||||
};
|
};
|
||||||
|
|
||||||
_tokens.TryAdd(networkId, state);
|
_tokens.TryAdd(networkId, state);
|
||||||
return state.Token;
|
|
||||||
|
// 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()
|
public string _generateToken()
|
||||||
|
@ -119,9 +119,9 @@ namespace IW4MAdmin.Application.RconParsers
|
|||||||
}
|
}
|
||||||
|
|
||||||
int validMatches = 0;
|
int validMatches = 0;
|
||||||
foreach (string S in Status)
|
foreach (string statusLine in Status)
|
||||||
{
|
{
|
||||||
string responseLine = S.Trim();
|
string responseLine = statusLine.Trim();
|
||||||
|
|
||||||
var regex = Regex.Match(responseLine, Configuration.Status.Pattern, RegexOptions.IgnoreCase);
|
var regex = Regex.Match(responseLine, Configuration.Status.Pattern, RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
@ -158,11 +158,11 @@ namespace IW4MAdmin.Application.RconParsers
|
|||||||
State = EFClient.ClientState.Connecting
|
State = EFClient.ClientState.Connecting
|
||||||
};
|
};
|
||||||
|
|
||||||
// they've not fully connected yet
|
//// they've not fully connected yet
|
||||||
if (!client.IsBot && ping == 999)
|
//if (!client.IsBot && ping == 999)
|
||||||
{
|
//{
|
||||||
continue;
|
// continue;
|
||||||
}
|
//}
|
||||||
|
|
||||||
StatusPlayers.Add(client);
|
StatusPlayers.Add(client);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<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|log_env|$(MSBuildProjectFullPath)</InterpreterId>
|
||||||
<EnableNativeCodeDebugging>False</EnableNativeCodeDebugging>
|
<EnableNativeCodeDebugging>False</EnableNativeCodeDebugging>
|
||||||
<Environment>DEBUG=True</Environment>
|
<Environment>DEBUG=True</Environment>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@ -50,7 +50,6 @@
|
|||||||
<Folder Include="GameLogServer\" />
|
<Folder Include="GameLogServer\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="requirements.txt" />
|
|
||||||
<None Include="Stable.pubxml" />
|
<None Include="Stable.pubxml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -63,6 +62,18 @@
|
|||||||
<PathEnvironmentVariable>PYTHONPATH</PathEnvironmentVariable>
|
<PathEnvironmentVariable>PYTHONPATH</PathEnvironmentVariable>
|
||||||
<Architecture>X64</Architecture>
|
<Architecture>X64</Architecture>
|
||||||
</Interpreter>
|
</Interpreter>
|
||||||
|
<Interpreter Include="log_env\">
|
||||||
|
<Id>log_env</Id>
|
||||||
|
<Version>3.6</Version>
|
||||||
|
<Description>log_env (Python 3.6 (64-bit))</Description>
|
||||||
|
<InterpreterPath>Scripts\python.exe</InterpreterPath>
|
||||||
|
<WindowsInterpreterPath>Scripts\pythonw.exe</WindowsInterpreterPath>
|
||||||
|
<PathEnvironmentVariable>PYTHONPATH</PathEnvironmentVariable>
|
||||||
|
<Architecture>X64</Architecture>
|
||||||
|
</Interpreter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="requirements.txt" />
|
||||||
</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" />
|
||||||
<!-- Specify pre- and post-build commands in the BeforeBuild and
|
<!-- Specify pre- and post-build commands in the BeforeBuild and
|
||||||
|
@ -5,55 +5,53 @@ 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 ) - 0.125 MB
|
# (if the time between checks is greater, ignore ) - in seconds
|
||||||
self.max_file_size_change = 125000
|
self.max_file_time_change = 30
|
||||||
# (if the time between checks is greater, ignore ) - 5 minutes
|
|
||||||
self.max_file_time_change = 60
|
|
||||||
|
|
||||||
def read_file(self, path):
|
def read_file(self, path):
|
||||||
|
# this removes old entries that are no longer valid
|
||||||
|
try:
|
||||||
|
self._clear_old_logs()
|
||||||
|
except Exception as e:
|
||||||
|
print('could not clear old logs')
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
if os.name != 'nt':
|
||||||
|
path = re.sub(r'^[A-Z]\:', '', path)
|
||||||
|
path = re.sub(r'\\+', '/', 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'^.+[\\|\/](.+)[\\|\/].+.log$', path):
|
if not re.search(r'^.+[\\|\/](.+)[\\|\/].+.log$', path):
|
||||||
return False
|
return False
|
||||||
# set the initialze size to the current file size
|
|
||||||
file_size = 0
|
|
||||||
|
|
||||||
if path not in self.log_file_sizes:
|
# get the new file size
|
||||||
self.log_file_sizes[path] = {
|
|
||||||
'length' : self.file_length(path),
|
|
||||||
'read': time.time()
|
|
||||||
}
|
|
||||||
return True
|
|
||||||
|
|
||||||
# grab the previous values
|
|
||||||
last_length = self.log_file_sizes[path]['length']
|
|
||||||
last_read = self.log_file_sizes[path]['read']
|
|
||||||
|
|
||||||
# the file is being tracked already
|
|
||||||
new_file_size = self.file_length(path)
|
new_file_size = self.file_length(path)
|
||||||
|
|
||||||
# the log size was unable to be read (probably the wrong path)
|
# the log size was unable to be read (probably the wrong path)
|
||||||
if new_file_size < 0:
|
if new_file_size < 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
now = time.time()
|
# this is the first time the log has been requested
|
||||||
|
if path not in self.log_file_sizes:
|
||||||
|
self.log_file_sizes[path] = {
|
||||||
|
'length' : new_file_size,
|
||||||
|
'read': time.time()
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
|
||||||
|
# grab the previous values
|
||||||
|
last_length = self.log_file_sizes[path]['length']
|
||||||
file_size_difference = new_file_size - last_length
|
file_size_difference = new_file_size - last_length
|
||||||
time_difference = now - last_read
|
|
||||||
|
|
||||||
# update the new size and actually read the data
|
# update the new size and actually read the data
|
||||||
self.log_file_sizes[path] = {
|
self.log_file_sizes[path] = {
|
||||||
'length': new_file_size,
|
'length': new_file_size,
|
||||||
'read': now
|
'read': time.time()
|
||||||
}
|
}
|
||||||
|
|
||||||
# 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"
|
|
||||||
if file_size_difference > self.max_file_size_change or time_difference > self.max_file_time_change:
|
|
||||||
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
|
||||||
|
|
||||||
@ -63,14 +61,25 @@ class LogReader(object):
|
|||||||
file_handle.seek(-length, 2)
|
file_handle.seek(-length, 2)
|
||||||
file_data = file_handle.read(length)
|
file_data = file_handle.read(length)
|
||||||
file_handle.close()
|
file_handle.close()
|
||||||
return file_data.decode('utf-8')
|
# using ignore errors omits the pesky 0xb2 bytes we're reading in for some reason
|
||||||
except:
|
return file_data.decode('utf-8', errors='ignore')
|
||||||
|
except Exception as e:
|
||||||
|
print('could not read the log file at {0}, wanted to read {1} bytes'.format(path, length))
|
||||||
|
print(e)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _clear_old_logs(self):
|
||||||
|
expired_logs = [path for path in self.log_file_sizes if int(time.time() - self.log_file_sizes[path]['read']) > self.max_file_time_change]
|
||||||
|
for log in expired_logs:
|
||||||
|
print('removing expired log {0}'.format(log))
|
||||||
|
del self.log_file_sizes[log]
|
||||||
|
|
||||||
def file_length(self, path):
|
def file_length(self, path):
|
||||||
try:
|
try:
|
||||||
return os.stat(path).st_size
|
return os.stat(path).st_size
|
||||||
except:
|
except Exception as e:
|
||||||
|
print('could not get the size of the log file at {0}'.format(path))
|
||||||
|
print(e)
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
reader = LogReader()
|
reader = LogReader()
|
||||||
|
@ -7,13 +7,8 @@ class LogResource(Resource):
|
|||||||
path = urlsafe_b64decode(path).decode('utf-8')
|
path = urlsafe_b64decode(path).decode('utf-8')
|
||||||
log_info = reader.read_file(path)
|
log_info = reader.read_file(path)
|
||||||
|
|
||||||
if log_info is False:
|
|
||||||
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 empty_read else len(log_info),
|
'length': 0 if log_info is False else len(log_info),
|
||||||
'data': log_info
|
'data': log_info
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
from flask_restful import Resource
|
#from flask_restful import Resource
|
||||||
from flask import request
|
#from flask import request
|
||||||
import requests
|
#import requests
|
||||||
import os
|
#import os
|
||||||
import subprocess
|
#import subprocess
|
||||||
import re
|
#import re
|
||||||
|
|
||||||
def get_pid_of_server_windows(port):
|
#def get_pid_of_server_windows(port):
|
||||||
process = subprocess.Popen('netstat -aon', shell=True, stdout=subprocess.PIPE)
|
# process = subprocess.Popen('netstat -aon', shell=True, stdout=subprocess.PIPE)
|
||||||
output = process.communicate()[0]
|
# 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'))
|
# 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:
|
# if matches is not None:
|
||||||
return matches.group(3)
|
# return matches.group(3)
|
||||||
else:
|
# else:
|
||||||
return 0
|
# return 0
|
||||||
|
|
||||||
class RestartResource(Resource):
|
#class RestartResource(Resource):
|
||||||
def get(self):
|
# def get(self):
|
||||||
try:
|
# try:
|
||||||
response = requests.get('http://' + request.remote_addr + ':1624/api/restartapproved')
|
# response = requests.get('http://' + request.remote_addr + ':1624/api/restartapproved')
|
||||||
if response.status_code == 200:
|
# if response.status_code == 200:
|
||||||
pid = get_pid_of_server_windows(response.json()['port'])
|
# pid = get_pid_of_server_windows(response.json()['port'])
|
||||||
subprocess.check_output("Taskkill /PID %s /F" % pid)
|
# subprocess.check_output("Taskkill /PID %s /F" % pid)
|
||||||
else:
|
# else:
|
||||||
return {}, 400
|
# return {}, 400
|
||||||
except Exception as e:
|
# except Exception as e:
|
||||||
print(e)
|
# print(e)
|
||||||
return {}, 500
|
# return {}, 500
|
||||||
return {}, 200
|
# return {}, 200
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
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
|
#from .restart_resource import RestartResource
|
||||||
|
import logging
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
|
log = logging.getLogger('werkzeug')
|
||||||
|
log.setLevel(logging.ERROR)
|
||||||
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')
|
#api.add_resource(RestartResource, '/restart')
|
||||||
|
@ -1,26 +1,12 @@
|
|||||||
aniso8601==3.0.2
|
aniso8601==6.0.0
|
||||||
APScheduler==3.5.3
|
Click==7.0
|
||||||
certifi==2018.10.15
|
|
||||||
chardet==3.0.4
|
|
||||||
click==6.7
|
|
||||||
Flask==1.0.2
|
Flask==1.0.2
|
||||||
Flask-JWT==0.3.2
|
Flask-RESTful==0.3.7
|
||||||
Flask-JWT-Extended==3.8.1
|
itsdangerous==1.1.0
|
||||||
Flask-RESTful==0.3.6
|
|
||||||
idna==2.7
|
|
||||||
itsdangerous==0.24
|
|
||||||
Jinja2==2.10
|
Jinja2==2.10
|
||||||
MarkupSafe==1.0
|
MarkupSafe==1.1.1
|
||||||
marshmallow==3.0.0b8
|
pip==10.0.1
|
||||||
pip==9.0.3
|
pytz==2018.9
|
||||||
psutil==5.4.8
|
setuptools==39.0.1
|
||||||
pygal==2.4.0
|
six==1.12.0
|
||||||
PyJWT==1.4.2
|
Werkzeug==0.15.2
|
||||||
pytz==2018.7
|
|
||||||
requests==2.20.0
|
|
||||||
setuptools==40.5.0
|
|
||||||
six==1.11.0
|
|
||||||
timeago==1.0.8
|
|
||||||
tzlocal==1.5.1
|
|
||||||
urllib3==1.24
|
|
||||||
Werkzeug==0.14.1
|
|
||||||
|
@ -48,6 +48,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ScriptPlugins", "ScriptPlug
|
|||||||
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
|
||||||
@ -286,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
|
||||||
@ -306,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
|
||||||
@ -315,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
|
||||||
@ -338,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
|
||||||
@ -350,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}
|
||||||
|
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>
|
29
Plugins/AutomessageFeed/Configuration.cs
Normal file
29
Plugins/AutomessageFeed/Configuration.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
|
||||||
|
namespace AutomessageFeed
|
||||||
|
{
|
||||||
|
class Configuration : IBaseConfiguration
|
||||||
|
{
|
||||||
|
public bool EnableFeed { get; set; }
|
||||||
|
public string FeedUrl { get; set; }
|
||||||
|
public int MaxFeedItems { get; set; }
|
||||||
|
|
||||||
|
public IBaseConfiguration Generate()
|
||||||
|
{
|
||||||
|
EnableFeed = Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_AUTOMESSAGEFEED_PROMPT_ENABLE"]);
|
||||||
|
|
||||||
|
if (EnableFeed)
|
||||||
|
{
|
||||||
|
FeedUrl = Utilities.PromptString(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_AUTOMESSAGEFEED_URL"]);
|
||||||
|
MaxFeedItems = Utilities.PromptInt(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_AUTOMESSAGEFEED_PROMPT_MAXITEMS"],
|
||||||
|
Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_AUTOMESSAGEFEED_PROMPT_MAXITEMS_DESC"],
|
||||||
|
0, int.MaxValue, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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<string> 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 && (_configuration.MaxFeedItems == 0 || _currentFeedItem < _configuration.MaxFeedItems))
|
||||||
|
{
|
||||||
|
_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,8 +1,5 @@
|
|||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using SharedLibraryCore.Database.Models;
|
using SharedLibraryCore.Database.Models;
|
||||||
using SharedLibraryCore.Objects;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Login.Commands
|
namespace IW4MAdmin.Plugins.Login.Commands
|
||||||
@ -22,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];
|
||||||
|
bool success = E.Owner.Manager.TokenAuthenticator.AuthorizeToken(E.Origin.NetworkId, E.Data);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
string[] hashedPassword = await Task.FromResult(SharedLibraryCore.Helpers.Hashing.Hash(E.Data, client.PasswordSalt));
|
string[] hashedPassword = await Task.FromResult(SharedLibraryCore.Helpers.Hashing.Hash(E.Data, client.PasswordSalt));
|
||||||
|
|
||||||
if (hashedPassword[0] == client.Password)
|
if (hashedPassword[0] == client.Password)
|
||||||
{
|
{
|
||||||
|
success = true;
|
||||||
Plugin.AuthorizedClients[E.Origin.ClientId] = true;
|
Plugin.AuthorizedClients[E.Origin.ClientId] = true;
|
||||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS"]);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -7,7 +7,6 @@ using SharedLibraryCore;
|
|||||||
using SharedLibraryCore.Configuration;
|
using SharedLibraryCore.Configuration;
|
||||||
using SharedLibraryCore.Database.Models;
|
using SharedLibraryCore.Database.Models;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using SharedLibraryCore.Objects;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.ProfanityDeterment
|
namespace IW4MAdmin.Plugins.ProfanityDeterment
|
||||||
{
|
{
|
||||||
|
@ -3,7 +3,7 @@ var eventParser;
|
|||||||
|
|
||||||
var plugin = {
|
var plugin = {
|
||||||
author: 'FrenchFry, RaidMax',
|
author: 'FrenchFry, RaidMax',
|
||||||
version: 0.2,
|
version: 0.3,
|
||||||
name: 'CoD4x Parser',
|
name: 'CoD4x Parser',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
|
|
||||||
@ -27,6 +27,7 @@ var plugin = {
|
|||||||
eventParser.Configuration.GameDirectory = 'main';
|
eventParser.Configuration.GameDirectory = 'main';
|
||||||
eventParser.Version = 'CoD4 X 1.8 win_mingw-x86 build 2055 May 2 2017';
|
eventParser.Version = 'CoD4 X 1.8 win_mingw-x86 build 2055 May 2 2017';
|
||||||
eventParser.GameName = 1; // IW3
|
eventParser.GameName = 1; // IW3
|
||||||
|
eventParser.URLProtocolFormat = 'cod4://{{ip}}:{{port}}';
|
||||||
},
|
},
|
||||||
|
|
||||||
onUnloadAsync: function () {
|
onUnloadAsync: function () {
|
||||||
|
@ -3,8 +3,8 @@ var eventParser;
|
|||||||
|
|
||||||
var plugin = {
|
var plugin = {
|
||||||
author: 'RaidMax',
|
author: 'RaidMax',
|
||||||
version: 0.2,
|
version: 0.3,
|
||||||
name: 'IW3 Parser',
|
name: 'IW4 Parser',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
|
|
||||||
onEventAsync: function (gameEvent, server) {
|
onEventAsync: function (gameEvent, server) {
|
||||||
@ -25,6 +25,7 @@ var plugin = {
|
|||||||
rconParser.GameName = 2; // IW4x
|
rconParser.GameName = 2; // IW4x
|
||||||
eventParser.Version = 'IW4x (v0.6.0)';
|
eventParser.Version = 'IW4x (v0.6.0)';
|
||||||
eventParser.GameName = 2; // IW4x
|
eventParser.GameName = 2; // IW4x
|
||||||
|
eventParser.URLProtocolFormat = 'iw4x://{{ip}}:{{port}}';
|
||||||
},
|
},
|
||||||
|
|
||||||
onUnloadAsync: function () {
|
onUnloadAsync: function () {
|
||||||
|
@ -4,7 +4,7 @@ var eventParser;
|
|||||||
var plugin = {
|
var plugin = {
|
||||||
author: 'RaidMax',
|
author: 'RaidMax',
|
||||||
version: 0.2,
|
version: 0.2,
|
||||||
name: 'Plutoniun T6 Parser',
|
name: 'Plutonium T6 Parser',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
|
|
||||||
onEventAsync: function (gameEvent, server) {
|
onEventAsync: function (gameEvent, server) {
|
||||||
|
@ -39,7 +39,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
where stats.ServerId == serverId
|
where stats.ServerId == serverId
|
||||||
where client.Level != EFClient.Permission.Banned
|
where client.Level != EFClient.Permission.Banned
|
||||||
where client.LastConnection >= thirtyDaysAgo
|
where client.LastConnection >= thirtyDaysAgo
|
||||||
orderby stats.Kills descending
|
orderby stats.TimePlayed descending
|
||||||
select new
|
select new
|
||||||
{
|
{
|
||||||
alias.Name,
|
alias.Name,
|
||||||
|
@ -69,7 +69,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
if (E.Message.IsBroadcastCommand())
|
if (E.Message.IsBroadcastCommand())
|
||||||
{
|
{
|
||||||
string name = E.Target == null ? E.Origin.Name : E.Target.Name;
|
string name = E.Target == null ? E.Origin.Name : E.Target.Name;
|
||||||
E.Owner.Broadcast($"{loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"]} ^5{name}^7");
|
E.Owner.Broadcast(loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"].FormatExt(name));
|
||||||
E.Owner.Broadcast(statLine);
|
E.Owner.Broadcast(statLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
{
|
{
|
||||||
if (E.Target != null)
|
if (E.Target != null)
|
||||||
{
|
{
|
||||||
E.Origin.Tell($"{loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"]} ^5{E.Target.Name}^7");
|
E.Origin.Tell(loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"].FormatExt(E.Target.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
E.Origin.Tell(statLine);
|
E.Origin.Tell(statLine);
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Dtos;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -12,25 +14,51 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers
|
|||||||
public class StatsController : BaseController
|
public class StatsController : BaseController
|
||||||
{
|
{
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> TopPlayersAsync()
|
public IActionResult TopPlayersAsync()
|
||||||
{
|
{
|
||||||
ViewBag.Title = Utilities.CurrentLocalization.LocalizationIndex.Set["WEBFRONT_STATS_INDEX_TITLE"];
|
ViewBag.Title = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_STATS_INDEX_TITLE"];
|
||||||
ViewBag.Description = Utilities.CurrentLocalization.LocalizationIndex.Set["WEBFRONT_STATS_INDEX_DESC"];
|
ViewBag.Description = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_STATS_INDEX_DESC"];
|
||||||
|
ViewBag.Servers = Manager.GetServers().Select(_server => new ServerInfo() { Name = _server.Hostname, ID = _server.EndPoint });
|
||||||
|
|
||||||
return View("Index", await Plugin.Manager.GetTopStats(0, 50));
|
return View("Index");
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> GetTopPlayersAsync(int count, int offset)
|
public async Task<IActionResult> GetTopPlayersAsync(int count, int offset, long? serverId = null)
|
||||||
{
|
{
|
||||||
return View("_List", await Plugin.Manager.GetTopStats(offset, count));
|
// this prevents empty results when we really want aggregate
|
||||||
|
if (serverId == 0)
|
||||||
|
{
|
||||||
|
serverId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var server = Manager.GetServers().FirstOrDefault(_server => _server.EndPoint == serverId);
|
||||||
|
|
||||||
|
if (server != null)
|
||||||
|
{
|
||||||
|
serverId = await StatManager.GetIdForServer(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
var results = await Plugin.Manager.GetTopStats(offset, count, serverId);
|
||||||
|
|
||||||
|
// this returns an empty result so we know to stale the loader
|
||||||
|
if (results.Count == 0 && offset > 0)
|
||||||
|
{
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return View("Components/TopPlayers/_List", results);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> GetMessageAsync(int serverId, DateTime when)
|
public async Task<IActionResult> GetMessageAsync(int serverId, long when)
|
||||||
{
|
{
|
||||||
var whenUpper = when.AddMinutes(5);
|
var whenTime = DateTime.FromFileTimeUtc(when);
|
||||||
var whenLower = when.AddMinutes(-5);
|
var whenUpper = whenTime.AddMinutes(5);
|
||||||
|
var whenLower = whenTime.AddMinutes(-5);
|
||||||
|
|
||||||
using (var ctx = new SharedLibraryCore.Database.DatabaseContext(true))
|
using (var ctx = new SharedLibraryCore.Database.DatabaseContext(true))
|
||||||
{
|
{
|
||||||
@ -38,7 +66,7 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers
|
|||||||
where message.ServerId == serverId
|
where message.ServerId == serverId
|
||||||
where message.TimeSent >= whenLower
|
where message.TimeSent >= whenLower
|
||||||
where message.TimeSent <= whenUpper
|
where message.TimeSent <= whenUpper
|
||||||
select new SharedLibraryCore.Dtos.ChatInfo()
|
select new ChatInfo()
|
||||||
{
|
{
|
||||||
ClientId = message.ClientId,
|
ClientId = message.ClientId,
|
||||||
Message = message.Message,
|
Message = message.Message,
|
||||||
@ -62,16 +90,30 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers
|
|||||||
{
|
{
|
||||||
using (var ctx = new SharedLibraryCore.Database.DatabaseContext(true))
|
using (var ctx = new SharedLibraryCore.Database.DatabaseContext(true))
|
||||||
{
|
{
|
||||||
var penaltyInfo = await ctx.Set<Models.EFACSnapshot>()
|
int linkId = await ctx.Clients
|
||||||
.Where(s => s.ClientId == clientId)
|
.Where(_client => _client.ClientId == clientId)
|
||||||
|
.Select(_client => _client.AliasLinkId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
var clientIds = await ctx.Clients.Where(_client => _client.AliasLinkId == linkId)
|
||||||
|
.Select(_client => _client.ClientId)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var iqPenaltyInfo = ctx.Set<Models.EFACSnapshot>()
|
||||||
|
.Where(s => clientIds.Contains(s.ClientId))
|
||||||
.Include(s => s.LastStrainAngle)
|
.Include(s => s.LastStrainAngle)
|
||||||
.Include(s => s.HitOrigin)
|
.Include(s => s.HitOrigin)
|
||||||
.Include(s => s.HitDestination)
|
.Include(s => s.HitDestination)
|
||||||
.Include(s => s.CurrentViewAngle)
|
.Include(s => s.CurrentViewAngle)
|
||||||
.Include(s => s.PredictedViewAngles)
|
.Include(s => s.PredictedViewAngles)
|
||||||
.OrderBy(s => s.When)
|
.OrderBy(s => s.When)
|
||||||
.ThenBy(s => s.Hits)
|
.ThenBy(s => s.Hits);
|
||||||
.ToListAsync();
|
|
||||||
|
#if DEBUG == true
|
||||||
|
var sql = iqPenaltyInfo.ToSql();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
var penaltyInfo = await iqPenaltyInfo.ToListAsync();
|
||||||
|
|
||||||
return View("_PenaltyInfo", penaltyInfo);
|
return View("_PenaltyInfo", penaltyInfo);
|
||||||
}
|
}
|
@ -82,13 +82,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<TopStatsInfo>> GetTopStats(int start, int count)
|
public async Task<List<TopStatsInfo>> GetTopStats(int start, int count, long? serverId = null)
|
||||||
{
|
{
|
||||||
using (var context = new DatabaseContext(true))
|
using (var context = new DatabaseContext(true))
|
||||||
{
|
{
|
||||||
// setup the query for the clients within the given rating range
|
// setup the query for the clients within the given rating range
|
||||||
var iqClientRatings = (from rating in context.Set<EFRating>()
|
var iqClientRatings = (from rating in context.Set<EFRating>()
|
||||||
.Where(GetRankingFunc())
|
.Where(GetRankingFunc(serverId))
|
||||||
select new
|
select new
|
||||||
{
|
{
|
||||||
rating.RatingHistory.ClientId,
|
rating.RatingHistory.ClientId,
|
||||||
@ -113,7 +113,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
|
|
||||||
var iqRatingInfo = from rating in context.Set<EFRating>()
|
var iqRatingInfo = from rating in context.Set<EFRating>()
|
||||||
where clientIds.Contains(rating.RatingHistory.ClientId)
|
where clientIds.Contains(rating.RatingHistory.ClientId)
|
||||||
where rating.ServerId == null
|
where rating.ServerId == serverId
|
||||||
select new
|
select new
|
||||||
{
|
{
|
||||||
rating.Ranking,
|
rating.Ranking,
|
||||||
@ -155,6 +155,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
var finished = topPlayers.Select(s => new TopStatsInfo()
|
var finished = topPlayers.Select(s => new TopStatsInfo()
|
||||||
{
|
{
|
||||||
ClientId = s.ClientId,
|
ClientId = s.ClientId,
|
||||||
|
Id = (int?)serverId ?? 0,
|
||||||
Deaths = s.Deaths,
|
Deaths = s.Deaths,
|
||||||
Kills = s.Kills,
|
Kills = s.Kills,
|
||||||
KDR = Math.Round(s.KDR, 2),
|
KDR = Math.Round(s.KDR, 2),
|
||||||
@ -427,19 +428,20 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
|
|
||||||
public void AddDamageEvent(string eventLine, int attackerClientId, int victimClientId, long 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>
|
||||||
|
@ -26,7 +26,7 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
public string Author => "RaidMax";
|
public string Author => "RaidMax";
|
||||||
|
|
||||||
public static StatManager Manager { get; private set; }
|
public static StatManager Manager { get; private set; }
|
||||||
private IManager ServerManager;
|
public static IManager ServerManager;
|
||||||
public static BaseConfigurationHandler<StatsConfiguration> Config { get; private set; }
|
public static BaseConfigurationHandler<StatsConfiguration> Config { get; private set; }
|
||||||
|
|
||||||
public async Task OnEventAsync(GameEvent E, Server S)
|
public async Task OnEventAsync(GameEvent E, Server S)
|
||||||
@ -51,7 +51,6 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
{
|
{
|
||||||
await Manager.AddMessageAsync(E.Origin.ClientId, await StatManager.GetIdForServer(E.Owner), 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(await StatManager.GetIdForServer(E.Owner), E.Owner.Gametype != "dm");
|
Manager.SetTeamBased(await StatManager.GetIdForServer(E.Owner), E.Owner.Gametype != "dm");
|
||||||
@ -79,6 +78,22 @@ 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)
|
||||||
{
|
{
|
||||||
|
if (E.Origin.ClientId <= 1 && E.Target.ClientId <= 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this treats "world" damage as self damage
|
||||||
|
if (E.Origin.ClientId <= 1)
|
||||||
|
{
|
||||||
|
E.Origin = E.Target;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (E.Target.ClientId <= 1)
|
||||||
|
{
|
||||||
|
E.Target = E.Origin;
|
||||||
|
}
|
||||||
|
|
||||||
await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, await StatManager.GetIdForServer(E.Owner), 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]);
|
||||||
}
|
}
|
||||||
@ -86,12 +101,44 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
case GameEvent.EventType.Kill:
|
case GameEvent.EventType.Kill:
|
||||||
if (!E.Owner.CustomCallback)
|
if (!E.Owner.CustomCallback)
|
||||||
{
|
{
|
||||||
|
if (E.Origin.ClientId <= 1 && E.Target.ClientId <= 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this treats "world" damage as self damage
|
||||||
|
if (E.Origin.ClientId <= 1)
|
||||||
|
{
|
||||||
|
E.Origin = E.Target;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (E.Target.ClientId <= 1)
|
||||||
|
{
|
||||||
|
E.Target = E.Origin;
|
||||||
|
}
|
||||||
|
|
||||||
await Manager.AddStandardKill(E.Origin, E.Target);
|
await Manager.AddStandardKill(E.Origin, E.Target);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GameEvent.EventType.Damage:
|
case GameEvent.EventType.Damage:
|
||||||
if (!E.Owner.CustomCallback)
|
if (!E.Owner.CustomCallback)
|
||||||
{
|
{
|
||||||
|
if (E.Origin.ClientId <= 1 && E.Target.ClientId <= 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this treats "world" damage as self damage
|
||||||
|
if (E.Origin.ClientId <= 1)
|
||||||
|
{
|
||||||
|
E.Origin = E.Target;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (E.Target.ClientId <= 1)
|
||||||
|
{
|
||||||
|
E.Target = E.Origin;
|
||||||
|
}
|
||||||
|
|
||||||
Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, await StatManager.GetIdForServer(E.Owner));
|
Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, await StatManager.GetIdForServer(E.Owner));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -99,6 +146,22 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
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)
|
||||||
{
|
{
|
||||||
|
if (E.Origin.ClientId <= 1 && E.Target.ClientId <= 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this treats "world" damage as self damage
|
||||||
|
if (E.Origin.ClientId <= 1)
|
||||||
|
{
|
||||||
|
E.Origin = E.Target;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (E.Target.ClientId <= 1)
|
||||||
|
{
|
||||||
|
E.Target = E.Origin;
|
||||||
|
}
|
||||||
|
|
||||||
await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, await StatManager.GetIdForServer(E.Owner), 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]);
|
||||||
}
|
}
|
||||||
@ -124,8 +187,13 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
"/Stats/TopPlayersAsync");
|
"/Stats/TopPlayersAsync");
|
||||||
|
|
||||||
// meta data info
|
// meta data info
|
||||||
async Task<List<ProfileMeta>> getStats(int clientId)
|
async Task<List<ProfileMeta>> getStats(int clientId, int offset, int count, DateTime? startAt)
|
||||||
{
|
{
|
||||||
|
if (count > 1)
|
||||||
|
{
|
||||||
|
return new List<ProfileMeta>();
|
||||||
|
}
|
||||||
|
|
||||||
IList<EFClientStatistics> clientStats;
|
IList<EFClientStatistics> clientStats;
|
||||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||||
{
|
{
|
||||||
@ -145,39 +213,63 @@ 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 StatManager.GetClientOverallRanking(clientId),
|
Value = "#" + (await StatManager.GetClientOverallRanking(clientId)).ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||||
|
Column = 0,
|
||||||
|
Order = 0,
|
||||||
|
Type = ProfileMeta.MetaType.Information
|
||||||
},
|
},
|
||||||
new ProfileMeta()
|
new ProfileMeta()
|
||||||
{
|
{
|
||||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KILLS"],
|
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KILLS"],
|
||||||
Value = kills
|
Value = kills.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||||
|
Column = 0,
|
||||||
|
Order = 1,
|
||||||
|
Type = ProfileMeta.MetaType.Information
|
||||||
},
|
},
|
||||||
new ProfileMeta()
|
new ProfileMeta()
|
||||||
{
|
{
|
||||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_DEATHS"],
|
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_DEATHS"],
|
||||||
Value = deaths
|
Value = deaths.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||||
|
Column = 0,
|
||||||
|
Order = 2,
|
||||||
|
Type = ProfileMeta.MetaType.Information
|
||||||
},
|
},
|
||||||
new ProfileMeta()
|
new ProfileMeta()
|
||||||
{
|
{
|
||||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KDR"],
|
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KDR"],
|
||||||
Value = kdr
|
Value = kdr.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||||
|
Column = 0,
|
||||||
|
Order = 3,
|
||||||
|
Type = ProfileMeta.MetaType.Information
|
||||||
},
|
},
|
||||||
new ProfileMeta()
|
new ProfileMeta()
|
||||||
{
|
{
|
||||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_PERFORMANCE"],
|
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_PERFORMANCE"],
|
||||||
Value = performance
|
Value = performance.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||||
|
Column = 0,
|
||||||
|
Order = 4,
|
||||||
|
Type = ProfileMeta.MetaType.Information
|
||||||
},
|
},
|
||||||
new ProfileMeta()
|
new ProfileMeta()
|
||||||
{
|
{
|
||||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_META_SPM"],
|
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_META_SPM"],
|
||||||
Value = spm
|
Value = spm.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||||
|
Column = 0,
|
||||||
|
Order = 5,
|
||||||
|
Type = ProfileMeta.MetaType.Information
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<List<ProfileMeta>> getAnticheatInfo(int clientId)
|
async Task<List<ProfileMeta>> getAnticheatInfo(int clientId, int offset, int count, DateTime? startAt)
|
||||||
{
|
{
|
||||||
|
if (count > 1)
|
||||||
|
{
|
||||||
|
return new List<ProfileMeta>();
|
||||||
|
}
|
||||||
|
|
||||||
IList<EFClientStatistics> clientStats;
|
IList<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>()
|
||||||
@ -219,104 +311,141 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
{
|
{
|
||||||
new ProfileMeta()
|
new ProfileMeta()
|
||||||
{
|
{
|
||||||
Key = "Chest Ratio",
|
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 1",
|
||||||
Value = chestRatio,
|
Value = chestRatio.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||||
|
Type = ProfileMeta.MetaType.Information,
|
||||||
|
Column = 2,
|
||||||
|
Order = 0,
|
||||||
Sensitive = true
|
Sensitive = true
|
||||||
},
|
},
|
||||||
new ProfileMeta()
|
new ProfileMeta()
|
||||||
{
|
{
|
||||||
Key = "Abdomen Ratio",
|
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 2",
|
||||||
Value = abdomenRatio,
|
Value = abdomenRatio.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||||
|
Type = ProfileMeta.MetaType.Information,
|
||||||
|
Column = 2,
|
||||||
|
Order = 1,
|
||||||
Sensitive = true
|
Sensitive = true
|
||||||
},
|
},
|
||||||
new ProfileMeta()
|
new ProfileMeta()
|
||||||
{
|
{
|
||||||
Key = "Chest To Abdomen Ratio",
|
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 3",
|
||||||
Value = chestAbdomenRatio,
|
Value = chestAbdomenRatio.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||||
|
Type = ProfileMeta.MetaType.Information,
|
||||||
|
Column = 2,
|
||||||
|
Order = 2,
|
||||||
Sensitive = true
|
Sensitive = true
|
||||||
},
|
},
|
||||||
new ProfileMeta()
|
new ProfileMeta()
|
||||||
{
|
{
|
||||||
Key = "Headshot Ratio",
|
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 4",
|
||||||
Value = headRatio,
|
Value = headRatio.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||||
|
Type = ProfileMeta.MetaType.Information,
|
||||||
|
Column = 2,
|
||||||
|
Order = 3,
|
||||||
Sensitive = true
|
Sensitive = true
|
||||||
},
|
},
|
||||||
new ProfileMeta()
|
new ProfileMeta()
|
||||||
{
|
{
|
||||||
Key = "Hit Offset Average",
|
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 5",
|
||||||
// todo: make sure this is wrapped somewhere else
|
// todo: make sure this is wrapped somewhere else
|
||||||
Value = $"{Math.Round(((float)hitOffsetAverage), 4).ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName))}°",
|
Value = $"{Math.Round(((float)hitOffsetAverage), 4).ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName))}°",
|
||||||
|
Type = ProfileMeta.MetaType.Information,
|
||||||
|
Column = 2,
|
||||||
|
Order = 4,
|
||||||
Sensitive = true
|
Sensitive = true
|
||||||
},
|
},
|
||||||
new ProfileMeta()
|
new ProfileMeta()
|
||||||
{
|
{
|
||||||
Key = "Max Strain",
|
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 6",
|
||||||
Value = Math.Round(maxStrain, 3),
|
Value = Math.Round(maxStrain, 3).ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||||
|
Type = ProfileMeta.MetaType.Information,
|
||||||
|
Column = 2,
|
||||||
|
Order = 5,
|
||||||
Sensitive = true
|
Sensitive = true
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<List<ProfileMeta>> getMessages(int clientId)
|
async Task<List<ProfileMeta>> getMessages(int clientId, int offset, int count, DateTime? startAt)
|
||||||
{
|
{
|
||||||
|
if (count <= 1)
|
||||||
|
{
|
||||||
|
using (var ctx = new DatabaseContext(true))
|
||||||
|
{
|
||||||
|
return new List<ProfileMeta>
|
||||||
|
{
|
||||||
|
new ProfileMeta()
|
||||||
|
{
|
||||||
|
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_MESSAGES"],
|
||||||
|
Value = (await ctx.Set<EFClientMessage>()
|
||||||
|
.CountAsync(_message => _message.ClientId == clientId))
|
||||||
|
.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||||
|
Column = 1,
|
||||||
|
Order= 4,
|
||||||
|
Type = ProfileMeta.MetaType.Information
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<ProfileMeta> messageMeta;
|
List<ProfileMeta> messageMeta;
|
||||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||||
{
|
{
|
||||||
var messages = ctx.Set<EFClientMessage>().Where(m => m.ClientId == clientId);
|
var messages = ctx.Set<EFClientMessage>()
|
||||||
|
.Where(m => m.ClientId == clientId)
|
||||||
|
.Where(_message => _message.TimeSent < startAt)
|
||||||
|
.OrderByDescending(_message => _message.TimeSent)
|
||||||
|
.Skip(offset)
|
||||||
|
.Take(count);
|
||||||
|
|
||||||
messageMeta = await messages.Select(m => new ProfileMeta()
|
messageMeta = await messages.Select(m => new ProfileMeta()
|
||||||
{
|
{
|
||||||
Key = "EventMessage",
|
Key = null,
|
||||||
Value = m.Message,
|
Value = m.Message,
|
||||||
When = m.TimeSent,
|
When = m.TimeSent,
|
||||||
Extra = m.ServerId.ToString()
|
Extra = m.ServerId.ToString(),
|
||||||
|
Type = ProfileMeta.MetaType.ChatMessage
|
||||||
}).ToListAsync();
|
}).ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
messageMeta.Add(new ProfileMeta()
|
|
||||||
{
|
|
||||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_MESSAGES"],
|
|
||||||
Value = messageMeta.Count
|
|
||||||
});
|
|
||||||
|
|
||||||
return messageMeta;
|
return messageMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
MetaService.AddMeta(getStats);
|
|
||||||
|
|
||||||
if (Config.Configuration().EnableAntiCheat)
|
if (Config.Configuration().EnableAntiCheat)
|
||||||
{
|
{
|
||||||
MetaService.AddMeta(getAnticheatInfo);
|
MetaService.AddRuntimeMeta(getAnticheatInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
MetaService.AddMeta(getMessages);
|
MetaService.AddRuntimeMeta(getStats);
|
||||||
|
MetaService.AddRuntimeMeta(getMessages);
|
||||||
|
|
||||||
string totalKills(Server server)
|
async Task<string> 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", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string totalPlayTime(Server server)
|
async Task<string> 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", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string topStats(Server s)
|
async Task<string> 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<string> 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));
|
||||||
@ -325,7 +454,6 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
manager.GetMessageTokens().Add(new MessageToken("MOSTPLAYED", mostPlayed));
|
manager.GetMessageTokens().Add(new MessageToken("MOSTPLAYED", mostPlayed));
|
||||||
|
|
||||||
ServerManager = manager;
|
ServerManager = manager;
|
||||||
|
|
||||||
Manager = new StatManager(manager);
|
Manager = new StatManager(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,12 +15,6 @@
|
|||||||
<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" />
|
||||||
@ -33,9 +27,4 @@
|
|||||||
<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>
|
||||||
|
28
Plugins/Stats/ViewComponents/TopPlayersViewComponent.cs
Normal file
28
Plugins/Stats/ViewComponents/TopPlayersViewComponent.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using IW4MAdmin.Plugins.Stats;
|
||||||
|
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Stats.ViewComponents
|
||||||
|
{
|
||||||
|
public class TopPlayersViewComponent : ViewComponent
|
||||||
|
{
|
||||||
|
public async Task<IViewComponentResult> InvokeAsync(int count, int offset, long? serverId = null)
|
||||||
|
{
|
||||||
|
if (serverId == 0)
|
||||||
|
{
|
||||||
|
serverId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var server = Plugin.ServerManager.GetServers().FirstOrDefault(_server => _server.EndPoint == serverId);
|
||||||
|
|
||||||
|
if (server != null)
|
||||||
|
{
|
||||||
|
serverId = await StatManager.GetIdForServer(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
return View("_List", await Plugin.Manager.GetTopStats(offset, count, serverId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +0,0 @@
|
|||||||
@model List<dynamic>
|
|
||||||
<h4 class="pb-2 text-center ">@ViewBag.Title</h4>
|
|
||||||
|
|
||||||
<div id="stats_top_players" class="striped border-top border-bottom">
|
|
||||||
@await Html.PartialAsync("_List", Model)
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@section scripts {
|
|
||||||
<environment include="Development">
|
|
||||||
<script type="text/javascript" src="~/js/loader.js"></script>
|
|
||||||
<script type="text/javascript" src="~/js/stats.js"></script>
|
|
||||||
</environment>
|
|
||||||
<script>initLoader('/Stats/GetTopPlayersAsync', '#stats_top_players', 50);</script>
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
@model IEnumerable<SharedLibraryCore.Dtos.ChatInfo>
|
|
||||||
@{
|
|
||||||
Layout = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="client-message-context bg-dark p-2 mt-2 mb-2 border-top border-bottom">
|
|
||||||
<h5>@Model.First().Time.ToString()</h5>
|
|
||||||
@foreach (var message in Model)
|
|
||||||
{
|
|
||||||
<span class="text-white">@Html.ActionLink(@message.Name, "ProfileAsync", "Client", new { id = message.ClientId})</span><span> — @message.Message</span><br />
|
|
||||||
}
|
|
||||||
</div>
|
|
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.
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.
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.
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.
@ -56,10 +56,10 @@ namespace Tests
|
|||||||
var warnEvent = client.Warn("test warn", new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
var warnEvent = client.Warn("test warn", new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||||
warnEvent.OnProcessed.Wait();
|
warnEvent.OnProcessed.Wait();
|
||||||
|
|
||||||
Assert.True((client.Warnings == 1 ||
|
//Assert.True((client.Warnings == 1 ||
|
||||||
warnEvent.Failed) &&
|
// warnEvent.Failed) &&
|
||||||
Manager.GetPenaltyService().GetClientPenaltiesAsync(client.ClientId).Result.Count(p => p.Type == Penalty.PenaltyType.Warning) == 1,
|
// Manager.GetPenaltyService().GetClientPenaltiesAsync(client.ClientId).Result.Count(p => p.Type == Penalty.PenaltyType.Warning) == 1,
|
||||||
"warning did not get applied");
|
// "warning did not get applied");
|
||||||
|
|
||||||
warnEvent = client.Warn("test warn", new EFClient() { ClientId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer });
|
warnEvent = client.Warn("test warn", new EFClient() { ClientId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||||
warnEvent.OnProcessed.Wait();
|
warnEvent.OnProcessed.Wait();
|
||||||
|
@ -18,7 +18,7 @@ namespace Tests
|
|||||||
{
|
{
|
||||||
File.WriteAllText("test_mp.log", "test_log_file");
|
File.WriteAllText("test_mp.log", "test_log_file");
|
||||||
|
|
||||||
IW4MAdmin.Application.Localization.Configure.Initialize("en-US");
|
//IW4MAdmin.Application.Localization.Configure.Initialize("en-US");
|
||||||
|
|
||||||
Manager = ApplicationManager.GetInstance();
|
Manager = ApplicationManager.GetInstance();
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ namespace Tests
|
|||||||
Maps = new List<MapConfiguration>(),
|
Maps = new List<MapConfiguration>(),
|
||||||
RConPollRate = 10000
|
RConPollRate = 10000
|
||||||
};
|
};
|
||||||
Manager.ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("test.json");
|
Manager.ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("test");
|
||||||
Manager.ConfigHandler.Set(config);
|
Manager.ConfigHandler.Set(config);
|
||||||
|
|
||||||
Manager.Init().Wait();
|
Manager.Init().Wait();
|
||||||
|
@ -34,7 +34,7 @@ namespace Tests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void AreCommandAliasesUnique()
|
public void AreCommandAliasesUnique()
|
||||||
{
|
{
|
||||||
var mgr = IW4MAdmin.Application.Program.ServerManager;
|
var mgr = Program.ServerManager;
|
||||||
bool test = mgr.GetCommands().Count == mgr.GetCommands().Select(c => c.Alias).Distinct().Count();
|
bool test = mgr.GetCommands().Count == mgr.GetCommands().Select(c => c.Alias).Distinct().Count();
|
||||||
|
|
||||||
Assert.True(test, "command aliases are not unique");
|
Assert.True(test, "command aliases are not unique");
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user