fixes for new polling setup

update database model for alias (nullable ip)
heartbeats now send ip to master server
This commit is contained in:
RaidMax 2018-11-25 20:00:36 -06:00
parent 9bdd7d1b8a
commit 5ac8a55c72
28 changed files with 1257 additions and 408 deletions

View File

@ -9,6 +9,8 @@ namespace IW4MAdmin.Application.API.Master
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("ip")]
public string IPAddress { get; set; }
[JsonProperty("port")]
public short Port { get; set; }
[JsonProperty("gametype")]

View File

@ -44,7 +44,8 @@ namespace IW4MAdmin.Application.API.Master
Map = s.CurrentMap.Name,
MaxClientNum = s.MaxClients,
Id = s.GetHashCode(),
Port = (short)s.GetPort()
Port = (short)s.GetPort(),
IPAddress = s.IP
}).ToList()
};

View File

@ -1,11 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using SharedLibraryCore;
using SharedLibraryCore;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Objects;
using System;
using System.Linq;
using System.Text.RegularExpressions;
namespace IW4MAdmin.Application.EventParsers
{
@ -149,7 +147,11 @@ namespace IW4MAdmin.Application.EventParsers
Owner = server,
Origin = new EFClient()
{
Name = regexMatch.Groups[4].ToString().StripColors(),
CurrentAlias = new EFAlias()
{
Active = false,
Name = regexMatch.Groups[4].ToString().StripColors(),
},
NetworkId = regexMatch.Groups[2].ToString().ConvertLong(),
ClientNumber = Convert.ToInt32(regexMatch.Groups[3].ToString()),
State = EFClient.ClientState.Connecting,
@ -171,7 +173,11 @@ namespace IW4MAdmin.Application.EventParsers
Owner = server,
Origin = new EFClient()
{
Name = regexMatch.Groups[4].ToString().StripColors(),
CurrentAlias = new EFAlias()
{
Active = false,
Name = regexMatch.Groups[4].ToString().StripColors()
},
NetworkId = regexMatch.Groups[2].ToString().ConvertLong(),
ClientNumber = Convert.ToInt32(regexMatch.Groups[3].ToString()),
State = EFClient.ClientState.Disconnecting
@ -217,6 +223,9 @@ namespace IW4MAdmin.Application.EventParsers
}
// other parsers can derive from this parser so we make it virtual
public virtual string GetGameDir() => "userraw";
public virtual string GetGameDir()
{
return "userraw";
}
}
}

View File

@ -1,18 +1,16 @@
using SharedLibraryCore;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace IW4MAdmin.Application.EventParsers
{
class IW5EventParser : IW4EventParser
{
public override string GetGameDir() => "logs";
public override string GetGameDir()
{
return "logs";
}
public override GameEvent GetEvent(Server server, string logLine)
{
@ -28,7 +26,11 @@ namespace IW4MAdmin.Application.EventParsers
{
NetworkId = lineSplit[1].ConvertLong(),
ClientNumber = clientNum,
Name = lineSplit[3]
CurrentAlias = new EFAlias()
{
Active = false,
Name = lineSplit[3]
}
};
return new GameEvent()
@ -48,7 +50,9 @@ namespace IW4MAdmin.Application.EventParsers
}
else
{
return base.GetEvent(server, logLine);
}
}
}
}

View File

