add index to time sent in EFCLientMessage, so we can retrieve faster in context view

set the maximum height of the
add link to profile on client chat
move change history into a seperate service
move around AC penalty processing
This commit is contained in:
RaidMax 2018-09-16 15:34:16 -05:00
parent 7c708f06f3
commit 4a46abc46d
30 changed files with 2662 additions and 1174 deletions

View File

@ -5,7 +5,7 @@
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish> <MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
<PackageId>RaidMax.IW4MAdmin.Application</PackageId> <PackageId>RaidMax.IW4MAdmin.Application</PackageId>
<Version>2.1.9.1</Version> <Version>2.1.9.2</Version>
<Authors>RaidMax</Authors> <Authors>RaidMax</Authors>
<Company>Forever None</Company> <Company>Forever None</Company>
<Product>IW4MAdmin</Product> <Product>IW4MAdmin</Product>

View File

@ -23,9 +23,9 @@ namespace IW4MAdmin.Application
public void AddEvent(GameEvent gameEvent) public void AddEvent(GameEvent gameEvent)
{ {
// IsProcessingEvent.Wait(); //IsProcessingEvent.Wait();
((Manager as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent)); ((Manager as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent));
// IsProcessingEvent.Release(1); //IsProcessingEvent.Release(1);
//if (gameEvent.Type == GameEvent.EventType.Connect) //if (gameEvent.Type == GameEvent.EventType.Connect)
//{ //{
// IsProcessingEvent.Wait(); // IsProcessingEvent.Wait();

View File

@ -39,6 +39,7 @@ namespace IW4MAdmin.Application
// 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; }
public string Version => Assembly.GetEntryAssembly().GetName().Version.ToString();
static ApplicationManager Instance; static ApplicationManager Instance;
readonly List<AsyncStatus> TaskStatuses; readonly List<AsyncStatus> TaskStatuses;
@ -102,9 +103,12 @@ namespace IW4MAdmin.Application
return; return;
} }
await newEvent.Owner.ExecuteEvent(newEvent);
// todo: this is a hacky mess // todo: this is a hacky mess
if (newEvent.Origin?.DelayedEvents.Count > 0 && if (newEvent.Origin?.DelayedEvents.Count > 0 &&
newEvent.Origin?.State == Player.ClientState.Connected) (newEvent.Origin?.State == Player.ClientState.Connected ||
newEvent.Type == GameEvent.EventType.Connect))
{ {
var events = newEvent.Origin.DelayedEvents; var events = newEvent.Origin.DelayedEvents;
@ -144,8 +148,6 @@ namespace IW4MAdmin.Application
} }
} }
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
@ -175,6 +177,9 @@ namespace IW4MAdmin.Application
} }
// tell anyone waiting for the output that we're done // tell anyone waiting for the output that we're done
newEvent.OnProcessed.Set(); newEvent.OnProcessed.Set();
var changeHistorySvc = new ChangeHistoryService();
await changeHistorySvc.Add(args.Event);
} }
public IList<Server> GetServers() public IList<Server> GetServers()
@ -263,7 +268,7 @@ namespace IW4MAdmin.Application
await new ContextSeed(db).Seed(); await new ContextSeed(db).Seed();
} }
// todo: optimize this // todo: optimize this (or replace it)
var ipList = (await ClientSvc.Find(c => c.Level > Player.Permission.Trusted)) var ipList = (await ClientSvc.Find(c => c.Level > Player.Permission.Trusted))
.Select(c => new .Select(c => new
{ {

View File

@ -1,197 +1,197 @@
using SharedLibraryCore; //using SharedLibraryCore;
using SharedLibraryCore.Objects; //using SharedLibraryCore.Objects;
using System; //using System;
using System.Collections.Generic; //using System.Collections.Generic;
using System.Linq; //using System.Linq;
using System.Text; //using System.Text;
using System.Threading.Tasks; //using System.Threading.Tasks;
namespace IW4ScriptCommands.Commands //namespace IW4ScriptCommands.Commands
{ //{
class Balance : Command // class Balance : Command
{ // {
private class TeamAssignment // private class TeamAssignment
{ // {
public IW4MAdmin.Plugins.Stats.IW4Info.Team CurrentTeam { get; set; } // public IW4MAdmin.Plugins.Stats.IW4Info.Team CurrentTeam { get; set; }
public int Num { get; set; } // public int Num { get; set; }
public IW4MAdmin.Plugins.Stats.Models.EFClientStatistics Stats { get; set; } // public IW4MAdmin.Plugins.Stats.Models.EFClientStatistics Stats { get; set; }
} // }
public Balance() : base("balance", "balance teams", "bal", Player.Permission.Trusted, false, null) // public Balance() : base("balance", "balance teams", "bal", Player.Permission.Trusted, false, null)
{ // {
} // }
public override async Task ExecuteAsync(GameEvent E) // public override async Task ExecuteAsync(GameEvent E)
{ // {
string teamsString = (await E.Owner.GetDvarAsync<string>("sv_iw4madmin_teams")).Value; // string teamsString = (await E.Owner.GetDvarAsync<string>("sv_iw4madmin_teams")).Value;
var scriptClientTeams = teamsString.Split(';', StringSplitOptions.RemoveEmptyEntries) // var scriptClientTeams = teamsString.Split(';', StringSplitOptions.RemoveEmptyEntries)
.Select(c => c.Split(',')) // .Select(c => c.Split(','))
.Select(c => new TeamAssignment() // .Select(c => new TeamAssignment()
{ // {
CurrentTeam = (IW4MAdmin.Plugins.Stats.IW4Info.Team)Enum.Parse(typeof(IW4MAdmin.Plugins.Stats.IW4Info.Team), c[1]), // CurrentTeam = (IW4MAdmin.Plugins.Stats.IW4Info.Team)Enum.Parse(typeof(IW4MAdmin.Plugins.Stats.IW4Info.Team), c[1]),
Num = E.Owner.Players.FirstOrDefault(p => p?.NetworkId == c[0].ConvertLong())?.ClientNumber ?? -1, // Num = E.Owner.Players.FirstOrDefault(p => p?.NetworkId == c[0].ConvertLong())?.ClientNumber ?? -1,
Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(E.Owner.Players.FirstOrDefault(p => p?.NetworkId == c[0].ConvertLong()).ClientId, E.Owner.GetHashCode()) // Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(E.Owner.Players.FirstOrDefault(p => p?.NetworkId == c[0].ConvertLong()).ClientId, E.Owner.GetHashCode())
}) // })
.ToList(); // .ToList();
// at least one team is full so we can't balance // // at least one team is full so we can't balance
if (scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis) >= Math.Floor(E.Owner.MaxClients / 2.0) // if (scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis) >= Math.Floor(E.Owner.MaxClients / 2.0)
|| scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies) >= Math.Floor(E.Owner.MaxClients / 2.0)) // || scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies) >= Math.Floor(E.Owner.MaxClients / 2.0))
{ // {
await E.Origin?.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL"]); // await E.Origin?.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL"]);
return; // return;
} // }
List<string> teamAssignments = new List<string>(); // List<string> teamAssignments = new List<string>();
var activeClients = E.Owner.GetPlayersAsList().Select(c => new TeamAssignment() // var activeClients = E.Owner.GetPlayersAsList().Select(c => new TeamAssignment()
{ // {
Num = c.ClientNumber, // Num = c.ClientNumber,
Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, E.Owner.GetHashCode()), // Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, E.Owner.GetHashCode()),
CurrentTeam = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, E.Owner.GetHashCode()).Team // CurrentTeam = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, E.Owner.GetHashCode()).Team
}) // })
.Where(c => scriptClientTeams.FirstOrDefault(sc => sc.Num == c.Num)?.CurrentTeam != IW4MAdmin.Plugins.Stats.IW4Info.Team.Spectator) // .Where(c => scriptClientTeams.FirstOrDefault(sc => sc.Num == c.Num)?.CurrentTeam != IW4MAdmin.Plugins.Stats.IW4Info.Team.Spectator)
.Where(c => c.CurrentTeam != scriptClientTeams.FirstOrDefault(p => p.Num == c.Num)?.CurrentTeam) // .Where(c => c.CurrentTeam != scriptClientTeams.FirstOrDefault(p => p.Num == c.Num)?.CurrentTeam)
.OrderByDescending(c => c.Stats.Performance) // .OrderByDescending(c => c.Stats.Performance)
.ToList(); // .ToList();
var alliesTeam = scriptClientTeams // var alliesTeam = scriptClientTeams
.Where(c => c.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies) // .Where(c => c.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies)
.Where(c => activeClients.Count(t => t.Num == c.Num) == 0) // .Where(c => activeClients.Count(t => t.Num == c.Num) == 0)
.ToList(); // .ToList();
var axisTeam = scriptClientTeams // var axisTeam = scriptClientTeams
.Where(c => c.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis) // .Where(c => c.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis)
.Where(c => activeClients.Count(t => t.Num == c.Num) == 0) // .Where(c => activeClients.Count(t => t.Num == c.Num) == 0)
.ToList(); // .ToList();
while (activeClients.Count() > 0) // while (activeClients.Count() > 0)
{ // {
int teamSizeDifference = alliesTeam.Count - axisTeam.Count; // int teamSizeDifference = alliesTeam.Count - axisTeam.Count;
double performanceDisparity = alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0 - // double performanceDisparity = alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0 -
axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0; // axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0;
if (teamSizeDifference == 0) // if (teamSizeDifference == 0)
{ // {
if (performanceDisparity == 0) // if (performanceDisparity == 0)
{ // {
alliesTeam.Add(activeClients.First()); // alliesTeam.Add(activeClients.First());
activeClients.RemoveAt(0); // activeClients.RemoveAt(0);
} // }
else // else
{ // {
if (performanceDisparity > 0) // if (performanceDisparity > 0)
{ // {
axisTeam.Add(activeClients.First()); // axisTeam.Add(activeClients.First());
activeClients.RemoveAt(0); // activeClients.RemoveAt(0);
} // }
else // else
{ // {
alliesTeam.Add(activeClients.First()); // alliesTeam.Add(activeClients.First());
activeClients.RemoveAt(0); // activeClients.RemoveAt(0);
} // }
} // }
} // }
else if (teamSizeDifference > 0) // else if (teamSizeDifference > 0)
{ // {
if (performanceDisparity > 0) // if (performanceDisparity > 0)
{ // {
axisTeam.Add(activeClients.First()); // axisTeam.Add(activeClients.First());
activeClients.RemoveAt(0); // activeClients.RemoveAt(0);
} // }
else // else
{ // {
axisTeam.Add(activeClients.Last()); // axisTeam.Add(activeClients.Last());
activeClients.RemoveAt(activeClients.Count - 1); // activeClients.RemoveAt(activeClients.Count - 1);
} // }
} // }
else // else
{ // {
if (performanceDisparity > 0) // if (performanceDisparity > 0)
{ // {
alliesTeam.Add(activeClients.First()); // alliesTeam.Add(activeClients.First());
activeClients.RemoveAt(0); // activeClients.RemoveAt(0);
} // }
else // else
{ // {
alliesTeam.Add(activeClients.Last()); // alliesTeam.Add(activeClients.Last());
activeClients.RemoveAt(activeClients.Count - 1); // activeClients.RemoveAt(activeClients.Count - 1);
} // }
} // }
} // }
alliesTeam = alliesTeam.OrderByDescending(t => t.Stats.Performance) // alliesTeam = alliesTeam.OrderByDescending(t => t.Stats.Performance)
.ToList(); // .ToList();
axisTeam = axisTeam.OrderByDescending(t => t.Stats.Performance) // axisTeam = axisTeam.OrderByDescending(t => t.Stats.Performance)
.ToList(); // .ToList();
while (Math.Abs(alliesTeam.Count - axisTeam.Count) > 1) // while (Math.Abs(alliesTeam.Count - axisTeam.Count) > 1)
{ // {
int teamSizeDifference = alliesTeam.Count - axisTeam.Count; // int teamSizeDifference = alliesTeam.Count - axisTeam.Count;
double performanceDisparity = alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0 - // double performanceDisparity = alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0 -
axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0; // axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0;
if (teamSizeDifference > 0) // if (teamSizeDifference > 0)
{ // {
if (performanceDisparity > 0) // if (performanceDisparity > 0)
{ // {
axisTeam.Add(alliesTeam.First()); // axisTeam.Add(alliesTeam.First());
alliesTeam.RemoveAt(0); // alliesTeam.RemoveAt(0);
} // }
else // else
{ // {
axisTeam.Add(alliesTeam.Last()); // axisTeam.Add(alliesTeam.Last());
alliesTeam.RemoveAt(axisTeam.Count - 1); // alliesTeam.RemoveAt(axisTeam.Count - 1);
} // }
} // }
else // else
{ // {
if (performanceDisparity > 0) // if (performanceDisparity > 0)
{ // {
alliesTeam.Add(axisTeam.Last()); // alliesTeam.Add(axisTeam.Last());
axisTeam.RemoveAt(axisTeam.Count - 1); // axisTeam.RemoveAt(axisTeam.Count - 1);
} // }
else // else
{ // {
alliesTeam.Add(axisTeam.First()); // alliesTeam.Add(axisTeam.First());
axisTeam.RemoveAt(0); // axisTeam.RemoveAt(0);
} // }
} // }
} // }
foreach (var assignment in alliesTeam) // foreach (var assignment in alliesTeam)
{ // {
teamAssignments.Add($"{assignment.Num},2"); // teamAssignments.Add($"{assignment.Num},2");
assignment.Stats.Team = IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies; // assignment.Stats.Team = IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies;
} // }
foreach (var assignment in axisTeam) // foreach (var assignment in axisTeam)
{ // {
teamAssignments.Add($"{assignment.Num},3"); // teamAssignments.Add($"{assignment.Num},3");
assignment.Stats.Team = IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis; // assignment.Stats.Team = IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis;
} // }
if (alliesTeam.Count(ac => scriptClientTeams.First(sc => sc.Num == ac.Num).CurrentTeam != ac.CurrentTeam) == 0 && // if (alliesTeam.Count(ac => scriptClientTeams.First(sc => sc.Num == ac.Num).CurrentTeam != ac.CurrentTeam) == 0 &&
axisTeam.Count(ac => scriptClientTeams.First(sc => sc.Num == ac.Num).CurrentTeam != ac.CurrentTeam) == 0) // axisTeam.Count(ac => scriptClientTeams.First(sc => sc.Num == ac.Num).CurrentTeam != ac.CurrentTeam) == 0)
{ // {
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL_BALANCED"]); // await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL_BALANCED"]);
return; // return;
} // }
if (E.Origin?.Level > Player.Permission.Administrator) // if (E.Origin?.Level > Player.Permission.Administrator)
{ // {
await E.Origin.Tell($"Allies Elo: {(alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0)}"); // await E.Origin.Tell($"Allies Elo: {(alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0)}");
await E.Origin.Tell($"Axis Elo: {(axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0)}"); // await E.Origin.Tell($"Axis Elo: {(axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0)}");
} // }
string args = string.Join(",", teamAssignments); // string args = string.Join(",", teamAssignments);
await E.Owner.ExecuteCommandAsync($"sv_iw4madmin_command \"balance:{args}\""); // await E.Owner.ExecuteCommandAsync($"sv_iw4madmin_command \"balance:{args}\"");
await E.Origin.Tell("Balance command sent"); // await E.Origin.Tell("Balance command sent");
} // }
} // }
} //}

