Compare commits

..

38 Commits

Author SHA1 Message Date
c8ec0eefa9 game log reader reads async now.
should have done that a long time ago
update profile page to have a bit better space usage
2019-03-09 10:28:04 -06:00
82bae772f0 most played command now ordered by play time
issue #68
2019-03-02 17:29:09 -06:00
af3aea77bc finish UI tweaks for issue #39 2019-03-02 14:29:09 -06:00
b3e5f468a1 finish implementation of per server top stats page 2019-02-27 20:13:15 -06:00
c21bf2ebf1 continue working on per servver topstats 2019-02-26 21:25:27 -06:00
d318a57830 start implementation for per server top stats
issue #39
2019-02-25 19:36:10 -06:00
61f1436faf prompt user to continue if not all servers can be connected
issue #58
2019-02-24 19:35:59 -06:00
0c527a5f65 Add lock menu icon for tempbanned players
issue #62
2019-02-24 19:07:56 -06:00
1457b843e2 one more small fix for meta service after testing live 2019-02-22 19:37:53 -06:00
de69bed792 small migration fix for MySql 2019-02-22 19:35:03 -06:00
4b1f44cc2a re-enable login to webfront with password
update cookie to last 3 months
add configuration option to limit # of rss feed items
prevent database tracking of bots if ignore bots requested
add last map and last server played to profile
2019-02-22 19:06:51 -06:00
74cdf8e885 Fix bug introduced with auto messages 2019-02-19 19:39:09 -06:00
5d41059641 accidentally copied a file to the wrong project 2019-02-18 19:48:28 -06:00
2e6889d9bb implement RSS feed in auto messages for issue #53
modified automessages to use async mesthods instead of synchronous
2019-02-18 19:30:38 -06:00
9c4d23f0b4 enhancement for issue #63 2019-02-17 18:48:40 -06:00
b4e3e8526a fix for issue #66 2019-02-17 13:16:48 -06:00
e3944fb8c2 add web project for stats to fix bug with pre compiled razor templates 2019-02-16 17:18:50 -06:00
40f1697c97 fix damage event not including log line
complete initiall implementation for "2FA"
issue #52
issue #66
2019-02-16 15:04:40 -06:00
2bbf2988da update application version 2019-02-15 22:20:45 -06:00
5e36bf4316 begin implementation of token authentication
replacing password authentication ingame
precompile views for webfront
issue #66
issue #52
2019-02-15 22:19:59 -06:00
a362caebac fix for T6 guid length including sign 2019-02-14 08:42:14 -06:00
2260d8974d update master to allow IW5 to pass validation
include version set on manual parser selection
update projects to .NET Core 2.2
add middleware to support ip whitelisting
(EnableWebfrontConnectionWhitelist and WebfrontConnectionWhitelist)
issue #59
2019-02-12 20:34:29 -06:00
5e04274da6 actually fix the encoding issue 2019-02-10 20:05:50 -06:00
6d2e6aee4f update application version 2019-02-09 21:26:39 -06:00
9e74c42246 fix small bug with log paths 2019-02-09 21:24:54 -06:00
dea5b3f954 fix reading PT6 having signed decimal GUID in log
fix  alternative encoding character converting
allow more paths for game log server
add localization for unknown ips in welcome plugin
add gamelog server uri to support game log server on games that must supply manual log path
misc fixes
2019-02-09 15:35:13 -06:00
7c6419a16a finish cod4x parser
add IW3 map names
2019-02-06 20:12:35 -06:00
0194196a33 update application version 2019-02-05 18:06:14 -06:00
044991272f update parsers to include game name
prompt to enter log path if game doesn't generate
2019-02-05 18:02:45 -06:00
f3290cf066 move IW4x parser out of code
add CoD4x parser
2019-02-05 11:14:43 -06:00
29eedea093 fix IW4x regression error with alternative encodings
add parser selection to server config setup
2019-02-04 19:38:24 -06:00
ce02f5dd68 Move T6 parser to javascript parser 2019-02-03 20:47:05 -06:00
e6bfa408f8 move IW3 parser to javascript 2019-02-02 20:19:24 -06:00
0a1dc46760 Add commenting for parsers
rename IW4*Parser to Base*Parser
2019-02-02 19:40:37 -06:00
97ba6aae2e put parser in right location :P 2019-02-02 19:13:17 -06:00
a456fab0e5 Increment version #
Add TeknoMW3 parser file
2019-02-02 19:11:34 -06:00
3e5282df87 Finish preliminary parser for TeknoMW3 2019-02-02 18:54:30 -06:00
59e0072744 Finish dynamic dvar parsing for IW4x 2019-02-01 19:49:25 -06:00
228 changed files with 2741 additions and 891 deletions

2
.gitignore vendored
View File

@ -223,7 +223,7 @@ global.min.js
bootstrap-custom.min.css
**/Master/static
**/Master/dev_env
/WebfrontCore/Views/Plugins/Stats
/WebfrontCore/Views/Plugins/*
/WebfrontCore/wwwroot/**/dds
/DiscordWebhook/env

View File

@ -2,15 +2,15 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
<TargetFramework>netcoreapp2.2</TargetFramework>
<RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion>
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
<PackageId>RaidMax.IW4MAdmin.Application</PackageId>
<Version>2.2.4.1</Version>
<Version>2.2.5.7</Version>
<Authors>RaidMax</Authors>
<Company>Forever None</Company>
<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>
<PackageLicenseUrl>https://github.com/RaidMax/IW4M-Admin/blob/master/LICENSE</PackageLicenseUrl>
<PackageProjectUrl>https://raidmax.org/IW4MAdmin</PackageProjectUrl>
@ -25,14 +25,14 @@
<ItemGroup>
<PackageReference Include="RestEase" Version="1.4.7" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />
</ItemGroup>
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
<TieredCompilation>true</TieredCompilation>
<AssemblyVersion>2.2.4.1</AssemblyVersion>
<FileVersion>2.2.4.1</FileVersion>
<AssemblyVersion>2.2.5.7</AssemblyVersion>
<FileVersion>2.2.5.6</FileVersion>
</PropertyGroup>
<ItemGroup>
@ -45,21 +45,6 @@
</ProjectReference>
</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>
<None Update="DefaultSettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
@ -79,7 +64,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
<PackageReference Update="Microsoft.NETCore.App" Version="2.2.2" />
</ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">

View File