@ -28,12 +28,12 @@ namespace IW4MAdmin.Application.IO
public long Length => -1;
public int UpdateInterval => 1000;
public int UpdateInterval => 350;
public async Task<ICollection<GameEvent>> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
{
#if DEBUG == true
server.Logger.WriteDebug($"Begin reading {fileSizeDiff} from http log");
server.Logger.WriteDebug($"Begin reading from http log");
#endif
var events = new List<GameEvent>();
string b64Path = server.LogPath.ToBase64UrlSafeString();

View File

@ -75,27 +75,9 @@ namespace IW4MAdmin
// client has connected in the past
else
{
var existingAlias = client.AliasLink.Children
.FirstOrDefault(a => a.Name == clientFromLog.Name);
if (existingAlias == null)
{
Logger.WriteDebug($"Client {clientFromLog} has connected previously under a different ip/name");
client.CurrentAlias = new EFAlias()
{
// this gets updated on client join
IPAddress = clientFromLog.IPAddress,
Name = clientFromLog.Name,
};
}
else
{
client.CurrentAlias = existingAlias;
client.CurrentAliasId = existingAlias.AliasId;
}
await Manager.GetClientService().Update(client);
// this is only a temporary version until the IPAddress is transmitted
client.CurrentAlias.Active = false;
client.CurrentAlias.Name = clientFromLog.Name;
}
Logger.WriteInfo($"Client {client} connected...");
@ -108,14 +90,15 @@ namespace IW4MAdmin
client.CurrentServer = this;
Clients[client.ClientNumber] = client;
client.OnConnect();
// this only happens the preconnect event occurred from RCon polling
if (clientFromLog.IPAddress != 0)
// this only happens if the preconnect event occurred from RCon polling
if (clientFromLog.IPAddress.HasValue)
{
await client.OnJoin(clientFromLog.IPAddress);
}
client.OnConnect();
client.State = EFClient.ClientState.Connected;
#if DEBUG == true
Logger.WriteDebug($"End PreConnect for {client}");
@ -218,11 +201,23 @@ namespace IW4MAdmin
/// <returns></returns>
override protected async Task<bool> ProcessEvent(GameEvent E)
{
if (E.Type == GameEvent.EventType.PreConnect)
if (E.Type == GameEvent.EventType.ChangePermission)
{
bool clientExists = GetClientsAsList().Exists(_client => _client.NetworkId.Equals(E.Origin));
if (!E.Target.IsPrivileged())
{
// remove banned or demoted privileged user
Manager.GetPrivilegedClients().Remove(E.Target.ClientId);
}
if (!clientExists)
else
{
Manager.GetPrivilegedClients()[E.Target.ClientId] = E.Target;
}
}
else if (E.Type == GameEvent.EventType.PreConnect)
{
if (Clients[E.Origin.ClientNumber] == null)
{
#if DEBUG == true
Logger.WriteDebug($"Begin PreConnect for {E.Origin}");
@ -468,7 +463,8 @@ namespace IW4MAdmin
client.Ping = origin.Ping;
client.Score = origin.Score;
if (origin.IPAddress == 0)
// update their IP if it hasn't been set yet
if (!client.IPAddress.HasValue)
{
return client.OnJoin(origin.IPAddress);
}
@ -499,17 +495,9 @@ namespace IW4MAdmin
#endif
Throttled = false;
foreach (var client in polledClients)
{
// todo: move out somehwere
var existingClient = Clients[client.ClientNumber] ?? client;
existingClient.Ping = client.Ping;
existingClient.Score = client.Score;
}
var disconnectingClients = currentClients.Except(polledClients);
var connectingClients = polledClients.Except(currentClients);
var updatedClients = polledClients.Except(connectingClients);
var updatedClients = polledClients.Except(connectingClients).Except(disconnectingClients);
return new List<EFClient>[]
{
@ -753,6 +741,7 @@ namespace IW4MAdmin
infoResponse["fs_game"];
var logfile = await this.GetDvarAsync<string>("g_log");
var logsync = await this.GetDvarAsync<int>("g_logsync");
var ip = await this.GetDvarAsync<string>("net_ip");
WorkingDirectory = basepath.Value;
@ -774,6 +763,8 @@ namespace IW4MAdmin
this.MaxClients = maxplayers;
this.FSGame = game;
this.Gametype = gametype;
this.IP = ip.Value;
if (logsync.Value == 0 || logfile.Value == string.Empty)
{
// this DVAR isn't set until the a map is loaded
@ -1000,8 +991,6 @@ namespace IW4MAdmin
};
await Manager.GetPenaltyService().Create(newPenalty);
// prevent them from logging in again
Manager.GetPrivilegedClients().Remove(Target.ClientId);
}
override public async Task Unban(string reason, EFClient Target, EFClient Origin)

View File

@ -1,25 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Text;
using System.Reflection;
using IW4MAdmin.Application.API.Master;
using SharedLibraryCore;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Commands;
using SharedLibraryCore.Helpers;
using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Objects;
using SharedLibraryCore.Services;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database;
using SharedLibraryCore.Events;
using IW4MAdmin.Application.API.Master;
using IW4MAdmin.Application.Migration;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Events;
using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Helpers;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace IW4MAdmin.Application
{
@ -61,7 +57,6 @@ namespace IW4MAdmin.Application
ClientSvc = new ClientService();
AliasSvc = new AliasService();
PenaltySvc = new PenaltyService();
PrivilegedClients = new Dictionary<int, EFClient>();
ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings");
StartTime = DateTime.UtcNow;
OnQuit = new ManualResetEventSlim();
@ -85,61 +80,12 @@ namespace IW4MAdmin.Application
}
try
{
{
{
await newEvent.Owner.ExecuteEvent(newEvent);
await newEvent.Owner.ExecuteEvent(newEvent);
// save the event info to the database
var changeHistorySvc = new ChangeHistoryService();
await changeHistorySvc.Add(args.Event);
// todo: this is a hacky mess
if (newEvent.Origin?.DelayedEvents.Count > 0 &&
(//newEvent.Origin?.State == Player.ClientState.Connected ||
newEvent.Type == GameEvent.EventType.Connect))
{
var events = newEvent.Origin.DelayedEvents;
// add the delayed event to the queue
while (events.Count > 0)
{
var oldEvent = events.Dequeue();
var e = new GameEvent()
{
Type = oldEvent.Type,
Origin = newEvent.Origin,
Data = oldEvent.Data,
Extra = oldEvent.Extra,
Owner = oldEvent.Owner,
Message = oldEvent.Message,
Target = oldEvent.Target,
Remote = oldEvent.Remote
};
e.Origin = newEvent.Origin;
// check if the target was assigned
if (e.Target != null)
{
// update the target incase they left or have newer info
e.Target = newEvent.Owner.GetClientsAsList()
.FirstOrDefault(p => p.NetworkId == e.Target.NetworkId);
// we have to throw out the event because they left
if (e.Target == null)
{
Logger.WriteWarning($"Delayed event for {e.Origin} was ignored because the target has left");
// hack: don't do anything with the event because the target is invalid
e.Origin = null;
e.Type = GameEvent.EventType.Unknown;
}
}
Logger.WriteDebug($"Adding delayed event of type {e.Type} for {e.Origin} back for processing");
this.GetEventHandler().AddEvent(e);
}
}
}
// save the event info to the database
var changeHistorySvc = new ChangeHistoryService();
await changeHistorySvc.Add(args.Event);
#if DEBUG
Logger.WriteDebug($"Processed event with id {newEvent.Id}");
@ -173,7 +119,7 @@ namespace IW4MAdmin.Application
Logger.WriteDebug(ex.GetExceptionInfo());
}
skip:
skip:
// tell anyone waiting for the output that we're done
newEvent.OnProcessed.Set();
@ -263,36 +209,7 @@ namespace IW4MAdmin.Application
await new ContextSeed(db).Seed();
}
// todo: optimize this (or replace it)
var ipList = (await ClientSvc.Find(c => c.Level > EFClient.Permission.Trusted))
.Select(c => new
{
c.Password,
c.PasswordSalt,
c.ClientId,
c.Level,
c.Name
});
foreach (var a in ipList)
{
try
{
PrivilegedClients.Add(a.ClientId, new EFClient()
{
Name = a.Name,
ClientId = a.ClientId,
Level = a.Level,
PasswordSalt = a.PasswordSalt,
Password = a.Password
});
}
catch (ArgumentException)
{
continue;
}
}
PrivilegedClients = (await ClientSvc.GetPrivilegedClients()).ToDictionary(_client => _client.ClientId);
#endregion
#region CONFIG
@ -341,7 +258,9 @@ namespace IW4MAdmin.Application
}
else if (config.Servers.Count == 0)
{
throw new ServerException("A server configuration in IW4MAdminSettings.json is invalid");
}
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Utilities.EncodingType = Encoding.GetEncoding(!string.IsNullOrEmpty(config.CustomParserEncoding) ? config.CustomParserEncoding : "windows-1252");
@ -367,7 +286,9 @@ namespace IW4MAdmin.Application
#region COMMANDS
if (ClientSvc.GetOwners().Result.Count == 0)
{
Commands.Add(new COwner());
}
Commands.Add(new CQuit());
Commands.Add(new CKick());
@ -408,7 +329,9 @@ namespace IW4MAdmin.Application
Commands.Add(new CNextMap());
foreach (Command C in SharedLibraryCore.Plugins.PluginImporter.ActiveCommands)
{
Commands.Add(C);
}
#endregion
#region INIT
@ -443,7 +366,9 @@ namespace IW4MAdmin.Application
{
Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_UNFIXABLE"]} [{Conf.IPAddress}:{Conf.Port}]");
if (e.GetType() == typeof(DvarException))
{
Logger.WriteDebug($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_DVAR"]} {(e as DvarException).Data["dvar_name"]} ({Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_DVAR_HELP"]})");
}
else if (e.GetType() == typeof(NetworkException))
{
Logger.WriteDebug(e.Message);
@ -572,23 +497,59 @@ namespace IW4MAdmin.Application
return MessageTokens;
}
public IList<EFClient> GetActiveClients() => _servers.SelectMany(s => s.Clients).Where(p => p != null).ToList();
public IList<EFClient> GetActiveClients()
{
return _servers.SelectMany(s => s.Clients).Where(p => p != null).ToList();
}
public ClientService GetClientService() => ClientSvc;
public AliasService GetAliasService() => AliasSvc;
public PenaltyService GetPenaltyService() => PenaltySvc;
public IConfigurationHandler<ApplicationConfiguration> GetApplicationSettings() => ConfigHandler;
public IDictionary<int, EFClient> GetPrivilegedClients() => PrivilegedClients;
public bool ShutdownRequested() => !Running;
public IEventHandler GetEventHandler() => Handler;
public ClientService GetClientService()
{
return ClientSvc;
}
public AliasService GetAliasService()
{
return AliasSvc;
}
public PenaltyService GetPenaltyService()
{
return PenaltySvc;
}
public IConfigurationHandler<ApplicationConfiguration> GetApplicationSettings()
{
return ConfigHandler;
}
public IDictionary<int, EFClient> GetPrivilegedClients()
{
return PrivilegedClients;
}
public bool ShutdownRequested()
{
return !Running;
}
public IEventHandler GetEventHandler()
{
return Handler;
}
public void SetHasEvent()
{
OnQuit.Set();
}
public IList<Assembly> GetPluginAssemblies() => SharedLibraryCore.Plugins.PluginImporter.PluginAssemblies;
public IList<Assembly> GetPluginAssemblies()
{
return SharedLibraryCore.Plugins.PluginImporter.PluginAssemblies;
}
public IPageList GetPageList() => PageList;
public IPageList GetPageList()
{
return PageList;
}
}
}

