diff --git a/Application/API/Master/ApiServer.cs b/Application/API/Master/ApiServer.cs index 41637ebaf..bd42066ac 100644 --- a/Application/API/Master/ApiServer.cs +++ b/Application/API/Master/ApiServer.cs @@ -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")] diff --git a/Application/API/Master/Heartbeat.cs b/Application/API/Master/Heartbeat.cs index f36d80ab2..ae3076970 100644 --- a/Application/API/Master/Heartbeat.cs +++ b/Application/API/Master/Heartbeat.cs @@ -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() }; diff --git a/Application/EventParsers/IW4EventParser.cs b/Application/EventParsers/IW4EventParser.cs index 4e37bed3f..9182ffc02 100644 --- a/Application/EventParsers/IW4EventParser.cs +++ b/Application/EventParsers/IW4EventParser.cs @@ -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"; + } } } diff --git a/Application/EventParsers/IW5EventParser.cs b/Application/EventParsers/IW5EventParser.cs index 4f6e76119..cc13094a4 100644 --- a/Application/EventParsers/IW5EventParser.cs +++ b/Application/EventParsers/IW5EventParser.cs @@ -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); + } } } } diff --git a/Application/IO/GameLogReaderHttp.cs b/Application/IO/GameLogReaderHttp.cs index 9ddf6cc9a..2cffb451a 100644 --- a/Application/IO/GameLogReaderHttp.cs +++ b/Application/IO/GameLogReaderHttp.cs @@ -28,12 +28,12 @@ namespace IW4MAdmin.Application.IO public long Length => -1; - public int UpdateInterval => 1000; + public int UpdateInterval => 350; public async Task> 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(); string b64Path = server.LogPath.ToBase64UrlSafeString(); diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index 89fb970df..cee278d2a 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -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 /// override protected async Task 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[] { @@ -753,6 +741,7 @@ namespace IW4MAdmin infoResponse["fs_game"]; var logfile = await this.GetDvarAsync("g_log"); var logsync = await this.GetDvarAsync("g_logsync"); + var ip = await this.GetDvarAsync("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) diff --git a/Application/Manager.cs b/Application/Manager.cs index dd4206272..9386e3e2b 100644 --- a/Application/Manager.cs +++ b/Application/Manager.cs @@ -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(); ConfigHandler = new BaseConfigurationHandler("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 GetActiveClients() => _servers.SelectMany(s => s.Clients).Where(p => p != null).ToList(); + public IList 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 GetApplicationSettings() => ConfigHandler; - public IDictionary 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 GetApplicationSettings() + { + return ConfigHandler; + } + + public IDictionary GetPrivilegedClients() + { + return PrivilegedClients; + } + + public bool ShutdownRequested() + { + return !Running; + } + + public IEventHandler GetEventHandler() + { + return Handler; + } public void SetHasEvent() { OnQuit.Set(); } - public IList GetPluginAssemblies() => SharedLibraryCore.Plugins.PluginImporter.PluginAssemblies; + public IList GetPluginAssemblies() + { + return SharedLibraryCore.Plugins.PluginImporter.PluginAssemblies; + } - public IPageList GetPageList() => PageList; + public IPageList GetPageList() + { + return PageList; + } } } diff --git a/Application/RconParsers/IW4RConParser.cs b/Application/RconParsers/IW4RConParser.cs index d04046a5b..c249ce479 100644 --- a/Application/RconParsers/IW4RConParser.cs +++ b/Application/RconParsers/IW4RConParser.cs @@ -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 ClientsFromStatus(string[] Status) { List StatusPlayers = new List(); 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, diff --git a/Master/master/resources/instance.py b/Master/master/resources/instance.py index c616fdb30..60c67207a 100644 --- a/Master/master/resources/instance.py +++ b/Master/master/resources/instance.py @@ -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 diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs index f5a0e2867..9d0713a9b 100644 --- a/Plugins/Stats/Helpers/StatManager.cs +++ b/Plugins/Stats/Helpers/StatManager.cs @@ -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; diff --git a/Plugins/Tests/ClientTests.cs b/Plugins/Tests/ClientTests.cs index d1b9572ba..66d6bd54f 100644 --- a/Plugins/Tests/ClientTests.cs +++ b/Plugins/Tests/ClientTests.cs @@ -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 diff --git a/Plugins/Welcome/Plugin.cs b/Plugins/Welcome/Plugin.cs index 2ef0e2507..54f069eaa 100644 --- a/Plugins/Welcome/Plugin.cs +++ b/Plugins/Welcome/Plugin.cs @@ -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) diff --git a/SharedLibraryCore/Commands/NativeCommands.cs b/SharedLibraryCore/Commands/NativeCommands.cs index 437f0fcca..5ea733891 100644 --- a/SharedLibraryCore/Commands/NativeCommands.cs +++ b/SharedLibraryCore/Commands/NativeCommands.cs @@ -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, diff --git a/SharedLibraryCore/Database/DatabaseContext.cs b/SharedLibraryCore/Database/DatabaseContext.cs index 61aa2c597..8844f7d21 100644 --- a/SharedLibraryCore/Database/DatabaseContext.cs +++ b/SharedLibraryCore/Database/DatabaseContext.cs @@ -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 opt) : base(opt) { } @@ -110,6 +110,7 @@ namespace SharedLibraryCore.Database modelBuilder.Entity(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 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 diff --git a/SharedLibraryCore/Database/Models/EFAlias.cs b/SharedLibraryCore/Database/Models/EFAlias.cs index 97f75890d..d276d9f5c 100644 --- a/SharedLibraryCore/Database/Models/EFAlias.cs +++ b/SharedLibraryCore/Database/Models/EFAlias.cs @@ -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; } } diff --git a/SharedLibraryCore/Database/Models/EFClient.cs b/SharedLibraryCore/Database/Models/EFClient.cs index 74b3c907e..b0b34e792 100644 --- a/SharedLibraryCore/Database/Models/EFClient.cs +++ b/SharedLibraryCore/Database/Models/EFClient.cs @@ -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 LinkedAccounts { get; set; } diff --git a/SharedLibraryCore/Events/GameEvent.cs b/SharedLibraryCore/Events/GameEvent.cs index 30d106e50..4b1a779d9 100644 --- a/SharedLibraryCore/Events/GameEvent.cs +++ b/SharedLibraryCore/Events/GameEvent.cs @@ -208,38 +208,5 @@ namespace SharedLibraryCore return this; }); } - - /// - /// determine whether an event should be delayed or not - /// applies only to the origin entity - /// - /// event to determine status for - /// true if event should be delayed, false otherwise - 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); - } - - /// - /// determine whether an event should be delayed or not - /// applies only to the target entity - /// - /// event to determine status for - /// true if event should be delayed, false otherwise - 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); - } } } diff --git a/SharedLibraryCore/Migrations/20181125193243_MakeClientIPNullable.Designer.cs b/SharedLibraryCore/Migrations/20181125193243_MakeClientIPNullable.Designer.cs new file mode 100644 index 000000000..4ca423dd1 --- /dev/null +++ b/SharedLibraryCore/Migrations/20181125193243_MakeClientIPNullable.Designer.cs @@ -0,0 +1,690 @@ +// +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("SnapshotId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("ClientId"); + + b.Property("CurrentSessionLength"); + + b.Property("CurrentStrain"); + + b.Property("CurrentViewAngleId"); + + b.Property("Deaths"); + + b.Property("Distance"); + + b.Property("EloRating"); + + b.Property("HitDestinationId"); + + b.Property("HitLocation"); + + b.Property("HitOriginId"); + + b.Property("HitType"); + + b.Property("Hits"); + + b.Property("Kills"); + + b.Property("LastStrainAngleId"); + + b.Property("SessionAngleOffset"); + + b.Property("SessionSPM"); + + b.Property("SessionScore"); + + b.Property("StrainAngleBetween"); + + b.Property("TimeSinceLastEvent"); + + b.Property("WeaponId"); + + b.Property("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("KillId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("AttackerId"); + + b.Property("Damage"); + + b.Property("DeathOriginVector3Id"); + + b.Property("DeathType"); + + b.Property("Fraction"); + + b.Property("HitLoc"); + + b.Property("IsKill"); + + b.Property("KillOriginVector3Id"); + + b.Property("Map"); + + b.Property("ServerId"); + + b.Property("VictimId"); + + b.Property("ViewAnglesVector3Id"); + + b.Property("VisibilityPercentage"); + + b.Property("Weapon"); + + b.Property("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("MessageId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("ClientId"); + + b.Property("Message"); + + b.Property("ServerId"); + + b.Property("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("RatingHistoryId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("ClientId"); + + b.HasKey("RatingHistoryId"); + + b.HasIndex("ClientId"); + + b.ToTable("EFClientRatingHistory"); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b => + { + b.Property("ClientId"); + + b.Property("ServerId"); + + b.Property("Active"); + + b.Property("Deaths"); + + b.Property("EloRating"); + + b.Property("Kills"); + + b.Property("MaxStrain"); + + b.Property("RollingWeightedKDR"); + + b.Property("SPM"); + + b.Property("Skill"); + + b.Property("TimePlayed"); + + b.Property("VisionAverage"); + + b.HasKey("ClientId", "ServerId"); + + b.HasIndex("ServerId"); + + b.ToTable("EFClientStatistics"); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b => + { + b.Property("HitLocationCountId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("ClientId") + .HasColumnName("EFClientStatistics_ClientId"); + + b.Property("HitCount"); + + b.Property("HitOffsetAverage"); + + b.Property("Location"); + + b.Property("MaxAngleDistance"); + + b.Property("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("RatingId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("ActivityAmount"); + + b.Property("Newest"); + + b.Property("Performance"); + + b.Property("Ranking"); + + b.Property("RatingHistoryId"); + + b.Property("ServerId"); + + b.Property("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("ServerId"); + + b.Property("Active"); + + b.Property("Port"); + + b.HasKey("ServerId"); + + b.ToTable("EFServers"); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b => + { + b.Property("StatisticId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("ServerId"); + + b.Property("TotalKills"); + + b.Property("TotalPlayTime"); + + b.HasKey("StatisticId"); + + b.HasIndex("ServerId"); + + b.ToTable("EFServerStatistics"); + }); + + modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b => + { + b.Property("AliasId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("DateAdded"); + + b.Property("IPAddress"); + + b.Property("LinkId"); + + b.Property("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("AliasLinkId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.HasKey("AliasLinkId"); + + b.ToTable("EFAliasLinks"); + }); + + modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b => + { + b.Property("ChangeHistoryId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("Comment") + .HasMaxLength(128); + + b.Property("CurrentValue"); + + b.Property("OriginEntityId"); + + b.Property("PreviousValue"); + + b.Property("TargetEntityId"); + + b.Property("TimeChanged"); + + b.Property("TypeOfChange"); + + b.HasKey("ChangeHistoryId"); + + b.ToTable("EFChangeHistory"); + }); + + modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b => + { + b.Property("ClientId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("AliasLinkId"); + + b.Property("Connections"); + + b.Property("CurrentAliasId"); + + b.Property("FirstConnection"); + + b.Property("LastConnection"); + + b.Property("Level"); + + b.Property("Masked"); + + b.Property("NetworkId"); + + b.Property("Password"); + + b.Property("PasswordSalt"); + + b.Property("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("MetaId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("ClientId"); + + b.Property("Created"); + + b.Property("Extra"); + + b.Property("Key") + .IsRequired(); + + b.Property("Updated"); + + b.Property("Value") + .IsRequired(); + + b.HasKey("MetaId"); + + b.HasIndex("ClientId"); + + b.ToTable("EFMeta"); + }); + + modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b => + { + b.Property("PenaltyId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("AutomatedOffense"); + + b.Property("Expires"); + + b.Property("LinkId"); + + b.Property("OffenderId"); + + b.Property("Offense") + .IsRequired(); + + b.Property("PunisherId"); + + b.Property("Type"); + + b.Property("When"); + + b.HasKey("PenaltyId"); + + b.HasIndex("LinkId"); + + b.HasIndex("OffenderId"); + + b.HasIndex("PunisherId"); + + b.ToTable("EFPenalties"); + }); + + modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b => + { + b.Property("Vector3Id") + .ValueGeneratedOnAdd(); + + b.Property("EFACSnapshotSnapshotId"); + + b.Property("X"); + + b.Property("Y"); + + b.Property("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 + } + } +} diff --git a/SharedLibraryCore/Migrations/20181125193243_MakeClientIPNullable.cs b/SharedLibraryCore/Migrations/20181125193243_MakeClientIPNullable.cs new file mode 100644 index 000000000..2b504e38b --- /dev/null +++ b/SharedLibraryCore/Migrations/20181125193243_MakeClientIPNullable.cs @@ -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( + name: "IPAddress", + table: "EFAlias", + nullable: true, + oldClrType: typeof(int)); + } + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "IPAddress", + table: "EFAlias", + nullable: false, + oldClrType: typeof(int), + oldNullable: true); + } + } +} diff --git a/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs b/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs index e2cedabab..3faa2c828 100644 --- a/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs +++ b/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs @@ -317,7 +317,7 @@ namespace SharedLibraryCore.Migrations b.Property("DateAdded"); - b.Property("IPAddress"); + b.Property("IPAddress"); b.Property("LinkId"); diff --git a/SharedLibraryCore/Objects/EFClient.cs b/SharedLibraryCore/Objects/EFClient.cs index 71c9a5ed1..28a21cd69 100644 --- a/SharedLibraryCore/Objects/EFClient.cs +++ b/SharedLibraryCore/Objects/EFClient.cs @@ -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] diff --git a/SharedLibraryCore/Server.cs b/SharedLibraryCore/Server.cs index 5d290debd..357449514 100644 --- a/SharedLibraryCore/Server.cs +++ b/SharedLibraryCore/Server.cs @@ -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; diff --git a/SharedLibraryCore/Services/ClientService.cs b/SharedLibraryCore/Services/ClientService.cs index 7bec57d79..89e87413d 100644 --- a/SharedLibraryCore/Services/ClientService.cs +++ b/SharedLibraryCore/Services/ClientService.cs @@ -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 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(); // 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> GetOwners() { using (var context = new DatabaseContext()) + { return await context.Clients .Where(c => c.Level == Permission.Owner) .ToListAsync(); + } } - public async Task> GetPrivilegedClients() + public async Task> 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 GetTotalClientsAsync() { using (var context = new DatabaseContext(true)) + { return await context.Clients .CountAsync(); + } } public Task CreateProxy() { throw new NotImplementedException(); } - - public async Task GetTotalPlayTime() - { - using (var context = new DatabaseContext(true)) - return await context.Clients.SumAsync(c => c.TotalConnectionTime); - } #endregion } } diff --git a/SharedLibraryCore/Services/PenaltyService.cs b/SharedLibraryCore/Services/PenaltyService.cs index 222e150d9..6f3824bd1 100644 --- a/SharedLibraryCore/Services/PenaltyService.cs +++ b/SharedLibraryCore/Services/PenaltyService.cs @@ -213,7 +213,7 @@ namespace SharedLibraryCore.Services } } - public async Task> GetActivePenaltiesAsync(int linkId, int ip = 0) + public async Task> 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) diff --git a/SharedLibraryCore/Utilities.cs b/SharedLibraryCore/Utilities.cs index efc43e8ef..ad8749a9b 100644 --- a/SharedLibraryCore/Utilities.cs +++ b/SharedLibraryCore/Utilities.cs @@ -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()); - 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 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(); 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> GetDvarAsync(this Server server, string dvarName) => server.RconParser.GetDvarAsync(server.RemoteConnection, dvarName); + public static Task> GetDvarAsync(this Server server, string dvarName) + { + return server.RconParser.GetDvarAsync(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 ExecuteCommandAsync(this Server server, string commandName) => await server.RconParser.ExecuteCommandAsync(server.RemoteConnection, commandName); + public static async Task ExecuteCommandAsync(this Server server, string commandName) + { + return await server.RconParser.ExecuteCommandAsync(server.RemoteConnection, commandName); + } - public static Task> GetStatusAsync(this Server server) => server.RconParser.GetStatusAsync(server.RemoteConnection); + public static Task> GetStatusAsync(this Server server) + { + return server.RconParser.GetStatusAsync(server.RemoteConnection); + } public static async Task> 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 diff --git a/WebfrontCore/Controllers/BaseController.cs b/WebfrontCore/Controllers/BaseController.cs index c04614d92..b3c435b88 100644 --- a/WebfrontCore/Controllers/BaseController.cs +++ b/WebfrontCore/Controllers/BaseController.cs @@ -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; diff --git a/WebfrontCore/Controllers/ClientController.cs b/WebfrontCore/Controllers/ClientController.cs index c6b7967eb..56ae4ed8a 100644 --- a/WebfrontCore/Controllers/ClientController.cs +++ b/WebfrontCore/Controllers/ClientController.cs @@ -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>(); diff --git a/WebfrontCore/Views/Client/Privileged/Index.cshtml b/WebfrontCore/Views/Client/Privileged/Index.cshtml index f6279fc9b..f38247dad 100644 --- a/WebfrontCore/Views/Client/Privileged/Index.cshtml +++ b/WebfrontCore/Views/Client/Privileged/Index.cshtml @@ -1,4 +1,4 @@ -@model Dictionary> +@model Dictionary>

@ViewBag.Title