View File

@ -10,6 +10,7 @@ namespace IW4MAdmin.Plugins.Stats.Config
public List<StreakMessageConfiguration> KillstreakMessages { get; set; } public List<StreakMessageConfiguration> KillstreakMessages { get; set; }
public List<StreakMessageConfiguration> DeathstreakMessages { get; set; } public List<StreakMessageConfiguration> DeathstreakMessages { get; set; }
public int TopPlayersMinPlayTime { get; set; } public int TopPlayersMinPlayTime { get; set; }
public bool StoreClientKills { get; set; }
public string Name() => "Stats"; public string Name() => "Stats";
public IBaseConfiguration Generate() public IBaseConfiguration Generate()
{ {
@ -49,6 +50,7 @@ namespace IW4MAdmin.Plugins.Stats.Config
}; };
TopPlayersMinPlayTime = 3600 * 3; TopPlayersMinPlayTime = 3600 * 3;
StoreClientKills = false;
return this; return this;
} }

View File

@ -486,21 +486,24 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
if (Plugin.Config.Configuration().EnableAntiCheat) if (Plugin.Config.Configuration().EnableAntiCheat)
{ {
async Task executePenalty(Cheat.DetectionPenaltyResult penalty) await ApplyPenalty(clientDetection.ProcessKill(hit, isDamage), clientDetection, attacker);
await ApplyPenalty(clientDetection.ProcessTotalRatio(clientStats), clientDetection, attacker);
await clientStatsSvc.SaveChangesAsync();
}
if (Plugin.Config.Configuration().StoreClientKills)
{ {
async Task saveLog() using (var ctx = new DatabaseContext())
{ {
using (var ctx = new DatabaseContext(false)) ctx.Set<EFClientKill>().Add(hit);
{
// todo: why does this cause duplicate primary key
foreach (var change in clientDetection.Tracker.GetChanges().Distinct())
{
ctx.Add(change);
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
} }
} }
} }
async Task ApplyPenalty(Cheat.DetectionPenaltyResult penalty, Cheat.Detection clientDetection, Player attacker)
{
await OnProcessingPenalty.WaitAsync(); await OnProcessingPenalty.WaitAsync();
try try
@ -509,7 +512,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
{ {
case Penalty.PenaltyType.Ban: case Penalty.PenaltyType.Ban:
if (attacker.Level == Player.Permission.Banned) if (attacker.Level == Player.Permission.Banned)
{
break; break;
}
if (clientDetection.Tracker.HasChanges)
{
await SaveTrackedSnapshots(clientDetection);
}
await attacker.Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], new Player() await attacker.Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], new Player()
{ {
ClientId = 1, ClientId = 1,
@ -523,11 +532,16 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
} }
} }
}); });
await saveLog();
break; break;
case Penalty.PenaltyType.Flag: case Penalty.PenaltyType.Flag:
if (attacker.Level != Player.Permission.User) if (attacker.Level != Player.Permission.User)
{
break; break;
}
if (clientDetection.Tracker.HasChanges)
{
await SaveTrackedSnapshots(clientDetection);
}
var e = new GameEvent() var e = new GameEvent()
{ {
Data = penalty.Type == Cheat.Detection.DetectionType.Bone ? Data = penalty.Type == Cheat.Detection.DetectionType.Bone ?
@ -544,32 +558,42 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
Owner = attacker.CurrentServer, Owner = attacker.CurrentServer,
Type = GameEvent.EventType.Flag Type = GameEvent.EventType.Flag
}; };
await saveLog();
// because we created an event it must be processed by the manager // because we created an event it must be processed by the manager
// even if it didn't really do anything // even if it didn't really do anything
Manager.GetEventHandler().AddEvent(e); Manager.GetEventHandler().AddEvent(e);
await new CFlag().ExecuteAsync(e); await new CFlag().ExecuteAsync(e);
break; break;
} }
OnProcessingPenalty.Release(); OnProcessingPenalty.Release(1);
} }
catch catch
{ {
OnProcessingPenalty.Release(); OnProcessingPenalty.Release(1);
}
} }
} async Task SaveTrackedSnapshots(Cheat.Detection clientDetection)
{
await executePenalty(clientDetection.ProcessKill(hit, isDamage)); using (var ctx = new DatabaseContext(true))
await executePenalty(clientDetection.ProcessTotalRatio(clientStats)); {
// todo: why does this cause duplicate primary key
await clientStatsSvc.SaveChangesAsync(); foreach (var change in clientDetection.Tracker
} .GetChanges()
.Where(c => c.SnapshotId == 0))
using (var ctx = new DatabaseContext()) {
ctx.Add(change);
}
try
{ {
ctx.Set<EFClientKill>().Add(hit);
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
clientDetection.Tracker.ClearChanges();
}
catch (Exception ex)
{
Log.WriteWarning(ex.GetExceptionInfo());
}
} }
} }
@ -1096,5 +1120,5 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
{ {
Servers[serverId].IsTeamBased = isTeamBased; Servers[serverId].IsTeamBased = isTeamBased;
} }
} }
} }