View File

@ -1,15 +1,13 @@
using System;
using SharedLibraryCore;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.RCon;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Text;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Objects;
using SharedLibraryCore;
using SharedLibraryCore.RCon;
using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Database.Models;
namespace IW4MAdmin.Application.RconParsers
{
@ -73,14 +71,19 @@ namespace IW4MAdmin.Application.RconParsers
return (await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, $"set {dvarName} {dvarValue}")).Length > 0;
}
public virtual CommandPrefix GetCommandPrefixes() => Prefixes;
public virtual CommandPrefix GetCommandPrefixes()
{
return Prefixes;
}
private List<EFClient> ClientsFromStatus(string[] Status)
{
List<EFClient> StatusPlayers = new List<EFClient>();
if (Status.Length < 4)
{
throw new ServerException("Unexpected status response received");
}
int validMatches = 0;
foreach (String S in Status)
@ -103,13 +106,21 @@ namespace IW4MAdmin.Application.RconParsers
ping = int.Parse(regex.Groups[3].Value);
}
else
{
continue;
}
long networkId = regex.Groups[4].Value.ConvertLong();
string name = regex.Groups[5].Value.StripColors().Trim();
int ip = regex.Groups[7].Value.Split(':')[0].ConvertToIP();
var P = new EFClient()
{
Name = name,
CurrentAlias = new EFAlias()
{
Name = name
},
NetworkId = networkId,
ClientNumber = clientNumber,
IPAddress = ip == 0 ? int.MinValue : ip,

View File

@ -23,7 +23,8 @@ class Instance(Resource):
def put(self, id):
try:
for server in request.json['servers']:
server['ip'] = request.remote_addr
if 'ip' not in server or server['ip'] == 'localhost':
server['ip'] = request.remote_addr
instance = InstanceSchema().load(request.json)
except ValidationError as err:
return {'message' : err.messages }, 400
@ -34,7 +35,8 @@ class Instance(Resource):
def post(self):
try:
for server in request.json['servers']:
server['ip'] = request.remote_addr
if 'ip' not in server or server['ip'] == 'localhost':
server['ip'] = request.remote_addr
instance = InstanceSchema().load(request.json)
except ValidationError as err:
return {'message' : err.messages }, 400

View File

@ -25,7 +25,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
private ILogger Log;
private readonly IManager Manager;
private readonly SemaphoreSlim OnProcessingPenalty;
private readonly SemaphoreSlim OnProcessingSensitive;
@ -348,7 +347,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
Log.WriteWarning("Could not add client to detection");
}
Log.WriteInfo($"Adding {pl} to stats");
pl.CurrentServer.Logger.WriteInfo($"Adding {pl} to stats");
}
return clientStats;

View File

