Compare commits
10 Commits
2.2-prerel
...
2.2
Author | SHA1 | Date | |
---|---|---|---|
305817d00c | |||
65cf3566db | |||
e91d076b41 | |||
b5e9519f0c | |||
7caee55a7e | |||
b289917319 | |||
c8366a22e5 | |||
de902a58ac | |||
c7547f1ad5 | |||
9d946d1bad |
1
.gitignore
vendored
1
.gitignore
vendored
@ -229,3 +229,4 @@ bootstrap-custom.min.css
|
||||
/DiscordWebhook/env
|
||||
/DiscordWebhook/config.dev.json
|
||||
/GameLogServer/env
|
||||
launchSettings.json
|
@ -3,9 +3,10 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
|
||||
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
|
||||
<PackageId>RaidMax.IW4MAdmin.Application</PackageId>
|
||||
<Version>2.1.9.2</Version>
|
||||
<Version>2.2</Version>
|
||||
<Authors>RaidMax</Authors>
|
||||
<Company>Forever None</Company>
|
||||
<Product>IW4MAdmin</Product>
|
||||
@ -30,6 +31,8 @@
|
||||
<PropertyGroup>
|
||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||
<TieredCompilation>true</TieredCompilation>
|
||||
<AssemblyVersion>2.2.0.0</AssemblyVersion>
|
||||
<FileVersion>2.2.0.0</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -76,7 +79,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.NETCore.App" />
|
||||
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||
@ -91,6 +94,6 @@
|
||||
</Target>
|
||||
|
||||
<Target Name="PostPublish" AfterTargets="Publish">
|
||||
<Exec Command="call $(ProjectDir)BuildScripts\PostPublish.bat $(ProjectDir)..\ $(ProjectDir) $(TargetDir) $(OutDir)" />
|
||||
<Exec Command="call $(ProjectDir)BuildScripts\PostPublish.bat $(ProjectDir)..\ $(ProjectDir) $(TargetDir) $(ConfigurationName)" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
@ -1,6 +1,8 @@
|
||||
set SolutionDir=%1
|
||||
set ProjectDir=%2
|
||||
set TargetDir=%3
|
||||
set CurrentConfiguration=%4
|
||||
SET COPYCMD=/Y
|
||||
|
||||
echo Deleting extra language files
|
||||
|
||||
@ -54,9 +56,52 @@ del "%SolutionDir%Publish\Windows\*pdb"
|
||||
if exist "%SolutionDir%Publish\WindowsPrerelease\web.config" del "%SolutionDir%Publish\WindowsPrerelease\web.config"
|
||||
del "%SolutionDir%Publish\WindowsPrerelease\*pdb"
|
||||
|
||||
echo making start scripts
|
||||
@echo dotnet IW4MAdmin.dll > "%SolutionDir%Publish\WindowsPrerelease\StartIW4MAdmin.cmd"
|
||||
@echo dotnet IW4MAdmin.dll > "%SolutionDir%Publish\Windows\StartIW4MAdmin.cmd"
|
||||
echo setting up library folders
|
||||
|
||||
@(echo #!/bin/bash && echo dotnet IW4MAdmin.dll) > "%SolutionDir%Publish\WindowsPrerelease\StartIW4MAdmin.sh"
|
||||
@(echo #!/bin/bash && echo dotnet IW4MAdmin.dll) > "%SolutionDir%Publish\Windows\StartIW4MAdmin.sh"
|
||||
if "%CurrentConfiguration%" == "Prerelease" (
|
||||
echo PR-Config
|
||||
if not exist "%SolutionDir%Publish\WindowsPrerelease\Configuration" md "%SolutionDir%Publish\WindowsPrerelease\Configuration"
|
||||
move "%SolutionDir%Publish\WindowsPrerelease\DefaultSettings.json" "%SolutionDir%Publish\WindowsPrerelease\Configuration\"
|
||||
)
|
||||
|
||||
if "%CurrentConfiguration%" == "Release" (
|
||||
echo R-Config
|
||||
if not exist "%SolutionDir%Publish\Windows\Configuration" md "%SolutionDir%Publish\Windows\Configuration"
|
||||
if exist "%SolutionDir%Publish\Windows\DefaultSettings.json" move "%SolutionDir%Publish\Windows\DefaultSettings.json" "%SolutionDir%Publish\Windows\Configuration\DefaultSettings.json"
|
||||
)
|
||||
|
||||
if "%CurrentConfiguration%" == "Prerelease" (
|
||||
echo PR-LIB
|
||||
if not exist "%SolutionDir%Publish\WindowsPrerelease\Lib\" md "%SolutionDir%Publish\WindowsPrerelease\Lib\"
|
||||
move "%SolutionDir%Publish\WindowsPrerelease\*.dll" "%SolutionDir%Publish\WindowsPrerelease\Lib\"
|
||||
move "%SolutionDir%Publish\WindowsPrerelease\*.json" "%SolutionDir%Publish\WindowsPrerelease\Lib\"
|
||||
)
|
||||
|
||||
if "%CurrentConfiguration%" == "Release" (
|
||||
echo R-LIB
|
||||
if not exist "%SolutionDir%Publish\Windows\Lib\" md "%SolutionDir%Publish\Windows\Lib\"
|
||||
move "%SolutionDir%Publish\Windows\*.dll" "%SolutionDir%Publish\Windows\Lib\"
|
||||
move "%SolutionDir%Publish\Windows\*.json" "%SolutionDir%Publish\Windows\Lib\"
|
||||
)
|
||||
|
||||
if "%CurrentConfiguration%" == "Prerelease" (
|
||||
echo PR-RT
|
||||
move "%SolutionDir%Publish\WindowsPrerelease\runtimes" "%SolutionDir%Publish\WindowsPrerelease\Lib\runtimes"
|
||||
if exist "%SolutionDir%Publish\WindowsPrerelease\refs" move "%SolutionDir%Publish\WindowsPrerelease\refs" "%SolutionDir%Publish\WindowsPrerelease\Lib\refs"
|
||||
)
|
||||
|
||||
|
||||
if "%CurrentConfiguration%" == "Release" (
|
||||
echo R-RT
|
||||
move "%SolutionDir%Publish\Windows\runtimes" "%SolutionDir%Publish\Windows\Lib\runtimes"
|
||||
if exist "%SolutionDir%Publish\Windows\refs" move "%SolutionDir%Publish\Windows\refs" "%SolutionDir%Publish\Windows\Lib\refs"
|
||||
)
|
||||
|
||||
echo making start scripts
|
||||
@(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 #!/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 "%CurrentConfiguration%"
|
||||
|
@ -67,7 +67,7 @@ namespace IW4MAdmin.Application.Core
|
||||
{
|
||||
if (AuthenticatedClients.Values.Count > 18)
|
||||
{
|
||||
Program.ServerManager.GetLogger().WriteWarning($"auth client count is {AuthenticatedClients.Values.Count}, this is bad");
|
||||
Program.ServerManager.GetLogger(0).WriteError($"auth client count is {AuthenticatedClients.Values.Count}, this is bad");
|
||||
return AuthenticatedClients.Values.Take(18).ToList();
|
||||
}
|
||||
|
||||
|
@ -25,11 +25,6 @@
|
||||
"Name": "mp_rust"
|
||||
},
|
||||
|
||||
{
|
||||
"Alias": "Highrise",
|
||||
"Name": "mp_highrise"
|
||||
},
|
||||
|
||||
{
|
||||
"Alias": "Terminal",
|
||||
"Name": "mp_terminal"
|
||||
@ -40,16 +35,6 @@
|
||||
"Name": "mp_crash"
|
||||
},
|
||||
|
||||
{
|
||||
"Alias": "Skidrow",
|
||||
"Name": "mp_nightshift"
|
||||
},
|
||||
|
||||
{
|
||||
"Alias": "Quarry",
|
||||
"Name": "mp_quarry"
|
||||
},
|
||||
|
||||
{
|
||||
"Alias": "Afghan",
|
||||
"Name": "mp_afghan"
|
||||
|
@ -55,9 +55,9 @@ namespace IW4MAdmin.Application.IO
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
Program.ServerManager.GetLogger().WriteWarning("Could not properly parse event line");
|
||||
Program.ServerManager.GetLogger().WriteDebug(e.Message);
|
||||
Program.ServerManager.GetLogger().WriteDebug(eventLine);
|
||||
server.Logger.WriteWarning("Could not properly parse event line");
|
||||
server.Logger.WriteDebug(e.Message);
|
||||
server.Logger.WriteDebug(eventLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,9 +59,9 @@ namespace IW4MAdmin.Application.IO
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
Program.ServerManager.GetLogger().WriteWarning("Could not properly parse event line");
|
||||
Program.ServerManager.GetLogger().WriteDebug(e.Message);
|
||||
Program.ServerManager.GetLogger().WriteDebug(eventLine);
|
||||
server.Logger.WriteWarning("Could not properly parse event line");
|
||||
server.Logger.WriteDebug(e.Message);
|
||||
server.Logger.WriteDebug(eventLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ namespace IW4MAdmin.Application.Localization
|
||||
public static void Initialize(string customLocale)
|
||||
{
|
||||
string currentLocale = string.IsNullOrEmpty(customLocale) ? CultureInfo.CurrentCulture.Name : customLocale;
|
||||
string[] localizationFiles = Directory.GetFiles("Localization", $"*.{currentLocale}.json");
|
||||
string[] localizationFiles = Directory.GetFiles(Path.Join(Utilities.OperatingDirectory, "Localization"), $"*.{currentLocale}.json");
|
||||
|
||||
try
|
||||
{
|
||||
@ -33,13 +33,13 @@ namespace IW4MAdmin.Application.Localization
|
||||
// culture doesn't exist so we just want language
|
||||
if (localizationFiles.Length == 0)
|
||||
{
|
||||
localizationFiles = Directory.GetFiles("Localization", $"*.{currentLocale.Substring(0, 2)}*.json");
|
||||
localizationFiles = Directory.GetFiles(Path.Join(Utilities.OperatingDirectory, "Localization"), $"*.{currentLocale.Substring(0, 2)}*.json");
|
||||
}
|
||||
|
||||
// language doesn't exist either so defaulting to english
|
||||
if (localizationFiles.Length == 0)
|
||||
{
|
||||
localizationFiles = Directory.GetFiles("Localization", "*.en-US.json");
|
||||
localizationFiles = Directory.GetFiles(Path.Join(Utilities.OperatingDirectory, "Localization"), "*.en-US.json");
|
||||
}
|
||||
|
||||
// this should never happen unless the localization folder is empty
|
||||
@ -59,12 +59,12 @@ namespace IW4MAdmin.Application.Localization
|
||||
{
|
||||
if (!localizationDict.TryAdd(item.Key, item.Value))
|
||||
{
|
||||
Program.ServerManager.GetLogger().WriteError($"Could not add locale string {item.Key} to localization");
|
||||
Program.ServerManager.GetLogger(0).WriteError($"Could not add locale string {item.Key} to localization");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string localizationFile = $"Localization{Path.DirectorySeparatorChar}IW4MAdmin.{currentLocale}-{currentLocale.ToUpper()}.json";
|
||||
string localizationFile = $"{Path.Join(Utilities.OperatingDirectory, "Localization")}{Path.DirectorySeparatorChar}IW4MAdmin.{currentLocale}-{currentLocale.ToUpper()}.json";
|
||||
|
||||
Utilities.CurrentLocalization = new SharedLibraryCore.Localization.Layout(localizationDict)
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace IW4MAdmin.Application
|
||||
{
|
||||
@ -18,18 +19,44 @@ namespace IW4MAdmin.Application
|
||||
}
|
||||
|
||||
readonly string FileName;
|
||||
readonly object ThreadLock;
|
||||
readonly SemaphoreSlim OnLogWriting;
|
||||
static readonly short MAX_LOG_FILES = 10;
|
||||
|
||||
public Logger(string fn)
|
||||
{
|
||||
FileName = fn;
|
||||
ThreadLock = new object();
|
||||
if (File.Exists(fn))
|
||||
File.Delete(fn);
|
||||
FileName = Path.Join(Utilities.OperatingDirectory, "Log", $"{fn}.log");
|
||||
OnLogWriting = new SemaphoreSlim(1, 1);
|
||||
RotateLogs();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// rotates logs when log is initialized
|
||||
/// </summary>
|
||||
private void RotateLogs()
|
||||
{
|
||||
string maxLog = FileName + MAX_LOG_FILES;
|
||||
|
||||
if (File.Exists(maxLog))
|
||||
{
|
||||
File.Delete(maxLog);
|
||||
}
|
||||
|
||||
for (int i = MAX_LOG_FILES - 1; i >= 0; i--)
|
||||
{
|
||||
string logToMove = i == 0 ? FileName : FileName + i;
|
||||
string movedLogName = FileName + (i + 1);
|
||||
|
||||
if (File.Exists(logToMove))
|
||||
{
|
||||
File.Move(logToMove, movedLogName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Write(string msg, LogType type)
|
||||
{
|
||||
OnLogWriting.Wait();
|
||||
|
||||
string stringType = type.ToString();
|
||||
|
||||
try
|
||||
@ -40,11 +67,10 @@ namespace IW4MAdmin.Application
|
||||
catch (Exception) { }
|
||||
|
||||
string LogLine = $"[{DateTime.Now.ToString("MM.dd.yyy HH:mm:ss.fff")}] - {stringType}: {msg}";
|
||||
lock (ThreadLock)
|
||||
try
|
||||
{
|
||||
#if DEBUG
|
||||
// lets keep it simple and dispose of everything quickly as logging wont be that much (relatively)
|
||||
|
||||
Console.WriteLine(LogLine);
|
||||
File.AppendAllText(FileName, LogLine + Environment.NewLine);
|
||||
#else
|
||||
@ -54,6 +80,14 @@ namespace IW4MAdmin.Application
|
||||
File.AppendAllText(FileName, $"{LogLine}{Environment.NewLine}");
|
||||
#endif
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Well.. It looks like your machine can't event write to the log file. That's something else...");
|
||||
Console.WriteLine(ex.GetExceptionInfo());
|
||||
}
|
||||
|
||||
OnLogWriting.Release(1);
|
||||
}
|
||||
|
||||
public void WriteVerbose(string msg)
|
||||
|
@ -10,19 +10,19 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using SharedLibraryCore.Localization;
|
||||
using IW4MAdmin.Application.Migration;
|
||||
|
||||
namespace IW4MAdmin.Application
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
static public double Version { get; private set; }
|
||||
static public ApplicationManager ServerManager = ApplicationManager.GetInstance();
|
||||
public static string OperatingDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + Path.DirectorySeparatorChar;
|
||||
static public ApplicationManager ServerManager;
|
||||
private static ManualResetEventSlim OnShutdownComplete = new ManualResetEventSlim();
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
AppDomain.CurrentDomain.SetData("DataDirectory", OperatingDirectory);
|
||||
AppDomain.CurrentDomain.SetData("DataDirectory", Utilities.OperatingDirectory);
|
||||
|
||||
Console.OutputEncoding = Encoding.UTF8;
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
@ -39,12 +39,17 @@ namespace IW4MAdmin.Application
|
||||
|
||||
try
|
||||
{
|
||||
loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||
Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKey);
|
||||
|
||||
CheckDirectories();
|
||||
// do any needed migrations
|
||||
// todo: move out
|
||||
ConfigurationMigration.MoveConfigFolder10518(null);
|
||||
|
||||
ServerManager = ApplicationManager.GetInstance();
|
||||
Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKey);
|
||||
Localization.Configure.Initialize(ServerManager.GetApplicationSettings().Configuration()?.CustomLocale);
|
||||
loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||
|
||||
|
||||
ServerManager.Logger.WriteInfo($"Version is {Version}");
|
||||
|
||||
@ -107,7 +112,7 @@ namespace IW4MAdmin.Application
|
||||
var consoleTask = Task.Run(async () =>
|
||||
{
|
||||
String userInput;
|
||||
Player Origin = Utilities.IW4MAdminClient;
|
||||
Player Origin = Utilities.IW4MAdminClient(ServerManager.Servers[0]);
|
||||
|
||||
do
|
||||
{
|
||||
@ -124,7 +129,6 @@ namespace IW4MAdmin.Application
|
||||
|
||||
if (userInput?.Length > 0)
|
||||
{
|
||||
Origin.CurrentServer = ServerManager.Servers[0];
|
||||
GameEvent E = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Command,
|
||||
@ -149,7 +153,7 @@ namespace IW4MAdmin.Application
|
||||
{
|
||||
e = e.InnerException;
|
||||
}
|
||||
Console.WriteLine($"Exception: {e.Message}");
|
||||
Console.WriteLine(e.Message);
|
||||
Console.WriteLine(loc["MANAGER_EXIT"]);
|
||||
Console.ReadKey();
|
||||
return;
|
||||
@ -169,15 +173,25 @@ namespace IW4MAdmin.Application
|
||||
private static void OnCancelKey(object sender, ConsoleCancelEventArgs e)
|
||||
{
|
||||
ServerManager.Stop();
|
||||
OnShutdownComplete.Wait(5000);
|
||||
OnShutdownComplete.Wait();
|
||||
}
|
||||
|
||||
static void CheckDirectories()
|
||||
{
|
||||
string curDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + Path.DirectorySeparatorChar;
|
||||
if (!Directory.Exists(Path.Join(Utilities.OperatingDirectory, "Plugins")))
|
||||
{
|
||||
Directory.CreateDirectory(Path.Join(Utilities.OperatingDirectory, "Plugins"));
|
||||
}
|
||||
|
||||
if (!Directory.Exists($"{curDirectory}Plugins"))
|
||||
Directory.CreateDirectory($"{curDirectory}Plugins");
|
||||
if (!Directory.Exists(Path.Join(Utilities.OperatingDirectory, "Database")))
|
||||
{
|
||||
Directory.CreateDirectory(Path.Join(Utilities.OperatingDirectory, "Database"));
|
||||
}
|
||||
|
||||
if (!Directory.Exists(Path.Join(Utilities.OperatingDirectory, "Log")))
|
||||
{
|
||||
Directory.CreateDirectory(Path.Join(Utilities.OperatingDirectory, "Log"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Events;
|
||||
|
||||
using IW4MAdmin.Application.API.Master;
|
||||
using IW4MAdmin.Application.Migration;
|
||||
|
||||
namespace IW4MAdmin.Application
|
||||
{
|
||||
@ -26,7 +27,7 @@ namespace IW4MAdmin.Application
|
||||
private List<Server> _servers;
|
||||
public List<Server> Servers => _servers.OrderByDescending(s => s.ClientNum).ToList();
|
||||
public Dictionary<int, Player> PrivilegedClients { get; set; }
|
||||
public ILogger Logger { get; private set; }
|
||||
public ILogger Logger => GetLogger(0);
|
||||
public bool Running { get; private set; }
|
||||
public bool IsInitialized { get; private set; }
|
||||
// define what the delagate function looks like
|
||||
@ -48,10 +49,10 @@ namespace IW4MAdmin.Application
|
||||
ManualResetEventSlim OnQuit;
|
||||
readonly IPageList PageList;
|
||||
readonly SemaphoreSlim ProcessingEvent = new SemaphoreSlim(1, 1);
|
||||
readonly Dictionary<int, ILogger> Loggers = new Dictionary<int, ILogger>();
|
||||
|
||||
private ApplicationManager()
|
||||
{
|
||||
Logger = new Logger($@"{Utilities.OperatingDirectory}IW4MAdmin.log");
|
||||
_servers = new List<Server>();
|
||||
Commands = new List<Command>();
|
||||
TaskStatuses = new List<AsyncStatus>();
|
||||
@ -379,11 +380,10 @@ namespace IW4MAdmin.Application
|
||||
await Plugin.OnLoadAsync(this);
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_PLUGIN"]} {Plugin.Name}");
|
||||
Logger.WriteDebug($"Exception: {e.Message}");
|
||||
Logger.WriteDebug($"Stack Trace: {e.StackTrace}");
|
||||
Logger.WriteDebug(ex.GetExceptionInfo());
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
@ -565,9 +565,29 @@ namespace IW4MAdmin.Application
|
||||
Running = false;
|
||||
}
|
||||
|
||||
public ILogger GetLogger()
|
||||
public ILogger GetLogger(int serverId)
|
||||
{
|
||||
return Logger;
|
||||
if (Loggers.ContainsKey(serverId))
|
||||
{
|
||||
return Loggers[serverId];
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Logger newLogger;
|
||||
|
||||
if (serverId == 0)
|
||||
{
|
||||
newLogger = new Logger("IW4MAdmin-Manager");
|
||||
}
|
||||
else
|
||||
{
|
||||
newLogger = new Logger($"IW4MAdmin-Server-{serverId}");
|
||||
}
|
||||
|
||||
Loggers.Add(serverId, newLogger);
|
||||
return newLogger;
|
||||
}
|
||||
}
|
||||
|
||||
public IList<MessageToken> GetMessageTokens()
|
||||
|
60
Application/Migration/ConfigurationMigration.cs
Normal file
60
Application/Migration/ConfigurationMigration.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace IW4MAdmin.Application.Migration
|
||||
{
|
||||
/// <summary>
|
||||
/// helps facilitate the migration of configs from one version and or location
|
||||
/// to another
|
||||
/// </summary>
|
||||
class ConfigurationMigration
|
||||
{
|
||||
/// <summary>
|
||||
/// moves existing configs from the root folder into a configs folder
|
||||
/// </summary>
|
||||
public static void MoveConfigFolder10518(ILogger log)
|
||||
{
|
||||
string currentDirectory = Utilities.OperatingDirectory;
|
||||
|
||||
// we don't want to do this for migrations or tests where the
|
||||
// property isn't initialized or it's wrong
|
||||
if (currentDirectory != null)
|
||||
{
|
||||
string configDirectory = Path.Join(currentDirectory, "Configuration");
|
||||
|
||||
if (!Directory.Exists(configDirectory))
|
||||
{
|
||||
log?.WriteDebug($"Creating directory for configs {configDirectory}");
|
||||
Directory.CreateDirectory(configDirectory);
|
||||
}
|
||||
|
||||
var configurationFiles = Directory.EnumerateFiles(currentDirectory, "*.json")
|
||||
.Select(f => f.Split(Path.DirectorySeparatorChar).Last())
|
||||
.Where(f => f.Count(c => c == '.') == 1);
|
||||
|
||||
foreach (var configFile in configurationFiles)
|
||||
{
|
||||
log?.WriteDebug($"Moving config file {configFile}");
|
||||
string destinationPath = Path.Join("Configuration", configFile);
|
||||
if (!File.Exists(destinationPath))
|
||||
{
|
||||
File.Move(configFile, destinationPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!File.Exists(Path.Join("Database", "Database.db")) &&
|
||||
File.Exists("Database.db"))
|
||||
{
|
||||
log?.WriteDebug("Moving database file");
|
||||
File.Move("Database.db", Path.Join("Database", "Database.db"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,8 +9,6 @@ using SharedLibraryCore.Objects;
|
||||
using SharedLibraryCore.RCon;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace IW4MAdmin.Application.RconParsers
|
||||
{
|
||||
|
@ -32,6 +32,8 @@ namespace IW4MAdmin
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
if (GameName == Game.IW4)
|
||||
{
|
||||
// todo: make this better with collisions
|
||||
int id = Math.Abs($"{IP}:{Port.ToString()}".Select(a => (int)a).Sum());
|
||||
@ -50,6 +52,13 @@ namespace IW4MAdmin
|
||||
return id;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
int id = HashCode.Combine(IP, Port);
|
||||
return id < 0 ? Math.Abs(id) : id;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task OnPlayerJoined(Player logClient)
|
||||
{
|
||||
var existingClient = Players[logClient.ClientNumber];
|
||||
@ -201,12 +210,11 @@ namespace IW4MAdmin
|
||||
{
|
||||
player.Level = Player.Permission.User;
|
||||
}
|
||||
#if DEBUG == false
|
||||
|
||||
if (currentBan != null)
|
||||
{
|
||||
Logger.WriteInfo($"Banned client {player} trying to connect...");
|
||||
var autoKickClient = Utilities.IW4MAdminClient;
|
||||
autoKickClient.CurrentServer = this;
|
||||
var autoKickClient = Utilities.IW4MAdminClient(this);
|
||||
|
||||
// the player is permanently banned
|
||||
if (currentBan.Type == Penalty.PenaltyType.Ban)
|
||||
@ -243,7 +251,6 @@ namespace IW4MAdmin
|
||||
Players[player.ClientNumber] = null;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
player.State = Player.ClientState.Connected;
|
||||
return true;
|
||||
@ -251,9 +258,9 @@ namespace IW4MAdmin
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
Manager.GetLogger().WriteError($"{loc["SERVER_ERROR_ADDPLAYER"]} {polledPlayer.Name}::{polledPlayer.NetworkId}");
|
||||
Manager.GetLogger().WriteDebug(ex.Message);
|
||||
Manager.GetLogger().WriteDebug(ex.StackTrace);
|
||||
Logger.WriteError($"{loc["SERVER_ERROR_ADDPLAYER"]} {polledPlayer.Name}::{polledPlayer.NetworkId}");
|
||||
Logger.WriteDebug(ex.Message);
|
||||
Logger.WriteDebug(ex.StackTrace);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -335,7 +342,7 @@ namespace IW4MAdmin
|
||||
(canExecuteCommand ||
|
||||
E.Origin?.Level == Player.Permission.Console))
|
||||
{
|
||||
var _ = (((Command)E.Extra).ExecuteAsync(E));
|
||||
await (((Command)E.Extra).ExecuteAsync(E));
|
||||
}
|
||||
}
|
||||
|
||||
@ -432,6 +439,11 @@ namespace IW4MAdmin
|
||||
await Kick(E.Data, E.Target, E.Origin);
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Warn)
|
||||
{
|
||||
await Warn(E.Data, E.Target, E.Origin);
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Quit)
|
||||
{
|
||||
var origin = Players.FirstOrDefault(p => p != null && p.NetworkId == E.Origin.NetworkId);
|
||||
@ -871,7 +883,7 @@ namespace IW4MAdmin
|
||||
Logger.WriteError($"{logPath} {loc["SERVER_ERROR_DNE"]}");
|
||||
#if !DEBUG
|
||||
throw new ServerException($"{loc["SERVER_ERROR_LOG"]} {logPath}");
|
||||
//#else
|
||||
#else
|
||||
LogEvent = new GameLogEventDetection(this, logPath, logfile.Value);
|
||||
#endif
|
||||
}
|
||||
@ -888,7 +900,7 @@ namespace IW4MAdmin
|
||||
#endif
|
||||
}
|
||||
|
||||
public override async Task Warn(String Reason, Player Target, Player Origin)
|
||||
protected override async Task Warn(String Reason, Player Target, Player Origin)
|
||||
{
|
||||
// ensure player gets warned if command not performed on them in game
|
||||
if (Target.ClientNumber < 0)
|
||||
@ -907,7 +919,7 @@ namespace IW4MAdmin
|
||||
{
|
||||
if (Target.Warnings >= 4)
|
||||
{
|
||||
Target.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient);
|
||||
Target.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient(this));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -963,7 +975,6 @@ namespace IW4MAdmin
|
||||
Offender = Target,
|
||||
Offense = Reason,
|
||||
Punisher = Origin,
|
||||
Active = true,
|
||||
When = DateTime.UtcNow,
|
||||
Link = Target.AliasLink
|
||||
};
|
||||
|
@ -11,6 +11,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
_customcallbacks.gsc = _customcallbacks.gsc
|
||||
README.md = README.md
|
||||
RunPublishPre.cmd = RunPublishPre.cmd
|
||||
RunPublishRelease.cmd = RunPublishRelease.cmd
|
||||
version.txt = version.txt
|
||||
EndProjectSection
|
||||
EndProject
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
@ -17,7 +18,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.NETCore.App"/>
|
||||
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
<PackageId>RaidMax.IW4MAdmin.Plugins.Login</PackageId>
|
||||
@ -21,7 +22,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.NETCore.App"/>
|
||||
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -25,7 +25,7 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
||||
public Task OnEventAsync(GameEvent E, Server S)
|
||||
{
|
||||
if (!Settings.Configuration().EnableProfanityDeterment)
|
||||
return Task.CompletedTask; ;
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (E.Type == GameEvent.EventType.Connect)
|
||||
{
|
||||
@ -50,7 +50,8 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
||||
{
|
||||
E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, new Player()
|
||||
{
|
||||
ClientId = 1
|
||||
ClientId = 1,
|
||||
CurrentServer = E.Owner
|
||||
});
|
||||
};
|
||||
}
|
||||
@ -86,7 +87,8 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
||||
{
|
||||
clientProfanity.Client.Kick(Settings.Configuration().ProfanityKickMessage, new Player()
|
||||
{
|
||||
ClientId = 1
|
||||
ClientId = 1,
|
||||
CurrentServer = E.Owner
|
||||
});
|
||||
}
|
||||
|
||||
@ -96,7 +98,8 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
||||
|
||||
clientProfanity.Client.Warn(Settings.Configuration().ProfanityWarningMessage, new Player()
|
||||
{
|
||||
ClientId = 1
|
||||
ClientId = 1,
|
||||
CurrentServer = E.Owner
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
<PackageId>RaidMax.IW4MAdmin.Plugins.ProfanityDeterment</PackageId>
|
||||
@ -19,7 +20,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.NETCore.App"/>
|
||||
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -1,6 +1,6 @@
|
||||
var plugin = {
|
||||
author: 'RaidMax',
|
||||
version: 1.0,
|
||||
version: 1.1,
|
||||
name: 'Shared GUID Kicker Plugin',
|
||||
|
||||
onEventAsync: function (gameEvent, server) {
|
||||
@ -10,8 +10,7 @@ var plugin = {
|
||||
}
|
||||
|
||||
// connect or join event
|
||||
if (gameEvent.Type === 3 ||
|
||||
gameEvent.Type === 4) {
|
||||
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);
|
||||
|
@ -28,7 +28,6 @@ var plugin = {
|
||||
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);
|
||||
// todo: does this work as expected now?
|
||||
co.Dispose();
|
||||
re.Dispose();
|
||||
cl.Dispose();
|
||||
@ -39,10 +38,7 @@ var plugin = {
|
||||
|
||||
if (usingVPN) {
|
||||
this.logger.WriteInfo(origin + ' is using a VPN (' + origin.IPAddressString + ')');
|
||||
var library = importNamespace('SharedLibraryCore');
|
||||
var kickOrigin = new library.Objects.Player();
|
||||
kickOrigin.ClientId = 1;
|
||||
origin.Kick(_localization.LocalizationIndex["SERVER_KICK_VPNS_NOTALLOWED"], kickOrigin);
|
||||
origin.Kick(_localization.LocalizationIndex["SERVER_KICK_VPNS_NOTALLOWED"], _IW4MAdminClient);
|
||||
}
|
||||
},
|
||||
|
||||
@ -55,7 +51,7 @@ var plugin = {
|
||||
|
||||
onLoadAsync: function (manager) {
|
||||
this.manager = manager;
|
||||
this.logger = manager.GetLogger();
|
||||
this.logger = manager.GetLogger(0);
|
||||
},
|
||||
|
||||
onUnloadAsync: function () {
|
||||
|
@ -26,8 +26,8 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
Dictionary<IW4Info.HitLocation, int> HitLocationCount;
|
||||
double AngleDifferenceAverage;
|
||||
EFClientStatistics ClientStats;
|
||||
DateTime LastHit;
|
||||
long LastOffset;
|
||||
IW4Info.WeaponName LastWeapon;
|
||||
ILogger Log;
|
||||
Strain Strain;
|
||||
readonly DateTime ConnectionTime = DateTime.UtcNow;
|
||||
@ -53,16 +53,16 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
if ((kill.DeathType != IW4Info.MeansOfDeath.MOD_PISTOL_BULLET &&
|
||||
kill.DeathType != IW4Info.MeansOfDeath.MOD_RIFLE_BULLET &&
|
||||
kill.DeathType != IW4Info.MeansOfDeath.MOD_HEAD_SHOT) ||
|
||||
kill.HitLoc == IW4Info.HitLocation.none || kill.TimeOffset - LastOffset < 0)
|
||||
kill.HitLoc == IW4Info.HitLocation.none || kill.TimeOffset - LastOffset < 0 ||
|
||||
// hack: prevents false positives
|
||||
(LastWeapon != kill.Weapon && (kill.TimeOffset - LastOffset) == 50))
|
||||
return new DetectionPenaltyResult()
|
||||
{
|
||||
ClientPenalty = Penalty.PenaltyType.Any,
|
||||
};
|
||||
|
||||
DetectionPenaltyResult result = null;
|
||||
|
||||
if (LastHit == DateTime.MinValue)
|
||||
LastHit = DateTime.UtcNow;
|
||||
LastWeapon = kill.Weapon;
|
||||
|
||||
HitLocationCount[kill.HitLoc]++;
|
||||
if (!isDamage)
|
||||
|
@ -6,6 +6,8 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharedLibraryCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
{
|
||||
@ -17,23 +19,30 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
{
|
||||
if (E.Origin.ClientNumber >= 0)
|
||||
{
|
||||
var svc = new SharedLibraryCore.Services.GenericRepository<EFClientStatistics>();
|
||||
int serverId = E.Owner.GetHashCode();
|
||||
var stats = svc.Find(s => s.ClientId == E.Origin.ClientId && s.ServerId == serverId).First();
|
||||
|
||||
stats.Deaths = 0;
|
||||
stats.Kills = 0;
|
||||
stats.SPM = 0.0;
|
||||
stats.Skill = 0.0;
|
||||
stats.TimePlayed = 0;
|
||||
int serverId = E.Owner.GetHashCode();
|
||||
|
||||
EFClientStatistics clientStats;
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
clientStats = await ctx.Set<EFClientStatistics>()
|
||||
.Where(s => s.ClientId == E.Origin.ClientId && s.ServerId == serverId)
|
||||
.FirstAsync();
|
||||
|
||||
clientStats.Deaths = 0;
|
||||
clientStats.Kills = 0;
|
||||
clientStats.SPM = 0.0;
|
||||
clientStats.Skill = 0.0;
|
||||
clientStats.TimePlayed = 0;
|
||||
// todo: make this more dynamic
|
||||
stats.EloRating = 200.0;
|
||||
clientStats.EloRating = 200.0;
|
||||
|
||||
// reset the cached version
|
||||
Plugin.Manager.ResetStats(E.Origin.ClientId, E.Owner.GetHashCode());
|
||||
|
||||
// fixme: this doesn't work properly when another context exists
|
||||
await svc.SaveChangesAsync();
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_RESET_SUCCESS"]);
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,8 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharedLibraryCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
{
|
||||
@ -39,20 +41,22 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
}
|
||||
}
|
||||
|
||||
var clientStats = new GenericRepository<EFClientStatistics>();
|
||||
int serverId = E.Owner.GetHashCode();
|
||||
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
if (E.Target != null)
|
||||
{
|
||||
pStats = (await clientStats.FindAsync(c => c.ServerId == serverId && c.ClientId == E.Target.ClientId)).First();
|
||||
pStats = (await ctx.Set<EFClientStatistics>().FirstAsync(c => c.ServerId == serverId && c.ClientId == E.Target.ClientId));
|
||||
statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()}";
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
pStats = (await clientStats.FindAsync(c => c.ServerId == serverId && c.ClientId == E.Origin.ClientId)).First();
|
||||
pStats = (await ctx.Set<EFClientStatistics>().FirstAsync((c => c.ServerId == serverId && c.ClientId == E.Origin.ClientId)));
|
||||
statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()}";
|
||||
}
|
||||
}
|
||||
|
||||
if (E.Message.IsBroadcastCommand())
|
||||
{
|
||||
|
@ -24,20 +24,20 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
public class StatManager
|
||||
{
|
||||
private ConcurrentDictionary<int, ServerStats> Servers;
|
||||
private ConcurrentDictionary<int, ThreadSafeStatsService> ContextThreads;
|
||||
private ILogger Log;
|
||||
private IManager Manager;
|
||||
private readonly IManager Manager;
|
||||
|
||||
|
||||
private readonly SemaphoreSlim OnProcessingPenalty;
|
||||
private readonly SemaphoreSlim OnProcessingSensitive;
|
||||
|
||||
public StatManager(IManager mgr)
|
||||
{
|
||||
Servers = new ConcurrentDictionary<int, ServerStats>();
|
||||
ContextThreads = new ConcurrentDictionary<int, ThreadSafeStatsService>();
|
||||
Log = mgr.GetLogger();
|
||||
Log = mgr.GetLogger(0);
|
||||
Manager = mgr;
|
||||
OnProcessingPenalty = new SemaphoreSlim(1, 1);
|
||||
OnProcessingSensitive = new SemaphoreSlim(1, 1);
|
||||
}
|
||||
|
||||
public EFClientStatistics GetClientStats(int clientId, int serverId) => Servers[serverId].PlayerStats[clientId];
|
||||
@ -188,14 +188,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
/// <param name="sv"></param>
|
||||
public void AddServer(Server sv)
|
||||
{
|
||||
// insert the server if it does not exist
|
||||
try
|
||||
{
|
||||
int serverId = sv.GetHashCode();
|
||||
var statsSvc = new ThreadSafeStatsService();
|
||||
ContextThreads.TryAdd(serverId, statsSvc);
|
||||
EFServer server;
|
||||
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
var serverSet = ctx.Set<EFServer>();
|
||||
// get the server from the database if it exists, otherwise create and insert a new one
|
||||
var server = statsSvc.ServerSvc.Find(c => c.ServerId == serverId).FirstOrDefault();
|
||||
server = serverSet.FirstOrDefault(c => c.ServerId == serverId);
|
||||
|
||||
if (server == null)
|
||||
{
|
||||
server = new EFServer()
|
||||
@ -205,16 +209,15 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
ServerId = serverId
|
||||
};
|
||||
|
||||
statsSvc.ServerSvc.Insert(server);
|
||||
server = serverSet.Add(server).Entity;
|
||||
// this doesn't need to be async as it's during initialization
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
// this doesn't need to be async as it's during initialization
|
||||
statsSvc.ServerSvc.SaveChanges();
|
||||
// check to see if the stats have ever been initialized
|
||||
InitializeServerStats(sv);
|
||||
statsSvc.ServerStatsSvc.SaveChanges();
|
||||
var serverStats = InitializeServerStats(sv);
|
||||
|
||||
var serverStats = statsSvc.ServerStatsSvc.Find(c => c.ServerId == serverId).FirstOrDefault();
|
||||
Servers.TryAdd(serverId, new ServerStats(server, serverStats)
|
||||
{
|
||||
IsTeamBased = sv.Gametype != "dm"
|
||||
@ -233,9 +236,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
/// <param name="pl">Player to add/retrieve stats for</param>
|
||||
/// <returns>EFClientStatistic of specified player</returns>
|
||||
public async Task<EFClientStatistics> AddPlayer(Player pl)
|
||||
{
|
||||
await OnProcessingSensitive.WaitAsync();
|
||||
|
||||
try
|
||||
{
|
||||
int serverId = pl.CurrentServer.GetHashCode();
|
||||
|
||||
|
||||
if (!Servers.ContainsKey(serverId))
|
||||
{
|
||||
Log.WriteError($"[Stats::AddPlayer] Server with id {serverId} could not be found");
|
||||
@ -244,18 +252,24 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
var playerStats = Servers[serverId].PlayerStats;
|
||||
var detectionStats = Servers[serverId].PlayerDetections;
|
||||
var statsSvc = ContextThreads[serverId];
|
||||
|
||||
if (playerStats.ContainsKey(pl.ClientId))
|
||||
{
|
||||
Log.WriteWarning($"Duplicate ClientId in stats {pl.ClientId}");
|
||||
return null;
|
||||
return playerStats[pl.ClientId];
|
||||
}
|
||||
|
||||
// get the client's stats from the database if it exists, otherwise create and attach a new one
|
||||
// if this fails we want to throw an exception
|
||||
var clientStatsSvc = statsSvc.ClientStatSvc;
|
||||
var clientStats = clientStatsSvc.Find(c => c.ClientId == pl.ClientId && c.ServerId == serverId).FirstOrDefault();
|
||||
|
||||
EFClientStatistics clientStats;
|
||||
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
var clientStatsSet = ctx.Set<EFClientStatistics>();
|
||||
clientStats = clientStatsSet
|
||||
.Include(cl => cl.HitLocations)
|
||||
.FirstOrDefault(c => c.ClientId == pl.ClientId && c.ServerId == serverId);
|
||||
|
||||
if (clientStats == null)
|
||||
{
|
||||
@ -278,21 +292,38 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
};
|
||||
|
||||
// insert if they've not been added
|
||||
clientStats = clientStatsSvc.Insert(clientStats);
|
||||
await clientStatsSvc.SaveChangesAsync();
|
||||
clientStats = clientStatsSet.Add(clientStats).Entity;
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
if (!playerStats.TryAdd(clientStats.ClientId, clientStats))
|
||||
{
|
||||
Log.WriteWarning("Adding new client to stats failed");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (!playerStats.TryAdd(clientStats.ClientId, clientStats))
|
||||
{
|
||||
Log.WriteWarning("Adding pre-existing client to stats failed");
|
||||
}
|
||||
}
|
||||
|
||||
// migration for previous existing stats
|
||||
if (clientStats.HitLocations.Count == 0)
|
||||
{
|
||||
clientStats.HitLocations = Enum.GetValues(typeof(IW4Info.HitLocation)).OfType<IW4Info.HitLocation>().Select(hl => new EFHitLocationCount()
|
||||
clientStats.HitLocations = Enum.GetValues(typeof(IW4Info.HitLocation)).OfType<IW4Info.HitLocation>()
|
||||
.Select(hl => new EFHitLocationCount()
|
||||
{
|
||||
Active = true,
|
||||
HitCount = 0,
|
||||
Location = hl
|
||||
})
|
||||
.ToList();
|
||||
await statsSvc.ClientStatSvc.SaveChangesAsync();
|
||||
|
||||
ctx.Update(clientStats);
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// for stats before rating
|
||||
@ -312,17 +343,31 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
clientStats.SessionScore = pl.Score;
|
||||
clientStats.LastScore = pl.Score;
|
||||
|
||||
Log.WriteInfo($"Adding {pl} to stats");
|
||||
|
||||
if (!playerStats.TryAdd(pl.ClientId, clientStats))
|
||||
Log.WriteDebug($"Could not add client to stats {pl}");
|
||||
|
||||
if (!detectionStats.TryAdd(pl.ClientId, new Cheat.Detection(Log, clientStats)))
|
||||
Log.WriteDebug("Could not add client to detection");
|
||||
{
|
||||
Log.WriteWarning("Could not add client to detection");
|
||||
}
|
||||
|
||||
Log.WriteInfo($"Adding {pl} to stats");
|
||||
}
|
||||
|
||||
return clientStats;
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.WriteWarning("Could not add client to stats");
|
||||
Log.WriteDebug(ex.GetExceptionInfo());
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
OnProcessingSensitive.Release(1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform stat updates for disconnecting client
|
||||
/// </summary>
|
||||
@ -336,7 +381,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
var playerStats = Servers[serverId].PlayerStats;
|
||||
var detectionStats = Servers[serverId].PlayerDetections;
|
||||
var serverStats = Servers[serverId].ServerStatistics;
|
||||
var statsSvc = ContextThreads[serverId];
|
||||
|
||||
if (!playerStats.ContainsKey(pl.ClientId))
|
||||
{
|
||||
@ -355,10 +399,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue4);
|
||||
|
||||
// sync their stats before they leave
|
||||
var clientStatsSvc = statsSvc.ClientStatSvc;
|
||||
clientStats = UpdateStats(clientStats);
|
||||
clientStatsSvc.Update(clientStats);
|
||||
await clientStatsSvc.SaveChangesAsync();
|
||||
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
ctx.Update(clientStats);
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// increment the total play time
|
||||
serverStats.TotalPlayTime += (int)(DateTime.UtcNow - pl.LastConnection).TotalSeconds;
|
||||
@ -389,7 +436,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads,
|
||||
string fraction, string visibilityPercentage, string snapAngles)
|
||||
{
|
||||
var statsSvc = ContextThreads[serverId];
|
||||
Vector3 vDeathOrigin = null;
|
||||
Vector3 vKillOrigin = null;
|
||||
Vector3 vViewAngles = null;
|
||||
@ -466,10 +512,20 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
return;
|
||||
}
|
||||
|
||||
// incase the add palyer event get delayed
|
||||
if (!Servers[serverId].PlayerStats.ContainsKey(attacker.ClientId))
|
||||
{
|
||||
await AddPlayer(attacker);
|
||||
}
|
||||
|
||||
var clientDetection = Servers[serverId].PlayerDetections[attacker.ClientId];
|
||||
var clientStats = Servers[serverId].PlayerStats[attacker.ClientId];
|
||||
var clientStatsSvc = statsSvc.ClientStatSvc;
|
||||
clientStatsSvc.Update(clientStats);
|
||||
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
ctx.Set<EFClientStatistics>().Update(clientStats);
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// increment their hit count
|
||||
if (hit.DeathType == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET ||
|
||||
@ -492,17 +548,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
if (Plugin.Config.Configuration().EnableAntiCheat)
|
||||
{
|
||||
await ApplyPenalty(clientDetection.ProcessKill(hit, isDamage), clientDetection, attacker, ctx);
|
||||
await ApplyPenalty(clientDetection.ProcessTotalRatio(clientStats), clientDetection, attacker, ctx);
|
||||
ApplyPenalty(clientDetection.ProcessKill(hit, isDamage), clientDetection, attacker, ctx);
|
||||
ApplyPenalty(clientDetection.ProcessTotalRatio(clientStats), clientDetection, attacker, ctx);
|
||||
}
|
||||
|
||||
await clientStatsSvc.SaveChangesAsync();
|
||||
ctx.Set<EFHitLocationCount>().UpdateRange(clientStats.HitLocations);
|
||||
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.WriteError("AC ERROR");
|
||||
Log.WriteError("Could not save hit or AC info");
|
||||
Log.WriteDebug(ex.GetExceptionInfo());
|
||||
}
|
||||
|
||||
@ -510,7 +567,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
async Task ApplyPenalty(Cheat.DetectionPenaltyResult penalty, Cheat.Detection clientDetection, Player attacker, DatabaseContext ctx)
|
||||
void ApplyPenalty(Cheat.DetectionPenaltyResult penalty, Cheat.Detection clientDetection, Player attacker, DatabaseContext ctx)
|
||||
{
|
||||
switch (penalty.ClientPenalty)
|
||||
{
|
||||
@ -530,7 +587,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
$"{penalty.Type}-{(int)penalty.Location}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}" :
|
||||
$"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}",
|
||||
}
|
||||
}
|
||||
},
|
||||
Level = Player.Permission.Console,
|
||||
CurrentServer = attacker.CurrentServer
|
||||
});
|
||||
if (clientDetection.Tracker.HasChanges)
|
||||
{
|
||||
@ -542,30 +601,23 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
break;
|
||||
}
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Data = penalty.Type == Cheat.Detection.DetectionType.Bone ?
|
||||
|
||||
string flagReason = penalty.Type == Cheat.Detection.DetectionType.Bone ?
|
||||
$"{penalty.Type}-{(int)penalty.Location}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}" :
|
||||
$"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}",
|
||||
Origin = new Player()
|
||||
$"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}";
|
||||
|
||||
attacker.Flag(flagReason, new Player()
|
||||
{
|
||||
ClientId = 1,
|
||||
Level = Player.Permission.Console,
|
||||
ClientNumber = -1,
|
||||
CurrentServer = attacker.CurrentServer
|
||||
},
|
||||
Target = attacker,
|
||||
Owner = attacker.CurrentServer,
|
||||
Type = GameEvent.EventType.Flag
|
||||
};
|
||||
// because we created an event it must be processed by the manager
|
||||
// even if it didn't really do anything
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
await new CFlag().ExecuteAsync(e);
|
||||
CurrentServer = attacker.CurrentServer,
|
||||
});
|
||||
|
||||
if (clientDetection.Tracker.HasChanges)
|
||||
{
|
||||
SaveTrackedSnapshots(clientDetection, ctx);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -712,10 +764,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
|
||||
// todo: do we want to save this immediately?
|
||||
var clientStatsSvc = ContextThreads[serverId].ClientStatSvc;
|
||||
clientStatsSvc.Update(attackerStats);
|
||||
clientStatsSvc.Update(victimStats);
|
||||
await clientStatsSvc.SaveChangesAsync();
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
var clientStatsSet = ctx.Set<EFClientStatistics>();
|
||||
|
||||
clientStatsSet.Update(attackerStats);
|
||||
clientStatsSet.Update(victimStats);
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -1048,40 +1104,44 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
return clientStats;
|
||||
}
|
||||
|
||||
public void InitializeServerStats(Server sv)
|
||||
public EFServerStatistics InitializeServerStats(Server sv)
|
||||
{
|
||||
int serverId = sv.GetHashCode();
|
||||
var statsSvc = ContextThreads[serverId];
|
||||
EFServerStatistics serverStats;
|
||||
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
var serverStatsSet = ctx.Set<EFServerStatistics>();
|
||||
serverStats = serverStatsSet.FirstOrDefault(s => s.ServerId == serverId);
|
||||
|
||||
var serverStats = statsSvc.ServerStatsSvc.Find(s => s.ServerId == serverId).FirstOrDefault();
|
||||
if (serverStats == null)
|
||||
{
|
||||
Log.WriteDebug($"Initializing server stats for {sv}");
|
||||
// server stats have never been generated before
|
||||
serverStats = new EFServerStatistics()
|
||||
{
|
||||
Active = true,
|
||||
ServerId = serverId,
|
||||
TotalKills = 0,
|
||||
TotalPlayTime = 0,
|
||||
};
|
||||
|
||||
var ieClientStats = statsSvc.ClientStatSvc.Find(cs => cs.ServerId == serverId);
|
||||
|
||||
// set these incase we've imported settings
|
||||
serverStats.TotalKills = ieClientStats.Sum(cs => cs.Kills);
|
||||
serverStats.TotalPlayTime = Manager.GetClientService().GetTotalPlayTime().Result;
|
||||
|
||||
statsSvc.ServerStatsSvc.Insert(serverStats);
|
||||
serverStats = serverStatsSet.Add(serverStats).Entity;
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
return serverStats;
|
||||
}
|
||||
|
||||
public void ResetKillstreaks(int serverId)
|
||||
{
|
||||
var serverStats = Servers[serverId];
|
||||
|
||||
foreach (var stat in serverStats.PlayerStats.Values)
|
||||
{
|
||||
stat.StartNewSession();
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetStats(int clientId, int serverId)
|
||||
{
|
||||
@ -1100,32 +1160,30 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
if (clientId < 1)
|
||||
return;
|
||||
|
||||
var messageSvc = ContextThreads[serverId].MessageSvc;
|
||||
messageSvc.Insert(new EFClientMessage()
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
ctx.Set<EFClientMessage>().Add(new EFClientMessage()
|
||||
{
|
||||
Active = true,
|
||||
ClientId = clientId,
|
||||
Message = message,
|
||||
ServerId = serverId,
|
||||
TimeSent = DateTime.UtcNow
|
||||
});
|
||||
await messageSvc.SaveChangesAsync();
|
||||
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Sync(Server sv)
|
||||
{
|
||||
int serverId = sv.GetHashCode();
|
||||
var statsSvc = ContextThreads[serverId];
|
||||
|
||||
// Log.WriteDebug("Syncing stats contexts");
|
||||
await statsSvc.ServerStatsSvc.SaveChangesAsync();
|
||||
//await statsSvc.ClientStatSvc.SaveChangesAsync();
|
||||
await statsSvc.KillStatsSvc.SaveChangesAsync();
|
||||
await statsSvc.ServerSvc.SaveChangesAsync();
|
||||
|
||||
statsSvc = null;
|
||||
// this should prevent the gunk from having a long lasting context.
|
||||
ContextThreads[serverId] = new ThreadSafeStatsService();
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
var serverSet = ctx.Set<EFServer>();
|
||||
serverSet.Update(Servers[serverId].Server);
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTeamBased(int serverId, bool isTeamBased)
|
||||
|
@ -1,38 +0,0 @@
|
||||
using SharedLibraryCore.Services;
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
public class ThreadSafeStatsService
|
||||
{
|
||||
public GenericRepository<EFClientStatistics> ClientStatSvc
|
||||
{
|
||||
get
|
||||
{
|
||||
return new GenericRepository<EFClientStatistics>(true);
|
||||
}
|
||||
}
|
||||
public GenericRepository<EFServer> ServerSvc { get; private set; }
|
||||
public GenericRepository<EFClientKill> KillStatsSvc { get; private set; }
|
||||
public GenericRepository<EFServerStatistics> ServerStatsSvc { get; private set; }
|
||||
public GenericRepository<EFClientMessage> MessageSvc
|
||||
{
|
||||
get
|
||||
{
|
||||
return new GenericRepository<EFClientMessage>();
|
||||
}
|
||||
}
|
||||
|
||||
public ThreadSafeStatsService()
|
||||
{
|
||||
ServerSvc = new GenericRepository<EFServer>();
|
||||
KillStatsSvc = new GenericRepository<EFClientKill>();
|
||||
ServerStatsSvc = new GenericRepository<EFServerStatistics>();
|
||||
}
|
||||
}
|
||||
}
|
@ -23,6 +23,5 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
||||
public int ServerId { get; set; }
|
||||
[ForeignKey("ServerId"), Column(Order = 1)]
|
||||
public EFServer Server { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ using SharedLibraryCore.Services;
|
||||
using IW4MAdmin.Plugins.Stats.Config;
|
||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using SharedLibraryCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats
|
||||
{
|
||||
@ -122,8 +124,11 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
// meta data info
|
||||
async Task<List<ProfileMeta>> getStats(int clientId)
|
||||
{
|
||||
var statsSvc = new GenericRepository<EFClientStatistics>();
|
||||
var clientStats = await statsSvc.FindAsync(c => c.ClientId == clientId);
|
||||
IList<EFClientStatistics> clientStats;
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
clientStats = await ctx.Set<EFClientStatistics>().Where(c => c.ClientId == clientId).ToListAsync();
|
||||
}
|
||||
|
||||
int kills = clientStats.Sum(c => c.Kills);
|
||||
int deaths = clientStats.Sum(c => c.Deaths);
|
||||
@ -170,8 +175,14 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
|
||||
async Task<List<ProfileMeta>> getAnticheatInfo(int clientId)
|
||||
{
|
||||
var statsSvc = new GenericRepository<EFClientStatistics>();
|
||||
var clientStats = await statsSvc.FindAsync(c => c.ClientId == clientId);
|
||||
IList<EFClientStatistics> clientStats;
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
clientStats = await ctx.Set<EFClientStatistics>()
|
||||
.Include(c => c.HitLocations)
|
||||
.Where(c => c.ClientId == clientId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
double headRatio = 0;
|
||||
double chestRatio = 0;
|
||||
@ -231,7 +242,8 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = "Hit Offset Average",
|
||||
Value = $"{Math.Round(((float)hitOffsetAverage), 4)}°",
|
||||
// todo: make sure this is wrapped somewhere else
|
||||
Value = $"{Math.Round(((float)hitOffsetAverage), 4).ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName))}°",
|
||||
Sensitive = true
|
||||
},
|
||||
new ProfileMeta()
|
||||
@ -245,19 +257,24 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
|
||||
async Task<List<ProfileMeta>> getMessages(int clientId)
|
||||
{
|
||||
var messageSvc = new GenericRepository<EFClientMessage>();
|
||||
var messages = await messageSvc.FindAsync(m => m.ClientId == clientId);
|
||||
var messageMeta = messages.Select(m => new ProfileMeta()
|
||||
List<ProfileMeta> messageMeta;
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
var messages = ctx.Set<EFClientMessage>().Where(m => m.ClientId == clientId);
|
||||
|
||||
messageMeta = await messages.Select(m => new ProfileMeta()
|
||||
{
|
||||
Key = "EventMessage",
|
||||
Value = m.Message,
|
||||
When = m.TimeSent,
|
||||
Extra = m.ServerId.ToString()
|
||||
}).ToList();
|
||||
}).ToListAsync();
|
||||
}
|
||||
|
||||
messageMeta.Add(new ProfileMeta()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_MESSAGES"],
|
||||
Value = messages.Count
|
||||
Value = messageMeta.Count
|
||||
});
|
||||
|
||||
return messageMeta;
|
||||
@ -274,16 +291,20 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
|
||||
string totalKills(Server server)
|
||||
{
|
||||
var serverStats = new GenericRepository<EFServerStatistics>();
|
||||
return serverStats.Find(s => s.Active)
|
||||
.Sum(c => c.TotalKills).ToString("#,##0");
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
long kills = ctx.Set<EFServerStatistics>().Where(s => s.Active).Sum(s => s.TotalKills);
|
||||
return kills.ToString("#,##0");
|
||||
}
|
||||
}
|
||||
|
||||
string totalPlayTime(Server server)
|
||||
{
|
||||
var serverStats = new GenericRepository<EFServerStatistics>();
|
||||
return Math.Ceiling((serverStats.GetQuery(s => s.Active)
|
||||
.Sum(c => c.TotalPlayTime) / 3600.0)).ToString("#,##0");
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
long playTime = ctx.Set<EFServerStatistics>().Where(s => s.Active).Sum(s => s.TotalPlayTime);
|
||||
return (playTime / 3600.0).ToString("#,##0");
|
||||
}
|
||||
}
|
||||
|
||||
string topStats(Server s)
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
<PackageId>RaidMax.IW4MAdmin.Plugins.Stats</PackageId>
|
||||
@ -26,7 +27,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.NETCore.App" />
|
||||
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -53,13 +53,15 @@ namespace Tests
|
||||
Assert.False(client == null, "no client found to warn");
|
||||
|
||||
var warnEvent = client.Warn("test warn", new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
warnEvent.OnProcessed.Wait(TestTimeout);
|
||||
warnEvent.OnProcessed.Wait();
|
||||
|
||||
Assert.True(client.Warnings == 1 ||
|
||||
warnEvent.Failed, "warning did not get applied");
|
||||
Assert.True((client.Warnings == 1 ||
|
||||
warnEvent.Failed) &&
|
||||
Manager.GetPenaltyService().GetClientPenaltiesAsync(client.ClientId).Result.Count(p => p.Type == Penalty.PenaltyType.Warning) == 1,
|
||||
"warning did not get applied");
|
||||
|
||||
warnEvent = client.Warn("test warn", new Player() { ClientId = 1, Level = Player.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
warnEvent.OnProcessed.Wait(TestTimeout);
|
||||
warnEvent.OnProcessed.Wait();
|
||||
|
||||
Assert.True(warnEvent.FailReason == GameEvent.EventFailReason.Permission &&
|
||||
client.Warnings == 1, "warning was applied without proper permissions");
|
||||
@ -86,8 +88,17 @@ namespace Tests
|
||||
var client = Manager.Servers.First().GetPlayersAsList().FirstOrDefault();
|
||||
Assert.False(client == null, "no client found to report");
|
||||
|
||||
// fail
|
||||
var player = new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer };
|
||||
player.SetAdditionalProperty("_reportCount", 3);
|
||||
var reportEvent = client.Report("test report", player);
|
||||
reportEvent.OnProcessed.Wait(TestTimeout);
|
||||
|
||||
Assert.True(reportEvent.FailReason == GameEvent.EventFailReason.Throttle &
|
||||
client.CurrentServer.Reports.Count(r => r.Target.NetworkId == client.NetworkId) == 0, $"too many reports were applied [{reportEvent.FailReason.ToString()}]");
|
||||
|
||||
// succeed
|
||||
var reportEvent = client.Report("test report", new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
reportEvent = client.Report("test report", new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
reportEvent.OnProcessed.Wait(TestTimeout);
|
||||
|
||||
Assert.True(!reportEvent.Failed &&
|
||||
|
@ -18,7 +18,9 @@ namespace Tests
|
||||
{
|
||||
File.WriteAllText("test_mp.log", "test_log_file");
|
||||
|
||||
Manager = Program.ServerManager;
|
||||
IW4MAdmin.Application.Localization.Configure.Initialize("en-US");
|
||||
|
||||
Manager = ApplicationManager.GetInstance();
|
||||
|
||||
var config = new ApplicationConfiguration
|
||||
{
|
||||
@ -43,6 +45,7 @@ namespace Tests
|
||||
Manager.ConfigHandler.Set(config);
|
||||
|
||||
Manager.Init().Wait();
|
||||
|
||||
Task.Run(() => Manager.Start());
|
||||
}
|
||||
|
||||
|
@ -188,6 +188,22 @@ namespace Tests
|
||||
|
||||
resetEvent.Wait(5000);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PrintCommands()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine("|Name |Alias|Description |Requires Target|Syntax |Required Level|");
|
||||
sb.AppendLine("|--------------| -----| --------------------------------------------------------| -----------------| -------------| ----------------|");
|
||||
|
||||
foreach (var command in Manager.GetCommands().OrderByDescending(c => c.Permission).ThenBy(c => c.Name))
|
||||
{
|
||||
sb.AppendLine($"|{command.Name}|{command.Alias}|{command.Description}|{command.RequiresTarget}|{command.Syntax.Substring(8).EscapeMarkdown()}|{command.Permission}|");
|
||||
}
|
||||
|
||||
Assert.True(false, sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
@ -22,7 +23,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.NETCore.App" />
|
||||
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -1,142 +0,0 @@
|
||||
/* CountryLookup.cs
|
||||
*
|
||||
* Copyright (C) 2008 MaxMind, Inc. All Rights Reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
using SharedLibraryCore;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
|
||||
namespace CountryLookupProj
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for CountryLookup.
|
||||
/// </summary>
|
||||
public class CountryLookup
|
||||
{
|
||||
private FileStream fileInput;
|
||||
private static long COUNTRY_BEGIN = 16776960;
|
||||
private static string[] countryCode =
|
||||
{ "--","AP","EU","AD","AE","AF","AG","AI","AL","AM","AN","AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO","BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD","CF","CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CX","CY","CZ","DE","DJ","DK","DM","DO","DZ",
|
||||
"EC","EE","EG","EH","ER","ES","ET","FI","FJ","FK","FM","FO","FR","FX","GA","GB","GD","GE","GF","GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY","HK","HM","HN","HR","HT","HU","ID","IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO","JP","KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ",
|
||||
"LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","MG","MH","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS","PT","PW","PY","QA",
|
||||
"RE","RO","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SR","ST","SV","SY","SZ","TC","TD","TF","TG","TH","TJ","TK","TM","TN","TO","TL","TR","TT","TV","TW","TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","YE","YT","RS","ZA","ZM","ME","ZW","A1","A2",
|
||||
"O1","AX","GG","IM","JE","BL","MF"
|
||||
};
|
||||
private static string[] countryName =
|
||||
{"a third world country","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles","Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados","Bangladesh","Belgium",
|
||||
"Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia","Brazil","Bahamas","Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands","Congo, The Democratic Republic of the","Central African Republic","Congo","Switzerland","Cote D'Ivoire",
|
||||
"Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica","Cuba","Cape Verde","Christmas Island","Cyprus","Czech Republic","Germany","Djibouti","Denmark","Dominica","Dominican Republic","Algeria","Ecuador","Estonia","Egypt","Western Sahara","Eritrea","Spain","Ethiopia","Finland","Fiji","Falkland Islands (Malvinas)",
|
||||
"Micronesia, Federated States of","Faroe Islands","France","France, Metropolitan","Gabon","United Kingdom","Grenada","Georgia","French Guiana","Ghana","Gibraltar","Greenland","Gambia","Guinea","Guadeloupe","Equatorial Guinea","Greece","South Georgia and the South Sandwich Islands","Guatemala","Guam","Guinea-Bissau","Guyana",
|
||||
"Hong Kong","Heard Island and McDonald Islands","Honduras","Croatia","Haiti","Hungary","Indonesia","Ireland","Israel","India","British Indian Ocean Territory","Iraq","Iran, Islamic Republic of","Iceland","Italy","Jamaica","Jordan","Japan","Kenya","Kyrgyzstan","Cambodia","Kiribati","Comoros","Saint Kitts and Nevis",
|
||||
"Korea, Democratic People's Republic of","Korea, Republic of","Kuwait","Cayman Islands","Kazakstan","Lao People's Democratic Republic","Lebanon","Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho","Lithuania","Luxembourg","Latvia","Libyan Arab Jamahiriya","Morocco","Monaco","Moldova, Republic of","Madagascar",
|
||||
"Marshall Islands","Macedonia","Mali","Myanmar","Mongolia","Macau","Northern Mariana Islands","Martinique","Mauritania","Montserrat","Malta","Mauritius","Maldives","Malawi","Mexico","Malaysia","Mozambique","Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua","Netherlands",
|
||||
"Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama","Peru","French Polynesia","Papua New Guinea","Philippines","Pakistan","Poland","Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico","Palestinian Territory","Portugal","Palau","Paraguay","Qatar","Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia",
|
||||
"Solomon Islands","Seychelles","Sudan","Sweden","Singapore","Saint Helena","Slovenia","Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino","Senegal","Somalia","Suriname","Sao Tome and Principe","El Salvador","Syrian Arab Republic","Swaziland","Turks and Caicos Islands","Chad","French Southern Territories","Togo",
|
||||
"Thailand","Tajikistan","Tokelau","Turkmenistan","Tunisia","Tonga","Timor-Leste","Turkey","Trinidad and Tobago","Tuvalu","Taiwan","Tanzania, United Republic of","Ukraine","Uganda","United States Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent and the Grenadines",
|
||||
"Venezuela","Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia","South Africa","Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider",
|
||||
"Other","Aland Islands","Guernsey","Isle of Man","Jersey","Saint Barthelemy","Saint Martin"};
|
||||
|
||||
|
||||
public CountryLookup(string fileName)
|
||||
{
|
||||
fileInput = new FileStream(fileName, FileMode.Open, FileAccess.Read);
|
||||
}
|
||||
|
||||
private long AddrToNum(IPAddress addr)
|
||||
{
|
||||
long ipnum = 0;
|
||||
byte[] b = BitConverter.GetBytes((UInt32)addr.ToString().ConvertToIP());
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
long y = b[i];
|
||||
if (y < 0)
|
||||
{
|
||||
y += 256;
|
||||
}
|
||||
ipnum += y << ((3 - i) * 8);
|
||||
}
|
||||
return ipnum;
|
||||
}
|
||||
|
||||
public string LookupCountryCode(IPAddress addr)
|
||||
{
|
||||
return (countryCode[(int)SeekCountry(0, AddrToNum(addr), 31)]);
|
||||
}
|
||||
|
||||
public string LookupCountryName(string str)
|
||||
{
|
||||
IPAddress addr;
|
||||
try
|
||||
{
|
||||
addr = IPAddress.Parse(str);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return "a third world country";
|
||||
}
|
||||
return LookupCountryName(addr);
|
||||
}
|
||||
|
||||
public string LookupCountryName(IPAddress addr)
|
||||
{
|
||||
return (countryName[(int)SeekCountry(0, AddrToNum(addr), 31)]);
|
||||
}
|
||||
|
||||
private long SeekCountry(long offset, long ipnum, int depth)
|
||||
{
|
||||
byte[] buf = new byte[6];
|
||||
long[] x = new long[2];
|
||||
|
||||
fileInput.Seek(6 * offset, 0);
|
||||
fileInput.Read(buf, 0, 6);
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
x[i] = 0;
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
int y = buf[i * 3 + j];
|
||||
if (y < 0)
|
||||
{
|
||||
y += 256;
|
||||
}
|
||||
x[i] += (y << (j * 8));
|
||||
}
|
||||
}
|
||||
|
||||
if ((ipnum & (1 << depth)) > 0)
|
||||
{
|
||||
if (x[1] >= COUNTRY_BEGIN)
|
||||
{
|
||||
return x[1] - COUNTRY_BEGIN;
|
||||
}
|
||||
return SeekCountry(x[1], ipnum, depth - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x[0] >= COUNTRY_BEGIN)
|
||||
{
|
||||
return x[0] - COUNTRY_BEGIN;
|
||||
}
|
||||
return SeekCountry(x[0], ipnum, depth - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
@ -8,6 +8,11 @@ using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Services;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using System.Linq;
|
||||
using SharedLibraryCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Welcome
|
||||
{
|
||||
@ -82,37 +87,66 @@ namespace IW4MAdmin.Plugins.Welcome
|
||||
{
|
||||
Player newPlayer = E.Origin;
|
||||
if (newPlayer.Level >= Player.Permission.Trusted && !E.Origin.Masked)
|
||||
E.Owner.Broadcast(ProcessAnnouncement(Config.Configuration().PrivilegedAnnouncementMessage, newPlayer));
|
||||
E.Owner.Broadcast(await ProcessAnnouncement(Config.Configuration().PrivilegedAnnouncementMessage, newPlayer));
|
||||
|
||||
newPlayer.Tell(ProcessAnnouncement(Config.Configuration().UserWelcomeMessage, newPlayer));
|
||||
newPlayer.Tell(await ProcessAnnouncement(Config.Configuration().UserWelcomeMessage, newPlayer));
|
||||
|
||||
if (newPlayer.Level == Player.Permission.Flagged)
|
||||
{
|
||||
var penalty = await new GenericRepository<EFPenalty>().FindAsync(p => p.OffenderId == newPlayer.ClientId && p.Type == Penalty.PenaltyType.Flag);
|
||||
E.Owner.ToAdmins($"^1NOTICE: ^7Flagged player ^5{newPlayer.Name} ^7({penalty.FirstOrDefault()?.Offense}) has joined!");
|
||||
string penaltyReason;
|
||||
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
penaltyReason = await ctx.Penalties
|
||||
.Where(p => p.OffenderId == newPlayer.ClientId && p.Type == Penalty.PenaltyType.Flag)
|
||||
.OrderByDescending(p => p.When)
|
||||
.Select(p => p.AutomatedOffense ?? p.Offense)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
E.Owner.ToAdmins($"^1NOTICE: ^7Flagged player ^5{newPlayer.Name} ^7({penaltyReason}) has joined!");
|
||||
}
|
||||
else
|
||||
E.Owner.Broadcast(ProcessAnnouncement(Config.Configuration().UserAnnouncementMessage, newPlayer));
|
||||
E.Owner.Broadcast(await ProcessAnnouncement(Config.Configuration().UserAnnouncementMessage, newPlayer));
|
||||
}
|
||||
}
|
||||
|
||||
private string ProcessAnnouncement(string msg, Player joining)
|
||||
private async Task<string> ProcessAnnouncement(string msg, Player joining)
|
||||
{
|
||||
msg = msg.Replace("{{ClientName}}", joining.Name);
|
||||
msg = msg.Replace("{{ClientLevel}}", Utilities.ConvertLevelToColor(joining.Level, joining.ClientPermission.Name));
|
||||
try
|
||||
// this prevents it from trying to evaluate it every message
|
||||
if (msg.Contains("{{ClientLocation}}"))
|
||||
{
|
||||
CountryLookupProj.CountryLookup CLT = new CountryLookupProj.CountryLookup($"{Utilities.OperatingDirectory}Plugins{System.IO.Path.DirectorySeparatorChar}GeoIP.dat");
|
||||
msg = msg.Replace("{{ClientLocation}}", CLT.LookupCountryName(joining.IPAddressString));
|
||||
}
|
||||
|
||||
catch (Exception)
|
||||
{
|
||||
joining.CurrentServer.Manager.GetLogger().WriteError("Could not open file Plugins\\GeoIP.dat for Welcome Plugin");
|
||||
msg = msg.Replace("{{ClientLocation}}", await GetCountryName(joining.IPAddressString));
|
||||
}
|
||||
msg = msg.Replace("{{TimesConnected}}", TimesConnected(joining));
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// makes a webrequest to determine IP origin
|
||||
/// </summary>
|
||||
/// <param name="ip">IP address to get location of</param>
|
||||
/// <returns></returns>
|
||||
private async Task<string> GetCountryName(string ip)
|
||||
{
|
||||
using (var wc = new WebClient())
|
||||
{
|
||||
try
|
||||
{
|
||||
string response = await wc.DownloadStringTaskAsync(new Uri($"http://extreme-ip-lookup.com/json/{ip}"));
|
||||
var responseObj = JObject.Parse(response);
|
||||
|
||||
return responseObj["country"].ToString();
|
||||
}
|
||||
|
||||
catch
|
||||
{
|
||||
return "a third world country";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
<PackageId>RaidMax.IW4MAdmin.Plugins.Welcome</PackageId>
|
||||
@ -19,17 +20,11 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="MaxMind\GeoIP.dat">
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.NETCore.App" />
|
||||
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="copy "$(TargetPath)" "$(SolutionDir)BUILD\Plugins"
copy "$(ProjectDir)MaxMind\GeoIP.dat" "$(SolutionDir)BUILD\Plugins\GeoIP.dat"" />
|
||||
<Exec Command="copy "$(TargetPath)" "$(SolutionDir)BUILD\Plugins"" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
8
RunPublishRelease.cmd
Normal file
8
RunPublishRelease.cmd
Normal file
@ -0,0 +1,8 @@
|
||||
dotnet publish WebfrontCore/WebfrontCore.csproj -c Release -o C:\Projects\IW4M-Admin\Publish\Windows
|
||||
dotnet publish Application/Application.csproj -c Release -o C:\Projects\IW4M-Admin\Publish\Windows
|
||||
dotnet publish GameLogServer/GameLogServer.pyproj -c Release -o C:\Projects\IW4M-Admin\Publish\Windows\GameLogServer
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\VsDevCmd.bat"
|
||||
msbuild GameLogServer/GameLogServer.pyproj /p:PublishProfile=Stable /p:DeployOnBuild=true /p:PublishProfileRootFolder=C:\Projects\IW4M-Admin\GameLogServer\
|
||||
msbuild DiscordWebhook/DiscordWebhook.pyproj /p:PublishProfile=Stable /p:DeployOnBuild=true /p:PublishProfileRootFolder=C:\Projects\IW4M-Admin\DiscordWebhook\
|
||||
cd "C:\Projects\IW4M-Admin\DEPLOY\"
|
||||
PowerShell ".\upload_release.ps1"
|
@ -773,12 +773,12 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
else if (unflagEvent.FailReason == GameEvent.EventFailReason.Invalid)
|
||||
{
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"]} ^5{E.Target.Name}");
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_NOTFLAGGED"]);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_NOTFLAGGED"]);
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"]} ^5{E.Target.Name}");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
@ -825,6 +825,11 @@ namespace SharedLibraryCore.Commands
|
||||
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_SELF"]);
|
||||
}
|
||||
|
||||
else if (reportEvent.FailReason == GameEvent.EventFailReason.Throttle)
|
||||
{
|
||||
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_TOOMANY"]);
|
||||
}
|
||||
|
||||
else if (reportEvent.Failed)
|
||||
{
|
||||
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_DUPLICATE"]);
|
||||
@ -1179,6 +1184,8 @@ namespace SharedLibraryCore.Commands
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1324,6 +1331,7 @@ namespace SharedLibraryCore.Commands
|
||||
var nextMapMatch = currentMap.First().Index != lastMap.Index ?
|
||||
regexMatches[regexMatches.IndexOf(currentMap.First()) + 1] :
|
||||
regexMatches.First();
|
||||
|
||||
nextMap = s.Maps.FirstOrDefault(m => m.Name == nextMapMatch.Groups[3].ToString()) ?? nextMap;
|
||||
string nextGametype = nextMapMatch.Groups[2].ToString().Length == 0 ?
|
||||
Utilities.GetLocalizedGametype(s.Gametype) :
|
||||
|
@ -24,7 +24,6 @@ namespace SharedLibraryCore.Database
|
||||
public DbSet<EFMeta> EFMeta { get; set; }
|
||||
public DbSet<EFChangeHistory> EFChangeHistory { get; set; }
|
||||
|
||||
|
||||
static string _ConnectionString;
|
||||
static string _provider;
|
||||
|
||||
@ -52,21 +51,17 @@ namespace SharedLibraryCore.Database
|
||||
{
|
||||
if (string.IsNullOrEmpty(_ConnectionString))
|
||||
{
|
||||
string currentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
|
||||
string currentPath = Utilities.OperatingDirectory;
|
||||
// allows the application to find the database file
|
||||
currentPath = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
||||
$"{Path.DirectorySeparatorChar}{currentPath}" :
|
||||
currentPath;
|
||||
|
||||
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = $"{currentPath}{Path.DirectorySeparatorChar}Database.db".Substring(6) };
|
||||
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = $"{currentPath}{Path.DirectorySeparatorChar}Database{Path.DirectorySeparatorChar}Database.db" };
|
||||
var connectionString = connectionStringBuilder.ToString();
|
||||
var connection = new SqliteConnection(connectionString);
|
||||
//#if DEBUG == true
|
||||
//optionsBuilder.UseMySql("UserId=root;Password=dev;Host=127.0.0.1;port=3306;Database=IW4MAdmin");
|
||||
// optionsBuilder.UseNpgsql("UserId=dev;Password=dev;Host=127.0.0.1;port=5432;Database=IW4MAdmin");
|
||||
//#else
|
||||
|
||||
optionsBuilder.UseSqlite(connection);
|
||||
//#endif
|
||||
}
|
||||
|
||||
else
|
||||
@ -130,15 +125,15 @@ namespace SharedLibraryCore.Database
|
||||
// https://aleemkhan.wordpress.com/2013/02/28/dynamically-adding-dbset-properties-in-dbcontext-for-entity-framework-code-first/
|
||||
IEnumerable<string> directoryFiles;
|
||||
|
||||
string pluginDir = $@"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}Debug{Path.DirectorySeparatorChar}netcoreapp2.0{Path.DirectorySeparatorChar}Plugins";
|
||||
string pluginDir = $@"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}Debug{Path.DirectorySeparatorChar}netcoreapp2.1{Path.DirectorySeparatorChar}Plugins";
|
||||
|
||||
if (!Directory.Exists(pluginDir))
|
||||
{
|
||||
pluginDir = $@"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}Plugins";
|
||||
pluginDir = Path.Join(Environment.CurrentDirectory, "Plugins");
|
||||
|
||||
if (!Directory.Exists(pluginDir))
|
||||
{
|
||||
pluginDir = Utilities.OperatingDirectory;
|
||||
pluginDir = Path.Join(Utilities.OperatingDirectory, "Plugins");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,11 @@ namespace SharedLibraryCore
|
||||
/// <summary>
|
||||
/// executing the event would cause an invalid state
|
||||
/// </summary>
|
||||
Invalid
|
||||
Invalid,
|
||||
/// <summary>
|
||||
/// client is doing too much of something
|
||||
/// </summary>
|
||||
Throttle
|
||||
}
|
||||
|
||||
public enum EventType
|
||||
@ -216,7 +220,8 @@ namespace SharedLibraryCore
|
||||
{
|
||||
return (queuedEvent.Target != null && queuedEvent.Target.ClientNumber != -1) &&
|
||||
(queuedEvent.Target.State != Player.ClientState.Connected &&
|
||||
queuedEvent.Target.NetworkId != 0);
|
||||
queuedEvent.Target.NetworkId != 0 &&
|
||||
queuedEvent.Origin?.ClientId != 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,107 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
|
||||
namespace SharedLibraryCore
|
||||
{
|
||||
public class RemoteFile : IFile
|
||||
{
|
||||
readonly string Location;
|
||||
string[] FileCache = new string[0];
|
||||
|
||||
public RemoteFile(string location) : base(string.Empty)
|
||||
{
|
||||
Location = location;
|
||||
}
|
||||
|
||||
private void Retrieve()
|
||||
{
|
||||
using (var cl = new HttpClient())
|
||||
FileCache = cl.GetStringAsync(Location).Result.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
public override long Length()
|
||||
{
|
||||
Retrieve();
|
||||
return FileCache.Sum(l => l.Length);
|
||||
}
|
||||
|
||||
public override Task<string[]> Tail(int lineCount)
|
||||
{
|
||||
return Task.FromResult(FileCache);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class IFile
|
||||
{
|
||||
public IFile(String fileName)
|
||||
{
|
||||
if (fileName != string.Empty)
|
||||
{
|
||||
Name = fileName;
|
||||
Handle = new StreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, true), Utilities.EncodingType);
|
||||
|
||||
sze = Handle.BaseStream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual long Length()
|
||||
{
|
||||
sze = Handle.BaseStream.Length;
|
||||
return sze;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
Handle?.Close();
|
||||
}
|
||||
|
||||
public String[] ReadAllLines()
|
||||
{
|
||||
return Handle?.ReadToEnd().Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
public String GetText()
|
||||
{
|
||||
return Handle?.ReadToEnd();
|
||||
}
|
||||
|
||||
public virtual async Task<String[]> Tail(int lineCount)
|
||||
{
|
||||
var buffer = new List<string>(lineCount);
|
||||
string line;
|
||||
for (int i = 0; i < lineCount; i++)
|
||||
{
|
||||
line = await Handle.ReadLineAsync();
|
||||
if (line == null) return buffer.ToArray();
|
||||
buffer.Add(line);
|
||||
}
|
||||
|
||||
int lastLine = lineCount - 1; //The index of the last line read from the buffer. Everything > this index was read earlier than everything <= this indes
|
||||
|
||||
while (null != (line = await Handle.ReadLineAsync()))
|
||||
{
|
||||
lastLine++;
|
||||
if (lastLine == lineCount) lastLine = 0;
|
||||
buffer[lastLine] = line;
|
||||
}
|
||||
|
||||
if (lastLine == lineCount - 1) return buffer.ToArray();
|
||||
var retVal = new string[lineCount];
|
||||
buffer.CopyTo(lastLine + 1, retVal, 0, lineCount - lastLine - 1);
|
||||
buffer.CopyTo(0, retVal, lineCount - lastLine - 1, lastLine + 1);
|
||||
return retVal;
|
||||
}
|
||||
//END
|
||||
|
||||
private long sze;
|
||||
private readonly String Name;
|
||||
private StreamReader Handle;
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ namespace SharedLibraryCore.Configuration
|
||||
public void Build()
|
||||
{
|
||||
ConfigurationRoot = new ConfigurationBuilder()
|
||||
.AddJsonFile($"{AppDomain.CurrentDomain.BaseDirectory}{Filename}.json", true)
|
||||
.AddJsonFile(Path.Join(Utilities.OperatingDirectory, "Configuration", $"{Filename}.json"), true)
|
||||
.Build();
|
||||
|
||||
_configuration = ConfigurationRoot.Get<T>();
|
||||
@ -37,10 +37,7 @@ namespace SharedLibraryCore.Configuration
|
||||
public Task Save()
|
||||
{
|
||||
var appConfigJSON = JsonConvert.SerializeObject(_configuration, Formatting.Indented);
|
||||
return Task.Factory.StartNew(() =>
|
||||
{
|
||||
File.WriteAllText($"{AppDomain.CurrentDomain.BaseDirectory}{Filename}.json", appConfigJSON);
|
||||
});
|
||||
return File.WriteAllTextAsync(Path.Join(Utilities.OperatingDirectory, "Configuration", $"{Filename}.json"), appConfigJSON);
|
||||
}
|
||||
|
||||
public T Configuration() => _configuration;
|
||||
|
@ -13,7 +13,7 @@ namespace SharedLibraryCore.Interfaces
|
||||
Task Init();
|
||||
void Start();
|
||||
void Stop();
|
||||
ILogger GetLogger();
|
||||
ILogger GetLogger(int serverId);
|
||||
IList<Server> GetServers();
|
||||
IList<Command> GetCommands();
|
||||
IList<Helpers.MessageToken> GetMessageTokens();
|
||||
|
@ -81,7 +81,10 @@ namespace SharedLibraryCore.Objects
|
||||
ConnectionTime = DateTime.UtcNow;
|
||||
ClientNumber = -1;
|
||||
DelayedEvents = new Queue<GameEvent>();
|
||||
_additionalProperties = new Dictionary<string, object>();
|
||||
_additionalProperties = new Dictionary<string, object>
|
||||
{
|
||||
{ "_reportCount", 0 }
|
||||
};
|
||||
}
|
||||
|
||||
public override string ToString() => $"{Name}::{NetworkId}";
|
||||
@ -116,19 +119,22 @@ namespace SharedLibraryCore.Objects
|
||||
{
|
||||
Type = GameEvent.EventType.Warn,
|
||||
Message = warnReason,
|
||||
Data = warnReason,
|
||||
Origin = sender,
|
||||
Target = this,
|
||||
Owner = sender.CurrentServer
|
||||
};
|
||||
|
||||
// enforce level restrictions
|
||||
if (sender.Level <= this.Level)
|
||||
if (this.Level > sender.Level)
|
||||
{
|
||||
e.FailReason = GameEvent.EventFailReason.Permission;
|
||||
return e;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
this.Warnings++;
|
||||
}
|
||||
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
return e;
|
||||
@ -181,6 +187,8 @@ namespace SharedLibraryCore.Objects
|
||||
Owner = sender.CurrentServer
|
||||
};
|
||||
|
||||
int reportCount = sender.GetAdditionalProperty<int>("_reportCount");
|
||||
|
||||
if (this.Level > sender.Level)
|
||||
{
|
||||
e.FailReason = GameEvent.EventFailReason.Permission;
|
||||
@ -191,12 +199,18 @@ namespace SharedLibraryCore.Objects
|
||||
e.FailReason = GameEvent.EventFailReason.Invalid;
|
||||
}
|
||||
|
||||
else if (reportCount > 2)
|
||||
{
|
||||
e.FailReason = GameEvent.EventFailReason.Throttle;
|
||||
}
|
||||
|
||||
else if (CurrentServer.Reports.Count(report => (report.Origin.NetworkId == sender.NetworkId &&
|
||||
report.Target.NetworkId == this.NetworkId)) > 0)
|
||||
{
|
||||
e.FailReason = GameEvent.EventFailReason.Exception;
|
||||
}
|
||||
|
||||
sender.SetAdditionalProperty("_reportCount", reportCount + 1);
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
return e;
|
||||
}
|
||||
@ -325,7 +339,6 @@ namespace SharedLibraryCore.Objects
|
||||
if (sender.Level <= this.Level)
|
||||
{
|
||||
e.FailReason = GameEvent.EventFailReason.Permission;
|
||||
return e;
|
||||
}
|
||||
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
@ -353,7 +366,6 @@ namespace SharedLibraryCore.Objects
|
||||
if (sender.Level <= this.Level)
|
||||
{
|
||||
e.FailReason = GameEvent.EventFailReason.Permission;
|
||||
return e;
|
||||
}
|
||||
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
@ -391,7 +403,18 @@ namespace SharedLibraryCore.Objects
|
||||
[NotMapped]
|
||||
Dictionary<string, object> _additionalProperties;
|
||||
public T GetAdditionalProperty<T>(string name) => (T)_additionalProperties[name];
|
||||
public void SetAdditionalProperty(string name, object value) => _additionalProperties.Add(name, value);
|
||||
public void SetAdditionalProperty(string name, object value)
|
||||
{
|
||||
if (_additionalProperties.ContainsKey(name))
|
||||
{
|
||||
_additionalProperties[name] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_additionalProperties.Add(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
[NotMapped]
|
||||
public int ClientNumber { get; set; }
|
||||
[NotMapped]
|
||||
|
@ -34,7 +34,7 @@ namespace SharedLibraryCore.Plugins
|
||||
if (dllFileNames.Length == 0 &&
|
||||
scriptFileNames.Length == 0)
|
||||
{
|
||||
Manager.GetLogger().WriteDebug(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_NOTFOUND"]);
|
||||
Manager.GetLogger(0).WriteDebug(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_NOTFOUND"]);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ namespace SharedLibraryCore.Plugins
|
||||
{
|
||||
var plugin = new ScriptPlugin(fileName);
|
||||
plugin.Initialize(Manager).Wait();
|
||||
Manager.GetLogger().WriteDebug($"Loaded script plugin \"{ plugin.Name }\" [{plugin.Version}]");
|
||||
Manager.GetLogger(0).WriteDebug($"Loaded script plugin \"{ plugin.Name }\" [{plugin.Version}]");
|
||||
ActivePlugins.Add(plugin);
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ namespace SharedLibraryCore.Plugins
|
||||
Object commandObject = Activator.CreateInstance(assemblyType);
|
||||
Command newCommand = (Command)commandObject;
|
||||
ActiveCommands.Add(newCommand);
|
||||
Manager.GetLogger().WriteDebug($"{Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_REGISTERCMD"]} \"{newCommand.Name}\"");
|
||||
Manager.GetLogger(0).WriteDebug($"{Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_REGISTERCMD"]} \"{newCommand.Name}\"");
|
||||
LoadedCommands++;
|
||||
continue;
|
||||
}
|
||||
@ -82,18 +82,18 @@ namespace SharedLibraryCore.Plugins
|
||||
{
|
||||
ActivePlugins.Add(newNotify);
|
||||
PluginAssemblies.Add(Plugin);
|
||||
Manager.GetLogger().WriteDebug($"Loaded plugin \"{ newNotify.Name }\" [{newNotify.Version}]");
|
||||
Manager.GetLogger(0).WriteDebug($"Loaded plugin \"{ newNotify.Name }\" [{newNotify.Version}]");
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception E)
|
||||
{
|
||||
Manager.GetLogger().WriteWarning($"{Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_ERROR"]} {Plugin.Location} - {E.Message}");
|
||||
Manager.GetLogger(0).WriteWarning($"{Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_ERROR"]} {Plugin.Location} - {E.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Manager.GetLogger().WriteInfo($"Loaded {ActivePlugins.Count} plugins and registered {LoadedCommands} commands.");
|
||||
Manager.GetLogger(0).WriteInfo($"Loaded {ActivePlugins.Count} plugins and registered {LoadedCommands} commands.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -95,6 +95,7 @@ namespace SharedLibraryCore.RCon
|
||||
Ttl = 42,
|
||||
ExclusiveAddressUse = true,
|
||||
};
|
||||
|
||||
connectionState.OnSentData.Reset();
|
||||
connectionState.OnReceivedData.Reset();
|
||||
connectionState.ConnectionAttempts++;
|
||||
@ -103,22 +104,22 @@ namespace SharedLibraryCore.RCon
|
||||
#endif
|
||||
try
|
||||
{
|
||||
response = await SendPayloadAsync(payload);
|
||||
response = await SendPayloadAsync(payload, waitForResponse);
|
||||
connectionState.OnComplete.Release(1);
|
||||
connectionState.ConnectionAttempts = 0;
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
catch/* (Exception ex)*/
|
||||
{
|
||||
if (connectionState.ConnectionAttempts < StaticHelpers.AllowedConnectionFails)
|
||||
{
|
||||
Log.WriteWarning($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})");
|
||||
// Log.WriteWarning($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})");
|
||||
await Task.Delay(StaticHelpers.FloodProtectionInterval);
|
||||
goto retrySend;
|
||||
}
|
||||
|
||||
connectionState.OnComplete.Release(1);
|
||||
Log.WriteDebug(ex.GetExceptionInfo());
|
||||
//Log.WriteDebug(ex.GetExceptionInfo());
|
||||
throw new NetworkException($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}]");
|
||||
}
|
||||
|
||||
@ -139,7 +140,7 @@ namespace SharedLibraryCore.RCon
|
||||
return splitResponse;
|
||||
}
|
||||
|
||||
private async Task<byte[]> SendPayloadAsync(byte[] payload)
|
||||
private async Task<byte[]> SendPayloadAsync(byte[] payload, bool waitForResponse)
|
||||
{
|
||||
var connectionState = ActiveQueries[this.Endpoint];
|
||||
var rconSocket = (Socket)connectionState.SendEventArgs.UserToken;
|
||||
@ -171,6 +172,11 @@ namespace SharedLibraryCore.RCon
|
||||
}
|
||||
}
|
||||
|
||||
if (!waitForResponse)
|
||||
{
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
connectionState.ReceiveEventArgs.SetBuffer(connectionState.ReceiveBuffer);
|
||||
|
||||
// get our response back
|
||||
|
@ -39,7 +39,7 @@ namespace SharedLibraryCore.RCon
|
||||
/// <summary>
|
||||
/// timeout in seconds to wait for a socket send or receive before giving up
|
||||
/// </summary>
|
||||
public static readonly int SocketTimeout = 1000;
|
||||
public static readonly int SocketTimeout = 10000;
|
||||
/// <summary>
|
||||
/// interval in milliseconds to wait before sending the next RCon request
|
||||
/// </summary>
|
||||
|
@ -42,8 +42,8 @@ namespace SharedLibraryCore
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Manager.GetLogger().WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_ERROR"]} {Name}");
|
||||
Manager.GetLogger().WriteDebug(ex.Message);
|
||||
Manager.GetLogger(0).WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_ERROR"]} {Name}");
|
||||
Manager.GetLogger(0).WriteDebug(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,7 +57,16 @@ namespace SharedLibraryCore
|
||||
}
|
||||
|
||||
Manager = mgr;
|
||||
string script = File.ReadAllText(FileName);
|
||||
string script;
|
||||
|
||||
using (var stream = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
using (var reader = new StreamReader(stream, Encoding.Default))
|
||||
{
|
||||
script = await reader.ReadToEndAsync();
|
||||
}
|
||||
}
|
||||
|
||||
ScriptEngine = new Jint.Engine(cfg =>
|
||||
cfg.AllowClr(new[]
|
||||
{
|
||||
@ -68,7 +77,6 @@ namespace SharedLibraryCore
|
||||
|
||||
ScriptEngine.Execute(script);
|
||||
ScriptEngine.SetValue("_localization", Utilities.CurrentLocalization);
|
||||
ScriptEngine.SetValue("_IW4MAdminClient", Utilities.IW4MAdminClient);
|
||||
dynamic pluginObject = ScriptEngine.GetValue("plugin").ToObject();
|
||||
|
||||
this.Author = pluginObject.author;
|
||||
@ -87,6 +95,7 @@ namespace SharedLibraryCore
|
||||
{
|
||||
ScriptEngine.SetValue("_gameEvent", E);
|
||||
ScriptEngine.SetValue("_server", S);
|
||||
ScriptEngine.SetValue("_IW4MAdminClient", Utilities.IW4MAdminClient(S));
|
||||
return Task.FromResult(ScriptEngine.Execute("plugin.onEventAsync(_gameEvent, _server)").GetCompletionValue());
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,8 @@ namespace SharedLibraryCore
|
||||
IP = config.IPAddress;
|
||||
Port = config.Port;
|
||||
Manager = mgr;
|
||||
Logger = Manager.GetLogger();
|
||||
Logger = Manager.GetLogger(this.GetHashCode());
|
||||
Logger.WriteInfo(this.ToString());
|
||||
ServerConfig = config;
|
||||
RemoteConnection = new RCon.Connection(IP, Port, Password, Logger);
|
||||
|
||||
@ -209,7 +210,7 @@ namespace SharedLibraryCore
|
||||
/// <param name="Origin">The person who banned the target</param>
|
||||
abstract protected Task Ban(String Reason, Player Target, Player Origin);
|
||||
|
||||
abstract public Task Warn(String Reason, Player Target, Player Origin);
|
||||
abstract protected Task Warn(String Reason, Player Target, Player Origin);
|
||||
|
||||
/// <summary>
|
||||
/// Unban a player by npID / GUID
|
||||
@ -263,7 +264,7 @@ namespace SharedLibraryCore
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{IP}_{Port}";
|
||||
return $"{IP}-{Port}";
|
||||
}
|
||||
|
||||
protected async Task<bool> ScriptLoaded()
|
||||
@ -320,7 +321,6 @@ namespace SharedLibraryCore
|
||||
protected int ConnectionErrors;
|
||||
protected List<string> BroadcastMessages;
|
||||
protected TimeSpan LastMessage;
|
||||
protected IFile LogFile;
|
||||
protected DateTime LastPoll;
|
||||
protected ManualResetEventSlim OnRemoteCommandResponse;
|
||||
|
||||
|
@ -1,154 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Database;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Services
|
||||
{
|
||||
// https://stackoverflow.com/questions/43677906/crud-operations-with-entityframework-using-generic-type
|
||||
public class GenericRepository<TEntity> where TEntity : class
|
||||
{
|
||||
private DatabaseContext _context;
|
||||
private DbSet<TEntity> _dbSet;
|
||||
private readonly bool ShouldTrack;
|
||||
|
||||
public GenericRepository(bool shouldTrack)
|
||||
{
|
||||
this.ShouldTrack = shouldTrack;
|
||||
}
|
||||
|
||||
public GenericRepository() { }
|
||||
|
||||
protected DbContext Context
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_context == null)
|
||||
{
|
||||
_context = new DatabaseContext(ShouldTrack);
|
||||
}
|
||||
|
||||
return _context;
|
||||
}
|
||||
}
|
||||
|
||||
protected DbSet<TEntity> DBSet
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dbSet == null)
|
||||
{
|
||||
_dbSet = this.Context.Set<TEntity>();
|
||||
}
|
||||
|
||||
return _dbSet;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual async Task<IList<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderExpression = null)
|
||||
{
|
||||
return await this.GetQuery(predicate, orderExpression).ToListAsync();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderExpression = null)
|
||||
{
|
||||
return this.GetQuery(predicate, orderExpression).AsEnumerable();
|
||||
}
|
||||
|
||||
public virtual IQueryable<TEntity> GetQuery(Expression<Func<TEntity, bool>> predicate = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderExpression = null)
|
||||
{
|
||||
IQueryable<TEntity> qry = this.DBSet;
|
||||
|
||||
foreach (var property in this.Context.Model.FindEntityType(typeof(TEntity)).GetNavigations())
|
||||
qry = qry.Include(property.Name);
|
||||
|
||||
|
||||
if (predicate != null)
|
||||
qry = qry.Where(predicate);
|
||||
|
||||
if (orderExpression != null)
|
||||
return orderExpression(qry);
|
||||
|
||||
|
||||
return qry;
|
||||
}
|
||||
|
||||
public virtual void Insert<T>(T entity) where T : class
|
||||
{
|
||||
DbSet<T> dbSet = this.Context.Set<T>();
|
||||
dbSet.Add(entity);
|
||||
}
|
||||
|
||||
public virtual TEntity Insert(TEntity entity)
|
||||
{
|
||||
return DBSet.Add(entity).Entity;
|
||||
}
|
||||
|
||||
public virtual void Update<T>(T entity) where T : class
|
||||
{
|
||||
DbSet<T> dbSet = this.Context.Set<T>();
|
||||
dbSet.Attach(entity);
|
||||
this.Context.Entry(entity).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
public virtual void Update(TEntity entity)
|
||||
{
|
||||
this.Attach(entity);
|
||||
this.Context.Entry(entity).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
public virtual void Delete<T>(T entity) where T : class
|
||||
{
|
||||
DbSet<T> dbSet = this.Context.Set<T>();
|
||||
|
||||
if (this.Context.Entry(entity).State == EntityState.Detached)
|
||||
dbSet.Attach(entity);
|
||||
|
||||
dbSet.Remove(entity);
|
||||
}
|
||||
|
||||
public virtual void Delete(TEntity entity)
|
||||
{
|
||||
if (this.Context.Entry(entity).State == EntityState.Detached)
|
||||
this.Attach(entity);
|
||||
|
||||
this.DBSet.Remove(entity);
|
||||
|
||||
}
|
||||
|
||||
public virtual void Delete<T>(object[] id) where T : class
|
||||
{
|
||||
DbSet<T> dbSet = this.Context.Set<T>();
|
||||
T entity = dbSet.Find(id);
|
||||
dbSet.Attach(entity);
|
||||
dbSet.Remove(entity);
|
||||
}
|
||||
|
||||
public virtual void Delete(object id)
|
||||
{
|
||||
TEntity entity = this.DBSet.Find(id);
|
||||
this.Delete(entity);
|
||||
}
|
||||
|
||||
|
||||
public virtual void Attach(TEntity entity)
|
||||
{
|
||||
if (this.Context.Entry(entity).State == EntityState.Detached)
|
||||
this.DBSet.Attach(entity);
|
||||
}
|
||||
|
||||
public virtual void SaveChanges()
|
||||
{
|
||||
this.Context.SaveChanges();
|
||||
}
|
||||
|
||||
public virtual Task SaveChangesAsync()
|
||||
{
|
||||
return this.Context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -24,7 +24,6 @@ namespace SharedLibraryCore.Services
|
||||
PunisherId = newEntity.Punisher.ClientId,
|
||||
LinkId = newEntity.Link.AliasLinkId,
|
||||
Type = newEntity.Type,
|
||||
Active = true,
|
||||
Expires = newEntity.Expires,
|
||||
Offense = newEntity.Offense,
|
||||
When = newEntity.When,
|
||||
@ -69,19 +68,7 @@ namespace SharedLibraryCore.Services
|
||||
public async Task<IList<EFPenalty>> Find(Func<EFPenalty, bool> expression)
|
||||
{
|
||||
throw await Task.FromResult(new Exception());
|
||||
/*
|
||||
return await Task.FromResult(new List<EFPenalty>());
|
||||
// fixme: this is so slow!
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
using (var context = new DatabaseContext())
|
||||
return context.Penalties
|
||||
.Include(p => p.Offender)
|
||||
.Include(p => p.Punisher)
|
||||
.Where(expression)
|
||||
.Where(p => p.Active)
|
||||
.ToList();
|
||||
});*/
|
||||
|
||||
}
|
||||
|
||||
public Task<EFPenalty> Get(int entityID)
|
||||
@ -225,8 +212,6 @@ namespace SharedLibraryCore.Services
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
<PackageId>RaidMax.IW4MAdmin.SharedLibraryCore</PackageId>
|
||||
@ -12,20 +13,11 @@
|
||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Migrations\20180502195240_Update.cs" />
|
||||
<Compile Remove="Migrations\20180923025324_FixForPostgreSQL.cs" />
|
||||
<Compile Remove="Migrations\20180923025702_FixForPostgreSQL.cs" />
|
||||
<Compile Remove="Migrations\20180923030248_FixForPostgreSQL.cs" />
|
||||
<Compile Remove="Migrations\20180923030426_FixForPostgreSQL.cs" />
|
||||
<Compile Remove="Migrations\20180923030528_FixForPostgreSQL.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Jint" Version="2.11.58" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="2.1.1" />
|
||||
@ -39,7 +31,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.NETCore.App" />
|
||||
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||
|
@ -20,10 +20,20 @@ namespace SharedLibraryCore
|
||||
{
|
||||
public static class Utilities
|
||||
{
|
||||
public static string OperatingDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + Path.DirectorySeparatorChar;
|
||||
#if DEBUG == true
|
||||
public static string OperatingDirectory => $"{Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)}{Path.DirectorySeparatorChar}";
|
||||
#else
|
||||
public static string OperatingDirectory => $"{Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)}{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}";
|
||||
#endif
|
||||
public static Encoding EncodingType;
|
||||
public static Localization.Layout CurrentLocalization = new Localization.Layout(new Dictionary<string, string>());
|
||||
public static Player IW4MAdminClient = new Player() { ClientId = 1, Level = Player.Permission.Console };
|
||||
public static Player IW4MAdminClient(Server server = null) => new Player()
|
||||
{
|
||||
ClientId = 1,
|
||||
State = Player.ClientState.Connected,
|
||||
Level = Player.Permission.Console,
|
||||
CurrentServer = server
|
||||
};
|
||||
|
||||
public static string HttpRequest(string location, string header, string headerValue)
|
||||
{
|
||||
|
@ -23,9 +23,9 @@ namespace WebfrontCore.Controllers
|
||||
public IActionResult Error()
|
||||
{
|
||||
var exceptionFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
|
||||
Manager.GetLogger().WriteError($"[Webfront] {exceptionFeature.Error.Message}");
|
||||
Manager.GetLogger().WriteDebug(exceptionFeature.Path);
|
||||
Manager.GetLogger().WriteDebug(exceptionFeature.Error.StackTrace);
|
||||
Manager.GetLogger(0).WriteError($"[Webfront] {exceptionFeature.Error.Message}");
|
||||
Manager.GetLogger(0).WriteDebug(exceptionFeature.Path);
|
||||
Manager.GetLogger(0).WriteDebug(exceptionFeature.Error.StackTrace);
|
||||
|
||||
ViewBag.Description = Localization["WEBFRONT_ERROR_DESC"];
|
||||
ViewBag.Title = Localization["WEBFRONT_ERROR_TITLE"];
|
||||
|
@ -1,5 +1,7 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Services;
|
||||
@ -33,8 +35,14 @@ namespace WebfrontCore.Controllers
|
||||
|
||||
public async Task<IActionResult> PublicAsync()
|
||||
{
|
||||
var penalties = await (new GenericRepository<EFPenalty>())
|
||||
.FindAsync(p => p.Type == SharedLibraryCore.Objects.Penalty.PenaltyType.Ban && p.Active);
|
||||
IList<EFPenalty> penalties;
|
||||
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
penalties = await ctx.Penalties
|
||||
.Where(p => p.Type == SharedLibraryCore.Objects.Penalty.PenaltyType.Ban && p.Active)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
var penaltiesDto = penalties.Select(p => new PenaltyInfo()
|
||||
{
|
||||
|
@ -30,7 +30,7 @@ namespace WebfrontCore
|
||||
#if DEBUG
|
||||
.UseContentRoot(Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), @"..\..\..\..\", "WebfrontCore")))
|
||||
#else
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseContentRoot(SharedLibraryCore.Utilities.OperatingDirectory)
|
||||
#endif
|
||||
.UseUrls(Manager.GetApplicationSettings().Configuration().WebfrontBindUrl)
|
||||
.UseKestrel()
|
||||
|
@ -20,7 +20,6 @@ namespace WebfrontCore
|
||||
public Startup(IHostingEnvironment env)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(env.ContentRootPath)
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
Configuration = builder.Build();
|
||||
@ -32,10 +31,10 @@ namespace WebfrontCore
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// Add framework services.
|
||||
var mvcBulder = services.AddMvc();
|
||||
var mvcBuilder = services.AddMvc();
|
||||
|
||||
foreach (var asm in Program.Manager.GetPluginAssemblies())
|
||||
mvcBulder.AddApplicationPart(asm);
|
||||
mvcBuilder.AddApplicationPart(asm);
|
||||
|
||||
services.Configure<RazorViewEngineOptions>(o =>
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
|
||||
<RazorCompileOnBuild>false</RazorCompileOnBuild>
|
||||
<RazorCompileOnPublish>false</RazorCompileOnPublish>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
@ -63,7 +64,7 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.1.1" />
|
||||
<PackageReference Update="Microsoft.NETCore.App" />
|
||||
<PackageReference Update="Microsoft.NETCore.App" Version="2.1.5" />
|
||||
<PackageReference Update="Microsoft.AspNetCore" Version="2.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
|
Reference in New Issue
Block a user