Added additional properties method to allow easier extension to client properties

updated VPN plugin to use WebClient
message is sent to client trying to execute commands before they are authenticated
fixed rare issue with ToAdmins failing
record bullet distance fraction for client kills (_customcallbacks)
change client level/permissions through webfront
ability to tempban through webfront
This commit is contained in:
RaidMax 2018-09-02 16:59:27 -05:00
parent cc7628d058
commit e77ef69ee8
33 changed files with 1099 additions and 177 deletions

View File

@ -17,4 +17,8 @@ xcopy /y "%SolutionDir%Build\Plugins" "%TargetDir%Plugins\"
echo Copying plugins for publish echo Copying plugins for publish
del %SolutionDir%BUILD\Plugins\Tests.dll del %SolutionDir%BUILD\Plugins\Tests.dll
xcopy /Y "%SolutionDir%BUILD\Plugins" "%SolutionDir%Publish\Windows\Plugins\" xcopy /Y "%SolutionDir%BUILD\Plugins" "%SolutionDir%Publish\Windows\Plugins\"
xcopy /Y "%SolutionDir%BUILD\Plugins" "%SolutionDir%Publish\WindowsPrerelease\Plugins\" xcopy /Y "%SolutionDir%BUILD\Plugins" "%SolutionDir%Publish\WindowsPrerelease\Plugins\"
echo Copying script plugins for publish
xcopy /Y "%SolutionDir%Plugins\ScriptPlugins" "%SolutionDir%Publish\Windows\Plugins\"
xcopy /Y "%SolutionDir%Plugins\ScriptPlugins" "%SolutionDir%Publish\WindowsPrerelease\Plugins\"

View File

@ -78,6 +78,7 @@ namespace IW4MAdmin.Application.EventParsers
Type = GameEvent.EventType.Kill, Type = GameEvent.EventType.Kill,
Data = logLine, Data = logLine,
Origin = origin, Origin = origin,
Target = target,
Owner = server Owner = server
}; };
} }
@ -116,19 +117,22 @@ namespace IW4MAdmin.Application.EventParsers
// damage // damage
if (eventType == "D") if (eventType == "D")
{ {
if (Regex.Match(eventType, @"^(D);((?:bot[0-9]+)|(?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$").Success) if (!server.CustomCallback)
{ {
var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong()); if (Regex.Match(eventType, @"^(D);((?:bot[0-9]+)|(?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$").Success)
var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
return new GameEvent()
{ {
Type = GameEvent.EventType.Damage, var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong());
Data = eventType, var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
Origin = origin,
Target = target, return new GameEvent()
Owner = server {
}; Type = GameEvent.EventType.Damage,
Data = eventType,
Origin = origin,
Target = target,
Owner = server
};
}
} }
} }

View File

@ -27,56 +27,54 @@ namespace IW4MAdmin.Application
#if DEBUG #if DEBUG
Manager.GetLogger().WriteDebug($"Got new event of type {gameEvent.Type} for {gameEvent.Owner} with id {gameEvent.Id}"); Manager.GetLogger().WriteDebug($"Got new event of type {gameEvent.Type} for {gameEvent.Owner} with id {gameEvent.Id}");
#endif #endif
GameEvent sortedEvent = null; // GameEvent sortedEvent = null;
lock (OutOfOrderEvents) // lock (OutOfOrderEvents)
{ // {
sortedEvent = OutOfOrderEvents.Values.FirstOrDefault(); // sortedEvent = OutOfOrderEvents.Values.FirstOrDefault();
}
while (sortedEvent?.Id == Interlocked.Read(ref NextEventId)) // while (sortedEvent?.Id == Interlocked.Read(ref NextEventId))
{ // {
lock (OutOfOrderEvents) // if (OutOfOrderEvents.Count > 0)
{ // {
OutOfOrderEvents.RemoveAt(0); // OutOfOrderEvents.RemoveAt(0);
} // }
AddEvent(sortedEvent);
lock (OutOfOrderEvents) // AddEvent(sortedEvent);
{ // sortedEvent = OutOfOrderEvents.Values.FirstOrDefault();
sortedEvent = OutOfOrderEvents.Values.FirstOrDefault(); // }
} // }
}
// both the gameEvent Id and the LastEventId are thread safe because we want to synchronize when the // // both the gameEvent Id and the LastEventId are thread safe because we want to synchronize when the
// event occurs // // event occurs
if (gameEvent.Id == Interlocked.Read(ref NextEventId)) // if (gameEvent.Id == Interlocked.Read(ref NextEventId))
{ // {
#if DEBUG == true //#if DEBUG == true
Manager.GetLogger().WriteDebug($"sent event with id {gameEvent.Id} to be processed"); // Manager.GetLogger().WriteDebug($"sent event with id {gameEvent.Id} to be processed");
#endif //#endif
((Manager as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent)); ((Manager as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent));
Interlocked.Increment(ref NextEventId); return true;
} // Interlocked.Increment(ref NextEventId);
// }
// a "newer" event has been added before and "older" one has been added (due to threads and context switching) // // a "newer" event has been added before and "older" one has been added (due to threads and context switching)
// so me must wait until the next expected one arrives // // so me must wait until the next expected one arrives
else // else
{ // {
#if DEBUG == true //#if DEBUG == true
Manager.GetLogger().WriteWarning("Incoming event is out of order"); // Manager.GetLogger().WriteWarning("Incoming event is out of order");
Manager.GetLogger().WriteDebug($"Expected event Id is {Interlocked.Read(ref NextEventId)}, but got {gameEvent.Id} instead"); // Manager.GetLogger().WriteDebug($"Expected event Id is {Interlocked.Read(ref NextEventId)}, but got {gameEvent.Id} instead");
#endif //#endif
// this prevents multiple threads from adding simultaneously // // this prevents multiple threads from adding simultaneously
lock (OutOfOrderEvents) // lock (OutOfOrderEvents)
{ // {
if (!OutOfOrderEvents.TryGetValue(gameEvent.Id, out GameEvent discard)) // if (!OutOfOrderEvents.TryGetValue(gameEvent.Id, out GameEvent discard))
{ // {
OutOfOrderEvents.Add(gameEvent.Id, gameEvent); // OutOfOrderEvents.Add(gameEvent.Id, gameEvent);
} // }
} // }
} // }
return true; // return true;
} }
} }
} }

View File

@ -50,6 +50,8 @@ namespace IW4MAdmin.Application
Localization.Configure.Initialize(ServerManager.GetApplicationSettings().Configuration()?.CustomLocale); Localization.Configure.Initialize(ServerManager.GetApplicationSettings().Configuration()?.CustomLocale);
loc = Utilities.CurrentLocalization.LocalizationIndex; loc = Utilities.CurrentLocalization.LocalizationIndex;
ServerManager.Logger.WriteInfo($"Version is {Version}");
var api = API.Master.Endpoint.Get(); var api = API.Master.Endpoint.Get();
var version = new API.Master.VersionInfo() var version = new API.Master.VersionInfo()