@ -250,7 +250,7 @@ namespace Tests
unbanCommand.ExecuteAsync(new GameEvent()
{
Origin = new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer },
Target = Manager.GetClientService().Find(c => c.NetworkId == client.NetworkId).Result.First().AsEFClient(),
Target = Manager.GetClientService().Find(c => c.NetworkId == client.NetworkId).Result.First(),
Data = "test unban",
Type = GameEvent.EventType.Command,
Owner = client.CurrentServer

View File

@ -84,7 +84,7 @@ namespace IW4MAdmin.Plugins.Welcome
public async Task OnEventAsync(GameEvent E, Server S)
{
if (E.Type == GameEvent.EventType.Connect)
if (E.Type == GameEvent.EventType.Join)
{
EFClient newPlayer = E.Origin;
if (newPlayer.Level >= Permission.Trusted && !E.Origin.Masked)

View File

@ -37,11 +37,25 @@ namespace SharedLibraryCore.Commands
{
if ((await (E.Owner.Manager.GetClientService() as ClientService).GetOwners()).Count == 0)
{
var oldPermission = E.Origin.Level;
E.Origin.Level = EFClient.Permission.Owner;
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_OWNER_SUCCESS"]);
// so setpassword/login works
E.Owner.Manager.GetPrivilegedClients().Add(E.Origin.ClientId, E.Origin);
await E.Owner.Manager.GetClientService().Update(E.Origin);
var e = new GameEvent()
{
Type = GameEvent.EventType.ChangePermission,
Origin = E.Origin,
Target = E.Origin,
Owner = E.Owner,
Extra = new Change()
{
PreviousValue = oldPermission.ToString(),
NewValue = E.Origin.Level.ToString()
}
};
E.Owner.Manager.GetEventHandler().AddEvent(e);
}
else
{
@ -494,17 +508,6 @@ namespace SharedLibraryCore.Commands
await E.Owner.Manager.GetClientService().Update(E.Target);
}
try
{
E.Owner.Manager.GetPrivilegedClients().Add(E.Target.ClientId, E.Target);
}
catch (Exception)
{
// this updates their privilege level to the webfront claims
E.Owner.Manager.GetPrivilegedClients()[E.Target.ClientId] = E.Target;
}
var e = new GameEvent()
{
Origin = E.Origin,

View File

@ -22,7 +22,7 @@ namespace SharedLibraryCore.Database
static string _ConnectionString;
static string _provider;
private static string _migrationPluginDirectory = @"X:\IW4MAdmin\BUILD\Plugins\";
private static readonly string _migrationPluginDirectory = @"X:\IW4MAdmin\BUILD\Plugins\";
public DatabaseContext(DbContextOptions<DatabaseContext> opt) : base(opt) { }
@ -110,6 +110,7 @@ namespace SharedLibraryCore.Database
modelBuilder.Entity<EFAlias>(ent =>
{
ent.Property(a => a.IPAddress).IsRequired(false);
ent.HasIndex(a => a.IPAddress);
ent.Property(a => a.Name).HasMaxLength(24);
ent.HasIndex(a => a.Name);
@ -123,14 +124,15 @@ namespace SharedLibraryCore.Database
// adapted from
// https://aleemkhan.wordpress.com/2013/02/28/dynamically-adding-dbset-properties-in-dbcontext-for-entity-framework-code-first/
#if DEBUG
string pluginDir = _migrationPluginDirectory;
#else
string pluginDir = Path.Join(Utilities.OperatingDirectory, "Plugins");
#endif
IEnumerable<string> directoryFiles = Directory.GetFiles(pluginDir).Where(f => f.EndsWith(".dll"));
#if DEBUG == TRUE
foreach (string dllPath in Directory.GetFiles(_migrationPluginDirectory).Where(f => f.EndsWith(".dll")))
#else
foreach (string dllPath in directoryFiles)
#endif
{
Assembly library;
try

View File

@ -16,7 +16,7 @@ namespace SharedLibraryCore.Database.Models
[MaxLength(24)]
public string Name { get; set; }
[Required]
public int IPAddress { get; set; }
public int? IPAddress { get; set; }
[Required]
public DateTime DateAdded { get; set; }
}

View File

@ -44,14 +44,14 @@ namespace SharedLibraryCore.Database.Models
set { CurrentAlias.Name = value; }
}
[NotMapped]
public virtual int IPAddress
public virtual int? IPAddress
{
get { return CurrentAlias.IPAddress; }
set { CurrentAlias.IPAddress = value; }
}
[NotMapped]
public string IPAddressString => new System.Net.IPAddress(BitConverter.GetBytes(IPAddress)).ToString();
public string IPAddressString => IPAddress.ConvertIPtoString();
[NotMapped]
public virtual IDictionary<int, long> LinkedAccounts { get; set; }

View File

@ -208,38 +208,5 @@ namespace SharedLibraryCore
return this;
});
}
/// <summary>
/// determine whether an event should be delayed or not
/// applies only to the origin entity
/// </summary>
/// <param name="queuedEvent">event to determine status for</param>
/// <returns>true if event should be delayed, false otherwise</returns>
public static bool ShouldOriginEventBeDelayed(GameEvent queuedEvent)
{
return queuedEvent.Origin != null &&
(queuedEvent.Origin.State != EFClient.ClientState.Connected &&
// we want to allow join and quit events
queuedEvent.Type != EventType.Connect &&
queuedEvent.Type != EventType.Join &&
queuedEvent.Type != EventType.Quit &&
queuedEvent.Type != EventType.Disconnect &&
// we don't care about unknown events
queuedEvent.Origin.NetworkId != 0);
}
/// <summary>
/// determine whether an event should be delayed or not
/// applies only to the target entity
/// </summary>
/// <param name="queuedEvent">event to determine status for</param>
/// <returns>true if event should be delayed, false otherwise</returns>
public static bool ShouldTargetEventBeDelayed(GameEvent queuedEvent)
{
return (queuedEvent.Target != null && queuedEvent.Target.ClientNumber != -1) &&
(queuedEvent.Target.State != EFClient.ClientState.Connected &&
queuedEvent.Target.NetworkId != 0 &&
queuedEvent.Origin?.ClientId != 1);
}
}
}

View File

@ -0,0 +1,690 @@
// <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("20181125193243_MakeClientIPNullable")]
partial class MakeClientIPNullable
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.4-rtm-31024");
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>("CurrentViewAngleId");
b.Property<int>("Deaths");
b.Property<double>("Distance");
b.Property<double>("EloRating");
b.Property<int>("HitDestinationId");
b.Property<int>("HitLocation");
b.Property<int>("HitOriginId");
b.Property<int>("HitType");
b.Property<int>("Hits");
b.Property<int>("Kills");
b.Property<int>("LastStrainAngleId");
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("CurrentViewAngleId");
b.HasIndex("HitDestinationId");
b.HasIndex("HitOriginId");
b.HasIndex("LastStrainAngleId");
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("CurrentViewAngleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination")
.WithMany()
.HasForeignKey("HitDestinationId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin")
.WithMany()
.HasForeignKey("HitOriginId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle")
.WithMany()
.HasForeignKey("LastStrainAngleId")
.OnDelete(DeleteBehavior.Cascade);
});
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,85 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace SharedLibraryCore.Migrations
{
public partial class MakeClientIPNullable : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.Sqlite")
{
migrationBuilder.Sql(@"PRAGMA foreign_keys = 0;
CREATE TABLE sqlitestudio_temp_table AS SELECT *
FROM EFAlias;
DROP TABLE EFAlias;
CREATE TABLE EFAlias (
AliasId INTEGER NOT NULL
CONSTRAINT PK_EFAlias PRIMARY KEY AUTOINCREMENT,
Active INTEGER NOT NULL,
DateAdded TEXT NOT NULL,
IPAddress INTEGER,
LinkId INTEGER NOT NULL,
Name TEXT NOT NULL,
CONSTRAINT FK_EFAlias_EFAliasLinks_LinkId FOREIGN KEY (
LinkId
)
REFERENCES EFAliasLinks (AliasLinkId) ON DELETE RESTRICT
);
INSERT INTO EFAlias (
AliasId,
Active,
DateAdded,
IPAddress,
LinkId,
Name
)
SELECT AliasId,
Active,
DateAdded,
IPAddress,
LinkId,
Name
FROM sqlitestudio_temp_table;
DROP TABLE sqlitestudio_temp_table;
CREATE INDEX IX_EFAlias_LinkId ON EFAlias (
""LinkId""
);
CREATE INDEX IX_EFAlias_IPAddress ON EFAlias(
""IPAddress""
);
CREATE INDEX IX_EFAlias_Name ON EFAlias(
""Name""
);
PRAGMA foreign_keys = 1;
");
}
else
{
migrationBuilder.AlterColumn<int>(
name: "IPAddress",
table: "EFAlias",
nullable: true,
oldClrType: typeof(int));
}
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<int>(
name: "IPAddress",
table: "EFAlias",
nullable: false,
oldClrType: typeof(int),
oldNullable: true);
}
}
}