View File

@ -30,6 +30,9 @@ namespace Stats.Models
builder.Entity<EFRating>() builder.Entity<EFRating>()
.HasIndex(p => p.When); .HasIndex(p => p.When);
builder.Entity<EFClientMessage>()
.HasIndex(p => p.TimeSent);
// force pluralization // force pluralization
builder.Entity<EFClientKill>().ToTable("EFClientKills"); builder.Entity<EFClientKill>().ToTable("EFClientKills");
builder.Entity<EFClientMessage>().ToTable("EFClientMessages"); builder.Entity<EFClientMessage>().ToTable("EFClientMessages");

View File

@ -40,11 +40,16 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers
where message.TimeSent <= whenUpper where message.TimeSent <= whenUpper
select new SharedLibraryCore.Dtos.ChatInfo() select new SharedLibraryCore.Dtos.ChatInfo()
{ {
ClientId = message.ClientId,
Message = message.Message, Message = message.Message,
Name = message.Client.CurrentAlias.Name, Name = message.Client.CurrentAlias.Name,
Time = message.TimeSent Time = message.TimeSent
}; };
#if DEBUG == true
var messagesSql = iqMessages.ToSql();
#endif
var messages = await iqMessages.ToListAsync(); var messages = await iqMessages.ToListAsync();
return View("_MessageContext", messages); return View("_MessageContext", messages);

