Compare commits
163 Commits
2022.02.14
...
2022.06.16
Author | SHA1 | Date | |
---|---|---|---|
a92f9fc29c | |||
fbf424c77d | |||
b8e001fcfe | |||
5ab5b73ecf | |||
4534d24fe6 | |||
73c8d0da33 | |||
16d75470b5 | |||
f02552faa1 | |||
a4923d03f9 | |||
8ae6561f4e | |||
deeb1dea87 | |||
9ab34614c5 | |||
2cff25d6b3 | |||
df3e226dc9 | |||
ef3db63ba7 | |||
49fe4520ff | |||
6587187a34 | |||
b337e232a2 | |||
a44b4e9475 | |||
ffb0e5cac1 | |||
ecc2b5bf54 | |||
2ac9cc4379 | |||
215037095f | |||
5433d7d1d2 | |||
0446fe1ec5 | |||
cf2a00e5b3 | |||
ab494a22cb | |||
b690579154 | |||
acc967e50a | |||
c493fbe13d | |||
ee56a5db1f | |||
f235d0fafd | |||
7ecf516278 | |||
210f1ca336 | |||
a38789adb9 | |||
e459b2fcde | |||
26853a0005 | |||
ee14306db9 | |||
169105e849 | |||
7c10e0e3de | |||
2f7eb07e39 | |||
0f9f4f597b | |||
880f9333d9 | |||
31da5d352e | |||
83a469cae3 | |||
1f13f9122c | |||
dd8c4f438f | |||
2230036d45 | |||
1700b7da91 | |||
fab97ccad4 | |||
0bf0d033f7 | |||
2bbabcb9e8 | |||
1995dbd080 | |||
a3b94b50e3 | |||
bc38b36e4a | |||
e346aa037e | |||
389c687420 | |||
074e36413e | |||
104d9bdc4c | |||
c51d28937b | |||
ffa8a46feb | |||
91c46dbdd4 | |||
ff0d22c142 | |||
3ad4aa2196 | |||
d462892467 | |||
cabedb6f0b | |||
5b7f5160b2 | |||
0a8e415af8 | |||
7b3ddd58c6 | |||
ed1032415e | |||
35b43e7438 | |||
284c2e9726 | |||
fd049edb3f | |||
4884abee76 | |||
1df76b6ac3 | |||
4c42a1d511 | |||
27635a6dd3 | |||
0175425708 | |||
5e12bf60b5 | |||
2f10ca8599 | |||
62ec18309e | |||
87361bf3d7 | |||
dc45136077 | |||
21b0a7998d | |||
20b8f0b99a | |||
314ff96e71 | |||
d7c4f5452c | |||
3cb50635e5 | |||
4fbe0ee0ed | |||
4023ca37d4 | |||
425ec2621d | |||
15c3ca53e2 | |||
6097ca504c | |||
70cd01eafb | |||
a2d5e37c6f | |||
19bd47d0f4 | |||
dc97956bc3 | |||
89fdc00f9b | |||
039a05d9ad | |||
25fb5fdc14 | |||
1e67f6e86c | |||
7dbdf87728 | |||
180a4911bc | |||
31123d9a33 | |||
3cf0f54ceb | |||
eafd7cb530 | |||
770785e979 | |||
92d713d188 | |||
34e531ef8d | |||
724992ef33 | |||
557cc1614f | |||
f90cdbef16 | |||
a863f78678 | |||
c93f896bc5 | |||
ccc8316a2f | |||
497c15a6a8 | |||
7be096e0b6 | |||
20858991e1 | |||
85d44b0eb0 | |||
51ef67ae9c | |||
63b04be4c7 | |||
36eb45bb2e | |||
04a4dcf153 | |||
b46b1eb5e7 | |||
287635fa36 | |||
f567a03fa7 | |||
1b6d8107ae | |||
1e8f06f3a3 | |||
064879fead | |||
e32e97b9e6 | |||
42313b7816 | |||
9f4d06c265 | |||
acf66da4ca | |||
59ca399045 | |||
ef70496546 | |||
e6e56d8d14 | |||
55b0caf900 | |||
a4c3f9c2d1 | |||
241aa0a5f6 | |||
ec0f59cdb1 | |||
59d69bd22b | |||
e9c8ead829 | |||
58d48a211e | |||
edf8e03b04 | |||
de2e804b84 | |||
b087d4c8de | |||
bd6c0dd5be | |||
4ace476242 | |||
bb7215dbb6 | |||
88bd47f3ae | |||
39a1066c74 | |||
18f3c59b9b | |||
0d88b6293f | |||
a6b56ceded | |||
78ef977268 | |||
d527a86911 | |||
2e531c4a50 | |||
45059fcfd9 | |||
482cd9c339 | |||
51667159a2 | |||
ea18a286b2 | |||
9a6d7c6a20 | |||
adcb75319c |
1
.gitignore
vendored
1
.gitignore
vendored
@ -224,7 +224,6 @@ bootstrap-custom.min.css
|
||||
bootstrap-custom.css
|
||||
**/Master/static
|
||||
**/Master/dev_env
|
||||
/WebfrontCore/Views/Plugins/*
|
||||
/WebfrontCore/wwwroot/**/dds
|
||||
/WebfrontCore/wwwroot/images/radar/*
|
||||
|
||||
|
55
Application/Alerts/AlertExtensions.cs
Normal file
55
Application/Alerts/AlertExtensions.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Alerts;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
|
||||
namespace IW4MAdmin.Application.Alerts;
|
||||
|
||||
public static class AlertExtensions
|
||||
{
|
||||
public static Alert.AlertState BuildAlert(this EFClient client, Alert.AlertCategory? type = null)
|
||||
{
|
||||
return new Alert.AlertState
|
||||
{
|
||||
RecipientId = client.ClientId,
|
||||
Category = type ?? Alert.AlertCategory.Information
|
||||
};
|
||||
}
|
||||
|
||||
public static Alert.AlertState WithCategory(this Alert.AlertState state, Alert.AlertCategory category)
|
||||
{
|
||||
state.Category = category;
|
||||
return state;
|
||||
}
|
||||
|
||||
public static Alert.AlertState OfType(this Alert.AlertState state, string type)
|
||||
{
|
||||
state.Type = type;
|
||||
return state;
|
||||
}
|
||||
|
||||
public static Alert.AlertState WithMessage(this Alert.AlertState state, string message)
|
||||
{
|
||||
state.Message = message;
|
||||
return state;
|
||||
}
|
||||
|
||||
public static Alert.AlertState ExpiresIn(this Alert.AlertState state, TimeSpan expiration)
|
||||
{
|
||||
state.ExpiresAt = DateTime.Now.Add(expiration);
|
||||
return state;
|
||||
}
|
||||
|
||||
public static Alert.AlertState FromSource(this Alert.AlertState state, string source)
|
||||
{
|
||||
state.Source = source;
|
||||
return state;
|
||||
}
|
||||
|
||||
public static Alert.AlertState FromClient(this Alert.AlertState state, EFClient client)
|
||||
{
|
||||
state.Source = client.Name.StripColors();
|
||||
state.SourceId = client.ClientId;
|
||||
return state;
|
||||
}
|
||||
}
|
137
Application/Alerts/AlertManager.cs
Normal file
137
Application/Alerts/AlertManager.cs
Normal file
@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SharedLibraryCore.Alerts;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
|
||||
namespace IW4MAdmin.Application.Alerts;
|
||||
|
||||
public class AlertManager : IAlertManager
|
||||
{
|
||||
private readonly ApplicationConfiguration _appConfig;
|
||||
private readonly ConcurrentDictionary<int, List<Alert.AlertState>> _states = new();
|
||||
private readonly List<Func<Task<IEnumerable<Alert.AlertState>>>> _staticSources = new();
|
||||
|
||||
public AlertManager(ApplicationConfiguration appConfig)
|
||||
{
|
||||
_appConfig = appConfig;
|
||||
_states.TryAdd(0, new List<Alert.AlertState>());
|
||||
}
|
||||
|
||||
public EventHandler<Alert.AlertState> OnAlertConsumed { get; set; }
|
||||
|
||||
public async Task Initialize()
|
||||
{
|
||||
foreach (var source in _staticSources)
|
||||
{
|
||||
var alerts = await source();
|
||||
foreach (var alert in alerts)
|
||||
{
|
||||
AddAlert(alert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Alert.AlertState> RetrieveAlerts(EFClient client)
|
||||
{
|
||||
lock (_states)
|
||||
{
|
||||
var alerts = Enumerable.Empty<Alert.AlertState>();
|
||||
if (client.Level > Data.Models.Client.EFClient.Permission.Trusted)
|
||||
{
|
||||
alerts = alerts.Concat(_states[0].Where(alert =>
|
||||
alert.MinimumPermission is null || alert.MinimumPermission <= client.Level));
|
||||
}
|
||||
|
||||
if (_states.ContainsKey(client.ClientId))
|
||||
{
|
||||
alerts = alerts.Concat(_states[client.ClientId].AsReadOnly());
|
||||
}
|
||||
|
||||
return alerts.OrderByDescending(alert => alert.OccuredAt);
|
||||
}
|
||||
}
|
||||
|
||||
public void MarkAlertAsRead(Guid alertId)
|
||||
{
|
||||
lock (_states)
|
||||
{
|
||||
foreach (var items in _states.Values)
|
||||
{
|
||||
var matchingEvent = items.FirstOrDefault(item => item.AlertId == alertId);
|
||||
|
||||
if (matchingEvent is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
items.Remove(matchingEvent);
|
||||
OnAlertConsumed?.Invoke(this, matchingEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MarkAllAlertsAsRead(int recipientId)
|
||||
{
|
||||
lock (_states)
|
||||
{
|
||||
foreach (var items in _states.Values)
|
||||
{
|
||||
items.RemoveAll(item =>
|
||||
{
|
||||
if (item.RecipientId != null && item.RecipientId != recipientId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
OnAlertConsumed?.Invoke(this, item);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddAlert(Alert.AlertState alert)
|
||||
{
|
||||
lock (_states)
|
||||
{
|
||||
if (alert.RecipientId is null)
|
||||
{
|
||||
_states[0].Add(alert);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_states.ContainsKey(alert.RecipientId.Value))
|
||||
{
|
||||
_states[alert.RecipientId.Value] = new List<Alert.AlertState>();
|
||||
}
|
||||
|
||||
if (_appConfig.MinimumAlertPermissions.ContainsKey(alert.Type))
|
||||
{
|
||||
alert.MinimumPermission = _appConfig.MinimumAlertPermissions[alert.Type];
|
||||
}
|
||||
|
||||
_states[alert.RecipientId.Value].Add(alert);
|
||||
|
||||
PruneOldAlerts();
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterStaticAlertSource(Func<Task<IEnumerable<Alert.AlertState>>> alertSource)
|
||||
{
|
||||
_staticSources.Add(alertSource);
|
||||
}
|
||||
|
||||
|
||||
private void PruneOldAlerts()
|
||||
{
|
||||
foreach (var value in _states.Values)
|
||||
{
|
||||
value.RemoveAll(item => item.ExpiresAt < DateTime.UtcNow);
|
||||
}
|
||||
}
|
||||
}
|
@ -24,7 +24,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Jint" Version="3.0.0-beta-2037" />
|
||||
<PackageReference Include="Jint" Version="3.0.0-beta-2038" />
|
||||
<PackageReference Include="MaxMind.GeoIP2" Version="5.1.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
@ -63,6 +64,9 @@
|
||||
<None Update="Configuration\LoggingConfiguration.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Resources\GeoLite2-Country.mmdb">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||
|
@ -57,9 +57,9 @@ namespace IW4MAdmin.Application
|
||||
private readonly List<MessageToken> MessageTokens;
|
||||
private readonly ClientService ClientSvc;
|
||||
readonly PenaltyService PenaltySvc;
|
||||
private readonly IAlertManager _alertManager;
|
||||
public IConfigurationHandler<ApplicationConfiguration> ConfigHandler;
|
||||
readonly IPageList PageList;
|
||||
private readonly IMetaService _metaService;
|
||||
private readonly TimeSpan _throttleTimeout = new TimeSpan(0, 1, 0);
|
||||
private readonly CancellationTokenSource _tokenSource;
|
||||
private readonly Dictionary<string, Task<IList>> _operationLookup = new Dictionary<string, Task<IList>>();
|
||||
@ -81,15 +81,16 @@ namespace IW4MAdmin.Application
|
||||
ITranslationLookup translationLookup, IConfigurationHandler<CommandConfiguration> commandConfiguration,
|
||||
IConfigurationHandler<ApplicationConfiguration> appConfigHandler, IGameServerInstanceFactory serverInstanceFactory,
|
||||
IEnumerable<IPlugin> plugins, IParserRegexFactory parserRegexFactory, IEnumerable<IRegisterEvent> customParserEvents,
|
||||
IEventHandler eventHandler, IScriptCommandFactory scriptCommandFactory, IDatabaseContextFactory contextFactory, IMetaService metaService,
|
||||
IEventHandler eventHandler, IScriptCommandFactory scriptCommandFactory, IDatabaseContextFactory contextFactory,
|
||||
IMetaRegistration metaRegistration, IScriptPluginServiceResolver scriptPluginServiceResolver, ClientService clientService, IServiceProvider serviceProvider,
|
||||
ChangeHistoryService changeHistoryService, ApplicationConfiguration appConfig, PenaltyService penaltyService)
|
||||
ChangeHistoryService changeHistoryService, ApplicationConfiguration appConfig, PenaltyService penaltyService, IAlertManager alertManager)
|
||||
{
|
||||
MiddlewareActionHandler = actionHandler;
|
||||
_servers = new ConcurrentBag<Server>();
|
||||
MessageTokens = new List<MessageToken>();
|
||||
ClientSvc = clientService;
|
||||
PenaltySvc = penaltyService;
|
||||
_alertManager = alertManager;
|
||||
ConfigHandler = appConfigHandler;
|
||||
StartTime = DateTime.UtcNow;
|
||||
PageList = new PageList();
|
||||
@ -97,7 +98,6 @@ namespace IW4MAdmin.Application
|
||||
AdditionalRConParsers = new List<IRConParser>() { new BaseRConParser(serviceProvider.GetRequiredService<ILogger<BaseRConParser>>(), parserRegexFactory) };
|
||||
TokenAuthenticator = new TokenAuthentication();
|
||||
_logger = logger;
|
||||
_metaService = metaService;
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
_commands = commands.ToList();
|
||||
_translationLookup = translationLookup;
|
||||
@ -412,7 +412,7 @@ namespace IW4MAdmin.Application
|
||||
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ConfigurationException(_translationLookup["MANAGER_CONFIGURATION_ERROR"])
|
||||
throw new ConfigurationException("Could not validate configuration")
|
||||
{
|
||||
Errors = validationResult.Errors.Select(_error => _error.ErrorMessage).ToArray(),
|
||||
ConfigurationFileName = ConfigHandler.FileName
|
||||
@ -510,6 +510,7 @@ namespace IW4MAdmin.Application
|
||||
#endregion
|
||||
|
||||
_metaRegistration.Register();
|
||||
await _alertManager.Initialize();
|
||||
|
||||
#region CUSTOM_EVENTS
|
||||
foreach (var customEvent in _customParserEvents.SelectMany(_events => _events.Events))
|
||||
@ -589,16 +590,29 @@ namespace IW4MAdmin.Application
|
||||
|
||||
public async Task Start() => await UpdateServerStates();
|
||||
|
||||
public void Stop()
|
||||
public async Task Stop()
|
||||
{
|
||||
foreach (var plugin in Plugins)
|
||||
{
|
||||
try
|
||||
{
|
||||
await plugin.OnUnloadAsync().WithTimeout(Utilities.DefaultCommandTimeout);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not cleanly unload plugin {PluginName}", plugin.Name);
|
||||
}
|
||||
}
|
||||
|
||||
_tokenSource.Cancel();
|
||||
|
||||
IsRunning = false;
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
{
|
||||
IsRestartRequested = true;
|
||||
Stop();
|
||||
Stop().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
@ -618,9 +632,9 @@ namespace IW4MAdmin.Application
|
||||
return _servers.SelectMany(s => s.Clients).ToList().Where(p => p != null).ToList();
|
||||
}
|
||||
|
||||
public EFClient FindActiveClient(EFClient client) =>client.ClientNumber < 0 ?
|
||||
public EFClient FindActiveClient(EFClient client) => client.ClientNumber < 0 ?
|
||||
GetActiveClients()
|
||||
.FirstOrDefault(c => c.NetworkId == client.NetworkId) ?? client :
|
||||
.FirstOrDefault(c => c.NetworkId == client.NetworkId && c.GameName == client.GameName) ?? client :
|
||||
client;
|
||||
|
||||
public ClientService GetClientService()
|
||||
@ -686,5 +700,6 @@ namespace IW4MAdmin.Application
|
||||
}
|
||||
|
||||
public void RemoveCommandByName(string commandName) => _commands.RemoveAll(_command => _command.Name == commandName);
|
||||
public IAlertManager AlertManager => _alertManager;
|
||||
}
|
||||
}
|
||||
|
64
Application/Commands/ClientTags/AddClientTagCommand.cs
Normal file
64
Application/Commands/ClientTags/AddClientTagCommand.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Models;
|
||||
using Data.Models.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Commands;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
|
||||
namespace IW4MAdmin.Application.Commands.ClientTags
|
||||
{
|
||||
public class AddClientTagCommand : Command
|
||||
{
|
||||
private readonly IMetaServiceV2 _metaService;
|
||||
|
||||
public AddClientTagCommand(ILogger<AddClientTagCommand> commandLogger, CommandConfiguration config,
|
||||
ITranslationLookup layout, IMetaServiceV2 metaService) :
|
||||
base(config, layout)
|
||||
{
|
||||
Name = "addclienttag";
|
||||
Description = layout["COMMANDS_ADD_CLIENT_TAG_DESC"];
|
||||
Alias = "act";
|
||||
Permission = EFClient.Permission.Owner;
|
||||
RequiresTarget = false;
|
||||
Arguments = new[]
|
||||
{
|
||||
new CommandArgument
|
||||
{
|
||||
Name = _translationLookup["COMMANDS_ARGUMENT_TAG"],
|
||||
Required = true
|
||||
}
|
||||
};
|
||||
|
||||
_metaService = metaService;
|
||||
logger = commandLogger;
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent gameEvent)
|
||||
{
|
||||
var existingTags = await _metaService.GetPersistentMetaValue<List<TagMeta>>(EFMeta.ClientTagNameV2) ??
|
||||
new List<TagMeta>();
|
||||
|
||||
var tagName = gameEvent.Data.Trim();
|
||||
|
||||
if (existingTags.Any(tag => tag.TagName == tagName))
|
||||
{
|
||||
logger.LogWarning("Tag with name {TagName} already exists", tagName);
|
||||
return;
|
||||
}
|
||||
|
||||
existingTags.Add(new TagMeta
|
||||
{
|
||||
Id = (existingTags.LastOrDefault()?.TagId ?? 0) + 1,
|
||||
Value = tagName
|
||||
});
|
||||
|
||||
await _metaService.SetPersistentMetaValue(EFMeta.ClientTagNameV2, existingTags,
|
||||
gameEvent.Owner.Manager.CancellationToken);
|
||||
gameEvent.Origin.Tell(_translationLookup["COMMANDS_ADD_CLIENT_TAG_SUCCESS"].FormatExt(gameEvent.Data));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +1,19 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Models;
|
||||
using Data.Models.Client;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
|
||||
namespace SharedLibraryCore.Commands
|
||||
namespace IW4MAdmin.Application.Commands.ClientTags
|
||||
{
|
||||
public class ListClientTags : Command
|
||||
{
|
||||
private readonly IMetaService _metaService;
|
||||
private readonly IMetaServiceV2 _metaService;
|
||||
|
||||
public ListClientTags(CommandConfiguration config, ITranslationLookup layout, IMetaService metaService) : base(
|
||||
public ListClientTags(CommandConfiguration config, ITranslationLookup layout, IMetaServiceV2 metaService) : base(
|
||||
config, layout)
|
||||
{
|
||||
Name = "listclienttags";
|
||||
@ -19,14 +21,18 @@ namespace SharedLibraryCore.Commands
|
||||
Alias = "lct";
|
||||
Permission = EFClient.Permission.Owner;
|
||||
RequiresTarget = false;
|
||||
|
||||
_metaService = metaService;
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent gameEvent)
|
||||
{
|
||||
var tags = await _metaService.GetPersistentMeta(EFMeta.ClientTagName);
|
||||
gameEvent.Origin.Tell(tags.Select(tag => tag.Value));
|
||||
var tags = await _metaService.GetPersistentMetaValue<List<TagMeta>>(EFMeta.ClientTagNameV2);
|
||||
|
||||
if (tags is not null)
|
||||
{
|
||||
await gameEvent.Origin.TellAsync(tags.Select(tag => tag.TagName),
|
||||
gameEvent.Owner.Manager.CancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +1,20 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using Data.Models;
|
||||
using Data.Models.Client;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Commands;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
|
||||
namespace SharedLibraryCore.Commands
|
||||
namespace IW4MAdmin.Application.Commands.ClientTags
|
||||
{
|
||||
public class RemoveClientTag : Command
|
||||
{
|
||||
private readonly IMetaService _metaService;
|
||||
private readonly IMetaServiceV2 _metaService;
|
||||
|
||||
public RemoveClientTag(CommandConfiguration config, ITranslationLookup layout, IMetaService metaService) : base(
|
||||
public RemoveClientTag(CommandConfiguration config, ITranslationLookup layout, IMetaServiceV2 metaService) : base(
|
||||
config, layout)
|
||||
{
|
||||
Name = "removeclienttag";
|
||||
@ -32,8 +36,13 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent gameEvent)
|
||||
{
|
||||
await _metaService.RemovePersistentMeta(EFMeta.ClientTagName, gameEvent.Data);
|
||||
var existingMeta = await _metaService.GetPersistentMetaValue<List<TagMeta>>(EFMeta.ClientTagNameV2,
|
||||
gameEvent.Owner.Manager.CancellationToken);
|
||||
existingMeta = existingMeta.Where(meta => meta.TagName != gameEvent.Data.Trim()).ToList();
|
||||
await _metaService.SetPersistentMetaValue(EFMeta.ClientTagNameV2, existingMeta,
|
||||
gameEvent.Owner.Manager.CancellationToken);
|
||||
|
||||
gameEvent.Origin.Tell(_translationLookup["COMMANDS_REMOVE_CLIENT_TAG_SUCCESS"].FormatExt(gameEvent.Data));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +1,22 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Models;
|
||||
using Data.Models.Client;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Commands;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
|
||||
namespace SharedLibraryCore.Commands
|
||||
namespace IW4MAdmin.Application.Commands.ClientTags
|
||||
{
|
||||
public class SetClientTagCommand : Command
|
||||
{
|
||||
private readonly IMetaService _metaService;
|
||||
private readonly IMetaServiceV2 _metaService;
|
||||
|
||||
|
||||
public SetClientTagCommand(CommandConfiguration config, ITranslationLookup layout, IMetaService metaService) :
|
||||
public SetClientTagCommand(CommandConfiguration config, ITranslationLookup layout, IMetaServiceV2 metaService) :
|
||||
base(config, layout)
|
||||
{
|
||||
Name = "setclienttag";
|
||||
@ -34,8 +38,10 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent gameEvent)
|
||||
{
|
||||
var availableTags = await _metaService.GetPersistentMeta(EFMeta.ClientTagName);
|
||||
var matchingTag = availableTags.FirstOrDefault(tag => tag.Value == gameEvent.Data);
|
||||
var token = gameEvent.Owner.Manager.CancellationToken;
|
||||
|
||||
var availableTags = await _metaService.GetPersistentMetaValue<List<LookupValue<string>>>(EFMeta.ClientTagNameV2, token);
|
||||
var matchingTag = availableTags.FirstOrDefault(tag => tag.Value == gameEvent.Data.Trim());
|
||||
|
||||
if (matchingTag == null)
|
||||
{
|
||||
@ -44,8 +50,9 @@ namespace SharedLibraryCore.Commands
|
||||
}
|
||||
|
||||
gameEvent.Target.Tag = matchingTag.Value;
|
||||
await _metaService.AddPersistentMeta(EFMeta.ClientTag, string.Empty, gameEvent.Target, matchingTag);
|
||||
await _metaService.SetPersistentMetaForLookupKey(EFMeta.ClientTagV2, EFMeta.ClientTagNameV2, matchingTag.Id,
|
||||
gameEvent.Target.ClientId, token);
|
||||
gameEvent.Origin.Tell(_translationLookup["COMMANDS_SET_CLIENT_TAG_SUCCESS"].FormatExt(matchingTag.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
Application/Commands/ClientTags/TagMeta.cs
Normal file
13
Application/Commands/ClientTags/TagMeta.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
|
||||
namespace IW4MAdmin.Application.Commands.ClientTags;
|
||||
|
||||
public class TagMeta : ILookupValue<string>
|
||||
{
|
||||
[JsonIgnore] public int TagId => Id;
|
||||
[JsonIgnore] public string TagName => Value;
|
||||
|
||||
public int Id { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
@ -1,17 +1,18 @@
|
||||
using System.Threading.Tasks;
|
||||
using Data.Models;
|
||||
using Data.Models.Client;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Commands;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
|
||||
namespace SharedLibraryCore.Commands
|
||||
namespace IW4MAdmin.Application.Commands.ClientTags
|
||||
{
|
||||
public class UnsetClientTagCommand : Command
|
||||
{
|
||||
private readonly IMetaService _metaService;
|
||||
private readonly IMetaServiceV2 _metaService;
|
||||
|
||||
|
||||
public UnsetClientTagCommand(CommandConfiguration config, ITranslationLookup layout, IMetaService metaService) :
|
||||
public UnsetClientTagCommand(CommandConfiguration config, ITranslationLookup layout, IMetaServiceV2 metaService) :
|
||||
base(config, layout)
|
||||
{
|
||||
Name = "unsetclienttag";
|
||||
@ -34,8 +35,9 @@ namespace SharedLibraryCore.Commands
|
||||
public override async Task ExecuteAsync(GameEvent gameEvent)
|
||||
{
|
||||
gameEvent.Target.Tag = null;
|
||||
await _metaService.RemovePersistentMeta(EFMeta.ClientTag, gameEvent.Target);
|
||||
await _metaService.RemovePersistentMeta(EFMeta.ClientTagV2, gameEvent.Target.ClientId,
|
||||
gameEvent.Owner.Manager.CancellationToken);
|
||||
gameEvent.Origin.Tell(_translationLookup["COMMANDS_UNSET_CLIENT_TAG_SUCCESS"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -29,9 +29,9 @@ namespace IW4MAdmin.Application.Commands
|
||||
$"[(Color::Accent){client.ClientPermission.Name}(Color::White){(string.IsNullOrEmpty(client.Tag) ? "" : $" {client.Tag}")}(Color::White)][(Color::Yellow)#{client.ClientNumber}(Color::White)] {client.Name}")
|
||||
.ToArray();
|
||||
|
||||
gameEvent.Origin.Tell(clientList);
|
||||
gameEvent.Origin.TellAsync(clientList, gameEvent.Owner.Manager.CancellationToken);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,20 +54,8 @@ namespace IW4MAdmin.Application.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
string map;
|
||||
string gametype;
|
||||
|
||||
if (match.Groups.Count > 3)
|
||||
{
|
||||
map = match.Groups[2].ToString();
|
||||
gametype = match.Groups[4].ToString();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
map = match.Groups[1].ToString();
|
||||
gametype = match.Groups[3].ToString();
|
||||
}
|
||||
var map = match.Groups[1].Length > 0 ? match.Groups[1].ToString() : match.Groups[2].ToString();
|
||||
var gametype = match.Groups[3].Length > 0 ? match.Groups[3].ToString() : match.Groups[4].ToString();
|
||||
|
||||
var matchingMaps = gameEvent.Owner.FindMap(map);
|
||||
var matchingGametypes = _defaultSettings.FindGametype(gametype, gameEvent.Owner.GameName);
|
||||
@ -104,7 +92,7 @@ namespace IW4MAdmin.Application.Commands
|
||||
|
||||
_logger.LogDebug("Changing map to {Map} and gametype {Gametype}", map, gametype);
|
||||
|
||||
await gameEvent.Owner.SetDvarAsync("g_gametype", gametype);
|
||||
await gameEvent.Owner.SetDvarAsync("g_gametype", gametype, gameEvent.Owner.Manager.CancellationToken);
|
||||
gameEvent.Owner.Broadcast(_translationLookup["COMMANDS_MAP_SUCCESS"].FormatExt(map));
|
||||
await Task.Delay(gameEvent.Owner.Manager.GetApplicationSettings().Configuration().MapChangeDelaySeconds);
|
||||
|
||||
|
@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Abstractions;
|
||||
using Data.Models.Client;
|
||||
using Data.Models.Misc;
|
||||
using IW4MAdmin.Application.Alerts;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Alerts;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
@ -16,19 +19,66 @@ namespace IW4MAdmin.Application.Commands
|
||||
{
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IAlertManager _alertManager;
|
||||
private const short MaxLength = 1024;
|
||||
|
||||
|
||||
public OfflineMessageCommand(CommandConfiguration config, ITranslationLookup layout,
|
||||
IDatabaseContextFactory contextFactory, ILogger<IDatabaseContextFactory> logger) : base(config, layout)
|
||||
IDatabaseContextFactory contextFactory, ILogger<IDatabaseContextFactory> logger, IAlertManager alertManager)
|
||||
: base(config, layout)
|
||||
{
|
||||
Name = "offlinemessage";
|
||||
Description = _translationLookup["COMMANDS_OFFLINE_MESSAGE_DESC"];
|
||||
Alias = "om";
|
||||
Permission = EFClient.Permission.Moderator;
|
||||
RequiresTarget = true;
|
||||
|
||||
|
||||
_contextFactory = contextFactory;
|
||||
_logger = logger;
|
||||
_alertManager = alertManager;
|
||||
|
||||
_alertManager.RegisterStaticAlertSource(async () =>
|
||||
{
|
||||
var context = contextFactory.CreateContext(false);
|
||||
return await context.InboxMessages.Where(message => !message.IsDelivered)
|
||||
.Where(message => message.CreatedDateTime >= DateTime.UtcNow.AddDays(-7))
|
||||
.Where(message => message.DestinationClient.Level > EFClient.Permission.User)
|
||||
.Select(message => new Alert.AlertState
|
||||
{
|
||||
OccuredAt = message.CreatedDateTime,
|
||||
Message = message.Message,
|
||||
ExpiresAt = DateTime.UtcNow.AddDays(7),
|
||||
Category = Alert.AlertCategory.Message,
|
||||
Source = message.SourceClient.CurrentAlias.Name.StripColors(),
|
||||
SourceId = message.SourceClientId,
|
||||
RecipientId = message.DestinationClientId,
|
||||
ReferenceId = message.InboxMessageId,
|
||||
Type = nameof(EFInboxMessage)
|
||||
}).ToListAsync();
|
||||
});
|
||||
|
||||
_alertManager.OnAlertConsumed += (_, state) =>
|
||||
{
|
||||
if (state.Category != Alert.AlertCategory.Message || state.ReferenceId is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var context = contextFactory.CreateContext(true);
|
||||
foreach (var message in context.InboxMessages
|
||||
.Where(message => message.InboxMessageId == state.ReferenceId.Value).ToList())
|
||||
{
|
||||
message.IsDelivered = true;
|
||||
}
|
||||
|
||||
context.SaveChanges();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not update message state for alert {@Alert}", state);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent gameEvent)
|
||||
@ -38,23 +88,24 @@ namespace IW4MAdmin.Application.Commands
|
||||
gameEvent.Origin.Tell(_translationLookup["COMMANDS_OFFLINE_MESSAGE_TOO_LONG"].FormatExt(MaxLength));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (gameEvent.Target.ClientId == gameEvent.Origin.ClientId)
|
||||
{
|
||||
gameEvent.Origin.Tell(_translationLookup["COMMANDS_OFFLINE_MESSAGE_SELF"].FormatExt(MaxLength));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (gameEvent.Target.IsIngame)
|
||||
{
|
||||
gameEvent.Origin.Tell(_translationLookup["COMMANDS_OFFLINE_MESSAGE_INGAME"].FormatExt(gameEvent.Target.Name));
|
||||
gameEvent.Origin.Tell(_translationLookup["COMMANDS_OFFLINE_MESSAGE_INGAME"]
|
||||
.FormatExt(gameEvent.Target.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
await using var context = _contextFactory.CreateContext(enableTracking: false);
|
||||
var server = await context.Servers.FirstAsync(srv => srv.EndPoint == gameEvent.Owner.ToString());
|
||||
|
||||
var newMessage = new EFInboxMessage()
|
||||
var newMessage = new EFInboxMessage
|
||||
{
|
||||
SourceClientId = gameEvent.Origin.ClientId,
|
||||
DestinationClientId = gameEvent.Target.ClientId,
|
||||
@ -62,6 +113,12 @@ namespace IW4MAdmin.Application.Commands
|
||||
Message = gameEvent.Data,
|
||||
};
|
||||
|
||||
_alertManager.AddAlert(gameEvent.Target.BuildAlert(Alert.AlertCategory.Message)
|
||||
.WithMessage(gameEvent.Data.Trim())
|
||||
.FromClient(gameEvent.Origin)
|
||||
.OfType(nameof(EFInboxMessage))
|
||||
.ExpiresIn(TimeSpan.FromDays(7)));
|
||||
|
||||
try
|
||||
{
|
||||
context.Set<EFInboxMessage>().Add(newMessage);
|
||||
@ -75,4 +132,4 @@ namespace IW4MAdmin.Application.Commands
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2021,7 +2021,7 @@
|
||||
"barrett": "Barrett .50cal",
|
||||
"mp44": "MP44",
|
||||
"remington700": "R700",
|
||||
"rpd": "RDP",
|
||||
"rpd": "RPD",
|
||||
"saw": " M249 SAW",
|
||||
"usp": "USP .45",
|
||||
"winchester1200": "W1200",
|
||||
@ -2083,6 +2083,126 @@
|
||||
"type99rifle": "Arisaka",
|
||||
"mosinrifle": "Mosin-Nagant",
|
||||
"ptrs41": "PTRS-41"
|
||||
},
|
||||
|
||||
"T6" : {
|
||||
|
||||
"mp7": "MP7",
|
||||
"pdw57": "PDW-57",
|
||||
"vector": "Vector K10",
|
||||
"insas": "MSMC",
|
||||
"qcw05": "Chicom CQB",
|
||||
"evoskorpion": "Skorpion EVO",
|
||||
"peacekeeper": "Peacekeeper",
|
||||
|
||||
"tar21": "MTAR",
|
||||
"type95": "Type 25",
|
||||
"sig556": "SWAT-556",
|
||||
"sa58": "FAL-OSW",
|
||||
"hk416": "M27",
|
||||
"scar": "SCAR-H",
|
||||
"saritch": "SMR",
|
||||
"xm8": "M8A1",
|
||||
"an94": "AN-94",
|
||||
|
||||
"870mcs": "Remington-870 MCS",
|
||||
"saiga12": "S12",
|
||||
"ksg": "KSG",
|
||||
"srm1216": "M1216",
|
||||
|
||||
"mk48": "MK 48",
|
||||
"qbb95": "QBB LSW",
|
||||
"lsat": "LSAT",
|
||||
"hamr": "HAMR",
|
||||
|
||||
"svu": "SVU-AS",
|
||||
"dsr50": "DSR 50",
|
||||
"ballista": "Ballista",
|
||||
"as50": "XPR-50",
|
||||
|
||||
"fiveseven": "Five-Seven",
|
||||
"fnp45": "TAC-45",
|
||||
"beretta93r": "B23R",
|
||||
"judge": "Executioner",
|
||||
"kard": "KAP-40",
|
||||
|
||||
"smaw": "SMAW",
|
||||
"fhj18": "FHJ-18 AA",
|
||||
"usrpg": "RPG",
|
||||
|
||||
"riotshield": "Assault Shield",
|
||||
"crossbow": "Crossbow",
|
||||
"knife_ballistic": "Ballistic Knife",
|
||||
"knife_held": "Knife",
|
||||
"knife": "Knife",
|
||||
|
||||
"frag_grenade": "Grenade",
|
||||
"hatchet": "Combat Axe",
|
||||
"sticky_grenade": "Semtex",
|
||||
"satchel_charge": "C4",
|
||||
"bouncingbetty": "Bouncing Betty",
|
||||
"claymore": "Claymore",
|
||||
|
||||
"smoke_center": "Smoke Grenade",
|
||||
"concussion_grenade": "Concussion",
|
||||
"emp_grenade": "EMP Grenade",
|
||||
"sensor_grenade": "Sensor Grenade",
|
||||
"flash_grenade": "Flashbang",
|
||||
"proximity_grenade": "Shock Charge",
|
||||
"pda_hack": "Black Hat",
|
||||
"trophy_system": "Trophy System",
|
||||
"tactical_insertion": "Tactical Insertion",
|
||||
|
||||
"acog": "ACOG",
|
||||
"stalker": "Stock",
|
||||
"swayreduc": "Ballistics CPU",
|
||||
"ir": "Dual Band",
|
||||
"dw": "Dual Wield",
|
||||
"extclip": "Extended Clip",
|
||||
"halo": "EOTech",
|
||||
"dualclip": "Fast Mag",
|
||||
"fmj": "FMJ",
|
||||
"grip": "Fore Grip",
|
||||
"gl": "Grenade Launcher",
|
||||
"dualoptic": "Hybrid Optic",
|
||||
"is": "Iron Sights",
|
||||
"steadyaim": "Laser Sight",
|
||||
"extbarrel": "Long Barrel",
|
||||
"mms": "MMS",
|
||||
"fastads": "Quickdraw",
|
||||
"rf": "Rapid Fire",
|
||||
"reflex": "Reflex Sight",
|
||||
"sf": "Select Fire",
|
||||
"silencer": "Suppressor",
|
||||
"tacknife": "Tactical Knife",
|
||||
"stackfire": "Tri-Bolt",
|
||||
"rangefinder": "Target Finder",
|
||||
"vzoom": "Variable Zoom",
|
||||
|
||||
"spyplane": "UAV",
|
||||
"rcbomb": "RC-XD",
|
||||
"missile_drone": "Hunter Killer",
|
||||
"supplydrop": "Care Package",
|
||||
"counteruav": "Counter-UAV",
|
||||
"microwave_turret": "Guardian",
|
||||
"remote_missile": "Hellstorm Missile",
|
||||
"planemortar": "Lightning Strike",
|
||||
"auto_turret": "Sentry Gun",
|
||||
"minigun": "Death Machine",
|
||||
"m32": "War Machine",
|
||||
"qrdrone": "Dragonfire",
|
||||
"ai_tank_drop": "AGR",
|
||||
"comlink": "Stealth Chopper",
|
||||
"spyplane_direction": "Orbital VSAT",
|
||||
"helicopter_guard": "Escort Drone",
|
||||
"emp": "EMP",
|
||||
"straferun": "Warthog",
|
||||
"remote_mortar": "Lodestar",
|
||||
"player_gunner": "VTOL Warship",
|
||||
"dogs": "K9 Unit",
|
||||
"missile_swarm": "Swarm"
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,13 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2);
|
||||
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
|
||||
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginName, 4);
|
||||
|
||||
Configuration.JoinTeam.Pattern = @"^(JT);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);([0-9]+);(\w+);(.+)$";
|
||||
Configuration.JoinTeam.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||
Configuration.JoinTeam.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2);
|
||||
Configuration.JoinTeam.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
|
||||
Configuration.JoinTeam.AddMapping(ParserRegex.GroupType.OriginTeam, 4);
|
||||
Configuration.JoinTeam.AddMapping(ParserRegex.GroupType.OriginName, 5);
|
||||
|
||||
Configuration.Damage.Pattern = @"^(D);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);(-?[0-9]+);(axis|allies|world|none)?;([^;]{1,32});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0)?;(-?[0-9]+);(axis|allies|world|none)?;([^;]{1,32})?;((?:[0-9]+|[a-z]+|_|\+)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
|
||||
Configuration.Damage.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||
@ -90,7 +97,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
{Configuration.Say, GameEvent.EventType.Say},
|
||||
{Configuration.Kill, GameEvent.EventType.Kill},
|
||||
{Configuration.MapChange, GameEvent.EventType.MapChange},
|
||||
{Configuration.MapEnd, GameEvent.EventType.MapEnd}
|
||||
{Configuration.MapEnd, GameEvent.EventType.MapEnd},
|
||||
{Configuration.JoinTeam, GameEvent.EventType.JoinTeam}
|
||||
};
|
||||
|
||||
_eventTypeMap = new Dictionary<string, GameEvent.EventType>
|
||||
@ -100,7 +108,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
{"K", GameEvent.EventType.Kill},
|
||||
{"D", GameEvent.EventType.Damage},
|
||||
{"J", GameEvent.EventType.PreConnect},
|
||||
{"Q", GameEvent.EventType.PreDisconnect},
|
||||
{"JT", GameEvent.EventType.JoinTeam},
|
||||
{"Q", GameEvent.EventType.PreDisconnect}
|
||||
};
|
||||
}
|
||||
|
||||
@ -322,6 +331,47 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
}
|
||||
}
|
||||
|
||||
if (eventType == GameEvent.EventType.JoinTeam)
|
||||
{
|
||||
var match = Configuration.JoinTeam.PatternMatcher.Match(logLine);
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
var originIdString = match.Values[Configuration.JoinTeam.GroupMapping[ParserRegex.GroupType.OriginNetworkId]];
|
||||
var originName = match.Values[Configuration.JoinTeam.GroupMapping[ParserRegex.GroupType.OriginName]];
|
||||
var team = match.Values[Configuration.JoinTeam.GroupMapping[ParserRegex.GroupType.OriginTeam]];
|
||||
|
||||
if (Configuration.TeamMapping.ContainsKey(team))
|
||||
{
|
||||
team = Configuration.TeamMapping[team].ToString();
|
||||
}
|
||||
|
||||
var networkId = originIdString.IsBotGuid() ?
|
||||
originName.GenerateGuidFromString() :
|
||||
originIdString.ConvertGuidToLong(Configuration.GuidNumberStyle);
|
||||
|
||||
return new GameEvent
|
||||
{
|
||||
Type = GameEvent.EventType.JoinTeam,
|
||||
Data = logLine,
|
||||
Origin = new EFClient
|
||||
{
|
||||
CurrentAlias = new EFAlias
|
||||
{
|
||||
Name = match.Values[Configuration.JoinTeam.GroupMapping[ParserRegex.GroupType.OriginName]].TrimNewLine(),
|
||||
},
|
||||
NetworkId = networkId,
|
||||
ClientNumber = Convert.ToInt32(match.Values[Configuration.JoinTeam.GroupMapping[ParserRegex.GroupType.OriginClientNumber]]),
|
||||
State = EFClient.ClientState.Connected,
|
||||
},
|
||||
Extra = team,
|
||||
RequiredEntity = GameEvent.EventRequiredEntity.Origin,
|
||||
GameTime = gameTime,
|
||||
Source = GameEvent.EventSource.Log
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (eventType == GameEvent.EventType.PreDisconnect)
|
||||
{
|
||||
var match = Configuration.Quit.PatternMatcher.Match(logLine);
|
||||
|
@ -1,6 +1,8 @@
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System.Globalization;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
|
||||
namespace IW4MAdmin.Application.EventParsers
|
||||
{
|
||||
@ -8,11 +10,12 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
/// generic implementation of the IEventParserConfiguration
|
||||
/// allows script plugins to generate dynamic configurations
|
||||
/// </summary>
|
||||
sealed internal class DynamicEventParserConfiguration : IEventParserConfiguration
|
||||
internal sealed class DynamicEventParserConfiguration : IEventParserConfiguration
|
||||
{
|
||||
public string GameDirectory { get; set; }
|
||||
public ParserRegex Say { get; set; }
|
||||
public ParserRegex Join { get; set; }
|
||||
public ParserRegex JoinTeam { get; set; }
|
||||
public ParserRegex Quit { get; set; }
|
||||
public ParserRegex Kill { get; set; }
|
||||
public ParserRegex Damage { get; set; }
|
||||
@ -22,10 +25,13 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
public ParserRegex MapEnd { get; set; }
|
||||
public NumberStyles GuidNumberStyle { get; set; } = NumberStyles.HexNumber;
|
||||
|
||||
public Dictionary<string, EFClient.TeamType> TeamMapping { get; set; } = new();
|
||||
|
||||
public DynamicEventParserConfiguration(IParserRegexFactory parserRegexFactory)
|
||||
{
|
||||
Say = parserRegexFactory.CreateParserRegex();
|
||||
Join = parserRegexFactory.CreateParserRegex();
|
||||
JoinTeam = parserRegexFactory.CreateParserRegex();
|
||||
Quit = parserRegexFactory.CreateParserRegex();
|
||||
Kill = parserRegexFactory.CreateParserRegex();
|
||||
Damage = parserRegexFactory.CreateParserRegex();
|
||||
|
@ -14,7 +14,7 @@ namespace IW4MAdmin.Application.Factories
|
||||
internal class GameServerInstanceFactory : IGameServerInstanceFactory
|
||||
{
|
||||
private readonly ITranslationLookup _translationLookup;
|
||||
private readonly IMetaService _metaService;
|
||||
private readonly IMetaServiceV2 _metaService;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
/// <summary>
|
||||
@ -23,7 +23,7 @@ namespace IW4MAdmin.Application.Factories
|
||||
/// <param name="translationLookup"></param>
|
||||
/// <param name="rconConnectionFactory"></param>
|
||||
public GameServerInstanceFactory(ITranslationLookup translationLookup,
|
||||
IMetaService metaService,
|
||||
IMetaServiceV2 metaService,
|
||||
IServiceProvider serviceProvider)
|
||||
{
|
||||
_translationLookup = translationLookup;
|
||||
@ -45,4 +45,4 @@ namespace IW4MAdmin.Application.Factories
|
||||
_serviceProvider.GetRequiredService<ILookupCache<EFServer>>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,8 +66,8 @@ namespace IW4MAdmin.Application.IO
|
||||
}
|
||||
return Task.FromResult(Enumerable.Empty<GameEvent>());
|
||||
}
|
||||
|
||||
new Thread(() => ReadNetworkData(client)).Start();
|
||||
|
||||
Task.Run(async () => await ReadNetworkData(client, _token), _token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -89,7 +89,7 @@ namespace IW4MAdmin.Application.IO
|
||||
|
||||
var lines = logData
|
||||
.Split('\n')
|
||||
.Where(line => line.Length > 0);
|
||||
.Where(line => line.Length > 0 && !line.Contains('ÿ'));
|
||||
|
||||
foreach (var eventLine in lines)
|
||||
{
|
||||
@ -111,9 +111,9 @@ namespace IW4MAdmin.Application.IO
|
||||
return Task.FromResult((IEnumerable<GameEvent>)events);
|
||||
}
|
||||
|
||||
private void ReadNetworkData(UdpClient client)
|
||||
private async Task ReadNetworkData(UdpClient client, CancellationToken token)
|
||||
{
|
||||
while (!_token.IsCancellationRequested)
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
// get more data
|
||||
IPEndPoint remoteEndpoint = null;
|
||||
@ -127,7 +127,13 @@ namespace IW4MAdmin.Application.IO
|
||||
|
||||
try
|
||||
{
|
||||
bufferedData = client.Receive(ref remoteEndpoint);
|
||||
var result = await client.ReceiveAsync(_token);
|
||||
remoteEndpoint = result.RemoteEndPoint;
|
||||
bufferedData = result.Buffer;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.LogDebug("Stopping network log receive");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -24,8 +24,10 @@ using Serilog.Context;
|
||||
using static SharedLibraryCore.Database.Models.EFClient;
|
||||
using Data.Models;
|
||||
using Data.Models.Server;
|
||||
using IW4MAdmin.Application.Alerts;
|
||||
using IW4MAdmin.Application.Commands;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Alerts;
|
||||
using static Data.Models.Client.EFClient;
|
||||
|
||||
namespace IW4MAdmin
|
||||
@ -35,7 +37,7 @@ namespace IW4MAdmin
|
||||
private static readonly SharedLibraryCore.Localization.TranslationLookup loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||
public GameLogEventDetection LogEvent;
|
||||
private readonly ITranslationLookup _translationLookup;
|
||||
private readonly IMetaService _metaService;
|
||||
private readonly IMetaServiceV2 _metaService;
|
||||
private const int REPORT_FLAG_COUNT = 4;
|
||||
private long lastGameTime = 0;
|
||||
|
||||
@ -49,7 +51,7 @@ namespace IW4MAdmin
|
||||
ServerConfiguration serverConfiguration,
|
||||
CommandConfiguration commandConfiguration,
|
||||
ITranslationLookup lookup,
|
||||
IMetaService metaService,
|
||||
IMetaServiceV2 metaService,
|
||||
IServiceProvider serviceProvider,
|
||||
IClientNoticeMessageFormatter messageFormatter,
|
||||
ILookupCache<EFServer> serverCache) : base(serviceProvider.GetRequiredService<ILogger<Server>>(),
|
||||
@ -73,7 +75,7 @@ namespace IW4MAdmin
|
||||
{
|
||||
ServerLogger.LogDebug("Client slot #{clientNumber} now reserved", clientFromLog.ClientNumber);
|
||||
|
||||
EFClient client = await Manager.GetClientService().GetUnique(clientFromLog.NetworkId);
|
||||
var client = await Manager.GetClientService().GetUnique(clientFromLog.NetworkId, GameName);
|
||||
|
||||
// first time client is connecting to server
|
||||
if (client == null)
|
||||
@ -96,6 +98,8 @@ namespace IW4MAdmin
|
||||
client.ClientNumber = clientFromLog.ClientNumber;
|
||||
client.Score = clientFromLog.Score;
|
||||
client.Ping = clientFromLog.Ping;
|
||||
client.Team = clientFromLog.Team;
|
||||
client.TeamName = clientFromLog.TeamName;
|
||||
client.CurrentServer = this;
|
||||
client.State = ClientState.Connecting;
|
||||
|
||||
@ -114,7 +118,7 @@ namespace IW4MAdmin
|
||||
|
||||
public override async Task OnClientDisconnected(EFClient client)
|
||||
{
|
||||
if (!GetClientsAsList().Any(_client => _client.NetworkId == client.NetworkId))
|
||||
if (GetClientsAsList().All(eachClient => eachClient.NetworkId != client.NetworkId))
|
||||
{
|
||||
using (LogContext.PushProperty("Server", ToString()))
|
||||
{
|
||||
@ -295,7 +299,7 @@ namespace IW4MAdmin
|
||||
}
|
||||
}
|
||||
|
||||
if (E.Type == GameEvent.EventType.ConnectionLost)
|
||||
else if (E.Type == GameEvent.EventType.ConnectionLost)
|
||||
{
|
||||
var exception = E.Extra as Exception;
|
||||
ServerLogger.LogError(exception,
|
||||
@ -304,30 +308,46 @@ namespace IW4MAdmin
|
||||
if (!Manager.GetApplicationSettings().Configuration().IgnoreServerConnectionLost)
|
||||
{
|
||||
Console.WriteLine(loc["SERVER_ERROR_COMMUNICATION"].FormatExt($"{IP}:{Port}"));
|
||||
|
||||
var alert = Alert.AlertState.Build().OfType(E.Type.ToString())
|
||||
.WithCategory(Alert.AlertCategory.Error)
|
||||
.FromSource("System")
|
||||
.WithMessage(loc["SERVER_ERROR_COMMUNICATION"].FormatExt($"{IP}:{Port}"))
|
||||
.ExpiresIn(TimeSpan.FromDays(1));
|
||||
|
||||
Manager.AlertManager.AddAlert(alert);
|
||||
}
|
||||
|
||||
|
||||
Throttled = true;
|
||||
}
|
||||
|
||||
if (E.Type == GameEvent.EventType.ConnectionRestored)
|
||||
else if (E.Type == GameEvent.EventType.ConnectionRestored)
|
||||
{
|
||||
ServerLogger.LogInformation(
|
||||
"Connection restored with {server}", ToString());
|
||||
|
||||
if (!Manager.GetApplicationSettings().Configuration().IgnoreServerConnectionLost)
|
||||
{
|
||||
Console.WriteLine(loc["MANAGER_CONNECTION_REST"].FormatExt($"[{IP}:{Port}]"));
|
||||
Console.WriteLine(loc["MANAGER_CONNECTION_REST"].FormatExt($"{IP}:{Port}"));
|
||||
|
||||
var alert = Alert.AlertState.Build().OfType(E.Type.ToString())
|
||||
.WithCategory(Alert.AlertCategory.Information)
|
||||
.FromSource("System")
|
||||
.WithMessage(loc["MANAGER_CONNECTION_REST"].FormatExt($"{IP}:{Port}"))
|
||||
.ExpiresIn(TimeSpan.FromDays(1));
|
||||
|
||||
Manager.AlertManager.AddAlert(alert);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(CustomSayName))
|
||||
{
|
||||
await this.SetDvarAsync("sv_sayname", CustomSayName);
|
||||
await this.SetDvarAsync("sv_sayname", CustomSayName, Manager.CancellationToken);
|
||||
}
|
||||
|
||||
Throttled = false;
|
||||
}
|
||||
|
||||
if (E.Type == GameEvent.EventType.ChangePermission)
|
||||
else if (E.Type == GameEvent.EventType.ChangePermission)
|
||||
{
|
||||
var newPermission = (Permission) E.Extra;
|
||||
ServerLogger.LogInformation("{origin} is setting {target} to permission level {newPermission}",
|
||||
@ -350,7 +370,8 @@ namespace IW4MAdmin
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
|
||||
var clientTag = await _metaService.GetPersistentMeta(EFMeta.ClientTag, E.Origin);
|
||||
var clientTag = await _metaService.GetPersistentMetaByLookup(EFMeta.ClientTagV2,
|
||||
EFMeta.ClientTagNameV2, E.Origin.ClientId, Manager.CancellationToken);
|
||||
|
||||
if (clientTag?.LinkedMeta != null)
|
||||
{
|
||||
@ -428,6 +449,7 @@ namespace IW4MAdmin
|
||||
Clients[E.Origin.ClientNumber] = E.Origin;
|
||||
try
|
||||
{
|
||||
E.Origin.GameName = (Reference.Game)GameName;
|
||||
E.Origin = await OnClientConnected(E.Origin);
|
||||
E.Target = E.Origin;
|
||||
}
|
||||
@ -482,7 +504,7 @@ namespace IW4MAdmin
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Unflag)
|
||||
{
|
||||
var unflagPenalty = new EFPenalty()
|
||||
var unflagPenalty = new EFPenalty
|
||||
{
|
||||
Type = EFPenalty.PenaltyType.Unflag,
|
||||
Expires = DateTime.UtcNow,
|
||||
@ -494,7 +516,8 @@ namespace IW4MAdmin
|
||||
};
|
||||
|
||||
E.Target.SetLevel(Permission.User, E.Origin);
|
||||
await Manager.GetPenaltyService().RemoveActivePenalties(E.Target.AliasLinkId);
|
||||
await Manager.GetPenaltyService().RemoveActivePenalties(E.Target.AliasLinkId, E.Target.NetworkId,
|
||||
E.Target.GameName, E.Target.CurrentAlias?.IPAddress);
|
||||
await Manager.GetPenaltyService().Create(unflagPenalty);
|
||||
}
|
||||
|
||||
@ -504,7 +527,8 @@ namespace IW4MAdmin
|
||||
{
|
||||
Origin = E.Origin,
|
||||
Target = E.Target,
|
||||
Reason = E.Data
|
||||
Reason = E.Data,
|
||||
ReportedOn = DateTime.UtcNow
|
||||
});
|
||||
|
||||
var newReport = new EFPenalty()
|
||||
@ -567,8 +591,8 @@ namespace IW4MAdmin
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await _metaService.AddPersistentMeta("LastMapPlayed", CurrentMap.Alias, E.Origin);
|
||||
await _metaService.AddPersistentMeta("LastServerPlayed", E.Owner.Hostname, E.Origin);
|
||||
await _metaService.SetPersistentMeta("LastMapPlayed", CurrentMap.Alias, E.Origin.ClientId);
|
||||
await _metaService.SetPersistentMeta("LastServerPlayed", E.Owner.Hostname, E.Origin.ClientId);
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.PreDisconnect)
|
||||
@ -619,7 +643,7 @@ namespace IW4MAdmin
|
||||
await OnClientUpdate(E.Origin);
|
||||
}
|
||||
|
||||
if (E.Type == GameEvent.EventType.Say)
|
||||
else if (E.Type == GameEvent.EventType.Say)
|
||||
{
|
||||
if (E.Data?.Length > 0)
|
||||
{
|
||||
@ -639,7 +663,7 @@ namespace IW4MAdmin
|
||||
}
|
||||
}
|
||||
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
ChatHistory.Add(new ChatInfo
|
||||
{
|
||||
Name = E.Origin.Name,
|
||||
Message = message,
|
||||
@ -649,7 +673,7 @@ namespace IW4MAdmin
|
||||
}
|
||||
}
|
||||
|
||||
if (E.Type == GameEvent.EventType.MapChange)
|
||||
else if (E.Type == GameEvent.EventType.MapChange)
|
||||
{
|
||||
ServerLogger.LogInformation("New map loaded - {clientCount} active players", ClientNum);
|
||||
|
||||
@ -690,7 +714,7 @@ namespace IW4MAdmin
|
||||
}
|
||||
}
|
||||
|
||||
if (E.Type == GameEvent.EventType.MapEnd)
|
||||
else if (E.Type == GameEvent.EventType.MapEnd)
|
||||
{
|
||||
ServerLogger.LogInformation("Game ending...");
|
||||
|
||||
@ -700,18 +724,23 @@ namespace IW4MAdmin
|
||||
}
|
||||
}
|
||||
|
||||
if (E.Type == GameEvent.EventType.Tell)
|
||||
else if (E.Type == GameEvent.EventType.Tell)
|
||||
{
|
||||
await Tell(E.Message, E.Target);
|
||||
}
|
||||
|
||||
if (E.Type == GameEvent.EventType.Broadcast)
|
||||
else if (E.Type == GameEvent.EventType.Broadcast)
|
||||
{
|
||||
if (!Utilities.IsDevelopment && E.Data != null) // hides broadcast when in development mode
|
||||
{
|
||||
await E.Owner.ExecuteCommandAsync(E.Data);
|
||||
}
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.JoinTeam)
|
||||
{
|
||||
E.Origin.UpdateTeam(E.Extra as string);
|
||||
}
|
||||
|
||||
lock (ChatHistory)
|
||||
{
|
||||
@ -734,7 +763,7 @@ namespace IW4MAdmin
|
||||
|
||||
private async Task OnClientUpdate(EFClient origin)
|
||||
{
|
||||
var client = Manager.GetActiveClients().FirstOrDefault(c => c.NetworkId == origin.NetworkId);
|
||||
var client = GetClientsAsList().FirstOrDefault(c => c.NetworkId == origin.NetworkId);
|
||||
|
||||
if (client == null)
|
||||
{
|
||||
@ -779,10 +808,16 @@ namespace IW4MAdmin
|
||||
/// array index 2 = updated clients
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
async Task<List<EFClient>[]> PollPlayersAsync()
|
||||
async Task<List<EFClient>[]> PollPlayersAsync(CancellationToken token)
|
||||
{
|
||||
var currentClients = GetClientsAsList();
|
||||
var statusResponse = (await this.GetStatusAsync());
|
||||
var statusResponse = await this.GetStatusAsync(token);
|
||||
|
||||
if (statusResponse is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var polledClients = statusResponse.Clients.AsEnumerable();
|
||||
|
||||
if (Manager.GetApplicationSettings().Configuration().IgnoreBots)
|
||||
@ -894,22 +929,16 @@ namespace IW4MAdmin
|
||||
|
||||
await e.WaitAsync(Utilities.DefaultCommandTimeout, new CancellationTokenRegistration().Token);
|
||||
}
|
||||
|
||||
foreach (var plugin in Manager.Plugins)
|
||||
{
|
||||
await plugin.OnUnloadAsync();
|
||||
}
|
||||
}
|
||||
|
||||
DateTime start = DateTime.Now;
|
||||
DateTime playerCountStart = DateTime.Now;
|
||||
DateTime lastCount = DateTime.Now;
|
||||
private DateTime _lastMessageSent = DateTime.Now;
|
||||
private DateTime _lastPlayerCount = DateTime.Now;
|
||||
|
||||
public override async Task<bool> ProcessUpdatesAsync(CancellationToken cts)
|
||||
public override async Task<bool> ProcessUpdatesAsync(CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (cts.IsCancellationRequested)
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
await ShutdownInternal();
|
||||
return true;
|
||||
@ -917,17 +946,24 @@ namespace IW4MAdmin
|
||||
|
||||
try
|
||||
{
|
||||
if (Manager.GetApplicationSettings().Configuration().RConPollRate == int.MaxValue && Utilities.IsDevelopment)
|
||||
if (Manager.GetApplicationSettings().Configuration().RConPollRate == int.MaxValue &&
|
||||
Utilities.IsDevelopment)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var polledClients = await PollPlayersAsync();
|
||||
var polledClients = await PollPlayersAsync(token);
|
||||
|
||||
foreach (var disconnectingClient in polledClients[1].Where(_client => !_client.IsZombieClient /* ignores "fake" zombie clients */))
|
||||
if (polledClients is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var disconnectingClient in polledClients[1]
|
||||
.Where(client => !client.IsZombieClient /* ignores "fake" zombie clients */))
|
||||
{
|
||||
disconnectingClient.CurrentServer = this;
|
||||
var e = new GameEvent()
|
||||
var e = new GameEvent
|
||||
{
|
||||
Type = GameEvent.EventType.PreDisconnect,
|
||||
Origin = disconnectingClient,
|
||||
@ -940,23 +976,20 @@ namespace IW4MAdmin
|
||||
}
|
||||
|
||||
// this are our new connecting clients
|
||||
foreach (var client in polledClients[0])
|
||||
foreach (var client in polledClients[0].Where(client =>
|
||||
!string.IsNullOrEmpty(client.Name) && (client.Ping != 999 || client.IsBot)))
|
||||
{
|
||||
// note: this prevents players in ZMBI state from being registered with no name
|
||||
if (string.IsNullOrEmpty(client.Name) || (client.Ping == 999 && !client.IsBot))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
client.CurrentServer = this;
|
||||
var e = new GameEvent()
|
||||
client.GameName = (Reference.Game)GameName;
|
||||
|
||||
var e = new GameEvent
|
||||
{
|
||||
Type = GameEvent.EventType.PreConnect,
|
||||
Origin = client,
|
||||
Owner = this,
|
||||
IsBlocking = true,
|
||||
Extra = client.GetAdditionalProperty<string>("BotGuid"),
|
||||
Source = GameEvent.EventSource.Status
|
||||
Source = GameEvent.EventSource.Status,
|
||||
};
|
||||
|
||||
Manager.AddEvent(e);
|
||||
@ -967,19 +1000,19 @@ namespace IW4MAdmin
|
||||
foreach (var client in polledClients[2])
|
||||
{
|
||||
client.CurrentServer = this;
|
||||
var e = new GameEvent()
|
||||
var gameEvent = new GameEvent
|
||||
{
|
||||
Type = GameEvent.EventType.Update,
|
||||
Origin = client,
|
||||
Owner = this
|
||||
};
|
||||
|
||||
Manager.AddEvent(e);
|
||||
Manager.AddEvent(gameEvent);
|
||||
}
|
||||
|
||||
if (Throttled)
|
||||
{
|
||||
var _event = new GameEvent()
|
||||
var gameEvent = new GameEvent
|
||||
{
|
||||
Type = GameEvent.EventType.ConnectionRestored,
|
||||
Owner = this,
|
||||
@ -987,65 +1020,52 @@ namespace IW4MAdmin
|
||||
Target = Utilities.IW4MAdminClient(this)
|
||||
};
|
||||
|
||||
Manager.AddEvent(_event);
|
||||
Manager.AddEvent(gameEvent);
|
||||
}
|
||||
|
||||
LastPoll = DateTime.Now;
|
||||
}
|
||||
|
||||
catch (NetworkException e)
|
||||
catch (NetworkException ex)
|
||||
{
|
||||
if (!Throttled)
|
||||
if (Throttled)
|
||||
{
|
||||
var _event = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.ConnectionLost,
|
||||
Owner = this,
|
||||
Origin = Utilities.IW4MAdminClient(this),
|
||||
Target = Utilities.IW4MAdminClient(this),
|
||||
Extra = e,
|
||||
Data = ConnectionErrors.ToString()
|
||||
};
|
||||
|
||||
Manager.AddEvent(_event);
|
||||
return true;
|
||||
}
|
||||
|
||||
var gameEvent = new GameEvent
|
||||
{
|
||||
Type = GameEvent.EventType.ConnectionLost,
|
||||
Owner = this,
|
||||
Origin = Utilities.IW4MAdminClient(this),
|
||||
Target = Utilities.IW4MAdminClient(this),
|
||||
Extra = ex,
|
||||
Data = ConnectionErrors.ToString()
|
||||
};
|
||||
|
||||
Manager.AddEvent(gameEvent);
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
RunServerCollection();
|
||||
}
|
||||
|
||||
if (DateTime.Now - _lastMessageSent <=
|
||||
TimeSpan.FromSeconds(Manager.GetApplicationSettings().Configuration().AutoMessagePeriod) ||
|
||||
BroadcastMessages.Count <= 0 || ClientNum <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
LastMessage = DateTime.Now - start;
|
||||
lastCount = DateTime.Now;
|
||||
|
||||
var appConfig = _serviceProvider.GetService<ApplicationConfiguration>();
|
||||
// update the player history
|
||||
if (lastCount - playerCountStart >= appConfig.ServerDataCollectionInterval)
|
||||
{
|
||||
var maxItems = Math.Ceiling(appConfig.MaxClientHistoryTime.TotalMinutes /
|
||||
appConfig.ServerDataCollectionInterval.TotalMinutes);
|
||||
while ( ClientHistory.Count > maxItems)
|
||||
{
|
||||
ClientHistory.Dequeue();
|
||||
}
|
||||
|
||||
ClientHistory.Enqueue(new PlayerHistory(ClientNum));
|
||||
playerCountStart = DateTime.Now;
|
||||
}
|
||||
|
||||
// send out broadcast messages
|
||||
if (LastMessage.TotalSeconds > Manager.GetApplicationSettings().Configuration().AutoMessagePeriod
|
||||
&& BroadcastMessages.Count > 0
|
||||
&& ClientNum > 0)
|
||||
{
|
||||
string[] messages = (await this.ProcessMessageToken(Manager.GetMessageTokens(), BroadcastMessages[NextMessage])).Split(Environment.NewLine);
|
||||
var messages =
|
||||
(await this.ProcessMessageToken(Manager.GetMessageTokens(), BroadcastMessages[NextMessage])).Split(
|
||||
Environment.NewLine);
|
||||
await BroadcastAsync(messages, token: Manager.CancellationToken);
|
||||
|
||||
foreach (string message in messages)
|
||||
{
|
||||
Broadcast(message);
|
||||
}
|
||||
|
||||
NextMessage = NextMessage == (BroadcastMessages.Count - 1) ? 0 : NextMessage + 1;
|
||||
start = DateTime.Now;
|
||||
}
|
||||
NextMessage = NextMessage == BroadcastMessages.Count - 1 ? 0 : NextMessage + 1;
|
||||
_lastMessageSent = DateTime.Now;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1057,26 +1077,56 @@ namespace IW4MAdmin
|
||||
}
|
||||
|
||||
// this one is ok
|
||||
catch (Exception e) when(e is ServerException || e is RConException)
|
||||
catch (Exception e) when (e is ServerException || e is RConException)
|
||||
{
|
||||
using(LogContext.PushProperty("Server", ToString()))
|
||||
using (LogContext.PushProperty("Server", ToString()))
|
||||
{
|
||||
ServerLogger.LogWarning(e, "Undesirable exception occured during processing updates");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
using(LogContext.PushProperty("Server", ToString()))
|
||||
using (LogContext.PushProperty("Server", ToString()))
|
||||
{
|
||||
ServerLogger.LogError(e, "Unexpected exception occured during processing updates");
|
||||
}
|
||||
|
||||
Console.WriteLine(loc["SERVER_ERROR_EXCEPTION"].FormatExt($"[{IP}:{Port}]"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void RunServerCollection()
|
||||
{
|
||||
var appConfig = _serviceProvider.GetService<ApplicationConfiguration>();
|
||||
|
||||
if (DateTime.Now - _lastPlayerCount < appConfig?.ServerDataCollectionInterval)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var maxItems = Math.Ceiling(appConfig.MaxClientHistoryTime.TotalMinutes /
|
||||
appConfig.ServerDataCollectionInterval.TotalMinutes);
|
||||
|
||||
while (ClientHistory.ClientCounts.Count > maxItems)
|
||||
{
|
||||
ClientHistory.ClientCounts.RemoveAt(0);
|
||||
}
|
||||
|
||||
ClientHistory.ClientCounts.Add(new ClientCountSnapshot
|
||||
{
|
||||
ClientCount = ClientNum,
|
||||
ConnectionInterrupted = Throttled,
|
||||
Time = DateTime.UtcNow,
|
||||
Map = CurrentMap.Name
|
||||
});
|
||||
|
||||
_lastPlayerCount = DateTime.Now;
|
||||
}
|
||||
|
||||
public async Task Initialize()
|
||||
{
|
||||
try
|
||||
@ -1108,7 +1158,7 @@ namespace IW4MAdmin
|
||||
RemoteConnection = RConConnectionFactory.CreateConnection(ResolvedIpEndPoint, Password, RconParser.RConEngine);
|
||||
RemoteConnection.SetConfiguration(RconParser);
|
||||
|
||||
var version = await this.GetMappedDvarValueOrDefaultAsync<string>("version");
|
||||
var version = await this.GetMappedDvarValueOrDefaultAsync<string>("version", token: Manager.CancellationToken);
|
||||
Version = version.Value;
|
||||
GameName = Utilities.GetGame(version.Value ?? RconParser.Version);
|
||||
|
||||
@ -1125,7 +1175,7 @@ namespace IW4MAdmin
|
||||
Version = RconParser.Version;
|
||||
}
|
||||
|
||||
var svRunning = await this.GetMappedDvarValueOrDefaultAsync<string>("sv_running");
|
||||
var svRunning = await this.GetMappedDvarValueOrDefaultAsync<string>("sv_running", token: Manager.CancellationToken);
|
||||
|
||||
if (!string.IsNullOrEmpty(svRunning.Value) && svRunning.Value != "1")
|
||||
{
|
||||
@ -1134,27 +1184,28 @@ namespace IW4MAdmin
|
||||
|
||||
var infoResponse = RconParser.Configuration.CommandPrefixes.RConGetInfo != null ? await this.GetInfoAsync() : null;
|
||||
|
||||
string hostname = (await this.GetMappedDvarValueOrDefaultAsync<string>("sv_hostname", "hostname", infoResponse)).Value;
|
||||
string mapname = (await this.GetMappedDvarValueOrDefaultAsync<string>("mapname", infoResponse: infoResponse)).Value;
|
||||
int maxplayers = (await this.GetMappedDvarValueOrDefaultAsync<int>("sv_maxclients", infoResponse: infoResponse)).Value;
|
||||
string gametype = (await this.GetMappedDvarValueOrDefaultAsync<string>("g_gametype", "gametype", infoResponse)).Value;
|
||||
var basepath = await this.GetMappedDvarValueOrDefaultAsync<string>("fs_basepath");
|
||||
var basegame = await this.GetMappedDvarValueOrDefaultAsync<string>("fs_basegame");
|
||||
var homepath = await this.GetMappedDvarValueOrDefaultAsync<string>("fs_homepath");
|
||||
var game = (await this.GetMappedDvarValueOrDefaultAsync<string>("fs_game", infoResponse: infoResponse));
|
||||
var logfile = await this.GetMappedDvarValueOrDefaultAsync<string>("g_log");
|
||||
var logsync = await this.GetMappedDvarValueOrDefaultAsync<int>("g_logsync");
|
||||
var ip = await this.GetMappedDvarValueOrDefaultAsync<string>("net_ip");
|
||||
var gamePassword = await this.GetMappedDvarValueOrDefaultAsync("g_password", overrideDefault: "");
|
||||
var hostname = (await this.GetMappedDvarValueOrDefaultAsync<string>("sv_hostname", "hostname", infoResponse, token: Manager.CancellationToken)).Value;
|
||||
var mapname = (await this.GetMappedDvarValueOrDefaultAsync<string>("mapname", infoResponse: infoResponse, token: Manager.CancellationToken)).Value;
|
||||
var maxplayers = (await this.GetMappedDvarValueOrDefaultAsync<int>("sv_maxclients", infoResponse: infoResponse, token: Manager.CancellationToken)).Value;
|
||||
var gametype = (await this.GetMappedDvarValueOrDefaultAsync<string>("g_gametype", "gametype", infoResponse, token: Manager.CancellationToken)).Value;
|
||||
var basepath = await this.GetMappedDvarValueOrDefaultAsync<string>("fs_basepath", token: Manager.CancellationToken);
|
||||
var basegame = await this.GetMappedDvarValueOrDefaultAsync<string>("fs_basegame", token: Manager.CancellationToken);
|
||||
var homepath = await this.GetMappedDvarValueOrDefaultAsync<string>("fs_homepath", token: Manager.CancellationToken);
|
||||
var game = await this.GetMappedDvarValueOrDefaultAsync<string>("fs_game", infoResponse: infoResponse, token: Manager.CancellationToken);
|
||||
var logfile = await this.GetMappedDvarValueOrDefaultAsync<string>("g_log", token: Manager.CancellationToken);
|
||||
var logsync = await this.GetMappedDvarValueOrDefaultAsync<int>("g_logsync", token: Manager.CancellationToken);
|
||||
var ip = await this.GetMappedDvarValueOrDefaultAsync<string>("net_ip", token: Manager.CancellationToken);
|
||||
var gamePassword = await this.GetMappedDvarValueOrDefaultAsync("g_password", overrideDefault: "", token: Manager.CancellationToken);
|
||||
|
||||
if (Manager.GetApplicationSettings().Configuration().EnableCustomSayName)
|
||||
{
|
||||
await this.SetDvarAsync("sv_sayname", Manager.GetApplicationSettings().Configuration().CustomSayName);
|
||||
await this.SetDvarAsync("sv_sayname", Manager.GetApplicationSettings().Configuration().CustomSayName,
|
||||
Manager.CancellationToken);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var website = await this.GetMappedDvarValueOrDefaultAsync<string>("_website");
|
||||
var website = await this.GetMappedDvarValueOrDefaultAsync<string>("_website", token: Manager.CancellationToken);
|
||||
|
||||
// this occurs for games that don't give us anything back when
|
||||
// the dvar is not set
|
||||
@ -1200,14 +1251,14 @@ namespace IW4MAdmin
|
||||
|
||||
if (logsync.Value == 0)
|
||||
{
|
||||
await this.SetDvarAsync("g_logsync", 2); // set to 2 for continous in other games, clamps to 1 for IW4
|
||||
await this.SetDvarAsync("g_logsync", 2, Manager.CancellationToken); // set to 2 for continous in other games, clamps to 1 for IW4
|
||||
needsRestart = true;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(logfile.Value))
|
||||
{
|
||||
logfile.Value = "games_mp.log";
|
||||
await this.SetDvarAsync("g_log", logfile.Value);
|
||||
await this.SetDvarAsync("g_log", logfile.Value, Manager.CancellationToken);
|
||||
needsRestart = true;
|
||||
}
|
||||
|
||||
@ -1219,7 +1270,7 @@ namespace IW4MAdmin
|
||||
}
|
||||
|
||||
// this DVAR isn't set until the a map is loaded
|
||||
await this.SetDvarAsync("logfile", 2);
|
||||
await this.SetDvarAsync("logfile", 2, Manager.CancellationToken);
|
||||
}
|
||||
|
||||
CustomCallback = await ScriptLoaded();
|
||||
@ -1444,7 +1495,6 @@ namespace IW4MAdmin
|
||||
Offender = targetClient,
|
||||
Offense = reason,
|
||||
Punisher = originClient,
|
||||
Link = targetClient.AliasLink,
|
||||
IsEvadedOffense = isEvade
|
||||
};
|
||||
|
||||
@ -1479,7 +1529,8 @@ namespace IW4MAdmin
|
||||
|
||||
ServerLogger.LogDebug("Creating unban penalty for {targetClient}", targetClient.ToString());
|
||||
targetClient.SetLevel(Permission.User, originClient);
|
||||
await Manager.GetPenaltyService().RemoveActivePenalties(targetClient.AliasLink.AliasLinkId);
|
||||
await Manager.GetPenaltyService().RemoveActivePenalties(targetClient.AliasLink.AliasLinkId,
|
||||
targetClient.NetworkId, targetClient.GameName, targetClient.CurrentAlias?.IPAddress);
|
||||
await Manager.GetPenaltyService().Create(unbanPenalty);
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ using SharedLibraryCore.Repositories;
|
||||
using SharedLibraryCore.Services;
|
||||
using Stats.Dtos;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
@ -26,6 +27,7 @@ using System.Threading.Tasks;
|
||||
using Data.Abstractions;
|
||||
using Data.Helpers;
|
||||
using Integrations.Source.Extensions;
|
||||
using IW4MAdmin.Application.Alerts;
|
||||
using IW4MAdmin.Application.Extensions;
|
||||
using IW4MAdmin.Application.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@ -76,8 +78,12 @@ namespace IW4MAdmin.Application
|
||||
/// <param name="e"></param>
|
||||
private static async void OnCancelKey(object sender, ConsoleCancelEventArgs e)
|
||||
{
|
||||
_serverManager?.Stop();
|
||||
if (_applicationTask != null)
|
||||
if (_serverManager is not null)
|
||||
{
|
||||
await _serverManager.Stop();
|
||||
}
|
||||
|
||||
if (_applicationTask is not null)
|
||||
{
|
||||
await _applicationTask;
|
||||
}
|
||||
@ -112,10 +118,11 @@ namespace IW4MAdmin.Application
|
||||
var tasks = new[]
|
||||
{
|
||||
versionChecker.CheckVersion(),
|
||||
_serverManager.Init(),
|
||||
_applicationTask
|
||||
};
|
||||
|
||||
await _serverManager.Init();
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
@ -138,11 +145,8 @@ namespace IW4MAdmin.Application
|
||||
|
||||
if (e is ConfigurationException configException)
|
||||
{
|
||||
if (translationLookup != null)
|
||||
{
|
||||
Console.WriteLine(translationLookup[configException.Message]
|
||||
.FormatExt(configException.ConfigurationFileName));
|
||||
}
|
||||
Console.WriteLine("{{fileName}} contains an error."
|
||||
.FormatExt(Path.GetFileName(configException.ConfigurationFileName)));
|
||||
|
||||
foreach (var error in configException.Errors)
|
||||
{
|
||||
@ -155,6 +159,11 @@ namespace IW4MAdmin.Application
|
||||
Console.WriteLine(e.Message);
|
||||
}
|
||||
|
||||
if (_serverManager is not null)
|
||||
{
|
||||
await _serverManager?.Stop();
|
||||
}
|
||||
|
||||
Console.WriteLine(exitMessage);
|
||||
await Console.In.ReadAsync(new char[1], 0, 1);
|
||||
return;
|
||||
@ -277,7 +286,7 @@ namespace IW4MAdmin.Application
|
||||
|
||||
// register the native commands
|
||||
foreach (var commandType in typeof(SharedLibraryCore.Commands.QuitCommand).Assembly.GetTypes()
|
||||
.Concat(typeof(Program).Assembly.GetTypes().Where(type => type.Namespace == "IW4MAdmin.Application.Commands"))
|
||||
.Concat(typeof(Program).Assembly.GetTypes().Where(type => type.Namespace?.StartsWith("IW4MAdmin.Application.Commands") ?? false))
|
||||
.Where(command => command.BaseType == typeof(Command)))
|
||||
{
|
||||
defaultLogger.LogDebug("Registered native command type {Name}", commandType.Name);
|
||||
@ -347,7 +356,7 @@ namespace IW4MAdmin.Application
|
||||
await defaultConfigHandler.BuildAsync();
|
||||
var commandConfigHandler = new BaseConfigurationHandler<CommandConfiguration>("CommandConfiguration");
|
||||
await commandConfigHandler.BuildAsync();
|
||||
var statsCommandHandler = new BaseConfigurationHandler<StatsConfiguration>();
|
||||
var statsCommandHandler = new BaseConfigurationHandler<StatsConfiguration>("StatsPluginSettings");
|
||||
await statsCommandHandler.BuildAsync();
|
||||
var defaultConfig = defaultConfigHandler.Configuration();
|
||||
var appConfig = appConfigHandler.Configuration();
|
||||
@ -404,7 +413,10 @@ namespace IW4MAdmin.Application
|
||||
.AddSingleton<IScriptCommandFactory, ScriptCommandFactory>()
|
||||
.AddSingleton<IAuditInformationRepository, AuditInformationRepository>()
|
||||
.AddSingleton<IEntityService<EFClient>, ClientService>()
|
||||
#pragma warning disable CS0618
|
||||
.AddSingleton<IMetaService, MetaService>()
|
||||
#pragma warning restore CS0618
|
||||
.AddSingleton<IMetaServiceV2, MetaServiceV2>()
|
||||
.AddSingleton<ClientService>()
|
||||
.AddSingleton<PenaltyService>()
|
||||
.AddSingleton<ChangeHistoryService>()
|
||||
@ -418,6 +430,7 @@ namespace IW4MAdmin.Application
|
||||
UpdatedAliasResourceQueryHelper>()
|
||||
.AddSingleton<IResourceQueryHelper<ChatSearchQuery, MessageResponse>, ChatResourceQueryHelper>()
|
||||
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, ConnectionHistoryResponse>, ConnectionsResourceQueryHelper>()
|
||||
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, PermissionLevelChangedResponse>, PermissionLevelChangedResourceQueryHelper>()
|
||||
.AddTransient<IParserPatternMatcher, ParserPatternMatcher>()
|
||||
.AddSingleton<IRemoteAssemblyHandler, RemoteAssemblyHandler>()
|
||||
.AddSingleton<IMasterCommunication, MasterCommunication>()
|
||||
@ -435,6 +448,8 @@ namespace IW4MAdmin.Application
|
||||
.AddSingleton<IServerDataViewer, ServerDataViewer>()
|
||||
.AddSingleton<IServerDataCollector, ServerDataCollector>()
|
||||
.AddSingleton<IEventPublisher, EventPublisher>()
|
||||
.AddSingleton<IGeoLocationService>(new GeoLocationService(Path.Join(".", "Resources", "GeoLite2-Country.mmdb")))
|
||||
.AddSingleton<IAlertManager, AlertManager>()
|
||||
.AddTransient<IScriptPluginTimerHelper, ScriptPluginTimerHelper>()
|
||||
.AddSingleton(translationLookup)
|
||||
.AddDatabaseContextOptions(appConfig);
|
||||
|
@ -5,6 +5,7 @@ using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
@ -15,19 +16,28 @@ namespace IW4MAdmin.Application.Meta
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private ITranslationLookup _transLookup;
|
||||
private readonly IMetaService _metaService;
|
||||
private readonly IMetaServiceV2 _metaService;
|
||||
private readonly IEntityService<EFClient> _clientEntityService;
|
||||
private readonly IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse> _receivedPenaltyHelper;
|
||||
private readonly IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse> _administeredPenaltyHelper;
|
||||
|
||||
private readonly IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse>
|
||||
_administeredPenaltyHelper;
|
||||
|
||||
private readonly IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse> _updatedAliasHelper;
|
||||
|
||||
private readonly IResourceQueryHelper<ClientPaginationRequest, ConnectionHistoryResponse>
|
||||
_connectionHistoryHelper;
|
||||
|
||||
public MetaRegistration(ILogger<MetaRegistration> logger, IMetaService metaService, ITranslationLookup transLookup, IEntityService<EFClient> clientEntityService,
|
||||
private readonly IResourceQueryHelper<ClientPaginationRequest, PermissionLevelChangedResponse>
|
||||
_permissionLevelHelper;
|
||||
|
||||
public MetaRegistration(ILogger<MetaRegistration> logger, IMetaServiceV2 metaService,
|
||||
ITranslationLookup transLookup, IEntityService<EFClient> clientEntityService,
|
||||
IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse> receivedPenaltyHelper,
|
||||
IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse> administeredPenaltyHelper,
|
||||
IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse> updatedAliasHelper,
|
||||
IResourceQueryHelper<ClientPaginationRequest, ConnectionHistoryResponse> connectionHistoryHelper)
|
||||
IResourceQueryHelper<ClientPaginationRequest, ConnectionHistoryResponse> connectionHistoryHelper,
|
||||
IResourceQueryHelper<ClientPaginationRequest, PermissionLevelChangedResponse> permissionLevelHelper)
|
||||
{
|
||||
_logger = logger;
|
||||
_transLookup = transLookup;
|
||||
@ -37,21 +47,31 @@ namespace IW4MAdmin.Application.Meta
|
||||
_administeredPenaltyHelper = administeredPenaltyHelper;
|
||||
_updatedAliasHelper = updatedAliasHelper;
|
||||
_connectionHistoryHelper = connectionHistoryHelper;
|
||||
_permissionLevelHelper = permissionLevelHelper;
|
||||
}
|
||||
|
||||
public void Register()
|
||||
{
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, InformationResponse>(MetaType.Information, GetProfileMeta);
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, ReceivedPenaltyResponse>(MetaType.ReceivedPenalty, GetReceivedPenaltiesMeta);
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, AdministeredPenaltyResponse>(MetaType.Penalized, GetAdministeredPenaltiesMeta);
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, UpdatedAliasResponse>(MetaType.AliasUpdate, GetUpdatedAliasMeta);
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, ConnectionHistoryResponse>(MetaType.ConnectionHistory, GetConnectionHistoryMeta);
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, InformationResponse>(MetaType.Information,
|
||||
GetProfileMeta);
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, ReceivedPenaltyResponse>(MetaType.ReceivedPenalty,
|
||||
GetReceivedPenaltiesMeta);
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, AdministeredPenaltyResponse>(MetaType.Penalized,
|
||||
GetAdministeredPenaltiesMeta);
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, UpdatedAliasResponse>(MetaType.AliasUpdate,
|
||||
GetUpdatedAliasMeta);
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, ConnectionHistoryResponse>(MetaType.ConnectionHistory,
|
||||
GetConnectionHistoryMeta);
|
||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, PermissionLevelChangedResponse>(
|
||||
MetaType.PermissionLevel, GetPermissionLevelMeta);
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<InformationResponse>> GetProfileMeta(ClientPaginationRequest request)
|
||||
private async Task<IEnumerable<InformationResponse>> GetProfileMeta(ClientPaginationRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var metaList = new List<InformationResponse>();
|
||||
var lastMapMeta = await _metaService.GetPersistentMeta("LastMapPlayed", new EFClient() { ClientId = request.ClientId });
|
||||
var lastMapMeta =
|
||||
await _metaService.GetPersistentMeta("LastMapPlayed", request.ClientId, cancellationToken);
|
||||
|
||||
if (lastMapMeta != null)
|
||||
{
|
||||
@ -63,12 +83,12 @@ namespace IW4MAdmin.Application.Meta
|
||||
Value = lastMapMeta.Value,
|
||||
ShouldDisplay = true,
|
||||
Type = MetaType.Information,
|
||||
Column = 1,
|
||||
Order = 6
|
||||
});
|
||||
}
|
||||
|
||||
var lastServerMeta = await _metaService.GetPersistentMeta("LastServerPlayed", new EFClient() { ClientId = request.ClientId });
|
||||
var lastServerMeta =
|
||||
await _metaService.GetPersistentMeta("LastServerPlayed", request.ClientId, cancellationToken);
|
||||
|
||||
if (lastServerMeta != null)
|
||||
{
|
||||
@ -80,8 +100,7 @@ namespace IW4MAdmin.Application.Meta
|
||||
Value = lastServerMeta.Value,
|
||||
ShouldDisplay = true,
|
||||
Type = MetaType.Information,
|
||||
Column = 0,
|
||||
Order = 6
|
||||
Order = 7
|
||||
});
|
||||
}
|
||||
|
||||
@ -89,7 +108,7 @@ namespace IW4MAdmin.Application.Meta
|
||||
|
||||
if (client == null)
|
||||
{
|
||||
_logger.LogWarning("No client found with id {clientId} when generating profile meta", request.ClientId);
|
||||
_logger.LogWarning("No client found with id {ClientId} when generating profile meta", request.ClientId);
|
||||
return metaList;
|
||||
}
|
||||
|
||||
@ -99,8 +118,7 @@ namespace IW4MAdmin.Application.Meta
|
||||
Key = _transLookup["WEBFRONT_PROFILE_META_PLAY_TIME"],
|
||||
Value = TimeSpan.FromHours(client.TotalConnectionTime / 3600.0).HumanizeForCurrentCulture(),
|
||||
ShouldDisplay = true,
|
||||
Column = 1,
|
||||
Order = 0,
|
||||
Order = 8,
|
||||
Type = MetaType.Information
|
||||
});
|
||||
|
||||
@ -110,8 +128,7 @@ namespace IW4MAdmin.Application.Meta
|
||||
Key = _transLookup["WEBFRONT_PROFILE_META_FIRST_SEEN"],
|
||||
Value = (DateTime.UtcNow - client.FirstConnection).HumanizeForCurrentCulture(),
|
||||
ShouldDisplay = true,
|
||||
Column = 1,
|
||||
Order = 1,
|
||||
Order = 9,
|
||||
Type = MetaType.Information
|
||||
});
|
||||
|
||||
@ -121,8 +138,7 @@ namespace IW4MAdmin.Application.Meta
|
||||
Key = _transLookup["WEBFRONT_PROFILE_META_LAST_SEEN"],
|
||||
Value = (DateTime.UtcNow - client.LastConnection).HumanizeForCurrentCulture(),
|
||||
ShouldDisplay = true,
|
||||
Column = 1,
|
||||
Order = 2,
|
||||
Order = 10,
|
||||
Type = MetaType.Information
|
||||
});
|
||||
|
||||
@ -130,10 +146,10 @@ namespace IW4MAdmin.Application.Meta
|
||||
{
|
||||
ClientId = client.ClientId,
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_CONNECTIONS"],
|
||||
Value = client.Connections.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Value = client.Connections.ToString("#,##0",
|
||||
new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
ShouldDisplay = true,
|
||||
Column = 1,
|
||||
Order = 3,
|
||||
Order = 11,
|
||||
Type = MetaType.Information
|
||||
});
|
||||
|
||||
@ -141,38 +157,50 @@ namespace IW4MAdmin.Application.Meta
|
||||
{
|
||||
ClientId = client.ClientId,
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_MASKED"],
|
||||
Value = client.Masked ? Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_TRUE"] : Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_FALSE"],
|
||||
Value = client.Masked
|
||||
? Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_TRUE"]
|
||||
: Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_FALSE"],
|
||||
IsSensitive = true,
|
||||
Column = 1,
|
||||
Order = 4,
|
||||
Order = 12,
|
||||
Type = MetaType.Information
|
||||
});
|
||||
|
||||
return metaList;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<ReceivedPenaltyResponse>> GetReceivedPenaltiesMeta(ClientPaginationRequest request)
|
||||
private async Task<IEnumerable<ReceivedPenaltyResponse>> GetReceivedPenaltiesMeta(
|
||||
ClientPaginationRequest request, CancellationToken token = default)
|
||||
{
|
||||
var penalties = await _receivedPenaltyHelper.QueryResource(request);
|
||||
return penalties.Results;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<AdministeredPenaltyResponse>> GetAdministeredPenaltiesMeta(ClientPaginationRequest request)
|
||||
private async Task<IEnumerable<AdministeredPenaltyResponse>> GetAdministeredPenaltiesMeta(
|
||||
ClientPaginationRequest request, CancellationToken token = default)
|
||||
{
|
||||
var penalties = await _administeredPenaltyHelper.QueryResource(request);
|
||||
return penalties.Results;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<UpdatedAliasResponse>> GetUpdatedAliasMeta(ClientPaginationRequest request)
|
||||
private async Task<IEnumerable<UpdatedAliasResponse>> GetUpdatedAliasMeta(ClientPaginationRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var aliases = await _updatedAliasHelper.QueryResource(request);
|
||||
return aliases.Results;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<ConnectionHistoryResponse>> GetConnectionHistoryMeta(ClientPaginationRequest request)
|
||||
|
||||
private async Task<IEnumerable<ConnectionHistoryResponse>> GetConnectionHistoryMeta(
|
||||
ClientPaginationRequest request, CancellationToken token = default)
|
||||
{
|
||||
var connections = await _connectionHistoryHelper.QueryResource(request);
|
||||
return connections.Results;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<PermissionLevelChangedResponse>> GetPermissionLevelMeta(
|
||||
ClientPaginationRequest request, CancellationToken token = default)
|
||||
{
|
||||
var permissionChanges = await _permissionLevelHelper.QueryResource(request);
|
||||
return permissionChanges.Results;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,51 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Abstractions;
|
||||
using Data.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
|
||||
namespace IW4MAdmin.Application.Meta;
|
||||
|
||||
public class
|
||||
PermissionLevelChangedResourceQueryHelper : IResourceQueryHelper<ClientPaginationRequest,
|
||||
PermissionLevelChangedResponse>
|
||||
{
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
|
||||
public PermissionLevelChangedResourceQueryHelper(IDatabaseContextFactory contextFactory)
|
||||
{
|
||||
_contextFactory = contextFactory;
|
||||
}
|
||||
|
||||
public async Task<ResourceQueryHelperResult<PermissionLevelChangedResponse>> QueryResource(
|
||||
ClientPaginationRequest query)
|
||||
{
|
||||
await using var context = _contextFactory.CreateContext();
|
||||
|
||||
var auditEntries = context.EFChangeHistory.Where(change => change.TargetEntityId == query.ClientId)
|
||||
.Where(change => change.TypeOfChange == EFChangeHistory.ChangeType.Permission);
|
||||
|
||||
var audits = from change in auditEntries
|
||||
join client in context.Clients
|
||||
on change.OriginEntityId equals client.ClientId
|
||||
select new PermissionLevelChangedResponse
|
||||
{
|
||||
ChangedById = change.OriginEntityId,
|
||||
ChangedByName = client.CurrentAlias.Name,
|
||||
PreviousPermissionLevelValue = change.PreviousValue,
|
||||
CurrentPermissionLevelValue = change.CurrentValue,
|
||||
When = change.TimeChanged,
|
||||
ClientId = change.TargetEntityId,
|
||||
IsSensitive = true
|
||||
};
|
||||
|
||||
return new ResourceQueryHelperResult<PermissionLevelChangedResponse>
|
||||
{
|
||||
Results = await audits.Skip(query.Offset).Take(query.Count).ToListAsync()
|
||||
};
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
using SharedLibraryCore.Services;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace IW4MAdmin.Application.Meta
|
||||
@ -19,13 +20,14 @@ namespace IW4MAdmin.Application.Meta
|
||||
/// implementation of IResourceQueryHelper
|
||||
/// used to pull in penalties applied to a given client id
|
||||
/// </summary>
|
||||
public class ReceivedPenaltyResourceQueryHelper : IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse>
|
||||
public class
|
||||
ReceivedPenaltyResourceQueryHelper : IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse>
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
private readonly ApplicationConfiguration _appConfig;
|
||||
|
||||
public ReceivedPenaltyResourceQueryHelper(ILogger<ReceivedPenaltyResourceQueryHelper> logger,
|
||||
public ReceivedPenaltyResourceQueryHelper(ILogger<ReceivedPenaltyResourceQueryHelper> logger,
|
||||
IDatabaseContextFactory contextFactory, ApplicationConfiguration appConfig)
|
||||
{
|
||||
_contextFactory = contextFactory;
|
||||
@ -33,45 +35,58 @@ namespace IW4MAdmin.Application.Meta
|
||||
_appConfig = appConfig;
|
||||
}
|
||||
|
||||
public async Task<ResourceQueryHelperResult<ReceivedPenaltyResponse>> QueryResource(ClientPaginationRequest query)
|
||||
public async Task<ResourceQueryHelperResult<ReceivedPenaltyResponse>> QueryResource(
|
||||
ClientPaginationRequest query)
|
||||
{
|
||||
var linkedPenaltyType = Utilities.LinkedPenaltyTypes();
|
||||
await using var ctx = _contextFactory.CreateContext(enableTracking: false);
|
||||
|
||||
var linkId = await ctx.Clients.AsNoTracking()
|
||||
.Where(_client => _client.ClientId == query.ClientId)
|
||||
.Select(_client => new {_client.AliasLinkId, _client.CurrentAliasId })
|
||||
.FirstOrDefaultAsync();
|
||||
.Where(_client => _client.ClientId == query.ClientId)
|
||||
.Select(_client => new { _client.AliasLinkId, _client.CurrentAliasId })
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
var iqPenalties = ctx.Penalties.AsNoTracking()
|
||||
.Where(_penalty => _penalty.OffenderId == query.ClientId ||
|
||||
linkedPenaltyType.Contains(_penalty.Type) && _penalty.LinkId == linkId.AliasLinkId);
|
||||
|
||||
IQueryable<EFPenalty> iqIpLinkedPenalties = null;
|
||||
|
||||
IQueryable<EFPenalty> identifierPenalties = null;
|
||||
|
||||
if (!_appConfig.EnableImplicitAccountLinking)
|
||||
{
|
||||
var usedIps = await ctx.Aliases.AsNoTracking()
|
||||
.Where(alias => (alias.LinkId == linkId.AliasLinkId || alias.AliasId == linkId.CurrentAliasId) && alias.IPAddress != null)
|
||||
.Where(alias =>
|
||||
(alias.LinkId == linkId.AliasLinkId || alias.AliasId == linkId.CurrentAliasId) &&
|
||||
alias.IPAddress != null)
|
||||
.Select(alias => alias.IPAddress).ToListAsync();
|
||||
|
||||
identifierPenalties = ctx.PenaltyIdentifiers.AsNoTracking().Where(identifier =>
|
||||
identifier.IPv4Address != null && usedIps.Contains(identifier.IPv4Address))
|
||||
.Select(id => id.Penalty);
|
||||
|
||||
var aliasedIds = await ctx.Aliases.AsNoTracking().Where(alias => usedIps.Contains(alias.IPAddress))
|
||||
.Select(alias => alias.LinkId)
|
||||
.ToListAsync();
|
||||
|
||||
iqIpLinkedPenalties = ctx.Penalties.AsNoTracking()
|
||||
.Where(penalty =>
|
||||
linkedPenaltyType.Contains(penalty.Type) && aliasedIds.Contains(penalty.LinkId));
|
||||
linkedPenaltyType.Contains(penalty.Type) && aliasedIds.Contains(penalty.LinkId ?? -1));
|
||||
}
|
||||
|
||||
var iqAllPenalties = iqPenalties;
|
||||
|
||||
|
||||
if (iqIpLinkedPenalties != null)
|
||||
{
|
||||
iqAllPenalties = iqPenalties.Union(iqIpLinkedPenalties);
|
||||
}
|
||||
|
||||
var penalties = await iqAllPenalties
|
||||
if (identifierPenalties != null)
|
||||
{
|
||||
iqAllPenalties = iqPenalties.Union(identifierPenalties);
|
||||
}
|
||||
|
||||
var penalties = await iqAllPenalties
|
||||
.Where(_penalty => _penalty.When < query.Before)
|
||||
.OrderByDescending(_penalty => _penalty.When)
|
||||
.Take(query.Count)
|
||||
@ -97,7 +112,7 @@ namespace IW4MAdmin.Application.Meta
|
||||
{
|
||||
// todo: maybe actually count
|
||||
RetrievedResultCount = penalties.Count,
|
||||
Results = penalties
|
||||
Results = penalties.Distinct()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ namespace IW4MAdmin.Application.Migration
|
||||
|
||||
public static void RemoveObsoletePlugins20210322()
|
||||
{
|
||||
var files = new[] {"StatsWeb.dll", "StatsWeb.Views.dll"};
|
||||
var files = new[] {"StatsWeb.dll", "StatsWeb.Views.dll", "IW4ScriptCommands.dll"};
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
|
12
Application/Misc/AsyncResult.cs
Normal file
12
Application/Misc/AsyncResult.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace IW4MAdmin.Application.Misc;
|
||||
|
||||
public class AsyncResult : IAsyncResult
|
||||
{
|
||||
public object AsyncState { get; set; }
|
||||
public WaitHandle AsyncWaitHandle { get; set; }
|
||||
public bool CompletedSynchronously { get; set; }
|
||||
public bool IsCompleted { get; set; }
|
||||
}
|
@ -27,6 +27,7 @@ namespace IW4MAdmin.Application.Misc
|
||||
_serializerOptions = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
};
|
||||
_serializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||
_onSaving = new SemaphoreSlim(1, 1);
|
||||
@ -59,7 +60,7 @@ namespace IW4MAdmin.Application.Misc
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ConfigurationException("MANAGER_CONFIGURATION_ERROR")
|
||||
throw new ConfigurationException("Could not load configuration")
|
||||
{
|
||||
Errors = new[] { e.Message },
|
||||
ConfigurationFileName = FileName
|
||||
|
@ -29,7 +29,7 @@ namespace IW4MAdmin.Application.Misc
|
||||
OnClientConnect?.Invoke(this, gameEvent);
|
||||
}
|
||||
|
||||
if (gameEvent.Type == GameEvent.EventType.Disconnect)
|
||||
if (gameEvent.Type == GameEvent.EventType.Disconnect && gameEvent.Origin.ClientId != 0)
|
||||
{
|
||||
OnClientDisconnect?.Invoke(this, gameEvent);
|
||||
}
|
||||
@ -41,4 +41,4 @@ namespace IW4MAdmin.Application.Misc
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
13
Application/Misc/GeoLocationResult.cs
Normal file
13
Application/Misc/GeoLocationResult.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using SharedLibraryCore.Interfaces;
|
||||
|
||||
namespace IW4MAdmin.Application.Misc;
|
||||
|
||||
public class GeoLocationResult : IGeoLocationResult
|
||||
{
|
||||
public string Country { get; set; }
|
||||
public string CountryCode { get; set; }
|
||||
public string Region { get; set; }
|
||||
public string ASN { get; set; }
|
||||
public string Timezone { get; set; }
|
||||
public string Organization { get; set; }
|
||||
}
|
40
Application/Misc/GeoLocationService.cs
Normal file
40
Application/Misc/GeoLocationService.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MaxMind.GeoIP2;
|
||||
using MaxMind.GeoIP2.Responses;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
|
||||
namespace IW4MAdmin.Application.Misc;
|
||||
|
||||
public class GeoLocationService : IGeoLocationService
|
||||
{
|
||||
private readonly string _sourceAddress;
|
||||
|
||||
public GeoLocationService(string sourceAddress)
|
||||
{
|
||||
_sourceAddress = sourceAddress;
|
||||
}
|
||||
|
||||
public Task<IGeoLocationResult> Locate(string address)
|
||||
{
|
||||
CountryResponse country = null;
|
||||
|
||||
try
|
||||
{
|
||||
using var reader = new DatabaseReader(_sourceAddress);
|
||||
reader.TryCountry(address, out country);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
var response = new GeoLocationResult
|
||||
{
|
||||
Country = country?.Country.Name ?? "Unknown",
|
||||
CountryCode = country?.Country.IsoCode ?? ""
|
||||
};
|
||||
|
||||
return Task.FromResult((IGeoLocationResult)response);
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ namespace IW4MAdmin.Application.Misc
|
||||
/// implementation of IMetaService
|
||||
/// used to add and retrieve runtime and persistent meta
|
||||
/// </summary>
|
||||
[Obsolete("Use MetaServiceV2")]
|
||||
public class MetaService : IMetaService
|
||||
{
|
||||
private readonly IDictionary<MetaType, List<dynamic>> _metaActions;
|
||||
|
453
Application/Misc/MetaServiceV2.cs
Normal file
453
Application/Misc/MetaServiceV2.cs
Normal file
@ -0,0 +1,453 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Abstractions;
|
||||
using Data.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace IW4MAdmin.Application.Misc;
|
||||
|
||||
public class MetaServiceV2 : IMetaServiceV2
|
||||
{
|
||||
private readonly IDictionary<MetaType, List<dynamic>> _metaActions;
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MetaServiceV2(ILogger<MetaServiceV2> logger, IDatabaseContextFactory contextFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_metaActions = new Dictionary<MetaType, List<dynamic>>();
|
||||
_contextFactory = contextFactory;
|
||||
}
|
||||
|
||||
public async Task SetPersistentMeta(string metaKey, string metaValue, int clientId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (!ValidArgs(metaKey, clientId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await using var context = _contextFactory.CreateContext();
|
||||
|
||||
var existingMeta = await context.EFMeta
|
||||
.Where(meta => meta.Key == metaKey)
|
||||
.Where(meta => meta.ClientId == clientId)
|
||||
.FirstOrDefaultAsync(token);
|
||||
|
||||
if (existingMeta != null)
|
||||
{
|
||||
_logger.LogDebug("Updating existing meta with key {Key} and id {Id}", existingMeta.Key,
|
||||
existingMeta.MetaId);
|
||||
existingMeta.Value = metaValue;
|
||||
existingMeta.Updated = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
_logger.LogDebug("Adding new meta with key {Key}", metaKey);
|
||||
context.EFMeta.Add(new EFMeta
|
||||
{
|
||||
ClientId = clientId,
|
||||
Created = DateTime.UtcNow,
|
||||
Key = metaKey,
|
||||
Value = metaValue,
|
||||
});
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync(token);
|
||||
}
|
||||
|
||||
public async Task SetPersistentMetaValue<T>(string metaKey, T metaValue, int clientId,
|
||||
CancellationToken token = default) where T : class
|
||||
{
|
||||
if (!ValidArgs(metaKey, clientId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string serializedValue;
|
||||
|
||||
try
|
||||
{
|
||||
serializedValue = JsonSerializer.Serialize(metaValue);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not serialize meta with key {Key}", metaKey);
|
||||
return;
|
||||
}
|
||||
|
||||
await SetPersistentMeta(metaKey, serializedValue, clientId, token);
|
||||
}
|
||||
|
||||
public async Task SetPersistentMetaForLookupKey(string metaKey, string lookupKey, int lookupId, int clientId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (!ValidArgs(metaKey, clientId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await using var context = _contextFactory.CreateContext();
|
||||
|
||||
var lookupMeta = await context.EFMeta.FirstOrDefaultAsync(meta => meta.Key == lookupKey, token);
|
||||
|
||||
if (lookupMeta is null)
|
||||
{
|
||||
_logger.LogWarning("No lookup meta exists for metaKey {MetaKey} and lookupKey {LookupKey}", metaKey,
|
||||
lookupKey);
|
||||
return;
|
||||
}
|
||||
|
||||
var lookupValues = JsonSerializer.Deserialize<List<LookupValue<string>>>(lookupMeta.Value);
|
||||
|
||||
if (lookupValues is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var foundLookup = lookupValues.FirstOrDefault(value => value.Id == lookupId);
|
||||
|
||||
if (foundLookup is null)
|
||||
{
|
||||
_logger.LogWarning("No lookup meta found for provided lookup id {MetaKey}, {LookupKey}, {LookupId}",
|
||||
metaKey, lookupKey, lookupId);
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogDebug("Setting meta for lookup {MetaKey}, {LookupKey}, {LookupId}",
|
||||
metaKey, lookupKey, lookupId);
|
||||
|
||||
await SetPersistentMeta(metaKey, lookupId.ToString(), clientId, token);
|
||||
}
|
||||
|
||||
public async Task IncrementPersistentMeta(string metaKey, int incrementAmount, int clientId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (!ValidArgs(metaKey, clientId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var existingMeta = await GetPersistentMeta(metaKey, clientId, token);
|
||||
|
||||
if (!long.TryParse(existingMeta?.Value ?? "0", out var existingValue))
|
||||
{
|
||||
existingValue = 0;
|
||||
}
|
||||
|
||||
var newValue = existingValue + incrementAmount;
|
||||
await SetPersistentMeta(metaKey, newValue.ToString(), clientId, token);
|
||||
}
|
||||
|
||||
public async Task DecrementPersistentMeta(string metaKey, int decrementAmount, int clientId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
await IncrementPersistentMeta(metaKey, -decrementAmount, clientId, token);
|
||||
}
|
||||
|
||||
public async Task<EFMeta> GetPersistentMeta(string metaKey, int clientId, CancellationToken token = default)
|
||||
{
|
||||
if (!ValidArgs(metaKey, clientId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
await using var ctx = _contextFactory.CreateContext(enableTracking: false);
|
||||
|
||||
return await ctx.EFMeta
|
||||
.Where(meta => meta.Key == metaKey)
|
||||
.Where(meta => meta.ClientId == clientId)
|
||||
.Select(meta => new EFMeta
|
||||
{
|
||||
MetaId = meta.MetaId,
|
||||
Key = meta.Key,
|
||||
ClientId = meta.ClientId,
|
||||
Value = meta.Value,
|
||||
})
|
||||
.FirstOrDefaultAsync(token);
|
||||
}
|
||||
|
||||
public async Task<T> GetPersistentMetaValue<T>(string metaKey, int clientId, CancellationToken token = default)
|
||||
where T : class
|
||||
{
|
||||
var meta = await GetPersistentMeta(metaKey, clientId, token);
|
||||
|
||||
if (meta is null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(meta.Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not deserialize meta with key {Key} and value {Value}", metaKey, meta.Value);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<EFMeta> GetPersistentMetaByLookup(string metaKey, string lookupKey, int clientId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
await using var context = _contextFactory.CreateContext();
|
||||
|
||||
var metaValue = await GetPersistentMeta(metaKey, clientId, token);
|
||||
|
||||
if (metaValue is null)
|
||||
{
|
||||
_logger.LogDebug("No meta exists for key {Key}, clientId {ClientId}", metaKey, clientId);
|
||||
return default;
|
||||
}
|
||||
|
||||
var lookupMeta = await context.EFMeta.FirstOrDefaultAsync(meta => meta.Key == lookupKey, token);
|
||||
|
||||
if (lookupMeta is null)
|
||||
{
|
||||
_logger.LogWarning("No lookup meta exists for metaKey {MetaKey} and lookupKey {LookupKey}", metaKey,
|
||||
lookupKey);
|
||||
return default;
|
||||
}
|
||||
|
||||
var lookupId = int.Parse(metaValue.Value);
|
||||
var lookupValues = JsonSerializer.Deserialize<List<LookupValue<string>>>(lookupMeta.Value);
|
||||
|
||||
if (lookupValues is null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var foundLookup = lookupValues.FirstOrDefault(value => value.Id == lookupId);
|
||||
|
||||
if (foundLookup is not null)
|
||||
{
|
||||
return new EFMeta
|
||||
{
|
||||
Created = metaValue.Created,
|
||||
Updated = metaValue.Updated,
|
||||
Extra = metaValue.Extra,
|
||||
MetaId = metaValue.MetaId,
|
||||
Value = foundLookup.Value
|
||||
};
|
||||
}
|
||||
|
||||
_logger.LogWarning("No lookup meta found for provided lookup id {MetaKey}, {LookupKey}, {LookupId}",
|
||||
metaKey, lookupKey, lookupId);
|
||||
return default;
|
||||
}
|
||||
|
||||
public async Task RemovePersistentMeta(string metaKey, int clientId, CancellationToken token = default)
|
||||
{
|
||||
if (!ValidArgs(metaKey, clientId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await using var context = _contextFactory.CreateContext();
|
||||
|
||||
var existingMeta = await context.EFMeta
|
||||
.FirstOrDefaultAsync(meta => meta.Key == metaKey && meta.ClientId == clientId, token);
|
||||
|
||||
if (existingMeta == null)
|
||||
{
|
||||
_logger.LogDebug("No meta with key {Key} found for client id {Id}", metaKey, clientId);
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogDebug("Removing meta for key {Key} with id {Id}", metaKey, existingMeta.MetaId);
|
||||
context.EFMeta.Remove(existingMeta);
|
||||
await context.SaveChangesAsync(token);
|
||||
}
|
||||
|
||||
public async Task SetPersistentMeta(string metaKey, string metaValue, CancellationToken token = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(metaKey))
|
||||
{
|
||||
_logger.LogWarning("Cannot save meta with no key");
|
||||
return;
|
||||
}
|
||||
|
||||
await using var ctx = _contextFactory.CreateContext();
|
||||
|
||||
var existingMeta = await ctx.EFMeta
|
||||
.Where(meta => meta.Key == metaKey)
|
||||
.Where(meta => meta.ClientId == null)
|
||||
.FirstOrDefaultAsync(token);
|
||||
|
||||
if (existingMeta is not null)
|
||||
{
|
||||
_logger.LogDebug("Updating existing meta with key {Key} and id {Id}", existingMeta.Key,
|
||||
existingMeta.MetaId);
|
||||
|
||||
existingMeta.Value = metaValue;
|
||||
existingMeta.Updated = DateTime.UtcNow;
|
||||
|
||||
await ctx.SaveChangesAsync(token);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
_logger.LogDebug("Adding new meta with key {Key}", metaKey);
|
||||
|
||||
ctx.EFMeta.Add(new EFMeta
|
||||
{
|
||||
Created = DateTime.UtcNow,
|
||||
Key = metaKey,
|
||||
Value = metaValue
|
||||
});
|
||||
|
||||
await ctx.SaveChangesAsync(token);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SetPersistentMetaValue<T>(string metaKey, T metaValue, CancellationToken token = default)
|
||||
where T : class
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(metaKey))
|
||||
{
|
||||
_logger.LogWarning("Meta key is null, not setting");
|
||||
return;
|
||||
}
|
||||
|
||||
if (metaValue is null)
|
||||
{
|
||||
_logger.LogWarning("Meta value is null, not setting");
|
||||
return;
|
||||
}
|
||||
|
||||
string serializedMeta;
|
||||
try
|
||||
{
|
||||
serializedMeta = JsonSerializer.Serialize(metaValue);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not serialize meta with {Key} and value {Value}", metaKey, metaValue);
|
||||
return;
|
||||
}
|
||||
|
||||
await SetPersistentMeta(metaKey, serializedMeta, token);
|
||||
}
|
||||
|
||||
public async Task<EFMeta> GetPersistentMeta(string metaKey, CancellationToken token = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(metaKey))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
await using var context = _contextFactory.CreateContext(false);
|
||||
return await context.EFMeta.FirstOrDefaultAsync(meta => meta.Key == metaKey, token);
|
||||
}
|
||||
|
||||
public async Task<T> GetPersistentMetaValue<T>(string metaKey, CancellationToken token = default) where T : class
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(metaKey))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var meta = await GetPersistentMeta(metaKey, token);
|
||||
|
||||
if (meta is null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(meta.Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not serialize meta with key {Key} and value {Value}", metaKey, meta.Value);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RemovePersistentMeta(string metaKey, CancellationToken token = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(metaKey))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await using var context = _contextFactory.CreateContext(enableTracking: false);
|
||||
|
||||
var existingMeta = await context.EFMeta
|
||||
.Where(meta => meta.Key == metaKey)
|
||||
.Where(meta => meta.ClientId == null)
|
||||
.FirstOrDefaultAsync(token);
|
||||
|
||||
if (existingMeta != null)
|
||||
{
|
||||
_logger.LogDebug("Removing meta for key {Key} with id {Id}", metaKey, existingMeta.MetaId);
|
||||
context.Remove(existingMeta);
|
||||
await context.SaveChangesAsync(token);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRuntimeMeta<T, TReturnType>(MetaType metaKey,
|
||||
Func<T, CancellationToken, Task<IEnumerable<TReturnType>>> metaAction)
|
||||
where T : PaginationRequest where TReturnType : IClientMeta
|
||||
{
|
||||
if (!_metaActions.ContainsKey(metaKey))
|
||||
{
|
||||
_metaActions.Add(metaKey, new List<dynamic> { metaAction });
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
_metaActions[metaKey].Add(metaAction);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<IClientMeta>> GetRuntimeMeta(ClientPaginationRequest request, CancellationToken token = default)
|
||||
{
|
||||
var metas = await Task.WhenAll(_metaActions.Where(kvp => kvp.Key != MetaType.Information)
|
||||
.Select(async kvp => await kvp.Value[0](request, token)));
|
||||
|
||||
return metas.SelectMany(m => (IEnumerable<IClientMeta>)m)
|
||||
.OrderByDescending(m => m.When)
|
||||
.Take(request.Count)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<T>> GetRuntimeMeta<T>(ClientPaginationRequest request, MetaType metaType, CancellationToken token = default)
|
||||
where T : IClientMeta
|
||||
{
|
||||
if (metaType == MetaType.Information)
|
||||
{
|
||||
var allMeta = new List<T>();
|
||||
|
||||
var completedMeta = await Task.WhenAll(_metaActions[metaType].Select(async individualMetaRegistration =>
|
||||
(IEnumerable<T>)await individualMetaRegistration(request, token)));
|
||||
|
||||
allMeta.AddRange(completedMeta.SelectMany(meta => meta));
|
||||
|
||||
return ProcessInformationMeta(allMeta);
|
||||
}
|
||||
|
||||
var meta = await _metaActions[metaType][0](request, token) as IEnumerable<T>;
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
private static IEnumerable<T> ProcessInformationMeta<T>(IEnumerable<T> meta) where T : IClientMeta
|
||||
{
|
||||
return meta;
|
||||
}
|
||||
|
||||
private static bool ValidArgs(string key, int clientId) => !string.IsNullOrWhiteSpace(key) && clientId > 0;
|
||||
}
|
@ -276,6 +276,8 @@ namespace IW4MAdmin.Application.Misc
|
||||
{
|
||||
_logger.LogDebug("OnLoad executing for {Name}", Name);
|
||||
_scriptEngine.SetValue("_manager", manager);
|
||||
_scriptEngine.SetValue("getDvar", BeginGetDvar);
|
||||
_scriptEngine.SetValue("setDvar", BeginSetDvar);
|
||||
_scriptEngine.Evaluate("plugin.onLoadAsync(_manager)");
|
||||
|
||||
return Task.CompletedTask;
|
||||
@ -341,7 +343,8 @@ namespace IW4MAdmin.Application.Misc
|
||||
/// <param name="commands">commands value from jint parser</param>
|
||||
/// <param name="scriptCommandFactory">factory to create the command from</param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<IManagerCommand> GenerateScriptCommands(JsValue commands, IScriptCommandFactory scriptCommandFactory)
|
||||
private IEnumerable<IManagerCommand> GenerateScriptCommands(JsValue commands,
|
||||
IScriptCommandFactory scriptCommandFactory)
|
||||
{
|
||||
var commandList = new List<IManagerCommand>();
|
||||
|
||||
@ -408,7 +411,7 @@ namespace IW4MAdmin.Application.Misc
|
||||
|
||||
_scriptEngine.SetValue("_event", gameEvent);
|
||||
var jsEventObject = _scriptEngine.Evaluate("_event");
|
||||
|
||||
|
||||
dynamicCommand.execute.Target.Invoke(_scriptEngine, jsEventObject);
|
||||
}
|
||||
|
||||
@ -422,7 +425,7 @@ namespace IW4MAdmin.Application.Misc
|
||||
|
||||
throw new PluginException("A runtime error occured while executing action for script plugin");
|
||||
}
|
||||
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
using (LogContext.PushProperty("Server", gameEvent.Owner?.ToString()))
|
||||
@ -451,6 +454,73 @@ namespace IW4MAdmin.Application.Misc
|
||||
|
||||
return commandList;
|
||||
}
|
||||
|
||||
private void BeginGetDvar(Server server, string dvarName, Delegate onCompleted)
|
||||
{
|
||||
var tokenSource = new CancellationTokenSource();
|
||||
tokenSource.CancelAfter(TimeSpan.FromSeconds(15));
|
||||
|
||||
server.BeginGetDvar(dvarName, result =>
|
||||
{
|
||||
var shouldRelease = false;
|
||||
try
|
||||
{
|
||||
_onProcessing.Wait(tokenSource.Token);
|
||||
shouldRelease = true;
|
||||
var (success, value) = (ValueTuple<bool, string>)result.AsyncState;
|
||||
|
||||
onCompleted.DynamicInvoke(JsValue.Undefined,
|
||||
new[]
|
||||
{
|
||||
JsValue.FromObject(_scriptEngine, server),
|
||||
JsValue.FromObject(_scriptEngine, dvarName),
|
||||
JsValue.FromObject(_scriptEngine, value),
|
||||
JsValue.FromObject(_scriptEngine, success)
|
||||
});
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
if (_onProcessing.CurrentCount == 0 && shouldRelease)
|
||||
{
|
||||
_onProcessing.Release();
|
||||
}
|
||||
}
|
||||
}, tokenSource.Token);
|
||||
}
|
||||
|
||||
private void BeginSetDvar(Server server, string dvarName, string dvarValue, Delegate onCompleted)
|
||||
{
|
||||
var tokenSource = new CancellationTokenSource();
|
||||
tokenSource.CancelAfter(TimeSpan.FromSeconds(15));
|
||||
|
||||
server.BeginSetDvar(dvarName, dvarValue, result =>
|
||||
{
|
||||
var shouldRelease = false;
|
||||
try
|
||||
{
|
||||
_onProcessing.Wait(tokenSource.Token);
|
||||
shouldRelease = true;
|
||||
var success = (bool)result.AsyncState;
|
||||
|
||||
onCompleted.DynamicInvoke(JsValue.Undefined,
|
||||
new[]
|
||||
{
|
||||
JsValue.FromObject(_scriptEngine, server),
|
||||
JsValue.FromObject(_scriptEngine, dvarName),
|
||||
JsValue.FromObject(_scriptEngine, dvarValue),
|
||||
JsValue.FromObject(_scriptEngine, success)
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_onProcessing.CurrentCount == 0 && shouldRelease)
|
||||
{
|
||||
_onProcessing.Release();
|
||||
}
|
||||
}
|
||||
}, tokenSource.Token);
|
||||
}
|
||||
}
|
||||
|
||||
public class PermissionLevelToStringConverter : IObjectConverter
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using IW4MAdmin.Application.Configuration;
|
||||
using Jint;
|
||||
@ -84,9 +85,9 @@ namespace IW4MAdmin.Application.Misc
|
||||
|
||||
var item = _config[_pluginName][key];
|
||||
|
||||
if (item is JArray array)
|
||||
if (item is JsonElement { ValueKind: JsonValueKind.Array } jElem)
|
||||
{
|
||||
item = array.ToObject<List<dynamic>>();
|
||||
item = jElem.Deserialize<List<dynamic>>();
|
||||
}
|
||||
|
||||
return JsValue.FromObject(_scriptEngine, item);
|
||||
|
@ -94,7 +94,8 @@ namespace IW4MAdmin.Application.Misc
|
||||
PeriodBlock = (int) (DateTimeOffset.UtcNow - DateTimeOffset.UnixEpoch).TotalMinutes,
|
||||
ServerId = await server.GetIdForServer(),
|
||||
MapId = await GetOrCreateMap(server.CurrentMap.Name, (Reference.Game) server.GameName, token),
|
||||
ClientCount = server.ClientNum
|
||||
ClientCount = server.ClientNum,
|
||||
ConnectionInterrupted = server.Throttled,
|
||||
}));
|
||||
|
||||
return data;
|
||||
@ -144,4 +145,4 @@ namespace IW4MAdmin.Application.Misc
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Abstractions;
|
||||
using Data.Models.Client;
|
||||
using Data.Models.Client.Stats;
|
||||
using Data.Models.Server;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@ -22,18 +23,20 @@ namespace IW4MAdmin.Application.Misc
|
||||
private readonly IDataValueCache<EFServerSnapshot, (int?, DateTime?)> _snapshotCache;
|
||||
private readonly IDataValueCache<EFClient, (int, int)> _serverStatsCache;
|
||||
private readonly IDataValueCache<EFServerSnapshot, List<ClientHistoryInfo>> _clientHistoryCache;
|
||||
private readonly IDataValueCache<EFClientRankingHistory, int> _rankedClientsCache;
|
||||
|
||||
private readonly TimeSpan? _cacheTimeSpan =
|
||||
Utilities.IsDevelopment ? TimeSpan.FromSeconds(30) : (TimeSpan?) TimeSpan.FromMinutes(10);
|
||||
|
||||
public ServerDataViewer(ILogger<ServerDataViewer> logger, IDataValueCache<EFServerSnapshot, (int?, DateTime?)> snapshotCache,
|
||||
IDataValueCache<EFClient, (int, int)> serverStatsCache,
|
||||
IDataValueCache<EFServerSnapshot, List<ClientHistoryInfo>> clientHistoryCache)
|
||||
IDataValueCache<EFServerSnapshot, List<ClientHistoryInfo>> clientHistoryCache, IDataValueCache<EFClientRankingHistory, int> rankedClientsCache)
|
||||
{
|
||||
_logger = logger;
|
||||
_snapshotCache = snapshotCache;
|
||||
_serverStatsCache = serverStatsCache;
|
||||
_clientHistoryCache = clientHistoryCache;
|
||||
_rankedClientsCache = rankedClientsCache;
|
||||
}
|
||||
|
||||
public async Task<(int?, DateTime?)>
|
||||
@ -135,7 +138,9 @@ namespace IW4MAdmin.Application.Misc
|
||||
{
|
||||
snapshot.ServerId,
|
||||
snapshot.CapturedAt,
|
||||
snapshot.ClientCount
|
||||
snapshot.ClientCount,
|
||||
snapshot.ConnectionInterrupted,
|
||||
MapName = snapshot.Map.Name,
|
||||
})
|
||||
.OrderBy(snapshot => snapshot.CapturedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
@ -143,8 +148,8 @@ namespace IW4MAdmin.Application.Misc
|
||||
return history.GroupBy(snapshot => snapshot.ServerId).Select(byServer => new ClientHistoryInfo
|
||||
{
|
||||
ServerId = byServer.Key,
|
||||
ClientCounts = byServer.Select(snapshot => new ClientCountSnapshot()
|
||||
{Time = snapshot.CapturedAt, ClientCount = snapshot.ClientCount}).ToList()
|
||||
ClientCounts = byServer.Select(snapshot => new ClientCountSnapshot
|
||||
{ Time = snapshot.CapturedAt, ClientCount = snapshot.ClientCount, ConnectionInterrupted = snapshot.ConnectionInterrupted ?? false, Map = snapshot.MapName}).ToList()
|
||||
}).ToList();
|
||||
}, nameof(_clientHistoryCache), TimeSpan.MaxValue);
|
||||
|
||||
@ -158,5 +163,30 @@ namespace IW4MAdmin.Application.Misc
|
||||
return Enumerable.Empty<ClientHistoryInfo>();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<int> RankedClientsCountAsync(long? serverId = null, CancellationToken token = default)
|
||||
{
|
||||
_rankedClientsCache.SetCacheItem(async (set, cancellationToken) =>
|
||||
{
|
||||
var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15);
|
||||
return await set
|
||||
.Where(rating => rating.Newest)
|
||||
.Where(rating => rating.ServerId == serverId)
|
||||
.Where(rating => rating.CreatedDateTime >= fifteenDaysAgo)
|
||||
.Where(rating => rating.Client.Level != EFClient.Permission.Banned)
|
||||
.Where(rating => rating.Ranking != null)
|
||||
.CountAsync(cancellationToken);
|
||||
}, nameof(_rankedClientsCache), serverId is null ? null: new[] { (object)serverId }, _cacheTimeSpan);
|
||||
|
||||
try
|
||||
{
|
||||
return await _rankedClientsCache.GetCacheItem(nameof(_rankedClientsCache), serverId, token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not retrieve data for {Name}", nameof(RankedClientsCountAsync));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,40 +9,41 @@ namespace IW4MAdmin.Application.Misc
|
||||
{
|
||||
internal class TokenAuthentication : ITokenAuthentication
|
||||
{
|
||||
private readonly ConcurrentDictionary<long, TokenState> _tokens;
|
||||
private readonly ConcurrentDictionary<int, TokenState> _tokens;
|
||||
private readonly RandomNumberGenerator _random;
|
||||
private static readonly TimeSpan TimeoutPeriod = new TimeSpan(0, 0, 120);
|
||||
private static readonly TimeSpan TimeoutPeriod = new(0, 0, 120);
|
||||
private const short TokenLength = 4;
|
||||
|
||||
public TokenAuthentication()
|
||||
{
|
||||
_tokens = new ConcurrentDictionary<long, TokenState>();
|
||||
_tokens = new ConcurrentDictionary<int, TokenState>();
|
||||
_random = RandomNumberGenerator.Create();
|
||||
}
|
||||
|
||||
public bool AuthorizeToken(long networkId, string token)
|
||||
public bool AuthorizeToken(ITokenIdentifier authInfo)
|
||||
{
|
||||
var authorizeSuccessful = _tokens.ContainsKey(networkId) && _tokens[networkId].Token == token;
|
||||
var authorizeSuccessful = _tokens.ContainsKey(authInfo.ClientId) &&
|
||||
_tokens[authInfo.ClientId].Token == authInfo.Token;
|
||||
|
||||
if (authorizeSuccessful)
|
||||
{
|
||||
_tokens.TryRemove(networkId, out _);
|
||||
_tokens.TryRemove(authInfo.ClientId, out _);
|
||||
}
|
||||
|
||||
return authorizeSuccessful;
|
||||
}
|
||||
|
||||
public TokenState GenerateNextToken(long networkId)
|
||||
public TokenState GenerateNextToken(ITokenIdentifier authInfo)
|
||||
{
|
||||
TokenState state;
|
||||
|
||||
if (_tokens.ContainsKey(networkId))
|
||||
if (_tokens.ContainsKey(authInfo.ClientId))
|
||||
{
|
||||
state = _tokens[networkId];
|
||||
state = _tokens[authInfo.ClientId];
|
||||
|
||||
if ((DateTime.Now - state.RequestTime) > TimeoutPeriod)
|
||||
if (DateTime.Now - state.RequestTime > TimeoutPeriod)
|
||||
{
|
||||
_tokens.TryRemove(networkId, out _);
|
||||
_tokens.TryRemove(authInfo.ClientId, out _);
|
||||
}
|
||||
|
||||
else
|
||||
@ -53,17 +54,16 @@ namespace IW4MAdmin.Application.Misc
|
||||
|
||||
state = new TokenState
|
||||
{
|
||||
NetworkId = networkId,
|
||||
Token = _generateToken(),
|
||||
TokenDuration = TimeoutPeriod
|
||||
};
|
||||
|
||||
_tokens.TryAdd(networkId, state);
|
||||
_tokens.TryAdd(authInfo.ClientId, state);
|
||||
|
||||
// perform some housekeeping so we don't have built up tokens if they're not ever used
|
||||
foreach (var (key, value) in _tokens)
|
||||
{
|
||||
if ((DateTime.Now - value.RequestTime) > TimeoutPeriod)
|
||||
if (DateTime.Now - value.RequestTime > TimeoutPeriod)
|
||||
{
|
||||
_tokens.TryRemove(key, out _);
|
||||
}
|
||||
|
@ -7,8 +7,10 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Models;
|
||||
using IW4MAdmin.Application.Misc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using static SharedLibraryCore.Server;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
@ -18,6 +20,7 @@ namespace IW4MAdmin.Application.RConParsers
|
||||
public class BaseRConParser : IRConParser
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private static string _botIpIndicator = "00000000.";
|
||||
|
||||
public BaseRConParser(ILogger<BaseRConParser> logger, IParserRegexFactory parserRegexFactory)
|
||||
{
|
||||
@ -77,19 +80,20 @@ namespace IW4MAdmin.Application.RConParsers
|
||||
public string RConEngine { get; set; } = "COD";
|
||||
public bool IsOneLog { get; set; }
|
||||
|
||||
public async Task<string[]> ExecuteCommandAsync(IRConConnection connection, string command)
|
||||
public async Task<string[]> ExecuteCommandAsync(IRConConnection connection, string command, CancellationToken token = default)
|
||||
{
|
||||
var response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, command);
|
||||
command = command.FormatMessageForEngine(Configuration?.ColorCodeMapping);
|
||||
var response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, command, token);
|
||||
return response.Where(item => item != Configuration.CommandPrefixes.RConResponse).ToArray();
|
||||
}
|
||||
|
||||
public async Task<Dvar<T>> GetDvarAsync<T>(IRConConnection connection, string dvarName, T fallbackValue = default)
|
||||
public async Task<Dvar<T>> GetDvarAsync<T>(IRConConnection connection, string dvarName, T fallbackValue = default, CancellationToken token = default)
|
||||
{
|
||||
string[] lineSplit;
|
||||
|
||||
try
|
||||
{
|
||||
lineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.GET_DVAR, dvarName);
|
||||
lineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.GET_DVAR, dvarName, token);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -98,10 +102,10 @@ namespace IW4MAdmin.Application.RConParsers
|
||||
throw;
|
||||
}
|
||||
|
||||
lineSplit = new string[0];
|
||||
lineSplit = Array.Empty<string>();
|
||||
}
|
||||
|
||||
string response = string.Join('\n', lineSplit).TrimEnd('\0');
|
||||
var response = string.Join('\n', lineSplit).TrimEnd('\0');
|
||||
var match = Regex.Match(response, Configuration.Dvar.Pattern);
|
||||
|
||||
if (response.Contains("Unknown command") ||
|
||||
@ -109,7 +113,7 @@ namespace IW4MAdmin.Application.RConParsers
|
||||
{
|
||||
if (fallbackValue != null)
|
||||
{
|
||||
return new Dvar<T>()
|
||||
return new Dvar<T>
|
||||
{
|
||||
Name = dvarName,
|
||||
Value = fallbackValue
|
||||
@ -119,17 +123,17 @@ namespace IW4MAdmin.Application.RConParsers
|
||||
throw new DvarException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_DVAR"].FormatExt(dvarName));
|
||||
}
|
||||
|
||||
string value = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarValue]].Value;
|
||||
string defaultValue = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarDefaultValue]].Value;
|
||||
string latchedValue = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarLatchedValue]].Value;
|
||||
var value = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarValue]].Value;
|
||||
var defaultValue = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarDefaultValue]].Value;
|
||||
var latchedValue = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarLatchedValue]].Value;
|
||||
|
||||
string removeTrailingColorCode(string input) => Regex.Replace(input, @"\^7$", "");
|
||||
string RemoveTrailingColorCode(string input) => Regex.Replace(input, @"\^7$", "");
|
||||
|
||||
value = removeTrailingColorCode(value);
|
||||
defaultValue = removeTrailingColorCode(defaultValue);
|
||||
latchedValue = removeTrailingColorCode(latchedValue);
|
||||
value = RemoveTrailingColorCode(value);
|
||||
defaultValue = RemoveTrailingColorCode(defaultValue);
|
||||
latchedValue = RemoveTrailingColorCode(latchedValue);
|
||||
|
||||
return new Dvar<T>()
|
||||
return new Dvar<T>
|
||||
{
|
||||
Name = dvarName,
|
||||
Value = string.IsNullOrEmpty(value) ? default : (T)Convert.ChangeType(value, typeof(T)),
|
||||
@ -139,10 +143,36 @@ namespace IW4MAdmin.Application.RConParsers
|
||||
};
|
||||
}
|
||||
|
||||
public virtual async Task<IStatusResponse> GetStatusAsync(IRConConnection connection)
|
||||
public void BeginGetDvar(IRConConnection connection, string dvarName, AsyncCallback callback, CancellationToken token = default)
|
||||
{
|
||||
var response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND_STATUS);
|
||||
_logger.LogDebug("Status Response {response}", string.Join(Environment.NewLine, response));
|
||||
GetDvarAsync<string>(connection, dvarName, token: token).ContinueWith(action =>
|
||||
{
|
||||
if (action.Exception is null)
|
||||
{
|
||||
callback?.Invoke(new AsyncResult
|
||||
{
|
||||
IsCompleted = true,
|
||||
AsyncState = (true, action.Result.Value)
|
||||
});
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
callback?.Invoke(new AsyncResult
|
||||
{
|
||||
IsCompleted = true,
|
||||
AsyncState = (false, (string)null)
|
||||
});
|
||||
}
|
||||
}, token);
|
||||
}
|
||||
|
||||
public virtual async Task<IStatusResponse> GetStatusAsync(IRConConnection connection, CancellationToken token = default)
|
||||
{
|
||||
var response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND_STATUS, "status", token);
|
||||
|
||||
_logger.LogDebug("Status Response {Response}", string.Join(Environment.NewLine, response));
|
||||
|
||||
return new StatusResponse
|
||||
{
|
||||
Clients = ClientsFromStatus(response).ToArray(),
|
||||
@ -183,13 +213,38 @@ namespace IW4MAdmin.Application.RConParsers
|
||||
return (T)Convert.ChangeType(value, typeof(T));
|
||||
}
|
||||
|
||||
public async Task<bool> SetDvarAsync(IRConConnection connection, string dvarName, object dvarValue)
|
||||
public async Task<bool> SetDvarAsync(IRConConnection connection, string dvarName, object dvarValue, CancellationToken token = default)
|
||||
{
|
||||
string dvarString = (dvarValue is string str)
|
||||
var dvarString = (dvarValue is string str)
|
||||
? $"{dvarName} \"{str}\""
|
||||
: $"{dvarName} {dvarValue}";
|
||||
|
||||
return (await connection.SendQueryAsync(StaticHelpers.QueryType.SET_DVAR, dvarString)).Length > 0;
|
||||
return (await connection.SendQueryAsync(StaticHelpers.QueryType.SET_DVAR, dvarString, token)).Length > 0;
|
||||
}
|
||||
|
||||
public void BeginSetDvar(IRConConnection connection, string dvarName, object dvarValue, AsyncCallback callback,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
SetDvarAsync(connection, dvarName, dvarValue, token).ContinueWith(action =>
|
||||
{
|
||||
if (action.Exception is null)
|
||||
{
|
||||
callback?.Invoke(new AsyncResult
|
||||
{
|
||||
IsCompleted = true,
|
||||
AsyncState = true
|
||||
});
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
callback?.Invoke(new AsyncResult
|
||||
{
|
||||
IsCompleted = true,
|
||||
AsyncState = false
|
||||
});
|
||||
}
|
||||
}, token);
|
||||
}
|
||||
|
||||
private List<EFClient> ClientsFromStatus(string[] Status)
|
||||
@ -236,8 +291,15 @@ namespace IW4MAdmin.Application.RConParsers
|
||||
long networkId;
|
||||
var name = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConName]].TrimNewLine();
|
||||
string networkIdString;
|
||||
|
||||
var ip = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConIpAddress]].Split(':')[0].ConvertToIP();
|
||||
|
||||
if (match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConIpAddress]]
|
||||
.Contains(_botIpIndicator))
|
||||
{
|
||||
ip = System.Net.IPAddress.Broadcast.ToString().ConvertToIP();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
networkIdString = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConNetworkId]];
|
||||
@ -252,9 +314,9 @@ namespace IW4MAdmin.Application.RConParsers
|
||||
continue;
|
||||
}
|
||||
|
||||
var client = new EFClient()
|
||||
var client = new EFClient
|
||||
{
|
||||
CurrentAlias = new EFAlias()
|
||||
CurrentAlias = new EFAlias
|
||||
{
|
||||
Name = name,
|
||||
IPAddress = ip
|
||||
|
@ -46,7 +46,7 @@ namespace IW4MAdmin.Application.RConParsers
|
||||
{ColorCodes.White.ToString(), "^7"},
|
||||
{ColorCodes.Map.ToString(), "^8"},
|
||||
{ColorCodes.Grey.ToString(), "^9"},
|
||||
{ColorCodes.Wildcard.ToString(), ":^"},
|
||||
{ColorCodes.Wildcard.ToString(), "^:"}
|
||||
};
|
||||
|
||||
public DynamicRConParserConfiguration(IParserRegexFactory parserRegexFactory)
|
||||
|
BIN
Application/Resources/GeoLite2-Country.mmdb
Normal file
BIN
Application/Resources/GeoLite2-Country.mmdb
Normal file
Binary file not shown.
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -9,6 +10,11 @@ namespace Data.Abstractions
|
||||
{
|
||||
void SetCacheItem(Func<DbSet<TEntityType>, CancellationToken, Task<TReturnType>> itemGetter, string keyName,
|
||||
TimeSpan? expirationTime = null, bool autoRefresh = false);
|
||||
|
||||
void SetCacheItem(Func<DbSet<TEntityType>, CancellationToken, Task<TReturnType>> itemGetter, string keyName,
|
||||
IEnumerable<object> ids = null, TimeSpan? expirationTime = null, bool autoRefresh = false);
|
||||
|
||||
Task<TReturnType> GetCacheItem(string keyName, CancellationToken token = default);
|
||||
Task<TReturnType> GetCacheItem(string keyName, object id = null, CancellationToken token = default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ namespace Data.Context
|
||||
public DbSet<EFAlias> Aliases { get; set; }
|
||||
public DbSet<EFAliasLink> AliasLinks { get; set; }
|
||||
public DbSet<EFPenalty> Penalties { get; set; }
|
||||
public DbSet<EFPenaltyIdentifier> PenaltyIdentifiers { get; set; }
|
||||
public DbSet<EFMeta> EFMeta { get; set; }
|
||||
public DbSet<EFChangeHistory> EFChangeHistory { get; set; }
|
||||
|
||||
@ -84,7 +85,15 @@ namespace Data.Context
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
// make network id unique
|
||||
modelBuilder.Entity<EFClient>(entity => { entity.HasIndex(e => e.NetworkId).IsUnique(); });
|
||||
modelBuilder.Entity<EFClient>(entity =>
|
||||
{
|
||||
entity.HasIndex(e => e.NetworkId);
|
||||
entity.HasAlternateKey(client => new
|
||||
{
|
||||
client.NetworkId,
|
||||
client.GameName
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity<EFPenalty>(entity =>
|
||||
{
|
||||
@ -119,6 +128,9 @@ namespace Data.Context
|
||||
ent.Property(_alias => _alias.SearchableName).HasMaxLength(24);
|
||||
ent.HasIndex(_alias => _alias.SearchableName);
|
||||
ent.HasIndex(_alias => new {_alias.Name, _alias.IPAddress});
|
||||
ent.Property(alias => alias.SearchableIPAddress)
|
||||
.HasComputedColumnSql(@"((IPAddress & 255) || '.' || ((IPAddress >> 8) & 255)) || '.' || ((IPAddress >> 16) & 255) || '.' || ((IPAddress >> 24) & 255)", stored: true);
|
||||
ent.HasIndex(alias => alias.SearchableIPAddress);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<EFMeta>(ent =>
|
||||
@ -130,6 +142,12 @@ namespace Data.Context
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<EFPenaltyIdentifier>(ent =>
|
||||
{
|
||||
ent.HasIndex(identifiers => identifiers.NetworkId);
|
||||
ent.HasIndex(identifiers => identifiers.IPv4Address);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<EFClientConnectionHistory>(ent => ent.HasIndex(history => history.CreatedDateTime));
|
||||
|
||||
// force full name for database conversion
|
||||
@ -137,6 +155,7 @@ namespace Data.Context
|
||||
modelBuilder.Entity<EFAlias>().ToTable("EFAlias");
|
||||
modelBuilder.Entity<EFAliasLink>().ToTable("EFAliasLinks");
|
||||
modelBuilder.Entity<EFPenalty>().ToTable("EFPenalties");
|
||||
modelBuilder.Entity<EFPenaltyIdentifier>().ToTable("EFPenaltyIdentifiers");
|
||||
modelBuilder.Entity<EFServerSnapshot>().ToTable(nameof(EFServerSnapshot));
|
||||
modelBuilder.Entity<EFClientConnectionHistory>().ToTable(nameof(EFClientConnectionHistory));
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Abstractions;
|
||||
@ -15,8 +17,8 @@ namespace Data.Helpers
|
||||
private readonly ILogger _logger;
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
|
||||
private readonly ConcurrentDictionary<string, CacheState<TReturnType>> _cacheStates =
|
||||
new ConcurrentDictionary<string, CacheState<TReturnType>>();
|
||||
private readonly ConcurrentDictionary<string, Dictionary<object, CacheState<TReturnType>>> _cacheStates = new();
|
||||
private readonly object _defaultKey = new();
|
||||
|
||||
private bool _autoRefresh;
|
||||
private const int DefaultExpireMinutes = 15;
|
||||
@ -51,41 +53,61 @@ namespace Data.Helpers
|
||||
public void SetCacheItem(Func<DbSet<TEntityType>, CancellationToken, Task<TReturnType>> getter, string key,
|
||||
TimeSpan? expirationTime = null, bool autoRefresh = false)
|
||||
{
|
||||
if (_cacheStates.ContainsKey(key))
|
||||
{
|
||||
_logger.LogDebug("Cache key {Key} is already added", key);
|
||||
return;
|
||||
}
|
||||
|
||||
var state = new CacheState<TReturnType>
|
||||
{
|
||||
Key = key,
|
||||
Getter = getter,
|
||||
ExpirationTime = expirationTime ?? TimeSpan.FromMinutes(DefaultExpireMinutes)
|
||||
};
|
||||
|
||||
_autoRefresh = autoRefresh;
|
||||
|
||||
_cacheStates.TryAdd(key, state);
|
||||
|
||||
if (!_autoRefresh || expirationTime == TimeSpan.MaxValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timer = new Timer(state.ExpirationTime.TotalMilliseconds);
|
||||
_timer.Elapsed += async (sender, args) => await RunCacheUpdate(state, CancellationToken.None);
|
||||
_timer.Start();
|
||||
SetCacheItem(getter, key, null, expirationTime, autoRefresh);
|
||||
}
|
||||
|
||||
public async Task<TReturnType> GetCacheItem(string keyName, CancellationToken cancellationToken = default)
|
||||
public void SetCacheItem(Func<DbSet<TEntityType>, CancellationToken, Task<TReturnType>> getter, string key,
|
||||
IEnumerable<object> ids = null, TimeSpan? expirationTime = null, bool autoRefresh = false)
|
||||
{
|
||||
ids ??= new[] { _defaultKey };
|
||||
|
||||
if (!_cacheStates.ContainsKey(key))
|
||||
{
|
||||
_cacheStates.TryAdd(key, new Dictionary<object, CacheState<TReturnType>>());
|
||||
}
|
||||
|
||||
foreach (var id in ids)
|
||||
{
|
||||
if (_cacheStates[key].ContainsKey(id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var state = new CacheState<TReturnType>
|
||||
{
|
||||
Key = key,
|
||||
Getter = getter,
|
||||
ExpirationTime = expirationTime ?? TimeSpan.FromMinutes(DefaultExpireMinutes)
|
||||
};
|
||||
|
||||
_cacheStates[key].Add(id, state);
|
||||
|
||||
_autoRefresh = autoRefresh;
|
||||
|
||||
|
||||
if (!_autoRefresh || expirationTime == TimeSpan.MaxValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timer = new Timer(state.ExpirationTime.TotalMilliseconds);
|
||||
_timer.Elapsed += async (sender, args) => await RunCacheUpdate(state, CancellationToken.None);
|
||||
_timer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<TReturnType> GetCacheItem(string keyName, CancellationToken cancellationToken = default) =>
|
||||
await GetCacheItem(keyName, null, cancellationToken);
|
||||
|
||||
public async Task<TReturnType> GetCacheItem(string keyName, object id = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!_cacheStates.ContainsKey(keyName))
|
||||
{
|
||||
throw new ArgumentException("No cache found for key {key}", keyName);
|
||||
}
|
||||
|
||||
var state = _cacheStates[keyName];
|
||||
var state = id is null ? _cacheStates[keyName].Values.First() : _cacheStates[keyName][id];
|
||||
|
||||
// when auto refresh is off we want to check the expiration and value
|
||||
// when auto refresh is on, we want to only check the value, because it'll be refreshed automatically
|
||||
@ -115,4 +137,4 @@ namespace Data.Helpers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,8 @@ namespace Data.MigrationContext
|
||||
{
|
||||
if (MigrationExtensions.IsMigration)
|
||||
{
|
||||
optionsBuilder.UseMySql(ServerVersion.AutoDetect("Server=127.0.0.1;Database=IW4MAdmin_Migration;Uid=root;Pwd=password;"))
|
||||
var connectionString = "Server=127.0.0.1;Database=IW4MAdmin_Migration;Uid=root;Pwd=password;";
|
||||
optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString))
|
||||
.EnableDetailedErrors()
|
||||
.EnableSensitiveDataLogging();
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ namespace Data.MigrationContext
|
||||
{
|
||||
if (MigrationExtensions.IsMigration)
|
||||
{
|
||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||
optionsBuilder.UseNpgsql(
|
||||
"Host=127.0.0.1;Database=IW4MAdmin_Migration;Username=postgres;Password=password;",
|
||||
options => options.SetPostgresVersion(new Version("12.9")))
|
||||
|
1622
Data/Migrations/MySql/20220222215149_AddEFPenaltyIdentifier.Designer.cs
generated
Normal file
1622
Data/Migrations/MySql/20220222215149_AddEFPenaltyIdentifier.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
250
Data/Migrations/MySql/20220222215149_AddEFPenaltyIdentifier.cs
Normal file
250
Data/Migrations/MySql/20220222215149_AddEFPenaltyIdentifier.cs
Normal file
@ -0,0 +1,250 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
public partial class AddEFPenaltyIdentifier : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFPenaltyIdentifiers",
|
||||
columns: table => new
|
||||
{
|
||||
PenaltyIdentifierId = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
IPv4Address = table.Column<int>(type: "int", nullable: true),
|
||||
NetworkId = table.Column<long>(type: "bigint", nullable: false),
|
||||
PenaltyId = table.Column<int>(type: "int", nullable: false),
|
||||
Active = table.Column<bool>(type: "tinyint(1)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFPenaltyIdentifiers", x => x.PenaltyIdentifierId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFPenaltyIdentifiers_EFPenalties_PenaltyId",
|
||||
column: x => x.PenaltyId,
|
||||
principalTable: "EFPenalties",
|
||||
principalColumn: "PenaltyId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFPenaltyIdentifiers_IPv4Address",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
column: "IPv4Address");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFPenaltyIdentifiers_NetworkId",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
column: "NetworkId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFPenaltyIdentifiers_PenaltyId",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
column: "PenaltyId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFPenaltyIdentifiers");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Message",
|
||||
table: "InboxMessages",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Name",
|
||||
table: "EFWeaponAttachments",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext")
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "HostName",
|
||||
table: "EFServers",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "EndPoint",
|
||||
table: "EFServers",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Offense",
|
||||
table: "EFPenalties",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext")
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "AutomatedOffense",
|
||||
table: "EFPenalties",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Value",
|
||||
table: "EFMeta",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext")
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Extra",
|
||||
table: "EFMeta",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Name",
|
||||
table: "EFMeansOfDeath",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext")
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Name",
|
||||
table: "EFMaps",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext")
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "PasswordSalt",
|
||||
table: "EFClients",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Password",
|
||||
table: "EFClients",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Message",
|
||||
table: "EFClientMessages",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "WeaponReference",
|
||||
table: "EFClientKills",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "PreviousValue",
|
||||
table: "EFChangeHistory",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "CurrentValue",
|
||||
table: "EFChangeHistory",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "WeaponReference",
|
||||
table: "EFACSnapshot",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "HitLocationReference",
|
||||
table: "EFACSnapshot",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
}
|
||||
}
|
1620
Data/Migrations/MySql/20220222230049_MakeEFPenaltyLinkIdNullable.Designer.cs
generated
Normal file
1620
Data/Migrations/MySql/20220222230049_MakeEFPenaltyLinkIdNullable.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,56 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
public partial class MakeEFPenaltyLinkIdNullable : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_EFPenalties_EFAliasLinks_LinkId",
|
||||
table: "EFPenalties");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "LinkId",
|
||||
table: "EFPenalties",
|
||||
type: "int",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_EFPenalties_EFAliasLinks_LinkId",
|
||||
table: "EFPenalties",
|
||||
column: "LinkId",
|
||||
principalTable: "EFAliasLinks",
|
||||
principalColumn: "AliasLinkId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_EFPenalties_EFAliasLinks_LinkId",
|
||||
table: "EFPenalties");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "LinkId",
|
||||
table: "EFPenalties",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_EFPenalties_EFAliasLinks_LinkId",
|
||||
table: "EFPenalties",
|
||||
column: "LinkId",
|
||||
principalTable: "EFAliasLinks",
|
||||
principalColumn: "AliasLinkId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
1623
Data/Migrations/MySql/20220223150028_AddAuditFieldsToEFPenaltyIdentifier.Designer.cs
generated
Normal file
1623
Data/Migrations/MySql/20220223150028_AddAuditFieldsToEFPenaltyIdentifier.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
public partial class AddAuditFieldsToEFPenaltyIdentifier : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Active",
|
||||
table: "EFPenaltyIdentifiers");
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "CreatedDateTime",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
type: "datetime(6)",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "UpdatedDateTime",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
type: "datetime(6)",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CreatedDateTime",
|
||||
table: "EFPenaltyIdentifiers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UpdatedDateTime",
|
||||
table: "EFPenaltyIdentifiers");
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "Active",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
}
|
||||
}
|
1626
Data/Migrations/MySql/20220329213440_AddConnectionInterruptedToEFServerSnapshot.Designer.cs
generated
Normal file
1626
Data/Migrations/MySql/20220329213440_AddConnectionInterruptedToEFServerSnapshot.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,25 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
public partial class AddConnectionInterruptedToEFServerSnapshot : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "ConnectionInterrupted",
|
||||
table: "EFServerSnapshot",
|
||||
type: "tinyint(1)",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ConnectionInterrupted",
|
||||
table: "EFServerSnapshot");
|
||||
}
|
||||
}
|
||||
}
|
1631
Data/Migrations/MySql/20220404151444_AddSearchableIPToEFAlias.Designer.cs
generated
Normal file
1631
Data/Migrations/MySql/20220404151444_AddSearchableIPToEFAlias.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,28 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
public partial class AddSearchableIPToEFAlias : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SearchableIPAddress",
|
||||
table: "EFAlias",
|
||||
type: "longtext",
|
||||
nullable: true,
|
||||
computedColumnSql: "CONCAT((IPAddress & 255), \".\", ((IPAddress >> 8) & 255), \".\", ((IPAddress >> 16) & 255), \".\", ((IPAddress >> 24) & 255))",
|
||||
stored: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SearchableIPAddress",
|
||||
table: "EFAlias");
|
||||
}
|
||||
}
|
||||
}
|
1633
Data/Migrations/MySql/20220404192417_AddIndexToSearchableIPToEFAlias.Designer.cs
generated
Normal file
1633
Data/Migrations/MySql/20220404192417_AddIndexToSearchableIPToEFAlias.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
public partial class AddIndexToSearchableIPToEFAlias : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFAlias_SearchableIPAddress",
|
||||
table: "EFAlias",
|
||||
column: "SearchableIPAddress");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_EFAlias_SearchableIPAddress",
|
||||
table: "EFAlias");
|
||||
}
|
||||
}
|
||||
}
|
1636
Data/Migrations/MySql/20220422202702_AddGameToEFClient.Designer.cs
generated
Normal file
1636
Data/Migrations/MySql/20220422202702_AddGameToEFClient.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
Data/Migrations/MySql/20220422202702_AddGameToEFClient.cs
Normal file
25
Data/Migrations/MySql/20220422202702_AddGameToEFClient.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
public partial class AddGameToEFClient : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "GameName",
|
||||
table: "EFClients",
|
||||
type: "int",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "GameName",
|
||||
table: "EFClients");
|
||||
}
|
||||
}
|
||||
}
|
1638
Data/Migrations/MySql/20220609135128_AddIndexToEFRankingHistoryCreatedDatetime.Designer.cs
generated
Normal file
1638
Data/Migrations/MySql/20220609135128_AddIndexToEFRankingHistoryCreatedDatetime.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
public partial class AddIndexToEFRankingHistoryCreatedDatetime : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFClientRankingHistory_CreatedDateTime",
|
||||
table: "EFClientRankingHistory",
|
||||
column: "CreatedDateTime");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_EFClientRankingHistory_CreatedDateTime",
|
||||
table: "EFClientRankingHistory");
|
||||
}
|
||||
}
|
||||
}
|
1639
Data/Migrations/MySql/20220613192602_AddAlternateKeyToEFClients.Designer.cs
generated
Normal file
1639
Data/Migrations/MySql/20220613192602_AddAlternateKeyToEFClients.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,63 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
public partial class AddAlternateKeyToEFClients : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_EFClients_NetworkId",
|
||||
table: "EFClients");
|
||||
|
||||
migrationBuilder.Sql("UPDATE `EFClients` set `GameName` = 0 WHERE `GameName` IS NULL");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "GameName",
|
||||
table: "EFClients",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddUniqueConstraint(
|
||||
name: "AK_EFClients_NetworkId_GameName",
|
||||
table: "EFClients",
|
||||
columns: new[] { "NetworkId", "GameName" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFClients_NetworkId",
|
||||
table: "EFClients",
|
||||
column: "NetworkId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropUniqueConstraint(
|
||||
name: "AK_EFClients_NetworkId_GameName",
|
||||
table: "EFClients");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_EFClients_NetworkId",
|
||||
table: "EFClients");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "GameName",
|
||||
table: "EFClients",
|
||||
type: "int",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFClients_NetworkId",
|
||||
table: "EFClients",
|
||||
column: "NetworkId",
|
||||
unique: true);
|
||||
}
|
||||
}
|
||||
}
|
1639
Data/Migrations/MySql/20220616224602_AddDescendingTimeSentIndexEFClientMessages.Designer.cs
generated
Normal file
1639
Data/Migrations/MySql/20220616224602_AddDescendingTimeSentIndexEFClientMessages.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,19 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
public partial class AddDescendingTimeSentIndexEFClientMessages : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.Sql(@"create index IX_EFClientMessages_TimeSentDesc on efclientmessages (TimeSent desc);");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.Sql(@"drop index IX_EFClientMessages_TimeSentDesc on efclientmessages;");
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,8 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
[DbContext(typeof(MySqlDatabaseContext))]
|
||||
@ -14,7 +16,7 @@ namespace Data.Migrations.MySql
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.10")
|
||||
.HasAnnotation("ProductVersion", "6.0.1")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b =>
|
||||
@ -38,7 +40,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("Vector3Id");
|
||||
|
||||
b.ToTable("EFACSnapshotVector3");
|
||||
b.ToTable("EFACSnapshotVector3", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClient", b =>
|
||||
@ -62,6 +64,9 @@ namespace Data.Migrations.MySql
|
||||
b.Property<DateTime>("FirstConnection")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("GameName")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("LastConnection")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
@ -75,24 +80,25 @@ namespace Data.Migrations.MySql
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("PasswordSalt")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("TotalConnectionTime")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("ClientId");
|
||||
|
||||
b.HasAlternateKey("NetworkId", "GameName");
|
||||
|
||||
b.HasIndex("AliasLinkId");
|
||||
|
||||
b.HasIndex("CurrentAliasId");
|
||||
|
||||
b.HasIndex("NetworkId")
|
||||
.IsUnique();
|
||||
b.HasIndex("NetworkId");
|
||||
|
||||
b.ToTable("EFClients");
|
||||
b.ToTable("EFClients", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientConnectionHistory", b =>
|
||||
@ -124,7 +130,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFClientConnectionHistory");
|
||||
b.ToTable("EFClientConnectionHistory", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientKill", b =>
|
||||
@ -179,7 +185,7 @@ namespace Data.Migrations.MySql
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("WeaponReference")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("datetime(6)");
|
||||
@ -198,7 +204,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("ViewAnglesVector3Id");
|
||||
|
||||
b.ToTable("EFClientKills");
|
||||
b.ToTable("EFClientKills", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientMessage", b =>
|
||||
@ -214,7 +220,7 @@ namespace Data.Migrations.MySql
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("SentIngame")
|
||||
.HasColumnType("tinyint(1)");
|
||||
@ -233,7 +239,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("TimeSent");
|
||||
|
||||
b.ToTable("EFClientMessages");
|
||||
b.ToTable("EFClientMessages", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b =>
|
||||
@ -273,7 +279,7 @@ namespace Data.Migrations.MySql
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("HitLocationReference")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("HitOriginId")
|
||||
.HasColumnType("int");
|
||||
@ -321,7 +327,7 @@ namespace Data.Migrations.MySql
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("WeaponReference")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("datetime(6)");
|
||||
@ -340,7 +346,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFACSnapshot");
|
||||
b.ToTable("EFACSnapshot", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b =>
|
||||
@ -414,7 +420,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("WeaponId");
|
||||
|
||||
b.ToTable("EFClientHitStatistics");
|
||||
b.ToTable("EFClientHitStatistics", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientRankingHistory", b =>
|
||||
@ -451,6 +457,8 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("CreatedDateTime");
|
||||
|
||||
b.HasIndex("Ranking");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
@ -459,7 +467,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("ZScore");
|
||||
|
||||
b.ToTable("EFClientRankingHistory");
|
||||
b.ToTable("EFClientRankingHistory", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b =>
|
||||
@ -478,7 +486,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("EFClientRatingHistory");
|
||||
b.ToTable("EFClientRatingHistory", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b =>
|
||||
@ -536,7 +544,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("ClientId", "TimePlayed", "ZScore");
|
||||
|
||||
b.ToTable("EFClientStatistics");
|
||||
b.ToTable("EFClientStatistics", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b =>
|
||||
@ -549,12 +557,12 @@ namespace Data.Migrations.MySql
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("EFClientStatisticsClientId")
|
||||
.HasColumnName("EFClientStatisticsClientId")
|
||||
.HasColumnType("int");
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("EFClientStatisticsClientId");
|
||||
|
||||
b.Property<long>("EFClientStatisticsServerId")
|
||||
.HasColumnName("EFClientStatisticsServerId")
|
||||
.HasColumnType("bigint");
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("EFClientStatisticsServerId");
|
||||
|
||||
b.Property<int>("HitCount")
|
||||
.HasColumnType("int");
|
||||
@ -574,7 +582,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("EFClientStatisticsClientId", "EFClientStatisticsServerId");
|
||||
|
||||
b.ToTable("EFHitLocationCounts");
|
||||
b.ToTable("EFHitLocationCounts", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b =>
|
||||
@ -617,7 +625,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("When", "ServerId", "Performance", "ActivityAmount");
|
||||
|
||||
b.ToTable("EFRating");
|
||||
b.ToTable("EFRating", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFHitLocation", b =>
|
||||
@ -634,7 +642,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedDateTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
@ -643,7 +651,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("EFHitLocations");
|
||||
b.ToTable("EFHitLocations", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMap", b =>
|
||||
@ -660,14 +668,14 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime?>("UpdatedDateTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("MapId");
|
||||
|
||||
b.ToTable("EFMaps");
|
||||
b.ToTable("EFMaps", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMeansOfDeath", b =>
|
||||
@ -684,14 +692,14 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime?>("UpdatedDateTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("MeansOfDeathId");
|
||||
|
||||
b.ToTable("EFMeansOfDeath");
|
||||
b.ToTable("EFMeansOfDeath", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeapon", b =>
|
||||
@ -708,7 +716,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedDateTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
@ -717,7 +725,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("EFWeapons");
|
||||
b.ToTable("EFWeapons", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachment", b =>
|
||||
@ -734,14 +742,14 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime?>("UpdatedDateTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("WeaponAttachmentId");
|
||||
|
||||
b.ToTable("EFWeaponAttachments");
|
||||
b.ToTable("EFWeaponAttachments", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b =>
|
||||
@ -776,7 +784,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("Attachment3Id");
|
||||
|
||||
b.ToTable("EFWeaponAttachmentCombos");
|
||||
b.ToTable("EFWeaponAttachmentCombos", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFAlias", b =>
|
||||
@ -799,12 +807,17 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(24) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(24);
|
||||
.HasMaxLength(24)
|
||||
.HasColumnType("varchar(24)");
|
||||
|
||||
b.Property<string>("SearchableIPAddress")
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("varchar(255)")
|
||||
.HasComputedColumnSql("((IPAddress & 255) || '.' || ((IPAddress >> 8) & 255)) || '.' || ((IPAddress >> 16) & 255) || '.' || ((IPAddress >> 24) & 255)", true);
|
||||
|
||||
b.Property<string>("SearchableName")
|
||||
.HasColumnType("varchar(24) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(24);
|
||||
.HasMaxLength(24)
|
||||
.HasColumnType("varchar(24)");
|
||||
|
||||
b.HasKey("AliasId");
|
||||
|
||||
@ -814,11 +827,13 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.HasIndex("SearchableIPAddress");
|
||||
|
||||
b.HasIndex("SearchableName");
|
||||
|
||||
b.HasIndex("Name", "IPAddress");
|
||||
|
||||
b.ToTable("EFAlias");
|
||||
b.ToTable("EFAlias", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFAliasLink", b =>
|
||||
@ -832,7 +847,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasKey("AliasLinkId");
|
||||
|
||||
b.ToTable("EFAliasLinks");
|
||||
b.ToTable("EFAliasLinks", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFChangeHistory", b =>
|
||||
@ -845,11 +860,11 @@ namespace Data.Migrations.MySql
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasColumnType("varchar(128) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(128);
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
|
||||
b.Property<string>("CurrentValue")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int?>("ImpersonationEntityId")
|
||||
.HasColumnType("int");
|
||||
@ -858,7 +873,7 @@ namespace Data.Migrations.MySql
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("PreviousValue")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("TargetEntityId")
|
||||
.HasColumnType("int");
|
||||
@ -890,12 +905,12 @@ namespace Data.Migrations.MySql
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Extra")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(32) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(32);
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("varchar(32)");
|
||||
|
||||
b.Property<int?>("LinkedMetaId")
|
||||
.HasColumnType("int");
|
||||
@ -905,7 +920,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("MetaId");
|
||||
|
||||
@ -928,7 +943,7 @@ namespace Data.Migrations.MySql
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("AutomatedOffense")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime?>("Expires")
|
||||
.HasColumnType("datetime(6)");
|
||||
@ -936,7 +951,7 @@ namespace Data.Migrations.MySql
|
||||
b.Property<bool>("IsEvadedOffense")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("LinkId")
|
||||
b.Property<int?>("LinkId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("OffenderId")
|
||||
@ -944,7 +959,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.Property<string>("Offense")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("PunisherId")
|
||||
.HasColumnType("int");
|
||||
@ -963,7 +978,39 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("PunisherId");
|
||||
|
||||
b.ToTable("EFPenalties");
|
||||
b.ToTable("EFPenalties", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFPenaltyIdentifier", b =>
|
||||
{
|
||||
b.Property<int>("PenaltyIdentifierId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedDateTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int?>("IPv4Address")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long>("NetworkId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("PenaltyId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedDateTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("PenaltyIdentifierId");
|
||||
|
||||
b.HasIndex("IPv4Address");
|
||||
|
||||
b.HasIndex("NetworkId");
|
||||
|
||||
b.HasIndex("PenaltyId");
|
||||
|
||||
b.ToTable("EFPenaltyIdentifiers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Misc.EFInboxMessage", b =>
|
||||
@ -982,7 +1029,7 @@ namespace Data.Migrations.MySql
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<long?>("ServerId")
|
||||
.HasColumnType("bigint");
|
||||
@ -1013,13 +1060,13 @@ namespace Data.Migrations.MySql
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("EndPoint")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int?>("GameName")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("HostName")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("IsPasswordProtected")
|
||||
.HasColumnType("tinyint(1)");
|
||||
@ -1029,7 +1076,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasKey("ServerId");
|
||||
|
||||
b.ToTable("EFServers");
|
||||
b.ToTable("EFServers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerSnapshot", b =>
|
||||
@ -1047,6 +1094,9 @@ namespace Data.Migrations.MySql
|
||||
b.Property<int>("ClientCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool?>("ConnectionInterrupted")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("MapId")
|
||||
.HasColumnType("int");
|
||||
|
||||
@ -1062,7 +1112,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFServerSnapshot");
|
||||
b.ToTable("EFServerSnapshot", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b =>
|
||||
@ -1087,7 +1137,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFServerStatistics");
|
||||
b.ToTable("EFServerStatistics", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Vector3", b =>
|
||||
@ -1107,7 +1157,7 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasKey("Vector3Id");
|
||||
|
||||
b.ToTable("Vector3");
|
||||
b.ToTable("Vector3", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b =>
|
||||
@ -1123,6 +1173,10 @@ namespace Data.Migrations.MySql
|
||||
.HasForeignKey("Vector3Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Snapshot");
|
||||
|
||||
b.Navigation("Vector");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClient", b =>
|
||||
@ -1138,6 +1192,10 @@ namespace Data.Migrations.MySql
|
||||
.HasForeignKey("CurrentAliasId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("AliasLink");
|
||||
|
||||
b.Navigation("CurrentAlias");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientConnectionHistory", b =>
|
||||
@ -1153,6 +1211,10 @@ namespace Data.Migrations.MySql
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientKill", b =>
|
||||
@ -1186,6 +1248,18 @@ namespace Data.Migrations.MySql
|
||||
b.HasOne("Data.Models.Vector3", "ViewAngles")
|
||||
.WithMany()
|
||||
.HasForeignKey("ViewAnglesVector3Id");
|
||||
|
||||
b.Navigation("Attacker");
|
||||
|
||||
b.Navigation("DeathOrigin");
|
||||
|
||||
b.Navigation("KillOrigin");
|
||||
|
||||
b.Navigation("Server");
|
||||
|
||||
b.Navigation("Victim");
|
||||
|
||||
b.Navigation("ViewAngles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientMessage", b =>
|
||||
@ -1201,6 +1275,10 @@ namespace Data.Migrations.MySql
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b =>
|
||||
@ -1238,6 +1316,18 @@ namespace Data.Migrations.MySql
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("CurrentViewAngle");
|
||||
|
||||
b.Navigation("HitDestination");
|
||||
|
||||
b.Navigation("HitOrigin");
|
||||
|
||||
b.Navigation("LastStrainAngle");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b =>
|
||||
@ -1267,6 +1357,18 @@ namespace Data.Migrations.MySql
|
||||
b.HasOne("Data.Models.Client.Stats.Reference.EFWeapon", "Weapon")
|
||||
.WithMany()
|
||||
.HasForeignKey("WeaponId");
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("HitLocation");
|
||||
|
||||
b.Navigation("MeansOfDeath");
|
||||
|
||||
b.Navigation("Server");
|
||||
|
||||
b.Navigation("Weapon");
|
||||
|
||||
b.Navigation("WeaponAttachmentCombo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientRankingHistory", b =>
|
||||
@ -1280,6 +1382,10 @@ namespace Data.Migrations.MySql
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b =>
|
||||
@ -1289,6 +1395,8 @@ namespace Data.Migrations.MySql
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b =>
|
||||
@ -1304,6 +1412,10 @@ namespace Data.Migrations.MySql
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b =>
|
||||
@ -1325,6 +1437,10 @@ namespace Data.Migrations.MySql
|
||||
.HasForeignKey("EFClientStatisticsClientId", "EFClientStatisticsServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b =>
|
||||
@ -1338,6 +1454,10 @@ namespace Data.Migrations.MySql
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
|
||||
b.Navigation("RatingHistory");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b =>
|
||||
@ -1355,6 +1475,12 @@ namespace Data.Migrations.MySql
|
||||
b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment3")
|
||||
.WithMany()
|
||||
.HasForeignKey("Attachment3Id");
|
||||
|
||||
b.Navigation("Attachment1");
|
||||
|
||||
b.Navigation("Attachment2");
|
||||
|
||||
b.Navigation("Attachment3");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFAlias", b =>
|
||||
@ -1364,6 +1490,8 @@ namespace Data.Migrations.MySql
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Link");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFMeta", b =>
|
||||
@ -1376,15 +1504,17 @@ namespace Data.Migrations.MySql
|
||||
.WithMany()
|
||||
.HasForeignKey("LinkedMetaId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("LinkedMeta");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFPenalty", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.EFAliasLink", "Link")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
.HasForeignKey("LinkId");
|
||||
|
||||
b.HasOne("Data.Models.Client.EFClient", "Offender")
|
||||
.WithMany("ReceivedPenalties")
|
||||
@ -1397,6 +1527,23 @@ namespace Data.Migrations.MySql
|
||||
.HasForeignKey("PunisherId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Link");
|
||||
|
||||
b.Navigation("Offender");
|
||||
|
||||
b.Navigation("Punisher");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFPenaltyIdentifier", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.EFPenalty", "Penalty")
|
||||
.WithMany()
|
||||
.HasForeignKey("PenaltyId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Penalty");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Misc.EFInboxMessage", b =>
|
||||
@ -1416,6 +1563,12 @@ namespace Data.Migrations.MySql
|
||||
.HasForeignKey("SourceClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("DestinationClient");
|
||||
|
||||
b.Navigation("Server");
|
||||
|
||||
b.Navigation("SourceClient");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerSnapshot", b =>
|
||||
@ -1431,6 +1584,10 @@ namespace Data.Migrations.MySql
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Map");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b =>
|
||||
@ -1440,6 +1597,39 @@ namespace Data.Migrations.MySql
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClient", b =>
|
||||
{
|
||||
b.Navigation("AdministeredPenalties");
|
||||
|
||||
b.Navigation("Meta");
|
||||
|
||||
b.Navigation("ReceivedPenalties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b =>
|
||||
{
|
||||
b.Navigation("PredictedViewAngles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.Navigation("Ratings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b =>
|
||||
{
|
||||
b.Navigation("HitLocations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFAliasLink", b =>
|
||||
{
|
||||
b.Navigation("Children");
|
||||
|
||||
b.Navigation("ReceivedPenalties");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
|
1679
Data/Migrations/Postgresql/20220222222045_AddEFPenaltyIdentifier.Designer.cs
generated
Normal file
1679
Data/Migrations/Postgresql/20220222222045_AddEFPenaltyIdentifier.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,56 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
public partial class AddEFPenaltyIdentifier : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFPenaltyIdentifiers",
|
||||
columns: table => new
|
||||
{
|
||||
PenaltyIdentifierId = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
IPv4Address = table.Column<int>(type: "integer", nullable: true),
|
||||
NetworkId = table.Column<long>(type: "bigint", nullable: false),
|
||||
PenaltyId = table.Column<int>(type: "integer", nullable: false),
|
||||
Active = table.Column<bool>(type: "boolean", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFPenaltyIdentifiers", x => x.PenaltyIdentifierId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFPenaltyIdentifiers_EFPenalties_PenaltyId",
|
||||
column: x => x.PenaltyId,
|
||||
principalTable: "EFPenalties",
|
||||
principalColumn: "PenaltyId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFPenaltyIdentifiers_IPv4Address",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
column: "IPv4Address");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFPenaltyIdentifiers_NetworkId",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
column: "NetworkId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFPenaltyIdentifiers_PenaltyId",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
column: "PenaltyId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFPenaltyIdentifiers");
|
||||
}
|
||||
}
|
||||
}
|
1677
Data/Migrations/Postgresql/20220222230153_MakeEFPenaltyLinkIdNullable.Designer.cs
generated
Normal file
1677
Data/Migrations/Postgresql/20220222230153_MakeEFPenaltyLinkIdNullable.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,56 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
public partial class MakeEFPenaltyLinkIdNullable : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_EFPenalties_EFAliasLinks_LinkId",
|
||||
table: "EFPenalties");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "LinkId",
|
||||
table: "EFPenalties",
|
||||
type: "integer",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "integer");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_EFPenalties_EFAliasLinks_LinkId",
|
||||
table: "EFPenalties",
|
||||
column: "LinkId",
|
||||
principalTable: "EFAliasLinks",
|
||||
principalColumn: "AliasLinkId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_EFPenalties_EFAliasLinks_LinkId",
|
||||
table: "EFPenalties");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "LinkId",
|
||||
table: "EFPenalties",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "integer",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_EFPenalties_EFAliasLinks_LinkId",
|
||||
table: "EFPenalties",
|
||||
column: "LinkId",
|
||||
principalTable: "EFAliasLinks",
|
||||
principalColumn: "AliasLinkId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
1680
Data/Migrations/Postgresql/20220223150106_AddAuditFieldsToEFPenaltyIdentifier.Designer.cs
generated
Normal file
1680
Data/Migrations/Postgresql/20220223150106_AddAuditFieldsToEFPenaltyIdentifier.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
public partial class AddAuditFieldsToEFPenaltyIdentifier : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Active",
|
||||
table: "EFPenaltyIdentifiers");
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "CreatedDateTime",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
type: "timestamp without time zone",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "UpdatedDateTime",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
type: "timestamp without time zone",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CreatedDateTime",
|
||||
table: "EFPenaltyIdentifiers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UpdatedDateTime",
|
||||
table: "EFPenaltyIdentifiers");
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "Active",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
}
|
||||
}
|
1683
Data/Migrations/Postgresql/20220329213521_AddConnectionInterruptedToEFServerSnapshot.Designer.cs
generated
Normal file
1683
Data/Migrations/Postgresql/20220329213521_AddConnectionInterruptedToEFServerSnapshot.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,25 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
public partial class AddConnectionInterruptedToEFServerSnapshot : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "ConnectionInterrupted",
|
||||
table: "EFServerSnapshot",
|
||||
type: "boolean",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ConnectionInterrupted",
|
||||
table: "EFServerSnapshot");
|
||||
}
|
||||
}
|
||||
}
|
1688
Data/Migrations/Postgresql/20220404185627_AddSearchableIPToEFAlias.Designer.cs
generated
Normal file
1688
Data/Migrations/Postgresql/20220404185627_AddSearchableIPToEFAlias.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,27 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
public partial class AddSearchableIPToEFAlias : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SearchableIPAddress",
|
||||
table: "EFAlias",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
computedColumnSql: "CASE WHEN \"IPAddress\" IS NULL THEN 'NULL'::text ELSE (\"IPAddress\" & 255)::text || '.'::text || ((\"IPAddress\" >> 8) & 255)::text || '.'::text || ((\"IPAddress\" >> 16) & 255)::text || '.'::text || ((\"IPAddress\" >> 24) & 255)::text END",
|
||||
stored: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SearchableIPAddress",
|
||||
table: "EFAlias");
|
||||
}
|
||||
}
|
||||
}
|
1690
Data/Migrations/Postgresql/20220404192553_AddIndexToSearchableIPToEFAlias.Designer.cs
generated
Normal file
1690
Data/Migrations/Postgresql/20220404192553_AddIndexToSearchableIPToEFAlias.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
public partial class AddIndexToSearchableIPToEFAlias : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFAlias_SearchableIPAddress",
|
||||
table: "EFAlias",
|
||||
column: "SearchableIPAddress");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_EFAlias_SearchableIPAddress",
|
||||
table: "EFAlias");
|
||||
}
|
||||
}
|
||||
}
|
1693
Data/Migrations/Postgresql/20220422203121_AddGameToEFClient.Designer.cs
generated
Normal file
1693
Data/Migrations/Postgresql/20220422203121_AddGameToEFClient.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,25 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
public partial class AddGameToEFClient : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "GameName",
|
||||
table: "EFClients",
|
||||
type: "integer",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "GameName",
|
||||
table: "EFClients");
|
||||
}
|
||||
}
|
||||
}
|
1695
Data/Migrations/Postgresql/20220609135210_AddIndexToEFRankingHistoryCreatedDatetime.Designer.cs
generated
Normal file
1695
Data/Migrations/Postgresql/20220609135210_AddIndexToEFRankingHistoryCreatedDatetime.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
public partial class AddIndexToEFRankingHistoryCreatedDatetime : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFClientRankingHistory_CreatedDateTime",
|
||||
table: "EFClientRankingHistory",
|
||||
column: "CreatedDateTime");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_EFClientRankingHistory_CreatedDateTime",
|
||||
table: "EFClientRankingHistory");
|
||||
}
|
||||
}
|
||||
}
|
1696
Data/Migrations/Postgresql/20220613181913_AddAlternateKeyToEFClients.Designer.cs
generated
Normal file
1696
Data/Migrations/Postgresql/20220613181913_AddAlternateKeyToEFClients.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,63 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
public partial class AddAlternateKeyToEFClients : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_EFClients_NetworkId",
|
||||
table: "EFClients");
|
||||
|
||||
migrationBuilder.Sql("UPDATE \"EFClients\" SET \"GameName\" = 0 WHERE \"GameName\" IS NULL");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "GameName",
|
||||
table: "EFClients",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "integer",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddUniqueConstraint(
|
||||
name: "AK_EFClients_NetworkId_GameName",
|
||||
table: "EFClients",
|
||||
columns: new[] { "NetworkId", "GameName" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFClients_NetworkId",
|
||||
table: "EFClients",
|
||||
column: "NetworkId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropUniqueConstraint(
|
||||
name: "AK_EFClients_NetworkId_GameName",
|
||||
table: "EFClients");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_EFClients_NetworkId",
|
||||
table: "EFClients");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "GameName",
|
||||
table: "EFClients",
|
||||
type: "integer",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "integer");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFClients_NetworkId",
|
||||
table: "EFClients",
|
||||
column: "NetworkId",
|
||||
unique: true);
|
||||
}
|
||||
}
|
||||
}
|
1696
Data/Migrations/Postgresql/20220616224145_AddDescendingTimeSentIndexEFClientMessages.Designer.cs
generated
Normal file
1696
Data/Migrations/Postgresql/20220616224145_AddDescendingTimeSentIndexEFClientMessages.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
public partial class AddDescendingTimeSentIndexEFClientMessages : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.Sql(
|
||||
@"CREATE INDEX""IX_EFClientMessages_TimeSentDesc""
|
||||
ON public.""EFClientMessages"" USING btree
|
||||
(""TimeSent"" DESC NULLS LAST)
|
||||
TABLESPACE pg_default;"
|
||||
);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.Sql(@"DROP INDEX public.""IX_EFClientMessages_TimeSentDesc""");
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,8 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
[DbContext(typeof(PostgresqlDatabaseContext))]
|
||||
@ -15,16 +17,18 @@ namespace Data.Migrations.Postgresql
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
|
||||
.HasAnnotation("ProductVersion", "3.1.10")
|
||||
.HasAnnotation("ProductVersion", "6.0.1")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b =>
|
||||
{
|
||||
b.Property<int>("ACSnapshotVector3Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("ACSnapshotVector3Id"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
@ -41,15 +45,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("Vector3Id");
|
||||
|
||||
b.ToTable("EFACSnapshotVector3");
|
||||
b.ToTable("EFACSnapshotVector3", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClient", b =>
|
||||
{
|
||||
b.Property<int>("ClientId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("ClientId"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
@ -66,6 +71,9 @@ namespace Data.Migrations.Postgresql
|
||||
b.Property<DateTime>("FirstConnection")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<int>("GameName")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("LastConnection")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
@ -89,22 +97,24 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasKey("ClientId");
|
||||
|
||||
b.HasAlternateKey("NetworkId", "GameName");
|
||||
|
||||
b.HasIndex("AliasLinkId");
|
||||
|
||||
b.HasIndex("CurrentAliasId");
|
||||
|
||||
b.HasIndex("NetworkId")
|
||||
.IsUnique();
|
||||
b.HasIndex("NetworkId");
|
||||
|
||||
b.ToTable("EFClients");
|
||||
b.ToTable("EFClients", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientConnectionHistory", b =>
|
||||
{
|
||||
b.Property<long>("ClientConnectionId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("ClientConnectionId"));
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("integer");
|
||||
@ -129,15 +139,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFClientConnectionHistory");
|
||||
b.ToTable("EFClientConnectionHistory", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientKill", b =>
|
||||
{
|
||||
b.Property<long>("KillId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("KillId"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
@ -204,15 +215,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("ViewAnglesVector3Id");
|
||||
|
||||
b.ToTable("EFClientKills");
|
||||
b.ToTable("EFClientKills", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientMessage", b =>
|
||||
{
|
||||
b.Property<long>("MessageId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("MessageId"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
@ -240,15 +252,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("TimeSent");
|
||||
|
||||
b.ToTable("EFClientMessages");
|
||||
b.ToTable("EFClientMessages", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b =>
|
||||
{
|
||||
b.Property<int>("SnapshotId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("SnapshotId"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
@ -348,15 +361,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFACSnapshot");
|
||||
b.ToTable("EFACSnapshot", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b =>
|
||||
{
|
||||
b.Property<int>("ClientHitStatisticId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("ClientHitStatisticId"));
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("integer");
|
||||
@ -423,15 +437,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("WeaponId");
|
||||
|
||||
b.ToTable("EFClientHitStatistics");
|
||||
b.ToTable("EFClientHitStatistics", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientRankingHistory", b =>
|
||||
{
|
||||
b.Property<long>("ClientRankingHistoryId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("ClientRankingHistoryId"));
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("integer");
|
||||
@ -461,6 +476,8 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("CreatedDateTime");
|
||||
|
||||
b.HasIndex("Ranking");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
@ -469,15 +486,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("ZScore");
|
||||
|
||||
b.ToTable("EFClientRankingHistory");
|
||||
b.ToTable("EFClientRankingHistory", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.Property<int>("RatingHistoryId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("RatingHistoryId"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
@ -489,7 +507,7 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("EFClientRatingHistory");
|
||||
b.ToTable("EFClientRatingHistory", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b =>
|
||||
@ -547,26 +565,27 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("ClientId", "TimePlayed", "ZScore");
|
||||
|
||||
b.ToTable("EFClientStatistics");
|
||||
b.ToTable("EFClientStatistics", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b =>
|
||||
{
|
||||
b.Property<int>("HitLocationCountId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("HitLocationCountId"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("EFClientStatisticsClientId")
|
||||
.HasColumnName("EFClientStatisticsClientId")
|
||||
.HasColumnType("integer");
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("EFClientStatisticsClientId");
|
||||
|
||||
b.Property<long>("EFClientStatisticsServerId")
|
||||
.HasColumnName("EFClientStatisticsServerId")
|
||||
.HasColumnType("bigint");
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("EFClientStatisticsServerId");
|
||||
|
||||
b.Property<int>("HitCount")
|
||||
.HasColumnType("integer");
|
||||
@ -586,15 +605,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("EFClientStatisticsClientId", "EFClientStatisticsServerId");
|
||||
|
||||
b.ToTable("EFHitLocationCounts");
|
||||
b.ToTable("EFHitLocationCounts", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b =>
|
||||
{
|
||||
b.Property<int>("RatingId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("RatingId"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
@ -630,15 +650,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("When", "ServerId", "Performance", "ActivityAmount");
|
||||
|
||||
b.ToTable("EFRating");
|
||||
b.ToTable("EFRating", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFHitLocation", b =>
|
||||
{
|
||||
b.Property<int>("HitLocationId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("HitLocationId"));
|
||||
|
||||
b.Property<DateTime>("CreatedDateTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
@ -657,15 +678,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("EFHitLocations");
|
||||
b.ToTable("EFHitLocations", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMap", b =>
|
||||
{
|
||||
b.Property<int>("MapId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("MapId"));
|
||||
|
||||
b.Property<DateTime>("CreatedDateTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
@ -682,15 +704,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasKey("MapId");
|
||||
|
||||
b.ToTable("EFMaps");
|
||||
b.ToTable("EFMaps", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFMeansOfDeath", b =>
|
||||
{
|
||||
b.Property<int>("MeansOfDeathId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("MeansOfDeathId"));
|
||||
|
||||
b.Property<DateTime>("CreatedDateTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
@ -707,15 +730,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasKey("MeansOfDeathId");
|
||||
|
||||
b.ToTable("EFMeansOfDeath");
|
||||
b.ToTable("EFMeansOfDeath", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeapon", b =>
|
||||
{
|
||||
b.Property<int>("WeaponId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("WeaponId"));
|
||||
|
||||
b.Property<DateTime>("CreatedDateTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
@ -734,15 +758,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("EFWeapons");
|
||||
b.ToTable("EFWeapons", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachment", b =>
|
||||
{
|
||||
b.Property<int>("WeaponAttachmentId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("WeaponAttachmentId"));
|
||||
|
||||
b.Property<DateTime>("CreatedDateTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
@ -759,15 +784,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasKey("WeaponAttachmentId");
|
||||
|
||||
b.ToTable("EFWeaponAttachments");
|
||||
b.ToTable("EFWeaponAttachments", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b =>
|
||||
{
|
||||
b.Property<int>("WeaponAttachmentComboId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("WeaponAttachmentComboId"));
|
||||
|
||||
b.Property<int>("Attachment1Id")
|
||||
.HasColumnType("integer");
|
||||
@ -795,15 +821,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("Attachment3Id");
|
||||
|
||||
b.ToTable("EFWeaponAttachmentCombos");
|
||||
b.ToTable("EFWeaponAttachmentCombos", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFAlias", b =>
|
||||
{
|
||||
b.Property<int>("AliasId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("AliasId"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
@ -819,12 +846,17 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(24)")
|
||||
.HasMaxLength(24);
|
||||
.HasMaxLength(24)
|
||||
.HasColumnType("character varying(24)");
|
||||
|
||||
b.Property<string>("SearchableIPAddress")
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("text")
|
||||
.HasComputedColumnSql("((IPAddress & 255) || '.' || ((IPAddress >> 8) & 255)) || '.' || ((IPAddress >> 16) & 255) || '.' || ((IPAddress >> 24) & 255)", true);
|
||||
|
||||
b.Property<string>("SearchableName")
|
||||
.HasColumnType("character varying(24)")
|
||||
.HasMaxLength(24);
|
||||
.HasMaxLength(24)
|
||||
.HasColumnType("character varying(24)");
|
||||
|
||||
b.HasKey("AliasId");
|
||||
|
||||
@ -834,41 +866,45 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.HasIndex("SearchableIPAddress");
|
||||
|
||||
b.HasIndex("SearchableName");
|
||||
|
||||
b.HasIndex("Name", "IPAddress");
|
||||
|
||||
b.ToTable("EFAlias");
|
||||
b.ToTable("EFAlias", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFAliasLink", b =>
|
||||
{
|
||||
b.Property<int>("AliasLinkId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("AliasLinkId"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.HasKey("AliasLinkId");
|
||||
|
||||
b.ToTable("EFAliasLinks");
|
||||
b.ToTable("EFAliasLinks", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFChangeHistory", b =>
|
||||
{
|
||||
b.Property<int>("ChangeHistoryId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("ChangeHistoryId"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasMaxLength(128);
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)");
|
||||
|
||||
b.Property<string>("CurrentValue")
|
||||
.HasColumnType("text");
|
||||
@ -900,8 +936,9 @@ namespace Data.Migrations.Postgresql
|
||||
{
|
||||
b.Property<int>("MetaId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("MetaId"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
@ -917,8 +954,8 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasMaxLength(32);
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<int?>("LinkedMetaId")
|
||||
.HasColumnType("integer");
|
||||
@ -945,8 +982,9 @@ namespace Data.Migrations.Postgresql
|
||||
{
|
||||
b.Property<int>("PenaltyId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("PenaltyId"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
@ -960,7 +998,7 @@ namespace Data.Migrations.Postgresql
|
||||
b.Property<bool>("IsEvadedOffense")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("LinkId")
|
||||
b.Property<int?>("LinkId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("OffenderId")
|
||||
@ -987,15 +1025,50 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("PunisherId");
|
||||
|
||||
b.ToTable("EFPenalties");
|
||||
b.ToTable("EFPenalties", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFPenaltyIdentifier", b =>
|
||||
{
|
||||
b.Property<int>("PenaltyIdentifierId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("PenaltyIdentifierId"));
|
||||
|
||||
b.Property<DateTime>("CreatedDateTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<int?>("IPv4Address")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long>("NetworkId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("PenaltyId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime?>("UpdatedDateTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.HasKey("PenaltyIdentifierId");
|
||||
|
||||
b.HasIndex("IPv4Address");
|
||||
|
||||
b.HasIndex("NetworkId");
|
||||
|
||||
b.HasIndex("PenaltyId");
|
||||
|
||||
b.ToTable("EFPenaltyIdentifiers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Misc.EFInboxMessage", b =>
|
||||
{
|
||||
b.Property<int>("InboxMessageId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("InboxMessageId"));
|
||||
|
||||
b.Property<DateTime>("CreatedDateTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
@ -1054,15 +1127,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasKey("ServerId");
|
||||
|
||||
b.ToTable("EFServers");
|
||||
b.ToTable("EFServers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerSnapshot", b =>
|
||||
{
|
||||
b.Property<long>("ServerSnapshotId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("ServerSnapshotId"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
@ -1073,6 +1147,9 @@ namespace Data.Migrations.Postgresql
|
||||
b.Property<int>("ClientCount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<bool?>("ConnectionInterrupted")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("MapId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
@ -1088,15 +1165,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFServerSnapshot");
|
||||
b.ToTable("EFServerSnapshot", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b =>
|
||||
{
|
||||
b.Property<int>("StatisticId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("StatisticId"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
@ -1114,15 +1192,16 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFServerStatistics");
|
||||
b.ToTable("EFServerStatistics", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Vector3", b =>
|
||||
{
|
||||
b.Property<int>("Vector3Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Vector3Id"));
|
||||
|
||||
b.Property<float>("X")
|
||||
.HasColumnType("real");
|
||||
@ -1135,7 +1214,7 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasKey("Vector3Id");
|
||||
|
||||
b.ToTable("Vector3");
|
||||
b.ToTable("Vector3", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b =>
|
||||
@ -1151,6 +1230,10 @@ namespace Data.Migrations.Postgresql
|
||||
.HasForeignKey("Vector3Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Snapshot");
|
||||
|
||||
b.Navigation("Vector");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClient", b =>
|
||||
@ -1166,6 +1249,10 @@ namespace Data.Migrations.Postgresql
|
||||
.HasForeignKey("CurrentAliasId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("AliasLink");
|
||||
|
||||
b.Navigation("CurrentAlias");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientConnectionHistory", b =>
|
||||
@ -1181,6 +1268,10 @@ namespace Data.Migrations.Postgresql
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientKill", b =>
|
||||
@ -1214,6 +1305,18 @@ namespace Data.Migrations.Postgresql
|
||||
b.HasOne("Data.Models.Vector3", "ViewAngles")
|
||||
.WithMany()
|
||||
.HasForeignKey("ViewAnglesVector3Id");
|
||||
|
||||
b.Navigation("Attacker");
|
||||
|
||||
b.Navigation("DeathOrigin");
|
||||
|
||||
b.Navigation("KillOrigin");
|
||||
|
||||
b.Navigation("Server");
|
||||
|
||||
b.Navigation("Victim");
|
||||
|
||||
b.Navigation("ViewAngles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientMessage", b =>
|
||||
@ -1229,6 +1332,10 @@ namespace Data.Migrations.Postgresql
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b =>
|
||||
@ -1266,6 +1373,18 @@ namespace Data.Migrations.Postgresql
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("CurrentViewAngle");
|
||||
|
||||
b.Navigation("HitDestination");
|
||||
|
||||
b.Navigation("HitOrigin");
|
||||
|
||||
b.Navigation("LastStrainAngle");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b =>
|
||||
@ -1295,6 +1414,18 @@ namespace Data.Migrations.Postgresql
|
||||
b.HasOne("Data.Models.Client.Stats.Reference.EFWeapon", "Weapon")
|
||||
.WithMany()
|
||||
.HasForeignKey("WeaponId");
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("HitLocation");
|
||||
|
||||
b.Navigation("MeansOfDeath");
|
||||
|
||||
b.Navigation("Server");
|
||||
|
||||
b.Navigation("Weapon");
|
||||
|
||||
b.Navigation("WeaponAttachmentCombo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientRankingHistory", b =>
|
||||
@ -1308,6 +1439,10 @@ namespace Data.Migrations.Postgresql
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b =>
|
||||
@ -1317,6 +1452,8 @@ namespace Data.Migrations.Postgresql
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b =>
|
||||
@ -1332,6 +1469,10 @@ namespace Data.Migrations.Postgresql
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFHitLocationCount", b =>
|
||||
@ -1353,6 +1494,10 @@ namespace Data.Migrations.Postgresql
|
||||
.HasForeignKey("EFClientStatisticsClientId", "EFClientStatisticsServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFRating", b =>
|
||||
@ -1366,6 +1511,10 @@ namespace Data.Migrations.Postgresql
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
|
||||
b.Navigation("RatingHistory");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.Reference.EFWeaponAttachmentCombo", b =>
|
||||
@ -1383,6 +1532,12 @@ namespace Data.Migrations.Postgresql
|
||||
b.HasOne("Data.Models.Client.Stats.Reference.EFWeaponAttachment", "Attachment3")
|
||||
.WithMany()
|
||||
.HasForeignKey("Attachment3Id");
|
||||
|
||||
b.Navigation("Attachment1");
|
||||
|
||||
b.Navigation("Attachment2");
|
||||
|
||||
b.Navigation("Attachment3");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFAlias", b =>
|
||||
@ -1392,6 +1547,8 @@ namespace Data.Migrations.Postgresql
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Link");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFMeta", b =>
|
||||
@ -1404,15 +1561,17 @@ namespace Data.Migrations.Postgresql
|
||||
.WithMany()
|
||||
.HasForeignKey("LinkedMetaId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("LinkedMeta");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFPenalty", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.EFAliasLink", "Link")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
.HasForeignKey("LinkId");
|
||||
|
||||
b.HasOne("Data.Models.Client.EFClient", "Offender")
|
||||
.WithMany("ReceivedPenalties")
|
||||
@ -1425,6 +1584,23 @@ namespace Data.Migrations.Postgresql
|
||||
.HasForeignKey("PunisherId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Link");
|
||||
|
||||
b.Navigation("Offender");
|
||||
|
||||
b.Navigation("Punisher");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFPenaltyIdentifier", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.EFPenalty", "Penalty")
|
||||
.WithMany()
|
||||
.HasForeignKey("PenaltyId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Penalty");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Misc.EFInboxMessage", b =>
|
||||
@ -1444,6 +1620,12 @@ namespace Data.Migrations.Postgresql
|
||||
.HasForeignKey("SourceClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("DestinationClient");
|
||||
|
||||
b.Navigation("Server");
|
||||
|
||||
b.Navigation("SourceClient");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerSnapshot", b =>
|
||||
@ -1459,6 +1641,10 @@ namespace Data.Migrations.Postgresql
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Map");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b =>
|
||||
@ -1468,6 +1654,39 @@ namespace Data.Migrations.Postgresql
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClient", b =>
|
||||
{
|
||||
b.Navigation("AdministeredPenalties");
|
||||
|
||||
b.Navigation("Meta");
|
||||
|
||||
b.Navigation("ReceivedPenalties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b =>
|
||||
{
|
||||
b.Navigation("PredictedViewAngles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.Navigation("Ratings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientStatistics", b =>
|
||||
{
|
||||
b.Navigation("HitLocations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.EFAliasLink", b =>
|
||||
{
|
||||
b.Navigation("Children");
|
||||
|
||||
b.Navigation("ReceivedPenalties");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
|
1620
Data/Migrations/Sqlite/20220222161952_AddEFPenaltyIdentifier.Designer.cs
generated
Normal file
1620
Data/Migrations/Sqlite/20220222161952_AddEFPenaltyIdentifier.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,55 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Sqlite
|
||||
{
|
||||
public partial class AddEFPenaltyIdentifier : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFPenaltyIdentifiers",
|
||||
columns: table => new
|
||||
{
|
||||
PenaltyIdentifierId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
IPv4Address = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
NetworkId = table.Column<long>(type: "INTEGER", nullable: false),
|
||||
PenaltyId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Active = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFPenaltyIdentifiers", x => x.PenaltyIdentifierId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFPenaltyIdentifiers_EFPenalties_PenaltyId",
|
||||
column: x => x.PenaltyId,
|
||||
principalTable: "EFPenalties",
|
||||
principalColumn: "PenaltyId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFPenaltyIdentifiers_IPv4Address",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
column: "IPv4Address");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFPenaltyIdentifiers_NetworkId",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
column: "NetworkId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFPenaltyIdentifiers_PenaltyId",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
column: "PenaltyId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFPenaltyIdentifiers");
|
||||
}
|
||||
}
|
||||
}
|
1618
Data/Migrations/Sqlite/20220222164454_MakeEFPenaltyLinkIdNullable.Designer.cs
generated
Normal file
1618
Data/Migrations/Sqlite/20220222164454_MakeEFPenaltyLinkIdNullable.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,56 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Sqlite
|
||||
{
|
||||
public partial class MakeEFPenaltyLinkIdNullable : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_EFPenalties_EFAliasLinks_LinkId",
|
||||
table: "EFPenalties");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "LinkId",
|
||||
table: "EFPenalties",
|
||||
type: "INTEGER",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "INTEGER");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_EFPenalties_EFAliasLinks_LinkId",
|
||||
table: "EFPenalties",
|
||||
column: "LinkId",
|
||||
principalTable: "EFAliasLinks",
|
||||
principalColumn: "AliasLinkId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_EFPenalties_EFAliasLinks_LinkId",
|
||||
table: "EFPenalties");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "LinkId",
|
||||
table: "EFPenalties",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "INTEGER",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_EFPenalties_EFAliasLinks_LinkId",
|
||||
table: "EFPenalties",
|
||||
column: "LinkId",
|
||||
principalTable: "EFAliasLinks",
|
||||
principalColumn: "AliasLinkId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
1621
Data/Migrations/Sqlite/20220223144615_AddAuditFieldsToEFPenaltyIdentifier.Designer.cs
generated
Normal file
1621
Data/Migrations/Sqlite/20220223144615_AddAuditFieldsToEFPenaltyIdentifier.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Sqlite
|
||||
{
|
||||
public partial class AddAuditFieldsToEFPenaltyIdentifier : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Active",
|
||||
table: "EFPenaltyIdentifiers");
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "CreatedDateTime",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: DateTime.UtcNow);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "UpdatedDateTime",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CreatedDateTime",
|
||||
table: "EFPenaltyIdentifiers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UpdatedDateTime",
|
||||
table: "EFPenaltyIdentifiers");
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "Active",
|
||||
table: "EFPenaltyIdentifiers",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
}
|
||||
}
|
1624
Data/Migrations/Sqlite/20220329163928_AddConnectionInterruptedToEFServerSnapshot.Designer.cs
generated
Normal file
1624
Data/Migrations/Sqlite/20220329163928_AddConnectionInterruptedToEFServerSnapshot.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,25 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Sqlite
|
||||
{
|
||||
public partial class AddConnectionInterruptedToEFServerSnapshot : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "ConnectionInterrupted",
|
||||
table: "EFServerSnapshot",
|
||||
type: "INTEGER",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ConnectionInterrupted",
|
||||
table: "EFServerSnapshot");
|
||||
}
|
||||
}
|
||||
}
|
1629
Data/Migrations/Sqlite/20220402211115_AddSearchableIPToEFAlias.Designer.cs
generated
Normal file
1629
Data/Migrations/Sqlite/20220402211115_AddSearchableIPToEFAlias.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,26 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Sqlite
|
||||
{
|
||||
public partial class AddSearchableIPToEFAlias : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SearchableIPAddress",
|
||||
table: "EFAlias",
|
||||
type: "TEXT",
|
||||
nullable: true,
|
||||
computedColumnSql: "((IPAddress & 255) || '.' || ((IPAddress >> 8) & 255)) || '.' || ((IPAddress >> 16) & 255) || '.' || ((IPAddress >> 24) & 255)");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SearchableIPAddress",
|
||||
table: "EFAlias");
|
||||
}
|
||||
}
|
||||
}
|
1631
Data/Migrations/Sqlite/20220404192319_AddIndexToSearchableIPToEFAlias.Designer.cs
generated
Normal file
1631
Data/Migrations/Sqlite/20220404192319_AddIndexToSearchableIPToEFAlias.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Sqlite
|
||||
{
|
||||
public partial class AddIndexToSearchableIPToEFAlias : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFAlias_SearchableIPAddress",
|
||||
table: "EFAlias",
|
||||
column: "SearchableIPAddress");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_EFAlias_SearchableIPAddress",
|
||||
table: "EFAlias");
|
||||
}
|
||||
}
|
||||
}
|
1634
Data/Migrations/Sqlite/20220422202315_AddGameToEFClient.Designer.cs
generated
Normal file
1634
Data/Migrations/Sqlite/20220422202315_AddGameToEFClient.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user