View File

@ -317,7 +317,7 @@ namespace SharedLibraryCore.Migrations
b.Property<DateTime>("DateAdded");
b.Property<int>("IPAddress");
b.Property<int?>("IPAddress");
b.Property<int>("LinkId");

View File

@ -80,7 +80,6 @@ namespace SharedLibraryCore.Database.Models
{
{ "_reportCount", 0 }
};
CurrentAlias = CurrentAlias ?? new EFAlias();
}
public override string ToString()
@ -405,7 +404,7 @@ namespace SharedLibraryCore.Database.Models
public void OnConnect()
{
var loc = Utilities.CurrentLocalization.LocalizationIndex;
#if !DEBUG
if (Name.Length < 3)
{
CurrentServer.Logger.WriteDebug($"Kicking {this} because their name is too short");
@ -440,8 +439,6 @@ namespace SharedLibraryCore.Database.Models
LastConnection = DateTime.UtcNow;
Connections += 1;
#endif
}
public async Task OnDisconnect()
@ -452,24 +449,11 @@ namespace SharedLibraryCore.Database.Models
await CurrentServer.Manager.GetClientService().Update(this);
}
public async Task OnJoin(int ipAddress)
public async Task OnJoin(int? ipAddress)
{
// todo: fix this up
var existingAlias = AliasLink.Children
.FirstOrDefault(a => a.Name == Name && a.IPAddress == ipAddress);
IPAddress = ipAddress;
if (existingAlias == null)
{
CurrentServer.Logger.WriteDebug($"Client {this} has connected previously under a different ip/name");
CurrentAlias = new EFAlias()
{
IPAddress = ipAddress,
Name = Name
};
}
await CurrentServer.Manager.GetClientService().Update(this);
await CurrentServer.Manager.GetClientService().UpdateAlias(this);
var loc = Utilities.CurrentLocalization.LocalizationIndex;
var activePenalties = await CurrentServer.Manager.GetPenaltyService().GetActivePenaltiesAsync(AliasLinkId, ipAddress);
@ -519,6 +503,19 @@ namespace SharedLibraryCore.Database.Models
Kick($"{loc["SERVER_TB_REMAIN"]} ({(currentBan.Expires.Value - DateTime.UtcNow).TimeSpanText()} {loc["WEBFRONT_PENALTY_TEMPLATE_REMAINING"]})", autoKickClient);
}
}
else
{
var e = new GameEvent()
{
Type = GameEvent.EventType.Join,
Origin = this,
Target = this,
Owner = CurrentServer
};
CurrentServer.Manager.GetEventHandler().AddEvent(e);
}
}
[NotMapped]
@ -557,18 +554,7 @@ namespace SharedLibraryCore.Database.Models
public int Score { get; set; }
[NotMapped]
public bool IsBot { get; set; }
//private int _ipaddress;
//public override int IPAddress
//{
// get => _ipaddress;
// set => _ipaddress = value;
//}
//private string _name;
//public override string Name
//{
// get => _name;
// set => _name = value;
//}
[NotMapped]
public ClientState State { get; set; }
[NotMapped]

View File

@ -313,7 +313,7 @@ namespace SharedLibraryCore
public bool RestartRequested { get; set; }
// Internal
protected string IP;
public string IP { get; protected set; }
protected int Port;
protected string FSGame;
protected int NextMessage;

View File