View File

@ -7,6 +7,6 @@
<h5>@Model.First().Time.ToString()</h5> <h5>@Model.First().Time.ToString()</h5>
@foreach (var message in Model) @foreach (var message in Model)
{ {
<span class="text-white">@message.Name</span><span> &mdash; @message.Message</span><br /> <span class="text-white">@Html.ActionLink(@message.Name, "ProfileAsync", "Client", new { id = message.ClientId})</span><span> &mdash; @message.Message</span><br />
} }
</div> </div>

View File

@ -8,6 +8,8 @@ using System.Linq;
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore.Metadata;
namespace SharedLibraryCore.Database namespace SharedLibraryCore.Database
{ {
@ -103,6 +105,13 @@ namespace SharedLibraryCore.Database
ent.HasIndex(a => a.Name); ent.HasIndex(a => a.Name);
}); });
modelBuilder.Entity<EFChangeHistory>(ent =>
{
ent.Property(c => c.ChangeHistoryId)
.ValueGeneratedOnAdd()
.HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
});
// force full name for database conversion // force full name for database conversion
modelBuilder.Entity<EFClient>().ToTable("EFClients"); modelBuilder.Entity<EFClient>().ToTable("EFClients");
modelBuilder.Entity<EFAlias>().ToTable("EFAlias"); modelBuilder.Entity<EFAlias>().ToTable("EFAlias");

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text; using System.Text;
namespace SharedLibraryCore.Database.Models namespace SharedLibraryCore.Database.Models

