adding Cod4 support (for steam GUID is truncated to 16 characters)
exit properly whoops add all linked accounts to drop down consolidate linked admin accounts to the most recently seen one limited some waits to 5s to hopefully prevent a rare thread lock
This commit is contained in:
parent
6e5501b32d
commit
699c19cd4b
@ -27,9 +27,9 @@
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!--<PropertyGroup>
|
||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||
</PropertyGroup>
|
||||
</PropertyGroup>-->
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SharedLibraryCore\SharedLibraryCore.csproj">
|
||||
|
@ -82,14 +82,17 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
|
||||
if (cleanedEventLine[0] == 'D')
|
||||
{
|
||||
return new GameEvent()
|
||||
if (Regex.Match(cleanedEventLine, @"^(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)
|
||||
{
|
||||
Type = GameEvent.EventType.Damage,
|
||||
Data = Regex.Replace(logLine, @"[0-9]+:[0-9]+\ ", "").Trim(),
|
||||
Origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong()),
|
||||
Target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong()),
|
||||
Owner = server
|
||||
};
|
||||
return new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Damage,
|
||||
Data = cleanedEventLine,
|
||||
Origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong()),
|
||||
Target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong()),
|
||||
Owner = server
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (cleanedEventLine.Contains("ExitLevel"))
|
||||
|
@ -49,8 +49,11 @@ namespace IW4MAdmin.Application.IO
|
||||
events.Add(Parser.GetEvent(server, eventLine));
|
||||
}
|
||||
|
||||
catch (Exception)
|
||||
catch (Exception e)
|
||||
{
|
||||
Program.ServerManager.GetLogger().WriteWarning("Could not properly parse event line");
|
||||
Program.ServerManager.GetLogger().WriteDebug(e.Message);
|
||||
Program.ServerManager.GetLogger().WriteDebug(eventLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ namespace IW4MAdmin.Application
|
||||
};
|
||||
|
||||
ServerManager.GetEventHandler().AddEvent(E);
|
||||
E.OnProcessed.Wait();
|
||||
E.OnProcessed.Wait(5000);
|
||||
}
|
||||
Console.Write('>');
|
||||
|
||||
@ -155,6 +155,7 @@ namespace IW4MAdmin.Application
|
||||
Console.WriteLine($"Exception: {e.Message}");
|
||||
Console.WriteLine(loc["MANAGER_EXIT"]);
|
||||
Console.ReadKey();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ServerManager.GetApplicationSettings().Configuration().EnableWebFront)
|
||||
@ -171,7 +172,7 @@ namespace IW4MAdmin.Application
|
||||
private static void OnCancelKey(object sender, ConsoleCancelEventArgs e)
|
||||
{
|
||||
ServerManager.Stop();
|
||||
OnShutdownComplete.Wait();
|
||||
OnShutdownComplete.Wait(5000);
|
||||
}
|
||||
|
||||
static void CheckDirectories()
|
||||
|
@ -341,7 +341,7 @@ namespace IW4MAdmin.Application
|
||||
{
|
||||
try
|
||||
{
|
||||
Heartbeat.Send(this, true).Wait();
|
||||
Heartbeat.Send(this, true).Wait(5000);
|
||||
heartbeatState.Connected = true;
|
||||
}
|
||||
|
||||
@ -356,7 +356,7 @@ namespace IW4MAdmin.Application
|
||||
{
|
||||
try
|
||||
{
|
||||
Heartbeat.Send(this).Wait();
|
||||
Heartbeat.Send(this).Wait(5000);
|
||||
}
|
||||
catch (System.Net.Http.HttpRequestException e)
|
||||
{
|
||||
|
@ -23,7 +23,7 @@ namespace Application.RconParsers
|
||||
TempBan = "tempbanclient {0} \"{1}\""
|
||||
};
|
||||
|
||||
private static string StatusRegex = @"^( *[0-9]+) +-*([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){16}|bot[0-9]+|(?:[0-9]+)) +(.{0,20}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback) +(-*[0-9]+) +([0-9]+) *$";
|
||||
private static string StatusRegex = @"^( *[0-9]+) +-*([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){16}|(?:[a-z]|[0-9]){32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback) +(-*[0-9]+) +([0-9]+) *$";
|
||||
|
||||
public async Task<string[]> ExecuteCommandAsync(Connection connection, string command)
|
||||
{
|
||||
|
@ -51,7 +51,6 @@ namespace IW4MAdmin
|
||||
|
||||
override public async Task<bool> AddPlayer(Player polledPlayer)
|
||||
{
|
||||
|
||||
if ((polledPlayer.Ping == 999 && !polledPlayer.IsBot) ||
|
||||
polledPlayer.Ping < 1 ||
|
||||
polledPlayer.ClientNumber < 0)
|
||||
@ -451,12 +450,16 @@ namespace IW4MAdmin
|
||||
{
|
||||
if (E.Type == GameEvent.EventType.Connect)
|
||||
{
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
// this may be a fix for a hard to reproduce null exception error
|
||||
lock (ChatHistory)
|
||||
{
|
||||
Name = E.Origin.Name,
|
||||
Message = "CONNECTED",
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
{
|
||||
Name = E.Origin?.Name ?? "ERROR!",
|
||||
Message = "CONNECTED",
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
if (E.Origin.Level > Player.Permission.Moderator)
|
||||
await E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count));
|
||||
@ -479,24 +482,35 @@ namespace IW4MAdmin
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Disconnect)
|
||||
{
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
// this may be a fix for a hard to reproduce null exception error
|
||||
lock (ChatHistory)
|
||||
{
|
||||
Name = E.Origin.Name,
|
||||
Message = "DISCONNECTED",
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
{
|
||||
Name = E.Origin?.Name ?? "ERROR!",
|
||||
Message = "DISCONNECTED",
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (E.Type == GameEvent.EventType.Say && E.Data?.Length >= 2)
|
||||
if (E.Type == GameEvent.EventType.Say)
|
||||
{
|
||||
E.Data = E.Data.StripColors();
|
||||
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
if (E.Data.Length > 0)
|
||||
{
|
||||
Name = E.Origin.Name,
|
||||
Message = E.Data,
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
// this may be a fix for a hard to reproduce null exception error
|
||||
lock (ChatHistory)
|
||||
{
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
{
|
||||
Name = E.Origin?.Name ?? "ERROR!",
|
||||
Message = E.Data,
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (E.Type == GameEvent.EventType.MapChange)
|
||||
@ -731,7 +745,10 @@ namespace IW4MAdmin
|
||||
GameName = Utilities.GetGame(version.Value);
|
||||
|
||||
if (GameName == Game.IW4)
|
||||
{
|
||||
EventParser = new IW4EventParser();
|
||||
RconParser = new IW4RConParser();
|
||||
}
|
||||
else if (GameName == Game.IW5)
|
||||
EventParser = new IW5EventParser();
|
||||
else if (GameName == Game.T5M)
|
||||
|
@ -81,8 +81,8 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
double newAverage = (previousAverage * (hitLoc.HitCount - 1) + realAgainstPredict) / hitLoc.HitCount;
|
||||
hitLoc.HitOffsetAverage = (float)newAverage;
|
||||
|
||||
|
||||
if (hitLoc.HitOffsetAverage > Thresholds.MaxOffset)
|
||||
if (hitLoc.HitOffsetAverage > Thresholds.MaxOffset &&
|
||||
hitLoc.HitCount > 15)
|
||||
{
|
||||
Log.WriteDebug("*** Reached Max Lifetime Average for Angle Difference ***");
|
||||
Log.WriteDebug($"Lifetime Average = {newAverage}");
|
||||
@ -92,9 +92,10 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
|
||||
return new DetectionPenaltyResult()
|
||||
{
|
||||
ClientPenalty = Penalty.PenaltyType.Flag,
|
||||
ClientPenalty = Penalty.PenaltyType.Ban,
|
||||
Value = hitLoc.HitOffsetAverage,
|
||||
HitCount = hitLoc.HitCount,
|
||||
Type = DetectionType.Offset
|
||||
};
|
||||
}
|
||||
|
||||
@ -102,7 +103,8 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
double sessAverage = (AngleDifferenceAverage * (HitCount - 1) + realAgainstPredict) / HitCount;
|
||||
AngleDifferenceAverage = sessAverage;
|
||||
|
||||
if (sessAverage > Thresholds.MaxOffset)
|
||||
if (sessAverage > Thresholds.MaxOffset &&
|
||||
HitCount > 15)
|
||||
{
|
||||
Log.WriteDebug("*** Reached Max Session Average for Angle Difference ***");
|
||||
Log.WriteDebug($"Session Average = {sessAverage}");
|
||||
@ -124,7 +126,6 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
#endif
|
||||
}
|
||||
var currentStrain = Strain.GetStrain(kill.ViewAngles, Math.Max(50, kill.TimeOffset - LastOffset));
|
||||
|
||||
LastOffset = kill.TimeOffset;
|
||||
|
||||
if (currentStrain > ClientStats.MaxStrain)
|
||||
|
@ -28,8 +28,8 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
public const double KillTimeThreshold = 0.2;
|
||||
|
||||
public const double MaxStrainBan = 0.4399;
|
||||
public const double MaxOffset = 4.789;
|
||||
public const double MaxStrainFlag = 0.2;
|
||||
public const double MaxOffset = 1.2;
|
||||
public const double MaxStrainFlag = 1;
|
||||
|
||||
public static double GetMarginOfError(int numKills) => 1.6455 / Math.Sqrt(numKills);
|
||||
|
||||
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharedLibraryCore.Services;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
|
@ -104,7 +104,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
// get the client's stats from the database if it exists, otherwise create and attach a new one
|
||||
// if this fails we want to throw an exception
|
||||
var clientStats = statsSvc.ClientStatSvc.Find(c => c.ClientId == pl.ClientId && c.ServerId == serverId).FirstOrDefault();
|
||||
var clientStatsSvc = statsSvc.ClientStatSvc;
|
||||
var clientStats = clientStatsSvc.Find(c => c.ClientId == pl.ClientId && c.ServerId == serverId).FirstOrDefault();
|
||||
|
||||
if (clientStats == null)
|
||||
{
|
||||
@ -127,7 +128,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
};
|
||||
|
||||
// insert if they've not been added
|
||||
var clientStatsSvc = statsSvc.ClientStatSvc;
|
||||
clientStats = clientStatsSvc.Insert(clientStats);
|
||||
await clientStatsSvc.SaveChangesAsync();
|
||||
}
|
||||
@ -193,10 +193,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue4);
|
||||
|
||||
// sync their stats before they leave
|
||||
// clientStats = UpdateStats(clientStats);
|
||||
// var clientStatsSvc = statsSvc.ClientStatSvc;
|
||||
// clientStatsSvc.Update(clientStats);
|
||||
// await clientStatsSvc.SaveChangesAsync();
|
||||
var clientStatsSvc = statsSvc.ClientStatSvc;
|
||||
clientStats = UpdateStats(clientStats);
|
||||
clientStatsSvc.Update(clientStats);
|
||||
await clientStatsSvc.SaveChangesAsync();
|
||||
|
||||
// increment the total play time
|
||||
serverStats.TotalPlayTime += (int)(DateTime.UtcNow - pl.LastConnection).TotalSeconds;
|
||||
@ -305,6 +305,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
var clientDetection = Servers[serverId].PlayerDetections[attacker.ClientId];
|
||||
var clientStats = Servers[serverId].PlayerStats[attacker.ClientId];
|
||||
var clientStatsSvc = statsSvc.ClientStatSvc;
|
||||
clientStatsSvc.Update(clientStats);
|
||||
|
||||
// increment their hit count
|
||||
if (kill.DeathType == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET ||
|
||||
@ -313,7 +315,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
clientStats.HitLocations.Single(hl => hl.Location == kill.HitLoc).HitCount += 1;
|
||||
|
||||
statsSvc.ClientStatSvc.Update(clientStats);
|
||||
//statsSvc.ClientStatSvc.Update(clientStats);
|
||||
// await statsSvc.ClientStatSvc.SaveChangesAsync();
|
||||
}
|
||||
|
||||
@ -345,7 +347,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
Data = penalty.Type == Cheat.Detection.DetectionType.Bone ?
|
||||
$"{penalty.Type}-{(int)penalty.Location}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}" :
|
||||
$"{penalty.Type} -{Math.Round(penalty.Value, 2)}@{penalty.HitCount}",
|
||||
$"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}",
|
||||
Origin = new Player()
|
||||
{
|
||||
ClientId = 1,
|
||||
@ -365,10 +367,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
await executePenalty(clientDetection.ProcessKill(kill, isDamage));
|
||||
await executePenalty(clientDetection.ProcessTotalRatio(clientStats));
|
||||
|
||||
#if DEBUG
|
||||
statsSvc.ClientStatSvc.Update(clientStats);
|
||||
await statsSvc.ClientStatSvc.SaveChangesAsync();
|
||||
#endif
|
||||
await clientStatsSvc.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@ -448,10 +447,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
|
||||
// todo: do we want to save this immediately?
|
||||
var statsSvc = ContextThreads[serverId].ClientStatSvc;
|
||||
statsSvc.Update(attackerStats);
|
||||
statsSvc.Update(victimStats);
|
||||
await statsSvc.SaveChangesAsync();
|
||||
var clientStatsSvc = ContextThreads[serverId].ClientStatSvc;
|
||||
clientStatsSvc.Update(attackerStats);
|
||||
clientStatsSvc.Update(victimStats);
|
||||
await clientStatsSvc.SaveChangesAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -478,7 +477,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
victimStats.KillStreak = 0;
|
||||
|
||||
// process the attacker's stats after the kills
|
||||
//attackerStats = UpdateStats(attackerStats);
|
||||
attackerStats = UpdateStats(attackerStats);
|
||||
|
||||
// update after calculation
|
||||
attackerStats.TimePlayed += (int)(DateTime.UtcNow - attackerStats.LastActive).TotalSeconds;
|
||||
@ -630,7 +629,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
Log.WriteDebug("Syncing stats contexts");
|
||||
await statsSvc.ServerStatsSvc.SaveChangesAsync();
|
||||
await statsSvc.ClientStatSvc.SaveChangesAsync();
|
||||
//await statsSvc.ClientStatSvc.SaveChangesAsync();
|
||||
await statsSvc.KillStatsSvc.SaveChangesAsync();
|
||||
await statsSvc.ServerSvc.SaveChangesAsync();
|
||||
|
||||
|
@ -20,7 +20,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
public GenericRepository<EFServer> ServerSvc { get; private set; }
|
||||
public GenericRepository<EFClientKill> KillStatsSvc { get; private set; }
|
||||
public GenericRepository<EFServerStatistics> ServerStatsSvc { get; private set; }
|
||||
public GenericRepository<EFClientMessage> MessageSvc { get; private set; }
|
||||
public GenericRepository<EFClientMessage> MessageSvc
|
||||
{
|
||||
get
|
||||
{
|
||||
return new GenericRepository<EFClientMessage>();
|
||||
}
|
||||
}
|
||||
|
||||
public ThreadSafeStatsService()
|
||||
{
|
||||
@ -28,7 +34,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
ServerSvc = new GenericRepository<EFServer>();
|
||||
KillStatsSvc = new GenericRepository<EFClientKill>();
|
||||
ServerStatsSvc = new GenericRepository<EFServerStatistics>();
|
||||
MessageSvc = new GenericRepository<EFClientMessage>();
|
||||
//MessageSvc = new GenericRepository<EFClientMessage>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -387,7 +387,7 @@ namespace SharedLibraryCore.Commands
|
||||
await E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAPROTATE"]} [^5{E.Origin.Name}^7]");
|
||||
else
|
||||
await E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAPROTATE"]);
|
||||
Task.Delay(5000).Wait();
|
||||
await Task.Delay(5000);
|
||||
await E.Owner.ExecuteCommandAsync("map_rotate");
|
||||
}
|
||||
}
|
||||
@ -553,14 +553,14 @@ namespace SharedLibraryCore.Commands
|
||||
if (m.Name.ToLower() == newMap || m.Alias.ToLower() == newMap)
|
||||
{
|
||||
await E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_SUCCESS"]} ^5{m.Alias}");
|
||||
Task.Delay(5000).Wait();
|
||||
await Task.Delay(5000);
|
||||
await E.Owner.LoadMap(m.Name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_UKN"]} ^5{newMap}");
|
||||
Task.Delay(5000).Wait();
|
||||
await Task.Delay(5000);
|
||||
await E.Owner.LoadMap(newMap);
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,8 @@ namespace SharedLibraryCore.Database.Models
|
||||
|
||||
[NotMapped]
|
||||
public string IPAddressString => new System.Net.IPAddress(BitConverter.GetBytes(IPAddress)).ToString();
|
||||
[NotMapped]
|
||||
public virtual IDictionary<int, long> LinkedAccounts { get; set; }
|
||||
|
||||
public virtual ICollection<EFPenalty> ReceivedPenalties { get; set; }
|
||||
public virtual ICollection<EFPenalty> AdministeredPenalties { get; set; }
|
||||
|
@ -24,5 +24,6 @@ namespace SharedLibraryCore.Dtos
|
||||
public List<ProfileMeta> Meta { get; set; }
|
||||
public bool Online { get; set; }
|
||||
public string TimeOnline { get; set; }
|
||||
public IDictionary<int, long> LinkedAccounts { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ namespace SharedLibraryCore.Dtos
|
||||
public string GameType { get; set; }
|
||||
public int ClientCount { get; set; }
|
||||
public int MaxClients { get; set; }
|
||||
public ChatInfo[] ChatHistory { get; set; }
|
||||
public List<ChatInfo> ChatHistory { get; set; }
|
||||
public List<PlayerInfo> Players { get; set; }
|
||||
public Helpers.PlayerHistory[] PlayerHistory { get; set; }
|
||||
public int ID { get; set; }
|
||||
|
@ -33,25 +33,6 @@ namespace SharedLibraryCore.RCon
|
||||
}
|
||||
}
|
||||
|
||||
class ResponseEvent
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string[] Response { get; set; }
|
||||
public Task Awaiter
|
||||
{
|
||||
get
|
||||
{
|
||||
return Task.Run(() => FinishedEvent.Wait());
|
||||
}
|
||||
}
|
||||
private ManualResetEventSlim FinishedEvent;
|
||||
|
||||
public ResponseEvent()
|
||||
{
|
||||
FinishedEvent = new ManualResetEventSlim();
|
||||
}
|
||||
}
|
||||
|
||||
public class Connection
|
||||
{
|
||||
public IPEndPoint Endpoint { get; private set; }
|
||||
@ -110,7 +91,7 @@ namespace SharedLibraryCore.RCon
|
||||
OnSent.Set();
|
||||
}
|
||||
|
||||
catch (SocketException)
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -167,9 +148,9 @@ namespace SharedLibraryCore.RCon
|
||||
public async Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "", bool waitForResponse = true)
|
||||
{
|
||||
// will this really prevent flooding?
|
||||
if ((DateTime.Now - LastQuery).TotalMilliseconds < 250)
|
||||
if ((DateTime.Now - LastQuery).TotalMilliseconds < 350)
|
||||
{
|
||||
await Task.Delay(250);
|
||||
await Task.Delay(350);
|
||||
}
|
||||
|
||||
LastQuery = DateTime.Now;
|
||||
|
@ -107,11 +107,33 @@ namespace SharedLibraryCore.Services
|
||||
{
|
||||
using (var context = new DatabaseContext())
|
||||
{
|
||||
return await context.Clients
|
||||
.AsNoTracking()
|
||||
.Include(c => c.CurrentAlias)
|
||||
.Include(c => c.AliasLink.Children)
|
||||
.SingleOrDefaultAsync(e => e.ClientId == entityID);
|
||||
context.ChangeTracker.AutoDetectChangesEnabled = false;
|
||||
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||
|
||||
var iqClient = from client in context.Clients
|
||||
.Include(c => c.CurrentAlias)
|
||||
.Include(c => c.AliasLink.Children)
|
||||
where client.ClientId == entityID
|
||||
select new
|
||||
{
|
||||
Client = client,
|
||||
LinkedAccounts = (from linkedClient in context.Clients
|
||||
where client.AliasLinkId == linkedClient.AliasLinkId
|
||||
select new
|
||||
{
|
||||
linkedClient.ClientId,
|
||||
linkedClient.NetworkId
|
||||
})
|
||||
};
|
||||
var foundClient = await iqClient.FirstOrDefaultAsync();
|
||||
|
||||
foundClient.Client.LinkedAccounts = new Dictionary<int, long>();
|
||||
// todo: find out the best way to do this
|
||||
// I'm doing this here because I don't know the best way to have multiple awaits in the query
|
||||
foreach (var linked in foundClient.LinkedAccounts)
|
||||
foundClient.Client.LinkedAccounts.Add(linked.ClientId, linked.NetworkId);
|
||||
|
||||
return foundClient.Client;
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,11 +238,15 @@ namespace SharedLibraryCore.Services
|
||||
{
|
||||
using (var context = new DatabaseContext())
|
||||
{
|
||||
return await context.Clients
|
||||
.AsNoTracking()
|
||||
.Include(c => c.CurrentAlias)
|
||||
.Where(c => c.Level >= Player.Permission.Trusted)
|
||||
.ToListAsync();
|
||||
context.ChangeTracker.AutoDetectChangesEnabled = false;
|
||||
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||
|
||||
var iqClients = from client in context.Clients
|
||||
.Include(c => c.CurrentAlias)
|
||||
where client.Level >= Player.Permission.Trusted
|
||||
select client;
|
||||
|
||||
return await iqClients.ToListAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,14 +259,14 @@ namespace SharedLibraryCore.Services
|
||||
{
|
||||
var iqClients = (from alias in context.Aliases
|
||||
.AsNoTracking()
|
||||
where alias.Name.ToLower()
|
||||
.Contains(name.ToLower())
|
||||
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)
|
||||
where alias.Name.ToLower()
|
||||
.Contains(name.ToLower())
|
||||
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);
|
||||
@ -255,13 +281,13 @@ namespace SharedLibraryCore.Services
|
||||
{
|
||||
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)
|
||||
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);
|
||||
|
@ -12,7 +12,7 @@ namespace SharedLibraryCore.Services
|
||||
// https://stackoverflow.com/questions/43677906/crud-operations-with-entityframework-using-generic-type
|
||||
public class GenericRepository<TEntity> where TEntity : class
|
||||
{
|
||||
private dynamic _context;
|
||||
private DatabaseContext _context;
|
||||
private DbSet<TEntity> _dbSet;
|
||||
|
||||
protected DbContext Context
|
||||
@ -101,7 +101,6 @@ namespace SharedLibraryCore.Services
|
||||
dbSet.Attach(entity);
|
||||
|
||||
dbSet.Remove(entity);
|
||||
|
||||
}
|
||||
|
||||
public virtual void Delete(TEntity entity)
|
||||
@ -119,7 +118,6 @@ namespace SharedLibraryCore.Services
|
||||
T entity = dbSet.Find(id);
|
||||
dbSet.Attach(entity);
|
||||
dbSet.Remove(entity);
|
||||
|
||||
}
|
||||
|
||||
public virtual void Delete(object id)
|
||||
|
@ -208,7 +208,8 @@ namespace SharedLibraryCore.Services
|
||||
Offense = penalty.Offense,
|
||||
Type = penalty.Type.ToString()
|
||||
},
|
||||
When = penalty.When
|
||||
When = penalty.When,
|
||||
Sensitive = penalty.Type == Penalty.PenaltyType.Flag
|
||||
};
|
||||
// fixme: is this good and fast?
|
||||
var list = await iqPenalties.ToListAsync();
|
||||
|
@ -182,6 +182,7 @@ namespace SharedLibraryCore
|
||||
|
||||
public static long ConvertLong(this string str)
|
||||
{
|
||||
str = str.Substring(0, Math.Min(str.Length, 16));
|
||||
if (Int64.TryParse(str, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out long id))
|
||||
return id;
|
||||
var bot = Regex.Match(str, @"bot[0-9]+").Value;
|
||||
|
@ -71,7 +71,7 @@ namespace WebfrontCore.Controllers
|
||||
catch (System.Collections.Generic.KeyNotFoundException)
|
||||
{
|
||||
// force the "banned" client to be signed out
|
||||
HttpContext.SignOutAsync().Wait();
|
||||
HttpContext.SignOutAsync().Wait(5000);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,8 @@ namespace WebfrontCore.Controllers
|
||||
.OrderBy(i => i)
|
||||
.ToList(),
|
||||
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
|
||||
};
|
||||
|
||||
var meta = await MetaService.GetMeta(client.ClientId);
|
||||
@ -54,7 +55,7 @@ namespace WebfrontCore.Controllers
|
||||
clientDto.Meta.Add(new ProfileMeta()
|
||||
{
|
||||
Key = Localization["WEBFRONT_CLIENT_META_MASKED"],
|
||||
Value = client.Masked ? Localization["WEBFRONT_CLIENT_META_TRUE"]: Localization["WEBFRONT_CLIENT_META_FALSE"],
|
||||
Value = client.Masked ? Localization["WEBFRONT_CLIENT_META_TRUE"] : Localization["WEBFRONT_CLIENT_META_FALSE"],
|
||||
Sensitive = true,
|
||||
When = DateTime.MinValue
|
||||
});
|
||||
@ -94,7 +95,9 @@ namespace WebfrontCore.Controllers
|
||||
{
|
||||
var admins = (await Manager.GetClientService().GetPrivilegedClients())
|
||||
.Where(a => a.Active)
|
||||
.OrderByDescending(a => a.Level);
|
||||
.OrderByDescending(a => a.Level).ThenByDescending(a => a.LastConnection)
|
||||
.GroupBy(a => a.AliasLinkId).Select(a => a.First());
|
||||
|
||||
var adminsDict = new Dictionary<SharedLibraryCore.Objects.Player.Permission, IList<ClientInfo>>();
|
||||
|
||||
foreach (var admin in admins)
|
||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SharedLibraryCore.Dtos;
|
||||
|
||||
@ -21,6 +22,11 @@ namespace WebfrontCore.Controllers
|
||||
|
||||
public IActionResult Error()
|
||||
{
|
||||
var exceptionFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
|
||||
Manager.GetLogger().WriteError($"[Webfront] {exceptionFeature.Error.Message}");
|
||||
Manager.GetLogger().WriteDebug(exceptionFeature.Path);
|
||||
Manager.GetLogger().WriteDebug(exceptionFeature.Error.StackTrace);
|
||||
|
||||
ViewBag.Description = Localization["WEBFRONT_ERROR_DESC"];
|
||||
ViewBag.Title = Localization["WEBFRONT_ERROR_TITLE"];
|
||||
return View();
|
||||
|
@ -13,7 +13,6 @@ namespace WebfrontCore.Controllers
|
||||
[ResponseCache(NoStore = true, Duration = 0)]
|
||||
public IActionResult ClientActivity(int id)
|
||||
{
|
||||
|
||||
var s = Manager.GetServers().FirstOrDefault(s2 => s2.GetHashCode() == id);
|
||||
if (s == null)
|
||||
return View("Error", "Invalid server!");
|
||||
@ -27,14 +26,15 @@ namespace WebfrontCore.Controllers
|
||||
ClientCount = s.ClientNum,
|
||||
MaxClients = s.MaxClients,
|
||||
GameType = s.Gametype,
|
||||
Players = s.Players.Where(p => p != null).Select(p => new PlayerInfo
|
||||
Players = s.GetPlayersAsList()
|
||||
.Select(p => new PlayerInfo
|
||||
{
|
||||
Name = p.Name,
|
||||
ClientId = p.ClientId,
|
||||
Level = p.Level.ToString(),
|
||||
LevelInt = (int)p.Level
|
||||
}).ToList(),
|
||||
ChatHistory = s.ChatHistory.OrderBy(c => c.Time).Take((int)Math.Ceiling(s.ClientNum / 2.0)).ToArray(),
|
||||
ChatHistory = s.ChatHistory,
|
||||
PlayerHistory = s.PlayerHistory.ToArray(),
|
||||
};
|
||||
return PartialView("_ClientActivity", serverInfo);
|
||||
|
@ -19,7 +19,7 @@ namespace WebfrontCore.ViewComponents
|
||||
MaxClients = s.MaxClients,
|
||||
GameType = s.Gametype,
|
||||
PlayerHistory = s.PlayerHistory.ToArray(),
|
||||
Players = s.Players.Where(p => p != null)
|
||||
Players = s.GetPlayersAsList()
|
||||
.Select(p => new PlayerInfo()
|
||||
{
|
||||
Name = p.Name,
|
||||
@ -27,7 +27,7 @@ namespace WebfrontCore.ViewComponents
|
||||
Level = p.Level.ToString(),
|
||||
LevelInt = (int)p.Level
|
||||
}).ToList(),
|
||||
ChatHistory = s.ChatHistory.ToArray(),
|
||||
ChatHistory = s.ChatHistory,
|
||||
Online = !s.Throttled
|
||||
}).ToList();
|
||||
return View("_List", serverInfo);
|
||||
|
@ -31,13 +31,13 @@
|
||||
<div id="profile_aliases_btn" class="oi oi-caret-bottom h3 ml-0 ml-md-2"></div>
|
||||
|
||||
@if (Model.LevelInt < (int)ViewBag.User.Level &&
|
||||
(SharedLibraryCore.Objects.Player.Permission)Model.LevelInt != SharedLibraryCore.Objects.Player.Permission.Banned)
|
||||
(SharedLibraryCore.Objects.Player.Permission)Model.LevelInt != SharedLibraryCore.Objects.Player.Permission.Banned)
|
||||
{
|
||||
<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 &&
|
||||
(SharedLibraryCore.Objects.Player.Permission)Model.LevelInt == SharedLibraryCore.Objects.Player.Permission.Banned)
|
||||
(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>
|
||||
}
|
||||
@ -45,10 +45,13 @@
|
||||
|
||||
<div id="profile_aliases" class="pr-0 pr-sm-4 pb-2 mb-2 text-muted order-0">
|
||||
@{
|
||||
<span class="text-secondary">@Model.NetworkId.ToString("X")</span> <br />
|
||||
foreach (var linked in Model.LinkedAccounts)
|
||||
{
|
||||
@Html.ActionLink(linked.Value.ToString("X"), "ProfileAsync", "Client", new { id = linked.Key}, new { @class = "link-inverse" })<br/>
|
||||
}
|
||||
foreach (string alias in Model.Aliases)
|
||||
{
|
||||
@alias <br />
|
||||
@alias<br />
|
||||
}
|
||||
|
||||
if (ViewBag.Authorized)
|
||||
|
@ -6,7 +6,7 @@
|
||||
}
|
||||
<div class="col-12 col-md-8 d-none d-md-block">
|
||||
@{
|
||||
for (int i = 0; i < Model.ChatHistory.Length; i++)
|
||||
for (int i = 0; i < Model.ChatHistory.Count; i++)
|
||||
{
|
||||
string message = @Model.ChatHistory[i].Message;
|
||||
if (Model.ChatHistory[i].Message == "CONNECTED")
|
||||
@ -47,13 +47,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (Model.ChatHistory.Length > 0)
|
||||
@if (Model.ChatHistory.Count > 0)
|
||||
{
|
||||
<div class="w-100 border-bottom d-md-none d-block mt-1 mb-1"></div>
|
||||
}
|
||||
<div class="col-12 col-md-8 d-md-none d-block text-left">
|
||||
@{
|
||||
for (int i = 0; i < Model.ChatHistory.Length; i++)
|
||||
for (int i = 0; i < Model.ChatHistory.Count; i++)
|
||||
{
|
||||
string message = @Model.ChatHistory[i].Message;
|
||||
if (Model.ChatHistory[i].Message == "CONNECTED")
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
@{
|
||||
Layout = null;
|
||||
ViewBag.Description += Model.Name + ", ";
|
||||
}
|
||||
|
||||
<div class="row server-header" id="server_header_@Model.ID">
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<!--<MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish>-->
|
||||
<MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
<TypeScriptToolsVersion>2.6</TypeScriptToolsVersion>
|
||||
<PackageId>RaidMax.IW4MAdmin.WebfrontCore</PackageId>
|
||||
|
Loading…
Reference in New Issue
Block a user