From c5375b661baa821f16882c38bb3f0f8f7ec3fb2e Mon Sep 17 00:00:00 2001 From: RaidMax Date: Mon, 22 Mar 2021 11:09:25 -0500 Subject: [PATCH] huge commit for advanced stats feature. broke data out into its own library. may be breaking changes with existing plugins --- Application/Application.csproj | 2 +- Application/ApplicationManager.cs | 25 +- Application/DefaultSettings.json | 130 +- Application/EventParsers/BaseEventParser.cs | 3 +- Application/Extensions/StartupExtensions.cs | 19 +- .../Factories/DatabaseContextFactory.cs | 6 +- .../Factories/GameServerInstanceFactory.cs | 4 +- Application/Factories/ScriptCommandFactory.cs | 4 +- Application/IW4MServer.cs | 51 +- Application/Main.cs | 92 +- .../AdministeredPenaltyResourceQueryHelper.cs | 3 +- .../ReceivedPenaltyResourceQueryHelper.cs | 6 +- .../Meta/UpdatedAliasResourceQueryHelper.cs | 1 + Application/Migration/DatabaseHousekeeping.cs | 3 +- Application/Misc/BaseConfigurationHandler.cs | 5 + .../Misc/ClientNoticeMessageFormatter.cs | 2 +- Application/Misc/MetaService.cs | 2 + Application/Misc/PluginImporter.cs | 14 +- Application/Misc/ScriptCommand.cs | 3 +- Application/Misc/SerializationHelpers.cs | 1 + Application/RconParsers/BaseRConParser.cs | 1 + Data/Abstractions/IDataValueCache.cs | 12 + .../Abstractions}/IDatabaseContextFactory.cs | 4 +- Data/Abstractions/ILookupCache.cs | 14 + .../Abstractions}/IPropertyExtender.cs | 2 +- Data/Abstractions/IUniqueId.cs | 13 + .../Database => Data/Context}/ContextSeed.cs | 15 +- Data/Context/DatabaseContext.cs | 135 ++ Data/Data.csproj | 82 ++ Data/Extensions/Extensions.cs | 10 + Data/Helpers/DataValueCache.cs | 84 ++ Data/Helpers/LookupCache.cs | 114 ++ .../MigrationContext/MySqlDatabaseContext.cs | 8 +- .../PostgresqlDatabaseContext.cs | 17 +- .../MigrationContext/SqliteDatabaseContext.cs | 8 +- .../20180409183408_InitialCreate.Designer.cs | 6 +- .../MySql/20180409183408_InitialCreate.cs | 2 +- .../MySql/20180502195450_Update.Designer.cs | 6 +- .../Migrations/MySql/20180502195450_Update.cs | 2 +- .../20180516023249_AddEloField.Designer.cs | 6 +- .../MySql/20180516023249_AddEloField.cs | 2 +- .../20180517223349_AddRollingKDR.Designer.cs | 6 +- .../MySql/20180517223349_AddRollingKDR.cs | 2 +- ...tomatedOffenseAndRatingHistory.Designer.cs | 4 +- ...903_AddAutomatedOffenseAndRatingHistory.cs | 2 +- ...180601172317_AddActivityAmount.Designer.cs | 4 +- .../MySql/20180601172317_AddActivityAmount.cs | 2 +- .../20180602041758_AddClientMeta.Designer.cs | 4 +- .../MySql/20180602041758_AddClientMeta.cs | 2 +- ...0180605191706_AddEFACSnapshots.Designer.cs | 4 +- .../MySql/20180605191706_AddEFACSnapshots.cs | 2 +- ...20180614014303_IndexForEFAlias.Designer.cs | 4 +- .../MySql/20180614014303_IndexForEFAlias.cs | 2 +- ...902035612_AddFractionAndIsKill.Designer.cs | 4 +- .../20180902035612_AddFractionAndIsKill.cs | 2 +- ...154622_AddVisibilityPercentage.Designer.cs | 4 +- .../20180904154622_AddVisibilityPercentage.cs | 2 +- .../20180907020706_AddVision.Designer.cs | 4 +- .../MySql/20180907020706_AddVision.cs | 2 +- ...20180908004053_AddWhenToRating.Designer.cs | 4 +- .../MySql/20180908004053_AddWhenToRating.cs | 2 +- ...0180910221749_AddRatingIndexes.Designer.cs | 4 +- .../MySql/20180910221749_AddRatingIndexes.cs | 2 +- ...0911184224_AddEFAliasNameIndex.Designer.cs | 4 +- .../20180911184224_AddEFAliasNameIndex.cs | 2 +- ...0823_AddEFAliasNameMaxLength24.Designer.cs | 4 +- ...0180911190823_AddEFAliasNameMaxLength24.cs | 2 +- ...sCurrentValueToEFChangeHistory.Designer.cs | 4 +- ...ddPreviousCurrentValueToEFChangeHistory.cs | 2 +- ...3111_AddIndexToMessageTimeSent.Designer.cs | 4 +- ...0180915163111_AddIndexToMessageTimeSent.cs | 2 +- ...0180922231310_RemoveACSnapShot.Designer.cs | 4 +- .../MySql/20180922231310_RemoveACSnapShot.cs | 2 +- ...20180922231600_ReaddACSnapshot.Designer.cs | 4 +- .../MySql/20180922231600_ReaddACSnapshot.cs | 2 +- ..._MakePenaltyExpirationNullable.Designer.cs | 4 +- ...014171848_MakePenaltyExpirationNullable.cs | 2 +- ...125193243_MakeClientIPNullable.Designer.cs | 4 +- .../20181125193243_MakeClientIPNullable.cs | 2 +- ...ntToEFServerUpdateServerIdType.Designer.cs | 4 +- ...AddEndpointToEFServerUpdateServerIdType.cs | 2 +- ...1216214513_AddEvadePenaltyFlag.Designer.cs | 4 +- .../20181216214513_AddEvadePenaltyFlag.cs | 2 +- ...ddIndexToEFMeta-KeyAndClientId.Designer.cs | 4 +- ...2234742_AddIndexToEFMeta-KeyAndClientId.cs | 2 +- ...23142128_AddGameNameToEFServer.Designer.cs | 4 +- .../20190423142128_AddGameNameToEFServer.cs | 2 +- ...90615145212_AddAvgRecoilOffset.Designer.cs | 4 +- .../20190615145212_AddAvgRecoilOffset.cs | 2 +- ...4055_AddRecoilOffsetToSnapshot.Designer.cs | 4 +- ...0190615214055_AddRecoilOffsetToSnapshot.cs | 2 +- ...90725000309_AlterEFRatingIndex.Designer.cs | 4 +- .../20190725000309_AlterEFRatingIndex.cs | 2 +- ...2174908_AddSearchNameToEFAlias.Designer.cs | 4 +- .../20190802174908_AddSearchNameToEFAlias.cs | 2 +- ...AvgSnapValueToClientStatistics.Designer.cs | 4 +- ...31210503_AvgSnapValueToClientStatistics.cs | 2 +- ...SnapHitCountToClientStatistics.Designer.cs | 4 +- ...80209_AddSnapHitCountToClientStatistics.cs | 2 +- ...unctionTableForSnapshotVector3.Designer.cs | 4 +- ...3620_UseJunctionTableForSnapshotVector3.cs | 2 +- ..._AddCurrentSnapValueToSnapshot.Designer.cs | 4 +- ...914011524_AddCurrentSnapValueToSnapshot.cs | 2 +- ...5_AddSessionSnapHitsToSnapshot.Designer.cs | 4 +- ...0914012015_AddSessionSnapHitsToSnapshot.cs | 2 +- ...eClientHitLocationCountColumns.Designer.cs | 4 +- ...550_RenameClientHitLocationCountColumns.cs | 2 +- ...rceUniqueIndexForEFAliasIPName.Designer.cs | 4 +- ...0713_EnforceUniqueIndexForEFAliasIPName.cs | 2 +- ...iveCoallationForAliasNameMySQL.Designer.cs | 4 +- ...aseSensitiveCoallationForAliasNameMySQL.cs | 2 +- ...140947_AddMissingActiveColumns.Designer.cs | 4 +- .../20191230140947_AddMissingActiveColumns.cs | 2 +- ...personationIdToEFChangeHistory.Designer.cs | 4 +- ...137_AddImpersonationIdToEFChangeHistory.cs | 2 +- ...21203304_AddHostnameToEFServer.Designer.cs | 4 +- .../20200521203304_AddHostnameToEFServer.cs | 2 +- ...9_AddIsPasswordProtectedColumn.Designer.cs | 4 +- ...0819224119_AddIsPasswordProtectedColumn.cs | 2 +- ...1114232340_UpdateEFRatingIndex.Designer.cs | 4 +- .../20201114232340_UpdateEFRatingIndex.cs | 2 +- ...dSentIngameFlagToClientMessage.Designer.cs | 4 +- ...023106_AddSentIngameFlagToClientMessage.cs | 2 +- ...024731_UpdateMigrationsToMySql.Designer.cs | 4 +- .../20201124024731_UpdateMigrationsToMySql.cs | 2 +- ...teEFMetaToSupportNonClientMeta.Designer.cs | 4 +- ...3921_UpdateEFMetaToSupportNonClientMeta.cs | 2 +- ...pdateEFMetaToSupportLinkedMeta.Designer.cs | 4 +- ...4170830_UpdateEFMetaToSupportLinkedMeta.cs | 2 +- ...0210315222843_AddAdvancedStats.Designer.cs | 1280 ++++++++++++++++ .../MySql/20210315222843_AddAdvancedStats.cs | 391 +++++ .../MySqlDatabaseContextModelSnapshot.cs | 1131 +++++++++----- .../20180409183408_InitialCreate.Designer.cs | 6 +- .../20180409183408_InitialCreate.cs | 2 +- .../20180502195450_Update.Designer.cs | 6 +- .../Postgresql}/20180502195450_Update.cs | 2 +- .../20180516023249_AddEloField.Designer.cs | 6 +- .../Postgresql}/20180516023249_AddEloField.cs | 2 +- .../20180517223349_AddRollingKDR.Designer.cs | 6 +- .../20180517223349_AddRollingKDR.cs | 2 +- ...tomatedOffenseAndRatingHistory.Designer.cs | 4 +- ...903_AddAutomatedOffenseAndRatingHistory.cs | 2 +- ...180601172317_AddActivityAmount.Designer.cs | 4 +- .../20180601172317_AddActivityAmount.cs | 2 +- .../20180602041758_AddClientMeta.Designer.cs | 4 +- .../20180602041758_AddClientMeta.cs | 2 +- ...0180605191706_AddEFACSnapshots.Designer.cs | 4 +- .../20180605191706_AddEFACSnapshots.cs | 2 +- ...20180614014303_IndexForEFAlias.Designer.cs | 4 +- .../20180614014303_IndexForEFAlias.cs | 2 +- ...902035612_AddFractionAndIsKill.Designer.cs | 4 +- .../20180902035612_AddFractionAndIsKill.cs | 2 +- ...154622_AddVisibilityPercentage.Designer.cs | 4 +- .../20180904154622_AddVisibilityPercentage.cs | 2 +- .../20180907020706_AddVision.Designer.cs | 4 +- .../Postgresql}/20180907020706_AddVision.cs | 2 +- ...20180908004053_AddWhenToRating.Designer.cs | 4 +- .../20180908004053_AddWhenToRating.cs | 2 +- ...0180910221749_AddRatingIndexes.Designer.cs | 4 +- .../20180910221749_AddRatingIndexes.cs | 2 +- ...0911184224_AddEFAliasNameIndex.Designer.cs | 4 +- .../20180911184224_AddEFAliasNameIndex.cs | 2 +- ...0823_AddEFAliasNameMaxLength24.Designer.cs | 4 +- ...0180911190823_AddEFAliasNameMaxLength24.cs | 2 +- ...sCurrentValueToEFChangeHistory.Designer.cs | 4 +- ...ddPreviousCurrentValueToEFChangeHistory.cs | 2 +- ...3111_AddIndexToMessageTimeSent.Designer.cs | 4 +- ...0180915163111_AddIndexToMessageTimeSent.cs | 2 +- ...0180922231310_RemoveACSnapShot.Designer.cs | 4 +- .../20180922231310_RemoveACSnapShot.cs | 2 +- ...20180922231600_ReaddACSnapshot.Designer.cs | 4 +- .../20180922231600_ReaddACSnapshot.cs | 2 +- ..._MakePenaltyExpirationNullable.Designer.cs | 4 +- ...014171848_MakePenaltyExpirationNullable.cs | 2 +- ...125193243_MakeClientIPNullable.Designer.cs | 4 +- .../20181125193243_MakeClientIPNullable.cs | 2 +- ...ntToEFServerUpdateServerIdType.Designer.cs | 4 +- ...AddEndpointToEFServerUpdateServerIdType.cs | 2 +- ...1216214513_AddEvadePenaltyFlag.Designer.cs | 4 +- .../20181216214513_AddEvadePenaltyFlag.cs | 2 +- ...ddIndexToEFMeta-KeyAndClientId.Designer.cs | 4 +- ...2234742_AddIndexToEFMeta-KeyAndClientId.cs | 2 +- ...23142128_AddGameNameToEFServer.Designer.cs | 4 +- .../20190423142128_AddGameNameToEFServer.cs | 2 +- ...90615145212_AddAvgRecoilOffset.Designer.cs | 4 +- .../20190615145212_AddAvgRecoilOffset.cs | 2 +- ...4055_AddRecoilOffsetToSnapshot.Designer.cs | 4 +- ...0190615214055_AddRecoilOffsetToSnapshot.cs | 2 +- ...90725000309_AlterEFRatingIndex.Designer.cs | 4 +- .../20190725000309_AlterEFRatingIndex.cs | 2 +- ...2174908_AddSearchNameToEFAlias.Designer.cs | 4 +- .../20190802174908_AddSearchNameToEFAlias.cs | 2 +- ...AvgSnapValueToClientStatistics.Designer.cs | 4 +- ...31210503_AvgSnapValueToClientStatistics.cs | 2 +- ...SnapHitCountToClientStatistics.Designer.cs | 4 +- ...80209_AddSnapHitCountToClientStatistics.cs | 2 +- ...unctionTableForSnapshotVector3.Designer.cs | 4 +- ...3620_UseJunctionTableForSnapshotVector3.cs | 2 +- ..._AddCurrentSnapValueToSnapshot.Designer.cs | 4 +- ...914011524_AddCurrentSnapValueToSnapshot.cs | 2 +- ...5_AddSessionSnapHitsToSnapshot.Designer.cs | 4 +- ...0914012015_AddSessionSnapHitsToSnapshot.cs | 2 +- ...eClientHitLocationCountColumns.Designer.cs | 4 +- ...550_RenameClientHitLocationCountColumns.cs | 2 +- ...rceUniqueIndexForEFAliasIPName.Designer.cs | 4 +- ...0713_EnforceUniqueIndexForEFAliasIPName.cs | 2 +- ...iveCoallationForAliasNameMySQL.Designer.cs | 4 +- ...aseSensitiveCoallationForAliasNameMySQL.cs | 2 +- ...140947_AddMissingActiveColumns.Designer.cs | 4 +- .../20191230140947_AddMissingActiveColumns.cs | 2 +- ...personationIdToEFChangeHistory.Designer.cs | 4 +- ...137_AddImpersonationIdToEFChangeHistory.cs | 2 +- ...21203304_AddHostnameToEFServer.Designer.cs | 4 +- .../20200521203304_AddHostnameToEFServer.cs | 2 +- ...9_AddIsPasswordProtectedColumn.Designer.cs | 4 +- ...0819224119_AddIsPasswordProtectedColumn.cs | 2 +- ...1114232340_UpdateEFRatingIndex.Designer.cs | 4 +- .../20201114232340_UpdateEFRatingIndex.cs | 2 +- ...dSentIngameFlagToClientMessage.Designer.cs | 4 +- ...023106_AddSentIngameFlagToClientMessage.cs | 2 +- ..._UpdateMigrationsForPostgresql.Designer.cs | 4 +- ...125160058_UpdateMigrationsForPostgresql.cs | 2 +- ...teEFMetaToSupportNonClientMeta.Designer.cs | 4 +- ...4304_UpdateEFMetaToSupportNonClientMeta.cs | 2 +- ...pdateEFMetaToSupportLinkedMeta.Designer.cs | 4 +- ...4170956_UpdateEFMetaToSupportLinkedMeta.cs | 2 +- ...0210316004759_AddAdvancedStats.Designer.cs | 1305 +++++++++++++++++ .../20210316004759_AddAdvancedStats.cs | 391 +++++ .../PostgresqlDatabaseContextModelSnapshot.cs | 1171 +++++++++------ .../20180409183408_InitialCreate.Designer.cs | 6 +- .../Sqlite}/20180409183408_InitialCreate.cs | 2 +- .../Sqlite/20180502195450_Update.Designer.cs | 6 +- .../Sqlite}/20180502195450_Update.cs | 2 +- .../20180516023249_AddEloField.Designer.cs | 6 +- .../Sqlite}/20180516023249_AddEloField.cs | 2 +- .../20180517223349_AddRollingKDR.Designer.cs | 6 +- .../Sqlite}/20180517223349_AddRollingKDR.cs | 2 +- ...tomatedOffenseAndRatingHistory.Designer.cs | 4 +- ...903_AddAutomatedOffenseAndRatingHistory.cs | 2 +- ...180601172317_AddActivityAmount.Designer.cs | 4 +- .../20180601172317_AddActivityAmount.cs | 2 +- .../20180602041758_AddClientMeta.Designer.cs | 4 +- .../Sqlite}/20180602041758_AddClientMeta.cs | 2 +- ...0180605191706_AddEFACSnapshots.Designer.cs | 4 +- .../20180605191706_AddEFACSnapshots.cs | 2 +- ...20180614014303_IndexForEFAlias.Designer.cs | 4 +- .../Sqlite}/20180614014303_IndexForEFAlias.cs | 2 +- ...902035612_AddFractionAndIsKill.Designer.cs | 4 +- .../20180902035612_AddFractionAndIsKill.cs | 2 +- ...154622_AddVisibilityPercentage.Designer.cs | 4 +- .../20180904154622_AddVisibilityPercentage.cs | 2 +- .../20180907020706_AddVision.Designer.cs | 4 +- .../Sqlite}/20180907020706_AddVision.cs | 2 +- ...20180908004053_AddWhenToRating.Designer.cs | 4 +- .../Sqlite}/20180908004053_AddWhenToRating.cs | 2 +- ...0180910221749_AddRatingIndexes.Designer.cs | 4 +- .../20180910221749_AddRatingIndexes.cs | 2 +- ...0911184224_AddEFAliasNameIndex.Designer.cs | 4 +- .../20180911184224_AddEFAliasNameIndex.cs | 2 +- ...0823_AddEFAliasNameMaxLength24.Designer.cs | 4 +- ...0180911190823_AddEFAliasNameMaxLength24.cs | 2 +- ...sCurrentValueToEFChangeHistory.Designer.cs | 4 +- ...ddPreviousCurrentValueToEFChangeHistory.cs | 2 +- ...3111_AddIndexToMessageTimeSent.Designer.cs | 4 +- ...0180915163111_AddIndexToMessageTimeSent.cs | 2 +- ...0180922231310_RemoveACSnapShot.Designer.cs | 4 +- .../20180922231310_RemoveACSnapShot.cs | 2 +- ...20180922231600_ReaddACSnapshot.Designer.cs | 4 +- .../Sqlite}/20180922231600_ReaddACSnapshot.cs | 2 +- ..._MakePenaltyExpirationNullable.Designer.cs | 4 +- ...014171848_MakePenaltyExpirationNullable.cs | 2 +- ...125193243_MakeClientIPNullable.Designer.cs | 4 +- .../20181125193243_MakeClientIPNullable.cs | 2 +- ...ntToEFServerUpdateServerIdType.Designer.cs | 4 +- ...AddEndpointToEFServerUpdateServerIdType.cs | 2 +- ...1216214513_AddEvadePenaltyFlag.Designer.cs | 4 +- .../20181216214513_AddEvadePenaltyFlag.cs | 2 +- ...ddIndexToEFMeta-KeyAndClientId.Designer.cs | 4 +- ...2234742_AddIndexToEFMeta-KeyAndClientId.cs | 2 +- ...23142128_AddGameNameToEFServer.Designer.cs | 4 +- .../20190423142128_AddGameNameToEFServer.cs | 2 +- ...90615145212_AddAvgRecoilOffset.Designer.cs | 4 +- .../20190615145212_AddAvgRecoilOffset.cs | 2 +- ...4055_AddRecoilOffsetToSnapshot.Designer.cs | 4 +- ...0190615214055_AddRecoilOffsetToSnapshot.cs | 2 +- ...90725000309_AlterEFRatingIndex.Designer.cs | 4 +- .../20190725000309_AlterEFRatingIndex.cs | 2 +- ...2174908_AddSearchNameToEFAlias.Designer.cs | 4 +- .../20190802174908_AddSearchNameToEFAlias.cs | 2 +- ...AvgSnapValueToClientStatistics.Designer.cs | 4 +- ...31210503_AvgSnapValueToClientStatistics.cs | 2 +- ...SnapHitCountToClientStatistics.Designer.cs | 4 +- ...80209_AddSnapHitCountToClientStatistics.cs | 2 +- ...unctionTableForSnapshotVector3.Designer.cs | 4 +- ...3620_UseJunctionTableForSnapshotVector3.cs | 2 +- ..._AddCurrentSnapValueToSnapshot.Designer.cs | 4 +- ...914011524_AddCurrentSnapValueToSnapshot.cs | 2 +- ...5_AddSessionSnapHitsToSnapshot.Designer.cs | 4 +- ...0914012015_AddSessionSnapHitsToSnapshot.cs | 2 +- ...eClientHitLocationCountColumns.Designer.cs | 4 +- ...550_RenameClientHitLocationCountColumns.cs | 2 +- ...rceUniqueIndexForEFAliasIPName.Designer.cs | 4 +- ...0713_EnforceUniqueIndexForEFAliasIPName.cs | 2 +- ...iveCoallationForAliasNameMySQL.Designer.cs | 4 +- ...aseSensitiveCoallationForAliasNameMySQL.cs | 2 +- ...140947_AddMissingActiveColumns.Designer.cs | 4 +- .../20191230140947_AddMissingActiveColumns.cs | 2 +- ...personationIdToEFChangeHistory.Designer.cs | 4 +- ...137_AddImpersonationIdToEFChangeHistory.cs | 2 +- ...21203304_AddHostnameToEFServer.Designer.cs | 4 +- .../20200521203304_AddHostnameToEFServer.cs | 2 +- ...9_AddIsPasswordProtectedColumn.Designer.cs | 4 +- ...0819224119_AddIsPasswordProtectedColumn.cs | 2 +- ...1114232340_UpdateEFRatingIndex.Designer.cs | 4 +- .../20201114232340_UpdateEFRatingIndex.cs | 2 +- ...dSentIngameFlagToClientMessage.Designer.cs | 4 +- ...023106_AddSentIngameFlagToClientMessage.cs | 2 +- ...portNonClientMetaAndLinkedMeta.Designer.cs | 4 +- ...MetaToSupportNonClientMetaAndLinkedMeta.cs | 2 +- ...AdditionalClientStatsAndZScore.Designer.cs | 1067 +++++++++----- ...31432_AddAdditionalClientStatsAndZScore.cs | 273 ++++ ...210310231004_AddRankingHistory.Designer.cs | 1279 ++++++++++++++++ .../20210310231004_AddRankingHistory.cs | 74 + .../SqliteDatabaseContextModelSnapshot.cs | 1277 ++++++++++++++++ Data/Models/AuditFields.cs | 12 + .../Models/Client}/EFACSnapshotVector3.cs | 8 +- .../Models => Data/Models/Client}/EFClient.cs | 48 +- .../Models/Client}/EFClientKill.cs | 53 +- .../Models/Client}/EFClientMessage.cs | 6 +- .../Models/Client/Stats}/EFACSnapshot.cs | 13 +- .../Client/Stats/EFClientHitStatistic.cs | 91 ++ .../Client/Stats/EFClientRankingHistory.cs | 31 + .../Client/Stats}/EFClientRatingHistory.cs | 8 +- .../Client/Stats}/EFClientStatistics.cs | 14 +- .../Client/Stats}/EFHitLocationCount.cs | 8 +- .../Models/Client/Stats}/EFRating.cs | 6 +- .../Client/Stats/Reference/EFHitLocation.cs | 21 + Data/Models/Client/Stats/Reference/EFMap.cs | 21 + .../Client/Stats/Reference/EFMeansOfDeath.cs | 21 + .../Models/Client/Stats/Reference/EFWeapon.cs | 21 + .../Stats/Reference/EFWeaponAttachment.cs | 21 + .../Reference/EFWeaponAttachmentCombo.cs | 35 + .../Configuration/StatsModelConfiguration.cs | 92 ++ .../Database => Data}/Models/EFAlias.cs | 2 +- .../Database => Data}/Models/EFAliasLink.cs | 4 +- .../Models/EFChangeHistory.cs | 2 +- .../Database => Data}/Models/EFMeta.cs | 5 +- .../Database => Data}/Models/EFPenalty.cs | 21 +- Data/Models/Reference.cs | 19 + .../Models => Data/Models/Server}/EFServer.cs | 14 +- .../Models/Server}/EFServerStatistics.cs | 5 +- .../Database => Data}/Models/SharedEntity.cs | 18 +- .../Helpers => Data/Models}/Vector3.cs | 65 +- IW4MAdmin.sln | 58 +- .../AutomessageFeed/AutomessageFeed.csproj | 2 +- .../IW4ScriptCommands.csproj | 2 +- Plugins/LiveRadar/LiveRadar.csproj | 7 +- Plugins/LiveRadar/Plugin.cs | 10 +- Plugins/LiveRadar/RadarEvent.cs | 4 +- Plugins/Login/Login.csproj | 8 +- Plugins/ProfanityDeterment/Plugin.cs | 3 +- .../ProfanityDeterment.csproj | 2 +- Plugins/Stats/Cheat/Detection.cs | 66 +- Plugins/Stats/Cheat/DetectionPenaltyResult.cs | 2 +- Plugins/Stats/Cheat/Strain.cs | 5 +- Plugins/Stats/Cheat/Thresholds.cs | 6 +- .../IClientStatisticCalculator.cs | 11 + .../Client/Abstractions/IHitInfoBuilder.cs | 10 + .../IServerDistributionCalculator.cs | 11 + .../Client/Abstractions/IWeaponNameParser.cs | 10 + Plugins/Stats/Client/Game/AttachmentInfo.cs | 11 + Plugins/Stats/Client/Game/HitInfo.cs | 27 + Plugins/Stats/Client/Game/WeaponInfo.cs | 11 + Plugins/Stats/Client/HitCalculator.cs | 611 ++++++++ Plugins/Stats/Client/HitInfoBuilder.cs | 64 + .../Client/ServerDistributionCalculator.cs | 150 ++ Plugins/Stats/Client/WeaponNameParser.cs | 75 + Plugins/Stats/Commands/MostKillsCommand.cs | 3 +- Plugins/Stats/Commands/MostPlayedCommand.cs | 4 +- Plugins/Stats/Commands/ResetStats.cs | 6 +- Plugins/Stats/Commands/TopStats.cs | 34 +- Plugins/Stats/Commands/ViewStats.cs | 8 +- Plugins/Stats/Config/StatsConfiguration.cs | 80 +- .../Config/WeaponNameParserConfiguration.cs | 11 + Plugins/Stats/Dtos/AdvancedStatsInfo.cs | 28 + Plugins/Stats/Dtos/StatsInfoRequest.cs | 1 + Plugins/Stats/Dtos/TopStatsInfo.cs | 6 +- Plugins/Stats/Extensions.cs | 196 +++ .../AdvancedClientStatsResourceQueryHelper.cs | 156 ++ .../Helpers}/ChatResourceQueryHelper.cs | 18 +- Plugins/Stats/Helpers/ServerStats.cs | 13 +- Plugins/Stats/Helpers/StatManager.cs | 555 +++++-- .../Stats/Helpers/StatsResourceQueryHelper.cs | 7 +- Plugins/Stats/Helpers/WeaponNameExtensions.cs | 10 + Plugins/Stats/Models/ModelConfiguration.cs | 43 - Plugins/Stats/Plugin.cs | 57 +- Plugins/Stats/Stats.csproj | 4 +- .../Extensions/SearchQueryExtensions.cs | 77 - Plugins/Web/StatsWeb/StatsWeb.csproj | 35 - .../ViewComponents/TopPlayersViewComponent.cs | 28 - .../Stats/Components/TopPlayers/_List.cshtml | 91 -- .../Web/StatsWeb/Views/_ViewImports.cshtml | 3 - .../icons/0_no-place/menu_div_no_place.png | Bin 12836 -> 0 bytes .../icons/1_iron/menu_div_iron_sub03.png | Bin 18393 -> 0 bytes .../icons/2_bronze/menu_div_bronze_sub03.png | Bin 18593 -> 0 bytes .../icons/3_silver/menu_div_silver_sub03.png | Bin 17540 -> 0 bytes .../icons/4_gold/menu_div_gold_sub03.png | Bin 19996 -> 0 bytes .../5_platinum/menu_div_platinum_sub03.png | Bin 20952 -> 0 bytes .../6_semipro/menu_div_semipro_sub03.png | Bin 21663 -> 0 bytes .../images/icons/7_pro/menu_div_pro_sub03.png | Bin 21238 -> 0 bytes Plugins/Welcome/Plugin.cs | 8 +- Plugins/Welcome/Welcome.csproj | 8 +- SharedLibraryCore/BaseController.cs | 6 +- .../Commands/AddClientTagCommand.cs | 1 + SharedLibraryCore/Commands/ListClientTags.cs | 1 + SharedLibraryCore/Commands/NativeCommands.cs | 6 +- .../Commands/RemoveClientTagCommand.cs | 1 + .../Commands/SetClientTagCommand.cs | 1 + .../Commands/UnsetClientTagCommand.cs | 1 + .../Configuration/ApplicationConfiguration.cs | 2 +- .../Configuration/CommandProperties.cs | 2 +- ...ultConfiguration.cs => DefaultSettings.cs} | 3 +- .../Configuration/GameStringConfiguration.cs | 24 + SharedLibraryCore/Database/DatabaseContext.cs | 196 --- .../Meta/Responses/ReceivedPenaltyResponse.cs | 4 +- SharedLibraryCore/Dtos/PenaltyInfo.cs | 8 +- SharedLibraryCore/Dtos/PlayerInfo.cs | 4 +- SharedLibraryCore/Dtos/ServerInfo.cs | 1 + SharedLibraryCore/Interfaces/IAuditFields.cs | 9 + .../IClientNoticeMessageFormatter.cs | 2 +- SharedLibraryCore/Interfaces/IGameServer.cs | 1 + .../Interfaces/IManagerCommand.cs | 2 +- SharedLibraryCore/Interfaces/IMetaService.cs | 3 +- .../Interfaces/IPluginImporter.cs | 2 +- SharedLibraryCore/Localization/Permission.cs | 2 +- SharedLibraryCore/PartialEntities/EFAlias.cs | 7 - SharedLibraryCore/PartialEntities/EFClient.cs | 51 +- .../PartialEntities/EFPenalty.cs | 22 - .../AuditInformationRepository.cs | 3 +- SharedLibraryCore/Server.cs | 1 + .../Services/ChangeHistoryService.cs | 7 +- SharedLibraryCore/Services/ClientService.cs | 28 +- SharedLibraryCore/Services/PenaltyService.cs | 4 +- SharedLibraryCore/SharedLibraryCore.csproj | 24 +- SharedLibraryCore/Utilities.cs | 56 +- Tests/ApplicationTests/StatsWebTests.cs | 269 ---- .../Controllers}/API/StatsController.cs | 0 .../Validation/FindClientRequestValidator.cs | 4 +- WebfrontCore/Controllers/ActionController.cs | 8 +- .../{ => Client}/ClientController.cs | 27 +- .../Client/ClientStatisticsController.cs | 38 + .../Client/Legacy}/StatsController.cs | 49 +- WebfrontCore/Controllers/ConsoleController.cs | 1 + WebfrontCore/Controllers/HomeController.cs | 42 +- WebfrontCore/Controllers/PenaltyController.cs | 10 +- .../Middleware/ClaimsPermissionRemoval.cs | 8 +- WebfrontCore/Startup.cs | 27 +- .../PenaltyListViewComponent.cs | 2 +- .../ViewComponents/TopPlayersViewComponent.cs | 42 + WebfrontCore/ViewModels/PenaltyFilterInfo.cs | 4 +- WebfrontCore/Views/Client/Find/Index.cshtml | 2 +- .../Views/Client}/Message/Find.cshtml | 0 .../Views/Client}/Message/_Item.cshtml | 0 .../Views/Client/Profile/Index.cshtml | 60 +- .../Meta/_AdministeredPenaltyResponse.cshtml | 2 +- .../Meta/_ReceivedPenaltyResponse.cshtml | 3 +- .../Views/Client/Statistics/Advanced.cshtml | 457 ++++++ .../Components/TopPlayers/_List.cshtml | 151 ++ .../Views/Client/Statistics}/Index.cshtml | 4 +- .../Views/Client}/_MessageContext.cshtml | 0 .../Views/Client}/_PenaltyInfo.cshtml | 2 +- WebfrontCore/Views/Penalty/List.cshtml | 10 +- WebfrontCore/Views/Shared/_Layout.cshtml | 5 +- WebfrontCore/WebfrontCore.csproj | 25 +- WebfrontCore/bundleconfig.json | 7 +- WebfrontCore/libman.json | 4 + WebfrontCore/wwwroot/css/src/main.scss | 43 +- .../images/stats/hit_location_model.png | Bin 0 -> 182329 bytes .../wwwroot/images/stats/ranks/rank_0.png | Bin 0 -> 12029 bytes .../wwwroot/images/stats/ranks/rank_1.png | Bin 0 -> 13569 bytes .../wwwroot/images/stats/ranks/rank_10.png | Bin 0 -> 18442 bytes .../wwwroot/images/stats/ranks/rank_11.png | Bin 0 -> 19296 bytes .../wwwroot/images/stats/ranks/rank_12.png | Bin 0 -> 20286 bytes .../wwwroot/images/stats/ranks/rank_13.png | Bin 0 -> 16339 bytes .../wwwroot/images/stats/ranks/rank_14.png | Bin 0 -> 16204 bytes .../wwwroot/images/stats/ranks/rank_15.png | Bin 0 -> 16724 bytes .../wwwroot/images/stats/ranks/rank_16.png | Bin 0 -> 15764 bytes .../wwwroot/images/stats/ranks/rank_17.png | Bin 0 -> 16098 bytes .../wwwroot/images/stats/ranks/rank_18.png | Bin 0 -> 16631 bytes .../wwwroot/images/stats/ranks/rank_19.png | Bin 0 -> 16675 bytes .../wwwroot/images/stats/ranks/rank_2.png | Bin 0 -> 15340 bytes .../wwwroot/images/stats/ranks/rank_20.png | Bin 0 -> 16976 bytes .../wwwroot/images/stats/ranks/rank_21.png | Bin 0 -> 17439 bytes .../wwwroot/images/stats/ranks/rank_22.png | Bin 0 -> 16379 bytes .../wwwroot/images/stats/ranks/rank_23.png | Bin 0 -> 16697 bytes .../wwwroot/images/stats/ranks/rank_24.png | Bin 0 -> 17089 bytes .../wwwroot/images/stats/ranks/rank_3.png | Bin 0 -> 17097 bytes .../wwwroot/images/stats/ranks/rank_4.png | Bin 0 -> 13993 bytes .../wwwroot/images/stats/ranks/rank_5.png | Bin 0 -> 15888 bytes .../wwwroot/images/stats/ranks/rank_6.png | Bin 0 -> 17501 bytes .../wwwroot/images/stats/ranks/rank_7.png | Bin 0 -> 16914 bytes .../wwwroot/images/stats/ranks/rank_8.png | Bin 0 -> 17491 bytes .../wwwroot/images/stats/ranks/rank_9.png | Bin 0 -> 19131 bytes WebfrontCore/wwwroot/js/advanced_stats.js | 406 +++++ WebfrontCore/wwwroot/js/stats.js | 12 +- 505 files changed, 13671 insertions(+), 3271 deletions(-) create mode 100644 Data/Abstractions/IDataValueCache.cs rename {SharedLibraryCore/Interfaces => Data/Abstractions}/IDatabaseContextFactory.cs (86%) create mode 100644 Data/Abstractions/ILookupCache.cs rename {SharedLibraryCore/Interfaces => Data/Abstractions}/IPropertyExtender.cs (94%) create mode 100644 Data/Abstractions/IUniqueId.cs rename {SharedLibraryCore/Database => Data/Context}/ContextSeed.cs (82%) create mode 100644 Data/Context/DatabaseContext.cs create mode 100644 Data/Data.csproj create mode 100644 Data/Extensions/Extensions.cs create mode 100644 Data/Helpers/DataValueCache.cs create mode 100644 Data/Helpers/LookupCache.cs rename {SharedLibraryCore/Database => Data}/MigrationContext/MySqlDatabaseContext.cs (80%) rename {SharedLibraryCore/Database => Data}/MigrationContext/PostgresqlDatabaseContext.cs (70%) rename {SharedLibraryCore/Database => Data}/MigrationContext/SqliteDatabaseContext.cs (80%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180409183408_InitialCreate.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180409183408_InitialCreate.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180502195450_Update.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180502195450_Update.cs (95%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180516023249_AddEloField.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180516023249_AddEloField.cs (93%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180517223349_AddRollingKDR.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180517223349_AddRollingKDR.cs (93%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180531212903_AddAutomatedOffenseAndRatingHistory.cs (98%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180601172317_AddActivityAmount.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180601172317_AddActivityAmount.cs (93%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180602041758_AddClientMeta.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180602041758_AddClientMeta.cs (97%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180605191706_AddEFACSnapshots.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180605191706_AddEFACSnapshots.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180614014303_IndexForEFAlias.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180614014303_IndexForEFAlias.cs (93%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180902035612_AddFractionAndIsKill.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180902035612_AddFractionAndIsKill.cs (98%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180904154622_AddVisibilityPercentage.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180904154622_AddVisibilityPercentage.cs (93%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180907020706_AddVision.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180907020706_AddVision.cs (93%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180908004053_AddWhenToRating.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180908004053_AddWhenToRating.cs (93%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180910221749_AddRatingIndexes.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180910221749_AddRatingIndexes.cs (96%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180911184224_AddEFAliasNameIndex.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180911184224_AddEFAliasNameIndex.cs (92%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180911190823_AddEFAliasNameMaxLength24.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180911190823_AddEFAliasNameMaxLength24.cs (94%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs (95%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180915163111_AddIndexToMessageTimeSent.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180915163111_AddIndexToMessageTimeSent.cs (93%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180922231310_RemoveACSnapShot.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180922231310_RemoveACSnapShot.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180922231600_ReaddACSnapshot.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20180922231600_ReaddACSnapshot.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20181014171848_MakePenaltyExpirationNullable.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20181014171848_MakePenaltyExpirationNullable.cs (98%) rename {SharedLibraryCore => Data}/Migrations/MySql/20181125193243_MakeClientIPNullable.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20181125193243_MakeClientIPNullable.cs (98%) rename {SharedLibraryCore => Data}/Migrations/MySql/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs (92%) rename {SharedLibraryCore => Data}/Migrations/MySql/20181216214513_AddEvadePenaltyFlag.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20181216214513_AddEvadePenaltyFlag.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs (94%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190423142128_AddGameNameToEFServer.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190423142128_AddGameNameToEFServer.cs (92%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190615145212_AddAvgRecoilOffset.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190615145212_AddAvgRecoilOffset.cs (91%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190615214055_AddRecoilOffsetToSnapshot.cs (93%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190725000309_AlterEFRatingIndex.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190725000309_AlterEFRatingIndex.cs (97%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190802174908_AddSearchNameToEFAlias.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190802174908_AddSearchNameToEFAlias.cs (95%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190831210503_AvgSnapValueToClientStatistics.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190831210503_AvgSnapValueToClientStatistics.cs (93%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190901180209_AddSnapHitCountToClientStatistics.cs (93%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190901223620_UseJunctionTableForSnapshotVector3.cs (98%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190914011524_AddCurrentSnapValueToSnapshot.cs (93%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20190914012015_AddSessionSnapHitsToSnapshot.cs (93%) rename {SharedLibraryCore => Data}/Migrations/MySql/20191004172550_RenameClientHitLocationCountColumns.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20191004172550_RenameClientHitLocationCountColumns.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs (98%) rename {SharedLibraryCore => Data}/Migrations/MySql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs (94%) rename {SharedLibraryCore => Data}/Migrations/MySql/20191230140947_AddMissingActiveColumns.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20191230140947_AddMissingActiveColumns.cs (93%) rename {SharedLibraryCore => Data}/Migrations/MySql/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20200423225137_AddImpersonationIdToEFChangeHistory.cs (93%) rename {SharedLibraryCore => Data}/Migrations/MySql/20200521203304_AddHostnameToEFServer.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20200521203304_AddHostnameToEFServer.cs (92%) rename {SharedLibraryCore => Data}/Migrations/MySql/20200819224119_AddIsPasswordProtectedColumn.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20200819224119_AddIsPasswordProtectedColumn.cs (95%) rename {SharedLibraryCore => Data}/Migrations/MySql/20201114232340_UpdateEFRatingIndex.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20201114232340_UpdateEFRatingIndex.cs (94%) rename {SharedLibraryCore => Data}/Migrations/MySql/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20201118023106_AddSentIngameFlagToClientMessage.cs (93%) rename {SharedLibraryCore => Data}/Migrations/MySql/20201124024731_UpdateMigrationsToMySql.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20201124024731_UpdateMigrationsToMySql.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20210123023921_UpdateEFMetaToSupportNonClientMeta.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20210123023921_UpdateEFMetaToSupportNonClientMeta.cs (97%) rename {SharedLibraryCore => Data}/Migrations/MySql/20210124170830_UpdateEFMetaToSupportLinkedMeta.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/MySql/20210124170830_UpdateEFMetaToSupportLinkedMeta.cs (96%) create mode 100644 Data/Migrations/MySql/20210315222843_AddAdvancedStats.Designer.cs create mode 100644 Data/Migrations/MySql/20210315222843_AddAdvancedStats.cs rename {SharedLibraryCore => Data}/Migrations/MySql/MySqlDatabaseContextModelSnapshot.cs (62%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180409183408_InitialCreate.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180409183408_InitialCreate.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180502195450_Update.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180502195450_Update.cs (95%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180516023249_AddEloField.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180516023249_AddEloField.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180517223349_AddRollingKDR.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180517223349_AddRollingKDR.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180531212903_AddAutomatedOffenseAndRatingHistory.cs (98%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180601172317_AddActivityAmount.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180601172317_AddActivityAmount.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180602041758_AddClientMeta.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180602041758_AddClientMeta.cs (97%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180605191706_AddEFACSnapshots.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180605191706_AddEFACSnapshots.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180614014303_IndexForEFAlias.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180614014303_IndexForEFAlias.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180902035612_AddFractionAndIsKill.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180902035612_AddFractionAndIsKill.cs (98%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180904154622_AddVisibilityPercentage.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180904154622_AddVisibilityPercentage.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180907020706_AddVision.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180907020706_AddVision.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180908004053_AddWhenToRating.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180908004053_AddWhenToRating.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180910221749_AddRatingIndexes.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180910221749_AddRatingIndexes.cs (96%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180911184224_AddEFAliasNameIndex.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180911184224_AddEFAliasNameIndex.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180911190823_AddEFAliasNameMaxLength24.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180911190823_AddEFAliasNameMaxLength24.cs (94%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs (95%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180915163111_AddIndexToMessageTimeSent.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180915163111_AddIndexToMessageTimeSent.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180922231310_RemoveACSnapShot.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180922231310_RemoveACSnapShot.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20180922231600_ReaddACSnapshot.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20180922231600_ReaddACSnapshot.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20181014171848_MakePenaltyExpirationNullable.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20181014171848_MakePenaltyExpirationNullable.cs (98%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20181125193243_MakeClientIPNullable.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20181125193243_MakeClientIPNullable.cs (98%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20181216214513_AddEvadePenaltyFlag.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20181216214513_AddEvadePenaltyFlag.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs (94%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20190423142128_AddGameNameToEFServer.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20190423142128_AddGameNameToEFServer.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20190615145212_AddAvgRecoilOffset.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20190615145212_AddAvgRecoilOffset.cs (91%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20190615214055_AddRecoilOffsetToSnapshot.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20190725000309_AlterEFRatingIndex.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20190725000309_AlterEFRatingIndex.cs (97%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20190802174908_AddSearchNameToEFAlias.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20190802174908_AddSearchNameToEFAlias.cs (95%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20190831210503_AvgSnapValueToClientStatistics.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20190831210503_AvgSnapValueToClientStatistics.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20190901180209_AddSnapHitCountToClientStatistics.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20190901223620_UseJunctionTableForSnapshotVector3.cs (98%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20190914011524_AddCurrentSnapValueToSnapshot.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20190914012015_AddSessionSnapHitsToSnapshot.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20191004172550_RenameClientHitLocationCountColumns.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20191004172550_RenameClientHitLocationCountColumns.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs (98%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs (94%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20191230140947_AddMissingActiveColumns.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20191230140947_AddMissingActiveColumns.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20200423225137_AddImpersonationIdToEFChangeHistory.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20200521203304_AddHostnameToEFServer.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20200521203304_AddHostnameToEFServer.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20200819224119_AddIsPasswordProtectedColumn.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20200819224119_AddIsPasswordProtectedColumn.cs (95%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20201114232340_UpdateEFRatingIndex.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20201114232340_UpdateEFRatingIndex.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Sqlite => Data/Migrations/Postgresql}/20201118023106_AddSentIngameFlagToClientMessage.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20201125160058_UpdateMigrationsForPostgresql.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20201125160058_UpdateMigrationsForPostgresql.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20210123024304_UpdateEFMetaToSupportNonClientMeta.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20210123024304_UpdateEFMetaToSupportNonClientMeta.cs (97%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20210124170956_UpdateEFMetaToSupportLinkedMeta.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Postgresql/20210124170956_UpdateEFMetaToSupportLinkedMeta.cs (96%) create mode 100644 Data/Migrations/Postgresql/20210316004759_AddAdvancedStats.Designer.cs create mode 100644 Data/Migrations/Postgresql/20210316004759_AddAdvancedStats.cs rename {SharedLibraryCore => Data}/Migrations/Postgresql/PostgresqlDatabaseContextModelSnapshot.cs (61%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180409183408_InitialCreate.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180409183408_InitialCreate.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180502195450_Update.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180502195450_Update.cs (95%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180516023249_AddEloField.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180516023249_AddEloField.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180517223349_AddRollingKDR.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180517223349_AddRollingKDR.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180531212903_AddAutomatedOffenseAndRatingHistory.cs (98%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180601172317_AddActivityAmount.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180601172317_AddActivityAmount.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180602041758_AddClientMeta.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180602041758_AddClientMeta.cs (97%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180605191706_AddEFACSnapshots.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180605191706_AddEFACSnapshots.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180614014303_IndexForEFAlias.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180614014303_IndexForEFAlias.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180902035612_AddFractionAndIsKill.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180902035612_AddFractionAndIsKill.cs (97%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180904154622_AddVisibilityPercentage.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180904154622_AddVisibilityPercentage.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180907020706_AddVision.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180907020706_AddVision.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180908004053_AddWhenToRating.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180908004053_AddWhenToRating.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180910221749_AddRatingIndexes.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180910221749_AddRatingIndexes.cs (95%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180911184224_AddEFAliasNameIndex.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180911184224_AddEFAliasNameIndex.cs (91%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180911190823_AddEFAliasNameMaxLength24.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180911190823_AddEFAliasNameMaxLength24.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs (94%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180915163111_AddIndexToMessageTimeSent.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180915163111_AddIndexToMessageTimeSent.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180922231310_RemoveACSnapShot.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180922231310_RemoveACSnapShot.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20180922231600_ReaddACSnapshot.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20180922231600_ReaddACSnapshot.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20181014171848_MakePenaltyExpirationNullable.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20181014171848_MakePenaltyExpirationNullable.cs (98%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20181125193243_MakeClientIPNullable.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20181125193243_MakeClientIPNullable.cs (97%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20181216214513_AddEvadePenaltyFlag.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20181216214513_AddEvadePenaltyFlag.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs (94%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20190423142128_AddGameNameToEFServer.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20190423142128_AddGameNameToEFServer.cs (91%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20190615145212_AddAvgRecoilOffset.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20190615145212_AddAvgRecoilOffset.cs (91%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20190615214055_AddRecoilOffsetToSnapshot.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20190725000309_AlterEFRatingIndex.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20190725000309_AlterEFRatingIndex.cs (96%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20190802174908_AddSearchNameToEFAlias.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20190802174908_AddSearchNameToEFAlias.cs (94%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20190831210503_AvgSnapValueToClientStatistics.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20190831210503_AvgSnapValueToClientStatistics.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20190901180209_AddSnapHitCountToClientStatistics.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20190901223620_UseJunctionTableForSnapshotVector3.cs (98%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20190914011524_AddCurrentSnapValueToSnapshot.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20190914012015_AddSessionSnapHitsToSnapshot.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20191004172550_RenameClientHitLocationCountColumns.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20191004172550_RenameClientHitLocationCountColumns.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs (98%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs (94%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20191230140947_AddMissingActiveColumns.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20191230140947_AddMissingActiveColumns.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20200423225137_AddImpersonationIdToEFChangeHistory.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20200521203304_AddHostnameToEFServer.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20200521203304_AddHostnameToEFServer.cs (91%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20200819224119_AddIsPasswordProtectedColumn.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20200819224119_AddIsPasswordProtectedColumn.cs (95%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20201114232340_UpdateEFRatingIndex.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20201114232340_UpdateEFRatingIndex.cs (93%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs (99%) rename {SharedLibraryCore/Migrations/Postgresql => Data/Migrations/Sqlite}/20201118023106_AddSentIngameFlagToClientMessage.cs (92%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20210124164906_UpdateEFMetaToSupportNonClientMetaAndLinkedMeta.Designer.cs (99%) rename {SharedLibraryCore => Data}/Migrations/Sqlite/20210124164906_UpdateEFMetaToSupportNonClientMetaAndLinkedMeta.cs (98%) rename SharedLibraryCore/Migrations/Sqlite/SqliteDatabaseContextModelSnapshot.cs => Data/Migrations/Sqlite/20210301231432_AddAdditionalClientStatsAndZScore.Designer.cs (65%) create mode 100644 Data/Migrations/Sqlite/20210301231432_AddAdditionalClientStatsAndZScore.cs create mode 100644 Data/Migrations/Sqlite/20210310231004_AddRankingHistory.Designer.cs create mode 100644 Data/Migrations/Sqlite/20210310231004_AddRankingHistory.cs create mode 100644 Data/Migrations/Sqlite/SqliteDatabaseContextModelSnapshot.cs create mode 100644 Data/Models/AuditFields.cs rename {Plugins/Stats/Models => Data/Models/Client}/EFACSnapshotVector3.cs (73%) rename {SharedLibraryCore/Database/Models => Data/Models/Client}/EFClient.cs (50%) rename {Plugins/Stats/Models => Data/Models/Client}/EFClientKill.cs (51%) rename {Plugins/Stats/Models => Data/Models/Client}/EFClientMessage.cs (86%) rename {Plugins/Stats/Models => Data/Models/Client/Stats}/EFACSnapshot.cs (87%) create mode 100644 Data/Models/Client/Stats/EFClientHitStatistic.cs create mode 100644 Data/Models/Client/Stats/EFClientRankingHistory.cs rename {Plugins/Stats/Models => Data/Models/Client/Stats}/EFClientRatingHistory.cs (69%) rename {Plugins/Stats/Models => Data/Models/Client/Stats}/EFClientStatistics.cs (90%) rename {Plugins/Stats/Models => Data/Models/Client/Stats}/EFHitLocationCount.cs (81%) rename {Plugins/Stats/Models => Data/Models/Client/Stats}/EFRating.cs (91%) create mode 100644 Data/Models/Client/Stats/Reference/EFHitLocation.cs create mode 100644 Data/Models/Client/Stats/Reference/EFMap.cs create mode 100644 Data/Models/Client/Stats/Reference/EFMeansOfDeath.cs create mode 100644 Data/Models/Client/Stats/Reference/EFWeapon.cs create mode 100644 Data/Models/Client/Stats/Reference/EFWeaponAttachment.cs create mode 100644 Data/Models/Client/Stats/Reference/EFWeaponAttachmentCombo.cs create mode 100644 Data/Models/Configuration/StatsModelConfiguration.cs rename {SharedLibraryCore/Database => Data}/Models/EFAlias.cs (94%) rename {SharedLibraryCore/Database => Data}/Models/EFAliasLink.cs (81%) rename {SharedLibraryCore/Database => Data}/Models/EFChangeHistory.cs (95%) rename {SharedLibraryCore/Database => Data}/Models/EFMeta.cs (94%) rename {SharedLibraryCore/Database => Data}/Models/EFPenalty.cs (73%) create mode 100644 Data/Models/Reference.cs rename {Plugins/Stats/Models => Data/Models/Server}/EFServer.cs (57%) rename {Plugins/Stats/Models => Data/Models/Server}/EFServerStatistics.cs (75%) rename {SharedLibraryCore/Database => Data}/Models/SharedEntity.cs (63%) rename {SharedLibraryCore/Helpers => Data/Models}/Vector3.cs (64%) create mode 100644 Plugins/Stats/Client/Abstractions/IClientStatisticCalculator.cs create mode 100644 Plugins/Stats/Client/Abstractions/IHitInfoBuilder.cs create mode 100644 Plugins/Stats/Client/Abstractions/IServerDistributionCalculator.cs create mode 100644 Plugins/Stats/Client/Abstractions/IWeaponNameParser.cs create mode 100644 Plugins/Stats/Client/Game/AttachmentInfo.cs create mode 100644 Plugins/Stats/Client/Game/HitInfo.cs create mode 100644 Plugins/Stats/Client/Game/WeaponInfo.cs create mode 100644 Plugins/Stats/Client/HitCalculator.cs create mode 100644 Plugins/Stats/Client/HitInfoBuilder.cs create mode 100644 Plugins/Stats/Client/ServerDistributionCalculator.cs create mode 100644 Plugins/Stats/Client/WeaponNameParser.cs create mode 100644 Plugins/Stats/Config/WeaponNameParserConfiguration.cs create mode 100644 Plugins/Stats/Dtos/AdvancedStatsInfo.cs create mode 100644 Plugins/Stats/Extensions.cs create mode 100644 Plugins/Stats/Helpers/AdvancedClientStatsResourceQueryHelper.cs rename Plugins/{Web/StatsWeb => Stats/Helpers}/ChatResourceQueryHelper.cs (96%) create mode 100644 Plugins/Stats/Helpers/WeaponNameExtensions.cs delete mode 100644 Plugins/Stats/Models/ModelConfiguration.cs delete mode 100644 Plugins/Web/StatsWeb/Extensions/SearchQueryExtensions.cs delete mode 100644 Plugins/Web/StatsWeb/StatsWeb.csproj delete mode 100644 Plugins/Web/StatsWeb/ViewComponents/TopPlayersViewComponent.cs delete mode 100644 Plugins/Web/StatsWeb/Views/Stats/Components/TopPlayers/_List.cshtml delete mode 100644 Plugins/Web/StatsWeb/Views/_ViewImports.cshtml delete mode 100644 Plugins/Web/StatsWeb/wwwroot/images/icons/0_no-place/menu_div_no_place.png delete mode 100644 Plugins/Web/StatsWeb/wwwroot/images/icons/1_iron/menu_div_iron_sub03.png delete mode 100644 Plugins/Web/StatsWeb/wwwroot/images/icons/2_bronze/menu_div_bronze_sub03.png delete mode 100644 Plugins/Web/StatsWeb/wwwroot/images/icons/3_silver/menu_div_silver_sub03.png delete mode 100644 Plugins/Web/StatsWeb/wwwroot/images/icons/4_gold/menu_div_gold_sub03.png delete mode 100644 Plugins/Web/StatsWeb/wwwroot/images/icons/5_platinum/menu_div_platinum_sub03.png delete mode 100644 Plugins/Web/StatsWeb/wwwroot/images/icons/6_semipro/menu_div_semipro_sub03.png delete mode 100644 Plugins/Web/StatsWeb/wwwroot/images/icons/7_pro/menu_div_pro_sub03.png rename SharedLibraryCore/Configuration/{DefaultConfiguration.cs => DefaultSettings.cs} (80%) create mode 100644 SharedLibraryCore/Configuration/GameStringConfiguration.cs delete mode 100644 SharedLibraryCore/Database/DatabaseContext.cs create mode 100644 SharedLibraryCore/Interfaces/IAuditFields.cs delete mode 100644 SharedLibraryCore/PartialEntities/EFAlias.cs delete mode 100644 SharedLibraryCore/PartialEntities/EFPenalty.cs delete mode 100644 Tests/ApplicationTests/StatsWebTests.cs rename {Plugins/Web/StatsWeb => WebfrontCore/Controllers}/API/StatsController.cs (100%) rename WebfrontCore/Controllers/{ => Client}/ClientController.cs (86%) create mode 100644 WebfrontCore/Controllers/Client/ClientStatisticsController.cs rename {Plugins/Web/StatsWeb/Controllers => WebfrontCore/Controllers/Client/Legacy}/StatsController.cs (78%) create mode 100644 WebfrontCore/ViewComponents/TopPlayersViewComponent.cs rename {Plugins/Web/StatsWeb/Views/Stats => WebfrontCore/Views/Client}/Message/Find.cshtml (100%) rename {Plugins/Web/StatsWeb/Views/Stats => WebfrontCore/Views/Client}/Message/_Item.cshtml (100%) create mode 100644 WebfrontCore/Views/Client/Statistics/Advanced.cshtml create mode 100644 WebfrontCore/Views/Client/Statistics/Components/TopPlayers/_List.cshtml rename {Plugins/Web/StatsWeb/Views/Stats => WebfrontCore/Views/Client/Statistics}/Index.cshtml (97%) rename {Plugins/Web/StatsWeb/Views/Stats => WebfrontCore/Views/Client}/_MessageContext.cshtml (100%) rename {Plugins/Web/StatsWeb/Views/Stats => WebfrontCore/Views/Client}/_PenaltyInfo.cshtml (91%) create mode 100644 WebfrontCore/wwwroot/images/stats/hit_location_model.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_0.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_1.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_10.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_11.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_12.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_13.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_14.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_15.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_16.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_17.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_18.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_19.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_2.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_20.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_21.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_22.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_23.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_24.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_3.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_4.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_5.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_6.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_7.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_8.png create mode 100644 WebfrontCore/wwwroot/images/stats/ranks/rank_9.png create mode 100644 WebfrontCore/wwwroot/js/advanced_stats.js diff --git a/Application/Application.csproj b/Application/Application.csproj index 00b23616f..8e83b7887 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -56,7 +56,7 @@ - Always + PreserveNewest PreserveNewest diff --git a/Application/ApplicationManager.cs b/Application/ApplicationManager.cs index 7ecafa7be..35ff09ba9 100644 --- a/Application/ApplicationManager.cs +++ b/Application/ApplicationManager.cs @@ -6,7 +6,6 @@ using SharedLibraryCore; using SharedLibraryCore.Commands; using SharedLibraryCore.Configuration; using SharedLibraryCore.Configuration.Validation; -using SharedLibraryCore.Database; using SharedLibraryCore.Database.Models; using SharedLibraryCore.Exceptions; using SharedLibraryCore.Helpers; @@ -21,6 +20,8 @@ using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; +using Data.Abstractions; +using Data.Context; using IW4MAdmin.Application.Migration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -291,6 +292,15 @@ namespace IW4MAdmin.Application IsRunning = true; ExternalIPAddress = await Utilities.GetExternalIP(); + #region DATABASE + _logger.LogInformation("Beginning database migration sync"); + Console.WriteLine(_translationLookup["MANAGER_MIGRATION_START"]); + await ContextSeed.Seed(_serviceProvider.GetRequiredService(), _tokenSource.Token); + await DatabaseHousekeeping.RemoveOldRatings(_serviceProvider.GetRequiredService(), _tokenSource.Token); + _logger.LogInformation("Finished database migration sync"); + Console.WriteLine(_translationLookup["MANAGER_MIGRATION_END"]); + #endregion + #region PLUGINS foreach (var plugin in Plugins) { @@ -331,7 +341,7 @@ namespace IW4MAdmin.Application // copy over default config if it doesn't exist if (!_appConfig.Servers?.Any() ?? true) { - var defaultConfig = new BaseConfigurationHandler("DefaultSettings").Configuration(); + var defaultConfig = new BaseConfigurationHandler("DefaultSettings").Configuration(); //ConfigHandler.Set((ApplicationConfiguration)new ApplicationConfiguration().Generate()); //var newConfig = ConfigHandler.Configuration(); @@ -424,15 +434,6 @@ namespace IW4MAdmin.Application #endregion - #region DATABASE - _logger.LogInformation("Beginning database migration sync"); - Console.WriteLine(_translationLookup["MANAGER_MIGRATION_START"]); - await ContextSeed.Seed(_serviceProvider.GetRequiredService(), _tokenSource.Token); - await DatabaseHousekeeping.RemoveOldRatings(_serviceProvider.GetRequiredService(), _tokenSource.Token); - _logger.LogInformation("Finished database migration sync"); - Console.WriteLine(_translationLookup["MANAGER_MIGRATION_END"]); - #endregion - #region COMMANDS if (await ClientSvc.HasOwnerAsync(_tokenSource.Token)) { @@ -517,7 +518,7 @@ namespace IW4MAdmin.Application // add the start event for this server var e = new GameEvent() { - Type = GameEvent.EventType.Start, + Type = EventType.Start, Data = $"{ServerInstance.GameName} started", Owner = ServerInstance }; diff --git a/Application/DefaultSettings.json b/Application/DefaultSettings.json index 811565d16..eeff723d2 100644 --- a/Application/DefaultSettings.json +++ b/Application/DefaultSettings.json @@ -16,7 +16,13 @@ "Keep grenade launcher use to a minimum", "Balance teams at ALL times" ], - "DisallowedClientNames": [ "Unknown Soldier", "VickNet", "UnknownSoldier", "CHEATER", "Play77" ], + "DisallowedClientNames": [ + "Unknown Soldier", + "VickNet", + "UnknownSoldier", + "CHEATER", + "Play77" + ], "QuickMessages": [ { "Game": "IW4", @@ -248,222 +254,178 @@ "Alias": "Rust", "Name": "mp_rust" }, - { "Alias": "Terminal", "Name": "mp_terminal" }, - { "Alias": "Crash", "Name": "mp_crash" }, - { "Alias": "Afghan", "Name": "mp_afghan" }, - { "Alias": "Derail", "Name": "mp_derail" }, - { "Alias": "Estate", "Name": "mp_estate" }, - { "Alias": "Favela", "Name": "mp_favela" }, - { "Alias": "Highrise", "Name": "mp_highrise" }, - { "Alias": "Invasion", "Name": "mp_invasion" }, - { "Alias": "Karachi", "Name": "mp_checkpoint" }, - { "Alias": "Quarry", "Name": "mp_quarry" }, - { "Alias": "Rundown", "Name": "mp_rundown" }, - { "Alias": "Scrapyard", "Name": "mp_boneyard" }, - { "Alias": "Skidrow", "Name": "mp_nightshift" }, - { "Alias": "Sub Base", "Name": "mp_subbase" }, - { "Alias": "Underpass", "Name": "mp_underpass" }, - { "Alias": "Wasteland", "Name": "mp_brecourt" }, - { "Alias": "Overgrown", "Name": "mp_overgrown" }, - { "Alias": "Strike", "Name": "mp_strike" }, - { "Alias": "Vacant", "Name": "mp_vacant" }, - { "Alias": "Carnival", "Name": "mp_abandon" }, - { "Alias": "Trailer Park", "Name": "mp_trailerpark" }, - { "Alias": "Fuel", "Name": "mp_fuel2" }, - { "Alias": "Storm", "Name": "mp_storm" }, - { "Alias": "Bailout", "Name": "mp_complex" }, - { "Alias": "Salvage", "Name": "mp_compact" }, - { "Alias": "Nuketown", "Name": "mp_nuked" }, - { "Alias": "Test map", "Name": "iw4_credits" }, - { "Alias": "Killhouse", "Name": "mp_killhouse" }, - { "Alias": "Bog", "Name": "mp_bog_sh" }, - { "Alias": "Freighter", "Name": "mp_cargoship_sh" }, - { "Alias": "Cargoship", "Name": "mp_cargoship" }, - { "Alias": "Shipment", "Name": "mp_shipment" }, - { "Alias": "Shipment - Long", "Name": "mp_shipment_long" }, - { "Alias": "Rust - Long", "Name": "mp_rust_long" }, - { "Alias": "Firing Range", "Name": "mp_firingrange" }, - { "Alias": "Chemical Plant", "Name": "mp_storm_spring" }, - { "Alias": "Tropical Favela", "Name": "mp_fav_tropical" }, - { "Alias": "Tropical Estate", "Name": "mp_estate_tropical" }, - { "Alias": "Tropical Crash", "Name": "mp_crash_tropical" }, - { "Alias": "Forgotten City", "Name": "mp_bloc_sh" }, - { "Alias": "Crossfire", "Name": "mp_cross_fire" }, - { "Alias": "Bloc", "Name": "mp_bloc" }, - { "Alias": "Oilrig", "Name": "oilrig" }, - { "Name": "Village", "Alias": "co_hunted" @@ -519,7 +481,7 @@ }, { "Alias": "Havana", - "Name": "mp_cairo" + "Name": "mp_cairo" }, { "Alias": "Hazard", @@ -885,5 +847,79 @@ } ] } - ] + ], + "GameStrings": { + "IW4": { + "torso_upper": "Upper Torso", + "torso_lower": "Lower Torso", + "right_leg_upper": "Upper Right Leg", + "right_leg_lower": "Lower Right Leg", + "right_hand": "Right Hand", + "right_foot": "Right Foot", + "right_arm_upper": "Upper Right Arm", + "right_arm_lower": "Lower Right Arm", + "left_leg_upper": "Upper Left Leg", + "left_leg_lower": "Lower Left Leg", + "left_hand": "Left Hand", + "left_foot": "Left Foot", + "left_arm_upper": "Upper Left Arm", + "left_arm_lower": "Lower Left Arm", + "acog": "ACOG Sight", + "eotech": "Holographic Sight", + "fmj": "FMJ", + "gl": "Grenade Launcher", + "heartbeat": "Heartbeat Sensor", + "reflex": "Red Dot Sight", + "rof": "Rapid Fire", + "thermal": "Thermal", + "xmags": "Extended Mags", + "m4": "M4A1", + "m40a3": "M40A3", + "ak47": "AK-47", + "ak47classic": "AK-47 Classic", + "fn2000": "F2000", + "masada": "ACR", + "famas": "FAMAS", + "fal": "FAL", + "scar": "SCAR-H", + "tavor": "TAR-21", + "m16": "M16A4", + "mp5k": "MP5K", + "ump45": "UMP45", + "kriss": "Vector", + "uzi": "Mini-Uzi", + "rpd": "RPD", + "sa80": "L86 LSW", + "mg4": "MG4", + "aug": "AUG HBAR", + "cheytac": "Intervention", + "barrett": "Barrett .50cal", + "wa2000": "WA2000", + "m21": "M21 EBR", + "pp2000": "PP2000", + "glock": "G18", + "beretta": "M93 Raffica", + "tmp": "TMP", + "spas12": "SPAS-12", + "aa12": "AA-12", + "model1887": "Model 1887", + "usp": "USP .45", + "coltanaconda": ".44 Magnum", + "deserteagle": "Desert Eagle", + "deserteaglegold": "Desert Eagle Gold", + "at4": "AT4-HS", + "m79": "Thumper", + "rpg": "RPG-7", + "concussion": "Stun", + "throwingknife": "Throwing Knife", + "ffar": "Airstrike", + "pavelow": "Pave Low", + "cobra": "Attack Helicopter", + "ac130": "AC-130", + "remotemissile": "Predator Missile", + "artillery": "Precision Airstrike", + "player": "", + "attach": "" + } + } } diff --git a/Application/EventParsers/BaseEventParser.cs b/Application/EventParsers/BaseEventParser.cs index 2bb40fbbb..ddb3578c1 100644 --- a/Application/EventParsers/BaseEventParser.cs +++ b/Application/EventParsers/BaseEventParser.cs @@ -5,6 +5,7 @@ using SharedLibraryCore.Interfaces; using System; using System.Collections.Generic; using System.Linq; +using Data.Models; using Microsoft.Extensions.Logging; using static SharedLibraryCore.Server; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -310,7 +311,7 @@ namespace IW4MAdmin.Application.EventParsers } } - if (eventType.Contains("ExitLevel")) + if (eventType.Contains("ExitLevel") || eventType.Contains("ShutdownGame")) { return new GameEvent() { diff --git a/Application/Extensions/StartupExtensions.cs b/Application/Extensions/StartupExtensions.cs index b59a4b0be..4e0e2242a 100644 --- a/Application/Extensions/StartupExtensions.cs +++ b/Application/Extensions/StartupExtensions.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Runtime.InteropServices; +using Data.MigrationContext; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -10,7 +11,6 @@ using Serilog; using Serilog.Events; using SharedLibraryCore; using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database.MigrationContext; using ILogger = Serilog.ILogger; namespace IW4MAdmin.Application.Extensions @@ -32,13 +32,12 @@ namespace IW4MAdmin.Application.Extensions .ReadFrom.Configuration(configuration) .MinimumLevel.Override("Microsoft", LogEventLevel.Warning); - if (Utilities.IsDevelopment) { loggerConfig = loggerConfig.WriteTo.Console( outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Server} {Level:u3}] {Message:lj}{NewLine}{Exception}") - .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) .MinimumLevel.Debug(); } @@ -67,10 +66,10 @@ namespace IW4MAdmin.Application.Extensions {DataSource = Path.Join(currentPath, "Database", "Database.db")}; var connectionString = connectionStringBuilder.ToString(); - var builder = new DbContextOptionsBuilder() - .UseSqlite(connectionString); - - services.AddSingleton((DbContextOptions) builder.Options); + services.AddSingleton(sp => (DbContextOptions) new DbContextOptionsBuilder() + .UseSqlite(connectionString) + .UseLoggerFactory(sp.GetRequiredService()) + .EnableSensitiveDataLogging().Options); return services; } @@ -90,7 +89,11 @@ namespace IW4MAdmin.Application.Extensions services.AddSingleton(sp => (DbContextOptions) new DbContextOptionsBuilder() .UseNpgsql(appConfig.ConnectionString + (appendTimeout ? ";Command Timeout=0" : ""), - postgresqlOptions => postgresqlOptions.EnableRetryOnFailure()) + postgresqlOptions => + { + postgresqlOptions.EnableRetryOnFailure(); + postgresqlOptions.SetPostgresVersion(new Version("9.4")); + }) .UseLoggerFactory(sp.GetRequiredService()).Options); return services; default: diff --git a/Application/Factories/DatabaseContextFactory.cs b/Application/Factories/DatabaseContextFactory.cs index 87b33f8fb..604d8e6ce 100644 --- a/Application/Factories/DatabaseContextFactory.cs +++ b/Application/Factories/DatabaseContextFactory.cs @@ -1,9 +1,9 @@ using System; +using Data.Abstractions; +using Data.Context; +using Data.MigrationContext; using Microsoft.EntityFrameworkCore; using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database; -using SharedLibraryCore.Database.MigrationContext; -using SharedLibraryCore.Interfaces; namespace IW4MAdmin.Application.Factories { diff --git a/Application/Factories/GameServerInstanceFactory.cs b/Application/Factories/GameServerInstanceFactory.cs index 8cadbc73b..dbf4b82b3 100644 --- a/Application/Factories/GameServerInstanceFactory.cs +++ b/Application/Factories/GameServerInstanceFactory.cs @@ -1,4 +1,6 @@ using System; +using Data.Abstractions; +using Data.Models.Server; using Microsoft.Extensions.DependencyInjection; using SharedLibraryCore; using SharedLibraryCore.Configuration; @@ -37,7 +39,7 @@ namespace IW4MAdmin.Application.Factories /// public Server CreateServer(ServerConfiguration config, IManager manager) { - return new IW4MServer(config, _translationLookup, _metaService, _serviceProvider, _serviceProvider.GetRequiredService()); + return new IW4MServer(config, _translationLookup, _metaService, _serviceProvider, _serviceProvider.GetRequiredService(), _serviceProvider.GetRequiredService>()); } } } diff --git a/Application/Factories/ScriptCommandFactory.cs b/Application/Factories/ScriptCommandFactory.cs index d2e3a38bb..ce6ea26e2 100644 --- a/Application/Factories/ScriptCommandFactory.cs +++ b/Application/Factories/ScriptCommandFactory.cs @@ -6,9 +6,9 @@ using SharedLibraryCore.Interfaces; using System; using System.Collections.Generic; using System.Linq; +using Data.Models.Client; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using static SharedLibraryCore.Database.Models.EFClient; namespace IW4MAdmin.Application.Factories { @@ -32,7 +32,7 @@ namespace IW4MAdmin.Application.Factories public IManagerCommand CreateScriptCommand(string name, string alias, string description, string permission, bool isTargetRequired, IEnumerable<(string, bool)> args, Action executeAction) { - var permissionEnum = Enum.Parse(permission); + var permissionEnum = Enum.Parse(permission); var argsArray = args.Select(_arg => new CommandArgument { Name = _arg.Item1, diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index a58f026f0..c3d103be8 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -15,10 +15,14 @@ using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using Data.Abstractions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Serilog.Context; using static SharedLibraryCore.Database.Models.EFClient; +using Data.Models; +using Data.Models.Server; +using static Data.Models.Client.EFClient; namespace IW4MAdmin { @@ -34,13 +38,15 @@ namespace IW4MAdmin public int Id { get; private set; } private readonly IServiceProvider _serviceProvider; private readonly IClientNoticeMessageFormatter _messageFormatter; + private readonly ILookupCache _serverCache; public IW4MServer( ServerConfiguration serverConfiguration, ITranslationLookup lookup, IMetaService metaService, IServiceProvider serviceProvider, - IClientNoticeMessageFormatter messageFormatter) : base(serviceProvider.GetRequiredService>(), + IClientNoticeMessageFormatter messageFormatter, + ILookupCache serverCache) : base(serviceProvider.GetRequiredService>(), serviceProvider.GetRequiredService(), serverConfiguration, serviceProvider.GetRequiredService(), @@ -51,6 +57,7 @@ namespace IW4MAdmin _metaService = metaService; _serviceProvider = serviceProvider; _messageFormatter = messageFormatter; + _serverCache = serverCache; } public override async Task OnClientConnected(EFClient clientFromLog) @@ -250,6 +257,28 @@ namespace IW4MAdmin { ServerLogger.LogDebug("processing event of type {type}", E.Type); + if (E.Type == GameEvent.EventType.Start) + { + var existingServer = (await _serverCache + .FirstAsync(server => server.Id == EndPoint)); + + var serverId = await GetIdForServer(E.Owner); + + if (existingServer == null) + { + var server = new EFServer() + { + Port = Port, + EndPoint = ToString(), + ServerId = serverId, + GameName = (Reference.Game?)GameName, + HostName = Hostname + }; + + await _serverCache.AddAsync(server); + } + } + if (E.Type == GameEvent.EventType.ConnectionLost) { var exception = E.Extra as Exception; @@ -734,6 +763,25 @@ namespace IW4MAdmin updatedClients.ToList() }; } + + private async Task GetIdForServer(Server server) + { + if ($"{server.IP}:{server.Port.ToString()}" == "66.150.121.184:28965") + { + return 886229536; + } + + // todo: this is not stable and will need to be migrated again... + long id = HashCode.Combine(server.IP, server.Port); + id = id < 0 ? Math.Abs(id) : id; + + var serverId = (await _serverCache + .FirstAsync(_server => _server.ServerId == server.EndPoint || + _server.EndPoint == server.ToString() || + _server.ServerId == id))?.ServerId; + + return !serverId.HasValue ? id : serverId.Value; + } private void UpdateMap(string mapname) { @@ -1113,6 +1161,7 @@ namespace IW4MAdmin this, GenerateUriForLog(LogPath, ServerConfig.GameLogServerUrl?.AbsoluteUri), gameLogReaderFactory); + await _serverCache.InitializeAsync(); _ = Task.Run(() => LogEvent.PollForChanges()); if (!Utilities.IsDevelopment) diff --git a/Application/Main.cs b/Application/Main.cs index c09159adb..8bd61a378 100644 --- a/Application/Main.cs +++ b/Application/Main.cs @@ -23,10 +23,18 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using Data.Abstractions; +using Data.Helpers; using IW4MAdmin.Application.Extensions; using IW4MAdmin.Application.Localization; using Microsoft.Extensions.Logging; using ILogger = Microsoft.Extensions.Logging.ILogger; +using IW4MAdmin.Plugins.Stats.Client.Abstractions; +using IW4MAdmin.Plugins.Stats.Client; +using IW4MAdmin.Plugins.Stats.Client.Game; +using Stats.Client.Abstractions; +using Stats.Client; +using Stats.Helpers; namespace IW4MAdmin.Application { @@ -80,13 +88,13 @@ namespace IW4MAdmin.Application /// private static async Task LaunchAsync(string[] args) { - restart: + restart: ITranslationLookup translationLookup = null; var logger = BuildDefaultLogger(new ApplicationConfiguration()); Utilities.DefaultLogger = logger; IServiceCollection services = null; logger.LogInformation("Begin IW4MAdmin startup. Version is {version} {@args}", Version, args); - + try { // do any needed housekeeping file/folder migrations @@ -96,7 +104,7 @@ namespace IW4MAdmin.Application services = ConfigureServices(args); serviceProvider = services.BuildServiceProvider(); var versionChecker = serviceProvider.GetRequiredService(); - ServerManager = (ApplicationManager)serviceProvider.GetRequiredService(); + ServerManager = (ApplicationManager) serviceProvider.GetRequiredService(); translationLookup = serviceProvider.GetRequiredService(); await versionChecker.CheckVersion(); @@ -105,8 +113,12 @@ namespace IW4MAdmin.Application catch (Exception e) { - string failMessage = translationLookup == null ? "Failed to initialize IW4MAdmin" : translationLookup["MANAGER_INIT_FAIL"]; - string exitMessage = translationLookup == null ? "Press enter to exit..." : translationLookup["MANAGER_EXIT"]; + string failMessage = translationLookup == null + ? "Failed to initialize IW4MAdmin" + : translationLookup["MANAGER_INIT_FAIL"]; + string exitMessage = translationLookup == null + ? "Press enter to exit..." + : translationLookup["MANAGER_EXIT"]; logger.LogCritical(e, "Failed to initialize IW4MAdmin"); Console.WriteLine(failMessage); @@ -120,7 +132,8 @@ namespace IW4MAdmin.Application { if (translationLookup != null) { - Console.WriteLine(translationLookup[configException.Message].FormatExt(configException.ConfigurationFileName)); + Console.WriteLine(translationLookup[configException.Message] + .FormatExt(configException.ConfigurationFileName)); } foreach (string error in configException.Errors) @@ -148,7 +161,9 @@ namespace IW4MAdmin.Application catch (Exception e) { logger.LogCritical(e, "Failed to launch IW4MAdmin"); - string failMessage = translationLookup == null ? "Failed to launch IW4MAdmin" : translationLookup["MANAGER_INIT_FAIL"]; + string failMessage = translationLookup == null + ? "Failed to launch IW4MAdmin" + : translationLookup["MANAGER_INIT_FAIL"]; Console.WriteLine($"{failMessage}: {e.GetExceptionInfo()}"); } @@ -166,9 +181,9 @@ namespace IW4MAdmin.Application /// private static async Task RunApplicationTasksAsync(ILogger logger, IServiceCollection services) { - var webfrontTask = ServerManager.GetApplicationSettings().Configuration().EnableWebFront ? - WebfrontCore.Program.Init(ServerManager, serviceProvider, services, ServerManager.CancellationToken) : - Task.CompletedTask; + var webfrontTask = ServerManager.GetApplicationSettings().Configuration().EnableWebFront + ? WebfrontCore.Program.Init(ServerManager, serviceProvider, services, ServerManager.CancellationToken) + : Task.CompletedTask; // we want to run this one on a manual thread instead of letting the thread pool handle it, // because we can't exit early from waiting on console input, and it prevents us from restarting @@ -179,7 +194,8 @@ namespace IW4MAdmin.Application { ServerManager.Start(), webfrontTask, - serviceProvider.GetRequiredService().RunUploadStatus(ServerManager.CancellationToken) + serviceProvider.GetRequiredService() + .RunUploadStatus(ServerManager.CancellationToken) }; logger.LogDebug("Starting webfront and input tasks"); @@ -231,11 +247,12 @@ namespace IW4MAdmin.Application } } catch (OperationCanceledException) - { } + { + } } - private static IServiceCollection HandlePluginRegistration(ApplicationConfiguration appConfig, - IServiceCollection serviceCollection, + private static IServiceCollection HandlePluginRegistration(ApplicationConfiguration appConfig, + IServiceCollection serviceCollection, IMasterApi masterApi) { var defaultLogger = BuildDefaultLogger(appConfig); @@ -248,33 +265,44 @@ namespace IW4MAdmin.Application .BuildServiceProvider(); var pluginImporter = pluginServiceProvider.GetRequiredService(); - + // we need to register the rest client with regular collection serviceCollection.AddSingleton(masterApi); - + // register the native commands foreach (var commandType in typeof(SharedLibraryCore.Commands.QuitCommand).Assembly.GetTypes() - .Where(_command => _command.BaseType == typeof(Command))) + .Where(_command => _command.BaseType == typeof(Command))) { defaultLogger.LogDebug("Registered native command type {name}", commandType.Name); serviceCollection.AddSingleton(typeof(IManagerCommand), commandType); } // register the plugin implementations - var pluginImplementations = pluginImporter.DiscoverAssemblyPluginImplementations(); - foreach (var pluginType in pluginImplementations.Item1) + var (plugins, commands, configurations) = pluginImporter.DiscoverAssemblyPluginImplementations(); + foreach (var pluginType in plugins) { defaultLogger.LogDebug("Registered plugin type {name}", pluginType.FullName); serviceCollection.AddSingleton(typeof(IPlugin), pluginType); } // register the plugin commands - foreach (var commandType in pluginImplementations.Item2) + foreach (var commandType in commands) { defaultLogger.LogDebug("Registered plugin command type {name}", commandType.FullName); serviceCollection.AddSingleton(typeof(IManagerCommand), commandType); } + foreach (var configurationType in configurations) + { + defaultLogger.LogDebug("Registered plugin config type {name}", configurationType.Name); + var configInstance = (IBaseConfiguration) Activator.CreateInstance(configurationType); + var handlerType = typeof(BaseConfigurationHandler<>).MakeGenericType(configurationType); + var handlerInstance = Activator.CreateInstance(handlerType, new[] {configInstance.Name()}); + var genericInterfaceType = typeof(IConfigurationHandler<>).MakeGenericType(configurationType); + + serviceCollection.AddSingleton(genericInterfaceType, handlerInstance); + } + // register any script plugins foreach (var scriptPlugin in pluginImporter.DiscoverScriptPlugins()) { @@ -284,10 +312,9 @@ namespace IW4MAdmin.Application // register any eventable types foreach (var assemblyType in typeof(Program).Assembly.GetTypes() .Where(_asmType => typeof(IRegisterEvent).IsAssignableFrom(_asmType)) - .Union(pluginImplementations - .Item1.SelectMany(_asm => _asm.Assembly.GetTypes()) - .Distinct() - .Where(_asmType => typeof(IRegisterEvent).IsAssignableFrom(_asmType)))) + .Union(plugins.SelectMany(_asm => _asm.Assembly.GetTypes()) + .Distinct() + .Where(_asmType => typeof(IRegisterEvent).IsAssignableFrom(_asmType)))) { var instance = Activator.CreateInstance(assemblyType) as IRegisterEvent; serviceCollection.AddSingleton(instance); @@ -295,7 +322,7 @@ namespace IW4MAdmin.Application return serviceCollection; } - + /// /// Configures the dependency injection services @@ -310,7 +337,7 @@ namespace IW4MAdmin.Application ? new Uri("http://127.0.0.1:8080") : appConfig?.MasterUrl ?? new ApplicationConfiguration().MasterUrl; var masterRestClient = RestClient.For(masterUri); - var translationLookup = Configure.Initialize(Utilities.DefaultLogger, masterRestClient, appConfig); + var translationLookup = Configure.Initialize(Utilities.DefaultLogger, masterRestClient, appConfig); if (appConfig == null) { @@ -327,13 +354,14 @@ namespace IW4MAdmin.Application Utilities.PermissionLevelOverrides.Add(key, value); } } - + // build the dependency list HandlePluginRegistration(appConfig, serviceCollection, masterRestClient); serviceCollection .AddBaseLogger(appConfig) .AddSingleton(_serviceProvider => serviceCollection) + .AddSingleton, BaseConfigurationHandler>() .AddSingleton((IConfigurationHandler) appConfigHandler) .AddSingleton( new BaseConfigurationHandler("CommandConfiguration") as @@ -372,6 +400,12 @@ namespace IW4MAdmin.Application .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(typeof(ILookupCache<>), typeof(LookupCache<>)) + .AddSingleton(typeof(IDataValueCache<,>), typeof(DataValueCache<,>)) .AddSingleton(translationLookup) .AddDatabaseContextOptions(appConfig); @@ -386,7 +420,7 @@ namespace IW4MAdmin.Application return serviceCollection; } - + private static ILogger BuildDefaultLogger(ApplicationConfiguration appConfig) { var collection = new ServiceCollection() @@ -396,4 +430,4 @@ namespace IW4MAdmin.Application return collection.GetRequiredService>(); } } -} +} \ No newline at end of file diff --git a/Application/Meta/AdministeredPenaltyResourceQueryHelper.cs b/Application/Meta/AdministeredPenaltyResourceQueryHelper.cs index 616e19dea..da9f80155 100644 --- a/Application/Meta/AdministeredPenaltyResourceQueryHelper.cs +++ b/Application/Meta/AdministeredPenaltyResourceQueryHelper.cs @@ -1,8 +1,9 @@ using System.Linq; using System.Threading.Tasks; +using Data.Abstractions; +using Data.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; -using SharedLibraryCore.Database.Models; using SharedLibraryCore.Dtos.Meta.Responses; using SharedLibraryCore.Helpers; using SharedLibraryCore.Interfaces; diff --git a/Application/Meta/ReceivedPenaltyResourceQueryHelper.cs b/Application/Meta/ReceivedPenaltyResourceQueryHelper.cs index f63ad0687..d9c8bbdd0 100644 --- a/Application/Meta/ReceivedPenaltyResourceQueryHelper.cs +++ b/Application/Meta/ReceivedPenaltyResourceQueryHelper.cs @@ -1,10 +1,10 @@ -using System; -using System.Linq; +using System.Linq; using System.Threading.Tasks; +using Data.Abstractions; +using Data.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using SharedLibraryCore; -using SharedLibraryCore.Database.Models; using SharedLibraryCore.Dtos.Meta.Responses; using SharedLibraryCore.Helpers; using SharedLibraryCore.Interfaces; diff --git a/Application/Meta/UpdatedAliasResourceQueryHelper.cs b/Application/Meta/UpdatedAliasResourceQueryHelper.cs index 4765cbf0b..8dbce96e6 100644 --- a/Application/Meta/UpdatedAliasResourceQueryHelper.cs +++ b/Application/Meta/UpdatedAliasResourceQueryHelper.cs @@ -6,6 +6,7 @@ using SharedLibraryCore.Interfaces; using SharedLibraryCore.QueryHelper; using System.Linq; using System.Threading.Tasks; +using Data.Abstractions; using Microsoft.Extensions.Logging; using ILogger = Microsoft.Extensions.Logging.ILogger; diff --git a/Application/Migration/DatabaseHousekeeping.cs b/Application/Migration/DatabaseHousekeeping.cs index eec18006c..d4331dd91 100644 --- a/Application/Migration/DatabaseHousekeeping.cs +++ b/Application/Migration/DatabaseHousekeeping.cs @@ -2,7 +2,8 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using IW4MAdmin.Plugins.Stats.Models; +using Data.Abstractions; +using Data.Models.Client.Stats; using SharedLibraryCore.Database; using SharedLibraryCore.Interfaces; diff --git a/Application/Misc/BaseConfigurationHandler.cs b/Application/Misc/BaseConfigurationHandler.cs index 23d8295b8..9aaedb061 100644 --- a/Application/Misc/BaseConfigurationHandler.cs +++ b/Application/Misc/BaseConfigurationHandler.cs @@ -22,6 +22,11 @@ namespace IW4MAdmin.Application.Misc Build(); } + public BaseConfigurationHandler() : this(typeof(T).Name) + { + + } + public string FileName { get; } public void Build() diff --git a/Application/Misc/ClientNoticeMessageFormatter.cs b/Application/Misc/ClientNoticeMessageFormatter.cs index d4885c6d2..3ab5c1209 100644 --- a/Application/Misc/ClientNoticeMessageFormatter.cs +++ b/Application/Misc/ClientNoticeMessageFormatter.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Data.Models; using SharedLibraryCore; using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database.Models; using SharedLibraryCore.Interfaces; namespace IW4MAdmin.Application.Misc diff --git a/Application/Misc/MetaService.cs b/Application/Misc/MetaService.cs index 7de83bf23..47f0bd1d1 100644 --- a/Application/Misc/MetaService.cs +++ b/Application/Misc/MetaService.cs @@ -7,8 +7,10 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Data.Abstractions; using Microsoft.Extensions.Logging; using ILogger = Microsoft.Extensions.Logging.ILogger; +using Data.Models; namespace IW4MAdmin.Application.Misc { diff --git a/Application/Misc/PluginImporter.cs b/Application/Misc/PluginImporter.cs index 79318b402..453b6b987 100644 --- a/Application/Misc/PluginImporter.cs +++ b/Application/Misc/PluginImporter.cs @@ -63,11 +63,12 @@ namespace IW4MAdmin.Application.Misc /// discovers all the C# assembly plugins and commands /// /// - public (IEnumerable, IEnumerable) DiscoverAssemblyPluginImplementations() + public (IEnumerable, IEnumerable, IEnumerable) DiscoverAssemblyPluginImplementations() { - string pluginDir = $"{Utilities.OperatingDirectory}{PLUGIN_DIR}{Path.DirectorySeparatorChar}"; + var pluginDir = $"{Utilities.OperatingDirectory}{PLUGIN_DIR}{Path.DirectorySeparatorChar}"; var pluginTypes = Enumerable.Empty(); var commandTypes = Enumerable.Empty(); + var configurationTypes = Enumerable.Empty(); if (Directory.Exists(pluginDir)) { @@ -92,10 +93,17 @@ namespace IW4MAdmin.Application.Misc .Where(_assemblyType => _assemblyType.IsClass && _assemblyType.BaseType == typeof(Command)); _logger.LogDebug("Discovered {count} plugin commands", commandTypes.Count()); + + configurationTypes = assemblies + .SelectMany(asm => asm.GetTypes()) + .Where(asmType => + asmType.IsClass && asmType.GetInterface(nameof(IBaseConfiguration), false) != null); + + _logger.LogDebug("Discovered {count} configuration implementations", configurationTypes.Count()); } } - return (pluginTypes, commandTypes); + return (pluginTypes, commandTypes, configurationTypes); } private IEnumerable GetRemoteAssemblies() diff --git a/Application/Misc/ScriptCommand.cs b/Application/Misc/ScriptCommand.cs index b4fe43a12..26d756e01 100644 --- a/Application/Misc/ScriptCommand.cs +++ b/Application/Misc/ScriptCommand.cs @@ -4,6 +4,7 @@ using SharedLibraryCore.Configuration; using SharedLibraryCore.Interfaces; using System; using System.Threading.Tasks; +using Data.Models.Client; using Microsoft.Extensions.Logging; using static SharedLibraryCore.Database.Models.EFClient; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -18,7 +19,7 @@ namespace IW4MAdmin.Application.Misc private readonly Action _executeAction; private readonly ILogger _logger; - public ScriptCommand(string name, string alias, string description, bool isTargetRequired, Permission permission, + public ScriptCommand(string name, string alias, string description, bool isTargetRequired, EFClient.Permission permission, CommandArgument[] args, Action executeAction, CommandConfiguration config, ITranslationLookup layout, ILogger logger) : base(config, layout) { diff --git a/Application/Misc/SerializationHelpers.cs b/Application/Misc/SerializationHelpers.cs index d2753dafa..bfb7e0b94 100644 --- a/Application/Misc/SerializationHelpers.cs +++ b/Application/Misc/SerializationHelpers.cs @@ -4,6 +4,7 @@ using SharedLibraryCore; using SharedLibraryCore.Database.Models; using System; using System.Net; +using Data.Models; using static SharedLibraryCore.Database.Models.EFClient; using static SharedLibraryCore.GameEvent; diff --git a/Application/RconParsers/BaseRConParser.cs b/Application/RconParsers/BaseRConParser.cs index 45b763c98..da5c3badc 100644 --- a/Application/RconParsers/BaseRConParser.cs +++ b/Application/RconParsers/BaseRConParser.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Data.Models; using Microsoft.Extensions.Logging; using static SharedLibraryCore.Server; using ILogger = Microsoft.Extensions.Logging.ILogger; diff --git a/Data/Abstractions/IDataValueCache.cs b/Data/Abstractions/IDataValueCache.cs new file mode 100644 index 000000000..a76bd28f7 --- /dev/null +++ b/Data/Abstractions/IDataValueCache.cs @@ -0,0 +1,12 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; + +namespace Data.Abstractions +{ + public interface IDataValueCache where T : class + { + void SetCacheItem(Func, Task> itemGetter, string keyName, TimeSpan? expirationTime = null); + Task GetCacheItem(string keyName); + } +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IDatabaseContextFactory.cs b/Data/Abstractions/IDatabaseContextFactory.cs similarity index 86% rename from SharedLibraryCore/Interfaces/IDatabaseContextFactory.cs rename to Data/Abstractions/IDatabaseContextFactory.cs index 179f4afb6..cd455c148 100644 --- a/SharedLibraryCore/Interfaces/IDatabaseContextFactory.cs +++ b/Data/Abstractions/IDatabaseContextFactory.cs @@ -1,6 +1,6 @@ -using SharedLibraryCore.Database; +using Data.Context; -namespace SharedLibraryCore.Interfaces +namespace Data.Abstractions { /// /// describes the capabilities of the database context factory diff --git a/Data/Abstractions/ILookupCache.cs b/Data/Abstractions/ILookupCache.cs new file mode 100644 index 000000000..70b275720 --- /dev/null +++ b/Data/Abstractions/ILookupCache.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Data.Abstractions +{ + public interface ILookupCache where T : class + { + Task InitializeAsync(); + Task AddAsync(T item); + Task FirstAsync(Func query); + IEnumerable GetAll(); + } +} diff --git a/SharedLibraryCore/Interfaces/IPropertyExtender.cs b/Data/Abstractions/IPropertyExtender.cs similarity index 94% rename from SharedLibraryCore/Interfaces/IPropertyExtender.cs rename to Data/Abstractions/IPropertyExtender.cs index 7ca1df8ce..33275498f 100644 --- a/SharedLibraryCore/Interfaces/IPropertyExtender.cs +++ b/Data/Abstractions/IPropertyExtender.cs @@ -1,4 +1,4 @@ -namespace SharedLibraryCore.Interfaces +namespace Data.Abstractions { /// /// describes the capability of extending properties by name diff --git a/Data/Abstractions/IUniqueId.cs b/Data/Abstractions/IUniqueId.cs new file mode 100644 index 000000000..65b73fb62 --- /dev/null +++ b/Data/Abstractions/IUniqueId.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Data.Abstractions +{ + public interface IUniqueId + { + [NotMapped] + long Id { get; } + + [NotMapped] + string Value { get; } + } +} diff --git a/SharedLibraryCore/Database/ContextSeed.cs b/Data/Context/ContextSeed.cs similarity index 82% rename from SharedLibraryCore/Database/ContextSeed.cs rename to Data/Context/ContextSeed.cs index 770230fbc..c9acf06f9 100644 --- a/SharedLibraryCore/Database/ContextSeed.cs +++ b/Data/Context/ContextSeed.cs @@ -1,13 +1,12 @@ -using Microsoft.EntityFrameworkCore; -using SharedLibraryCore.Database.Models; -using System; -using System.Linq; +using System; using System.Threading; using System.Threading.Tasks; -using SharedLibraryCore.Interfaces; -using static SharedLibraryCore.Database.Models.EFClient; +using Data.Abstractions; +using Data.Models; +using Data.Models.Client; +using Microsoft.EntityFrameworkCore; -namespace SharedLibraryCore.Database +namespace Data.Context { public static class ContextSeed { @@ -30,7 +29,7 @@ namespace SharedLibraryCore.Database Connections = 0, FirstConnection = DateTime.UtcNow, LastConnection = DateTime.UtcNow, - Level = Permission.Console, + Level = EFClient.Permission.Console, Masked = true, NetworkId = 0, AliasLink = link, diff --git a/Data/Context/DatabaseContext.cs b/Data/Context/DatabaseContext.cs new file mode 100644 index 000000000..041912ed0 --- /dev/null +++ b/Data/Context/DatabaseContext.cs @@ -0,0 +1,135 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Threading; +using System.Threading.Tasks; +using Data.Extensions; +using Data.Models; +using Data.Models.Client; +using Data.Models.Client.Stats; +using Data.Models.Client.Stats.Reference; +using Data.Models.Server; + +namespace Data.Context +{ + public abstract class DatabaseContext : DbContext + { + public DbSet Clients { get; set; } + public DbSet Aliases { get; set; } + public DbSet AliasLinks { get; set; } + public DbSet Penalties { get; set; } + public DbSet EFMeta { get; set; } + public DbSet EFChangeHistory { get; set; } + + #region STATS + + public DbSet Vector3s { get; set; } + public DbSet SnapshotVector3s { get; set; } + public DbSet ACSnapshots { get; set; } + public DbSet Servers { get; set; } + public DbSet ClientKills { get; set; } + public DbSet ClientMessages { get; set; } + + public DbSet ServerStatistics { get; set; } + public DbSet HitLocations { get; set; } + public DbSet HitStatistics { get; set; } + public DbSet Weapons { get; set; } + public DbSet WeaponAttachments { get; set; } + public DbSet Maps { get; set; } + + #endregion + + private void SetAuditColumns() + { + return; + } + + public DatabaseContext() + { + if (!MigrationExtensions.IsMigration) + { + throw new InvalidOperationException(); + } + } + + public DatabaseContext(DbContextOptions options) : base(options) + { + } + + protected DatabaseContext(DbContextOptions options) : base(options) + { + } + + public override Task SaveChangesAsync(bool acceptAllChangesOnSuccess, + CancellationToken cancellationToken = default) + { + SetAuditColumns(); + return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); + } + + public override int SaveChanges() + { + SetAuditColumns(); + return base.SaveChanges(); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + // make network id unique + modelBuilder.Entity(entity => { entity.HasIndex(e => e.NetworkId).IsUnique(); }); + + modelBuilder.Entity(entity => + { + entity.HasOne(p => p.Offender) + .WithMany(c => c.ReceivedPenalties) + .HasForeignKey(c => c.OffenderId) + .OnDelete(DeleteBehavior.Restrict); + + entity.HasOne(p => p.Punisher) + .WithMany(p => p.AdministeredPenalties) + .HasForeignKey(c => c.PunisherId) + .OnDelete(DeleteBehavior.Restrict); + + entity.Property(p => p.Expires) + .IsRequired(false); + }); + + modelBuilder.Entity(entity => + { + entity.HasMany(e => e.Children) + .WithOne(a => a.Link) + .HasForeignKey(k => k.LinkId) + .OnDelete(DeleteBehavior.Restrict); + }); + + 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); + ent.Property(_alias => _alias.SearchableName).HasMaxLength(24); + ent.HasIndex(_alias => _alias.SearchableName); + ent.HasIndex(_alias => new {_alias.Name, _alias.IPAddress}).IsUnique(); + }); + + modelBuilder.Entity(ent => + { + ent.HasIndex(_meta => _meta.Key); + ent.HasIndex(_meta => _meta.LinkedMetaId); + ent.HasOne(_meta => _meta.LinkedMeta) + .WithMany() + .OnDelete(DeleteBehavior.SetNull); + }); + + // force full name for database conversion + modelBuilder.Entity().ToTable("EFClients"); + modelBuilder.Entity().ToTable("EFAlias"); + modelBuilder.Entity().ToTable("EFAliasLinks"); + modelBuilder.Entity().ToTable("EFPenalties"); + + Models.Configuration.StatsModelConfiguration.Configure(modelBuilder); + + base.OnModelCreating(modelBuilder); + } + } +} \ No newline at end of file diff --git a/Data/Data.csproj b/Data/Data.csproj new file mode 100644 index 000000000..671d685a0 --- /dev/null +++ b/Data/Data.csproj @@ -0,0 +1,82 @@ + + + + netcoreapp3.1 + Debug;Release;Prerelease + AnyCPU + true + RaidMax.IW4MAdmin.Data + RaidMax.IW4MAdmin.Data + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles + + + + + + + + + + + + diff --git a/Data/Extensions/Extensions.cs b/Data/Extensions/Extensions.cs new file mode 100644 index 000000000..0ca9d9fdd --- /dev/null +++ b/Data/Extensions/Extensions.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; + +namespace Data.Extensions +{ + public static class MigrationExtensions + { + public static bool IsMigration => Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Migration"; + } +} \ No newline at end of file diff --git a/Data/Helpers/DataValueCache.cs b/Data/Helpers/DataValueCache.cs new file mode 100644 index 000000000..f6ac89376 --- /dev/null +++ b/Data/Helpers/DataValueCache.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Data.Abstractions; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +namespace Data.Helpers +{ + public class DataValueCache : IDataValueCache where T : class + { + private readonly ILogger _logger; + private readonly IDatabaseContextFactory _contextFactory; + private readonly Dictionary _cacheStates = new Dictionary(); + private const int DefaultExpireMinutes = 15; + + private class CacheState + { + public string Key { get; set; } + public DateTime LastRetrieval { get; set; } + public TimeSpan ExpirationTime { get; set; } + public Func, Task> Getter { get; set; } + public V Value { get; set; } + public bool IsExpired => (DateTime.Now - LastRetrieval.Add(ExpirationTime)).TotalSeconds > 0; + } + + public DataValueCache(ILogger> logger, IDatabaseContextFactory contextFactory) + { + _logger = logger; + _contextFactory = contextFactory; + } + + public void SetCacheItem(Func, Task> getter, string key, TimeSpan? expirationTime = null) + { + if (_cacheStates.ContainsKey(key)) + { + _logger.LogDebug("Cache key {key} is already added", key); + return; + } + + var state = new CacheState() + { + Key = key, + Getter = getter, + ExpirationTime = expirationTime ?? TimeSpan.FromMinutes(DefaultExpireMinutes) + }; + + _cacheStates.Add(key, state); + } + + public async Task GetCacheItem(string keyName) + { + if (!_cacheStates.ContainsKey(keyName)) + { + throw new ArgumentException("No cache found for key {key}", keyName); + } + + var state = _cacheStates[keyName]; + + if (state.IsExpired) + { + await RunCacheUpdate(state); + } + + return state.Value; + } + + private async Task RunCacheUpdate(CacheState state) + { + try + { + await using var context = _contextFactory.CreateContext(false); + var set = context.Set(); + var value = await state.Getter(set); + state.Value = value; + state.LastRetrieval = DateTime.Now; + } + catch (Exception ex) + { + _logger.LogError(ex, "Could not get cached value for {key}", state.Key); + } + } + } +} \ No newline at end of file diff --git a/Data/Helpers/LookupCache.cs b/Data/Helpers/LookupCache.cs new file mode 100644 index 000000000..fd45b8bcb --- /dev/null +++ b/Data/Helpers/LookupCache.cs @@ -0,0 +1,114 @@ +using Data.Abstractions; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ILogger = Microsoft.Extensions.Logging.ILogger; + +namespace Data.Helpers +{ + public class LookupCache : ILookupCache where T : class, IUniqueId + { + private readonly ILogger _logger; + private readonly IDatabaseContextFactory _contextFactory; + private Dictionary _cachedItems; + private readonly SemaphoreSlim _onOperation = new SemaphoreSlim(1, 1); + + public LookupCache(ILogger> logger, IDatabaseContextFactory contextFactory) + { + _logger = logger; + _contextFactory = contextFactory; + } + + public async Task AddAsync(T item) + { + await _onOperation.WaitAsync(); + T existingItem = null; + + if (_cachedItems.ContainsKey(item.Id)) + { + existingItem = _cachedItems[item.Id]; + } + + if (existingItem != null) + { + _logger.LogDebug("Cached item already added for {type} {id} {value}", typeof(T).Name, item.Id, + item.Value); + _onOperation.Release(); + return existingItem; + } + + try + { + _logger.LogDebug("Adding new {type} with {id} {value}", typeof(T).Name, item.Id, item.Value); + await using var context = _contextFactory.CreateContext(); + context.Set().Add(item); + await context.SaveChangesAsync(); + _cachedItems.Add(item.Id, item); + return item; + } + catch (Exception ex) + { + _logger.LogError(ex, "Could not add item to cache for {type}", typeof(T).Name); + throw new Exception("Could not add item to cache"); + } + finally + { + if (_onOperation.CurrentCount == 0) + { + _onOperation.Release(); + } + } + } + + public async Task FirstAsync(Func query) + { + await _onOperation.WaitAsync(); + + try + { + var cachedResult = _cachedItems.Values.Where(query); + + if (cachedResult.Any()) + { + return cachedResult.FirstOrDefault(); + } + } + + catch + { + } + + finally + { + if (_onOperation.CurrentCount == 0) + { + _onOperation.Release(1); + } + } + + return null; + } + + public IEnumerable GetAll() + { + return _cachedItems.Values; + } + + public async Task InitializeAsync() + { + try + { + await using var context = _contextFactory.CreateContext(); + _cachedItems = await context.Set().ToDictionaryAsync(item => item.Id); + } + catch (Exception ex) + { + _logger.LogError(ex, "Could not initialize caching for {cacheType}", typeof(T).Name); + } + } + } +} \ No newline at end of file diff --git a/SharedLibraryCore/Database/MigrationContext/MySqlDatabaseContext.cs b/Data/MigrationContext/MySqlDatabaseContext.cs similarity index 80% rename from SharedLibraryCore/Database/MigrationContext/MySqlDatabaseContext.cs rename to Data/MigrationContext/MySqlDatabaseContext.cs index 88fd9cef3..785869c82 100644 --- a/SharedLibraryCore/Database/MigrationContext/MySqlDatabaseContext.cs +++ b/Data/MigrationContext/MySqlDatabaseContext.cs @@ -1,13 +1,15 @@ using System; +using Data.Context; +using Data.Extensions; using Microsoft.EntityFrameworkCore; -namespace SharedLibraryCore.Database.MigrationContext +namespace Data.MigrationContext { public class MySqlDatabaseContext : DatabaseContext { public MySqlDatabaseContext() { - if (!Utilities.IsMigration) + if (!MigrationExtensions.IsMigration) { throw new InvalidOperationException(); } @@ -20,7 +22,7 @@ namespace SharedLibraryCore.Database.MigrationContext protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - if (Utilities.IsMigration) + if (MigrationExtensions.IsMigration) { optionsBuilder.UseMySql("Server=127.0.0.1;Database=IW4MAdmin_Migration;Uid=root;Pwd=password;") .EnableDetailedErrors(true) diff --git a/SharedLibraryCore/Database/MigrationContext/PostgresqlDatabaseContext.cs b/Data/MigrationContext/PostgresqlDatabaseContext.cs similarity index 70% rename from SharedLibraryCore/Database/MigrationContext/PostgresqlDatabaseContext.cs rename to Data/MigrationContext/PostgresqlDatabaseContext.cs index cf6c57b8d..d3df902e2 100644 --- a/SharedLibraryCore/Database/MigrationContext/PostgresqlDatabaseContext.cs +++ b/Data/MigrationContext/PostgresqlDatabaseContext.cs @@ -1,33 +1,34 @@ using System; +using Data.Context; +using Data.Extensions; using Microsoft.EntityFrameworkCore; -namespace SharedLibraryCore.Database.MigrationContext +namespace Data.MigrationContext { public class PostgresqlDatabaseContext : DatabaseContext { public PostgresqlDatabaseContext() { - if (!Utilities.IsMigration) + if (!MigrationExtensions.IsMigration) { throw new InvalidOperationException(); } } - + public PostgresqlDatabaseContext(DbContextOptions options) : base(options) { - } - + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - if (Utilities.IsMigration) + if (MigrationExtensions.IsMigration) { optionsBuilder.UseNpgsql( - "Host=127.0.0.1;Database=IW4MAdmin_Migration;Username=postgres;Password=password;") + "Host=127.0.0.1;Database=IW4MAdmin_Migration;Username=postgres;Password=password;", + options => options.SetPostgresVersion(new Version("9.4"))) .EnableDetailedErrors(true) .EnableSensitiveDataLogging(true); } } - } } \ No newline at end of file diff --git a/SharedLibraryCore/Database/MigrationContext/SqliteDatabaseContext.cs b/Data/MigrationContext/SqliteDatabaseContext.cs similarity index 80% rename from SharedLibraryCore/Database/MigrationContext/SqliteDatabaseContext.cs rename to Data/MigrationContext/SqliteDatabaseContext.cs index 89a8274cc..22421a7e7 100644 --- a/SharedLibraryCore/Database/MigrationContext/SqliteDatabaseContext.cs +++ b/Data/MigrationContext/SqliteDatabaseContext.cs @@ -1,13 +1,15 @@ using System; +using Data.Context; +using Data.Extensions; using Microsoft.EntityFrameworkCore; -namespace SharedLibraryCore.Database.MigrationContext +namespace Data.MigrationContext { public class SqliteDatabaseContext : DatabaseContext { public SqliteDatabaseContext() { - if (!Utilities.IsMigration) + if (!MigrationExtensions.IsMigration) { throw new InvalidOperationException(); } @@ -20,7 +22,7 @@ namespace SharedLibraryCore.Database.MigrationContext protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - if (Utilities.IsMigration) + if (MigrationExtensions.IsMigration) { optionsBuilder.UseSqlite("Data Source=IW4MAdmin_Migration.db") .EnableDetailedErrors(true) diff --git a/SharedLibraryCore/Migrations/MySql/20180409183408_InitialCreate.Designer.cs b/Data/Migrations/MySql/20180409183408_InitialCreate.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180409183408_InitialCreate.Designer.cs rename to Data/Migrations/MySql/20180409183408_InitialCreate.Designer.cs index 686e0fb5c..72836bff6 100644 --- a/SharedLibraryCore/Migrations/MySql/20180409183408_InitialCreate.Designer.cs +++ b/Data/Migrations/MySql/20180409183408_InitialCreate.Designer.cs @@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database; -using SharedLibraryCore.Database.MigrationContext; +using Data; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180409183408_InitialCreate")] diff --git a/SharedLibraryCore/Migrations/MySql/20180409183408_InitialCreate.cs b/Data/Migrations/MySql/20180409183408_InitialCreate.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180409183408_InitialCreate.cs rename to Data/Migrations/MySql/20180409183408_InitialCreate.cs index 32e488c7e..4e4ec5300 100644 --- a/SharedLibraryCore/Migrations/MySql/20180409183408_InitialCreate.cs +++ b/Data/Migrations/MySql/20180409183408_InitialCreate.cs @@ -4,7 +4,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class InitialCreate : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180502195450_Update.Designer.cs b/Data/Migrations/MySql/20180502195450_Update.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180502195450_Update.Designer.cs rename to Data/Migrations/MySql/20180502195450_Update.Designer.cs index 576eeed1b..200a84a19 100644 --- a/SharedLibraryCore/Migrations/MySql/20180502195450_Update.Designer.cs +++ b/Data/Migrations/MySql/20180502195450_Update.Designer.cs @@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database; -using SharedLibraryCore.Database.MigrationContext; +using Data; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180502195450_Update")] diff --git a/SharedLibraryCore/Migrations/MySql/20180502195450_Update.cs b/Data/Migrations/MySql/20180502195450_Update.cs similarity index 95% rename from SharedLibraryCore/Migrations/MySql/20180502195450_Update.cs rename to Data/Migrations/MySql/20180502195450_Update.cs index fc7795f51..f331a64d1 100644 --- a/SharedLibraryCore/Migrations/MySql/20180502195450_Update.cs +++ b/Data/Migrations/MySql/20180502195450_Update.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class Update : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180516023249_AddEloField.Designer.cs b/Data/Migrations/MySql/20180516023249_AddEloField.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180516023249_AddEloField.Designer.cs rename to Data/Migrations/MySql/20180516023249_AddEloField.Designer.cs index 7cda630b3..95b08e7b2 100644 --- a/SharedLibraryCore/Migrations/MySql/20180516023249_AddEloField.Designer.cs +++ b/Data/Migrations/MySql/20180516023249_AddEloField.Designer.cs @@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database; -using SharedLibraryCore.Database.MigrationContext; +using Data; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180516023249_AddEloField")] diff --git a/SharedLibraryCore/Migrations/MySql/20180516023249_AddEloField.cs b/Data/Migrations/MySql/20180516023249_AddEloField.cs similarity index 93% rename from SharedLibraryCore/Migrations/MySql/20180516023249_AddEloField.cs rename to Data/Migrations/MySql/20180516023249_AddEloField.cs index ccf1b51e0..0412f35d3 100644 --- a/SharedLibraryCore/Migrations/MySql/20180516023249_AddEloField.cs +++ b/Data/Migrations/MySql/20180516023249_AddEloField.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddEloField : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180517223349_AddRollingKDR.Designer.cs b/Data/Migrations/MySql/20180517223349_AddRollingKDR.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180517223349_AddRollingKDR.Designer.cs rename to Data/Migrations/MySql/20180517223349_AddRollingKDR.Designer.cs index 0e6991cfd..ac6e8758d 100644 --- a/SharedLibraryCore/Migrations/MySql/20180517223349_AddRollingKDR.Designer.cs +++ b/Data/Migrations/MySql/20180517223349_AddRollingKDR.Designer.cs @@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database; -using SharedLibraryCore.Database.MigrationContext; +using Data; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180517223349_AddRollingKDR")] diff --git a/SharedLibraryCore/Migrations/MySql/20180517223349_AddRollingKDR.cs b/Data/Migrations/MySql/20180517223349_AddRollingKDR.cs similarity index 93% rename from SharedLibraryCore/Migrations/MySql/20180517223349_AddRollingKDR.cs rename to Data/Migrations/MySql/20180517223349_AddRollingKDR.cs index 638a27431..5fe056cd3 100644 --- a/SharedLibraryCore/Migrations/MySql/20180517223349_AddRollingKDR.cs +++ b/Data/Migrations/MySql/20180517223349_AddRollingKDR.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddRollingKDR : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs b/Data/Migrations/MySql/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs rename to Data/Migrations/MySql/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs index 442b7098a..61813c53d 100644 --- a/SharedLibraryCore/Migrations/MySql/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs +++ b/Data/Migrations/MySql/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs @@ -5,10 +5,10 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180531212903_AddAutomatedOffenseAndRatingHistory")] diff --git a/SharedLibraryCore/Migrations/MySql/20180531212903_AddAutomatedOffenseAndRatingHistory.cs b/Data/Migrations/MySql/20180531212903_AddAutomatedOffenseAndRatingHistory.cs similarity index 98% rename from SharedLibraryCore/Migrations/MySql/20180531212903_AddAutomatedOffenseAndRatingHistory.cs rename to Data/Migrations/MySql/20180531212903_AddAutomatedOffenseAndRatingHistory.cs index b497989a3..d83629a37 100644 --- a/SharedLibraryCore/Migrations/MySql/20180531212903_AddAutomatedOffenseAndRatingHistory.cs +++ b/Data/Migrations/MySql/20180531212903_AddAutomatedOffenseAndRatingHistory.cs @@ -4,7 +4,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddAutomatedOffenseAndRatingHistory : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180601172317_AddActivityAmount.Designer.cs b/Data/Migrations/MySql/20180601172317_AddActivityAmount.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180601172317_AddActivityAmount.Designer.cs rename to Data/Migrations/MySql/20180601172317_AddActivityAmount.Designer.cs index 0e4bdd5b7..103fa6b68 100644 --- a/SharedLibraryCore/Migrations/MySql/20180601172317_AddActivityAmount.Designer.cs +++ b/Data/Migrations/MySql/20180601172317_AddActivityAmount.Designer.cs @@ -5,10 +5,10 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180601172317_AddActivityAmount")] diff --git a/SharedLibraryCore/Migrations/MySql/20180601172317_AddActivityAmount.cs b/Data/Migrations/MySql/20180601172317_AddActivityAmount.cs similarity index 93% rename from SharedLibraryCore/Migrations/MySql/20180601172317_AddActivityAmount.cs rename to Data/Migrations/MySql/20180601172317_AddActivityAmount.cs index a8ff56ffa..82dade0a4 100644 --- a/SharedLibraryCore/Migrations/MySql/20180601172317_AddActivityAmount.cs +++ b/Data/Migrations/MySql/20180601172317_AddActivityAmount.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddActivityAmount : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180602041758_AddClientMeta.Designer.cs b/Data/Migrations/MySql/20180602041758_AddClientMeta.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180602041758_AddClientMeta.Designer.cs rename to Data/Migrations/MySql/20180602041758_AddClientMeta.Designer.cs index 66dc57025..13a91e905 100644 --- a/SharedLibraryCore/Migrations/MySql/20180602041758_AddClientMeta.Designer.cs +++ b/Data/Migrations/MySql/20180602041758_AddClientMeta.Designer.cs @@ -5,10 +5,10 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180602041758_AddClientMeta")] diff --git a/SharedLibraryCore/Migrations/MySql/20180602041758_AddClientMeta.cs b/Data/Migrations/MySql/20180602041758_AddClientMeta.cs similarity index 97% rename from SharedLibraryCore/Migrations/MySql/20180602041758_AddClientMeta.cs rename to Data/Migrations/MySql/20180602041758_AddClientMeta.cs index 9566c2100..54e510e24 100644 --- a/SharedLibraryCore/Migrations/MySql/20180602041758_AddClientMeta.cs +++ b/Data/Migrations/MySql/20180602041758_AddClientMeta.cs @@ -4,7 +4,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddClientMeta : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180605191706_AddEFACSnapshots.Designer.cs b/Data/Migrations/MySql/20180605191706_AddEFACSnapshots.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180605191706_AddEFACSnapshots.Designer.cs rename to Data/Migrations/MySql/20180605191706_AddEFACSnapshots.Designer.cs index 7d586f1b2..ac4de91f0 100644 --- a/SharedLibraryCore/Migrations/MySql/20180605191706_AddEFACSnapshots.Designer.cs +++ b/Data/Migrations/MySql/20180605191706_AddEFACSnapshots.Designer.cs @@ -5,10 +5,10 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180605191706_AddEFACSnapshots")] diff --git a/SharedLibraryCore/Migrations/MySql/20180605191706_AddEFACSnapshots.cs b/Data/Migrations/MySql/20180605191706_AddEFACSnapshots.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180605191706_AddEFACSnapshots.cs rename to Data/Migrations/MySql/20180605191706_AddEFACSnapshots.cs index e6c7e721c..6011345de 100644 --- a/SharedLibraryCore/Migrations/MySql/20180605191706_AddEFACSnapshots.cs +++ b/Data/Migrations/MySql/20180605191706_AddEFACSnapshots.cs @@ -4,7 +4,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddEFACSnapshots : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180614014303_IndexForEFAlias.Designer.cs b/Data/Migrations/MySql/20180614014303_IndexForEFAlias.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180614014303_IndexForEFAlias.Designer.cs rename to Data/Migrations/MySql/20180614014303_IndexForEFAlias.Designer.cs index 24cee69e5..cfbd70c5c 100644 --- a/SharedLibraryCore/Migrations/MySql/20180614014303_IndexForEFAlias.Designer.cs +++ b/Data/Migrations/MySql/20180614014303_IndexForEFAlias.Designer.cs @@ -5,10 +5,10 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180614014303_IndexForEFAlias")] diff --git a/SharedLibraryCore/Migrations/MySql/20180614014303_IndexForEFAlias.cs b/Data/Migrations/MySql/20180614014303_IndexForEFAlias.cs similarity index 93% rename from SharedLibraryCore/Migrations/MySql/20180614014303_IndexForEFAlias.cs rename to Data/Migrations/MySql/20180614014303_IndexForEFAlias.cs index 4137b988e..95fe7acdf 100644 --- a/SharedLibraryCore/Migrations/MySql/20180614014303_IndexForEFAlias.cs +++ b/Data/Migrations/MySql/20180614014303_IndexForEFAlias.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class IndexForEFAlias : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180902035612_AddFractionAndIsKill.Designer.cs b/Data/Migrations/MySql/20180902035612_AddFractionAndIsKill.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180902035612_AddFractionAndIsKill.Designer.cs rename to Data/Migrations/MySql/20180902035612_AddFractionAndIsKill.Designer.cs index 76646c907..cb2f8150b 100644 --- a/SharedLibraryCore/Migrations/MySql/20180902035612_AddFractionAndIsKill.Designer.cs +++ b/Data/Migrations/MySql/20180902035612_AddFractionAndIsKill.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180902035612_AddFractionAndIsKill")] diff --git a/SharedLibraryCore/Migrations/MySql/20180902035612_AddFractionAndIsKill.cs b/Data/Migrations/MySql/20180902035612_AddFractionAndIsKill.cs similarity index 98% rename from SharedLibraryCore/Migrations/MySql/20180902035612_AddFractionAndIsKill.cs rename to Data/Migrations/MySql/20180902035612_AddFractionAndIsKill.cs index 529a79c40..7c3e20340 100644 --- a/SharedLibraryCore/Migrations/MySql/20180902035612_AddFractionAndIsKill.cs +++ b/Data/Migrations/MySql/20180902035612_AddFractionAndIsKill.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddFractionAndIsKill : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180904154622_AddVisibilityPercentage.Designer.cs b/Data/Migrations/MySql/20180904154622_AddVisibilityPercentage.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180904154622_AddVisibilityPercentage.Designer.cs rename to Data/Migrations/MySql/20180904154622_AddVisibilityPercentage.Designer.cs index b9ec66ff1..94bd439c2 100644 --- a/SharedLibraryCore/Migrations/MySql/20180904154622_AddVisibilityPercentage.Designer.cs +++ b/Data/Migrations/MySql/20180904154622_AddVisibilityPercentage.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180904154622_AddVisibilityPercentage")] diff --git a/SharedLibraryCore/Migrations/MySql/20180904154622_AddVisibilityPercentage.cs b/Data/Migrations/MySql/20180904154622_AddVisibilityPercentage.cs similarity index 93% rename from SharedLibraryCore/Migrations/MySql/20180904154622_AddVisibilityPercentage.cs rename to Data/Migrations/MySql/20180904154622_AddVisibilityPercentage.cs index d4a8132e0..0cb44ec85 100644 --- a/SharedLibraryCore/Migrations/MySql/20180904154622_AddVisibilityPercentage.cs +++ b/Data/Migrations/MySql/20180904154622_AddVisibilityPercentage.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddVisibilityPercentage : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180907020706_AddVision.Designer.cs b/Data/Migrations/MySql/20180907020706_AddVision.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180907020706_AddVision.Designer.cs rename to Data/Migrations/MySql/20180907020706_AddVision.Designer.cs index ade98e4ef..f8658f5af 100644 --- a/SharedLibraryCore/Migrations/MySql/20180907020706_AddVision.Designer.cs +++ b/Data/Migrations/MySql/20180907020706_AddVision.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180907020706_AddVision")] diff --git a/SharedLibraryCore/Migrations/MySql/20180907020706_AddVision.cs b/Data/Migrations/MySql/20180907020706_AddVision.cs similarity index 93% rename from SharedLibraryCore/Migrations/MySql/20180907020706_AddVision.cs rename to Data/Migrations/MySql/20180907020706_AddVision.cs index 8085963a8..19bc5e3f4 100644 --- a/SharedLibraryCore/Migrations/MySql/20180907020706_AddVision.cs +++ b/Data/Migrations/MySql/20180907020706_AddVision.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddVision : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180908004053_AddWhenToRating.Designer.cs b/Data/Migrations/MySql/20180908004053_AddWhenToRating.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180908004053_AddWhenToRating.Designer.cs rename to Data/Migrations/MySql/20180908004053_AddWhenToRating.Designer.cs index 2d700dce0..6a4c0bb9f 100644 --- a/SharedLibraryCore/Migrations/MySql/20180908004053_AddWhenToRating.Designer.cs +++ b/Data/Migrations/MySql/20180908004053_AddWhenToRating.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180908004053_AddWhenToRating")] diff --git a/SharedLibraryCore/Migrations/MySql/20180908004053_AddWhenToRating.cs b/Data/Migrations/MySql/20180908004053_AddWhenToRating.cs similarity index 93% rename from SharedLibraryCore/Migrations/MySql/20180908004053_AddWhenToRating.cs rename to Data/Migrations/MySql/20180908004053_AddWhenToRating.cs index 63e84cc32..59a98152d 100644 --- a/SharedLibraryCore/Migrations/MySql/20180908004053_AddWhenToRating.cs +++ b/Data/Migrations/MySql/20180908004053_AddWhenToRating.cs @@ -1,7 +1,7 @@ using System; using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddWhenToRating : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180910221749_AddRatingIndexes.Designer.cs b/Data/Migrations/MySql/20180910221749_AddRatingIndexes.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180910221749_AddRatingIndexes.Designer.cs rename to Data/Migrations/MySql/20180910221749_AddRatingIndexes.Designer.cs index 15b4bd6db..f37f1321d 100644 --- a/SharedLibraryCore/Migrations/MySql/20180910221749_AddRatingIndexes.Designer.cs +++ b/Data/Migrations/MySql/20180910221749_AddRatingIndexes.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180910221749_AddRatingIndexes")] diff --git a/SharedLibraryCore/Migrations/MySql/20180910221749_AddRatingIndexes.cs b/Data/Migrations/MySql/20180910221749_AddRatingIndexes.cs similarity index 96% rename from SharedLibraryCore/Migrations/MySql/20180910221749_AddRatingIndexes.cs rename to Data/Migrations/MySql/20180910221749_AddRatingIndexes.cs index fc26e0d71..f81d83dc6 100644 --- a/SharedLibraryCore/Migrations/MySql/20180910221749_AddRatingIndexes.cs +++ b/Data/Migrations/MySql/20180910221749_AddRatingIndexes.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddRatingIndexes : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180911184224_AddEFAliasNameIndex.Designer.cs b/Data/Migrations/MySql/20180911184224_AddEFAliasNameIndex.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180911184224_AddEFAliasNameIndex.Designer.cs rename to Data/Migrations/MySql/20180911184224_AddEFAliasNameIndex.Designer.cs index e740317a6..0d74c86a0 100644 --- a/SharedLibraryCore/Migrations/MySql/20180911184224_AddEFAliasNameIndex.Designer.cs +++ b/Data/Migrations/MySql/20180911184224_AddEFAliasNameIndex.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180911184224_AddEFAliasNameIndex")] diff --git a/SharedLibraryCore/Migrations/MySql/20180911184224_AddEFAliasNameIndex.cs b/Data/Migrations/MySql/20180911184224_AddEFAliasNameIndex.cs similarity index 92% rename from SharedLibraryCore/Migrations/MySql/20180911184224_AddEFAliasNameIndex.cs rename to Data/Migrations/MySql/20180911184224_AddEFAliasNameIndex.cs index f810fc9f8..7e62a4f09 100644 --- a/SharedLibraryCore/Migrations/MySql/20180911184224_AddEFAliasNameIndex.cs +++ b/Data/Migrations/MySql/20180911184224_AddEFAliasNameIndex.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddEFAliasNameIndex : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180911190823_AddEFAliasNameMaxLength24.Designer.cs b/Data/Migrations/MySql/20180911190823_AddEFAliasNameMaxLength24.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180911190823_AddEFAliasNameMaxLength24.Designer.cs rename to Data/Migrations/MySql/20180911190823_AddEFAliasNameMaxLength24.Designer.cs index 2c099c6e4..6580d1567 100644 --- a/SharedLibraryCore/Migrations/MySql/20180911190823_AddEFAliasNameMaxLength24.Designer.cs +++ b/Data/Migrations/MySql/20180911190823_AddEFAliasNameMaxLength24.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180911190823_AddEFAliasNameMaxLength24")] diff --git a/SharedLibraryCore/Migrations/MySql/20180911190823_AddEFAliasNameMaxLength24.cs b/Data/Migrations/MySql/20180911190823_AddEFAliasNameMaxLength24.cs similarity index 94% rename from SharedLibraryCore/Migrations/MySql/20180911190823_AddEFAliasNameMaxLength24.cs rename to Data/Migrations/MySql/20180911190823_AddEFAliasNameMaxLength24.cs index 7e03a9097..0b281c63a 100644 --- a/SharedLibraryCore/Migrations/MySql/20180911190823_AddEFAliasNameMaxLength24.cs +++ b/Data/Migrations/MySql/20180911190823_AddEFAliasNameMaxLength24.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddEFAliasNameMaxLength24 : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs b/Data/Migrations/MySql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs rename to Data/Migrations/MySql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs index 68be3dbea..5cc66330f 100644 --- a/SharedLibraryCore/Migrations/MySql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs +++ b/Data/Migrations/MySql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180912015012_AddPreviousCurrentValueToEFChangeHistory")] diff --git a/SharedLibraryCore/Migrations/MySql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs b/Data/Migrations/MySql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs similarity index 95% rename from SharedLibraryCore/Migrations/MySql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs rename to Data/Migrations/MySql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs index a910959c3..ee1aa976c 100644 --- a/SharedLibraryCore/Migrations/MySql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs +++ b/Data/Migrations/MySql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddPreviousCurrentValueToEFChangeHistory : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180915163111_AddIndexToMessageTimeSent.Designer.cs b/Data/Migrations/MySql/20180915163111_AddIndexToMessageTimeSent.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180915163111_AddIndexToMessageTimeSent.Designer.cs rename to Data/Migrations/MySql/20180915163111_AddIndexToMessageTimeSent.Designer.cs index 27b20cb99..a48477872 100644 --- a/SharedLibraryCore/Migrations/MySql/20180915163111_AddIndexToMessageTimeSent.Designer.cs +++ b/Data/Migrations/MySql/20180915163111_AddIndexToMessageTimeSent.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180915163111_AddIndexToMessageTimeSent")] diff --git a/SharedLibraryCore/Migrations/MySql/20180915163111_AddIndexToMessageTimeSent.cs b/Data/Migrations/MySql/20180915163111_AddIndexToMessageTimeSent.cs similarity index 93% rename from SharedLibraryCore/Migrations/MySql/20180915163111_AddIndexToMessageTimeSent.cs rename to Data/Migrations/MySql/20180915163111_AddIndexToMessageTimeSent.cs index 18e399380..4baf71ce7 100644 --- a/SharedLibraryCore/Migrations/MySql/20180915163111_AddIndexToMessageTimeSent.cs +++ b/Data/Migrations/MySql/20180915163111_AddIndexToMessageTimeSent.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddIndexToMessageTimeSent : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180922231310_RemoveACSnapShot.Designer.cs b/Data/Migrations/MySql/20180922231310_RemoveACSnapShot.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180922231310_RemoveACSnapShot.Designer.cs rename to Data/Migrations/MySql/20180922231310_RemoveACSnapShot.Designer.cs index f0cc690b7..64f28ed53 100644 --- a/SharedLibraryCore/Migrations/MySql/20180922231310_RemoveACSnapShot.Designer.cs +++ b/Data/Migrations/MySql/20180922231310_RemoveACSnapShot.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180922231310_RemoveACSnapShot")] diff --git a/SharedLibraryCore/Migrations/MySql/20180922231310_RemoveACSnapShot.cs b/Data/Migrations/MySql/20180922231310_RemoveACSnapShot.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180922231310_RemoveACSnapShot.cs rename to Data/Migrations/MySql/20180922231310_RemoveACSnapShot.cs index d1804ad04..5e85477ab 100644 --- a/SharedLibraryCore/Migrations/MySql/20180922231310_RemoveACSnapShot.cs +++ b/Data/Migrations/MySql/20180922231310_RemoveACSnapShot.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class RemoveACSnapShot : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20180922231600_ReaddACSnapshot.Designer.cs b/Data/Migrations/MySql/20180922231600_ReaddACSnapshot.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180922231600_ReaddACSnapshot.Designer.cs rename to Data/Migrations/MySql/20180922231600_ReaddACSnapshot.Designer.cs index acb00ec67..3dbc65745 100644 --- a/SharedLibraryCore/Migrations/MySql/20180922231600_ReaddACSnapshot.Designer.cs +++ b/Data/Migrations/MySql/20180922231600_ReaddACSnapshot.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20180922231600_ReaddACSnapshot")] diff --git a/SharedLibraryCore/Migrations/MySql/20180922231600_ReaddACSnapshot.cs b/Data/Migrations/MySql/20180922231600_ReaddACSnapshot.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20180922231600_ReaddACSnapshot.cs rename to Data/Migrations/MySql/20180922231600_ReaddACSnapshot.cs index da7303123..ff9797dd8 100644 --- a/SharedLibraryCore/Migrations/MySql/20180922231600_ReaddACSnapshot.cs +++ b/Data/Migrations/MySql/20180922231600_ReaddACSnapshot.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class ReaddACSnapshot : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20181014171848_MakePenaltyExpirationNullable.Designer.cs b/Data/Migrations/MySql/20181014171848_MakePenaltyExpirationNullable.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20181014171848_MakePenaltyExpirationNullable.Designer.cs rename to Data/Migrations/MySql/20181014171848_MakePenaltyExpirationNullable.Designer.cs index 387e3e69a..eb2745922 100644 --- a/SharedLibraryCore/Migrations/MySql/20181014171848_MakePenaltyExpirationNullable.Designer.cs +++ b/Data/Migrations/MySql/20181014171848_MakePenaltyExpirationNullable.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20181014171848_MakePenaltyExpirationNullable")] diff --git a/SharedLibraryCore/Migrations/MySql/20181014171848_MakePenaltyExpirationNullable.cs b/Data/Migrations/MySql/20181014171848_MakePenaltyExpirationNullable.cs similarity index 98% rename from SharedLibraryCore/Migrations/MySql/20181014171848_MakePenaltyExpirationNullable.cs rename to Data/Migrations/MySql/20181014171848_MakePenaltyExpirationNullable.cs index d1ab35708..8b007788e 100644 --- a/SharedLibraryCore/Migrations/MySql/20181014171848_MakePenaltyExpirationNullable.cs +++ b/Data/Migrations/MySql/20181014171848_MakePenaltyExpirationNullable.cs @@ -1,7 +1,7 @@ using System; using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class MakePenaltyExpirationNullable : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20181125193243_MakeClientIPNullable.Designer.cs b/Data/Migrations/MySql/20181125193243_MakeClientIPNullable.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20181125193243_MakeClientIPNullable.Designer.cs rename to Data/Migrations/MySql/20181125193243_MakeClientIPNullable.Designer.cs index 62ca0ffb3..5afe44ecf 100644 --- a/SharedLibraryCore/Migrations/MySql/20181125193243_MakeClientIPNullable.Designer.cs +++ b/Data/Migrations/MySql/20181125193243_MakeClientIPNullable.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20181125193243_MakeClientIPNullable")] diff --git a/SharedLibraryCore/Migrations/MySql/20181125193243_MakeClientIPNullable.cs b/Data/Migrations/MySql/20181125193243_MakeClientIPNullable.cs similarity index 98% rename from SharedLibraryCore/Migrations/MySql/20181125193243_MakeClientIPNullable.cs rename to Data/Migrations/MySql/20181125193243_MakeClientIPNullable.cs index 8e858b04c..60a3221bd 100644 --- a/SharedLibraryCore/Migrations/MySql/20181125193243_MakeClientIPNullable.cs +++ b/Data/Migrations/MySql/20181125193243_MakeClientIPNullable.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class MakeClientIPNullable : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs b/Data/Migrations/MySql/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs rename to Data/Migrations/MySql/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs index f6a21ff97..d6b4d6ed1 100644 --- a/SharedLibraryCore/Migrations/MySql/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs +++ b/Data/Migrations/MySql/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20181127144417_AddEndpointToEFServerUpdateServerIdType")] diff --git a/SharedLibraryCore/Migrations/MySql/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs b/Data/Migrations/MySql/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs similarity index 92% rename from SharedLibraryCore/Migrations/MySql/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs rename to Data/Migrations/MySql/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs index 52d1d831c..a41c8a2d3 100644 --- a/SharedLibraryCore/Migrations/MySql/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs +++ b/Data/Migrations/MySql/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddEndpointToEFServerUpdateServerIdType : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20181216214513_AddEvadePenaltyFlag.Designer.cs b/Data/Migrations/MySql/20181216214513_AddEvadePenaltyFlag.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20181216214513_AddEvadePenaltyFlag.Designer.cs rename to Data/Migrations/MySql/20181216214513_AddEvadePenaltyFlag.Designer.cs index 2f6186b83..44e2f1b78 100644 --- a/SharedLibraryCore/Migrations/MySql/20181216214513_AddEvadePenaltyFlag.Designer.cs +++ b/Data/Migrations/MySql/20181216214513_AddEvadePenaltyFlag.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20181216214513_AddEvadePenaltyFlag")] diff --git a/SharedLibraryCore/Migrations/MySql/20181216214513_AddEvadePenaltyFlag.cs b/Data/Migrations/MySql/20181216214513_AddEvadePenaltyFlag.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20181216214513_AddEvadePenaltyFlag.cs rename to Data/Migrations/MySql/20181216214513_AddEvadePenaltyFlag.cs index 0037aae51..a5913a040 100644 --- a/SharedLibraryCore/Migrations/MySql/20181216214513_AddEvadePenaltyFlag.cs +++ b/Data/Migrations/MySql/20181216214513_AddEvadePenaltyFlag.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddEvadePenaltyFlag : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs b/Data/Migrations/MySql/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs rename to Data/Migrations/MySql/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs index a114d3a9e..06b128943 100644 --- a/SharedLibraryCore/Migrations/MySql/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs +++ b/Data/Migrations/MySql/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20190222234742_AddIndexToEFMeta-KeyAndClientId")] diff --git a/SharedLibraryCore/Migrations/MySql/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs b/Data/Migrations/MySql/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs similarity index 94% rename from SharedLibraryCore/Migrations/MySql/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs rename to Data/Migrations/MySql/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs index 3b9621cd5..e5d1e9a9a 100644 --- a/SharedLibraryCore/Migrations/MySql/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs +++ b/Data/Migrations/MySql/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddIndexToEFMetaKeyAndClientId : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20190423142128_AddGameNameToEFServer.Designer.cs b/Data/Migrations/MySql/20190423142128_AddGameNameToEFServer.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20190423142128_AddGameNameToEFServer.Designer.cs rename to Data/Migrations/MySql/20190423142128_AddGameNameToEFServer.Designer.cs index 0ff159143..eb142757a 100644 --- a/SharedLibraryCore/Migrations/MySql/20190423142128_AddGameNameToEFServer.Designer.cs +++ b/Data/Migrations/MySql/20190423142128_AddGameNameToEFServer.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20190423142128_AddGameNameToEFServer")] diff --git a/SharedLibraryCore/Migrations/MySql/20190423142128_AddGameNameToEFServer.cs b/Data/Migrations/MySql/20190423142128_AddGameNameToEFServer.cs similarity index 92% rename from SharedLibraryCore/Migrations/MySql/20190423142128_AddGameNameToEFServer.cs rename to Data/Migrations/MySql/20190423142128_AddGameNameToEFServer.cs index 45b6a189e..b5e1fe02e 100644 --- a/SharedLibraryCore/Migrations/MySql/20190423142128_AddGameNameToEFServer.cs +++ b/Data/Migrations/MySql/20190423142128_AddGameNameToEFServer.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddGameNameToEFServer : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20190615145212_AddAvgRecoilOffset.Designer.cs b/Data/Migrations/MySql/20190615145212_AddAvgRecoilOffset.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20190615145212_AddAvgRecoilOffset.Designer.cs rename to Data/Migrations/MySql/20190615145212_AddAvgRecoilOffset.Designer.cs index 2023a0615..a2af60edf 100644 --- a/SharedLibraryCore/Migrations/MySql/20190615145212_AddAvgRecoilOffset.Designer.cs +++ b/Data/Migrations/MySql/20190615145212_AddAvgRecoilOffset.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20190615145212_AddAvgRecoilOffset")] diff --git a/SharedLibraryCore/Migrations/MySql/20190615145212_AddAvgRecoilOffset.cs b/Data/Migrations/MySql/20190615145212_AddAvgRecoilOffset.cs similarity index 91% rename from SharedLibraryCore/Migrations/MySql/20190615145212_AddAvgRecoilOffset.cs rename to Data/Migrations/MySql/20190615145212_AddAvgRecoilOffset.cs index d74bfd957..7ddea78fe 100644 --- a/SharedLibraryCore/Migrations/MySql/20190615145212_AddAvgRecoilOffset.cs +++ b/Data/Migrations/MySql/20190615145212_AddAvgRecoilOffset.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddAvgRecoilOffset : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs b/Data/Migrations/MySql/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs rename to Data/Migrations/MySql/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs index 25f0f3f38..694e823ed 100644 --- a/SharedLibraryCore/Migrations/MySql/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs +++ b/Data/Migrations/MySql/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20190615214055_AddRecoilOffsetToSnapshot")] diff --git a/SharedLibraryCore/Migrations/MySql/20190615214055_AddRecoilOffsetToSnapshot.cs b/Data/Migrations/MySql/20190615214055_AddRecoilOffsetToSnapshot.cs similarity index 93% rename from SharedLibraryCore/Migrations/MySql/20190615214055_AddRecoilOffsetToSnapshot.cs rename to Data/Migrations/MySql/20190615214055_AddRecoilOffsetToSnapshot.cs index 481e74bd2..44aa0fbdf 100644 --- a/SharedLibraryCore/Migrations/MySql/20190615214055_AddRecoilOffsetToSnapshot.cs +++ b/Data/Migrations/MySql/20190615214055_AddRecoilOffsetToSnapshot.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddRecoilOffsetToSnapshot : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20190725000309_AlterEFRatingIndex.Designer.cs b/Data/Migrations/MySql/20190725000309_AlterEFRatingIndex.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20190725000309_AlterEFRatingIndex.Designer.cs rename to Data/Migrations/MySql/20190725000309_AlterEFRatingIndex.Designer.cs index d26ad7cff..4b1a6f971 100644 --- a/SharedLibraryCore/Migrations/MySql/20190725000309_AlterEFRatingIndex.Designer.cs +++ b/Data/Migrations/MySql/20190725000309_AlterEFRatingIndex.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20190725000309_AlterEFRatingIndex")] diff --git a/SharedLibraryCore/Migrations/MySql/20190725000309_AlterEFRatingIndex.cs b/Data/Migrations/MySql/20190725000309_AlterEFRatingIndex.cs similarity index 97% rename from SharedLibraryCore/Migrations/MySql/20190725000309_AlterEFRatingIndex.cs rename to Data/Migrations/MySql/20190725000309_AlterEFRatingIndex.cs index d198ee2d2..994bcfc5a 100644 --- a/SharedLibraryCore/Migrations/MySql/20190725000309_AlterEFRatingIndex.cs +++ b/Data/Migrations/MySql/20190725000309_AlterEFRatingIndex.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AlterEFRatingIndex : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20190802174908_AddSearchNameToEFAlias.Designer.cs b/Data/Migrations/MySql/20190802174908_AddSearchNameToEFAlias.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20190802174908_AddSearchNameToEFAlias.Designer.cs rename to Data/Migrations/MySql/20190802174908_AddSearchNameToEFAlias.Designer.cs index 2498fc416..55550d321 100644 --- a/SharedLibraryCore/Migrations/MySql/20190802174908_AddSearchNameToEFAlias.Designer.cs +++ b/Data/Migrations/MySql/20190802174908_AddSearchNameToEFAlias.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20190802174908_AddSearchNameToEFAlias")] diff --git a/SharedLibraryCore/Migrations/MySql/20190802174908_AddSearchNameToEFAlias.cs b/Data/Migrations/MySql/20190802174908_AddSearchNameToEFAlias.cs similarity index 95% rename from SharedLibraryCore/Migrations/MySql/20190802174908_AddSearchNameToEFAlias.cs rename to Data/Migrations/MySql/20190802174908_AddSearchNameToEFAlias.cs index 249713b61..6a2650ccd 100644 --- a/SharedLibraryCore/Migrations/MySql/20190802174908_AddSearchNameToEFAlias.cs +++ b/Data/Migrations/MySql/20190802174908_AddSearchNameToEFAlias.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddSearchNameToEFAlias : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20190831210503_AvgSnapValueToClientStatistics.Designer.cs b/Data/Migrations/MySql/20190831210503_AvgSnapValueToClientStatistics.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20190831210503_AvgSnapValueToClientStatistics.Designer.cs rename to Data/Migrations/MySql/20190831210503_AvgSnapValueToClientStatistics.Designer.cs index ca976673a..ad2c53829 100644 --- a/SharedLibraryCore/Migrations/MySql/20190831210503_AvgSnapValueToClientStatistics.Designer.cs +++ b/Data/Migrations/MySql/20190831210503_AvgSnapValueToClientStatistics.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20190831210503_AvgSnapValueToClientStatistics")] diff --git a/SharedLibraryCore/Migrations/MySql/20190831210503_AvgSnapValueToClientStatistics.cs b/Data/Migrations/MySql/20190831210503_AvgSnapValueToClientStatistics.cs similarity index 93% rename from SharedLibraryCore/Migrations/MySql/20190831210503_AvgSnapValueToClientStatistics.cs rename to Data/Migrations/MySql/20190831210503_AvgSnapValueToClientStatistics.cs index ddd0278ab..0ac58e127 100644 --- a/SharedLibraryCore/Migrations/MySql/20190831210503_AvgSnapValueToClientStatistics.cs +++ b/Data/Migrations/MySql/20190831210503_AvgSnapValueToClientStatistics.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AvgSnapValueToClientStatistics : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs b/Data/Migrations/MySql/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs rename to Data/Migrations/MySql/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs index 46ca9d25f..16cb66b70 100644 --- a/SharedLibraryCore/Migrations/MySql/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs +++ b/Data/Migrations/MySql/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20190901180209_AddSnapHitCountToClientStatistics")] diff --git a/SharedLibraryCore/Migrations/MySql/20190901180209_AddSnapHitCountToClientStatistics.cs b/Data/Migrations/MySql/20190901180209_AddSnapHitCountToClientStatistics.cs similarity index 93% rename from SharedLibraryCore/Migrations/MySql/20190901180209_AddSnapHitCountToClientStatistics.cs rename to Data/Migrations/MySql/20190901180209_AddSnapHitCountToClientStatistics.cs index 0b102af30..fd6258da4 100644 --- a/SharedLibraryCore/Migrations/MySql/20190901180209_AddSnapHitCountToClientStatistics.cs +++ b/Data/Migrations/MySql/20190901180209_AddSnapHitCountToClientStatistics.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddSnapHitCountToClientStatistics : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs b/Data/Migrations/MySql/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs rename to Data/Migrations/MySql/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs index a16f5ed50..435383a40 100644 --- a/SharedLibraryCore/Migrations/MySql/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs +++ b/Data/Migrations/MySql/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20190901223620_UseJunctionTableForSnapshotVector3")] diff --git a/SharedLibraryCore/Migrations/MySql/20190901223620_UseJunctionTableForSnapshotVector3.cs b/Data/Migrations/MySql/20190901223620_UseJunctionTableForSnapshotVector3.cs similarity index 98% rename from SharedLibraryCore/Migrations/MySql/20190901223620_UseJunctionTableForSnapshotVector3.cs rename to Data/Migrations/MySql/20190901223620_UseJunctionTableForSnapshotVector3.cs index 82d20240b..e256200b9 100644 --- a/SharedLibraryCore/Migrations/MySql/20190901223620_UseJunctionTableForSnapshotVector3.cs +++ b/Data/Migrations/MySql/20190901223620_UseJunctionTableForSnapshotVector3.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class UseJunctionTableForSnapshotVector3 : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs b/Data/Migrations/MySql/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs rename to Data/Migrations/MySql/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs index 4f4d44ab9..0c9a6fd96 100644 --- a/SharedLibraryCore/Migrations/MySql/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs +++ b/Data/Migrations/MySql/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20190914011524_AddCurrentSnapValueToSnapshot")] diff --git a/SharedLibraryCore/Migrations/MySql/20190914011524_AddCurrentSnapValueToSnapshot.cs b/Data/Migrations/MySql/20190914011524_AddCurrentSnapValueToSnapshot.cs similarity index 93% rename from SharedLibraryCore/Migrations/MySql/20190914011524_AddCurrentSnapValueToSnapshot.cs rename to Data/Migrations/MySql/20190914011524_AddCurrentSnapValueToSnapshot.cs index 9b225d5d4..cd9755a81 100644 --- a/SharedLibraryCore/Migrations/MySql/20190914011524_AddCurrentSnapValueToSnapshot.cs +++ b/Data/Migrations/MySql/20190914011524_AddCurrentSnapValueToSnapshot.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddCurrentSnapValueToSnapshot : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs b/Data/Migrations/MySql/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs rename to Data/Migrations/MySql/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs index 971189612..3bbec3796 100644 --- a/SharedLibraryCore/Migrations/MySql/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs +++ b/Data/Migrations/MySql/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20190914012015_AddSessionSnapHitsToSnapshot")] diff --git a/SharedLibraryCore/Migrations/MySql/20190914012015_AddSessionSnapHitsToSnapshot.cs b/Data/Migrations/MySql/20190914012015_AddSessionSnapHitsToSnapshot.cs similarity index 93% rename from SharedLibraryCore/Migrations/MySql/20190914012015_AddSessionSnapHitsToSnapshot.cs rename to Data/Migrations/MySql/20190914012015_AddSessionSnapHitsToSnapshot.cs index e4b2340b1..45cbc9b71 100644 --- a/SharedLibraryCore/Migrations/MySql/20190914012015_AddSessionSnapHitsToSnapshot.cs +++ b/Data/Migrations/MySql/20190914012015_AddSessionSnapHitsToSnapshot.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddSessionSnapHitsToSnapshot : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20191004172550_RenameClientHitLocationCountColumns.Designer.cs b/Data/Migrations/MySql/20191004172550_RenameClientHitLocationCountColumns.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20191004172550_RenameClientHitLocationCountColumns.Designer.cs rename to Data/Migrations/MySql/20191004172550_RenameClientHitLocationCountColumns.Designer.cs index 91727ea78..a56222c62 100644 --- a/SharedLibraryCore/Migrations/MySql/20191004172550_RenameClientHitLocationCountColumns.Designer.cs +++ b/Data/Migrations/MySql/20191004172550_RenameClientHitLocationCountColumns.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20191004172550_RenameClientHitLocationCountColumns")] diff --git a/SharedLibraryCore/Migrations/MySql/20191004172550_RenameClientHitLocationCountColumns.cs b/Data/Migrations/MySql/20191004172550_RenameClientHitLocationCountColumns.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20191004172550_RenameClientHitLocationCountColumns.cs rename to Data/Migrations/MySql/20191004172550_RenameClientHitLocationCountColumns.cs index 4ebe4ccdb..cec9d7cff 100644 --- a/SharedLibraryCore/Migrations/MySql/20191004172550_RenameClientHitLocationCountColumns.cs +++ b/Data/Migrations/MySql/20191004172550_RenameClientHitLocationCountColumns.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class RenameClientHitLocationCountColumns : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs b/Data/Migrations/MySql/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs rename to Data/Migrations/MySql/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs index d156f99b0..a36575e91 100644 --- a/SharedLibraryCore/Migrations/MySql/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs +++ b/Data/Migrations/MySql/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20191030000713_EnforceUniqueIndexForEFAliasIPName")] diff --git a/SharedLibraryCore/Migrations/MySql/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs b/Data/Migrations/MySql/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs similarity index 98% rename from SharedLibraryCore/Migrations/MySql/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs rename to Data/Migrations/MySql/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs index bd5c6a828..e5e781c5d 100644 --- a/SharedLibraryCore/Migrations/MySql/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs +++ b/Data/Migrations/MySql/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class EnforceUniqueIndexForEFAliasIPName : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs b/Data/Migrations/MySql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs rename to Data/Migrations/MySql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs index 59ca1e24c..0f86740a3 100644 --- a/SharedLibraryCore/Migrations/MySql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs +++ b/Data/Migrations/MySql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL")] diff --git a/SharedLibraryCore/Migrations/MySql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs b/Data/Migrations/MySql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs similarity index 94% rename from SharedLibraryCore/Migrations/MySql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs rename to Data/Migrations/MySql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs index e91b3f53b..0fad88fef 100644 --- a/SharedLibraryCore/Migrations/MySql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs +++ b/Data/Migrations/MySql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class SetCaseSensitiveCoallationForAliasNameMySQL : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20191230140947_AddMissingActiveColumns.Designer.cs b/Data/Migrations/MySql/20191230140947_AddMissingActiveColumns.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20191230140947_AddMissingActiveColumns.Designer.cs rename to Data/Migrations/MySql/20191230140947_AddMissingActiveColumns.Designer.cs index 01a9b9f22..de6cd57d1 100644 --- a/SharedLibraryCore/Migrations/MySql/20191230140947_AddMissingActiveColumns.Designer.cs +++ b/Data/Migrations/MySql/20191230140947_AddMissingActiveColumns.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20191230140947_AddMissingActiveColumns")] diff --git a/SharedLibraryCore/Migrations/MySql/20191230140947_AddMissingActiveColumns.cs b/Data/Migrations/MySql/20191230140947_AddMissingActiveColumns.cs similarity index 93% rename from SharedLibraryCore/Migrations/MySql/20191230140947_AddMissingActiveColumns.cs rename to Data/Migrations/MySql/20191230140947_AddMissingActiveColumns.cs index 13afb77f7..0b7eb0c73 100644 --- a/SharedLibraryCore/Migrations/MySql/20191230140947_AddMissingActiveColumns.cs +++ b/Data/Migrations/MySql/20191230140947_AddMissingActiveColumns.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddMissingActiveColumns : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs b/Data/Migrations/MySql/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs rename to Data/Migrations/MySql/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs index b3cf9f082..54d7c9a08 100644 --- a/SharedLibraryCore/Migrations/MySql/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs +++ b/Data/Migrations/MySql/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20200423225137_AddImpersonationIdToEFChangeHistory")] diff --git a/SharedLibraryCore/Migrations/MySql/20200423225137_AddImpersonationIdToEFChangeHistory.cs b/Data/Migrations/MySql/20200423225137_AddImpersonationIdToEFChangeHistory.cs similarity index 93% rename from SharedLibraryCore/Migrations/MySql/20200423225137_AddImpersonationIdToEFChangeHistory.cs rename to Data/Migrations/MySql/20200423225137_AddImpersonationIdToEFChangeHistory.cs index b1d337d9a..1fa955a23 100644 --- a/SharedLibraryCore/Migrations/MySql/20200423225137_AddImpersonationIdToEFChangeHistory.cs +++ b/Data/Migrations/MySql/20200423225137_AddImpersonationIdToEFChangeHistory.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddImpersonationIdToEFChangeHistory : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20200521203304_AddHostnameToEFServer.Designer.cs b/Data/Migrations/MySql/20200521203304_AddHostnameToEFServer.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20200521203304_AddHostnameToEFServer.Designer.cs rename to Data/Migrations/MySql/20200521203304_AddHostnameToEFServer.Designer.cs index 0de748a51..cadc49292 100644 --- a/SharedLibraryCore/Migrations/MySql/20200521203304_AddHostnameToEFServer.Designer.cs +++ b/Data/Migrations/MySql/20200521203304_AddHostnameToEFServer.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20200521203304_AddHostnameToEFServer")] diff --git a/SharedLibraryCore/Migrations/MySql/20200521203304_AddHostnameToEFServer.cs b/Data/Migrations/MySql/20200521203304_AddHostnameToEFServer.cs similarity index 92% rename from SharedLibraryCore/Migrations/MySql/20200521203304_AddHostnameToEFServer.cs rename to Data/Migrations/MySql/20200521203304_AddHostnameToEFServer.cs index 7f0e86577..88e94533c 100644 --- a/SharedLibraryCore/Migrations/MySql/20200521203304_AddHostnameToEFServer.cs +++ b/Data/Migrations/MySql/20200521203304_AddHostnameToEFServer.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddHostnameToEFServer : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20200819224119_AddIsPasswordProtectedColumn.Designer.cs b/Data/Migrations/MySql/20200819224119_AddIsPasswordProtectedColumn.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20200819224119_AddIsPasswordProtectedColumn.Designer.cs rename to Data/Migrations/MySql/20200819224119_AddIsPasswordProtectedColumn.Designer.cs index 2dbf60c85..8e777ea48 100644 --- a/SharedLibraryCore/Migrations/MySql/20200819224119_AddIsPasswordProtectedColumn.Designer.cs +++ b/Data/Migrations/MySql/20200819224119_AddIsPasswordProtectedColumn.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20200819224119_AddIsPasswordProtectedColumn")] diff --git a/SharedLibraryCore/Migrations/MySql/20200819224119_AddIsPasswordProtectedColumn.cs b/Data/Migrations/MySql/20200819224119_AddIsPasswordProtectedColumn.cs similarity index 95% rename from SharedLibraryCore/Migrations/MySql/20200819224119_AddIsPasswordProtectedColumn.cs rename to Data/Migrations/MySql/20200819224119_AddIsPasswordProtectedColumn.cs index 410ad8ffd..7db2d353f 100644 --- a/SharedLibraryCore/Migrations/MySql/20200819224119_AddIsPasswordProtectedColumn.cs +++ b/Data/Migrations/MySql/20200819224119_AddIsPasswordProtectedColumn.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddIsPasswordProtectedColumn : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20201114232340_UpdateEFRatingIndex.Designer.cs b/Data/Migrations/MySql/20201114232340_UpdateEFRatingIndex.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20201114232340_UpdateEFRatingIndex.Designer.cs rename to Data/Migrations/MySql/20201114232340_UpdateEFRatingIndex.Designer.cs index 721088313..6cb95a41f 100644 --- a/SharedLibraryCore/Migrations/MySql/20201114232340_UpdateEFRatingIndex.Designer.cs +++ b/Data/Migrations/MySql/20201114232340_UpdateEFRatingIndex.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20201114232340_UpdateEFRatingIndex")] diff --git a/SharedLibraryCore/Migrations/MySql/20201114232340_UpdateEFRatingIndex.cs b/Data/Migrations/MySql/20201114232340_UpdateEFRatingIndex.cs similarity index 94% rename from SharedLibraryCore/Migrations/MySql/20201114232340_UpdateEFRatingIndex.cs rename to Data/Migrations/MySql/20201114232340_UpdateEFRatingIndex.cs index 510663d05..34b82fcd8 100644 --- a/SharedLibraryCore/Migrations/MySql/20201114232340_UpdateEFRatingIndex.cs +++ b/Data/Migrations/MySql/20201114232340_UpdateEFRatingIndex.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class UpdateEFRatingIndex : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs b/Data/Migrations/MySql/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs rename to Data/Migrations/MySql/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs index d807ea9be..67be6ad15 100644 --- a/SharedLibraryCore/Migrations/MySql/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs +++ b/Data/Migrations/MySql/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20201118023106_AddSentIngameFlagToClientMessage")] diff --git a/SharedLibraryCore/Migrations/MySql/20201118023106_AddSentIngameFlagToClientMessage.cs b/Data/Migrations/MySql/20201118023106_AddSentIngameFlagToClientMessage.cs similarity index 93% rename from SharedLibraryCore/Migrations/MySql/20201118023106_AddSentIngameFlagToClientMessage.cs rename to Data/Migrations/MySql/20201118023106_AddSentIngameFlagToClientMessage.cs index 56a27833d..3493dc756 100644 --- a/SharedLibraryCore/Migrations/MySql/20201118023106_AddSentIngameFlagToClientMessage.cs +++ b/Data/Migrations/MySql/20201118023106_AddSentIngameFlagToClientMessage.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class AddSentIngameFlagToClientMessage : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20201124024731_UpdateMigrationsToMySql.Designer.cs b/Data/Migrations/MySql/20201124024731_UpdateMigrationsToMySql.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20201124024731_UpdateMigrationsToMySql.Designer.cs rename to Data/Migrations/MySql/20201124024731_UpdateMigrationsToMySql.Designer.cs index ae78d67bc..c16642724 100644 --- a/SharedLibraryCore/Migrations/MySql/20201124024731_UpdateMigrationsToMySql.Designer.cs +++ b/Data/Migrations/MySql/20201124024731_UpdateMigrationsToMySql.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20201124024731_UpdateMigrationsToMySql")] diff --git a/SharedLibraryCore/Migrations/MySql/20201124024731_UpdateMigrationsToMySql.cs b/Data/Migrations/MySql/20201124024731_UpdateMigrationsToMySql.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20201124024731_UpdateMigrationsToMySql.cs rename to Data/Migrations/MySql/20201124024731_UpdateMigrationsToMySql.cs index 83b7e2a23..64815bffe 100644 --- a/SharedLibraryCore/Migrations/MySql/20201124024731_UpdateMigrationsToMySql.cs +++ b/Data/Migrations/MySql/20201124024731_UpdateMigrationsToMySql.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class UpdateMigrationsToMySql : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20210123023921_UpdateEFMetaToSupportNonClientMeta.Designer.cs b/Data/Migrations/MySql/20210123023921_UpdateEFMetaToSupportNonClientMeta.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20210123023921_UpdateEFMetaToSupportNonClientMeta.Designer.cs rename to Data/Migrations/MySql/20210123023921_UpdateEFMetaToSupportNonClientMeta.Designer.cs index 933e63984..a38ded01d 100644 --- a/SharedLibraryCore/Migrations/MySql/20210123023921_UpdateEFMetaToSupportNonClientMeta.Designer.cs +++ b/Data/Migrations/MySql/20210123023921_UpdateEFMetaToSupportNonClientMeta.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20210123023921_UpdateEFMetaToSupportNonClientMeta")] diff --git a/SharedLibraryCore/Migrations/MySql/20210123023921_UpdateEFMetaToSupportNonClientMeta.cs b/Data/Migrations/MySql/20210123023921_UpdateEFMetaToSupportNonClientMeta.cs similarity index 97% rename from SharedLibraryCore/Migrations/MySql/20210123023921_UpdateEFMetaToSupportNonClientMeta.cs rename to Data/Migrations/MySql/20210123023921_UpdateEFMetaToSupportNonClientMeta.cs index 1a748afd4..802891087 100644 --- a/SharedLibraryCore/Migrations/MySql/20210123023921_UpdateEFMetaToSupportNonClientMeta.cs +++ b/Data/Migrations/MySql/20210123023921_UpdateEFMetaToSupportNonClientMeta.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class UpdateEFMetaToSupportNonClientMeta : Migration { diff --git a/SharedLibraryCore/Migrations/MySql/20210124170830_UpdateEFMetaToSupportLinkedMeta.Designer.cs b/Data/Migrations/MySql/20210124170830_UpdateEFMetaToSupportLinkedMeta.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/MySql/20210124170830_UpdateEFMetaToSupportLinkedMeta.Designer.cs rename to Data/Migrations/MySql/20210124170830_UpdateEFMetaToSupportLinkedMeta.Designer.cs index 745b1b34a..bf502a4d1 100644 --- a/SharedLibraryCore/Migrations/MySql/20210124170830_UpdateEFMetaToSupportLinkedMeta.Designer.cs +++ b/Data/Migrations/MySql/20210124170830_UpdateEFMetaToSupportLinkedMeta.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] [Migration("20210124170830_UpdateEFMetaToSupportLinkedMeta")] diff --git a/SharedLibraryCore/Migrations/MySql/20210124170830_UpdateEFMetaToSupportLinkedMeta.cs b/Data/Migrations/MySql/20210124170830_UpdateEFMetaToSupportLinkedMeta.cs similarity index 96% rename from SharedLibraryCore/Migrations/MySql/20210124170830_UpdateEFMetaToSupportLinkedMeta.cs rename to Data/Migrations/MySql/20210124170830_UpdateEFMetaToSupportLinkedMeta.cs index 30e37b9b4..2794932d2 100644 --- a/SharedLibraryCore/Migrations/MySql/20210124170830_UpdateEFMetaToSupportLinkedMeta.cs +++ b/Data/Migrations/MySql/20210124170830_UpdateEFMetaToSupportLinkedMeta.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { public partial class UpdateEFMetaToSupportLinkedMeta : Migration { diff --git a/Data/Migrations/MySql/20210315222843_AddAdvancedStats.Designer.cs b/Data/Migrations/MySql/20210315222843_AddAdvancedStats.Designer.cs new file mode 100644 index 000000000..2af10efac --- /dev/null +++ b/Data/Migrations/MySql/20210315222843_AddAdvancedStats.Designer.cs @@ -0,0 +1,1280 @@ +// +using System; +using Data.MigrationContext; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Data.Migrations.MySql +{ + [DbContext(typeof(MySqlDatabaseContext))] + [Migration("20210315222843_AddAdvancedStats")] + partial class AddAdvancedStats + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.10") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b => + { + b.Property("ACSnapshotVector3Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("SnapshotId") + .HasColumnType("int"); + + b.Property("Vector3Id") + .HasColumnType("int"); + + b.HasKey("ACSnapshotVector3Id"); + + b.HasIndex("SnapshotId"); + + b.HasIndex("Vector3Id"); + + b.ToTable("EFACSnapshotVector3"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClient", b => + { + b.Property("ClientId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("AliasLinkId") + .HasColumnType("int"); + + b.Property("Connections") + .HasColumnType("int"); + + b.Property("CurrentAliasId") + .HasColumnType("int"); + + b.Property("FirstConnection") + .HasColumnType("datetime(6)"); + + b.Property("LastConnection") + .HasColumnType("datetime(6)"); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("Masked") + .HasColumnType("tinyint(1)"); + + b.Property("NetworkId") + .HasColumnType("bigint"); + + b.Property("Password") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("PasswordSalt") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("TotalConnectionTime") + .HasColumnType("int"); + + b.HasKey("ClientId"); + + b.HasIndex("AliasLinkId"); + + b.HasIndex("CurrentAliasId"); + + b.HasIndex("NetworkId") + .IsUnique(); + + b.ToTable("EFClients"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientKill", b => + { + b.Property("KillId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("AttackerId") + .HasColumnType("int"); + + b.Property("Damage") + .HasColumnType("int"); + + b.Property("DeathOriginVector3Id") + .HasColumnType("int"); + + b.Property("DeathType") + .HasColumnType("int"); + + b.Property("Fraction") + .HasColumnType("double"); + + b.Property("HitLoc") + .HasColumnType("int"); + + b.Property("IsKill") + .HasColumnType("tinyint(1)"); + + b.Property("KillOriginVector3Id") + .HasColumnType("int"); + + b.Property("Map") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("VictimId") + .HasColumnType("int"); + + b.Property("ViewAnglesVector3Id") + .HasColumnType("int"); + + b.Property("VisibilityPercentage") + .HasColumnType("double"); + + b.Property("Weapon") + .HasColumnType("int"); + + b.Property("When") + .HasColumnType("datetime(6)"); + + 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("Data.Models.Client.EFClientMessage", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("ClientId") + .HasColumnType("int"); + + b.Property("Message") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("SentIngame") + .HasColumnType("tinyint(1)"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("TimeSent") + .HasColumnType("datetime(6)"); + + b.HasKey("MessageId"); + + b.HasIndex("ClientId"); + + b.HasIndex("ServerId"); + + b.HasIndex("TimeSent"); + + b.ToTable("EFClientMessages"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b => + { + b.Property("SnapshotId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("ClientId") + .HasColumnType("int"); + + b.Property("CurrentSessionLength") + .HasColumnType("int"); + + b.Property("CurrentStrain") + .HasColumnType("double"); + + b.Property("CurrentViewAngleId") + .HasColumnType("int"); + + b.Property("Deaths") + .HasColumnType("int"); + + b.Property("Distance") + .HasColumnType("double"); + + b.Property("EloRating") + .HasColumnType("double"); + + b.Property("HitDestinationId") + .HasColumnType("int"); + + b.Property("HitLocation") + .HasColumnType("int"); + + b.Property("HitOriginId") + .HasColumnType("int"); + + b.Property("HitType") + .HasColumnType("int"); + + b.Property("Hits") + .HasColumnType("int"); + + b.Property("Kills") + .HasColumnType("int"); + + b.Property("LastStrainAngleId") + .HasColumnType("int"); + + b.Property("RecoilOffset") + .HasColumnType("double"); + + b.Property("SessionAngleOffset") + .HasColumnType("double"); + + b.Property("SessionAverageSnapValue") + .HasColumnType("double"); + + b.Property("SessionSPM") + .HasColumnType("double"); + + b.Property("SessionScore") + .HasColumnType("int"); + + b.Property("SessionSnapHits") + .HasColumnType("int"); + + b.Property("StrainAngleBetween") + .HasColumnType("double"); + + b.Property("TimeSinceLastEvent") + .HasColumnType("int"); + + b.Property("WeaponId") + .HasColumnType("int"); + + b.Property("When") + .HasColumnType("datetime(6)"); + + b.HasKey("SnapshotId"); + + b.HasIndex("ClientId"); + + b.HasIndex("CurrentViewAngleId"); + + b.HasIndex("HitDestinationId"); + + b.HasIndex("HitOriginId"); + + b.HasIndex("LastStrainAngleId"); + + b.ToTable("EFACSnapshot"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b => + { + b.Property("ClientHitStatisticId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ClientId") + .HasColumnType("int"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("DamageInflicted") + .HasColumnType("int"); + + b.Property("DamageReceived") + .HasColumnType("int"); + + b.Property("DeathCount") + .HasColumnType("int"); + + b.Property("HitCount") + .HasColumnType("int"); + + b.Property("HitLocationId") + .HasColumnType("int"); + + b.Property("KillCount") + .HasColumnType("int"); + + b.Property("MeansOfDeathId") + .HasColumnType("int"); + + b.Property("ReceivedHitCount") + .HasColumnType("int"); + + b.Property("Score") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("SuicideCount") + .HasColumnType("int"); + + b.Property("UpdatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("UsageSeconds") + .HasColumnType("int"); + + b.Property("WeaponAttachmentComboId") + .HasColumnType("int"); + + b.Property("WeaponId") + .HasColumnType("int"); + + b.HasKey("ClientHitStatisticId"); + + b.HasIndex("ClientId"); + + b.HasIndex("HitLocationId"); + + b.HasIndex("MeansOfDeathId"); + + b.HasIndex("ServerId"); + + b.HasIndex("WeaponAttachmentComboId"); + + b.HasIndex("WeaponId"); + + b.ToTable("EFClientHitStatistics"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRankingHistory", b => + { + b.Property("ClientRankingHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ClientId") + .HasColumnType("int"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Newest") + .HasColumnType("tinyint(1)"); + + b.Property("PerformanceMetric") + .HasColumnType("double"); + + b.Property("Ranking") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("UpdatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("ZScore") + .HasColumnType("double"); + + b.HasKey("ClientRankingHistoryId"); + + b.HasIndex("ClientId"); + + b.HasIndex("Ranking"); + + b.HasIndex("ServerId"); + + b.HasIndex("UpdatedDateTime"); + + b.HasIndex("ZScore"); + + b.ToTable("EFClientRankingHistory"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b => + { + b.Property("RatingHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("ClientId") + .HasColumnType("int"); + + b.HasKey("RatingHistoryId"); + + b.HasIndex("ClientId"); + + b.ToTable("EFClientRatingHistory"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b => + { + b.Property("ClientId") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("AverageSnapValue") + .HasColumnType("double"); + + b.Property("Deaths") + .HasColumnType("int"); + + b.Property("EloRating") + .HasColumnType("double"); + + b.Property("Kills") + .HasColumnType("int"); + + b.Property("MaxStrain") + .HasColumnType("double"); + + b.Property("RollingWeightedKDR") + .HasColumnType("double"); + + b.Property("SPM") + .HasColumnType("double"); + + b.Property("Skill") + .HasColumnType("double"); + + b.Property("SnapHitCount") + .HasColumnType("int"); + + b.Property("TimePlayed") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ZScore") + .HasColumnType("double"); + + b.HasKey("ClientId", "ServerId"); + + b.HasIndex("ServerId"); + + b.HasIndex("ZScore"); + + b.HasIndex("ClientId", "TimePlayed", "ZScore"); + + b.ToTable("EFClientStatistics"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b => + { + b.Property("HitLocationCountId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("EFClientStatisticsClientId") + .HasColumnName("EFClientStatisticsClientId") + .HasColumnType("int"); + + b.Property("EFClientStatisticsServerId") + .HasColumnName("EFClientStatisticsServerId") + .HasColumnType("bigint"); + + b.Property("HitCount") + .HasColumnType("int"); + + b.Property("HitOffsetAverage") + .HasColumnType("float"); + + b.Property("Location") + .HasColumnType("int"); + + b.Property("MaxAngleDistance") + .HasColumnType("float"); + + b.HasKey("HitLocationCountId"); + + b.HasIndex("EFClientStatisticsServerId"); + + b.HasIndex("EFClientStatisticsClientId", "EFClientStatisticsServerId"); + + b.ToTable("EFHitLocationCounts"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b => + { + b.Property("RatingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("ActivityAmount") + .HasColumnType("int"); + + b.Property("Newest") + .HasColumnType("tinyint(1)"); + + b.Property("Performance") + .HasColumnType("double"); + + b.Property("Ranking") + .HasColumnType("int"); + + b.Property("RatingHistoryId") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("When") + .HasColumnType("datetime(6)"); + + b.HasKey("RatingId"); + + b.HasIndex("RatingHistoryId"); + + b.HasIndex("ServerId"); + + b.HasIndex("Performance", "Ranking", "When"); + + b.HasIndex("When", "ServerId", "Performance", "ActivityAmount"); + + b.ToTable("EFRating"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFHitLocation", b => + { + b.Property("HitLocationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255) CHARACTER SET utf8mb4"); + + b.Property("UpdatedDateTime") + .HasColumnType("datetime(6)"); + + b.HasKey("HitLocationId"); + + b.HasIndex("Name"); + + b.ToTable("EFHitLocations"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMap", b => + { + b.Property("MapId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("UpdatedDateTime") + .HasColumnType("datetime(6)"); + + b.HasKey("MapId"); + + b.ToTable("EFMaps"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMeansOfDeath", b => + { + b.Property("MeansOfDeathId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("UpdatedDateTime") + .HasColumnType("datetime(6)"); + + b.HasKey("MeansOfDeathId"); + + b.ToTable("EFMeansOfDeath"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeapon", b => + { + b.Property("WeaponId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255) CHARACTER SET utf8mb4"); + + b.Property("UpdatedDateTime") + .HasColumnType("datetime(6)"); + + b.HasKey("WeaponId"); + + b.HasIndex("Name"); + + b.ToTable("EFWeapons"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachment", b => + { + b.Property("WeaponAttachmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("UpdatedDateTime") + .HasColumnType("datetime(6)"); + + b.HasKey("WeaponAttachmentId"); + + b.ToTable("EFWeaponAttachments"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b => + { + b.Property("WeaponAttachmentComboId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Attachment1Id") + .HasColumnType("int"); + + b.Property("Attachment2Id") + .HasColumnType("int"); + + b.Property("Attachment3Id") + .HasColumnType("int"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("UpdatedDateTime") + .HasColumnType("datetime(6)"); + + b.HasKey("WeaponAttachmentComboId"); + + b.HasIndex("Attachment1Id"); + + b.HasIndex("Attachment2Id"); + + b.HasIndex("Attachment3Id"); + + b.ToTable("EFWeaponAttachmentCombos"); + }); + + modelBuilder.Entity("Data.Models.EFAlias", b => + { + b.Property("AliasId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("DateAdded") + .HasColumnType("datetime(6)"); + + b.Property("IPAddress") + .HasColumnType("int"); + + b.Property("LinkId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(24) CHARACTER SET utf8mb4") + .HasMaxLength(24); + + b.Property("SearchableName") + .HasColumnType("varchar(24) CHARACTER SET utf8mb4") + .HasMaxLength(24); + + b.HasKey("AliasId"); + + b.HasIndex("IPAddress"); + + b.HasIndex("LinkId"); + + b.HasIndex("Name"); + + b.HasIndex("SearchableName"); + + b.HasIndex("Name", "IPAddress") + .IsUnique(); + + b.ToTable("EFAlias"); + }); + + modelBuilder.Entity("Data.Models.EFAliasLink", b => + { + b.Property("AliasLinkId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.HasKey("AliasLinkId"); + + b.ToTable("EFAliasLinks"); + }); + + modelBuilder.Entity("Data.Models.EFChangeHistory", b => + { + b.Property("ChangeHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Comment") + .HasColumnType("varchar(128) CHARACTER SET utf8mb4") + .HasMaxLength(128); + + b.Property("CurrentValue") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("ImpersonationEntityId") + .HasColumnType("int"); + + b.Property("OriginEntityId") + .HasColumnType("int"); + + b.Property("PreviousValue") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("TargetEntityId") + .HasColumnType("int"); + + b.Property("TimeChanged") + .HasColumnType("datetime(6)"); + + b.Property("TypeOfChange") + .HasColumnType("int"); + + b.HasKey("ChangeHistoryId"); + + b.ToTable("EFChangeHistory"); + }); + + modelBuilder.Entity("Data.Models.EFMeta", b => + { + b.Property("MetaId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("ClientId") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("Extra") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("Key") + .IsRequired() + .HasColumnType("varchar(32) CHARACTER SET utf8mb4") + .HasMaxLength(32); + + b.Property("LinkedMetaId") + .HasColumnType("int"); + + b.Property("Updated") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.HasKey("MetaId"); + + b.HasIndex("ClientId"); + + b.HasIndex("Key"); + + b.HasIndex("LinkedMetaId"); + + b.ToTable("EFMeta"); + }); + + modelBuilder.Entity("Data.Models.EFPenalty", b => + { + b.Property("PenaltyId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("AutomatedOffense") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("Expires") + .HasColumnType("datetime(6)"); + + b.Property("IsEvadedOffense") + .HasColumnType("tinyint(1)"); + + b.Property("LinkId") + .HasColumnType("int"); + + b.Property("OffenderId") + .HasColumnType("int"); + + b.Property("Offense") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("PunisherId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("When") + .HasColumnType("datetime(6)"); + + b.HasKey("PenaltyId"); + + b.HasIndex("LinkId"); + + b.HasIndex("OffenderId"); + + b.HasIndex("PunisherId"); + + b.ToTable("EFPenalties"); + }); + + modelBuilder.Entity("Data.Models.Server.EFServer", b => + { + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("EndPoint") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("GameName") + .HasColumnType("int"); + + b.Property("HostName") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("IsPasswordProtected") + .HasColumnType("tinyint(1)"); + + b.Property("Port") + .HasColumnType("int"); + + b.HasKey("ServerId"); + + b.ToTable("EFServers"); + }); + + modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b => + { + b.Property("StatisticId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("TotalKills") + .HasColumnType("bigint"); + + b.Property("TotalPlayTime") + .HasColumnType("bigint"); + + b.HasKey("StatisticId"); + + b.HasIndex("ServerId"); + + b.ToTable("EFServerStatistics"); + }); + + modelBuilder.Entity("Data.Models.Vector3", b => + { + b.Property("Vector3Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("X") + .HasColumnType("float"); + + b.Property("Y") + .HasColumnType("float"); + + b.Property("Z") + .HasColumnType("float"); + + b.HasKey("Vector3Id"); + + b.ToTable("Vector3"); + }); + + modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b => + { + b.HasOne("Data.Models.Client.Stats.EFACSnapshot", "Snapshot") + .WithMany("PredictedViewAngles") + .HasForeignKey("SnapshotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "Vector") + .WithMany() + .HasForeignKey("Vector3Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.EFClient", b => + { + b.HasOne("Data.Models.EFAliasLink", "AliasLink") + .WithMany() + .HasForeignKey("AliasLinkId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.EFAlias", "CurrentAlias") + .WithMany() + .HasForeignKey("CurrentAliasId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientKill", b => + { + b.HasOne("Data.Models.Client.EFClient", "Attacker") + .WithMany() + .HasForeignKey("AttackerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "DeathOrigin") + .WithMany() + .HasForeignKey("DeathOriginVector3Id"); + + b.HasOne("Data.Models.Vector3", "KillOrigin") + .WithMany() + .HasForeignKey("KillOriginVector3Id"); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.EFClient", "Victim") + .WithMany() + .HasForeignKey("VictimId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "ViewAngles") + .WithMany() + .HasForeignKey("ViewAnglesVector3Id"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientMessage", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "CurrentViewAngle") + .WithMany() + .HasForeignKey("CurrentViewAngleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "HitDestination") + .WithMany() + .HasForeignKey("HitDestinationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "HitOrigin") + .WithMany() + .HasForeignKey("HitOriginId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "LastStrainAngle") + .WithMany() + .HasForeignKey("LastStrainAngleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.Reference.EFHitLocation", "HitLocation") + .WithMany() + .HasForeignKey("HitLocationId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFMeansOfDeath", "MeansOfDeath") + .WithMany() + .HasForeignKey("MeansOfDeathId"); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", "WeaponAttachmentCombo") + .WithMany() + .HasForeignKey("WeaponAttachmentComboId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeapon", "Weapon") + .WithMany() + .HasForeignKey("WeaponId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRankingHistory", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("EFClientStatisticsClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("EFClientStatisticsServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.EFClientStatistics", null) + .WithMany("HitLocations") + .HasForeignKey("EFClientStatisticsClientId", "EFClientStatisticsServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b => + { + b.HasOne("Data.Models.Client.Stats.EFClientRatingHistory", "RatingHistory") + .WithMany("Ratings") + .HasForeignKey("RatingHistoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b => + { + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment1") + .WithMany() + .HasForeignKey("Attachment1Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment2") + .WithMany() + .HasForeignKey("Attachment2Id"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment3") + .WithMany() + .HasForeignKey("Attachment3Id"); + }); + + modelBuilder.Entity("Data.Models.EFAlias", b => + { + b.HasOne("Data.Models.EFAliasLink", "Link") + .WithMany("Children") + .HasForeignKey("LinkId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.EFMeta", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany("Meta") + .HasForeignKey("ClientId"); + + b.HasOne("Data.Models.EFMeta", "LinkedMeta") + .WithMany() + .HasForeignKey("LinkedMetaId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("Data.Models.EFPenalty", b => + { + b.HasOne("Data.Models.EFAliasLink", "Link") + .WithMany("ReceivedPenalties") + .HasForeignKey("LinkId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.EFClient", "Offender") + .WithMany("ReceivedPenalties") + .HasForeignKey("OffenderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Data.Models.Client.EFClient", "Punisher") + .WithMany("AdministeredPenalties") + .HasForeignKey("PunisherId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b => + { + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Data/Migrations/MySql/20210315222843_AddAdvancedStats.cs b/Data/Migrations/MySql/20210315222843_AddAdvancedStats.cs new file mode 100644 index 000000000..f9bcc9f5d --- /dev/null +++ b/Data/Migrations/MySql/20210315222843_AddAdvancedStats.cs @@ -0,0 +1,391 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Data.Migrations.MySql +{ + public partial class AddAdvancedStats : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "AverageRecoilOffset", + table: "EFClientStatistics"); + + migrationBuilder.DropColumn( + name: "VisionAverage", + table: "EFClientStatistics"); + + migrationBuilder.AddColumn( + name: "UpdatedAt", + table: "EFClientStatistics", + nullable: true); + + migrationBuilder.AddColumn( + name: "ZScore", + table: "EFClientStatistics", + nullable: false, + defaultValue: 0.0); + + migrationBuilder.CreateTable( + name: "EFClientRankingHistory", + columns: table => new + { + ClientRankingHistoryId = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + ClientId = table.Column(nullable: false), + ServerId = table.Column(nullable: true), + Newest = table.Column(nullable: false), + Ranking = table.Column(nullable: true), + ZScore = table.Column(nullable: true), + PerformanceMetric = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EFClientRankingHistory", x => x.ClientRankingHistoryId); + table.ForeignKey( + name: "FK_EFClientRankingHistory_EFClients_ClientId", + column: x => x.ClientId, + principalTable: "EFClients", + principalColumn: "ClientId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_EFClientRankingHistory_EFServers_ServerId", + column: x => x.ServerId, + principalTable: "EFServers", + principalColumn: "ServerId", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "EFHitLocations", + columns: table => new + { + HitLocationId = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Name = table.Column(nullable: false), + Game = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EFHitLocations", x => x.HitLocationId); + }); + + migrationBuilder.CreateTable( + name: "EFMaps", + columns: table => new + { + MapId = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Name = table.Column(nullable: false), + Game = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EFMaps", x => x.MapId); + }); + + migrationBuilder.CreateTable( + name: "EFMeansOfDeath", + columns: table => new + { + MeansOfDeathId = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Name = table.Column(nullable: false), + Game = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EFMeansOfDeath", x => x.MeansOfDeathId); + }); + + migrationBuilder.CreateTable( + name: "EFWeaponAttachments", + columns: table => new + { + WeaponAttachmentId = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Name = table.Column(nullable: false), + Game = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EFWeaponAttachments", x => x.WeaponAttachmentId); + }); + + migrationBuilder.CreateTable( + name: "EFWeapons", + columns: table => new + { + WeaponId = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Name = table.Column(nullable: false), + Game = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EFWeapons", x => x.WeaponId); + }); + + migrationBuilder.CreateTable( + name: "EFWeaponAttachmentCombos", + columns: table => new + { + WeaponAttachmentComboId = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Game = table.Column(nullable: false), + Attachment1Id = table.Column(nullable: false), + Attachment2Id = table.Column(nullable: true), + Attachment3Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EFWeaponAttachmentCombos", x => x.WeaponAttachmentComboId); + table.ForeignKey( + name: "FK_EFWeaponAttachmentCombos_EFWeaponAttachments_Attachment1Id", + column: x => x.Attachment1Id, + principalTable: "EFWeaponAttachments", + principalColumn: "WeaponAttachmentId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_EFWeaponAttachmentCombos_EFWeaponAttachments_Attachment2Id", + column: x => x.Attachment2Id, + principalTable: "EFWeaponAttachments", + principalColumn: "WeaponAttachmentId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EFWeaponAttachmentCombos_EFWeaponAttachments_Attachment3Id", + column: x => x.Attachment3Id, + principalTable: "EFWeaponAttachments", + principalColumn: "WeaponAttachmentId", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "EFClientHitStatistics", + columns: table => new + { + ClientHitStatisticId = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + ClientId = table.Column(nullable: false), + ServerId = table.Column(nullable: true), + HitLocationId = table.Column(nullable: true), + MeansOfDeathId = table.Column(nullable: true), + WeaponId = table.Column(nullable: true), + WeaponAttachmentComboId = table.Column(nullable: true), + HitCount = table.Column(nullable: false), + KillCount = table.Column(nullable: false), + DamageInflicted = table.Column(nullable: false), + ReceivedHitCount = table.Column(nullable: false), + DeathCount = table.Column(nullable: false), + DamageReceived = table.Column(nullable: false), + SuicideCount = table.Column(nullable: false), + UsageSeconds = table.Column(nullable: true), + Score = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EFClientHitStatistics", x => x.ClientHitStatisticId); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFClients_ClientId", + column: x => x.ClientId, + principalTable: "EFClients", + principalColumn: "ClientId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFHitLocations_HitLocationId", + column: x => x.HitLocationId, + principalTable: "EFHitLocations", + principalColumn: "HitLocationId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFMeansOfDeath_MeansOfDeathId", + column: x => x.MeansOfDeathId, + principalTable: "EFMeansOfDeath", + principalColumn: "MeansOfDeathId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFServers_ServerId", + column: x => x.ServerId, + principalTable: "EFServers", + principalColumn: "ServerId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFWeaponAttachmentCombos_WeaponAttachm~", + column: x => x.WeaponAttachmentComboId, + principalTable: "EFWeaponAttachmentCombos", + principalColumn: "WeaponAttachmentComboId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFWeapons_WeaponId", + column: x => x.WeaponId, + principalTable: "EFWeapons", + principalColumn: "WeaponId", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_EFClientStatistics_ZScore", + table: "EFClientStatistics", + column: "ZScore"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientStatistics_ClientId_TimePlayed_ZScore", + table: "EFClientStatistics", + columns: new[] { "ClientId", "TimePlayed", "ZScore" }); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_ClientId", + table: "EFClientHitStatistics", + column: "ClientId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_HitLocationId", + table: "EFClientHitStatistics", + column: "HitLocationId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_MeansOfDeathId", + table: "EFClientHitStatistics", + column: "MeansOfDeathId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_ServerId", + table: "EFClientHitStatistics", + column: "ServerId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_WeaponAttachmentComboId", + table: "EFClientHitStatistics", + column: "WeaponAttachmentComboId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_WeaponId", + table: "EFClientHitStatistics", + column: "WeaponId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientRankingHistory_ClientId", + table: "EFClientRankingHistory", + column: "ClientId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientRankingHistory_Ranking", + table: "EFClientRankingHistory", + column: "Ranking"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientRankingHistory_ServerId", + table: "EFClientRankingHistory", + column: "ServerId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientRankingHistory_UpdatedDateTime", + table: "EFClientRankingHistory", + column: "UpdatedDateTime"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientRankingHistory_ZScore", + table: "EFClientRankingHistory", + column: "ZScore"); + + migrationBuilder.CreateIndex( + name: "IX_EFHitLocations_Name", + table: "EFHitLocations", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_EFWeaponAttachmentCombos_Attachment1Id", + table: "EFWeaponAttachmentCombos", + column: "Attachment1Id"); + + migrationBuilder.CreateIndex( + name: "IX_EFWeaponAttachmentCombos_Attachment2Id", + table: "EFWeaponAttachmentCombos", + column: "Attachment2Id"); + + migrationBuilder.CreateIndex( + name: "IX_EFWeaponAttachmentCombos_Attachment3Id", + table: "EFWeaponAttachmentCombos", + column: "Attachment3Id"); + + migrationBuilder.CreateIndex( + name: "IX_EFWeapons_Name", + table: "EFWeapons", + column: "Name"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "EFClientHitStatistics"); + + migrationBuilder.DropTable( + name: "EFClientRankingHistory"); + + migrationBuilder.DropTable( + name: "EFMaps"); + + migrationBuilder.DropTable( + name: "EFHitLocations"); + + migrationBuilder.DropTable( + name: "EFMeansOfDeath"); + + migrationBuilder.DropTable( + name: "EFWeaponAttachmentCombos"); + + migrationBuilder.DropTable( + name: "EFWeapons"); + + migrationBuilder.DropTable( + name: "EFWeaponAttachments"); + + migrationBuilder.DropIndex( + name: "IX_EFClientStatistics_ZScore", + table: "EFClientStatistics"); + + migrationBuilder.DropIndex( + name: "IX_EFClientStatistics_ClientId_TimePlayed_ZScore", + table: "EFClientStatistics"); + + migrationBuilder.DropColumn( + name: "UpdatedAt", + table: "EFClientStatistics"); + + migrationBuilder.DropColumn( + name: "ZScore", + table: "EFClientStatistics"); + + migrationBuilder.AddColumn( + name: "AverageRecoilOffset", + table: "EFClientStatistics", + type: "double", + nullable: false, + defaultValue: 0.0); + + migrationBuilder.AddColumn( + name: "VisionAverage", + table: "EFClientStatistics", + type: "double", + nullable: false, + defaultValue: 0.0); + } + } +} diff --git a/SharedLibraryCore/Migrations/MySql/MySqlDatabaseContextModelSnapshot.cs b/Data/Migrations/MySql/MySqlDatabaseContextModelSnapshot.cs similarity index 62% rename from SharedLibraryCore/Migrations/MySql/MySqlDatabaseContextModelSnapshot.cs rename to Data/Migrations/MySql/MySqlDatabaseContextModelSnapshot.cs index 933b9b726..39862696a 100644 --- a/SharedLibraryCore/Migrations/MySql/MySqlDatabaseContextModelSnapshot.cs +++ b/Data/Migrations/MySql/MySqlDatabaseContextModelSnapshot.cs @@ -1,11 +1,11 @@ // using System; +using Data.MigrationContext; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; -namespace SharedLibraryCore.Migrations.MySql +namespace Data.Migrations.MySql { [DbContext(typeof(MySqlDatabaseContext))] partial class MySqlDatabaseContextModelSnapshot : ModelSnapshot @@ -17,7 +17,191 @@ namespace SharedLibraryCore.Migrations.MySql .HasAnnotation("ProductVersion", "3.1.10") .HasAnnotation("Relational:MaxIdentifierLength", 64); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b => + modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b => + { + b.Property("ACSnapshotVector3Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("SnapshotId") + .HasColumnType("int"); + + b.Property("Vector3Id") + .HasColumnType("int"); + + b.HasKey("ACSnapshotVector3Id"); + + b.HasIndex("SnapshotId"); + + b.HasIndex("Vector3Id"); + + b.ToTable("EFACSnapshotVector3"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClient", b => + { + b.Property("ClientId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("AliasLinkId") + .HasColumnType("int"); + + b.Property("Connections") + .HasColumnType("int"); + + b.Property("CurrentAliasId") + .HasColumnType("int"); + + b.Property("FirstConnection") + .HasColumnType("datetime(6)"); + + b.Property("LastConnection") + .HasColumnType("datetime(6)"); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("Masked") + .HasColumnType("tinyint(1)"); + + b.Property("NetworkId") + .HasColumnType("bigint"); + + b.Property("Password") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("PasswordSalt") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("TotalConnectionTime") + .HasColumnType("int"); + + b.HasKey("ClientId"); + + b.HasIndex("AliasLinkId"); + + b.HasIndex("CurrentAliasId"); + + b.HasIndex("NetworkId") + .IsUnique(); + + b.ToTable("EFClients"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientKill", b => + { + b.Property("KillId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("AttackerId") + .HasColumnType("int"); + + b.Property("Damage") + .HasColumnType("int"); + + b.Property("DeathOriginVector3Id") + .HasColumnType("int"); + + b.Property("DeathType") + .HasColumnType("int"); + + b.Property("Fraction") + .HasColumnType("double"); + + b.Property("HitLoc") + .HasColumnType("int"); + + b.Property("IsKill") + .HasColumnType("tinyint(1)"); + + b.Property("KillOriginVector3Id") + .HasColumnType("int"); + + b.Property("Map") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("VictimId") + .HasColumnType("int"); + + b.Property("ViewAnglesVector3Id") + .HasColumnType("int"); + + b.Property("VisibilityPercentage") + .HasColumnType("double"); + + b.Property("Weapon") + .HasColumnType("int"); + + b.Property("When") + .HasColumnType("datetime(6)"); + + 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("Data.Models.Client.EFClientMessage", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("ClientId") + .HasColumnType("int"); + + b.Property("Message") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("SentIngame") + .HasColumnType("tinyint(1)"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("TimeSent") + .HasColumnType("datetime(6)"); + + b.HasKey("MessageId"); + + b.HasIndex("ClientId"); + + b.HasIndex("ServerId"); + + b.HasIndex("TimeSent"); + + b.ToTable("EFClientMessages"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b => { b.Property("SnapshotId") .ValueGeneratedOnAdd() @@ -113,137 +297,126 @@ namespace SharedLibraryCore.Migrations.MySql b.ToTable("EFACSnapshot"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshotVector3", b => + modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b => { - b.Property("ACSnapshotVector3Id") + b.Property("ClientHitStatisticId") .ValueGeneratedOnAdd() .HasColumnType("int"); - b.Property("Active") - .HasColumnType("tinyint(1)"); - - b.Property("SnapshotId") - .HasColumnType("int"); - - b.Property("Vector3Id") - .HasColumnType("int"); - - b.HasKey("ACSnapshotVector3Id"); - - b.HasIndex("SnapshotId"); - - b.HasIndex("Vector3Id"); - - b.ToTable("EFACSnapshotVector3"); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b => - { - b.Property("KillId") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - b.Property("Active") - .HasColumnType("tinyint(1)"); - - b.Property("AttackerId") - .HasColumnType("int"); - - b.Property("Damage") - .HasColumnType("int"); - - b.Property("DeathOriginVector3Id") - .HasColumnType("int"); - - b.Property("DeathType") - .HasColumnType("int"); - - b.Property("Fraction") - .HasColumnType("double"); - - b.Property("HitLoc") - .HasColumnType("int"); - - b.Property("IsKill") - .HasColumnType("tinyint(1)"); - - b.Property("KillOriginVector3Id") - .HasColumnType("int"); - - b.Property("Map") - .HasColumnType("int"); - - b.Property("ServerId") - .HasColumnType("bigint"); - - b.Property("VictimId") - .HasColumnType("int"); - - b.Property("ViewAnglesVector3Id") - .HasColumnType("int"); - - b.Property("VisibilityPercentage") - .HasColumnType("double"); - - b.Property("Weapon") - .HasColumnType("int"); - - b.Property("When") - .HasColumnType("datetime(6)"); - - 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() - .HasColumnType("bigint"); - - b.Property("Active") - .HasColumnType("tinyint(1)"); - b.Property("ClientId") .HasColumnType("int"); - b.Property("Message") - .HasColumnType("longtext CHARACTER SET utf8mb4"); - - b.Property("SentIngame") - .HasColumnType("tinyint(1)"); - - b.Property("ServerId") - .HasColumnType("bigint"); - - b.Property("TimeSent") + b.Property("CreatedDateTime") .HasColumnType("datetime(6)"); - b.HasKey("MessageId"); + b.Property("DamageInflicted") + .HasColumnType("int"); + + b.Property("DamageReceived") + .HasColumnType("int"); + + b.Property("DeathCount") + .HasColumnType("int"); + + b.Property("HitCount") + .HasColumnType("int"); + + b.Property("HitLocationId") + .HasColumnType("int"); + + b.Property("KillCount") + .HasColumnType("int"); + + b.Property("MeansOfDeathId") + .HasColumnType("int"); + + b.Property("ReceivedHitCount") + .HasColumnType("int"); + + b.Property("Score") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("SuicideCount") + .HasColumnType("int"); + + b.Property("UpdatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("UsageSeconds") + .HasColumnType("int"); + + b.Property("WeaponAttachmentComboId") + .HasColumnType("int"); + + b.Property("WeaponId") + .HasColumnType("int"); + + b.HasKey("ClientHitStatisticId"); b.HasIndex("ClientId"); + b.HasIndex("HitLocationId"); + + b.HasIndex("MeansOfDeathId"); + b.HasIndex("ServerId"); - b.HasIndex("TimeSent"); + b.HasIndex("WeaponAttachmentComboId"); - b.ToTable("EFClientMessages"); + b.HasIndex("WeaponId"); + + b.ToTable("EFClientHitStatistics"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b => + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRankingHistory", b => + { + b.Property("ClientRankingHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ClientId") + .HasColumnType("int"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Newest") + .HasColumnType("tinyint(1)"); + + b.Property("PerformanceMetric") + .HasColumnType("double"); + + b.Property("Ranking") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("UpdatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("ZScore") + .HasColumnType("double"); + + b.HasKey("ClientRankingHistoryId"); + + b.HasIndex("ClientId"); + + b.HasIndex("Ranking"); + + b.HasIndex("ServerId"); + + b.HasIndex("UpdatedDateTime"); + + b.HasIndex("ZScore"); + + b.ToTable("EFClientRankingHistory"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b => { b.Property("RatingHistoryId") .ValueGeneratedOnAdd() @@ -262,7 +435,7 @@ namespace SharedLibraryCore.Migrations.MySql b.ToTable("EFClientRatingHistory"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b => + modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b => { b.Property("ClientId") .HasColumnType("int"); @@ -273,9 +446,6 @@ namespace SharedLibraryCore.Migrations.MySql b.Property("Active") .HasColumnType("tinyint(1)"); - b.Property("AverageRecoilOffset") - .HasColumnType("double"); - b.Property("AverageSnapValue") .HasColumnType("double"); @@ -306,17 +476,24 @@ namespace SharedLibraryCore.Migrations.MySql b.Property("TimePlayed") .HasColumnType("int"); - b.Property("VisionAverage") + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ZScore") .HasColumnType("double"); b.HasKey("ClientId", "ServerId"); b.HasIndex("ServerId"); + b.HasIndex("ZScore"); + + b.HasIndex("ClientId", "TimePlayed", "ZScore"); + b.ToTable("EFClientStatistics"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b => + modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b => { b.Property("HitLocationCountId") .ValueGeneratedOnAdd() @@ -354,7 +531,7 @@ namespace SharedLibraryCore.Migrations.MySql b.ToTable("EFHitLocationCounts"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b => + modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b => { b.Property("RatingId") .ValueGeneratedOnAdd() @@ -397,60 +574,166 @@ namespace SharedLibraryCore.Migrations.MySql b.ToTable("EFRating"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b => + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFHitLocation", b => { - b.Property("ServerId") - .HasColumnType("bigint"); - - b.Property("Active") - .HasColumnType("tinyint(1)"); - - b.Property("EndPoint") - .HasColumnType("longtext CHARACTER SET utf8mb4"); - - b.Property("GameName") - .HasColumnType("int"); - - b.Property("HostName") - .HasColumnType("longtext CHARACTER SET utf8mb4"); - - b.Property("IsPasswordProtected") - .HasColumnType("tinyint(1)"); - - b.Property("Port") - .HasColumnType("int"); - - b.HasKey("ServerId"); - - b.ToTable("EFServers"); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b => - { - b.Property("StatisticId") + b.Property("HitLocationId") .ValueGeneratedOnAdd() .HasColumnType("int"); - b.Property("Active") - .HasColumnType("tinyint(1)"); + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); - b.Property("ServerId") - .HasColumnType("bigint"); + b.Property("Game") + .HasColumnType("int"); - b.Property("TotalKills") - .HasColumnType("bigint"); + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255) CHARACTER SET utf8mb4"); - b.Property("TotalPlayTime") - .HasColumnType("bigint"); + b.Property("UpdatedDateTime") + .HasColumnType("datetime(6)"); - b.HasKey("StatisticId"); + b.HasKey("HitLocationId"); - b.HasIndex("ServerId"); + b.HasIndex("Name"); - b.ToTable("EFServerStatistics"); + b.ToTable("EFHitLocations"); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b => + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMap", b => + { + b.Property("MapId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("UpdatedDateTime") + .HasColumnType("datetime(6)"); + + b.HasKey("MapId"); + + b.ToTable("EFMaps"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMeansOfDeath", b => + { + b.Property("MeansOfDeathId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("UpdatedDateTime") + .HasColumnType("datetime(6)"); + + b.HasKey("MeansOfDeathId"); + + b.ToTable("EFMeansOfDeath"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeapon", b => + { + b.Property("WeaponId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255) CHARACTER SET utf8mb4"); + + b.Property("UpdatedDateTime") + .HasColumnType("datetime(6)"); + + b.HasKey("WeaponId"); + + b.HasIndex("Name"); + + b.ToTable("EFWeapons"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachment", b => + { + b.Property("WeaponAttachmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("UpdatedDateTime") + .HasColumnType("datetime(6)"); + + b.HasKey("WeaponAttachmentId"); + + b.ToTable("EFWeaponAttachments"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b => + { + b.Property("WeaponAttachmentComboId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Attachment1Id") + .HasColumnType("int"); + + b.Property("Attachment2Id") + .HasColumnType("int"); + + b.Property("Attachment3Id") + .HasColumnType("int"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("UpdatedDateTime") + .HasColumnType("datetime(6)"); + + b.HasKey("WeaponAttachmentComboId"); + + b.HasIndex("Attachment1Id"); + + b.HasIndex("Attachment2Id"); + + b.HasIndex("Attachment3Id"); + + b.ToTable("EFWeaponAttachmentCombos"); + }); + + modelBuilder.Entity("Data.Models.EFAlias", b => { b.Property("AliasId") .ValueGeneratedOnAdd() @@ -493,7 +776,7 @@ namespace SharedLibraryCore.Migrations.MySql b.ToTable("EFAlias"); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b => + modelBuilder.Entity("Data.Models.EFAliasLink", b => { b.Property("AliasLinkId") .ValueGeneratedOnAdd() @@ -507,7 +790,7 @@ namespace SharedLibraryCore.Migrations.MySql b.ToTable("EFAliasLinks"); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b => + modelBuilder.Entity("Data.Models.EFChangeHistory", b => { b.Property("ChangeHistoryId") .ValueGeneratedOnAdd() @@ -546,61 +829,7 @@ namespace SharedLibraryCore.Migrations.MySql b.ToTable("EFChangeHistory"); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b => - { - b.Property("ClientId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Active") - .HasColumnType("tinyint(1)"); - - b.Property("AliasLinkId") - .HasColumnType("int"); - - b.Property("Connections") - .HasColumnType("int"); - - b.Property("CurrentAliasId") - .HasColumnType("int"); - - b.Property("FirstConnection") - .HasColumnType("datetime(6)"); - - b.Property("LastConnection") - .HasColumnType("datetime(6)"); - - b.Property("Level") - .HasColumnType("int"); - - b.Property("Masked") - .HasColumnType("tinyint(1)"); - - b.Property("NetworkId") - .HasColumnType("bigint"); - - b.Property("Password") - .HasColumnType("longtext CHARACTER SET utf8mb4"); - - b.Property("PasswordSalt") - .HasColumnType("longtext CHARACTER SET utf8mb4"); - - b.Property("TotalConnectionTime") - .HasColumnType("int"); - - b.HasKey("ClientId"); - - b.HasIndex("AliasLinkId"); - - b.HasIndex("CurrentAliasId"); - - b.HasIndex("NetworkId") - .IsUnique(); - - b.ToTable("EFClients"); - }); - - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b => + modelBuilder.Entity("Data.Models.EFMeta", b => { b.Property("MetaId") .ValueGeneratedOnAdd() @@ -644,7 +873,7 @@ namespace SharedLibraryCore.Migrations.MySql b.ToTable("EFMeta"); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b => + modelBuilder.Entity("Data.Models.EFPenalty", b => { b.Property("PenaltyId") .ValueGeneratedOnAdd() @@ -692,7 +921,60 @@ namespace SharedLibraryCore.Migrations.MySql b.ToTable("EFPenalties"); }); - modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b => + modelBuilder.Entity("Data.Models.Server.EFServer", b => + { + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("EndPoint") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("GameName") + .HasColumnType("int"); + + b.Property("HostName") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("IsPasswordProtected") + .HasColumnType("tinyint(1)"); + + b.Property("Port") + .HasColumnType("int"); + + b.HasKey("ServerId"); + + b.ToTable("EFServers"); + }); + + modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b => + { + b.Property("StatisticId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("TotalKills") + .HasColumnType("bigint"); + + b.Property("TotalPlayTime") + .HasColumnType("bigint"); + + b.HasKey("StatisticId"); + + b.HasIndex("ServerId"); + + b.ToTable("EFServerStatistics"); + }); + + modelBuilder.Entity("Data.Models.Vector3", b => { b.Property("Vector3Id") .ValueGeneratedOnAdd() @@ -712,225 +994,284 @@ namespace SharedLibraryCore.Migrations.MySql b.ToTable("Vector3"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b => + modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b => { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") - .WithMany() - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SharedLibraryCore.Helpers.Vector3", "CurrentViewAngle") - .WithMany() - .HasForeignKey("CurrentViewAngleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination") - .WithMany() - .HasForeignKey("HitDestinationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin") - .WithMany() - .HasForeignKey("HitOriginId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle") - .WithMany() - .HasForeignKey("LastStrainAngleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshotVector3", b => - { - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", "Snapshot") + b.HasOne("Data.Models.Client.Stats.EFACSnapshot", "Snapshot") .WithMany("PredictedViewAngles") .HasForeignKey("SnapshotId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("SharedLibraryCore.Helpers.Vector3", "Vector") + b.HasOne("Data.Models.Vector3", "Vector") .WithMany() .HasForeignKey("Vector3Id") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b => + modelBuilder.Entity("Data.Models.Client.EFClient", b => { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker") - .WithMany() - .HasForeignKey("AttackerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - 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) - .IsRequired(); - - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim") - .WithMany() - .HasForeignKey("VictimId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - 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) - .IsRequired(); - - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server") - .WithMany() - .HasForeignKey("ServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b => - { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") - .WithMany() - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b => - { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") - .WithMany() - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server") - .WithMany() - .HasForeignKey("ServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b => - { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") - .WithMany() - .HasForeignKey("EFClientStatisticsClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server") - .WithMany() - .HasForeignKey("EFClientStatisticsServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", null) - .WithMany("HitLocations") - .HasForeignKey("EFClientStatisticsClientId", "EFClientStatisticsServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b => - { - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", "RatingHistory") - .WithMany("Ratings") - .HasForeignKey("RatingHistoryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - 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) - .IsRequired(); - }); - - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b => - { - b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link") - .WithMany("Children") - .HasForeignKey("LinkId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - }); - - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b => - { - b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink") + b.HasOne("Data.Models.EFAliasLink", "AliasLink") .WithMany() .HasForeignKey("AliasLinkId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias") + b.HasOne("Data.Models.EFAlias", "CurrentAlias") .WithMany() .HasForeignKey("CurrentAliasId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b => + modelBuilder.Entity("Data.Models.Client.EFClientKill", b => { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") + b.HasOne("Data.Models.Client.EFClient", "Attacker") + .WithMany() + .HasForeignKey("AttackerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "DeathOrigin") + .WithMany() + .HasForeignKey("DeathOriginVector3Id"); + + b.HasOne("Data.Models.Vector3", "KillOrigin") + .WithMany() + .HasForeignKey("KillOriginVector3Id"); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.EFClient", "Victim") + .WithMany() + .HasForeignKey("VictimId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "ViewAngles") + .WithMany() + .HasForeignKey("ViewAnglesVector3Id"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientMessage", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "CurrentViewAngle") + .WithMany() + .HasForeignKey("CurrentViewAngleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "HitDestination") + .WithMany() + .HasForeignKey("HitDestinationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "HitOrigin") + .WithMany() + .HasForeignKey("HitOriginId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "LastStrainAngle") + .WithMany() + .HasForeignKey("LastStrainAngleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.Reference.EFHitLocation", "HitLocation") + .WithMany() + .HasForeignKey("HitLocationId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFMeansOfDeath", "MeansOfDeath") + .WithMany() + .HasForeignKey("MeansOfDeathId"); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", "WeaponAttachmentCombo") + .WithMany() + .HasForeignKey("WeaponAttachmentComboId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeapon", "Weapon") + .WithMany() + .HasForeignKey("WeaponId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRankingHistory", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("EFClientStatisticsClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("EFClientStatisticsServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.EFClientStatistics", null) + .WithMany("HitLocations") + .HasForeignKey("EFClientStatisticsClientId", "EFClientStatisticsServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b => + { + b.HasOne("Data.Models.Client.Stats.EFClientRatingHistory", "RatingHistory") + .WithMany("Ratings") + .HasForeignKey("RatingHistoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b => + { + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment1") + .WithMany() + .HasForeignKey("Attachment1Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment2") + .WithMany() + .HasForeignKey("Attachment2Id"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment3") + .WithMany() + .HasForeignKey("Attachment3Id"); + }); + + modelBuilder.Entity("Data.Models.EFAlias", b => + { + b.HasOne("Data.Models.EFAliasLink", "Link") + .WithMany("Children") + .HasForeignKey("LinkId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.EFMeta", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") .WithMany("Meta") .HasForeignKey("ClientId"); - b.HasOne("SharedLibraryCore.Database.Models.EFMeta", "LinkedMeta") + b.HasOne("Data.Models.EFMeta", "LinkedMeta") .WithMany() .HasForeignKey("LinkedMetaId") .OnDelete(DeleteBehavior.SetNull); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b => + modelBuilder.Entity("Data.Models.EFPenalty", b => { - b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link") + b.HasOne("Data.Models.EFAliasLink", "Link") .WithMany("ReceivedPenalties") .HasForeignKey("LinkId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender") + b.HasOne("Data.Models.Client.EFClient", "Offender") .WithMany("ReceivedPenalties") .HasForeignKey("OffenderId") .OnDelete(DeleteBehavior.Restrict) .IsRequired(); - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher") + b.HasOne("Data.Models.Client.EFClient", "Punisher") .WithMany("AdministeredPenalties") .HasForeignKey("PunisherId") .OnDelete(DeleteBehavior.Restrict) .IsRequired(); }); + + modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b => + { + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); #pragma warning restore 612, 618 } } diff --git a/SharedLibraryCore/Migrations/Postgresql/20180409183408_InitialCreate.Designer.cs b/Data/Migrations/Postgresql/20180409183408_InitialCreate.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180409183408_InitialCreate.Designer.cs rename to Data/Migrations/Postgresql/20180409183408_InitialCreate.Designer.cs index 8bbe0b2e5..3451fa60b 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180409183408_InitialCreate.Designer.cs +++ b/Data/Migrations/Postgresql/20180409183408_InitialCreate.Designer.cs @@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database; -using SharedLibraryCore.Database.MigrationContext; +using Data; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180409183408_InitialCreate")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180409183408_InitialCreate.cs b/Data/Migrations/Postgresql/20180409183408_InitialCreate.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180409183408_InitialCreate.cs rename to Data/Migrations/Postgresql/20180409183408_InitialCreate.cs index 4727c9271..875817e65 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180409183408_InitialCreate.cs +++ b/Data/Migrations/Postgresql/20180409183408_InitialCreate.cs @@ -4,7 +4,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class InitialCreate : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180502195450_Update.Designer.cs b/Data/Migrations/Postgresql/20180502195450_Update.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180502195450_Update.Designer.cs rename to Data/Migrations/Postgresql/20180502195450_Update.Designer.cs index 7650eb204..8e824e9be 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180502195450_Update.Designer.cs +++ b/Data/Migrations/Postgresql/20180502195450_Update.Designer.cs @@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database; -using SharedLibraryCore.Database.MigrationContext; +using Data; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180502195450_Update")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180502195450_Update.cs b/Data/Migrations/Postgresql/20180502195450_Update.cs similarity index 95% rename from SharedLibraryCore/Migrations/Sqlite/20180502195450_Update.cs rename to Data/Migrations/Postgresql/20180502195450_Update.cs index eebdb16d3..7bb8993e7 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180502195450_Update.cs +++ b/Data/Migrations/Postgresql/20180502195450_Update.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class Update : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180516023249_AddEloField.Designer.cs b/Data/Migrations/Postgresql/20180516023249_AddEloField.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180516023249_AddEloField.Designer.cs rename to Data/Migrations/Postgresql/20180516023249_AddEloField.Designer.cs index f887c6f45..a26a6fa59 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180516023249_AddEloField.Designer.cs +++ b/Data/Migrations/Postgresql/20180516023249_AddEloField.Designer.cs @@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database; -using SharedLibraryCore.Database.MigrationContext; +using Data; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180516023249_AddEloField")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180516023249_AddEloField.cs b/Data/Migrations/Postgresql/20180516023249_AddEloField.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20180516023249_AddEloField.cs rename to Data/Migrations/Postgresql/20180516023249_AddEloField.cs index 0b6eb37ae..c97174544 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180516023249_AddEloField.cs +++ b/Data/Migrations/Postgresql/20180516023249_AddEloField.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddEloField : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180517223349_AddRollingKDR.Designer.cs b/Data/Migrations/Postgresql/20180517223349_AddRollingKDR.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180517223349_AddRollingKDR.Designer.cs rename to Data/Migrations/Postgresql/20180517223349_AddRollingKDR.Designer.cs index 967395762..b8d9eff95 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180517223349_AddRollingKDR.Designer.cs +++ b/Data/Migrations/Postgresql/20180517223349_AddRollingKDR.Designer.cs @@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database; -using SharedLibraryCore.Database.MigrationContext; +using Data; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180517223349_AddRollingKDR")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180517223349_AddRollingKDR.cs b/Data/Migrations/Postgresql/20180517223349_AddRollingKDR.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20180517223349_AddRollingKDR.cs rename to Data/Migrations/Postgresql/20180517223349_AddRollingKDR.cs index 83b39456a..61f513f0b 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180517223349_AddRollingKDR.cs +++ b/Data/Migrations/Postgresql/20180517223349_AddRollingKDR.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddRollingKDR : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs b/Data/Migrations/Postgresql/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs rename to Data/Migrations/Postgresql/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs index fcf2a27e9..4c8f1831c 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs +++ b/Data/Migrations/Postgresql/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs @@ -5,10 +5,10 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180531212903_AddAutomatedOffenseAndRatingHistory")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180531212903_AddAutomatedOffenseAndRatingHistory.cs b/Data/Migrations/Postgresql/20180531212903_AddAutomatedOffenseAndRatingHistory.cs similarity index 98% rename from SharedLibraryCore/Migrations/Sqlite/20180531212903_AddAutomatedOffenseAndRatingHistory.cs rename to Data/Migrations/Postgresql/20180531212903_AddAutomatedOffenseAndRatingHistory.cs index 1b597008e..7f09e127a 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180531212903_AddAutomatedOffenseAndRatingHistory.cs +++ b/Data/Migrations/Postgresql/20180531212903_AddAutomatedOffenseAndRatingHistory.cs @@ -4,7 +4,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddAutomatedOffenseAndRatingHistory : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180601172317_AddActivityAmount.Designer.cs b/Data/Migrations/Postgresql/20180601172317_AddActivityAmount.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180601172317_AddActivityAmount.Designer.cs rename to Data/Migrations/Postgresql/20180601172317_AddActivityAmount.Designer.cs index a2baa4490..345efb90a 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180601172317_AddActivityAmount.Designer.cs +++ b/Data/Migrations/Postgresql/20180601172317_AddActivityAmount.Designer.cs @@ -5,10 +5,10 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180601172317_AddActivityAmount")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180601172317_AddActivityAmount.cs b/Data/Migrations/Postgresql/20180601172317_AddActivityAmount.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20180601172317_AddActivityAmount.cs rename to Data/Migrations/Postgresql/20180601172317_AddActivityAmount.cs index 61ab05185..91462a0c4 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180601172317_AddActivityAmount.cs +++ b/Data/Migrations/Postgresql/20180601172317_AddActivityAmount.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddActivityAmount : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180602041758_AddClientMeta.Designer.cs b/Data/Migrations/Postgresql/20180602041758_AddClientMeta.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180602041758_AddClientMeta.Designer.cs rename to Data/Migrations/Postgresql/20180602041758_AddClientMeta.Designer.cs index 7d17e01ec..0ed37dd26 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180602041758_AddClientMeta.Designer.cs +++ b/Data/Migrations/Postgresql/20180602041758_AddClientMeta.Designer.cs @@ -5,10 +5,10 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180602041758_AddClientMeta")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180602041758_AddClientMeta.cs b/Data/Migrations/Postgresql/20180602041758_AddClientMeta.cs similarity index 97% rename from SharedLibraryCore/Migrations/Sqlite/20180602041758_AddClientMeta.cs rename to Data/Migrations/Postgresql/20180602041758_AddClientMeta.cs index 5fff4b5cc..0a0e53369 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180602041758_AddClientMeta.cs +++ b/Data/Migrations/Postgresql/20180602041758_AddClientMeta.cs @@ -4,7 +4,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddClientMeta : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180605191706_AddEFACSnapshots.Designer.cs b/Data/Migrations/Postgresql/20180605191706_AddEFACSnapshots.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180605191706_AddEFACSnapshots.Designer.cs rename to Data/Migrations/Postgresql/20180605191706_AddEFACSnapshots.Designer.cs index dde715ca6..7638414cf 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180605191706_AddEFACSnapshots.Designer.cs +++ b/Data/Migrations/Postgresql/20180605191706_AddEFACSnapshots.Designer.cs @@ -5,10 +5,10 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180605191706_AddEFACSnapshots")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180605191706_AddEFACSnapshots.cs b/Data/Migrations/Postgresql/20180605191706_AddEFACSnapshots.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180605191706_AddEFACSnapshots.cs rename to Data/Migrations/Postgresql/20180605191706_AddEFACSnapshots.cs index bdf00e1cd..392958600 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180605191706_AddEFACSnapshots.cs +++ b/Data/Migrations/Postgresql/20180605191706_AddEFACSnapshots.cs @@ -4,7 +4,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddEFACSnapshots : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180614014303_IndexForEFAlias.Designer.cs b/Data/Migrations/Postgresql/20180614014303_IndexForEFAlias.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180614014303_IndexForEFAlias.Designer.cs rename to Data/Migrations/Postgresql/20180614014303_IndexForEFAlias.Designer.cs index b55a67c92..350e09073 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180614014303_IndexForEFAlias.Designer.cs +++ b/Data/Migrations/Postgresql/20180614014303_IndexForEFAlias.Designer.cs @@ -5,10 +5,10 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180614014303_IndexForEFAlias")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180614014303_IndexForEFAlias.cs b/Data/Migrations/Postgresql/20180614014303_IndexForEFAlias.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20180614014303_IndexForEFAlias.cs rename to Data/Migrations/Postgresql/20180614014303_IndexForEFAlias.cs index de188ae22..023cbc322 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180614014303_IndexForEFAlias.cs +++ b/Data/Migrations/Postgresql/20180614014303_IndexForEFAlias.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class IndexForEFAlias : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180902035612_AddFractionAndIsKill.Designer.cs b/Data/Migrations/Postgresql/20180902035612_AddFractionAndIsKill.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180902035612_AddFractionAndIsKill.Designer.cs rename to Data/Migrations/Postgresql/20180902035612_AddFractionAndIsKill.Designer.cs index 454d1a420..ae0d6e457 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180902035612_AddFractionAndIsKill.Designer.cs +++ b/Data/Migrations/Postgresql/20180902035612_AddFractionAndIsKill.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180902035612_AddFractionAndIsKill")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180902035612_AddFractionAndIsKill.cs b/Data/Migrations/Postgresql/20180902035612_AddFractionAndIsKill.cs similarity index 98% rename from SharedLibraryCore/Migrations/Sqlite/20180902035612_AddFractionAndIsKill.cs rename to Data/Migrations/Postgresql/20180902035612_AddFractionAndIsKill.cs index be6423b47..078e7ed7e 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180902035612_AddFractionAndIsKill.cs +++ b/Data/Migrations/Postgresql/20180902035612_AddFractionAndIsKill.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddFractionAndIsKill : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180904154622_AddVisibilityPercentage.Designer.cs b/Data/Migrations/Postgresql/20180904154622_AddVisibilityPercentage.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180904154622_AddVisibilityPercentage.Designer.cs rename to Data/Migrations/Postgresql/20180904154622_AddVisibilityPercentage.Designer.cs index afba07428..8399d5c5f 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180904154622_AddVisibilityPercentage.Designer.cs +++ b/Data/Migrations/Postgresql/20180904154622_AddVisibilityPercentage.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180904154622_AddVisibilityPercentage")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180904154622_AddVisibilityPercentage.cs b/Data/Migrations/Postgresql/20180904154622_AddVisibilityPercentage.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20180904154622_AddVisibilityPercentage.cs rename to Data/Migrations/Postgresql/20180904154622_AddVisibilityPercentage.cs index 01407acca..edf762e2a 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180904154622_AddVisibilityPercentage.cs +++ b/Data/Migrations/Postgresql/20180904154622_AddVisibilityPercentage.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddVisibilityPercentage : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180907020706_AddVision.Designer.cs b/Data/Migrations/Postgresql/20180907020706_AddVision.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180907020706_AddVision.Designer.cs rename to Data/Migrations/Postgresql/20180907020706_AddVision.Designer.cs index eb6d0fcb3..1c27a8290 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180907020706_AddVision.Designer.cs +++ b/Data/Migrations/Postgresql/20180907020706_AddVision.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180907020706_AddVision")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180907020706_AddVision.cs b/Data/Migrations/Postgresql/20180907020706_AddVision.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20180907020706_AddVision.cs rename to Data/Migrations/Postgresql/20180907020706_AddVision.cs index 80ac8fe61..e8ad8ca93 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180907020706_AddVision.cs +++ b/Data/Migrations/Postgresql/20180907020706_AddVision.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddVision : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180908004053_AddWhenToRating.Designer.cs b/Data/Migrations/Postgresql/20180908004053_AddWhenToRating.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180908004053_AddWhenToRating.Designer.cs rename to Data/Migrations/Postgresql/20180908004053_AddWhenToRating.Designer.cs index 9411e8d12..83dfcb779 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180908004053_AddWhenToRating.Designer.cs +++ b/Data/Migrations/Postgresql/20180908004053_AddWhenToRating.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180908004053_AddWhenToRating")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180908004053_AddWhenToRating.cs b/Data/Migrations/Postgresql/20180908004053_AddWhenToRating.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20180908004053_AddWhenToRating.cs rename to Data/Migrations/Postgresql/20180908004053_AddWhenToRating.cs index 1af6caa45..2a1ee3986 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180908004053_AddWhenToRating.cs +++ b/Data/Migrations/Postgresql/20180908004053_AddWhenToRating.cs @@ -1,7 +1,7 @@ using System; using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddWhenToRating : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180910221749_AddRatingIndexes.Designer.cs b/Data/Migrations/Postgresql/20180910221749_AddRatingIndexes.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180910221749_AddRatingIndexes.Designer.cs rename to Data/Migrations/Postgresql/20180910221749_AddRatingIndexes.Designer.cs index 06dfdf360..60b19bf80 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180910221749_AddRatingIndexes.Designer.cs +++ b/Data/Migrations/Postgresql/20180910221749_AddRatingIndexes.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180910221749_AddRatingIndexes")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180910221749_AddRatingIndexes.cs b/Data/Migrations/Postgresql/20180910221749_AddRatingIndexes.cs similarity index 96% rename from SharedLibraryCore/Migrations/Sqlite/20180910221749_AddRatingIndexes.cs rename to Data/Migrations/Postgresql/20180910221749_AddRatingIndexes.cs index 5f634a086..0bf904ba7 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180910221749_AddRatingIndexes.cs +++ b/Data/Migrations/Postgresql/20180910221749_AddRatingIndexes.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddRatingIndexes : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180911184224_AddEFAliasNameIndex.Designer.cs b/Data/Migrations/Postgresql/20180911184224_AddEFAliasNameIndex.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180911184224_AddEFAliasNameIndex.Designer.cs rename to Data/Migrations/Postgresql/20180911184224_AddEFAliasNameIndex.Designer.cs index e8acef8c8..368de2c29 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180911184224_AddEFAliasNameIndex.Designer.cs +++ b/Data/Migrations/Postgresql/20180911184224_AddEFAliasNameIndex.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180911184224_AddEFAliasNameIndex")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180911184224_AddEFAliasNameIndex.cs b/Data/Migrations/Postgresql/20180911184224_AddEFAliasNameIndex.cs similarity index 92% rename from SharedLibraryCore/Migrations/Sqlite/20180911184224_AddEFAliasNameIndex.cs rename to Data/Migrations/Postgresql/20180911184224_AddEFAliasNameIndex.cs index ab34699e0..9eacd55e3 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180911184224_AddEFAliasNameIndex.cs +++ b/Data/Migrations/Postgresql/20180911184224_AddEFAliasNameIndex.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddEFAliasNameIndex : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180911190823_AddEFAliasNameMaxLength24.Designer.cs b/Data/Migrations/Postgresql/20180911190823_AddEFAliasNameMaxLength24.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180911190823_AddEFAliasNameMaxLength24.Designer.cs rename to Data/Migrations/Postgresql/20180911190823_AddEFAliasNameMaxLength24.Designer.cs index 8aabb7cc5..dc157f409 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180911190823_AddEFAliasNameMaxLength24.Designer.cs +++ b/Data/Migrations/Postgresql/20180911190823_AddEFAliasNameMaxLength24.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180911190823_AddEFAliasNameMaxLength24")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180911190823_AddEFAliasNameMaxLength24.cs b/Data/Migrations/Postgresql/20180911190823_AddEFAliasNameMaxLength24.cs similarity index 94% rename from SharedLibraryCore/Migrations/Sqlite/20180911190823_AddEFAliasNameMaxLength24.cs rename to Data/Migrations/Postgresql/20180911190823_AddEFAliasNameMaxLength24.cs index 6389196c8..6b7dedc9a 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180911190823_AddEFAliasNameMaxLength24.cs +++ b/Data/Migrations/Postgresql/20180911190823_AddEFAliasNameMaxLength24.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddEFAliasNameMaxLength24 : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs b/Data/Migrations/Postgresql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs rename to Data/Migrations/Postgresql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs index cb9cc3c0e..86563a508 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs +++ b/Data/Migrations/Postgresql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180912015012_AddPreviousCurrentValueToEFChangeHistory")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs b/Data/Migrations/Postgresql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs similarity index 95% rename from SharedLibraryCore/Migrations/Sqlite/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs rename to Data/Migrations/Postgresql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs index 195bbda0d..a73b27f91 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs +++ b/Data/Migrations/Postgresql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddPreviousCurrentValueToEFChangeHistory : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180915163111_AddIndexToMessageTimeSent.Designer.cs b/Data/Migrations/Postgresql/20180915163111_AddIndexToMessageTimeSent.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180915163111_AddIndexToMessageTimeSent.Designer.cs rename to Data/Migrations/Postgresql/20180915163111_AddIndexToMessageTimeSent.Designer.cs index 30151d621..2148bbbd1 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180915163111_AddIndexToMessageTimeSent.Designer.cs +++ b/Data/Migrations/Postgresql/20180915163111_AddIndexToMessageTimeSent.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180915163111_AddIndexToMessageTimeSent")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180915163111_AddIndexToMessageTimeSent.cs b/Data/Migrations/Postgresql/20180915163111_AddIndexToMessageTimeSent.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20180915163111_AddIndexToMessageTimeSent.cs rename to Data/Migrations/Postgresql/20180915163111_AddIndexToMessageTimeSent.cs index 9e82c11de..befe3b6ff 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180915163111_AddIndexToMessageTimeSent.cs +++ b/Data/Migrations/Postgresql/20180915163111_AddIndexToMessageTimeSent.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddIndexToMessageTimeSent : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180922231310_RemoveACSnapShot.Designer.cs b/Data/Migrations/Postgresql/20180922231310_RemoveACSnapShot.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180922231310_RemoveACSnapShot.Designer.cs rename to Data/Migrations/Postgresql/20180922231310_RemoveACSnapShot.Designer.cs index d87312272..e4abed3e1 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180922231310_RemoveACSnapShot.Designer.cs +++ b/Data/Migrations/Postgresql/20180922231310_RemoveACSnapShot.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180922231310_RemoveACSnapShot")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180922231310_RemoveACSnapShot.cs b/Data/Migrations/Postgresql/20180922231310_RemoveACSnapShot.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180922231310_RemoveACSnapShot.cs rename to Data/Migrations/Postgresql/20180922231310_RemoveACSnapShot.cs index 60d4622ae..d323c010c 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180922231310_RemoveACSnapShot.cs +++ b/Data/Migrations/Postgresql/20180922231310_RemoveACSnapShot.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class RemoveACSnapShot : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20180922231600_ReaddACSnapshot.Designer.cs b/Data/Migrations/Postgresql/20180922231600_ReaddACSnapshot.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180922231600_ReaddACSnapshot.Designer.cs rename to Data/Migrations/Postgresql/20180922231600_ReaddACSnapshot.Designer.cs index e7917a400..ee70ee2a0 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180922231600_ReaddACSnapshot.Designer.cs +++ b/Data/Migrations/Postgresql/20180922231600_ReaddACSnapshot.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20180922231600_ReaddACSnapshot")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20180922231600_ReaddACSnapshot.cs b/Data/Migrations/Postgresql/20180922231600_ReaddACSnapshot.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180922231600_ReaddACSnapshot.cs rename to Data/Migrations/Postgresql/20180922231600_ReaddACSnapshot.cs index 849f683d5..692464b13 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180922231600_ReaddACSnapshot.cs +++ b/Data/Migrations/Postgresql/20180922231600_ReaddACSnapshot.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class ReaddACSnapshot : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20181014171848_MakePenaltyExpirationNullable.Designer.cs b/Data/Migrations/Postgresql/20181014171848_MakePenaltyExpirationNullable.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20181014171848_MakePenaltyExpirationNullable.Designer.cs rename to Data/Migrations/Postgresql/20181014171848_MakePenaltyExpirationNullable.Designer.cs index 97e85323e..7074cdb42 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20181014171848_MakePenaltyExpirationNullable.Designer.cs +++ b/Data/Migrations/Postgresql/20181014171848_MakePenaltyExpirationNullable.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20181014171848_MakePenaltyExpirationNullable")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20181014171848_MakePenaltyExpirationNullable.cs b/Data/Migrations/Postgresql/20181014171848_MakePenaltyExpirationNullable.cs similarity index 98% rename from SharedLibraryCore/Migrations/Sqlite/20181014171848_MakePenaltyExpirationNullable.cs rename to Data/Migrations/Postgresql/20181014171848_MakePenaltyExpirationNullable.cs index 7a1815e58..7e8b135e6 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20181014171848_MakePenaltyExpirationNullable.cs +++ b/Data/Migrations/Postgresql/20181014171848_MakePenaltyExpirationNullable.cs @@ -1,7 +1,7 @@ using System; using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class MakePenaltyExpirationNullable : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20181125193243_MakeClientIPNullable.Designer.cs b/Data/Migrations/Postgresql/20181125193243_MakeClientIPNullable.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20181125193243_MakeClientIPNullable.Designer.cs rename to Data/Migrations/Postgresql/20181125193243_MakeClientIPNullable.Designer.cs index 9ce5daa41..87d84283e 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20181125193243_MakeClientIPNullable.Designer.cs +++ b/Data/Migrations/Postgresql/20181125193243_MakeClientIPNullable.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20181125193243_MakeClientIPNullable")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20181125193243_MakeClientIPNullable.cs b/Data/Migrations/Postgresql/20181125193243_MakeClientIPNullable.cs similarity index 98% rename from SharedLibraryCore/Migrations/Sqlite/20181125193243_MakeClientIPNullable.cs rename to Data/Migrations/Postgresql/20181125193243_MakeClientIPNullable.cs index 0d201fe55..d42d43bc2 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20181125193243_MakeClientIPNullable.cs +++ b/Data/Migrations/Postgresql/20181125193243_MakeClientIPNullable.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class MakeClientIPNullable : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs b/Data/Migrations/Postgresql/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs rename to Data/Migrations/Postgresql/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs index df4a6f0e5..df8769fa1 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs +++ b/Data/Migrations/Postgresql/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20181127144417_AddEndpointToEFServerUpdateServerIdType")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs b/Data/Migrations/Postgresql/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs similarity index 92% rename from SharedLibraryCore/Migrations/Sqlite/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs rename to Data/Migrations/Postgresql/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs index 284d5fa32..4316ea657 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs +++ b/Data/Migrations/Postgresql/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddEndpointToEFServerUpdateServerIdType : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20181216214513_AddEvadePenaltyFlag.Designer.cs b/Data/Migrations/Postgresql/20181216214513_AddEvadePenaltyFlag.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20181216214513_AddEvadePenaltyFlag.Designer.cs rename to Data/Migrations/Postgresql/20181216214513_AddEvadePenaltyFlag.Designer.cs index f006af9ea..57ec2be73 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20181216214513_AddEvadePenaltyFlag.Designer.cs +++ b/Data/Migrations/Postgresql/20181216214513_AddEvadePenaltyFlag.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20181216214513_AddEvadePenaltyFlag")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20181216214513_AddEvadePenaltyFlag.cs b/Data/Migrations/Postgresql/20181216214513_AddEvadePenaltyFlag.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20181216214513_AddEvadePenaltyFlag.cs rename to Data/Migrations/Postgresql/20181216214513_AddEvadePenaltyFlag.cs index 0d49cc07b..e28d3e18a 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20181216214513_AddEvadePenaltyFlag.cs +++ b/Data/Migrations/Postgresql/20181216214513_AddEvadePenaltyFlag.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddEvadePenaltyFlag : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs b/Data/Migrations/Postgresql/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs rename to Data/Migrations/Postgresql/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs index 81f027810..c6201e328 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs +++ b/Data/Migrations/Postgresql/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20190222234742_AddIndexToEFMeta-KeyAndClientId")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs b/Data/Migrations/Postgresql/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs similarity index 94% rename from SharedLibraryCore/Migrations/Sqlite/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs rename to Data/Migrations/Postgresql/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs index 10b7fbd91..7020b88b9 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs +++ b/Data/Migrations/Postgresql/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddIndexToEFMetaKeyAndClientId : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20190423142128_AddGameNameToEFServer.Designer.cs b/Data/Migrations/Postgresql/20190423142128_AddGameNameToEFServer.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20190423142128_AddGameNameToEFServer.Designer.cs rename to Data/Migrations/Postgresql/20190423142128_AddGameNameToEFServer.Designer.cs index f57d2e45b..7bc3148f1 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190423142128_AddGameNameToEFServer.Designer.cs +++ b/Data/Migrations/Postgresql/20190423142128_AddGameNameToEFServer.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20190423142128_AddGameNameToEFServer")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20190423142128_AddGameNameToEFServer.cs b/Data/Migrations/Postgresql/20190423142128_AddGameNameToEFServer.cs similarity index 92% rename from SharedLibraryCore/Migrations/Sqlite/20190423142128_AddGameNameToEFServer.cs rename to Data/Migrations/Postgresql/20190423142128_AddGameNameToEFServer.cs index ded914a99..a1213ae49 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190423142128_AddGameNameToEFServer.cs +++ b/Data/Migrations/Postgresql/20190423142128_AddGameNameToEFServer.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddGameNameToEFServer : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20190615145212_AddAvgRecoilOffset.Designer.cs b/Data/Migrations/Postgresql/20190615145212_AddAvgRecoilOffset.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20190615145212_AddAvgRecoilOffset.Designer.cs rename to Data/Migrations/Postgresql/20190615145212_AddAvgRecoilOffset.Designer.cs index 789c7b13e..7dd070395 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190615145212_AddAvgRecoilOffset.Designer.cs +++ b/Data/Migrations/Postgresql/20190615145212_AddAvgRecoilOffset.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20190615145212_AddAvgRecoilOffset")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20190615145212_AddAvgRecoilOffset.cs b/Data/Migrations/Postgresql/20190615145212_AddAvgRecoilOffset.cs similarity index 91% rename from SharedLibraryCore/Migrations/Sqlite/20190615145212_AddAvgRecoilOffset.cs rename to Data/Migrations/Postgresql/20190615145212_AddAvgRecoilOffset.cs index ccd978c6c..b773ce7a2 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190615145212_AddAvgRecoilOffset.cs +++ b/Data/Migrations/Postgresql/20190615145212_AddAvgRecoilOffset.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddAvgRecoilOffset : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs b/Data/Migrations/Postgresql/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs rename to Data/Migrations/Postgresql/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs index 54190afcf..3ba79fe28 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs +++ b/Data/Migrations/Postgresql/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20190615214055_AddRecoilOffsetToSnapshot")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20190615214055_AddRecoilOffsetToSnapshot.cs b/Data/Migrations/Postgresql/20190615214055_AddRecoilOffsetToSnapshot.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20190615214055_AddRecoilOffsetToSnapshot.cs rename to Data/Migrations/Postgresql/20190615214055_AddRecoilOffsetToSnapshot.cs index 5256281c2..b795ec0af 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190615214055_AddRecoilOffsetToSnapshot.cs +++ b/Data/Migrations/Postgresql/20190615214055_AddRecoilOffsetToSnapshot.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddRecoilOffsetToSnapshot : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20190725000309_AlterEFRatingIndex.Designer.cs b/Data/Migrations/Postgresql/20190725000309_AlterEFRatingIndex.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20190725000309_AlterEFRatingIndex.Designer.cs rename to Data/Migrations/Postgresql/20190725000309_AlterEFRatingIndex.Designer.cs index 52a495515..12e9502ed 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190725000309_AlterEFRatingIndex.Designer.cs +++ b/Data/Migrations/Postgresql/20190725000309_AlterEFRatingIndex.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20190725000309_AlterEFRatingIndex")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20190725000309_AlterEFRatingIndex.cs b/Data/Migrations/Postgresql/20190725000309_AlterEFRatingIndex.cs similarity index 97% rename from SharedLibraryCore/Migrations/Sqlite/20190725000309_AlterEFRatingIndex.cs rename to Data/Migrations/Postgresql/20190725000309_AlterEFRatingIndex.cs index 74d61590d..35227abd7 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190725000309_AlterEFRatingIndex.cs +++ b/Data/Migrations/Postgresql/20190725000309_AlterEFRatingIndex.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AlterEFRatingIndex : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20190802174908_AddSearchNameToEFAlias.Designer.cs b/Data/Migrations/Postgresql/20190802174908_AddSearchNameToEFAlias.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20190802174908_AddSearchNameToEFAlias.Designer.cs rename to Data/Migrations/Postgresql/20190802174908_AddSearchNameToEFAlias.Designer.cs index 02226b021..8c2ecafa1 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190802174908_AddSearchNameToEFAlias.Designer.cs +++ b/Data/Migrations/Postgresql/20190802174908_AddSearchNameToEFAlias.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20190802174908_AddSearchNameToEFAlias")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20190802174908_AddSearchNameToEFAlias.cs b/Data/Migrations/Postgresql/20190802174908_AddSearchNameToEFAlias.cs similarity index 95% rename from SharedLibraryCore/Migrations/Sqlite/20190802174908_AddSearchNameToEFAlias.cs rename to Data/Migrations/Postgresql/20190802174908_AddSearchNameToEFAlias.cs index c9dc245b5..ea9518eb1 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190802174908_AddSearchNameToEFAlias.cs +++ b/Data/Migrations/Postgresql/20190802174908_AddSearchNameToEFAlias.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddSearchNameToEFAlias : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20190831210503_AvgSnapValueToClientStatistics.Designer.cs b/Data/Migrations/Postgresql/20190831210503_AvgSnapValueToClientStatistics.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20190831210503_AvgSnapValueToClientStatistics.Designer.cs rename to Data/Migrations/Postgresql/20190831210503_AvgSnapValueToClientStatistics.Designer.cs index b0c541ff2..db84c9cd7 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190831210503_AvgSnapValueToClientStatistics.Designer.cs +++ b/Data/Migrations/Postgresql/20190831210503_AvgSnapValueToClientStatistics.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20190831210503_AvgSnapValueToClientStatistics")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20190831210503_AvgSnapValueToClientStatistics.cs b/Data/Migrations/Postgresql/20190831210503_AvgSnapValueToClientStatistics.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20190831210503_AvgSnapValueToClientStatistics.cs rename to Data/Migrations/Postgresql/20190831210503_AvgSnapValueToClientStatistics.cs index 944173d5d..63bc22857 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190831210503_AvgSnapValueToClientStatistics.cs +++ b/Data/Migrations/Postgresql/20190831210503_AvgSnapValueToClientStatistics.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AvgSnapValueToClientStatistics : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs b/Data/Migrations/Postgresql/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs rename to Data/Migrations/Postgresql/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs index c4100f24c..9073923b7 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs +++ b/Data/Migrations/Postgresql/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20190901180209_AddSnapHitCountToClientStatistics")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20190901180209_AddSnapHitCountToClientStatistics.cs b/Data/Migrations/Postgresql/20190901180209_AddSnapHitCountToClientStatistics.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20190901180209_AddSnapHitCountToClientStatistics.cs rename to Data/Migrations/Postgresql/20190901180209_AddSnapHitCountToClientStatistics.cs index 96c9161d3..68c7a0bc3 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190901180209_AddSnapHitCountToClientStatistics.cs +++ b/Data/Migrations/Postgresql/20190901180209_AddSnapHitCountToClientStatistics.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddSnapHitCountToClientStatistics : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs b/Data/Migrations/Postgresql/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs rename to Data/Migrations/Postgresql/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs index 631b0a20c..457053eac 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs +++ b/Data/Migrations/Postgresql/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20190901223620_UseJunctionTableForSnapshotVector3")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20190901223620_UseJunctionTableForSnapshotVector3.cs b/Data/Migrations/Postgresql/20190901223620_UseJunctionTableForSnapshotVector3.cs similarity index 98% rename from SharedLibraryCore/Migrations/Sqlite/20190901223620_UseJunctionTableForSnapshotVector3.cs rename to Data/Migrations/Postgresql/20190901223620_UseJunctionTableForSnapshotVector3.cs index b4a816b88..5ba630455 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190901223620_UseJunctionTableForSnapshotVector3.cs +++ b/Data/Migrations/Postgresql/20190901223620_UseJunctionTableForSnapshotVector3.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class UseJunctionTableForSnapshotVector3 : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs b/Data/Migrations/Postgresql/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs rename to Data/Migrations/Postgresql/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs index 9ffe59a63..662afbc40 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs +++ b/Data/Migrations/Postgresql/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20190914011524_AddCurrentSnapValueToSnapshot")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20190914011524_AddCurrentSnapValueToSnapshot.cs b/Data/Migrations/Postgresql/20190914011524_AddCurrentSnapValueToSnapshot.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20190914011524_AddCurrentSnapValueToSnapshot.cs rename to Data/Migrations/Postgresql/20190914011524_AddCurrentSnapValueToSnapshot.cs index 3dc2405b2..5a67832e9 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190914011524_AddCurrentSnapValueToSnapshot.cs +++ b/Data/Migrations/Postgresql/20190914011524_AddCurrentSnapValueToSnapshot.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddCurrentSnapValueToSnapshot : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs b/Data/Migrations/Postgresql/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs rename to Data/Migrations/Postgresql/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs index 8bcd882af..6ee298eed 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs +++ b/Data/Migrations/Postgresql/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20190914012015_AddSessionSnapHitsToSnapshot")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20190914012015_AddSessionSnapHitsToSnapshot.cs b/Data/Migrations/Postgresql/20190914012015_AddSessionSnapHitsToSnapshot.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20190914012015_AddSessionSnapHitsToSnapshot.cs rename to Data/Migrations/Postgresql/20190914012015_AddSessionSnapHitsToSnapshot.cs index 4f233b2fc..c867a1e1c 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190914012015_AddSessionSnapHitsToSnapshot.cs +++ b/Data/Migrations/Postgresql/20190914012015_AddSessionSnapHitsToSnapshot.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddSessionSnapHitsToSnapshot : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20191004172550_RenameClientHitLocationCountColumns.Designer.cs b/Data/Migrations/Postgresql/20191004172550_RenameClientHitLocationCountColumns.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20191004172550_RenameClientHitLocationCountColumns.Designer.cs rename to Data/Migrations/Postgresql/20191004172550_RenameClientHitLocationCountColumns.Designer.cs index fe2cc55a9..da6301d64 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20191004172550_RenameClientHitLocationCountColumns.Designer.cs +++ b/Data/Migrations/Postgresql/20191004172550_RenameClientHitLocationCountColumns.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20191004172550_RenameClientHitLocationCountColumns")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20191004172550_RenameClientHitLocationCountColumns.cs b/Data/Migrations/Postgresql/20191004172550_RenameClientHitLocationCountColumns.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20191004172550_RenameClientHitLocationCountColumns.cs rename to Data/Migrations/Postgresql/20191004172550_RenameClientHitLocationCountColumns.cs index 92c6fe9be..acb9222da 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20191004172550_RenameClientHitLocationCountColumns.cs +++ b/Data/Migrations/Postgresql/20191004172550_RenameClientHitLocationCountColumns.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class RenameClientHitLocationCountColumns : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs b/Data/Migrations/Postgresql/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs rename to Data/Migrations/Postgresql/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs index 84f64a95f..8eec0d3fc 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs +++ b/Data/Migrations/Postgresql/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20191030000713_EnforceUniqueIndexForEFAliasIPName")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs b/Data/Migrations/Postgresql/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs similarity index 98% rename from SharedLibraryCore/Migrations/Sqlite/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs rename to Data/Migrations/Postgresql/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs index 5e713f81a..38e762ff5 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs +++ b/Data/Migrations/Postgresql/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class EnforceUniqueIndexForEFAliasIPName : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs b/Data/Migrations/Postgresql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs rename to Data/Migrations/Postgresql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs index 525481e75..190001b9d 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs +++ b/Data/Migrations/Postgresql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs b/Data/Migrations/Postgresql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs similarity index 94% rename from SharedLibraryCore/Migrations/Sqlite/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs rename to Data/Migrations/Postgresql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs index 38b6b6199..c276533a2 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs +++ b/Data/Migrations/Postgresql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class SetCaseSensitiveCoallationForAliasNameMySQL : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20191230140947_AddMissingActiveColumns.Designer.cs b/Data/Migrations/Postgresql/20191230140947_AddMissingActiveColumns.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20191230140947_AddMissingActiveColumns.Designer.cs rename to Data/Migrations/Postgresql/20191230140947_AddMissingActiveColumns.Designer.cs index c6bf3eeec..3eb09d831 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20191230140947_AddMissingActiveColumns.Designer.cs +++ b/Data/Migrations/Postgresql/20191230140947_AddMissingActiveColumns.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20191230140947_AddMissingActiveColumns")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20191230140947_AddMissingActiveColumns.cs b/Data/Migrations/Postgresql/20191230140947_AddMissingActiveColumns.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20191230140947_AddMissingActiveColumns.cs rename to Data/Migrations/Postgresql/20191230140947_AddMissingActiveColumns.cs index 58ee70ea2..6e6262bcf 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20191230140947_AddMissingActiveColumns.cs +++ b/Data/Migrations/Postgresql/20191230140947_AddMissingActiveColumns.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddMissingActiveColumns : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs b/Data/Migrations/Postgresql/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs rename to Data/Migrations/Postgresql/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs index 5497e0c86..b268d8f43 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs +++ b/Data/Migrations/Postgresql/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20200423225137_AddImpersonationIdToEFChangeHistory")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20200423225137_AddImpersonationIdToEFChangeHistory.cs b/Data/Migrations/Postgresql/20200423225137_AddImpersonationIdToEFChangeHistory.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20200423225137_AddImpersonationIdToEFChangeHistory.cs rename to Data/Migrations/Postgresql/20200423225137_AddImpersonationIdToEFChangeHistory.cs index cadd981f8..9fde76623 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20200423225137_AddImpersonationIdToEFChangeHistory.cs +++ b/Data/Migrations/Postgresql/20200423225137_AddImpersonationIdToEFChangeHistory.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddImpersonationIdToEFChangeHistory : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20200521203304_AddHostnameToEFServer.Designer.cs b/Data/Migrations/Postgresql/20200521203304_AddHostnameToEFServer.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20200521203304_AddHostnameToEFServer.Designer.cs rename to Data/Migrations/Postgresql/20200521203304_AddHostnameToEFServer.Designer.cs index c25872e6d..62ee61cae 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20200521203304_AddHostnameToEFServer.Designer.cs +++ b/Data/Migrations/Postgresql/20200521203304_AddHostnameToEFServer.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20200521203304_AddHostnameToEFServer")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20200521203304_AddHostnameToEFServer.cs b/Data/Migrations/Postgresql/20200521203304_AddHostnameToEFServer.cs similarity index 92% rename from SharedLibraryCore/Migrations/Sqlite/20200521203304_AddHostnameToEFServer.cs rename to Data/Migrations/Postgresql/20200521203304_AddHostnameToEFServer.cs index 79c48c9b1..db12b317c 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20200521203304_AddHostnameToEFServer.cs +++ b/Data/Migrations/Postgresql/20200521203304_AddHostnameToEFServer.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddHostnameToEFServer : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20200819224119_AddIsPasswordProtectedColumn.Designer.cs b/Data/Migrations/Postgresql/20200819224119_AddIsPasswordProtectedColumn.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20200819224119_AddIsPasswordProtectedColumn.Designer.cs rename to Data/Migrations/Postgresql/20200819224119_AddIsPasswordProtectedColumn.Designer.cs index ac88256b0..ac8048329 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20200819224119_AddIsPasswordProtectedColumn.Designer.cs +++ b/Data/Migrations/Postgresql/20200819224119_AddIsPasswordProtectedColumn.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20200819224119_AddIsPasswordProtectedColumn")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20200819224119_AddIsPasswordProtectedColumn.cs b/Data/Migrations/Postgresql/20200819224119_AddIsPasswordProtectedColumn.cs similarity index 95% rename from SharedLibraryCore/Migrations/Sqlite/20200819224119_AddIsPasswordProtectedColumn.cs rename to Data/Migrations/Postgresql/20200819224119_AddIsPasswordProtectedColumn.cs index 506be9b75..a4d42d668 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20200819224119_AddIsPasswordProtectedColumn.cs +++ b/Data/Migrations/Postgresql/20200819224119_AddIsPasswordProtectedColumn.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddIsPasswordProtectedColumn : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20201114232340_UpdateEFRatingIndex.Designer.cs b/Data/Migrations/Postgresql/20201114232340_UpdateEFRatingIndex.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20201114232340_UpdateEFRatingIndex.Designer.cs rename to Data/Migrations/Postgresql/20201114232340_UpdateEFRatingIndex.Designer.cs index 9c968e9a5..7f5845123 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20201114232340_UpdateEFRatingIndex.Designer.cs +++ b/Data/Migrations/Postgresql/20201114232340_UpdateEFRatingIndex.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20201114232340_UpdateEFRatingIndex")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20201114232340_UpdateEFRatingIndex.cs b/Data/Migrations/Postgresql/20201114232340_UpdateEFRatingIndex.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20201114232340_UpdateEFRatingIndex.cs rename to Data/Migrations/Postgresql/20201114232340_UpdateEFRatingIndex.cs index 2f476b005..b6bdeb850 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20201114232340_UpdateEFRatingIndex.cs +++ b/Data/Migrations/Postgresql/20201114232340_UpdateEFRatingIndex.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class UpdateEFRatingIndex : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs b/Data/Migrations/Postgresql/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs rename to Data/Migrations/Postgresql/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs index 1aa80f9b8..f1cb660c0 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs +++ b/Data/Migrations/Postgresql/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20201118023106_AddSentIngameFlagToClientMessage")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20201118023106_AddSentIngameFlagToClientMessage.cs b/Data/Migrations/Postgresql/20201118023106_AddSentIngameFlagToClientMessage.cs similarity index 93% rename from SharedLibraryCore/Migrations/Sqlite/20201118023106_AddSentIngameFlagToClientMessage.cs rename to Data/Migrations/Postgresql/20201118023106_AddSentIngameFlagToClientMessage.cs index 48300e724..df056f482 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20201118023106_AddSentIngameFlagToClientMessage.cs +++ b/Data/Migrations/Postgresql/20201118023106_AddSentIngameFlagToClientMessage.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Postgresql { public partial class AddSentIngameFlagToClientMessage : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20201125160058_UpdateMigrationsForPostgresql.Designer.cs b/Data/Migrations/Postgresql/20201125160058_UpdateMigrationsForPostgresql.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20201125160058_UpdateMigrationsForPostgresql.Designer.cs rename to Data/Migrations/Postgresql/20201125160058_UpdateMigrationsForPostgresql.Designer.cs index 7f27b9c00..63d5b10fb 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20201125160058_UpdateMigrationsForPostgresql.Designer.cs +++ b/Data/Migrations/Postgresql/20201125160058_UpdateMigrationsForPostgresql.Designer.cs @@ -5,9 +5,9 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20201125160058_UpdateMigrationsForPostgresql")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20201125160058_UpdateMigrationsForPostgresql.cs b/Data/Migrations/Postgresql/20201125160058_UpdateMigrationsForPostgresql.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20201125160058_UpdateMigrationsForPostgresql.cs rename to Data/Migrations/Postgresql/20201125160058_UpdateMigrationsForPostgresql.cs index c44a50085..fb0cacfe1 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20201125160058_UpdateMigrationsForPostgresql.cs +++ b/Data/Migrations/Postgresql/20201125160058_UpdateMigrationsForPostgresql.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { public partial class UpdateMigrationsForPostgresql : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20210123024304_UpdateEFMetaToSupportNonClientMeta.Designer.cs b/Data/Migrations/Postgresql/20210123024304_UpdateEFMetaToSupportNonClientMeta.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20210123024304_UpdateEFMetaToSupportNonClientMeta.Designer.cs rename to Data/Migrations/Postgresql/20210123024304_UpdateEFMetaToSupportNonClientMeta.Designer.cs index cfb4d9e8d..bc582025f 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20210123024304_UpdateEFMetaToSupportNonClientMeta.Designer.cs +++ b/Data/Migrations/Postgresql/20210123024304_UpdateEFMetaToSupportNonClientMeta.Designer.cs @@ -5,9 +5,9 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20210123024304_UpdateEFMetaToSupportNonClientMeta")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20210123024304_UpdateEFMetaToSupportNonClientMeta.cs b/Data/Migrations/Postgresql/20210123024304_UpdateEFMetaToSupportNonClientMeta.cs similarity index 97% rename from SharedLibraryCore/Migrations/Postgresql/20210123024304_UpdateEFMetaToSupportNonClientMeta.cs rename to Data/Migrations/Postgresql/20210123024304_UpdateEFMetaToSupportNonClientMeta.cs index fc4f4aa30..67b80f102 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20210123024304_UpdateEFMetaToSupportNonClientMeta.cs +++ b/Data/Migrations/Postgresql/20210123024304_UpdateEFMetaToSupportNonClientMeta.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { public partial class UpdateEFMetaToSupportNonClientMeta : Migration { diff --git a/SharedLibraryCore/Migrations/Postgresql/20210124170956_UpdateEFMetaToSupportLinkedMeta.Designer.cs b/Data/Migrations/Postgresql/20210124170956_UpdateEFMetaToSupportLinkedMeta.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20210124170956_UpdateEFMetaToSupportLinkedMeta.Designer.cs rename to Data/Migrations/Postgresql/20210124170956_UpdateEFMetaToSupportLinkedMeta.Designer.cs index cd28418d2..f8357d9e2 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20210124170956_UpdateEFMetaToSupportLinkedMeta.Designer.cs +++ b/Data/Migrations/Postgresql/20210124170956_UpdateEFMetaToSupportLinkedMeta.Designer.cs @@ -5,9 +5,9 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] [Migration("20210124170956_UpdateEFMetaToSupportLinkedMeta")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20210124170956_UpdateEFMetaToSupportLinkedMeta.cs b/Data/Migrations/Postgresql/20210124170956_UpdateEFMetaToSupportLinkedMeta.cs similarity index 96% rename from SharedLibraryCore/Migrations/Postgresql/20210124170956_UpdateEFMetaToSupportLinkedMeta.cs rename to Data/Migrations/Postgresql/20210124170956_UpdateEFMetaToSupportLinkedMeta.cs index 55927c720..c05e87f3b 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20210124170956_UpdateEFMetaToSupportLinkedMeta.cs +++ b/Data/Migrations/Postgresql/20210124170956_UpdateEFMetaToSupportLinkedMeta.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { public partial class UpdateEFMetaToSupportLinkedMeta : Migration { diff --git a/Data/Migrations/Postgresql/20210316004759_AddAdvancedStats.Designer.cs b/Data/Migrations/Postgresql/20210316004759_AddAdvancedStats.Designer.cs new file mode 100644 index 000000000..4bbe438a0 --- /dev/null +++ b/Data/Migrations/Postgresql/20210316004759_AddAdvancedStats.Designer.cs @@ -0,0 +1,1305 @@ +// +using System; +using Data.MigrationContext; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace Data.Migrations.Postgresql +{ + [DbContext(typeof(PostgresqlDatabaseContext))] + [Migration("20210316004759_AddAdvancedStats")] + partial class AddAdvancedStats + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) + .HasAnnotation("ProductVersion", "3.1.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b => + { + b.Property("ACSnapshotVector3Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("SnapshotId") + .HasColumnType("integer"); + + b.Property("Vector3Id") + .HasColumnType("integer"); + + b.HasKey("ACSnapshotVector3Id"); + + b.HasIndex("SnapshotId"); + + b.HasIndex("Vector3Id"); + + b.ToTable("EFACSnapshotVector3"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClient", b => + { + b.Property("ClientId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("AliasLinkId") + .HasColumnType("integer"); + + b.Property("Connections") + .HasColumnType("integer"); + + b.Property("CurrentAliasId") + .HasColumnType("integer"); + + b.Property("FirstConnection") + .HasColumnType("timestamp without time zone"); + + b.Property("LastConnection") + .HasColumnType("timestamp without time zone"); + + b.Property("Level") + .HasColumnType("integer"); + + b.Property("Masked") + .HasColumnType("boolean"); + + b.Property("NetworkId") + .HasColumnType("bigint"); + + b.Property("Password") + .HasColumnType("text"); + + b.Property("PasswordSalt") + .HasColumnType("text"); + + b.Property("TotalConnectionTime") + .HasColumnType("integer"); + + b.HasKey("ClientId"); + + b.HasIndex("AliasLinkId"); + + b.HasIndex("CurrentAliasId"); + + b.HasIndex("NetworkId") + .IsUnique(); + + b.ToTable("EFClients"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientKill", b => + { + b.Property("KillId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("AttackerId") + .HasColumnType("integer"); + + b.Property("Damage") + .HasColumnType("integer"); + + b.Property("DeathOriginVector3Id") + .HasColumnType("integer"); + + b.Property("DeathType") + .HasColumnType("integer"); + + b.Property("Fraction") + .HasColumnType("double precision"); + + b.Property("HitLoc") + .HasColumnType("integer"); + + b.Property("IsKill") + .HasColumnType("boolean"); + + b.Property("KillOriginVector3Id") + .HasColumnType("integer"); + + b.Property("Map") + .HasColumnType("integer"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("VictimId") + .HasColumnType("integer"); + + b.Property("ViewAnglesVector3Id") + .HasColumnType("integer"); + + b.Property("VisibilityPercentage") + .HasColumnType("double precision"); + + b.Property("Weapon") + .HasColumnType("integer"); + + b.Property("When") + .HasColumnType("timestamp without time zone"); + + 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("Data.Models.Client.EFClientMessage", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("Message") + .HasColumnType("text"); + + b.Property("SentIngame") + .HasColumnType("boolean"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("TimeSent") + .HasColumnType("timestamp without time zone"); + + b.HasKey("MessageId"); + + b.HasIndex("ClientId"); + + b.HasIndex("ServerId"); + + b.HasIndex("TimeSent"); + + b.ToTable("EFClientMessages"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b => + { + b.Property("SnapshotId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("CurrentSessionLength") + .HasColumnType("integer"); + + b.Property("CurrentStrain") + .HasColumnType("double precision"); + + b.Property("CurrentViewAngleId") + .HasColumnType("integer"); + + b.Property("Deaths") + .HasColumnType("integer"); + + b.Property("Distance") + .HasColumnType("double precision"); + + b.Property("EloRating") + .HasColumnType("double precision"); + + b.Property("HitDestinationId") + .HasColumnType("integer"); + + b.Property("HitLocation") + .HasColumnType("integer"); + + b.Property("HitOriginId") + .HasColumnType("integer"); + + b.Property("HitType") + .HasColumnType("integer"); + + b.Property("Hits") + .HasColumnType("integer"); + + b.Property("Kills") + .HasColumnType("integer"); + + b.Property("LastStrainAngleId") + .HasColumnType("integer"); + + b.Property("RecoilOffset") + .HasColumnType("double precision"); + + b.Property("SessionAngleOffset") + .HasColumnType("double precision"); + + b.Property("SessionAverageSnapValue") + .HasColumnType("double precision"); + + b.Property("SessionSPM") + .HasColumnType("double precision"); + + b.Property("SessionScore") + .HasColumnType("integer"); + + b.Property("SessionSnapHits") + .HasColumnType("integer"); + + b.Property("StrainAngleBetween") + .HasColumnType("double precision"); + + b.Property("TimeSinceLastEvent") + .HasColumnType("integer"); + + b.Property("WeaponId") + .HasColumnType("integer"); + + b.Property("When") + .HasColumnType("timestamp without time zone"); + + b.HasKey("SnapshotId"); + + b.HasIndex("ClientId"); + + b.HasIndex("CurrentViewAngleId"); + + b.HasIndex("HitDestinationId"); + + b.HasIndex("HitOriginId"); + + b.HasIndex("LastStrainAngleId"); + + b.ToTable("EFACSnapshot"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b => + { + b.Property("ClientHitStatisticId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("CreatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("DamageInflicted") + .HasColumnType("integer"); + + b.Property("DamageReceived") + .HasColumnType("integer"); + + b.Property("DeathCount") + .HasColumnType("integer"); + + b.Property("HitCount") + .HasColumnType("integer"); + + b.Property("HitLocationId") + .HasColumnType("integer"); + + b.Property("KillCount") + .HasColumnType("integer"); + + b.Property("MeansOfDeathId") + .HasColumnType("integer"); + + b.Property("ReceivedHitCount") + .HasColumnType("integer"); + + b.Property("Score") + .HasColumnType("integer"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("SuicideCount") + .HasColumnType("integer"); + + b.Property("UpdatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("UsageSeconds") + .HasColumnType("integer"); + + b.Property("WeaponAttachmentComboId") + .HasColumnType("integer"); + + b.Property("WeaponId") + .HasColumnType("integer"); + + b.HasKey("ClientHitStatisticId"); + + b.HasIndex("ClientId"); + + b.HasIndex("HitLocationId"); + + b.HasIndex("MeansOfDeathId"); + + b.HasIndex("ServerId"); + + b.HasIndex("WeaponAttachmentComboId"); + + b.HasIndex("WeaponId"); + + b.ToTable("EFClientHitStatistics"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRankingHistory", b => + { + b.Property("ClientRankingHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("CreatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Newest") + .HasColumnType("boolean"); + + b.Property("PerformanceMetric") + .HasColumnType("double precision"); + + b.Property("Ranking") + .HasColumnType("integer"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("UpdatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ZScore") + .HasColumnType("double precision"); + + b.HasKey("ClientRankingHistoryId"); + + b.HasIndex("ClientId"); + + b.HasIndex("Ranking"); + + b.HasIndex("ServerId"); + + b.HasIndex("UpdatedDateTime"); + + b.HasIndex("ZScore"); + + b.ToTable("EFClientRankingHistory"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b => + { + b.Property("RatingHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.HasKey("RatingHistoryId"); + + b.HasIndex("ClientId"); + + b.ToTable("EFClientRatingHistory"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b => + { + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("AverageSnapValue") + .HasColumnType("double precision"); + + b.Property("Deaths") + .HasColumnType("integer"); + + b.Property("EloRating") + .HasColumnType("double precision"); + + b.Property("Kills") + .HasColumnType("integer"); + + b.Property("MaxStrain") + .HasColumnType("double precision"); + + b.Property("RollingWeightedKDR") + .HasColumnType("double precision"); + + b.Property("SPM") + .HasColumnType("double precision"); + + b.Property("Skill") + .HasColumnType("double precision"); + + b.Property("SnapHitCount") + .HasColumnType("integer"); + + b.Property("TimePlayed") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ZScore") + .HasColumnType("double precision"); + + b.HasKey("ClientId", "ServerId"); + + b.HasIndex("ServerId"); + + b.HasIndex("ZScore"); + + b.HasIndex("ClientId", "TimePlayed", "ZScore"); + + b.ToTable("EFClientStatistics"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b => + { + b.Property("HitLocationCountId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("EFClientStatisticsClientId") + .HasColumnName("EFClientStatisticsClientId") + .HasColumnType("integer"); + + b.Property("EFClientStatisticsServerId") + .HasColumnName("EFClientStatisticsServerId") + .HasColumnType("bigint"); + + b.Property("HitCount") + .HasColumnType("integer"); + + b.Property("HitOffsetAverage") + .HasColumnType("real"); + + b.Property("Location") + .HasColumnType("integer"); + + b.Property("MaxAngleDistance") + .HasColumnType("real"); + + b.HasKey("HitLocationCountId"); + + b.HasIndex("EFClientStatisticsServerId"); + + b.HasIndex("EFClientStatisticsClientId", "EFClientStatisticsServerId"); + + b.ToTable("EFHitLocationCounts"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b => + { + b.Property("RatingId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("ActivityAmount") + .HasColumnType("integer"); + + b.Property("Newest") + .HasColumnType("boolean"); + + b.Property("Performance") + .HasColumnType("double precision"); + + b.Property("Ranking") + .HasColumnType("integer"); + + b.Property("RatingHistoryId") + .HasColumnType("integer"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("When") + .HasColumnType("timestamp without time zone"); + + b.HasKey("RatingId"); + + b.HasIndex("RatingHistoryId"); + + b.HasIndex("ServerId"); + + b.HasIndex("Performance", "Ranking", "When"); + + b.HasIndex("When", "ServerId", "Performance", "ActivityAmount"); + + b.ToTable("EFRating"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFHitLocation", b => + { + b.Property("HitLocationId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("CreatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Game") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.HasKey("HitLocationId"); + + b.HasIndex("Name"); + + b.ToTable("EFHitLocations"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMap", b => + { + b.Property("MapId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("CreatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Game") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.HasKey("MapId"); + + b.ToTable("EFMaps"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMeansOfDeath", b => + { + b.Property("MeansOfDeathId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("CreatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Game") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.HasKey("MeansOfDeathId"); + + b.ToTable("EFMeansOfDeath"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeapon", b => + { + b.Property("WeaponId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("CreatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Game") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.HasKey("WeaponId"); + + b.HasIndex("Name"); + + b.ToTable("EFWeapons"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachment", b => + { + b.Property("WeaponAttachmentId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("CreatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Game") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.HasKey("WeaponAttachmentId"); + + b.ToTable("EFWeaponAttachments"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b => + { + b.Property("WeaponAttachmentComboId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Attachment1Id") + .HasColumnType("integer"); + + b.Property("Attachment2Id") + .HasColumnType("integer"); + + b.Property("Attachment3Id") + .HasColumnType("integer"); + + b.Property("CreatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Game") + .HasColumnType("integer"); + + b.Property("UpdatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.HasKey("WeaponAttachmentComboId"); + + b.HasIndex("Attachment1Id"); + + b.HasIndex("Attachment2Id"); + + b.HasIndex("Attachment3Id"); + + b.ToTable("EFWeaponAttachmentCombos"); + }); + + modelBuilder.Entity("Data.Models.EFAlias", b => + { + b.Property("AliasId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("DateAdded") + .HasColumnType("timestamp without time zone"); + + b.Property("IPAddress") + .HasColumnType("integer"); + + b.Property("LinkId") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("character varying(24)") + .HasMaxLength(24); + + b.Property("SearchableName") + .HasColumnType("character varying(24)") + .HasMaxLength(24); + + b.HasKey("AliasId"); + + b.HasIndex("IPAddress"); + + b.HasIndex("LinkId"); + + b.HasIndex("Name"); + + b.HasIndex("SearchableName"); + + b.HasIndex("Name", "IPAddress") + .IsUnique(); + + b.ToTable("EFAlias"); + }); + + modelBuilder.Entity("Data.Models.EFAliasLink", b => + { + b.Property("AliasLinkId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.HasKey("AliasLinkId"); + + b.ToTable("EFAliasLinks"); + }); + + modelBuilder.Entity("Data.Models.EFChangeHistory", b => + { + b.Property("ChangeHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("Comment") + .HasColumnType("character varying(128)") + .HasMaxLength(128); + + b.Property("CurrentValue") + .HasColumnType("text"); + + b.Property("ImpersonationEntityId") + .HasColumnType("integer"); + + b.Property("OriginEntityId") + .HasColumnType("integer"); + + b.Property("PreviousValue") + .HasColumnType("text"); + + b.Property("TargetEntityId") + .HasColumnType("integer"); + + b.Property("TimeChanged") + .HasColumnType("timestamp without time zone"); + + b.Property("TypeOfChange") + .HasColumnType("integer"); + + b.HasKey("ChangeHistoryId"); + + b.ToTable("EFChangeHistory"); + }); + + modelBuilder.Entity("Data.Models.EFMeta", b => + { + b.Property("MetaId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("Created") + .HasColumnType("timestamp without time zone"); + + b.Property("Extra") + .HasColumnType("text"); + + b.Property("Key") + .IsRequired() + .HasColumnType("character varying(32)") + .HasMaxLength(32); + + b.Property("LinkedMetaId") + .HasColumnType("integer"); + + b.Property("Updated") + .HasColumnType("timestamp without time zone"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("MetaId"); + + b.HasIndex("ClientId"); + + b.HasIndex("Key"); + + b.HasIndex("LinkedMetaId"); + + b.ToTable("EFMeta"); + }); + + modelBuilder.Entity("Data.Models.EFPenalty", b => + { + b.Property("PenaltyId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("AutomatedOffense") + .HasColumnType("text"); + + b.Property("Expires") + .HasColumnType("timestamp without time zone"); + + b.Property("IsEvadedOffense") + .HasColumnType("boolean"); + + b.Property("LinkId") + .HasColumnType("integer"); + + b.Property("OffenderId") + .HasColumnType("integer"); + + b.Property("Offense") + .IsRequired() + .HasColumnType("text"); + + b.Property("PunisherId") + .HasColumnType("integer"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("When") + .HasColumnType("timestamp without time zone"); + + b.HasKey("PenaltyId"); + + b.HasIndex("LinkId"); + + b.HasIndex("OffenderId"); + + b.HasIndex("PunisherId"); + + b.ToTable("EFPenalties"); + }); + + modelBuilder.Entity("Data.Models.Server.EFServer", b => + { + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("EndPoint") + .HasColumnType("text"); + + b.Property("GameName") + .HasColumnType("integer"); + + b.Property("HostName") + .HasColumnType("text"); + + b.Property("IsPasswordProtected") + .HasColumnType("boolean"); + + b.Property("Port") + .HasColumnType("integer"); + + b.HasKey("ServerId"); + + b.ToTable("EFServers"); + }); + + modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b => + { + b.Property("StatisticId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("TotalKills") + .HasColumnType("bigint"); + + b.Property("TotalPlayTime") + .HasColumnType("bigint"); + + b.HasKey("StatisticId"); + + b.HasIndex("ServerId"); + + b.ToTable("EFServerStatistics"); + }); + + modelBuilder.Entity("Data.Models.Vector3", b => + { + b.Property("Vector3Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("X") + .HasColumnType("real"); + + b.Property("Y") + .HasColumnType("real"); + + b.Property("Z") + .HasColumnType("real"); + + b.HasKey("Vector3Id"); + + b.ToTable("Vector3"); + }); + + modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b => + { + b.HasOne("Data.Models.Client.Stats.EFACSnapshot", "Snapshot") + .WithMany("PredictedViewAngles") + .HasForeignKey("SnapshotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "Vector") + .WithMany() + .HasForeignKey("Vector3Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.EFClient", b => + { + b.HasOne("Data.Models.EFAliasLink", "AliasLink") + .WithMany() + .HasForeignKey("AliasLinkId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.EFAlias", "CurrentAlias") + .WithMany() + .HasForeignKey("CurrentAliasId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientKill", b => + { + b.HasOne("Data.Models.Client.EFClient", "Attacker") + .WithMany() + .HasForeignKey("AttackerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "DeathOrigin") + .WithMany() + .HasForeignKey("DeathOriginVector3Id"); + + b.HasOne("Data.Models.Vector3", "KillOrigin") + .WithMany() + .HasForeignKey("KillOriginVector3Id"); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.EFClient", "Victim") + .WithMany() + .HasForeignKey("VictimId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "ViewAngles") + .WithMany() + .HasForeignKey("ViewAnglesVector3Id"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientMessage", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "CurrentViewAngle") + .WithMany() + .HasForeignKey("CurrentViewAngleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "HitDestination") + .WithMany() + .HasForeignKey("HitDestinationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "HitOrigin") + .WithMany() + .HasForeignKey("HitOriginId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "LastStrainAngle") + .WithMany() + .HasForeignKey("LastStrainAngleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.Reference.EFHitLocation", "HitLocation") + .WithMany() + .HasForeignKey("HitLocationId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFMeansOfDeath", "MeansOfDeath") + .WithMany() + .HasForeignKey("MeansOfDeathId"); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", "WeaponAttachmentCombo") + .WithMany() + .HasForeignKey("WeaponAttachmentComboId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeapon", "Weapon") + .WithMany() + .HasForeignKey("WeaponId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRankingHistory", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("EFClientStatisticsClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("EFClientStatisticsServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.EFClientStatistics", null) + .WithMany("HitLocations") + .HasForeignKey("EFClientStatisticsClientId", "EFClientStatisticsServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b => + { + b.HasOne("Data.Models.Client.Stats.EFClientRatingHistory", "RatingHistory") + .WithMany("Ratings") + .HasForeignKey("RatingHistoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b => + { + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment1") + .WithMany() + .HasForeignKey("Attachment1Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment2") + .WithMany() + .HasForeignKey("Attachment2Id"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment3") + .WithMany() + .HasForeignKey("Attachment3Id"); + }); + + modelBuilder.Entity("Data.Models.EFAlias", b => + { + b.HasOne("Data.Models.EFAliasLink", "Link") + .WithMany("Children") + .HasForeignKey("LinkId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.EFMeta", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany("Meta") + .HasForeignKey("ClientId"); + + b.HasOne("Data.Models.EFMeta", "LinkedMeta") + .WithMany() + .HasForeignKey("LinkedMetaId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("Data.Models.EFPenalty", b => + { + b.HasOne("Data.Models.EFAliasLink", "Link") + .WithMany("ReceivedPenalties") + .HasForeignKey("LinkId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.EFClient", "Offender") + .WithMany("ReceivedPenalties") + .HasForeignKey("OffenderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Data.Models.Client.EFClient", "Punisher") + .WithMany("AdministeredPenalties") + .HasForeignKey("PunisherId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b => + { + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Data/Migrations/Postgresql/20210316004759_AddAdvancedStats.cs b/Data/Migrations/Postgresql/20210316004759_AddAdvancedStats.cs new file mode 100644 index 000000000..dc966abba --- /dev/null +++ b/Data/Migrations/Postgresql/20210316004759_AddAdvancedStats.cs @@ -0,0 +1,391 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace Data.Migrations.Postgresql +{ + public partial class AddAdvancedStats : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "AverageRecoilOffset", + table: "EFClientStatistics"); + + migrationBuilder.DropColumn( + name: "VisionAverage", + table: "EFClientStatistics"); + + migrationBuilder.AddColumn( + name: "UpdatedAt", + table: "EFClientStatistics", + nullable: true); + + migrationBuilder.AddColumn( + name: "ZScore", + table: "EFClientStatistics", + nullable: false, + defaultValue: 0.0); + + migrationBuilder.CreateTable( + name: "EFClientRankingHistory", + columns: table => new + { + ClientRankingHistoryId = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + ClientId = table.Column(nullable: false), + ServerId = table.Column(nullable: true), + Newest = table.Column(nullable: false), + Ranking = table.Column(nullable: true), + ZScore = table.Column(nullable: true), + PerformanceMetric = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EFClientRankingHistory", x => x.ClientRankingHistoryId); + table.ForeignKey( + name: "FK_EFClientRankingHistory_EFClients_ClientId", + column: x => x.ClientId, + principalTable: "EFClients", + principalColumn: "ClientId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_EFClientRankingHistory_EFServers_ServerId", + column: x => x.ServerId, + principalTable: "EFServers", + principalColumn: "ServerId", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "EFHitLocations", + columns: table => new + { + HitLocationId = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Name = table.Column(nullable: false), + Game = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EFHitLocations", x => x.HitLocationId); + }); + + migrationBuilder.CreateTable( + name: "EFMaps", + columns: table => new + { + MapId = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Name = table.Column(nullable: false), + Game = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EFMaps", x => x.MapId); + }); + + migrationBuilder.CreateTable( + name: "EFMeansOfDeath", + columns: table => new + { + MeansOfDeathId = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Name = table.Column(nullable: false), + Game = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EFMeansOfDeath", x => x.MeansOfDeathId); + }); + + migrationBuilder.CreateTable( + name: "EFWeaponAttachments", + columns: table => new + { + WeaponAttachmentId = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Name = table.Column(nullable: false), + Game = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EFWeaponAttachments", x => x.WeaponAttachmentId); + }); + + migrationBuilder.CreateTable( + name: "EFWeapons", + columns: table => new + { + WeaponId = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Name = table.Column(nullable: false), + Game = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EFWeapons", x => x.WeaponId); + }); + + migrationBuilder.CreateTable( + name: "EFWeaponAttachmentCombos", + columns: table => new + { + WeaponAttachmentComboId = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Game = table.Column(nullable: false), + Attachment1Id = table.Column(nullable: false), + Attachment2Id = table.Column(nullable: true), + Attachment3Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EFWeaponAttachmentCombos", x => x.WeaponAttachmentComboId); + table.ForeignKey( + name: "FK_EFWeaponAttachmentCombos_EFWeaponAttachments_Attachment1Id", + column: x => x.Attachment1Id, + principalTable: "EFWeaponAttachments", + principalColumn: "WeaponAttachmentId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_EFWeaponAttachmentCombos_EFWeaponAttachments_Attachment2Id", + column: x => x.Attachment2Id, + principalTable: "EFWeaponAttachments", + principalColumn: "WeaponAttachmentId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EFWeaponAttachmentCombos_EFWeaponAttachments_Attachment3Id", + column: x => x.Attachment3Id, + principalTable: "EFWeaponAttachments", + principalColumn: "WeaponAttachmentId", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "EFClientHitStatistics", + columns: table => new + { + ClientHitStatisticId = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + ClientId = table.Column(nullable: false), + ServerId = table.Column(nullable: true), + HitLocationId = table.Column(nullable: true), + MeansOfDeathId = table.Column(nullable: true), + WeaponId = table.Column(nullable: true), + WeaponAttachmentComboId = table.Column(nullable: true), + HitCount = table.Column(nullable: false), + KillCount = table.Column(nullable: false), + DamageInflicted = table.Column(nullable: false), + ReceivedHitCount = table.Column(nullable: false), + DeathCount = table.Column(nullable: false), + DamageReceived = table.Column(nullable: false), + SuicideCount = table.Column(nullable: false), + UsageSeconds = table.Column(nullable: true), + Score = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EFClientHitStatistics", x => x.ClientHitStatisticId); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFClients_ClientId", + column: x => x.ClientId, + principalTable: "EFClients", + principalColumn: "ClientId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFHitLocations_HitLocationId", + column: x => x.HitLocationId, + principalTable: "EFHitLocations", + principalColumn: "HitLocationId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFMeansOfDeath_MeansOfDeathId", + column: x => x.MeansOfDeathId, + principalTable: "EFMeansOfDeath", + principalColumn: "MeansOfDeathId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFServers_ServerId", + column: x => x.ServerId, + principalTable: "EFServers", + principalColumn: "ServerId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFWeaponAttachmentCombos_WeaponAttach~", + column: x => x.WeaponAttachmentComboId, + principalTable: "EFWeaponAttachmentCombos", + principalColumn: "WeaponAttachmentComboId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFWeapons_WeaponId", + column: x => x.WeaponId, + principalTable: "EFWeapons", + principalColumn: "WeaponId", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_EFClientStatistics_ZScore", + table: "EFClientStatistics", + column: "ZScore"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientStatistics_ClientId_TimePlayed_ZScore", + table: "EFClientStatistics", + columns: new[] { "ClientId", "TimePlayed", "ZScore" }); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_ClientId", + table: "EFClientHitStatistics", + column: "ClientId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_HitLocationId", + table: "EFClientHitStatistics", + column: "HitLocationId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_MeansOfDeathId", + table: "EFClientHitStatistics", + column: "MeansOfDeathId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_ServerId", + table: "EFClientHitStatistics", + column: "ServerId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_WeaponAttachmentComboId", + table: "EFClientHitStatistics", + column: "WeaponAttachmentComboId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_WeaponId", + table: "EFClientHitStatistics", + column: "WeaponId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientRankingHistory_ClientId", + table: "EFClientRankingHistory", + column: "ClientId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientRankingHistory_Ranking", + table: "EFClientRankingHistory", + column: "Ranking"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientRankingHistory_ServerId", + table: "EFClientRankingHistory", + column: "ServerId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientRankingHistory_UpdatedDateTime", + table: "EFClientRankingHistory", + column: "UpdatedDateTime"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientRankingHistory_ZScore", + table: "EFClientRankingHistory", + column: "ZScore"); + + migrationBuilder.CreateIndex( + name: "IX_EFHitLocations_Name", + table: "EFHitLocations", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_EFWeaponAttachmentCombos_Attachment1Id", + table: "EFWeaponAttachmentCombos", + column: "Attachment1Id"); + + migrationBuilder.CreateIndex( + name: "IX_EFWeaponAttachmentCombos_Attachment2Id", + table: "EFWeaponAttachmentCombos", + column: "Attachment2Id"); + + migrationBuilder.CreateIndex( + name: "IX_EFWeaponAttachmentCombos_Attachment3Id", + table: "EFWeaponAttachmentCombos", + column: "Attachment3Id"); + + migrationBuilder.CreateIndex( + name: "IX_EFWeapons_Name", + table: "EFWeapons", + column: "Name"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "EFClientHitStatistics"); + + migrationBuilder.DropTable( + name: "EFClientRankingHistory"); + + migrationBuilder.DropTable( + name: "EFMaps"); + + migrationBuilder.DropTable( + name: "EFHitLocations"); + + migrationBuilder.DropTable( + name: "EFMeansOfDeath"); + + migrationBuilder.DropTable( + name: "EFWeaponAttachmentCombos"); + + migrationBuilder.DropTable( + name: "EFWeapons"); + + migrationBuilder.DropTable( + name: "EFWeaponAttachments"); + + migrationBuilder.DropIndex( + name: "IX_EFClientStatistics_ZScore", + table: "EFClientStatistics"); + + migrationBuilder.DropIndex( + name: "IX_EFClientStatistics_ClientId_TimePlayed_ZScore", + table: "EFClientStatistics"); + + migrationBuilder.DropColumn( + name: "UpdatedAt", + table: "EFClientStatistics"); + + migrationBuilder.DropColumn( + name: "ZScore", + table: "EFClientStatistics"); + + migrationBuilder.AddColumn( + name: "AverageRecoilOffset", + table: "EFClientStatistics", + type: "double precision", + nullable: false, + defaultValue: 0.0); + + migrationBuilder.AddColumn( + name: "VisionAverage", + table: "EFClientStatistics", + type: "double precision", + nullable: false, + defaultValue: 0.0); + } + } +} diff --git a/SharedLibraryCore/Migrations/Postgresql/PostgresqlDatabaseContextModelSnapshot.cs b/Data/Migrations/Postgresql/PostgresqlDatabaseContextModelSnapshot.cs similarity index 61% rename from SharedLibraryCore/Migrations/Postgresql/PostgresqlDatabaseContextModelSnapshot.cs rename to Data/Migrations/Postgresql/PostgresqlDatabaseContextModelSnapshot.cs index 0d4427530..85451b472 100644 --- a/SharedLibraryCore/Migrations/Postgresql/PostgresqlDatabaseContextModelSnapshot.cs +++ b/Data/Migrations/Postgresql/PostgresqlDatabaseContextModelSnapshot.cs @@ -1,12 +1,12 @@ // using System; +using Data.MigrationContext; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using SharedLibraryCore.Database.MigrationContext; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Postgresql { [DbContext(typeof(PostgresqlDatabaseContext))] partial class PostgresqlDatabaseContextModelSnapshot : ModelSnapshot @@ -15,16 +15,204 @@ namespace SharedLibraryCore.Migrations.Postgresql { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) .HasAnnotation("ProductVersion", "3.1.10") .HasAnnotation("Relational:MaxIdentifierLength", 63); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b => + modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b => + { + b.Property("ACSnapshotVector3Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("SnapshotId") + .HasColumnType("integer"); + + b.Property("Vector3Id") + .HasColumnType("integer"); + + b.HasKey("ACSnapshotVector3Id"); + + b.HasIndex("SnapshotId"); + + b.HasIndex("Vector3Id"); + + b.ToTable("EFACSnapshotVector3"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClient", b => + { + b.Property("ClientId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("AliasLinkId") + .HasColumnType("integer"); + + b.Property("Connections") + .HasColumnType("integer"); + + b.Property("CurrentAliasId") + .HasColumnType("integer"); + + b.Property("FirstConnection") + .HasColumnType("timestamp without time zone"); + + b.Property("LastConnection") + .HasColumnType("timestamp without time zone"); + + b.Property("Level") + .HasColumnType("integer"); + + b.Property("Masked") + .HasColumnType("boolean"); + + b.Property("NetworkId") + .HasColumnType("bigint"); + + b.Property("Password") + .HasColumnType("text"); + + b.Property("PasswordSalt") + .HasColumnType("text"); + + b.Property("TotalConnectionTime") + .HasColumnType("integer"); + + b.HasKey("ClientId"); + + b.HasIndex("AliasLinkId"); + + b.HasIndex("CurrentAliasId"); + + b.HasIndex("NetworkId") + .IsUnique(); + + b.ToTable("EFClients"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientKill", b => + { + b.Property("KillId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("AttackerId") + .HasColumnType("integer"); + + b.Property("Damage") + .HasColumnType("integer"); + + b.Property("DeathOriginVector3Id") + .HasColumnType("integer"); + + b.Property("DeathType") + .HasColumnType("integer"); + + b.Property("Fraction") + .HasColumnType("double precision"); + + b.Property("HitLoc") + .HasColumnType("integer"); + + b.Property("IsKill") + .HasColumnType("boolean"); + + b.Property("KillOriginVector3Id") + .HasColumnType("integer"); + + b.Property("Map") + .HasColumnType("integer"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("VictimId") + .HasColumnType("integer"); + + b.Property("ViewAnglesVector3Id") + .HasColumnType("integer"); + + b.Property("VisibilityPercentage") + .HasColumnType("double precision"); + + b.Property("Weapon") + .HasColumnType("integer"); + + b.Property("When") + .HasColumnType("timestamp without time zone"); + + 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("Data.Models.Client.EFClientMessage", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("Message") + .HasColumnType("text"); + + b.Property("SentIngame") + .HasColumnType("boolean"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("TimeSent") + .HasColumnType("timestamp without time zone"); + + b.HasKey("MessageId"); + + b.HasIndex("ClientId"); + + b.HasIndex("ServerId"); + + b.HasIndex("TimeSent"); + + b.ToTable("EFClientMessages"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b => { b.Property("SnapshotId") .ValueGeneratedOnAdd() .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); b.Property("Active") .HasColumnType("boolean"); @@ -116,145 +304,133 @@ namespace SharedLibraryCore.Migrations.Postgresql b.ToTable("EFACSnapshot"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshotVector3", b => + modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b => { - b.Property("ACSnapshotVector3Id") + b.Property("ClientHitStatisticId") .ValueGeneratedOnAdd() .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Active") - .HasColumnType("boolean"); - - b.Property("SnapshotId") - .HasColumnType("integer"); - - b.Property("Vector3Id") - .HasColumnType("integer"); - - b.HasKey("ACSnapshotVector3Id"); - - b.HasIndex("SnapshotId"); - - b.HasIndex("Vector3Id"); - - b.ToTable("EFACSnapshotVector3"); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b => - { - b.Property("KillId") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Active") - .HasColumnType("boolean"); - - b.Property("AttackerId") - .HasColumnType("integer"); - - b.Property("Damage") - .HasColumnType("integer"); - - b.Property("DeathOriginVector3Id") - .HasColumnType("integer"); - - b.Property("DeathType") - .HasColumnType("integer"); - - b.Property("Fraction") - .HasColumnType("double precision"); - - b.Property("HitLoc") - .HasColumnType("integer"); - - b.Property("IsKill") - .HasColumnType("boolean"); - - b.Property("KillOriginVector3Id") - .HasColumnType("integer"); - - b.Property("Map") - .HasColumnType("integer"); - - b.Property("ServerId") - .HasColumnType("bigint"); - - b.Property("VictimId") - .HasColumnType("integer"); - - b.Property("ViewAnglesVector3Id") - .HasColumnType("integer"); - - b.Property("VisibilityPercentage") - .HasColumnType("double precision"); - - b.Property("Weapon") - .HasColumnType("integer"); - - b.Property("When") - .HasColumnType("timestamp without time zone"); - - 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() - .HasColumnType("bigint") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Active") - .HasColumnType("boolean"); + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); b.Property("ClientId") .HasColumnType("integer"); - b.Property("Message") - .HasColumnType("text"); - - b.Property("SentIngame") - .HasColumnType("boolean"); - - b.Property("ServerId") - .HasColumnType("bigint"); - - b.Property("TimeSent") + b.Property("CreatedDateTime") .HasColumnType("timestamp without time zone"); - b.HasKey("MessageId"); + b.Property("DamageInflicted") + .HasColumnType("integer"); + + b.Property("DamageReceived") + .HasColumnType("integer"); + + b.Property("DeathCount") + .HasColumnType("integer"); + + b.Property("HitCount") + .HasColumnType("integer"); + + b.Property("HitLocationId") + .HasColumnType("integer"); + + b.Property("KillCount") + .HasColumnType("integer"); + + b.Property("MeansOfDeathId") + .HasColumnType("integer"); + + b.Property("ReceivedHitCount") + .HasColumnType("integer"); + + b.Property("Score") + .HasColumnType("integer"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("SuicideCount") + .HasColumnType("integer"); + + b.Property("UpdatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("UsageSeconds") + .HasColumnType("integer"); + + b.Property("WeaponAttachmentComboId") + .HasColumnType("integer"); + + b.Property("WeaponId") + .HasColumnType("integer"); + + b.HasKey("ClientHitStatisticId"); b.HasIndex("ClientId"); + b.HasIndex("HitLocationId"); + + b.HasIndex("MeansOfDeathId"); + b.HasIndex("ServerId"); - b.HasIndex("TimeSent"); + b.HasIndex("WeaponAttachmentComboId"); - b.ToTable("EFClientMessages"); + b.HasIndex("WeaponId"); + + b.ToTable("EFClientHitStatistics"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b => + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRankingHistory", b => + { + b.Property("ClientRankingHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("CreatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Newest") + .HasColumnType("boolean"); + + b.Property("PerformanceMetric") + .HasColumnType("double precision"); + + b.Property("Ranking") + .HasColumnType("integer"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("UpdatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ZScore") + .HasColumnType("double precision"); + + b.HasKey("ClientRankingHistoryId"); + + b.HasIndex("ClientId"); + + b.HasIndex("Ranking"); + + b.HasIndex("ServerId"); + + b.HasIndex("UpdatedDateTime"); + + b.HasIndex("ZScore"); + + b.ToTable("EFClientRankingHistory"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b => { b.Property("RatingHistoryId") .ValueGeneratedOnAdd() .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); b.Property("Active") .HasColumnType("boolean"); @@ -269,7 +445,7 @@ namespace SharedLibraryCore.Migrations.Postgresql b.ToTable("EFClientRatingHistory"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b => + modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b => { b.Property("ClientId") .HasColumnType("integer"); @@ -280,9 +456,6 @@ namespace SharedLibraryCore.Migrations.Postgresql b.Property("Active") .HasColumnType("boolean"); - b.Property("AverageRecoilOffset") - .HasColumnType("double precision"); - b.Property("AverageSnapValue") .HasColumnType("double precision"); @@ -313,22 +486,29 @@ namespace SharedLibraryCore.Migrations.Postgresql b.Property("TimePlayed") .HasColumnType("integer"); - b.Property("VisionAverage") + b.Property("UpdatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ZScore") .HasColumnType("double precision"); b.HasKey("ClientId", "ServerId"); b.HasIndex("ServerId"); + b.HasIndex("ZScore"); + + b.HasIndex("ClientId", "TimePlayed", "ZScore"); + b.ToTable("EFClientStatistics"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b => + modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b => { b.Property("HitLocationCountId") .ValueGeneratedOnAdd() .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); b.Property("Active") .HasColumnType("boolean"); @@ -362,12 +542,12 @@ namespace SharedLibraryCore.Migrations.Postgresql b.ToTable("EFHitLocationCounts"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b => + modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b => { b.Property("RatingId") .ValueGeneratedOnAdd() .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); b.Property("Active") .HasColumnType("boolean"); @@ -406,66 +586,177 @@ namespace SharedLibraryCore.Migrations.Postgresql b.ToTable("EFRating"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b => + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFHitLocation", b => { - b.Property("ServerId") - .HasColumnType("bigint"); - - b.Property("Active") - .HasColumnType("boolean"); - - b.Property("EndPoint") - .HasColumnType("text"); - - b.Property("GameName") - .HasColumnType("integer"); - - b.Property("HostName") - .HasColumnType("text"); - - b.Property("IsPasswordProtected") - .HasColumnType("boolean"); - - b.Property("Port") - .HasColumnType("integer"); - - b.HasKey("ServerId"); - - b.ToTable("EFServers"); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b => - { - b.Property("StatisticId") + b.Property("HitLocationId") .ValueGeneratedOnAdd() .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); - b.Property("Active") - .HasColumnType("boolean"); + b.Property("CreatedDateTime") + .HasColumnType("timestamp without time zone"); - b.Property("ServerId") - .HasColumnType("bigint"); + b.Property("Game") + .HasColumnType("integer"); - b.Property("TotalKills") - .HasColumnType("bigint"); + b.Property("Name") + .IsRequired() + .HasColumnType("text"); - b.Property("TotalPlayTime") - .HasColumnType("bigint"); + b.Property("UpdatedDateTime") + .HasColumnType("timestamp without time zone"); - b.HasKey("StatisticId"); + b.HasKey("HitLocationId"); - b.HasIndex("ServerId"); + b.HasIndex("Name"); - b.ToTable("EFServerStatistics"); + b.ToTable("EFHitLocations"); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b => + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMap", b => + { + b.Property("MapId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("CreatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Game") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.HasKey("MapId"); + + b.ToTable("EFMaps"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMeansOfDeath", b => + { + b.Property("MeansOfDeathId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("CreatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Game") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.HasKey("MeansOfDeathId"); + + b.ToTable("EFMeansOfDeath"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeapon", b => + { + b.Property("WeaponId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("CreatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Game") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.HasKey("WeaponId"); + + b.HasIndex("Name"); + + b.ToTable("EFWeapons"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachment", b => + { + b.Property("WeaponAttachmentId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("CreatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Game") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.HasKey("WeaponAttachmentId"); + + b.ToTable("EFWeaponAttachments"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b => + { + b.Property("WeaponAttachmentComboId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Attachment1Id") + .HasColumnType("integer"); + + b.Property("Attachment2Id") + .HasColumnType("integer"); + + b.Property("Attachment3Id") + .HasColumnType("integer"); + + b.Property("CreatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Game") + .HasColumnType("integer"); + + b.Property("UpdatedDateTime") + .HasColumnType("timestamp without time zone"); + + b.HasKey("WeaponAttachmentComboId"); + + b.HasIndex("Attachment1Id"); + + b.HasIndex("Attachment2Id"); + + b.HasIndex("Attachment3Id"); + + b.ToTable("EFWeaponAttachmentCombos"); + }); + + modelBuilder.Entity("Data.Models.EFAlias", b => { b.Property("AliasId") .ValueGeneratedOnAdd() .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); b.Property("Active") .HasColumnType("boolean"); @@ -504,12 +795,12 @@ namespace SharedLibraryCore.Migrations.Postgresql b.ToTable("EFAlias"); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b => + modelBuilder.Entity("Data.Models.EFAliasLink", b => { b.Property("AliasLinkId") .ValueGeneratedOnAdd() .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); b.Property("Active") .HasColumnType("boolean"); @@ -519,12 +810,12 @@ namespace SharedLibraryCore.Migrations.Postgresql b.ToTable("EFAliasLinks"); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b => + modelBuilder.Entity("Data.Models.EFChangeHistory", b => { b.Property("ChangeHistoryId") .ValueGeneratedOnAdd() .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); b.Property("Active") .HasColumnType("boolean"); @@ -559,67 +850,12 @@ namespace SharedLibraryCore.Migrations.Postgresql b.ToTable("EFChangeHistory"); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b => - { - b.Property("ClientId") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Active") - .HasColumnType("boolean"); - - b.Property("AliasLinkId") - .HasColumnType("integer"); - - b.Property("Connections") - .HasColumnType("integer"); - - b.Property("CurrentAliasId") - .HasColumnType("integer"); - - b.Property("FirstConnection") - .HasColumnType("timestamp without time zone"); - - b.Property("LastConnection") - .HasColumnType("timestamp without time zone"); - - b.Property("Level") - .HasColumnType("integer"); - - b.Property("Masked") - .HasColumnType("boolean"); - - b.Property("NetworkId") - .HasColumnType("bigint"); - - b.Property("Password") - .HasColumnType("text"); - - b.Property("PasswordSalt") - .HasColumnType("text"); - - b.Property("TotalConnectionTime") - .HasColumnType("integer"); - - b.HasKey("ClientId"); - - b.HasIndex("AliasLinkId"); - - b.HasIndex("CurrentAliasId"); - - b.HasIndex("NetworkId") - .IsUnique(); - - b.ToTable("EFClients"); - }); - - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b => + modelBuilder.Entity("Data.Models.EFMeta", b => { b.Property("MetaId") .ValueGeneratedOnAdd() .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); b.Property("Active") .HasColumnType("boolean"); @@ -659,12 +895,12 @@ namespace SharedLibraryCore.Migrations.Postgresql b.ToTable("EFMeta"); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b => + modelBuilder.Entity("Data.Models.EFPenalty", b => { b.Property("PenaltyId") .ValueGeneratedOnAdd() .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); b.Property("Active") .HasColumnType("boolean"); @@ -708,12 +944,66 @@ namespace SharedLibraryCore.Migrations.Postgresql b.ToTable("EFPenalties"); }); - modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b => + modelBuilder.Entity("Data.Models.Server.EFServer", b => + { + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("EndPoint") + .HasColumnType("text"); + + b.Property("GameName") + .HasColumnType("integer"); + + b.Property("HostName") + .HasColumnType("text"); + + b.Property("IsPasswordProtected") + .HasColumnType("boolean"); + + b.Property("Port") + .HasColumnType("integer"); + + b.HasKey("ServerId"); + + b.ToTable("EFServers"); + }); + + modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b => + { + b.Property("StatisticId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("ServerId") + .HasColumnType("bigint"); + + b.Property("TotalKills") + .HasColumnType("bigint"); + + b.Property("TotalPlayTime") + .HasColumnType("bigint"); + + b.HasKey("StatisticId"); + + b.HasIndex("ServerId"); + + b.ToTable("EFServerStatistics"); + }); + + modelBuilder.Entity("Data.Models.Vector3", b => { b.Property("Vector3Id") .ValueGeneratedOnAdd() .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn); b.Property("X") .HasColumnType("real"); @@ -729,225 +1019,284 @@ namespace SharedLibraryCore.Migrations.Postgresql b.ToTable("Vector3"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b => + modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b => { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") - .WithMany() - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SharedLibraryCore.Helpers.Vector3", "CurrentViewAngle") - .WithMany() - .HasForeignKey("CurrentViewAngleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination") - .WithMany() - .HasForeignKey("HitDestinationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin") - .WithMany() - .HasForeignKey("HitOriginId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle") - .WithMany() - .HasForeignKey("LastStrainAngleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshotVector3", b => - { - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", "Snapshot") + b.HasOne("Data.Models.Client.Stats.EFACSnapshot", "Snapshot") .WithMany("PredictedViewAngles") .HasForeignKey("SnapshotId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("SharedLibraryCore.Helpers.Vector3", "Vector") + b.HasOne("Data.Models.Vector3", "Vector") .WithMany() .HasForeignKey("Vector3Id") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b => + modelBuilder.Entity("Data.Models.Client.EFClient", b => { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker") - .WithMany() - .HasForeignKey("AttackerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - 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) - .IsRequired(); - - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim") - .WithMany() - .HasForeignKey("VictimId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - 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) - .IsRequired(); - - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server") - .WithMany() - .HasForeignKey("ServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b => - { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") - .WithMany() - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b => - { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") - .WithMany() - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server") - .WithMany() - .HasForeignKey("ServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b => - { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") - .WithMany() - .HasForeignKey("EFClientStatisticsClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server") - .WithMany() - .HasForeignKey("EFClientStatisticsServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", null) - .WithMany("HitLocations") - .HasForeignKey("EFClientStatisticsClientId", "EFClientStatisticsServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b => - { - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", "RatingHistory") - .WithMany("Ratings") - .HasForeignKey("RatingHistoryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - 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) - .IsRequired(); - }); - - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b => - { - b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link") - .WithMany("Children") - .HasForeignKey("LinkId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - }); - - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b => - { - b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink") + b.HasOne("Data.Models.EFAliasLink", "AliasLink") .WithMany() .HasForeignKey("AliasLinkId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias") + b.HasOne("Data.Models.EFAlias", "CurrentAlias") .WithMany() .HasForeignKey("CurrentAliasId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b => + modelBuilder.Entity("Data.Models.Client.EFClientKill", b => { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") + b.HasOne("Data.Models.Client.EFClient", "Attacker") + .WithMany() + .HasForeignKey("AttackerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "DeathOrigin") + .WithMany() + .HasForeignKey("DeathOriginVector3Id"); + + b.HasOne("Data.Models.Vector3", "KillOrigin") + .WithMany() + .HasForeignKey("KillOriginVector3Id"); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.EFClient", "Victim") + .WithMany() + .HasForeignKey("VictimId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "ViewAngles") + .WithMany() + .HasForeignKey("ViewAnglesVector3Id"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientMessage", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "CurrentViewAngle") + .WithMany() + .HasForeignKey("CurrentViewAngleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "HitDestination") + .WithMany() + .HasForeignKey("HitDestinationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "HitOrigin") + .WithMany() + .HasForeignKey("HitOriginId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "LastStrainAngle") + .WithMany() + .HasForeignKey("LastStrainAngleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.Reference.EFHitLocation", "HitLocation") + .WithMany() + .HasForeignKey("HitLocationId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFMeansOfDeath", "MeansOfDeath") + .WithMany() + .HasForeignKey("MeansOfDeathId"); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", "WeaponAttachmentCombo") + .WithMany() + .HasForeignKey("WeaponAttachmentComboId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeapon", "Weapon") + .WithMany() + .HasForeignKey("WeaponId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRankingHistory", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("EFClientStatisticsClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("EFClientStatisticsServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.EFClientStatistics", null) + .WithMany("HitLocations") + .HasForeignKey("EFClientStatisticsClientId", "EFClientStatisticsServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b => + { + b.HasOne("Data.Models.Client.Stats.EFClientRatingHistory", "RatingHistory") + .WithMany("Ratings") + .HasForeignKey("RatingHistoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b => + { + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment1") + .WithMany() + .HasForeignKey("Attachment1Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment2") + .WithMany() + .HasForeignKey("Attachment2Id"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment3") + .WithMany() + .HasForeignKey("Attachment3Id"); + }); + + modelBuilder.Entity("Data.Models.EFAlias", b => + { + b.HasOne("Data.Models.EFAliasLink", "Link") + .WithMany("Children") + .HasForeignKey("LinkId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.EFMeta", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") .WithMany("Meta") .HasForeignKey("ClientId"); - b.HasOne("SharedLibraryCore.Database.Models.EFMeta", "LinkedMeta") + b.HasOne("Data.Models.EFMeta", "LinkedMeta") .WithMany() .HasForeignKey("LinkedMetaId") .OnDelete(DeleteBehavior.SetNull); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b => + modelBuilder.Entity("Data.Models.EFPenalty", b => { - b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link") + b.HasOne("Data.Models.EFAliasLink", "Link") .WithMany("ReceivedPenalties") .HasForeignKey("LinkId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender") + b.HasOne("Data.Models.Client.EFClient", "Offender") .WithMany("ReceivedPenalties") .HasForeignKey("OffenderId") .OnDelete(DeleteBehavior.Restrict) .IsRequired(); - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher") + b.HasOne("Data.Models.Client.EFClient", "Punisher") .WithMany("AdministeredPenalties") .HasForeignKey("PunisherId") .OnDelete(DeleteBehavior.Restrict) .IsRequired(); }); + + modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b => + { + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); #pragma warning restore 612, 618 } } diff --git a/SharedLibraryCore/Migrations/Sqlite/20180409183408_InitialCreate.Designer.cs b/Data/Migrations/Sqlite/20180409183408_InitialCreate.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180409183408_InitialCreate.Designer.cs rename to Data/Migrations/Sqlite/20180409183408_InitialCreate.Designer.cs index e71e66ace..969ca1b9c 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180409183408_InitialCreate.Designer.cs +++ b/Data/Migrations/Sqlite/20180409183408_InitialCreate.Designer.cs @@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database; -using SharedLibraryCore.Database.MigrationContext; +using Data; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180409183408_InitialCreate")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180409183408_InitialCreate.cs b/Data/Migrations/Sqlite/20180409183408_InitialCreate.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180409183408_InitialCreate.cs rename to Data/Migrations/Sqlite/20180409183408_InitialCreate.cs index acd57084c..7b5463342 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180409183408_InitialCreate.cs +++ b/Data/Migrations/Sqlite/20180409183408_InitialCreate.cs @@ -4,7 +4,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class InitialCreate : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180502195450_Update.Designer.cs b/Data/Migrations/Sqlite/20180502195450_Update.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180502195450_Update.Designer.cs rename to Data/Migrations/Sqlite/20180502195450_Update.Designer.cs index 9ca5b7e39..bad1e03cf 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180502195450_Update.Designer.cs +++ b/Data/Migrations/Sqlite/20180502195450_Update.Designer.cs @@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database; -using SharedLibraryCore.Database.MigrationContext; +using Data; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180502195450_Update")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180502195450_Update.cs b/Data/Migrations/Sqlite/20180502195450_Update.cs similarity index 95% rename from SharedLibraryCore/Migrations/Postgresql/20180502195450_Update.cs rename to Data/Migrations/Sqlite/20180502195450_Update.cs index 79b424169..e3b166507 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180502195450_Update.cs +++ b/Data/Migrations/Sqlite/20180502195450_Update.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class Update : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180516023249_AddEloField.Designer.cs b/Data/Migrations/Sqlite/20180516023249_AddEloField.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180516023249_AddEloField.Designer.cs rename to Data/Migrations/Sqlite/20180516023249_AddEloField.Designer.cs index debb85074..7ce4d36f9 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180516023249_AddEloField.Designer.cs +++ b/Data/Migrations/Sqlite/20180516023249_AddEloField.Designer.cs @@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database; -using SharedLibraryCore.Database.MigrationContext; +using Data; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180516023249_AddEloField")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180516023249_AddEloField.cs b/Data/Migrations/Sqlite/20180516023249_AddEloField.cs similarity index 93% rename from SharedLibraryCore/Migrations/Postgresql/20180516023249_AddEloField.cs rename to Data/Migrations/Sqlite/20180516023249_AddEloField.cs index 088d71f79..078a22cd4 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180516023249_AddEloField.cs +++ b/Data/Migrations/Sqlite/20180516023249_AddEloField.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddEloField : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180517223349_AddRollingKDR.Designer.cs b/Data/Migrations/Sqlite/20180517223349_AddRollingKDR.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180517223349_AddRollingKDR.Designer.cs rename to Data/Migrations/Sqlite/20180517223349_AddRollingKDR.Designer.cs index 0888266d9..d163970b4 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180517223349_AddRollingKDR.Designer.cs +++ b/Data/Migrations/Sqlite/20180517223349_AddRollingKDR.Designer.cs @@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database; -using SharedLibraryCore.Database.MigrationContext; +using Data; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180517223349_AddRollingKDR")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180517223349_AddRollingKDR.cs b/Data/Migrations/Sqlite/20180517223349_AddRollingKDR.cs similarity index 93% rename from SharedLibraryCore/Migrations/Postgresql/20180517223349_AddRollingKDR.cs rename to Data/Migrations/Sqlite/20180517223349_AddRollingKDR.cs index 8b971689a..82dd46184 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180517223349_AddRollingKDR.cs +++ b/Data/Migrations/Sqlite/20180517223349_AddRollingKDR.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddRollingKDR : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs b/Data/Migrations/Sqlite/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs rename to Data/Migrations/Sqlite/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs index db0edba5a..2964b1a2c 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs +++ b/Data/Migrations/Sqlite/20180531212903_AddAutomatedOffenseAndRatingHistory.Designer.cs @@ -5,10 +5,10 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180531212903_AddAutomatedOffenseAndRatingHistory")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180531212903_AddAutomatedOffenseAndRatingHistory.cs b/Data/Migrations/Sqlite/20180531212903_AddAutomatedOffenseAndRatingHistory.cs similarity index 98% rename from SharedLibraryCore/Migrations/Postgresql/20180531212903_AddAutomatedOffenseAndRatingHistory.cs rename to Data/Migrations/Sqlite/20180531212903_AddAutomatedOffenseAndRatingHistory.cs index eeff0d6ef..703a8ab62 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180531212903_AddAutomatedOffenseAndRatingHistory.cs +++ b/Data/Migrations/Sqlite/20180531212903_AddAutomatedOffenseAndRatingHistory.cs @@ -4,7 +4,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddAutomatedOffenseAndRatingHistory : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180601172317_AddActivityAmount.Designer.cs b/Data/Migrations/Sqlite/20180601172317_AddActivityAmount.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180601172317_AddActivityAmount.Designer.cs rename to Data/Migrations/Sqlite/20180601172317_AddActivityAmount.Designer.cs index 52414186b..468e50f11 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180601172317_AddActivityAmount.Designer.cs +++ b/Data/Migrations/Sqlite/20180601172317_AddActivityAmount.Designer.cs @@ -5,10 +5,10 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180601172317_AddActivityAmount")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180601172317_AddActivityAmount.cs b/Data/Migrations/Sqlite/20180601172317_AddActivityAmount.cs similarity index 92% rename from SharedLibraryCore/Migrations/Postgresql/20180601172317_AddActivityAmount.cs rename to Data/Migrations/Sqlite/20180601172317_AddActivityAmount.cs index 332f10bb3..a8ff76b94 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180601172317_AddActivityAmount.cs +++ b/Data/Migrations/Sqlite/20180601172317_AddActivityAmount.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddActivityAmount : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180602041758_AddClientMeta.Designer.cs b/Data/Migrations/Sqlite/20180602041758_AddClientMeta.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180602041758_AddClientMeta.Designer.cs rename to Data/Migrations/Sqlite/20180602041758_AddClientMeta.Designer.cs index ca0572325..fe3503a86 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180602041758_AddClientMeta.Designer.cs +++ b/Data/Migrations/Sqlite/20180602041758_AddClientMeta.Designer.cs @@ -5,10 +5,10 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180602041758_AddClientMeta")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180602041758_AddClientMeta.cs b/Data/Migrations/Sqlite/20180602041758_AddClientMeta.cs similarity index 97% rename from SharedLibraryCore/Migrations/Postgresql/20180602041758_AddClientMeta.cs rename to Data/Migrations/Sqlite/20180602041758_AddClientMeta.cs index 31c8e15dd..08dbdbf3b 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180602041758_AddClientMeta.cs +++ b/Data/Migrations/Sqlite/20180602041758_AddClientMeta.cs @@ -4,7 +4,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddClientMeta : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180605191706_AddEFACSnapshots.Designer.cs b/Data/Migrations/Sqlite/20180605191706_AddEFACSnapshots.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180605191706_AddEFACSnapshots.Designer.cs rename to Data/Migrations/Sqlite/20180605191706_AddEFACSnapshots.Designer.cs index 9a941e7d6..d15294951 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180605191706_AddEFACSnapshots.Designer.cs +++ b/Data/Migrations/Sqlite/20180605191706_AddEFACSnapshots.Designer.cs @@ -5,10 +5,10 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180605191706_AddEFACSnapshots")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180605191706_AddEFACSnapshots.cs b/Data/Migrations/Sqlite/20180605191706_AddEFACSnapshots.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180605191706_AddEFACSnapshots.cs rename to Data/Migrations/Sqlite/20180605191706_AddEFACSnapshots.cs index d989c1e83..b719bb8d1 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180605191706_AddEFACSnapshots.cs +++ b/Data/Migrations/Sqlite/20180605191706_AddEFACSnapshots.cs @@ -4,7 +4,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddEFACSnapshots : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180614014303_IndexForEFAlias.Designer.cs b/Data/Migrations/Sqlite/20180614014303_IndexForEFAlias.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180614014303_IndexForEFAlias.Designer.cs rename to Data/Migrations/Sqlite/20180614014303_IndexForEFAlias.Designer.cs index 553c71831..3300e12fb 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180614014303_IndexForEFAlias.Designer.cs +++ b/Data/Migrations/Sqlite/20180614014303_IndexForEFAlias.Designer.cs @@ -5,10 +5,10 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; using System; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180614014303_IndexForEFAlias")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180614014303_IndexForEFAlias.cs b/Data/Migrations/Sqlite/20180614014303_IndexForEFAlias.cs similarity index 92% rename from SharedLibraryCore/Migrations/Postgresql/20180614014303_IndexForEFAlias.cs rename to Data/Migrations/Sqlite/20180614014303_IndexForEFAlias.cs index 81ffaf1a7..289a56976 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180614014303_IndexForEFAlias.cs +++ b/Data/Migrations/Sqlite/20180614014303_IndexForEFAlias.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class IndexForEFAlias : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180902035612_AddFractionAndIsKill.Designer.cs b/Data/Migrations/Sqlite/20180902035612_AddFractionAndIsKill.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180902035612_AddFractionAndIsKill.Designer.cs rename to Data/Migrations/Sqlite/20180902035612_AddFractionAndIsKill.Designer.cs index a79bb713b..c477fef6a 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180902035612_AddFractionAndIsKill.Designer.cs +++ b/Data/Migrations/Sqlite/20180902035612_AddFractionAndIsKill.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180902035612_AddFractionAndIsKill")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180902035612_AddFractionAndIsKill.cs b/Data/Migrations/Sqlite/20180902035612_AddFractionAndIsKill.cs similarity index 97% rename from SharedLibraryCore/Migrations/Postgresql/20180902035612_AddFractionAndIsKill.cs rename to Data/Migrations/Sqlite/20180902035612_AddFractionAndIsKill.cs index ae30df08e..1fe4ab131 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180902035612_AddFractionAndIsKill.cs +++ b/Data/Migrations/Sqlite/20180902035612_AddFractionAndIsKill.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddFractionAndIsKill : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180904154622_AddVisibilityPercentage.Designer.cs b/Data/Migrations/Sqlite/20180904154622_AddVisibilityPercentage.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180904154622_AddVisibilityPercentage.Designer.cs rename to Data/Migrations/Sqlite/20180904154622_AddVisibilityPercentage.Designer.cs index c2f06539d..93a6ae30b 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180904154622_AddVisibilityPercentage.Designer.cs +++ b/Data/Migrations/Sqlite/20180904154622_AddVisibilityPercentage.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180904154622_AddVisibilityPercentage")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180904154622_AddVisibilityPercentage.cs b/Data/Migrations/Sqlite/20180904154622_AddVisibilityPercentage.cs similarity index 92% rename from SharedLibraryCore/Migrations/Postgresql/20180904154622_AddVisibilityPercentage.cs rename to Data/Migrations/Sqlite/20180904154622_AddVisibilityPercentage.cs index 77b24262f..7d5842f9b 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180904154622_AddVisibilityPercentage.cs +++ b/Data/Migrations/Sqlite/20180904154622_AddVisibilityPercentage.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddVisibilityPercentage : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180907020706_AddVision.Designer.cs b/Data/Migrations/Sqlite/20180907020706_AddVision.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180907020706_AddVision.Designer.cs rename to Data/Migrations/Sqlite/20180907020706_AddVision.Designer.cs index 7b996dc66..0521e3984 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180907020706_AddVision.Designer.cs +++ b/Data/Migrations/Sqlite/20180907020706_AddVision.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180907020706_AddVision")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180907020706_AddVision.cs b/Data/Migrations/Sqlite/20180907020706_AddVision.cs similarity index 92% rename from SharedLibraryCore/Migrations/Postgresql/20180907020706_AddVision.cs rename to Data/Migrations/Sqlite/20180907020706_AddVision.cs index 17ce2ea9d..ded66273d 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180907020706_AddVision.cs +++ b/Data/Migrations/Sqlite/20180907020706_AddVision.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddVision : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180908004053_AddWhenToRating.Designer.cs b/Data/Migrations/Sqlite/20180908004053_AddWhenToRating.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180908004053_AddWhenToRating.Designer.cs rename to Data/Migrations/Sqlite/20180908004053_AddWhenToRating.Designer.cs index fe43268f5..ad650da0a 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180908004053_AddWhenToRating.Designer.cs +++ b/Data/Migrations/Sqlite/20180908004053_AddWhenToRating.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180908004053_AddWhenToRating")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180908004053_AddWhenToRating.cs b/Data/Migrations/Sqlite/20180908004053_AddWhenToRating.cs similarity index 93% rename from SharedLibraryCore/Migrations/Postgresql/20180908004053_AddWhenToRating.cs rename to Data/Migrations/Sqlite/20180908004053_AddWhenToRating.cs index f6482236e..3610472fd 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180908004053_AddWhenToRating.cs +++ b/Data/Migrations/Sqlite/20180908004053_AddWhenToRating.cs @@ -1,7 +1,7 @@ using System; using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddWhenToRating : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180910221749_AddRatingIndexes.Designer.cs b/Data/Migrations/Sqlite/20180910221749_AddRatingIndexes.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180910221749_AddRatingIndexes.Designer.cs rename to Data/Migrations/Sqlite/20180910221749_AddRatingIndexes.Designer.cs index 9b3b9e99e..bbacef28a 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180910221749_AddRatingIndexes.Designer.cs +++ b/Data/Migrations/Sqlite/20180910221749_AddRatingIndexes.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180910221749_AddRatingIndexes")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180910221749_AddRatingIndexes.cs b/Data/Migrations/Sqlite/20180910221749_AddRatingIndexes.cs similarity index 95% rename from SharedLibraryCore/Migrations/Postgresql/20180910221749_AddRatingIndexes.cs rename to Data/Migrations/Sqlite/20180910221749_AddRatingIndexes.cs index 781d5bbaf..07ea61fc9 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180910221749_AddRatingIndexes.cs +++ b/Data/Migrations/Sqlite/20180910221749_AddRatingIndexes.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddRatingIndexes : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180911184224_AddEFAliasNameIndex.Designer.cs b/Data/Migrations/Sqlite/20180911184224_AddEFAliasNameIndex.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180911184224_AddEFAliasNameIndex.Designer.cs rename to Data/Migrations/Sqlite/20180911184224_AddEFAliasNameIndex.Designer.cs index e9feb60e1..2d4d694c9 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180911184224_AddEFAliasNameIndex.Designer.cs +++ b/Data/Migrations/Sqlite/20180911184224_AddEFAliasNameIndex.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180911184224_AddEFAliasNameIndex")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180911184224_AddEFAliasNameIndex.cs b/Data/Migrations/Sqlite/20180911184224_AddEFAliasNameIndex.cs similarity index 91% rename from SharedLibraryCore/Migrations/Postgresql/20180911184224_AddEFAliasNameIndex.cs rename to Data/Migrations/Sqlite/20180911184224_AddEFAliasNameIndex.cs index 90247335d..5ccab445d 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180911184224_AddEFAliasNameIndex.cs +++ b/Data/Migrations/Sqlite/20180911184224_AddEFAliasNameIndex.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddEFAliasNameIndex : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180911190823_AddEFAliasNameMaxLength24.Designer.cs b/Data/Migrations/Sqlite/20180911190823_AddEFAliasNameMaxLength24.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180911190823_AddEFAliasNameMaxLength24.Designer.cs rename to Data/Migrations/Sqlite/20180911190823_AddEFAliasNameMaxLength24.Designer.cs index f62754170..6abeea81a 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180911190823_AddEFAliasNameMaxLength24.Designer.cs +++ b/Data/Migrations/Sqlite/20180911190823_AddEFAliasNameMaxLength24.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180911190823_AddEFAliasNameMaxLength24")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180911190823_AddEFAliasNameMaxLength24.cs b/Data/Migrations/Sqlite/20180911190823_AddEFAliasNameMaxLength24.cs similarity index 93% rename from SharedLibraryCore/Migrations/Postgresql/20180911190823_AddEFAliasNameMaxLength24.cs rename to Data/Migrations/Sqlite/20180911190823_AddEFAliasNameMaxLength24.cs index 53ef00b9c..7a16d9c41 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180911190823_AddEFAliasNameMaxLength24.cs +++ b/Data/Migrations/Sqlite/20180911190823_AddEFAliasNameMaxLength24.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddEFAliasNameMaxLength24 : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs b/Data/Migrations/Sqlite/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs rename to Data/Migrations/Sqlite/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs index ba6dcaf76..72aba35e8 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs +++ b/Data/Migrations/Sqlite/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180912015012_AddPreviousCurrentValueToEFChangeHistory")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs b/Data/Migrations/Sqlite/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs similarity index 94% rename from SharedLibraryCore/Migrations/Postgresql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs rename to Data/Migrations/Sqlite/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs index c7c7a911d..fb3ee809b 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs +++ b/Data/Migrations/Sqlite/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddPreviousCurrentValueToEFChangeHistory : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180915163111_AddIndexToMessageTimeSent.Designer.cs b/Data/Migrations/Sqlite/20180915163111_AddIndexToMessageTimeSent.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180915163111_AddIndexToMessageTimeSent.Designer.cs rename to Data/Migrations/Sqlite/20180915163111_AddIndexToMessageTimeSent.Designer.cs index 3ec9944cb..4a5a0c63d 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180915163111_AddIndexToMessageTimeSent.Designer.cs +++ b/Data/Migrations/Sqlite/20180915163111_AddIndexToMessageTimeSent.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180915163111_AddIndexToMessageTimeSent")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180915163111_AddIndexToMessageTimeSent.cs b/Data/Migrations/Sqlite/20180915163111_AddIndexToMessageTimeSent.cs similarity index 92% rename from SharedLibraryCore/Migrations/Postgresql/20180915163111_AddIndexToMessageTimeSent.cs rename to Data/Migrations/Sqlite/20180915163111_AddIndexToMessageTimeSent.cs index 93186b385..d24bead57 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180915163111_AddIndexToMessageTimeSent.cs +++ b/Data/Migrations/Sqlite/20180915163111_AddIndexToMessageTimeSent.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddIndexToMessageTimeSent : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180922231310_RemoveACSnapShot.Designer.cs b/Data/Migrations/Sqlite/20180922231310_RemoveACSnapShot.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180922231310_RemoveACSnapShot.Designer.cs rename to Data/Migrations/Sqlite/20180922231310_RemoveACSnapShot.Designer.cs index 1c7aac121..ecbd0cfe2 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180922231310_RemoveACSnapShot.Designer.cs +++ b/Data/Migrations/Sqlite/20180922231310_RemoveACSnapShot.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180922231310_RemoveACSnapShot")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180922231310_RemoveACSnapShot.cs b/Data/Migrations/Sqlite/20180922231310_RemoveACSnapShot.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180922231310_RemoveACSnapShot.cs rename to Data/Migrations/Sqlite/20180922231310_RemoveACSnapShot.cs index b767126f5..6688d46f6 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180922231310_RemoveACSnapShot.cs +++ b/Data/Migrations/Sqlite/20180922231310_RemoveACSnapShot.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class RemoveACSnapShot : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20180922231600_ReaddACSnapshot.Designer.cs b/Data/Migrations/Sqlite/20180922231600_ReaddACSnapshot.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20180922231600_ReaddACSnapshot.Designer.cs rename to Data/Migrations/Sqlite/20180922231600_ReaddACSnapshot.Designer.cs index 3c43b4bfe..f37f18b70 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20180922231600_ReaddACSnapshot.Designer.cs +++ b/Data/Migrations/Sqlite/20180922231600_ReaddACSnapshot.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20180922231600_ReaddACSnapshot")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20180922231600_ReaddACSnapshot.cs b/Data/Migrations/Sqlite/20180922231600_ReaddACSnapshot.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20180922231600_ReaddACSnapshot.cs rename to Data/Migrations/Sqlite/20180922231600_ReaddACSnapshot.cs index b0938c854..ad668b2b2 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20180922231600_ReaddACSnapshot.cs +++ b/Data/Migrations/Sqlite/20180922231600_ReaddACSnapshot.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class ReaddACSnapshot : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20181014171848_MakePenaltyExpirationNullable.Designer.cs b/Data/Migrations/Sqlite/20181014171848_MakePenaltyExpirationNullable.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20181014171848_MakePenaltyExpirationNullable.Designer.cs rename to Data/Migrations/Sqlite/20181014171848_MakePenaltyExpirationNullable.Designer.cs index 62deb8a60..b0a0be754 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20181014171848_MakePenaltyExpirationNullable.Designer.cs +++ b/Data/Migrations/Sqlite/20181014171848_MakePenaltyExpirationNullable.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20181014171848_MakePenaltyExpirationNullable")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20181014171848_MakePenaltyExpirationNullable.cs b/Data/Migrations/Sqlite/20181014171848_MakePenaltyExpirationNullable.cs similarity index 98% rename from SharedLibraryCore/Migrations/Postgresql/20181014171848_MakePenaltyExpirationNullable.cs rename to Data/Migrations/Sqlite/20181014171848_MakePenaltyExpirationNullable.cs index 6fb055ef0..3eac0d00a 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20181014171848_MakePenaltyExpirationNullable.cs +++ b/Data/Migrations/Sqlite/20181014171848_MakePenaltyExpirationNullable.cs @@ -1,7 +1,7 @@ using System; using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class MakePenaltyExpirationNullable : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20181125193243_MakeClientIPNullable.Designer.cs b/Data/Migrations/Sqlite/20181125193243_MakeClientIPNullable.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20181125193243_MakeClientIPNullable.Designer.cs rename to Data/Migrations/Sqlite/20181125193243_MakeClientIPNullable.Designer.cs index 9ec28f7f7..96a12275a 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20181125193243_MakeClientIPNullable.Designer.cs +++ b/Data/Migrations/Sqlite/20181125193243_MakeClientIPNullable.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20181125193243_MakeClientIPNullable")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20181125193243_MakeClientIPNullable.cs b/Data/Migrations/Sqlite/20181125193243_MakeClientIPNullable.cs similarity index 97% rename from SharedLibraryCore/Migrations/Postgresql/20181125193243_MakeClientIPNullable.cs rename to Data/Migrations/Sqlite/20181125193243_MakeClientIPNullable.cs index 9999193f7..a81d86d00 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20181125193243_MakeClientIPNullable.cs +++ b/Data/Migrations/Sqlite/20181125193243_MakeClientIPNullable.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class MakeClientIPNullable : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs b/Data/Migrations/Sqlite/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs rename to Data/Migrations/Sqlite/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs index 8042335f2..cfce8c220 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs +++ b/Data/Migrations/Sqlite/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20181127144417_AddEndpointToEFServerUpdateServerIdType")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs b/Data/Migrations/Sqlite/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs similarity index 92% rename from SharedLibraryCore/Migrations/Postgresql/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs rename to Data/Migrations/Sqlite/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs index 3f6871f16..8d9e61692 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs +++ b/Data/Migrations/Sqlite/20181127144417_AddEndpointToEFServerUpdateServerIdType.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddEndpointToEFServerUpdateServerIdType : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20181216214513_AddEvadePenaltyFlag.Designer.cs b/Data/Migrations/Sqlite/20181216214513_AddEvadePenaltyFlag.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20181216214513_AddEvadePenaltyFlag.Designer.cs rename to Data/Migrations/Sqlite/20181216214513_AddEvadePenaltyFlag.Designer.cs index 389817fd0..0fd3f5539 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20181216214513_AddEvadePenaltyFlag.Designer.cs +++ b/Data/Migrations/Sqlite/20181216214513_AddEvadePenaltyFlag.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20181216214513_AddEvadePenaltyFlag")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20181216214513_AddEvadePenaltyFlag.cs b/Data/Migrations/Sqlite/20181216214513_AddEvadePenaltyFlag.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20181216214513_AddEvadePenaltyFlag.cs rename to Data/Migrations/Sqlite/20181216214513_AddEvadePenaltyFlag.cs index eaf9fbf33..5d69bffec 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20181216214513_AddEvadePenaltyFlag.cs +++ b/Data/Migrations/Sqlite/20181216214513_AddEvadePenaltyFlag.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddEvadePenaltyFlag : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs b/Data/Migrations/Sqlite/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs rename to Data/Migrations/Sqlite/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs index 6f1aa63ed..608600cfb 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs +++ b/Data/Migrations/Sqlite/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20190222234742_AddIndexToEFMeta-KeyAndClientId")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs b/Data/Migrations/Sqlite/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs similarity index 94% rename from SharedLibraryCore/Migrations/Postgresql/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs rename to Data/Migrations/Sqlite/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs index ef8fbff1d..cb3ff6d61 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs +++ b/Data/Migrations/Sqlite/20190222234742_AddIndexToEFMeta-KeyAndClientId.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddIndexToEFMetaKeyAndClientId : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20190423142128_AddGameNameToEFServer.Designer.cs b/Data/Migrations/Sqlite/20190423142128_AddGameNameToEFServer.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20190423142128_AddGameNameToEFServer.Designer.cs rename to Data/Migrations/Sqlite/20190423142128_AddGameNameToEFServer.Designer.cs index 2522be0fb..dc0c49418 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190423142128_AddGameNameToEFServer.Designer.cs +++ b/Data/Migrations/Sqlite/20190423142128_AddGameNameToEFServer.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20190423142128_AddGameNameToEFServer")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20190423142128_AddGameNameToEFServer.cs b/Data/Migrations/Sqlite/20190423142128_AddGameNameToEFServer.cs similarity index 91% rename from SharedLibraryCore/Migrations/Postgresql/20190423142128_AddGameNameToEFServer.cs rename to Data/Migrations/Sqlite/20190423142128_AddGameNameToEFServer.cs index f9bca4c59..bebb186c6 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190423142128_AddGameNameToEFServer.cs +++ b/Data/Migrations/Sqlite/20190423142128_AddGameNameToEFServer.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddGameNameToEFServer : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20190615145212_AddAvgRecoilOffset.Designer.cs b/Data/Migrations/Sqlite/20190615145212_AddAvgRecoilOffset.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20190615145212_AddAvgRecoilOffset.Designer.cs rename to Data/Migrations/Sqlite/20190615145212_AddAvgRecoilOffset.Designer.cs index 04bcc51ca..0e78cde1b 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190615145212_AddAvgRecoilOffset.Designer.cs +++ b/Data/Migrations/Sqlite/20190615145212_AddAvgRecoilOffset.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20190615145212_AddAvgRecoilOffset")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20190615145212_AddAvgRecoilOffset.cs b/Data/Migrations/Sqlite/20190615145212_AddAvgRecoilOffset.cs similarity index 91% rename from SharedLibraryCore/Migrations/Postgresql/20190615145212_AddAvgRecoilOffset.cs rename to Data/Migrations/Sqlite/20190615145212_AddAvgRecoilOffset.cs index a884a9fe2..735c8f5b6 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190615145212_AddAvgRecoilOffset.cs +++ b/Data/Migrations/Sqlite/20190615145212_AddAvgRecoilOffset.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddAvgRecoilOffset : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs b/Data/Migrations/Sqlite/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs rename to Data/Migrations/Sqlite/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs index 51fa8efde..a26566a16 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs +++ b/Data/Migrations/Sqlite/20190615214055_AddRecoilOffsetToSnapshot.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20190615214055_AddRecoilOffsetToSnapshot")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20190615214055_AddRecoilOffsetToSnapshot.cs b/Data/Migrations/Sqlite/20190615214055_AddRecoilOffsetToSnapshot.cs similarity index 92% rename from SharedLibraryCore/Migrations/Postgresql/20190615214055_AddRecoilOffsetToSnapshot.cs rename to Data/Migrations/Sqlite/20190615214055_AddRecoilOffsetToSnapshot.cs index b7ef1b243..2b57056a5 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190615214055_AddRecoilOffsetToSnapshot.cs +++ b/Data/Migrations/Sqlite/20190615214055_AddRecoilOffsetToSnapshot.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddRecoilOffsetToSnapshot : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20190725000309_AlterEFRatingIndex.Designer.cs b/Data/Migrations/Sqlite/20190725000309_AlterEFRatingIndex.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20190725000309_AlterEFRatingIndex.Designer.cs rename to Data/Migrations/Sqlite/20190725000309_AlterEFRatingIndex.Designer.cs index 1a953f48f..81879a90f 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190725000309_AlterEFRatingIndex.Designer.cs +++ b/Data/Migrations/Sqlite/20190725000309_AlterEFRatingIndex.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20190725000309_AlterEFRatingIndex")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20190725000309_AlterEFRatingIndex.cs b/Data/Migrations/Sqlite/20190725000309_AlterEFRatingIndex.cs similarity index 96% rename from SharedLibraryCore/Migrations/Postgresql/20190725000309_AlterEFRatingIndex.cs rename to Data/Migrations/Sqlite/20190725000309_AlterEFRatingIndex.cs index c85535b73..50d0cb961 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190725000309_AlterEFRatingIndex.cs +++ b/Data/Migrations/Sqlite/20190725000309_AlterEFRatingIndex.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AlterEFRatingIndex : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20190802174908_AddSearchNameToEFAlias.Designer.cs b/Data/Migrations/Sqlite/20190802174908_AddSearchNameToEFAlias.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20190802174908_AddSearchNameToEFAlias.Designer.cs rename to Data/Migrations/Sqlite/20190802174908_AddSearchNameToEFAlias.Designer.cs index 576968f25..069939e79 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190802174908_AddSearchNameToEFAlias.Designer.cs +++ b/Data/Migrations/Sqlite/20190802174908_AddSearchNameToEFAlias.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20190802174908_AddSearchNameToEFAlias")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20190802174908_AddSearchNameToEFAlias.cs b/Data/Migrations/Sqlite/20190802174908_AddSearchNameToEFAlias.cs similarity index 94% rename from SharedLibraryCore/Migrations/Postgresql/20190802174908_AddSearchNameToEFAlias.cs rename to Data/Migrations/Sqlite/20190802174908_AddSearchNameToEFAlias.cs index d73110932..ef1540514 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190802174908_AddSearchNameToEFAlias.cs +++ b/Data/Migrations/Sqlite/20190802174908_AddSearchNameToEFAlias.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddSearchNameToEFAlias : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20190831210503_AvgSnapValueToClientStatistics.Designer.cs b/Data/Migrations/Sqlite/20190831210503_AvgSnapValueToClientStatistics.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20190831210503_AvgSnapValueToClientStatistics.Designer.cs rename to Data/Migrations/Sqlite/20190831210503_AvgSnapValueToClientStatistics.Designer.cs index da6167835..6dcb45327 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190831210503_AvgSnapValueToClientStatistics.Designer.cs +++ b/Data/Migrations/Sqlite/20190831210503_AvgSnapValueToClientStatistics.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20190831210503_AvgSnapValueToClientStatistics")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20190831210503_AvgSnapValueToClientStatistics.cs b/Data/Migrations/Sqlite/20190831210503_AvgSnapValueToClientStatistics.cs similarity index 92% rename from SharedLibraryCore/Migrations/Postgresql/20190831210503_AvgSnapValueToClientStatistics.cs rename to Data/Migrations/Sqlite/20190831210503_AvgSnapValueToClientStatistics.cs index 625b3ca13..0aea6fa6a 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190831210503_AvgSnapValueToClientStatistics.cs +++ b/Data/Migrations/Sqlite/20190831210503_AvgSnapValueToClientStatistics.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AvgSnapValueToClientStatistics : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs b/Data/Migrations/Sqlite/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs rename to Data/Migrations/Sqlite/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs index a199c8145..40c168ec4 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs +++ b/Data/Migrations/Sqlite/20190901180209_AddSnapHitCountToClientStatistics.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20190901180209_AddSnapHitCountToClientStatistics")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20190901180209_AddSnapHitCountToClientStatistics.cs b/Data/Migrations/Sqlite/20190901180209_AddSnapHitCountToClientStatistics.cs similarity index 92% rename from SharedLibraryCore/Migrations/Postgresql/20190901180209_AddSnapHitCountToClientStatistics.cs rename to Data/Migrations/Sqlite/20190901180209_AddSnapHitCountToClientStatistics.cs index d3ddd2c52..10579fc7d 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190901180209_AddSnapHitCountToClientStatistics.cs +++ b/Data/Migrations/Sqlite/20190901180209_AddSnapHitCountToClientStatistics.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddSnapHitCountToClientStatistics : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs b/Data/Migrations/Sqlite/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs rename to Data/Migrations/Sqlite/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs index 4c012301d..aa7e85848 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs +++ b/Data/Migrations/Sqlite/20190901223620_UseJunctionTableForSnapshotVector3.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20190901223620_UseJunctionTableForSnapshotVector3")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20190901223620_UseJunctionTableForSnapshotVector3.cs b/Data/Migrations/Sqlite/20190901223620_UseJunctionTableForSnapshotVector3.cs similarity index 98% rename from SharedLibraryCore/Migrations/Postgresql/20190901223620_UseJunctionTableForSnapshotVector3.cs rename to Data/Migrations/Sqlite/20190901223620_UseJunctionTableForSnapshotVector3.cs index 9f23f7769..8f8c5ff50 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190901223620_UseJunctionTableForSnapshotVector3.cs +++ b/Data/Migrations/Sqlite/20190901223620_UseJunctionTableForSnapshotVector3.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class UseJunctionTableForSnapshotVector3 : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs b/Data/Migrations/Sqlite/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs rename to Data/Migrations/Sqlite/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs index 54a1a9898..c02031702 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs +++ b/Data/Migrations/Sqlite/20190914011524_AddCurrentSnapValueToSnapshot.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20190914011524_AddCurrentSnapValueToSnapshot")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20190914011524_AddCurrentSnapValueToSnapshot.cs b/Data/Migrations/Sqlite/20190914011524_AddCurrentSnapValueToSnapshot.cs similarity index 92% rename from SharedLibraryCore/Migrations/Postgresql/20190914011524_AddCurrentSnapValueToSnapshot.cs rename to Data/Migrations/Sqlite/20190914011524_AddCurrentSnapValueToSnapshot.cs index e0d23b6df..3da2b5a94 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190914011524_AddCurrentSnapValueToSnapshot.cs +++ b/Data/Migrations/Sqlite/20190914011524_AddCurrentSnapValueToSnapshot.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddCurrentSnapValueToSnapshot : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs b/Data/Migrations/Sqlite/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs rename to Data/Migrations/Sqlite/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs index 9ddf29699..70c21140a 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs +++ b/Data/Migrations/Sqlite/20190914012015_AddSessionSnapHitsToSnapshot.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20190914012015_AddSessionSnapHitsToSnapshot")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20190914012015_AddSessionSnapHitsToSnapshot.cs b/Data/Migrations/Sqlite/20190914012015_AddSessionSnapHitsToSnapshot.cs similarity index 92% rename from SharedLibraryCore/Migrations/Postgresql/20190914012015_AddSessionSnapHitsToSnapshot.cs rename to Data/Migrations/Sqlite/20190914012015_AddSessionSnapHitsToSnapshot.cs index 687fe3ed2..cf3a3d1c8 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20190914012015_AddSessionSnapHitsToSnapshot.cs +++ b/Data/Migrations/Sqlite/20190914012015_AddSessionSnapHitsToSnapshot.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddSessionSnapHitsToSnapshot : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20191004172550_RenameClientHitLocationCountColumns.Designer.cs b/Data/Migrations/Sqlite/20191004172550_RenameClientHitLocationCountColumns.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20191004172550_RenameClientHitLocationCountColumns.Designer.cs rename to Data/Migrations/Sqlite/20191004172550_RenameClientHitLocationCountColumns.Designer.cs index 200be1d08..f294aca6e 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20191004172550_RenameClientHitLocationCountColumns.Designer.cs +++ b/Data/Migrations/Sqlite/20191004172550_RenameClientHitLocationCountColumns.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20191004172550_RenameClientHitLocationCountColumns")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20191004172550_RenameClientHitLocationCountColumns.cs b/Data/Migrations/Sqlite/20191004172550_RenameClientHitLocationCountColumns.cs similarity index 99% rename from SharedLibraryCore/Migrations/Postgresql/20191004172550_RenameClientHitLocationCountColumns.cs rename to Data/Migrations/Sqlite/20191004172550_RenameClientHitLocationCountColumns.cs index ec56f701c..bf1e79677 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20191004172550_RenameClientHitLocationCountColumns.cs +++ b/Data/Migrations/Sqlite/20191004172550_RenameClientHitLocationCountColumns.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class RenameClientHitLocationCountColumns : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs b/Data/Migrations/Sqlite/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs rename to Data/Migrations/Sqlite/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs index 7000be7a2..3e91ebefd 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs +++ b/Data/Migrations/Sqlite/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20191030000713_EnforceUniqueIndexForEFAliasIPName")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs b/Data/Migrations/Sqlite/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs similarity index 98% rename from SharedLibraryCore/Migrations/Postgresql/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs rename to Data/Migrations/Sqlite/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs index f8d771135..3f9652e10 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs +++ b/Data/Migrations/Sqlite/20191030000713_EnforceUniqueIndexForEFAliasIPName.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class EnforceUniqueIndexForEFAliasIPName : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs b/Data/Migrations/Sqlite/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs rename to Data/Migrations/Sqlite/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs index 840ea8780..62e6845af 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs +++ b/Data/Migrations/Sqlite/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs b/Data/Migrations/Sqlite/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs similarity index 94% rename from SharedLibraryCore/Migrations/Postgresql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs rename to Data/Migrations/Sqlite/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs index ae96a8c48..e58ca32e2 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs +++ b/Data/Migrations/Sqlite/20191225202141_SetCaseSensitiveCoallationForAliasNameMySQL.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class SetCaseSensitiveCoallationForAliasNameMySQL : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20191230140947_AddMissingActiveColumns.Designer.cs b/Data/Migrations/Sqlite/20191230140947_AddMissingActiveColumns.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20191230140947_AddMissingActiveColumns.Designer.cs rename to Data/Migrations/Sqlite/20191230140947_AddMissingActiveColumns.Designer.cs index e412d2c7e..dcebb574e 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20191230140947_AddMissingActiveColumns.Designer.cs +++ b/Data/Migrations/Sqlite/20191230140947_AddMissingActiveColumns.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20191230140947_AddMissingActiveColumns")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20191230140947_AddMissingActiveColumns.cs b/Data/Migrations/Sqlite/20191230140947_AddMissingActiveColumns.cs similarity index 93% rename from SharedLibraryCore/Migrations/Postgresql/20191230140947_AddMissingActiveColumns.cs rename to Data/Migrations/Sqlite/20191230140947_AddMissingActiveColumns.cs index c1ce223ae..f12929633 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20191230140947_AddMissingActiveColumns.cs +++ b/Data/Migrations/Sqlite/20191230140947_AddMissingActiveColumns.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddMissingActiveColumns : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs b/Data/Migrations/Sqlite/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs rename to Data/Migrations/Sqlite/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs index 6a74f44c4..7512ca9f8 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs +++ b/Data/Migrations/Sqlite/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20200423225137_AddImpersonationIdToEFChangeHistory")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20200423225137_AddImpersonationIdToEFChangeHistory.cs b/Data/Migrations/Sqlite/20200423225137_AddImpersonationIdToEFChangeHistory.cs similarity index 92% rename from SharedLibraryCore/Migrations/Postgresql/20200423225137_AddImpersonationIdToEFChangeHistory.cs rename to Data/Migrations/Sqlite/20200423225137_AddImpersonationIdToEFChangeHistory.cs index b2e74946a..d2a62b0a9 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20200423225137_AddImpersonationIdToEFChangeHistory.cs +++ b/Data/Migrations/Sqlite/20200423225137_AddImpersonationIdToEFChangeHistory.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddImpersonationIdToEFChangeHistory : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20200521203304_AddHostnameToEFServer.Designer.cs b/Data/Migrations/Sqlite/20200521203304_AddHostnameToEFServer.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20200521203304_AddHostnameToEFServer.Designer.cs rename to Data/Migrations/Sqlite/20200521203304_AddHostnameToEFServer.Designer.cs index 4bcbd7b71..89d5572e6 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20200521203304_AddHostnameToEFServer.Designer.cs +++ b/Data/Migrations/Sqlite/20200521203304_AddHostnameToEFServer.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20200521203304_AddHostnameToEFServer")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20200521203304_AddHostnameToEFServer.cs b/Data/Migrations/Sqlite/20200521203304_AddHostnameToEFServer.cs similarity index 91% rename from SharedLibraryCore/Migrations/Postgresql/20200521203304_AddHostnameToEFServer.cs rename to Data/Migrations/Sqlite/20200521203304_AddHostnameToEFServer.cs index 3fa054c4f..8211d5c1c 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20200521203304_AddHostnameToEFServer.cs +++ b/Data/Migrations/Sqlite/20200521203304_AddHostnameToEFServer.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddHostnameToEFServer : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20200819224119_AddIsPasswordProtectedColumn.Designer.cs b/Data/Migrations/Sqlite/20200819224119_AddIsPasswordProtectedColumn.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20200819224119_AddIsPasswordProtectedColumn.Designer.cs rename to Data/Migrations/Sqlite/20200819224119_AddIsPasswordProtectedColumn.Designer.cs index cb82b6fba..e4c0a0394 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20200819224119_AddIsPasswordProtectedColumn.Designer.cs +++ b/Data/Migrations/Sqlite/20200819224119_AddIsPasswordProtectedColumn.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20200819224119_AddIsPasswordProtectedColumn")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20200819224119_AddIsPasswordProtectedColumn.cs b/Data/Migrations/Sqlite/20200819224119_AddIsPasswordProtectedColumn.cs similarity index 95% rename from SharedLibraryCore/Migrations/Postgresql/20200819224119_AddIsPasswordProtectedColumn.cs rename to Data/Migrations/Sqlite/20200819224119_AddIsPasswordProtectedColumn.cs index 0fb89d052..4f6231dde 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20200819224119_AddIsPasswordProtectedColumn.cs +++ b/Data/Migrations/Sqlite/20200819224119_AddIsPasswordProtectedColumn.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddIsPasswordProtectedColumn : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20201114232340_UpdateEFRatingIndex.Designer.cs b/Data/Migrations/Sqlite/20201114232340_UpdateEFRatingIndex.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20201114232340_UpdateEFRatingIndex.Designer.cs rename to Data/Migrations/Sqlite/20201114232340_UpdateEFRatingIndex.Designer.cs index 1a50b70fe..e0fde6cb9 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20201114232340_UpdateEFRatingIndex.Designer.cs +++ b/Data/Migrations/Sqlite/20201114232340_UpdateEFRatingIndex.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20201114232340_UpdateEFRatingIndex")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20201114232340_UpdateEFRatingIndex.cs b/Data/Migrations/Sqlite/20201114232340_UpdateEFRatingIndex.cs similarity index 93% rename from SharedLibraryCore/Migrations/Postgresql/20201114232340_UpdateEFRatingIndex.cs rename to Data/Migrations/Sqlite/20201114232340_UpdateEFRatingIndex.cs index 779a1dc6f..220f4acb7 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20201114232340_UpdateEFRatingIndex.cs +++ b/Data/Migrations/Sqlite/20201114232340_UpdateEFRatingIndex.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class UpdateEFRatingIndex : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs b/Data/Migrations/Sqlite/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs rename to Data/Migrations/Sqlite/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs index c43c421f9..81eb862d0 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs +++ b/Data/Migrations/Sqlite/20201118023106_AddSentIngameFlagToClientMessage.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20201118023106_AddSentIngameFlagToClientMessage")] diff --git a/SharedLibraryCore/Migrations/Postgresql/20201118023106_AddSentIngameFlagToClientMessage.cs b/Data/Migrations/Sqlite/20201118023106_AddSentIngameFlagToClientMessage.cs similarity index 92% rename from SharedLibraryCore/Migrations/Postgresql/20201118023106_AddSentIngameFlagToClientMessage.cs rename to Data/Migrations/Sqlite/20201118023106_AddSentIngameFlagToClientMessage.cs index eb652b89a..6fbc15763 100644 --- a/SharedLibraryCore/Migrations/Postgresql/20201118023106_AddSentIngameFlagToClientMessage.cs +++ b/Data/Migrations/Sqlite/20201118023106_AddSentIngameFlagToClientMessage.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Postgresql +namespace Data.Migrations.Sqlite { public partial class AddSentIngameFlagToClientMessage : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/20210124164906_UpdateEFMetaToSupportNonClientMetaAndLinkedMeta.Designer.cs b/Data/Migrations/Sqlite/20210124164906_UpdateEFMetaToSupportNonClientMetaAndLinkedMeta.Designer.cs similarity index 99% rename from SharedLibraryCore/Migrations/Sqlite/20210124164906_UpdateEFMetaToSupportNonClientMetaAndLinkedMeta.Designer.cs rename to Data/Migrations/Sqlite/20210124164906_UpdateEFMetaToSupportNonClientMetaAndLinkedMeta.Designer.cs index 8966c2d4c..9c4e5de21 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20210124164906_UpdateEFMetaToSupportNonClientMetaAndLinkedMeta.Designer.cs +++ b/Data/Migrations/Sqlite/20210124164906_UpdateEFMetaToSupportNonClientMetaAndLinkedMeta.Designer.cs @@ -4,9 +4,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; +using Data.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] [Migration("20210124164906_UpdateEFMetaToSupportNonClientMetaAndLinkedMeta")] diff --git a/SharedLibraryCore/Migrations/Sqlite/20210124164906_UpdateEFMetaToSupportNonClientMetaAndLinkedMeta.cs b/Data/Migrations/Sqlite/20210124164906_UpdateEFMetaToSupportNonClientMetaAndLinkedMeta.cs similarity index 98% rename from SharedLibraryCore/Migrations/Sqlite/20210124164906_UpdateEFMetaToSupportNonClientMetaAndLinkedMeta.cs rename to Data/Migrations/Sqlite/20210124164906_UpdateEFMetaToSupportNonClientMetaAndLinkedMeta.cs index 5137722e9..0a6f64111 100644 --- a/SharedLibraryCore/Migrations/Sqlite/20210124164906_UpdateEFMetaToSupportNonClientMetaAndLinkedMeta.cs +++ b/Data/Migrations/Sqlite/20210124164906_UpdateEFMetaToSupportNonClientMetaAndLinkedMeta.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { public partial class UpdateEFMetaToSupportNonClientMetaAndLinkedMeta : Migration { diff --git a/SharedLibraryCore/Migrations/Sqlite/SqliteDatabaseContextModelSnapshot.cs b/Data/Migrations/Sqlite/20210301231432_AddAdditionalClientStatsAndZScore.Designer.cs similarity index 65% rename from SharedLibraryCore/Migrations/Sqlite/SqliteDatabaseContextModelSnapshot.cs rename to Data/Migrations/Sqlite/20210301231432_AddAdditionalClientStatsAndZScore.Designer.cs index 90f555e4e..246a50c04 100644 --- a/SharedLibraryCore/Migrations/Sqlite/SqliteDatabaseContextModelSnapshot.cs +++ b/Data/Migrations/Sqlite/20210301231432_AddAdditionalClientStatsAndZScore.Designer.cs @@ -1,22 +1,208 @@ // using System; +using Data.MigrationContext; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SharedLibraryCore.Database.MigrationContext; -namespace SharedLibraryCore.Migrations.Sqlite +namespace Data.Migrations.Sqlite { [DbContext(typeof(SqliteDatabaseContext))] - partial class SqliteDatabaseContextModelSnapshot : ModelSnapshot + [Migration("20210301231432_AddAdditionalClientStatsAndZScore")] + partial class AddAdditionalClientStatsAndZScore { - protected override void BuildModel(ModelBuilder modelBuilder) + protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "3.1.10"); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b => + modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b => + { + b.Property("ACSnapshotVector3Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("SnapshotId") + .HasColumnType("INTEGER"); + + b.Property("Vector3Id") + .HasColumnType("INTEGER"); + + b.HasKey("ACSnapshotVector3Id"); + + b.HasIndex("SnapshotId"); + + b.HasIndex("Vector3Id"); + + b.ToTable("EFACSnapshotVector3"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClient", b => + { + b.Property("ClientId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("AliasLinkId") + .HasColumnType("INTEGER"); + + b.Property("Connections") + .HasColumnType("INTEGER"); + + b.Property("CurrentAliasId") + .HasColumnType("INTEGER"); + + b.Property("FirstConnection") + .HasColumnType("TEXT"); + + b.Property("LastConnection") + .HasColumnType("TEXT"); + + b.Property("Level") + .HasColumnType("INTEGER"); + + b.Property("Masked") + .HasColumnType("INTEGER"); + + b.Property("NetworkId") + .HasColumnType("INTEGER"); + + b.Property("Password") + .HasColumnType("TEXT"); + + b.Property("PasswordSalt") + .HasColumnType("TEXT"); + + b.Property("TotalConnectionTime") + .HasColumnType("INTEGER"); + + b.HasKey("ClientId"); + + b.HasIndex("AliasLinkId"); + + b.HasIndex("CurrentAliasId"); + + b.HasIndex("NetworkId") + .IsUnique(); + + b.ToTable("EFClients"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientKill", b => + { + b.Property("KillId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("AttackerId") + .HasColumnType("INTEGER"); + + b.Property("Damage") + .HasColumnType("INTEGER"); + + b.Property("DeathOriginVector3Id") + .HasColumnType("INTEGER"); + + b.Property("DeathType") + .HasColumnType("INTEGER"); + + b.Property("Fraction") + .HasColumnType("REAL"); + + b.Property("HitLoc") + .HasColumnType("INTEGER"); + + b.Property("IsKill") + .HasColumnType("INTEGER"); + + b.Property("KillOriginVector3Id") + .HasColumnType("INTEGER"); + + b.Property("Map") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("VictimId") + .HasColumnType("INTEGER"); + + b.Property("ViewAnglesVector3Id") + .HasColumnType("INTEGER"); + + b.Property("VisibilityPercentage") + .HasColumnType("REAL"); + + b.Property("Weapon") + .HasColumnType("INTEGER"); + + b.Property("When") + .HasColumnType("TEXT"); + + 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("Data.Models.Client.EFClientMessage", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.Property("Message") + .HasColumnType("TEXT"); + + b.Property("SentIngame") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("TimeSent") + .HasColumnType("TEXT"); + + b.HasKey("MessageId"); + + b.HasIndex("ClientId"); + + b.HasIndex("ServerId"); + + b.HasIndex("TimeSent"); + + b.ToTable("EFClientMessages"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b => { b.Property("SnapshotId") .ValueGeneratedOnAdd() @@ -112,137 +298,81 @@ namespace SharedLibraryCore.Migrations.Sqlite b.ToTable("EFACSnapshot"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshotVector3", b => + modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b => { - b.Property("ACSnapshotVector3Id") + b.Property("ClientHitStatisticId") .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.Property("Active") - .HasColumnType("INTEGER"); - - b.Property("SnapshotId") - .HasColumnType("INTEGER"); - - b.Property("Vector3Id") - .HasColumnType("INTEGER"); - - b.HasKey("ACSnapshotVector3Id"); - - b.HasIndex("SnapshotId"); - - b.HasIndex("Vector3Id"); - - b.ToTable("EFACSnapshotVector3"); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b => - { - b.Property("KillId") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Active") - .HasColumnType("INTEGER"); - - b.Property("AttackerId") - .HasColumnType("INTEGER"); - - b.Property("Damage") - .HasColumnType("INTEGER"); - - b.Property("DeathOriginVector3Id") - .HasColumnType("INTEGER"); - - b.Property("DeathType") - .HasColumnType("INTEGER"); - - b.Property("Fraction") - .HasColumnType("REAL"); - - b.Property("HitLoc") - .HasColumnType("INTEGER"); - - b.Property("IsKill") - .HasColumnType("INTEGER"); - - b.Property("KillOriginVector3Id") - .HasColumnType("INTEGER"); - - b.Property("Map") - .HasColumnType("INTEGER"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("VictimId") - .HasColumnType("INTEGER"); - - b.Property("ViewAnglesVector3Id") - .HasColumnType("INTEGER"); - - b.Property("VisibilityPercentage") - .HasColumnType("REAL"); - - b.Property("Weapon") - .HasColumnType("INTEGER"); - - b.Property("When") - .HasColumnType("TEXT"); - - 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() - .HasColumnType("INTEGER"); - - b.Property("Active") - .HasColumnType("INTEGER"); - b.Property("ClientId") .HasColumnType("INTEGER"); - b.Property("Message") + b.Property("CreatedDateTime") .HasColumnType("TEXT"); - b.Property("SentIngame") + b.Property("DamageInflicted") .HasColumnType("INTEGER"); - b.Property("ServerId") + b.Property("DamageReceived") .HasColumnType("INTEGER"); - b.Property("TimeSent") + b.Property("DeathCount") + .HasColumnType("INTEGER"); + + b.Property("HitCount") + .HasColumnType("INTEGER"); + + b.Property("HitLocationId") + .HasColumnType("INTEGER"); + + b.Property("KillCount") + .HasColumnType("INTEGER"); + + b.Property("MeansOfDeathId") + .HasColumnType("INTEGER"); + + b.Property("ReceivedHitCount") + .HasColumnType("INTEGER"); + + b.Property("Score") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("SuicideCount") + .HasColumnType("INTEGER"); + + b.Property("UpdatedDateTime") .HasColumnType("TEXT"); - b.HasKey("MessageId"); + b.Property("UsageSeconds") + .HasColumnType("INTEGER"); + + b.Property("WeaponAttachmentComboId") + .HasColumnType("INTEGER"); + + b.Property("WeaponId") + .HasColumnType("INTEGER"); + + b.HasKey("ClientHitStatisticId"); b.HasIndex("ClientId"); + b.HasIndex("HitLocationId"); + + b.HasIndex("MeansOfDeathId"); + b.HasIndex("ServerId"); - b.HasIndex("TimeSent"); + b.HasIndex("WeaponAttachmentComboId"); - b.ToTable("EFClientMessages"); + b.HasIndex("WeaponId"); + + b.ToTable("EFClientHitStatistics"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b => + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b => { b.Property("RatingHistoryId") .ValueGeneratedOnAdd() @@ -261,7 +391,7 @@ namespace SharedLibraryCore.Migrations.Sqlite b.ToTable("EFClientRatingHistory"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b => + modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b => { b.Property("ClientId") .HasColumnType("INTEGER"); @@ -272,9 +402,6 @@ namespace SharedLibraryCore.Migrations.Sqlite b.Property("Active") .HasColumnType("INTEGER"); - b.Property("AverageRecoilOffset") - .HasColumnType("REAL"); - b.Property("AverageSnapValue") .HasColumnType("REAL"); @@ -305,17 +432,24 @@ namespace SharedLibraryCore.Migrations.Sqlite b.Property("TimePlayed") .HasColumnType("INTEGER"); - b.Property("VisionAverage") + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("ZScore") .HasColumnType("REAL"); b.HasKey("ClientId", "ServerId"); b.HasIndex("ServerId"); + b.HasIndex("ZScore"); + + b.HasIndex("ClientId", "TimePlayed", "ZScore"); + b.ToTable("EFClientStatistics"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b => + modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b => { b.Property("HitLocationCountId") .ValueGeneratedOnAdd() @@ -353,7 +487,7 @@ namespace SharedLibraryCore.Migrations.Sqlite b.ToTable("EFHitLocationCounts"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b => + modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b => { b.Property("RatingId") .ValueGeneratedOnAdd() @@ -396,60 +530,166 @@ namespace SharedLibraryCore.Migrations.Sqlite b.ToTable("EFRating"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b => + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFHitLocation", b => { - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Active") - .HasColumnType("INTEGER"); - - b.Property("EndPoint") - .HasColumnType("TEXT"); - - b.Property("GameName") - .HasColumnType("INTEGER"); - - b.Property("HostName") - .HasColumnType("TEXT"); - - b.Property("IsPasswordProtected") - .HasColumnType("INTEGER"); - - b.Property("Port") - .HasColumnType("INTEGER"); - - b.HasKey("ServerId"); - - b.ToTable("EFServers"); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b => - { - b.Property("StatisticId") + b.Property("HitLocationId") .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.Property("Active") + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") .HasColumnType("INTEGER"); - b.Property("ServerId") - .HasColumnType("INTEGER"); + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); - b.Property("TotalKills") - .HasColumnType("INTEGER"); + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); - b.Property("TotalPlayTime") - .HasColumnType("INTEGER"); + b.HasKey("HitLocationId"); - b.HasKey("StatisticId"); + b.HasIndex("Name"); - b.HasIndex("ServerId"); - - b.ToTable("EFServerStatistics"); + b.ToTable("EFHitLocations"); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b => + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMap", b => + { + b.Property("MapId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("MapId"); + + b.ToTable("EFMaps"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMeansOfDeath", b => + { + b.Property("MeansOfDeathId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("MeansOfDeathId"); + + b.ToTable("EFMeansOfDeath"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeapon", b => + { + b.Property("WeaponId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("WeaponId"); + + b.HasIndex("Name"); + + b.ToTable("EFWeapons"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachment", b => + { + b.Property("WeaponAttachmentId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("WeaponAttachmentId"); + + b.ToTable("EFWeaponAttachments"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b => + { + b.Property("WeaponAttachmentComboId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attachment1Id") + .HasColumnType("INTEGER"); + + b.Property("Attachment2Id") + .HasColumnType("INTEGER"); + + b.Property("Attachment3Id") + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("WeaponAttachmentComboId"); + + b.HasIndex("Attachment1Id"); + + b.HasIndex("Attachment2Id"); + + b.HasIndex("Attachment3Id"); + + b.ToTable("EFWeaponAttachmentCombos"); + }); + + modelBuilder.Entity("Data.Models.EFAlias", b => { b.Property("AliasId") .ValueGeneratedOnAdd() @@ -492,7 +732,7 @@ namespace SharedLibraryCore.Migrations.Sqlite b.ToTable("EFAlias"); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b => + modelBuilder.Entity("Data.Models.EFAliasLink", b => { b.Property("AliasLinkId") .ValueGeneratedOnAdd() @@ -506,7 +746,7 @@ namespace SharedLibraryCore.Migrations.Sqlite b.ToTable("EFAliasLinks"); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b => + modelBuilder.Entity("Data.Models.EFChangeHistory", b => { b.Property("ChangeHistoryId") .ValueGeneratedOnAdd() @@ -545,61 +785,7 @@ namespace SharedLibraryCore.Migrations.Sqlite b.ToTable("EFChangeHistory"); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b => - { - b.Property("ClientId") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Active") - .HasColumnType("INTEGER"); - - b.Property("AliasLinkId") - .HasColumnType("INTEGER"); - - b.Property("Connections") - .HasColumnType("INTEGER"); - - b.Property("CurrentAliasId") - .HasColumnType("INTEGER"); - - b.Property("FirstConnection") - .HasColumnType("TEXT"); - - b.Property("LastConnection") - .HasColumnType("TEXT"); - - b.Property("Level") - .HasColumnType("INTEGER"); - - b.Property("Masked") - .HasColumnType("INTEGER"); - - b.Property("NetworkId") - .HasColumnType("INTEGER"); - - b.Property("Password") - .HasColumnType("TEXT"); - - b.Property("PasswordSalt") - .HasColumnType("TEXT"); - - b.Property("TotalConnectionTime") - .HasColumnType("INTEGER"); - - b.HasKey("ClientId"); - - b.HasIndex("AliasLinkId"); - - b.HasIndex("CurrentAliasId"); - - b.HasIndex("NetworkId") - .IsUnique(); - - b.ToTable("EFClients"); - }); - - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b => + modelBuilder.Entity("Data.Models.EFMeta", b => { b.Property("MetaId") .ValueGeneratedOnAdd() @@ -643,7 +829,7 @@ namespace SharedLibraryCore.Migrations.Sqlite b.ToTable("EFMeta"); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b => + modelBuilder.Entity("Data.Models.EFPenalty", b => { b.Property("PenaltyId") .ValueGeneratedOnAdd() @@ -691,7 +877,60 @@ namespace SharedLibraryCore.Migrations.Sqlite b.ToTable("EFPenalties"); }); - modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b => + modelBuilder.Entity("Data.Models.Server.EFServer", b => + { + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("EndPoint") + .HasColumnType("TEXT"); + + b.Property("GameName") + .HasColumnType("INTEGER"); + + b.Property("HostName") + .HasColumnType("TEXT"); + + b.Property("IsPasswordProtected") + .HasColumnType("INTEGER"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.HasKey("ServerId"); + + b.ToTable("EFServers"); + }); + + modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b => + { + b.Property("StatisticId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("TotalKills") + .HasColumnType("INTEGER"); + + b.Property("TotalPlayTime") + .HasColumnType("INTEGER"); + + b.HasKey("StatisticId"); + + b.HasIndex("ServerId"); + + b.ToTable("EFServerStatistics"); + }); + + modelBuilder.Entity("Data.Models.Vector3", b => { b.Property("Vector3Id") .ValueGeneratedOnAdd() @@ -711,225 +950,271 @@ namespace SharedLibraryCore.Migrations.Sqlite b.ToTable("Vector3"); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b => + modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b => { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") - .WithMany() - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SharedLibraryCore.Helpers.Vector3", "CurrentViewAngle") - .WithMany() - .HasForeignKey("CurrentViewAngleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination") - .WithMany() - .HasForeignKey("HitDestinationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin") - .WithMany() - .HasForeignKey("HitOriginId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle") - .WithMany() - .HasForeignKey("LastStrainAngleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshotVector3", b => - { - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", "Snapshot") + b.HasOne("Data.Models.Client.Stats.EFACSnapshot", "Snapshot") .WithMany("PredictedViewAngles") .HasForeignKey("SnapshotId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("SharedLibraryCore.Helpers.Vector3", "Vector") + b.HasOne("Data.Models.Vector3", "Vector") .WithMany() .HasForeignKey("Vector3Id") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b => + modelBuilder.Entity("Data.Models.Client.EFClient", b => { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker") - .WithMany() - .HasForeignKey("AttackerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - 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) - .IsRequired(); - - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim") - .WithMany() - .HasForeignKey("VictimId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - 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) - .IsRequired(); - - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server") - .WithMany() - .HasForeignKey("ServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b => - { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") - .WithMany() - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b => - { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") - .WithMany() - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server") - .WithMany() - .HasForeignKey("ServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b => - { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") - .WithMany() - .HasForeignKey("EFClientStatisticsClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server") - .WithMany() - .HasForeignKey("EFClientStatisticsServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", null) - .WithMany("HitLocations") - .HasForeignKey("EFClientStatisticsClientId", "EFClientStatisticsServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b => - { - b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", "RatingHistory") - .WithMany("Ratings") - .HasForeignKey("RatingHistoryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - 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) - .IsRequired(); - }); - - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b => - { - b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link") - .WithMany("Children") - .HasForeignKey("LinkId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - }); - - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b => - { - b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink") + b.HasOne("Data.Models.EFAliasLink", "AliasLink") .WithMany() .HasForeignKey("AliasLinkId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias") + b.HasOne("Data.Models.EFAlias", "CurrentAlias") .WithMany() .HasForeignKey("CurrentAliasId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b => + modelBuilder.Entity("Data.Models.Client.EFClientKill", b => { - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") + b.HasOne("Data.Models.Client.EFClient", "Attacker") + .WithMany() + .HasForeignKey("AttackerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "DeathOrigin") + .WithMany() + .HasForeignKey("DeathOriginVector3Id"); + + b.HasOne("Data.Models.Vector3", "KillOrigin") + .WithMany() + .HasForeignKey("KillOriginVector3Id"); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.EFClient", "Victim") + .WithMany() + .HasForeignKey("VictimId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "ViewAngles") + .WithMany() + .HasForeignKey("ViewAnglesVector3Id"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientMessage", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "CurrentViewAngle") + .WithMany() + .HasForeignKey("CurrentViewAngleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "HitDestination") + .WithMany() + .HasForeignKey("HitDestinationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "HitOrigin") + .WithMany() + .HasForeignKey("HitOriginId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "LastStrainAngle") + .WithMany() + .HasForeignKey("LastStrainAngleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.Reference.EFHitLocation", "HitLocation") + .WithMany() + .HasForeignKey("HitLocationId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFMeansOfDeath", "MeansOfDeath") + .WithMany() + .HasForeignKey("MeansOfDeathId"); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", "WeaponAttachmentCombo") + .WithMany() + .HasForeignKey("WeaponAttachmentComboId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeapon", "Weapon") + .WithMany() + .HasForeignKey("WeaponId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("EFClientStatisticsClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("EFClientStatisticsServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.EFClientStatistics", null) + .WithMany("HitLocations") + .HasForeignKey("EFClientStatisticsClientId", "EFClientStatisticsServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b => + { + b.HasOne("Data.Models.Client.Stats.EFClientRatingHistory", "RatingHistory") + .WithMany("Ratings") + .HasForeignKey("RatingHistoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b => + { + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment1") + .WithMany() + .HasForeignKey("Attachment1Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment2") + .WithMany() + .HasForeignKey("Attachment2Id"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment3") + .WithMany() + .HasForeignKey("Attachment3Id"); + }); + + modelBuilder.Entity("Data.Models.EFAlias", b => + { + b.HasOne("Data.Models.EFAliasLink", "Link") + .WithMany("Children") + .HasForeignKey("LinkId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.EFMeta", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") .WithMany("Meta") .HasForeignKey("ClientId"); - b.HasOne("SharedLibraryCore.Database.Models.EFMeta", "LinkedMeta") + b.HasOne("Data.Models.EFMeta", "LinkedMeta") .WithMany() .HasForeignKey("LinkedMetaId") .OnDelete(DeleteBehavior.SetNull); }); - modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b => + modelBuilder.Entity("Data.Models.EFPenalty", b => { - b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link") + b.HasOne("Data.Models.EFAliasLink", "Link") .WithMany("ReceivedPenalties") .HasForeignKey("LinkId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender") + b.HasOne("Data.Models.Client.EFClient", "Offender") .WithMany("ReceivedPenalties") .HasForeignKey("OffenderId") .OnDelete(DeleteBehavior.Restrict) .IsRequired(); - b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher") + b.HasOne("Data.Models.Client.EFClient", "Punisher") .WithMany("AdministeredPenalties") .HasForeignKey("PunisherId") .OnDelete(DeleteBehavior.Restrict) .IsRequired(); }); + + modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b => + { + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); #pragma warning restore 612, 618 } } diff --git a/Data/Migrations/Sqlite/20210301231432_AddAdditionalClientStatsAndZScore.cs b/Data/Migrations/Sqlite/20210301231432_AddAdditionalClientStatsAndZScore.cs new file mode 100644 index 000000000..da480a023 --- /dev/null +++ b/Data/Migrations/Sqlite/20210301231432_AddAdditionalClientStatsAndZScore.cs @@ -0,0 +1,273 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Data.Migrations.Sqlite +{ + public partial class AddAdditionalClientStatsAndZScore : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "UpdatedAt", + table: "EFClientStatistics", + nullable: true); + + migrationBuilder.AddColumn( + name: "ZScore", + table: "EFClientStatistics", + nullable: false, + defaultValue: 0.0); + + migrationBuilder.CreateTable( + name: "EFHitLocations", + columns: table => new + { + HitLocationId = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Name = table.Column(nullable: false), + Game = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EFHitLocations", x => x.HitLocationId); + }); + + migrationBuilder.CreateTable( + name: "EFMaps", + columns: table => new + { + MapId = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Name = table.Column(nullable: false), + Game = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EFMaps", x => x.MapId); + }); + + migrationBuilder.CreateTable( + name: "EFMeansOfDeath", + columns: table => new + { + MeansOfDeathId = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Name = table.Column(nullable: false), + Game = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EFMeansOfDeath", x => x.MeansOfDeathId); + }); + + migrationBuilder.CreateTable( + name: "EFWeaponAttachments", + columns: table => new + { + WeaponAttachmentId = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Name = table.Column(nullable: false), + Game = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EFWeaponAttachments", x => x.WeaponAttachmentId); + }); + + migrationBuilder.CreateTable( + name: "EFWeapons", + columns: table => new + { + WeaponId = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Name = table.Column(nullable: false), + Game = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EFWeapons", x => x.WeaponId); + }); + + migrationBuilder.CreateTable( + name: "EFWeaponAttachmentCombos", + columns: table => new + { + WeaponAttachmentComboId = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + Game = table.Column(nullable: false), + Attachment1Id = table.Column(nullable: false), + Attachment2Id = table.Column(nullable: true), + Attachment3Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EFWeaponAttachmentCombos", x => x.WeaponAttachmentComboId); + table.ForeignKey( + name: "FK_EFWeaponAttachmentCombos_EFWeaponAttachments_Attachment1Id", + column: x => x.Attachment1Id, + principalTable: "EFWeaponAttachments", + principalColumn: "WeaponAttachmentId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_EFWeaponAttachmentCombos_EFWeaponAttachments_Attachment2Id", + column: x => x.Attachment2Id, + principalTable: "EFWeaponAttachments", + principalColumn: "WeaponAttachmentId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EFWeaponAttachmentCombos_EFWeaponAttachments_Attachment3Id", + column: x => x.Attachment3Id, + principalTable: "EFWeaponAttachments", + principalColumn: "WeaponAttachmentId", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "EFClientHitStatistics", + columns: table => new + { + ClientHitStatisticId = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + ClientId = table.Column(nullable: false), + ServerId = table.Column(nullable: true), + HitLocationId = table.Column(nullable: true), + MeansOfDeathId = table.Column(nullable: true), + WeaponId = table.Column(nullable: true), + WeaponAttachmentComboId = table.Column(nullable: true), + HitCount = table.Column(nullable: false), + KillCount = table.Column(nullable: false), + DamageInflicted = table.Column(nullable: false), + ReceivedHitCount = table.Column(nullable: false), + DeathCount = table.Column(nullable: false), + DamageReceived = table.Column(nullable: false), + SuicideCount = table.Column(nullable: false), + UsageSeconds = table.Column(nullable: true), + Score = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EFClientHitStatistics", x => x.ClientHitStatisticId); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFClients_ClientId", + column: x => x.ClientId, + principalTable: "EFClients", + principalColumn: "ClientId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFHitLocations_HitLocationId", + column: x => x.HitLocationId, + principalTable: "EFHitLocations", + principalColumn: "HitLocationId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFMeansOfDeath_MeansOfDeathId", + column: x => x.MeansOfDeathId, + principalTable: "EFMeansOfDeath", + principalColumn: "MeansOfDeathId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFServers_ServerId", + column: x => x.ServerId, + principalTable: "EFServers", + principalColumn: "ServerId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFWeaponAttachmentCombos_WeaponAttachmentComboId", + column: x => x.WeaponAttachmentComboId, + principalTable: "EFWeaponAttachmentCombos", + principalColumn: "WeaponAttachmentComboId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EFClientHitStatistics_EFWeapons_WeaponId", + column: x => x.WeaponId, + principalTable: "EFWeapons", + principalColumn: "WeaponId", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_EFClientStatistics_ZScore", + table: "EFClientStatistics", + column: "ZScore"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientStatistics_ClientId_TimePlayed_ZScore", + table: "EFClientStatistics", + columns: new[] { "ClientId", "TimePlayed", "ZScore" }); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_ClientId", + table: "EFClientHitStatistics", + column: "ClientId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_HitLocationId", + table: "EFClientHitStatistics", + column: "HitLocationId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_MeansOfDeathId", + table: "EFClientHitStatistics", + column: "MeansOfDeathId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_ServerId", + table: "EFClientHitStatistics", + column: "ServerId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_WeaponAttachmentComboId", + table: "EFClientHitStatistics", + column: "WeaponAttachmentComboId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientHitStatistics_WeaponId", + table: "EFClientHitStatistics", + column: "WeaponId"); + + migrationBuilder.CreateIndex( + name: "IX_EFHitLocations_Name", + table: "EFHitLocations", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_EFWeaponAttachmentCombos_Attachment1Id", + table: "EFWeaponAttachmentCombos", + column: "Attachment1Id"); + + migrationBuilder.CreateIndex( + name: "IX_EFWeaponAttachmentCombos_Attachment2Id", + table: "EFWeaponAttachmentCombos", + column: "Attachment2Id"); + + migrationBuilder.CreateIndex( + name: "IX_EFWeaponAttachmentCombos_Attachment3Id", + table: "EFWeaponAttachmentCombos", + column: "Attachment3Id"); + + migrationBuilder.CreateIndex( + name: "IX_EFWeapons_Name", + table: "EFWeapons", + column: "Name"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Data/Migrations/Sqlite/20210310231004_AddRankingHistory.Designer.cs b/Data/Migrations/Sqlite/20210310231004_AddRankingHistory.Designer.cs new file mode 100644 index 000000000..e4fe819ae --- /dev/null +++ b/Data/Migrations/Sqlite/20210310231004_AddRankingHistory.Designer.cs @@ -0,0 +1,1279 @@ +// +using System; +using Data.MigrationContext; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Data.Migrations.Sqlite +{ + [DbContext(typeof(SqliteDatabaseContext))] + [Migration("20210310231004_AddRankingHistory")] + partial class AddRankingHistory + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.10"); + + modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b => + { + b.Property("ACSnapshotVector3Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("SnapshotId") + .HasColumnType("INTEGER"); + + b.Property("Vector3Id") + .HasColumnType("INTEGER"); + + b.HasKey("ACSnapshotVector3Id"); + + b.HasIndex("SnapshotId"); + + b.HasIndex("Vector3Id"); + + b.ToTable("EFACSnapshotVector3"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClient", b => + { + b.Property("ClientId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("AliasLinkId") + .HasColumnType("INTEGER"); + + b.Property("Connections") + .HasColumnType("INTEGER"); + + b.Property("CurrentAliasId") + .HasColumnType("INTEGER"); + + b.Property("FirstConnection") + .HasColumnType("TEXT"); + + b.Property("LastConnection") + .HasColumnType("TEXT"); + + b.Property("Level") + .HasColumnType("INTEGER"); + + b.Property("Masked") + .HasColumnType("INTEGER"); + + b.Property("NetworkId") + .HasColumnType("INTEGER"); + + b.Property("Password") + .HasColumnType("TEXT"); + + b.Property("PasswordSalt") + .HasColumnType("TEXT"); + + b.Property("TotalConnectionTime") + .HasColumnType("INTEGER"); + + b.HasKey("ClientId"); + + b.HasIndex("AliasLinkId"); + + b.HasIndex("CurrentAliasId"); + + b.HasIndex("NetworkId") + .IsUnique(); + + b.ToTable("EFClients"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientKill", b => + { + b.Property("KillId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("AttackerId") + .HasColumnType("INTEGER"); + + b.Property("Damage") + .HasColumnType("INTEGER"); + + b.Property("DeathOriginVector3Id") + .HasColumnType("INTEGER"); + + b.Property("DeathType") + .HasColumnType("INTEGER"); + + b.Property("Fraction") + .HasColumnType("REAL"); + + b.Property("HitLoc") + .HasColumnType("INTEGER"); + + b.Property("IsKill") + .HasColumnType("INTEGER"); + + b.Property("KillOriginVector3Id") + .HasColumnType("INTEGER"); + + b.Property("Map") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("VictimId") + .HasColumnType("INTEGER"); + + b.Property("ViewAnglesVector3Id") + .HasColumnType("INTEGER"); + + b.Property("VisibilityPercentage") + .HasColumnType("REAL"); + + b.Property("Weapon") + .HasColumnType("INTEGER"); + + b.Property("When") + .HasColumnType("TEXT"); + + 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("Data.Models.Client.EFClientMessage", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.Property("Message") + .HasColumnType("TEXT"); + + b.Property("SentIngame") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("TimeSent") + .HasColumnType("TEXT"); + + b.HasKey("MessageId"); + + b.HasIndex("ClientId"); + + b.HasIndex("ServerId"); + + b.HasIndex("TimeSent"); + + b.ToTable("EFClientMessages"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b => + { + b.Property("SnapshotId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.Property("CurrentSessionLength") + .HasColumnType("INTEGER"); + + b.Property("CurrentStrain") + .HasColumnType("REAL"); + + b.Property("CurrentViewAngleId") + .HasColumnType("INTEGER"); + + b.Property("Deaths") + .HasColumnType("INTEGER"); + + b.Property("Distance") + .HasColumnType("REAL"); + + b.Property("EloRating") + .HasColumnType("REAL"); + + b.Property("HitDestinationId") + .HasColumnType("INTEGER"); + + b.Property("HitLocation") + .HasColumnType("INTEGER"); + + b.Property("HitOriginId") + .HasColumnType("INTEGER"); + + b.Property("HitType") + .HasColumnType("INTEGER"); + + b.Property("Hits") + .HasColumnType("INTEGER"); + + b.Property("Kills") + .HasColumnType("INTEGER"); + + b.Property("LastStrainAngleId") + .HasColumnType("INTEGER"); + + b.Property("RecoilOffset") + .HasColumnType("REAL"); + + b.Property("SessionAngleOffset") + .HasColumnType("REAL"); + + b.Property("SessionAverageSnapValue") + .HasColumnType("REAL"); + + b.Property("SessionSPM") + .HasColumnType("REAL"); + + b.Property("SessionScore") + .HasColumnType("INTEGER"); + + b.Property("SessionSnapHits") + .HasColumnType("INTEGER"); + + b.Property("StrainAngleBetween") + .HasColumnType("REAL"); + + b.Property("TimeSinceLastEvent") + .HasColumnType("INTEGER"); + + b.Property("WeaponId") + .HasColumnType("INTEGER"); + + b.Property("When") + .HasColumnType("TEXT"); + + b.HasKey("SnapshotId"); + + b.HasIndex("ClientId"); + + b.HasIndex("CurrentViewAngleId"); + + b.HasIndex("HitDestinationId"); + + b.HasIndex("HitOriginId"); + + b.HasIndex("LastStrainAngleId"); + + b.ToTable("EFACSnapshot"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b => + { + b.Property("ClientHitStatisticId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("DamageInflicted") + .HasColumnType("INTEGER"); + + b.Property("DamageReceived") + .HasColumnType("INTEGER"); + + b.Property("DeathCount") + .HasColumnType("INTEGER"); + + b.Property("HitCount") + .HasColumnType("INTEGER"); + + b.Property("HitLocationId") + .HasColumnType("INTEGER"); + + b.Property("KillCount") + .HasColumnType("INTEGER"); + + b.Property("MeansOfDeathId") + .HasColumnType("INTEGER"); + + b.Property("ReceivedHitCount") + .HasColumnType("INTEGER"); + + b.Property("Score") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("SuicideCount") + .HasColumnType("INTEGER"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.Property("UsageSeconds") + .HasColumnType("INTEGER"); + + b.Property("WeaponAttachmentComboId") + .HasColumnType("INTEGER"); + + b.Property("WeaponId") + .HasColumnType("INTEGER"); + + b.HasKey("ClientHitStatisticId"); + + b.HasIndex("ClientId"); + + b.HasIndex("HitLocationId"); + + b.HasIndex("MeansOfDeathId"); + + b.HasIndex("ServerId"); + + b.HasIndex("WeaponAttachmentComboId"); + + b.HasIndex("WeaponId"); + + b.ToTable("EFClientHitStatistics"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRankingHistory", b => + { + b.Property("ClientRankingHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Newest") + .HasColumnType("INTEGER"); + + b.Property("PerformanceMetric") + .HasColumnType("REAL"); + + b.Property("Ranking") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.Property("ZScore") + .HasColumnType("REAL"); + + b.HasKey("ClientRankingHistoryId"); + + b.HasIndex("ClientId"); + + b.HasIndex("Ranking"); + + b.HasIndex("ServerId"); + + b.HasIndex("UpdatedDateTime"); + + b.HasIndex("ZScore"); + + b.ToTable("EFClientRankingHistory"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b => + { + b.Property("RatingHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.HasKey("RatingHistoryId"); + + b.HasIndex("ClientId"); + + b.ToTable("EFClientRatingHistory"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b => + { + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("AverageSnapValue") + .HasColumnType("REAL"); + + b.Property("Deaths") + .HasColumnType("INTEGER"); + + b.Property("EloRating") + .HasColumnType("REAL"); + + b.Property("Kills") + .HasColumnType("INTEGER"); + + b.Property("MaxStrain") + .HasColumnType("REAL"); + + b.Property("RollingWeightedKDR") + .HasColumnType("REAL"); + + b.Property("SPM") + .HasColumnType("REAL"); + + b.Property("Skill") + .HasColumnType("REAL"); + + b.Property("SnapHitCount") + .HasColumnType("INTEGER"); + + b.Property("TimePlayed") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("ZScore") + .HasColumnType("REAL"); + + b.HasKey("ClientId", "ServerId"); + + b.HasIndex("ServerId"); + + b.HasIndex("ZScore"); + + b.HasIndex("ClientId", "TimePlayed", "ZScore"); + + b.ToTable("EFClientStatistics"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b => + { + b.Property("HitLocationCountId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("EFClientStatisticsClientId") + .HasColumnName("EFClientStatisticsClientId") + .HasColumnType("INTEGER"); + + b.Property("EFClientStatisticsServerId") + .HasColumnName("EFClientStatisticsServerId") + .HasColumnType("INTEGER"); + + b.Property("HitCount") + .HasColumnType("INTEGER"); + + b.Property("HitOffsetAverage") + .HasColumnType("REAL"); + + b.Property("Location") + .HasColumnType("INTEGER"); + + b.Property("MaxAngleDistance") + .HasColumnType("REAL"); + + b.HasKey("HitLocationCountId"); + + b.HasIndex("EFClientStatisticsServerId"); + + b.HasIndex("EFClientStatisticsClientId", "EFClientStatisticsServerId"); + + b.ToTable("EFHitLocationCounts"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b => + { + b.Property("RatingId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("ActivityAmount") + .HasColumnType("INTEGER"); + + b.Property("Newest") + .HasColumnType("INTEGER"); + + b.Property("Performance") + .HasColumnType("REAL"); + + b.Property("Ranking") + .HasColumnType("INTEGER"); + + b.Property("RatingHistoryId") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("When") + .HasColumnType("TEXT"); + + b.HasKey("RatingId"); + + b.HasIndex("RatingHistoryId"); + + b.HasIndex("ServerId"); + + b.HasIndex("Performance", "Ranking", "When"); + + b.HasIndex("When", "ServerId", "Performance", "ActivityAmount"); + + b.ToTable("EFRating"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFHitLocation", b => + { + b.Property("HitLocationId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("HitLocationId"); + + b.HasIndex("Name"); + + b.ToTable("EFHitLocations"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMap", b => + { + b.Property("MapId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("MapId"); + + b.ToTable("EFMaps"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMeansOfDeath", b => + { + b.Property("MeansOfDeathId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("MeansOfDeathId"); + + b.ToTable("EFMeansOfDeath"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeapon", b => + { + b.Property("WeaponId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("WeaponId"); + + b.HasIndex("Name"); + + b.ToTable("EFWeapons"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachment", b => + { + b.Property("WeaponAttachmentId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("WeaponAttachmentId"); + + b.ToTable("EFWeaponAttachments"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b => + { + b.Property("WeaponAttachmentComboId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attachment1Id") + .HasColumnType("INTEGER"); + + b.Property("Attachment2Id") + .HasColumnType("INTEGER"); + + b.Property("Attachment3Id") + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("WeaponAttachmentComboId"); + + b.HasIndex("Attachment1Id"); + + b.HasIndex("Attachment2Id"); + + b.HasIndex("Attachment3Id"); + + b.ToTable("EFWeaponAttachmentCombos"); + }); + + modelBuilder.Entity("Data.Models.EFAlias", b => + { + b.Property("AliasId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("IPAddress") + .HasColumnType("INTEGER"); + + b.Property("LinkId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(24); + + b.Property("SearchableName") + .HasColumnType("TEXT") + .HasMaxLength(24); + + b.HasKey("AliasId"); + + b.HasIndex("IPAddress"); + + b.HasIndex("LinkId"); + + b.HasIndex("Name"); + + b.HasIndex("SearchableName"); + + b.HasIndex("Name", "IPAddress") + .IsUnique(); + + b.ToTable("EFAlias"); + }); + + modelBuilder.Entity("Data.Models.EFAliasLink", b => + { + b.Property("AliasLinkId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.HasKey("AliasLinkId"); + + b.ToTable("EFAliasLinks"); + }); + + modelBuilder.Entity("Data.Models.EFChangeHistory", b => + { + b.Property("ChangeHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("Comment") + .HasColumnType("TEXT") + .HasMaxLength(128); + + b.Property("CurrentValue") + .HasColumnType("TEXT"); + + b.Property("ImpersonationEntityId") + .HasColumnType("INTEGER"); + + b.Property("OriginEntityId") + .HasColumnType("INTEGER"); + + b.Property("PreviousValue") + .HasColumnType("TEXT"); + + b.Property("TargetEntityId") + .HasColumnType("INTEGER"); + + b.Property("TimeChanged") + .HasColumnType("TEXT"); + + b.Property("TypeOfChange") + .HasColumnType("INTEGER"); + + b.HasKey("ChangeHistoryId"); + + b.ToTable("EFChangeHistory"); + }); + + modelBuilder.Entity("Data.Models.EFMeta", b => + { + b.Property("MetaId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.Property("Created") + .HasColumnType("TEXT"); + + b.Property("Extra") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(32); + + b.Property("LinkedMetaId") + .HasColumnType("INTEGER"); + + b.Property("Updated") + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("MetaId"); + + b.HasIndex("ClientId"); + + b.HasIndex("Key"); + + b.HasIndex("LinkedMetaId"); + + b.ToTable("EFMeta"); + }); + + modelBuilder.Entity("Data.Models.EFPenalty", b => + { + b.Property("PenaltyId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("AutomatedOffense") + .HasColumnType("TEXT"); + + b.Property("Expires") + .HasColumnType("TEXT"); + + b.Property("IsEvadedOffense") + .HasColumnType("INTEGER"); + + b.Property("LinkId") + .HasColumnType("INTEGER"); + + b.Property("OffenderId") + .HasColumnType("INTEGER"); + + b.Property("Offense") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PunisherId") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("When") + .HasColumnType("TEXT"); + + b.HasKey("PenaltyId"); + + b.HasIndex("LinkId"); + + b.HasIndex("OffenderId"); + + b.HasIndex("PunisherId"); + + b.ToTable("EFPenalties"); + }); + + modelBuilder.Entity("Data.Models.Server.EFServer", b => + { + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("EndPoint") + .HasColumnType("TEXT"); + + b.Property("GameName") + .HasColumnType("INTEGER"); + + b.Property("HostName") + .HasColumnType("TEXT"); + + b.Property("IsPasswordProtected") + .HasColumnType("INTEGER"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.HasKey("ServerId"); + + b.ToTable("EFServers"); + }); + + modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b => + { + b.Property("StatisticId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("TotalKills") + .HasColumnType("INTEGER"); + + b.Property("TotalPlayTime") + .HasColumnType("INTEGER"); + + b.HasKey("StatisticId"); + + b.HasIndex("ServerId"); + + b.ToTable("EFServerStatistics"); + }); + + modelBuilder.Entity("Data.Models.Vector3", b => + { + b.Property("Vector3Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("X") + .HasColumnType("REAL"); + + b.Property("Y") + .HasColumnType("REAL"); + + b.Property("Z") + .HasColumnType("REAL"); + + b.HasKey("Vector3Id"); + + b.ToTable("Vector3"); + }); + + modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b => + { + b.HasOne("Data.Models.Client.Stats.EFACSnapshot", "Snapshot") + .WithMany("PredictedViewAngles") + .HasForeignKey("SnapshotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "Vector") + .WithMany() + .HasForeignKey("Vector3Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.EFClient", b => + { + b.HasOne("Data.Models.EFAliasLink", "AliasLink") + .WithMany() + .HasForeignKey("AliasLinkId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.EFAlias", "CurrentAlias") + .WithMany() + .HasForeignKey("CurrentAliasId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientKill", b => + { + b.HasOne("Data.Models.Client.EFClient", "Attacker") + .WithMany() + .HasForeignKey("AttackerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "DeathOrigin") + .WithMany() + .HasForeignKey("DeathOriginVector3Id"); + + b.HasOne("Data.Models.Vector3", "KillOrigin") + .WithMany() + .HasForeignKey("KillOriginVector3Id"); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.EFClient", "Victim") + .WithMany() + .HasForeignKey("VictimId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "ViewAngles") + .WithMany() + .HasForeignKey("ViewAnglesVector3Id"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientMessage", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "CurrentViewAngle") + .WithMany() + .HasForeignKey("CurrentViewAngleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "HitDestination") + .WithMany() + .HasForeignKey("HitDestinationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "HitOrigin") + .WithMany() + .HasForeignKey("HitOriginId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "LastStrainAngle") + .WithMany() + .HasForeignKey("LastStrainAngleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.Reference.EFHitLocation", "HitLocation") + .WithMany() + .HasForeignKey("HitLocationId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFMeansOfDeath", "MeansOfDeath") + .WithMany() + .HasForeignKey("MeansOfDeathId"); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", "WeaponAttachmentCombo") + .WithMany() + .HasForeignKey("WeaponAttachmentComboId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeapon", "Weapon") + .WithMany() + .HasForeignKey("WeaponId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRankingHistory", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("EFClientStatisticsClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("EFClientStatisticsServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.EFClientStatistics", null) + .WithMany("HitLocations") + .HasForeignKey("EFClientStatisticsClientId", "EFClientStatisticsServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b => + { + b.HasOne("Data.Models.Client.Stats.EFClientRatingHistory", "RatingHistory") + .WithMany("Ratings") + .HasForeignKey("RatingHistoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b => + { + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment1") + .WithMany() + .HasForeignKey("Attachment1Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment2") + .WithMany() + .HasForeignKey("Attachment2Id"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment3") + .WithMany() + .HasForeignKey("Attachment3Id"); + }); + + modelBuilder.Entity("Data.Models.EFAlias", b => + { + b.HasOne("Data.Models.EFAliasLink", "Link") + .WithMany("Children") + .HasForeignKey("LinkId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.EFMeta", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany("Meta") + .HasForeignKey("ClientId"); + + b.HasOne("Data.Models.EFMeta", "LinkedMeta") + .WithMany() + .HasForeignKey("LinkedMetaId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("Data.Models.EFPenalty", b => + { + b.HasOne("Data.Models.EFAliasLink", "Link") + .WithMany("ReceivedPenalties") + .HasForeignKey("LinkId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.EFClient", "Offender") + .WithMany("ReceivedPenalties") + .HasForeignKey("OffenderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Data.Models.Client.EFClient", "Punisher") + .WithMany("AdministeredPenalties") + .HasForeignKey("PunisherId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b => + { + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Data/Migrations/Sqlite/20210310231004_AddRankingHistory.cs b/Data/Migrations/Sqlite/20210310231004_AddRankingHistory.cs new file mode 100644 index 000000000..2196f757d --- /dev/null +++ b/Data/Migrations/Sqlite/20210310231004_AddRankingHistory.cs @@ -0,0 +1,74 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Data.Migrations.Sqlite +{ + public partial class AddRankingHistory : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "EFClientRankingHistory", + columns: table => new + { + ClientRankingHistoryId = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + CreatedDateTime = table.Column(nullable: false), + UpdatedDateTime = table.Column(nullable: true), + ClientId = table.Column(nullable: false), + ServerId = table.Column(nullable: true), + Newest = table.Column(nullable: false), + Ranking = table.Column(nullable: true), + ZScore = table.Column(nullable: true), + PerformanceMetric = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EFClientRankingHistory", x => x.ClientRankingHistoryId); + table.ForeignKey( + name: "FK_EFClientRankingHistory_EFClients_ClientId", + column: x => x.ClientId, + principalTable: "EFClients", + principalColumn: "ClientId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_EFClientRankingHistory_EFServers_ServerId", + column: x => x.ServerId, + principalTable: "EFServers", + principalColumn: "ServerId", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_EFClientRankingHistory_ClientId", + table: "EFClientRankingHistory", + column: "ClientId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientRankingHistory_Ranking", + table: "EFClientRankingHistory", + column: "Ranking"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientRankingHistory_ServerId", + table: "EFClientRankingHistory", + column: "ServerId"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientRankingHistory_UpdatedDateTime", + table: "EFClientRankingHistory", + column: "UpdatedDateTime"); + + migrationBuilder.CreateIndex( + name: "IX_EFClientRankingHistory_ZScore", + table: "EFClientRankingHistory", + column: "ZScore"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "EFClientRankingHistory"); + } + } +} diff --git a/Data/Migrations/Sqlite/SqliteDatabaseContextModelSnapshot.cs b/Data/Migrations/Sqlite/SqliteDatabaseContextModelSnapshot.cs new file mode 100644 index 000000000..02302175a --- /dev/null +++ b/Data/Migrations/Sqlite/SqliteDatabaseContextModelSnapshot.cs @@ -0,0 +1,1277 @@ +// +using System; +using Data.MigrationContext; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Data.Migrations.Sqlite +{ + [DbContext(typeof(SqliteDatabaseContext))] + partial class SqliteDatabaseContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.10"); + + modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b => + { + b.Property("ACSnapshotVector3Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("SnapshotId") + .HasColumnType("INTEGER"); + + b.Property("Vector3Id") + .HasColumnType("INTEGER"); + + b.HasKey("ACSnapshotVector3Id"); + + b.HasIndex("SnapshotId"); + + b.HasIndex("Vector3Id"); + + b.ToTable("EFACSnapshotVector3"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClient", b => + { + b.Property("ClientId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("AliasLinkId") + .HasColumnType("INTEGER"); + + b.Property("Connections") + .HasColumnType("INTEGER"); + + b.Property("CurrentAliasId") + .HasColumnType("INTEGER"); + + b.Property("FirstConnection") + .HasColumnType("TEXT"); + + b.Property("LastConnection") + .HasColumnType("TEXT"); + + b.Property("Level") + .HasColumnType("INTEGER"); + + b.Property("Masked") + .HasColumnType("INTEGER"); + + b.Property("NetworkId") + .HasColumnType("INTEGER"); + + b.Property("Password") + .HasColumnType("TEXT"); + + b.Property("PasswordSalt") + .HasColumnType("TEXT"); + + b.Property("TotalConnectionTime") + .HasColumnType("INTEGER"); + + b.HasKey("ClientId"); + + b.HasIndex("AliasLinkId"); + + b.HasIndex("CurrentAliasId"); + + b.HasIndex("NetworkId") + .IsUnique(); + + b.ToTable("EFClients"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientKill", b => + { + b.Property("KillId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("AttackerId") + .HasColumnType("INTEGER"); + + b.Property("Damage") + .HasColumnType("INTEGER"); + + b.Property("DeathOriginVector3Id") + .HasColumnType("INTEGER"); + + b.Property("DeathType") + .HasColumnType("INTEGER"); + + b.Property("Fraction") + .HasColumnType("REAL"); + + b.Property("HitLoc") + .HasColumnType("INTEGER"); + + b.Property("IsKill") + .HasColumnType("INTEGER"); + + b.Property("KillOriginVector3Id") + .HasColumnType("INTEGER"); + + b.Property("Map") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("VictimId") + .HasColumnType("INTEGER"); + + b.Property("ViewAnglesVector3Id") + .HasColumnType("INTEGER"); + + b.Property("VisibilityPercentage") + .HasColumnType("REAL"); + + b.Property("Weapon") + .HasColumnType("INTEGER"); + + b.Property("When") + .HasColumnType("TEXT"); + + 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("Data.Models.Client.EFClientMessage", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.Property("Message") + .HasColumnType("TEXT"); + + b.Property("SentIngame") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("TimeSent") + .HasColumnType("TEXT"); + + b.HasKey("MessageId"); + + b.HasIndex("ClientId"); + + b.HasIndex("ServerId"); + + b.HasIndex("TimeSent"); + + b.ToTable("EFClientMessages"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b => + { + b.Property("SnapshotId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.Property("CurrentSessionLength") + .HasColumnType("INTEGER"); + + b.Property("CurrentStrain") + .HasColumnType("REAL"); + + b.Property("CurrentViewAngleId") + .HasColumnType("INTEGER"); + + b.Property("Deaths") + .HasColumnType("INTEGER"); + + b.Property("Distance") + .HasColumnType("REAL"); + + b.Property("EloRating") + .HasColumnType("REAL"); + + b.Property("HitDestinationId") + .HasColumnType("INTEGER"); + + b.Property("HitLocation") + .HasColumnType("INTEGER"); + + b.Property("HitOriginId") + .HasColumnType("INTEGER"); + + b.Property("HitType") + .HasColumnType("INTEGER"); + + b.Property("Hits") + .HasColumnType("INTEGER"); + + b.Property("Kills") + .HasColumnType("INTEGER"); + + b.Property("LastStrainAngleId") + .HasColumnType("INTEGER"); + + b.Property("RecoilOffset") + .HasColumnType("REAL"); + + b.Property("SessionAngleOffset") + .HasColumnType("REAL"); + + b.Property("SessionAverageSnapValue") + .HasColumnType("REAL"); + + b.Property("SessionSPM") + .HasColumnType("REAL"); + + b.Property("SessionScore") + .HasColumnType("INTEGER"); + + b.Property("SessionSnapHits") + .HasColumnType("INTEGER"); + + b.Property("StrainAngleBetween") + .HasColumnType("REAL"); + + b.Property("TimeSinceLastEvent") + .HasColumnType("INTEGER"); + + b.Property("WeaponId") + .HasColumnType("INTEGER"); + + b.Property("When") + .HasColumnType("TEXT"); + + b.HasKey("SnapshotId"); + + b.HasIndex("ClientId"); + + b.HasIndex("CurrentViewAngleId"); + + b.HasIndex("HitDestinationId"); + + b.HasIndex("HitOriginId"); + + b.HasIndex("LastStrainAngleId"); + + b.ToTable("EFACSnapshot"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b => + { + b.Property("ClientHitStatisticId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("DamageInflicted") + .HasColumnType("INTEGER"); + + b.Property("DamageReceived") + .HasColumnType("INTEGER"); + + b.Property("DeathCount") + .HasColumnType("INTEGER"); + + b.Property("HitCount") + .HasColumnType("INTEGER"); + + b.Property("HitLocationId") + .HasColumnType("INTEGER"); + + b.Property("KillCount") + .HasColumnType("INTEGER"); + + b.Property("MeansOfDeathId") + .HasColumnType("INTEGER"); + + b.Property("ReceivedHitCount") + .HasColumnType("INTEGER"); + + b.Property("Score") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("SuicideCount") + .HasColumnType("INTEGER"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.Property("UsageSeconds") + .HasColumnType("INTEGER"); + + b.Property("WeaponAttachmentComboId") + .HasColumnType("INTEGER"); + + b.Property("WeaponId") + .HasColumnType("INTEGER"); + + b.HasKey("ClientHitStatisticId"); + + b.HasIndex("ClientId"); + + b.HasIndex("HitLocationId"); + + b.HasIndex("MeansOfDeathId"); + + b.HasIndex("ServerId"); + + b.HasIndex("WeaponAttachmentComboId"); + + b.HasIndex("WeaponId"); + + b.ToTable("EFClientHitStatistics"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRankingHistory", b => + { + b.Property("ClientRankingHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Newest") + .HasColumnType("INTEGER"); + + b.Property("PerformanceMetric") + .HasColumnType("REAL"); + + b.Property("Ranking") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.Property("ZScore") + .HasColumnType("REAL"); + + b.HasKey("ClientRankingHistoryId"); + + b.HasIndex("ClientId"); + + b.HasIndex("Ranking"); + + b.HasIndex("ServerId"); + + b.HasIndex("UpdatedDateTime"); + + b.HasIndex("ZScore"); + + b.ToTable("EFClientRankingHistory"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b => + { + b.Property("RatingHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.HasKey("RatingHistoryId"); + + b.HasIndex("ClientId"); + + b.ToTable("EFClientRatingHistory"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b => + { + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("AverageSnapValue") + .HasColumnType("REAL"); + + b.Property("Deaths") + .HasColumnType("INTEGER"); + + b.Property("EloRating") + .HasColumnType("REAL"); + + b.Property("Kills") + .HasColumnType("INTEGER"); + + b.Property("MaxStrain") + .HasColumnType("REAL"); + + b.Property("RollingWeightedKDR") + .HasColumnType("REAL"); + + b.Property("SPM") + .HasColumnType("REAL"); + + b.Property("Skill") + .HasColumnType("REAL"); + + b.Property("SnapHitCount") + .HasColumnType("INTEGER"); + + b.Property("TimePlayed") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("ZScore") + .HasColumnType("REAL"); + + b.HasKey("ClientId", "ServerId"); + + b.HasIndex("ServerId"); + + b.HasIndex("ZScore"); + + b.HasIndex("ClientId", "TimePlayed", "ZScore"); + + b.ToTable("EFClientStatistics"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b => + { + b.Property("HitLocationCountId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("EFClientStatisticsClientId") + .HasColumnName("EFClientStatisticsClientId") + .HasColumnType("INTEGER"); + + b.Property("EFClientStatisticsServerId") + .HasColumnName("EFClientStatisticsServerId") + .HasColumnType("INTEGER"); + + b.Property("HitCount") + .HasColumnType("INTEGER"); + + b.Property("HitOffsetAverage") + .HasColumnType("REAL"); + + b.Property("Location") + .HasColumnType("INTEGER"); + + b.Property("MaxAngleDistance") + .HasColumnType("REAL"); + + b.HasKey("HitLocationCountId"); + + b.HasIndex("EFClientStatisticsServerId"); + + b.HasIndex("EFClientStatisticsClientId", "EFClientStatisticsServerId"); + + b.ToTable("EFHitLocationCounts"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b => + { + b.Property("RatingId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("ActivityAmount") + .HasColumnType("INTEGER"); + + b.Property("Newest") + .HasColumnType("INTEGER"); + + b.Property("Performance") + .HasColumnType("REAL"); + + b.Property("Ranking") + .HasColumnType("INTEGER"); + + b.Property("RatingHistoryId") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("When") + .HasColumnType("TEXT"); + + b.HasKey("RatingId"); + + b.HasIndex("RatingHistoryId"); + + b.HasIndex("ServerId"); + + b.HasIndex("Performance", "Ranking", "When"); + + b.HasIndex("When", "ServerId", "Performance", "ActivityAmount"); + + b.ToTable("EFRating"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFHitLocation", b => + { + b.Property("HitLocationId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("HitLocationId"); + + b.HasIndex("Name"); + + b.ToTable("EFHitLocations"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMap", b => + { + b.Property("MapId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("MapId"); + + b.ToTable("EFMaps"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMeansOfDeath", b => + { + b.Property("MeansOfDeathId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("MeansOfDeathId"); + + b.ToTable("EFMeansOfDeath"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeapon", b => + { + b.Property("WeaponId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("WeaponId"); + + b.HasIndex("Name"); + + b.ToTable("EFWeapons"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachment", b => + { + b.Property("WeaponAttachmentId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("WeaponAttachmentId"); + + b.ToTable("EFWeaponAttachments"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b => + { + b.Property("WeaponAttachmentComboId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attachment1Id") + .HasColumnType("INTEGER"); + + b.Property("Attachment2Id") + .HasColumnType("INTEGER"); + + b.Property("Attachment3Id") + .HasColumnType("INTEGER"); + + b.Property("CreatedDateTime") + .HasColumnType("TEXT"); + + b.Property("Game") + .HasColumnType("INTEGER"); + + b.Property("UpdatedDateTime") + .HasColumnType("TEXT"); + + b.HasKey("WeaponAttachmentComboId"); + + b.HasIndex("Attachment1Id"); + + b.HasIndex("Attachment2Id"); + + b.HasIndex("Attachment3Id"); + + b.ToTable("EFWeaponAttachmentCombos"); + }); + + modelBuilder.Entity("Data.Models.EFAlias", b => + { + b.Property("AliasId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("IPAddress") + .HasColumnType("INTEGER"); + + b.Property("LinkId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(24); + + b.Property("SearchableName") + .HasColumnType("TEXT") + .HasMaxLength(24); + + b.HasKey("AliasId"); + + b.HasIndex("IPAddress"); + + b.HasIndex("LinkId"); + + b.HasIndex("Name"); + + b.HasIndex("SearchableName"); + + b.HasIndex("Name", "IPAddress") + .IsUnique(); + + b.ToTable("EFAlias"); + }); + + modelBuilder.Entity("Data.Models.EFAliasLink", b => + { + b.Property("AliasLinkId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.HasKey("AliasLinkId"); + + b.ToTable("EFAliasLinks"); + }); + + modelBuilder.Entity("Data.Models.EFChangeHistory", b => + { + b.Property("ChangeHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("Comment") + .HasColumnType("TEXT") + .HasMaxLength(128); + + b.Property("CurrentValue") + .HasColumnType("TEXT"); + + b.Property("ImpersonationEntityId") + .HasColumnType("INTEGER"); + + b.Property("OriginEntityId") + .HasColumnType("INTEGER"); + + b.Property("PreviousValue") + .HasColumnType("TEXT"); + + b.Property("TargetEntityId") + .HasColumnType("INTEGER"); + + b.Property("TimeChanged") + .HasColumnType("TEXT"); + + b.Property("TypeOfChange") + .HasColumnType("INTEGER"); + + b.HasKey("ChangeHistoryId"); + + b.ToTable("EFChangeHistory"); + }); + + modelBuilder.Entity("Data.Models.EFMeta", b => + { + b.Property("MetaId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.Property("Created") + .HasColumnType("TEXT"); + + b.Property("Extra") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(32); + + b.Property("LinkedMetaId") + .HasColumnType("INTEGER"); + + b.Property("Updated") + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("MetaId"); + + b.HasIndex("ClientId"); + + b.HasIndex("Key"); + + b.HasIndex("LinkedMetaId"); + + b.ToTable("EFMeta"); + }); + + modelBuilder.Entity("Data.Models.EFPenalty", b => + { + b.Property("PenaltyId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("AutomatedOffense") + .HasColumnType("TEXT"); + + b.Property("Expires") + .HasColumnType("TEXT"); + + b.Property("IsEvadedOffense") + .HasColumnType("INTEGER"); + + b.Property("LinkId") + .HasColumnType("INTEGER"); + + b.Property("OffenderId") + .HasColumnType("INTEGER"); + + b.Property("Offense") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PunisherId") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("When") + .HasColumnType("TEXT"); + + b.HasKey("PenaltyId"); + + b.HasIndex("LinkId"); + + b.HasIndex("OffenderId"); + + b.HasIndex("PunisherId"); + + b.ToTable("EFPenalties"); + }); + + modelBuilder.Entity("Data.Models.Server.EFServer", b => + { + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("EndPoint") + .HasColumnType("TEXT"); + + b.Property("GameName") + .HasColumnType("INTEGER"); + + b.Property("HostName") + .HasColumnType("TEXT"); + + b.Property("IsPasswordProtected") + .HasColumnType("INTEGER"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.HasKey("ServerId"); + + b.ToTable("EFServers"); + }); + + modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b => + { + b.Property("StatisticId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("TotalKills") + .HasColumnType("INTEGER"); + + b.Property("TotalPlayTime") + .HasColumnType("INTEGER"); + + b.HasKey("StatisticId"); + + b.HasIndex("ServerId"); + + b.ToTable("EFServerStatistics"); + }); + + modelBuilder.Entity("Data.Models.Vector3", b => + { + b.Property("Vector3Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("X") + .HasColumnType("REAL"); + + b.Property("Y") + .HasColumnType("REAL"); + + b.Property("Z") + .HasColumnType("REAL"); + + b.HasKey("Vector3Id"); + + b.ToTable("Vector3"); + }); + + modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b => + { + b.HasOne("Data.Models.Client.Stats.EFACSnapshot", "Snapshot") + .WithMany("PredictedViewAngles") + .HasForeignKey("SnapshotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "Vector") + .WithMany() + .HasForeignKey("Vector3Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.EFClient", b => + { + b.HasOne("Data.Models.EFAliasLink", "AliasLink") + .WithMany() + .HasForeignKey("AliasLinkId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.EFAlias", "CurrentAlias") + .WithMany() + .HasForeignKey("CurrentAliasId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientKill", b => + { + b.HasOne("Data.Models.Client.EFClient", "Attacker") + .WithMany() + .HasForeignKey("AttackerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "DeathOrigin") + .WithMany() + .HasForeignKey("DeathOriginVector3Id"); + + b.HasOne("Data.Models.Vector3", "KillOrigin") + .WithMany() + .HasForeignKey("KillOriginVector3Id"); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.EFClient", "Victim") + .WithMany() + .HasForeignKey("VictimId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "ViewAngles") + .WithMany() + .HasForeignKey("ViewAnglesVector3Id"); + }); + + modelBuilder.Entity("Data.Models.Client.EFClientMessage", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "CurrentViewAngle") + .WithMany() + .HasForeignKey("CurrentViewAngleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "HitDestination") + .WithMany() + .HasForeignKey("HitDestinationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "HitOrigin") + .WithMany() + .HasForeignKey("HitOriginId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Vector3", "LastStrainAngle") + .WithMany() + .HasForeignKey("LastStrainAngleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.Reference.EFHitLocation", "HitLocation") + .WithMany() + .HasForeignKey("HitLocationId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFMeansOfDeath", "MeansOfDeath") + .WithMany() + .HasForeignKey("MeansOfDeathId"); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", "WeaponAttachmentCombo") + .WithMany() + .HasForeignKey("WeaponAttachmentComboId"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeapon", "Weapon") + .WithMany() + .HasForeignKey("WeaponId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRankingHistory", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany() + .HasForeignKey("EFClientStatisticsClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("EFClientStatisticsServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.EFClientStatistics", null) + .WithMany("HitLocations") + .HasForeignKey("EFClientStatisticsClientId", "EFClientStatisticsServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b => + { + b.HasOne("Data.Models.Client.Stats.EFClientRatingHistory", "RatingHistory") + .WithMany("Ratings") + .HasForeignKey("RatingHistoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b => + { + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment1") + .WithMany() + .HasForeignKey("Attachment1Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment2") + .WithMany() + .HasForeignKey("Attachment2Id"); + + b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment3") + .WithMany() + .HasForeignKey("Attachment3Id"); + }); + + modelBuilder.Entity("Data.Models.EFAlias", b => + { + b.HasOne("Data.Models.EFAliasLink", "Link") + .WithMany("Children") + .HasForeignKey("LinkId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.EFMeta", b => + { + b.HasOne("Data.Models.Client.EFClient", "Client") + .WithMany("Meta") + .HasForeignKey("ClientId"); + + b.HasOne("Data.Models.EFMeta", "LinkedMeta") + .WithMany() + .HasForeignKey("LinkedMetaId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("Data.Models.EFPenalty", b => + { + b.HasOne("Data.Models.EFAliasLink", "Link") + .WithMany("ReceivedPenalties") + .HasForeignKey("LinkId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Data.Models.Client.EFClient", "Offender") + .WithMany("ReceivedPenalties") + .HasForeignKey("OffenderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Data.Models.Client.EFClient", "Punisher") + .WithMany("AdministeredPenalties") + .HasForeignKey("PunisherId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b => + { + b.HasOne("Data.Models.Server.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Data/Models/AuditFields.cs b/Data/Models/AuditFields.cs new file mode 100644 index 000000000..43dcfecfb --- /dev/null +++ b/Data/Models/AuditFields.cs @@ -0,0 +1,12 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Stats.Models +{ + public class AuditFields + { + [Required] + public DateTime CreatedDateTime { get; set; } = DateTime.UtcNow; + public DateTime? UpdatedDateTime { get; set; } + } +} \ No newline at end of file diff --git a/Plugins/Stats/Models/EFACSnapshotVector3.cs b/Data/Models/Client/EFACSnapshotVector3.cs similarity index 73% rename from Plugins/Stats/Models/EFACSnapshotVector3.cs rename to Data/Models/Client/EFACSnapshotVector3.cs index 1401f5aa9..8751c3ebf 100644 --- a/Plugins/Stats/Models/EFACSnapshotVector3.cs +++ b/Data/Models/Client/EFACSnapshotVector3.cs @@ -1,9 +1,9 @@ -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Helpers; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using System.Numerics; +using Data.Models.Client.Stats; -namespace IW4MAdmin.Plugins.Stats.Models +namespace Data.Models.Client { public class EFACSnapshotVector3 : SharedEntity { diff --git a/SharedLibraryCore/Database/Models/EFClient.cs b/Data/Models/Client/EFClient.cs similarity index 50% rename from SharedLibraryCore/Database/Models/EFClient.cs rename to Data/Models/Client/EFClient.cs index 0117b84f6..89703050f 100644 --- a/SharedLibraryCore/Database/Models/EFClient.cs +++ b/Data/Models/Client/EFClient.cs @@ -3,10 +3,54 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace SharedLibraryCore.Database.Models +namespace Data.Models.Client { - public partial class EFClient : SharedEntity + public class EFClient : SharedEntity { + public enum Permission + { + /// + /// client has been banned + /// + Banned = -1, + /// + /// default client state upon first connect + /// + User = 0, + /// + /// client has been flagged + /// + Flagged = 1, + /// + /// client is trusted + /// + Trusted = 2, + /// + /// client is a moderator + /// + Moderator = 3, + /// + /// client is an administrator + /// + Administrator = 4, + /// + /// client is a senior administrator + /// + SeniorAdmin = 5, + /// + /// client is a owner + /// + Owner = 6, + /// + /// not used + /// + Creator = 7, + /// + /// reserved for default account + /// + Console = 8 + } + [Key] public int ClientId { get; set; } public long NetworkId { get; set; } diff --git a/Plugins/Stats/Models/EFClientKill.cs b/Data/Models/Client/EFClientKill.cs similarity index 51% rename from Plugins/Stats/Models/EFClientKill.cs rename to Data/Models/Client/EFClientKill.cs index 29f6dcc65..1c84f5c31 100644 --- a/Plugins/Stats/Models/EFClientKill.cs +++ b/Data/Models/Client/EFClientKill.cs @@ -1,52 +1,41 @@ using System; - -using SharedLibraryCore.Database.Models; -using System.ComponentModel.DataAnnotations.Schema; -using SharedLibraryCore.Helpers; -using System.ComponentModel.DataAnnotations; using System.Collections.Generic; -using static SharedLibraryCore.Server; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Data.Models.Server; -namespace IW4MAdmin.Plugins.Stats.Models +namespace Data.Models.Client { public class EFClientKill : SharedEntity { - [Key] - public long KillId { get; set; } + [Key] public long KillId { get; set; } public int VictimId { get; set; } - [ForeignKey("VictimId")] - public virtual EFClient Victim { get; set; } + [ForeignKey("VictimId")] public virtual EFClient Victim { get; set; } public int AttackerId { get; set; } - [ForeignKey("AttackerId")] - public virtual EFClient Attacker { get; set; } + [ForeignKey("AttackerId")] public virtual EFClient Attacker { get; set; } public long ServerId { get; set; } - [ForeignKey("ServerId")] - public virtual EFServer Server { get; set; } - public IW4Info.HitLocation HitLoc { get; set; } - public IW4Info.MeansOfDeath DeathType { get; set; } + [ForeignKey("ServerId")] public virtual EFServer Server { get; set; } + public int HitLoc { get; set; } + public int DeathType { get; set; } public int Damage { get; set; } - public IW4Info.WeaponName Weapon { get; set; } + public int Weapon { get; set; } public Vector3 KillOrigin { get; set; } public Vector3 DeathOrigin { get; set; } public Vector3 ViewAngles { get; set; } public DateTime When { get; set; } public double Fraction { get; set; } public bool IsKill { get; set; } + public double VisibilityPercentage { get; set; } + // http://wiki.modsrepository.com/index.php?title=Call_of_Duty_5:_Gameplay_standards for conversion to meters - [NotMapped] - public double Distance => Vector3.Distance(KillOrigin, DeathOrigin) * 0.0254; - public IW4Info.MapName Map { get; set; } - [NotMapped] - public long TimeOffset { get; set; } - [NotMapped] - public bool IsKillstreakKill { get; set; } - [NotMapped] - public float AdsPercent { get; set; } - [NotMapped] - public List AnglesList { get; set; } - [NotMapped] - public Game GameName { get; set; } + [NotMapped] public double Distance => Vector3.Distance(KillOrigin, DeathOrigin) * 0.0254; + public int Map { get; set; } + [NotMapped] public long TimeOffset { get; set; } + [NotMapped] public bool IsKillstreakKill { get; set; } + [NotMapped] public float AdsPercent { get; set; } + [NotMapped] public List AnglesList { get; set; } + [NotMapped] public int GameName { get; set; } /// /// Indicates if the attacker was alive after last captured angle @@ -60,4 +49,4 @@ namespace IW4MAdmin.Plugins.Stats.Models [NotMapped] public long TimeSinceLastAttack { get; set; } } -} +} \ No newline at end of file diff --git a/Plugins/Stats/Models/EFClientMessage.cs b/Data/Models/Client/EFClientMessage.cs similarity index 86% rename from Plugins/Stats/Models/EFClientMessage.cs rename to Data/Models/Client/EFClientMessage.cs index 49c2a9316..d491e4775 100644 --- a/Plugins/Stats/Models/EFClientMessage.cs +++ b/Data/Models/Client/EFClientMessage.cs @@ -1,9 +1,9 @@ -using SharedLibraryCore.Database.Models; -using System; +using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using Data.Models.Server; -namespace IW4MAdmin.Plugins.Stats.Models +namespace Data.Models.Client { public class EFClientMessage : SharedEntity { diff --git a/Plugins/Stats/Models/EFACSnapshot.cs b/Data/Models/Client/Stats/EFACSnapshot.cs similarity index 87% rename from Plugins/Stats/Models/EFACSnapshot.cs rename to Data/Models/Client/Stats/EFACSnapshot.cs index 7c2abdace..f59f87205 100644 --- a/Plugins/Stats/Models/EFACSnapshot.cs +++ b/Data/Models/Client/Stats/EFACSnapshot.cs @@ -1,12 +1,11 @@ -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Helpers; -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; +using System.Numerics; -namespace IW4MAdmin.Plugins.Stats.Models +namespace Data.Models.Client.Stats { /// /// This class houses the information for anticheat snapshots (used for validating a ban) @@ -47,9 +46,9 @@ namespace IW4MAdmin.Plugins.Stats.Models public int CurrentViewAngleId { get; set; } [ForeignKey("CurrentViewAngleId")] public Vector3 CurrentViewAngle { get; set; } - public IW4Info.WeaponName WeaponId { get; set; } - public IW4Info.HitLocation HitLocation { get; set; } - public IW4Info.MeansOfDeath HitType { get; set; } + public int WeaponId { get; set; } + public int HitLocation { get; set; } + public int HitType { get; set; } public virtual ICollection PredictedViewAngles { get; set; } [NotMapped] diff --git a/Data/Models/Client/Stats/EFClientHitStatistic.cs b/Data/Models/Client/Stats/EFClientHitStatistic.cs new file mode 100644 index 000000000..ad25716b1 --- /dev/null +++ b/Data/Models/Client/Stats/EFClientHitStatistic.cs @@ -0,0 +1,91 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Data.Models.Client.Stats.Reference; +using Data.Models.Server; +using Stats.Models; + +namespace Data.Models.Client.Stats +{ + public class EFClientHitStatistic : AuditFields + { + [Key] + public int ClientHitStatisticId { get; set; } + + [Required] + public int ClientId { get; set; } + + [ForeignKey(nameof(ClientId))] + public virtual EFClient Client { get; set; } + + public long? ServerId { get; set; } + + [ForeignKey(nameof(ServerId))] + public virtual EFServer Server { get; set; } + + public int? HitLocationId { get; set; } + + [ForeignKey(nameof(HitLocationId))] + public virtual EFHitLocation HitLocation { get; set; } + + public int? MeansOfDeathId { get; set; } + + [ForeignKey(nameof(MeansOfDeathId))] + public virtual EFMeansOfDeath MeansOfDeath { get; set; } + + public int? WeaponId { get; set; } + + [ForeignKey(nameof(WeaponId))] + public virtual EFWeapon Weapon { get; set; } + + public int? WeaponAttachmentComboId { get; set; } + + [ForeignKey(nameof(WeaponAttachmentComboId))] + public virtual EFWeaponAttachmentCombo WeaponAttachmentCombo { get; set; } + + /// + /// how many hits the player got + /// + public int HitCount { get; set; } + + /// + /// how many kills the player got + /// + public int KillCount { get; set; } + + /// + /// how much damage the player inflicted + /// + public int DamageInflicted { get; set; } + + /// + /// how many hits the player received + /// + public int ReceivedHitCount { get; set; } + + /// + /// how many kills the player received + /// + public int DeathCount { get; set; } + + /// + /// how much damage the player received + /// + public int DamageReceived { get; set; } + + /// + /// how many times the player killed themself + /// + public int SuicideCount { get; set; } + + /// + /// estimation of time spent with the configuration + /// + public int? UsageSeconds { get; set; } + + /// + /// total in-game score + /// + public int? Score { get; set; } + } +} diff --git a/Data/Models/Client/Stats/EFClientRankingHistory.cs b/Data/Models/Client/Stats/EFClientRankingHistory.cs new file mode 100644 index 000000000..1991a5b31 --- /dev/null +++ b/Data/Models/Client/Stats/EFClientRankingHistory.cs @@ -0,0 +1,31 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Data.Models.Server; +using Stats.Models; + +namespace Data.Models.Client.Stats +{ + public class EFClientRankingHistory: AuditFields + { + public const int MaxRankingCount = 30; + + [Key] + public long ClientRankingHistoryId { get; set; } + + [Required] + public int ClientId { get; set; } + + [ForeignKey(nameof(ClientId))] + public virtual EFClient Client { get; set; } + + public long? ServerId { get; set; } + + [ForeignKey(nameof(ServerId))] + public virtual EFServer Server { get; set; } + + public bool Newest { get; set; } + public int? Ranking { get; set; } + public double? ZScore { get; set; } + public double? PerformanceMetric { get; set; } + } +} \ No newline at end of file diff --git a/Plugins/Stats/Models/EFClientRatingHistory.cs b/Data/Models/Client/Stats/EFClientRatingHistory.cs similarity index 69% rename from Plugins/Stats/Models/EFClientRatingHistory.cs rename to Data/Models/Client/Stats/EFClientRatingHistory.cs index 51d04d0e0..e5e37c6eb 100644 --- a/Plugins/Stats/Models/EFClientRatingHistory.cs +++ b/Data/Models/Client/Stats/EFClientRatingHistory.cs @@ -1,12 +1,8 @@ -using IW4MAdmin.Plugins.Stats.Models; -using SharedLibraryCore.Database.Models; -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Text; -namespace IW4MAdmin.Plugins.Stats.Models +namespace Data.Models.Client.Stats { public class EFClientRatingHistory : SharedEntity { diff --git a/Plugins/Stats/Models/EFClientStatistics.cs b/Data/Models/Client/Stats/EFClientStatistics.cs similarity index 90% rename from Plugins/Stats/Models/EFClientStatistics.cs rename to Data/Models/Client/Stats/EFClientStatistics.cs index 83f1524b4..ed6d01c18 100644 --- a/Plugins/Stats/Models/EFClientStatistics.cs +++ b/Data/Models/Client/Stats/EFClientStatistics.cs @@ -4,9 +4,9 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Threading; -using SharedLibraryCore.Database.Models; +using Data.Models.Server; -namespace IW4MAdmin.Plugins.Stats.Models +namespace Data.Models.Client.Stats { public class EFClientStatistics : SharedEntity { @@ -31,16 +31,16 @@ namespace IW4MAdmin.Plugins.Stats.Models [Required] public int Deaths { get; set; } public double EloRating { get; set; } + public double ZScore { get; set; } + public DateTime? UpdatedAt { get; set; } public virtual ICollection HitLocations { get; set; } public double RollingWeightedKDR { get; set; } - public double VisionAverage { get; set; } - public double AverageRecoilOffset { get; set; } public double AverageSnapValue { get; set; } public int SnapHitCount { get; set; } [NotMapped] public double Performance { - get => Math.Round((EloRating + Skill) / 2.0, 2); + get => Math.Round(EloRating * 1/3.0 + Skill * 2/3.0, 2); } [NotMapped] public double KDR @@ -83,7 +83,7 @@ namespace IW4MAdmin.Plugins.Stats.Models DeathStreak = 0; LastScore = 0; SessionScores.Add(0); - Team = IW4Info.Team.None; + Team = 0; } [NotMapped] public int SessionScore @@ -103,7 +103,7 @@ namespace IW4MAdmin.Plugins.Stats.Models [NotMapped] private readonly List SessionScores = new List() { 0 }; [NotMapped] - public IW4Info.Team Team { get; set; } + public int Team { get; set; } [NotMapped] public DateTime LastStatHistoryUpdate { get; set; } = DateTime.UtcNow; [NotMapped] diff --git a/Plugins/Stats/Models/EFHitLocationCount.cs b/Data/Models/Client/Stats/EFHitLocationCount.cs similarity index 81% rename from Plugins/Stats/Models/EFHitLocationCount.cs rename to Data/Models/Client/Stats/EFHitLocationCount.cs index 726dc9b40..cc8bd042f 100644 --- a/Plugins/Stats/Models/EFHitLocationCount.cs +++ b/Data/Models/Client/Stats/EFHitLocationCount.cs @@ -1,15 +1,15 @@ -using SharedLibraryCore.Database.Models; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using Data.Models.Server; -namespace IW4MAdmin.Plugins.Stats.Models +namespace Data.Models.Client.Stats { public class EFHitLocationCount : SharedEntity { [Key] public int HitLocationCountId { get; set; } [Required] - public IW4Info.HitLocation Location { get; set; } + public int Location { get; set; } [Required] public int HitCount { get; set; } [Required] diff --git a/Plugins/Stats/Models/EFRating.cs b/Data/Models/Client/Stats/EFRating.cs similarity index 91% rename from Plugins/Stats/Models/EFRating.cs rename to Data/Models/Client/Stats/EFRating.cs index 9aae5f4ed..2e50b08c0 100644 --- a/Plugins/Stats/Models/EFRating.cs +++ b/Data/Models/Client/Stats/EFRating.cs @@ -1,9 +1,9 @@ -using SharedLibraryCore.Database.Models; -using System; +using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using Data.Models.Server; -namespace IW4MAdmin.Plugins.Stats.Models +namespace Data.Models.Client.Stats { public class EFRating : SharedEntity { diff --git a/Data/Models/Client/Stats/Reference/EFHitLocation.cs b/Data/Models/Client/Stats/Reference/EFHitLocation.cs new file mode 100644 index 000000000..dde2dcf81 --- /dev/null +++ b/Data/Models/Client/Stats/Reference/EFHitLocation.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using Data.Abstractions; +using Stats.Models; + +namespace Data.Models.Client.Stats.Reference +{ + public class EFHitLocation : AuditFields, IUniqueId + { + [Key] + public int HitLocationId { get; set; } + + [Required] + public string Name { get; set; } + + [Required] + public Models.Reference.Game Game { get; set; } + + public long Id => HitLocationId; + public string Value => Name; + } +} \ No newline at end of file diff --git a/Data/Models/Client/Stats/Reference/EFMap.cs b/Data/Models/Client/Stats/Reference/EFMap.cs new file mode 100644 index 000000000..2f765b7ab --- /dev/null +++ b/Data/Models/Client/Stats/Reference/EFMap.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using Data.Abstractions; +using Stats.Models; + +namespace Data.Models.Client.Stats.Reference +{ + public class EFMap : AuditFields, IUniqueId + { + [Key] + public int MapId { get; set; } + + [Required] + public string Name { get; set; } + + [Required] + public Models.Reference.Game Game { get; set; } + + public long Id => MapId; + public string Value => Name; + } +} \ No newline at end of file diff --git a/Data/Models/Client/Stats/Reference/EFMeansOfDeath.cs b/Data/Models/Client/Stats/Reference/EFMeansOfDeath.cs new file mode 100644 index 000000000..ba3c0d552 --- /dev/null +++ b/Data/Models/Client/Stats/Reference/EFMeansOfDeath.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using Data.Abstractions; +using Stats.Models; + +namespace Data.Models.Client.Stats.Reference +{ + public class EFMeansOfDeath: AuditFields, IUniqueId + { + [Key] + public int MeansOfDeathId { get; set; } + + [Required] + public string Name { get; set; } + + [Required] + public Models.Reference.Game Game { get; set; } + + public long Id => MeansOfDeathId; + public string Value => Name; + } +} \ No newline at end of file diff --git a/Data/Models/Client/Stats/Reference/EFWeapon.cs b/Data/Models/Client/Stats/Reference/EFWeapon.cs new file mode 100644 index 000000000..54b674431 --- /dev/null +++ b/Data/Models/Client/Stats/Reference/EFWeapon.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using Data.Abstractions; +using Stats.Models; + +namespace Data.Models.Client.Stats.Reference +{ + public class EFWeapon : AuditFields, IUniqueId + { + [Key] + public int WeaponId { get; set; } + + [Required] + public string Name { get; set; } + + [Required] + public Models.Reference.Game Game { get; set; } + + public long Id => WeaponId; + public string Value => Name; + } +} \ No newline at end of file diff --git a/Data/Models/Client/Stats/Reference/EFWeaponAttachment.cs b/Data/Models/Client/Stats/Reference/EFWeaponAttachment.cs new file mode 100644 index 000000000..0d92ea52a --- /dev/null +++ b/Data/Models/Client/Stats/Reference/EFWeaponAttachment.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using Data.Abstractions; +using Stats.Models; + +namespace Data.Models.Client.Stats.Reference +{ + public class EFWeaponAttachment : AuditFields, IUniqueId + { + [Key] + public int WeaponAttachmentId { get; set; } + + [Required] + public string Name { get; set; } + + [Required] + public Models.Reference.Game Game { get; set; } + + public long Id => WeaponAttachmentId; + public string Value => Name; + } +} diff --git a/Data/Models/Client/Stats/Reference/EFWeaponAttachmentCombo.cs b/Data/Models/Client/Stats/Reference/EFWeaponAttachmentCombo.cs new file mode 100644 index 000000000..1560bf94c --- /dev/null +++ b/Data/Models/Client/Stats/Reference/EFWeaponAttachmentCombo.cs @@ -0,0 +1,35 @@ +using Data.Abstractions; +using Stats.Models; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Data.Models.Client.Stats.Reference +{ + public class EFWeaponAttachmentCombo : AuditFields, IUniqueId + { + [Key] + public int WeaponAttachmentComboId { get; set; } + + [Required] + public Models.Reference.Game Game { get; set; } + + [Required] + public int Attachment1Id { get; set; } + + [ForeignKey(nameof(Attachment1Id))] + public virtual EFWeaponAttachment Attachment1 { get; set; } + + public int? Attachment2Id { get; set; } + + [ForeignKey(nameof(Attachment2Id))] + public virtual EFWeaponAttachment Attachment2 { get; set; } + + public int? Attachment3Id { get; set; } + + [ForeignKey(nameof(Attachment3Id))] + public virtual EFWeaponAttachment Attachment3 { get; set; } + + public long Id => WeaponAttachmentComboId; + public string Value => $"{Attachment1Id}{Attachment2Id}{Attachment3Id}"; + } +} diff --git a/Data/Models/Configuration/StatsModelConfiguration.cs b/Data/Models/Configuration/StatsModelConfiguration.cs new file mode 100644 index 000000000..ad172a2e2 --- /dev/null +++ b/Data/Models/Configuration/StatsModelConfiguration.cs @@ -0,0 +1,92 @@ +using Data.Models.Client; +using Data.Models.Client.Stats; +using Data.Models.Client.Stats.Reference; +using Data.Models.Server; +using Microsoft.EntityFrameworkCore; + +namespace Data.Models.Configuration +{ + public class StatsModelConfiguration + { + public static void Configure(ModelBuilder builder) + { + builder.Entity(entity => + { + entity.HasKey(cs => new {cs.ClientId, cs.ServerId}); + entity.HasIndex(cs => new {cs.ClientId, cs.TimePlayed, PerformancePercentile = cs.ZScore}); + entity.HasIndex(cs => new {PerformancePercentile = cs.ZScore}); + entity.ToTable("EFClientStatistics"); + }); + + + // fix linking from SQLCe + builder.Entity(entity => + { + entity.Property(c => c.EFClientStatisticsClientId) + .HasColumnName("EFClientStatisticsClientId"); + entity.Property(c => c.EFClientStatisticsServerId) + .HasColumnName("EFClientStatisticsServerId"); + + entity.ToTable("EFHitLocationCounts"); + }); + + + builder.Entity(entity => + { + entity.HasIndex(p => new {p.Performance, p.Ranking, p.When}); + entity.HasIndex(p => new {p.When, p.ServerId, p.Performance, p.ActivityAmount}); + entity.ToTable(nameof(EFRating)); + }); + + + builder.Entity(entity => + { + entity.HasIndex(p => p.TimeSent); + entity.ToTable("EFClientMessages"); + }); + + builder.Entity(entity => { entity.ToTable(nameof(EFClientStatistics)); }); + + builder.Entity(entity => { entity.ToTable(nameof(EFRating)); }); + + builder.Entity(entity => { entity.ToTable(nameof(EFClientRatingHistory)); }); + + builder.Entity(entity => { entity.ToTable("EFHitLocationCounts"); }); + + builder.Entity(entity => { entity.ToTable("EFServerStatistics"); }); + + builder.Entity(entity => { entity.ToTable("EFServers"); }); + + builder.Entity(entity => { entity.ToTable("EFClientKills"); }); + + builder.Entity().ToTable(nameof(Vector3)); + builder.Entity().ToTable(nameof(EFACSnapshot)); + builder.Entity().ToTable(nameof(EFACSnapshotVector3)); + + builder.Entity(entity => + { + entity.HasIndex(loc => loc.Name); + entity.ToTable("EFHitLocations"); + }); + + builder.Entity(entity => + { + entity.HasIndex(weapon => weapon.Name); + entity.ToTable("EFWeapons"); + }); + + builder.Entity(entity => { entity.ToTable("EFMaps"); }); + builder.Entity(entity => { entity.ToTable("EFClientHitStatistics"); }); + builder.Entity(entity => { entity.ToTable("EFWeaponAttachments"); }); + builder.Entity(entity => { entity.ToTable("EFWeaponAttachmentCombos"); }); + builder.Entity(entity => { entity.ToTable("EFMeansOfDeath"); }); + builder.Entity(entity => + { + entity.ToTable(nameof(EFClientRankingHistory)); + entity.HasIndex(ranking => ranking.Ranking); + entity.HasIndex(ranking => ranking.ZScore); + entity.HasIndex(ranking => ranking.UpdatedDateTime); + }); + } + } +} \ No newline at end of file diff --git a/SharedLibraryCore/Database/Models/EFAlias.cs b/Data/Models/EFAlias.cs similarity index 94% rename from SharedLibraryCore/Database/Models/EFAlias.cs rename to Data/Models/EFAlias.cs index b8eee0eb6..7f9bbd358 100644 --- a/SharedLibraryCore/Database/Models/EFAlias.cs +++ b/Data/Models/EFAlias.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace SharedLibraryCore.Database.Models +namespace Data.Models { public partial class EFAlias : SharedEntity { diff --git a/SharedLibraryCore/Database/Models/EFAliasLink.cs b/Data/Models/EFAliasLink.cs similarity index 81% rename from SharedLibraryCore/Database/Models/EFAliasLink.cs rename to Data/Models/EFAliasLink.cs index b6b08eb17..3cb4c559d 100644 --- a/SharedLibraryCore/Database/Models/EFAliasLink.cs +++ b/Data/Models/EFAliasLink.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -namespace SharedLibraryCore.Database.Models +namespace Data.Models { public class EFAliasLink : SharedEntity { diff --git a/SharedLibraryCore/Database/Models/EFChangeHistory.cs b/Data/Models/EFChangeHistory.cs similarity index 95% rename from SharedLibraryCore/Database/Models/EFChangeHistory.cs rename to Data/Models/EFChangeHistory.cs index fae53ab0f..733b01fce 100644 --- a/SharedLibraryCore/Database/Models/EFChangeHistory.cs +++ b/Data/Models/EFChangeHistory.cs @@ -1,7 +1,7 @@ using System; using System.ComponentModel.DataAnnotations; -namespace SharedLibraryCore.Database.Models +namespace Data.Models { /// /// This class models the change to different entities diff --git a/SharedLibraryCore/Database/Models/EFMeta.cs b/Data/Models/EFMeta.cs similarity index 94% rename from SharedLibraryCore/Database/Models/EFMeta.cs rename to Data/Models/EFMeta.cs index 792797ee4..2d2f81ac2 100644 --- a/SharedLibraryCore/Database/Models/EFMeta.cs +++ b/Data/Models/EFMeta.cs @@ -1,8 +1,9 @@ -using System; +using Data.Models.Client; +using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace SharedLibraryCore.Database.Models +namespace Data.Models { /// /// This class encapsulates any meta fields as a simple string diff --git a/SharedLibraryCore/Database/Models/EFPenalty.cs b/Data/Models/EFPenalty.cs similarity index 73% rename from SharedLibraryCore/Database/Models/EFPenalty.cs rename to Data/Models/EFPenalty.cs index 5aa370cec..2102ec0f7 100644 --- a/SharedLibraryCore/Database/Models/EFPenalty.cs +++ b/Data/Models/EFPenalty.cs @@ -1,11 +1,26 @@ -using System; +using Data.Models.Client; +using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace SharedLibraryCore.Database.Models +namespace Data.Models { - public partial class EFPenalty : SharedEntity + public class EFPenalty : SharedEntity { + public enum PenaltyType + { + Report, + Warning, + Flag, + Kick, + TempBan, + Ban, + Unban, + Any, + Unflag, + Other = 100 + } + [Key] public int PenaltyId { get; set; } [Required] diff --git a/Data/Models/Reference.cs b/Data/Models/Reference.cs new file mode 100644 index 000000000..6322d6df0 --- /dev/null +++ b/Data/Models/Reference.cs @@ -0,0 +1,19 @@ +namespace Data.Models +{ + public class Reference + { + public enum Game + { + COD = -1, + UKN = 0, + IW3 = 1, + IW4 = 2, + IW5 = 3, + IW6 = 4, + T4 = 5, + T5 = 6, + T6 = 7, + T7 = 8 + } + } +} \ No newline at end of file diff --git a/Plugins/Stats/Models/EFServer.cs b/Data/Models/Server/EFServer.cs similarity index 57% rename from Plugins/Stats/Models/EFServer.cs rename to Data/Models/Server/EFServer.cs index c547278d1..70a49cd49 100644 --- a/Plugins/Stats/Models/EFServer.cs +++ b/Data/Models/Server/EFServer.cs @@ -1,12 +1,10 @@ - -using SharedLibraryCore.Database.Models; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using static SharedLibraryCore.Server; +using Data.Abstractions; -namespace IW4MAdmin.Plugins.Stats.Models +namespace Data.Models.Server { - public class EFServer : SharedEntity + public class EFServer : SharedEntity, IUniqueId { [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] @@ -14,8 +12,10 @@ namespace IW4MAdmin.Plugins.Stats.Models [Required] public int Port { get; set; } public string EndPoint { get; set; } - public Game? GameName { get; set; } + public Reference.Game? GameName { get; set; } public string HostName { get; set; } public bool IsPasswordProtected { get; set; } + public long Id => ServerId; + public string Value => EndPoint; } } diff --git a/Plugins/Stats/Models/EFServerStatistics.cs b/Data/Models/Server/EFServerStatistics.cs similarity index 75% rename from Plugins/Stats/Models/EFServerStatistics.cs rename to Data/Models/Server/EFServerStatistics.cs index c60f59085..9cc22e623 100644 --- a/Plugins/Stats/Models/EFServerStatistics.cs +++ b/Data/Models/Server/EFServerStatistics.cs @@ -1,8 +1,7 @@ -using SharedLibraryCore.Database.Models; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace IW4MAdmin.Plugins.Stats.Models +namespace Data.Models.Server { public class EFServerStatistics : SharedEntity { diff --git a/SharedLibraryCore/Database/Models/SharedEntity.cs b/Data/Models/SharedEntity.cs similarity index 63% rename from SharedLibraryCore/Database/Models/SharedEntity.cs rename to Data/Models/SharedEntity.cs index cd7c2b947..bd5271ce3 100644 --- a/SharedLibraryCore/Database/Models/SharedEntity.cs +++ b/Data/Models/SharedEntity.cs @@ -1,9 +1,7 @@ -using SharedLibraryCore.Interfaces; -using System; +using Data.Abstractions; using System.Collections.Concurrent; -using System.ComponentModel.DataAnnotations.Schema; -namespace SharedLibraryCore.Database.Models +namespace Data.Models { public class SharedEntity : IPropertyExtender { @@ -35,17 +33,5 @@ namespace SharedLibraryCore.Database.Models _additionalProperties.TryAdd(name, value); } } - - ///// - ///// Specifies when the entity was created - ///// - //[Column(TypeName="datetime")] - //public DateTime CreatedDateTime { get; set; } - - ///// - ///// Specifies when the entity was updated - ///// - //[Column(TypeName = "datetime")] - //public DateTime? UpdatedDateTime { get;set; } } } diff --git a/SharedLibraryCore/Helpers/Vector3.cs b/Data/Models/Vector3.cs similarity index 64% rename from SharedLibraryCore/Helpers/Vector3.cs rename to Data/Models/Vector3.cs index 4bdf3c9d9..c4c53af59 100644 --- a/SharedLibraryCore/Helpers/Vector3.cs +++ b/Data/Models/Vector3.cs @@ -1,17 +1,12 @@ using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; -namespace SharedLibraryCore.Helpers +namespace Data.Models { public class Vector3 { - [Key] - public int Vector3Id { get; set; } + [Key] public int Vector3Id { get; set; } public float X { get; protected set; } public float Y { get; protected set; } public float Z { get; protected set; } @@ -19,7 +14,6 @@ namespace SharedLibraryCore.Helpers // this is for EF and really should be somewhere else public Vector3() { - } public Vector3(float x, float y, float z) @@ -46,7 +40,9 @@ namespace SharedLibraryCore.Helpers public static Vector3 Parse(string s) { - bool valid = Regex.Match(s, @"\((-?[0-9]+\.?[0-9]*|-?[0-9]+\.?[0-9]*e-[0-9]+),\ (-?[0-9]+\.?[0-9]*|-?[0-9]+\.?[0-9]*e-[0-9]+),\ (-?[0-9]+\.?[0-9]*|-?[0-9]+\.?[0-9]*e-[0-9]+)\)").Success; + bool valid = Regex.Match(s, + @"\((-?[0-9]+\.?[0-9]*|-?[0-9]+\.?[0-9]*e-[0-9]+),\ (-?[0-9]+\.?[0-9]*|-?[0-9]+\.?[0-9]*e-[0-9]+),\ (-?[0-9]+\.?[0-9]*|-?[0-9]+\.?[0-9]*e-[0-9]+)\)") + .Success; if (!valid) { throw new FormatException("Vector3 is not in correct format"); @@ -55,9 +51,13 @@ namespace SharedLibraryCore.Helpers string removeParenthesis = s.Substring(1, s.Length - 2); string[] eachPoint = removeParenthesis.Split(','); - return new Vector3(float.Parse(eachPoint[0], System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture), - float.Parse(eachPoint[1], System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture), - float.Parse(eachPoint[2], System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture)); + return new Vector3( + float.Parse(eachPoint[0], System.Globalization.NumberStyles.Any, + System.Globalization.CultureInfo.InvariantCulture), + float.Parse(eachPoint[1], System.Globalization.NumberStyles.Any, + System.Globalization.CultureInfo.InvariantCulture), + float.Parse(eachPoint[2], System.Globalization.NumberStyles.Any, + System.Globalization.CultureInfo.InvariantCulture)); } public static double Distance(Vector3 a, Vector3 b) @@ -76,44 +76,7 @@ namespace SharedLibraryCore.Helpers return Math.Sqrt((dx * dx) + (dy * dy)); } - - public static double SnapDistance(Vector3 a, Vector3 b, Vector3 c) - { - a = a.FixIW4Angles(); - b = b.FixIW4Angles(); - c = c.FixIW4Angles(); - - float preserveDirectionAngle(float a1, float b1) - { - float difference = b1 - a1; - while (difference < -180) difference += 360; - while (difference > 180) difference -= 360; - return difference; - } - - var directions = new[] - { - new Vector3() - { - X = preserveDirectionAngle(b.X, a.X), - Y = preserveDirectionAngle(b.Y, a.Y) - }, - new Vector3() - { - X = preserveDirectionAngle(c.X, b.X), - Y = preserveDirectionAngle(c.Y, b.Y) - } - }; - - var distance = new Vector3 - { - X = Math.Abs(directions[1].X - directions[0].X), - Y = Math.Abs(directions[1].Y - directions[0].Y) - }; - - return Math.Sqrt((distance.X * distance.X) + (distance.Y * distance.Y)); - } - + public static double ViewAngleDistance(Vector3 a, Vector3 b, Vector3 c) { double dabX = Math.Abs(a.X - b.X); @@ -147,4 +110,4 @@ namespace SharedLibraryCore.Helpers public double AngleBetween(Vector3 a) => Math.Acos(this.DotProduct(a) / (a.Magnitude() * this.Magnitude())); } -} +} \ No newline at end of file diff --git a/IW4MAdmin.sln b/IW4MAdmin.sln index 1cc503478..e8a9d1c13 100644 --- a/IW4MAdmin.sln +++ b/IW4MAdmin.sln @@ -39,18 +39,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ScriptPlugins", "ScriptPlug Plugins\ScriptPlugins\ParserPIW5.js = Plugins\ScriptPlugins\ParserPIW5.js Plugins\ScriptPlugins\ParserPT6.js = Plugins\ScriptPlugins\ParserPT6.js Plugins\ScriptPlugins\ParserRektT5M.js = Plugins\ScriptPlugins\ParserRektT5M.js + Plugins\ScriptPlugins\ParserT4.js = Plugins\ScriptPlugins\ParserT4.js Plugins\ScriptPlugins\ParserT7.js = Plugins\ScriptPlugins\ParserT7.js Plugins\ScriptPlugins\ParserTeknoMW3.js = Plugins\ScriptPlugins\ParserTeknoMW3.js Plugins\ScriptPlugins\SampleScriptPluginCommand.js = Plugins\ScriptPlugins\SampleScriptPluginCommand.js Plugins\ScriptPlugins\SharedGUIDKick.js = Plugins\ScriptPlugins\SharedGUIDKick.js Plugins\ScriptPlugins\VPNDetection.js = Plugins\ScriptPlugins\VPNDetection.js - Plugins\ScriptPlugins\ParserT4.js = Plugins\ScriptPlugins\ParserT4.js EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{A848FCF1-8527-4AA8-A1AA-50D29695C678}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StatsWeb", "Plugins\Web\StatsWeb\StatsWeb.csproj", "{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutomessageFeed", "Plugins\AutomessageFeed\AutomessageFeed.csproj", "{F5815359-CFC7-44B4-9A3B-C04BACAD5836}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LiveRadar", "Plugins\LiveRadar\LiveRadar.csproj", "{00A1FED2-2254-4AF7-A5DB-2357FA7C88CD}" @@ -59,6 +55,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{3065279E EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplicationTests", "Tests\ApplicationTests\ApplicationTests.csproj", "{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Data", "Data\Data.csproj", "{81689023-E55E-48ED-B7A8-53F4E21BBF2D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -267,30 +265,6 @@ Global {6C706CE5-A206-4E46-8712-F8C48D526091}.Release|x64.Build.0 = Release|Any CPU {6C706CE5-A206-4E46-8712-F8C48D526091}.Release|x86.ActiveCfg = Release|Any CPU {6C706CE5-A206-4E46-8712-F8C48D526091}.Release|x86.Build.0 = Release|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|x64.ActiveCfg = Debug|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|x64.Build.0 = Debug|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|x86.ActiveCfg = Debug|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|x86.Build.0 = Debug|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|Any CPU.Build.0 = Prerelease|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|Mixed Platforms.ActiveCfg = Debug|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|Mixed Platforms.Build.0 = Debug|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|x64.ActiveCfg = Debug|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|x64.Build.0 = Debug|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|x86.ActiveCfg = Debug|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|x86.Build.0 = Debug|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|Any CPU.Build.0 = Release|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|x64.ActiveCfg = Release|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|x64.Build.0 = Release|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|x86.ActiveCfg = Release|Any CPU - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|x86.Build.0 = Release|Any CPU {F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|Any CPU.Build.0 = Debug|Any CPU {F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -362,6 +336,30 @@ Global {581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Release|x64.Build.0 = Release|Any CPU {581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Release|x86.ActiveCfg = Release|Any CPU {581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Release|x86.Build.0 = Release|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Debug|x64.ActiveCfg = Debug|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Debug|x64.Build.0 = Debug|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Debug|x86.ActiveCfg = Debug|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Debug|x86.Build.0 = Debug|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Prerelease|Mixed Platforms.ActiveCfg = Debug|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Prerelease|Mixed Platforms.Build.0 = Debug|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Prerelease|x64.ActiveCfg = Debug|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Prerelease|x64.Build.0 = Debug|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Prerelease|x86.ActiveCfg = Debug|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Prerelease|x86.Build.0 = Debug|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Release|Any CPU.Build.0 = Release|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Release|x64.ActiveCfg = Release|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Release|x64.Build.0 = Release|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Release|x86.ActiveCfg = Release|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Release|x86.Build.0 = Release|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU + {81689023-E55E-48ED-B7A8-53F4E21BBF2D}.Prerelease|Any CPU.Build.0 = Prerelease|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -373,8 +371,6 @@ Global {D9F2ED28-6FA5-40CA-9912-E7A849147AB1} = {26E8B310-269E-46D4-A612-24601F16065F} {6C706CE5-A206-4E46-8712-F8C48D526091} = {26E8B310-269E-46D4-A612-24601F16065F} {3F9ACC27-26DB-49FA-BCD2-50C54A49C9FA} = {26E8B310-269E-46D4-A612-24601F16065F} - {A848FCF1-8527-4AA8-A1AA-50D29695C678} = {26E8B310-269E-46D4-A612-24601F16065F} - {776B348B-F818-4A0F-A625-D0AF8BAD3E9B} = {A848FCF1-8527-4AA8-A1AA-50D29695C678} {F5815359-CFC7-44B4-9A3B-C04BACAD5836} = {26E8B310-269E-46D4-A612-24601F16065F} {00A1FED2-2254-4AF7-A5DB-2357FA7C88CD} = {26E8B310-269E-46D4-A612-24601F16065F} {581FA7AF-FEF6-483C-A7D0-2D13EF50801B} = {3065279E-17F0-4CE0-AF5B-014E04263D77} diff --git a/Plugins/AutomessageFeed/AutomessageFeed.csproj b/Plugins/AutomessageFeed/AutomessageFeed.csproj index 78bf21a1f..f7b0ad0d8 100644 --- a/Plugins/AutomessageFeed/AutomessageFeed.csproj +++ b/Plugins/AutomessageFeed/AutomessageFeed.csproj @@ -10,7 +10,7 @@ - + diff --git a/Plugins/IW4ScriptCommands/IW4ScriptCommands.csproj b/Plugins/IW4ScriptCommands/IW4ScriptCommands.csproj index 24f83fe51..0f23e1514 100644 --- a/Plugins/IW4ScriptCommands/IW4ScriptCommands.csproj +++ b/Plugins/IW4ScriptCommands/IW4ScriptCommands.csproj @@ -10,7 +10,7 @@ - + diff --git a/Plugins/LiveRadar/LiveRadar.csproj b/Plugins/LiveRadar/LiveRadar.csproj index c767d4539..77c28bf4f 100644 --- a/Plugins/LiveRadar/LiveRadar.csproj +++ b/Plugins/LiveRadar/LiveRadar.csproj @@ -15,9 +15,6 @@ - - - @@ -25,6 +22,10 @@ + + + + diff --git a/Plugins/LiveRadar/Plugin.cs b/Plugins/LiveRadar/Plugin.cs index 245129af2..7c921a147 100644 --- a/Plugins/LiveRadar/Plugin.cs +++ b/Plugins/LiveRadar/Plugin.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using SharedLibraryCore.Configuration; using SharedLibraryCore.Interfaces; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -24,12 +25,14 @@ namespace LiveRadar private bool addedPage; private readonly object lockObject = new object(); private readonly ILogger _logger; + private readonly ApplicationConfiguration _appConfig; - public Plugin(ILogger logger, IConfigurationHandlerFactory configurationHandlerFactory) + public Plugin(ILogger logger, IConfigurationHandlerFactory configurationHandlerFactory, ApplicationConfiguration appConfig) { _configurationHandler = configurationHandlerFactory.GetConfigurationHandler("LiveRadarConfiguration"); _botGuidLookups = new Dictionary(); _logger = logger; + _appConfig = appConfig; } public Task OnEventAsync(GameEvent E, Server S) @@ -64,6 +67,11 @@ namespace LiveRadar { try { + if (((string) E.Extra).IsBotGuid() && _appConfig.IgnoreBots) + { + return Task.CompletedTask; + } + string botKey = $"BotGuid_{E.Extra}"; long generatedBotGuid; diff --git a/Plugins/LiveRadar/RadarEvent.cs b/Plugins/LiveRadar/RadarEvent.cs index dc2354c03..3dcf8bfab 100644 --- a/Plugins/LiveRadar/RadarEvent.cs +++ b/Plugins/LiveRadar/RadarEvent.cs @@ -1,5 +1,5 @@ -using SharedLibraryCore; -using SharedLibraryCore.Helpers; +using Data.Models; +using SharedLibraryCore; using System; using System.Linq; diff --git a/Plugins/Login/Login.csproj b/Plugins/Login/Login.csproj index 03a890c1e..406a1f324 100644 --- a/Plugins/Login/Login.csproj +++ b/Plugins/Login/Login.csproj @@ -18,12 +18,12 @@ TRACE;DEBUG; + + + + - - - - diff --git a/Plugins/ProfanityDeterment/Plugin.cs b/Plugins/ProfanityDeterment/Plugin.cs index de2fda40d..be0f752b4 100644 --- a/Plugins/ProfanityDeterment/Plugin.cs +++ b/Plugins/ProfanityDeterment/Plugin.cs @@ -2,9 +2,8 @@ using System.Reflection; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Data.Models; using SharedLibraryCore; -using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database.Models; using SharedLibraryCore.Interfaces; namespace IW4MAdmin.Plugins.ProfanityDeterment diff --git a/Plugins/ProfanityDeterment/ProfanityDeterment.csproj b/Plugins/ProfanityDeterment/ProfanityDeterment.csproj index 3c4a7c0e4..d3d0fbb94 100644 --- a/Plugins/ProfanityDeterment/ProfanityDeterment.csproj +++ b/Plugins/ProfanityDeterment/ProfanityDeterment.csproj @@ -16,7 +16,7 @@ - + diff --git a/Plugins/Stats/Cheat/Detection.cs b/Plugins/Stats/Cheat/Detection.cs index f5357939a..64172b1e0 100644 --- a/Plugins/Stats/Cheat/Detection.cs +++ b/Plugins/Stats/Cheat/Detection.cs @@ -1,12 +1,15 @@ -using IW4MAdmin.Plugins.Stats.Models; -using SharedLibraryCore.Database.Models; +using SharedLibraryCore.Database.Models; using SharedLibraryCore.Helpers; using SharedLibraryCore.Interfaces; using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using Data.Models; +using Data.Models.Client; +using Data.Models.Client.Stats; using Microsoft.Extensions.Logging; +using SharedLibraryCore; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace IW4MAdmin.Plugins.Stats.Cheat @@ -66,6 +69,33 @@ namespace IW4MAdmin.Plugins.Stats.Cheat Tracker = new ChangeTracking(); TrackedHits = new List(); } + + private static double SnapDistance(Vector3 a, Vector3 b, Vector3 c) + { + a = a.FixIW4Angles(); + b = b.FixIW4Angles(); + c = c.FixIW4Angles(); + + + float preserveDirectionAngle(float a1, float b1) + { + float difference = b1 - a1; + while (difference < -180) difference += 360; + while (difference > 180) difference -= 360; + return difference; + } + + var directions = new[] + { + new Vector3(preserveDirectionAngle(b.X, a.X),preserveDirectionAngle(b.Y, a.Y), 0), + new Vector3( preserveDirectionAngle(c.X, b.X), preserveDirectionAngle(c.Y, b.Y), 0) + }; + + var distance = new Vector3(Math.Abs(directions[1].X - directions[0].X), + Math.Abs(directions[1].Y - directions[0].Y), 0); + + return Math.Sqrt((distance.X * distance.X) + (distance.Y * distance.Y)); + } /// /// Analyze kill and see if performed by a cheater @@ -76,12 +106,12 @@ namespace IW4MAdmin.Plugins.Stats.Cheat { var results = new List(); - if ((hit.DeathType != IW4Info.MeansOfDeath.MOD_PISTOL_BULLET && - hit.DeathType != IW4Info.MeansOfDeath.MOD_RIFLE_BULLET && - hit.DeathType != IW4Info.MeansOfDeath.MOD_HEAD_SHOT) || - hit.HitLoc == IW4Info.HitLocation.none || hit.TimeOffset - LastOffset < 0 || + if ((hit.DeathType != (int)IW4Info.MeansOfDeath.MOD_PISTOL_BULLET && + hit.DeathType != (int)IW4Info.MeansOfDeath.MOD_RIFLE_BULLET && + hit.DeathType != (int)IW4Info.MeansOfDeath.MOD_HEAD_SHOT) || + hit.HitLoc == (int)IW4Info.HitLocation.none || hit.TimeOffset - LastOffset < 0 || // hack: prevents false positives - (LastWeapon != hit.Weapon && (hit.TimeOffset - LastOffset) == 50)) + ((int)LastWeapon != hit.Weapon && (hit.TimeOffset - LastOffset) == 50)) { return new[] {new DetectionPenaltyResult() { @@ -89,9 +119,9 @@ namespace IW4MAdmin.Plugins.Stats.Cheat }}; } - LastWeapon = hit.Weapon; + LastWeapon = (IW4Info.WeaponName)(hit.Weapon); - HitLocationCount[hit.HitLoc].Count++; + HitLocationCount[(IW4Info.HitLocation)hit.HitLoc].Count++; HitCount++; if (hit.IsKill) @@ -116,7 +146,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat { ClientStats.SnapHitCount++; sessionSnapHits++; - var currentSnapDistance = Vector3.SnapDistance(hit.AnglesList[0], hit.AnglesList[1], hit.ViewAngles); + var currentSnapDistance = SnapDistance(hit.AnglesList[0], hit.AnglesList[1], hit.ViewAngles); double previousAverage = ClientStats.AverageSnapValue; ClientStats.AverageSnapValue = (previousAverage * (ClientStats.SnapHitCount - 1) + currentSnapDistance) / ClientStats.SnapHitCount; double previousSessionAverage = sessionAverageSnapAmount; @@ -211,7 +241,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat } // SESSION - var sessionHitLoc = HitLocationCount[hit.HitLoc]; + var sessionHitLoc = HitLocationCount[(IW4Info.HitLocation)hit.HitLoc]; sessionHitLoc.Offset = (sessionHitLoc.Offset * (sessionHitLoc.Count - 1) + realAgainstPredict) / sessionHitLoc.Count; int totalSessionHits = HitLocationCount.Sum(_hit => _hit.Value.Count); @@ -229,7 +259,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat Value = weightedSessionAverage, HitCount = HitCount, Type = DetectionType.Offset, - Location = hitLoc.Location + Location = (IW4Info.HitLocation)hitLoc.Location }); } @@ -278,8 +308,8 @@ namespace IW4MAdmin.Plugins.Stats.Cheat bool shouldIgnoreDetection = false; try { - shouldIgnoreDetection = Plugin.Config.Configuration().AnticheatConfiguration.IgnoredDetectionSpecification[hit.GameName][DetectionType.Recoil] - .Any(_weaponRegex => Regex.IsMatch(hit.Weapon.ToString(), _weaponRegex)); + shouldIgnoreDetection = Plugin.Config.Configuration().AnticheatConfiguration.IgnoredDetectionSpecification[(Server.Game)hit.GameName][DetectionType.Recoil] + .Any(_weaponRegex => Regex.IsMatch(((IW4Info.WeaponName)(hit.Weapon)).ToString(), _weaponRegex)); } catch (KeyNotFoundException) @@ -310,8 +340,8 @@ namespace IW4MAdmin.Plugins.Stats.Cheat try { shouldIgnoreDetection = false; - shouldIgnoreDetection = Plugin.Config.Configuration().AnticheatConfiguration.IgnoredDetectionSpecification[hit.GameName][DetectionType.Button] - .Any(_weaponRegex => Regex.IsMatch(hit.Weapon.ToString(), _weaponRegex)); + shouldIgnoreDetection = Plugin.Config.Configuration().AnticheatConfiguration.IgnoredDetectionSpecification[(Server.Game)hit.GameName][DetectionType.Button] + .Any(_weaponRegex => Regex.IsMatch(((IW4Info.WeaponName)(hit.Weapon)).ToString(), _weaponRegex)); } catch (KeyNotFoundException) @@ -423,8 +453,8 @@ namespace IW4MAdmin.Plugins.Stats.Cheat try { shouldIgnoreDetection = false; // reset previous value - shouldIgnoreDetection = Plugin.Config.Configuration().AnticheatConfiguration.IgnoredDetectionSpecification[hit.GameName][DetectionType.Chest] - .Any(_weaponRegex => Regex.IsMatch(hit.Weapon.ToString(), _weaponRegex)); + shouldIgnoreDetection = Plugin.Config.Configuration().AnticheatConfiguration.IgnoredDetectionSpecification[(Server.Game)hit.GameName][DetectionType.Chest] + .Any(_weaponRegex => Regex.IsMatch(((IW4Info.WeaponName)(hit.Weapon)).ToString(), _weaponRegex)); } catch (KeyNotFoundException) diff --git a/Plugins/Stats/Cheat/DetectionPenaltyResult.cs b/Plugins/Stats/Cheat/DetectionPenaltyResult.cs index 1f1edf2dc..ae9be54d5 100644 --- a/Plugins/Stats/Cheat/DetectionPenaltyResult.cs +++ b/Plugins/Stats/Cheat/DetectionPenaltyResult.cs @@ -1,4 +1,4 @@ -using SharedLibraryCore.Database.Models; +using Data.Models; namespace IW4MAdmin.Plugins.Stats.Cheat { diff --git a/Plugins/Stats/Cheat/Strain.cs b/Plugins/Stats/Cheat/Strain.cs index 7fe4b1d53..e06caf225 100644 --- a/Plugins/Stats/Cheat/Strain.cs +++ b/Plugins/Stats/Cheat/Strain.cs @@ -1,9 +1,6 @@ using SharedLibraryCore; -using SharedLibraryCore.Helpers; -using SharedLibraryCore.Interfaces; using System; -using System.Collections.Generic; -using System.Text; +using Data.Models; namespace IW4MAdmin.Plugins.Stats.Cheat { diff --git a/Plugins/Stats/Cheat/Thresholds.cs b/Plugins/Stats/Cheat/Thresholds.cs index 37f2c9601..f9b0fa944 100644 --- a/Plugins/Stats/Cheat/Thresholds.cs +++ b/Plugins/Stats/Cheat/Thresholds.cs @@ -1,19 +1,19 @@ using Stats.Config; using System; -using static SharedLibraryCore.Database.Models.EFPenalty; +using Data.Models; namespace IW4MAdmin.Plugins.Stats.Cheat { public static class DistributionHelper { - public static double CalculateMaxValue(this DistributionConfiguration config, PenaltyType penaltyType, int sampleSize) + public static double CalculateMaxValue(this DistributionConfiguration config, EFPenalty.PenaltyType penaltyType, int sampleSize) { switch (config.Type) { case DistributionConfiguration.DistributionType.Normal: break; case DistributionConfiguration.DistributionType.LogNormal: - double deviationNumber = penaltyType == PenaltyType.Flag ? 3.0 : 4.0; + double deviationNumber = penaltyType == EFPenalty.PenaltyType.Flag ? 3.0 : 4.0; double marginOfError = 1.644 / (config.StandardDeviation / Math.Sqrt(sampleSize)); double maxValue = (config.StandardDeviation * deviationNumber) + marginOfError; return maxValue; diff --git a/Plugins/Stats/Client/Abstractions/IClientStatisticCalculator.cs b/Plugins/Stats/Client/Abstractions/IClientStatisticCalculator.cs new file mode 100644 index 000000000..872278b99 --- /dev/null +++ b/Plugins/Stats/Client/Abstractions/IClientStatisticCalculator.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using SharedLibraryCore; + +namespace IW4MAdmin.Plugins.Stats.Client.Abstractions +{ + public interface IClientStatisticCalculator + { + Task GatherDependencies(); + Task CalculateForEvent(GameEvent gameEvent); + } +} \ No newline at end of file diff --git a/Plugins/Stats/Client/Abstractions/IHitInfoBuilder.cs b/Plugins/Stats/Client/Abstractions/IHitInfoBuilder.cs new file mode 100644 index 000000000..93ff90c0f --- /dev/null +++ b/Plugins/Stats/Client/Abstractions/IHitInfoBuilder.cs @@ -0,0 +1,10 @@ +using IW4MAdmin.Plugins.Stats.Client.Game; +using SharedLibraryCore; + +namespace Stats.Client.Abstractions +{ + public interface IHitInfoBuilder + { + HitInfo Build(string[] log, int entityId, bool isSelf, bool isVictim, Server.Game gameName); + } +} \ No newline at end of file diff --git a/Plugins/Stats/Client/Abstractions/IServerDistributionCalculator.cs b/Plugins/Stats/Client/Abstractions/IServerDistributionCalculator.cs new file mode 100644 index 000000000..9630c1232 --- /dev/null +++ b/Plugins/Stats/Client/Abstractions/IServerDistributionCalculator.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace Stats.Client.Abstractions +{ + public interface IServerDistributionCalculator + { + Task Initialize(); + Task GetZScoreForServer(long serverId, double value); + Task GetRatingForZScore(double? value); + } +} \ No newline at end of file diff --git a/Plugins/Stats/Client/Abstractions/IWeaponNameParser.cs b/Plugins/Stats/Client/Abstractions/IWeaponNameParser.cs new file mode 100644 index 000000000..17367af10 --- /dev/null +++ b/Plugins/Stats/Client/Abstractions/IWeaponNameParser.cs @@ -0,0 +1,10 @@ +using SharedLibraryCore; +using Stats.Client.Game; + +namespace Stats.Client.Abstractions +{ + public interface IWeaponNameParser + { + WeaponInfo Parse(string weaponName, Server.Game gameName); + } +} diff --git a/Plugins/Stats/Client/Game/AttachmentInfo.cs b/Plugins/Stats/Client/Game/AttachmentInfo.cs new file mode 100644 index 000000000..8b60870a0 --- /dev/null +++ b/Plugins/Stats/Client/Game/AttachmentInfo.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Stats.Client.Game +{ + public class AttachmentInfo + { + public string Name { get; set; } + } +} diff --git a/Plugins/Stats/Client/Game/HitInfo.cs b/Plugins/Stats/Client/Game/HitInfo.cs new file mode 100644 index 000000000..55d0dcb34 --- /dev/null +++ b/Plugins/Stats/Client/Game/HitInfo.cs @@ -0,0 +1,27 @@ +using Data.Models; +using Stats.Client.Game; + +namespace IW4MAdmin.Plugins.Stats.Client.Game +{ + public enum HitType + { + Unknown, + Kill, + Damage, + WasKilled, + WasDamaged, + Suicide + } + + public class HitInfo + { + public Reference.Game Game { get; set; } + public int EntityId { get; set; } + public bool IsVictim { get; set; } + public HitType HitType { get; set; } + public int Damage { get; set; } + public string Location { get; set; } + public string MeansOfDeath { get; set; } + public WeaponInfo Weapon { get; set; } + } +} \ No newline at end of file diff --git a/Plugins/Stats/Client/Game/WeaponInfo.cs b/Plugins/Stats/Client/Game/WeaponInfo.cs new file mode 100644 index 000000000..09aea1b1a --- /dev/null +++ b/Plugins/Stats/Client/Game/WeaponInfo.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Stats.Client.Game +{ + public class WeaponInfo + { + public string RawName { get; set; } + public string Name { get; set; } + public IList Attachments { get; set; } = new List(); + } +} diff --git a/Plugins/Stats/Client/HitCalculator.cs b/Plugins/Stats/Client/HitCalculator.cs new file mode 100644 index 000000000..8b23a0a47 --- /dev/null +++ b/Plugins/Stats/Client/HitCalculator.cs @@ -0,0 +1,611 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Data.Abstractions; +using Data.Models; +using Data.Models.Client.Stats; +using Data.Models.Client.Stats.Reference; +using Data.Models.Server; +using IW4MAdmin.Plugins.Stats.Client.Abstractions; +using IW4MAdmin.Plugins.Stats.Client.Game; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using SharedLibraryCore; +using SharedLibraryCore.Database.Models; +using Stats.Client.Abstractions; +using Stats.Client.Game; + +namespace IW4MAdmin.Plugins.Stats.Client +{ + public class HitState + { + public HitState() + { + OnTransaction = new SemaphoreSlim(1, 1); + } + + ~HitState() + { + OnTransaction.Dispose(); + } + + public List Hits { get; set; } + public DateTime? LastUsage { get; set; } + public int? LastWeaponId { get; set; } + public EFServer Server { get; set; } + public SemaphoreSlim OnTransaction { get; } + public int UpdateCount { get; set; } + } + + public class HitCalculator : IClientStatisticCalculator + { + private readonly IDatabaseContextFactory _contextFactory; + private readonly ILogger _logger; + + private readonly ConcurrentDictionary _clientHitStatistics = + new ConcurrentDictionary(); + + private readonly SemaphoreSlim _onTransaction = new SemaphoreSlim(1, 1); + + private readonly ILookupCache _serverCache; + private readonly ILookupCache _hitLocationCache; + private readonly ILookupCache _weaponCache; + private readonly ILookupCache _attachmentCache; + private readonly ILookupCache _attachmentComboCache; + private readonly ILookupCache _modCache; + private readonly IHitInfoBuilder _hitInfoBuilder; + private readonly IServerDistributionCalculator _serverDistributionCalculator; + + private readonly TimeSpan _maxActiveTime = TimeSpan.FromMinutes(2); + private const int MaxUpdatesBeforePersist = 20; + private const string SessionScores = nameof(SessionScores); + + public HitCalculator(ILogger logger, IDatabaseContextFactory contextFactory, + ILookupCache hitLocationCache, ILookupCache weaponCache, + ILookupCache attachmentCache, + ILookupCache attachmentComboCache, + ILookupCache serverCache, ILookupCache modCache, IHitInfoBuilder hitInfoBuilder, + IServerDistributionCalculator serverDistributionCalculator) + { + _contextFactory = contextFactory; + _logger = logger; + _hitLocationCache = hitLocationCache; + _weaponCache = weaponCache; + _attachmentCache = attachmentCache; + _attachmentComboCache = attachmentComboCache; + _serverCache = serverCache; + _hitInfoBuilder = hitInfoBuilder; + _modCache = modCache; + _serverDistributionCalculator = serverDistributionCalculator; + } + + public async Task GatherDependencies() + { + await _hitLocationCache.InitializeAsync(); + await _weaponCache.InitializeAsync(); + await _attachmentCache.InitializeAsync(); + await _attachmentComboCache.InitializeAsync(); + await _serverCache.InitializeAsync(); + await _modCache.InitializeAsync(); + } + + public async Task CalculateForEvent(GameEvent gameEvent) + { + if (gameEvent.Type == GameEvent.EventType.Connect) + { + // if no servers have been cached yet we need to pull them here + // as they could have gotten added after we've initialized + if (!_serverCache.GetAll().Any()) + { + await _serverCache.InitializeAsync(); + } + + gameEvent.Origin.SetAdditionalProperty(SessionScores, new List<(int, DateTime)>()); + return; + } + + if (gameEvent.Type == GameEvent.EventType.Disconnect) + { + _clientHitStatistics.Remove(gameEvent.Origin.ClientId, out var state); + + if (state == null) + { + _logger.LogWarning("No client hit state available for disconnecting client {client}", + gameEvent.Origin.ToString()); + return; + } + + try + { + await state.OnTransaction.WaitAsync(); + HandleDisconnectCalculations(gameEvent.Origin, state); + await UpdateClientStatistics(gameEvent.Origin.ClientId, state); + } + + catch (Exception ex) + { + _logger.LogError(ex, "Could not handle disconnect calculations for client {client}", + gameEvent.Origin.ToString()); + } + + finally + { + if (state.OnTransaction.CurrentCount == 0) + { + state.OnTransaction.Release(); + } + } + + return; + } + + if (gameEvent.Type == GameEvent.EventType.MapEnd) + { + foreach (var client in gameEvent.Owner.GetClientsAsList()) + { + var scores = client.GetAdditionalProperty>(SessionScores); + scores?.Add((client.Score, DateTime.Now)); + } + } + + if (gameEvent.Type != GameEvent.EventType.Kill && gameEvent.Type != GameEvent.EventType.Damage) + { + return; + } + + var eventRegex = gameEvent.Type == GameEvent.EventType.Kill + ? gameEvent.Owner.EventParser.Configuration.Kill + : gameEvent.Owner.EventParser.Configuration.Damage; + + var match = eventRegex.PatternMatcher.Match(gameEvent.Data); + + if (!match.Success) + { + _logger.LogWarning("Log for event type {type} does not match pattern {logLine}", gameEvent.Type, + gameEvent.Data); + return; + } + + var attackerHitInfo = _hitInfoBuilder.Build(match.Values.Skip(1).ToArray(), gameEvent.Origin.ClientId, + gameEvent.Origin.ClientId == gameEvent.Target.ClientId, false, gameEvent.Owner.GameName); + var victimHitInfo = _hitInfoBuilder.Build(match.Values.Skip(1).ToArray(), gameEvent.Target.ClientId, + gameEvent.Origin.ClientId == gameEvent.Target.ClientId, true, gameEvent.Owner.GameName); + + foreach (var hitInfo in new[] {attackerHitInfo, victimHitInfo}) + { + try + { + await _onTransaction.WaitAsync(); + if (!_clientHitStatistics.ContainsKey(hitInfo.EntityId)) + { + _logger.LogDebug("Starting to track hits for {client}", hitInfo.EntityId); + var clientHits = await GetHitsForClient(hitInfo.EntityId); + _clientHitStatistics.TryAdd(hitInfo.EntityId, new HitState() + { + Hits = clientHits, + Server = (await _serverCache + .FirstAsync(server => + server.EndPoint == gameEvent.Owner.ToString() && server.HostName != null)) + }); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Could not retrieve previous hit data for client {client}"); + continue; + } + + finally + { + if (_onTransaction.CurrentCount == 0) + { + _onTransaction.Release(); + } + } + + var state = _clientHitStatistics[hitInfo.EntityId]; + + try + { + await _onTransaction.WaitAsync(); + var calculatedHits = await RunTasksForHitInfo(hitInfo, state.Server.ServerId); + + foreach (var clientHit in calculatedHits) + { + RunCalculation(clientHit, hitInfo, state); + } + } + + catch (Exception ex) + { + _logger.LogError(ex, "Could not update hit calculations for {client}", hitInfo.EntityId); + } + + finally + { + if (_onTransaction.CurrentCount == 0) + { + _onTransaction.Release(); + } + } + } + } + + + + private async Task> RunTasksForHitInfo(HitInfo hitInfo, long? serverId) + { + var weapon = await GetOrAddWeapon(hitInfo.Weapon, hitInfo.Game); + var attachments = + await Task.WhenAll(hitInfo.Weapon.Attachments.Select(attachment => + GetOrAddAttachment(attachment, hitInfo.Game))); + var attachmentCombo = await GetOrAddAttachmentCombo(attachments, hitInfo.Game); + var matchingLocation = await GetOrAddHitLocation(hitInfo.Location, hitInfo.Game); + var meansOfDeath = await GetOrAddMeansOfDeath(hitInfo.MeansOfDeath, hitInfo.Game); + + var baseTasks = new[] + { + // just the client + GetOrAddClientHit(hitInfo.EntityId, null), + // client and server + GetOrAddClientHit(hitInfo.EntityId, serverId), + // just the location + GetOrAddClientHit(hitInfo.EntityId, null, matchingLocation.HitLocationId), + // location and server + GetOrAddClientHit(hitInfo.EntityId, serverId, matchingLocation.HitLocationId), + // per weapon + GetOrAddClientHit(hitInfo.EntityId, null, null, weapon.WeaponId), + // per weapon and server + GetOrAddClientHit(hitInfo.EntityId, serverId, null, weapon.WeaponId), + // means of death aggregate + GetOrAddClientHit(hitInfo.EntityId, meansOfDeathId: meansOfDeath.MeansOfDeathId), + // means of death per server aggregate + GetOrAddClientHit(hitInfo.EntityId, serverId, + meansOfDeathId: meansOfDeath.MeansOfDeathId) + }; + + var allTasks = baseTasks.AsEnumerable(); + + if (attachmentCombo != null) + { + allTasks = allTasks + // per weapon per attachment combo + .Append(GetOrAddClientHit(hitInfo.EntityId, null, null, + weapon.WeaponId, attachmentCombo.WeaponAttachmentComboId)) + .Append(GetOrAddClientHit(hitInfo.EntityId, serverId, null, + weapon.WeaponId, attachmentCombo.WeaponAttachmentComboId)); + } + + return await Task.WhenAll(allTasks); + } + + private void RunCalculation(EFClientHitStatistic clientHit, HitInfo hitInfo, HitState hitState) + { + if (hitInfo.HitType == HitType.Kill || hitInfo.HitType == HitType.Damage) + { + if (clientHit.WeaponId != null) // we only want to calculate usage time for weapons + { + var timeElapsed = DateTime.Now - hitState.LastUsage; + var isSameWeapon = clientHit.WeaponId == hitState.LastWeaponId; + + clientHit.UsageSeconds ??= 60; + + if (timeElapsed.HasValue && timeElapsed <= _maxActiveTime) + { + clientHit.UsageSeconds + += // if it's the same weapon we can count the entire elapsed time + // otherwise we split it to make a best guess + (int) Math.Round(timeElapsed.Value.TotalSeconds / (isSameWeapon ? 1.0 : 2.0)); + } + + hitState.LastUsage = DateTime.Now; + } + + clientHit.DamageInflicted += hitInfo.Damage; + clientHit.HitCount++; + } + + if (hitInfo.HitType == HitType.Kill) + { + clientHit.KillCount++; + } + + if (hitInfo.HitType == HitType.WasKilled || hitInfo.HitType == HitType.WasDamaged || + hitInfo.HitType == HitType.Suicide) + { + clientHit.ReceivedHitCount++; + clientHit.DamageReceived += hitInfo.Damage; + } + + if (hitInfo.HitType == HitType.WasKilled) + { + clientHit.DeathCount++; + } + } + + private async Task> GetHitsForClient(int clientId) + { + try + { + await using var context = _contextFactory.CreateContext(); + var hitLocations = await context.Set() + .Where(stat => stat.ClientId == clientId) + .ToListAsync(); + + return !hitLocations.Any() ? new List() : hitLocations; + } + catch (Exception ex) + { + _logger.LogError(ex, "Could not retrieve {hitName} for client with id {id}", + nameof(EFClientHitStatistic), clientId); + } + + return new List(); + } + + private async Task UpdateClientStatistics(int clientId, HitState locState = null) + { + if (!_clientHitStatistics.ContainsKey(clientId) && locState == null) + { + _logger.LogError("No {statsName} found for id {id}", nameof(EFClientHitStatistic), clientId); + return; + } + + var state = locState ?? _clientHitStatistics[clientId]; + + try + { + await using var context = _contextFactory.CreateContext(); + context.Set().UpdateRange(state.Hits); + await context.SaveChangesAsync(); + } + + catch (Exception ex) + { + _logger.LogError(ex, "Could not update hit location stats for id {id}", clientId); + } + } + + private async Task GetOrAddClientHit(int clientId, long? serverId = null, + int? hitLocationId = null, int? weaponId = null, int? attachmentComboId = null, + int? meansOfDeathId = null) + { + var state = _clientHitStatistics[clientId]; + await state.OnTransaction.WaitAsync(); + + var hitStat = state.Hits + .FirstOrDefault(hit => hit.HitLocationId == hitLocationId + && hit.WeaponId == weaponId + && hit.WeaponAttachmentComboId == attachmentComboId + && hit.MeansOfDeathId == meansOfDeathId + && hit.ServerId == serverId); + + if (hitStat != null) + { + state.OnTransaction.Release(); + return hitStat; + } + + hitStat = new EFClientHitStatistic() + { + ClientId = clientId, + ServerId = serverId, + WeaponId = weaponId, + WeaponAttachmentComboId = attachmentComboId, + HitLocationId = hitLocationId, + MeansOfDeathId = meansOfDeathId + }; + + try + { + /*if (state.UpdateCount > MaxUpdatesBeforePersist) + { + await UpdateClientStatistics(clientId); + state.UpdateCount = 0; + } + + state.UpdateCount++;*/ + state.Hits.Add(hitStat); + } + catch (Exception ex) + { + _logger.LogError(ex, "Could not add {statsName} for {id}", nameof(EFClientHitStatistic), + clientId); + state.Hits.Remove(hitStat); + } + finally + { + if (state.OnTransaction.CurrentCount == 0) + { + state.OnTransaction.Release(); + } + } + + return hitStat; + } + + private async Task GetOrAddHitLocation(string location, Reference.Game game) + { + var matchingLocation = (await _hitLocationCache + .FirstAsync(loc => loc.Name == location && loc.Game == game)); + + if (matchingLocation != null) + { + return matchingLocation; + } + + var hitLocation = new EFHitLocation() + { + Name = location, + Game = game + }; + + hitLocation = await _hitLocationCache.AddAsync(hitLocation); + + return hitLocation; + } + + private async Task GetOrAddWeapon(WeaponInfo weapon, Reference.Game game) + { + var matchingWeapon = (await _weaponCache + .FirstAsync(wep => wep.Name == weapon.Name && wep.Game == game)); + + if (matchingWeapon != null) + { + return matchingWeapon; + } + + matchingWeapon = new EFWeapon() + { + Name = weapon.Name, + Game = game + }; + + matchingWeapon = await _weaponCache.AddAsync(matchingWeapon); + + return matchingWeapon; + } + + private async Task GetOrAddAttachment(AttachmentInfo attachment, Reference.Game game) + { + var matchingAttachment = (await _attachmentCache + .FirstAsync(attach => attach.Name == attachment.Name && attach.Game == game)); + + if (matchingAttachment != null) + { + return matchingAttachment; + } + + matchingAttachment = new EFWeaponAttachment() + { + Name = attachment.Name, + Game = game + }; + + matchingAttachment = await _attachmentCache.AddAsync(matchingAttachment); + + return matchingAttachment; + } + + private async Task GetOrAddAttachmentCombo(EFWeaponAttachment[] attachments, + Reference.Game game) + { + if (!attachments.Any()) + { + return null; + } + + var allAttachments = attachments.ToList(); + + if (allAttachments.Count() < 3) + { + for (var i = allAttachments.Count(); i <= 3; i++) + { + allAttachments.Add(null); + } + } + + var matchingAttachmentCombo = (await _attachmentComboCache.FirstAsync(combo => + combo.Game == game + && combo.Attachment1Id == allAttachments[0].Id + && combo.Attachment2Id == allAttachments[1]?.Id + && combo.Attachment3Id == allAttachments[2]?.Id)); + + if (matchingAttachmentCombo != null) + { + return matchingAttachmentCombo; + } + + matchingAttachmentCombo = new EFWeaponAttachmentCombo() + { + Game = game, + Attachment1Id = (int) allAttachments[0].Id, + Attachment2Id = (int?) allAttachments[1]?.Id, + Attachment3Id = (int?) allAttachments[2]?.Id, + }; + + matchingAttachmentCombo = await _attachmentComboCache.AddAsync(matchingAttachmentCombo); + + return matchingAttachmentCombo; + } + + private async Task GetOrAddMeansOfDeath(string meansOfDeath, Reference.Game game) + { + var matchingMod = (await _modCache + .FirstAsync(mod => mod.Name == meansOfDeath && mod.Game == game)); + + if (matchingMod != null) + { + return matchingMod; + } + + var mod = new EFMeansOfDeath() + { + Name = meansOfDeath, + Game = game + }; + + mod = await _modCache.AddAsync(mod); + + return mod; + } + + private void HandleDisconnectCalculations(EFClient client, HitState state) + { + // todo: this not added to states fast connect/disconnect + var serverStats = state.Hits.FirstOrDefault(stat => + stat.ServerId == state.Server.ServerId && stat.WeaponId == null && + stat.WeaponAttachmentComboId == null && stat.HitLocationId == null && stat.MeansOfDeathId == null); + + if (serverStats == null) + { + _logger.LogWarning("No server hits were found for {serverId} on disconnect for {client}", + state.Server.ServerId, client.ToString()); + return; + } + + var aggregate = state.Hits.FirstOrDefault(stat => stat.WeaponId == null && + stat.WeaponAttachmentComboId == null && + stat.HitLocationId == null && + stat.MeansOfDeathId == null && + stat.ServerId == null); + + if (aggregate == null) + { + _logger.LogWarning("No aggregate found for {serverId} on disconnect for {client}", + state.Server.ServerId, client.ToString()); + return; + } + + var sessionScores = client.GetAdditionalProperty>(SessionScores); + + if (sessionScores == null) + { + _logger.LogWarning($"No session scores available for {client}"); + return; + } + + foreach (var stat in new[] {serverStats, aggregate}) + { + stat.Score ??= 0; + + if (sessionScores.Count == 0) + { + stat.Score += client.Score; + } + + else + { + stat.Score += sessionScores.Sum(item => item.Item1) + + (sessionScores.Last().Item1 == client.Score && + (DateTime.Now - sessionScores.Last().Item2).TotalMinutes < 1 + ? 0 + : client.Score); + } + } + } + } +} \ No newline at end of file diff --git a/Plugins/Stats/Client/HitInfoBuilder.cs b/Plugins/Stats/Client/HitInfoBuilder.cs new file mode 100644 index 000000000..e203b3b48 --- /dev/null +++ b/Plugins/Stats/Client/HitInfoBuilder.cs @@ -0,0 +1,64 @@ +using System; +using System.Linq; +using Data.Models; +using IW4MAdmin.Plugins.Stats.Client.Game; +using Microsoft.Extensions.Logging; +using SharedLibraryCore; +using SharedLibraryCore.Interfaces; +using Stats.Client.Abstractions; +using ILogger = Microsoft.Extensions.Logging.ILogger; + +namespace Stats.Client +{ + public class HitInfoBuilder : IHitInfoBuilder + { + private readonly IWeaponNameParser _weaponNameParser; + private readonly ILogger _logger; + private const int MaximumDamage = 1000; + + public HitInfoBuilder(ILogger logger, IWeaponNameParser weaponNameParser) + { + _weaponNameParser = weaponNameParser; + _logger = logger; + } + + public HitInfo Build(string[] log, int entityId, bool isSelf, bool isVictim, Server.Game gameName) + { + var eventType = log[(uint) ParserRegex.GroupType.EventType].First(); + HitType hitType; + + if (isVictim) + { + if (isSelf) + { + hitType = HitType.Suicide; + } + + else + { + hitType = eventType == 'D' ? HitType.WasDamaged : HitType.WasKilled; + } + } + + else + { + hitType = eventType == 'D' ? HitType.Damage : HitType.Kill; + } + + var hitInfo = new HitInfo() + { + EntityId = entityId, + IsVictim = isVictim, + HitType = hitType, + Damage = Math.Min(MaximumDamage, int.Parse(log[(uint) ParserRegex.GroupType.Damage])), + Location = log[(uint) ParserRegex.GroupType.HitLocation], + Weapon = _weaponNameParser.Parse(log[(uint) ParserRegex.GroupType.Weapon], gameName), + MeansOfDeath = log[(uint)ParserRegex.GroupType.MeansOfDeath], + Game = (Reference.Game)gameName + }; + + //_logger.LogDebug("Generated new hitInfo {@hitInfo}", hitInfo); + return hitInfo; + } + } +} \ No newline at end of file diff --git a/Plugins/Stats/Client/ServerDistributionCalculator.cs b/Plugins/Stats/Client/ServerDistributionCalculator.cs new file mode 100644 index 000000000..aa54753ff --- /dev/null +++ b/Plugins/Stats/Client/ServerDistributionCalculator.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Data.Abstractions; +using Data.Models.Client; +using Data.Models.Client.Stats; +using IW4MAdmin.Plugins.Stats; +using IW4MAdmin.Plugins.Stats.Config; +using Microsoft.EntityFrameworkCore; +using SharedLibraryCore; +using SharedLibraryCore.Interfaces; +using Stats.Client.Abstractions; +using Stats.Helpers; + +namespace Stats.Client +{ + public class ServerDistributionCalculator : IServerDistributionCalculator + { + private readonly IDatabaseContextFactory _contextFactory; + + private readonly IDataValueCache> + _distributionCache; + + private readonly IDataValueCache + _maxZScoreCache; + + private readonly IConfigurationHandler _configurationHandler; + private readonly List _serverIds = new List(); + + private const string DistributionCacheKey = nameof(DistributionCacheKey); + private const string MaxZScoreCacheKey = nameof(MaxZScoreCacheKey); + + public ServerDistributionCalculator(IDatabaseContextFactory contextFactory, + IDataValueCache> distributionCache, + IDataValueCache maxZScoreCache, + IConfigurationHandlerFactory configFactory) + { + _contextFactory = contextFactory; + _distributionCache = distributionCache; + _maxZScoreCache = maxZScoreCache; + _configurationHandler = configFactory.GetConfigurationHandler("StatsPluginSettings"); + } + + public async Task Initialize() + { + await LoadServers(); + _distributionCache.SetCacheItem((async set => + { + var validPlayTime = _configurationHandler.Configuration()?.TopPlayersMinPlayTime ?? 3600 * 3; + + var distributions = new Dictionary(); + + await LoadServers(); + + foreach (var serverId in _serverIds) + { + var performance = await set + .Where(s => s.ServerId == serverId) + .Where(s => s.Skill > 0) + .Where(s => s.EloRating > 0) + .Where(s => s.Client.Level != EFClient.Permission.Banned) + .Where(s => s.TimePlayed >= validPlayTime) + .Where(s => s.UpdatedAt >= Extensions.FifteenDaysAgo()) + .Select(s => s.EloRating * 1/3.0 + s.Skill * 2/3.0).ToListAsync(); + var distributionParams = performance.GenerateDistributionParameters(); + distributions.Add(serverId, distributionParams); + } + + return distributions; + }), DistributionCacheKey, Utilities.IsDevelopment ? TimeSpan.FromMinutes(5) : TimeSpan.FromHours(1)); + + _maxZScoreCache.SetCacheItem(async set => + { + var validPlayTime = _configurationHandler.Configuration()?.TopPlayersMinPlayTime ?? 3600 * 3; + + var zScore = await set + .Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(validPlayTime)) + .Where(s => s.Skill > 0) + .Where(s => s.EloRating > 0) + .MaxAsync(s => (double?)s.ZScore); + return zScore ?? 0; + }, MaxZScoreCacheKey, Utilities.IsDevelopment ? TimeSpan.FromMinutes(5) : TimeSpan.FromMinutes(30)); + + await _distributionCache.GetCacheItem(DistributionCacheKey); + await _maxZScoreCache.GetCacheItem(MaxZScoreCacheKey); + + /*foreach (var serverId in _serverIds) + { + await using var ctx = _contextFactory.CreateContext(enableTracking: true); + + var a = await ctx.Set() + .Where(s => s.ServerId == serverId) + //.Where(s=> s.ClientId == 216105) + .Where(s => s.Skill > 0) + .Where(s => s.EloRating > 0) + .Where(s => s.Client.Level != EFClient.Permission.Banned) + .Where(s => s.TimePlayed >= 3600 * 3) + .Where(s => s.UpdatedAt >= Extensions.FifteenDaysAgo()) + .ToListAsync(); + + var b = a.Distinct(); + + foreach (var item in b) + { + await Plugin.Manager.UpdateHistoricalRanking(item.ClientId, item, item.ServerId); + //item.ZScore = await GetZScoreForServer(serverId, item.Performance); + //item.UpdatedAt = DateTime.UtcNow; + } + + await ctx.SaveChangesAsync(); + }*/ + } + + private async Task LoadServers() + { + if (_serverIds.Count == 0) + { + await using var context = _contextFactory.CreateContext(false); + _serverIds.AddRange(await context.Servers + .Where(s => s.EndPoint != null && s.HostName != null) + .Select(s => s.ServerId) + .ToListAsync()); + } + } + + public async Task GetZScoreForServer(long serverId, double value) + { + var serverParams = await _distributionCache.GetCacheItem(DistributionCacheKey); + if (!serverParams.ContainsKey(serverId)) + { + return 0.0; + } + + var sdParams = serverParams[serverId]; + if (sdParams.Sigma == 0) + { + return 0.0; + } + var zScore = (Math.Log(value) - sdParams.Mean) / sdParams.Sigma; + return zScore; + } + + public async Task GetRatingForZScore(double? value) + { + var maxZScore = await _maxZScoreCache.GetCacheItem(MaxZScoreCacheKey); + return maxZScore == 0 ? 0 : value.GetRatingForZScore(maxZScore); + } + } +} \ No newline at end of file diff --git a/Plugins/Stats/Client/WeaponNameParser.cs b/Plugins/Stats/Client/WeaponNameParser.cs new file mode 100644 index 000000000..62e1503fa --- /dev/null +++ b/Plugins/Stats/Client/WeaponNameParser.cs @@ -0,0 +1,75 @@ +using Microsoft.Extensions.Logging; +using Stats.Client.Abstractions; +using Stats.Client.Game; +using System.Collections.Generic; +using System.Linq; +using IW4MAdmin.Plugins.Stats.Config; +using SharedLibraryCore; +using SharedLibraryCore.Interfaces; +using ILogger = Microsoft.Extensions.Logging.ILogger; + +namespace Stats.Client +{ + public class WeaponNameParser : IWeaponNameParser + { + private readonly ILogger _logger; + private readonly StatsConfiguration _config; + + public WeaponNameParser(ILogger logger, IConfigurationHandler config) + { + _logger = logger; + _config = config.Configuration(); + } + + public WeaponInfo Parse(string weaponName, Server.Game gameName) + { + var configForGame = _config.WeaponNameParserConfigurations + ?.FirstOrDefault(config => config.Game == gameName); + + if (configForGame == null) + { + _logger.LogWarning("No weapon parser config available for game {game}", gameName); + return new WeaponInfo() + { + Name = "Unknown" + }; + } + + var splitWeaponName = weaponName.Split(configForGame.Delimiters); + + if (!splitWeaponName.Any()) + { + _logger.LogError("Could not parse weapon name {weapon}", weaponName); + + return new WeaponInfo() + { + Name = "Unknown" + }; + } + + // remove the _mp suffix + var filtered = splitWeaponName.Where(part => part != configForGame.WeaponSuffix); + var baseName = splitWeaponName.First(); + var attachments = new List(); + + if (filtered.Count() > 1) + { + attachments.AddRange(filtered.Skip(1)); + } + + var weaponInfo = new WeaponInfo() + { + RawName = weaponName, + Name = baseName, + Attachments = attachments.Select(attachment => new AttachmentInfo() + { + Name = attachment + }).ToList() + }; + + // _logger.LogDebug("Parsed weapon info {@info}", weaponInfo); + + return weaponInfo; + } + } +} \ No newline at end of file diff --git a/Plugins/Stats/Commands/MostKillsCommand.cs b/Plugins/Stats/Commands/MostKillsCommand.cs index 71d081ccf..b6f34294d 100644 --- a/Plugins/Stats/Commands/MostKillsCommand.cs +++ b/Plugins/Stats/Commands/MostKillsCommand.cs @@ -3,8 +3,9 @@ using System; using System.Linq; using System.Threading.Tasks; using SharedLibraryCore; -using IW4MAdmin.Plugins.Stats.Models; using System.Collections.Generic; +using Data.Abstractions; +using Data.Models.Client.Stats; using SharedLibraryCore.Database.Models; using SharedLibraryCore.Configuration; using SharedLibraryCore.Interfaces; diff --git a/Plugins/Stats/Commands/MostPlayedCommand.cs b/Plugins/Stats/Commands/MostPlayedCommand.cs index 9ad777361..b93ebc28b 100644 --- a/Plugins/Stats/Commands/MostPlayedCommand.cs +++ b/Plugins/Stats/Commands/MostPlayedCommand.cs @@ -4,9 +4,9 @@ using System.Linq; using System.Threading.Tasks; using SharedLibraryCore; -using IW4MAdmin.Plugins.Stats.Models; -using SharedLibraryCore.Database; using System.Collections.Generic; +using Data.Abstractions; +using Data.Models.Client.Stats; using SharedLibraryCore.Database.Models; using IW4MAdmin.Plugins.Stats.Helpers; using SharedLibraryCore.Configuration; diff --git a/Plugins/Stats/Commands/ResetStats.cs b/Plugins/Stats/Commands/ResetStats.cs index 320b319c4..69395ceab 100644 --- a/Plugins/Stats/Commands/ResetStats.cs +++ b/Plugins/Stats/Commands/ResetStats.cs @@ -1,12 +1,12 @@ -using IW4MAdmin.Plugins.Stats.Models; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using SharedLibraryCore; using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database; using SharedLibraryCore.Database.Models; using SharedLibraryCore.Interfaces; using System.Linq; using System.Threading.Tasks; +using Data.Abstractions; +using Data.Models.Client.Stats; namespace IW4MAdmin.Plugins.Stats.Commands { diff --git a/Plugins/Stats/Commands/TopStats.cs b/Plugins/Stats/Commands/TopStats.cs index 19b33f999..81a9a05b3 100644 --- a/Plugins/Stats/Commands/TopStats.cs +++ b/Plugins/Stats/Commands/TopStats.cs @@ -4,9 +4,9 @@ using System.Linq; using System.Threading.Tasks; using SharedLibraryCore; -using IW4MAdmin.Plugins.Stats.Models; -using SharedLibraryCore.Database; using System.Collections.Generic; +using Data.Abstractions; +using Data.Models.Client.Stats; using SharedLibraryCore.Database.Models; using IW4MAdmin.Plugins.Stats.Helpers; using SharedLibraryCore.Configuration; @@ -16,7 +16,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands { class TopStats : Command { - public static async Task> GetTopStats(Server s, ITranslationLookup translationLookup, IDatabaseContextFactory contextFactory) + public static async Task> GetTopStats(Server s, ITranslationLookup translationLookup) { long serverId = StatManager.GetIdForServer(s); var topStatsText = new List() @@ -24,30 +24,8 @@ namespace IW4MAdmin.Plugins.Stats.Commands $"^5--{translationLookup["PLUGINS_STATS_COMMANDS_TOP_TEXT"]}--" }; - await using var context = contextFactory.CreateContext(false); - var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15); - int minPlayTime = Plugin.Config.Configuration().TopPlayersMinPlayTime; - - var iqStats = (from stats in context.Set() - join client in context.Clients - on stats.ClientId equals client.ClientId - join alias in context.Aliases - on client.CurrentAliasId equals alias.AliasId - where stats.ServerId == serverId - where stats.TimePlayed >= minPlayTime - where client.Level != EFClient.Permission.Banned - where client.LastConnection >= fifteenDaysAgo - orderby (stats.EloRating + stats.Skill) / 2.0d descending - select new - { - stats.KDR, - stats.Performance, - alias.Name - }) - .Take(5); - - var statsList = (await iqStats.ToListAsync()) - .Select(stats => $"^3{stats.Name}^7 - ^5{stats.KDR} ^7{translationLookup["PLUGINS_STATS_TEXT_KDR"]} | ^5{stats.Performance} ^7{translationLookup["PLUGINS_STATS_COMMANDS_PERFORMANCE"]}"); + var stats = await Plugin.Manager.GetTopStats(0, 5, serverId); + var statsList = stats.Select(stats => $"^3{stats.Name}^7 - ^5{stats.KDR} ^7{translationLookup["PLUGINS_STATS_TEXT_KDR"]} | ^5{stats.Performance} ^7{translationLookup["PLUGINS_STATS_COMMANDS_PERFORMANCE"]}"); topStatsText.AddRange(statsList); @@ -81,7 +59,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands public override async Task ExecuteAsync(GameEvent E) { - var topStats = await GetTopStats(E.Owner, _translationLookup, _contextFactory); + var topStats = await GetTopStats(E.Owner, _translationLookup); if (!E.Message.IsBroadcastCommand(_config.BroadcastCommandPrefix)) { foreach (var stat in topStats) diff --git a/Plugins/Stats/Commands/ViewStats.cs b/Plugins/Stats/Commands/ViewStats.cs index 2ace6997c..96c409f49 100644 --- a/Plugins/Stats/Commands/ViewStats.cs +++ b/Plugins/Stats/Commands/ViewStats.cs @@ -1,8 +1,8 @@ using SharedLibraryCore; -using IW4MAdmin.Plugins.Stats.Models; using System.Linq; using System.Threading.Tasks; -using SharedLibraryCore.Database; +using Data.Abstractions; +using Data.Models.Client.Stats; using Microsoft.EntityFrameworkCore; using IW4MAdmin.Plugins.Stats.Helpers; using SharedLibraryCore.Database.Models; @@ -56,7 +56,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands // getting stats for a particular client if (E.Target != null) { - var performanceRanking = await Plugin.Manager.GetClientOverallRanking(E.Target.ClientId); + var performanceRanking = await Plugin.Manager.GetClientOverallRanking(E.Target.ClientId, serverId); var performanceRankingString = performanceRanking == 0 ? _translationLookup["WEBFRONT_STATS_INDEX_UNRANKED"] : $"{_translationLookup["WEBFRONT_STATS_INDEX_RANKED"]} #{performanceRanking}"; @@ -84,7 +84,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands // getting self stats else { - var performanceRanking = await Plugin.Manager.GetClientOverallRanking(E.Origin.ClientId); + var performanceRanking = await Plugin.Manager.GetClientOverallRanking(E.Origin.ClientId, serverId); var performanceRankingString = performanceRanking == 0 ? _translationLookup["WEBFRONT_STATS_INDEX_UNRANKED"] : $"{_translationLookup["WEBFRONT_STATS_INDEX_RANKED"]} #{performanceRanking}"; diff --git a/Plugins/Stats/Config/StatsConfiguration.cs b/Plugins/Stats/Config/StatsConfiguration.cs index c4d091b3f..d14388223 100644 --- a/Plugins/Stats/Config/StatsConfiguration.cs +++ b/Plugins/Stats/Config/StatsConfiguration.cs @@ -9,16 +9,32 @@ namespace IW4MAdmin.Plugins.Stats.Config { public class StatsConfiguration : IBaseConfiguration { - [Obsolete] - public bool? EnableAntiCheat { get; set; } + [Obsolete] public bool? EnableAntiCheat { get; set; } public List KillstreakMessages { get; set; } public List DeathstreakMessages { get; set; } public int TopPlayersMinPlayTime { get; set; } public bool StoreClientKills { get; set; } public int MostKillsMaxInactivityDays { get; set; } = 30; public int MostKillsClientLimit { get; set; } = 5; - [Obsolete] - public IDictionary ServerDetectionTypes { get; set; } + public bool EnableAdvancedMetrics { get; set; } = true; + + public WeaponNameParserConfiguration[] WeaponNameParserConfigurations { get; set; } = new[] + { + new WeaponNameParserConfiguration() + { + Game = Server.Game.IW4, + WeaponSuffix = "mp", + Delimiters = new[] {'_'} + }, + new WeaponNameParserConfiguration() + { + Game = Server.Game.T6, + WeaponSuffix = "mp", + Delimiters = new[] {'_', '+'} + } + }; + + [Obsolete] public IDictionary ServerDetectionTypes { get; set; } public AnticheatConfiguration AnticheatConfiguration { get; set; } = new AnticheatConfiguration(); #pragma warning disable CS0612 // Type or member is obsolete @@ -41,41 +57,47 @@ namespace IW4MAdmin.Plugins.Stats.Config #pragma warning restore CS0612 // Type or member is obsolete public string Name() => "StatsPluginSettings"; + public IBaseConfiguration Generate() { - AnticheatConfiguration.Enable = Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_SETUP_ENABLEAC"]); + AnticheatConfiguration.Enable = + Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_SETUP_ENABLEAC"]); KillstreakMessages = new List() { - new StreakMessageConfiguration(){ + new StreakMessageConfiguration() + { Count = -1, Message = "Try not to kill yourself anymore" }, - new StreakMessageConfiguration() { - Count = 5, - Message = "Great job! You're on a ^55 killstreak!" - }, - new StreakMessageConfiguration() - { - Count = 10, - Message = "Amazing! ^510 kills ^7without dying!" - }, - new StreakMessageConfiguration(){ - Count = 25, - Message = "You better call in that nuke, ^525 killstreak^7!" - } + new StreakMessageConfiguration() + { + Count = 5, + Message = "Great job! You're on a ^55 killstreak!" + }, + new StreakMessageConfiguration() + { + Count = 10, + Message = "Amazing! ^510 kills ^7without dying!" + }, + new StreakMessageConfiguration() + { + Count = 25, + Message = "You better call in that nuke, ^525 killstreak^7!" + } }; DeathstreakMessages = new List() { - new StreakMessageConfiguration() - { - Count = 5, - Message = "Pick it up soldier, you've died ^55 times ^7in a row..." - }, - new StreakMessageConfiguration(){ - Count = 10, - Message = "Seriously? ^510 deaths ^7without getting a kill?" - }, + new StreakMessageConfiguration() + { + Count = 5, + Message = "Pick it up soldier, you've died ^55 times ^7in a row..." + }, + new StreakMessageConfiguration() + { + Count = 10, + Message = "Seriously? ^510 deaths ^7without getting a kill?" + }, }; TopPlayersMinPlayTime = 3600 * 3; @@ -84,4 +106,4 @@ namespace IW4MAdmin.Plugins.Stats.Config return this; } } -} +} \ No newline at end of file diff --git a/Plugins/Stats/Config/WeaponNameParserConfiguration.cs b/Plugins/Stats/Config/WeaponNameParserConfiguration.cs new file mode 100644 index 000000000..d824ad9ed --- /dev/null +++ b/Plugins/Stats/Config/WeaponNameParserConfiguration.cs @@ -0,0 +1,11 @@ +using SharedLibraryCore; + +namespace Stats.Config +{ + public class WeaponNameParserConfiguration + { + public Server.Game Game { get; set; } + public char[] Delimiters { get; set; } + public string WeaponSuffix { get; set; } + } +} \ No newline at end of file diff --git a/Plugins/Stats/Dtos/AdvancedStatsInfo.cs b/Plugins/Stats/Dtos/AdvancedStatsInfo.cs new file mode 100644 index 000000000..d12f64056 --- /dev/null +++ b/Plugins/Stats/Dtos/AdvancedStatsInfo.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Data.Models.Client; +using Data.Models.Client.Stats; +using SharedLibraryCore.Dtos; + +namespace Stats.Dtos +{ + public class AdvancedStatsInfo + { + public long? ServerId { get; set; } + public string ServerEndpoint { get; set; } + public string ClientName { get; set; } + public int ClientId { get; set; } + public EFClient.Permission Level { get; set; } + public double? Performance { get; set; } + public int? Ranking { get; set; } + public double? ZScore { get; set; } + public double? Rating { get; set; } + public List Servers { get; set; } + public List All { get; set; } + public EFClientHitStatistic Aggregate { get; set; } + public List ByHitLocation { get; set; } + public List ByWeapon { get; set; } + public List ByAttachmentCombo { get; set; } + public List Ratings { get; set; } + public List LegacyStats { get; set; } + } +} \ No newline at end of file diff --git a/Plugins/Stats/Dtos/StatsInfoRequest.cs b/Plugins/Stats/Dtos/StatsInfoRequest.cs index 5cca4e865..3340a06ff 100644 --- a/Plugins/Stats/Dtos/StatsInfoRequest.cs +++ b/Plugins/Stats/Dtos/StatsInfoRequest.cs @@ -6,5 +6,6 @@ /// client identifier /// public int? ClientId { get; set; } + public string ServerEndpoint { get; set; } } } diff --git a/Plugins/Stats/Dtos/TopStatsInfo.cs b/Plugins/Stats/Dtos/TopStatsInfo.cs index 22fa8a8f0..fdd357a12 100644 --- a/Plugins/Stats/Dtos/TopStatsInfo.cs +++ b/Plugins/Stats/Dtos/TopStatsInfo.cs @@ -11,12 +11,16 @@ namespace IW4MAdmin.Plugins.Stats.Web.Dtos public string Name { get; set; } public int ClientId { get; set; } public double KDR { get; set; } - public double Performance { get; set; } + public double? Performance { get; set; } public string TimePlayed { get; set; } + public TimeSpan TimePlayedValue { get; set; } public string LastSeen { get; set; } + public TimeSpan LastSeenValue { get; set; } public int Kills { get; set; } public int Deaths { get; set; } public int RatingChange { get; set; } public List PerformanceHistory { get; set; } + public double? ZScore { get; set; } + public long? ServerId { get; set; } } } diff --git a/Plugins/Stats/Extensions.cs b/Plugins/Stats/Extensions.cs new file mode 100644 index 000000000..22acfdc9a --- /dev/null +++ b/Plugins/Stats/Extensions.cs @@ -0,0 +1,196 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Data.Models.Client.Stats; +using Microsoft.EntityFrameworkCore.Internal; +using SharedLibraryCore.Dtos; +using Stats.Dtos; + +namespace IW4MAdmin.Plugins.Stats +{ + public static class Extensions + { + private const int ZScoreRange = 3; + private const int RankIconDivisions = 24; + private const int MaxMessages = 100; + + public class LogParams + { + public double Mean { get; set; } + public double Sigma { get; set; } + } + + public static DateTime FifteenDaysAgo() => DateTime.UtcNow.AddDays(-15); + + public static double? WeightValueByPlaytime(this IEnumerable stats, string propertyName, + int minTimePlayed, Func validation = null) + { + if (!stats.Any()) + { + return null; + } + + validation ??= (item) => item.Performance > 0 && item.TimePlayed >= minTimePlayed; + + var items = stats.Where(validation).ToList(); + var performancePlayTime = items.Sum(s => s.TimePlayed); + + var propInfo = typeof(EFClientStatistics).GetProperty(propertyName); + var weightedValues = items.Sum(item => + (double?) propInfo?.GetValue(item) * (item.TimePlayed / (double) performancePlayTime)); + return weightedValues.Equals(double.NaN) ? 0 : weightedValues ?? 0; + } + + public static LogParams GenerateDistributionParameters(this IEnumerable values) + { + if (!values.Any()) + { + return new LogParams() + { + Mean = 0, + Sigma = 0 + }; + } + + var ti = 0.0; + var ti2 = 0.0; + var n = 0L; + + foreach (var val in values) + { + var logVal = Math.Log(val); + ti += logVal * logVal; + ti2 += logVal; + n++; + if (n % 50 == 0) // this isn't ideal, but we want to reduce the amount of CPU usage that the + // loops takes so people don't complain + { + Thread.Sleep(1); + } + } + + var mean = ti2 / n; + ti2 *= ti2; + var bottom = n == 1 ? 1 : n * (n - 1); + var sigma = Math.Sqrt(((n * ti) - ti2) / bottom); + + return new LogParams() + { + Sigma = sigma, + Mean = mean + }; + } + + public static double? GetRatingForZScore(this double? zScore, double maxZScore) + { + const int ratingScalar = 1000; + + if (!zScore.HasValue) + { + return null; + } + + // we just want everything positive so we can go from 0-max + var adjustedZScore = zScore < -ZScoreRange ? 0 : zScore + ZScoreRange; + return adjustedZScore / (maxZScore + ZScoreRange) * ratingScalar; + } + + public static int RankIconIndexForZScore(this double? zScore) + { + if (zScore == null) + { + return 0; + } + + const double divisionIncrement = (ZScoreRange * 2) / (double) RankIconDivisions; + var rank = 1; + for (var i = rank; i <= RankIconDivisions; i++) + { + var bottom = Math.Round(-ZScoreRange + (i - 1) * divisionIncrement, 5); + var top = Math.Round(-ZScoreRange + i * divisionIncrement, 5); + + if (zScore > bottom && zScore <= top) + { + return rank; + } + + if (i == 1 && zScore < bottom // catch all for really bad players + // catch all for very good players + || i == RankIconDivisions && zScore > top) + { + return i; + } + + rank++; + } + + return 0; + } + + /// + /// todo: lets abstract this out to a generic buildable query + /// this is just a dirty PoC + /// + /// + /// + public static ChatSearchQuery ParseSearchInfo(this string query, int count, int offset) + { + string[] filters = query.Split('|'); + var searchRequest = new ChatSearchQuery + { + Filter = query, + Count = count, + Offset = offset + }; + + // sanity checks + searchRequest.Count = Math.Min(searchRequest.Count, MaxMessages); + searchRequest.Count = Math.Max(searchRequest.Count, 0); + searchRequest.Offset = Math.Max(searchRequest.Offset, 0); + + if (filters.Length > 1) + { + if (filters[0].ToLower() != "chat") + { + throw new ArgumentException("Query is not compatible with chat"); + } + + foreach (string filter in filters.Skip(1)) + { + string[] args = filter.Split(' '); + + if (args.Length > 1) + { + string recombinedArgs = string.Join(' ', args.Skip(1)); + switch (args[0].ToLower()) + { + case "before": + searchRequest.SentBefore = DateTime.Parse(recombinedArgs); + break; + case "after": + searchRequest.SentAfter = DateTime.Parse(recombinedArgs); + break; + case "server": + searchRequest.ServerId = args[1]; + break; + case "client": + searchRequest.ClientId = int.Parse(args[1]); + break; + case "contains": + searchRequest.MessageContains = string.Join(' ', args.Skip(1)); + break; + case "sort": + searchRequest.Direction = Enum.Parse(args[1], ignoreCase: true); + break; + } + } + } + + return searchRequest; + } + + throw new ArgumentException("No filters specified for chat search"); + } + } +} \ No newline at end of file diff --git a/Plugins/Stats/Helpers/AdvancedClientStatsResourceQueryHelper.cs b/Plugins/Stats/Helpers/AdvancedClientStatsResourceQueryHelper.cs new file mode 100644 index 000000000..571654251 --- /dev/null +++ b/Plugins/Stats/Helpers/AdvancedClientStatsResourceQueryHelper.cs @@ -0,0 +1,156 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Data.Abstractions; +using Data.Models.Client; +using Data.Models.Client.Stats; +using IW4MAdmin.Plugins.Stats; +using IW4MAdmin.Plugins.Stats.Config; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using SharedLibraryCore.Dtos; +using SharedLibraryCore.Helpers; +using SharedLibraryCore.Interfaces; +using Stats.Client.Abstractions; +using Stats.Dtos; +using ILogger = Microsoft.Extensions.Logging.ILogger; + +namespace Stats.Helpers +{ + public class AdvancedClientStatsResourceQueryHelper : IResourceQueryHelper + { + private readonly IDatabaseContextFactory _contextFactory; + private readonly ILogger _logger; + private readonly IManager _manager; + + public AdvancedClientStatsResourceQueryHelper(ILogger logger, + IDatabaseContextFactory contextFactory, IManager manager) + { + _contextFactory = contextFactory; + _logger = logger; + _manager = manager; + } + + public async Task> QueryResource(StatsInfoRequest query) + { + await using var context = _contextFactory.CreateContext(enableTracking: false); + + long? serverId = null; + + if (!string.IsNullOrEmpty(query.ServerEndpoint)) + { + serverId = (await context.Servers + .Select(server => new {server.EndPoint, server.Id}) + .FirstOrDefaultAsync(server => server.EndPoint == query.ServerEndpoint)) + ?.Id; + } + + var clientInfo = await context.Clients.Select(client => new + { + client.ClientId, + client.CurrentAlias.Name, + client.Level + }).FirstOrDefaultAsync(client => client.ClientId == query.ClientId); + + if (clientInfo == null) + { + return null; + } + + // gets all the hit stats for the client + var hitStats = await context.Set() + .Include(stat => stat.HitLocation) + .Include(stat => stat.MeansOfDeath) + .Include(stat => stat.Weapon) + .Include(stat => stat.WeaponAttachmentCombo) + .ThenInclude(attachment => attachment.Attachment1) + .Include(stat => stat.WeaponAttachmentCombo) + .ThenInclude(attachment => attachment.Attachment2) + .Include(stat => stat.WeaponAttachmentCombo) + .ThenInclude(attachment => attachment.Attachment3) + .Where(stat => stat.ClientId == query.ClientId) + .Where(stat => stat.ServerId == serverId) + .ToListAsync(); + + var ratings = await context.Set() + .Where(r => r.ClientId == clientInfo.ClientId) + .Where(r => r.ServerId == serverId) + .Where(r => r.Ranking != null) + .OrderByDescending(r => r.UpdatedDateTime) + .ToListAsync(); + + var mostRecentRanking = ratings.FirstOrDefault(ranking => ranking.Newest); + var ranking = mostRecentRanking?.Ranking + 1; + + // get stat for server, or all if no serverId + var legacyStats = await context.Set() + .Where(stat => stat.ClientId == query.ClientId) + .Where(stat => serverId == null || stat.ServerId == serverId) + .ToListAsync(); + + if (mostRecentRanking != null && mostRecentRanking.CreatedDateTime < Extensions.FifteenDaysAgo()) + { + ranking = 0; + } + + if (clientInfo.Level == EFClient.Permission.Banned) + { + ranking = null; + } + + var hitInfo = new AdvancedStatsInfo() + { + ServerId = serverId, + Performance = mostRecentRanking?.PerformanceMetric, + ZScore = mostRecentRanking?.ZScore, + ServerEndpoint = query.ServerEndpoint, + ClientName = clientInfo.Name, + ClientId = clientInfo.ClientId, + Level = clientInfo.Level, + Rating = mostRecentRanking?.PerformanceMetric, + All = hitStats, + Servers = _manager.GetServers() + .Select(server => new ServerInfo() + {Name = server.Hostname, IPAddress = server.IP, Port = server.Port}) + .ToList(), + Aggregate = hitStats.FirstOrDefault(hit => + hit.HitLocationId == null && hit.ServerId == serverId && hit.WeaponId == null && + hit.MeansOfDeathId == null), + ByHitLocation = hitStats + .Where(hit => hit.HitLocationId != null) + .Where(hit => hit.WeaponId == null) + .Where(hit => hit.WeaponAttachmentComboId == null) + .ToList(), + ByWeapon = hitStats + .Where(hit => hit.HitLocationId == null) + .Where(hit => hit.WeaponId != null) + .ToList(), + ByAttachmentCombo = hitStats + .Where(hit => hit.HitLocationId == null) + .Where(hit => hit.WeaponId != null) + .Where(hit => hit.WeaponAttachmentComboId != null) + .ToList(), + Ratings = ratings, + LegacyStats = legacyStats, + Ranking = ranking, + }; + + // todo: when nothign found + return new ResourceQueryHelperResult() + { + Results = new[] {hitInfo} + }; + } + + public static Expression> GetRankingFunc(int minPlayTime, double? zScore = null, + long? serverId = null) + { + return (stats) => (serverId == null || stats.ServerId == serverId) && + stats.UpdatedAt >= Extensions.FifteenDaysAgo() && + stats.Client.Level != EFClient.Permission.Banned && + stats.TimePlayed >= minPlayTime + && (zScore == null || stats.ZScore > zScore); + } + } +} \ No newline at end of file diff --git a/Plugins/Web/StatsWeb/ChatResourceQueryHelper.cs b/Plugins/Stats/Helpers/ChatResourceQueryHelper.cs similarity index 96% rename from Plugins/Web/StatsWeb/ChatResourceQueryHelper.cs rename to Plugins/Stats/Helpers/ChatResourceQueryHelper.cs index c8b484baa..4b871906b 100644 --- a/Plugins/Web/StatsWeb/ChatResourceQueryHelper.cs +++ b/Plugins/Stats/Helpers/ChatResourceQueryHelper.cs @@ -1,19 +1,21 @@ -using IW4MAdmin.Plugins.Stats.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Data.Abstractions; +using Data.Models.Client; +using Data.Models.Server; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; using SharedLibraryCore; using SharedLibraryCore.Configuration; using SharedLibraryCore.Dtos.Meta.Responses; using SharedLibraryCore.Helpers; using SharedLibraryCore.Interfaces; using Stats.Dtos; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; using ILogger = Microsoft.Extensions.Logging.ILogger; -namespace StatsWeb +namespace Stats.Helpers { /// /// implementation of IResourceQueryHelper @@ -81,7 +83,7 @@ namespace StatsWeb When = _message.TimeSent, Message = _message.Message, ServerName = query.IsProfileMeta ? "" : _message.Server.HostName, - GameName = _message.Server.GameName == null ? Server.Game.IW4 : _message.Server.GameName.Value, + GameName = _message.Server.GameName == null ? Server.Game.IW4 : (Server.Game)_message.Server.GameName.Value, SentIngame = _message.SentIngame }); diff --git a/Plugins/Stats/Helpers/ServerStats.cs b/Plugins/Stats/Helpers/ServerStats.cs index e1c9af66d..f9e25d837 100644 --- a/Plugins/Stats/Helpers/ServerStats.cs +++ b/Plugins/Stats/Helpers/ServerStats.cs @@ -1,12 +1,11 @@ -using IW4MAdmin.Plugins.Stats.Cheat; -using IW4MAdmin.Plugins.Stats.Models; -using SharedLibraryCore; -using SharedLibraryCore.Database.Models; +using SharedLibraryCore; using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; +using Data.Models.Client; +using Data.Models.Client.Stats; +using Data.Models.Server; namespace IW4MAdmin.Plugins.Stats.Helpers { @@ -39,9 +38,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers .Select(_c => _c.GetAdditionalProperty(StatManager.CLIENT_STATS_KEY)) .Where(_c => _c != null); - if (PlayerStats.Count(p => p.Team == IW4Info.Team.None) / (double)PlayerStats.Count() <= 0.25) + if (PlayerStats.Count(p => p.Team == (int)IW4Info.Team.None) / (double)PlayerStats.Count() <= 0.25) { - return IsTeamBased ? Math.Max(PlayerStats.Count(p => p.Team == teamName), 1) : Math.Max(PlayerStats.Count() - 1, 1); + return IsTeamBased ? Math.Max(PlayerStats.Count(p => p.Team == (int)teamName), 1) : Math.Max(PlayerStats.Count() - 1, 1); } else diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs index b9df54b9f..312d145e9 100644 --- a/Plugins/Stats/Helpers/StatManager.cs +++ b/Plugins/Stats/Helpers/StatManager.cs @@ -1,10 +1,8 @@ using IW4MAdmin.Plugins.Stats.Cheat; using IW4MAdmin.Plugins.Stats.Config; -using IW4MAdmin.Plugins.Stats.Models; using IW4MAdmin.Plugins.Stats.Web.Dtos; using Microsoft.EntityFrameworkCore; using SharedLibraryCore; -using SharedLibraryCore.Database.Models; using SharedLibraryCore.Helpers; using SharedLibraryCore.Interfaces; using System; @@ -14,8 +12,18 @@ using System.Linq; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; +using Data.Abstractions; +using Data.Context; +using Data.Models; +using Data.Models.Client; +using Data.Models.Client.Stats; +using Data.Models.Server; +using Humanizer.Localisation; using Microsoft.Extensions.Logging; +using Stats.Client.Abstractions; +using Stats.Helpers; using static IW4MAdmin.Plugins.Stats.Cheat.Detection; +using EFClient = SharedLibraryCore.Database.Models.EFClient; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace IW4MAdmin.Plugins.Stats.Helpers @@ -30,21 +38,25 @@ namespace IW4MAdmin.Plugins.Stats.Helpers private static List serverModels; public static string CLIENT_STATS_KEY = "ClientStats"; public static string CLIENT_DETECTIONS_KEY = "ClientDetections"; - private readonly SemaphoreSlim _addPlayerWaiter = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim _addPlayerWaiter = new SemaphoreSlim(1, 1); + private readonly IServerDistributionCalculator _serverDistributionCalculator; - public StatManager(ILogger logger, IManager mgr, IDatabaseContextFactory contextFactory, IConfigurationHandler configHandler) + public StatManager(ILogger logger, IManager mgr, IDatabaseContextFactory contextFactory, + IConfigurationHandler configHandler, + IServerDistributionCalculator serverDistributionCalculator) { _servers = new ConcurrentDictionary(); _log = logger; _contextFactory = contextFactory; _configHandler = configHandler; + _serverDistributionCalculator = serverDistributionCalculator; } ~StatManager() { _addPlayerWaiter.Dispose(); } - + private void SetupServerIds() { using var ctx = _contextFactory.CreateContext(enableTracking: false); @@ -55,10 +67,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers { var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15); return (r) => r.ServerId == serverId && - r.When > fifteenDaysAgo && - r.RatingHistory.Client.Level != EFClient.Permission.Banned && - r.Newest && - r.ActivityAmount >= _configHandler.Configuration().TopPlayersMinPlayTime; + r.When > fifteenDaysAgo && + r.RatingHistory.Client.Level != EFClient.Permission.Banned && + r.Newest && + r.ActivityAmount >= _configHandler.Configuration().TopPlayersMinPlayTime; } /// @@ -66,13 +78,23 @@ namespace IW4MAdmin.Plugins.Stats.Helpers /// /// client id of the player /// - public async Task GetClientOverallRanking(int clientId) + public async Task GetClientOverallRanking(int clientId, long? serverId = null) { await using var context = _contextFactory.CreateContext(enableTracking: false); + + if (_configHandler.Configuration().EnableAdvancedMetrics) + { + var clientRanking = await context.Set() + .Where(r => r.ClientId == clientId) + .Where(r => r.ServerId == serverId) + .Where(r => r.Newest) + .FirstOrDefaultAsync(); + return clientRanking?.Ranking + 1 ?? 0; + } var clientPerformance = await context.Set() .Where(r => r.RatingHistory.ClientId == clientId) - .Where(r => r.ServerId == null) + .Where(r => r.ServerId == serverId) .Where(r => r.Newest) .Select(r => r.Performance) .FirstOrDefaultAsync(); @@ -90,25 +112,115 @@ namespace IW4MAdmin.Plugins.Stats.Helpers return 0; } + public async Task> GetNewTopStats(int start, int count, long? serverId = null) + { + await using var context = _contextFactory.CreateContext(false); + + var clientIdsList = await context.Set() + .Where(ranking => ranking.ServerId == serverId) + .Where(ranking => ranking.Client.Level != Data.Models.Client.EFClient.Permission.Banned) + .Where(ranking => ranking.Client.LastConnection >= Extensions.FifteenDaysAgo()) + .Where(ranking => ranking.ZScore != null) + .Where(ranking => ranking.PerformanceMetric != null) + .Where(ranking => ranking.Newest) + .Where(ranking => + ranking.Client.TotalConnectionTime >= _configHandler.Configuration().TopPlayersMinPlayTime) + .OrderByDescending(ranking => ranking.PerformanceMetric) + .Select(ranking => ranking.ClientId) + .Skip(start) + .Take(count) + .ToListAsync(); + + var rankings = await context.Set() + .Where(ranking => clientIdsList.Contains(ranking.ClientId)) + .Where(ranking => ranking.ServerId == serverId) + .Select(ranking => new + { + ranking.ClientId, + ranking.Client.CurrentAlias.Name, + ranking.Client.LastConnection, + ranking.PerformanceMetric, + ranking.ZScore, + ranking.Ranking, + ranking.CreatedDateTime + }) + .ToListAsync(); + + var rankingsDict = rankings.GroupBy(rank => rank.ClientId) + .ToDictionary(rank => rank.Key, rank => rank.OrderBy(r => r.CreatedDateTime).ToList()); + + var statsInfo = await context.Set() + .Where(stat => clientIdsList.Contains(stat.ClientId)) + .Where(stat => stat.TimePlayed > 0) + .Where(stat => stat.Kills > 0 || stat.Deaths > 0) + .Where(stat => serverId == null || stat.ServerId == serverId) + .GroupBy(stat => stat.ClientId) + .Select(s => new + { + ClientId = s.Key, + Kills = s.Sum(c => c.Kills), + Deaths = s.Sum(c => c.Deaths), + KDR = s.Sum(c => (c.Kills / (double) (c.Deaths == 0 ? 1 : c.Deaths)) * c.TimePlayed) / + s.Sum(c => c.TimePlayed), + TotalTimePlayed = s.Sum(c => c.TimePlayed), + }) + .ToListAsync(); + + var finished = statsInfo + .OrderByDescending(stat => rankingsDict[stat.ClientId].Last().PerformanceMetric) + .Select((s, index) => new TopStatsInfo() + { + ClientId = s.ClientId, + Id = (int?) serverId ?? 0, + Deaths = s.Deaths, + Kills = s.Kills, + KDR = Math.Round(s.KDR, 2), + LastSeen = (DateTime.UtcNow - rankingsDict[s.ClientId].First().LastConnection) + .HumanizeForCurrentCulture(1, TimeUnit.Week, TimeUnit.Second, ",", false), + LastSeenValue = (DateTime.UtcNow - rankingsDict[s.ClientId].First().LastConnection), + Name = rankingsDict[s.ClientId].First().Name, + Performance = Math.Round(rankingsDict[s.ClientId].Last().PerformanceMetric ?? 0, 2), + RatingChange = (rankingsDict[s.ClientId].First().Ranking - + rankingsDict[s.ClientId].Last().Ranking) ?? 0, + PerformanceHistory = rankingsDict[s.ClientId].Select(ranking => ranking.PerformanceMetric ?? 0).ToList(), + TimePlayed = Math.Round(s.TotalTimePlayed / 3600.0, 1).ToString("#,##0"), + TimePlayedValue = TimeSpan.FromSeconds(s.TotalTimePlayed), + Ranking = index + start + 1, + ZScore = rankingsDict[s.ClientId].Last().ZScore, + ServerId = serverId + }) + .OrderBy(r => r.Ranking) + .ToList(); + + return finished; + } + public async Task> GetTopStats(int start, int count, long? serverId = null) { + if (_configHandler.Configuration().EnableAdvancedMetrics) + { + return await GetNewTopStats(start, count, serverId); + } + await using var context = _contextFactory.CreateContext(enableTracking: false); // setup the query for the clients within the given rating range var iqClientRatings = (from rating in context.Set() - .Where(GetRankingFunc(serverId)) - select new - { - rating.RatingHistory.ClientId, - rating.RatingHistory.Client.CurrentAlias.Name, - rating.RatingHistory.Client.LastConnection, - rating.Performance, - }) + .Where(GetRankingFunc(serverId)) + select new + { + rating.RatingHistory.ClientId, + rating.RatingHistory.Client.CurrentAlias.Name, + rating.RatingHistory.Client.LastConnection, + rating.Performance, + }) .OrderByDescending(c => c.Performance) .Skip(start) .Take(count); // materialized list - var clientRatings = await iqClientRatings.ToListAsync(); + var clientRatings = (await iqClientRatings.ToListAsync()) + .GroupBy(rating => rating.ClientId) // prevent duplicate keys + .Select(group => group.FirstOrDefault()); // get all the unique client ids that are in the top stats var clientIds = clientRatings @@ -117,59 +229,67 @@ namespace IW4MAdmin.Plugins.Stats.Helpers .ToList(); var iqRatingInfo = from rating in context.Set() - where clientIds.Contains(rating.RatingHistory.ClientId) - where rating.ServerId == serverId - select new - { - rating.Ranking, - rating.Performance, - rating.RatingHistory.ClientId, - rating.When - }; + where clientIds.Contains(rating.RatingHistory.ClientId) + where rating.ServerId == serverId + select new + { + rating.Ranking, + rating.Performance, + rating.RatingHistory.ClientId, + rating.When + }; var ratingInfo = (await iqRatingInfo.ToListAsync()) .GroupBy(r => r.ClientId) .Select(grp => new { grp.Key, - Ratings = grp.Select(r => new { r.Performance, r.Ranking, r.When }) + Ratings = grp.Select(r => new {r.Performance, r.Ranking, r.When}) }); var iqStatsInfo = (from stat in context.Set() - where clientIds.Contains(stat.ClientId) - where stat.Kills > 0 || stat.Deaths > 0 - where serverId == null ? true : stat.ServerId == serverId - group stat by stat.ClientId into s - select new - { - ClientId = s.Key, - Kills = s.Sum(c => c.Kills), - Deaths = s.Sum(c => c.Deaths), - KDR = s.Sum(c => (c.Kills / (double)(c.Deaths == 0 ? 1 : c.Deaths)) * c.TimePlayed) / s.Sum(c => c.TimePlayed), - TotalTimePlayed = s.Sum(c => c.TimePlayed), - }); + where clientIds.Contains(stat.ClientId) + where stat.Kills > 0 || stat.Deaths > 0 + where serverId == null || stat.ServerId == serverId + group stat by stat.ClientId + into s + select new + { + ClientId = s.Key, + Kills = s.Sum(c => c.Kills), + Deaths = s.Sum(c => c.Deaths), + KDR = s.Sum(c => (c.Kills / (double) (c.Deaths == 0 ? 1 : c.Deaths)) * c.TimePlayed) / + s.Sum(c => c.TimePlayed), + TotalTimePlayed = s.Sum(c => c.TimePlayed), + }); var topPlayers = await iqStatsInfo.ToListAsync(); var clientRatingsDict = clientRatings.ToDictionary(r => r.ClientId); var finished = topPlayers.Select(s => new TopStatsInfo() - { - ClientId = s.ClientId, - Id = (int?)serverId ?? 0, - Deaths = s.Deaths, - Kills = s.Kills, - KDR = Math.Round(s.KDR, 2), - LastSeen = (DateTime.UtcNow - clientRatingsDict[s.ClientId].LastConnection).HumanizeForCurrentCulture(), - Name = clientRatingsDict[s.ClientId].Name, - Performance = Math.Round(clientRatingsDict[s.ClientId].Performance, 2), - RatingChange = ratingInfo.First(r => r.Key == s.ClientId).Ratings.First().Ranking - ratingInfo.First(r => r.Key == s.ClientId).Ratings.Last().Ranking, - PerformanceHistory = ratingInfo.First(r => r.Key == s.ClientId).Ratings.Count() > 1 ? - ratingInfo.First(r => r.Key == s.ClientId).Ratings.OrderBy(r => r.When).Select(r => r.Performance).ToList() : - new List() { clientRatingsDict[s.ClientId].Performance, clientRatingsDict[s.ClientId].Performance }, - TimePlayed = Math.Round(s.TotalTimePlayed / 3600.0, 1).ToString("#,##0"), - }) - .OrderByDescending(r => r.Performance) - .ToList(); + { + ClientId = s.ClientId, + Id = (int?) serverId ?? 0, + Deaths = s.Deaths, + Kills = s.Kills, + KDR = Math.Round(s.KDR, 2), + LastSeen = (DateTime.UtcNow - clientRatingsDict[s.ClientId].LastConnection) + .HumanizeForCurrentCulture(), + LastSeenValue = DateTime.UtcNow - clientRatingsDict[s.ClientId].LastConnection, + Name = clientRatingsDict[s.ClientId].Name, + Performance = Math.Round(clientRatingsDict[s.ClientId].Performance, 2), + RatingChange = ratingInfo.First(r => r.Key == s.ClientId).Ratings.First().Ranking - + ratingInfo.First(r => r.Key == s.ClientId).Ratings.Last().Ranking, + PerformanceHistory = ratingInfo.First(r => r.Key == s.ClientId).Ratings.Count() > 1 + ? ratingInfo.First(r => r.Key == s.ClientId).Ratings.OrderBy(r => r.When) + .Select(r => r.Performance).ToList() + : new List() + {clientRatingsDict[s.ClientId].Performance, clientRatingsDict[s.ClientId].Performance}, + TimePlayed = Math.Round(s.TotalTimePlayed / 3600.0, 1).ToString("#,##0"), + TimePlayedValue = TimeSpan.FromSeconds(s.TotalTimePlayed) + }) + .OrderByDescending(r => r.Performance) + .ToList(); // set the ranking numerically int i = start + 1; @@ -226,7 +346,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers Port = sv.Port, EndPoint = sv.ToString(), ServerId = serverId, - GameName = sv.GameName, + GameName = (Reference.Game?) sv.GameName, HostName = sv.Hostname }; @@ -236,9 +356,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers } // we want to set the gamename up if it's never been set, or it changed - else if (!server.GameName.HasValue || server.GameName.HasValue && server.GameName.Value != sv.GameName) + else if (!server.GameName.HasValue || server.GameName.Value != (Reference.Game) sv.GameName) { - server.GameName = sv.GameName; + server.GameName = (Reference.Game) sv.GameName; ctx.Entry(server).Property(_prop => _prop.GameName).IsModified = true; ctx.SaveChanges(); } @@ -265,7 +385,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers catch (Exception e) { - _log.LogError(e, "{message}", Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_ERROR_ADD"]); + _log.LogError(e, "{message}", + Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_ERROR_ADD"]); } } @@ -277,7 +398,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers public async Task AddPlayer(EFClient pl) { var existingStats = pl.GetAdditionalProperty(CLIENT_STATS_KEY); - + if (existingStats != null) { return existingStats; @@ -322,7 +443,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers { Active = true, HitCount = 0, - Location = hl + Location = (int) hl }).ToList() }; @@ -342,7 +463,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers { Active = true, HitCount = 0, - Location = hl + Location = (int) hl }) .ToList(); @@ -404,7 +525,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers return; } - long serverId = GetIdForServer(pl.CurrentServer); + var serverId = GetIdForServer(pl.CurrentServer); var serverStats = _servers[serverId].ServerStatistics; // get individual client's stats @@ -414,9 +535,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers { clientStats = UpdateStats(clientStats); await SaveClientStats(clientStats); + if (_configHandler.Configuration().EnableAdvancedMetrics) + { + await UpdateHistoricalRanking(pl.ClientId, clientStats, serverId); + } // increment the total play time serverStats.TotalPlayTime += pl.ConnectionLength; + pl.SetAdditionalProperty(CLIENT_STATS_KEY, null); } else @@ -440,8 +566,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers /// Process stats for kill event /// /// - public async Task AddScriptHit(bool isDamage, DateTime time, EFClient attacker, EFClient victim, long serverId, string map, string hitLoc, string type, - string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads, + public async Task AddScriptHit(bool isDamage, DateTime time, EFClient attacker, EFClient victim, long serverId, + string map, string hitLoc, string type, + string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, + string isKillstreakKill, string Ads, string fraction, string visibilityPercentage, string snapAngles, string isAlive, string lastAttackTime) { Vector3 vDeathOrigin = null; @@ -478,25 +606,26 @@ namespace IW4MAdmin.Plugins.Stats.Helpers ServerId = serverId, DeathOrigin = vDeathOrigin, KillOrigin = vKillOrigin, - DeathType = ParseEnum.Get(type, typeof(IW4Info.MeansOfDeath)), + DeathType = (int) ParseEnum.Get(type, typeof(IW4Info.MeansOfDeath)), Damage = int.Parse(damage), - HitLoc = ParseEnum.Get(hitLoc, typeof(IW4Info.HitLocation)), - Weapon = ParseEnum.Get(weapon, typeof(IW4Info.WeaponName)), + HitLoc = (int) ParseEnum.Get(hitLoc, typeof(IW4Info.HitLocation)), + Weapon = (int) ParseEnum.Get(weapon, typeof(IW4Info.WeaponName)), ViewAngles = vViewAngles, TimeOffset = long.Parse(offset), When = time, IsKillstreakKill = isKillstreakKill[0] != '0', AdsPercent = float.Parse(Ads, System.Globalization.CultureInfo.InvariantCulture), Fraction = double.Parse(fraction, System.Globalization.CultureInfo.InvariantCulture), - VisibilityPercentage = double.Parse(visibilityPercentage, System.Globalization.CultureInfo.InvariantCulture), + VisibilityPercentage = double.Parse(visibilityPercentage, + System.Globalization.CultureInfo.InvariantCulture), IsKill = !isDamage, AnglesList = snapshotAngles, IsAlive = isAlive == "1", TimeSinceLastAttack = long.Parse(lastAttackTime), - GameName = attacker.CurrentServer.GameName + GameName = (int) attacker.CurrentServer.GameName }; - if (hit.HitLoc == IW4Info.HitLocation.shield) + if (hit.HitLoc == (int) IW4Info.HitLocation.shield) { // we don't care about shield hits return; @@ -509,9 +638,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers await waiter.WaitAsync(Utilities.DefaultCommandTimeout, Plugin.ServerManager.CancellationToken); // increment their hit count - if (hit.DeathType == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET || - hit.DeathType == IW4Info.MeansOfDeath.MOD_RIFLE_BULLET || - hit.DeathType == IW4Info.MeansOfDeath.MOD_HEAD_SHOT) + if (hit.DeathType == (int) IW4Info.MeansOfDeath.MOD_PISTOL_BULLET || + hit.DeathType == (int) IW4Info.MeansOfDeath.MOD_RIFLE_BULLET || + hit.DeathType == (int) IW4Info.MeansOfDeath.MOD_HEAD_SHOT) { clientStats.HitLocations.First(hl => hl.Location == hit.HitLoc).HitCount += 1; } @@ -550,7 +679,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers } } - if (Plugin.Config.Configuration().AnticheatConfiguration.Enable && !attacker.IsBot && attacker.ClientId != victim.ClientId) + if (Plugin.Config.Configuration().AnticheatConfiguration.Enable && !attacker.IsBot && + attacker.ClientId != victim.ClientId) { clientDetection.TrackedHits.Add(hit); @@ -573,7 +703,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers await ApplyPenalty(result, attacker); } - if (clientDetection.Tracker.HasChanges && result.ClientPenalty != EFPenalty.PenaltyType.Any) + if (clientDetection.Tracker.HasChanges && + result.ClientPenalty != EFPenalty.PenaltyType.Any) { await SaveTrackedSnapshots(clientDetection); @@ -590,10 +721,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers } } - catch (TaskCanceledException) { } + catch (TaskCanceledException) + { + } catch (Exception ex) { - _log.LogError(ex, "Could not save hit or anti-cheat info {@attacker} {@victim} {server}", attacker, victim, serverId); + _log.LogError(ex, "Could not save hit or anti-cheat info {@attacker} {@victim} {server}", attacker, + victim, serverId); } finally @@ -605,16 +739,17 @@ namespace IW4MAdmin.Plugins.Stats.Helpers } } - private DetectionPenaltyResult DeterminePenaltyResult(IEnumerable results, EFClient client) + private DetectionPenaltyResult DeterminePenaltyResult(IEnumerable results, + EFClient client) { // allow disabling of certain detection types results = results.Where(_result => ShouldUseDetection(client.CurrentServer, _result.Type, client.ClientId)); return results.FirstOrDefault(_result => _result.ClientPenalty == EFPenalty.PenaltyType.Ban) ?? - results.FirstOrDefault(_result => _result.ClientPenalty == EFPenalty.PenaltyType.Flag) ?? - new DetectionPenaltyResult() - { - ClientPenalty = EFPenalty.PenaltyType.Any, - }; + results.FirstOrDefault(_result => _result.ClientPenalty == EFPenalty.PenaltyType.Flag) ?? + new DetectionPenaltyResult() + { + ClientPenalty = EFPenalty.PenaltyType.Any, + }; } public async Task SaveHitCache(long serverId) @@ -647,7 +782,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers catch (KeyNotFoundException) { - } return true; @@ -668,13 +802,16 @@ namespace IW4MAdmin.Plugins.Stats.Helpers { new EFPenalty() { - AutomatedOffense = penalty.Type == Detection.DetectionType.Bone ? - $"{penalty.Type}-{(int)penalty.Location}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}" : - $"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}", + AutomatedOffense = penalty.Type == Detection.DetectionType.Bone + ? $"{penalty.Type}-{(int) penalty.Location}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}" + : $"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}", } }; - await attacker.Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], penaltyClient, false).WaitAsync(Utilities.DefaultCommandTimeout, attacker.CurrentServer.Manager.CancellationToken); + await attacker + .Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], + penaltyClient, false).WaitAsync(Utilities.DefaultCommandTimeout, + attacker.CurrentServer.Manager.CancellationToken); break; case EFPenalty.PenaltyType.Flag: if (attacker.Level != EFClient.Permission.User) @@ -682,9 +819,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers break; } - string flagReason = penalty.Type == Cheat.Detection.DetectionType.Bone ? - $"{penalty.Type}-{(int)penalty.Location}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}" : - $"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}"; + string flagReason = penalty.Type == Cheat.Detection.DetectionType.Bone + ? $"{penalty.Type}-{(int) penalty.Location}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}" + : $"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}"; penaltyClient.AdministeredPenalties = new List() { @@ -694,7 +831,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers } }; - await attacker.Flag(flagReason, penaltyClient, new TimeSpan(168, 0, 0)).WaitAsync(Utilities.DefaultCommandTimeout, attacker.CurrentServer.Manager.CancellationToken); + await attacker.Flag(flagReason, penaltyClient, new TimeSpan(168, 0, 0)) + .WaitAsync(Utilities.DefaultCommandTimeout, attacker.CurrentServer.Manager.CancellationToken); break; } } @@ -708,6 +846,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers { ctx.Add(change); } + await ctx.SaveChangesAsync(); } @@ -743,9 +882,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers victimStats.LastScore = victim.Score; // show encouragement/discouragement - string streakMessage = (attackerStats.ClientId != victimStats.ClientId) ? - StreakMessage.MessageOnStreak(attackerStats.KillStreak, attackerStats.DeathStreak) : - StreakMessage.MessageOnStreak(-1, -1); + string streakMessage = (attackerStats.ClientId != victimStats.ClientId) + ? StreakMessage.MessageOnStreak(attackerStats.KillStreak, attackerStats.DeathStreak) + : StreakMessage.MessageOnStreak(-1, -1); if (streakMessage != string.Empty) { @@ -768,15 +907,26 @@ namespace IW4MAdmin.Plugins.Stats.Helpers } // update their performance - if ((DateTime.UtcNow - attackerStats.LastStatHistoryUpdate).TotalMinutes >= 2.5) + if ((DateTime.UtcNow - attackerStats.LastStatHistoryUpdate).TotalMinutes >= + (Utilities.IsDevelopment ? 0.5 : _configHandler.Configuration().EnableAdvancedMetrics ? 10.0 : 2.5)) { try { // kill event is not designated as blocking, so we should be able to enter and exit // we need to make this thread safe because we can potentially have kills qualify // for stat history update, but one is already processing that invalidates the original - await attackerStats.ProcessingHit.WaitAsync(Utilities.DefaultCommandTimeout, Plugin.ServerManager.CancellationToken); - await UpdateStatHistory(attacker, attackerStats); + await attackerStats.ProcessingHit.WaitAsync(Utilities.DefaultCommandTimeout, + Plugin.ServerManager.CancellationToken); + if (_configHandler.Configuration().EnableAdvancedMetrics) + { + await UpdateHistoricalRanking(attacker.ClientId, attackerStats, serverId); + } + + else + { + await UpdateStatHistory(attacker, attackerStats); + } + attackerStats.LastStatHistoryUpdate = DateTime.UtcNow; } @@ -796,14 +946,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers } /// - /// Update the invidual and average stat history for a client + /// Update the individual and average stat history for a client /// /// client to update /// stats of client that is being updated /// public async Task UpdateStatHistory(EFClient client, EFClientStatistics clientStats) { - int currentSessionTime = (int)(DateTime.UtcNow - client.LastConnection).TotalSeconds; + int currentSessionTime = (int) (DateTime.UtcNow - client.LastConnection).TotalSeconds; // don't update their stat history if they haven't played long if (currentSessionTime < 60) @@ -816,18 +966,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers await using var ctx = _contextFactory.CreateContext(enableTracking: true); // select the rating history for client var iqHistoryLink = from history in ctx.Set() - .Include(h => h.Ratings) - where history.ClientId == client.ClientId - select history; + .Include(h => h.Ratings) + where history.ClientId == client.ClientId + select history; // get the client ratings var clientHistory = await iqHistoryLink .FirstOrDefaultAsync() ?? new EFClientRatingHistory() - { - Active = true, - ClientId = client.ClientId, - Ratings = new List() - }; + { + Active = true, + ClientId = client.ClientId, + Ratings = new List() + }; // it's the first time they've played if (clientHistory.RatingHistoryId == 0) @@ -836,13 +986,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers } #region INDIVIDUAL_SERVER_PERFORMANCE + // get the client ranking for the current server int individualClientRanking = await ctx.Set() - .Where(GetRankingFunc(clientStats.ServerId)) - // ignore themselves in the query - .Where(c => c.RatingHistory.ClientId != client.ClientId) - .Where(c => c.Performance > clientStats.Performance) - .CountAsync() + 1; + .Where(GetRankingFunc(clientStats.ServerId)) + // ignore themselves in the query + .Where(c => c.RatingHistory.ClientId != client.ClientId) + .Where(c => c.Performance > clientStats.Performance) + .CountAsync() + 1; // limit max history per server to 40 if (clientHistory.Ratings.Count(r => r.ServerId == clientStats.ServerId) >= 40) @@ -887,16 +1038,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers ctx.Add(newServerRating); #endregion + #region OVERALL_RATING + // select all performance & time played for current client var iqClientStats = from stats in ctx.Set() - where stats.ClientId == client.ClientId - where stats.ServerId != clientStats.ServerId - select new - { - stats.Performance, - stats.TimePlayed - }; + where stats.ClientId == client.ClientId + where stats.ServerId != clientStats.ServerId + select new + { + stats.Performance, + stats.TimePlayed + }; var clientStatsList = await iqClientStats.ToListAsync(); @@ -908,7 +1061,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers }); // weight the overall performance based on play time - double performanceAverage = clientStatsList.Sum(p => (p.Performance * p.TimePlayed)) / clientStatsList.Sum(p => p.TimePlayed); + double performanceAverage = clientStatsList.Sum(p => (p.Performance * p.TimePlayed)) / + clientStatsList.Sum(p => p.TimePlayed); // shouldn't happen but just in case the sum of time played is 0 if (double.IsNaN(performanceAverage)) @@ -917,10 +1071,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers } int overallClientRanking = await ctx.Set() - .Where(GetRankingFunc()) - .Where(r => r.RatingHistory.ClientId != client.ClientId) - .Where(r => r.Performance > performanceAverage) - .CountAsync() + 1; + .Where(GetRankingFunc()) + .Where(r => r.RatingHistory.ClientId != client.ClientId) + .Where(r => r.Performance > performanceAverage) + .CountAsync() + 1; // limit max average history to 40 if (clientHistory.Ratings.Count(r => r.ServerId == null) >= 40) @@ -962,11 +1116,112 @@ namespace IW4MAdmin.Plugins.Stats.Helpers }; ctx.Add(averageRating); + #endregion await ctx.SaveChangesAsync(); } + public async Task UpdateHistoricalRanking(int clientId, EFClientStatistics clientStats, long serverId) + { + await using var context = _contextFactory.CreateContext(); + + var performances = await context.Set() + .AsNoTracking() + .Where(stat => stat.ClientId == clientId) + .Where(stat => stat.ServerId != serverId) // ignore the one we're currently tracking + .Where(stats => stats.UpdatedAt >= Extensions.FifteenDaysAgo()) + .Where(stats => stats.TimePlayed >= _configHandler.Configuration().TopPlayersMinPlayTime) + .ToListAsync(); + + if (clientStats.TimePlayed >= _configHandler.Configuration().TopPlayersMinPlayTime) + { + clientStats.ZScore = await _serverDistributionCalculator.GetZScoreForServer(serverId, + clientStats.Performance); + + var serverRanking = await context.Set() + .Where(stats => stats.ClientId != clientStats.ClientId) + .Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc( + _configHandler.Configuration().TopPlayersMinPlayTime, clientStats.ZScore, serverId)) + .CountAsync(); + + var serverRankingSnapshot = new EFClientRankingHistory() + { + ClientId = clientId, + ServerId = serverId, + ZScore = clientStats.ZScore, + Ranking = serverRanking, + PerformanceMetric = clientStats.Performance, + Newest = true + }; + + context.Add(serverRankingSnapshot); + await PruneOldRankings(context, clientId, serverId); + await context.SaveChangesAsync(); + + performances.Add(clientStats); + } + + if (performances.Any(performance => performance.TimePlayed >= _configHandler.Configuration().TopPlayersMinPlayTime)) + { + var aggregateZScore = performances.WeightValueByPlaytime(nameof(EFClientStatistics.ZScore), _configHandler.Configuration().TopPlayersMinPlayTime); + + int? aggregateRanking = await context.Set() + .Where(stat => stat.ClientId != clientId) + .Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(_configHandler.Configuration() + .TopPlayersMinPlayTime)) + .GroupBy(stat => stat.ClientId) + .Where(group => + group.Sum(stat => stat.ZScore * stat.TimePlayed) / group.Sum(stat => stat.TimePlayed) > + aggregateZScore) + .Select(c => c.Key) + .CountAsync(); + + var aggregateRankingSnapshot = new EFClientRankingHistory() + { + ClientId = clientId, + ZScore = aggregateZScore, + Ranking = aggregateRanking, + PerformanceMetric = await _serverDistributionCalculator.GetRatingForZScore(aggregateZScore), + Newest = true, + }; + + context.Add(aggregateRankingSnapshot); + + await PruneOldRankings(context, clientId); + await context.SaveChangesAsync(); + } + } + + private async Task PruneOldRankings(DatabaseContext context, int clientId, long? serverId = null) + { + var totalRankingEntries = await context.Set() + .Where(r => r.ClientId == clientId) + .Where(r => r.ServerId == serverId) + .CountAsync(); + + var mostRecent = await context.Set() + .Where(r => r.ClientId == clientId) + .Where(r => r.ServerId == serverId) + .FirstOrDefaultAsync(r => r.Newest); + + if (mostRecent != null) + { + mostRecent.Newest = false; + context.Update(mostRecent); + } + + if (totalRankingEntries > EFClientRankingHistory.MaxRankingCount) + { + var lastRating = await context.Set() + .Where(r => r.ClientId == clientId) + .Where(r => r.ServerId == serverId) + .OrderBy(r => r.CreatedDateTime) + .FirstOrDefaultAsync(); + context.Remove(lastRating); + } + } + /// /// Performs the incrementation of kills and deaths for client statistics /// @@ -994,6 +1249,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers attackerStats = UpdateStats(attackerStats); #region DEPRECATED + /* var validAttackerLobbyRatings = Servers[attackerStats.ServerId].PlayerStats .Where(cs => cs.Value.ClientId != attackerStats.ClientId) .Where(cs => @@ -1017,10 +1273,12 @@ namespace IW4MAdmin.Plugins.Stats.Helpers double victimLobbyRating = validVictimLobbyRatings.Count() > 0 ? validVictimLobbyRatings.Average(cs => cs.Value.EloRating) : victimStats.EloRating;*/ + #endregion // calculate elo - double attackerEloDifference = Math.Log(Math.Max(1, victimStats.EloRating)) - Math.Log(Math.Max(1, attackerStats.EloRating)); + double attackerEloDifference = Math.Log(Math.Max(1, victimStats.EloRating)) - + Math.Log(Math.Max(1, attackerStats.EloRating)); double winPercentage = 1.0 / (1 + Math.Pow(10, attackerEloDifference / Math.E)); // double victimEloDifference = Math.Log(Math.Max(1, attackerStats.EloRating)) - Math.Log(Math.Max(1, victimStats.EloRating)); @@ -1033,8 +1291,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers victimStats.EloRating = Math.Max(0, Math.Round(victimStats.EloRating, 2)); // update after calculation - attackerStats.TimePlayed += (int)(DateTime.UtcNow - attackerStats.LastActive).TotalSeconds; - victimStats.TimePlayed += (int)(DateTime.UtcNow - victimStats.LastActive).TotalSeconds; + attackerStats.TimePlayed += (int) (DateTime.UtcNow - attackerStats.LastActive).TotalSeconds; + victimStats.TimePlayed += (int) (DateTime.UtcNow - victimStats.LastActive).TotalSeconds; attackerStats.LastActive = DateTime.UtcNow; victimStats.LastActive = DateTime.UtcNow; } @@ -1072,7 +1330,12 @@ namespace IW4MAdmin.Plugins.Stats.Helpers } double killSPM = scoreDifference / timeSinceLastCalc; - double spmMultiplier = 2.934 * Math.Pow(_servers[clientStats.ServerId].TeamCount(clientStats.Team == IW4Info.Team.Allies ? IW4Info.Team.Axis : IW4Info.Team.Allies), -0.454); + double spmMultiplier = 2.934 * + Math.Pow( + _servers[clientStats.ServerId] + .TeamCount((IW4Info.Team) clientStats.Team == IW4Info.Team.Allies + ? IW4Info.Team.Axis + : IW4Info.Team.Allies), -0.454); killSPM *= Math.Max(1, spmMultiplier); // update this for ac tracking @@ -1080,15 +1343,17 @@ namespace IW4MAdmin.Plugins.Stats.Helpers // calculate how much the KDR should weigh // 1.637 is a Eddie-Generated number that weights the KDR nicely - double currentKDR = clientStats.SessionDeaths == 0 ? clientStats.SessionKills : clientStats.SessionKills / clientStats.SessionDeaths; + double currentKDR = clientStats.SessionDeaths == 0 + ? clientStats.SessionKills + : clientStats.SessionKills / clientStats.SessionDeaths; double alpha = Math.Sqrt(2) / Math.Min(600, Math.Max(clientStats.Kills + clientStats.Deaths, 1)); clientStats.RollingWeightedKDR = (alpha * currentKDR) + (1.0 - alpha) * clientStats.KDR; double KDRWeight = Math.Round(Math.Pow(clientStats.RollingWeightedKDR, 1.637 / Math.E), 3); // calculate the weight of the new play time against last 10 hours of gameplay - int totalPlayTime = (clientStats.TimePlayed == 0) ? - (int)(DateTime.UtcNow - clientStats.LastActive).TotalSeconds : - clientStats.TimePlayed + (int)(DateTime.UtcNow - clientStats.LastActive).TotalSeconds; + int totalPlayTime = (clientStats.TimePlayed == 0) + ? (int) (DateTime.UtcNow - clientStats.LastActive).TotalSeconds + : clientStats.TimePlayed + (int) (DateTime.UtcNow - clientStats.LastActive).TotalSeconds; double SPMAgainstPlayWeight = timeSinceLastCalc / Math.Min(600, (totalPlayTime / 60.0)); @@ -1107,13 +1372,15 @@ namespace IW4MAdmin.Plugins.Stats.Helpers // fixme: how does this happen? if (double.IsNaN(clientStats.SPM) || double.IsNaN(clientStats.Skill)) { - _log.LogWarning("clientStats SPM/Skill NaN {@killInfo}", new {killSPM, KDRWeight, totalPlayTime, SPMAgainstPlayWeight, clientStats, scoreDifference}); + _log.LogWarning("clientStats SPM/Skill NaN {@killInfo}", + new {killSPM, KDRWeight, totalPlayTime, SPMAgainstPlayWeight, clientStats, scoreDifference}); clientStats.SPM = 0; clientStats.Skill = 0; } clientStats.LastStatCalculation = DateTime.UtcNow; //clientStats.LastScore = clientStats.SessionScore; + clientStats.UpdatedAt = DateTime.UtcNow; return clientStats; } @@ -1161,13 +1428,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers public void ResetStats(EFClient client) { var stats = client.GetAdditionalProperty(CLIENT_STATS_KEY); - + // the cached stats have not been loaded yet if (stats == null) { return; } - + stats.Kills = 0; stats.Deaths = 0; stats.SPM = 0; @@ -1253,8 +1520,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers long? serverId; serverId = serverModels.FirstOrDefault(_server => _server.ServerId == server.EndPoint || - _server.EndPoint == server.ToString() || - _server.ServerId == id)?.ServerId; + _server.EndPoint == server.ToString() || + _server.ServerId == id)?.ServerId; if (!serverId.HasValue) { @@ -1264,4 +1531,4 @@ namespace IW4MAdmin.Plugins.Stats.Helpers return serverId.Value; } } -} +} \ No newline at end of file diff --git a/Plugins/Stats/Helpers/StatsResourceQueryHelper.cs b/Plugins/Stats/Helpers/StatsResourceQueryHelper.cs index 219f671e2..165f12dec 100644 --- a/Plugins/Stats/Helpers/StatsResourceQueryHelper.cs +++ b/Plugins/Stats/Helpers/StatsResourceQueryHelper.cs @@ -1,11 +1,12 @@ -using IW4MAdmin.Plugins.Stats.Models; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using SharedLibraryCore.Helpers; using SharedLibraryCore.Interfaces; using Stats.Dtos; using System; using System.Linq; using System.Threading.Tasks; +using Data.Abstractions; +using Data.Models.Client.Stats; namespace Stats.Helpers { @@ -48,7 +49,7 @@ namespace Stats.Helpers ServerId = _stats.ServerId, Kills = _stats.Kills, Deaths = _stats.Deaths, - Performance = Math.Round((_stats.EloRating + _stats.Skill) / 2.0, 2), + Performance = Math.Round(_stats.EloRating * 1/3.0 + _stats.Skill * 2/3.0, 2), ScorePerMinute = _stats.SPM, LastPlayed = _stats.Client.LastConnection, TotalSecondsPlayed = _stats.TimePlayed, diff --git a/Plugins/Stats/Helpers/WeaponNameExtensions.cs b/Plugins/Stats/Helpers/WeaponNameExtensions.cs new file mode 100644 index 000000000..76da43b0e --- /dev/null +++ b/Plugins/Stats/Helpers/WeaponNameExtensions.cs @@ -0,0 +1,10 @@ +using Data.Models.Client.Stats; + +namespace Stats.Helpers +{ + public static class WeaponNameExtensions + { + public static string RebuildWeaponName(this EFClientHitStatistic stat) => + $"{stat.Weapon?.Name}{string.Join("_", stat.WeaponAttachmentCombo?.Attachment1?.Name, stat.WeaponAttachmentCombo?.Attachment2?.Name, stat.WeaponAttachmentCombo?.Attachment3?.Name)}"; + } +} \ No newline at end of file diff --git a/Plugins/Stats/Models/ModelConfiguration.cs b/Plugins/Stats/Models/ModelConfiguration.cs deleted file mode 100644 index b2427afc9..000000000 --- a/Plugins/Stats/Models/ModelConfiguration.cs +++ /dev/null @@ -1,43 +0,0 @@ -using IW4MAdmin.Plugins.Stats.Models; -using Microsoft.EntityFrameworkCore; -using SharedLibraryCore.Interfaces; - -namespace Stats.Models -{ - public class ModelConfiguration : IModelConfiguration - { - public void Configure(ModelBuilder builder) - { - builder.Entity() - .HasKey(cs => new { cs.ClientId, cs.ServerId }); - - // fix linking from SQLCe - builder.Entity() - .Property(c => c.EFClientStatisticsClientId) - .HasColumnName("EFClientStatisticsClientId"); - - builder.Entity() - .Property(c => c.EFClientStatisticsServerId) - .HasColumnName("EFClientStatisticsServerId"); - - builder.Entity() - .HasIndex(p => new { p.Performance, p.Ranking, p.When }); - - builder.Entity() - .HasIndex(p => new { p.When, p.ServerId, p.Performance, p.ActivityAmount }); - - builder.Entity(message => - { - message.HasIndex(p => p.TimeSent); - }); - - // force pluralization - builder.Entity().ToTable("EFClientKills"); - builder.Entity().ToTable("EFClientMessages"); - builder.Entity().ToTable("EFClientStatistics"); - builder.Entity().ToTable("EFHitLocationCounts"); - builder.Entity().ToTable("EFServers"); - builder.Entity().ToTable("EFServerStatistics"); - } - } -} diff --git a/Plugins/Stats/Plugin.cs b/Plugins/Stats/Plugin.cs index d21e613aa..fe6456f45 100644 --- a/Plugins/Stats/Plugin.cs +++ b/Plugins/Stats/Plugin.cs @@ -1,10 +1,7 @@ using IW4MAdmin.Plugins.Stats.Config; using IW4MAdmin.Plugins.Stats.Helpers; -using IW4MAdmin.Plugins.Stats.Models; using Microsoft.EntityFrameworkCore; using SharedLibraryCore; -using SharedLibraryCore.Database; -using SharedLibraryCore.Database.Models; using SharedLibraryCore.Dtos.Meta.Responses; using SharedLibraryCore.Helpers; using SharedLibraryCore.Interfaces; @@ -14,8 +11,16 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Data.Abstractions; +using Data.Models.Client; +using Data.Models.Client.Stats; +using Data.Models.Server; +using Humanizer; using Microsoft.Extensions.Logging; using SharedLibraryCore.Commands; +using IW4MAdmin.Plugins.Stats.Client.Abstractions; +using Stats.Client.Abstractions; +using EFClient = SharedLibraryCore.Database.Models.EFClient; namespace IW4MAdmin.Plugins.Stats { @@ -37,9 +42,12 @@ namespace IW4MAdmin.Plugins.Stats private readonly IResourceQueryHelper _chatQueryHelper; private readonly ILogger _managerLogger; private readonly ILogger _logger; + private readonly List _statCalculators; + private readonly IServerDistributionCalculator _serverDistributionCalculator; public Plugin(ILogger logger, IConfigurationHandlerFactory configurationHandlerFactory, IDatabaseContextFactory databaseContextFactory, - ITranslationLookup translationLookup, IMetaService metaService, IResourceQueryHelper chatQueryHelper, ILogger managerLogger) + ITranslationLookup translationLookup, IMetaService metaService, IResourceQueryHelper chatQueryHelper, ILogger managerLogger, + IEnumerable statCalculators, IServerDistributionCalculator serverDistributionCalculator) { Config = configurationHandlerFactory.GetConfigurationHandler("StatsPluginSettings"); _databaseContextFactory = databaseContextFactory; @@ -48,6 +56,8 @@ namespace IW4MAdmin.Plugins.Stats _chatQueryHelper = chatQueryHelper; _managerLogger = managerLogger; _logger = logger; + _statCalculators = statCalculators.ToList(); + _serverDistributionCalculator = serverDistributionCalculator; } public async Task OnEventAsync(GameEvent E, Server S) @@ -149,6 +159,16 @@ namespace IW4MAdmin.Plugins.Stats } break; } + + if (!Config.Configuration().EnableAdvancedMetrics) + { + return; + } + + foreach (var calculator in _statCalculators) + { + await calculator.CalculateForEvent(E); + } } public async Task OnLoadAsync(IManager manager) @@ -267,20 +287,20 @@ namespace IW4MAdmin.Plugins.Stats if (clientStats.Where(cs => cs.HitLocations.Count > 0).FirstOrDefault() != null) { chestRatio = Math.Round((clientStats.Where(c => c.HitLocations.Count > 0).Sum(c => - c.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_upper).HitCount) / + c.HitLocations.First(hl => hl.Location == (int)IW4Info.HitLocation.torso_upper).HitCount) / (double)clientStats.Where(c => c.HitLocations.Count > 0) - .Sum(c => c.HitLocations.Where(hl => hl.Location != IW4Info.HitLocation.none).Sum(f => f.HitCount))) * 100.0, 0); + .Sum(c => c.HitLocations.Where(hl => hl.Location != (int)IW4Info.HitLocation.none).Sum(f => f.HitCount))) * 100.0, 0); abdomenRatio = Math.Round((clientStats.Where(c => c.HitLocations.Count > 0).Sum(c => - c.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_lower).HitCount) / - (double)clientStats.Where(c => c.HitLocations.Count > 0).Sum(c => c.HitLocations.Where(hl => hl.Location != IW4Info.HitLocation.none).Sum(f => f.HitCount))) * 100.0, 0); + c.HitLocations.First(hl => hl.Location == (int)IW4Info.HitLocation.torso_lower).HitCount) / + (double)clientStats.Where(c => c.HitLocations.Count > 0).Sum(c => c.HitLocations.Where(hl => hl.Location != (int)IW4Info.HitLocation.none).Sum(f => f.HitCount))) * 100.0, 0); - chestAbdomenRatio = Math.Round((clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_upper).HitCount) / - (double)clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_lower).HitCount)) * 100.0, 0); + chestAbdomenRatio = Math.Round((clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == (int)IW4Info.HitLocation.torso_upper).HitCount) / + (double)clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == (int)IW4Info.HitLocation.torso_lower).HitCount)) * 100.0, 0); - headRatio = Math.Round((clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.head).HitCount) / + headRatio = Math.Round((clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == (int)IW4Info.HitLocation.head).HitCount) / (double)clientStats.Where(c => c.HitLocations.Count > 0) - .Sum(c => c.HitLocations.Where(hl => hl.Location != IW4Info.HitLocation.none).Sum(f => f.HitCount))) * 100.0, 0); + .Sum(c => c.HitLocations.Where(hl => hl.Location != (int)IW4Info.HitLocation.none).Sum(f => f.HitCount))) * 100.0, 0); var validOffsets = clientStats.Where(c => c.HitLocations.Count(hl => hl.HitCount > 0) > 0).SelectMany(hl => hl.HitLocations); hitOffsetAverage = validOffsets.Sum(o => o.HitCount * o.HitOffsetAverage) / (double)validOffsets.Sum(o => o.HitCount); @@ -402,7 +422,7 @@ namespace IW4MAdmin.Plugins.Stats async Task topStats(Server s) { // todo: this needs to needs to be updated when we DI the lookup - return string.Join(Environment.NewLine, await Commands.TopStats.GetTopStats(s, Utilities.CurrentLocalization.LocalizationIndex, _databaseContextFactory)); + return string.Join(Environment.NewLine, await Commands.TopStats.GetTopStats(s, Utilities.CurrentLocalization.LocalizationIndex)); } async Task mostPlayed(Server s) @@ -423,8 +443,17 @@ namespace IW4MAdmin.Plugins.Stats manager.GetMessageTokens().Add(new MessageToken("MOSTPLAYED", mostPlayed)); manager.GetMessageTokens().Add(new MessageToken("MOSTKILLS", mostKills)); + if (Config.Configuration().EnableAdvancedMetrics) + { + foreach (var calculator in _statCalculators) + { + await calculator.GatherDependencies(); + } + } + ServerManager = manager; - Manager = new StatManager(_managerLogger, manager, _databaseContextFactory, Config); + Manager = new StatManager(_managerLogger, manager, _databaseContextFactory, Config, _serverDistributionCalculator); + await _serverDistributionCalculator.Initialize(); } public Task OnTickAsync(Server S) diff --git a/Plugins/Stats/Stats.csproj b/Plugins/Stats/Stats.csproj index 79344a62b..1dd2a9c9d 100644 --- a/Plugins/Stats/Stats.csproj +++ b/Plugins/Stats/Stats.csproj @@ -15,9 +15,9 @@ 8.0 false - + - + diff --git a/Plugins/Web/StatsWeb/Extensions/SearchQueryExtensions.cs b/Plugins/Web/StatsWeb/Extensions/SearchQueryExtensions.cs deleted file mode 100644 index bde74b826..000000000 --- a/Plugins/Web/StatsWeb/Extensions/SearchQueryExtensions.cs +++ /dev/null @@ -1,77 +0,0 @@ -using SharedLibraryCore.Dtos; -using Stats.Dtos; -using System; -using System.Linq; - -namespace StatsWeb.Extensions -{ - public static class SearchQueryExtensions - { - private const int MAX_MESSAGES = 100; - - /// - /// todo: lets abstract this out to a generic buildable query - /// this is just a dirty PoC - /// - /// - /// - public static ChatSearchQuery ParseSearchInfo(this string query, int count, int offset) - { - string[] filters = query.Split('|'); - var searchRequest = new ChatSearchQuery - { - Filter = query, - Count = count, - Offset = offset - }; - - // sanity checks - searchRequest.Count = Math.Min(searchRequest.Count, MAX_MESSAGES); - searchRequest.Count = Math.Max(searchRequest.Count, 0); - searchRequest.Offset = Math.Max(searchRequest.Offset, 0); - - if (filters.Length > 1) - { - if (filters[0].ToLower() != "chat") - { - throw new ArgumentException("Query is not compatible with chat"); - } - - foreach (string filter in filters.Skip(1)) - { - string[] args = filter.Split(' '); - - if (args.Length > 1) - { - string recombinedArgs = string.Join(' ', args.Skip(1)); - switch (args[0].ToLower()) - { - case "before": - searchRequest.SentBefore = DateTime.Parse(recombinedArgs); - break; - case "after": - searchRequest.SentAfter = DateTime.Parse(recombinedArgs); - break; - case "server": - searchRequest.ServerId = args[1]; - break; - case "client": - searchRequest.ClientId = int.Parse(args[1]); - break; - case "contains": - searchRequest.MessageContains = string.Join(' ', args.Skip(1)); - break; - case "sort": - searchRequest.Direction = Enum.Parse(args[1], ignoreCase: true); - break; - } - } - } - - return searchRequest; - } - - throw new ArgumentException("No filters specified for chat search"); - } - } -} diff --git a/Plugins/Web/StatsWeb/StatsWeb.csproj b/Plugins/Web/StatsWeb/StatsWeb.csproj deleted file mode 100644 index 65b5a055e..000000000 --- a/Plugins/Web/StatsWeb/StatsWeb.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - netcoreapp3.1 - true - true - false - false - true - Debug;Release;Prerelease - 8.0 - - Library - - Always - - - - - - - - false - - - - - - Never - - - - - - - diff --git a/Plugins/Web/StatsWeb/ViewComponents/TopPlayersViewComponent.cs b/Plugins/Web/StatsWeb/ViewComponents/TopPlayersViewComponent.cs deleted file mode 100644 index d6951bc0a..000000000 --- a/Plugins/Web/StatsWeb/ViewComponents/TopPlayersViewComponent.cs +++ /dev/null @@ -1,28 +0,0 @@ -using IW4MAdmin.Plugins.Stats; -using IW4MAdmin.Plugins.Stats.Helpers; -using Microsoft.AspNetCore.Mvc; -using System.Linq; -using System.Threading.Tasks; - -namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers -{ - public class TopPlayersViewComponent : ViewComponent - { - public async Task InvokeAsync(int count, int offset, long? serverId = null) - { - if (serverId == 0) - { - serverId = null; - } - - var server = Plugin.ServerManager.GetServers().FirstOrDefault(_server => _server.EndPoint == serverId); - - if (server != null) - { - serverId = StatManager.GetIdForServer(server); - } - - return View("_List", await Plugin.Manager.GetTopStats(offset, count, serverId)); - } - } -} diff --git a/Plugins/Web/StatsWeb/Views/Stats/Components/TopPlayers/_List.cshtml b/Plugins/Web/StatsWeb/Views/Stats/Components/TopPlayers/_List.cshtml deleted file mode 100644 index b747b6fcc..000000000 --- a/Plugins/Web/StatsWeb/Views/Stats/Components/TopPlayers/_List.cshtml +++ /dev/null @@ -1,91 +0,0 @@ -@model List -@{ - Layout = null; - var loc = SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex.Set; - double getDeviation(double deviations) => Math.Pow(Math.E, 5.259 + (deviations * 0.812)); - string rankIcon(double elo) - { - if (elo >= getDeviation(-0.75) && elo < getDeviation(1.25)) - { - return "0_no-place/menu_div_no_place.png"; - } - if (elo >= getDeviation(0.125) && elo < getDeviation(0.625)) - { - return "1_iron/menu_div_iron_sub03.png"; - } - if (elo >= getDeviation(0.625) && elo < getDeviation(1.0)) - { - return "2_bronze/menu_div_bronze_sub03.png"; - } - if (elo >= getDeviation(1.0) && elo < getDeviation(1.25)) - { - return "3_silver/menu_div_silver_sub03.png"; - } - if (elo >= getDeviation(1.25) && elo < getDeviation(1.5)) - { - return "4_gold/menu_div_gold_sub03.png"; - } - if (elo >= getDeviation(1.5) && elo < getDeviation(1.75)) - { - return "5_platinum/menu_div_platinum_sub03.png"; - } - if (elo >= getDeviation(1.75) && elo < getDeviation(2.0)) - { - return "6_semipro/menu_div_semipro_sub03.png"; - } - if (elo >= getDeviation(2.0)) - { - return "7_pro/menu_div_pro_sub03.png"; - } - - return "0_no-place/menu_div_no_place.png"; - } -} - -@if (Model.Count == 0) -{ -
@SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_NOQUALIFY"]
-} -@foreach (var stat in Model) -{ -
-
-
-
#@stat.Ranking
- @if (stat.RatingChange > 0) - { -
-
-
@stat.RatingChange
-
- } - @if (stat.RatingChange < 0) - { -
-
@Math.Abs(stat.RatingChange)
-
-
- } - - - - -
- - @stat.Performance @loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"]
- @stat.KDR @loc["PLUGINS_STATS_TEXT_KDR"] - @stat.Kills @loc["PLUGINS_STATS_TEXT_KILLS"] - @stat.Deaths @loc["PLUGINS_STATS_TEXT_DEATHS"]
- @loc["WEBFRONT_PROFILE_PLAYER"] @stat.TimePlayed @loc["GLOBAL_TIME_HOURS"]
- @loc["WEBFRONT_PROFILE_LSEEN"] @stat.LastSeen @loc["WEBFRONT_PENALTY_TEMPLATE_AGO"] -
- -
- -
- -
- -
-
-} diff --git a/Plugins/Web/StatsWeb/Views/_ViewImports.cshtml b/Plugins/Web/StatsWeb/Views/_ViewImports.cshtml deleted file mode 100644 index 1ba5217d2..000000000 --- a/Plugins/Web/StatsWeb/Views/_ViewImports.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@using SharedLibraryCore -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers -@addTagHelper *, SharedLibraryCore \ No newline at end of file diff --git a/Plugins/Web/StatsWeb/wwwroot/images/icons/0_no-place/menu_div_no_place.png b/Plugins/Web/StatsWeb/wwwroot/images/icons/0_no-place/menu_div_no_place.png deleted file mode 100644 index cc9e0e56fbdd121bc52b912e8453991f23510560..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12836 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 Ia^?~L2hdX!r~m)} diff --git a/Plugins/Web/StatsWeb/wwwroot/images/icons/1_iron/menu_div_iron_sub03.png b/Plugins/Web/StatsWeb/wwwroot/images/icons/1_iron/menu_div_iron_sub03.png deleted file mode 100644 index 9ee5d882d674bc8f8e07899e87373d4a27045267..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18393 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 SdCNKg0000`WcGX$Ey1Gy8Ue(og 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 IM6N<$f|Y%5?f?J) diff --git a/Plugins/Web/StatsWeb/wwwroot/images/icons/3_silver/menu_div_silver_sub03.png b/Plugins/Web/StatsWeb/wwwroot/images/icons/3_silver/menu_div_silver_sub03.png deleted file mode 100644 index 8eb75894c48a612552f6c81783f10e9760c59efb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17540 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 diff --git a/Plugins/Web/StatsWeb/wwwroot/images/icons/4_gold/menu_div_gold_sub03.png b/Plugins/Web/StatsWeb/wwwroot/images/icons/4_gold/menu_div_gold_sub03.png deleted file mode 100644 index f44ef1645ae7c5f16216ac30c95db6014ac22fc5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19996 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 KMNUMnLSTZtwjwS7 diff --git a/Plugins/Web/StatsWeb/wwwroot/images/icons/5_platinum/menu_div_platinum_sub03.png b/Plugins/Web/StatsWeb/wwwroot/images/icons/5_platinum/menu_div_platinum_sub03.png deleted file mode 100644 index 5738a84ab3df787badae40f5df4f89b39280e8c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20952 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 bs`P&W*wbwj^1JM#00000NkvXXu0mjff+ar_ diff --git a/Plugins/Web/StatsWeb/wwwroot/images/icons/6_semipro/menu_div_semipro_sub03.png b/Plugins/Web/StatsWeb/wwwroot/images/icons/6_semipro/menu_div_semipro_sub03.png deleted file mode 100644 index 284818b74435baf440af91ae7bc1b9f3f7b9b81d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21663 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 - - - - + + + + diff --git a/SharedLibraryCore/BaseController.cs b/SharedLibraryCore/BaseController.cs index d03dfac77..c91070425 100644 --- a/SharedLibraryCore/BaseController.cs +++ b/SharedLibraryCore/BaseController.cs @@ -1,9 +1,7 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; -using SharedLibraryCore.Database; using SharedLibraryCore.Database.Models; using SharedLibraryCore.Dtos; using SharedLibraryCore.Interfaces; @@ -12,6 +10,8 @@ using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; +using Data.Context; +using Data.Models; namespace SharedLibraryCore { @@ -25,7 +25,7 @@ namespace SharedLibraryCore public IManager Manager { get; private set; } protected readonly DatabaseContext Context; protected bool Authorized { get; set; } - protected SharedLibraryCore.Localization.TranslationLookup Localization { get; private set; } + protected Localization.TranslationLookup Localization { get; private set; } protected EFClient Client { get; private set; } private static readonly byte[] LocalHost = { 127, 0, 0, 1 }; private static string SocialLink; diff --git a/SharedLibraryCore/Commands/AddClientTagCommand.cs b/SharedLibraryCore/Commands/AddClientTagCommand.cs index c696f5ade..9cd7c4084 100644 --- a/SharedLibraryCore/Commands/AddClientTagCommand.cs +++ b/SharedLibraryCore/Commands/AddClientTagCommand.cs @@ -2,6 +2,7 @@ using SharedLibraryCore.Database.Models; using SharedLibraryCore.Interfaces; using System.Threading.Tasks; +using Data.Models; namespace SharedLibraryCore.Commands { diff --git a/SharedLibraryCore/Commands/ListClientTags.cs b/SharedLibraryCore/Commands/ListClientTags.cs index ef1f23f19..7ce5588c9 100644 --- a/SharedLibraryCore/Commands/ListClientTags.cs +++ b/SharedLibraryCore/Commands/ListClientTags.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Data.Models; namespace SharedLibraryCore.Commands { diff --git a/SharedLibraryCore/Commands/NativeCommands.cs b/SharedLibraryCore/Commands/NativeCommands.cs index abd659b6f..eb4037987 100644 --- a/SharedLibraryCore/Commands/NativeCommands.cs +++ b/SharedLibraryCore/Commands/NativeCommands.cs @@ -1,6 +1,5 @@ using Microsoft.EntityFrameworkCore; using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database; using SharedLibraryCore.Database.Models; using SharedLibraryCore.Helpers; using SharedLibraryCore.Interfaces; @@ -12,9 +11,11 @@ using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Data.Abstractions; +using Data.Models; using Microsoft.Extensions.Logging; using Serilog.Context; -using static SharedLibraryCore.Database.Models.EFClient; +using static Data.Models.Client.EFClient; namespace SharedLibraryCore.Commands { @@ -1474,6 +1475,7 @@ namespace SharedLibraryCore.Commands inactiveUsers = await context.Clients .Where(c => c.Level > Permission.Flagged && c.Level <= Permission.Moderator) .Where(c => c.LastConnection < lastActive) + .Select(c => c.ToPartialClient()) .ToListAsync(); inactiveUsers.ForEach(c => c.SetLevel(Permission.User, E.Origin)); await context.SaveChangesAsync(); diff --git a/SharedLibraryCore/Commands/RemoveClientTagCommand.cs b/SharedLibraryCore/Commands/RemoveClientTagCommand.cs index 10c529109..95e7e16b2 100644 --- a/SharedLibraryCore/Commands/RemoveClientTagCommand.cs +++ b/SharedLibraryCore/Commands/RemoveClientTagCommand.cs @@ -2,6 +2,7 @@ using SharedLibraryCore.Database.Models; using SharedLibraryCore.Interfaces; using System.Threading.Tasks; +using Data.Models; namespace SharedLibraryCore.Commands { diff --git a/SharedLibraryCore/Commands/SetClientTagCommand.cs b/SharedLibraryCore/Commands/SetClientTagCommand.cs index c0952c8a1..52e3628ca 100644 --- a/SharedLibraryCore/Commands/SetClientTagCommand.cs +++ b/SharedLibraryCore/Commands/SetClientTagCommand.cs @@ -3,6 +3,7 @@ using SharedLibraryCore.Database.Models; using SharedLibraryCore.Interfaces; using System.Linq; using System.Threading.Tasks; +using Data.Models; namespace SharedLibraryCore.Commands { diff --git a/SharedLibraryCore/Commands/UnsetClientTagCommand.cs b/SharedLibraryCore/Commands/UnsetClientTagCommand.cs index 4dbd2a1c7..96737accb 100644 --- a/SharedLibraryCore/Commands/UnsetClientTagCommand.cs +++ b/SharedLibraryCore/Commands/UnsetClientTagCommand.cs @@ -3,6 +3,7 @@ using SharedLibraryCore.Database.Models; using SharedLibraryCore.Interfaces; using System.Linq; using System.Threading.Tasks; +using Data.Models; namespace SharedLibraryCore.Commands { diff --git a/SharedLibraryCore/Configuration/ApplicationConfiguration.cs b/SharedLibraryCore/Configuration/ApplicationConfiguration.cs index 3ec1ad803..630f0da3b 100644 --- a/SharedLibraryCore/Configuration/ApplicationConfiguration.cs +++ b/SharedLibraryCore/Configuration/ApplicationConfiguration.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; -using static SharedLibraryCore.Database.Models.EFClient; +using static Data.Models.Client.EFClient; namespace SharedLibraryCore.Configuration { diff --git a/SharedLibraryCore/Configuration/CommandProperties.cs b/SharedLibraryCore/Configuration/CommandProperties.cs index 88e52914d..3643c8c5f 100644 --- a/SharedLibraryCore/Configuration/CommandProperties.cs +++ b/SharedLibraryCore/Configuration/CommandProperties.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; -using static SharedLibraryCore.Database.Models.EFClient; +using static Data.Models.Client.EFClient; using static SharedLibraryCore.Server; namespace SharedLibraryCore.Configuration diff --git a/SharedLibraryCore/Configuration/DefaultConfiguration.cs b/SharedLibraryCore/Configuration/DefaultSettings.cs similarity index 80% rename from SharedLibraryCore/Configuration/DefaultConfiguration.cs rename to SharedLibraryCore/Configuration/DefaultSettings.cs index d6f560906..d9ebd0a6a 100644 --- a/SharedLibraryCore/Configuration/DefaultConfiguration.cs +++ b/SharedLibraryCore/Configuration/DefaultSettings.cs @@ -2,13 +2,14 @@ namespace SharedLibraryCore.Configuration { - public class DefaultConfiguration : IBaseConfiguration + public class DefaultSettings : IBaseConfiguration { public string[] AutoMessages { get; set; } public string[] GlobalRules { get; set; } public MapConfiguration[] Maps { get; set; } public QuickMessageConfiguration[] QuickMessages {get; set;} public string[] DisallowedClientNames { get; set; } + public GameStringConfiguration GameStrings { get; set; } public IBaseConfiguration Generate() => this; diff --git a/SharedLibraryCore/Configuration/GameStringConfiguration.cs b/SharedLibraryCore/Configuration/GameStringConfiguration.cs new file mode 100644 index 000000000..9f465b2aa --- /dev/null +++ b/SharedLibraryCore/Configuration/GameStringConfiguration.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using Humanizer; + +namespace SharedLibraryCore.Configuration +{ + public class GameStringConfiguration : Dictionary> + { + public string GetStringForGame(string key, Server.Game game = Server.Game.IW4) + { + if (key == null) + { + return null; + } + + if (!ContainsKey(game)) + { + return key.Transform(To.TitleCase); + } + + var strings = this[game]; + return !strings.ContainsKey(key) ? key.Transform(To.TitleCase) : strings[key]; + } + } +} \ No newline at end of file diff --git a/SharedLibraryCore/Database/DatabaseContext.cs b/SharedLibraryCore/Database/DatabaseContext.cs deleted file mode 100644 index c7e7a9203..000000000 --- a/SharedLibraryCore/Database/DatabaseContext.cs +++ /dev/null @@ -1,196 +0,0 @@ -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; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; - -namespace SharedLibraryCore.Database -{ - public abstract class DatabaseContext : DbContext - { - public DbSet Clients { get; set; } - public DbSet Aliases { get; set; } - public DbSet AliasLinks { get; set; } - public DbSet Penalties { get; set; } - public DbSet EFMeta { get; set; } - public DbSet EFChangeHistory { get; set; } - - private void SetAuditColumns() - { - return; - var entries = ChangeTracker - .Entries() - .Where(e => e.Entity is SharedEntity && ( - e.State == EntityState.Added - || e.State == EntityState.Modified)).ToList(); - - foreach (var entityEntry in entries) - { - if (entityEntry.State == EntityState.Added) - { - //((SharedEntity)entityEntry.Entity).CreatedDateTime = DateTime.UtcNow; - } - - else - { - //((SharedEntity)entityEntry.Entity).UpdatedDateTime = DateTime.UtcNow; - } - } - } - - public DatabaseContext() - { - if (!Utilities.IsMigration) - { - throw new InvalidOperationException(); - } - } - - public DatabaseContext(DbContextOptions options) : base(options) - { - - } - - protected DatabaseContext(DbContextOptions options) : base(options) - { - - } - - public override Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) - { - SetAuditColumns(); - return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); - } - - public override int SaveChanges() - { - SetAuditColumns(); - return base.SaveChanges(); - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - // make network id unique - modelBuilder.Entity(entity => - { - entity.HasIndex(e => e.NetworkId).IsUnique(); - }); - - modelBuilder.Entity(entity => - { - entity.HasOne(p => p.Offender) - .WithMany(c => c.ReceivedPenalties) - .HasForeignKey(c => c.OffenderId) - .OnDelete(DeleteBehavior.Restrict); - - entity.HasOne(p => p.Punisher) - .WithMany(p => p.AdministeredPenalties) - .HasForeignKey(c => c.PunisherId) - .OnDelete(DeleteBehavior.Restrict); - - entity.Property(p => p.Expires) - .IsRequired(false); - }); - - modelBuilder.Entity(entity => - { - entity.HasMany(e => e.Children) - .WithOne(a => a.Link) - .HasForeignKey(k => k.LinkId) - .OnDelete(DeleteBehavior.Restrict); - }); - - 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); - ent.Property(_alias => _alias.SearchableName).HasMaxLength(24); - ent.HasIndex(_alias => _alias.SearchableName); - ent.HasIndex(_alias => new { _alias.Name, _alias.IPAddress }).IsUnique(); - }); - - modelBuilder.Entity(ent => - { - ent.HasIndex(_meta => _meta.Key); - ent.HasIndex(_meta => _meta.LinkedMetaId); - ent.HasOne(_meta => _meta.LinkedMeta) - .WithMany() - .OnDelete(DeleteBehavior.SetNull); - }); - - // force full name for database conversion - modelBuilder.Entity().ToTable("EFClients"); - modelBuilder.Entity().ToTable("EFAlias"); - modelBuilder.Entity().ToTable("EFAliasLinks"); - modelBuilder.Entity().ToTable("EFPenalties"); - - // adapted from - // https://aleemkhan.wordpress.com/2013/02/28/dynamically-adding-dbset-properties-in-dbcontext-for-entity-framework-code-first/ - - string pluginDir = Path.Join(Utilities.OperatingDirectory, "Plugins"); - - if (Utilities.IsDevelopment) - { - pluginDir = Path.Join(Utilities.OperatingDirectory, "..", "..", "..", "..", "BUILD", "Plugins"); - } - - IEnumerable directoryFiles = Enumerable.Empty(); - - try - { - directoryFiles = Directory.GetFiles(pluginDir).Where(f => f.EndsWith(".dll")); - } - - catch (DirectoryNotFoundException) - { - // this is just an ugly thing for unit testing - directoryFiles = Directory.GetFiles(@"X:\IW4MAdmin\Tests\ApplicationTests\bin\Debug\netcoreapp3.1").Where(f => f.EndsWith("dll")); - } - - foreach (string dllPath in directoryFiles) - { - Assembly library; - try - { - library = Assembly.LoadFrom(dllPath); - } - - // not a valid assembly, ie plugin support files - catch (Exception) - { - continue; - } - - var configurations = library.ExportedTypes.Where(c => c.GetInterfaces().FirstOrDefault(i => typeof(IModelConfiguration).IsAssignableFrom(i)) != null) - .Select(c => (IModelConfiguration)Activator.CreateInstance(c)); - - foreach (var configurable in configurations) - { - configurable.Configure(modelBuilder); - } - - foreach (var type in library.ExportedTypes) - { - if (type.IsClass && type.IsSubclassOf(typeof(SharedEntity))) - { - var method = modelBuilder.GetType().GetMethod("Entity", new[] { typeof(Type) }); - method.Invoke(modelBuilder, new[] { type }); - } - } - } - - base.OnModelCreating(modelBuilder); - } - } -} diff --git a/SharedLibraryCore/Dtos/Meta/Responses/ReceivedPenaltyResponse.cs b/SharedLibraryCore/Dtos/Meta/Responses/ReceivedPenaltyResponse.cs index 6911ab7f1..9a796f01a 100644 --- a/SharedLibraryCore/Dtos/Meta/Responses/ReceivedPenaltyResponse.cs +++ b/SharedLibraryCore/Dtos/Meta/Responses/ReceivedPenaltyResponse.cs @@ -1,5 +1,5 @@ using System; -using static SharedLibraryCore.Database.Models.EFPenalty; +using Data.Models; namespace SharedLibraryCore.Dtos.Meta.Responses { @@ -10,7 +10,7 @@ namespace SharedLibraryCore.Dtos.Meta.Responses public string OffenderName { get; set; } public string PunisherName { get; set; } public int PunisherClientId { get; set; } - public PenaltyType PenaltyType { get; set; } + public EFPenalty.PenaltyType PenaltyType { get; set; } public string Offense { get; set; } public string AutomatedOffense { get; set; } public DateTime? ExpirationDate { get; set; } diff --git a/SharedLibraryCore/Dtos/PenaltyInfo.cs b/SharedLibraryCore/Dtos/PenaltyInfo.cs index e8f4c8871..f325227ca 100644 --- a/SharedLibraryCore/Dtos/PenaltyInfo.cs +++ b/SharedLibraryCore/Dtos/PenaltyInfo.cs @@ -1,6 +1,6 @@ using System; -using static SharedLibraryCore.Database.Models.EFClient; -using static SharedLibraryCore.Database.Models.EFPenalty; +using Data.Models; +using static Data.Models.Client.EFClient; namespace SharedLibraryCore.Dtos { @@ -18,14 +18,14 @@ namespace SharedLibraryCore.Dtos public string PunisherLevelText => PunisherLevel.ToLocalizedLevelName(); public string Offense { get; set; } public string AutomatedOffense { get; set; } - public PenaltyType PenaltyType { get; set; } + public EFPenalty.PenaltyType PenaltyType { get; set; } public string PenaltyTypeText => PenaltyType.ToString(); public DateTime TimePunished { get; set; } public string TimePunishedString => TimePunished.HumanizeForCurrentCulture(); public string TimeRemaining => DateTime.UtcNow > Expires ? "" : $"{((Expires ?? DateTime.MaxValue).Year == DateTime.MaxValue.Year ? TimePunishedString : ((Expires ?? DateTime.MaxValue) - DateTime.UtcNow).HumanizeForCurrentCulture())}"; public bool Expired => Expires.HasValue && Expires <= DateTime.UtcNow; public DateTime? Expires { get; set; } - public override bool Sensitive => PenaltyType == PenaltyType.Flag || PenaltyType == PenaltyType.Unflag; + public override bool Sensitive => PenaltyType == EFPenalty.PenaltyType.Flag || PenaltyType == EFPenalty.PenaltyType.Unflag; public bool IsEvade { get; set; } public string AdditionalPenaltyInformation => $"{(!string.IsNullOrEmpty(AutomatedOffense) ? $" ({AutomatedOffense})" : "")}{(IsEvade ? $" ({Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PENALTY_EVADE"]})" : "")}"; } diff --git a/SharedLibraryCore/Dtos/PlayerInfo.cs b/SharedLibraryCore/Dtos/PlayerInfo.cs index eecaa6154..d5aed2c7d 100644 --- a/SharedLibraryCore/Dtos/PlayerInfo.cs +++ b/SharedLibraryCore/Dtos/PlayerInfo.cs @@ -1,8 +1,8 @@ -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Dtos.Meta.Responses; +using SharedLibraryCore.Dtos.Meta.Responses; using SharedLibraryCore.Interfaces; using System; using System.Collections.Generic; +using Data.Models; namespace SharedLibraryCore.Dtos { diff --git a/SharedLibraryCore/Dtos/ServerInfo.cs b/SharedLibraryCore/Dtos/ServerInfo.cs index ca54da2ac..f1bc891aa 100644 --- a/SharedLibraryCore/Dtos/ServerInfo.cs +++ b/SharedLibraryCore/Dtos/ServerInfo.cs @@ -22,5 +22,6 @@ namespace SharedLibraryCore.Dtos public string ConnectProtocolUrl { get; set; } public string IPAddress { get; set; } public bool IsPasswordProtected { get; set; } + public string Endpoint => $"{IPAddress}:{Port}"; } } diff --git a/SharedLibraryCore/Interfaces/IAuditFields.cs b/SharedLibraryCore/Interfaces/IAuditFields.cs new file mode 100644 index 000000000..6a70977e7 --- /dev/null +++ b/SharedLibraryCore/Interfaces/IAuditFields.cs @@ -0,0 +1,9 @@ +using System; + +namespace SharedLibraryCore.Interfaces +{ + public interface IAuditFields + { + DateTime CreatedDateTime { get; set; } + } +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IClientNoticeMessageFormatter.cs b/SharedLibraryCore/Interfaces/IClientNoticeMessageFormatter.cs index 503daf897..b7af3ea7b 100644 --- a/SharedLibraryCore/Interfaces/IClientNoticeMessageFormatter.cs +++ b/SharedLibraryCore/Interfaces/IClientNoticeMessageFormatter.cs @@ -1,4 +1,4 @@ -using SharedLibraryCore.Database.Models; +using Data.Models; namespace SharedLibraryCore.Interfaces { diff --git a/SharedLibraryCore/Interfaces/IGameServer.cs b/SharedLibraryCore/Interfaces/IGameServer.cs index 614b0b46d..0b5df51cd 100644 --- a/SharedLibraryCore/Interfaces/IGameServer.cs +++ b/SharedLibraryCore/Interfaces/IGameServer.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Data.Models; using SharedLibraryCore.Database.Models; namespace SharedLibraryCore.Interfaces diff --git a/SharedLibraryCore/Interfaces/IManagerCommand.cs b/SharedLibraryCore/Interfaces/IManagerCommand.cs index d9222bdab..2999cac56 100644 --- a/SharedLibraryCore/Interfaces/IManagerCommand.cs +++ b/SharedLibraryCore/Interfaces/IManagerCommand.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using static SharedLibraryCore.Database.Models.EFClient; +using static Data.Models.Client.EFClient; using static SharedLibraryCore.Server; namespace SharedLibraryCore.Interfaces diff --git a/SharedLibraryCore/Interfaces/IMetaService.cs b/SharedLibraryCore/Interfaces/IMetaService.cs index 42c06c8bb..8c124f943 100644 --- a/SharedLibraryCore/Interfaces/IMetaService.cs +++ b/SharedLibraryCore/Interfaces/IMetaService.cs @@ -1,4 +1,5 @@ -using SharedLibraryCore.Database.Models; +using Data.Models; +using SharedLibraryCore.Database.Models; using SharedLibraryCore.Dtos; using SharedLibraryCore.QueryHelper; using System; diff --git a/SharedLibraryCore/Interfaces/IPluginImporter.cs b/SharedLibraryCore/Interfaces/IPluginImporter.cs index 32173efae..4748506be 100644 --- a/SharedLibraryCore/Interfaces/IPluginImporter.cs +++ b/SharedLibraryCore/Interfaces/IPluginImporter.cs @@ -12,7 +12,7 @@ namespace SharedLibraryCore.Interfaces /// discovers C# assembly plugin and command types ///
/// tuple of IPlugin implementation type definitions, and IManagerCommand type definitions - (IEnumerable, IEnumerable) DiscoverAssemblyPluginImplementations(); + (IEnumerable, IEnumerable, IEnumerable) DiscoverAssemblyPluginImplementations(); /// /// discovers the script plugins diff --git a/SharedLibraryCore/Localization/Permission.cs b/SharedLibraryCore/Localization/Permission.cs index fe12686bf..0fca389c2 100644 --- a/SharedLibraryCore/Localization/Permission.cs +++ b/SharedLibraryCore/Localization/Permission.cs @@ -1,4 +1,4 @@ -using static SharedLibraryCore.Database.Models.EFClient; +using static Data.Models.Client.EFClient; namespace SharedLibraryCore.Localization { diff --git a/SharedLibraryCore/PartialEntities/EFAlias.cs b/SharedLibraryCore/PartialEntities/EFAlias.cs deleted file mode 100644 index a7aeba982..000000000 --- a/SharedLibraryCore/PartialEntities/EFAlias.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace SharedLibraryCore.Database.Models -{ - public partial class EFAlias : SharedEntity - { - - } -} diff --git a/SharedLibraryCore/PartialEntities/EFClient.cs b/SharedLibraryCore/PartialEntities/EFClient.cs index b0789462a..cba16f07d 100644 --- a/SharedLibraryCore/PartialEntities/EFClient.cs +++ b/SharedLibraryCore/PartialEntities/EFClient.cs @@ -1,19 +1,18 @@ -using Newtonsoft.Json.Converters; -using SharedLibraryCore.Localization; +using SharedLibraryCore.Localization; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -using System.Text.Json.Serialization; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Serilog.Context; +using Data.Models; namespace SharedLibraryCore.Database.Models { - public partial class EFClient + public class EFClient : Data.Models.Client.EFClient { public enum ClientState { @@ -40,50 +39,6 @@ namespace SharedLibraryCore.Database.Models Disconnecting } - public enum Permission - { - /// - /// client has been banned - /// - Banned = -1, - /// - /// default client state upon first connect - /// - User = 0, - /// - /// client has been flagged - /// - Flagged = 1, - /// - /// client is trusted - /// - Trusted = 2, - /// - /// client is a moderator - /// - Moderator = 3, - /// - /// client is an administrator - /// - Administrator = 4, - /// - /// client is a senior administrator - /// - SeniorAdmin = 5, - /// - /// client is a owner - /// - Owner = 6, - /// - /// not used - /// - Creator = 7, - /// - /// reserved for default account - /// - Console = 8 - } - public EFClient() { ConnectionTime = DateTime.UtcNow; diff --git a/SharedLibraryCore/PartialEntities/EFPenalty.cs b/SharedLibraryCore/PartialEntities/EFPenalty.cs deleted file mode 100644 index 8663d6b93..000000000 --- a/SharedLibraryCore/PartialEntities/EFPenalty.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using SharedLibraryCore; - -namespace SharedLibraryCore.Database.Models -{ - public partial class EFPenalty - { - public enum PenaltyType - { - Report, - Warning, - Flag, - Kick, - TempBan, - Ban, - Unban, - Any, - Unflag, - Other = 100 - } - } -} diff --git a/SharedLibraryCore/Repositories/AuditInformationRepository.cs b/SharedLibraryCore/Repositories/AuditInformationRepository.cs index d712ad2f7..6a6548064 100644 --- a/SharedLibraryCore/Repositories/AuditInformationRepository.cs +++ b/SharedLibraryCore/Repositories/AuditInformationRepository.cs @@ -4,6 +4,7 @@ using SharedLibraryCore.Interfaces; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Data.Abstractions; namespace SharedLibraryCore.Repositories { @@ -24,7 +25,7 @@ namespace SharedLibraryCore.Repositories { await using var ctx = _contextFactory.CreateContext(enableTracking: false); var iqItems = (from change in ctx.EFChangeHistory - where change.TypeOfChange != Database.Models.EFChangeHistory.ChangeType.Ban + where change.TypeOfChange != Data.Models.EFChangeHistory.ChangeType.Ban orderby change.TimeChanged descending join originClient in ctx.Clients on (change.ImpersonationEntityId ?? change.OriginEntityId) equals originClient.ClientId diff --git a/SharedLibraryCore/Server.cs b/SharedLibraryCore/Server.cs index f13e12390..9622be951 100644 --- a/SharedLibraryCore/Server.cs +++ b/SharedLibraryCore/Server.cs @@ -11,6 +11,7 @@ using SharedLibraryCore.Configuration; using SharedLibraryCore.Interfaces; using SharedLibraryCore.Database.Models; using ILogger = Microsoft.Extensions.Logging.ILogger; +using Data.Models; namespace SharedLibraryCore { diff --git a/SharedLibraryCore/Services/ChangeHistoryService.cs b/SharedLibraryCore/Services/ChangeHistoryService.cs index 24e00f8fe..4aa4a3f68 100644 --- a/SharedLibraryCore/Services/ChangeHistoryService.cs +++ b/SharedLibraryCore/Services/ChangeHistoryService.cs @@ -1,10 +1,9 @@ -using SharedLibraryCore.Database; -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Interfaces; +using SharedLibraryCore.Database.Models; using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Data.Abstractions; +using Data.Models; using Microsoft.Extensions.Logging; using ILogger = Microsoft.Extensions.Logging.ILogger; diff --git a/SharedLibraryCore/Services/ClientService.cs b/SharedLibraryCore/Services/ClientService.cs index 2397e890c..9de5bc087 100644 --- a/SharedLibraryCore/Services/ClientService.cs +++ b/SharedLibraryCore/Services/ClientService.cs @@ -1,5 +1,4 @@ using Microsoft.EntityFrameworkCore; -using SharedLibraryCore.Database; using SharedLibraryCore.Database.Models; using SharedLibraryCore.Dtos; using SharedLibraryCore.Helpers; @@ -9,10 +8,13 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Data.Abstractions; +using Data.Context; using Microsoft.Extensions.Logging; using Serilog.Context; -using static SharedLibraryCore.Database.Models.EFClient; +using static Data.Models.Client.EFClient; using ILogger = Microsoft.Extensions.Logging.ILogger; +using Data.Models; namespace SharedLibraryCore.Services { @@ -118,9 +120,8 @@ namespace SharedLibraryCore.Services } } - private async Task UpdateAlias(string originalName, int? ip, EFClient entity, DatabaseContext context) + private async Task UpdateAlias(string originalName, int? ip, Data.Models.Client.EFClient entity, DatabaseContext context) { - using (LogContext.PushProperty("Server", entity?.CurrentServer?.ToString())) { string name = originalName.CapClientName(EFAlias.MAX_NAME_LENGTH); @@ -276,7 +277,7 @@ namespace SharedLibraryCore.Services entity.Level = newPermission; await ctx.SaveChangesAsync(); - using (LogContext.PushProperty("Server", entity?.CurrentServer?.ToString())) + using (LogContext.PushProperty("Server", temporalClient?.CurrentServer?.ToString())) { _logger.LogInformation("Updated {clientId} to {newPermission}", temporalClient.ClientId, newPermission); @@ -413,15 +414,13 @@ namespace SharedLibraryCore.Services public async Task UpdateAlias(EFClient temporalClient) { - await using var context = _contextFactory.CreateContext(); + await using var context = _contextFactory.CreateContext(enableTracking:true); var entity = context.Clients .Include(c => c.AliasLink) .Include(c => c.CurrentAlias) .First(e => e.ClientId == temporalClient.ClientId); - entity.CurrentServer = temporalClient.CurrentServer; - await UpdateAlias(temporalClient.Name, temporalClient.IPAddress, entity, context); temporalClient.CurrentAlias = entity.CurrentAlias; @@ -474,7 +473,7 @@ namespace SharedLibraryCore.Services // update in database await context.SaveChangesAsync(); - return entity; + return entity.ToPartialClient(); } #region ServiceSpecific @@ -483,6 +482,7 @@ namespace SharedLibraryCore.Services await using var context = _contextFactory.CreateContext(false); return await context.Clients .Where(c => c.Level == Permission.Owner) + .Select(c => c.ToPartialClient()) .ToListAsync(); } @@ -704,7 +704,7 @@ namespace SharedLibraryCore.Services client.AliasLinkId = newLink.AliasLinkId; client.Level = Permission.User; - await ctx.Aliases.Where(_alias => _alias.IPAddress == client.IPAddress) + await ctx.Aliases.Where(_alias => _alias.IPAddress == client.CurrentAlias.IPAddress && _alias.IPAddress != null) .ForEachAsync(_alias => _alias.LinkId = newLink.AliasLinkId); await ctx.SaveChangesAsync(); @@ -725,12 +725,16 @@ namespace SharedLibraryCore.Services if (!string.IsNullOrEmpty(query.Xuid)) { long networkId = query.Xuid.ConvertGuidToLong(System.Globalization.NumberStyles.HexNumber); - iqClients = context.Clients.Where(_client => _client.NetworkId == networkId); + iqClients = context.Clients. + Where(_client => _client.NetworkId == networkId) + .Select(client => client.ToPartialClient()); } else if (!string.IsNullOrEmpty(query.Name)) { - iqClients = context.Clients.Where(_client => EF.Functions.Like(_client.CurrentAlias.Name.ToLower(), $"%{query.Name.ToLower()}%")); + iqClients = context.Clients + .Where(_client => EF.Functions.Like(_client.CurrentAlias.Name.ToLower(), $"%{query.Name.ToLower()}%")) + .Select(client => client.ToPartialClient()); } if (query.Direction == SortDirection.Ascending) diff --git a/SharedLibraryCore/Services/PenaltyService.cs b/SharedLibraryCore/Services/PenaltyService.cs index 1270699e5..28e3b1dab 100644 --- a/SharedLibraryCore/Services/PenaltyService.cs +++ b/SharedLibraryCore/Services/PenaltyService.cs @@ -7,11 +7,13 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; +using Data.Abstractions; +using Data.Models; using SharedLibraryCore.Interfaces; namespace SharedLibraryCore.Services { - public class PenaltyService : Interfaces.IEntityService + public class PenaltyService : IEntityService { private readonly IDatabaseContextFactory _contextFactory; diff --git a/SharedLibraryCore/SharedLibraryCore.csproj b/SharedLibraryCore/SharedLibraryCore.csproj index 8bbcf55bf..39eed5aec 100644 --- a/SharedLibraryCore/SharedLibraryCore.csproj +++ b/SharedLibraryCore/SharedLibraryCore.csproj @@ -4,7 +4,7 @@ Library netcoreapp3.1 RaidMax.IW4MAdmin.SharedLibraryCore - 2020.12.20.1 + 2021.3.5.1 RaidMax Forever None Debug;Release;Prerelease @@ -13,13 +13,13 @@ IW4MAdmin https://github.com/RaidMax/IW4M-Admin/ https://www.raidmax.org/IW4MAdmin/ - 2020 + 2021 true true true MIT Shared Library for IW4MAdmin - 2020.12.20.1 + 2021.3.19.1 @@ -37,10 +37,6 @@ - - all - runtime; build; native; contentfiles - @@ -48,23 +44,11 @@ - - - + - - - - - - - - - - diff --git a/SharedLibraryCore/Utilities.cs b/SharedLibraryCore/Utilities.cs index 4d7272472..03d28f6a3 100644 --- a/SharedLibraryCore/Utilities.cs +++ b/SharedLibraryCore/Utilities.cs @@ -3,7 +3,6 @@ using Humanizer; using Humanizer.Localisation; using SharedLibraryCore.Database.Models; using SharedLibraryCore.Dtos.Meta; -using SharedLibraryCore.Helpers; using SharedLibraryCore.Interfaces; using System; using System.Collections.Generic; @@ -19,10 +18,11 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using SharedLibraryCore.Configuration; -using static SharedLibraryCore.Database.Models.EFClient; -using static SharedLibraryCore.Database.Models.EFPenalty; using static SharedLibraryCore.Server; using ILogger = Microsoft.Extensions.Logging.ILogger; +using static Data.Models.Client.EFClient; +using Data.Models; +using static Data.Models.EFPenalty; namespace SharedLibraryCore { @@ -965,9 +965,6 @@ namespace SharedLibraryCore /// public static bool IsDevelopment => Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development"; - public static bool IsMigration => Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Migration"; - - /// /// replaces any directory separator chars with the platform specific character /// @@ -987,7 +984,7 @@ namespace SharedLibraryCore /// wrapper method for humanizee that uses current current culture /// public static string HumanizeForCurrentCulture(this TimeSpan timeSpan, int precision = 1, TimeUnit maxUnit = TimeUnit.Week, - TimeUnit minUnit = TimeUnit.Millisecond, string collectionSeparator = ", ", bool toWords = false) + TimeUnit minUnit = TimeUnit.Second, string collectionSeparator = ", ", bool toWords = false) { return timeSpan.Humanize(precision, CurrentLocalization.Culture, maxUnit, minUnit, collectionSeparator, toWords); } @@ -1005,6 +1002,51 @@ namespace SharedLibraryCore return CurrentLocalization.LocalizationIndex[$"META_TYPE_{metaType.ToString().ToUpper()}_NAME"]; } + public static EFClient ToPartialClient(this Data.Models.Client.EFClient client) + { + return new EFClient() + { + ClientId = client.ClientId, + NetworkId = client.NetworkId, + Connections = client.Connections, + TotalConnectionTime = client.TotalConnectionTime, + FirstConnection = client.FirstConnection, + LastConnection = client.LastConnection, + Masked = client.Masked, + AliasLinkId = client.AliasLinkId, + AliasLink = client.AliasLink, + Level = client.Level, + CurrentAliasId = client.CurrentAliasId, + CurrentAlias = client.CurrentAlias, + Password = client.Password, + PasswordSalt = client.PasswordSalt, + Meta = client.Meta, + ReceivedPenalties = client.ReceivedPenalties, + AdministeredPenalties = client.AdministeredPenalties, + Active = client.Active + }; + } + + public static string ToNumericalString(this int? value) + { + return value?.ToNumericalString(); + } + + public static string ToNumericalString(this int value) + { + return value.ToString("#,##0", CurrentLocalization.Culture); + } + + public static string ToNumericalString(this double value, int precision = 0) + { + return value.ToString("#,##0" + $"{(precision > 0 ? "." : "")}" + new string(Enumerable.Repeat('0', precision).ToArray()), CurrentLocalization.Culture); + } + + public static string ToNumericalString(this double? value, int precision = 0) + { + return value?.ToNumericalString(precision); + } + public static string FindRuleForReason(this string reason, ApplicationConfiguration appConfig, Server server) { // allow for penalty presets diff --git a/Tests/ApplicationTests/StatsWebTests.cs b/Tests/ApplicationTests/StatsWebTests.cs deleted file mode 100644 index c4b95e99c..000000000 --- a/Tests/ApplicationTests/StatsWebTests.cs +++ /dev/null @@ -1,269 +0,0 @@ -using ApplicationTests.Fixtures; -using IW4MAdmin.Plugins.Stats.Models; -using Microsoft.Extensions.DependencyInjection; -using NUnit.Framework; -using SharedLibraryCore.Database; -using SharedLibraryCore.Dtos; -using SharedLibraryCore.Interfaces; -using StatsWeb; -using StatsWeb.Extensions; -using System; -using System.Linq; -using System.Threading.Tasks; - -namespace ApplicationTests -{ - [TestFixture] - public class StatsWebTests - { - private IServiceProvider serviceProvider; - private DatabaseContext dbContext; - private ChatResourceQueryHelper queryHelper; - - ~StatsWebTests() - { - dbContext.Dispose(); - } - - [SetUp] - public void Setup() - { - serviceProvider = new ServiceCollection() - .AddSingleton() - .BuildBase() - .BuildServiceProvider(); - - SetupDatabase(); - - queryHelper = serviceProvider.GetRequiredService(); - } - - private void SetupDatabase() - { - var contextFactory = serviceProvider.GetRequiredService(); - dbContext = contextFactory.CreateContext(); - } - - #region PARSE_SEARCH_INFO - [Test] - public void Test_ParseSearchInfo_SanityChecks() - { - var query = "chat|".ParseSearchInfo(-1, -1); - - Assert.AreEqual(0, query.Count); - Assert.AreEqual(0, query.Offset); - - query = "chat|".ParseSearchInfo(int.MaxValue, int.MaxValue); - - Assert.Greater(int.MaxValue, query.Count); - } - - [Test] - public void Test_ParseSearchInfo_BeforeFilter_Happy() - { - var now = DateTime.Now; - var date = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second); - var query = $"chat|before {date.ToString()}".ParseSearchInfo(0, 0); - - Assert.AreEqual(date, query.SentBefore); - } - - [Test] - public void Test_ParseSearchInfo_AfterFilter_Happy() - { - var now = DateTime.Now; - var date = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second); - var query = $"chat|after {date.ToString()}".ParseSearchInfo(0, 0); - - Assert.AreEqual(date, query.SentAfter); - } - - [Test] - public void Test_ParseSearchInfo_ServerFilter_Happy() - { - string serverId = "127.0.0.1:28960"; - var query = $"chat|server {serverId}".ParseSearchInfo(0, 0); - - Assert.AreEqual(serverId, query.ServerId); - } - - [Test] - public void Test_ParseSearchInfo_ClientFilter_Happy() - { - int clientId = 123; - var query = $"chat|client {clientId.ToString()}".ParseSearchInfo(0, 0); - - Assert.AreEqual(clientId, query.ClientId); - } - - [Test] - public void Test_ParseSearchInfo_ContainsFilter_Happy() - { - string content = "test"; - var query = $"chat|contains {content}".ParseSearchInfo(0, 0); - - Assert.AreEqual(content, query.MessageContains); - } - - [Test] - public void Test_ParseSearchInfo_SortFilter_Happy() - { - var direction = SortDirection.Ascending; - var query = $"chat|sort {direction.ToString().ToLower()}".ParseSearchInfo(0, 0); - - Assert.AreEqual(direction, query.Direction); - - direction = SortDirection.Descending; - query = $"chat|sort {direction.ToString().ToLower()}".ParseSearchInfo(0, 0); - - Assert.AreEqual(direction, query.Direction); - } - - [Test] - public void Test_ParseSearchInfo_InvalidQueryType() - { - Assert.Throws(() => "player|test".ParseSearchInfo(0, 0)); - } - - [Test] - public void Test_ParseSearchInfo_NoQueryType() - { - Assert.Throws(() => "".ParseSearchInfo(0, 0)); - } - #endregion] - - #region CHAT_RESOURCE_QUERY_HELPER - [Test] - public void Test_ChatResourceQueryHelper_Invalid() - { - var helper = serviceProvider.GetRequiredService(); - - Assert.ThrowsAsync(() => helper.QueryResource(null)); - } - - [Test] - public async Task Test_ChatResourceQueryHelper_SentAfter() - { - var oneHourAhead = DateTime.Now.AddHours(1); - var msg = MessageGenerators.GenerateMessage(sent: oneHourAhead); - - dbContext.Set() - .Add(msg); - await dbContext.SaveChangesAsync(); - - var query = $"chat|after {DateTime.Now.ToString()}".ParseSearchInfo(1, 0); - var result = await queryHelper.QueryResource(query); - - Assert.AreEqual(oneHourAhead, result.Results.First().When); - - dbContext.Remove(msg); - await dbContext.SaveChangesAsync(); - } - - [Test] - public async Task Test_ChatResourceQueryHelper_SentBefore() - { - var oneHourAgo = DateTime.Now.AddHours(-1); - var msg = MessageGenerators.GenerateMessage(sent: oneHourAgo); - - dbContext.Set() - .Add(msg); - await dbContext.SaveChangesAsync(); - - var query = $"chat|before {DateTime.Now.ToString()}".ParseSearchInfo(1, 0); - var result = await queryHelper.QueryResource(query); - - Assert.AreEqual(oneHourAgo, result.Results.First().When); - - dbContext.Remove(msg); - await dbContext.SaveChangesAsync(); - } - - [Test] - public async Task Test_ChatResourceQueryHelper_Server() - { - var msg = MessageGenerators.GenerateMessage(sent: DateTime.Now); - - dbContext.Set() - .Add(msg); - await dbContext.SaveChangesAsync(); - - string serverId = msg.Server.EndPoint; - var query = $"chat|server {serverId}".ParseSearchInfo(1, 0); - var result = await queryHelper.QueryResource(query); - - Assert.IsNotEmpty(result.Results); - - dbContext.Remove(msg); - await dbContext.SaveChangesAsync(); - } - - [Test] - public async Task Test_ChatResourceQueryHelper_Client() - { - var msg = MessageGenerators.GenerateMessage(sent: DateTime.Now); - - dbContext.Set() - .Add(msg); - await dbContext.SaveChangesAsync(); - - int clientId = msg.Client.ClientId; - var query = $"chat|client {clientId}".ParseSearchInfo(1, 0); - var result = await queryHelper.QueryResource(query); - - Assert.AreEqual(clientId, result.Results.First().ClientId); - - dbContext.Remove(msg); - await dbContext.SaveChangesAsync(); - } - - [Test] - public async Task Test_ChatResourceQueryHelper_Contains() - { - var msg = MessageGenerators.GenerateMessage(sent: DateTime.Now); - msg.Message = "this is a test"; - - dbContext.Set() - .Add(msg); - await dbContext.SaveChangesAsync(); - - var query = $"chat|contains {msg.Message}".ParseSearchInfo(1, 0); - var result = await queryHelper.QueryResource(query); - - Assert.AreEqual(msg.Message, result.Results.First().Message); - - dbContext.Remove(msg); - await dbContext.SaveChangesAsync(); - } - - [Test] - public async Task Test_ChatResourceQueryHelper_Sort() - { - var firstMessage = MessageGenerators.GenerateMessage(sent: DateTime.Now.AddHours(-1)); - var secondMessage = MessageGenerators.GenerateMessage(sent: DateTime.Now); - - dbContext.Set() - .Add(firstMessage); - dbContext.Set() - .Add(secondMessage); - await dbContext.SaveChangesAsync(); - - var query = $"chat|sort {SortDirection.Ascending}".ParseSearchInfo(2, 0); - var result = await queryHelper.QueryResource(query); - - Assert.AreEqual(firstMessage.TimeSent, result.Results.First().When); - Assert.AreEqual(secondMessage.TimeSent, result.Results.Last().When); - - query = $"chat|sort {SortDirection.Descending}".ParseSearchInfo(2, 0); - result = await queryHelper.QueryResource(query); - - Assert.AreEqual(firstMessage.TimeSent, result.Results.Last().When); - Assert.AreEqual(secondMessage.TimeSent, result.Results.First().When); - - dbContext.Remove(firstMessage); - dbContext.Remove(secondMessage); - await dbContext.SaveChangesAsync(); - } - #endregion - } -} diff --git a/Plugins/Web/StatsWeb/API/StatsController.cs b/WebfrontCore/Controllers/API/StatsController.cs similarity index 100% rename from Plugins/Web/StatsWeb/API/StatsController.cs rename to WebfrontCore/Controllers/API/StatsController.cs diff --git a/WebfrontCore/Controllers/API/Validation/FindClientRequestValidator.cs b/WebfrontCore/Controllers/API/Validation/FindClientRequestValidator.cs index c9d3fc86f..968c653d1 100644 --- a/WebfrontCore/Controllers/API/Validation/FindClientRequestValidator.cs +++ b/WebfrontCore/Controllers/API/Validation/FindClientRequestValidator.cs @@ -1,5 +1,5 @@ -using FluentValidation; -using SharedLibraryCore.Database.Models; +using Data.Models; +using FluentValidation; using SharedLibraryCore.Dtos; namespace WebfrontCore.Controllers.API.Validation diff --git a/WebfrontCore/Controllers/ActionController.cs b/WebfrontCore/Controllers/ActionController.cs index 774c1a3ea..32e83c365 100644 --- a/WebfrontCore/Controllers/ActionController.cs +++ b/WebfrontCore/Controllers/ActionController.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading.Tasks; +using Data.Models.Client; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using SharedLibraryCore; @@ -10,7 +11,6 @@ using SharedLibraryCore.Commands; using SharedLibraryCore.Configuration; using SharedLibraryCore.Interfaces; using WebfrontCore.ViewModels; -using static SharedLibraryCore.Database.Models.EFClient; namespace WebfrontCore.Controllers { @@ -215,10 +215,10 @@ namespace WebfrontCore.Controllers Name = "level", Label = Localization["WEBFRONT_PROFILE_LEVEL"], Type = "select", - Values = Enum.GetValues(typeof(Permission)).OfType() + Values = Enum.GetValues(typeof(EFClient.Permission)).OfType() .Where(p => p <= Client.Level) - .Where(p => p != Permission.Banned) - .Where(p => p != Permission.Flagged) + .Where(p => p != EFClient.Permission.Banned) + .Where(p => p != EFClient.Permission.Flagged) .ToDictionary(p => p.ToString(), p => p.ToLocalizedLevelName()) }, }, diff --git a/WebfrontCore/Controllers/ClientController.cs b/WebfrontCore/Controllers/Client/ClientController.cs similarity index 86% rename from WebfrontCore/Controllers/ClientController.cs rename to WebfrontCore/Controllers/Client/ClientController.cs index 86a7e353c..dc7f07605 100644 --- a/WebfrontCore/Controllers/ClientController.cs +++ b/WebfrontCore/Controllers/Client/ClientController.cs @@ -5,24 +5,26 @@ using SharedLibraryCore.Dtos; using SharedLibraryCore.Dtos.Meta.Responses; using SharedLibraryCore.Interfaces; using SharedLibraryCore.QueryHelper; -using SharedLibraryCore.Services; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Data.Models; +using IW4MAdmin.Plugins.Stats.Config; using WebfrontCore.ViewComponents; -using static SharedLibraryCore.Database.Models.EFClient; -using static SharedLibraryCore.Database.Models.EFPenalty; namespace WebfrontCore.Controllers { public class ClientController : BaseController { private readonly IMetaService _metaService; + private readonly IConfigurationHandler _configurationHandler; - public ClientController(IManager manager, IMetaService metaService) : base(manager) + public ClientController(IManager manager, IMetaService metaService, + IConfigurationHandler configurationHandler) : base(manager) { _metaService = metaService; + _configurationHandler = configurationHandler; } public async Task ProfileAsync(int id, MetaType? metaFilterType) @@ -42,13 +44,13 @@ namespace WebfrontCore.Controllers client.SetAdditionalProperty(EFMeta.ClientTag, tag.LinkedMeta.Value); } - int displayLevelInt = (int)client.Level; - string displayLevel = client.Level.ToLocalizedLevelName(); + var displayLevelInt = (int)client.Level; + var displayLevel = client.Level.ToLocalizedLevelName(); if (!Authorized && client.Level.ShouldHideLevel()) { - displayLevelInt = (int)Permission.User; - displayLevel = Permission.User.ToLocalizedLevelName(); + displayLevelInt = (int)Data.Models.Client.EFClient.Permission.User; + displayLevel = Data.Models.Client.EFClient.Permission.User.ToLocalizedLevelName(); } displayLevel = string.IsNullOrEmpty(client.Tag) ? displayLevel : $"{displayLevel} ({client.Tag})"; @@ -77,7 +79,7 @@ namespace WebfrontCore.Controllers .Prepend(client.CurrentAlias.IPAddress.ConvertIPtoString()) .Distinct() .ToList(), - HasActivePenalty = activePenalties.Any(_penalty => _penalty.Type != PenaltyType.Flag), + HasActivePenalty = activePenalties.Any(_penalty => _penalty.Type != EFPenalty.PenaltyType.Flag), Online = Manager.GetActiveClients().FirstOrDefault(c => c.ClientId == client.ClientId) != null, TimeOnline = (DateTime.UtcNow - client.LastConnection).HumanizeForCurrentCulture(), LinkedAccounts = client.LinkedAccounts, @@ -111,6 +113,7 @@ namespace WebfrontCore.Controllers ViewBag.Title += " " + Localization["WEBFRONT_CLIENT_PROFILE_TITLE"]; ViewBag.Description = $"Client information for {strippedName}"; ViewBag.Keywords = $"IW4MAdmin, client, profile, {strippedName}"; + ViewBag.UseNewStats = _configurationHandler.Configuration().EnableAdvancedMetrics; return View("Profile/Index", clientDto); } @@ -155,10 +158,10 @@ namespace WebfrontCore.Controllers foreach (var client in clientsDto) { - if (!Authorized && ((Permission)client.LevelInt).ShouldHideLevel()) + if (!Authorized && ((Data.Models.Client.EFClient.Permission)client.LevelInt).ShouldHideLevel()) { - client.LevelInt = (int)Permission.User; - client.Level = Permission.User.ToLocalizedLevelName(); + client.LevelInt = (int)Data.Models.Client.EFClient.Permission.User; + client.Level = Data.Models.Client.EFClient.Permission.User.ToLocalizedLevelName(); } } diff --git a/WebfrontCore/Controllers/Client/ClientStatisticsController.cs b/WebfrontCore/Controllers/Client/ClientStatisticsController.cs new file mode 100644 index 000000000..829c01f49 --- /dev/null +++ b/WebfrontCore/Controllers/Client/ClientStatisticsController.cs @@ -0,0 +1,38 @@ +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using SharedLibraryCore; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; +using Stats.Dtos; + +namespace WebfrontCore.Controllers +{ + [Route("clientstatistics")] + public class ClientStatisticsController : BaseController + { + private IResourceQueryHelper _queryHelper; + private readonly DefaultSettings _defaultConfig; + + public ClientStatisticsController(IManager manager, + IResourceQueryHelper queryHelper, + IConfigurationHandler configurationHandler) : base(manager) + { + _queryHelper = queryHelper; + _defaultConfig = configurationHandler.Configuration(); + } + + [HttpGet("{id:int}/advanced")] + public async Task Advanced(int id, [FromQuery] string serverId) + { + ViewBag.Config = _defaultConfig.GameStrings; + var hitInfo = await _queryHelper.QueryResource(new StatsInfoRequest + { + ClientId = id, + ServerEndpoint = serverId + }); + + return View("~/Views/Client/Statistics/Advanced.cshtml", hitInfo.Results.First()); + } + } +} \ No newline at end of file diff --git a/Plugins/Web/StatsWeb/Controllers/StatsController.cs b/WebfrontCore/Controllers/Client/Legacy/StatsController.cs similarity index 78% rename from Plugins/Web/StatsWeb/Controllers/StatsController.cs rename to WebfrontCore/Controllers/Client/Legacy/StatsController.cs index a6dee0d77..0d998647f 100644 --- a/Plugins/Web/StatsWeb/Controllers/StatsController.cs +++ b/WebfrontCore/Controllers/Client/Legacy/StatsController.cs @@ -8,13 +8,14 @@ using SharedLibraryCore.Dtos; using SharedLibraryCore.Dtos.Meta.Responses; using SharedLibraryCore.Interfaces; using Stats.Dtos; -using StatsWeb.Extensions; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using ILogger = Microsoft.Extensions.Logging.ILogger; +using Data.Abstractions; +using IW4MAdmin.Plugins.Stats.Config; namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers { @@ -25,16 +26,19 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers private readonly IResourceQueryHelper _chatResourceQueryHelper; private readonly ITranslationLookup _translationLookup; private readonly IDatabaseContextFactory _contextFactory; + private readonly IConfigurationHandler _configurationHandler; - public StatsController(ILogger logger, IManager manager, IResourceQueryHelper resourceQueryHelper, ITranslationLookup translationLookup, - IDatabaseContextFactory contextFactory) : base(manager) + public StatsController(ILogger logger, IManager manager, IResourceQueryHelper resourceQueryHelper, ITranslationLookup translationLookup, + IDatabaseContextFactory contextFactory, + IConfigurationHandler configurationHandler) : base(manager) { _logger = logger; _manager = manager; _chatResourceQueryHelper = resourceQueryHelper; _translationLookup = translationLookup; _contextFactory = contextFactory; + _configurationHandler = configurationHandler; } [HttpGet] @@ -42,10 +46,11 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers { ViewBag.Title = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_STATS_INDEX_TITLE"]; ViewBag.Description = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_STATS_INDEX_DESC"]; - ViewBag.Servers = _manager.GetServers().Select(_server => new ServerInfo() { Name = _server.Hostname, ID = _server.EndPoint }); + ViewBag.Servers = _manager.GetServers() + .Select(_server => new ServerInfo() {Name = _server.Hostname, ID = _server.EndPoint}); ViewBag.Localization = _translationLookup; - return View("Index"); + return View("~/Views/Client/Statistics/Index.cshtml"); } [HttpGet] @@ -64,7 +69,9 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers serverId = StatManager.GetIdForServer(server); } - var results = await Plugin.Manager.GetTopStats(offset, count, serverId); + var results = _configurationHandler.Configuration().EnableAdvancedMetrics + ? await Plugin.Manager.GetNewTopStats(offset, count, serverId) + : await Plugin.Manager.GetTopStats(offset, count, serverId); // this returns an empty result so we know to stale the loader if (results.Count == 0 && offset > 0) @@ -72,10 +79,8 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers return Ok(); } - else - { - return View("Components/TopPlayers/_List", results); - } + ViewBag.UseNewStats = _configurationHandler.Configuration().EnableAdvancedMetrics; + return View("~/Views/Client/Statistics/Components/TopPlayers/_List.cshtml", results); } [HttpGet] @@ -92,7 +97,7 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers SentAfter = whenLower }); - return View("_MessageContext", messages.Results.ToList()); + return View("~/Views/Client/_MessageContext.cshtml", messages.Results.ToList()); } [HttpGet("Message/Find")] @@ -125,11 +130,12 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers } var result = searchRequest != null ? await _chatResourceQueryHelper.QueryResource(searchRequest) : null; - return View("Message/Find", result); + return View("~/Views/Client/Message/Find.cshtml", result); } [HttpGet("Message/FindNext")] - public async Task FindNextMessages([FromQuery] string query, [FromQuery] int count, [FromQuery] int offset) + public async Task FindNextMessages([FromQuery] string query, [FromQuery] int count, + [FromQuery] int offset) { ChatSearchQuery searchRequest; @@ -151,7 +157,7 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers } var result = await _chatResourceQueryHelper.QueryResource(searchRequest); - return PartialView("Message/_Item", result.Results); + return PartialView("~/Views/Client/Message/_Item.cshtml", result.Results); } [HttpGet] @@ -159,9 +165,10 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers public async Task GetAutomatedPenaltyInfoAsync(int penaltyId) { await using var context = _contextFactory.CreateContext(false); - + var penalty = await context.Penalties - .Select(_penalty => new { _penalty.OffenderId, _penalty.PenaltyId, _penalty.When, _penalty.AutomatedOffense }) + .Select(_penalty => new + {_penalty.OffenderId, _penalty.PenaltyId, _penalty.When, _penalty.AutomatedOffense}) .FirstOrDefaultAsync(_penalty => _penalty.PenaltyId == penaltyId); if (penalty == null) @@ -170,7 +177,7 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers } // todo: this can be optimized - var iqSnapshotInfo = context.Set() + var iqSnapshotInfo = context.ACSnapshots .Where(s => s.ClientId == penalty.OffenderId) .Include(s => s.LastStrainAngle) .Include(s => s.HitOrigin) @@ -185,13 +192,13 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers if (penaltyInfo.Count > 0) { - return View("_PenaltyInfo", penaltyInfo); + return View("~/Views/Client/_PenaltyInfo.cshtml", penaltyInfo); } // we want to show anything related to the automated offense else { - return View("_MessageContext", new List + return View("~/Views/Client/_MessageContext.cshtml", new List { new MessageResponse() { @@ -203,4 +210,4 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers } } } -} +} \ No newline at end of file diff --git a/WebfrontCore/Controllers/ConsoleController.cs b/WebfrontCore/Controllers/ConsoleController.cs index 5d2bddad4..910d8223f 100644 --- a/WebfrontCore/Controllers/ConsoleController.cs +++ b/WebfrontCore/Controllers/ConsoleController.cs @@ -6,6 +6,7 @@ using SharedLibraryCore.Dtos; using SharedLibraryCore.Interfaces; using System.Linq; using System.Threading.Tasks; +using Data.Models; namespace WebfrontCore.Controllers { diff --git a/WebfrontCore/Controllers/HomeController.cs b/WebfrontCore/Controllers/HomeController.cs index d6d73bb98..595ac25f8 100644 --- a/WebfrontCore/Controllers/HomeController.cs +++ b/WebfrontCore/Controllers/HomeController.cs @@ -1,10 +1,15 @@ -using Microsoft.AspNetCore.Diagnostics; +using System; +using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Mvc; using SharedLibraryCore; using SharedLibraryCore.Dtos; using SharedLibraryCore.Interfaces; using System.Linq; using System.Threading.Tasks; +using Data.Abstractions; +using Data.Models.Client; +using Data.Models.Client.Stats; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using static SharedLibraryCore.Server; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -15,11 +20,23 @@ namespace WebfrontCore.Controllers { private readonly ITranslationLookup _translationLookup; private readonly ILogger _logger; + private readonly IDataValueCache _serverStatsCache; + private const string ServerStatKey = nameof(ServerStatKey); - public HomeController(ILogger logger, IManager manager, ITranslationLookup translationLookup) : base(manager) + public HomeController(ILogger logger, IManager manager, ITranslationLookup translationLookup, + IDataValueCache serverStatsCache) : base(manager) { _logger = logger; _translationLookup = translationLookup; + _serverStatsCache = serverStatsCache; + + _serverStatsCache.SetCacheItem(async set => + { + var count = await set.CountAsync(); + var startOfPeriod = DateTime.UtcNow.AddHours(-24); + var recentCount = await set.CountAsync(client => client.LastConnection >= startOfPeriod); + return (count, recentCount); + }, ServerStatKey); } public async Task Index(Game? game = null) @@ -28,14 +45,15 @@ namespace WebfrontCore.Controllers ViewBag.Title = Localization["WEBFRONT_HOME_TITLE"]; ViewBag.Keywords = Localization["WEBFRONT_KEWORDS_HOME"]; - var servers = Manager.GetServers().Where(_server => !game.HasValue ? true : _server.GameName == game); + var servers = Manager.GetServers().Where(_server => !game.HasValue || _server.GameName == game); + var (count, recentCount) = await _serverStatsCache.GetCacheItem(ServerStatKey); var model = new IW4MAdminInfo() { TotalAvailableClientSlots = servers.Sum(_server => _server.MaxClients), TotalOccupiedClientSlots = servers.SelectMany(_server => _server.GetClientsAsList()).Count(), - TotalClientCount = await Manager.GetClientService().GetTotalClientsAsync(), - RecentClientCount = await Manager.GetClientService().GetRecentClientCount(), + TotalClientCount = count, + RecentClientCount = recentCount, Game = game, ActiveServerGames = Manager.GetServers().Select(_server => _server.GameName).Distinct().ToArray() }; @@ -46,7 +64,8 @@ namespace WebfrontCore.Controllers public IActionResult Error() { var exceptionFeature = HttpContext.Features.Get(); - _logger.LogError("[Webfront] {path} {message} {@exception}", exceptionFeature.Path, exceptionFeature.Error.Message, exceptionFeature.Error); + _logger.LogError("[Webfront] {path} {message} {@exception}", exceptionFeature.Path, + exceptionFeature.Error.Message, exceptionFeature.Error); ViewBag.Description = Localization["WEBFRONT_ERROR_DESC"]; ViewBag.Title = Localization["WEBFRONT_ERROR_TITLE"]; return View(exceptionFeature.Error); @@ -71,15 +90,16 @@ namespace WebfrontCore.Controllers .GroupBy(_cmd => { // we need the plugin type the command is defined in - var pluginType = _cmd.GetType().Assembly.GetTypes().FirstOrDefault(_type => _type.Assembly != excludedAssembly && typeof(IPlugin).IsAssignableFrom(_type)); - return pluginType == null ? - _translationLookup["WEBFRONT_HELP_COMMAND_NATIVE"] : + var pluginType = _cmd.GetType().Assembly.GetTypes().FirstOrDefault(_type => + _type.Assembly != excludedAssembly && typeof(IPlugin).IsAssignableFrom(_type)); + return pluginType == null ? _translationLookup["WEBFRONT_HELP_COMMAND_NATIVE"] : pluginType.Name == "ScriptPlugin" ? _translationLookup["WEBFRONT_HELP_SCRIPT_PLUGIN"] : - Manager.Plugins.First(_plugin => _plugin.GetType() == pluginType).Name; // for now we're just returning the name of the plugin, maybe later we'll include more info + Manager.Plugins.First(_plugin => _plugin.GetType() == pluginType) + .Name; // for now we're just returning the name of the plugin, maybe later we'll include more info }) .Select(_grp => (_grp.Key, _grp.AsEnumerable())); return View(commands); } } -} +} \ No newline at end of file diff --git a/WebfrontCore/Controllers/PenaltyController.cs b/WebfrontCore/Controllers/PenaltyController.cs index 7620a1652..944cd88f9 100644 --- a/WebfrontCore/Controllers/PenaltyController.cs +++ b/WebfrontCore/Controllers/PenaltyController.cs @@ -1,13 +1,13 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using SharedLibraryCore; -using SharedLibraryCore.Database; using SharedLibraryCore.Dtos; using SharedLibraryCore.Interfaces; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using static SharedLibraryCore.Database.Models.EFPenalty; +using Data.Abstractions; +using Data.Models; namespace WebfrontCore.Controllers { @@ -20,7 +20,7 @@ namespace WebfrontCore.Controllers _contextFactory = contextFactory; } - public IActionResult List(PenaltyType showOnly = PenaltyType.Any, bool hideAutomatedPenalties = true) + public IActionResult List(EFPenalty.PenaltyType showOnly = EFPenalty.PenaltyType.Any, bool hideAutomatedPenalties = true) { ViewBag.Description = Localization["WEBFRONT_DESCRIPTION_PENALTIES"]; ViewBag.Title = Localization["WEBFRONT_PENALTY_TITLE"]; @@ -30,7 +30,7 @@ namespace WebfrontCore.Controllers return View(showOnly); } - public async Task ListAsync(int offset = 0, PenaltyType showOnly = PenaltyType.Any, bool hideAutomatedPenalties = true) + public async Task ListAsync(int offset = 0, EFPenalty.PenaltyType showOnly = EFPenalty.PenaltyType.Any, bool hideAutomatedPenalties = true) { return await Task.FromResult(View("_List", new ViewModels.PenaltyFilterInfo() { @@ -52,7 +52,7 @@ namespace WebfrontCore.Controllers await using var ctx = _contextFactory.CreateContext(false); var iqPenalties = ctx.Penalties .AsNoTracking() - .Where(p => p.Type == PenaltyType.Ban && p.Active) + .Where(p => p.Type == EFPenalty.PenaltyType.Ban && p.Active) .OrderByDescending(_penalty => _penalty.When) .Select(p => new PenaltyInfo() { diff --git a/WebfrontCore/Middleware/ClaimsPermissionRemoval.cs b/WebfrontCore/Middleware/ClaimsPermissionRemoval.cs index bc8e4c813..3d9dfb179 100644 --- a/WebfrontCore/Middleware/ClaimsPermissionRemoval.cs +++ b/WebfrontCore/Middleware/ClaimsPermissionRemoval.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; -using static SharedLibraryCore.Database.Models.EFClient; +using Data.Models.Client; using static SharedLibraryCore.GameEvent; namespace WebfrontCore.Middleware @@ -36,10 +36,10 @@ namespace WebfrontCore.Middleware private void OnGameEvent(object sender, GameEvent gameEvent) { if (gameEvent.Type == EventType.ChangePermission && - gameEvent.Extra is Permission perm) + gameEvent.Extra is EFClient.Permission perm) { // we want to remove the claims when the client is demoted - if (perm < Permission.Trusted) + if (perm < EFClient.Permission.Trusted) { lock (_privilegedClientIds) { @@ -47,7 +47,7 @@ namespace WebfrontCore.Middleware } } // and add if promoted - else if (perm > Permission.Trusted && + else if (perm > EFClient.Permission.Trusted && !_privilegedClientIds.Contains(gameEvent.Target.ClientId)) { lock (_privilegedClientIds) diff --git a/WebfrontCore/Startup.cs b/WebfrontCore/Startup.cs index 1ec1205de..429636fd1 100644 --- a/WebfrontCore/Startup.cs +++ b/WebfrontCore/Startup.cs @@ -4,27 +4,28 @@ using FluentValidation.AspNetCore; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using SharedLibraryCore; using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database; using SharedLibraryCore.Dtos; using SharedLibraryCore.Dtos.Meta.Responses; using SharedLibraryCore.Interfaces; using SharedLibraryCore.Services; using Stats.Dtos; using Stats.Helpers; -using StatsWeb; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Threading.Tasks; +using Data.Abstractions; +using Data.Helpers; +using IW4MAdmin.Plugins.Stats.Config; +using Stats.Client.Abstractions; using WebfrontCore.Controllers.API.Validation; using WebfrontCore.Middleware; @@ -80,11 +81,14 @@ namespace WebfrontCore }); #if DEBUG - mvcBuilder = mvcBuilder.AddRazorRuntimeCompilation(); - services.Configure(_options => { - _options.ViewLocationFormats.Add(@"/Views/Plugins/{1}/{0}" + RazorViewEngine.ViewExtension); - }); + mvcBuilder = mvcBuilder.AddRazorRuntimeCompilation(); + services.Configure(_options => + { + _options.ViewLocationFormats.Add(@"/Views/Plugins/{1}/{0}" + RazorViewEngine.ViewExtension); + _options.ViewLocationFormats.Add("/Views/Plugins/Stats/Advanced.cshtml"); + }); + } #endif foreach (var asm in pluginAssemblies()) @@ -106,7 +110,8 @@ namespace WebfrontCore services.AddTransient, FindClientRequestValidator>(); services.AddSingleton, ClientService>(); services.AddSingleton, StatsResourceQueryHelper>(); - + services.AddSingleton, AdvancedClientStatsResourceQueryHelper>(); + services.AddSingleton(typeof(IDataValueCache<,>), typeof(DataValueCache<,>)); // todo: this needs to be handled more gracefully services.AddSingleton(Program.ApplicationServiceProvider.GetService()); services.AddSingleton(Program.ApplicationServiceProvider.GetService()); @@ -116,6 +121,12 @@ namespace WebfrontCore services.AddSingleton(Program.ApplicationServiceProvider.GetService()); services.AddSingleton(Program.ApplicationServiceProvider.GetService()); services.AddSingleton(Program.ApplicationServiceProvider.GetRequiredService()); + services.AddSingleton( + Program.ApplicationServiceProvider.GetRequiredService()); + services.AddSingleton(Program.ApplicationServiceProvider + .GetRequiredService>()); + services.AddSingleton(Program.ApplicationServiceProvider + .GetRequiredService>()); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/WebfrontCore/ViewComponents/PenaltyListViewComponent.cs b/WebfrontCore/ViewComponents/PenaltyListViewComponent.cs index ccb1ceae0..829656d3e 100644 --- a/WebfrontCore/ViewComponents/PenaltyListViewComponent.cs +++ b/WebfrontCore/ViewComponents/PenaltyListViewComponent.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; -using SharedLibraryCore.Database.Models; using System.Linq; using System.Threading.Tasks; +using Data.Models; namespace WebfrontCore.ViewComponents { diff --git a/WebfrontCore/ViewComponents/TopPlayersViewComponent.cs b/WebfrontCore/ViewComponents/TopPlayersViewComponent.cs new file mode 100644 index 000000000..6e92c5d5c --- /dev/null +++ b/WebfrontCore/ViewComponents/TopPlayersViewComponent.cs @@ -0,0 +1,42 @@ +using System.Linq; +using System.Threading.Tasks; +using IW4MAdmin.Plugins.Stats; +using IW4MAdmin.Plugins.Stats.Config; +using IW4MAdmin.Plugins.Stats.Helpers; +using Microsoft.AspNetCore.Mvc; +using SharedLibraryCore.Interfaces; + +namespace WebfrontCore.ViewComponents +{ + public class TopPlayersViewComponent : ViewComponent + { + private readonly IConfigurationHandler _configurationHandler; + + public TopPlayersViewComponent(IConfigurationHandler configurationHandler) + { + _configurationHandler = configurationHandler; + } + + public async Task InvokeAsync(int count, int offset, long? serverId = null) + { + if (serverId == 0) + { + serverId = null; + } + + var server = Plugin.ServerManager.GetServers().FirstOrDefault(_server => _server.EndPoint == serverId); + + if (server != null) + { + serverId = StatManager.GetIdForServer(server); + } + + + ViewBag.UseNewStats = _configurationHandler.Configuration().EnableAdvancedMetrics; + return View("~/Views/Client/Statistics/Components/TopPlayers/_List.cshtml", + _configurationHandler.Configuration().EnableAdvancedMetrics + ? await Plugin.Manager.GetNewTopStats(offset, count, serverId) + : await Plugin.Manager.GetTopStats(offset, count, serverId)); + } + } +} \ No newline at end of file diff --git a/WebfrontCore/ViewModels/PenaltyFilterInfo.cs b/WebfrontCore/ViewModels/PenaltyFilterInfo.cs index ad1d64369..81ef8a7c9 100644 --- a/WebfrontCore/ViewModels/PenaltyFilterInfo.cs +++ b/WebfrontCore/ViewModels/PenaltyFilterInfo.cs @@ -1,4 +1,4 @@ -using static SharedLibraryCore.Database.Models.EFPenalty; +using Data.Models; namespace WebfrontCore.ViewModels { @@ -15,7 +15,7 @@ namespace WebfrontCore.ViewModels /// /// show only a certain type of penalty /// - public PenaltyType ShowOnly { get; set; } + public EFPenalty.PenaltyType ShowOnly { get; set; } /// /// ignore penalties that are automated diff --git a/WebfrontCore/Views/Client/Find/Index.cshtml b/WebfrontCore/Views/Client/Find/Index.cshtml index edcf8e1d6..a0fc389e3 100644 --- a/WebfrontCore/Views/Client/Find/Index.cshtml +++ b/WebfrontCore/Views/Client/Find/Index.cshtml @@ -1,6 +1,6 @@ @model IList @{ - var loc = SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex; + var loc = Utilities.CurrentLocalization.LocalizationIndex; }
diff --git a/Plugins/Web/StatsWeb/Views/Stats/Message/Find.cshtml b/WebfrontCore/Views/Client/Message/Find.cshtml similarity index 100% rename from Plugins/Web/StatsWeb/Views/Stats/Message/Find.cshtml rename to WebfrontCore/Views/Client/Message/Find.cshtml diff --git a/Plugins/Web/StatsWeb/Views/Stats/Message/_Item.cshtml b/WebfrontCore/Views/Client/Message/_Item.cshtml similarity index 100% rename from Plugins/Web/StatsWeb/Views/Stats/Message/_Item.cshtml rename to WebfrontCore/Views/Client/Message/_Item.cshtml diff --git a/WebfrontCore/Views/Client/Profile/Index.cshtml b/WebfrontCore/Views/Client/Profile/Index.cshtml index 8d967d067..2b5386272 100644 --- a/WebfrontCore/Views/Client/Profile/Index.cshtml +++ b/WebfrontCore/Views/Client/Profile/Index.cshtml @@ -1,17 +1,16 @@ -@using SharedLibraryCore.Database.Models -@using SharedLibraryCore.Interfaces -@using SharedLibraryCore +@using SharedLibraryCore.Interfaces +@using Data.Models @model SharedLibraryCore.Dtos.PlayerInfo @{ string match = System.Text.RegularExpressions.Regex.Match(Model.Name.ToUpper(), "[A-Z]").Value; string shortCode = match == string.Empty ? "?" : match; var loc = SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex; string gravatarUrl = Model.Meta.FirstOrDefault(m => m.Key == "GravatarEmail")?.Value; - bool isFlagged = Model.LevelInt == (int)SharedLibraryCore.Database.Models.EFClient.Permission.Flagged; - bool isPermBanned = Model.LevelInt == (int)SharedLibraryCore.Database.Models.EFClient.Permission.Banned; + bool isFlagged = Model.LevelInt == (int) SharedLibraryCore.Database.Models.EFClient.Permission.Flagged; + bool isPermBanned = Model.LevelInt == (int) SharedLibraryCore.Database.Models.EFClient.Permission.Banned; bool isTempBanned = Model.ActivePenalty?.Type == EFPenalty.PenaltyType.TempBan; string translationKey = $"WEBFRONT_PROFILE_{Model.ActivePenalty?.Type.ToString().ToUpper()}_INFO"; - var ignoredMetaTypes = new[] { MetaType.Information, MetaType.Other, MetaType.QuickMessage }; + var ignoredMetaTypes = new[] {MetaType.Information, MetaType.Other, MetaType.QuickMessage}; }
@@ -25,7 +24,9 @@
-
+
+ +
@if (ViewBag.Authorized) {
@@ -37,16 +38,18 @@
@foreach (var linked in Model.LinkedAccounts) { - @Html.ActionLink(linked.Value.ToString("X"), "ProfileAsync", "Client", new { id = linked.Key }, new { @class = "link-inverse" })
+ @Html.ActionLink(linked.Value.ToString("X"), "ProfileAsync", "Client", new {id = linked.Key}, new {@class = "link-inverse"})
} - @foreach (string alias in Model.Aliases) + @foreach (var alias in Model.Aliases) { -
+ +
} @foreach (string ip in Model.IPs) { - @ip
+ @ip +
}
} @@ -62,7 +65,7 @@ break; case "time": - @Utilities.HumanizeForCurrentCulture(Model.ActivePenalty.Expires.Value - DateTime.UtcNow) + @((Model.ActivePenalty.Expires.Value - DateTime.UtcNow).HumanizeForCurrentCulture()) break; default: @@ -89,20 +92,21 @@ } }
- @if (ViewBag.Authorized) - { -
+ +
+ @if (ViewBag.Authorized) + { @if (!isPermBanned) { } - @if (Model.LevelInt < (int)ViewBag.User.Level && !Model.HasActivePenalty) + @if (Model.LevelInt < (int) ViewBag.User.Level && !Model.HasActivePenalty) { } - @if (Model.LevelInt < (int)ViewBag.User.Level && Model.HasActivePenalty) + @if (Model.LevelInt < (int) ViewBag.User.Level && Model.HasActivePenalty) { @if (isTempBanned) { @@ -120,12 +124,16 @@ { } -
- } + } + @if (ViewBag.UseNewStats) + { + + } +
- +
@@ -136,7 +144,9 @@
@ViewBag.Localization["META_TYPE_ALL_NAME"] + asp-route-id="@Model.ClientId"> + @ViewBag.Localization["META_TYPE_ALL_NAME"] + @foreach (MetaType type in Enum.GetValues(typeof(MetaType))) { @@ -146,7 +156,9 @@ class="nav-link p-2 pl-3 pr-3 text-center col-12 col-md-auto text-md-left @(Model.MetaFilterType.HasValue && Model.MetaFilterType.Value.ToString() == type.ToString() ? "btn-primary text-white" : "text-muted")" asp-route-id="@Model.ClientId" asp-route-metaFilterType="@type" - data-meta-type="@type">@type.ToTranslatedName() + data-meta-type="@type"> + @type.ToTranslatedName() + } }
@@ -166,7 +178,7 @@
@section targetid { - + } @section scripts { @@ -175,4 +187,4 @@ -} +} \ No newline at end of file diff --git a/WebfrontCore/Views/Client/Profile/Meta/_AdministeredPenaltyResponse.cshtml b/WebfrontCore/Views/Client/Profile/Meta/_AdministeredPenaltyResponse.cshtml index bb573a236..3023b51ad 100644 --- a/WebfrontCore/Views/Client/Profile/Meta/_AdministeredPenaltyResponse.cshtml +++ b/WebfrontCore/Views/Client/Profile/Meta/_AdministeredPenaltyResponse.cshtml @@ -26,7 +26,7 @@ else if (match.MatchValue == "reason") { - @if (ViewBag.Authorized && !string.IsNullOrEmpty(Model.AutomatedOffense) && Model.PenaltyType != SharedLibraryCore.Database.Models.EFPenalty.PenaltyType.Warning) + @if (ViewBag.Authorized && !string.IsNullOrEmpty(Model.AutomatedOffense) && Model.PenaltyType != Data.Models.EFPenalty.PenaltyType.Warning) { @Utilities.FormatExt(ViewBag.Localization["WEBFRONT_PROFILE_ANTICHEAT_DETECTION"], Model.AutomatedOffense) diff --git a/WebfrontCore/Views/Client/Profile/Meta/_ReceivedPenaltyResponse.cshtml b/WebfrontCore/Views/Client/Profile/Meta/_ReceivedPenaltyResponse.cshtml index 205d99334..709bf63e9 100644 --- a/WebfrontCore/Views/Client/Profile/Meta/_ReceivedPenaltyResponse.cshtml +++ b/WebfrontCore/Views/Client/Profile/Meta/_ReceivedPenaltyResponse.cshtml @@ -1,5 +1,4 @@ @using SharedLibraryCore.Dtos.Meta.Responses -@using SharedLibraryCore @model ReceivedPenaltyResponse @{ @@ -28,7 +27,7 @@ else if (match.MatchValue == "reason") { - @if (ViewBag.Authorized && !string.IsNullOrEmpty(Model.AutomatedOffense) && Model.PenaltyType != SharedLibraryCore.Database.Models.EFPenalty.PenaltyType.Warning && Model.PenaltyType != SharedLibraryCore.Database.Models.EFPenalty.PenaltyType.Kick) + @if (ViewBag.Authorized && !string.IsNullOrEmpty(Model.AutomatedOffense) && Model.PenaltyType != Data.Models.EFPenalty.PenaltyType.Warning && Model.PenaltyType != Data.Models.EFPenalty.PenaltyType.Kick) { @Utilities.FormatExt(ViewBag.Localization["WEBFRONT_PROFILE_ANTICHEAT_DETECTION"], Model.AutomatedOffense) diff --git a/WebfrontCore/Views/Client/Statistics/Advanced.cshtml b/WebfrontCore/Views/Client/Statistics/Advanced.cshtml new file mode 100644 index 000000000..f918c4cb2 --- /dev/null +++ b/WebfrontCore/Views/Client/Statistics/Advanced.cshtml @@ -0,0 +1,457 @@ +@using SharedLibraryCore.Configuration +@using Data.Models.Client.Stats +@using Stats.Helpers +@using Data.Models.Client +@using Data.Models.Client.Stats.Reference +@using Humanizer +@using Humanizer.Localisation +@using IW4MAdmin.Plugins.Stats +@model Stats.Dtos.AdvancedStatsInfo +@{ + ViewBag.Title = "Advanced Client Statistics"; + ViewBag.Description = Model.ClientName; + + const int maxItems = 5; + const string headshotKey = "MOD_HEAD_SHOT"; + const string meleeKey = "MOD_MELEE"; + + var suicideKeys = new[] {"MOD_SUICIDE", "MOD_FALLING"}; + var config = (GameStringConfiguration) ViewBag.Config; + + var headerClass = Model.Level == EFClient.Permission.Banned ? "bg-danger" : "bg-primary"; + var textClass = Model.Level == EFClient.Permission.Banned ? "text-danger" : "text-primary"; + var borderBottomClass = Model.Level == EFClient.Permission.Banned ? "border-bottom-danger border-top-danger" : "border-bottom border-top"; + var borderClass = Model.Level == EFClient.Permission.Banned ? "border-danger" : "border-primary"; + var buttonClass = Model.Level == EFClient.Permission.Banned ? "btn-danger" : "btn-primary"; + + string GetWeaponNameForHit(EFClientHitStatistic stat) + { + if (stat == null) + { + return null; + } + var rebuiltName = stat.RebuildWeaponName(); + var name = config.GetStringForGame(rebuiltName); + return !rebuiltName.Equals(name, StringComparison.InvariantCultureIgnoreCase) + ? name + : config.GetStringForGame(stat.Weapon.Name); + } + + string GetWeaponAttachmentName(EFWeaponAttachmentCombo attachment) + { + if (attachment == null) + { + return null; + } + + var attachmentText = string.Join('+', new[] + { + config.GetStringForGame(attachment.Attachment1.Name), + config.GetStringForGame(attachment.Attachment2?.Name), + config.GetStringForGame(attachment.Attachment3?.Name) + }.Where(attach => !string.IsNullOrWhiteSpace(attach))); + + return attachmentText; + } + + var weapons = Model.ByWeapon + .Where(hit => hit.DamageInflicted > 0) + .GroupBy(hit => new {hit.WeaponId}) + .Select(group => + { + var withoutAttachments = group.FirstOrDefault(hit => hit.WeaponAttachmentComboId == null); + var mostUsedAttachment = group.Except(new[] {withoutAttachments}) + .OrderByDescending(g => g.DamageInflicted) + .GroupBy(g => g.WeaponAttachmentComboId) + .FirstOrDefault() + ?.FirstOrDefault(); + + if (withoutAttachments == null || mostUsedAttachment == null) + { + return withoutAttachments; + } + + withoutAttachments.WeaponAttachmentComboId = mostUsedAttachment.WeaponAttachmentComboId; + withoutAttachments.WeaponAttachmentCombo = mostUsedAttachment.WeaponAttachmentCombo; + + return withoutAttachments; + }) + .Where(hit => hit != null) + .OrderByDescending(hit => hit.KillCount) + .ToList(); + + var allPerServer = Model.All.Where(hit => hit.ServerId == Model.ServerId).ToList(); + + // if the serverId is supplied we want all the entries with serverID but nothing else + var aggregate = Model.ServerId == null + ? Model.Aggregate + : allPerServer.Where(hit => hit.WeaponId == null) + .Where(hit => hit.HitLocation == null) + .Where(hit => hit.ServerId == Model.ServerId) + .Where(hit => hit.WeaponAttachmentComboId == null) + .FirstOrDefault(hit => hit.MeansOfDeathId == null); + + var filteredHitLocations = Model.ByHitLocation + .Where(hit => hit.HitCount > 0) + .Where(hit => hit.HitLocation.Name != "none") + .Where(hit => hit.HitLocation.Name != "neck") + .Where(hit => hit.ServerId == Model.ServerId) + .OrderByDescending(hit => hit.HitCount) + .ThenBy(hit => hit.HitLocationId) + .ToList(); + + var uniqueWeapons = allPerServer.Any() + ? Model.ByWeapon.Where(hit => hit.ServerId == Model.ServerId) + .Where(weapon => weapon.DamageInflicted > 0) + .GroupBy(weapon => weapon.WeaponId) + .Count() + : (int?) null; // want to default to -- in ui instead of 0 + + var activeTime = weapons.Any() + ? TimeSpan.FromSeconds(weapons.Sum(weapon => weapon.UsageSeconds ?? 0)) + : (TimeSpan?) null; // want to default to -- in ui instead of 0 + + var kdr = aggregate == null + ? null + : Math.Round(aggregate.KillCount / (float) aggregate.DeathCount, 2).ToString(Utilities.CurrentLocalization.Culture); + + var serverLegacyStat = Model.LegacyStats + .FirstOrDefault(stat => stat.ServerId == Model.ServerId); + + // legacy stats section + var performance = Model.Performance; + var skill = Model.ServerId != null ? serverLegacyStat?.Skill.ToNumericalString() : Model.LegacyStats.WeightValueByPlaytime(nameof(EFClientStatistics.Skill), 0).ToNumericalString(); + var elo = Model.ServerId != null ? serverLegacyStat?.EloRating.ToNumericalString() : Model.LegacyStats.WeightValueByPlaytime(nameof(EFClientStatistics.EloRating), 0).ToNumericalString(); + var spm = Model.ServerId != null ? serverLegacyStat?.SPM.ToNumericalString() : Model.LegacyStats.WeightValueByPlaytime(nameof(EFClientStatistics.SPM), 0).ToNumericalString(); + + var performanceHistory = Model.Ratings + .Select(rating => rating.PerformanceMetric); + + if (performance != null) + { + performanceHistory = performanceHistory.Append(performance.Value); + } + + var score = allPerServer.Any() + ? allPerServer.Sum(stat => stat.Score) + : null; + + var headShots = allPerServer.Any() + ? allPerServer.Where(hit => hit.MeansOfDeath?.Name == headshotKey).Sum(hit => hit.HitCount) + : (int?) null; // want to default to -- in ui instead of 0 + + var meleeKills = allPerServer.Any() + ? allPerServer.Where(hit => hit.MeansOfDeath?.Name == meleeKey).Sum(hit => hit.KillCount) + : (int?) null; + + var suicides = allPerServer.Any() + ? allPerServer.Where(hit => suicideKeys.Contains(hit.MeansOfDeath?.Name ?? "")).Sum(hit => hit.KillCount) + : (int?) null; + + var statCards = new[] + { + new + { + Name = (ViewBag.Localization["PLUGINS_STATS_TEXT_KILLS"] as string).Titleize(), + Value = aggregate?.KillCount.ToNumericalString() + }, + new + { + Name = (ViewBag.Localization["PLUGINS_STATS_TEXT_DEATHS"] as string).Titleize(), + Value = aggregate?.DeathCount.ToNumericalString() + }, + new + { + Name = (ViewBag.Localization["PLUGINS_STATS_TEXT_KDR"] as string).Titleize(), + Value = kdr + }, + new + { + Name = (ViewBag.Localization["WEBFRONT_ADV_STATS_SCORE"] as string).Titleize(), + Value = score.ToNumericalString() + }, + new + { + Name = (ViewBag.Localization["WEBFRONT_ADV_STATS_ZSCORE"] as string), + Value = Model.ZScore.ToNumericalString(2) + }, + new + { + Name = (ViewBag.Localization["PLUGINS_STATS_TEXT_SKILL"] as string).ToLower().Titleize(), + Value = skill + }, + new + { + Name = (ViewBag.Localization["WEBFRONT_ADV_STATS_ELO"] as string).Titleize(), + Value = elo + }, + new + { + Name = (ViewBag.Localization["PLUGINS_STATS_META_SPM"] as string).Titleize(), + Value = spm + }, + new + { + Name = ViewBag.Localization["WEBFRONT_ADV_STATS_TOTAL_DAMAGE"] as string, + Value = aggregate?.DamageInflicted.ToNumericalString() + }, + new + { + Name = ViewBag.Localization["WEBFRONT_ADV_STATS_SUICIDES"] as string, + Value = suicides.ToNumericalString() + }, + new + { + Name = ViewBag.Localization["WEBFRONT_ADV_STATS_HEADSHOTS"] as string, + Value = headShots.ToNumericalString() + }, + new + { + Name = ViewBag.Localization["WEBFRONT_ADV_STATS_MELEES"] as string, + Value = meleeKills.ToNumericalString() + }, + new + { + Name = ViewBag.Localization["WEBFRONT_ADV_STATS_FAV_WEAP"] as string, + Value = GetWeaponNameForHit(weapons.FirstOrDefault()) + }, + new + { + Name = ViewBag.Localization["WEBFRONT_ADV_STATS_FAV_ATTACHMENTS"] as string, + Value = GetWeaponAttachmentName(weapons.FirstOrDefault()?.WeaponAttachmentCombo) + }, + new + { + Name = ViewBag.Localization["WEBFRONT_ADV_STATS_TOTAL_WEAPONS_USED"] as string, + Value = uniqueWeapons.ToNumericalString() + }, + new + { + Name = ViewBag.Localization["WEBFRONT_ADV_STATS_TOTAL_ACTIVE_TIME"] as string, + Value = activeTime?.HumanizeForCurrentCulture() + } + }; +} + +
+ +
+
+ +
+
+ @performance +
+
+ + @if (Model.Level == EFClient.Permission.Banned) + { +
@ViewBag.Localization["GLOBAL_PERMISSION_BANNED"]
+ } + else if (Model.ZScore != null) + { + if (Model.ServerId != null) + { +
@((ViewBag.Localization["WEBFRONT_ADV_STATS_PERFORMANCE"] as string).FormatExt(performance.ToNumericalString()))
+ } + + else + { +
@((ViewBag.Localization["WEBFRONT_ADV_STATS_RATING"] as string).FormatExt(Model.Rating.ToNumericalString()))
+ } + + if (Model.Ranking > 0) + { +
@((ViewBag.Localization["WEBFRONT_ADV_STATS_RANKED"] as string).FormatExt(Model.Ranking.ToNumericalString()))
+ } + + else + { +
@ViewBag.Localization["WEBFRONT_ADV_STATS_EXPIRED"]
+ } + } + + else + { +
@ViewBag.Localization["WEBFRONT_STATS_INDEX_UNRANKED"]
+ } +
+
+
+ +
+ +
+ +
+ @foreach (var card in statCards) + { +
+ @if (string.IsNullOrWhiteSpace(card.Value)) + { +
+ } + else + { +
@card.Value
+ } +
@card.Name
+
+ } +
+
+ +
+
+
@ViewBag.Localization["WEBFRONT_ADV_STATS_WEAP_USAGE"]
+
+ + + + + + + + + + @foreach (var weaponHit in weapons.Take(maxItems)) + { + + + @{ var attachments = GetWeaponAttachmentName(weaponHit.WeaponAttachmentCombo); } + @if (string.IsNullOrWhiteSpace(attachments)) + { + + } + else + { + + } + + + + + + } + + @foreach (var weaponHit in weapons.Skip(maxItems)) + { + + + @{ var attachments = GetWeaponAttachmentName(weaponHit.WeaponAttachmentCombo); } + @if (string.IsNullOrWhiteSpace(attachments)) + { + + } + else + { + + } + + + + + + } + +
@ViewBag.Localization["WEBFRONT_ADV_STATS_WEAPON"]@ViewBag.Localization["WEBFRONT_ADV_STATS_FAV_ATTACHMENTS"]@ViewBag.Localization["WEBFRONT_ADV_STATS_KILLS"]@ViewBag.Localization["WEBFRONT_ADV_STATS_HITS"]@ViewBag.Localization["WEBFRONT_ADV_STATS_DAMAGE"]@ViewBag.Localization["WEBFRONT_ADV_STATS_USAGE"]
@GetWeaponNameForHit(weaponHit)@attachments@weaponHit.KillCount.ToNumericalString()@weaponHit.HitCount.ToNumericalString()@weaponHit.DamageInflicted.ToNumericalString()@TimeSpan.FromSeconds(weaponHit.UsageSeconds ?? 0).HumanizeForCurrentCulture(minUnit: TimeUnit.Second)
+ +
+
+
+ +
+
+
@ViewBag.Localization["WEBFRONT_ADV_STATS_HIT_LOCATIONS"]
+
+ + + + + + + + @{ + var totalHits = filteredHitLocations.Sum(hit => hit.HitCount); + } + @foreach (var hitLocation in filteredHitLocations.Take(8)) + { + + + + + + + } + + @foreach (var hitLocation in filteredHitLocations.Skip(8)) + { + + + + + + + } +
@ViewBag.Localization["WEBFRONT_ADV_STATS_LOCATION"]@ViewBag.Localization["WEBFRONT_ADV_STATS_HITS"]@ViewBag.Localization["WEBFRONT_ADV_STATS_PERCENTAGE"]@ViewBag.Localization["WEBFRONT_ADV_STATS_DAMAGE"]
@config.GetStringForGame(hitLocation.HitLocation.Name)@hitLocation.HitCount@Math.Round((hitLocation.HitCount / (float) totalHits) * 100.0).ToString(Utilities.CurrentLocalization.Culture)%@hitLocation.DamageInflicted.ToNumericalString()
+ +
+
+
+ + +
+
+
+ +@{ + var projection = filteredHitLocations.Select(loc => new + { + name = loc.HitLocation.Name, + // we want to count head and neck as the same + percentage = (loc.HitLocation.Name == "head" + ? filteredHitLocations.FirstOrDefault(c => c.HitLocation.Name == "neck")?.HitCount ?? 0 + loc.HitCount + : loc.HitCount) / (float) totalHits + }).ToList(); + var maxPercentage = projection.Any() ? projection.Max(p => p.percentage) : 0; +} + +@section scripts +{ + + + + + +} \ No newline at end of file diff --git a/WebfrontCore/Views/Client/Statistics/Components/TopPlayers/_List.cshtml b/WebfrontCore/Views/Client/Statistics/Components/TopPlayers/_List.cshtml new file mode 100644 index 000000000..b53759e44 --- /dev/null +++ b/WebfrontCore/Views/Client/Statistics/Components/TopPlayers/_List.cshtml @@ -0,0 +1,151 @@ +@using IW4MAdmin.Plugins.Stats +@model List +@{ + Layout = null; + var loc = Utilities.CurrentLocalization.LocalizationIndex.Set; + double getDeviation(double deviations) => Math.Pow(Math.E, 5.259 + (deviations * 0.812)); + string rankIcon(double? elo) + { + if (elo >= getDeviation(-0.75) && elo < getDeviation(1.25)) + { + return "0_no-place/menu_div_no_place.png"; + } + if (elo >= getDeviation(0.125) && elo < getDeviation(0.625)) + { + return "1_iron/menu_div_iron_sub03.png"; + } + if (elo >= getDeviation(0.625) && elo < getDeviation(1.0)) + { + return "2_bronze/menu_div_bronze_sub03.png"; + } + if (elo >= getDeviation(1.0) && elo < getDeviation(1.25)) + { + return "3_silver/menu_div_silver_sub03.png"; + } + if (elo >= getDeviation(1.25) && elo < getDeviation(1.5)) + { + return "4_gold/menu_div_gold_sub03.png"; + } + if (elo >= getDeviation(1.5) && elo < getDeviation(1.75)) + { + return "5_platinum/menu_div_platinum_sub03.png"; + } + if (elo >= getDeviation(1.75) && elo < getDeviation(2.0)) + { + return "6_semipro/menu_div_semipro_sub03.png"; + } + if (elo >= getDeviation(2.0)) + { + return "7_pro/menu_div_pro_sub03.png"; + } + + return "0_no-place/menu_div_no_place.png"; + } +} + +@if (Model.Count == 0) +{ +
@Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_NOQUALIFY"]
+} +@foreach (var stat in Model) +{ +
+ @if (ViewBag.UseNewStats) + { + @stat.Performance + } +
+
+
#@stat.Ranking
+ @if (stat.RatingChange > 0) + { +
+
+
@stat.RatingChange
+
+ } + @if (stat.RatingChange < 0) + { +
+
@Math.Abs(stat.RatingChange)
+
+
+ } + + @if (!ViewBag.UseNewStats) + { + + + + } + else + { + + + + } +
+ + @if (ViewBag.UseNewStats) + { +
+
+ + @stat.Performance.ToNumericalString() + + @if (stat.ServerId == null) + { + @loc["WEBFRONT_ADV_STATS_RATING"].FormatExt("").ToLower() + } + + else + { + @loc["WEBFRONT_ADV_STATS_PERFORMANCE"].FormatExt("").ToLower() + } +
+
+ @stat.Kills.ToNumericalString() @loc["PLUGINS_STATS_TEXT_KILLS"] +
+
+ @stat.Deaths.ToNumericalString() @loc["PLUGINS_STATS_TEXT_DEATHS"]
+
+
+ @stat.KDR @loc["PLUGINS_STATS_TEXT_KDR"] +
+
+ @stat.TimePlayedValue.HumanizeForCurrentCulture() @loc["WEBFRONT_PROFILE_PLAYER"] +
+
+ @stat.LastSeenValue.HumanizeForCurrentCulture() @loc["WEBFRONT_PROFILE_LSEEN"] +
+
+ } + else + { + @stat.Performance @loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"] +
+ @stat.KDR @loc["PLUGINS_STATS_TEXT_KDR"] + @stat.Kills @loc["PLUGINS_STATS_TEXT_KILLS"] + @stat.Deaths @loc["PLUGINS_STATS_TEXT_DEATHS"]
+ @loc["WEBFRONT_PROFILE_PLAYER"] @stat.TimePlayed @loc["GLOBAL_TIME_HOURS"]
+ @loc["WEBFRONT_PROFILE_LSEEN"] @stat.LastSeen @loc["WEBFRONT_PENALTY_TEMPLATE_AGO"] + } +
+ +
+ +
+ +
+ @if (ViewBag.UseNewStats) + { + @stat.Performance + } + + else + { + + } +
+
+} diff --git a/Plugins/Web/StatsWeb/Views/Stats/Index.cshtml b/WebfrontCore/Views/Client/Statistics/Index.cshtml similarity index 97% rename from Plugins/Web/StatsWeb/Views/Stats/Index.cshtml rename to WebfrontCore/Views/Client/Statistics/Index.cshtml index 7d8ace74c..64d931fe1 100644 --- a/Plugins/Web/StatsWeb/Views/Stats/Index.cshtml +++ b/WebfrontCore/Views/Client/Statistics/Index.cshtml @@ -14,7 +14,7 @@
- @await Component.InvokeAsync("TopPlayers", new { count = 10, offset = 0 }) + @await Component.InvokeAsync("TopPlayers", new { count = 25, offset = 0 })
@foreach (var server in ViewBag.Servers) @@ -30,5 +30,5 @@ - + } diff --git a/Plugins/Web/StatsWeb/Views/Stats/_MessageContext.cshtml b/WebfrontCore/Views/Client/_MessageContext.cshtml similarity index 100% rename from Plugins/Web/StatsWeb/Views/Stats/_MessageContext.cshtml rename to WebfrontCore/Views/Client/_MessageContext.cshtml diff --git a/Plugins/Web/StatsWeb/Views/Stats/_PenaltyInfo.cshtml b/WebfrontCore/Views/Client/_PenaltyInfo.cshtml similarity index 91% rename from Plugins/Web/StatsWeb/Views/Stats/_PenaltyInfo.cshtml rename to WebfrontCore/Views/Client/_PenaltyInfo.cshtml index fe20c4bea..fc4f9c5ad 100644 --- a/Plugins/Web/StatsWeb/Views/Stats/_PenaltyInfo.cshtml +++ b/WebfrontCore/Views/Client/_PenaltyInfo.cshtml @@ -1,4 +1,4 @@ -@model IEnumerable +@model IEnumerable @{ Layout = null; } diff --git a/WebfrontCore/Views/Penalty/List.cshtml b/WebfrontCore/Views/Penalty/List.cshtml index 0966054c4..1df43ff42 100644 --- a/WebfrontCore/Views/Penalty/List.cshtml +++ b/WebfrontCore/Views/Penalty/List.cshtml @@ -1,4 +1,4 @@ -@model SharedLibraryCore.Database.Models.EFPenalty.PenaltyType +@model Data.Models.EFPenalty.PenaltyType @{ var loc = SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex; } @@ -7,11 +7,11 @@