View File

@ -4,6 +4,7 @@ namespace SharedLibraryCore.Dtos
{ {
public class ChatInfo public class ChatInfo
{ {
public int ClientId { get; set; }
public string Message { get; set; } public string Message { get; set; }
public DateTime Time { get; set; } public DateTime Time { get; set; }
public string Name { get; set; } public string Name { get; set; }

View File

@ -25,92 +25,7 @@ namespace SharedLibraryCore.Events
return eventList; return eventList;
} }
private static async Task SaveChangeHistory(GameEvent e) public static void OnGameEvent(object sender, GameEventArgs eventState)
{
EFChangeHistory change = null;
switch (e.Type)
{
case GameEvent.EventType.Unknown:
break;
case GameEvent.EventType.Start:
break;
case GameEvent.EventType.Stop:
break;
case GameEvent.EventType.Connect:
break;
case GameEvent.EventType.Join:
break;
case GameEvent.EventType.Quit:
break;
case GameEvent.EventType.Disconnect:
break;
case GameEvent.EventType.MapEnd:
break;
case GameEvent.EventType.MapChange:
break;
case GameEvent.EventType.Say:
break;
case GameEvent.EventType.Warn:
break;
case GameEvent.EventType.Report:
break;
case GameEvent.EventType.Flag:
break;
case GameEvent.EventType.Unflag:
break;
case GameEvent.EventType.Kick:
break;
case GameEvent.EventType.TempBan:
break;
case GameEvent.EventType.Ban:
change = new EFChangeHistory()
{
OriginEntityId = e.Origin.ClientId,
TargetEntityId = e.Target.ClientId,
TypeOfChange = EFChangeHistory.ChangeType.Ban,
Comment = e.Data
};
break;
case GameEvent.EventType.Command:
break;
case GameEvent.EventType.ChangePermission:
change = new EFChangeHistory()
{
OriginEntityId = e.Origin.ClientId,
TargetEntityId = e.Target.ClientId,
TypeOfChange = EFChangeHistory.ChangeType.Permission,
PreviousValue = ((Change)e.Extra).PreviousValue,
CurrentValue = ((Change)e.Extra).NewValue
};
break;
case GameEvent.EventType.Broadcast:
break;
case GameEvent.EventType.Tell:
break;
case GameEvent.EventType.ScriptDamage:
break;
case GameEvent.EventType.ScriptKill:
break;
case GameEvent.EventType.Damage:
break;
case GameEvent.EventType.Kill:
break;
case GameEvent.EventType.JoinTeam:
break;
}
if (change != null)
{
using (var ctx = new DatabaseContext(true))
{
ctx.EFChangeHistory.Add(change);
await ctx.SaveChangesAsync();
}
}
}
public static async void OnGameEvent(object sender, GameEventArgs eventState)
{ {
var E = eventState.Event; var E = eventState.Event;
// don't want to clog up the api with unknown events // don't want to clog up the api with unknown events
@ -150,8 +65,6 @@ namespace SharedLibraryCore.Events
// add the new event to the list // add the new event to the list
AddNewEvent(apiEvent); AddNewEvent(apiEvent);
await SaveChangeHistory(E);
} }
/// <summary> /// <summary>

View File

@ -19,13 +19,24 @@ namespace SharedLibraryCore.Helpers
} }
public void OnChange(T value) public void OnChange(T value)
{
lock (value)
{ {
// clear the first value when count max count reached // clear the first value when count max count reached
if (Values.Count > 30) if (Values.Count > 30)
Values.RemoveAt(0); Values.RemoveAt(0);
Values.Add(value); Values.Add(value);
} }
}
public List<T> GetChanges() => Values; public T[] GetChanges() => Values.ToArray();
public bool HasChanges => Values.Count > 0;
public void ClearChanges()
{
lock (Values)
Values.Clear();
}
} }
} }