View File

@ -37,7 +37,7 @@ namespace IW4MAdmin.Application
// define what the delagate function looks like // define what the delagate function looks like
public delegate void OnServerEventEventHandler(object sender, GameEventArgs e); public delegate void OnServerEventEventHandler(object sender, GameEventArgs e);
// expose the event handler so we can execute the events // expose the event handler so we can execute the events
public OnServerEventEventHandler OnServerEvent { get; set; } public OnServerEventEventHandler OnServerEvent { get; set; }
public DateTime StartTime { get; private set; } public DateTime StartTime { get; private set; }
static ApplicationManager Instance; static ApplicationManager Instance;
@ -81,6 +81,11 @@ namespace IW4MAdmin.Application
if (GameEvent.ShouldOriginEventBeDelayed(newEvent)) if (GameEvent.ShouldOriginEventBeDelayed(newEvent))
{ {
Logger.WriteDebug($"Delaying origin execution of event type {newEvent.Type} for {newEvent.Origin} because they are not authed"); Logger.WriteDebug($"Delaying origin execution of event type {newEvent.Type} for {newEvent.Origin} because they are not authed");
if (newEvent.Type == GameEvent.EventType.Command)
{
await newEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["SERVER_DELAYED_EVENT_WAIT"]);
}
// offload it to the player to keep // offload it to the player to keep
newEvent.Origin.DelayedEvents.Enqueue(newEvent); newEvent.Origin.DelayedEvents.Enqueue(newEvent);
return; return;
@ -95,7 +100,7 @@ namespace IW4MAdmin.Application
return; return;
} }
//// todo: this is a hacky mess //// todo: this is a hacky mess
if (newEvent.Origin?.DelayedEvents.Count > 0 && if (newEvent.Origin?.DelayedEvents.Count > 0 &&
@ -104,7 +109,7 @@ namespace IW4MAdmin.Application
var events = newEvent.Origin.DelayedEvents; var events = newEvent.Origin.DelayedEvents;
// add the delayed event to the queue // add the delayed event to the queue
while(events.Count > 0) while (events.Count > 0)
{ {
var oldEvent = events.Dequeue(); var oldEvent = events.Dequeue();
@ -143,7 +148,7 @@ namespace IW4MAdmin.Application
await newEvent.Owner.ExecuteEvent(newEvent); await newEvent.Owner.ExecuteEvent(newEvent);
#if DEBUG #if DEBUG
Logger.WriteDebug($"Processed event with id {newEvent.Id}"); Logger.WriteDebug($"Processed event with id {newEvent.Id}");
#endif #endif
} }

View File

@ -19,7 +19,6 @@ using SharedLibraryCore.Localization;
using IW4MAdmin.Application.RconParsers; using IW4MAdmin.Application.RconParsers;
using IW4MAdmin.Application.EventParsers; using IW4MAdmin.Application.EventParsers;
using IW4MAdmin.Application.IO; using IW4MAdmin.Application.IO;
using IW4MAdmin.Application.Core;
namespace IW4MAdmin namespace IW4MAdmin
{ {
@ -300,40 +299,6 @@ namespace IW4MAdmin
} }
} }
//// this allows us to catch exceptions but still run it parallel
//async Task pluginHandlingAsync(Task onEvent, string pluginName)
//{
// try
// {
// await onEvent;
// }
// // this happens if a plugin (login) wants to stop commands from executing
// catch (AuthorizationException e)
// {
// await E.Origin.Tell($"{loc["COMMAND_NOTAUTHORIZED"]} - {e.Message}");
// canExecuteCommand = false;
// }
// catch (Exception Except)
// {
// Logger.WriteError($"{loc["SERVER_PLUGIN_ERROR"]} [{pluginName}]");
// Logger.WriteDebug(String.Format("Error Message: {0}", Except.Message));
// Logger.WriteDebug(String.Format("Error Trace: {0}", Except.StackTrace));
// while (Except.InnerException != null)
// {
// Except = Except.InnerException;
// Logger.WriteDebug($"Inner exception: {Except.Message}");
// }
// }
//}
//var pluginTasks = SharedLibraryCore.Plugins.PluginImporter.ActivePlugins.
// Select(p => pluginHandlingAsync(p.OnEventAsync(E, this), p.Name));
//// execute all the plugin updates simultaneously
//await Task.WhenAll(pluginTasks);
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins) foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
{ {
try try
@ -629,6 +594,13 @@ namespace IW4MAdmin
// this are our new connecting clients // this are our new connecting clients
foreach (var client in polledClients[0]) foreach (var client in polledClients[0])
{ {
// this prevents duplicate events from being sent to the event api
if (GetPlayersAsList().Count(c => c.NetworkId == client.NetworkId &&
c.State == Player.ClientState.Connected) != 0)
{
continue;
}
var e = new GameEvent() var e = new GameEvent()
{ {
Type = GameEvent.EventType.Connect, Type = GameEvent.EventType.Connect,
@ -810,7 +782,7 @@ namespace IW4MAdmin
logfile = await this.GetDvarAsync<string>("g_log"); logfile = await this.GetDvarAsync<string>("g_log");
} }
CustomCallback = await ScriptLoaded(); //CustomCallback = await ScriptLoaded();
string mainPath = EventParser.GetGameDir(); string mainPath = EventParser.GetGameDir();
#if DEBUG #if DEBUG
basepath.Value = @"D:\"; basepath.Value = @"D:\";

View File

@ -24,20 +24,20 @@ const plugin = {
let usingVPN = false; let usingVPN = false;
try { try {
let httpRequest = System.Net.WebRequest.Create('https://api.xdefcon.com/proxy/check/?ip=' + origin.IPAddressString); let cl = new System.Net.Http.HttpClient();
let response = httpRequest.GetResponse(); let re = cl.GetAsync('https://api.xdefcon.com/proxy/check/?ip=' + origin.IPAddressString).Result;
let data = response.GetResponseStream(); let co = re.Content;
let streamReader = new System.IO.StreamReader(data); let parsedJSON = JSON.parse(co.ReadAsStringAsync().Result);
let jsonResponse = streamReader.ReadToEnd(); //co.Dispose();
streamReader.Dispose(); //re.Dispose();
response.Close(); //cl.Dispose();
let parsedJSON = JSON.parse(jsonResponse);
usingVPN = parsedJSON['success'] && parsedJSON['proxy']; usingVPN = parsedJSON['success'] && parsedJSON['proxy'];
} catch (e) { } catch (e) {
this.logger.WriteError(e.message); this.logger.WriteError(e.message);
} }
if (usingVPN) { if (usingVPN) {
this.logger.WriteInfo(origin + ' is using a VPN (' + origin.IPAddressString + ')');
let library = importNamespace('SharedLibraryCore'); let library = importNamespace('SharedLibraryCore');
let kickOrigin = new library.Objects.Player(); let kickOrigin = new library.Objects.Player();
kickOrigin.ClientId = 1; kickOrigin.ClientId = 1;

View File

@ -324,7 +324,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public async Task AddScriptHit(bool isDamage, DateTime time, Player attacker, Player victim, int serverId, string map, string hitLoc, string type, public async Task AddScriptHit(bool isDamage, DateTime time, Player attacker, Player victim, int serverId, string map, string hitLoc, string type,
string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads, string snapAngles) string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads, string fraction, string snapAngles)
{ {
var statsSvc = ContextThreads[serverId]; var statsSvc = ContextThreads[serverId];
Vector3 vDeathOrigin = null; Vector3 vDeathOrigin = null;
@ -362,7 +362,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
return; return;
} }
var kill = new EFClientKill() var hit = new EFClientKill()
{ {
Active = true, Active = true,
AttackerId = attacker.ClientId, AttackerId = attacker.ClientId,
@ -380,11 +380,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
When = time, When = time,
IsKillstreakKill = isKillstreakKill[0] != '0', IsKillstreakKill = isKillstreakKill[0] != '0',
AdsPercent = float.Parse(Ads), AdsPercent = float.Parse(Ads),
Fraction = double.Parse(fraction),
IsKill = !isDamage,
AnglesList = snapshotAngles AnglesList = snapshotAngles
}; };
if (kill.DeathType == IW4Info.MeansOfDeath.MOD_SUICIDE && if (hit.DeathType == IW4Info.MeansOfDeath.MOD_SUICIDE &&
kill.Damage == 100000) hit.Damage == 100000)
{ {
// suicide by switching teams so let's not count it against them // suicide by switching teams so let's not count it against them
return; return;
@ -395,7 +397,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
await AddStandardKill(attacker, victim); await AddStandardKill(attacker, victim);
} }
if (kill.IsKillstreakKill) if (hit.IsKillstreakKill)
{ {
return; return;
} }
@ -406,11 +408,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
clientStatsSvc.Update(clientStats); clientStatsSvc.Update(clientStats);
// increment their hit count // increment their hit count
if (kill.DeathType == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET || if (hit.DeathType == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET ||
kill.DeathType == IW4Info.MeansOfDeath.MOD_RIFLE_BULLET || hit.DeathType == IW4Info.MeansOfDeath.MOD_RIFLE_BULLET ||
kill.DeathType == IW4Info.MeansOfDeath.MOD_HEAD_SHOT) hit.DeathType == IW4Info.MeansOfDeath.MOD_HEAD_SHOT)
{ {
clientStats.HitLocations.Single(hl => hl.Location == kill.HitLoc).HitCount += 1; clientStats.HitLocations.Single(hl => hl.Location == hit.HitLoc).HitCount += 1;
} }
if (Plugin.Config.Configuration().EnableAntiCheat) if (Plugin.Config.Configuration().EnableAntiCheat)
@ -478,11 +480,17 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
} }
} }
await executePenalty(clientDetection.ProcessKill(kill, isDamage)); await executePenalty(clientDetection.ProcessKill(hit, isDamage));
await executePenalty(clientDetection.ProcessTotalRatio(clientStats)); await executePenalty(clientDetection.ProcessTotalRatio(clientStats));
await clientStatsSvc.SaveChangesAsync(); await clientStatsSvc.SaveChangesAsync();
} }
using (var ctx = new DatabaseContext())
{
ctx.Set<EFClientKill>().Add(hit);
await ctx.SaveChangesAsync();
}
} }
public async Task AddStandardKill(Player attacker, Player victim) public async Task AddStandardKill(Player attacker, Player victim)