@ -1,11 +1,13 @@
using IW4MAdmin.Application.API.Master;
using IW4MAdmin.Application.EventParsers;
using IW4MAdmin.Application.Misc;
using IW4MAdmin.Application.RconParsers;
using SharedLibraryCore;
using SharedLibraryCore.Commands;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Dtos;
using SharedLibraryCore.Events;
using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Helpers;
@ -36,12 +38,12 @@ namespace IW4MAdmin.Application
public DateTime StartTime { get; private set; }
public string Version => Assembly.GetEntryAssembly().GetName().Version.ToString();
public IList<IRConParser> AdditionalRConParsers => _additionalRConParsers.ToList<IRConParser>();
public IList<IRConParser> AdditionalRConParsers { get; }
public IList<IEventParser> AdditionalEventParsers { get; }
public IList<IEventParser> AdditionalEventParsers => _additionalEventParsers.ToList<IEventParser>();
public ITokenAuthentication TokenAuthenticator => Authenticator;
private readonly IList<DynamicRConParser> _additionalRConParsers;
private readonly IList<DynamicEventParser> _additionalEventParsers;
public ITokenAuthentication Authenticator => _authenticator;
static ApplicationManager Instance;
readonly List<AsyncStatus> TaskStatuses;
@ -56,6 +58,8 @@ namespace IW4MAdmin.Application
readonly IPageList PageList;
readonly SemaphoreSlim ProcessingEvent = new SemaphoreSlim(1, 1);
readonly Dictionary<long, ILogger> Loggers = new Dictionary<long, ILogger>();
readonly ITokenAuthentication _authenticator;
private readonly MetaService _metaService;
private ApplicationManager()
{
@ -70,10 +74,12 @@ namespace IW4MAdmin.Application
StartTime = DateTime.UtcNow;
OnQuit = new ManualResetEventSlim();
PageList = new PageList();
_additionalEventParsers = new List<DynamicEventParser>();
_additionalRConParsers = new List<DynamicRConParser>();
AdditionalEventParsers = new List<IEventParser>();
AdditionalRConParsers = new List<IRConParser>();
OnServerEvent += OnGameEvent;
OnServerEvent += EventApi.OnGameEvent;
_authenticator = new TokenAuthentication();
_metaService = new MetaService();
}
private async void OnGameEvent(object sender, GameEventArgs args)
@ -214,6 +220,25 @@ namespace IW4MAdmin.Application
{
Running = true;
#region PLUGINS
SharedLibraryCore.Plugins.PluginImporter.Load(this);
foreach (var Plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
{
try
{
await Plugin.OnLoadAsync(this);
}
catch (Exception ex)
{
Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_PLUGIN"]} {Plugin.Name}");
Logger.WriteDebug(ex.GetExceptionInfo());
}
}
#endregion
#region CONFIG
var config = ConfigHandler.Configuration();
@ -236,7 +261,18 @@ namespace IW4MAdmin.Application
do
{
newConfig.Servers.Add((ServerConfiguration)new ServerConfiguration().Generate());
var serverConfig = new ServerConfiguration();
foreach (var parser in AdditionalRConParsers)
{
serverConfig.AddRConParser(parser);
}
foreach (var parser in AdditionalEventParsers)
{
serverConfig.AddEventParser(parser);
}
newConfig.Servers.Add((ServerConfiguration)serverConfig.Generate());
} while (Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationIndex["SETUP_SERVER_SAVE"]));
config = newConfig;
@ -257,6 +293,27 @@ namespace IW4MAdmin.Application
config.WebfrontBindUrl = "http://0.0.0.0:1624";
await ConfigHandler.Save();
}
foreach (var serverConfig in config.Servers)
{
Migration.ConfigurationMigration.ModifyLogPath020919(serverConfig);
if (serverConfig.RConParserVersion == null || serverConfig.EventParserVersion == null)
{
foreach (var parser in AdditionalRConParsers)
{
serverConfig.AddRConParser(parser);
}
foreach (var parser in AdditionalEventParsers)
{
serverConfig.AddEventParser(parser);
}
serverConfig.ModifyParsers();
}
await ConfigHandler.Save();
}
}
else if (config.Servers.Count == 0)
@ -279,24 +336,6 @@ namespace IW4MAdmin.Application
PrivilegedClients = (await ClientSvc.GetPrivilegedClients()).ToDictionary(_client => _client.ClientId);
#endregion
#region PLUGINS
SharedLibraryCore.Plugins.PluginImporter.Load(this);
foreach (var Plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
{
try
{
await Plugin.OnLoadAsync(this);
}
catch (Exception ex)
{
Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_PLUGIN"]} {Plugin.Name}");
Logger.WriteDebug(ex.GetExceptionInfo());
}
}
#endregion
#region COMMANDS
if (ClientSvc.GetOwners().Result.Count == 0)
{
@ -340,6 +379,7 @@ namespace IW4MAdmin.Application
Commands.Add(new CPing());
Commands.Add(new CSetGravatar());
Commands.Add(new CNextMap());
Commands.Add(new RequestTokenCommand());
foreach (Command C in SharedLibraryCore.Plugins.PluginImporter.ActiveCommands)
{
@ -347,11 +387,56 @@ namespace IW4MAdmin.Application
}
#endregion
#region META
async Task<List<ProfileMeta>> getLastMap(int clientId)
{
var meta = await _metaService.GetPersistentMeta("LastMapPlayed", new EFClient() { ClientId = clientId });
return meta == null ? new List<ProfileMeta>() : new List<ProfileMeta>()
{
new ProfileMeta()
{
Id = meta.MetaId,
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_LAST_MAP"],
Value = meta.Value,
Show = true
}
};
}
async Task<List<ProfileMeta>> getLastServer(int clientId)
{
var meta = await _metaService.GetPersistentMeta("LastServerPlayed", new EFClient() { ClientId = clientId });
return meta == null ? new List<ProfileMeta>() : new List<ProfileMeta>()
{
new ProfileMeta()
{
Id = meta.MetaId,
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_LAST_SERVER"],
Value = meta.Value,
Show = true
}
};
}
MetaService.AddRuntimeMeta(getLastMap);
MetaService.AddRuntimeMeta(getLastServer);
#endregion
#region INIT
int failedServers = 0;
int successServers = 0;
Exception lastException = null;
async Task Init(ServerConfiguration Conf)
{
// setup the event handler after the class is initialized
Handler = new GameEventHandler(this);
try
{
var ServerInstance = new IW4MServer(this, Conf);
@ -373,26 +458,37 @@ namespace IW4MAdmin.Application
};
Handler.AddEvent(e);
successServers++;
}
catch (ServerException e)
{
Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_UNFIXABLE"]} [{Conf.IPAddress}:{Conf.Port}]");
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"]})");
}
else if (e.GetType() == typeof(NetworkException))
{
Logger.WriteDebug(e.Message);
}
// throw the exception to the main method to stop before instantly exiting
throw e;
failedServers++;
lastException = e;
}
}
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
}
@ -557,7 +653,7 @@ namespace IW4MAdmin.Application
public IList<Assembly> GetPluginAssemblies()
{
return SharedLibraryCore.Plugins.PluginImporter.PluginAssemblies;
return SharedLibraryCore.Plugins.PluginImporter.PluginAssemblies.Union(SharedLibraryCore.Plugins.PluginImporter.Assemblies).ToList();
}
public IPageList GetPageList()

View File

@ -242,12 +242,12 @@
{
"Name": "Village",
"Alias": "co_hunted"
"Alias": "co_hunted"
}
]
},
{
"Game": "T6M",
"Game": "T6",
"Maps": [
{
"Alias": "Aftermath",
@ -402,6 +402,95 @@
"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"
}
]
}
]
}

View File