View File

@ -9,7 +9,6 @@ namespace SharedLibraryCore.Interfaces
{ {
public interface IEntityService<T> public interface IEntityService<T>
{ {
Task<T> CreateProxy();
Task<T> Create(T entity); Task<T> Create(T entity);
Task<T> Delete(T entity); Task<T> Delete(T entity);
Task<T> Update(T entity); Task<T> Update(T entity);

View File

@ -39,5 +39,6 @@ namespace SharedLibraryCore.Interfaces
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
IPageList GetPageList(); IPageList GetPageList();
string Version { get;}
} }
} }

View File

@ -0,0 +1,686 @@
// <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("20180915163111_AddIndexToMessageTimeSent")]
partial class AddIndexToMessageTimeSent
{
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<double>("VisibilityPercentage");
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.HasIndex("TimeSent");
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.Property<double>("VisionAverage");
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.Property<DateTime>("When");
b.HasKey("RatingId");
b.HasIndex("Performance");
b.HasIndex("Ranking");
b.HasIndex("RatingHistoryId");
b.HasIndex("ServerId");
b.HasIndex("When");
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()
.HasMaxLength(24);
b.HasKey("AliasId");
b.HasIndex("IPAddress");
b.HasIndex("LinkId");
b.HasIndex("Name");
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<string>("CurrentValue");
b.Property<int>("OriginEntityId");
b.Property<string>("PreviousValue");
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,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace SharedLibraryCore.Migrations
{
public partial class AddIndexToMessageTimeSent : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateIndex(
name: "IX_EFClientMessages_TimeSent",
table: "EFClientMessages",
column: "TimeSent");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_EFClientMessages_TimeSent",
table: "EFClientMessages");
}
}
}

View File

