diff --git a/Application/API/GameLogServer/IGameLogServer.cs b/Application/API/GameLogServer/IGameLogServer.cs index cbf9ff677..302c90400 100644 --- a/Application/API/GameLogServer/IGameLogServer.cs +++ b/Application/API/GameLogServer/IGameLogServer.cs @@ -6,7 +6,7 @@ namespace IW4MAdmin.Application.API.GameLogServer [Header("User-Agent", "IW4MAdmin-RestEase")] public interface IGameLogServer { - [Get("log/{path}")] - Task Log([Path] string path); + [Get("log/{path}/{key}")] + Task Log([Path] string path, [Path] string key); } } diff --git a/Application/API/GameLogServer/LogInfo.cs b/Application/API/GameLogServer/LogInfo.cs index 12cd10480..612d4cdcf 100644 --- a/Application/API/GameLogServer/LogInfo.cs +++ b/Application/API/GameLogServer/LogInfo.cs @@ -13,5 +13,7 @@ namespace IW4MAdmin.Application.API.GameLogServer public int Length { get; set; } [JsonProperty("data")] public string Data { get; set; } + [JsonProperty("next_key")] + public string NextKey { get; set; } } } diff --git a/Application/Application.csproj b/Application/Application.csproj index 692c71eb0..d2d3f06e1 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -30,7 +30,8 @@ - true + false + true true 2.2.8.1 2.2.8.1 diff --git a/Application/IO/GameLogEventDetection.cs b/Application/IO/GameLogEventDetection.cs index a23ce5fa1..f137ffe8c 100644 --- a/Application/IO/GameLogEventDetection.cs +++ b/Application/IO/GameLogEventDetection.cs @@ -42,8 +42,7 @@ namespace IW4MAdmin.Application.IO catch (Exception e) { _server.Logger.WriteWarning($"Failed to update log event for {_server.EndPoint}"); - _server.Logger.WriteDebug($"Exception: {e.Message}"); - _server.Logger.WriteDebug($"StackTrace: {e.StackTrace}"); + _server.Logger.WriteDebug(e.GetExceptionInfo()); } } diff --git a/Application/IO/GameLogReaderHttp.cs b/Application/IO/GameLogReaderHttp.cs index 33ea70885..d0e1d72ef 100644 --- a/Application/IO/GameLogReaderHttp.cs +++ b/Application/IO/GameLogReaderHttp.cs @@ -20,6 +20,7 @@ namespace IW4MAdmin.Application.IO readonly IGameLogServer Api; readonly string logPath; private bool? ignoreBots; + private string lastKey = "next"; public GameLogReaderHttp(Uri gameLogServerUri, string logPath, IEventParser parser) { @@ -30,12 +31,12 @@ namespace IW4MAdmin.Application.IO public long Length => -1; - public int UpdateInterval => 550; + public int UpdateInterval => 250; public async Task> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition) { #if DEBUG == true - server.Logger.WriteDebug($"Begin reading from http log"); + server.Logger.WriteDebug($"Begin reading from http log at {DateTime.Now.Millisecond}"); #endif if (!ignoreBots.HasValue) @@ -45,72 +46,79 @@ namespace IW4MAdmin.Application.IO var events = new List(); string b64Path = logPath; - var response = await Api.Log(b64Path); + var response = await Api.Log(b64Path, lastKey); + lastKey = response.NextKey; - if (!response.Success) + if (!response.Success && string.IsNullOrEmpty(lastKey)) { server.Logger.WriteError($"Could not get log server info of {logPath}/{b64Path} ({server.LogPath})"); return events; } - // parse each line - foreach (string eventLine in response.Data.Split(Environment.NewLine)) + else if (response.Data != null) { - if (eventLine.Length > 0) + // parse each line + foreach (string eventLine in response.Data.Split(Environment.NewLine)) { - try + if (eventLine.Length > 0) { - var gameEvent = Parser.GenerateGameEvent(eventLine); - // we don't want to add the event if ignoreBots is on and the event comes from a bot - if (!ignoreBots.Value || (ignoreBots.Value && !((gameEvent.Origin?.IsBot ?? false) || (gameEvent.Target?.IsBot ?? false)))) + try { - gameEvent.Owner = server; - - if ((gameEvent.RequiredEntity & GameEvent.EventRequiredEntity.Origin) == GameEvent.EventRequiredEntity.Origin && gameEvent.Origin.NetworkId != 1) + var gameEvent = Parser.GenerateGameEvent(eventLine); + // we don't want to add the event if ignoreBots is on and the event comes from a bot + if (!ignoreBots.Value || (ignoreBots.Value && !((gameEvent.Origin?.IsBot ?? false) || (gameEvent.Target?.IsBot ?? false)))) { - gameEvent.Origin = server.GetClientsAsList().First(_client => _client.NetworkId == gameEvent.Origin?.NetworkId); - } + gameEvent.Owner = server; - if ((gameEvent.RequiredEntity & GameEvent.EventRequiredEntity.Target) == GameEvent.EventRequiredEntity.Target) - { - gameEvent.Target = server.GetClientsAsList().First(_client => _client.NetworkId == gameEvent.Target?.NetworkId); - } + if ((gameEvent.RequiredEntity & GameEvent.EventRequiredEntity.Origin) == GameEvent.EventRequiredEntity.Origin && gameEvent.Origin.NetworkId != 1) + { + gameEvent.Origin = server.GetClientsAsList().First(_client => _client.NetworkId == gameEvent.Origin?.NetworkId); + } - if (gameEvent.Origin != null) - { - gameEvent.Origin.CurrentServer = server; - } + if ((gameEvent.RequiredEntity & GameEvent.EventRequiredEntity.Target) == GameEvent.EventRequiredEntity.Target) + { + gameEvent.Target = server.GetClientsAsList().First(_client => _client.NetworkId == gameEvent.Target?.NetworkId); + } - if (gameEvent.Target != null) - { - gameEvent.Target.CurrentServer = server; - } + if (gameEvent.Origin != null) + { + gameEvent.Origin.CurrentServer = server; + } - events.Add(gameEvent); - } + if (gameEvent.Target != null) + { + gameEvent.Target.CurrentServer = server; + } + + events.Add(gameEvent); + } #if DEBUG == true - server.Logger.WriteDebug($"Parsed event with id {gameEvent.Id} from http"); + server.Logger.WriteDebug($"Parsed event with id {gameEvent.Id} from http"); #endif - } + } - catch (InvalidOperationException) - { - if (!ignoreBots.Value) + catch (InvalidOperationException) { - server.Logger.WriteWarning("Could not find client in client list when parsing event line"); + if (!ignoreBots.Value) + { + server.Logger.WriteWarning("Could not find client in client list when parsing event line"); + server.Logger.WriteDebug(eventLine); + } + } + + catch (Exception e) + { + server.Logger.WriteWarning("Could not properly parse remote event line"); + server.Logger.WriteDebug(e.Message); server.Logger.WriteDebug(eventLine); } } - - catch (Exception e) - { - server.Logger.WriteWarning("Could not properly parse remote event line"); - server.Logger.WriteDebug(e.Message); - server.Logger.WriteDebug(eventLine); - } } } +#if DEBUG == true + server.Logger.WriteDebug($"End reading from http log at {DateTime.Now.Millisecond}"); +#endif return events; } } diff --git a/Application/Misc/Logger.cs b/Application/Misc/Logger.cs index 69e7dd94c..c10a90fc4 100644 --- a/Application/Misc/Logger.cs +++ b/Application/Misc/Logger.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using System.IO; using System.Threading; +using System.Threading.Tasks; namespace IW4MAdmin.Application { @@ -54,9 +55,9 @@ namespace IW4MAdmin.Application } } - void Write(string msg, LogType type) + async Task Write(string msg, LogType type) { - OnLogWriting.Wait(); + await OnLogWriting.WaitAsync(); string stringType = type.ToString(); @@ -73,14 +74,14 @@ namespace IW4MAdmin.Application #if DEBUG // lets keep it simple and dispose of everything quickly as logging wont be that much (relatively) Console.WriteLine(LogLine); - File.AppendAllText(FileName, LogLine + Environment.NewLine); - Debug.WriteLine(msg); + await File.AppendAllTextAsync(FileName, $"{LogLine}{Environment.NewLine}"); + //Debug.WriteLine(msg); #else if (type == LogType.Error || type == LogType.Verbose) { Console.WriteLine(LogLine); } - File.AppendAllText(FileName, $"{LogLine}{Environment.NewLine}"); + await File.AppendAllTextAsync(FileName, $"{LogLine}{Environment.NewLine}"); #endif } diff --git a/GameLogServer/GameLogServer/log_reader.py b/GameLogServer/GameLogServer/log_reader.py index 3ad9bbf8c..e4fb87024 100644 --- a/GameLogServer/GameLogServer/log_reader.py +++ b/GameLogServer/GameLogServer/log_reader.py @@ -1,6 +1,8 @@ import re import os import time +import random +import string class LogReader(object): def __init__(self): @@ -8,7 +10,7 @@ class LogReader(object): # (if the time between checks is greater, ignore ) - in seconds self.max_file_time_change = 60 - def read_file(self, path): + def read_file(self, path, retrieval_key): # this removes old entries that are no longer valid try: self._clear_old_logs() @@ -22,57 +24,95 @@ class LogReader(object): # prevent traversing directories if re.search('r^.+\.\.\\.+$', path): - return False + return self._generate_bad_response() + # must be a valid log path and log file if not re.search(r'^.+[\\|\/](.+)[\\|\/].+.log$', path): - return False + return self._generate_bad_response() # get the new file size new_file_size = self.file_length(path) # the log size was unable to be read (probably the wrong path) if new_file_size < 0: - return False + return self._generate_bad_response() - # this is the first time the log has been requested - if path not in self.log_file_sizes: - self.log_file_sizes[path] = { - 'length' : new_file_size, - 'read': time.time() + next_retrieval_key = self._generate_key() + + #print('next key is %s' % next_retrieval_key) + + # this is the first time the key has been requested, so we need to the next one + if retrieval_key not in self.log_file_sizes: + print ('requested key %s does not exist' % retrieval_key) + self.log_file_sizes[next_retrieval_key] = { + 'size': new_file_size, + 'length': 0, + 'read': time.time(), + 'next_key': next_retrieval_key, # the key that IW4MAdmin should request next + 'previous_key': retrieval_key # the old key that can be deleted next } - return '' + return { + 'content': None, + 'next_key': next_retrieval_key + } + else: + current_log_info = self.log_file_sizes[retrieval_key] # grab the previous values - last_length = self.log_file_sizes[path]['length'] - file_size_difference = new_file_size - last_length + last_length = current_log_info['length'] + last_size = current_log_info['size'] + file_size_difference = new_file_size - last_size + + #print('generating info for next key %s' % next_retrieval_key) # update the new size and actually read the data - self.log_file_sizes[path] = { - 'length': new_file_size, - 'read': time.time() + self.log_file_sizes[next_retrieval_key] = { + 'size' : new_file_size, + 'length': last_length, + 'read': time.time(), + 'next_key': next_retrieval_key, + 'previous_key': retrieval_key } - new_log_info = self.get_file_lines(path, file_size_difference) - return new_log_info + if current_log_info['previous_key'] in self.log_file_sizes.keys(): + #print('deleting old key %s' % current_log_info['previous_key']) + del self.log_file_sizes[current_log_info['previous_key']] - def get_file_lines(self, path, length): + #print('reading %i bytes starting at %i' % (file_size_difference, last_size)) + + new_log_info = self.get_file_lines(path, last_size, file_size_difference) + return { + 'content': new_log_info, + 'next_key': next_retrieval_key + } + + def get_file_lines(self, path, start_position, length_to_read): try: file_handle = open(path, 'rb') - file_handle.seek(-length, 2) - file_data = file_handle.read(length) + file_handle.seek(start_position) + file_data = file_handle.read(length_to_read) file_handle.close() # using ignore errors omits the pesky 0xb2 bytes we're reading in for some reason return file_data.decode('utf-8', errors='ignore') except Exception as e: - print('could not read the log file at {0}, wanted to read {1} bytes'.format(path, length)) + print('could not read the log file at {0}, wanted to read {1} bytes'.format(path, length_to_read)) print(e) return False def _clear_old_logs(self): expired_logs = [path for path in self.log_file_sizes if int(time.time() - self.log_file_sizes[path]['read']) > self.max_file_time_change] - for log in expired_logs: - print('removing expired log {0}'.format(log)) - del self.log_file_sizes[log] + for key in expired_logs: + print('removing expired log with key {0}'.format(key)) + del self.log_file_sizes[key] + + def _generate_bad_response(self): + return { + 'content': None, + 'next_key': None + } + + def _generate_key(self): + return ''.join(random.choices(string.ascii_uppercase + string.digits, k=8)) def file_length(self, path): try: diff --git a/GameLogServer/GameLogServer/log_resource.py b/GameLogServer/GameLogServer/log_resource.py index 184bee624..b7d5d620c 100644 --- a/GameLogServer/GameLogServer/log_resource.py +++ b/GameLogServer/GameLogServer/log_resource.py @@ -3,12 +3,14 @@ from GameLogServer.log_reader import reader from base64 import urlsafe_b64decode class LogResource(Resource): - def get(self, path): + def get(self, path, retrieval_key): path = urlsafe_b64decode(path).decode('utf-8') - log_info = reader.read_file(path) + log_info = reader.read_file(path, retrieval_key) + content = log_info['content'] return { - 'success' : log_info is not False, - 'length': 0 if log_info is False else len(log_info), - 'data': log_info + 'success' : content is not None, + 'length': 0 if content is None else len(content), + 'data': content, + 'next_key': log_info['next_key'] } diff --git a/GameLogServer/GameLogServer/server.py b/GameLogServer/GameLogServer/server.py index ba555932e..9fde829cb 100644 --- a/GameLogServer/GameLogServer/server.py +++ b/GameLogServer/GameLogServer/server.py @@ -10,5 +10,5 @@ def init(): log = logging.getLogger('werkzeug') log.setLevel(logging.ERROR) api = Api(app) - api.add_resource(LogResource, '/log/') + api.add_resource(LogResource, '/log//') #api.add_resource(RestartResource, '/restart') diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs index 1734e51c5..c22449786 100644 --- a/Plugins/Stats/Helpers/StatManager.cs +++ b/Plugins/Stats/Helpers/StatManager.cs @@ -858,7 +858,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers #region INDIVIDUAL_SERVER_PERFORMANCE // get the client ranking for the current server - int individualClientRanking = await ctx.Set() + int individualClientRanking = await ctx.Set() .Where(GetRankingFunc(clientStats.ServerId)) // ignore themselves in the query .Where(c => c.RatingHistory.ClientId != client.ClientId) diff --git a/Plugins/Stats/Models/ModelConfiguration.cs b/Plugins/Stats/Models/ModelConfiguration.cs index 1234e166b..d02cf7fd9 100644 --- a/Plugins/Stats/Models/ModelConfiguration.cs +++ b/Plugins/Stats/Models/ModelConfiguration.cs @@ -21,13 +21,7 @@ namespace Stats.Models .HasColumnName("EFClientStatistics_ServerId"); builder.Entity() - .HasIndex(p => p.Performance); - - builder.Entity() - .HasIndex(p => p.Ranking); - - builder.Entity() - .HasIndex(p => p.When); + .HasIndex(p => new { p.Performance, p.Ranking, p.When }); builder.Entity() .HasIndex(p => p.TimeSent); diff --git a/SharedLibraryCore/Database/DatabaseContext.cs b/SharedLibraryCore/Database/DatabaseContext.cs index bf644876b..adbd0b04e 100644 --- a/SharedLibraryCore/Database/DatabaseContext.cs +++ b/SharedLibraryCore/Database/DatabaseContext.cs @@ -1,5 +1,7 @@ using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Console; using SharedLibraryCore.Database.Models; using SharedLibraryCore.Interfaces; using System; @@ -20,6 +22,11 @@ namespace SharedLibraryCore.Database public DbSet EFMeta { get; set; } public DbSet EFChangeHistory { get; set; } + [Obsolete] + private static readonly ILoggerFactory _loggerFactory = new LoggerFactory(new[] { + new ConsoleLoggerProvider((category, level) => level == LogLevel.Information, true) + }); + static string _ConnectionString; static string _provider; private static readonly string _migrationPluginDirectory = @"X:\IW4MAdmin\BUILD\Plugins"; @@ -103,6 +110,13 @@ namespace SharedLibraryCore.Database break; } } + +#if DEBUG +#pragma warning disable CS0612 // Type or member is obsolete + optionsBuilder.UseLoggerFactory(_loggerFactory) +#pragma warning restore CS0612 // Type or member is obsolete + .EnableSensitiveDataLogging(); +#endif } protected override void OnModelCreating(ModelBuilder modelBuilder) diff --git a/SharedLibraryCore/Migrations/20190725000309_AlterEFRatingIndex.Designer.cs b/SharedLibraryCore/Migrations/20190725000309_AlterEFRatingIndex.Designer.cs new file mode 100644 index 000000000..6f960f017 --- /dev/null +++ b/SharedLibraryCore/Migrations/20190725000309_AlterEFRatingIndex.Designer.cs @@ -0,0 +1,699 @@ +// +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("20190725000309_AlterEFRatingIndex")] + partial class AlterEFRatingIndex + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.4-servicing-10062"); + + 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("RecoilOffset"); + + 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("AverageRecoilOffset"); + + 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("RatingHistoryId"); + + b.HasIndex("ServerId"); + + b.HasIndex("Performance", "Ranking", "When"); + + b.ToTable("EFRating"); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b => + { + b.Property("ServerId"); + + b.Property("Active"); + + b.Property("EndPoint"); + + b.Property("GameName"); + + 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() + .HasMaxLength(32); + + b.Property("Updated"); + + b.Property("Value") + .IsRequired(); + + b.HasKey("MetaId"); + + b.HasIndex("ClientId"); + + b.HasIndex("Key"); + + 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("IsEvadedOffense"); + + 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/20190725000309_AlterEFRatingIndex.cs b/SharedLibraryCore/Migrations/20190725000309_AlterEFRatingIndex.cs new file mode 100644 index 000000000..9022c31b5 --- /dev/null +++ b/SharedLibraryCore/Migrations/20190725000309_AlterEFRatingIndex.cs @@ -0,0 +1,49 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace SharedLibraryCore.Migrations +{ + public partial class AlterEFRatingIndex : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_EFRating_Performance", + table: "EFRating"); + + migrationBuilder.DropIndex( + name: "IX_EFRating_Ranking", + table: "EFRating"); + + migrationBuilder.DropIndex( + name: "IX_EFRating_When", + table: "EFRating"); + + migrationBuilder.CreateIndex( + name: "IX_EFRating_Performance_Ranking_When", + table: "EFRating", + columns: new[] { "Performance", "Ranking", "When" }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_EFRating_Performance_Ranking_When", + table: "EFRating"); + + migrationBuilder.CreateIndex( + name: "IX_EFRating_Performance", + table: "EFRating", + column: "Performance"); + + migrationBuilder.CreateIndex( + name: "IX_EFRating_Ranking", + table: "EFRating", + column: "Ranking"); + + migrationBuilder.CreateIndex( + name: "IX_EFRating_When", + table: "EFRating", + column: "When"); + } + } +} diff --git a/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs b/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs index fedcf31be..6dbba306d 100644 --- a/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs +++ b/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs @@ -266,15 +266,11 @@ namespace SharedLibraryCore.Migrations b.HasKey("RatingId"); - b.HasIndex("Performance"); - - b.HasIndex("Ranking"); - b.HasIndex("RatingHistoryId"); b.HasIndex("ServerId"); - b.HasIndex("When"); + b.HasIndex("Performance", "Ranking", "When"); b.ToTable("EFRating"); }); diff --git a/SharedLibraryCore/SharedLibraryCore.csproj b/SharedLibraryCore/SharedLibraryCore.csproj index 05784c987..d8a700ebb 100644 --- a/SharedLibraryCore/SharedLibraryCore.csproj +++ b/SharedLibraryCore/SharedLibraryCore.csproj @@ -42,6 +42,7 @@ + diff --git a/WebfrontCore/WebfrontCore.csproj b/WebfrontCore/WebfrontCore.csproj index aa1575f9c..8db75336c 100644 --- a/WebfrontCore/WebfrontCore.csproj +++ b/WebfrontCore/WebfrontCore.csproj @@ -25,7 +25,8 @@ - true + false + true true 7.1