@ -4,70 +4,75 @@ using SharedLibraryCore.Interfaces;
using System;
using System.Linq;
using System.Text.RegularExpressions;
using static SharedLibraryCore.Server;
namespace IW4MAdmin.Application.EventParsers
{
class IW4EventParser : IEventParser
class BaseEventParser : IEventParser
{
public IW4EventParser()
public BaseEventParser()
{
Configuration = new DynamicEventParserConfiguration()
{
GameDirectory = "userraw",
GameDirectory = "main",
};
Configuration.Say.Pattern = @"^(say|sayteam);(.{1,32});([0-9]+)(.*);(.*)$";
Configuration.Say.GroupMapping.Add(ParserRegex.GroupType.EventType, 1);
Configuration.Say.GroupMapping.Add(ParserRegex.GroupType.OriginNetworkId, 2);
Configuration.Say.GroupMapping.Add(ParserRegex.GroupType.OriginClientNumber, 3);
Configuration.Say.GroupMapping.Add(ParserRegex.GroupType.OriginName, 4);
Configuration.Say.GroupMapping.Add(ParserRegex.GroupType.Message, 5);
Configuration.Say.Pattern = @"^(say|sayteam);(-?[A-Fa-f0-9_]{8,32});([0-9]+);(.+);(.*)$";
Configuration.Say.AddMapping(ParserRegex.GroupType.EventType, 1);
Configuration.Say.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2);
Configuration.Say.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
Configuration.Say.AddMapping(ParserRegex.GroupType.OriginName, 4);
Configuration.Say.AddMapping(ParserRegex.GroupType.Message, 5);
Configuration.Quit.Pattern = @"^(Q);(.{16,32}|bot[0-9]+);([0-9]+);(.*)$";
Configuration.Quit.GroupMapping.Add(ParserRegex.GroupType.EventType, 1);
Configuration.Quit.GroupMapping.Add(ParserRegex.GroupType.OriginNetworkId, 2);
Configuration.Quit.GroupMapping.Add(ParserRegex.GroupType.OriginClientNumber, 3);
Configuration.Quit.GroupMapping.Add(ParserRegex.GroupType.OriginName, 4);
Configuration.Quit.Pattern = @"^(Q);(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+);([0-9]+);(.*)$";
Configuration.Quit.AddMapping(ParserRegex.GroupType.EventType, 1);
Configuration.Quit.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2);
Configuration.Quit.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
Configuration.Quit.AddMapping(ParserRegex.GroupType.OriginName, 4);
Configuration.Join.Pattern = @"^(J);(.{16,32}|bot[0-9]+);([0-9]+);(.*)$";
Configuration.Join.GroupMapping.Add(ParserRegex.GroupType.EventType, 1);
Configuration.Join.GroupMapping.Add(ParserRegex.GroupType.OriginNetworkId, 2);
Configuration.Join.GroupMapping.Add(ParserRegex.GroupType.OriginClientNumber, 3);
Configuration.Join.GroupMapping.Add(ParserRegex.GroupType.OriginName, 4);
Configuration.Join.Pattern = @"^(J);(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+);([0-9]+);(.*)$";
Configuration.Join.AddMapping(ParserRegex.GroupType.EventType, 1);
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2);
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginName, 4);
Configuration.Damage.Pattern = @"^(D);([A-Fa-f0-9_]{16,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world);(.{1,24});([A-Fa-f0-9_]{16,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world);(.{1,24})?;((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
Configuration.Damage.GroupMapping.Add(ParserRegex.GroupType.EventType, 1);
Configuration.Damage.GroupMapping.Add(ParserRegex.GroupType.TargetNetworkId, 2);
Configuration.Damage.GroupMapping.Add(ParserRegex.GroupType.TargetClientNumber, 3);
Configuration.Damage.GroupMapping.Add(ParserRegex.GroupType.TargetTeam, 4);
Configuration.Damage.GroupMapping.Add(ParserRegex.GroupType.TargetName, 5);
Configuration.Damage.GroupMapping.Add(ParserRegex.GroupType.OriginNetworkId, 6);
Configuration.Damage.GroupMapping.Add(ParserRegex.GroupType.OriginClientNumber, 7);
Configuration.Damage.GroupMapping.Add(ParserRegex.GroupType.OriginTeam, 8);
Configuration.Damage.GroupMapping.Add(ParserRegex.GroupType.OriginName, 9);
Configuration.Damage.GroupMapping.Add(ParserRegex.GroupType.Weapon, 10);
Configuration.Damage.GroupMapping.Add(ParserRegex.GroupType.Damage, 11);
Configuration.Damage.GroupMapping.Add(ParserRegex.GroupType.MeansOfDeath, 12);
Configuration.Damage.GroupMapping.Add(ParserRegex.GroupType.HitLocation, 13);
Configuration.Damage.Pattern = @"^(D);(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world);(.{1,24});(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world);(.{1,24})?;((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
Configuration.Damage.AddMapping(ParserRegex.GroupType.EventType, 1);
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2);
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3);
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetTeam, 4);
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetName, 5);
Configuration.Damage.AddMapping(ParserRegex.GroupType.OriginNetworkId, 6);
Configuration.Damage.AddMapping(ParserRegex.GroupType.OriginClientNumber, 7);
Configuration.Damage.AddMapping(ParserRegex.GroupType.OriginTeam, 8);
Configuration.Damage.AddMapping(ParserRegex.GroupType.OriginName, 9);
Configuration.Damage.AddMapping(ParserRegex.GroupType.Weapon, 10);
Configuration.Damage.AddMapping(ParserRegex.GroupType.Damage, 11);
Configuration.Damage.AddMapping(ParserRegex.GroupType.MeansOfDeath, 12);
Configuration.Damage.AddMapping(ParserRegex.GroupType.HitLocation, 13);
Configuration.Kill.Pattern = @"^(K);([A-Fa-f0-9_]{16,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world);(.{1,24});([A-Fa-f0-9_]{16,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world);(.{1,24})?;((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
Configuration.Kill.GroupMapping.Add(ParserRegex.GroupType.EventType, 1);
Configuration.Kill.GroupMapping.Add(ParserRegex.GroupType.TargetNetworkId, 2);
Configuration.Kill.GroupMapping.Add(ParserRegex.GroupType.TargetClientNumber, 3);
Configuration.Kill.GroupMapping.Add(ParserRegex.GroupType.TargetTeam, 4);
Configuration.Kill.GroupMapping.Add(ParserRegex.GroupType.TargetName, 5);
Configuration.Kill.GroupMapping.Add(ParserRegex.GroupType.OriginNetworkId, 6);
Configuration.Kill.GroupMapping.Add(ParserRegex.GroupType.OriginClientNumber, 7);
Configuration.Kill.GroupMapping.Add(ParserRegex.GroupType.OriginTeam, 8);
Configuration.Kill.GroupMapping.Add(ParserRegex.GroupType.OriginName, 9);
Configuration.Kill.GroupMapping.Add(ParserRegex.GroupType.Weapon, 10);
Configuration.Kill.GroupMapping.Add(ParserRegex.GroupType.Damage, 11);
Configuration.Kill.GroupMapping.Add(ParserRegex.GroupType.MeansOfDeath, 12);
Configuration.Kill.GroupMapping.Add(ParserRegex.GroupType.HitLocation, 13);
Configuration.Kill.Pattern = @"^(K);(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world);(.{1,24});(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world);(.{1,24})?;((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
Configuration.Kill.AddMapping(ParserRegex.GroupType.EventType, 1);
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2);
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3);
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetTeam, 4);
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetName, 5);
Configuration.Kill.AddMapping(ParserRegex.GroupType.OriginNetworkId, 6);
Configuration.Kill.AddMapping(ParserRegex.GroupType.OriginClientNumber, 7);
Configuration.Kill.AddMapping(ParserRegex.GroupType.OriginTeam, 8);
Configuration.Kill.AddMapping(ParserRegex.GroupType.OriginName, 9);
Configuration.Kill.AddMapping(ParserRegex.GroupType.Weapon, 10);
Configuration.Kill.AddMapping(ParserRegex.GroupType.Damage, 11);
Configuration.Kill.AddMapping(ParserRegex.GroupType.MeansOfDeath, 12);
Configuration.Kill.AddMapping(ParserRegex.GroupType.HitLocation, 13);
}
public IEventParserConfiguration Configuration { get; set; }
public string Version { get; set; } = "CoD";
public Game GameName { get; set; } = Game.COD;
public virtual GameEvent GetEvent(Server server, string logLine)
{
logLine = Regex.Replace(logLine, @"([0-9]+:[0-9]+ |^[0-9]+ )", "").Trim();
@ -82,7 +87,7 @@ namespace IW4MAdmin.Application.EventParsers
return new GameEvent()
{
Type = GameEvent.EventType.JoinTeam,
Data = eventType,
Data = logLine,
Origin = origin,
Owner = server
};
@ -198,7 +203,7 @@ namespace IW4MAdmin.Application.EventParsers
return new GameEvent()
{
Type = GameEvent.EventType.Damage,
Data = eventType,
Data = logLine,
Origin = origin,
Target = target,
Owner = server
@ -213,6 +218,8 @@ namespace IW4MAdmin.Application.EventParsers
var regexMatch = Regex.Match(logLine, Configuration.Join.Pattern);
if (regexMatch.Success)
{
bool isBot = regexMatch.Groups[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().Contains("bot");
return new GameEvent()
{
Type = GameEvent.EventType.PreConnect,
@ -228,7 +235,8 @@ namespace IW4MAdmin.Application.EventParsers
NetworkId = regexMatch.Groups[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertLong(),
ClientNumber = Convert.ToInt32(regexMatch.Groups[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginClientNumber]].ToString()),
State = EFClient.ClientState.Connecting,
CurrentServer = server
CurrentServer = server,
IsBot = isBot
}
};
}

View File

@ -5,8 +5,11 @@ using static SharedLibraryCore.Server;
namespace IW4MAdmin.Application.EventParsers
{
sealed internal class DynamicEventParser : IW4EventParser
/// <summary>
/// empty generic implementation of the IEventParserConfiguration
/// allows script plugins to generate dynamic event parsers
/// </summary>
sealed internal class DynamicEventParser : BaseEventParser
{
public string Version { get; set; }
}
}

View File

@ -2,10 +2,13 @@
namespace IW4MAdmin.Application.EventParsers
{
class DynamicEventParserConfiguration : IEventParserConfiguration
/// <summary>
/// generic implementation of the IEventParserConfiguration
/// allows script plugins to generate dynamic configurations
/// </summary>
sealed internal class DynamicEventParserConfiguration : IEventParserConfiguration
{
public string GameDirectory { get; set; }
public ParserRegex Say { get; set; } = new ParserRegex();
public ParserRegex Join { get; set; } = new ParserRegex();
public ParserRegex Quit { get; set; } = new ParserRegex();

View File

@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace IW4MAdmin.Application.EventParsers
{
class IW3EventParser : IW4EventParser
{
public IW3EventParser() : base()
{
Configuration.GameDirectory = "main";
}
}
}

View File

@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace IW4MAdmin.Application.EventParsers
{
class T5MEventParser : IW4EventParser
{
public T5MEventParser() : base()
{
Configuration.GameDirectory = "v2";
}
}
}

View File

@ -1,12 +0,0 @@
using System.IO;
namespace IW4MAdmin.Application.EventParsers
{
class T6MEventParser : IW4EventParser
{
public T6MEventParser() : base()
{
Configuration.GameDirectory = $"t6r{Path.DirectorySeparatorChar}data";
}
}
}

View File

@ -1,8 +1,6 @@
using SharedLibraryCore;
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@ -21,19 +19,10 @@ namespace IW4MAdmin.Application.IO
public string ServerId { get; set; }
}
public GameLogEventDetection(Server server, string gameLogPath, string gameLogName)
public GameLogEventDetection(Server server, string gameLogPath, Uri gameLogServerUri)
{
GameLogFile = gameLogPath;
// todo: abtract this more
if (gameLogPath.StartsWith("http"))
{
Reader = new GameLogReaderHttp(gameLogPath, server.EventParser);
}
else
{
Reader = new GameLogReader(gameLogPath, server.EventParser);
}
Reader = gameLogServerUri != null ? new GameLogReaderHttp(gameLogServerUri, gameLogPath, server.EventParser) : Reader = new GameLogReader(gameLogPath, server.EventParser);
Server = server;
}

View File

@ -34,9 +34,9 @@ namespace IW4MAdmin.Application.IO
// todo: max async
// take the old start position and go back the number of new characters
rd.BaseStream.Seek(-fileSizeDiff, SeekOrigin.End);
// the difference should be in the range of a int :P
string newLine;
while (!String.IsNullOrEmpty(newLine = rd.ReadLine()))
while (!string.IsNullOrEmpty(newLine = await rd.ReadLineAsync()))
{
logLines.Add(newLine);
}

View File

@ -17,13 +17,13 @@ namespace IW4MAdmin.Application.IO
{
readonly IEventParser Parser;
readonly IGameLogServer Api;
readonly string LogFile;
readonly string logPath;
public GameLogReaderHttp(string logFile, IEventParser parser)
public GameLogReaderHttp(Uri gameLogServerUri, string logPath, IEventParser parser)
{
LogFile = logFile;
this.logPath = logPath.ToBase64UrlSafeString(); ;
Parser = parser;
Api = RestClient.For<IGameLogServer>(logFile);
Api = RestClient.For<IGameLogServer>(gameLogServerUri);
}
public long Length => -1;
@ -36,12 +36,12 @@ namespace IW4MAdmin.Application.IO
server.Logger.WriteDebug($"Begin reading from http log");
#endif
var events = new List<GameEvent>();
string b64Path = server.LogPath.ToBase64UrlSafeString();
string b64Path = logPath;
var response = await Api.Log(b64Path);
if (!response.Success)
{
server.Logger.WriteError($"Could not get log server info of {LogFile}/{b64Path} ({server.LogPath})");
server.Logger.WriteError($"Could not get log server info of {logPath}/{b64Path} ({server.LogPath})");
return events;
}

View File

@ -9,6 +9,7 @@ using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Localization;
using SharedLibraryCore.Objects;
using SharedLibraryCore.Services;
using System;
using System.Collections.Generic;
using System.IO;
@ -97,20 +98,27 @@ namespace IW4MAdmin
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
Logger.WriteDebug($"End PreDisconnect for {client}");
#endif
var e = new GameEvent()
if (client.ClientNumber >= 0)
{
Origin = client,
Owner = this,
Type = GameEvent.EventType.Disconnect
};
#endif
Logger.WriteInfo($"Client {client} [{client.State.ToString().ToLower()}] disconnecting...");
await client.OnDisconnect();
Clients[client.ClientNumber] = null;
#if DEBUG == true
Logger.WriteDebug($"End PreDisconnect for {client}");
#endif
var e = new GameEvent()
{
Origin = client,
Owner = this,
Type = GameEvent.EventType.Disconnect
};
Manager.GetEventHandler().AddEvent(e);
Manager.GetEventHandler().AddEvent(e);
#if DEBUG == true
}
#endif
}
public override async Task ExecuteEvent(GameEvent E)
@ -192,6 +200,12 @@ namespace IW4MAdmin
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;
}
if (Clients[E.Origin.ClientNumber] == null)
{
#if DEBUG == true
@ -299,6 +313,12 @@ namespace IW4MAdmin
}
}
else if (E.Type == GameEvent.EventType.Disconnect)
{
await new MetaService().AddPersistentMeta("LastMapPlayed", CurrentMap.Alias, E.Origin);
await new MetaService().AddPersistentMeta("LastServerPlayed", E.Owner.Hostname, E.Origin);
}
else if (E.Type == GameEvent.EventType.PreDisconnect)
{
if ((DateTime.UtcNow - SessionStart).TotalSeconds < 30)
@ -389,6 +409,7 @@ namespace IW4MAdmin
var dict = (Dictionary<string, string>)E.Extra;
Gametype = dict["g_gametype"].StripColors();
Hostname = dict["sv_hostname"].StripColors();
MaxClients = Int32.Parse(dict["sv_maxclients"]);
string mapname = dict["mapname"].StripColors();
CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map()
@ -629,7 +650,7 @@ namespace IW4MAdmin
&& BroadcastMessages.Count > 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)
{
@ -665,44 +686,34 @@ namespace IW4MAdmin
public async Task Initialize()
{
RconParser = ServerConfig.UseT6MParser ?
(IRConParser)new T6MRConParser() :
new IW3RConParser();
RconParser = Manager.AdditionalRConParsers
.FirstOrDefault(_parser => _parser.Version == ServerConfig.RConParserVersion);
EventParser = Manager.AdditionalEventParsers
.FirstOrDefault(_parser => _parser.Version == ServerConfig.EventParserVersion);
RconParser = RconParser ?? new BaseRConParser();
EventParser = EventParser ?? new BaseEventParser();
RemoteConnection.SetConfiguration(RconParser.Configuration);
var version = await this.GetDvarAsync<string>("version");
Version = version.Value;
GameName = Utilities.GetGame(version.Value);
if (GameName == Game.IW4)
{
EventParser = new IW4EventParser();
RconParser = new IW4RConParser();
}
else if (GameName == Game.T5M)
{
EventParser = new T5MEventParser();
}
else if (GameName == Game.T6M)
{
EventParser = new T6MEventParser();
}
else
{
EventParser = new IW3EventParser(); // this uses the 'main' folder for log paths
}
GameName = Utilities.GetGame(version?.Value ?? RconParser.Version);
if (GameName == Game.UKN)
{
Logger.WriteWarning($"Game name not recognized: {version}");
EventParser = Manager.AdditionalEventParsers.FirstOrDefault(_parser => (_parser as DynamicEventParser).Version == version.Value) ?? EventParser;
RconParser = Manager.AdditionalRConParsers.FirstOrDefault(_parser => (_parser as DynamicRConParser).Version == version.Value) ?? RconParser;
GameName = RconParser.GameName;
}
var infoResponse = await this.GetInfoAsync();
if (version?.Value?.Length != 0)
{
RconParser = Manager.AdditionalRConParsers.FirstOrDefault(_parser => _parser.Version == version.Value) ?? RconParser;
EventParser = Manager.AdditionalEventParsers.FirstOrDefault(_parser => _parser.Version == version.Value) ?? EventParser;
Version = RconParser.Version;
}
var infoResponse = RconParser.Configuration.CommandPrefixes.RConGetInfo != null ? await this.GetInfoAsync() : null;
// this is normally slow, but I'm only doing it because different games have different prefixes
var hostname = infoResponse == null ?
(await this.GetDvarAsync<string>("sv_hostname")).Value :
@ -746,61 +757,50 @@ namespace IW4MAdmin
this.MaxClients = maxplayers;
this.FSGame = game;
this.Gametype = gametype;
this.IP = ip.Value == "localhost" ? ServerConfig.IPAddress : ip.Value;
this.IP = ip.Value == "localhost" ? ServerConfig.IPAddress : ip.Value ?? ServerConfig.IPAddress;
if (logsync.Value == 0 || logfile.Value == string.Empty)
if ((logsync.Value == 0 || logfile.Value == string.Empty) && RconParser.CanGenerateLogPath)
{
// this DVAR isn't set until the a map is loaded
await this.SetDvarAsync("logfile", 2);
await this.SetDvarAsync("g_logsync", 2); // set to 2 for continous in other games, clamps to 1 for IW4
await this.SetDvarAsync("g_log", "games_mp.log");
//await this.SetDvarAsync("g_log", "games_mp.log");
Logger.WriteWarning("Game log file not properly initialized, restarting map...");
await this.ExecuteCommandAsync("map_restart");
logfile = await this.GetDvarAsync<string>("g_log");
}
CustomCallback = await ScriptLoaded();
string mainPath = EventParser.Configuration.GameDirectory;
string logPath = string.Empty;
LogPath = game == string.Empty ?
$"{basepath.Value.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{mainPath}{Path.DirectorySeparatorChar}{logfile.Value}" :
$"{basepath.Value.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{game.Replace('/', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{logfile.Value}";
bool remoteLog = false;
if (GameName == Game.IW5 || ServerConfig.ManualLogPath?.Length > 0)
// they've manually specified the log path
if (!string.IsNullOrEmpty(ServerConfig.ManualLogPath))
{
logPath = ServerConfig.ManualLogPath;
remoteLog = logPath.StartsWith("http");
}
else
{
logPath = LogPath;
}
if (remoteLog)
{
LogEvent = new GameLogEventDetection(this, logPath, logfile.Value);
LogPath = ServerConfig.ManualLogPath;
}
else
{
string mainPath = EventParser.Configuration.GameDirectory;
LogPath = string.IsNullOrEmpty(game) ?
$"{basepath?.Value?.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{mainPath}{Path.DirectorySeparatorChar}{logfile?.Value}" :
$"{basepath?.Value?.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{game?.Replace('/', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{logfile?.Value}";
// fix wine drive name mangling
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
logPath = Regex.Replace($"{Path.DirectorySeparatorChar}{LogPath}", @"[A-Z]:/", "");
LogPath = Regex.Replace($"{Path.DirectorySeparatorChar}{LogPath}", @"[A-Z]:/", "");
}
if (!File.Exists(logPath))
if (!File.Exists(LogPath) && ServerConfig.GameLogServerUrl == null)
{
Logger.WriteError($"{logPath} {loc["SERVER_ERROR_DNE"]}");
throw new ServerException($"{loc["SERVER_ERROR_LOG"]} {logPath}");
Logger.WriteError($"{LogPath} {loc["SERVER_ERROR_DNE"]}");
throw new ServerException($"{loc["SERVER_ERROR_LOG"]} {LogPath}");
}
LogEvent = new GameLogEventDetection(this, logPath, logfile.Value);
}
Logger.WriteInfo($"Log file is {logPath}");
LogEvent = new GameLogEventDetection(this, LogPath, ServerConfig.GameLogServerUrl);
Logger.WriteInfo($"Log file is {LogPath}");
_ = Task.Run(() => LogEvent.PollForChanges());
#if !DEBUG
@ -991,10 +991,10 @@ namespace IW4MAdmin
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("VERSION", (Server s) => 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("ADMINS", (Server s) => SharedLibraryCore.Commands.CListAdmins.OnlineAdmins(s)));
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) => Task.FromResult(Application.Program.Version.ToString())));
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) => Task.FromResult(SharedLibraryCore.Commands.CListAdmins.OnlineAdmins(s))));
}
}
}