@ -1,15 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using SharedLibraryCore.Database;
using SharedLibraryCore.Database.Models;
using System.Linq.Expressions;
using SharedLibraryCore.Objects;
using Microsoft.EntityFrameworkCore;
using SharedLibraryCore.Dtos;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static SharedLibraryCore.Database.Models.EFClient;
namespace SharedLibraryCore.Services
@ -21,55 +17,28 @@ namespace SharedLibraryCore.Services
{
using (var context = new DatabaseContext())
{
bool hasExistingAlias = false;
// get all aliases by IP
var aliases = await context.Aliases
.Include(a => a.Link)
.Where(a => a.IPAddress == entity.IPAddress)
.ToListAsync();
// see if they have a matching IP + Name but new NetworkId
var existingAlias = aliases.FirstOrDefault(a => a.Name == entity.Name);
// if existing alias matches link them
EFAliasLink aliasLink = existingAlias?.Link;
// if no exact matches find the first IP that matches
aliasLink = aliasLink ?? aliases.FirstOrDefault()?.Link;
// if no exact or IP matches, create new link
aliasLink = aliasLink ?? new EFAliasLink()
{
Active = true,
};
// this has to be set here because we can't evalute it properly later
hasExistingAlias = existingAlias != null;
// if no existing alias create new alias
existingAlias = existingAlias ?? new EFAlias()
{
Active = true,
DateAdded = DateTime.UtcNow,
IPAddress = entity.IPAddress,
Link = aliasLink,
Name = entity.Name,
};
var client = new EFClient()
{
Active = true,
// set the level to the level of the existing client if they have the same IP + Name but new NetworkId
// fixme: issues?
Level = hasExistingAlias ?
(await context.Clients.Where(c => c.AliasLinkId == existingAlias.LinkId)
.OrderByDescending(c => c.Level)
.FirstOrDefaultAsync())?.Level ?? Permission.User :
Permission.User,
Level = Permission.User,
FirstConnection = DateTime.UtcNow,
Connections = 1,
LastConnection = DateTime.UtcNow,
Masked = false,
NetworkId = entity.NetworkId,
AliasLink = aliasLink,
CurrentAlias = existingAlias,
AliasLink = new EFAliasLink()
{
Active = false
},
};
client.CurrentAlias = new Alias()
{
Name = entity.Name,
Link = client.AliasLink,
DateAdded = DateTime.UtcNow,
// the first time a client is created, we may not have their ip,
// so we create a temporary alias
Active = false
};
context.Clients.Add(client);
@ -79,6 +48,101 @@ namespace SharedLibraryCore.Services
}
}
public async Task UpdateAlias(EFClient entity)
{
using (var context = new DatabaseContext())
{
context.Attach(entity);
string name = entity.Name;
int? ip = entity.IPAddress;
bool hasExistingAlias = false;
// get all aliases by IP
var aliases = await context.Aliases
.Include(a => a.Link)
.Where(a => a.IPAddress != null && a.IPAddress == ip)
.ToListAsync();
// see if they have a matching IP + Name but new NetworkId
var existingAlias = aliases.FirstOrDefault(a => a.Name == name);
// if existing alias matches link them
EFAliasLink aliasLink = existingAlias?.Link;
// if no exact matches find the first IP that matches
aliasLink = aliasLink ?? aliases.FirstOrDefault()?.Link;
// if no exact or IP matches, create new link
aliasLink = aliasLink ?? new EFAliasLink();
// this has to be set here because we can't evalute it properly later
hasExistingAlias = existingAlias != null;
if (hasExistingAlias && !entity.AliasLink.Active)
{
// we want to delete the temporary alias
context.Entry(entity.CurrentAlias).State = EntityState.Deleted;
entity.CurrentAlias = null;
// we want to delete the temporary alias link
context.Entry(entity.AliasLink).State = EntityState.Deleted;
entity.AliasLink = null;
// they have an existing alias so assign it
entity.CurrentAlias = existingAlias;
entity.AliasLink = aliasLink;
await context.SaveChangesAsync();
}
// update the temporary alias to permanent one
else if (!entity.AliasLink.Active)
{
// we want to track the current alias and link
var alias = context.Update(entity.CurrentAlias).Entity;
var _aliasLink = context.Update(entity.AliasLink).Entity;
alias.Active = true;
alias.IPAddress = ip;
alias.Name = name;
_aliasLink.Active = true;
existingAlias = alias;
aliasLink = _aliasLink;
}
// if no existing alias create new alias
existingAlias = existingAlias ?? new EFAlias()
{
DateAdded = DateTime.UtcNow,
IPAddress = ip,
Link = aliasLink,
Name = name,
};
var iqExistingPermission = context.Clients.Where(c => c.AliasLinkId == existingAlias.LinkId)
.OrderByDescending(client => client.Level)
.Select(c => new EFClient() { Level = c.Level });
entity.Level = hasExistingAlias ?
(await iqExistingPermission.FirstOrDefaultAsync())?.Level ?? Permission.User :
Permission.User;
#if DEBUG
string sql = iqExistingPermission.AsQueryable().ToSql();
#endif
if (entity.CurrentAlias != existingAlias ||
entity.AliasLink != aliasLink)
{
entity.CurrentAlias = existingAlias;
entity.AliasLink = aliasLink;
context.Update(entity);
}
await context.SaveChangesAsync();
}
}
public async Task<EFClient> Delete(EFClient entity)
{
using (var context = new DatabaseContext())
@ -132,13 +196,17 @@ namespace SharedLibraryCore.Services
var foundClient = await iqClient.FirstOrDefaultAsync();
if (foundClient == null)
{
return null;
}
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;
}
@ -168,7 +236,7 @@ namespace SharedLibraryCore.Services
var client = context.Clients
.Include(c => c.AliasLink)
.Include(c => c.CurrentAlias)
.Single(e => e.ClientId == entity.ClientId);
.First(e => e.ClientId == entity.ClientId);
// if their level has been changed
if (entity.Level != client.Level)
@ -191,7 +259,7 @@ namespace SharedLibraryCore.Services
{
client.CurrentAlias = new EFAlias()
{
Active = true,
Active = entity.CurrentAlias.IPAddress.HasValue ? true : false,
DateAdded = DateTime.UtcNow,
IPAddress = entity.CurrentAlias.IPAddress,
Name = entity.CurrentAlias.Name,
@ -202,6 +270,8 @@ namespace SharedLibraryCore.Services
else
{
client.CurrentAliasId = entity.CurrentAliasId;
client.IPAddress = entity.IPAddress;
client.Name = entity.Name;
}
// set remaining non-navigation properties that may have been updated
@ -219,7 +289,10 @@ namespace SharedLibraryCore.Services
// this is set so future updates don't trigger a new alias add
if (entity.CurrentAlias.AliasId == 0)
{
entity.CurrentAlias.AliasId = client.CurrentAlias.AliasId;
}
return client;
}
}
@ -228,24 +301,27 @@ namespace SharedLibraryCore.Services
public async Task<IList<EFClient>> GetOwners()
{
using (var context = new DatabaseContext())
{
return await context.Clients
.Where(c => c.Level == Permission.Owner)
.ToListAsync();
}
}
public async Task<IList<ClientInfo>> GetPrivilegedClients()
public async Task<List<EFClient>> GetPrivilegedClients()
{
using (var context = new DatabaseContext(disableTracking: true))
{
var iqClients = from client in context.Clients
where client.Level >= Permission.Trusted
where client.Active
select new ClientInfo()
select new EFClient()
{
CurrentAlias = client.CurrentAlias,
ClientId = client.ClientId,
Name = client.CurrentAlias.Name,
LinkId = client.AliasLinkId,
Level = client.Level
Level = client.Level,
Password = client.Password,
PasswordSalt = client.PasswordSalt
};
#if DEBUG == true
@ -294,20 +370,16 @@ namespace SharedLibraryCore.Services
public async Task<int> GetTotalClientsAsync()
{
using (var context = new DatabaseContext(true))
{
return await context.Clients
.CountAsync();
}
}
public Task<EFClient> CreateProxy()
{
throw new NotImplementedException();
}
public async Task<int> GetTotalPlayTime()
{
using (var context = new DatabaseContext(true))
return await context.Clients.SumAsync(c => c.TotalConnectionTime);
}
#endregion
}
}

View File