@ -0,0 +1,688 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using SharedLibraryCore.Database;
namespace SharedLibraryCore.Migrations
{
[DbContext(typeof(DatabaseContext))]
[Migration("20180915164118_ForceAutoIncrementChangeHistory")]
partial class ForceAutoIncrementChangeHistory
{
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<double>("VisibilityPercentage");
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.HasIndex("TimeSent");
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.Property<double>("VisionAverage");
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.Property<DateTime>("When");
b.HasKey("RatingId");
b.HasIndex("Performance");
b.HasIndex("Ranking");
b.HasIndex("RatingHistoryId");
b.HasIndex("ServerId");
b.HasIndex("When");
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()
.HasMaxLength(24);
b.HasKey("AliasId");
b.HasIndex("IPAddress");
b.HasIndex("LinkId");
b.HasIndex("Name");
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()
.HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
b.Property<bool>("Active");
b.Property<string>("Comment")
.HasMaxLength(128);
b.Property<string>("CurrentValue");
b.Property<int>("OriginEntityId");
b.Property<string>("PreviousValue");
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,25 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
namespace SharedLibraryCore.Migrations
{
public partial class ForceAutoIncrementChangeHistory : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
// hack: we can't alter the column on SQLite, but we need max length limit for the Index in MySQL etc
if (migrationBuilder.ActiveProvider != "Microsoft.EntityFrameworkCore.Sqlite")
{
migrationBuilder.AlterColumn<int>(
name: "ChangeHistoryId",
table: "EFChangeHistory"
).Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
}
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

View File

@ -2,6 +2,7 @@
using System; 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.Storage.ValueConversion;
using SharedLibraryCore.Database; using SharedLibraryCore.Database;
@ -155,6 +156,8 @@ namespace SharedLibraryCore.Migrations
b.HasIndex("ServerId"); b.HasIndex("ServerId");
b.HasIndex("TimeSent");
b.ToTable("EFClientMessages"); b.ToTable("EFClientMessages");
}); });
@ -349,7 +352,8 @@ namespace SharedLibraryCore.Migrations
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b => modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b =>
{ {
b.Property<int>("ChangeHistoryId") b.Property<int>("ChangeHistoryId")
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd()
.HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
b.Property<bool>("Active"); b.Property<bool>("Active");

View File

@ -54,9 +54,10 @@ namespace SharedLibraryCore.Services
public async Task<IList<EFAlias>> Find(Func<EFAlias, bool> expression) public async Task<IList<EFAlias>> Find(Func<EFAlias, bool> expression)
{ {
// todo: max better?
return await Task.Run(() => return await Task.Run(() =>
{ {
using (var context = new DatabaseContext()) using (var context = new DatabaseContext(true))
return context.Aliases return context.Aliases
.AsNoTracking() .AsNoTracking()
.Include(a => a.Link.Children) .Include(a => a.Link.Children)
@ -67,10 +68,9 @@ namespace SharedLibraryCore.Services
public async Task<EFAlias> Get(int entityID) public async Task<EFAlias> Get(int entityID)
{ {
using (var context = new DatabaseContext()) using (var context = new DatabaseContext(true))
return await context.Aliases return await context.Aliases
.AsNoTracking() .FirstOrDefaultAsync(e => e.AliasId == entityID);
.SingleOrDefaultAsync(e => e.AliasId == entityID);
} }
public Task<EFAlias> GetUnique(long entityProperty) public Task<EFAlias> GetUnique(long entityProperty)
@ -81,23 +81,6 @@ namespace SharedLibraryCore.Services
public async Task<EFAlias> Update(EFAlias entity) public async Task<EFAlias> Update(EFAlias entity)
{ {
throw await Task.FromResult(new Exception()); throw await Task.FromResult(new Exception());
/*using (var context = new DatabaseContext())
{
entity = context.Aliases.Attach(entity);
context.Entry(entity).State = EntityState.Modified;
await context.SaveChangesAsync();
return entity;
}*/
}
public async Task<EFAliasLink> CreateLink(EFAliasLink link)
{
using (var context = new DatabaseContext())
{
context.AliasLinks.Add(link);
await context.SaveChangesAsync();
return link;
}
} }
} }
} }

View File

@ -0,0 +1,95 @@
using SharedLibraryCore.Database;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Events;
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace SharedLibraryCore.Services
{
public class ChangeHistoryService : IEntityService<EFChangeHistory>
{
public Task<EFChangeHistory> Create(EFChangeHistory entity)
{
throw new NotImplementedException();
}
public async Task<EFChangeHistory> Add(GameEvent e)
{
EFChangeHistory change = null;
switch (e.Type)
{
case GameEvent.EventType.Ban:
change = new EFChangeHistory()
{
OriginEntityId = e.Origin.ClientId,
TargetEntityId = e.Target.ClientId,
TypeOfChange = EFChangeHistory.ChangeType.Ban,
Comment = e.Data
};
break;
case GameEvent.EventType.Command:
break;
case GameEvent.EventType.ChangePermission:
change = new EFChangeHistory()
{
OriginEntityId = e.Origin.ClientId,
TargetEntityId = e.Target.ClientId,
TypeOfChange = EFChangeHistory.ChangeType.Permission,
PreviousValue = ((Change)e.Extra).PreviousValue,
CurrentValue = ((Change)e.Extra).NewValue
};
break;
default:
break;
}
if (change != null)
{
using (var ctx = new DatabaseContext(true))
{
ctx.EFChangeHistory.Add(change);
try
{
await ctx.SaveChangesAsync();
}
catch (Exception ex)
{
e.Owner.Logger.WriteDebug(ex.GetExceptionInfo());
}
}
}
return change;
}
public Task<EFChangeHistory> Delete(EFChangeHistory entity)
{
throw new NotImplementedException();
}
public Task<IList<EFChangeHistory>> Find(Func<EFChangeHistory, bool> expression)
{
throw new NotImplementedException();
}
public Task<EFChangeHistory> Get(int entityID)
{
throw new NotImplementedException();
}
public Task<EFChangeHistory> GetUnique(long entityProperty)
{
throw new NotImplementedException();
}
public Task<EFChangeHistory> Update(EFChangeHistory entity)
{
throw new NotImplementedException();
}
}
}

View File

@ -223,7 +223,7 @@ namespace SharedLibraryCore.Services
} }
} }
#region ServiceSpecific #region ServiceSpecific
public async Task<IList<EFClient>> GetOwners() public async Task<IList<EFClient>> GetOwners()
{ {
using (var context = new DatabaseContext()) using (var context = new DatabaseContext())
@ -262,7 +262,7 @@ namespace SharedLibraryCore.Services
name = name.ToLower(); name = name.ToLower();
using (var context = new DatabaseContext(true)) using (var context = new DatabaseContext(disableTracking: true))
{ {
int asIP = name.ConvertToIP(); int asIP = name.ConvertToIP();
// hack: so IW4MAdmin and bots don't show up in search results // hack: so IW4MAdmin and bots don't show up in search results
@ -287,57 +287,9 @@ namespace SharedLibraryCore.Services
} }
} }
public async Task<IList<EFClient>> GetClientByIP(int ipAddress)
{
using (var context = new DatabaseContext(true))
{
var iqClients = (from alias in context.Aliases
.AsNoTracking()
where alias.IPAddress == ipAddress
join link in context.AliasLinks
on alias.LinkId equals link.AliasLinkId
join client in context.Clients
.AsNoTracking()
on alias.LinkId equals client.AliasLinkId
select client)
.Distinct()
.Include(c => c.CurrentAlias)
.Include(c => c.AliasLink.Children);
return await iqClients.ToListAsync();
}
}
public async Task<IList<EFClient>> GetRecentClients(int offset, int count)
{
using (var context = new DatabaseContext())
return await context.Clients
.AsNoTracking()
.Include(c => c.CurrentAlias)
.Include(p => p.AliasLink)
.OrderByDescending(p => p.ClientId)
.Skip(offset)
.Take(count)
.ToListAsync();
}
public async Task<IList<EFClient>> PruneInactivePrivilegedClients(int inactiveDays)
{
using (var context = new DatabaseContext())
{
var inactive = await context.Clients.Where(c => c.Level > Objects.Player.Permission.Flagged)
.AsNoTracking()
.Where(c => (DateTime.UtcNow - c.LastConnection).TotalDays >= inactiveDays)
.ToListAsync();
inactive.ForEach(c => c.Level = Player.Permission.User);
await context.SaveChangesAsync();
return inactive;
}
}
public async Task<int> GetTotalClientsAsync() public async Task<int> GetTotalClientsAsync()
{ {
using (var context = new DatabaseContext()) using (var context = new DatabaseContext(true))
return await context.Clients return await context.Clients
.CountAsync(); .CountAsync();
} }
@ -349,9 +301,9 @@ namespace SharedLibraryCore.Services
public async Task<int> GetTotalPlayTime() public async Task<int> GetTotalPlayTime()
{ {
using (var context = new DatabaseContext()) using (var context = new DatabaseContext(true))
return await context.Clients.SumAsync(c => c.TotalConnectionTime); return await context.Clients.SumAsync(c => c.TotalConnectionTime);
} }
#endregion #endregion
} }
} }