View File

@ -56,5 +56,14 @@ namespace IW4MAdmin.Application.Migration
}
}
}
public static void ModifyLogPath020919(SharedLibraryCore.Configuration.ServerConfiguration config)
{
if (config.ManualLogPath.IsRemoteLog())
{
config.GameLogServerUrl = new Uri(config.ManualLogPath);
config.ManualLogPath = null;
}
}
}
}

View File

@ -0,0 +1,100 @@
using SharedLibraryCore.Helpers;
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Concurrent;
using System.Security.Cryptography;
using System.Text;
namespace IW4MAdmin.Application.Misc
{
class TokenAuthentication : ITokenAuthentication
{
private readonly ConcurrentDictionary<long, TokenState> _tokens;
private readonly RNGCryptoServiceProvider _random;
private readonly static TimeSpan _timeoutPeriod = new TimeSpan(0, 0, 120);
private const short TOKEN_LENGTH = 4;
public TokenAuthentication()
{
_tokens = new ConcurrentDictionary<long, TokenState>();
_random = new RNGCryptoServiceProvider();
}
public bool AuthorizeToken(long networkId, string token)
{
bool authorizeSuccessful = _tokens.ContainsKey(networkId) && _tokens[networkId].Token == token;
if (authorizeSuccessful)
{
_tokens.TryRemove(networkId, out TokenState _);
}
return authorizeSuccessful;
}
public TokenState GenerateNextToken(long networkId)
{
TokenState state = null;
if (_tokens.ContainsKey(networkId))
{
state = _tokens[networkId];
if ((DateTime.Now - state.RequestTime) > _timeoutPeriod)
{
_tokens.TryRemove(networkId, out TokenState _);
}
else
{
return state;
}
}
state = new TokenState()
{
NetworkId = networkId,
Token = _generateToken(),
TokenDuration = _timeoutPeriod
};
_tokens.TryAdd(networkId, state);
// perform some housekeeping so we don't have built up tokens if they're not ever used
foreach (var (key, value) in _tokens)
{
if ((DateTime.Now - value.RequestTime) > _timeoutPeriod)
{
_tokens.TryRemove(key, out TokenState _);
}
}
return state;
}
public string _generateToken()
{
bool validCharacter(char c)
{
// this ensure that the characters are 0-9, A-Z, a-z
return (c > 47 && c < 58) || (c > 64 && c < 91) || (c > 96 && c < 123);
}
StringBuilder token = new StringBuilder();
while (token.Length < TOKEN_LENGTH)
{
byte[] charSet = new byte[1];
_random.GetBytes(charSet);
if (validCharacter((char)charSet[0]))
{
token.Append((char)charSet[0]);
}
}
_random.Dispose();
return token.ToString();
}
}
}