View File

@ -29,6 +29,8 @@ namespace IW4MAdmin.Plugins.Stats.Models
public Vector3 DeathOrigin { get; set; } public Vector3 DeathOrigin { get; set; }
public Vector3 ViewAngles { get; set; } public Vector3 ViewAngles { get; set; }
public DateTime When { get; set; } public DateTime When { get; set; }
public double Fraction { get; set; }
public bool IsKill { get; set; }
// http://wiki.modsrepository.com/index.php?title=Call_of_Duty_5:_Gameplay_standards for conversion to meters // http://wiki.modsrepository.com/index.php?title=Call_of_Duty_5:_Gameplay_standards for conversion to meters
[NotMapped] [NotMapped]
public double Distance => Vector3.Distance(KillOrigin, DeathOrigin) * 0.0254; public double Distance => Vector3.Distance(KillOrigin, DeathOrigin) * 0.0254;

View File

@ -73,10 +73,10 @@ namespace IW4MAdmin.Plugins.Stats
break; break;
case GameEvent.EventType.ScriptKill: case GameEvent.EventType.ScriptKill:
string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0]; string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
if (killInfo.Length >= 13) if (killInfo.Length >= 14)
{ {
await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8], await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13]); killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14]);
} }
break; break;
case GameEvent.EventType.Kill: case GameEvent.EventType.Kill:
@ -84,15 +84,15 @@ namespace IW4MAdmin.Plugins.Stats
await Manager.AddStandardKill(E.Origin, E.Target); await Manager.AddStandardKill(E.Origin, E.Target);
break; break;
case GameEvent.EventType.Damage: case GameEvent.EventType.Damage:
// if (!E.Owner.CustomCallback) if (!E.Owner.CustomCallback)
Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, E.Owner.GetHashCode()); Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, E.Owner.GetHashCode());
break; break;
case GameEvent.EventType.ScriptDamage: case GameEvent.EventType.ScriptDamage:
killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0]; killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
if (killInfo.Length >= 13) if (killInfo.Length >= 14)
{ {
await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8], await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13]); killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14]);
} }
break; break;
} }

View File