@ -213,7 +213,7 @@ namespace SharedLibraryCore.Services
}
}
public async Task<List<EFPenalty>> GetActivePenaltiesAsync(int linkId, int ip = 0)
public async Task<List<EFPenalty>> GetActivePenaltiesAsync(int linkId, int? ip = null)
{
var now = DateTime.UtcNow;
@ -221,7 +221,7 @@ namespace SharedLibraryCore.Services
{
var iqPenalties = context.Penalties
.Where(p => p.LinkId == linkId ||
p.Link.Children.Any(a => a.IPAddress == ip))
ip.HasValue ? p.Link.Children.Any(a => a.IPAddress == ip) : false)
.Where(p => p.Type == Penalty.PenaltyType.TempBan ||
p.Type == Penalty.PenaltyType.Ban ||
p.Type == Penalty.PenaltyType.Flag)

View File

@ -1,21 +1,18 @@
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
using System.Collections.Generic;
using SharedLibraryCore.Objects;
using static SharedLibraryCore.Server;
using System.Reflection;
using System.IO;
using System.Threading.Tasks;
using System.Globalization;
using System.Diagnostics;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using SharedLibraryCore.Database.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using static SharedLibraryCore.Server;
namespace SharedLibraryCore
{
@ -28,14 +25,20 @@ namespace SharedLibraryCore
#endif
public static Encoding EncodingType;
public static Localization.Layout CurrentLocalization = new Localization.Layout(new Dictionary<string, string>());
public static EFClient IW4MAdminClient(Server server = null) => new EFClient()
public static EFClient IW4MAdminClient(Server server = null)
{
ClientId = 1,
State = EFClient.ClientState.Connected,
Level = EFClient.Permission.Console,
CurrentServer = server,
Name = "IW4MAdmin"
};
return new EFClient()
{
ClientId = 1,
State = EFClient.ClientState.Connected,
Level = EFClient.Permission.Console,
CurrentServer = server,
CurrentAlias = new EFAlias()
{
Name = "IW4MAdmin"
}
};
}
public static string HttpRequest(string location, string header, string headerValue)
{
@ -64,7 +67,9 @@ namespace SharedLibraryCore
public static String RemoveWords(this string str, int num)
{
if (str == null || str.Length == 0)
{
return "";
}
String newStr = String.Empty;
String[] tmp = str.Split(' ');
@ -72,7 +77,9 @@ namespace SharedLibraryCore
for (int i = 0; i < tmp.Length; i++)
{
if (i >= num)
{
newStr += tmp[i] + ' ';
}
}
return newStr;
@ -105,9 +112,13 @@ namespace SharedLibraryCore
String lookingFor = str.ToLower();
for (EFClient.Permission Perm = EFClient.Permission.User; Perm < EFClient.Permission.Console; Perm++)
{
if (lookingFor.Contains(Perm.ToString().ToLower())
|| lookingFor.Contains(CurrentLocalization.LocalizationIndex[$"GLOBAL_PERMISSION_{Perm.ToString().ToUpper()}"].ToLower()))
{
return Perm;
}
}
return EFClient.Permission.Banned;
}
@ -120,7 +131,10 @@ namespace SharedLibraryCore
public static String StripColors(this string str)
{
if (str == null)
{
return "";
}
str = Regex.Replace(str, @"(\^+((?![a-z]|[A-Z]).){0,1})+", "");
string str2 = Regex.Match(str, @"(^\/+.*$)|(^.*\/+$)")
.Value
@ -161,7 +175,10 @@ namespace SharedLibraryCore
return $"^{colorCode}{localizedLevel ?? level.ToString()}";
}
public static string ToLocalizedLevelName(this EFClient.Permission perm) => CurrentLocalization.LocalizationIndex[$"GLOBAL_PERMISSION_{perm.ToString().ToUpper()}"];
public static string ToLocalizedLevelName(this EFClient.Permission perm)
{
return CurrentLocalization.LocalizationIndex[$"GLOBAL_PERMISSION_{perm.ToString().ToUpper()}"];
}
public static String ProcessMessageToken(this Server server, IList<Helpers.MessageToken> tokens, String str)
{
@ -174,7 +191,9 @@ namespace SharedLibraryCore
var found = tokens.FirstOrDefault(t => t.Name.ToLower() == Identifier.ToLower());
if (found != null)
{
str = str.Replace(Match, found.Process(server));
}
}
return str;
@ -245,11 +264,17 @@ namespace SharedLibraryCore
{
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;
if (!string.IsNullOrEmpty(bot))
{
// should set their GUID to the negation of their 1 based index (-1 - -18)
return -(Convert.ToInt64(bot.Substring(3)) + 1);
}
return long.MinValue;
}
@ -260,9 +285,9 @@ namespace SharedLibraryCore
return ip == null ? int.MaxValue : BitConverter.ToInt32(ip.GetAddressBytes(), 0);
}
public static string ConvertIPtoString(this int ip)
public static string ConvertIPtoString(this int? ip)
{
return new System.Net.IPAddress(BitConverter.GetBytes(ip)).ToString();
return !ip.HasValue ? "" : new System.Net.IPAddress(BitConverter.GetBytes(ip.Value)).ToString();
}
public static String GetTimePassed(DateTime start)
@ -282,19 +307,28 @@ namespace SharedLibraryCore
if (Elapsed.TotalMinutes < 120)
{
if (Elapsed.TotalMinutes < 1.5)
{
return $"1 {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_MINUTES"]}{ago}";
}
return Math.Round(Elapsed.TotalMinutes, 0) + $" {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_MINUTES"]}{ago}";
}
if (Elapsed.TotalHours <= 24)
{
if (Elapsed.TotalHours < 1.5)
{
return $"1 {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_HOURS"]}{ago}";
}
return Math.Round(Elapsed.TotalHours, 0) + $" { CurrentLocalization.LocalizationIndex["GLOBAL_TIME_HOURS"]}{ago}";
}
if (Elapsed.TotalDays <= 90)
{
if (Elapsed.TotalDays < 1.5)
{
return $"1 {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_DAYS"]}{ago}";
}
return Math.Round(Elapsed.TotalDays, 0) + $" {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_DAYS"]}{ago}";
}
if (Elapsed.TotalDays <= 365)
@ -310,19 +344,39 @@ namespace SharedLibraryCore
public static Game GetGame(string gameName)
{
if (gameName.Contains("IW4"))
{
return Game.IW4;
}
if (gameName.Contains("CoD4"))
{
return Game.IW3;
}
if (gameName.Contains("COD_WaW"))
{
return Game.T4;
}
if (gameName.Contains("COD_T5_S"))
{
return Game.T5;
}
if (gameName.Contains("T5M"))
{
return Game.T5M;
}
if (gameName.Contains("IW5"))
{
return Game.IW5;
}
if (gameName.Contains("COD_T6_S"))
{
return Game.T6M;
}
return Game.UKN;
}
@ -337,7 +391,9 @@ namespace SharedLibraryCore
var expressionMatch = Regex.Match(input, @"([0-9]+)(\w+)");
if (!expressionMatch.Success) // fallback to default tempban length of 1 hour
{
return new TimeSpan(1, 0, 0);
}
char lengthDenote = expressionMatch.Groups[2].ToString()[0];
int length = Int32.Parse(expressionMatch.Groups[1].ToString());
@ -377,52 +433,42 @@ namespace SharedLibraryCore
var loc = CurrentLocalization.LocalizationIndex;
if (span.TotalMinutes < 60)
{
return $"{span.Minutes} {loc["GLOBAL_TIME_MINUTES"]}";
}
else if (span.Hours >= 1 && span.TotalHours < 24)
{
return $"{span.Hours} {loc["GLOBAL_TIME_HOURS"]}";
}
else if (span.TotalDays >= 1 && span.TotalDays < 7)
{
return $"{span.Days} {loc["GLOBAL_TIME_DAYS"]}";
}
else if (span.TotalDays >= 7 && span.TotalDays < 90)
{
return $"{Math.Round(span.Days / 7.0, 0)} {loc["GLOBAL_TIME_WEEKS"]}";
}
else if (span.TotalDays >= 90 && span.TotalDays < 365)
{
return $"{Math.Round(span.Days / 30.0, 0)} {loc["GLOBAL_TIME_MONTHS"]}";
}
else if (span.TotalDays >= 365 && span.TotalDays < 36500)
{
return $"{Math.Round(span.Days / 365.0, 0)} {loc["GLOBAL_TIME_YEARS"]}";
}
else if (span.TotalDays >= 36500)
{
return loc["GLOBAL_TIME_FOREVER"];
}
return "unknown";
}
public static EFClient AsEFClient(this Database.Models.EFClient client)
public static bool IsPrivileged(this EFClient p)
{
return client == null ? null : new EFClient()
{
Active = client.Active,
AliasLink = client.AliasLink,
AliasLinkId = client.AliasLinkId,
ClientId = client.ClientId,
ClientNumber = -1,
FirstConnection = client.FirstConnection,
Connections = client.Connections,
NetworkId = client.NetworkId,
TotalConnectionTime = client.TotalConnectionTime,
Masked = client.Masked,
Name = client.CurrentAlias.Name,
IPAddress = client.CurrentAlias.IPAddress,
Level = client.Level,
LastConnection = client.LastConnection == DateTime.MinValue ? DateTime.UtcNow : client.LastConnection,
CurrentAlias = client.CurrentAlias,
CurrentAliasId = client.CurrentAlias.AliasId,
// todo: make sure this is up to date
IsBot = client.IPAddress == int.MinValue,
Password = client.Password,
PasswordSalt = client.PasswordSalt
};
return p.Level > EFClient.Permission.User;
}
public static bool IsPrivileged(this EFClient p) => p.Level > EFClient.Permission.User;
public static bool PromptBool(string question)
{
Console.Write($"{question}? [y/n]: ");
@ -474,7 +520,9 @@ namespace SharedLibraryCore
int.TryParse(lineSplit[cIDPos].Trim(), out pID);
if (pID == -1) // special case similar to mod_suicide
{
int.TryParse(lineSplit[2], out pID);
}
return pID;
}
@ -489,7 +537,9 @@ namespace SharedLibraryCore
{
dict = new Dictionary<string, string>();
for (int i = 0; i < values.Length; i += 2)
{
dict.Add(values[i], values[i + 1]);
}
}
return dict;
@ -518,15 +568,30 @@ namespace SharedLibraryCore
return cmdLine.Length > 1 ? cmdLine[1] : cmdLine[0];
}
public static string ToBase64UrlSafeString(this string src) => Convert.ToBase64String(src.Select(c => Convert.ToByte(c)).ToArray()).Replace('+', '-').Replace('/', '_');
public static string ToBase64UrlSafeString(this string src)
{
return Convert.ToBase64String(src.Select(c => Convert.ToByte(c)).ToArray()).Replace('+', '-').Replace('/', '_');
}
public static Task<Dvar<T>> GetDvarAsync<T>(this Server server, string dvarName) => server.RconParser.GetDvarAsync<T>(server.RemoteConnection, dvarName);
public static Task<Dvar<T>> GetDvarAsync<T>(this Server server, string dvarName)
{
return server.RconParser.GetDvarAsync<T>(server.RemoteConnection, dvarName);
}
public static Task SetDvarAsync(this Server server, string dvarName, object dvarValue) => server.RconParser.SetDvarAsync(server.RemoteConnection, dvarName, dvarValue);
public static Task SetDvarAsync(this Server server, string dvarName, object dvarValue)
{
return server.RconParser.SetDvarAsync(server.RemoteConnection, dvarName, dvarValue);
}
public static async Task<string[]> ExecuteCommandAsync(this Server server, string commandName) => await server.RconParser.ExecuteCommandAsync(server.RemoteConnection, commandName);
public static async Task<string[]> ExecuteCommandAsync(this Server server, string commandName)
{
return await server.RconParser.ExecuteCommandAsync(server.RemoteConnection, commandName);
}
public static Task<List<EFClient>> GetStatusAsync(this Server server) => server.RconParser.GetStatusAsync(server.RemoteConnection);
public static Task<List<EFClient>> GetStatusAsync(this Server server)
{
return server.RconParser.GetStatusAsync(server.RemoteConnection);
}
public static async Task<Dictionary<string, string>> GetInfoAsync(this Server server)
{
@ -535,7 +600,10 @@ namespace SharedLibraryCore
{
response = await server.RemoteConnection.SendQueryAsync(RCon.StaticHelpers.QueryType.GET_INFO);
if (response.Length == 2)
{
break;
}
await Task.Delay(RCon.StaticHelpers.FloodProtectionInterval);
}
return response.FirstOrDefault(r => r[0] == '\\')?.DictionaryFromKeyValue();
@ -548,7 +616,10 @@ namespace SharedLibraryCore
return double.Parse(version) / 1000.0;
}
public static string GetVersionAsString() => Assembly.GetCallingAssembly().GetName().Version.ToString();
public static string GetVersionAsString()
{
return Assembly.GetCallingAssembly().GetName().Version.ToString();
}
#if DEBUG == true