View File

@ -8,37 +8,54 @@ using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using static SharedLibraryCore.Server;
namespace IW4MAdmin.Application.RconParsers
{
class IW4RConParser : IRConParser
class BaseRConParser : IRConParser
{
public IW4RConParser()
public BaseRConParser()
{
Configuration = new DynamicRConParserConfiguration()
{
CommandPrefixes = new CommandPrefix()
{
Tell = "tellraw {0} {1}",
Say = "sayraw {0}",
Tell = "tell {0} {1}",
Say = "say {0}",
Kick = "clientkick {0} \"{1}\"",
Ban = "clientkick {0} \"{1}\"",
TempBan = "tempbanclient {0} \"{1}\""
TempBan = "tempbanclient {0} \"{1}\"",
RConCommand = "ÿÿÿÿrcon {0} {1}",
RConGetDvar = "ÿÿÿÿrcon {0} {1}",
RConSetDvar = "ÿÿÿÿrcon {0} set {1}",
RConGetStatus = "ÿÿÿÿgetstatus",
RConGetInfo = "ÿÿÿÿgetinfo",
RConResponse = "ÿÿÿÿprint",
},
GameName = Server.Game.IW4
};
Configuration.Status.Pattern = @"^ *([0-9]+) +-?([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){16}|(?:[a-z]|[0-9]){32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback) +(-*[0-9]+) +([0-9]+) *$";
Configuration.Status.GroupMapping.Add(ParserRegex.GroupType.RConClientNumber, 1);
Configuration.Status.GroupMapping.Add(ParserRegex.GroupType.RConScore, 2);
Configuration.Status.GroupMapping.Add(ParserRegex.GroupType.RConPing, 3);
Configuration.Status.GroupMapping.Add(ParserRegex.GroupType.RConNetworkId, 4);
Configuration.Status.GroupMapping.Add(ParserRegex.GroupType.RConName, 5);
Configuration.Status.GroupMapping.Add(ParserRegex.GroupType.RConIpAddress, 7);
Configuration.Status.Pattern = @"^ *([0-9]+) +-?([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){8,32}|(?:[a-z]|[0-9]){8,32}|bot[0-9]+|(?:[0-9]+)) +(.{0,32}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback) +(-*[0-9]+) +([0-9]+) *$";
Configuration.Status.AddMapping(ParserRegex.GroupType.RConClientNumber, 1);
Configuration.Status.AddMapping(ParserRegex.GroupType.RConScore, 2);
Configuration.Status.AddMapping(ParserRegex.GroupType.RConPing, 3);
Configuration.Status.AddMapping(ParserRegex.GroupType.RConNetworkId, 4);
Configuration.Status.AddMapping(ParserRegex.GroupType.RConName, 5);
Configuration.Status.AddMapping(ParserRegex.GroupType.RConIpAddress, 7);
Configuration.Dvar.Pattern = "^\"(.+)\" is: \"(.+)?\" default: \"(.+)?\"\n(?:latched: \"(.+)?\"\n)? *(.+)$";
Configuration.Dvar.AddMapping(ParserRegex.GroupType.RConDvarName, 1);
Configuration.Dvar.AddMapping(ParserRegex.GroupType.RConDvarValue, 2);
Configuration.Dvar.AddMapping(ParserRegex.GroupType.RConDvarDefaultValue, 3);
Configuration.Dvar.AddMapping(ParserRegex.GroupType.RConDvarLatchedValue, 4);
Configuration.Dvar.AddMapping(ParserRegex.GroupType.RConDvarDomain, 5);
}
public IRConParserConfiguration Configuration { get; set; }
public string Version { get; set; } = "CoD";
public Game GameName { get; set; } = Game.COD;
public bool CanGenerateLogPath { get; set; } = true;
public async Task<string[]> ExecuteCommandAsync(Connection connection, string command)
{
var response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, command);
@ -47,44 +64,49 @@ namespace IW4MAdmin.Application.RconParsers
public async Task<Dvar<T>> GetDvarAsync<T>(Connection connection, string dvarName)
{
string[] LineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.DVAR, dvarName);
string[] lineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.GET_DVAR, dvarName);
string response = string.Join('\n', lineSplit.Skip(1));
if (LineSplit.Length < 3)
if (!lineSplit[0].Contains(Configuration.CommandPrefixes.RConResponse))
{
var e = new DvarException($"DVAR \"{dvarName}\" does not exist");
e.Data["dvar_name"] = dvarName;
throw e;
throw new DvarException($"Could not retrieve DVAR \"{dvarName}\"");
}
// todo: can this be made more portable and modifiable from plugin
string[] ValueSplit = LineSplit[1].Split(new char[] { '"' }, StringSplitOptions.RemoveEmptyEntries);
if (ValueSplit.Length < 5)
if (response.Contains("Unknown command"))
{
var e = new DvarException($"DVAR \"{dvarName}\" does not exist");
e.Data["dvar_name"] = dvarName;
throw e;
throw new DvarException($"DVAR \"{dvarName}\" does not exist");
}
string DvarName = Regex.Replace(ValueSplit[0], @"\^[0-9]", "");
string DvarCurrentValue = Regex.Replace(ValueSplit[2], @"\^[0-9]", "");
string DvarDefaultValue = Regex.Replace(ValueSplit[4], @"\^[0-9]", "");
var match = Regex.Match(response, Configuration.Dvar.Pattern);
return new Dvar<T>(DvarName)
if (!match.Success)
{
Value = (T)Convert.ChangeType(DvarCurrentValue, typeof(T))
throw new DvarException($"Could not retrieve DVAR \"{dvarName}\"");
}
string value = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarValue]].Value.StripColors();
string defaultValue = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarDefaultValue]].Value.StripColors();
string latchedValue = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarLatchedValue]].Value.StripColors();
return new Dvar<T>()
{
Name = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarName]].Value.StripColors(),
Value = string.IsNullOrEmpty(value) ? default(T) : (T)Convert.ChangeType(value, typeof(T)),
DefaultValue = string.IsNullOrEmpty(defaultValue) ? default(T) : (T)Convert.ChangeType(defaultValue, typeof(T)),
LatchedValue = string.IsNullOrEmpty(latchedValue) ? default(T) : (T)Convert.ChangeType(latchedValue, typeof(T)),
Domain = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarDomain]].Value.StripColors()
};
}
public async Task<List<EFClient>> GetStatusAsync(Connection connection)
{
string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, "status");
string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND_STATUS);
return ClientsFromStatus(response);
}
public async Task<bool> SetDvarAsync(Connection connection, string dvarName, object dvarValue)
{
return (await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, $"set {dvarName} {dvarValue}")).Length > 0;
return (await connection.SendQueryAsync(StaticHelpers.QueryType.SET_DVAR, $"{dvarName} {dvarValue}")).Length > 0;
}
private List<EFClient> ClientsFromStatus(string[] Status)
@ -97,9 +119,9 @@ namespace IW4MAdmin.Application.RconParsers
}
int validMatches = 0;
foreach (String S in Status)
foreach (string S in Status)
{
String responseLine = S.Trim();
string responseLine = S.Trim();
var regex = Regex.Match(responseLine, Configuration.Status.Pattern, RegexOptions.IgnoreCase);

View File

@ -1,12 +1,10 @@
using SharedLibraryCore;
using SharedLibraryCore.RCon;
using System;
using static SharedLibraryCore.Server;
namespace IW4MAdmin.Application.RconParsers
namespace IW4MAdmin.Application.RconParsers
{
sealed internal class DynamicRConParser : IW4RConParser
/// <summary>
/// empty implementation of the IW4RConParser
/// allows script plugins to generate dynamic RCon parsers
/// </summary>
sealed internal class DynamicRConParser : BaseRConParser
{
public string Version { get; set; }
}
}

View File

@ -4,10 +4,15 @@ using SharedLibraryCore.RCon;
namespace IW4MAdmin.Application.RconParsers
{
class DynamicRConParserConfiguration : IRConParserConfiguration
/// <summary>
/// generic implementation of the IRConParserConfiguration
/// allows script plugins to generate dynamic RCon configurations
/// </summary>
sealed internal class DynamicRConParserConfiguration : IRConParserConfiguration
{
public CommandPrefix CommandPrefixes { get; set; }
public Server.Game GameName { get; set; }
public ParserRegex Status { get; set; } = new ParserRegex();
public ParserRegex Dvar { get; set; } = new ParserRegex();
public bool WaitForResponse { get; set; } = true;
}
}

View File

@ -1,25 +0,0 @@
using SharedLibraryCore;
using SharedLibraryCore.RCon;
using System;
using System.Collections.Generic;
using System.Text;
namespace IW4MAdmin.Application.RconParsers
{
class IW3RConParser : IW4RConParser
{
public IW3RConParser() : base()
{
Configuration.CommandPrefixes = new CommandPrefix()
{
Tell = "tell {0} {1}",
Say = "say {0}",
Kick = "clientkick {0} \"{1}\"",
Ban = "clientkick {0} \"{1}\"",
TempBan = "tempbanclient {0} \"{1}\""
};
Configuration.GameName = Server.Game.IW3;
}
}
}

View File

@ -1,131 +0,0 @@
using SharedLibraryCore;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.RCon;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace IW4MAdmin.Application.RconParsers
{
public class T6MRConParser : IRConParser
{
public IRConParserConfiguration Configuration { get; set; }
public T6MRConParser()
{
Configuration = new DynamicRConParserConfiguration()
{
CommandPrefixes = new CommandPrefix()
{
Tell = "tell {0} {1}",
Say = "say {0}",
Kick = "clientkick_for_reason {0} \"{1}\"",
Ban = "clientkick_for_reason {0} \"{1}\"",
TempBan = "clientkick_for_reason {0} \"{1}\""
},
GameName = Server.Game.T6M
};
}
public async Task<string[]> ExecuteCommandAsync(Connection connection, string command)
{
await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, command, false);
return new string[] { "Command Executed" };
}
public async Task<Dvar<T>> GetDvarAsync<T>(Connection connection, string dvarName)
{
string[] LineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, $"get {dvarName}");
if (LineSplit.Length < 2)
{
var e = new DvarException($"DVAR \"{dvarName}\" does not exist");
e.Data["dvar_name"] = dvarName;
throw e;
}
string[] ValueSplit = LineSplit[1].Split(new char[] { '"' });
if (ValueSplit.Length == 0)
{
var e = new DvarException($"DVAR \"{dvarName}\" does not exist");
e.Data["dvar_name"] = dvarName;
throw e;
}
string DvarName = dvarName;
string DvarCurrentValue = Regex.Replace(ValueSplit[1], @"\^[0-9]", "");
return new Dvar<T>(DvarName)
{
Value = (T)Convert.ChangeType(DvarCurrentValue, typeof(T))
};
}
public async Task<List<EFClient>> GetStatusAsync(Connection connection)
{
string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, "status");
return ClientsFromStatus(response);
}
public async Task<bool> SetDvarAsync(Connection connection, string dvarName, object dvarValue)
{
// T6M doesn't respond with anything when a value is set, so we can only hope for the best :c
await connection.SendQueryAsync(StaticHelpers.QueryType.DVAR, $"set {dvarName} {dvarValue}", false);
return true;
}
private List<EFClient> ClientsFromStatus(string[] status)
{
List<EFClient> StatusPlayers = new List<EFClient>();
foreach (string statusLine in status)
{
String responseLine = statusLine;
if (Regex.Matches(responseLine, @"^ *\d+", RegexOptions.IgnoreCase).Count > 0) // its a client line!
{
String[] playerInfo = responseLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
int clientId = -1;
int Ping = -1;
Int32.TryParse(playerInfo[3], out Ping);
var regex = Regex.Match(responseLine, @"\^7.*\ +0 ");
string name = Encoding.UTF8.GetString(Encoding.Convert(Utilities.EncodingType, Encoding.UTF8, Utilities.EncodingType.GetBytes(regex.Value.Substring(0, regex.Value.Length - 2).StripColors().Trim())));
long networkId = playerInfo[4].ConvertLong();
int.TryParse(playerInfo[0], out clientId);
regex = Regex.Match(responseLine, @"\d+\.\d+\.\d+.\d+\:\d{1,5}");
#if DEBUG
Ping = 1;
#endif
int? ipAddress = regex.Value.Split(':')[0].ConvertToIP();
regex = Regex.Match(responseLine, @"[0-9]{1,2}\s+[0-9]+\s+");
var p = new EFClient()
{
Name = name,
NetworkId = networkId,
ClientNumber = clientId,
IPAddress = ipAddress,
Ping = Ping,
Score = 0,
State = EFClient.ClientState.Connecting,
IsBot = ipAddress == null
};
if (p.IsBot)
{
p.NetworkId = -p.ClientNumber;
}
StatusPlayers.Add(p);
}
}
return StatusPlayers;
}
}
}