@ -59,7 +59,7 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers
} }
[HttpGet] [HttpGet]
[Authorize] // [Authorize]
public async Task<IActionResult> GetAutomatedPenaltyInfoAsync(int clientId) public async Task<IActionResult> GetAutomatedPenaltyInfoAsync(int clientId)
{ {
using (var ctx = new SharedLibraryCore.Database.DatabaseContext()) using (var ctx = new SharedLibraryCore.Database.DatabaseContext())

View File

@ -0,0 +1,29 @@
using SharedLibraryCore.Objects;
using System;
using System.Collections.Generic;
using System.Text;
using Xunit;
namespace Tests
{
public class ClientTests
{
[Fact]
public void SetAdditionalPropertyShouldSucceed()
{
var client = new Player();
int newProp = 5;
client.SetAdditionalProperty("NewProp", newProp);
}
[Fact]
public void GetAdditionalPropertyShouldSucceed()
{
var client = new Player();
int newProp = 5;
client.SetAdditionalProperty("NewProp", newProp);
Assert.True(client.GetAdditionalProperty<int>("NewProp") == 5, "added property does not match retrieved property");
}
}
}

View File

@ -43,7 +43,6 @@ namespace SharedLibraryCore.Database
currentPath = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? currentPath = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
$"{Path.DirectorySeparatorChar}{currentPath}" : $"{Path.DirectorySeparatorChar}{currentPath}" :
currentPath; currentPath;
// todo: fix later
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = $"{currentPath}{Path.DirectorySeparatorChar}Database.db".Substring(6) }; var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = $"{currentPath}{Path.DirectorySeparatorChar}Database.db".Substring(6) };
var connectionString = connectionStringBuilder.ToString(); var connectionString = connectionStringBuilder.ToString();
@ -100,10 +99,6 @@ namespace SharedLibraryCore.Database
// adapted from // adapted from
// https://aleemkhan.wordpress.com/2013/02/28/dynamically-adding-dbset-properties-in-dbcontext-for-entity-framework-code-first/ // https://aleemkhan.wordpress.com/2013/02/28/dynamically-adding-dbset-properties-in-dbcontext-for-entity-framework-code-first/
//#if DEBUG == TRUE
// // foreach (string dllPath in Directory.GetFiles($"{Utilities.OperatingDirectory}Plugins"))
//#else
//todo: fix the debug thingie for entity scanning
IEnumerable<string> directoryFiles; 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.0{Path.DirectorySeparatorChar}Plugins";
@ -119,9 +114,11 @@ namespace SharedLibraryCore.Database
} }
directoryFiles = Directory.GetFiles(pluginDir).Where(f => f.Contains(".dll")); directoryFiles = Directory.GetFiles(pluginDir).Where(f => f.Contains(".dll"));
#if DEBUG == TRUE
foreach (string dllPath in directoryFiles) foreach (string dllPath in Directory.GetFiles(@"C:\Projects\IW4M-Admin\Application\bin\Debug\netcoreapp2.1\Plugins"))
//#endif #else
foreach (string dllPath in directoryFiles)
#endif
{ {
Assembly library; Assembly library;
try try

View File

@ -16,6 +16,7 @@ namespace SharedLibraryCore.Dtos
public long NetworkId { get; set; } public long NetworkId { get; set; }
public List<string> Aliases { get; set; } public List<string> Aliases { get; set; }
public List<string> IPs { get; set; } public List<string> IPs { get; set; }
public bool HasActivePenalty { get; set; }
public int ConnectionCount { get; set; } public int ConnectionCount { get; set; }
public string LastSeen { get; set; } public string LastSeen { get; set; }
public string FirstSeen { get; set; } public string FirstSeen { get; set; }

View File

@ -0,0 +1,665 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using SharedLibraryCore.Database;
namespace SharedLibraryCore.Migrations
{
[DbContext(typeof(DatabaseContext))]
[Migration("20180902035612_AddFractionAndIsKill")]
partial class AddFractionAndIsKill
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.2-rtm-30932");
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
{
b.Property<int>("SnapshotId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ClientId");
b.Property<int>("CurrentSessionLength");
b.Property<double>("CurrentStrain");
b.Property<int?>("CurrentViewAngleVector3Id");
b.Property<int>("Deaths");
b.Property<double>("Distance");
b.Property<double>("EloRating");
b.Property<int?>("HitDestinationVector3Id");
b.Property<int>("HitLocation");
b.Property<int?>("HitOriginVector3Id");
b.Property<int>("HitType");
b.Property<int>("Hits");
b.Property<int>("Kills");
b.Property<int?>("LastStrainAngleVector3Id");
b.Property<double>("SessionAngleOffset");
b.Property<double>("SessionSPM");
b.Property<int>("SessionScore");
b.Property<double>("StrainAngleBetween");
b.Property<int>("TimeSinceLastEvent");
b.Property<int>("WeaponId");
b.Property<DateTime>("When");
b.HasKey("SnapshotId");
b.HasIndex("ClientId");
b.HasIndex("CurrentViewAngleVector3Id");
b.HasIndex("HitDestinationVector3Id");
b.HasIndex("HitOriginVector3Id");
b.HasIndex("LastStrainAngleVector3Id");
b.ToTable("EFACSnapshot");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
{
b.Property<long>("KillId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("AttackerId");
b.Property<int>("Damage");
b.Property<int?>("DeathOriginVector3Id");
b.Property<int>("DeathType");
b.Property<double>("Fraction");
b.Property<int>("HitLoc");
b.Property<bool>("IsKill");
b.Property<int?>("KillOriginVector3Id");
b.Property<int>("Map");
b.Property<int>("ServerId");
b.Property<int>("VictimId");
b.Property<int?>("ViewAnglesVector3Id");
b.Property<int>("Weapon");
b.Property<DateTime>("When");
b.HasKey("KillId");
b.HasIndex("AttackerId");
b.HasIndex("DeathOriginVector3Id");
b.HasIndex("KillOriginVector3Id");
b.HasIndex("ServerId");
b.HasIndex("VictimId");
b.HasIndex("ViewAnglesVector3Id");
b.ToTable("EFClientKills");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
{
b.Property<long>("MessageId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ClientId");
b.Property<string>("Message");
b.Property<int>("ServerId");
b.Property<DateTime>("TimeSent");
b.HasKey("MessageId");
b.HasIndex("ClientId");
b.HasIndex("ServerId");
b.ToTable("EFClientMessages");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
{
b.Property<int>("RatingHistoryId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ClientId");
b.HasKey("RatingHistoryId");
b.HasIndex("ClientId");
b.ToTable("EFClientRatingHistory");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
{
b.Property<int>("ClientId");
b.Property<int>("ServerId");
b.Property<bool>("Active");
b.Property<int>("Deaths");
b.Property<double>("EloRating");
b.Property<int>("Kills");
b.Property<double>("MaxStrain");
b.Property<double>("RollingWeightedKDR");
b.Property<double>("SPM");
b.Property<double>("Skill");
b.Property<int>("TimePlayed");
b.HasKey("ClientId", "ServerId");
b.HasIndex("ServerId");
b.ToTable("EFClientStatistics");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
{
b.Property<int>("HitLocationCountId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ClientId")
.HasColumnName("EFClientStatistics_ClientId");
b.Property<int>("HitCount");
b.Property<float>("HitOffsetAverage");
b.Property<int>("Location");
b.Property<float>("MaxAngleDistance");
b.Property<int>("ServerId")
.HasColumnName("EFClientStatistics_ServerId");
b.HasKey("HitLocationCountId");
b.HasIndex("ServerId");
b.HasIndex("ClientId", "ServerId");
b.ToTable("EFHitLocationCounts");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
{
b.Property<int>("RatingId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ActivityAmount");
b.Property<bool>("Newest");
b.Property<double>("Performance");
b.Property<int>("Ranking");
b.Property<int>("RatingHistoryId");
b.Property<int?>("ServerId");
b.HasKey("RatingId");
b.HasIndex("RatingHistoryId");
b.HasIndex("ServerId");
b.ToTable("EFRating");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b =>
{
b.Property<int>("ServerId");
b.Property<bool>("Active");
b.Property<int>("Port");
b.HasKey("ServerId");
b.ToTable("EFServers");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
{
b.Property<int>("StatisticId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ServerId");
b.Property<long>("TotalKills");
b.Property<long>("TotalPlayTime");
b.HasKey("StatisticId");
b.HasIndex("ServerId");
b.ToTable("EFServerStatistics");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
{
b.Property<int>("AliasId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<DateTime>("DateAdded");
b.Property<int>("IPAddress");
b.Property<int>("LinkId");
b.Property<string>("Name")
.IsRequired();
b.HasKey("AliasId");
b.HasIndex("IPAddress");
b.HasIndex("LinkId");
b.ToTable("EFAlias");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b =>
{
b.Property<int>("AliasLinkId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.HasKey("AliasLinkId");
b.ToTable("EFAliasLinks");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b =>
{
b.Property<int>("ChangeHistoryId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<string>("Comment")
.HasMaxLength(128);
b.Property<int>("OriginEntityId");
b.Property<int>("TargetEntityId");
b.Property<DateTime>("TimeChanged");
b.Property<int>("TypeOfChange");
b.HasKey("ChangeHistoryId");
b.ToTable("EFChangeHistory");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
{
b.Property<int>("ClientId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("AliasLinkId");
b.Property<int>("Connections");
b.Property<int>("CurrentAliasId");
b.Property<DateTime>("FirstConnection");
b.Property<DateTime>("LastConnection");
b.Property<int>("Level");
b.Property<bool>("Masked");
b.Property<long>("NetworkId");
b.Property<string>("Password");
b.Property<string>("PasswordSalt");
b.Property<int>("TotalConnectionTime");
b.HasKey("ClientId");
b.HasIndex("AliasLinkId");
b.HasIndex("CurrentAliasId");
b.HasIndex("NetworkId")
.IsUnique();
b.ToTable("EFClients");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
{
b.Property<int>("MetaId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ClientId");
b.Property<DateTime>("Created");
b.Property<string>("Extra");
b.Property<string>("Key")
.IsRequired();
b.Property<DateTime>("Updated");
b.Property<string>("Value")
.IsRequired();
b.HasKey("MetaId");
b.HasIndex("ClientId");
b.ToTable("EFMeta");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
{
b.Property<int>("PenaltyId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<string>("AutomatedOffense");
b.Property<DateTime>("Expires");
b.Property<int>("LinkId");
b.Property<int>("OffenderId");
b.Property<string>("Offense")
.IsRequired();
b.Property<int>("PunisherId");
b.Property<int>("Type");
b.Property<DateTime>("When");
b.HasKey("PenaltyId");
b.HasIndex("LinkId");
b.HasIndex("OffenderId");
b.HasIndex("PunisherId");
b.ToTable("EFPenalties");
});
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
{
b.Property<int>("Vector3Id")
.ValueGeneratedOnAdd();
b.Property<int?>("EFACSnapshotSnapshotId");
b.Property<float>("X");
b.Property<float>("Y");
b.Property<float>("Z");
b.HasKey("Vector3Id");
b.HasIndex("EFACSnapshotSnapshotId");
b.ToTable("Vector3");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany()
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Helpers.Vector3", "CurrentViewAngle")
.WithMany()
.HasForeignKey("CurrentViewAngleVector3Id");
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination")
.WithMany()
.HasForeignKey("HitDestinationVector3Id");
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin")
.WithMany()
.HasForeignKey("HitOriginVector3Id");
b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle")
.WithMany()
.HasForeignKey("LastStrainAngleVector3Id");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker")
.WithMany()
.HasForeignKey("AttackerId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Helpers.Vector3", "DeathOrigin")
.WithMany()
.HasForeignKey("DeathOriginVector3Id");
b.HasOne("SharedLibraryCore.Helpers.Vector3", "KillOrigin")
.WithMany()
.HasForeignKey("KillOriginVector3Id");
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim")
.WithMany()
.HasForeignKey("VictimId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Helpers.Vector3", "ViewAngles")
.WithMany()
.HasForeignKey("ViewAnglesVector3Id");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany()
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany()
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany()
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany()
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics")
.WithMany("HitLocations")
.HasForeignKey("ClientId", "ServerId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
{
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", "RatingHistory")
.WithMany("Ratings")
.HasForeignKey("RatingHistoryId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
{
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
.WithMany("Children")
.HasForeignKey("LinkId")
.OnDelete(DeleteBehavior.Restrict);
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink")
.WithMany()
.HasForeignKey("AliasLinkId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias")
.WithMany()
.HasForeignKey("CurrentAliasId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany("Meta")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
.WithMany("ReceivedPenalties")
.HasForeignKey("LinkId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender")
.WithMany("ReceivedPenalties")
.HasForeignKey("OffenderId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher")
.WithMany("AdministeredPenalties")
.HasForeignKey("PunisherId")
.OnDelete(DeleteBehavior.Restrict);
});
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
{
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot")
.WithMany("PredictedViewAngles")
.HasForeignKey("EFACSnapshotSnapshotId");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,59 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace SharedLibraryCore.Migrations
{
public partial class AddFractionAndIsKill : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<double>(
name: "Fraction",
table: "EFClientKills",
nullable: false,
defaultValue: 0.0);
migrationBuilder.AddColumn<bool>(
name: "IsKill",
table: "EFClientKills",
nullable: false,
defaultValue: false);
migrationBuilder.CreateTable(
name: "EFChangeHistory",
columns: table => new
{
Active = table.Column<bool>(nullable: false),
ChangeHistoryId = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
OriginEntityId = table.Column<int>(nullable: false),
TargetEntityId = table.Column<int>(nullable: false),
TypeOfChange = table.Column<int>(nullable: false),
TimeChanged = table.Column<DateTime>(nullable: false),
Comment = table.Column<string>(maxLength: 128, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_EFChangeHistory", x => x.ChangeHistoryId);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "EFChangeHistory");
migrationBuilder.DropIndex(
name: "IX_Vector3_EFACSnapshotSnapshotId",
table: "Vector3");
migrationBuilder.DropColumn(
name: "Fraction",
table: "EFClientKills");
migrationBuilder.DropColumn(
name: "IsKill",
table: "EFClientKills");
}
}
}

View File

@ -1,13 +1,9 @@
// <auto-generated /> // <auto-generated />
using System;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.Internal;
using SharedLibraryCore.Database; using SharedLibraryCore.Database;
using SharedLibraryCore.Objects;
using System;
namespace SharedLibraryCore.Migrations namespace SharedLibraryCore.Migrations
{ {
@ -18,7 +14,7 @@ namespace SharedLibraryCore.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "2.0.2-rtm-10011"); .HasAnnotation("ProductVersion", "2.1.2-rtm-30932");
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b => modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
{ {
@ -99,8 +95,12 @@ namespace SharedLibraryCore.Migrations
b.Property<int>("DeathType"); b.Property<int>("DeathType");
b.Property<double>("Fraction");
b.Property<int>("HitLoc"); b.Property<int>("HitLoc");
b.Property<bool>("IsKill");
b.Property<int?>("KillOriginVector3Id"); b.Property<int?>("KillOriginVector3Id");
b.Property<int>("Map"); b.Property<int>("Map");
@ -331,6 +331,29 @@ namespace SharedLibraryCore.Migrations
b.ToTable("EFAliasLinks"); b.ToTable("EFAliasLinks");
}); });
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b =>
{
b.Property<int>("ChangeHistoryId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<string>("Comment")
.HasMaxLength(128);
b.Property<int>("OriginEntityId");
b.Property<int>("TargetEntityId");
b.Property<DateTime>("TimeChanged");
b.Property<int>("TypeOfChange");
b.HasKey("ChangeHistoryId");
b.ToTable("EFChangeHistory");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b => modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
{ {
b.Property<int>("ClientId") b.Property<int>("ClientId")

View File

@ -34,15 +34,45 @@ namespace SharedLibraryCore.Objects
public enum Permission public enum Permission
{ {
/// <summary>
/// client has been banned
/// </summary>
Banned = -1, Banned = -1,
/// <summary>
/// default client state upon first connect
/// </summary>
User = 0, User = 0,
/// <summary>
/// client has been flagged
/// </summary>
Flagged = 1, Flagged = 1,
/// <summary>
/// client is trusted
/// </summary>
Trusted = 2, Trusted = 2,
/// <summary>
/// client is a moderator
/// </summary>
Moderator = 3, Moderator = 3,
/// <summary>
/// client is an administrator
/// </summary>
Administrator = 4, Administrator = 4,
/// <summary>
/// client is a senior administrator
/// </summary>
SeniorAdmin = 5, SeniorAdmin = 5,
/// <summary>
/// client is a owner
/// </summary>
Owner = 6, Owner = 6,
/// <summary>
/// not used
/// </summary>
Creator = 7, Creator = 7,
/// <summary>
/// reserved for default account
/// </summary>
Console = 8 Console = 8
} }
@ -51,12 +81,10 @@ namespace SharedLibraryCore.Objects
ConnectionTime = DateTime.UtcNow; ConnectionTime = DateTime.UtcNow;
ClientNumber = -1; ClientNumber = -1;
DelayedEvents = new Queue<GameEvent>(); DelayedEvents = new Queue<GameEvent>();
_additionalProperties = new Dictionary<string, object>();
} }
public override string ToString() public override string ToString() => $"{Name}::{NetworkId}";
{
return $"{Name}::{NetworkId}";
}
public String GetLastConnection() public String GetLastConnection()
{ {
@ -105,7 +133,10 @@ namespace SharedLibraryCore.Objects
{ {
await CurrentServer.Ban(Message, this, Sender); await CurrentServer.Ban(Message, this, Sender);
} }
[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);
[NotMapped] [NotMapped]
public int ClientNumber { get; set; } public int ClientNumber { get; set; }
[NotMapped] [NotMapped]
@ -150,9 +181,6 @@ namespace SharedLibraryCore.Objects
return ((Player)obj).NetworkId == NetworkId; return ((Player)obj).NetworkId == NetworkId;
} }
public override int GetHashCode() public override int GetHashCode() => (int)NetworkId;
{
return NetworkId.GetHashCode();
}
} }
} }

View File

@ -61,7 +61,7 @@ namespace SharedLibraryCore
ScriptEngine = new Jint.Engine(cfg => ScriptEngine = new Jint.Engine(cfg =>
cfg.AllowClr(new[] cfg.AllowClr(new[]
{ {
typeof(System.Net.WebRequest).Assembly, typeof(System.Net.Http.HttpClient).Assembly,
typeof(Objects.Player).Assembly, typeof(Objects.Player).Assembly,
}) })
.CatchClrExceptions()); .CatchClrExceptions());

View File

@ -179,13 +179,9 @@ namespace SharedLibraryCore
/// <param name="message">Message to send out</param> /// <param name="message">Message to send out</param>
public async Task ToAdmins(String message) public async Task ToAdmins(String message)
{ {
foreach (Player P in Players) foreach (var client in GetPlayersAsList().Where(c => c.Level > Player.Permission.Flagged))
{ {
if (P == null) await client.Tell(message);
continue;
if (P.Level > Player.Permission.Flagged)
await P.Tell(message);
} }
} }

View File

@ -181,7 +181,7 @@ namespace SharedLibraryCore.Services
var pi = ((PenaltyInfo)p.Value); var pi = ((PenaltyInfo)p.Value);
if (pi.TimeRemaining.Length > 0) if (pi.TimeRemaining.Length > 0)
pi.TimeRemaining = (DateTime.Parse(((PenaltyInfo)p.Value).TimeRemaining) - now).TimeSpanText(); pi.TimeRemaining = (DateTime.Parse(((PenaltyInfo)p.Value).TimeRemaining) - now).TimeSpanText();
}); });
return list; return list;
} }
@ -235,6 +235,8 @@ namespace SharedLibraryCore.Services
public async Task<List<EFPenalty>> GetActivePenaltiesAsync(int aliasId, int ip = 0) public async Task<List<EFPenalty>> GetActivePenaltiesAsync(int aliasId, int ip = 0)
{ {
var now = DateTime.UtcNow;
using (var context = new DatabaseContext()) using (var context = new DatabaseContext())
{ {
var iqPenalties = await (from link in context.AliasLinks var iqPenalties = await (from link in context.AliasLinks
@ -242,6 +244,8 @@ namespace SharedLibraryCore.Services
join penalty in context.Penalties join penalty in context.Penalties
on link.AliasLinkId equals penalty.LinkId on link.AliasLinkId equals penalty.LinkId
where penalty.Active where penalty.Active
where penalty.Expires > now
orderby penalty.When descending
select penalty).ToListAsync(); select penalty).ToListAsync();
if (ip != 0) if (ip != 0)
{ {
@ -250,6 +254,8 @@ namespace SharedLibraryCore.Services
join penalty in context.Penalties join penalty in context.Penalties
on alias.LinkId equals penalty.LinkId on alias.LinkId equals penalty.LinkId
where penalty.Active where penalty.Active
where penalty.Expires > now
orderby penalty.When descending
select penalty).ToListAsync()); select penalty).ToListAsync());
} }
return iqPenalties; return iqPenalties;

View File

@ -20,6 +20,7 @@
<PackageReference Include="Jint" Version="3.0.0-beta-1249" /> <PackageReference Include="Jint" Version="3.0.0-beta-1249" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="2.1.1" /> <PackageReference Include="Microsoft.Extensions.Localization" Version="2.1.1" />

View File

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using SharedLibraryCore; using SharedLibraryCore;
using WebfrontCore.ViewModels; using WebfrontCore.ViewModels;
using static SharedLibraryCore.Objects.Player;
namespace WebfrontCore.Controllers namespace WebfrontCore.Controllers
{ {
@ -22,6 +23,20 @@ namespace WebfrontCore.Controllers
{ {
Name = "Reason", Name = "Reason",
Label = Localization["WEBFRONT_ACTION_LABEL_REASON"], Label = Localization["WEBFRONT_ACTION_LABEL_REASON"],
},
new InputInfo()
{
Name ="Duration",
Label=Localization["WEBFRONT_ACTION_LABEL_DURATION"],
Type="select",
Values = new Dictionary<string, string>()
{
{"1", $"1 {Localization["GLOBAL_TIME_HOUR"]}" },
{"2", $"6 {Localization["GLOBAL_TIME_HOURS"]}" },
{"3", $"1 {Localization["GLOBAL_TIME_DAY"]}" },
{"4", $"1 {Localization["GLOBAL_TIME_WEEK"]}" },
{"5", $"{Localization["WEBFRONT_ACTION_SELECTION_PERMANENT"]}" },
}
} }
}, },
Action = "BanAsync" Action = "BanAsync"
@ -30,14 +45,36 @@ namespace WebfrontCore.Controllers
return View("_ActionForm", info); return View("_ActionForm", info);
} }
public async Task<IActionResult> BanAsync(int targetId, string Reason) public async Task<IActionResult> BanAsync(int targetId, string Reason, int Duration)
{ {
string duration = string.Empty;
switch (Duration)
{
case 1:
duration = "1h";
break;
case 2:
duration = "6h";
break;
case 3:
duration = "1d";
break;
case 4:
duration = "1w";
break;
}
string command = Duration == 5 ?
$"!ban @{targetId} {Reason}" :
$"!tempban @{targetId} {duration} {Reason}";
var server = Manager.GetServers().First(); var server = Manager.GetServers().First();
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
{ {
serverId = server.GetHashCode(), serverId = server.GetHashCode(),
command = $"!ban @{targetId} {Reason}" command
})); }));
} }
@ -102,5 +139,42 @@ namespace WebfrontCore.Controllers
{ {
return await Task.FromResult(RedirectToAction("LoginAsync", "Account", new { clientId, password })); return await Task.FromResult(RedirectToAction("LoginAsync", "Account", new { clientId, password }));
} }
public IActionResult EditForm()
{
var info = new ActionInfo()
{
ActionButtonLabel = Localization["WEBFRONT_ACTION_LABEL_EDIT"],
Name = "Edit",
Inputs = new List<InputInfo>()
{
new InputInfo()
{
Name ="level",
Label=Localization["WEBFRONT_PROFILE_LEVEL"],
Type="select",
Values = Enum.GetValues(typeof(Permission)).OfType<Permission>()
.Where(p => p <= Client.Level)
.Where(p => p != Permission.Banned)
.Where(p => p != Permission.Flagged)
.ToDictionary(p => p.ToString(), p=> p.ToLocalizedLevelName())
},
},
Action = "EditAsync"
};
return View("_ActionForm", info);
}
public async Task<IActionResult> EditAsync(int targetId, string level)
{
var server = Manager.GetServers().First();
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
{
serverId = server.GetHashCode(),
command = $"!setlevel @{targetId} {level}"
}));
}
} }
} }

View File

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using static SharedLibraryCore.Objects.Penalty;
namespace WebfrontCore.Controllers namespace WebfrontCore.Controllers
{ {
@ -20,6 +21,8 @@ namespace WebfrontCore.Controllers
return NotFound(); return NotFound();
} }
var activePenalties = await Manager.GetPenaltyService().GetActivePenaltiesAsync(client.AliasLinkId, client.IPAddress);
#if DEBUG #if DEBUG
Authorized = true; Authorized = true;
#endif #endif
@ -48,6 +51,7 @@ namespace WebfrontCore.Controllers
.Distinct() .Distinct()
.OrderBy(i => i) .OrderBy(i => i)
.ToList(), .ToList(),
HasActivePenalty = activePenalties.Count > 0,
Online = Manager.GetActiveClients().FirstOrDefault(c => c.ClientId == client.ClientId) != null, Online = Manager.GetActiveClients().FirstOrDefault(c => c.ClientId == client.ClientId) != null,
TimeOnline = (DateTime.UtcNow - client.LastConnection).TimeSpanText(), TimeOnline = (DateTime.UtcNow - client.LastConnection).TimeSpanText(),
LinkedAccounts = client.LinkedAccounts LinkedAccounts = client.LinkedAccounts

View File

@ -12,5 +12,7 @@ namespace WebfrontCore.ViewModels
public string Placeholder { get; set; } public string Placeholder { get; set; }
public string Type { get; set; } public string Type { get; set; }
public string Value { get; set; } public string Value { get; set; }
public Dictionary<string, string> Values { get; set; }
public bool Checked { get; set; }
} }
} }

View File

@ -5,19 +5,41 @@
<form class="action-form" action="/Action/@Model.Action"> <form class="action-form" action="/Action/@Model.Action">
@foreach (var input in Model.Inputs) @foreach (var input in Model.Inputs)
{ {
<div class="input-group mb-3"> <div class="input-group mb-3">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text" id="basic-addon-@input.Name">@input.Label</span> <span class="input-group-text" id="basic-addon-@input.Name">@input.Label</span>
</div> </div>
@{ @{
string inputType = input.Type ?? "text"; string inputType = input.Type ?? "text";
string value = input.Value ?? ""; string value = input.Value ?? "";
<input type="@inputType" name="@input.Name" value="@value" class="form-control" placeholder="@input.Placeholder" aria-label="@input.Name" aria-describedby="basic-addon-@input.Name"> if (inputType == "select")
{
<select name="@input.Name" class="form-control" aria-label="@input.Name" aria-describedby="basic-addon-@input.Name">
@foreach (var item in input.Values)
{
<option value="@item.Key">@item.Value</option>
}
</select>
} }
</div> else if (inputType == "checkbox")
{
<div class="btn-group-toggle" data-toggle="buttons">
<label class="btn btn-primary active">
<input type="checkbox" name="@input.Name" @(input.Checked ? "checked" : "") autocomplete="off">@input.Label
</label>
</div>
}
else
{
<input type="@inputType" name="@input.Name" value="@value" class="form-control" placeholder="@input.Placeholder" aria-label="@input.Name" aria-describedby="basic-addon-@input.Name">
}
}
</div>
} }
<button type="submit" class="btn btn-block btn-primary">@Model.ActionButtonLabel</button> <button type="submit" class="btn btn-block btn-primary">@Model.ActionButtonLabel</button>
</form> </form>

View File

@ -33,18 +33,20 @@
{ {
<div class="d-flex d-md-inline-flex justify-content-center order-1"> <div class="d-flex d-md-inline-flex justify-content-center order-1">
<div id="profile_aliases_btn" class="oi oi-caret-bottom h3 ml-0 ml-md-2"></div> <div id="profile_aliases_btn" class="oi oi-caret-bottom h3 ml-0 ml-md-2"></div>
@if (Model.LevelInt != -1)
@if (Model.LevelInt < (int)ViewBag.User.Level && {
(SharedLibraryCore.Objects.Player.Permission)Model.LevelInt != SharedLibraryCore.Objects.Player.Permission.Banned) <div id="profile_action_edit_btn" class="profile-action oi oi-cog text-muted h3 ml-2" title="Client Options" data-action="edit" aria-hidden="true"></div>
}
@if (Model.LevelInt < (int)ViewBag.User.Level && !Model.HasActivePenalty)
{ {
<div id="profile_action_ban_btn" class="profile-action oi oi-lock-unlocked text-success h3 ml-2" title="Ban Client" data-action="ban" aria-hidden="true"></div> <div id="profile_action_ban_btn" class="profile-action oi oi-lock-unlocked text-success h3 ml-2" title="Ban Client" data-action="ban" aria-hidden="true"></div>
} }
@if (Model.LevelInt < (int)ViewBag.User.Level && @if (Model.LevelInt < (int)ViewBag.User.Level && Model.HasActivePenalty)
(SharedLibraryCore.Objects.Player.Permission)Model.LevelInt == SharedLibraryCore.Objects.Player.Permission.Banned)
{ {
<div id="profile_action_unban_btn" class="profile-action oi oi-lock-locked text-danger h3 ml-2" title="Unban Client" data-action="unban" aria-hidden="true"></div> <div id="profile_action_unban_btn" class="profile-action oi oi-lock-locked text-danger h3 ml-2" title="Unban Client" data-action="unban" aria-hidden="true"></div>
} }
</div> </div>
<div id="profile_aliases" class="pr-0 pr-sm-4 pb-2 mb-2 text-muted order-0"> <div id="profile_aliases" class="pr-0 pr-sm-4 pb-2 mb-2 text-muted order-0">

View File

@ -6,6 +6,7 @@
@{ @{
foreach (var response in Model) foreach (var response in Model)
{ {
<span class="text-success">@response.Response</span><br /> <span class="text-muted">@response.Response</span><br />
} }
<hr />
} }

View File

@ -6321,6 +6321,9 @@ a.link-inverse:hover {
#console_command_response { #console_command_response {
min-height: 20rem; } min-height: 20rem; }
#console_command_response hr {
border-color: #6c757d; }
#console .form-control, #console button { #console .form-control, #console button {
border-radius: 0; border-radius: 0;
border-color: #007ACC; border-color: #007ACC;
@ -6339,7 +6342,7 @@ a.link-inverse:hover {
form *, select { form *, select {
border-radius: 0 !important; } border-radius: 0 !important; }
select { #penalty_filter_selection {
border-left: none !important; border-left: none !important;
border-right: none !important; border-right: none !important;
border-bottom: none !important; border-bottom: none !important;

View File

@ -96,6 +96,10 @@ a.link-inverse:hover {
min-height: 20rem; min-height: 20rem;
} }
#console_command_response hr {
border-color: #6c757d;
}
#console .form-control, #console button { #console .form-control, #console button {
border-radius: 0; border-radius: 0;
border-color: $primary; border-color: $primary;
@ -119,7 +123,7 @@ form *, select {
border-radius: 0 !important; border-radius: 0 !important;
} }
select { #penalty_filter_selection {
border-left: none !important; border-left: none !important;
border-right: none !important; border-right: none !important;
border-bottom: none !important; border-bottom: none !important;
@ -205,4 +209,3 @@ select {
.client-message, .automated-penalty-info-detailed { .client-message, .automated-penalty-info-detailed {
cursor: pointer; cursor: pointer;
} }

View File

@ -24,7 +24,7 @@ $(document).ready(function () {
* hide loader when clicking * hide loader when clicking
*/ */
$(document).click(function (e) { $(document).click(function (e) {
hideLoader() //hideLoader()
}); });
/* /*
@ -39,7 +39,7 @@ $(document).ready(function () {
$('#actionModal').modal(); $('#actionModal').modal();
}) })
.fail(function (jqxhr, textStatus, error) { .fail(function (jqxhr, textStatus, error) {
$('#actionModal .modal-message').text('Error &mdash ' + error); $('#actionModal .modal-message').text('Error ' + error);
$('#actionModal').modal(); $('#actionModal').modal();
$('#actionModal .modal-message').fadeIn('fast'); $('#actionModal .modal-message').fadeIn('fast');
}); });

View File

@ -14,7 +14,7 @@
$.get('/Console/ExecuteAsync', { serverId: serverId, command: command }) $.get('/Console/ExecuteAsync', { serverId: serverId, command: command })
.done(function (response) { .done(function (response) {
hideLoader(); hideLoader();
$('#console_command_response').html(response); $('#console_command_response').append(response);
$('#console_command_value').val(""); $('#console_command_value').val("");
}) })
.fail(function (jqxhr, textStatus, error) { .fail(function (jqxhr, textStatus, error) {

View File

@ -14,6 +14,7 @@ init()
level.callbackPlayerDamage = ::Callback_PlayerDamage; level.callbackPlayerDamage = ::Callback_PlayerDamage;
} }
onPlayerConnect(player) onPlayerConnect(player)
{ {
for(;;) for(;;)
@ -111,6 +112,11 @@ waitForAdditionalAngles(logString)
logPrint(logString + ";" + anglesStr + "\n"); logPrint(logString + ";" + anglesStr + "\n");
} }
vectorScale(vector, scale)
{
return (vector[0] * scale, vector[1] * scale, vector[2] * scale);
}
Process_Hit(type, attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon) Process_Hit(type, attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon)
{ {
victim = self; victim = self;
@ -123,7 +129,12 @@ Process_Hit(type, attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon)
location = victim GetTagOrigin(hitLocationToBone(sHitLoc)); location = victim GetTagOrigin(hitLocationToBone(sHitLoc));
isKillstreakKill = !isPlayer(attacker) || isKillstreakWeapon(sWeapon); isKillstreakKill = !isPlayer(attacker) || isKillstreakWeapon(sWeapon);
logLine = "Script" + type + ";" + _attacker.guid + ";" + victim.guid + ";" + _attacker GetTagOrigin("tag_eye") + ";" + location + ";" + iDamage + ";" + sWeapon + ";" + sHitLoc + ";" + sMeansOfDeath + ";" + _attacker getPlayerAngles() + ";" + gettime() + ";" + isKillstreakKill + ";" + _attacker playerADS(); // do the tracing stuff
start = _attacker getTagOrigin("tag_eye");
end = location;
trace = bulletTrace(start, end, true, _attacker);
logLine = "Script" + type + ";" + _attacker.guid + ";" + victim.guid + ";" + _attacker GetTagOrigin("tag_eye") + ";" + location + ";" + iDamage + ";" + sWeapon + ";" + sHitLoc + ";" + sMeansOfDeath + ";" + _attacker getPlayerAngles() + ";" + gettime() + ";" + isKillstreakKill + ";" + _attacker playerADS() + ";" + trace["fraction"];
attacker thread waitForAdditionalAngles(logLine); attacker thread waitForAdditionalAngles(logLine);
} }