View File

@ -66,6 +66,28 @@ namespace SharedLibraryCore
return newStr; return newStr;
} }
/// <summary>
/// helper method to get the information about an exception and inner exceptions
/// </summary>
/// <param name="ex"></param>
/// <returns></returns>
public static string GetExceptionInfo(this Exception ex)
{
var sb = new StringBuilder();
int depth = 0;
while (ex != null)
{
sb.AppendLine($"Exception[{depth}] Name: {ex.GetType().FullName}");
sb.AppendLine($"Exception[{depth}] Message: {ex.Message}");
sb.AppendLine($"Exception[{depth}] Call Stack: {ex.StackTrace}");
sb.AppendLine($"Exception[{depth}] Source: {ex.Source}");
depth++;
ex = ex.InnerException;
}
return sb.ToString();
}
public static Player.Permission MatchPermission(String str) public static Player.Permission MatchPermission(String str)
{ {
String lookingFor = str.ToLower(); String lookingFor = str.ToLower();

View File

@ -37,6 +37,8 @@ namespace WebfrontCore.Controllers
SocialLink = Manager.GetApplicationSettings().Configuration().SocialLinkAddress; SocialLink = Manager.GetApplicationSettings().Configuration().SocialLinkAddress;
SocialTitle = Manager.GetApplicationSettings().Configuration().SocialLinkTitle; SocialTitle = Manager.GetApplicationSettings().Configuration().SocialLinkTitle;
} }
ViewBag.Version = Manager.Version;
} }
public override void OnActionExecuting(ActionExecutingContext context) public override void OnActionExecuting(ActionExecutingContext context)

View File

@ -135,7 +135,22 @@
<div class="container p-4"> <div class="container p-4">
@RenderBody() @RenderBody()
<footer></footer> <footer id="footer_text">
<div class="d-lg-none d-block text-center pt-4 pb-4">
<a href="https://github.com/RaidMax/IW4M-Admin/releases" target="_blank">
@Program.Manager.Version
</a>
<br />
<span class="text-muted">Developed by RaidMax</span>
</div>
<div class="footer-mobile d-lg-block d-none text-center">
<a href="https://github.com/RaidMax/IW4M-Admin/releases" target="_blank">
@Program.Manager.Version
</a>
<br />
<span class="text-muted">Developed by RaidMax</span>
</div>
</footer>
</div> </div>
<environment include="Development"> <environment include="Development">
<script type="text/javascript" src="~/lib/jQuery/dist/jquery.js"></script> <script type="text/javascript" src="~/lib/jQuery/dist/jquery.js"></script>

View File

@ -6409,3 +6409,11 @@ form *, select {
.client-message, .automated-penalty-info-detailed { .client-message, .automated-penalty-info-detailed {
cursor: pointer; } cursor: pointer; }
#footer_text {
font-size: 0.85rem; }
.footer-mobile {
position: fixed;
bottom: 1em;
right: 1em; }

View File

@ -209,3 +209,13 @@ form *, select {
.client-message, .automated-penalty-info-detailed { .client-message, .automated-penalty-info-detailed {
cursor: pointer; cursor: pointer;
} }
#footer_text {
font-size: 0.85rem;
}
.footer-mobile {
position: fixed;
bottom: 1em;
right: 1em;
}

View File

@ -1,4 +1,4 @@
function getPlayerHistoryChart(playerHistory, i, width, color) { function getPlayerHistoryChart(playerHistory, i, width, color, maxClients) {
/////////////////////////////////////// ///////////////////////////////////////
// thanks to canvasjs :( // thanks to canvasjs :(
playerHistory.forEach(function (item, i) { playerHistory.forEach(function (item, i) {
@ -29,6 +29,7 @@
lineThickness: 0, lineThickness: 0,
tickThickness: 0, tickThickness: 0,
minimum: 0, minimum: 0,
maximum: maxClients + 1,
margin: 0, margin: 0,
valueFormatString: " ", valueFormatString: " ",
labelMaxWidth: 0 labelMaxWidth: 0
@ -53,9 +54,10 @@ var charts = {};
$('.server-history-row').each(function (index, element) { $('.server-history-row').each(function (index, element) {
let clientHistory = $(this).data('clienthistory'); let clientHistory = $(this).data('clienthistory');
let serverId = $(this).data('serverid'); let serverId = $(this).data('serverid');
let maxClients = parseInt($('#server_header_' + serverId + ' .server-maxclients').text());
let color = $(this).data('online') === 'True' ? 'rgba(0, 122, 204, 0.432)' : '#ff6060' let color = $(this).data('online') === 'True' ? 'rgba(0, 122, 204, 0.432)' : '#ff6060'
let width = $('.server-header').first().width(); let width = $('.server-header').first().width();
let historyChart = getPlayerHistoryChart(clientHistory, serverId, width, color); let historyChart = getPlayerHistoryChart(clientHistory, serverId, width, color, maxClients);
historyChart.render(); historyChart.render();
charts[serverId] = historyChart; charts[serverId] = historyChart;
}); });
@ -82,7 +84,7 @@ function refreshClientActivity() {
$('#server_clientactivity_' + serverId).html(response); $('#server_clientactivity_' + serverId).html(response);
}) })
.fail(function (jqxhr, textStatus, error) { .fail(function (jqxhr, textStatus, error) {
$('#server_clientactivity_' + serverId).html("Could not load client activity - " + error); $('#server_clientactivity_' + serverId).html(" Could not load client activity - " + error);
}); });
}); });
} }