View File

@ -1,31 +0,0 @@
var plugin = {
author: 'RaidMax',
version: 1.1,
name: 'Shared GUID Kicker Plugin',
onEventAsync: function (gameEvent, server) {
// make sure we only check for IW4(x)
if (server.GameName !== 2) {
return false;
}
// connect or join event
if (gameEvent.Type === 3) {
// this GUID seems to have been packed in a IW4 torrent and results in an unreasonable amount of people using the same GUID
if (gameEvent.Origin.NetworkId === -805366929435212061) {
gameEvent.Origin.Kick('Your GUID is generic. Delete players/guids.dat and rejoin', _IW4MAdminClient);
}
}
},
onLoadAsync: function (manager) {
},
onUnloadAsync: function () {
},
onTickAsync: function (server) {
}
};

View File

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

View File

@ -11,13 +11,15 @@
<SearchPath>
</SearchPath>
<WorkingDirectory>.</WorkingDirectory>
<LaunchProvider>Web launcher</LaunchProvider>
<LaunchProvider>Standard Python launcher</LaunchProvider>
<WebBrowserUrl>http://localhost</WebBrowserUrl>
<OutputPath>.</OutputPath>
<SuppressCollectPythonCloudServiceFiles>true</SuppressCollectPythonCloudServiceFiles>
<Name>GameLogServer</Name>
<RootNamespace>GameLogServer</RootNamespace>
<InterpreterId>MSBuild|env|$(MSBuildProjectFullPath)</InterpreterId>
<EnableNativeCodeDebugging>False</EnableNativeCodeDebugging>
<Environment>DEBUG=True</Environment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>

View File

@ -15,7 +15,7 @@ class LogReader(object):
if re.search('r^.+\.\.\\.+$', path):
return False
# must be a valid log path and log file
if not re.search(r'^.+[\\|\/](userraw|mods|main)[\\|\/].+.log$', path):
if not re.search(r'^.+[\\|\/](.+)[\\|\/].+.log$', path):
return False
# set the initialze size to the current file size
file_size = 0

View File

@ -12,4 +12,4 @@ if __name__ == '__main__':
except ValueError:
PORT = 5555
init()
app.run(HOST, PORT, debug=False)
app.run('0.0.0.0', PORT, debug=False)

View File

