support UTF8 in log files
sayteam registered as say event updated readme
This commit is contained in:
parent
438718507b
commit
7c691c2327
@ -12,7 +12,7 @@ namespace Application.EventParsers
|
|||||||
public GameEvent GetEvent(Server server, string logLine)
|
public GameEvent GetEvent(Server server, string logLine)
|
||||||
{
|
{
|
||||||
string[] lineSplit = logLine.Split(';');
|
string[] lineSplit = logLine.Split(';');
|
||||||
string cleanedEventLine = Regex.Replace(lineSplit[0], @"[0-9]+:[0-9]+\ ", "");
|
string cleanedEventLine = Regex.Replace(lineSplit[0], @"[0-9]+:[0-9]+\ ", "").Trim();
|
||||||
|
|
||||||
if (cleanedEventLine[0] == 'K')
|
if (cleanedEventLine[0] == 'K')
|
||||||
{
|
{
|
||||||
@ -29,7 +29,7 @@ namespace Application.EventParsers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lineSplit[0].Substring(lineSplit[0].Length - 3).Trim() == "say")
|
if (cleanedEventLine == "say" || cleanedEventLine == "sayteam")
|
||||||
{
|
{
|
||||||
return new GameEvent()
|
return new GameEvent()
|
||||||
{
|
{
|
||||||
@ -37,7 +37,7 @@ namespace Application.EventParsers
|
|||||||
Data = lineSplit[4].Replace("\x15", ""),
|
Data = lineSplit[4].Replace("\x15", ""),
|
||||||
Origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)),
|
Origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)),
|
||||||
Owner = server,
|
Owner = server,
|
||||||
Message = lineSplit[4]
|
Message = lineSplit[4].Replace("\x15", "")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ namespace Application.EventParsers
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lineSplit[0] == "say")
|
if (lineSplit[0] == "say" || lineSplit[0] == "sayteam")
|
||||||
{
|
{
|
||||||
return new GameEvent()
|
return new GameEvent()
|
||||||
{
|
{
|
||||||
|
@ -648,7 +648,7 @@ namespace IW4MAdmin
|
|||||||
CustomCallback = await ScriptLoaded();
|
CustomCallback = await ScriptLoaded();
|
||||||
string mainPath = EventParser.GetGameDir();
|
string mainPath = EventParser.GetGameDir();
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
basepath.Value = @"\\192.168.88.253\Call of Duty Black Ops II";
|
basepath.Value = @"D:\";
|
||||||
#endif
|
#endif
|
||||||
string logPath = game.Value == string.Empty ?
|
string logPath = game.Value == string.Empty ?
|
||||||
$"{basepath.Value.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{mainPath}{Path.DirectorySeparatorChar}{logfile.Value}" :
|
$"{basepath.Value.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{mainPath}{Path.DirectorySeparatorChar}{logfile.Value}" :
|
||||||
|
@ -12,8 +12,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
version.txt = version.txt
|
version.txt = version.txt
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Plugins\Tests\Tests.csproj", "{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharedLibraryCore", "SharedLibraryCore\SharedLibraryCore.csproj", "{AA0541A2-8D51-4AD9-B0AC-3D1F5B162481}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharedLibraryCore", "SharedLibraryCore\SharedLibraryCore.csproj", "{AA0541A2-8D51-4AD9-B0AC-3D1F5B162481}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebfrontCore", "WebfrontCore\WebfrontCore.csproj", "{D59AC1F1-2FB9-4BE7-813E-0CCCC4FE9067}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebfrontCore", "WebfrontCore\WebfrontCore.csproj", "{D59AC1F1-2FB9-4BE7-813E-0CCCC4FE9067}"
|
||||||
@ -30,6 +28,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Login", "Plugins\Login\Logi
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "Master", "Master\Master.pyproj", "{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}"
|
Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "Master", "Master\Master.pyproj", "{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Plugins\Tests\Tests.csproj", "{B72DEBFB-9D48-4076-8FF5-1FD72A830845}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -46,26 +46,6 @@ Global
|
|||||||
Release|x86 = Release|x86
|
Release|x86 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Debug|x86.ActiveCfg = Debug|x86
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Debug|x86.Build.0 = Debug|x86
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Prerelease|Any CPU.ActiveCfg = Release-Stable|Any CPU
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Prerelease|Mixed Platforms.ActiveCfg = Release-Stable|x86
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Prerelease|x64.ActiveCfg = Release-Stable|Any CPU
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Prerelease|x86.ActiveCfg = Release-Stable|x86
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release|Any CPU.ActiveCfg = Release-Stable|Any CPU
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release|Any CPU.Build.0 = Release-Stable|Any CPU
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release|Mixed Platforms.ActiveCfg = Release-Stable|x86
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release|Mixed Platforms.Build.0 = Release-Stable|x86
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release|x64.ActiveCfg = Release-Stable|Any CPU
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release|x64.Build.0 = Release-Stable|Any CPU
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release|x86.ActiveCfg = Release-Stable|x86
|
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}.Release|x86.Build.0 = Release-Stable|x86
|
|
||||||
{AA0541A2-8D51-4AD9-B0AC-3D1F5B162481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{AA0541A2-8D51-4AD9-B0AC-3D1F5B162481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{AA0541A2-8D51-4AD9-B0AC-3D1F5B162481}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{AA0541A2-8D51-4AD9-B0AC-3D1F5B162481}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{AA0541A2-8D51-4AD9-B0AC-3D1F5B162481}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
{AA0541A2-8D51-4AD9-B0AC-3D1F5B162481}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
@ -258,16 +238,38 @@ Global
|
|||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Release|x64.Build.0 = Release|Any CPU
|
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Release|x86.ActiveCfg = Release|Any CPU
|
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Release|x86.Build.0 = Release|Any CPU
|
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Prerelease|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Prerelease|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Prerelease|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Prerelease|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Prerelease|x64.Build.0 = Debug|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Prerelease|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Prerelease|x86.Build.0 = Debug|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{B8C2A759-8663-4F6F-9BA4-19595F5E12C1} = {26E8B310-269E-46D4-A612-24601F16065F}
|
|
||||||
{98BE4A81-8AFD-4957-83F7-009D353C6BCB} = {26E8B310-269E-46D4-A612-24601F16065F}
|
{98BE4A81-8AFD-4957-83F7-009D353C6BCB} = {26E8B310-269E-46D4-A612-24601F16065F}
|
||||||
{179140D3-97AA-4CB4-8BF6-A0C73CA75701} = {26E8B310-269E-46D4-A612-24601F16065F}
|
{179140D3-97AA-4CB4-8BF6-A0C73CA75701} = {26E8B310-269E-46D4-A612-24601F16065F}
|
||||||
{958FF7EC-0226-4E85-A85B-B84EC768197D} = {26E8B310-269E-46D4-A612-24601F16065F}
|
{958FF7EC-0226-4E85-A85B-B84EC768197D} = {26E8B310-269E-46D4-A612-24601F16065F}
|
||||||
{D9F2ED28-6FA5-40CA-9912-E7A849147AB1} = {26E8B310-269E-46D4-A612-24601F16065F}
|
{D9F2ED28-6FA5-40CA-9912-E7A849147AB1} = {26E8B310-269E-46D4-A612-24601F16065F}
|
||||||
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845} = {26E8B310-269E-46D4-A612-24601F16065F}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {84F8F8E0-1F73-41E0-BD8D-BB6676E2EE87}
|
SolutionGuid = {84F8F8E0-1F73-41E0-BD8D-BB6676E2EE87}
|
||||||
|
@ -12,6 +12,10 @@
|
|||||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<DefineConstants>TRACE;DEBUG;NETCOREAPP2_0</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\SharedLibraryCore\SharedLibraryCore.csproj" />
|
<ProjectReference Include="..\..\SharedLibraryCore\SharedLibraryCore.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -1,20 +1,11 @@
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
using SharedLibrary;
|
using SharedLibraryCore;
|
||||||
using SharedLibrary.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using SharedLibrary.Helpers;
|
using SharedLibraryCore.Helpers;
|
||||||
using SharedLibrary.Objects;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using StatsPlugin.Models;
|
|
||||||
using SharedLibrary.Services;
|
|
||||||
using SharedLibrary.Database.Models;
|
|
||||||
using SharedLibrary.Database;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins
|
namespace IW4MAdmin.Plugins
|
||||||
{
|
{
|
||||||
@ -26,12 +17,9 @@ namespace IW4MAdmin.Plugins
|
|||||||
|
|
||||||
public string Author => "RaidMax";
|
public string Author => "RaidMax";
|
||||||
|
|
||||||
private DateTime Interval;
|
public async Task OnEventAsync(GameEvent E, Server S)
|
||||||
|
|
||||||
public async Task OnEventAsync(Event E, Server S)
|
|
||||||
{
|
{
|
||||||
return;
|
if (E.Type == GameEvent.EventType.Start)
|
||||||
if (E.Type == Event.GType.Start)
|
|
||||||
{
|
{
|
||||||
#region PLAYER_HISTORY
|
#region PLAYER_HISTORY
|
||||||
var rand = new Random(GetHashCode());
|
var rand = new Random(GetHashCode());
|
||||||
@ -61,361 +49,11 @@ namespace IW4MAdmin.Plugins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnLoadAsync(IManager manager)
|
public Task OnLoadAsync(IManager manager) => Task.CompletedTask;
|
||||||
{
|
|
||||||
// #if DO_IMPORT
|
|
||||||
var svc = new GenericRepository<EFServer>();
|
|
||||||
svc.Insert(new EFServer()
|
|
||||||
{
|
|
||||||
Active = true,
|
|
||||||
Port = 28960,
|
|
||||||
ServerId = Math.Abs("127.0.0.1:28960".GetHashCode()),
|
|
||||||
});
|
|
||||||
|
|
||||||
svc.Insert(new EFServer()
|
|
||||||
{
|
|
||||||
Active = true,
|
|
||||||
Port = 28965,
|
|
||||||
ServerId = Math.Abs("127.0.0.1:28965".GetHashCode()),
|
|
||||||
});
|
|
||||||
|
|
||||||
svc.Insert(new EFServer()
|
|
||||||
{
|
|
||||||
Active = true,
|
|
||||||
Port = 28970,
|
|
||||||
ServerId = Math.Abs("127.0.0.1:28970".GetHashCode()),
|
|
||||||
});
|
|
||||||
|
|
||||||
svc.SaveChanges();
|
|
||||||
// #endif
|
|
||||||
Interval = DateTime.Now;
|
|
||||||
var clients = new List<Player>();
|
|
||||||
var oldClients = new Dictionary<int, Player>();
|
|
||||||
#region CLIENTS
|
|
||||||
if (File.Exists("import_clients.csv"))
|
|
||||||
{
|
|
||||||
manager.GetLogger().WriteVerbose("Beginning import of existing clients");
|
|
||||||
|
|
||||||
var lines = File.ReadAllLines("import_clients.csv").Skip(1);
|
|
||||||
foreach (string line in lines)
|
|
||||||
{
|
|
||||||
string[] fields = Regex.Replace(line, "\".*\"", "").Split(',');
|
|
||||||
fields.All(f =>
|
|
||||||
{
|
|
||||||
f = f.StripColors().Trim();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (fields.Length != 11)
|
|
||||||
{
|
|
||||||
manager.GetLogger().WriteError("Invalid client import file... aborting import");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fields[1].Substring(0, 5) == "01100" || fields[0] == string.Empty || fields[1] == string.Empty || fields[6] == string.Empty)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!Regex.Match(fields[6], @"^\d+\.\d+\.\d+.\d+$").Success)
|
|
||||||
fields[6] = "0";
|
|
||||||
|
|
||||||
var client = new Player()
|
|
||||||
{
|
|
||||||
Name = fields[0],
|
|
||||||
NetworkId = fields[1].ConvertLong(),
|
|
||||||
IPAddress = fields[6].ConvertToIP(),
|
|
||||||
Level = (Player.Permission)Convert.ToInt32(fields[3]),
|
|
||||||
Connections = Convert.ToInt32(fields[5]),
|
|
||||||
LastConnection = DateTime.Parse(fields[7]),
|
|
||||||
};
|
|
||||||
|
|
||||||
clients.Add(client);
|
|
||||||
oldClients.Add(Convert.ToInt32(fields[2]), client);
|
|
||||||
}
|
|
||||||
clients = clients.Distinct().ToList();
|
|
||||||
// #if DO_IMPORT
|
|
||||||
|
|
||||||
/*clients = clients
|
|
||||||
.GroupBy(c => new { c.Name, c.IPAddress })
|
|
||||||
.Select(c => c.FirstOrDefault())
|
|
||||||
.ToList();*/
|
|
||||||
|
|
||||||
//newClients = clients.ToList();
|
|
||||||
//newClients.ForEach(c => c.ClientId = 0);
|
|
||||||
|
|
||||||
manager.GetLogger().WriteVerbose($"Read {clients.Count} clients for import");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SharedLibrary.Database.Importer.ImportClients(clients);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
manager.GetLogger().WriteError("Saving imported clients failed");
|
|
||||||
}
|
|
||||||
// #endif
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
// load the entire database lol
|
|
||||||
var ctx = new DatabaseContext();
|
|
||||||
ctx.Configuration.ProxyCreationEnabled = false;
|
|
||||||
var cls = ctx.Clients.Include("AliasLink.Children").ToList(); //manager.GetClientService().Find(c => c.Active).Result;
|
|
||||||
ctx.Dispose();
|
|
||||||
|
|
||||||
#region ALIASES
|
|
||||||
if (File.Exists("import_aliases.csv"))
|
|
||||||
{
|
|
||||||
manager.GetLogger().WriteVerbose("Beginning import of existing aliases");
|
|
||||||
|
|
||||||
var aliases = new List<EFAlias>();
|
|
||||||
|
|
||||||
var lines = File.ReadAllLines("import_aliases.csv").Skip(1);
|
|
||||||
foreach (string line in lines)
|
|
||||||
{
|
|
||||||
string[] fields = Regex.Replace(line, "\".*\"", "").Split(',');
|
|
||||||
fields.All(f =>
|
|
||||||
{
|
|
||||||
f = f.StripColors().Trim();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (fields.Length != 3)
|
|
||||||
{
|
|
||||||
manager.GetLogger().WriteError("Invalid alias import file... aborting import");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
int number = Int32.Parse(fields[0]);
|
|
||||||
var names = fields[1].Split(';').Where(n => n != String.Empty && n.Length > 2);
|
|
||||||
|
|
||||||
var oldClient = oldClients[number];
|
|
||||||
var newClient = cls.FirstOrDefault(c => c.NetworkId == oldClient.NetworkId);
|
|
||||||
|
|
||||||
foreach (string name in names)
|
|
||||||
{
|
|
||||||
// this is slow :D
|
|
||||||
if (newClient.AliasLink.Children.FirstOrDefault(n => n.Name == name) != null) continue;
|
|
||||||
var alias = new EFAlias()
|
|
||||||
{
|
|
||||||
Active = true,
|
|
||||||
DateAdded = DateTime.UtcNow,
|
|
||||||
Name = name,
|
|
||||||
LinkId = newClient.AliasLinkId,
|
|
||||||
IPAddress = newClient.IPAddress
|
|
||||||
};
|
|
||||||
|
|
||||||
aliases.Add(alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (KeyNotFoundException)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
manager.GetLogger().WriteVerbose($"Could not import alias with line {line}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SharedLibrary.Database.Importer.ImportSQLite(aliases);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
#region PENALTIES
|
|
||||||
if (File.Exists("import_penalties.csv"))
|
|
||||||
{
|
|
||||||
var penalties = new List<Penalty>();
|
|
||||||
manager.GetLogger().WriteVerbose("Beginning import of existing penalties");
|
|
||||||
foreach (string line in File.ReadAllLines("import_penalties.csv").Skip(1))
|
|
||||||
{
|
|
||||||
string comma = Regex.Match(line, "\".*,.*\"").Value.Replace(",", "");
|
|
||||||
string[] fields = Regex.Replace(line, "\".*,.*\"", comma).Split(',');
|
|
||||||
|
|
||||||
fields.All(f =>
|
|
||||||
{
|
|
||||||
f = f.StripColors().Trim();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (fields.Length != 7)
|
|
||||||
{
|
|
||||||
manager.GetLogger().WriteError("Invalid penalty import file... aborting import");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fields[2].Substring(0, 5) == "01100" || fields[2].Contains("0000000"))
|
|
||||||
continue;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
var expires = DateTime.Parse(fields[6]);
|
|
||||||
var when = DateTime.Parse(fields[5]);
|
|
||||||
|
|
||||||
var penaltyType = (Penalty.PenaltyType)Int32.Parse(fields[0]);
|
|
||||||
if (penaltyType == Penalty.PenaltyType.Ban)
|
|
||||||
expires = DateTime.MaxValue;
|
|
||||||
|
|
||||||
var penalty = new Penalty()
|
|
||||||
{
|
|
||||||
Type = penaltyType,
|
|
||||||
Expires = expires == DateTime.MinValue ? when : expires,
|
|
||||||
Punisher = new SharedLibrary.Database.Models.EFClient() { NetworkId = fields[3].ConvertLong() },
|
|
||||||
Offender = new SharedLibrary.Database.Models.EFClient() { NetworkId = fields[2].ConvertLong() },
|
|
||||||
Offense = fields[1].Replace("\"", "").Trim(),
|
|
||||||
Active = true,
|
|
||||||
When = when,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
penalties.Add(penalty);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
manager.GetLogger().WriteVerbose($"Could not import penalty with line {line}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#if DO_IMPORT
|
|
||||||
SharedLibrary.Database.Importer.ImportPenalties(penalties);
|
|
||||||
manager.GetLogger().WriteVerbose($"Imported {penalties.Count} penalties");
|
|
||||||
//#endif
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
#region CHATHISTORY
|
|
||||||
|
|
||||||
if (File.Exists("import_chathistory.csv"))
|
|
||||||
{
|
|
||||||
var chatHistory = new List<EFClientMessage>();
|
|
||||||
manager.GetLogger().WriteVerbose("Beginning import of existing messages");
|
|
||||||
foreach (string line in File.ReadAllLines("import_chathistory.csv").Skip(1))
|
|
||||||
{
|
|
||||||
string comma = Regex.Match(line, "\".*,.*\"").Value.Replace(",", "");
|
|
||||||
string[] fields = Regex.Replace(line, "\".*,.*\"", comma).Split(',');
|
|
||||||
|
|
||||||
fields.All(f =>
|
|
||||||
{
|
|
||||||
f = f.StripColors().Trim();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (fields.Length != 4)
|
|
||||||
{
|
|
||||||
manager.GetLogger().WriteError("Invalid chat history import file... aborting import");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
int cId = Convert.ToInt32(fields[0]);
|
|
||||||
var linkedClient = oldClients[cId];
|
|
||||||
|
|
||||||
var newcl = cls.FirstOrDefault(c => c.NetworkId == linkedClient.NetworkId);
|
|
||||||
if (newcl == null)
|
|
||||||
newcl = cls.FirstOrDefault(c => c.Name == linkedClient.Name && c.IPAddress == linkedClient.IPAddress);
|
|
||||||
int newCId = newcl.ClientId;
|
|
||||||
|
|
||||||
var chatMessage = new EFClientMessage()
|
|
||||||
{
|
|
||||||
Active = true,
|
|
||||||
ClientId = newCId,
|
|
||||||
Message = fields[1],
|
|
||||||
TimeSent = DateTime.Parse(fields[3]),
|
|
||||||
ServerId = Math.Abs($"127.0.0.1:{Convert.ToInt32(fields[2]).ToString()}".GetHashCode())
|
|
||||||
};
|
|
||||||
|
|
||||||
chatHistory.Add(chatMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
manager.GetLogger().WriteVerbose($"Could not import chatmessage with line {line}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
manager.GetLogger().WriteVerbose($"Read {chatHistory.Count} messages for import");
|
|
||||||
SharedLibrary.Database.Importer.ImportSQLite(chatHistory);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
#region STATS
|
|
||||||
foreach (string file in Directory.GetFiles(Environment.CurrentDirectory))
|
|
||||||
{
|
|
||||||
if (Regex.Match(file, @"import_stats_[0-9]+.csv").Success)
|
|
||||||
{
|
|
||||||
int port = Int32.Parse(Regex.Match(file, "[0-9]{5}").Value);
|
|
||||||
var stats = new List<EFClientStatistics>();
|
|
||||||
manager.GetLogger().WriteVerbose("Beginning import of existing client stats");
|
|
||||||
|
|
||||||
var lines = File.ReadAllLines(file).Skip(1);
|
|
||||||
foreach (string line in lines)
|
|
||||||
{
|
|
||||||
string[] fields = line.Split(',');
|
|
||||||
|
|
||||||
if (fields.Length != 9)
|
|
||||||
{
|
|
||||||
manager.GetLogger().WriteError("Invalid client import file... aborting import");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (fields[0].Substring(0, 5) == "01100")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
long id = fields[0].ConvertLong();
|
|
||||||
var client = cls.Single(c => c.NetworkId == id);
|
|
||||||
|
|
||||||
var time = Convert.ToInt32(fields[8]);
|
|
||||||
double spm = time < 60 ? 0 : Math.Round(Convert.ToInt32(fields[1]) * 100.0 / time, 3);
|
|
||||||
if (spm > 1000)
|
|
||||||
spm = 0;
|
|
||||||
|
|
||||||
var st = new EFClientStatistics()
|
|
||||||
{
|
|
||||||
Active = true,
|
|
||||||
ClientId = client.ClientId,
|
|
||||||
ServerId = Math.Abs($"127.0.0.1:{port}".GetHashCode()),
|
|
||||||
Kills = Convert.ToInt32(fields[1]),
|
|
||||||
Deaths = Convert.ToInt32(fields[2]),
|
|
||||||
SPM = spm,
|
|
||||||
Skill = 0,
|
|
||||||
TimePlayed = time * 60
|
|
||||||
};
|
|
||||||
// client.TotalConnectionTime += time;
|
|
||||||
stats.Add(st);
|
|
||||||
stats = stats.AsEnumerable()
|
|
||||||
.GroupBy(c => new { c.ClientId })
|
|
||||||
.Select(c => c.FirstOrDefault()).ToList();
|
|
||||||
|
|
||||||
var cl = await manager.GetClientService().Get(st.ClientId);
|
|
||||||
cl.TotalConnectionTime += time * 60;
|
|
||||||
await manager.GetClientService().Update(cl);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
manager.GetLogger().WriteVerbose($"Read {stats.Count} clients stats for import");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SharedLibrary.Database.Importer.ImportSQLite(stats);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
manager.GetLogger().WriteError("Saving imported stats failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task OnTickAsync(Server S)
|
public async Task OnTickAsync(Server S)
|
||||||
{
|
{
|
||||||
return;
|
/*
|
||||||
if ((DateTime.Now - Interval).TotalSeconds > 1)
|
if ((DateTime.Now - Interval).TotalSeconds > 1)
|
||||||
{
|
{
|
||||||
var rand = new Random();
|
var rand = new Random();
|
||||||
@ -492,13 +130,10 @@ namespace IW4MAdmin.Plugins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnUnloadAsync()
|
public Task OnUnloadAsync() => Task.CompletedTask;
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
@ -1,36 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
|
||||||
// set of attributes. Change these attribute values to modify the information
|
|
||||||
// associated with an assembly.
|
|
||||||
[assembly: AssemblyTitle("Tests")]
|
|
||||||
[assembly: AssemblyDescription("")]
|
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("")]
|
|
||||||
[assembly: AssemblyProduct("Tests")]
|
|
||||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
|
||||||
[assembly: AssemblyTrademark("")]
|
|
||||||
[assembly: AssemblyCulture("")]
|
|
||||||
|
|
||||||
// Setting ComVisible to false makes the types in this assembly not visible
|
|
||||||
// to COM components. If you need to access a type in this assembly from
|
|
||||||
// COM, set the ComVisible attribute to true on that type.
|
|
||||||
[assembly: ComVisible(false)]
|
|
||||||
|
|
||||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|
||||||
[assembly: Guid("b8c2a759-8663-4f6f-9ba4-19595f5e12c1")]
|
|
||||||
|
|
||||||
// Version information for an assembly consists of the following four values:
|
|
||||||
//
|
|
||||||
// Major Version
|
|
||||||
// Minor Version
|
|
||||||
// Build Number
|
|
||||||
// Revision
|
|
||||||
//
|
|
||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
|
||||||
// by using the '*' as shown below:
|
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -1,97 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProjectGuid>{B8C2A759-8663-4F6F-9BA4-19595F5E12C1}</ProjectGuid>
|
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<RootNamespace>Tests</RootNamespace>
|
<ApplicationIcon />
|
||||||
<AssemblyName>Tests</AssemblyName>
|
<StartupObject />
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
|
||||||
<FileAlignment>512</FileAlignment>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DebugType>full</DebugType>
|
<DefineConstants>TRACE;DEBUG;NETCOREAPP2_0</DefineConstants>
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Debug\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release-Nightly|AnyCPU' ">
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release-Nightly|x86' ">
|
|
||||||
<OutputPath>bin\x86\Release\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release-Stable|AnyCPU'">
|
|
||||||
<OutputPath>bin\Release-Stable\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release-Stable|x86'">
|
|
||||||
<OutputPath>bin\x86\Release-Stable\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
|
<Exec Command="copy "$(TargetPath)" "$(SolutionDir)BUILD\Plugins"" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
<ProjectReference Include="..\..\SharedLibraryCore\SharedLibraryCore.csproj" />
|
||||||
<Reference Include="System" />
|
|
||||||
<Reference Include="System.Core" />
|
|
||||||
<Reference Include="System.Xml.Linq" />
|
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
|
||||||
<Reference Include="Microsoft.CSharp" />
|
|
||||||
<Reference Include="System.Data" />
|
|
||||||
<Reference Include="System.Net.Http" />
|
|
||||||
<Reference Include="System.Xml" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="Plugin.cs" />
|
</Project>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\SharedLibrary\SharedLibrary.csproj">
|
|
||||||
<Project>{d51eeceb-438a-47da-870f-7d7b41bc24d6}</Project>
|
|
||||||
<Name>SharedLibrary</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
<ProjectReference Include="..\SimpleStats\StatsPlugin.csproj">
|
|
||||||
<Project>{4785ab75-66f3-4391-985d-63a5a049a0fa}</Project>
|
|
||||||
<Name>StatsPlugin</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
|
||||||
<PropertyGroup>
|
|
||||||
<PostBuildEvent>if $(ConfigurationName) == Debug (copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)BUILD\plugins\")</PostBuildEvent>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
|
||||||
|
207
README.md
207
README.md
@ -1,111 +1,155 @@
|
|||||||
# <span style="color: #007ACC;">IW4MAdmin</span>
|
|
||||||
### <span style="color: #007ACC; opacity:0.75;">Quick Start Guide</span>
|
# IW4MAdmin
|
||||||
### Version 1.5
|
### Quick Start Guide
|
||||||
|
### Version 2.0
|
||||||
_______
|
_______
|
||||||
|
### About
|
||||||
|
**IW4MAdmin** is an administration tool for [IW4x](https://iw4xcachep26muba.onion.link/), [T6M](https://plutonium.pw/), and most Call of Duty® dedicated servers. It allows complete control of your server; from changing maps, to banning players, **IW4MAdmin** monitors and records activity on your server(s). With plugin support, extending its functionality is a breeze.
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
IW4MAdmin requires minimal configuration to run. There is only one prerequisite.
|
**IW4MAdmin** requires minimal configuration to run. There is only one prerequisite.
|
||||||
1. [.NET Framework 4.5](https://www.microsoft.com/en-us/download/details.aspx?id=30653) *or newer*
|
* [.NET Core 2.0 Runtime](https://www.microsoft.com/net/download/dotnet-core/runtime-2.0.5) *or newer*
|
||||||
|
|
||||||
Extract `IW4MAdmin.zip`
|
1. Extract `IW4MAdmin-<version>.zip`
|
||||||
Run `IW4MAdmin.exe`
|
2. Open command prompt or terminal in the extracted folder
|
||||||
|
3. Run `>dotnet IW4MAdmin.dll`
|
||||||
___
|
___
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
_If you wish to customize your experience of IW4MAdmin, the following configuration files will allow you to changes core options._
|
#### Initial Configuration
|
||||||
|
When **IW4MAdmin** is launched for the _first time_, you will be prompted to setup your configuration.
|
||||||
|
|
||||||
`maps.cfg`
|
`Enable webfront`
|
||||||
* This is the configuration file that links an IW4 map file name to its common/in-game name
|
* Enables you to monitor and control your server(s) through a web interface [defaults to `http://127.0.0.1:1624`]
|
||||||
* This can be safely modified to add additional SP/DLC maps
|
|
||||||
|
|
||||||
`messages.cfg`
|
`Enable multiple owners`
|
||||||
* This is the configuration file that broadcasts messages to your server at a set time
|
* Enables more than one client to be promoted to level of `Owner`
|
||||||
* The _first line_ specifies the amount of time between messages (in seconds)
|
|
||||||
* Every new line is interpreted as a new message
|
|
||||||
* Color codes are allowed in the messages
|
|
||||||
* Tokens are denoted by double braces: {{TOKEN}}
|
|
||||||
|
|
||||||
`rules.cfg`
|
`Enable stepped privilege hierarchy`
|
||||||
* This is the configuration file that sets the server's rules.
|
* Allows privileged clients to promote other clients to the level below their current level
|
||||||
* Every new line is interpreted as a new rule
|
|
||||||
* All rules are _global_ across servers
|
|
||||||
|
|
||||||
`web.cfg`
|
`Enable custom say name`
|
||||||
* This is the configuration file that specifies the web front bindings
|
* Shows a prefix to every message send by **IW4MAdmin** -- `[Admin] message`
|
||||||
* The first line specifies the `IP` or `Hostname` to bind to
|
* _This feature requires you specify a custom say name_
|
||||||
* The second line specifies the `port` to bind to
|
|
||||||
|
|
||||||
|
`Enable client VPNs`
|
||||||
|
* Allow clients to use a [VPN](https://en.wikipedia.org/wiki/Virtual_private_network)
|
||||||
|
* _This feature requires an active api key on [iphub.info](https://iphub.info/)_
|
||||||
|
|
||||||
|
`Enable discord link`
|
||||||
|
* Shows a link to your server's discord on the webfront
|
||||||
|
* _This feature requires an invite link to your discord server_
|
||||||
|
|
||||||
|
#### Advanced Configuration
|
||||||
|
If you wish to further customize your experience of **IW4MAdmin**, the following configuration file(s) will allow you to changes core options using any text-editor.
|
||||||
|
|
||||||
|
#### `IW4MAdminSettings.json`-- _this file is created after initial setup_
|
||||||
|
* This file uses the [JSON](https://en.wikipedia.org/wiki/JSON#JSON_sample) specification, so please validate it before running **IW4MAdmin**
|
||||||
|
|
||||||
|
`WebfrontBindUrl`
|
||||||
|
* Specifies the address and port the webfront will listen on.
|
||||||
|
* The value can be an [IP Address](https://en.wikipedia.org/wiki/IP_address):port or [Domain Name](https://en.wikipedia.org/wiki/Domain_name):port
|
||||||
|
|
||||||
|
`Servers`
|
||||||
|
* Specifies the list of servers **IW4MAdmin** will monitor
|
||||||
|
* `IPAddress`
|
||||||
|
* Specifies the IP Address of the particular server
|
||||||
|
* `Port`
|
||||||
|
* Specifies the port of the particular server
|
||||||
|
* `Password`
|
||||||
|
* Specifies the `rcon_password` of the particular server
|
||||||
|
* `AutoMessages`
|
||||||
|
* Specifies the list of messages that are broadcasted to the particular server
|
||||||
|
* `Rules`
|
||||||
|
* Specifies the list of rules that apply to the particular server
|
||||||
|
|
||||||
|
`AutoMessagePeriod`
|
||||||
|
* Specifies (in seconds) how often messages should be broadcasted to the server(s)
|
||||||
|
|
||||||
|
`AutoMessages`
|
||||||
|
* Specifies the list of messages that are broadcasted to **all** servers
|
||||||
|
|
||||||
|
`GlobalRules`
|
||||||
|
* Specifies the list of rules that apply to **all** servers`
|
||||||
|
|
||||||
|
`Maps`
|
||||||
|
* Specifies the list of maps for each supported game
|
||||||
|
* `Name`
|
||||||
|
* Specifies the name of the map as returned by the game
|
||||||
|
* `Alias`
|
||||||
|
* Specifies the display name of the map (as seen while loading in)
|
||||||
___
|
___
|
||||||
|
|
||||||
### Commands
|
### Commands
|
||||||
|Name |Alias|Description |Requires Target|Syntax |Required Level|
|
|Name |Alias|Description |Requires Target|Syntax |Required Level|
|
||||||
|--------------| -----| --------------------------------------------------------| -----------------| -------------| ----------------|
|
|--------------| -----| --------------------------------------------------------| -----------------| -------------| ----------------|
|
||||||
|disabletrusted|dt|disable trusted player group for the server|False|!dt |Owner|
|
|prune|pa|demote any admins that have not connected recently (defaults to 30 days)|False|!pa \<optional inactive days\>|Owner|
|
||||||
|enabletrusted|et|enable trusted player group for the server|False|!et |Owner|
|
|
||||||
|prune|p|demote any admins that have not connected recently (defaults to 30 days)|False|!p \<optional inactive days\>|Owner|
|
|
||||||
|quit|q|quit IW4MAdmin|False|!q |Owner|
|
|quit|q|quit IW4MAdmin|False|!q |Owner|
|
||||||
|rcon|rcon|send rcon command to server|False|!rcon \<command\>|Owner|
|
|rcon|rcon|send rcon command to server|False|!rcon \<command\>|Owner|
|
||||||
|reload|rl|reload configuration files|False|!rl |Owner|
|
|
||||||
|setlevel|sl|set player to specified administration level|True|!sl \<player\> \<level\>|Owner|
|
|
||||||
|ban|b|permanently ban a player from the server|True|!b \<player\> \<reason\>|SeniorAdmin|
|
|ban|b|permanently ban a player from the server|True|!b \<player\> \<reason\>|SeniorAdmin|
|
||||||
|fredisable|frd|disable fast restarting at the end of a map|False|!frd |SeniorAdmin|
|
|unban|ub|unban player by database id|True|!ub \<databaseID\> \<reason\>|SeniorAdmin|
|
||||||
|frenable|fre|enable fast restarting at the end of a map|False|!fre |SeniorAdmin|
|
|
||||||
|unban|ub|unban player by database id|True|!ub \<databaseID\>|SeniorAdmin|
|
|
||||||
|find|f|find player in database|False|!f \<player\>|Administrator|
|
|find|f|find player in database|False|!f \<player\>|Administrator|
|
||||||
|findall|fa|find a player by their aliase(s)|False|!fa \<player\>|Administrator|
|
|killserver|kill|kill the game server|False|!kill |Administrator|
|
||||||
|map|m|change to specified map|False|!m \<map\>|Administrator|
|
|map|m|change to specified map|False|!m \<map\>|Administrator|
|
||||||
|maprotate|mr|cycle to the next map in rotation|False|!mr |Administrator|
|
|maprotate|mr|cycle to the next map in rotation|False|!mr |Administrator|
|
||||||
|mask|hide|hide your online presence from online admin list|False|!hide |Administrator|
|
|
||||||
|plugins|p|view all loaded plugins|False|!p |Administrator|
|
|plugins|p|view all loaded plugins|False|!p |Administrator|
|
||||||
|alias|known|get past aliases and ips of a player|True|!known \<player\>|Moderator|
|
|alias|known|get past aliases and ips of a player|True|!known \<player\>|Moderator|
|
||||||
|baninfo|bi|get information about a ban for a player|True|!bi \<player\>|Moderator|
|
|baninfo|bi|get information about a ban for a player|True|!bi \<player\>|Moderator|
|
||||||
|fastrestart|fr|fast restart current map|False|!fr |Moderator|
|
|fastrestart|fr|fast restart current map|False|!fr |Moderator|
|
||||||
|flag|fp|flag a suspicious player and announce to admins on join|True|!fp \<player\> \<reason\>|Moderator|
|
|flag|fp|flag a suspicious player and announce to admins on join|True|!fp \<player\> \<reason\>|Moderator|
|
||||||
|list|l|list active clients|False|!l |Moderator|
|
|list|l|list active clients|False|!l |Moderator|
|
||||||
|
|mask|hide|hide your presence as an administrator|False|!hide |Moderator|
|
||||||
|reports|reps|get or clear recent reports|False|!reps \<optional clear\>|Moderator|
|
|reports|reps|get or clear recent reports|False|!reps \<optional clear\>|Moderator|
|
||||||
|say|s|broadcast message to all players|False|!s \<message\>|Moderator|
|
|say|s|broadcast message to all players|False|!s \<message\>|Moderator|
|
||||||
|
|setlevel|sl|set player to specified administration level|True|!sl \<player\> \<level\>|Moderator|
|
||||||
|
|setpassword|sp|set your authentication password|False|!sp \<password\>|Moderator|
|
||||||
|tempban|tb|temporarily ban a player for specified time (defaults to 1 hour)|True|!tb \<player\> \<duration (m\|h\|d\|w\|y)\> \<reason\>|Moderator|
|
|tempban|tb|temporarily ban a player for specified time (defaults to 1 hour)|True|!tb \<player\> \<duration (m\|h\|d\|w\|y)\> \<reason\>|Moderator|
|
||||||
|uptime|up|get current application running time|False|!up |Moderator|
|
|uptime|up|get current application running time|False|!up |Moderator|
|
||||||
|usage|us|get current application memory usage|False|!us |Moderator|
|
|usage|us|get current application memory usage|False|!us |Moderator|
|
||||||
|kick|k|kick a player by name|True|!k \<player\> \<reason\>|Trusted|
|
|kick|k|kick a player by name|True|!k \<player\> \<reason\>|Trusted|
|
||||||
|
|login|l|login using password|False|!l \<password\>|Trusted|
|
||||||
|warn|w|warn player for infringing rules|True|!w \<player\> \<reason\>|Trusted|
|
|warn|w|warn player for infringing rules|True|!w \<player\> \<reason\>|Trusted|
|
||||||
|warnclear|wc|remove all warning for a player|True|!wc \<player\>|Trusted|
|
|warnclear|wc|remove all warning for a player|True|!wc \<player\>|Trusted|
|
||||||
|admins|a|list currently connected admins|False|!a |User|
|
|admins|a|list currently connected admins|False|!a |User|
|
||||||
|getexternalip|ip|view your external IP address|False|!ip |User|
|
|getexternalip|ip|view your external IP address|False|!ip |User|
|
||||||
|help|h|list all available commands|False|!h \<optional command\>|User|
|
|help|h|list all available commands|False|!h \<optional command\>|User|
|
||||||
|
|ping|pi|get client's ping|False|!pi \<optional client\>|User|
|
||||||
|privatemessage|pm|send message to other player|True|!pm \<player\> \<message\>|User|
|
|privatemessage|pm|send message to other player|True|!pm \<player\> \<message\>|User|
|
||||||
|report|rep|report a player for suspicious behavior|True|!rep \<player\> \<reason\>|User|
|
|report|rep|report a player for suspicious behavior|True|!rep \<player\> \<reason\>|User|
|
||||||
|resetstats|rs|reset your stats to factory-new|False|!rs |User|
|
|resetstats|rs|reset your stats to factory-new|False|!rs |User|
|
||||||
|rules|r|list server rules|False|!r |User|
|
|rules|r|list server rules|False|!r |User|
|
||||||
|stats|xlrstats|view your stats|False|!xlrstats \<optional player\>|User|
|
|stats|xlrstats|view your stats|False|!xlrstats \<optional player\>|User|
|
||||||
|topstats|ts|view the top 5 players on this server|False|!ts |User|
|
|topstats|ts|view the top 5 players on this server|False|!ts |User|
|
||||||
|vote|v|vote for the next map|False|!v \<map\>|User|
|
|
||||||
|votecancel|vc|cancel your vote for the next map|False|!vc |User|
|
|
||||||
|whoami|who|give information about yourself.|False|!who |User|
|
|whoami|who|give information about yourself.|False|!who |User|
|
||||||
|
_These commands include all shipped plugin commands._
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
#### Player Identification
|
#### Player Identification
|
||||||
All players are identified 4 seperate ways
|
All players are identified 4 seperate ways
|
||||||
1. `npID/GUID/XUID` - The ID corresponding to the player's hardware or forum account
|
1. `npID/GUID/XUID` - The ID corresponding to the player's hardware or forum account
|
||||||
2. `IP` - The player's IP Address
|
2. `IP` - The player's IP Address
|
||||||
3. `Database ID` - The internal reference to a player, generated by IW4MAdmin
|
3. `Client ID` - The internal reference to a player, generated by **IW4MAdmin**
|
||||||
4. `Name` - The visible player name as it appears in game
|
4. `Name` - The visible player name as it appears in game
|
||||||
|
|
||||||
For most commands players are identified by their `Name`
|
For most commands players are identified by their `Name`
|
||||||
However, if they are currently offline, or their name contains un-typable characters, their `Database ID` must be used
|
However, if they are currently offline, or their name contains un-typable characters, their `Client ID` must be used
|
||||||
|
|
||||||
The `dbID` is specified by prefixing a player's reference number with `@`.
|
The `Client ID` is specified by prefixing a player's reference number with `@`.
|
||||||
For example, `@123` would reference the player with a `dbID` of 123.
|
For example, `@123` would reference the player with a `Client ID` of 123.
|
||||||
Players can also be referenced by `clientID`, which is simply their slot (0 - 17)
|
While in-game, [layers can also be referenced by `Client Number`, which is simply their slot [0 - 17]
|
||||||
|
|
||||||
**All commands that require a `target` look at the `first argument` for a form of player identification**
|
**All commands that require a `target` look at the `first argument` for a form of player identification**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
#### Additional Command Parameters
|
#### Additional Command Examples
|
||||||
`setlevel`
|
`setlevel`
|
||||||
- _shortcut_ - `sl`
|
- _shortcut_ - `sl`
|
||||||
- _Parameter 1_ - Player to modify level of
|
- _Parameter 1_ - Player to modify level of
|
||||||
- _Parameter 2_ - Level to set the player to ```[ User, Trusted, Moderator, Administrator, SeniorAdmin ]```
|
- _Parameter 2_ - Level to set the player to ```[ User, Trusted, Moderator, Administrator, SeniorAdmin, Owner ]```
|
||||||
- _Example_ - `!setlevel Player1 SeniorAdmin`, `!sl @123 Moderator`
|
- _Example_ - `!setlevel Player1 SeniorAdmin`, `!sl @123 Moderator`
|
||||||
- **NOTE** - It has been purposefully designed that there should only be **1 Owner** ( owner cannot set another player's level to owner unless the configuration option is enabled during setup)
|
- **NOTE** - An `owner` cannot set another player's level to `owner` unless the configuration option is enabled during setup
|
||||||
|
|
||||||
`ban`
|
`ban`
|
||||||
- _Shortcut_ - `b`
|
- _Shortcut_ - `b`
|
||||||
@ -126,58 +170,57 @@ Players can also be referenced by `clientID`, which is simply their slot (0 - 17
|
|||||||
|
|
||||||
___
|
___
|
||||||
### Plugins
|
### Plugins
|
||||||
#### EventAPI
|
|
||||||
- This plugin adds a page to the webfront that serves JSON content in the form of server events
|
|
||||||
- The page is located at 127.0.0.1/api/events
|
|
||||||
- JSON Object Structure
|
|
||||||
* **eventCount** - Number of events in the generated response ( 0 or 1 )
|
|
||||||
* **Event** - The event object corresponding to generated event ( will be null if eventCount = 0 )
|
|
||||||
* _Version_ - The supported version of the Event Object ( IW4MAdmin = 0 )
|
|
||||||
* _Type_ - The type of Event Object ( Notification = 0, Status = 1, Alert = 2 )
|
|
||||||
* _Message_ - The string contents of the Event Object ( ie chat message text )
|
|
||||||
* _Title_ - The string header/title of the Event Object ( optional )
|
|
||||||
* _Origin_ - The string origin of the Event Object ( ie player name or sv_hostname )
|
|
||||||
* _Target_ - The string target of the Event Object ( ie reported player's name )
|
|
||||||
* _ID_ - The int ID of the Event Object ( will be unique unless two events are generated simultaneously )
|
|
||||||
- Optional Parameters
|
|
||||||
* appending a `GET` parameter of `status=1` to your request will generate a list of currently monitored servers
|
|
||||||
* For example: 127.0.0.1/api/events?status=1
|
|
||||||
* The contents of the response will be in the `Message` property of the response
|
|
||||||
- Each event will be consumed ( eaten by the request, so save the event if you need to use it later )
|
|
||||||
- The plugin additionally scans chat messages for phrases that indicate a cheater on the server
|
|
||||||
- If enough matching phrases are detected, an alert will be generated
|
|
||||||
- No commands are added by this plugin
|
|
||||||
- Additional Features will be added in the future
|
|
||||||
#### Welcome
|
#### Welcome
|
||||||
- This plugin uses geolocation data to welcome a player based on their IP's country
|
- This plugin uses geo-location data to welcome a player based on their country of origin
|
||||||
- All privileged users ( Trusted or higher ) recieve a specialized welcome message as well
|
- All privileged users ( Trusted or higher ) receive a specialized welcome message as well
|
||||||
|
- Welcome messages can be customized in `WelcomePluginSettings.json`
|
||||||
|
|
||||||
#### Stats
|
#### Stats
|
||||||
- This plugin calculates basic player performance, skill approximation, and kill/death ratio
|
- This plugin calculates basic player performance, skill approximation, and kill/death ratio
|
||||||
- Total play-time is stored by this plugin
|
|
||||||
- After 3 days ( 36 hours ) of total play-time, a user earns the `Trusted` rank ( will be optional in a later release )
|
|
||||||
|
|
||||||
**Commands added by this plugin**
|
**Commands added by this plugin**
|
||||||
|
|
||||||
|
|
||||||
|Name |Alias|Description |Requires Target|Syntax |Required Level|
|
|Name |Alias|Description |Requires Target|Syntax |Required Level|
|
||||||
|--------------| -----| --------------------------------------------------------| -----------------| -------------| ----------------|
|
|--------------| -----| --------------------------------------------------------| -----------------| -------------| ----------------|
|
||||||
|disabletrusted|dt|disable trusted player group for the server|False|!dt |Owner|
|
|
||||||
|enabletrusted|et|enable trusted player group for the server|False|!et |Owner|
|
|
||||||
|prune|p|demote any admins that have not connected recently (defaults to 30 days)|False|!p \<optional inactive days\>|Owner|
|
|
||||||
|resetstats|rs|reset your stats to factory-new|False|!rs |User|
|
|resetstats|rs|reset your stats to factory-new|False|!rs |User|
|
||||||
|stats|xlrstats|view your stats|False|!xlrstats \<optional player\>|User|
|
|stats|xlrstats|view your stats|False|!xlrstats \<optional player\>|User|
|
||||||
|topstats|ts|view the top 5 players on this server|False|!ts |User|
|
|topstats|ts|view the top 5 players on this server|False|!ts |User|
|
||||||
|
|
||||||
- To qualify for top stats, a player must meet the following criteria
|
- To qualify for top stats, a client must have played for at least `1 hour` and connected within the past `30 days`.
|
||||||
* `Skill` > 10
|
|
||||||
* `Kills` > 150
|
|
||||||
* `Play Time` > 1 hour
|
|
||||||
|
|
||||||
- Each server has seperated stats and can be reset by deleting `stats_<port>.rm`
|
#### Login
|
||||||
|
- This plugin deters GUID spoofing by requiring privileged users to login with their password before executing commands
|
||||||
|
- A password must be set using the `setpassword` command before logging in
|
||||||
|
|
||||||
|
**Commands added by this plugin**
|
||||||
|
|Name |Alias|Description |Requires Target|Syntax |Required Level|
|
||||||
|
|--------------| -----| --------------------------------------------------------| -----------------| -------------| ----------------|
|
||||||
|
|login|l|login using password|False|!l \<password\>|Trusted|
|
||||||
|
|
||||||
|
#### Profanity Determent
|
||||||
|
- This plugin warns and kicks players for using profanity
|
||||||
|
- Profane words and warning message can be specified in `ProfanityDetermentSettings.json`
|
||||||
___
|
___
|
||||||
|
### Webfront
|
||||||
|
`Home`
|
||||||
|
* Shows an overview of the monitored server(s)
|
||||||
|
|
||||||
|
`Penalties`
|
||||||
|
* Shows a chronological ordered list of client penalties (scrolling down loads older penalties)
|
||||||
|
|
||||||
|
`Admins`
|
||||||
|
* Shows a list of privileged clients
|
||||||
|
|
||||||
|
`Login`
|
||||||
|
* Allows privileged users to login using their `Client ID` and password set via `setpassword`
|
||||||
|
|
||||||
|
`Profile`
|
||||||
|
* Shows a client's information and history
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Misc
|
### Misc
|
||||||
#### Database Storage
|
#### Database Storage
|
||||||
All unique client information is stored in `clients.rm`. Should you need to reset your database, this file can simply be deleted.
|
All **IW4MAdmin** information is stored in `Database.db`. Should you need to reset your database, this file can simply be deleted. Additionally, this file should be preserved during updates to retain client information.
|
||||||
Player aliases and previous ips are stored in `aliases.rm`.
|
|
@ -39,7 +39,7 @@ namespace SharedLibraryCore
|
|||||||
if (fileName != string.Empty)
|
if (fileName != string.Empty)
|
||||||
{
|
{
|
||||||
Name = fileName;
|
Name = fileName;
|
||||||
Handle = new StreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, true), Encoding.UTF7);
|
Handle = new StreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, true), Encoding.UTF8);
|
||||||
|
|
||||||
sze = Handle.BaseStream.Length;
|
sze = Handle.BaseStream.Length;
|
||||||
}
|
}
|
||||||
|
@ -267,6 +267,11 @@ namespace SharedLibraryCore
|
|||||||
return Game.UKN;
|
return Game.UKN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string EscapeMarkdown(this string markdownString)
|
||||||
|
{
|
||||||
|
return markdownString.Replace("<", "\\<").Replace(">", "\\>").Replace("|", "\\|");
|
||||||
|
}
|
||||||
|
|
||||||
public static TimeSpan ParseTimespan(this string input)
|
public static TimeSpan ParseTimespan(this string input)
|
||||||
{
|
{
|
||||||
var expressionMatch = Regex.Match(input, @"[0-9]+.\b");
|
var expressionMatch = Regex.Match(input, @"[0-9]+.\b");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user