View File

@ -57,13 +57,6 @@ namespace WebfrontCore.Controllers
Client.ClientId = Convert.ToInt32(base.User.Claims.First(c => c.Type == ClaimTypes.Sid).Value);
Client.Level = (EFClient.Permission)Enum.Parse(typeof(EFClient.Permission), User.Claims.First(c => c.Type == ClaimTypes.Role).Value);
Client.CurrentAlias = new EFAlias() { Name = User.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value };
var stillExists = Manager.GetPrivilegedClients()[Client.ClientId];
// this happens if their level has been updated
if (stillExists.Level != Client.Level)
{
Client.Level = stillExists.Level;
}
}
catch (InvalidOperationException)
@ -78,11 +71,13 @@ namespace WebfrontCore.Controllers
}
}
// give the local host full access
else
{
Client.ClientId = 1;
Client.Level = EFClient.Permission.Console;
Client.CurrentAlias = new EFAlias() { Name = "IW4MAdmin" };
Authorized = true;
}
Authorized = Client.ClientId >= 0;

View File

@ -121,7 +121,7 @@ namespace WebfrontCore.Controllers
{
var admins = (await Manager.GetClientService().GetPrivilegedClients())
.OrderByDescending(a => a.Level)
.GroupBy(a => a.LinkId).Select(a => a.First());
.GroupBy(a => a.AliasLinkId).Select(a => a.First());
var adminsDict = new Dictionary<EFClient.Permission, IList<ClientInfo>>();

View File

@ -1,4 +1,4 @@
@model Dictionary<SharedLibraryCore.Objects.Player.Permission, IList<SharedLibraryCore.Dtos.ClientInfo>>
@model Dictionary<SharedLibraryCore.Database.Models.EFClient.Permission, IList<SharedLibraryCore.Dtos.ClientInfo>>
<h4 class="pb-2 text-center ">@ViewBag.Title</h4>