@ -38,12 +38,22 @@ Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "DiscordWebhook", "DiscordWe
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ScriptPlugins", "ScriptPlugins", "{3F9ACC27-26DB-49FA-BCD2-50C54A49C9FA}"
ProjectSection(SolutionItems) = preProject
Plugins\ScriptPlugins\ParserCoD4x.js = Plugins\ScriptPlugins\ParserCoD4x.js
Plugins\ScriptPlugins\ParserIW4x.js = Plugins\ScriptPlugins\ParserIW4x.js
Plugins\ScriptPlugins\ParserPT6.js = Plugins\ScriptPlugins\ParserPT6.js
Plugins\ScriptPlugins\ParserTeknoMW3.js = Plugins\ScriptPlugins\ParserTeknoMW3.js
Plugins\ScriptPlugins\SharedGUIDKick.js = Plugins\ScriptPlugins\SharedGUIDKick.js
Plugins\ScriptPlugins\VPNDetection.js = Plugins\ScriptPlugins\VPNDetection.js
EndProjectSection
EndProject
Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "GameLogServer", "GameLogServer\GameLogServer.pyproj", "{42EFDA12-10D3-4C40-A210-9483520116BC}"
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
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -282,8 +292,8 @@ Global
{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.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.Build.0 = 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 = Prerelease|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|x64.ActiveCfg = Debug|Any CPU
@ -302,7 +312,7 @@ Global
{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|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|x64.ActiveCfg = Release|Any CPU
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|x86.ActiveCfg = Release|Any CPU
@ -311,15 +321,13 @@ Global
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Release|x64.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.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.Build.0 = 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|x86.ActiveCfg = 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.Build.0 = Release|Any CPU
{42EFDA12-10D3-4C40-A210-9483520116BC}.Prerelease|Any CPU.ActiveCfg = Prerelease|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|x64.ActiveCfg = Release|Any CPU
@ -334,6 +342,54 @@ Global
{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.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
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -346,6 +402,9 @@ Global
{B72DEBFB-9D48-4076-8FF5-1FD72A830845} = {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}
{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
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {84F8F8E0-1F73-41E0-BD8D-BB6676E2EE87}

View File

@ -11,7 +11,7 @@
<SearchPath>
</SearchPath>
<WorkingDirectory>.</WorkingDirectory>
<LaunchProvider>Web launcher</LaunchProvider>
<LaunchProvider>Standard Python launcher</LaunchProvider>
<WebBrowserUrl>http://localhost</WebBrowserUrl>
<OutputPath>.</OutputPath>
<SuppressCollectPythonCloudServiceFiles>true</SuppressCollectPythonCloudServiceFiles>
@ -25,6 +25,7 @@
</PythonDebugWebServerCommand>
<PythonRunWebServerCommandType>script</PythonRunWebServerCommandType>
<PythonDebugWebServerCommandType>script</PythonDebugWebServerCommandType>
<EnableNativeCodeDebugging>False</EnableNativeCodeDebugging>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>

View File

@ -5,6 +5,7 @@ from marshmallow import ValidationError
from master.schema.instanceschema import InstanceSchema
from master import ctx
import json
from netaddr import IPAddress
class Instance(Resource):
def get(self, id=None):
@ -23,7 +24,7 @@ class Instance(Resource):
def put(self, id):
try:
for server in request.json['servers']:
if 'ip' not in server or server['ip'] == 'localhost':
if 'ip' not in server or IPAddress(server['ip']).is_private() or IPAddress(server['ip']).is_loopback():
server['ip'] = request.remote_addr
if 'version' not in server:
server['version'] = 'Unknown'

View File

@ -4,7 +4,7 @@ from master.models.servermodel import ServerModel
class ServerSchema(Schema):
id = fields.Int(
required=True,
validate=validate.Range(1, 25525525525565535, 'invalid id')
validate=validate.Range(0, 25525525525565535, 'invalid id')
)
ip = fields.Str(
required=True
@ -35,7 +35,7 @@ class ServerSchema(Schema):
)
map = fields.String(
required=True,
validate=validate.Length(1, 32, 'invalid map name')
validate=validate.Length(0, 64, 'invalid map name')
)
gametype = fields.String(
required=True,

View 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 &quot;$(TargetPath)&quot; &quot;$(SolutionDir)BUILD\Plugins&quot;"/>
<Exec Command="copy &quot;$(TargetDir)Microsoft.SyndicationFeed.ReaderWriter.dll&quot; &quot;$(SolutionDir)BUILD\Plugins&quot;"/>
</Target>
</Project>

View 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";
}
}

View 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();
}
}
}

View File

@ -2,8 +2,8 @@
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
<TargetFramework>netcoreapp2.2</TargetFramework>
<RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion>
<ApplicationIcon />
<StartupObject />
<Configurations>Debug;Release;Prerelease</Configurations>
@ -19,7 +19,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
<PackageReference Update="Microsoft.NETCore.App" Version="2.2.2" />
</ItemGroup>
</Project>

View File

@ -1,8 +1,5 @@
using SharedLibraryCore;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Objects;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace IW4MAdmin.Plugins.Login.Commands
@ -22,18 +19,22 @@ namespace IW4MAdmin.Plugins.Login.Commands
public override async Task ExecuteAsync(GameEvent E)
{
var client = E.Owner.Manager.GetPrivilegedClients()[E.Origin.ClientId];
string[] hashedPassword = await Task.FromResult(SharedLibraryCore.Helpers.Hashing.Hash(E.Data, client.PasswordSalt));
bool success = E.Owner.Manager.TokenAuthenticator.AuthorizeToken(E.Origin.NetworkId, E.Data);
if (hashedPassword[0] == client.Password)
if (!success)
{
Plugin.AuthorizedClients[E.Origin.ClientId] = true;
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS"]);
string[] hashedPassword = await Task.FromResult(SharedLibraryCore.Helpers.Hashing.Hash(E.Data, client.PasswordSalt));
if (hashedPassword[0] == client.Password)
{
success = true;
Plugin.AuthorizedClients[E.Origin.ClientId] = true;
}
}
else
{
_ = success ?
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS"]) :
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_FAIL"]);
}
}
}
}

View File

@ -2,8 +2,8 @@
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
<TargetFramework>netcoreapp2.2</TargetFramework>
<RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion>
<ApplicationIcon />
<StartupObject />
<PackageId>RaidMax.IW4MAdmin.Plugins.Login</PackageId>
@ -22,7 +22,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
<PackageReference Update="Microsoft.NETCore.App" Version="2.2.2" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">

View File

@ -2,8 +2,8 @@
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
<TargetFramework>netcoreapp2.2</TargetFramework>
<RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion>
<ApplicationIcon />
<StartupObject />
<PackageId>RaidMax.IW4MAdmin.Plugins.ProfanityDeterment</PackageId>
@ -20,7 +20,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
<PackageReference Update="Microsoft.NETCore.App" Version="2.2.2" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">

View File

@ -0,0 +1,37 @@
var rconParser;
var eventParser;
var plugin = {
author: 'FrenchFry, RaidMax',
version: 0.2,
name: 'CoD4x Parser',
isParser: true,
onEventAsync: function (gameEvent, server) {
},
onLoadAsync: function (manager) {
rconParser = manager.GenerateDynamicRConParser();
eventParser = manager.GenerateDynamicEventParser();
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +-?([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){16}|(?:[a-z]|[0-9]){32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +([0-9]+) +(\\d+\\.\\d+\\.\\d+.\\d+\\:-*\\d{1,5}|0+.0+:-*\\d{1,5}|loopback) +(-*[0-9]+) +([0-9]+) *$'
rconParser.Configuration.Status.AddMapping(104, 6); // RConName
rconParser.Configuration.Status.AddMapping(105, 8); // RConIPAddress
rconParser.Configuration.Dvar.Pattern = '^"(.+)" is: "(.+)?" default: "(.+)?" info: "(.+)?"$';
rconParser.Configuration.Dvar.AddMapping(109, 2); // DVAR latched value
rconParser.Configuration.Dvar.AddMapping(110, 4); // dvar info
rconParser.Version = 'CoD4 X 1.8 win_mingw-x86 build 2055 May 2 2017';
rconParser.GameName = 1; // IW3
eventParser.Configuration.GameDirectory = 'main';
eventParser.Version = 'CoD4 X 1.8 win_mingw-x86 build 2055 May 2 2017';
eventParser.GameName = 1; // IW3
},
onUnloadAsync: function () {
},
onTickAsync: function (server) {
}
};

View File

@ -0,0 +1,35 @@
var rconParser;
var eventParser;
var plugin = {
author: 'RaidMax',
version: 0.2,
name: 'IW3 Parser',
isParser: true,
onEventAsync: function (gameEvent, server) {
},
onLoadAsync: function (manager) {
rconParser = manager.GenerateDynamicRConParser();
eventParser = manager.GenerateDynamicEventParser();
rconParser.Configuration.CommandPrefixes.Tell = 'tellraw {0} {1}';
rconParser.Configuration.CommandPrefixes.Say = 'sayraw {0}';
rconParser.Configuration.CommandPrefixes.Kick = 'clientkick {0} "{1}"';
rconParser.Configuration.CommandPrefixes.Ban = 'clientkick {0} "{1}"';
rconParser.Configuration.CommandPrefixes.TempBan = 'tempbanclient {0} "{1}"';
eventParser.Configuration.GameDirectory = 'userraw';
rconParser.Version = 'IW4x (v0.6.0)';
rconParser.GameName = 2; // IW4x
eventParser.Version = 'IW4x (v0.6.0)';
eventParser.GameName = 2; // IW4x
},
onUnloadAsync: function () {
},
onTickAsync: function (server) {
}
};

View File

@ -0,0 +1,50 @@
var rconParser;
var eventParser;
var plugin = {
author: 'RaidMax',
version: 0.2,
name: 'Plutoniun T6 Parser',
isParser: true,
onEventAsync: function (gameEvent, server) {
},
onLoadAsync: function (manager) {
rconParser = manager.GenerateDynamicRConParser();
eventParser = manager.GenerateDynamicEventParser();
rconParser.Configuration.CommandPrefixes.Tell = 'tell {0} {1}';
rconParser.Configuration.CommandPrefixes.Say = 'say {0}';
rconParser.Configuration.CommandPrefixes.Kick = 'clientkick_for_reason {0} "{1}"';
rconParser.Configuration.CommandPrefixes.Ban = 'clientkick_for_reason {0} "{1}"';
rconParser.Configuration.CommandPrefixes.TempBan = 'clientkick_for_reason {0} "{1}"';
rconParser.Configuration.CommandPrefixes.RConGetDvar = '\xff\xff\xff\xffrcon {0} get {1}';
rconParser.Configuration.Dvar.Pattern = '^(.+) is "(.+)?"$';
rconParser.Configuration.Dvar.AddMapping(106, 1);
rconParser.Configuration.Dvar.AddMapping(107, 2);
rconParser.Configuration.WaitForResponse = false;
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +([0-9]+) +(.+) +((?:[A-Z]+|[0-9]+)) +((?:[A-Z]|[0-9]){8,16}) +(.{0,16}) +([0-9]+) +(\\d+\\.\\d+\\.\\d+\\.\\d+\\:-?\\d{1,5}|0+\\.0+:-?\\d{1,5}|loopback) +(-?[0-9]+) +([0-9]+) *$'
rconParser.Configuration.Status.AddMapping(100, 1);
rconParser.Configuration.Status.AddMapping(101, 2);
rconParser.Configuration.Status.AddMapping(102, 4);
rconParser.Configuration.Status.AddMapping(103, 5);
rconParser.Configuration.Status.AddMapping(104, 6);
rconParser.Configuration.Status.AddMapping(105, 8);
eventParser.Configuration.GameDirectory = 't6r\\data';
rconParser.Version = 'Call of Duty Multiplayer - Ship COD_T6_S MP build 1.0.44 CL(1759941) CODPCAB2 CEG Fri May 9 19:19:19 2014 win-x86 813e66d5';
rconParser.GameName = 7; // T6
eventParser.Version = 'Call of Duty Multiplayer - Ship COD_T6_S MP build 1.0.44 CL(1759941) CODPCAB2 CEG Fri May 9 19:19:19 2014 win-x86 813e66d5';
eventParser.GameName = 7; // T6
},
onUnloadAsync: function () {
},
onTickAsync: function (server) {
}
};

View File

@ -0,0 +1,43 @@
var rconParser;
var eventParser;
var plugin = {
author: 'RaidMax',
version: 0.2,
name: 'Tekno MW3 Parser',
isParser: true,
onEventAsync: function (gameEvent, server) {
},
onLoadAsync: function (manager) {
rconParser = manager.GenerateDynamicRConParser();
eventParser = manager.GenerateDynamicEventParser();
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[A-Z]|[0-9]){16,32})\t +(.{0,16}) +([0-9]+) +(\\d+\\.\\d+\\.\\d+\\.\\d+\\:-?\\d{1,5}|0+\\.0+\\:-?\\d{1,5}|loopback) *$';
rconParser.Configuration.Status.AddMapping(104, 5); // RConName
rconParser.Configuration.Status.AddMapping(103, 4); // RConNetworkId
rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined;
rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xff';
rconParser.Configuration.CommandPrefixes.Tell = 'tell {0} {1}';
rconParser.Configuration.CommandPrefixes.Say = 'say {0}';
rconParser.Configuration.CommandPrefixes.Kick = 'dropclient {0} "{1}"';
rconParser.Configuration.CommandPrefixes.Ban = 'dropclient {0} "{1}"';
rconParser.Configuration.CommandPrefixes.TempBan = 'tempbanclient {0} "{1}"';
rconParser.Configuration.Dvar.AddMapping(107, 1); // RCon DvarValue
rconParser.Configuration.Dvar.Pattern = '^(.*)$';
rconParser.Version = 'IW5 MP 1.4 build 382 latest Thu Jan 19 2012 11:09:49AM win-x86';
rconParser.GameName = 3; // IW5
rconParser.CanGenerateLogPath = false;
eventParser.Configuration.GameDirectory = 'scripts';
eventParser.Version = 'IW5 MP 1.4 build 382 latest Thu Jan 19 2012 11:09:49AM win-x86';
eventParser.GameName = 3; // IW5
},
onUnloadAsync: function () {
},
onTickAsync: function (server) {
}
};

View File

@ -39,7 +39,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
where stats.ServerId == serverId
where client.Level != EFClient.Permission.Banned
where client.LastConnection >= thirtyDaysAgo
orderby stats.Kills descending
orderby stats.TimePlayed descending
select new
{
alias.Name,

View File

@ -1,7 +1,9 @@
using Microsoft.AspNetCore.Authorization;
using IW4MAdmin.Plugins.Stats.Helpers;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SharedLibraryCore;
using SharedLibraryCore.Dtos;
using System;
using System.Linq;
using System.Threading.Tasks;
@ -12,18 +14,43 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers
public class StatsController : BaseController
{
[HttpGet]
public async Task<IActionResult> TopPlayersAsync()
public IActionResult TopPlayersAsync()
{
ViewBag.Title = Utilities.CurrentLocalization.LocalizationIndex.Set["WEBFRONT_STATS_INDEX_TITLE"];
ViewBag.Description = Utilities.CurrentLocalization.LocalizationIndex.Set["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]
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]
@ -38,7 +65,7 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers
where message.ServerId == serverId
where message.TimeSent >= whenLower
where message.TimeSent <= whenUpper
select new SharedLibraryCore.Dtos.ChatInfo()
select new ChatInfo()
{
ClientId = message.ClientId,
Message = message.Message,

View File

@ -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))
{
// setup the query for the clients within the given rating range
var iqClientRatings = (from rating in context.Set<EFRating>()
.Where(GetRankingFunc())
.Where(GetRankingFunc(serverId))
select new
{
rating.RatingHistory.ClientId,
@ -113,7 +113,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
var iqRatingInfo = from rating in context.Set<EFRating>()
where clientIds.Contains(rating.RatingHistory.ClientId)
where rating.ServerId == null
where rating.ServerId == serverId
select new
{
rating.Ranking,
@ -155,6 +155,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
var finished = topPlayers.Select(s => new TopStatsInfo()
{
ClientId = s.ClientId,
Id = (int?)serverId ?? 0,
Deaths = s.Deaths,
Kills = s.Kills,
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)
{
string regex = @"^(D);(.+);([0-9]+);(allies|axis);(.+);([0-9]+);(allies|axis);(.+);(.+);([0-9]+);(.+);(.+)$";
var match = Regex.Match(eventLine, regex, RegexOptions.IgnoreCase);
// todo: maybe do something with this
//string regex = @"^(D);(.+);([0-9]+);(allies|axis);(.+);([0-9]+);(allies|axis);(.+);(.+);([0-9]+);(.+);(.+)$";
//var match = Regex.Match(eventLine, regex, RegexOptions.IgnoreCase);
if (match.Success)
{
// this gives us what team the player is on
var attackerStats = Servers[serverId].PlayerStats[attackerClientId];
var victimStats = Servers[serverId].PlayerStats[victimClientId];
IW4Info.Team victimTeam = (IW4Info.Team)Enum.Parse(typeof(IW4Info.Team), match.Groups[4].ToString());
IW4Info.Team attackerTeam = (IW4Info.Team)Enum.Parse(typeof(IW4Info.Team), match.Groups[7].ToString());
attackerStats.Team = attackerTeam;
victimStats.Team = victimTeam;
}
//if (match.Success)
//{
// // this gives us what team the player is on
// var attackerStats = Servers[serverId].PlayerStats[attackerClientId];
// var victimStats = Servers[serverId].PlayerStats[victimClientId];
// 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(), true);
// attackerStats.Team = attackerTeam;
// victimStats.Team = victimTeam;
//}
}
/// <summary>

View File

@ -26,7 +26,7 @@ namespace IW4MAdmin.Plugins.Stats
public string Author => "RaidMax";
public static StatManager Manager { get; private set; }
private IManager ServerManager;
public static IManager ServerManager;
public static BaseConfigurationHandler<StatsConfiguration> Config { get; private set; }
public async Task OnEventAsync(GameEvent E, Server S)
@ -282,41 +282,41 @@ namespace IW4MAdmin.Plugins.Stats
return messageMeta;
}
MetaService.AddMeta(getStats);
MetaService.AddRuntimeMeta(getStats);
if (Config.Configuration().EnableAntiCheat)
{
MetaService.AddMeta(getAnticheatInfo);
MetaService.AddRuntimeMeta(getAnticheatInfo);
}
MetaService.AddMeta(getMessages);
MetaService.AddRuntimeMeta(getMessages);
string totalKills(Server server)
async Task<string> totalKills(Server server)
{
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");
}
}
string totalPlayTime(Server server)
async Task<string> totalPlayTime(Server server)
{
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");
}
}
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));
@ -325,7 +325,6 @@ namespace IW4MAdmin.Plugins.Stats
manager.GetMessageTokens().Add(new MessageToken("MOSTPLAYED", mostPlayed));
ServerManager = manager;
Manager = new StatManager(manager);
}

View File

@ -2,8 +2,8 @@
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
<TargetFramework>netcoreapp2.2</TargetFramework>
<RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion>
<ApplicationIcon />
<StartupObject />
<PackageId>RaidMax.IW4MAdmin.Plugins.Stats</PackageId>
@ -15,27 +15,16 @@
<Configurations>Debug;Release;Prerelease</Configurations>
</PropertyGroup>
<ItemGroup>
<Content Include="Web\Views\Stats\_MessageContext.cshtml">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\SharedLibraryCore\SharedLibraryCore.csproj" />
<ProjectReference Include="..\..\WebfrontCore\WebfrontCore.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
<PackageReference Update="Microsoft.NETCore.App" Version="2.2.2" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="copy &quot;$(TargetPath)&quot; &quot;$(SolutionDir)BUILD\Plugins&quot;" />
</Target>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="xcopy /E /K /Y /C /I &quot;$(ProjectDir)Web\Views&quot; &quot;$(SolutionDir)WebfrontCore\Views\Plugins&quot;&#xD;&#xA;xcopy /E /K /Y /C /I &quot;$(ProjectDir)Web\wwwroot\images&quot; &quot;$(SolutionDir)WebfrontCore\wwwroot\images&quot;" />
</Target>
</Project>

View 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));
}
}
}

View File

@ -1,14 +0,0 @@
@model List<IW4MAdmin.Plugins.Stats.Web.Dtos.TopStatsInfo>
<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>
}

Some files were not shown because too many files have changed in this diff Show More