Compare commits
83 Commits
2021.06.16
...
2021.11.02
Author | SHA1 | Date | |
---|---|---|---|
769faaa31b | |||
2734a3f138 | |||
914b37b20a | |||
755c149495 | |||
bbcbc4c042 | |||
f3bead8eb5 | |||
7eb45f2bc9 | |||
5837885653 | |||
761d156209 | |||
77f04058de | |||
1317102d00 | |||
a2c7d92162 | |||
b2afc410f2 | |||
5b3420b97a | |||
74bb3da459 | |||
3916278422 | |||
a01543c89b | |||
694431d789 | |||
d5f978858d | |||
e80753a4d3 | |||
d4fb75d07c | |||
e97119211f | |||
87985b3e68 | |||
33c63f01db | |||
68c1151191 | |||
54e39fabb1 | |||
a4f0726b32 | |||
05e228633d | |||
e267bd95da | |||
c7fab5d36c | |||
1f8b7cde3f | |||
c5f9a68102 | |||
eff8a29a39 | |||
0191c8b7a7 | |||
fa6524c3b1 | |||
5b11196b29 | |||
3b7a22edef | |||
deff4f2947 | |||
02e5e78f67 | |||
162006da29 | |||
27e9ecfd9d | |||
da301bef40 | |||
a815bcbff5 | |||
19a49504b8 | |||
3bb87dffb0 | |||
02942e5c03 | |||
8c5ff440db | |||
a0b7781e66 | |||
596272a3de | |||
b83ea57579 | |||
75f68b6385 | |||
d5e4d083c5 | |||
602ec66afe | |||
435b079b94 | |||
a4eec5981f | |||
0b6e261dbb | |||
7e1221f467 | |||
a6b0911af9 | |||
fa66381193 | |||
67c2406325 | |||
e2ea5c6ce0 | |||
5ef00d6dae | |||
5921098dce | |||
31ee71260a | |||
ed8067a4a2 | |||
e2116712e7 | |||
8b06da5783 | |||
33a427bb8a | |||
c9d7a957dc | |||
9c6ff6f353 | |||
7444cb6472 | |||
c7e5c9c8dd | |||
0256fc35d2 | |||
0019ed8dde | |||
56aec53e72 | |||
1b773f21c6 | |||
bccbcce3c1 | |||
fc0bed2405 | |||
16cfb33109 | |||
42979dc5ae | |||
95cbc85144 | |||
8530444ffa | |||
2512b9f251 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -245,4 +245,6 @@ launchSettings.json
|
||||
/Tests/ApplicationTests/Files/replay.json
|
||||
/GameLogServer/game_log_server_env
|
||||
.idea/*
|
||||
*.db
|
||||
*.db
|
||||
/Data/IW4MAdmin_Migration.db-shm
|
||||
/Data/IW4MAdmin_Migration.db-wal
|
||||
|
@ -22,7 +22,7 @@ namespace IW4MAdmin.Application.API.Master
|
||||
public int Uptime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifices the version of the instance
|
||||
/// Specifies the version of the instance
|
||||
/// </summary>
|
||||
[JsonProperty("version")]
|
||||
[JsonConverter(typeof(BuildNumberJsonConverter))]
|
||||
@ -33,5 +33,11 @@ namespace IW4MAdmin.Application.API.Master
|
||||
/// </summary>
|
||||
[JsonProperty("servers")]
|
||||
public List<ApiServer> Servers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Url IW4MAdmin is listening on
|
||||
/// </summary>
|
||||
[JsonProperty("webfront_url")]
|
||||
public string WebfrontUrl { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ namespace IW4MAdmin.Application
|
||||
}
|
||||
|
||||
// remove the update tasks as they have completed
|
||||
foreach (var serverId in serverTasksToRemove)
|
||||
foreach (var serverId in serverTasksToRemove.Where(serverId => runningUpdateTasks.ContainsKey(serverId)))
|
||||
{
|
||||
if (!runningUpdateTasks[serverId].tokenSource.Token.IsCancellationRequested)
|
||||
{
|
||||
@ -259,7 +259,11 @@ namespace IW4MAdmin.Application
|
||||
{
|
||||
try
|
||||
{
|
||||
await server.ProcessUpdatesAsync(_tokenSource.Token).WithWaitCancellation(runningUpdateTasks[server.EndPoint].tokenSource.Token);
|
||||
if (runningUpdateTasks.ContainsKey(server.EndPoint))
|
||||
{
|
||||
await server.ProcessUpdatesAsync(_tokenSource.Token)
|
||||
.WithWaitCancellation(runningUpdateTasks[server.EndPoint].tokenSource.Token);
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
@ -353,9 +357,7 @@ namespace IW4MAdmin.Application
|
||||
|
||||
_appConfig.AutoMessages = defaultConfig.AutoMessages;
|
||||
_appConfig.GlobalRules = defaultConfig.GlobalRules;
|
||||
_appConfig.Maps = defaultConfig.Maps;
|
||||
_appConfig.DisallowedClientNames = defaultConfig.DisallowedClientNames;
|
||||
_appConfig.QuickMessages = defaultConfig.QuickMessages;
|
||||
|
||||
//if (newConfig.Servers == null)
|
||||
{
|
||||
@ -396,6 +398,18 @@ namespace IW4MAdmin.Application
|
||||
await ConfigHandler.Save();
|
||||
}
|
||||
|
||||
#pragma warning disable 618
|
||||
if (_appConfig.Maps != null)
|
||||
{
|
||||
_appConfig.Maps = null;
|
||||
}
|
||||
|
||||
if (_appConfig.QuickMessages != null)
|
||||
{
|
||||
_appConfig.QuickMessages = null;
|
||||
}
|
||||
#pragma warning restore 618
|
||||
|
||||
var validator = new ApplicationConfigurationValidator();
|
||||
var validationResult = validator.Validate(_appConfig);
|
||||
|
||||
@ -410,7 +424,7 @@ namespace IW4MAdmin.Application
|
||||
|
||||
foreach (var serverConfig in _appConfig.Servers)
|
||||
{
|
||||
Migration.ConfigurationMigration.ModifyLogPath020919(serverConfig);
|
||||
ConfigurationMigration.ModifyLogPath020919(serverConfig);
|
||||
|
||||
if (serverConfig.RConParserVersion == null || serverConfig.EventParserVersion == null)
|
||||
{
|
||||
@ -591,6 +605,11 @@ namespace IW4MAdmin.Application
|
||||
return _servers.SelectMany(s => s.Clients).ToList().Where(p => p != null).ToList();
|
||||
}
|
||||
|
||||
public EFClient FindActiveClient(EFClient client) =>client.ClientNumber < 0 ?
|
||||
GetActiveClients()
|
||||
.FirstOrDefault(c => c.NetworkId == client.NetworkId) ?? client :
|
||||
client;
|
||||
|
||||
public ClientService GetClientService()
|
||||
{
|
||||
return ClientSvc;
|
||||
|
78
Application/Commands/OfflineMessageCommand.cs
Normal file
78
Application/Commands/OfflineMessageCommand.cs
Normal file
@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Abstractions;
|
||||
using Data.Models.Client;
|
||||
using Data.Models.Misc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace IW4MAdmin.Application.Commands
|
||||
{
|
||||
public class OfflineMessageCommand : Command
|
||||
{
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
private readonly ILogger _logger;
|
||||
private const short MaxLength = 1024;
|
||||
|
||||
public OfflineMessageCommand(CommandConfiguration config, ITranslationLookup layout,
|
||||
IDatabaseContextFactory contextFactory, ILogger<IDatabaseContextFactory> logger) : base(config, layout)
|
||||
{
|
||||
Name = "offlinemessage";
|
||||
Description = _translationLookup["COMMANDS_OFFLINE_MESSAGE_DESC"];
|
||||
Alias = "om";
|
||||
Permission = EFClient.Permission.Moderator;
|
||||
RequiresTarget = true;
|
||||
|
||||
_contextFactory = contextFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent gameEvent)
|
||||
{
|
||||
if (gameEvent.Data.Length > MaxLength)
|
||||
{
|
||||
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));
|
||||
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()
|
||||
{
|
||||
SourceClientId = gameEvent.Origin.ClientId,
|
||||
DestinationClientId = gameEvent.Target.ClientId,
|
||||
ServerId = server.Id,
|
||||
Message = gameEvent.Data,
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
context.Set<EFInboxMessage>().Add(newMessage);
|
||||
await context.SaveChangesAsync();
|
||||
gameEvent.Origin.Tell(_translationLookup["COMMANDS_OFFLINE_MESSAGE_SUCCESS"]);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not save offline message {@Message}", newMessage);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
79
Application/Commands/ReadMessageCommand.cs
Normal file
79
Application/Commands/ReadMessageCommand.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Abstractions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using EFClient = Data.Models.Client.EFClient;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
|
||||
namespace IW4MAdmin.Application.Commands
|
||||
{
|
||||
public class ReadMessageCommand : Command
|
||||
{
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public ReadMessageCommand(CommandConfiguration config, ITranslationLookup layout,
|
||||
IDatabaseContextFactory contextFactory, ILogger<IDatabaseContextFactory> logger) : base(config, layout)
|
||||
{
|
||||
Name = "readmessage";
|
||||
Description = _translationLookup["COMMANDS_READ_MESSAGE_DESC"];
|
||||
Alias = "rm";
|
||||
Permission = EFClient.Permission.Flagged;
|
||||
|
||||
_contextFactory = contextFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent gameEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var context = _contextFactory.CreateContext();
|
||||
|
||||
var inboxItems = await context.InboxMessages
|
||||
.Include(message => message.SourceClient)
|
||||
.ThenInclude(client => client.CurrentAlias)
|
||||
.Where(message => message.DestinationClientId == gameEvent.Origin.ClientId)
|
||||
.Where(message => !message.IsDelivered)
|
||||
.ToListAsync();
|
||||
|
||||
if (!inboxItems.Any())
|
||||
{
|
||||
gameEvent.Origin.Tell(_translationLookup["COMMANDS_READ_MESSAGE_NONE"]);
|
||||
return;
|
||||
}
|
||||
|
||||
var index = 1;
|
||||
foreach (var inboxItem in inboxItems)
|
||||
{
|
||||
await gameEvent.Origin.Tell(_translationLookup["COMMANDS_READ_MESSAGE_SUCCESS"]
|
||||
.FormatExt($"{index}/{inboxItems.Count}", inboxItem.SourceClient.CurrentAlias.Name))
|
||||
.WaitAsync();
|
||||
|
||||
foreach (var messageFragment in inboxItem.Message.FragmentMessageForDisplay())
|
||||
{
|
||||
await gameEvent.Origin.Tell(messageFragment).WaitAsync();
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
inboxItems.ForEach(item => { item.IsDelivered = true; });
|
||||
|
||||
context.UpdateRange(inboxItems);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Could not retrieve offline messages for {Client}", gameEvent.Origin.ToString());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,13 @@
|
||||
"Using": [
|
||||
"Serilog.Sinks.File"
|
||||
],
|
||||
"MinimumLevel": "Information",
|
||||
"MinimumLevel": {
|
||||
"Default": "Information",
|
||||
"Override": {
|
||||
"System": "Warning",
|
||||
"Microsoft": "Warning"
|
||||
}
|
||||
},
|
||||
"WriteTo": [
|
||||
{
|
||||
"Name": "File",
|
||||
|
@ -435,8 +435,8 @@
|
||||
"Name": "oilrig"
|
||||
},
|
||||
{
|
||||
"Name": "Village",
|
||||
"Alias": "co_hunted"
|
||||
"Alias": "Village",
|
||||
"Name": "co_hunted"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -699,6 +699,10 @@
|
||||
{
|
||||
"Alias": "Rust",
|
||||
"Name": "mp_rust"
|
||||
},
|
||||
{
|
||||
"Alias": "Highrise",
|
||||
"Name": "mp_highrise"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -859,11 +863,132 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Game": "T7",
|
||||
"Maps": [
|
||||
{
|
||||
"Alias": "Evac",
|
||||
"Name": "mp_apartments"
|
||||
},
|
||||
{
|
||||
"Alias": "Aquarium",
|
||||
"Name": "mp_biodome"
|
||||
},
|
||||
{
|
||||
"Alias": "Exodus",
|
||||
"Name": "mp_chinatown"
|
||||
},
|
||||
{
|
||||
"Alias": "Hunted",
|
||||
"Name": "mp_ethiopia"
|
||||
},
|
||||
{
|
||||
"Alias": "Havoc",
|
||||
"Name": "mp_havoc"
|
||||
},
|
||||
{
|
||||
"Alias": "Infection",
|
||||
"Name": "mp_infection"
|
||||
},
|
||||
{
|
||||
"Alias": "Metro",
|
||||
"Name": "mp_metro"
|
||||
},
|
||||
{
|
||||
"Alias": "Redwood",
|
||||
"Name": "mp_redwood"
|
||||
},
|
||||
{
|
||||
"Alias": "Combine",
|
||||
"Name": "mp_sector"
|
||||
},
|
||||
{
|
||||
"Alias": "Breach",
|
||||
"Name": "mp_spire"
|
||||
},
|
||||
{
|
||||
"Alias": "Stronghold",
|
||||
"Name": "mp_stronghold"
|
||||
},
|
||||
{
|
||||
"Alias": "Fringe",
|
||||
"Name": "mp_veiled"
|
||||
},
|
||||
{
|
||||
"Alias": "Nuk3town",
|
||||
"Name": "mp_nuketown_x"
|
||||
},
|
||||
{
|
||||
"Alias": "Gauntlet",
|
||||
"Name": "mp_crucible"
|
||||
},
|
||||
{
|
||||
"Alias": "Rise",
|
||||
"Name": "mp_rise"
|
||||
},
|
||||
{
|
||||
"Alias": "Skyjacked",
|
||||
"Name": "mp_skyjacked"
|
||||
},
|
||||
{
|
||||
"Alias": "Splash",
|
||||
"Name": "mp_waterpark"
|
||||
},
|
||||
{
|
||||
"Alias": "Spire",
|
||||
"Name": "mp_aerospace"
|
||||
},
|
||||
{
|
||||
"Alias": "Verge",
|
||||
"Name": "mp_banzai"
|
||||
},
|
||||
{
|
||||
"Alias": "Rift",
|
||||
"Name": "mp_conduit"
|
||||
},
|
||||
{
|
||||
"Alias": "Knockout",
|
||||
"Name": "mp_kung_fu"
|
||||
},
|
||||
{
|
||||
"Alias": "Rumble",
|
||||
"Name": "mp_arena"
|
||||
},
|
||||
{
|
||||
"Alias": "Cyrogen",
|
||||
"Name": "mp_cryogen"
|
||||
},
|
||||
{
|
||||
"Alias": "Empire",
|
||||
"Name": "mp_rome"
|
||||
},
|
||||
{
|
||||
"Alias": "Berserk",
|
||||
"Name": "mp_shrine"
|
||||
},
|
||||
{
|
||||
"Alias": "Rupture",
|
||||
"Name": "mp_city"
|
||||
},
|
||||
{
|
||||
"Alias": "Micro",
|
||||
"Name": "mp_miniature"
|
||||
},
|
||||
{
|
||||
"Alias": "Citadel",
|
||||
"Name": "mp_ruins"
|
||||
},
|
||||
{
|
||||
"Alias": "Outlaw",
|
||||
"Name": "mp_western"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Game": "IW6",
|
||||
"Maps": [
|
||||
{
|
||||
"Alias": "Prision Break",
|
||||
"Alias": "Prison Break",
|
||||
"Name": "mp_prisonbreak"
|
||||
},
|
||||
{
|
||||
|
@ -22,7 +22,7 @@ namespace IW4MAdmin.Application.Factories
|
||||
/// </summary>
|
||||
/// <param name="translationLookup"></param>
|
||||
/// <param name="rconConnectionFactory"></param>
|
||||
public GameServerInstanceFactory(ITranslationLookup translationLookup,
|
||||
public GameServerInstanceFactory(ITranslationLookup translationLookup,
|
||||
IMetaService metaService,
|
||||
IServiceProvider serviceProvider)
|
||||
{
|
||||
@ -39,7 +39,10 @@ namespace IW4MAdmin.Application.Factories
|
||||
/// <returns></returns>
|
||||
public Server CreateServer(ServerConfiguration config, IManager manager)
|
||||
{
|
||||
return new IW4MServer(config, _translationLookup, _metaService, _serviceProvider, _serviceProvider.GetRequiredService<IClientNoticeMessageFormatter>(), _serviceProvider.GetRequiredService<ILookupCache<EFServer>>());
|
||||
return new IW4MServer(config,
|
||||
_serviceProvider.GetRequiredService<CommandConfiguration>(), _translationLookup, _metaService,
|
||||
_serviceProvider, _serviceProvider.GetRequiredService<IClientNoticeMessageFormatter>(),
|
||||
_serviceProvider.GetRequiredService<ILookupCache<EFServer>>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System.Text;
|
||||
using Integrations.Cod;
|
||||
@ -6,6 +7,7 @@ using Integrations.Source;
|
||||
using Integrations.Source.Interfaces;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore.Configuration;
|
||||
|
||||
namespace IW4MAdmin.Application.Factories
|
||||
{
|
||||
@ -26,21 +28,17 @@ namespace IW4MAdmin.Application.Factories
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// creates a new rcon connection instance
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">ip address of the server</param>
|
||||
/// <param name="port">port of the server</param>
|
||||
/// <param name="password">rcon password of the server</param>
|
||||
/// <returns></returns>
|
||||
public IRConConnection CreateConnection(string ipAddress, int port, string password, string rconEngine)
|
||||
/// <inheritdoc/>
|
||||
public IRConConnection CreateConnection(IPEndPoint ipEndpoint, string password, string rconEngine)
|
||||
{
|
||||
return rconEngine switch
|
||||
{
|
||||
"COD" => new CodRConConnection(ipAddress, port, password,
|
||||
_serviceProvider.GetRequiredService<ILogger<CodRConConnection>>(), GameEncoding),
|
||||
"Source" => new SourceRConConnection(_serviceProvider.GetRequiredService<ILogger<SourceRConConnection>>(),
|
||||
_serviceProvider.GetRequiredService<IRConClientFactory>(), ipAddress, port, password),
|
||||
"COD" => new CodRConConnection(ipEndpoint, password,
|
||||
_serviceProvider.GetRequiredService<ILogger<CodRConConnection>>(), GameEncoding,
|
||||
_serviceProvider.GetRequiredService<ApplicationConfiguration>()?.ServerConnectionAttempts ?? 6),
|
||||
"Source" => new SourceRConConnection(
|
||||
_serviceProvider.GetRequiredService<ILogger<SourceRConConnection>>(),
|
||||
_serviceProvider.GetRequiredService<IRConClientFactory>(), ipEndpoint, password),
|
||||
_ => throw new ArgumentException($"No supported RCon engine available for '{rconEngine}'")
|
||||
};
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ namespace IW4MAdmin.Application
|
||||
{
|
||||
private readonly EventLog _eventLog;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IEventPublisher _eventPublisher;
|
||||
private static readonly GameEvent.EventType[] overrideEvents = new[]
|
||||
{
|
||||
GameEvent.EventType.Connect,
|
||||
@ -21,10 +22,11 @@ namespace IW4MAdmin.Application
|
||||
GameEvent.EventType.Stop
|
||||
};
|
||||
|
||||
public GameEventHandler(ILogger<GameEventHandler> logger)
|
||||
public GameEventHandler(ILogger<GameEventHandler> logger, IEventPublisher eventPublisher)
|
||||
{
|
||||
_eventLog = new EventLog();
|
||||
_logger = logger;
|
||||
_eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
public void HandleEvent(IManager manager, GameEvent gameEvent)
|
||||
@ -32,6 +34,7 @@ namespace IW4MAdmin.Application
|
||||
if (manager.IsRunning || overrideEvents.Contains(gameEvent.Type))
|
||||
{
|
||||
EventApi.OnGameEvent(gameEvent);
|
||||
_eventPublisher.Publish(gameEvent);
|
||||
Task.Factory.StartNew(() => manager.ExecuteEvent(gameEvent));
|
||||
}
|
||||
else
|
||||
|
@ -11,6 +11,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
@ -22,6 +23,7 @@ using Serilog.Context;
|
||||
using static SharedLibraryCore.Database.Models.EFClient;
|
||||
using Data.Models;
|
||||
using Data.Models.Server;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using static Data.Models.Client.EFClient;
|
||||
|
||||
namespace IW4MAdmin
|
||||
@ -39,9 +41,11 @@ namespace IW4MAdmin
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IClientNoticeMessageFormatter _messageFormatter;
|
||||
private readonly ILookupCache<EFServer> _serverCache;
|
||||
private readonly CommandConfiguration _commandConfiguration;
|
||||
|
||||
public IW4MServer(
|
||||
ServerConfiguration serverConfiguration,
|
||||
CommandConfiguration commandConfiguration,
|
||||
ITranslationLookup lookup,
|
||||
IMetaService metaService,
|
||||
IServiceProvider serviceProvider,
|
||||
@ -58,6 +62,7 @@ namespace IW4MAdmin
|
||||
_serviceProvider = serviceProvider;
|
||||
_messageFormatter = messageFormatter;
|
||||
_serverCache = serverCache;
|
||||
_commandConfiguration = commandConfiguration;
|
||||
}
|
||||
|
||||
public override async Task<EFClient> OnClientConnected(EFClient clientFromLog)
|
||||
@ -158,7 +163,7 @@ namespace IW4MAdmin
|
||||
{
|
||||
try
|
||||
{
|
||||
C = await SharedLibraryCore.Commands.CommandProcessing.ValidateCommand(E, Manager.GetApplicationSettings().Configuration());
|
||||
C = await SharedLibraryCore.Commands.CommandProcessing.ValidateCommand(E, Manager.GetApplicationSettings().Configuration(), _commandConfiguration);
|
||||
}
|
||||
|
||||
catch (CommandException e)
|
||||
@ -305,6 +310,11 @@ namespace IW4MAdmin
|
||||
Console.WriteLine(loc["MANAGER_CONNECTION_REST"].FormatExt($"[{IP}:{Port}]"));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(CustomSayName))
|
||||
{
|
||||
await this.SetDvarAsync("sv_sayname", CustomSayName);
|
||||
}
|
||||
|
||||
Throttled = false;
|
||||
}
|
||||
|
||||
@ -338,7 +348,26 @@ namespace IW4MAdmin
|
||||
E.Origin.Tag = clientTag.LinkedMeta.Value;
|
||||
}
|
||||
|
||||
await E.Origin.OnJoin(E.Origin.IPAddress);
|
||||
try
|
||||
{
|
||||
var factory = _serviceProvider.GetRequiredService<IDatabaseContextFactory>();
|
||||
await using var context = factory.CreateContext();
|
||||
|
||||
var messageCount = await context.InboxMessages
|
||||
.CountAsync(msg => msg.DestinationClientId == E.Origin.ClientId && !msg.IsDelivered);
|
||||
|
||||
if (messageCount > 0)
|
||||
{
|
||||
E.Origin.Tell(_translationLookup["SERVER_JOIN_OFFLINE_MESSAGES"]);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServerLogger.LogError(ex, "Could not get offline message count for {Client}", E.Origin.ToString());
|
||||
throw;
|
||||
}
|
||||
|
||||
await E.Origin.OnJoin(E.Origin.IPAddress, Manager.GetApplicationSettings().Configuration().EnableImplicitAccountLinking);
|
||||
}
|
||||
}
|
||||
|
||||
@ -438,7 +467,7 @@ namespace IW4MAdmin
|
||||
Link = E.Target.AliasLink
|
||||
};
|
||||
|
||||
var addedPenalty = await Manager.GetPenaltyService().Create(newPenalty);
|
||||
await Manager.GetPenaltyService().Create(newPenalty);
|
||||
E.Target.SetLevel(Permission.Flagged, E.Origin);
|
||||
}
|
||||
|
||||
@ -590,7 +619,7 @@ namespace IW4MAdmin
|
||||
{
|
||||
try
|
||||
{
|
||||
message = Manager.GetApplicationSettings().Configuration()
|
||||
message = _serviceProvider.GetRequiredService<DefaultSettings>()
|
||||
.QuickMessages
|
||||
.First(_qm => _qm.Game == GameName)
|
||||
.Messages[E.Data.Substring(1)];
|
||||
@ -696,11 +725,11 @@ namespace IW4MAdmin
|
||||
|
||||
private async Task OnClientUpdate(EFClient origin)
|
||||
{
|
||||
var client = GetClientsAsList().FirstOrDefault(_client => _client.Equals(origin));
|
||||
var client = Manager.GetActiveClients().FirstOrDefault(c => c.NetworkId == origin.NetworkId);
|
||||
|
||||
if (client == null)
|
||||
{
|
||||
ServerLogger.LogWarning("{origin} expected to exist in client list for update, but they do not", origin.ToString());
|
||||
ServerLogger.LogWarning("{Origin} expected to exist in client list for update, but they do not", origin.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -714,7 +743,7 @@ namespace IW4MAdmin
|
||||
{
|
||||
try
|
||||
{
|
||||
await client.OnJoin(origin.IPAddress);
|
||||
await client.OnJoin(origin.IPAddress, Manager.GetApplicationSettings().Configuration().EnableImplicitAccountLinking);
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
@ -726,11 +755,11 @@ namespace IW4MAdmin
|
||||
}
|
||||
}
|
||||
|
||||
else if ((client.IPAddress != null && client.State == ClientState.Disconnecting) ||
|
||||
else if (client.IPAddress != null && client.State == ClientState.Disconnecting ||
|
||||
client.Level == Permission.Banned)
|
||||
{
|
||||
ServerLogger.LogWarning("{client} state is Unknown (probably kicked), but they are still connected. trying to kick again...", origin.ToString());
|
||||
await client.CanConnect(client.IPAddress);
|
||||
ServerLogger.LogWarning("{Client} state is Unknown (probably kicked), but they are still connected. trying to kick again...", origin.ToString());
|
||||
await client.CanConnect(client.IPAddress, Manager.GetApplicationSettings().Configuration().EnableImplicitAccountLinking);
|
||||
}
|
||||
}
|
||||
|
||||
@ -768,8 +797,10 @@ namespace IW4MAdmin
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<long> GetIdForServer(Server server)
|
||||
public override async Task<long> GetIdForServer(Server server = null)
|
||||
{
|
||||
server ??= this;
|
||||
|
||||
if ($"{server.IP}:{server.Port.ToString()}" == "66.150.121.184:28965")
|
||||
{
|
||||
return 886229536;
|
||||
@ -976,10 +1007,13 @@ namespace IW4MAdmin
|
||||
LastMessage = DateTime.Now - start;
|
||||
lastCount = DateTime.Now;
|
||||
|
||||
var appConfig = _serviceProvider.GetService<ApplicationConfiguration>();
|
||||
// update the player history
|
||||
if ((lastCount - playerCountStart).TotalMinutes >= PlayerHistory.UpdateInterval)
|
||||
if (lastCount - playerCountStart >= appConfig.ServerDataCollectionInterval)
|
||||
{
|
||||
while (ClientHistory.Count > ((60 / PlayerHistory.UpdateInterval) * 12)) // 12 times a hour for 12 hours
|
||||
var maxItems = Math.Ceiling(appConfig.MaxClientHistoryTime.TotalMinutes /
|
||||
appConfig.ServerDataCollectionInterval.TotalMinutes);
|
||||
while ( ClientHistory.Count > maxItems)
|
||||
{
|
||||
ClientHistory.Dequeue();
|
||||
}
|
||||
@ -1036,6 +1070,16 @@ namespace IW4MAdmin
|
||||
|
||||
public async Task Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
ResolvedIpEndPoint = new IPEndPoint((await Dns.GetHostAddressesAsync(IP)).First(), Port);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServerLogger.LogWarning(ex, "Could not resolve hostname or IP for RCon connection {IP}:{Port}", IP, Port);
|
||||
ResolvedIpEndPoint = new IPEndPoint(IPAddress.Parse(IP), Port);
|
||||
}
|
||||
|
||||
RconParser = Manager.AdditionalRConParsers
|
||||
.FirstOrDefault(_parser => _parser.Version == ServerConfig.RConParserVersion);
|
||||
|
||||
@ -1045,7 +1089,7 @@ namespace IW4MAdmin
|
||||
RconParser ??= Manager.AdditionalRConParsers[0];
|
||||
EventParser ??= Manager.AdditionalEventParsers[0];
|
||||
|
||||
RemoteConnection = RConConnectionFactory.CreateConnection(IP, Port, Password, RconParser.RConEngine);
|
||||
RemoteConnection = RConConnectionFactory.CreateConnection(ResolvedIpEndPoint, Password, RconParser.RConEngine);
|
||||
RemoteConnection.SetConfiguration(RconParser);
|
||||
|
||||
var version = await this.GetMappedDvarValueOrDefaultAsync<string>("version");
|
||||
@ -1078,8 +1122,9 @@ namespace IW4MAdmin
|
||||
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 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");
|
||||
@ -1115,8 +1160,14 @@ namespace IW4MAdmin
|
||||
{
|
||||
Manager.GetApplicationSettings().Configuration().ContactUri = Website;
|
||||
}
|
||||
|
||||
var defaultConfig = _serviceProvider.GetRequiredService<DefaultSettings>();
|
||||
var gameMaps = defaultConfig?.Maps?.FirstOrDefault(map => map.Game == GameName);
|
||||
|
||||
InitializeMaps();
|
||||
if (gameMaps != null)
|
||||
{
|
||||
Maps.AddRange(gameMaps.Maps);
|
||||
}
|
||||
|
||||
WorkingDirectory = basepath.Value;
|
||||
this.Hostname = hostname;
|
||||
@ -1174,6 +1225,7 @@ namespace IW4MAdmin
|
||||
{
|
||||
BaseGameDirectory = basegame.Value,
|
||||
BasePathDirectory = basepath.Value,
|
||||
HomePathDirectory = homepath.Value,
|
||||
GameDirectory = EventParser.Configuration.GameDirectory ?? "",
|
||||
ModDirectory = game.Value ?? "",
|
||||
LogFile = logfile.Value,
|
||||
@ -1224,15 +1276,25 @@ namespace IW4MAdmin
|
||||
{
|
||||
string logPath;
|
||||
var workingDirectory = logInfo.BasePathDirectory;
|
||||
|
||||
bool IsValidGamePath (string path)
|
||||
{
|
||||
var baseGameIsDirectory = !string.IsNullOrWhiteSpace(path) &&
|
||||
path.IndexOfAny(Utilities.DirectorySeparatorChars) != -1;
|
||||
|
||||
var baseGameIsDirectory = !string.IsNullOrWhiteSpace(logInfo.BaseGameDirectory) &&
|
||||
logInfo.BaseGameDirectory.IndexOfAny(Utilities.DirectorySeparatorChars) != -1;
|
||||
var baseGameIsRelative = path.FixDirectoryCharacters()
|
||||
.Equals(logInfo.GameDirectory.FixDirectoryCharacters(), StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
var baseGameIsRelative = logInfo.BaseGameDirectory.FixDirectoryCharacters()
|
||||
.Equals(logInfo.GameDirectory.FixDirectoryCharacters(), StringComparison.InvariantCultureIgnoreCase);
|
||||
return baseGameIsDirectory && !baseGameIsRelative;
|
||||
}
|
||||
|
||||
// we want to see if base game is provided and it 'looks' like a directory
|
||||
if (baseGameIsDirectory && !baseGameIsRelative)
|
||||
if (IsValidGamePath(logInfo.HomePathDirectory))
|
||||
{
|
||||
workingDirectory = logInfo.HomePathDirectory;
|
||||
}
|
||||
|
||||
else if (IsValidGamePath(logInfo.BaseGameDirectory))
|
||||
{
|
||||
workingDirectory = logInfo.BaseGameDirectory;
|
||||
}
|
||||
@ -1259,12 +1321,9 @@ namespace IW4MAdmin
|
||||
public override async Task Warn(string reason, EFClient targetClient, EFClient targetOrigin)
|
||||
{
|
||||
// ensure player gets warned if command not performed on them in game
|
||||
targetClient = targetClient.ClientNumber < 0 ?
|
||||
Manager.GetActiveClients()
|
||||
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
|
||||
targetClient;
|
||||
var activeClient = Manager.FindActiveClient(targetClient);
|
||||
|
||||
var newPenalty = new EFPenalty()
|
||||
var newPenalty = new EFPenalty
|
||||
{
|
||||
Type = EFPenalty.PenaltyType.Warning,
|
||||
Expires = DateTime.UtcNow,
|
||||
@ -1274,31 +1333,28 @@ namespace IW4MAdmin
|
||||
Link = targetClient.AliasLink
|
||||
};
|
||||
|
||||
ServerLogger.LogDebug("Creating warn penalty for {targetClient}", targetClient.ToString());
|
||||
ServerLogger.LogDebug("Creating warn penalty for {TargetClient}", targetClient.ToString());
|
||||
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger);
|
||||
|
||||
if (targetClient.IsIngame)
|
||||
if (activeClient.IsIngame)
|
||||
{
|
||||
if (targetClient.Warnings >= 4)
|
||||
if (activeClient.Warnings >= 4)
|
||||
{
|
||||
targetClient.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient(this));
|
||||
activeClient.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient(this));
|
||||
return;
|
||||
}
|
||||
|
||||
// todo: move to translation sheet
|
||||
string message = $"^1{loc["SERVER_WARNING"]} ^7[^3{targetClient.Warnings}^7]: ^3{targetClient.Name}^7, {reason}";
|
||||
targetClient.CurrentServer.Broadcast(message);
|
||||
var message = loc["COMMANDS_WARNING_FORMAT"]
|
||||
.FormatExt(activeClient.Warnings, activeClient.Name, reason);
|
||||
activeClient.CurrentServer.Broadcast(message);
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task Kick(string reason, EFClient targetClient, EFClient originClient, EFPenalty previousPenalty)
|
||||
{
|
||||
targetClient = targetClient.ClientNumber < 0 ?
|
||||
Manager.GetActiveClients()
|
||||
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
|
||||
targetClient;
|
||||
var activeClient = Manager.FindActiveClient(targetClient);
|
||||
|
||||
var newPenalty = new EFPenalty()
|
||||
var newPenalty = new EFPenalty
|
||||
{
|
||||
Type = EFPenalty.PenaltyType.Kick,
|
||||
Expires = DateTime.UtcNow,
|
||||
@ -1308,77 +1364,64 @@ namespace IW4MAdmin
|
||||
Link = targetClient.AliasLink
|
||||
};
|
||||
|
||||
ServerLogger.LogDebug("Creating kick penalty for {targetClient}", targetClient.ToString());
|
||||
ServerLogger.LogDebug("Creating kick penalty for {TargetClient}", targetClient.ToString());
|
||||
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger);
|
||||
|
||||
if (targetClient.IsIngame)
|
||||
if (activeClient.IsIngame)
|
||||
{
|
||||
var e = new GameEvent()
|
||||
var gameEvent = new GameEvent
|
||||
{
|
||||
Type = GameEvent.EventType.PreDisconnect,
|
||||
Origin = targetClient,
|
||||
Origin = activeClient,
|
||||
Owner = this
|
||||
};
|
||||
|
||||
Manager.AddEvent(e);
|
||||
|
||||
var temporalClientId = targetClient.GetAdditionalProperty<string>("ConnectionClientId");
|
||||
var parsedClientId = string.IsNullOrEmpty(temporalClientId) ? (int?)null : int.Parse(temporalClientId);
|
||||
var clientNumber = parsedClientId ?? targetClient.ClientNumber;
|
||||
Manager.AddEvent(gameEvent);
|
||||
|
||||
var formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick,
|
||||
clientNumber,
|
||||
activeClient.TemporalClientNumber,
|
||||
_messageFormatter.BuildFormattedMessage(RconParser.Configuration,
|
||||
newPenalty,
|
||||
previousPenalty));
|
||||
await targetClient.CurrentServer.ExecuteCommandAsync(formattedKick);
|
||||
ServerLogger.LogDebug("Executing tempban kick command for {ActiveClient}", activeClient.ToString());
|
||||
await activeClient.CurrentServer.ExecuteCommandAsync(formattedKick);
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task TempBan(string Reason, TimeSpan length, EFClient targetClient, EFClient originClient)
|
||||
public override async Task TempBan(string reason, TimeSpan length, EFClient targetClient, EFClient originClient)
|
||||
{
|
||||
// ensure player gets kicked if command not performed on them in the same server
|
||||
targetClient = targetClient.ClientNumber < 0 ?
|
||||
Manager.GetActiveClients()
|
||||
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
|
||||
targetClient;
|
||||
var activeClient = Manager.FindActiveClient(targetClient);
|
||||
|
||||
var newPenalty = new EFPenalty()
|
||||
var newPenalty = new EFPenalty
|
||||
{
|
||||
Type = EFPenalty.PenaltyType.TempBan,
|
||||
Expires = DateTime.UtcNow + length,
|
||||
Offender = targetClient,
|
||||
Offense = Reason,
|
||||
Offense = reason,
|
||||
Punisher = originClient,
|
||||
Link = targetClient.AliasLink
|
||||
};
|
||||
|
||||
ServerLogger.LogDebug("Creating tempban penalty for {targetClient}", targetClient.ToString());
|
||||
ServerLogger.LogDebug("Creating tempban penalty for {TargetClient}", targetClient.ToString());
|
||||
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger);
|
||||
|
||||
if (targetClient.IsIngame)
|
||||
if (activeClient.IsIngame)
|
||||
{
|
||||
var temporalClientId = targetClient.GetAdditionalProperty<string>("ConnectionClientId");
|
||||
var parsedClientId = string.IsNullOrEmpty(temporalClientId) ? (int?)null : int.Parse(temporalClientId);
|
||||
var clientNumber = parsedClientId ?? targetClient.ClientNumber;
|
||||
|
||||
var formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick,
|
||||
clientNumber,
|
||||
activeClient.TemporalClientNumber,
|
||||
_messageFormatter.BuildFormattedMessage(RconParser.Configuration, newPenalty));
|
||||
ServerLogger.LogDebug("Executing tempban kick command for {targetClient}", targetClient.ToString());
|
||||
await targetClient.CurrentServer.ExecuteCommandAsync(formattedKick);
|
||||
ServerLogger.LogDebug("Executing tempban kick command for {ActiveClient}", activeClient.ToString());
|
||||
await activeClient.CurrentServer.ExecuteCommandAsync(formattedKick);
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task Ban(string reason, EFClient targetClient, EFClient originClient, bool isEvade = false)
|
||||
{
|
||||
// ensure player gets kicked if command not performed on them in the same server
|
||||
targetClient = targetClient.ClientNumber < 0 ?
|
||||
Manager.GetActiveClients()
|
||||
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
|
||||
targetClient;
|
||||
var activeClient = Manager.FindActiveClient(targetClient);
|
||||
|
||||
EFPenalty newPenalty = new EFPenalty()
|
||||
var newPenalty = new EFPenalty
|
||||
{
|
||||
Type = EFPenalty.PenaltyType.Ban,
|
||||
Expires = null,
|
||||
@ -1389,46 +1432,42 @@ namespace IW4MAdmin
|
||||
IsEvadedOffense = isEvade
|
||||
};
|
||||
|
||||
ServerLogger.LogDebug("Creating ban penalty for {targetClient}", targetClient.ToString());
|
||||
targetClient.SetLevel(Permission.Banned, originClient);
|
||||
ServerLogger.LogDebug("Creating ban penalty for {TargetClient}", targetClient.ToString());
|
||||
activeClient.SetLevel(Permission.Banned, originClient);
|
||||
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger);
|
||||
|
||||
if (targetClient.IsIngame)
|
||||
if (activeClient.IsIngame)
|
||||
{
|
||||
ServerLogger.LogDebug("Attempting to kicking newly banned client {targetClient}", targetClient.ToString());
|
||||
|
||||
var temporalClientId = targetClient.GetAdditionalProperty<string>("ConnectionClientId");
|
||||
var parsedClientId = string.IsNullOrEmpty(temporalClientId) ? (int?)null : int.Parse(temporalClientId);
|
||||
var clientNumber = parsedClientId ?? targetClient.ClientNumber;
|
||||
ServerLogger.LogDebug("Attempting to kicking newly banned client {ActiveClient}", activeClient.ToString());
|
||||
|
||||
var formattedString = string.Format(RconParser.Configuration.CommandPrefixes.Kick,
|
||||
clientNumber,
|
||||
activeClient.TemporalClientNumber,
|
||||
_messageFormatter.BuildFormattedMessage(RconParser.Configuration, newPenalty));
|
||||
await targetClient.CurrentServer.ExecuteCommandAsync(formattedString);
|
||||
await activeClient.CurrentServer.ExecuteCommandAsync(formattedString);
|
||||
}
|
||||
}
|
||||
|
||||
override public async Task Unban(string reason, EFClient Target, EFClient Origin)
|
||||
public override async Task Unban(string reason, EFClient targetClient, EFClient originClient)
|
||||
{
|
||||
var unbanPenalty = new EFPenalty()
|
||||
var unbanPenalty = new EFPenalty
|
||||
{
|
||||
Type = EFPenalty.PenaltyType.Unban,
|
||||
Expires = DateTime.Now,
|
||||
Offender = Target,
|
||||
Offender = targetClient,
|
||||
Offense = reason,
|
||||
Punisher = Origin,
|
||||
Punisher = originClient,
|
||||
When = DateTime.UtcNow,
|
||||
Active = true,
|
||||
Link = Target.AliasLink
|
||||
Link = targetClient.AliasLink
|
||||
};
|
||||
|
||||
ServerLogger.LogDebug("Creating unban penalty for {targetClient}", Target.ToString());
|
||||
Target.SetLevel(Permission.User, Origin);
|
||||
await Manager.GetPenaltyService().RemoveActivePenalties(Target.AliasLink.AliasLinkId);
|
||||
ServerLogger.LogDebug("Creating unban penalty for {targetClient}", targetClient.ToString());
|
||||
targetClient.SetLevel(Permission.User, originClient);
|
||||
await Manager.GetPenaltyService().RemoveActivePenalties(targetClient.AliasLink.AliasLinkId);
|
||||
await Manager.GetPenaltyService().Create(unbanPenalty);
|
||||
}
|
||||
|
||||
override public void InitializeTokens()
|
||||
public override void InitializeTokens()
|
||||
{
|
||||
Manager.GetMessageTokens().Add(new MessageToken("TOTALPLAYERS", (Server s) => Task.Run(async () => (await Manager.GetClientService().GetTotalClientsAsync()).ToString())));
|
||||
Manager.GetMessageTokens().Add(new MessageToken("VERSION", (Server s) => Task.FromResult(Application.Program.Version.ToString())));
|
||||
|
@ -19,6 +19,7 @@ using SharedLibraryCore.Services;
|
||||
using Stats.Dtos;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -185,6 +186,8 @@ namespace IW4MAdmin.Application
|
||||
? WebfrontCore.Program.Init(ServerManager, serviceProvider, services, ServerManager.CancellationToken)
|
||||
: Task.CompletedTask;
|
||||
|
||||
var collectionService = serviceProvider.GetRequiredService<IServerDataCollector>();
|
||||
|
||||
// we want to run this one on a manual thread instead of letting the thread pool handle it,
|
||||
// because we can't exit early from waiting on console input, and it prevents us from restarting
|
||||
var inputThread = new Thread(async () => await ReadConsoleInput(logger));
|
||||
@ -195,7 +198,8 @@ namespace IW4MAdmin.Application
|
||||
ServerManager.Start(),
|
||||
webfrontTask,
|
||||
serviceProvider.GetRequiredService<IMasterCommunication>()
|
||||
.RunUploadStatus(ServerManager.CancellationToken)
|
||||
.RunUploadStatus(ServerManager.CancellationToken),
|
||||
collectionService.BeginCollectionAsync(cancellationToken: ServerManager.CancellationToken)
|
||||
};
|
||||
|
||||
logger.LogDebug("Starting webfront and input tasks");
|
||||
@ -271,6 +275,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"))
|
||||
.Where(_command => _command.BaseType == typeof(Command)))
|
||||
{
|
||||
defaultLogger.LogDebug("Registered native command type {name}", commandType.Name);
|
||||
@ -332,11 +337,18 @@ namespace IW4MAdmin.Application
|
||||
// setup the static resources (config/master api/translations)
|
||||
var serviceCollection = new ServiceCollection();
|
||||
var appConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings");
|
||||
var defaultConfigHandler = new BaseConfigurationHandler<DefaultSettings>("DefaultSettings");
|
||||
var defaultConfig = defaultConfigHandler.Configuration();
|
||||
var appConfig = appConfigHandler.Configuration();
|
||||
var masterUri = Utilities.IsDevelopment
|
||||
? new Uri("http://127.0.0.1:8080")
|
||||
: appConfig?.MasterUrl ?? new ApplicationConfiguration().MasterUrl;
|
||||
var masterRestClient = RestClient.For<IMasterApi>(masterUri);
|
||||
var httpClient = new HttpClient
|
||||
{
|
||||
BaseAddress = masterUri,
|
||||
Timeout = TimeSpan.FromSeconds(15)
|
||||
};
|
||||
var masterRestClient = RestClient.For<IMasterApi>(httpClient);
|
||||
var translationLookup = Configure.Initialize(Utilities.DefaultLogger, masterRestClient, appConfig);
|
||||
|
||||
if (appConfig == null)
|
||||
@ -360,6 +372,7 @@ namespace IW4MAdmin.Application
|
||||
|
||||
serviceCollection
|
||||
.AddBaseLogger(appConfig)
|
||||
.AddSingleton(defaultConfig)
|
||||
.AddSingleton<IServiceCollection>(_serviceProvider => serviceCollection)
|
||||
.AddSingleton<IConfigurationHandler<DefaultSettings>, BaseConfigurationHandler<DefaultSettings>>()
|
||||
.AddSingleton((IConfigurationHandler<ApplicationConfiguration>) appConfigHandler)
|
||||
@ -394,6 +407,7 @@ namespace IW4MAdmin.Application
|
||||
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse>,
|
||||
UpdatedAliasResourceQueryHelper>()
|
||||
.AddSingleton<IResourceQueryHelper<ChatSearchQuery, MessageResponse>, ChatResourceQueryHelper>()
|
||||
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, ConnectionHistoryResponse>, ConnectionsResourceQueryHelper>()
|
||||
.AddTransient<IParserPatternMatcher, ParserPatternMatcher>()
|
||||
.AddSingleton<IRemoteAssemblyHandler, RemoteAssemblyHandler>()
|
||||
.AddSingleton<IMasterCommunication, MasterCommunication>()
|
||||
@ -406,6 +420,9 @@ namespace IW4MAdmin.Application
|
||||
.AddSingleton<IHitInfoBuilder, HitInfoBuilder>()
|
||||
.AddSingleton(typeof(ILookupCache<>), typeof(LookupCache<>))
|
||||
.AddSingleton(typeof(IDataValueCache<,>), typeof(DataValueCache<,>))
|
||||
.AddSingleton<IServerDataViewer, ServerDataViewer>()
|
||||
.AddSingleton<IServerDataCollector, ServerDataCollector>()
|
||||
.AddSingleton<IEventPublisher, EventPublisher>()
|
||||
.AddSingleton(translationLookup)
|
||||
.AddDatabaseContextOptions(appConfig);
|
||||
|
||||
|
60
Application/Meta/ConnectionsResourceQueryHelper.cs
Normal file
60
Application/Meta/ConnectionsResourceQueryHelper.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Abstractions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.QueryHelper;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace IW4MAdmin.Application.Meta
|
||||
{
|
||||
public class
|
||||
ConnectionsResourceQueryHelper : IResourceQueryHelper<ClientPaginationRequest, ConnectionHistoryResponse>
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
|
||||
public ConnectionsResourceQueryHelper(ILogger<ConnectionsResourceQueryHelper> logger,
|
||||
IDatabaseContextFactory contextFactory)
|
||||
{
|
||||
_contextFactory = contextFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<ResourceQueryHelperResult<ConnectionHistoryResponse>> QueryResource(
|
||||
ClientPaginationRequest query)
|
||||
{
|
||||
_logger.LogDebug("{Class} {@Request}", nameof(ConnectionsResourceQueryHelper), query);
|
||||
|
||||
await using var context = _contextFactory.CreateContext(enableTracking: false);
|
||||
|
||||
var iqConnections = context.ConnectionHistory.AsNoTracking()
|
||||
.Where(history => query.ClientId == history.ClientId)
|
||||
.Where(history => history.CreatedDateTime < query.Before)
|
||||
.OrderByDescending(history => history.CreatedDateTime);
|
||||
|
||||
var connections = await iqConnections.Select(history => new ConnectionHistoryResponse
|
||||
{
|
||||
MetaId = history.ClientConnectionId,
|
||||
ClientId = history.ClientId,
|
||||
Type = MetaType.ConnectionHistory,
|
||||
ShouldDisplay = true,
|
||||
When = history.CreatedDateTime,
|
||||
ServerName = history.Server.HostName,
|
||||
ConnectionType = history.ConnectionType
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
_logger.LogDebug("{Class} retrieved {Number} items", nameof(ConnectionsResourceQueryHelper),
|
||||
connections.Count);
|
||||
|
||||
return new ResourceQueryHelperResult<ConnectionHistoryResponse>
|
||||
{
|
||||
Results = connections
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -20,11 +20,14 @@ namespace IW4MAdmin.Application.Meta
|
||||
private readonly IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse> _receivedPenaltyHelper;
|
||||
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,
|
||||
IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse> receivedPenaltyHelper,
|
||||
IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse> administeredPenaltyHelper,
|
||||
IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse> updatedAliasHelper)
|
||||
IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse> updatedAliasHelper,
|
||||
IResourceQueryHelper<ClientPaginationRequest, ConnectionHistoryResponse> connectionHistoryHelper)
|
||||
{
|
||||
_logger = logger;
|
||||
_transLookup = transLookup;
|
||||
@ -33,6 +36,7 @@ namespace IW4MAdmin.Application.Meta
|
||||
_receivedPenaltyHelper = receivedPenaltyHelper;
|
||||
_administeredPenaltyHelper = administeredPenaltyHelper;
|
||||
_updatedAliasHelper = updatedAliasHelper;
|
||||
_connectionHistoryHelper = connectionHistoryHelper;
|
||||
}
|
||||
|
||||
public void Register()
|
||||
@ -41,6 +45,7 @@ namespace IW4MAdmin.Application.Meta
|
||||
_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);
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<InformationResponse>> GetProfileMeta(ClientPaginationRequest request)
|
||||
@ -163,5 +168,11 @@ namespace IW4MAdmin.Application.Meta
|
||||
var aliases = await _updatedAliasHelper.QueryResource(request);
|
||||
return aliases.Results;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<ConnectionHistoryResponse>> GetConnectionHistoryMeta(ClientPaginationRequest request)
|
||||
{
|
||||
var connections = await _connectionHistoryHelper.QueryResource(request);
|
||||
return connections.Results;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Abstractions;
|
||||
using Data.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
@ -21,11 +23,14 @@ namespace IW4MAdmin.Application.Meta
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
private readonly ApplicationConfiguration _appConfig;
|
||||
|
||||
public ReceivedPenaltyResourceQueryHelper(ILogger<ReceivedPenaltyResourceQueryHelper> logger, IDatabaseContextFactory contextFactory)
|
||||
public ReceivedPenaltyResourceQueryHelper(ILogger<ReceivedPenaltyResourceQueryHelper> logger,
|
||||
IDatabaseContextFactory contextFactory, ApplicationConfiguration appConfig)
|
||||
{
|
||||
_contextFactory = contextFactory;
|
||||
_logger = logger;
|
||||
_appConfig = appConfig;
|
||||
}
|
||||
|
||||
public async Task<ResourceQueryHelperResult<ReceivedPenaltyResponse>> QueryResource(ClientPaginationRequest query)
|
||||
@ -35,15 +40,40 @@ namespace IW4MAdmin.Application.Meta
|
||||
|
||||
var linkId = await ctx.Clients.AsNoTracking()
|
||||
.Where(_client => _client.ClientId == query.ClientId)
|
||||
.Select(_client => _client.AliasLinkId)
|
||||
.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))
|
||||
.Where(_penalty => _penalty.When < query.Before)
|
||||
.OrderByDescending(_penalty => _penalty.When);
|
||||
.Where(_penalty => _penalty.OffenderId == query.ClientId ||
|
||||
linkedPenaltyType.Contains(_penalty.Type) && _penalty.LinkId == linkId.AliasLinkId);
|
||||
|
||||
var penalties = await iqPenalties
|
||||
IQueryable<EFPenalty> iqIpLinkedPenalties = null;
|
||||
|
||||
if (!_appConfig.EnableImplicitAccountLinking)
|
||||
{
|
||||
var usedIps = await ctx.Aliases.AsNoTracking()
|
||||
.Where(alias => (alias.LinkId == linkId.AliasLinkId || alias.AliasId == linkId.CurrentAliasId) && alias.IPAddress != null)
|
||||
.Select(alias => alias.IPAddress).ToListAsync();
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
var iqAllPenalties = iqPenalties;
|
||||
|
||||
if (iqIpLinkedPenalties != null)
|
||||
{
|
||||
iqAllPenalties = iqPenalties.Union(iqIpLinkedPenalties);
|
||||
}
|
||||
|
||||
var penalties = await iqAllPenalties
|
||||
.Where(_penalty => _penalty.When < query.Before)
|
||||
.OrderByDescending(_penalty => _penalty.When)
|
||||
.Take(query.Count)
|
||||
.Select(_penalty => new ReceivedPenaltyResponse()
|
||||
{
|
||||
|
@ -4,8 +4,6 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Abstractions;
|
||||
using Data.Models.Client.Stats;
|
||||
using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
|
||||
namespace IW4MAdmin.Application.Migration
|
||||
{
|
||||
|
44
Application/Misc/EventPublisher.cs
Normal file
44
Application/Misc/EventPublisher.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace IW4MAdmin.Application.Misc
|
||||
{
|
||||
public class EventPublisher : IEventPublisher
|
||||
{
|
||||
public event EventHandler<GameEvent> OnClientDisconnect;
|
||||
public event EventHandler<GameEvent> OnClientConnect;
|
||||
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public EventPublisher(ILogger<EventPublisher> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Publish(GameEvent gameEvent)
|
||||
{
|
||||
_logger.LogDebug("Handling publishing event of type {EventType}", gameEvent.Type);
|
||||
|
||||
try
|
||||
{
|
||||
if (gameEvent.Type == GameEvent.EventType.Connect)
|
||||
{
|
||||
OnClientConnect?.Invoke(this, gameEvent);
|
||||
}
|
||||
|
||||
if (gameEvent.Type == GameEvent.EventType.Disconnect)
|
||||
{
|
||||
OnClientDisconnect?.Invoke(this, gameEvent);
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not publish event of type {EventType}", gameEvent.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -19,6 +19,12 @@ namespace IW4MAdmin.Application.Misc
|
||||
/// </summary>
|
||||
public string BasePathDirectory { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// directory for local storage
|
||||
/// <remarks>fs_homepath</remarks>
|
||||
/// </summary>
|
||||
public string HomePathDirectory { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// overide game directory
|
||||
/// <remarks>plugin driven</remarks>
|
||||
|
@ -179,7 +179,8 @@ namespace IW4MAdmin.Application.Misc
|
||||
Id = s.EndPoint,
|
||||
Port = (short)s.Port,
|
||||
IPAddress = s.IP
|
||||
}).ToList()
|
||||
}).ToList(),
|
||||
WebfrontUrl = _appConfig.WebfrontUrl
|
||||
};
|
||||
|
||||
Response<ResultMessage> response = null;
|
||||
|
147
Application/Misc/ServerDataCollector.cs
Normal file
147
Application/Misc/ServerDataCollector.cs
Normal file
@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Abstractions;
|
||||
using Data.Models;
|
||||
using Data.Models.Client;
|
||||
using Data.Models.Client.Stats.Reference;
|
||||
using Data.Models.Server;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
|
||||
namespace IW4MAdmin.Application.Misc
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public class ServerDataCollector : IServerDataCollector
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IManager _manager;
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
private readonly ApplicationConfiguration _appConfig;
|
||||
private readonly IEventPublisher _eventPublisher;
|
||||
|
||||
private bool _inProgress;
|
||||
private TimeSpan _period;
|
||||
|
||||
public ServerDataCollector(ILogger<ServerDataCollector> logger, ApplicationConfiguration appConfig,
|
||||
IManager manager, IDatabaseContextFactory contextFactory, IEventPublisher eventPublisher)
|
||||
{
|
||||
_logger = logger;
|
||||
_appConfig = appConfig;
|
||||
_manager = manager;
|
||||
_contextFactory = contextFactory;
|
||||
_eventPublisher = eventPublisher;
|
||||
|
||||
_eventPublisher.OnClientConnect += SaveConnectionInfo;
|
||||
_eventPublisher.OnClientDisconnect += SaveConnectionInfo;
|
||||
}
|
||||
|
||||
~ServerDataCollector()
|
||||
{
|
||||
_eventPublisher.OnClientConnect -= SaveConnectionInfo;
|
||||
_eventPublisher.OnClientDisconnect -= SaveConnectionInfo;
|
||||
}
|
||||
|
||||
public async Task BeginCollectionAsync(TimeSpan? period = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_inProgress)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(ServerDataCollector)} is already collecting data");
|
||||
}
|
||||
|
||||
_logger.LogDebug("Initializing data collection with {Name}", nameof(ServerDataCollector));
|
||||
_inProgress = true;
|
||||
_period = period ?? (Utilities.IsDevelopment
|
||||
? TimeSpan.FromMinutes(1)
|
||||
: _appConfig.ServerDataCollectionInterval);
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(_period, cancellationToken);
|
||||
_logger.LogDebug("{Name} is collecting server data", nameof(ServerDataCollector));
|
||||
|
||||
var data = await BuildCollectionData(cancellationToken);
|
||||
await SaveData(data, cancellationToken);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
_logger.LogInformation("Shutdown requested for {Name}", nameof(ServerDataCollector));
|
||||
return;
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Unexpected error encountered collecting server data for {Name}",
|
||||
nameof(ServerDataCollector));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<EFServerSnapshot>> BuildCollectionData(CancellationToken token)
|
||||
{
|
||||
var data = await Task.WhenAll(_manager.GetServers()
|
||||
.Select(async server => new EFServerSnapshot
|
||||
{
|
||||
CapturedAt = DateTime.UtcNow,
|
||||
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
|
||||
}));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private async Task<int> GetOrCreateMap(string mapName, Reference.Game game, CancellationToken token)
|
||||
{
|
||||
await using var context = _contextFactory.CreateContext();
|
||||
var existingMap =
|
||||
await context.Maps.FirstOrDefaultAsync(map => map.Name == mapName && map.Game == game, token);
|
||||
|
||||
if (existingMap != null)
|
||||
{
|
||||
return existingMap.MapId;
|
||||
}
|
||||
|
||||
var newMap = new EFMap
|
||||
{
|
||||
Name = mapName,
|
||||
Game = game
|
||||
};
|
||||
|
||||
context.Maps.Add(newMap);
|
||||
await context.SaveChangesAsync(token);
|
||||
|
||||
return newMap.MapId;
|
||||
}
|
||||
|
||||
private async Task SaveData(IEnumerable<EFServerSnapshot> snapshots, CancellationToken token)
|
||||
{
|
||||
await using var context = _contextFactory.CreateContext();
|
||||
context.ServerSnapshots.AddRange(snapshots);
|
||||
await context.SaveChangesAsync(token);
|
||||
}
|
||||
|
||||
private void SaveConnectionInfo(object sender, GameEvent gameEvent)
|
||||
{
|
||||
using var context = _contextFactory.CreateContext(enableTracking: false);
|
||||
context.ConnectionHistory.Add(new EFClientConnectionHistory
|
||||
{
|
||||
ClientId = gameEvent.Origin.ClientId,
|
||||
ServerId = gameEvent.Owner.GetIdForServer().Result,
|
||||
ConnectionType = gameEvent.Type == GameEvent.EventType.Connect
|
||||
? Reference.ConnectionType.Connect
|
||||
: Reference.ConnectionType.Disconnect
|
||||
});
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
161
Application/Misc/ServerDataViewer.cs
Normal file
161
Application/Misc/ServerDataViewer.cs
Normal file
@ -0,0 +1,161 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Abstractions;
|
||||
using Data.Models.Client;
|
||||
using Data.Models.Server;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace IW4MAdmin.Application.Misc
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public class ServerDataViewer : IServerDataViewer
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IDataValueCache<EFServerSnapshot, (int?, DateTime?)> _snapshotCache;
|
||||
private readonly IDataValueCache<EFClient, (int, int)> _serverStatsCache;
|
||||
private readonly IDataValueCache<EFServerSnapshot, List<ClientHistoryInfo>> _clientHistoryCache;
|
||||
|
||||
private readonly TimeSpan? _cacheTimeSpan =
|
||||
Utilities.IsDevelopment ? TimeSpan.FromSeconds(1) : (TimeSpan?) TimeSpan.FromMinutes(1);
|
||||
|
||||
public ServerDataViewer(ILogger<ServerDataViewer> logger, IDataValueCache<EFServerSnapshot, (int?, DateTime?)> snapshotCache,
|
||||
IDataValueCache<EFClient, (int, int)> serverStatsCache,
|
||||
IDataValueCache<EFServerSnapshot, List<ClientHistoryInfo>> clientHistoryCache)
|
||||
{
|
||||
_logger = logger;
|
||||
_snapshotCache = snapshotCache;
|
||||
_serverStatsCache = serverStatsCache;
|
||||
_clientHistoryCache = clientHistoryCache;
|
||||
}
|
||||
|
||||
public async Task<(int?, DateTime?)> MaxConcurrentClientsAsync(long? serverId = null, TimeSpan? overPeriod = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
_snapshotCache.SetCacheItem(async (snapshots, cancellationToken) =>
|
||||
{
|
||||
var oldestEntry = overPeriod.HasValue
|
||||
? DateTime.UtcNow - overPeriod.Value
|
||||
: DateTime.UtcNow.AddDays(-1);
|
||||
|
||||
int? maxClients;
|
||||
DateTime? maxClientsTime;
|
||||
|
||||
if (serverId != null)
|
||||
{
|
||||
var clients = await snapshots.Where(snapshot => snapshot.ServerId == serverId)
|
||||
.Where(snapshot => snapshot.CapturedAt >= oldestEntry)
|
||||
.OrderByDescending(snapshot => snapshot.ClientCount)
|
||||
.Select(snapshot => new
|
||||
{
|
||||
snapshot.ClientCount,
|
||||
snapshot.CapturedAt
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
maxClients = clients?.ClientCount;
|
||||
maxClientsTime = clients?.CapturedAt;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var clients = await snapshots.Where(snapshot => snapshot.CapturedAt >= oldestEntry)
|
||||
.GroupBy(snapshot => snapshot.PeriodBlock)
|
||||
.Select(grp => new
|
||||
{
|
||||
ClientCount = grp.Sum(snapshot => (int?) snapshot.ClientCount),
|
||||
Time = grp.Max(snapshot => (DateTime?) snapshot.CapturedAt)
|
||||
})
|
||||
.OrderByDescending(snapshot => snapshot.ClientCount)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
maxClients = clients?.ClientCount;
|
||||
maxClientsTime = clients?.Time;
|
||||
}
|
||||
|
||||
_logger.LogDebug("Max concurrent clients since {Start} is {Clients}", oldestEntry, maxClients);
|
||||
|
||||
return (maxClients, maxClientsTime);
|
||||
}, nameof(MaxConcurrentClientsAsync), _cacheTimeSpan);
|
||||
|
||||
try
|
||||
{
|
||||
return await _snapshotCache.GetCacheItem(nameof(MaxConcurrentClientsAsync), token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not retrieve data for {Name}", nameof(MaxConcurrentClientsAsync));
|
||||
return (null, null);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(int, int)> ClientCountsAsync(TimeSpan? overPeriod = null, CancellationToken token = default)
|
||||
{
|
||||
_serverStatsCache.SetCacheItem(async (set, cancellationToken) =>
|
||||
{
|
||||
var count = await set.CountAsync(cancellationToken);
|
||||
var startOfPeriod =
|
||||
DateTime.UtcNow.AddHours(-overPeriod?.TotalHours ?? -24);
|
||||
var recentCount = await set.CountAsync(client => client.LastConnection >= startOfPeriod,
|
||||
cancellationToken);
|
||||
|
||||
return (count, recentCount);
|
||||
}, nameof(_serverStatsCache), _cacheTimeSpan);
|
||||
|
||||
try
|
||||
{
|
||||
return await _serverStatsCache.GetCacheItem(nameof(_serverStatsCache), token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not retrieve data for {Name}", nameof(ClientCountsAsync));
|
||||
return (0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ClientHistoryInfo>> ClientHistoryAsync(TimeSpan? overPeriod = null, CancellationToken token = default)
|
||||
{
|
||||
_clientHistoryCache.SetCacheItem(async (set, cancellationToken) =>
|
||||
{
|
||||
var oldestEntry = overPeriod.HasValue
|
||||
? DateTime.UtcNow - overPeriod.Value
|
||||
: DateTime.UtcNow.AddHours(-12);
|
||||
|
||||
var history = await set.Where(snapshot => snapshot.CapturedAt >= oldestEntry)
|
||||
.Select(snapshot =>
|
||||
new
|
||||
{
|
||||
snapshot.ServerId,
|
||||
snapshot.CapturedAt,
|
||||
snapshot.ClientCount
|
||||
})
|
||||
.OrderBy(snapshot => snapshot.CapturedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
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()
|
||||
}).ToList();
|
||||
}, nameof(_clientHistoryCache), TimeSpan.MaxValue);
|
||||
|
||||
try
|
||||
{
|
||||
return await _clientHistoryCache.GetCacheItem(nameof(_clientHistoryCache), token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not retrieve data for {Name}", nameof(ClientHistoryAsync));
|
||||
return Enumerable.Empty<ClientHistoryInfo>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
Data/Abstractions/IAuditFields.cs
Normal file
10
Data/Abstractions/IAuditFields.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace Data.Abstractions
|
||||
{
|
||||
public class IAuditFields
|
||||
{
|
||||
DateTime CreatedDateTime { get; set; }
|
||||
DateTime? UpdatedDateTime { get; set; }
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@ -6,7 +7,7 @@ namespace Data.Abstractions
|
||||
{
|
||||
public interface IDataValueCache<T, V> where T : class
|
||||
{
|
||||
void SetCacheItem(Func<DbSet<T>, Task<V>> itemGetter, string keyName, TimeSpan? expirationTime = null);
|
||||
Task<V> GetCacheItem(string keyName);
|
||||
void SetCacheItem(Func<DbSet<T>, CancellationToken, Task<V>> itemGetter, string keyName, TimeSpan? expirationTime = null);
|
||||
Task<V> GetCacheItem(string keyName, CancellationToken token = default);
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ using Data.Models;
|
||||
using Data.Models.Client;
|
||||
using Data.Models.Client.Stats;
|
||||
using Data.Models.Client.Stats.Reference;
|
||||
using Data.Models.Misc;
|
||||
using Data.Models.Server;
|
||||
|
||||
namespace Data.Context
|
||||
@ -35,6 +36,14 @@ namespace Data.Context
|
||||
public DbSet<EFWeapon> Weapons { get; set; }
|
||||
public DbSet<EFWeaponAttachment> WeaponAttachments { get; set; }
|
||||
public DbSet<EFMap> Maps { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region MISC
|
||||
|
||||
public DbSet<EFInboxMessage> InboxMessages { get; set; }
|
||||
public DbSet<EFServerSnapshot> ServerSnapshots { get;set; }
|
||||
public DbSet<EFClientConnectionHistory> ConnectionHistory { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@ -121,11 +130,15 @@ namespace Data.Context
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<EFClientConnectionHistory>(ent => ent.HasIndex(history => history.CreatedDateTime));
|
||||
|
||||
// force full name for database conversion
|
||||
modelBuilder.Entity<EFClient>().ToTable("EFClients");
|
||||
modelBuilder.Entity<EFAlias>().ToTable("EFAlias");
|
||||
modelBuilder.Entity<EFAliasLink>().ToTable("EFAliasLinks");
|
||||
modelBuilder.Entity<EFPenalty>().ToTable("EFPenalties");
|
||||
modelBuilder.Entity<EFServerSnapshot>().ToTable(nameof(EFServerSnapshot));
|
||||
modelBuilder.Entity<EFClientConnectionHistory>().ToTable(nameof(EFClientConnectionHistory));
|
||||
|
||||
Models.Configuration.StatsModelConfiguration.Configure(modelBuilder);
|
||||
|
||||
|
@ -8,62 +8,9 @@
|
||||
<PackageId>RaidMax.IW4MAdmin.Data</PackageId>
|
||||
<Title>RaidMax.IW4MAdmin.Data</Title>
|
||||
<Authors />
|
||||
<PackageVersion>1.0.1</PackageVersion>
|
||||
<PackageVersion>1.0.7</PackageVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Migrations\MySql\20210210221342_AddAdditionalClientStats.cs" />
|
||||
<Compile Remove="Migrations\MySql\20210210221342_AddAdditionalClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Postgresql\20210224014503_AddAdditionalClientStats.cs" />
|
||||
<Compile Remove="Migrations\Postgresql\20210224014503_AddAdditionalClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Postgresql\20210224030227_AddAdditionalClientStats.cs" />
|
||||
<Compile Remove="Migrations\Postgresql\20210224030227_AddAdditionalClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Postgresql\20210224031245_AddAdditionalClientStats.cs" />
|
||||
<Compile Remove="Migrations\Postgresql\20210224031245_AddAdditionalClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Postgresql\20210227041237_AddPerformancePercentileToClientStats.cs" />
|
||||
<Compile Remove="Migrations\Postgresql\20210227041237_AddPerformancePercentileToClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Postgresql\20210227161333_AddPerformancePercentileToClientStats.cs" />
|
||||
<Compile Remove="Migrations\Postgresql\20210227161333_AddPerformancePercentileToClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Postgresql\20210307163752_AddRankingHistory.cs" />
|
||||
<Compile Remove="Migrations\Postgresql\20210307163752_AddRankingHistory.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210209205243_AddAdditionaClientStats.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210209205243_AddAdditionaClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210209205948_AddAdditionaClientStats.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210209205948_AddAdditionaClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210209211745_AddAdditionaClientStats.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210209211745_AddAdditionaClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210209212725_AddAdditionaClientStats.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210209212725_AddAdditionaClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210210020314_AddAdditionaClientStats.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210210020314_AddAdditionaClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210210140835_AddAdditionaClientStats.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210210140835_AddAdditionaClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210210154738_AddAdditionaClientStats.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210210154738_AddAdditionaClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210210163803_AddAdditionaClientStats.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210210163803_AddAdditionaClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210210193852_AddAdditionaClientStats.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210210193852_AddAdditionaClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210211033835_AddAdditionalClientStats.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210211033835_AddAdditionalClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210219013429_AddAdditionalClientStats.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210219013429_AddAdditionalClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210220171950_AddAdditionalClientStats.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210220171950_AddAdditionalClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210223163022_AddAdditionalClientStats.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210223163022_AddAdditionalClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210226215929_AddPerformancePercentileToClientStats.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210226215929_AddPerformancePercentileToClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210227160800_AddPerformancePercentileToClientStats.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210227160800_AddPerformancePercentileToClientStats.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210305033616_AddRankingHistory.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210305033616_AddRankingHistory.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210305033846_AddRankingHistory.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210305033846_AddRankingHistory.Designer.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210306223712_AddRankingHistory.cs" />
|
||||
<Compile Remove="Migrations\Sqlite\20210306223712_AddRankingHistory.Designer.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.10">
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Abstractions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -19,36 +20,39 @@ namespace Data.Helpers
|
||||
public string Key { get; set; }
|
||||
public DateTime LastRetrieval { get; set; }
|
||||
public TimeSpan ExpirationTime { get; set; }
|
||||
public Func<DbSet<T>, Task<V>> Getter { get; set; }
|
||||
public Func<DbSet<T>, CancellationToken, Task<V>> Getter { get; set; }
|
||||
public V Value { get; set; }
|
||||
public bool IsExpired => (DateTime.Now - LastRetrieval.Add(ExpirationTime)).TotalSeconds > 0;
|
||||
|
||||
public bool IsExpired => ExpirationTime != TimeSpan.MaxValue &&
|
||||
(DateTime.Now - LastRetrieval.Add(ExpirationTime)).TotalSeconds > 0;
|
||||
}
|
||||
|
||||
|
||||
public DataValueCache(ILogger<DataValueCache<T, V>> logger, IDatabaseContextFactory contextFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_contextFactory = contextFactory;
|
||||
}
|
||||
|
||||
public void SetCacheItem(Func<DbSet<T>, Task<V>> getter, string key, TimeSpan? expirationTime = null)
|
||||
|
||||
public void SetCacheItem(Func<DbSet<T>, CancellationToken, Task<V>> getter, string key,
|
||||
TimeSpan? expirationTime = null)
|
||||
{
|
||||
if (_cacheStates.ContainsKey(key))
|
||||
{
|
||||
_logger.LogDebug("Cache key {key} is already added", key);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var state = new CacheState()
|
||||
{
|
||||
Key = key,
|
||||
Getter = getter,
|
||||
ExpirationTime = expirationTime ?? TimeSpan.FromMinutes(DefaultExpireMinutes)
|
||||
};
|
||||
|
||||
|
||||
_cacheStates.Add(key, state);
|
||||
}
|
||||
|
||||
public async Task<V> GetCacheItem(string keyName)
|
||||
|
||||
public async Task<V> GetCacheItem(string keyName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!_cacheStates.ContainsKey(keyName))
|
||||
{
|
||||
@ -57,21 +61,21 @@ namespace Data.Helpers
|
||||
|
||||
var state = _cacheStates[keyName];
|
||||
|
||||
if (state.IsExpired)
|
||||
if (state.IsExpired || state.Value == null)
|
||||
{
|
||||
await RunCacheUpdate(state);
|
||||
await RunCacheUpdate(state, cancellationToken);
|
||||
}
|
||||
|
||||
return state.Value;
|
||||
}
|
||||
|
||||
private async Task RunCacheUpdate(CacheState state)
|
||||
private async Task RunCacheUpdate(CacheState state, CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var context = _contextFactory.CreateContext(false);
|
||||
var set = context.Set<T>();
|
||||
var value = await state.Getter(set);
|
||||
var value = await state.Getter(set, token);
|
||||
state.Value = value;
|
||||
state.LastRetrieval = DateTime.Now;
|
||||
}
|
||||
|
1283
Data/Migrations/MySql/20210628153649_AddWeaponReferenceToEFClientKill.Designer.cs
generated
Normal file
1283
Data/Migrations/MySql/20210628153649_AddWeaponReferenceToEFClientKill.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
public partial class AddWeaponReferenceToEFClientKill : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "WeaponReference",
|
||||
table: "EFClientKills",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "WeaponReference",
|
||||
table: "EFClientKills");
|
||||
}
|
||||
}
|
||||
}
|
1295
Data/Migrations/MySql/20210628160144_AddWeaponReferenceAndServerIdToEFACSnapshot.Designer.cs
generated
Normal file
1295
Data/Migrations/MySql/20210628160144_AddWeaponReferenceAndServerIdToEFACSnapshot.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,52 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
public partial class AddWeaponReferenceAndServerIdToEFACSnapshot : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<long>(
|
||||
name: "ServerId",
|
||||
table: "EFACSnapshot",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "WeaponReference",
|
||||
table: "EFACSnapshot",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFACSnapshot_ServerId",
|
||||
table: "EFACSnapshot",
|
||||
column: "ServerId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_EFACSnapshot_EFServers_ServerId",
|
||||
table: "EFACSnapshot",
|
||||
column: "ServerId",
|
||||
principalTable: "EFServers",
|
||||
principalColumn: "ServerId",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_EFACSnapshot_EFServers_ServerId",
|
||||
table: "EFACSnapshot");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_EFACSnapshot_ServerId",
|
||||
table: "EFACSnapshot");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ServerId",
|
||||
table: "EFACSnapshot");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "WeaponReference",
|
||||
table: "EFACSnapshot");
|
||||
}
|
||||
}
|
||||
}
|
1298
Data/Migrations/MySql/20210629022028_AddHitLocationReferenceToEFACSnapshot.Designer.cs
generated
Normal file
1298
Data/Migrations/MySql/20210629022028_AddHitLocationReferenceToEFACSnapshot.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
public partial class AddHitLocationReferenceToEFACSnapshot : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "HitLocationReference",
|
||||
table: "EFACSnapshot",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "HitLocationReference",
|
||||
table: "EFACSnapshot");
|
||||
}
|
||||
}
|
||||
}
|
1355
Data/Migrations/MySql/20210709010749_AddEFInboxMessage.Designer.cs
generated
Normal file
1355
Data/Migrations/MySql/20210709010749_AddEFInboxMessage.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
70
Data/Migrations/MySql/20210709010749_AddEFInboxMessage.cs
Normal file
70
Data/Migrations/MySql/20210709010749_AddEFInboxMessage.cs
Normal file
@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
public partial class AddEFInboxMessage : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "InboxMessages",
|
||||
columns: table => new
|
||||
{
|
||||
InboxMessageId = table.Column<int>(nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
CreatedDateTime = table.Column<DateTime>(nullable: false),
|
||||
UpdatedDateTime = table.Column<DateTime>(nullable: true),
|
||||
SourceClientId = table.Column<int>(nullable: false),
|
||||
DestinationClientId = table.Column<int>(nullable: false),
|
||||
ServerId = table.Column<long>(nullable: true),
|
||||
Message = table.Column<string>(nullable: true),
|
||||
IsDelivered = table.Column<bool>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_InboxMessages", x => x.InboxMessageId);
|
||||
table.ForeignKey(
|
||||
name: "FK_InboxMessages_EFClients_DestinationClientId",
|
||||
column: x => x.DestinationClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_InboxMessages_EFServers_ServerId",
|
||||
column: x => x.ServerId,
|
||||
principalTable: "EFServers",
|
||||
principalColumn: "ServerId",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_InboxMessages_EFClients_SourceClientId",
|
||||
column: x => x.SourceClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_InboxMessages_DestinationClientId",
|
||||
table: "InboxMessages",
|
||||
column: "DestinationClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_InboxMessages_ServerId",
|
||||
table: "InboxMessages",
|
||||
column: "ServerId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_InboxMessages_SourceClientId",
|
||||
table: "InboxMessages",
|
||||
column: "SourceClientId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "InboxMessages");
|
||||
}
|
||||
}
|
||||
}
|
1403
Data/Migrations/MySql/20210826222452_AddEFServerSnapshot.Designer.cs
generated
Normal file
1403
Data/Migrations/MySql/20210826222452_AddEFServerSnapshot.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
58
Data/Migrations/MySql/20210826222452_AddEFServerSnapshot.cs
Normal file
58
Data/Migrations/MySql/20210826222452_AddEFServerSnapshot.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
public partial class AddEFServerSnapshot : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFServerSnapshot",
|
||||
columns: table => new
|
||||
{
|
||||
ServerSnapshotId = table.Column<long>(nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Active = table.Column<bool>(nullable: false),
|
||||
CapturedAt = table.Column<DateTime>(nullable: false),
|
||||
PeriodBlock = table.Column<int>(nullable: false),
|
||||
ServerId = table.Column<long>(nullable: false),
|
||||
MapId = table.Column<int>(nullable: false),
|
||||
ClientCount = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFServerSnapshot", x => x.ServerSnapshotId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFServerSnapshot_EFMaps_MapId",
|
||||
column: x => x.MapId,
|
||||
principalTable: "EFMaps",
|
||||
principalColumn: "MapId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFServerSnapshot_EFServers_ServerId",
|
||||
column: x => x.ServerId,
|
||||
principalTable: "EFServers",
|
||||
principalColumn: "ServerId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFServerSnapshot_MapId",
|
||||
table: "EFServerSnapshot",
|
||||
column: "MapId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFServerSnapshot_ServerId",
|
||||
table: "EFServerSnapshot",
|
||||
column: "ServerId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFServerSnapshot");
|
||||
}
|
||||
}
|
||||
}
|
1450
Data/Migrations/MySql/20210831231006_AddEFClientConnectionHistory.Designer.cs
generated
Normal file
1450
Data/Migrations/MySql/20210831231006_AddEFClientConnectionHistory.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Data.Migrations.MySql
|
||||
{
|
||||
public partial class AddEFClientConnectionHistory : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFClientConnectionHistory",
|
||||
columns: table => new
|
||||
{
|
||||
ClientConnectionId = table.Column<long>(nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
CreatedDateTime = table.Column<DateTime>(nullable: false),
|
||||
UpdatedDateTime = table.Column<DateTime>(nullable: true),
|
||||
ClientId = table.Column<int>(nullable: false),
|
||||
ServerId = table.Column<long>(nullable: false),
|
||||
ConnectionType = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFClientConnectionHistory", x => x.ClientConnectionId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFClientConnectionHistory_EFClients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFClientConnectionHistory_EFServers_ServerId",
|
||||
column: x => x.ServerId,
|
||||
principalTable: "EFServers",
|
||||
principalColumn: "ServerId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFClientConnectionHistory_ClientId",
|
||||
table: "EFClientConnectionHistory",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFClientConnectionHistory_CreatedDateTime",
|
||||
table: "EFClientConnectionHistory",
|
||||
column: "CreatedDateTime");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFClientConnectionHistory_ServerId",
|
||||
table: "EFClientConnectionHistory",
|
||||
column: "ServerId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFClientConnectionHistory");
|
||||
}
|
||||
}
|
||||
}
|
@ -95,6 +95,38 @@ namespace Data.Migrations.MySql
|
||||
b.ToTable("EFClients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientConnectionHistory", b =>
|
||||
{
|
||||
b.Property<long>("ClientConnectionId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("ConnectionType")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedDateTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime?>("UpdatedDateTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("ClientConnectionId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("CreatedDateTime");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFClientConnectionHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientKill", b =>
|
||||
{
|
||||
b.Property<long>("KillId")
|
||||
@ -146,6 +178,9 @@ namespace Data.Migrations.MySql
|
||||
b.Property<int>("Weapon")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("WeaponReference")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
@ -237,6 +272,9 @@ namespace Data.Migrations.MySql
|
||||
b.Property<int>("HitLocation")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("HitLocationReference")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<int>("HitOriginId")
|
||||
.HasColumnType("int");
|
||||
|
||||
@ -255,6 +293,9 @@ namespace Data.Migrations.MySql
|
||||
b.Property<double>("RecoilOffset")
|
||||
.HasColumnType("double");
|
||||
|
||||
b.Property<long?>("ServerId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<double>("SessionAngleOffset")
|
||||
.HasColumnType("double");
|
||||
|
||||
@ -279,6 +320,9 @@ namespace Data.Migrations.MySql
|
||||
b.Property<int>("WeaponId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("WeaponReference")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
@ -294,6 +338,8 @@ namespace Data.Migrations.MySql
|
||||
|
||||
b.HasIndex("LastStrainAngleId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFACSnapshot");
|
||||
});
|
||||
|
||||
@ -921,6 +967,44 @@ namespace Data.Migrations.MySql
|
||||
b.ToTable("EFPenalties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Misc.EFInboxMessage", b =>
|
||||
{
|
||||
b.Property<int>("InboxMessageId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedDateTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("DestinationClientId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("IsDelivered")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<long?>("ServerId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("SourceClientId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedDateTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("InboxMessageId");
|
||||
|
||||
b.HasIndex("DestinationClientId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("SourceClientId");
|
||||
|
||||
b.ToTable("InboxMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServer", b =>
|
||||
{
|
||||
b.Property<long>("ServerId")
|
||||
@ -949,6 +1033,39 @@ namespace Data.Migrations.MySql
|
||||
b.ToTable("EFServers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerSnapshot", b =>
|
||||
{
|
||||
b.Property<long>("ServerSnapshotId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<DateTime>("CapturedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("ClientCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("MapId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("PeriodBlock")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("ServerSnapshotId");
|
||||
|
||||
b.HasIndex("MapId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFServerSnapshot");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b =>
|
||||
{
|
||||
b.Property<int>("StatisticId")
|
||||
@ -1024,6 +1141,21 @@ namespace Data.Migrations.MySql
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientConnectionHistory", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientKill", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", "Attacker")
|
||||
@ -1103,6 +1235,10 @@ namespace Data.Migrations.MySql
|
||||
.HasForeignKey("LastStrainAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b =>
|
||||
@ -1264,6 +1400,40 @@ namespace Data.Migrations.MySql
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Misc.EFInboxMessage", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", "DestinationClient")
|
||||
.WithMany()
|
||||
.HasForeignKey("DestinationClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
|
||||
b.HasOne("Data.Models.Client.EFClient", "SourceClient")
|
||||
.WithMany()
|
||||
.HasForeignKey("SourceClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerSnapshot", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.Stats.Reference.EFMap", "Map")
|
||||
.WithMany()
|
||||
.HasForeignKey("MapId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
|
1308
Data/Migrations/Postgresql/20210628153932_AddWeaponReferenceToEFClientKill.Designer.cs
generated
Normal file
1308
Data/Migrations/Postgresql/20210628153932_AddWeaponReferenceToEFClientKill.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
public partial class AddWeaponReferenceToEFClientKill : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "WeaponReference",
|
||||
table: "EFClientKills",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "WeaponReference",
|
||||
table: "EFClientKills");
|
||||
}
|
||||
}
|
||||
}
|
1320
Data/Migrations/Postgresql/20210628160226_AddWeaponReferenceAndServerIdToEFACSnapshot.Designer.cs
generated
Normal file
1320
Data/Migrations/Postgresql/20210628160226_AddWeaponReferenceAndServerIdToEFACSnapshot.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,52 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
public partial class AddWeaponReferenceAndServerIdToEFACSnapshot : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<long>(
|
||||
name: "ServerId",
|
||||
table: "EFACSnapshot",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "WeaponReference",
|
||||
table: "EFACSnapshot",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFACSnapshot_ServerId",
|
||||
table: "EFACSnapshot",
|
||||
column: "ServerId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_EFACSnapshot_EFServers_ServerId",
|
||||
table: "EFACSnapshot",
|
||||
column: "ServerId",
|
||||
principalTable: "EFServers",
|
||||
principalColumn: "ServerId",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_EFACSnapshot_EFServers_ServerId",
|
||||
table: "EFACSnapshot");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_EFACSnapshot_ServerId",
|
||||
table: "EFACSnapshot");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ServerId",
|
||||
table: "EFACSnapshot");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "WeaponReference",
|
||||
table: "EFACSnapshot");
|
||||
}
|
||||
}
|
||||
}
|
1323
Data/Migrations/Postgresql/20210629022117_AddHitLocationReferenceToEFACSnapshot.Designer.cs
generated
Normal file
1323
Data/Migrations/Postgresql/20210629022117_AddHitLocationReferenceToEFACSnapshot.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
public partial class AddHitLocationReferenceToEFACSnapshot : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "HitLocationReference",
|
||||
table: "EFACSnapshot",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "HitLocationReference",
|
||||
table: "EFACSnapshot");
|
||||
}
|
||||
}
|
||||
}
|
1381
Data/Migrations/Postgresql/20210709010920_AddEFInboxMessage.Designer.cs
generated
Normal file
1381
Data/Migrations/Postgresql/20210709010920_AddEFInboxMessage.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
public partial class AddEFInboxMessage : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "InboxMessages",
|
||||
columns: table => new
|
||||
{
|
||||
InboxMessageId = table.Column<int>(nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
|
||||
CreatedDateTime = table.Column<DateTime>(nullable: false),
|
||||
UpdatedDateTime = table.Column<DateTime>(nullable: true),
|
||||
SourceClientId = table.Column<int>(nullable: false),
|
||||
DestinationClientId = table.Column<int>(nullable: false),
|
||||
ServerId = table.Column<long>(nullable: true),
|
||||
Message = table.Column<string>(nullable: true),
|
||||
IsDelivered = table.Column<bool>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_InboxMessages", x => x.InboxMessageId);
|
||||
table.ForeignKey(
|
||||
name: "FK_InboxMessages_EFClients_DestinationClientId",
|
||||
column: x => x.DestinationClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_InboxMessages_EFServers_ServerId",
|
||||
column: x => x.ServerId,
|
||||
principalTable: "EFServers",
|
||||
principalColumn: "ServerId",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_InboxMessages_EFClients_SourceClientId",
|
||||
column: x => x.SourceClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_InboxMessages_DestinationClientId",
|
||||
table: "InboxMessages",
|
||||
column: "DestinationClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_InboxMessages_ServerId",
|
||||
table: "InboxMessages",
|
||||
column: "ServerId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_InboxMessages_SourceClientId",
|
||||
table: "InboxMessages",
|
||||
column: "SourceClientId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "InboxMessages");
|
||||
}
|
||||
}
|
||||
}
|
1430
Data/Migrations/Postgresql/20210826222523_AddEFServerSnapshot.Designer.cs
generated
Normal file
1430
Data/Migrations/Postgresql/20210826222523_AddEFServerSnapshot.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
public partial class AddEFServerSnapshot : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFServerSnapshot",
|
||||
columns: table => new
|
||||
{
|
||||
ServerSnapshotId = table.Column<long>(nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
|
||||
Active = table.Column<bool>(nullable: false),
|
||||
CapturedAt = table.Column<DateTime>(nullable: false),
|
||||
PeriodBlock = table.Column<int>(nullable: false),
|
||||
ServerId = table.Column<long>(nullable: false),
|
||||
MapId = table.Column<int>(nullable: false),
|
||||
ClientCount = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFServerSnapshot", x => x.ServerSnapshotId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFServerSnapshot_EFMaps_MapId",
|
||||
column: x => x.MapId,
|
||||
principalTable: "EFMaps",
|
||||
principalColumn: "MapId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFServerSnapshot_EFServers_ServerId",
|
||||
column: x => x.ServerId,
|
||||
principalTable: "EFServers",
|
||||
principalColumn: "ServerId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFServerSnapshot_MapId",
|
||||
table: "EFServerSnapshot",
|
||||
column: "MapId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFServerSnapshot_ServerId",
|
||||
table: "EFServerSnapshot",
|
||||
column: "ServerId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFServerSnapshot");
|
||||
}
|
||||
}
|
||||
}
|
1478
Data/Migrations/Postgresql/20210831231031_AddEFClientConnectionHistory.Designer.cs
generated
Normal file
1478
Data/Migrations/Postgresql/20210831231031_AddEFClientConnectionHistory.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
public partial class AddEFClientConnectionHistory : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFClientConnectionHistory",
|
||||
columns: table => new
|
||||
{
|
||||
ClientConnectionId = table.Column<long>(nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
|
||||
CreatedDateTime = table.Column<DateTime>(nullable: false),
|
||||
UpdatedDateTime = table.Column<DateTime>(nullable: true),
|
||||
ClientId = table.Column<int>(nullable: false),
|
||||
ServerId = table.Column<long>(nullable: false),
|
||||
ConnectionType = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFClientConnectionHistory", x => x.ClientConnectionId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFClientConnectionHistory_EFClients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFClientConnectionHistory_EFServers_ServerId",
|
||||
column: x => x.ServerId,
|
||||
principalTable: "EFServers",
|
||||
principalColumn: "ServerId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFClientConnectionHistory_ClientId",
|
||||
table: "EFClientConnectionHistory",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFClientConnectionHistory_CreatedDateTime",
|
||||
table: "EFClientConnectionHistory",
|
||||
column: "CreatedDateTime");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFClientConnectionHistory_ServerId",
|
||||
table: "EFClientConnectionHistory",
|
||||
column: "ServerId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFClientConnectionHistory");
|
||||
}
|
||||
}
|
||||
}
|
@ -99,6 +99,39 @@ namespace Data.Migrations.Postgresql
|
||||
b.ToTable("EFClients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientConnectionHistory", b =>
|
||||
{
|
||||
b.Property<long>("ClientConnectionId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("ConnectionType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("CreatedDateTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime?>("UpdatedDateTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.HasKey("ClientConnectionId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("CreatedDateTime");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFClientConnectionHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientKill", b =>
|
||||
{
|
||||
b.Property<long>("KillId")
|
||||
@ -151,6 +184,9 @@ namespace Data.Migrations.Postgresql
|
||||
b.Property<int>("Weapon")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("WeaponReference")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
@ -244,6 +280,9 @@ namespace Data.Migrations.Postgresql
|
||||
b.Property<int>("HitLocation")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("HitLocationReference")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("HitOriginId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
@ -262,6 +301,9 @@ namespace Data.Migrations.Postgresql
|
||||
b.Property<double>("RecoilOffset")
|
||||
.HasColumnType("double precision");
|
||||
|
||||
b.Property<long?>("ServerId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<double>("SessionAngleOffset")
|
||||
.HasColumnType("double precision");
|
||||
|
||||
@ -286,6 +328,9 @@ namespace Data.Migrations.Postgresql
|
||||
b.Property<int>("WeaponId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("WeaponReference")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
@ -301,6 +346,8 @@ namespace Data.Migrations.Postgresql
|
||||
|
||||
b.HasIndex("LastStrainAngleId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFACSnapshot");
|
||||
});
|
||||
|
||||
@ -944,6 +991,45 @@ namespace Data.Migrations.Postgresql
|
||||
b.ToTable("EFPenalties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Misc.EFInboxMessage", b =>
|
||||
{
|
||||
b.Property<int>("InboxMessageId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
|
||||
b.Property<DateTime>("CreatedDateTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<int>("DestinationClientId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<bool>("IsDelivered")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long?>("ServerId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("SourceClientId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime?>("UpdatedDateTime")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.HasKey("InboxMessageId");
|
||||
|
||||
b.HasIndex("DestinationClientId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("SourceClientId");
|
||||
|
||||
b.ToTable("InboxMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServer", b =>
|
||||
{
|
||||
b.Property<long>("ServerId")
|
||||
@ -972,6 +1058,40 @@ namespace Data.Migrations.Postgresql
|
||||
b.ToTable("EFServers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerSnapshot", b =>
|
||||
{
|
||||
b.Property<long>("ServerSnapshotId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn);
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTime>("CapturedAt")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<int>("ClientCount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("MapId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("PeriodBlock")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("ServerSnapshotId");
|
||||
|
||||
b.HasIndex("MapId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFServerSnapshot");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b =>
|
||||
{
|
||||
b.Property<int>("StatisticId")
|
||||
@ -1049,6 +1169,21 @@ namespace Data.Migrations.Postgresql
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientConnectionHistory", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientKill", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", "Attacker")
|
||||
@ -1128,6 +1263,10 @@ namespace Data.Migrations.Postgresql
|
||||
.HasForeignKey("LastStrainAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b =>
|
||||
@ -1289,6 +1428,40 @@ namespace Data.Migrations.Postgresql
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Misc.EFInboxMessage", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", "DestinationClient")
|
||||
.WithMany()
|
||||
.HasForeignKey("DestinationClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
|
||||
b.HasOne("Data.Models.Client.EFClient", "SourceClient")
|
||||
.WithMany()
|
||||
.HasForeignKey("SourceClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerSnapshot", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.Stats.Reference.EFMap", "Map")
|
||||
.WithMany()
|
||||
.HasForeignKey("MapId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
|
1282
Data/Migrations/Sqlite/20210628144550_AddWeaponReferenceToEFClientKill.Designer.cs
generated
Normal file
1282
Data/Migrations/Sqlite/20210628144550_AddWeaponReferenceToEFClientKill.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Data.Migrations.Sqlite
|
||||
{
|
||||
public partial class AddWeaponReferenceToEFClientKill : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "WeaponReference",
|
||||
table: "EFClientKills",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "WeaponReference",
|
||||
table: "EFClientKills");
|
||||
}
|
||||
}
|
||||
}
|
1294
Data/Migrations/Sqlite/20210628154945_AddWeaponReferenceAndServerIdToEFACSnapshot.Designer.cs
generated
Normal file
1294
Data/Migrations/Sqlite/20210628154945_AddWeaponReferenceAndServerIdToEFACSnapshot.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,162 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Data.Migrations.Sqlite
|
||||
{
|
||||
public partial class AddWeaponReferenceAndServerIdToEFACSnapshot : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.Sql(@"PRAGMA foreign_keys = 0;
|
||||
|
||||
CREATE TABLE sqlitestudio_temp_table AS SELECT *
|
||||
FROM EFACSnapshot;
|
||||
|
||||
DROP TABLE EFACSnapshot;
|
||||
|
||||
CREATE TABLE EFACSnapshot (
|
||||
Active INTEGER NOT NULL,
|
||||
TimeSinceLastEvent INTEGER NOT NULL,
|
||||
SnapshotId INTEGER NOT NULL
|
||||
CONSTRAINT PK_EFACSnapshot PRIMARY KEY AUTOINCREMENT,
|
||||
ClientId INTEGER NOT NULL,
|
||||
ServerId INTEGER CONSTRAINT FK_EFACSnapshot_EFServers_ServerId REFERENCES EFServers (ServerId) ON DELETE RESTRICT,
|
||||
[When] TEXT NOT NULL,
|
||||
CurrentSessionLength INTEGER NOT NULL,
|
||||
EloRating REAL NOT NULL,
|
||||
SessionScore INTEGER NOT NULL,
|
||||
SessionSPM REAL NOT NULL,
|
||||
Hits INTEGER NOT NULL,
|
||||
Kills INTEGER NOT NULL,
|
||||
Deaths INTEGER NOT NULL,
|
||||
CurrentStrain REAL NOT NULL,
|
||||
StrainAngleBetween REAL NOT NULL,
|
||||
SessionAngleOffset REAL NOT NULL,
|
||||
LastStrainAngleId INTEGER NOT NULL,
|
||||
HitOriginId INTEGER NOT NULL,
|
||||
HitDestinationId INTEGER NOT NULL,
|
||||
Distance REAL NOT NULL,
|
||||
CurrentViewAngleId INTEGER,
|
||||
WeaponId INTEGER NOT NULL,
|
||||
WeaponReference TEXT,
|
||||
HitLocation INTEGER NOT NULL,
|
||||
HitType INTEGER NOT NULL,
|
||||
RecoilOffset REAL NOT NULL
|
||||
DEFAULT 0.0,
|
||||
SessionAverageSnapValue REAL NOT NULL
|
||||
DEFAULT 0.0,
|
||||
SessionSnapHits INTEGER NOT NULL
|
||||
DEFAULT 0,
|
||||
CONSTRAINT FK_EFACSnapshot_EFClients_ClientId FOREIGN KEY (
|
||||
ClientId
|
||||
)
|
||||
REFERENCES EFClients (ClientId) ON DELETE CASCADE,
|
||||
CONSTRAINT FK_EFACSnapshot_Vector3_CurrentViewAngleId FOREIGN KEY (
|
||||
CurrentViewAngleId
|
||||
)
|
||||
REFERENCES Vector3 (Vector3Id) ON DELETE RESTRICT,
|
||||
CONSTRAINT FK_EFACSnapshot_Vector3_HitDestinationId FOREIGN KEY (
|
||||
HitDestinationId
|
||||
)
|
||||
REFERENCES Vector3 (Vector3Id) ON DELETE CASCADE,
|
||||
CONSTRAINT FK_EFACSnapshot_Vector3_HitOriginId FOREIGN KEY (
|
||||
HitOriginId
|
||||
)
|
||||
REFERENCES Vector3 (Vector3Id) ON DELETE CASCADE,
|
||||
CONSTRAINT FK_EFACSnapshot_Vector3_LastStrainAngleId FOREIGN KEY (
|
||||
LastStrainAngleId
|
||||
)
|
||||
REFERENCES Vector3 (Vector3Id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
INSERT INTO EFACSnapshot (
|
||||
Active,
|
||||
TimeSinceLastEvent,
|
||||
SnapshotId,
|
||||
ClientId,
|
||||
[When],
|
||||
CurrentSessionLength,
|
||||
EloRating,
|
||||
SessionScore,
|
||||
SessionSPM,
|
||||
Hits,
|
||||
Kills,
|
||||
Deaths,
|
||||
CurrentStrain,
|
||||
StrainAngleBetween,
|
||||
SessionAngleOffset,
|
||||
LastStrainAngleId,
|
||||
HitOriginId,
|
||||
HitDestinationId,
|
||||
Distance,
|
||||
CurrentViewAngleId,
|
||||
WeaponId,
|
||||
HitLocation,
|
||||
HitType,
|
||||
RecoilOffset,
|
||||
SessionAverageSnapValue,
|
||||
SessionSnapHits
|
||||
)
|
||||
SELECT Active,
|
||||
TimeSinceLastEvent,
|
||||
SnapshotId,
|
||||
ClientId,
|
||||
""When"",
|
||||
CurrentSessionLength,
|
||||
EloRating,
|
||||
SessionScore,
|
||||
SessionSPM,
|
||||
Hits,
|
||||
Kills,
|
||||
Deaths,
|
||||
CurrentStrain,
|
||||
StrainAngleBetween,
|
||||
SessionAngleOffset,
|
||||
LastStrainAngleId,
|
||||
HitOriginId,
|
||||
HitDestinationId,
|
||||
Distance,
|
||||
CurrentViewAngleId,
|
||||
WeaponId,
|
||||
HitLocation,
|
||||
HitType,
|
||||
RecoilOffset,
|
||||
SessionAverageSnapValue,
|
||||
SessionSnapHits
|
||||
FROM sqlitestudio_temp_table;
|
||||
|
||||
DROP TABLE sqlitestudio_temp_table;
|
||||
|
||||
CREATE INDEX IX_EFACSnapshot_ClientId ON EFACSnapshot (
|
||||
""ClientId""
|
||||
);
|
||||
|
||||
CREATE INDEX IX_EFACSnapshot_CurrentViewAngleId ON EFACSnapshot (
|
||||
""CurrentViewAngleId""
|
||||
);
|
||||
|
||||
CREATE INDEX IX_EFACSnapshot_HitDestinationId ON EFACSnapshot (
|
||||
""HitDestinationId""
|
||||
);
|
||||
|
||||
CREATE INDEX IX_EFACSnapshot_HitOriginId ON EFACSnapshot (
|
||||
""HitOriginId""
|
||||
);
|
||||
|
||||
CREATE INDEX IX_EFACSnapshot_LastStrainAngleId ON EFACSnapshot (
|
||||
""LastStrainAngleId""
|
||||
);
|
||||
|
||||
CREATE INDEX IX_EFACSnapshot_ServerId ON EFACSnapshot (
|
||||
""_ServerId""
|
||||
);
|
||||
|
||||
|
||||
PRAGMA foreign_keys = 1;
|
||||
");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
1297
Data/Migrations/Sqlite/20210629021801_AddHitLocationReferenceToEFACSnapshot.Designer.cs
generated
Normal file
1297
Data/Migrations/Sqlite/20210629021801_AddHitLocationReferenceToEFACSnapshot.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Data.Migrations.Sqlite
|
||||
{
|
||||
public partial class AddHitLocationReferenceToEFACSnapshot : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "HitLocationReference",
|
||||
table: "EFACSnapshot",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "HitLocationReference",
|
||||
table: "EFACSnapshot");
|
||||
}
|
||||
}
|
||||
}
|
1354
Data/Migrations/Sqlite/20210703141113_AddEFInboxMessage.Designer.cs
generated
Normal file
1354
Data/Migrations/Sqlite/20210703141113_AddEFInboxMessage.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
69
Data/Migrations/Sqlite/20210703141113_AddEFInboxMessage.cs
Normal file
69
Data/Migrations/Sqlite/20210703141113_AddEFInboxMessage.cs
Normal file
@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Data.Migrations.Sqlite
|
||||
{
|
||||
public partial class AddEFInboxMessage : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "InboxMessages",
|
||||
columns: table => new
|
||||
{
|
||||
InboxMessageId = table.Column<int>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
CreatedDateTime = table.Column<DateTime>(nullable: false),
|
||||
UpdatedDateTime = table.Column<DateTime>(nullable: true),
|
||||
SourceClientId = table.Column<int>(nullable: false),
|
||||
DestinationClientId = table.Column<int>(nullable: false),
|
||||
ServerId = table.Column<long>(nullable: true),
|
||||
Message = table.Column<string>(nullable: true),
|
||||
IsDelivered = table.Column<bool>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_InboxMessages", x => x.InboxMessageId);
|
||||
table.ForeignKey(
|
||||
name: "FK_InboxMessages_EFClients_DestinationClientId",
|
||||
column: x => x.DestinationClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_InboxMessages_EFServers_ServerId",
|
||||
column: x => x.ServerId,
|
||||
principalTable: "EFServers",
|
||||
principalColumn: "ServerId",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_InboxMessages_EFClients_SourceClientId",
|
||||
column: x => x.SourceClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_InboxMessages_DestinationClientId",
|
||||
table: "InboxMessages",
|
||||
column: "DestinationClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_InboxMessages_ServerId",
|
||||
table: "InboxMessages",
|
||||
column: "ServerId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_InboxMessages_SourceClientId",
|
||||
table: "InboxMessages",
|
||||
column: "SourceClientId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "InboxMessages");
|
||||
}
|
||||
}
|
||||
}
|
1402
Data/Migrations/Sqlite/20210826222412_AddEFServerSnapshot.Designer.cs
generated
Normal file
1402
Data/Migrations/Sqlite/20210826222412_AddEFServerSnapshot.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
57
Data/Migrations/Sqlite/20210826222412_AddEFServerSnapshot.cs
Normal file
57
Data/Migrations/Sqlite/20210826222412_AddEFServerSnapshot.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Data.Migrations.Sqlite
|
||||
{
|
||||
public partial class AddEFServerSnapshot : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFServerSnapshot",
|
||||
columns: table => new
|
||||
{
|
||||
ServerSnapshotId = table.Column<long>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Active = table.Column<bool>(nullable: false),
|
||||
CapturedAt = table.Column<DateTime>(nullable: false),
|
||||
PeriodBlock = table.Column<int>(nullable: false),
|
||||
ServerId = table.Column<long>(nullable: false),
|
||||
MapId = table.Column<int>(nullable: false),
|
||||
ClientCount = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFServerSnapshot", x => x.ServerSnapshotId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFServerSnapshot_EFMaps_MapId",
|
||||
column: x => x.MapId,
|
||||
principalTable: "EFMaps",
|
||||
principalColumn: "MapId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFServerSnapshot_EFServers_ServerId",
|
||||
column: x => x.ServerId,
|
||||
principalTable: "EFServers",
|
||||
principalColumn: "ServerId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFServerSnapshot_MapId",
|
||||
table: "EFServerSnapshot",
|
||||
column: "MapId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFServerSnapshot_ServerId",
|
||||
table: "EFServerSnapshot",
|
||||
column: "ServerId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFServerSnapshot");
|
||||
}
|
||||
}
|
||||
}
|
1449
Data/Migrations/Sqlite/20210831230904_AddEFClientConnectionHistory.Designer.cs
generated
Normal file
1449
Data/Migrations/Sqlite/20210831230904_AddEFClientConnectionHistory.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Data.Migrations.Sqlite
|
||||
{
|
||||
public partial class AddEFClientConnectionHistory : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFClientConnectionHistory",
|
||||
columns: table => new
|
||||
{
|
||||
ClientConnectionId = table.Column<long>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
CreatedDateTime = table.Column<DateTime>(nullable: false),
|
||||
UpdatedDateTime = table.Column<DateTime>(nullable: true),
|
||||
ClientId = table.Column<int>(nullable: false),
|
||||
ServerId = table.Column<long>(nullable: false),
|
||||
ConnectionType = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFClientConnectionHistory", x => x.ClientConnectionId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFClientConnectionHistory_EFClients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFClientConnectionHistory_EFServers_ServerId",
|
||||
column: x => x.ServerId,
|
||||
principalTable: "EFServers",
|
||||
principalColumn: "ServerId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFClientConnectionHistory_ClientId",
|
||||
table: "EFClientConnectionHistory",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFClientConnectionHistory_CreatedDateTime",
|
||||
table: "EFClientConnectionHistory",
|
||||
column: "CreatedDateTime");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFClientConnectionHistory_ServerId",
|
||||
table: "EFClientConnectionHistory",
|
||||
column: "ServerId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFClientConnectionHistory");
|
||||
}
|
||||
}
|
||||
}
|
@ -94,6 +94,38 @@ namespace Data.Migrations.Sqlite
|
||||
b.ToTable("EFClients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientConnectionHistory", b =>
|
||||
{
|
||||
b.Property<long>("ClientConnectionId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ConnectionType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedDateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("UpdatedDateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("ClientConnectionId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("CreatedDateTime");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFClientConnectionHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientKill", b =>
|
||||
{
|
||||
b.Property<long>("KillId")
|
||||
@ -145,6 +177,9 @@ namespace Data.Migrations.Sqlite
|
||||
b.Property<int>("Weapon")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("WeaponReference")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@ -236,6 +271,9 @@ namespace Data.Migrations.Sqlite
|
||||
b.Property<int>("HitLocation")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("HitLocationReference")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("HitOriginId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@ -254,6 +292,9 @@ namespace Data.Migrations.Sqlite
|
||||
b.Property<double>("RecoilOffset")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<long?>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("SessionAngleOffset")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
@ -278,6 +319,9 @@ namespace Data.Migrations.Sqlite
|
||||
b.Property<int>("WeaponId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("WeaponReference")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@ -293,6 +337,8 @@ namespace Data.Migrations.Sqlite
|
||||
|
||||
b.HasIndex("LastStrainAngleId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFACSnapshot");
|
||||
});
|
||||
|
||||
@ -920,6 +966,44 @@ namespace Data.Migrations.Sqlite
|
||||
b.ToTable("EFPenalties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Misc.EFInboxMessage", b =>
|
||||
{
|
||||
b.Property<int>("InboxMessageId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedDateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("DestinationClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsDelivered")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long?>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SourceClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("UpdatedDateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InboxMessageId");
|
||||
|
||||
b.HasIndex("DestinationClientId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("SourceClientId");
|
||||
|
||||
b.ToTable("InboxMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServer", b =>
|
||||
{
|
||||
b.Property<long>("ServerId")
|
||||
@ -948,6 +1032,39 @@ namespace Data.Migrations.Sqlite
|
||||
b.ToTable("EFServers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerSnapshot", b =>
|
||||
{
|
||||
b.Property<long>("ServerSnapshotId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CapturedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ClientCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MapId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("PeriodBlock")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("ServerSnapshotId");
|
||||
|
||||
b.HasIndex("MapId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFServerSnapshot");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b =>
|
||||
{
|
||||
b.Property<int>("StatisticId")
|
||||
@ -1023,6 +1140,21 @@ namespace Data.Migrations.Sqlite
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientConnectionHistory", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClientKill", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", "Attacker")
|
||||
@ -1102,6 +1234,10 @@ namespace Data.Migrations.Sqlite
|
||||
.HasForeignKey("LastStrainAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFClientHitStatistic", b =>
|
||||
@ -1263,6 +1399,40 @@ namespace Data.Migrations.Sqlite
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Misc.EFInboxMessage", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", "DestinationClient")
|
||||
.WithMany()
|
||||
.HasForeignKey("DestinationClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
|
||||
b.HasOne("Data.Models.Client.EFClient", "SourceClient")
|
||||
.WithMany()
|
||||
.HasForeignKey("SourceClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerSnapshot", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.Stats.Reference.EFMap", "Map")
|
||||
.WithMany()
|
||||
.HasForeignKey("MapId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
|
@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Data.Abstractions;
|
||||
|
||||
namespace Stats.Models
|
||||
{
|
||||
public class AuditFields
|
||||
public class AuditFields : IAuditFields
|
||||
{
|
||||
[Required]
|
||||
public DateTime CreatedDateTime { get; set; } = DateTime.UtcNow;
|
||||
|
25
Data/Models/Client/EFClientConnectionHistory.cs
Normal file
25
Data/Models/Client/EFClientConnectionHistory.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Data.Models.Server;
|
||||
using Stats.Models;
|
||||
|
||||
namespace Data.Models.Client
|
||||
{
|
||||
public class EFClientConnectionHistory : AuditFields
|
||||
{
|
||||
[Key]
|
||||
public long ClientConnectionId { get; set; }
|
||||
|
||||
public int ClientId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(ClientId))]
|
||||
public EFClient Client { get;set; }
|
||||
|
||||
public long ServerId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(ServerId))]
|
||||
public EFServer Server { get;set; }
|
||||
|
||||
public Reference.ConnectionType ConnectionType { get; set; }
|
||||
}
|
||||
}
|
@ -18,7 +18,9 @@ namespace Data.Models.Client
|
||||
public int HitLoc { get; set; }
|
||||
public int DeathType { get; set; }
|
||||
public int Damage { get; set; }
|
||||
[Obsolete]
|
||||
public int Weapon { get; set; }
|
||||
public string WeaponReference { get; set; }
|
||||
public Vector3 KillOrigin { get; set; }
|
||||
public Vector3 DeathOrigin { get; set; }
|
||||
public Vector3 ViewAngles { get; set; }
|
||||
|
@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Data.Models.Server;
|
||||
|
||||
namespace Data.Models.Client.Stats
|
||||
{
|
||||
@ -17,7 +18,9 @@ namespace Data.Models.Client.Stats
|
||||
public int ClientId { get; set; }
|
||||
[ForeignKey("ClientId")]
|
||||
public EFClient Client { get; set; }
|
||||
|
||||
public long? ServerId { get; set; }
|
||||
[ForeignKey(nameof(ServerId))]
|
||||
public EFServer Server { get; set; }
|
||||
public DateTime When { get; set; }
|
||||
public int CurrentSessionLength { get; set; }
|
||||
public int TimeSinceLastEvent { get; set; }
|
||||
@ -46,8 +49,11 @@ namespace Data.Models.Client.Stats
|
||||
public int CurrentViewAngleId { get; set; }
|
||||
[ForeignKey("CurrentViewAngleId")]
|
||||
public Vector3 CurrentViewAngle { get; set; }
|
||||
[Obsolete]
|
||||
public int WeaponId { get; set; }
|
||||
public string WeaponReference { get; set; }
|
||||
public int HitLocation { get; set; }
|
||||
public string HitLocationReference { get; set; }
|
||||
public int HitType { get; set; }
|
||||
public virtual ICollection<EFACSnapshotVector3> PredictedViewAngles { get; set; }
|
||||
|
||||
@ -55,5 +61,7 @@ namespace Data.Models.Client.Stats
|
||||
public string CapturedViewAngles => PredictedViewAngles?.Count > 0 ?
|
||||
string.Join(", ", PredictedViewAngles.OrderBy(_angle => _angle.ACSnapshotVector3Id).Select(_angle => _angle.Vector.ToString())) :
|
||||
"";
|
||||
|
||||
[NotMapped] public string ServerName => Server?.HostName ?? "--";
|
||||
}
|
||||
}
|
||||
|
35
Data/Models/Misc/EFInboxMessage.cs
Normal file
35
Data/Models/Misc/EFInboxMessage.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Data.Models.Client;
|
||||
using Data.Models.Server;
|
||||
using Stats.Models;
|
||||
|
||||
namespace Data.Models.Misc
|
||||
{
|
||||
public class EFInboxMessage : AuditFields
|
||||
{
|
||||
[Key]
|
||||
public int InboxMessageId { get; set; }
|
||||
|
||||
[Required]
|
||||
public int SourceClientId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(SourceClientId))]
|
||||
public EFClient SourceClient { get; set; }
|
||||
|
||||
[Required]
|
||||
public int DestinationClientId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(DestinationClientId))]
|
||||
public EFClient DestinationClient { get; set; }
|
||||
|
||||
public long? ServerId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(ServerId))]
|
||||
public EFServer Server { get; set; }
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
public bool IsDelivered { get; set; }
|
||||
}
|
||||
}
|
@ -17,5 +17,11 @@
|
||||
SHG1 = 9,
|
||||
CSGO = 10
|
||||
}
|
||||
|
||||
public enum ConnectionType
|
||||
{
|
||||
Connect,
|
||||
Disconnect
|
||||
}
|
||||
}
|
||||
}
|
36
Data/Models/Server/EFServerSnapshot.cs
Normal file
36
Data/Models/Server/EFServerSnapshot.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Data.Models.Client.Stats.Reference;
|
||||
|
||||
namespace Data.Models.Server
|
||||
{
|
||||
public class EFServerSnapshot : SharedEntity
|
||||
{
|
||||
[Key]
|
||||
public long ServerSnapshotId { get; set; }
|
||||
|
||||
public DateTime CapturedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies at which time block during a period the snapshot occured
|
||||
/// | 1:00 | 1:05 | 1:10 |
|
||||
/// | 5 minutes | 5 minutes | 5 minutes |
|
||||
/// | 0 | 1 | 2 |
|
||||
/// </summary>
|
||||
public int PeriodBlock { get; set; }
|
||||
|
||||
[Required]
|
||||
public long ServerId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(ServerId))]
|
||||
public EFServer Server { get; set; }
|
||||
|
||||
public int MapId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(MapId))]
|
||||
public EFMap Map { get; set; }
|
||||
|
||||
public int ClientCount { get; set; }
|
||||
}
|
||||
}
|
@ -139,35 +139,35 @@ steps:
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/IW4MAdmin-$(Build.BuildNumber).zip'
|
||||
replaceExistingArchive: true
|
||||
|
||||
- task: FtpUpload@2
|
||||
displayName: 'Upload zip file to website'
|
||||
inputs:
|
||||
credentialsOption: 'inputs'
|
||||
serverUrl: '$(FTPUrl)'
|
||||
username: '$(FTPUsername)'
|
||||
password: '$(FTPPassword)'
|
||||
rootDirectory: '$(Build.ArtifactStagingDirectory)'
|
||||
filePatterns: '*.zip'
|
||||
remoteDirectory: 'IW4MAdmin/Download'
|
||||
clean: false
|
||||
cleanContents: false
|
||||
preservePaths: false
|
||||
trustSSL: false
|
||||
#- task: FtpUpload@2
|
||||
# displayName: 'Upload zip file to website'
|
||||
# inputs:
|
||||
# credentialsOption: 'inputs'
|
||||
# serverUrl: '$(FTPUrl)'
|
||||
# username: '$(FTPUsername)'
|
||||
# password: '$(FTPPassword)'
|
||||
# rootDirectory: '$(Build.ArtifactStagingDirectory)'
|
||||
# filePatterns: '*.zip'
|
||||
# remoteDirectory: 'IW4MAdmin/Download'
|
||||
# clean: false
|
||||
# cleanContents: false
|
||||
# preservePaths: false
|
||||
# trustSSL: false
|
||||
|
||||
- task: FtpUpload@2
|
||||
displayName: 'Upload version info to website'
|
||||
inputs:
|
||||
credentialsOption: 'inputs'
|
||||
serverUrl: '$(FTPUrl)'
|
||||
username: '$(FTPUsername)'
|
||||
password: '$(FTPPassword)'
|
||||
rootDirectory: '$(Build.ArtifactStagingDirectory)'
|
||||
filePatterns: 'version_$(releaseType).txt'
|
||||
remoteDirectory: 'IW4MAdmin'
|
||||
clean: false
|
||||
cleanContents: false
|
||||
preservePaths: false
|
||||
trustSSL: false
|
||||
#- task: FtpUpload@2
|
||||
# displayName: 'Upload version info to website'
|
||||
# inputs:
|
||||
# credentialsOption: 'inputs'
|
||||
# serverUrl: '$(FTPUrl)'
|
||||
# username: '$(FTPUsername)'
|
||||
# password: '$(FTPPassword)'
|
||||
# rootDirectory: '$(Build.ArtifactStagingDirectory)'
|
||||
# filePatterns: 'version_$(releaseType).txt'
|
||||
# remoteDirectory: 'IW4MAdmin'
|
||||
# clean: false
|
||||
# cleanContents: false
|
||||
# preservePaths: false
|
||||
# trustSSL: false
|
||||
|
||||
- task: GitHubRelease@1
|
||||
displayName: 'Make GitHub release'
|
||||
|
23
GameFiles/IW5/storage/iw5/scripts/README.MD
Normal file
23
GameFiles/IW5/storage/iw5/scripts/README.MD
Normal file
@ -0,0 +1,23 @@
|
||||
# IW5
|
||||
|
||||
This expands IW4M-Admins's Anti-cheat to Plutonium IW5
|
||||
## Installation
|
||||
|
||||
Add ``_customcallbacks.gsc`` into the scripts folder. (%localappdata%\Plutonium\storage\iw5\scripts)
|
||||
|
||||
For more info check out Chase's [how-to guide](https://forum.plutonium.pw/topic/10738/tutorial-loading-custom-gsc-scripts).
|
||||
|
||||
You need to add this to you ``StatsPluginSettings.json`` found in your IW4M-Admin configuration folder.
|
||||
|
||||
```
|
||||
"IW5": {
|
||||
"Recoil": [
|
||||
"iw5_1887_mp.*",
|
||||
"turret_minigun_mp"
|
||||
],
|
||||
"Button": [
|
||||
".*akimbo.*"
|
||||
]
|
||||
}
|
||||
```
|
||||
[Example](https://imgur.com/Ji9AafI)
|
249
GameFiles/IW5/storage/iw5/scripts/_customcallbacks.gsc
Normal file
249
GameFiles/IW5/storage/iw5/scripts/_customcallbacks.gsc
Normal file
@ -0,0 +1,249 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init()
|
||||
{
|
||||
Print("IW4MADMIN Anti-Cheat Loaded");
|
||||
SetDvarIfUninitialized( "sv_customcallbacks", true );
|
||||
SetDvarIfUninitialized( "sv_framewaittime", 0.05 );
|
||||
SetDvarIfUninitialized( "sv_additionalwaittime", 0.1 );
|
||||
SetDvarIfUninitialized( "sv_maxstoredframes", 12 );
|
||||
SetDvarIfUninitialized( "sv_printradarupdates", 0 );
|
||||
SetDvarIfUninitialized( "sv_printradar_updateinterval", 500 );
|
||||
SetDvarIfUninitialized( "sv_iw4madmin_url", "http://127.0.0.1:1624" );
|
||||
|
||||
level thread onPlayerConnect();
|
||||
if (getDvarInt("sv_printradarupdates") == 1)
|
||||
{
|
||||
level thread runRadarUpdates();
|
||||
}
|
||||
|
||||
level waittill( "prematch_over" );
|
||||
level.callbackPlayerKilled = ::Callback_PlayerKilled;
|
||||
level.callbackPlayerDamage = ::Callback_PlayerDamage;
|
||||
level.callbackPlayerDisconnect = ::Callback_PlayerDisconnect;
|
||||
}
|
||||
|
||||
onPlayerConnect( player )
|
||||
{
|
||||
for( ;; )
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
player setClientDvar("cl_demo_enabled", 1);
|
||||
player thread waitForFrameThread();
|
||||
player thread waitForAttack();
|
||||
}
|
||||
}
|
||||
|
||||
waitForAttack()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
self.lastAttackTime = 0;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
self notifyOnPlayerCommand( "player_shot", "+attack" );
|
||||
self waittill( "player_shot" );
|
||||
|
||||
self.lastAttackTime = getTime();
|
||||
}
|
||||
}
|
||||
|
||||
runRadarUpdates()
|
||||
{
|
||||
interval = int(getDvar("sv_printradar_updateinterval"));
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
for ( i = 0; i <= 17; i++ )
|
||||
{
|
||||
player = level.players[i];
|
||||
|
||||
if ( isDefined( player ) )
|
||||
{
|
||||
payload = player.guid + ";" + player.origin + ";" + player getPlayerAngles() + ";" + player.team + ";" + player.kills + ";" + player.deaths + ";" + player.score + ";" + player GetCurrentWeapon() + ";" + player.health + ";" + isAlive(player) + ";" + player.timePlayed["total"];
|
||||
logPrint( "LiveRadar;" + payload + "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
wait( interval / 1000 );
|
||||
}
|
||||
}
|
||||
|
||||
hitLocationToBone( hitloc )
|
||||
{
|
||||
switch( hitloc )
|
||||
{
|
||||
case "helmet":
|
||||
return "j_helmet";
|
||||
case "head":
|
||||
return "j_head";
|
||||
case "neck":
|
||||
return "j_neck";
|
||||
case "torso_upper":
|
||||
return "j_spineupper";
|
||||
case "torso_lower":
|
||||
return "j_spinelower";
|
||||
case "right_arm_upper":
|
||||
return "j_shoulder_ri";
|
||||
case "left_arm_upper":
|
||||
return "j_shoulder_le";
|
||||
case "right_arm_lower":
|
||||
return "j_elbow_ri";
|
||||
case "left_arm_lower":
|
||||
return "j_elbow_le";
|
||||
case "right_hand":
|
||||
return "j_wrist_ri";
|
||||
case "left_hand":
|
||||
return "j_wrist_le";
|
||||
case "right_leg_upper":
|
||||
return "j_hip_ri";
|
||||
case "left_leg_upper":
|
||||
return "j_hip_le";
|
||||
case "right_leg_lower":
|
||||
return "j_knee_ri";
|
||||
case "left_leg_lower":
|
||||
return "j_knee_le";
|
||||
case "right_foot":
|
||||
return "j_ankle_ri";
|
||||
case "left_foot":
|
||||
return "j_ankle_le";
|
||||
default:
|
||||
return "tag_origin";
|
||||
}
|
||||
}
|
||||
|
||||
waitForFrameThread()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
self.currentAnglePosition = 0;
|
||||
self.anglePositions = [];
|
||||
|
||||
for (i = 0; i < getDvarInt( "sv_maxstoredframes" ); i++)
|
||||
{
|
||||
self.anglePositions[i] = self getPlayerAngles();
|
||||
}
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
self.anglePositions[self.currentAnglePosition] = self getPlayerAngles();
|
||||
wait( getDvarFloat( "sv_framewaittime" ) );
|
||||
self.currentAnglePosition = (self.currentAnglePosition + 1) % getDvarInt( "sv_maxstoredframes" );
|
||||
}
|
||||
}
|
||||
|
||||
waitForAdditionalAngles( logString, beforeFrameCount, afterFrameCount )
|
||||
{
|
||||
currentIndex = self.currentAnglePosition;
|
||||
wait( 0.05 * afterFrameCount );
|
||||
|
||||
self.angleSnapshot = [];
|
||||
|
||||
for( j = 0; j < self.anglePositions.size; j++ )
|
||||
{
|
||||
self.angleSnapshot[j] = self.anglePositions[j];
|
||||
}
|
||||
|
||||
anglesStr = "";
|
||||
collectedFrames = 0;
|
||||
i = currentIndex - beforeFrameCount;
|
||||
|
||||
while (collectedFrames < beforeFrameCount)
|
||||
{
|
||||
fixedIndex = i;
|
||||
if (i < 0)
|
||||
{
|
||||
fixedIndex = self.angleSnapshot.size - abs(i);
|
||||
}
|
||||
anglesStr += self.angleSnapshot[int(fixedIndex)] + ":";
|
||||
collectedFrames++;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i == currentIndex)
|
||||
{
|
||||
anglesStr += self.angleSnapshot[i] + ":";
|
||||
i++;
|
||||
}
|
||||
|
||||
collectedFrames = 0;
|
||||
|
||||
while (collectedFrames < afterFrameCount)
|
||||
{
|
||||
fixedIndex = i;
|
||||
if (i > self.angleSnapshot.size - 1)
|
||||
{
|
||||
fixedIndex = i % self.angleSnapshot.size;
|
||||
}
|
||||
anglesStr += self.angleSnapshot[int(fixedIndex)] + ":";
|
||||
collectedFrames++;
|
||||
i++;
|
||||
}
|
||||
|
||||
lastAttack = int(getTime()) - int(self.lastAttackTime);
|
||||
isAlive = isAlive(self);
|
||||
|
||||
logPrint(logString + ";" + anglesStr + ";" + isAlive + ";" + lastAttack + "\n" );
|
||||
}
|
||||
|
||||
vectorScale( vector, scale )
|
||||
{
|
||||
return ( vector[0] * scale, vector[1] * scale, vector[2] * scale );
|
||||
}
|
||||
|
||||
Process_Hit( type, attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon )
|
||||
{
|
||||
if (sMeansOfDeath == "MOD_FALLING" || !isPlayer(attacker))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
victim = self;
|
||||
_attacker = attacker;
|
||||
|
||||
if ( !isPlayer( attacker ) && isDefined( attacker.owner ) )
|
||||
{
|
||||
_attacker = attacker.owner;
|
||||
}
|
||||
|
||||
else if( !isPlayer( attacker ) && sMeansOfDeath == "MOD_FALLING" )
|
||||
{
|
||||
_attacker = victim;
|
||||
}
|
||||
|
||||
location = victim GetTagOrigin( hitLocationToBone( sHitLoc ) );
|
||||
isKillstreakKill = !isPlayer( attacker ) || isKillstreakWeapon( sWeapon );
|
||||
|
||||
logLine = "Script" + type + ";" + _attacker.guid + ";" + victim.guid + ";" + _attacker GetTagOrigin("tag_eye") + ";" + location + ";" + iDamage + ";" + sWeapon + ";" + sHitLoc + ";" + sMeansOfDeath + ";" + _attacker getPlayerAngles() + ";" + int(gettime()) + ";" + isKillstreakKill + ";" + _attacker playerADS() + ";0;0";
|
||||
attacker thread waitForAdditionalAngles( logLine, 2, 2 );
|
||||
}
|
||||
|
||||
Callback_PlayerDamage( eInflictor, attacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime )
|
||||
{
|
||||
if ( self.health - iDamage > 0 )
|
||||
{
|
||||
isFriendlyFire = level.teamBased && isDefined( attacker ) && ( self != attacker ) && isDefined( attacker.team ) && ( self.pers[ "team" ] == attacker.team );
|
||||
|
||||
if ( !isFriendlyFire )
|
||||
{
|
||||
self Process_Hit( "Damage", attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon );
|
||||
}
|
||||
}
|
||||
|
||||
self maps\mp\gametypes\_damage::Callback_PlayerDamage( eInflictor, attacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime );
|
||||
}
|
||||
|
||||
Callback_PlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration )
|
||||
{
|
||||
Process_Hit( "Kill", attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon );
|
||||
self maps\mp\gametypes\_damage::Callback_PlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration );
|
||||
}
|
||||
|
||||
Callback_PlayerDisconnect()
|
||||
{
|
||||
level notify( "disconnected", self );
|
||||
self maps\mp\gametypes\_playerlogic::Callback_PlayerDisconnect();
|
||||
}
|
35
GameFiles/PT6/README.MD
Normal file
35
GameFiles/PT6/README.MD
Normal file
@ -0,0 +1,35 @@
|
||||
# T6
|
||||
|
||||
This brings IW4M-Admins's Anti-cheat to Plutonium T6
|
||||
The following limitations are known:
|
||||
* Can't get the recoil from weapons fire; you have to disable this detection type manually.
|
||||
* in extreme cases it can produce false positives for Snap and Offset detection.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
Move `_customcallbacks.gsc` to `%localappdata%\Plutonium\storage\t6\scripts\mp\`
|
||||
|
||||
Add this to the WeaponNameParserConfigurations List in the StatsPluginSettings.json file:
|
||||
|
||||
```
|
||||
{
|
||||
"Game": "T6",
|
||||
"Delimiters": [
|
||||
"_",
|
||||
"+"
|
||||
],
|
||||
"WeaponSuffix": "mp",
|
||||
"WeaponPrefix": null
|
||||
}
|
||||
```
|
||||
|
||||
Now create the following entry for __EVERY__ T6 server you are using this on in the ServerDetectionTypes list:
|
||||
|
||||
```
|
||||
"1270014976": [
|
||||
"Offset",
|
||||
"Strain",
|
||||
"Snap"
|
||||
]
|
||||
```
|
BIN
GameFiles/PT6/storage/t6/scripts/mp/_customcallbacks.gsc
Normal file
BIN
GameFiles/PT6/storage/t6/scripts/mp/_customcallbacks.gsc
Normal file
Binary file not shown.
@ -3,24 +3,6 @@
|
||||
#include common_scripts\utility;
|
||||
|
||||
init()
|
||||
{
|
||||
level.clientid = 0;
|
||||
level thread onplayerconnect();
|
||||
level thread IW4MA_init();
|
||||
}
|
||||
|
||||
onplayerconnect()
|
||||
{
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill( "connecting", player );
|
||||
player.clientid = level.clientid;
|
||||
level.clientid++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
IW4MA_init()
|
||||
{
|
||||
SetDvarIfUninitialized( "sv_customcallbacks", true );
|
||||
SetDvarIfUninitialized( "sv_framewaittime", 0.05 );
|
||||
@ -29,53 +11,52 @@ IW4MA_init()
|
||||
SetDvarIfUninitialized( "sv_printradarupdates", 0 );
|
||||
SetDvarIfUninitialized( "sv_printradar_updateinterval", 500 );
|
||||
SetDvarIfUninitialized( "sv_iw4madmin_url", "http://127.0.0.1:1624" );
|
||||
|
||||
level thread IW4MA_onPlayerConnect();
|
||||
|
||||
level thread onPlayerConnect();
|
||||
if (getDvarInt("sv_printradarupdates") == 1)
|
||||
{
|
||||
level thread runRadarUpdates();
|
||||
}
|
||||
|
||||
|
||||
level waittill( "prematch_over" );
|
||||
level.callbackPlayerKilled = ::Callback_PlayerKilled;
|
||||
level.callbackPlayerDamage = ::Callback_PlayerDamage;
|
||||
level.callbackPlayerDisconnect = ::Callback_PlayerDisconnect;
|
||||
}
|
||||
|
||||
//Does not exist in T6
|
||||
//It's called slightly different in T6
|
||||
//set_dvar_if_unset(dvar, val, reset)
|
||||
SetDvarIfUninitialized(dvar, val)
|
||||
{
|
||||
curval = getDvar(dvar);
|
||||
if (curval == "")
|
||||
SetDvar(dvar,val);
|
||||
set_dvar_if_unset(dvar,val);
|
||||
}
|
||||
|
||||
IW4MA_onPlayerConnect( player )
|
||||
onPlayerConnect( player )
|
||||
{
|
||||
for( ;; )
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
player thread waitForFrameThread();
|
||||
//player thread waitForAttack();
|
||||
player thread waitForAttack();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Does not work in T6
|
||||
/*waitForAttack()
|
||||
//Got added to T6 on April 2020
|
||||
waitForAttack()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
|
||||
self.lastAttackTime = 0;
|
||||
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
self notifyOnPlayerCommand( "player_shot", "+attack" );
|
||||
self waittill( "player_shot" );
|
||||
|
||||
|
||||
self.lastAttackTime = getTime();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
runRadarUpdates()
|
||||
{
|
||||
@ -95,7 +76,7 @@ runRadarUpdates()
|
||||
}
|
||||
|
||||
wait( interval / 1000 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hitLocationToBone( hitloc )
|
||||
@ -144,7 +125,7 @@ hitLocationToBone( hitloc )
|
||||
waitForFrameThread()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
|
||||
self.currentAnglePosition = 0;
|
||||
self.anglePositions = [];
|
||||
|
||||
@ -152,7 +133,7 @@ waitForFrameThread()
|
||||
{
|
||||
self.anglePositions[i] = self getPlayerAngles();
|
||||
}
|
||||
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
self.anglePositions[self.currentAnglePosition] = self getPlayerAngles();
|
||||
@ -165,9 +146,9 @@ waitForAdditionalAngles( logString, beforeFrameCount, afterFrameCount )
|
||||
{
|
||||
currentIndex = self.currentAnglePosition;
|
||||
wait( 0.05 * afterFrameCount );
|
||||
|
||||
|
||||
self.angleSnapshot = [];
|
||||
|
||||
|
||||
for( j = 0; j < self.anglePositions.size; j++ )
|
||||
{
|
||||
self.angleSnapshot[j] = self.anglePositions[j];
|
||||
@ -209,7 +190,7 @@ waitForAdditionalAngles( logString, beforeFrameCount, afterFrameCount )
|
||||
i++;
|
||||
}
|
||||
|
||||
lastAttack = 100;//int(getTime()) - int(self.lastAttackTime);
|
||||
lastAttack = int(getTime()) - int(self.lastAttackTime);
|
||||
isAlive = isAlive(self);
|
||||
|
||||
logPrint(logString + ";" + anglesStr + ";" + isAlive + ";" + lastAttack + "\n" );
|
||||
@ -261,7 +242,7 @@ Callback_PlayerDamage( eInflictor, attacker, iDamage, iDFlags, sMeansOfDeath, sW
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if ( self.health - iDamage > 0 )
|
||||
{
|
||||
self Process_Hit( "Damage", attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon );
|
Binary file not shown.
@ -31,13 +31,15 @@ namespace Integrations.Cod
|
||||
private IRConParserConfiguration config;
|
||||
private readonly ILogger _log;
|
||||
private readonly Encoding _gameEncoding;
|
||||
private readonly int _retryAttempts;
|
||||
|
||||
public CodRConConnection(string ipAddress, int port, string password, ILogger<CodRConConnection> log, Encoding gameEncoding)
|
||||
public CodRConConnection(IPEndPoint ipEndpoint, string password, ILogger<CodRConConnection> log, Encoding gameEncoding, int retryAttempts)
|
||||
{
|
||||
Endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
|
||||
_gameEncoding = gameEncoding;
|
||||
RConPassword = password;
|
||||
_gameEncoding = gameEncoding;
|
||||
_log = log;
|
||||
Endpoint = ipEndpoint;
|
||||
_retryAttempts = retryAttempts;
|
||||
}
|
||||
|
||||
public void SetConfiguration(IRConParser parser)
|
||||
@ -137,7 +139,7 @@ namespace Integrations.Cod
|
||||
_log.LogInformation(
|
||||
"Retrying RCon message ({connectionAttempts}/{allowedConnectionFailures} attempts) with parameters {payload}",
|
||||
connectionState.ConnectionAttempts,
|
||||
StaticHelpers.AllowedConnectionFails, parameters);
|
||||
_retryAttempts, parameters);
|
||||
}
|
||||
}
|
||||
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
|
||||
@ -155,7 +157,7 @@ namespace Integrations.Cod
|
||||
bool exceptionCaught = false;
|
||||
|
||||
_log.LogDebug("Sending {payloadLength} bytes to [{endpoint}] ({connectionAttempts}/{allowedConnectionFailures})",
|
||||
payload.Length, Endpoint, connectionState.ConnectionAttempts, StaticHelpers.AllowedConnectionFails);
|
||||
payload.Length, Endpoint, connectionState.ConnectionAttempts, _retryAttempts);
|
||||
|
||||
try
|
||||
{
|
||||
@ -173,7 +175,7 @@ namespace Integrations.Cod
|
||||
catch
|
||||
{
|
||||
// we want to retry with a delay
|
||||
if (connectionState.ConnectionAttempts < StaticHelpers.AllowedConnectionFails)
|
||||
if (connectionState.ConnectionAttempts < _retryAttempts)
|
||||
{
|
||||
exceptionCaught = true;
|
||||
await Task.Delay(StaticHelpers.SocketTimeout(connectionState.ConnectionAttempts));
|
||||
|
@ -1,9 +1,10 @@
|
||||
using RconSharp;
|
||||
using System.Net;
|
||||
using RconSharp;
|
||||
|
||||
namespace Integrations.Source.Interfaces
|
||||
{
|
||||
public interface IRConClientFactory
|
||||
{
|
||||
RconClient CreateClient(string hostname, int port);
|
||||
RconClient CreateClient(IPEndPoint ipEndPoint);
|
||||
}
|
||||
}
|
@ -1,13 +1,14 @@
|
||||
using Integrations.Source.Interfaces;
|
||||
using System.Net;
|
||||
using Integrations.Source.Interfaces;
|
||||
using RconSharp;
|
||||
|
||||
namespace Integrations.Source
|
||||
{
|
||||
public class RConClientFactory : IRConClientFactory
|
||||
{
|
||||
public RconClient CreateClient(string hostname, int port)
|
||||
public RconClient CreateClient(IPEndPoint ipEndPoint)
|
||||
{
|
||||
return RconClient.Create(hostname, port);
|
||||
return RconClient.Create(ipEndPoint.Address.ToString(), ipEndPoint.Port);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -20,8 +21,7 @@ namespace Integrations.Source
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly string _password;
|
||||
private readonly string _hostname;
|
||||
private readonly int _port;
|
||||
private readonly IPEndPoint _ipEndPoint;
|
||||
private readonly IRConClientFactory _rconClientFactory;
|
||||
private readonly SemaphoreSlim _activeQuery;
|
||||
|
||||
@ -34,13 +34,12 @@ namespace Integrations.Source
|
||||
private bool _needNewSocket = true;
|
||||
|
||||
public SourceRConConnection(ILogger<SourceRConConnection> logger, IRConClientFactory rconClientFactory,
|
||||
string hostname, int port, string password)
|
||||
IPEndPoint ipEndPoint, string password)
|
||||
{
|
||||
_rconClientFactory = rconClientFactory;
|
||||
_password = password;
|
||||
_hostname = hostname;
|
||||
_port = port;
|
||||
_logger = logger;
|
||||
_ipEndPoint = ipEndPoint;
|
||||
_activeQuery = new SemaphoreSlim(1, 1);
|
||||
}
|
||||
|
||||
@ -67,12 +66,12 @@ namespace Integrations.Source
|
||||
// ignored
|
||||
}
|
||||
|
||||
_rconClient = _rconClientFactory.CreateClient(_hostname, _port);
|
||||
_rconClient = _rconClientFactory.CreateClient(_ipEndPoint);
|
||||
_authenticated = false;
|
||||
_needNewSocket = false;
|
||||
}
|
||||
|
||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||
using (LogContext.PushProperty("Server", _ipEndPoint.ToString()))
|
||||
{
|
||||
_logger.LogDebug("Connecting to RCon socket");
|
||||
}
|
||||
@ -90,7 +89,7 @@ namespace Integrations.Source
|
||||
parameters = parameters.ReplaceUnfriendlyCharacters();
|
||||
parameters = parameters.StripColors();
|
||||
|
||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||
using (LogContext.PushProperty("Server", _ipEndPoint.ToString()))
|
||||
{
|
||||
_logger.LogDebug("Sending query {Type} with parameters \"{Parameters}\"", type, parameters);
|
||||
}
|
||||
@ -98,7 +97,7 @@ namespace Integrations.Source
|
||||
var response = await _rconClient.ExecuteCommandAsync(parameters, multiPacket)
|
||||
.WithTimeout(ConnectionTimeout);
|
||||
|
||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||
using (LogContext.PushProperty("Server", $"{_ipEndPoint}"))
|
||||
{
|
||||
_logger.LogDebug("Received RCon response {Response}", response);
|
||||
}
|
||||
@ -115,7 +114,7 @@ namespace Integrations.Source
|
||||
|
||||
catch (SocketException ex)
|
||||
{
|
||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||
using (LogContext.PushProperty("Server", _ipEndPoint.ToString()))
|
||||
{
|
||||
_logger.LogError(ex, "Socket exception encountered while attempting to communicate with server");
|
||||
}
|
||||
@ -128,7 +127,7 @@ namespace Integrations.Source
|
||||
catch (Exception ex) when (ex.GetType() != typeof(NetworkException) &&
|
||||
ex.GetType() != typeof(ServerException))
|
||||
{
|
||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||
using (LogContext.PushProperty("Server", _ipEndPoint.ToString()))
|
||||
{
|
||||
_logger.LogError(ex, "Could not execute RCon query {Parameters}", parameters);
|
||||
}
|
||||
@ -160,7 +159,7 @@ namespace Integrations.Source
|
||||
{
|
||||
if (!_authenticated)
|
||||
{
|
||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||
using (LogContext.PushProperty("Server", _ipEndPoint.ToString()))
|
||||
{
|
||||
_logger.LogDebug("Authenticating to RCon socket");
|
||||
}
|
||||
@ -170,7 +169,7 @@ namespace Integrations.Source
|
||||
|
||||
if (!_authenticated)
|
||||
{
|
||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||
using (LogContext.PushProperty("Server", _ipEndPoint.ToString()))
|
||||
{
|
||||
_logger.LogError("Could not login to server");
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.3.19.1" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.31.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.3.19.1" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.31.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -5,6 +5,7 @@ using SharedLibraryCore;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace LiveRadar.Web.Controllers
|
||||
{
|
||||
@ -48,16 +49,22 @@ namespace LiveRadar.Web.Controllers
|
||||
public IActionResult Map(long? serverId = null)
|
||||
{
|
||||
var server = serverId == null ? _manager.GetServers().FirstOrDefault() : _manager.GetServers().FirstOrDefault(_server => _server.EndPoint == serverId);
|
||||
|
||||
if (server == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var map = _config.Maps.FirstOrDefault(_map => _map.Name == server.CurrentMap.Name);
|
||||
|
||||
if (map != null)
|
||||
if (map == null)
|
||||
{
|
||||
map.Alias = server.CurrentMap.Alias;
|
||||
return Json(map);
|
||||
// occurs if we don't recognize the map
|
||||
return StatusCode(StatusCodes.Status422UnprocessableEntity);
|
||||
}
|
||||
|
||||
// occurs if we don't recognize the map
|
||||
return StatusCode(500);
|
||||
|
||||
map.Alias = server.CurrentMap.Alias;
|
||||
return Json(map);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
|
@ -23,7 +23,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.3.19.1" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.31.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -19,7 +19,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.3.19.1" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.31.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -16,7 +16,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.3.19.1" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.31.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -3,7 +3,7 @@ let eventParser;
|
||||
|
||||
const plugin = {
|
||||
author: 'RaidMax',
|
||||
version: 0.2,
|
||||
version: 0.3,
|
||||
name: 'CS:GO Parser',
|
||||
engine: 'Source',
|
||||
isParser: true,
|
||||
@ -46,6 +46,7 @@ const plugin = {
|
||||
rconParser.Configuration.DefaultDvarValues.Add('version', this.engine);
|
||||
rconParser.Configuration.DefaultDvarValues.Add('fs_basepath', '');
|
||||
rconParser.Configuration.DefaultDvarValues.Add('fs_basegame', '');
|
||||
rconParser.Configuration.DefaultDvarValues.Add('fs_homepath', '');
|
||||
rconParser.Configuration.DefaultDvarValues.Add('g_log', '');
|
||||
rconParser.Configuration.DefaultDvarValues.Add('net_ip', 'localhost');
|
||||
|
||||
@ -66,14 +67,14 @@ const plugin = {
|
||||
rconParser.Configuration.CommandPrefixes.Say = 'say {0}';
|
||||
rconParser.Configuration.CommandPrefixes.Tell = 'say [{0}] {1}'; // no tell exists in vanilla
|
||||
|
||||
eventParser.Configuration.Say.Pattern = '^"(.+)<(\\d+)><(.+)><(.*?)>" say "(.*)"$';
|
||||
eventParser.Configuration.Say.Pattern = '^"(.+)<(\\d+)><(.+)><(.*?)>" (?:say|say_team) "(.*)"$';
|
||||
eventParser.Configuration.Say.AddMapping(5, 1);
|
||||
eventParser.Configuration.Say.AddMapping(3, 2);
|
||||
eventParser.Configuration.Say.AddMapping(1, 3);
|
||||
eventParser.Configuration.Say.AddMapping(7, 4);
|
||||
eventParser.Configuration.Say.AddMapping(13, 5);
|
||||
|
||||
eventParser.Configuration.Kill.Pattern = '"(.+)<(\\d+)><(.+)><(.*)>" \\[-?\\d+ -?\\d+ -?\\d+\\] killed "(.+)<(\\d+)><(.+)><(.*)>" \\[-?\\d+ -?\\d+ -?\\d+\\] with "(\\S*)"(.*)$';
|
||||
eventParser.Configuration.Kill.Pattern = '^"(.+)<(\\d+)><(.+)><(.*)>" \\[-?\\d+ -?\\d+ -?\\d+\\] killed "(.+)<(\\d+)><(.+)><(.*)>" \\[-?\\d+ -?\\d+ -?\\d+\\] with "(\\S*)" *(?:\\((\\w+)((?: ).+)?\\))?$';
|
||||
eventParser.Configuration.Kill.AddMapping(5, 1);
|
||||
eventParser.Configuration.Kill.AddMapping(3, 2);
|
||||
eventParser.Configuration.Kill.AddMapping(1, 3);
|
||||
@ -83,6 +84,7 @@ const plugin = {
|
||||
eventParser.Configuration.Kill.AddMapping(2, 7);
|
||||
eventParser.Configuration.Kill.AddMapping(8, 8);
|
||||
eventParser.Configuration.Kill.AddMapping(9, 9);
|
||||
eventParser.Configuration.Kill.AddMapping(12, 10);
|
||||
|
||||
eventParser.Configuration.Time.Pattern = '^L [01]\\d/[0-3]\\d/\\d+ - [0-2]\\d:[0-5]\\d:[0-5]\\d:';
|
||||
|
||||
|
@ -3,7 +3,7 @@ let eventParser;
|
||||
|
||||
const plugin = {
|
||||
author: 'RaidMax',
|
||||
version: 0.2,
|
||||
version: 0.3,
|
||||
name: 'CS:GO (SourceMod) Parser',
|
||||
engine: 'Source',
|
||||
isParser: true,
|
||||
@ -46,6 +46,7 @@ const plugin = {
|
||||
rconParser.Configuration.DefaultDvarValues.Add('version', this.engine);
|
||||
rconParser.Configuration.DefaultDvarValues.Add('fs_basepath', '');
|
||||
rconParser.Configuration.DefaultDvarValues.Add('fs_basegame', '');
|
||||
rconParser.Configuration.DefaultDvarValues.Add('fs_homepath', '');
|
||||
rconParser.Configuration.DefaultDvarValues.Add('g_log', '');
|
||||
rconParser.Configuration.DefaultDvarValues.Add('net_ip', 'localhost');
|
||||
|
||||
@ -66,14 +67,14 @@ const plugin = {
|
||||
rconParser.Configuration.CommandPrefixes.Say = 'sm_say {0}';
|
||||
rconParser.Configuration.CommandPrefixes.Tell = 'sm_psay #{0} "{1}"';
|
||||
|
||||
eventParser.Configuration.Say.Pattern = '^"(.+)<(\\d+)><(.+)><(.*?)>" say "(.*)"$';
|
||||
eventParser.Configuration.Say.Pattern = '^"(.+)<(\\d+)><(.+)><(.*?)>" (?:say|say_team) "(.*)"$';
|
||||
eventParser.Configuration.Say.AddMapping(5, 1);
|
||||
eventParser.Configuration.Say.AddMapping(3, 2);
|
||||
eventParser.Configuration.Say.AddMapping(1, 3);
|
||||
eventParser.Configuration.Say.AddMapping(7, 4);
|
||||
eventParser.Configuration.Say.AddMapping(13, 5);
|
||||
|
||||
eventParser.Configuration.Kill.Pattern = '"(.+)<(\\d+)><(.+)><(.*)>" \\[-?\\d+ -?\\d+ -?\\d+\\] killed "(.+)<(\\d+)><(.+)><(.*)>" \\[-?\\d+ -?\\d+ -?\\d+\\] with "(\\S*)"(.*)$';
|
||||
eventParser.Configuration.Kill.Pattern = '^"(.+)<(\\d+)><(.+)><(.*)>" \\[-?\\d+ -?\\d+ -?\\d+\\] killed "(.+)<(\\d+)><(.+)><(.*)>" \\[-?\\d+ -?\\d+ -?\\d+\\] with "(\\S*)" *(?:\\((\\w+)((?: ).+)?\\))?$';
|
||||
eventParser.Configuration.Kill.AddMapping(5, 1);
|
||||
eventParser.Configuration.Kill.AddMapping(3, 2);
|
||||
eventParser.Configuration.Kill.AddMapping(1, 3);
|
||||
@ -83,6 +84,7 @@ const plugin = {
|
||||
eventParser.Configuration.Kill.AddMapping(2, 7);
|
||||
eventParser.Configuration.Kill.AddMapping(8, 8);
|
||||
eventParser.Configuration.Kill.AddMapping(9, 9);
|
||||
eventParser.Configuration.Kill.AddMapping(12, 10);
|
||||
|
||||
eventParser.Configuration.Time.Pattern = '^L [01]\\d/[0-3]\\d/\\d+ - [0-2]\\d:[0-5]\\d:[0-5]\\d:';
|
||||
|
||||
|
@ -2,8 +2,8 @@ var rconParser;
|
||||
var eventParser;
|
||||
|
||||
var plugin = {
|
||||
author: 'Xerxes, RaidMax',
|
||||
version: 0.2,
|
||||
author: 'Xerxes, RaidMax, st0rm',
|
||||
version: 0.3,
|
||||
name: 'IW6x Parser',
|
||||
isParser: true,
|
||||
|
||||
@ -27,14 +27,12 @@ var plugin = {
|
||||
rconParser.Configuration.Status.AddMapping(102, 4);
|
||||
rconParser.Configuration.Status.AddMapping(103, 5);
|
||||
rconParser.Configuration.Status.AddMapping(104, 6);
|
||||
rconParser.Configuration.DefaultDvarValues.Add('g_log', 'games_mp.log'); // todo: remove this once proper log support is implemented
|
||||
rconParser.Configuration.DefaultDvarValues.Add('g_logsync', '1'); // todo: remove this once proper log support is implemented
|
||||
|
||||
rconParser.Version = 'IW6 MP 3.15 build 2 Sat Sep 14 2013 03:58:30PM win64';
|
||||
rconParser.GameName = 4; // IW6
|
||||
eventParser.Version = 'IW6 MP 3.15 build 2 Sat Sep 14 2013 03:58:30PM win64';
|
||||
eventParser.GameName = 4; // IW6
|
||||
eventParser.Configuration.GameDirectory = 'iw6x';
|
||||
eventParser.Configuration.GameDirectory = '';
|
||||
},
|
||||
|
||||
onUnloadAsync: function () {
|
||||
|
@ -3,7 +3,7 @@ var eventParser;
|
||||
|
||||
var plugin = {
|
||||
author: 'RaidMax',
|
||||
version: 0.8,
|
||||
version: 0.9,
|
||||
name: 'Plutonium IW5 Parser',
|
||||
isParser: true,
|
||||
|
||||
@ -34,6 +34,8 @@ var plugin = {
|
||||
rconParser.Configuration.Status.AddMapping(102, 4);
|
||||
rconParser.Configuration.Status.AddMapping(103, 5);
|
||||
rconParser.Configuration.Status.AddMapping(104, 6);
|
||||
// basegame should not contain an absolute directory, but alas...
|
||||
rconParser.Configuration.OverrideDvarNameMapping.Add('fs_homepath', 'fs_basegame');
|
||||
|
||||
rconParser.IsOneLog = true;
|
||||
rconParser.Version = 'IW5 MP 1.9 build 388110 Fri Sep 14 00:04:28 2012 win-x86';
|
||||
|
@ -3,7 +3,7 @@ var eventParser;
|
||||
|
||||
var plugin = {
|
||||
author: 'RaidMax, Xerxes',
|
||||
version: 0.10,
|
||||
version: 1.0,
|
||||
name: 'Plutonium T6 Parser',
|
||||
isParser: true,
|
||||
|
||||
@ -37,7 +37,7 @@ var plugin = {
|
||||
rconParser.Configuration.Status.AddMapping(104, 5);
|
||||
rconParser.Configuration.Status.AddMapping(105, 6);
|
||||
|
||||
eventParser.Configuration.GameDirectory = 't6';
|
||||
eventParser.Configuration.GameDirectory = '';
|
||||
eventParser.Configuration.GuidNumberStyle = 7; // Integer
|
||||
|
||||
rconParser.Version = 'Call of Duty Multiplayer - Ship COD_T6_S MP build 1.0.44 CL(1759941) CODPCAB2 CEG Fri May 9 19:19:19 2014 win-x86 813e66d5';
|
||||
|
@ -33,6 +33,7 @@ var plugin = {
|
||||
rconParser.Configuration.DefaultDvarValues.Add('g_gametype', '');
|
||||
rconParser.Configuration.DefaultDvarValues.Add('fs_basepath', '');
|
||||
rconParser.Configuration.DefaultDvarValues.Add('fs_basegame', '');
|
||||
rconParser.Configuration.DefaultDvarValues.Add('fs_homepath', '');
|
||||
rconParser.Configuration.DefaultDvarValues.Add('fs_game', '');
|
||||
|
||||
rconParser.Configuration.Status.AddMapping(105, 6); // ip address
|
||||
|
@ -38,7 +38,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
double AngleDifferenceAverage;
|
||||
EFClientStatistics ClientStats;
|
||||
long LastOffset;
|
||||
IW4Info.WeaponName LastWeapon;
|
||||
string LastWeapon;
|
||||
ILogger Log;
|
||||
Strain Strain;
|
||||
readonly DateTime ConnectionTime = DateTime.UtcNow;
|
||||
@ -111,7 +111,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
hit.DeathType != (int)IW4Info.MeansOfDeath.MOD_HEAD_SHOT) ||
|
||||
hit.HitLoc == (int)IW4Info.HitLocation.none || hit.TimeOffset - LastOffset < 0 ||
|
||||
// hack: prevents false positives
|
||||
((int)LastWeapon != hit.Weapon && (hit.TimeOffset - LastOffset) == 50))
|
||||
(LastWeapon != hit.WeaponReference && (hit.TimeOffset - LastOffset) == 50))
|
||||
{
|
||||
return new[] {new DetectionPenaltyResult()
|
||||
{
|
||||
@ -119,7 +119,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
}};
|
||||
}
|
||||
|
||||
LastWeapon = (IW4Info.WeaponName)(hit.Weapon);
|
||||
LastWeapon = hit.WeaponReference;
|
||||
|
||||
HitLocationCount[(IW4Info.HitLocation)hit.HitLoc].Count++;
|
||||
HitCount++;
|
||||
@ -309,7 +309,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
try
|
||||
{
|
||||
shouldIgnoreDetection = Plugin.Config.Configuration().AnticheatConfiguration.IgnoredDetectionSpecification[(Server.Game)hit.GameName][DetectionType.Recoil]
|
||||
.Any(_weaponRegex => Regex.IsMatch(((IW4Info.WeaponName)(hit.Weapon)).ToString(), _weaponRegex));
|
||||
.Any(_weaponRegex => Regex.IsMatch(hit.WeaponReference, _weaponRegex));
|
||||
}
|
||||
|
||||
catch (KeyNotFoundException)
|
||||
@ -341,7 +341,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
{
|
||||
shouldIgnoreDetection = false;
|
||||
shouldIgnoreDetection = Plugin.Config.Configuration().AnticheatConfiguration.IgnoredDetectionSpecification[(Server.Game)hit.GameName][DetectionType.Button]
|
||||
.Any(_weaponRegex => Regex.IsMatch(((IW4Info.WeaponName)(hit.Weapon)).ToString(), _weaponRegex));
|
||||
.Any(_weaponRegex => Regex.IsMatch(hit.WeaponReference, _weaponRegex));
|
||||
}
|
||||
|
||||
catch (KeyNotFoundException)
|
||||
@ -454,7 +454,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
{
|
||||
shouldIgnoreDetection = false; // reset previous value
|
||||
shouldIgnoreDetection = Plugin.Config.Configuration().AnticheatConfiguration.IgnoredDetectionSpecification[(Server.Game)hit.GameName][DetectionType.Chest]
|
||||
.Any(_weaponRegex => Regex.IsMatch(((IW4Info.WeaponName)(hit.Weapon)).ToString(), _weaponRegex));
|
||||
.Any(_weaponRegex => Regex.IsMatch(hit.WeaponReference, _weaponRegex));
|
||||
}
|
||||
|
||||
catch (KeyNotFoundException)
|
||||
@ -506,6 +506,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
{
|
||||
When = hit.When,
|
||||
ClientId = ClientStats.ClientId,
|
||||
ServerId = ClientStats.ServerId,
|
||||
SessionAngleOffset = AngleDifferenceAverage,
|
||||
RecoilOffset = hitRecoilAverage,
|
||||
CurrentSessionLength = (int)(DateTime.UtcNow - ConnectionTime).TotalMinutes,
|
||||
@ -527,7 +528,8 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
SessionSPM = Math.Round(ClientStats.SessionSPM, 0),
|
||||
StrainAngleBetween = Strain.LastDistance,
|
||||
TimeSinceLastEvent = (int)Strain.LastDeltaTime,
|
||||
WeaponId = hit.Weapon,
|
||||
WeaponReference = hit.WeaponReference,
|
||||
HitLocationReference = hit.GetAdditionalProperty<string>("HitLocationReference"),
|
||||
SessionSnapHits = sessionSnapHits,
|
||||
SessionAverageSnapValue = sessionAverageSnapAmount
|
||||
};
|
||||
|
@ -1,10 +1,11 @@
|
||||
using IW4MAdmin.Plugins.Stats.Client.Game;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
|
||||
namespace Stats.Client.Abstractions
|
||||
{
|
||||
public interface IHitInfoBuilder
|
||||
{
|
||||
HitInfo Build(string[] log, int entityId, bool isSelf, bool isVictim, Server.Game gameName);
|
||||
HitInfo Build(string[] log, ParserRegex parserRegex, int entityId, bool isSelf, bool isVictim, Server.Game gameName);
|
||||
}
|
||||
}
|
@ -170,9 +170,9 @@ namespace IW4MAdmin.Plugins.Stats.Client
|
||||
return;
|
||||
}
|
||||
|
||||
var attackerHitInfo = _hitInfoBuilder.Build(match.Values.Skip(1).ToArray(), gameEvent.Origin.ClientId,
|
||||
var attackerHitInfo = _hitInfoBuilder.Build(match.Values.ToArray(), eventRegex, gameEvent.Origin.ClientId,
|
||||
gameEvent.Origin.ClientId == gameEvent.Target.ClientId, false, gameEvent.Owner.GameName);
|
||||
var victimHitInfo = _hitInfoBuilder.Build(match.Values.Skip(1).ToArray(), gameEvent.Target.ClientId,
|
||||
var victimHitInfo = _hitInfoBuilder.Build(match.Values.ToArray(), eventRegex, gameEvent.Target.ClientId,
|
||||
gameEvent.Origin.ClientId == gameEvent.Target.ClientId, true, gameEvent.Owner.GameName);
|
||||
|
||||
foreach (var hitInfo in new[] {attackerHitInfo, victimHitInfo})
|
||||
@ -241,8 +241,6 @@ namespace IW4MAdmin.Plugins.Stats.Client
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private async Task<IEnumerable<EFClientHitStatistic>> RunTasksForHitInfo(HitInfo hitInfo, long? serverId)
|
||||
{
|
||||
var weapon = await GetOrAddWeapon(hitInfo.Weapon, hitInfo.Game);
|
||||
|
@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using Stats.Client.Abstractions;
|
||||
using Stats.Client.Game;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace Stats.Client
|
||||
@ -22,7 +23,8 @@ namespace Stats.Client
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public HitInfo Build(string[] log, int entityId, bool isSelf, bool isVictim, Server.Game gameName)
|
||||
public HitInfo Build(string[] log, ParserRegex parserRegex, int entityId, bool isSelf, bool isVictim,
|
||||
Server.Game gameName)
|
||||
{
|
||||
var eventType = log[(uint) ParserRegex.GroupType.EventType].First();
|
||||
HitType hitType;
|
||||
@ -45,27 +47,37 @@ namespace Stats.Client
|
||||
hitType = eventType == 'D' ? HitType.Damage : HitType.Kill;
|
||||
}
|
||||
|
||||
var damage = 0;
|
||||
try
|
||||
{
|
||||
damage = Math.Min(MaximumDamage,
|
||||
log.Length > parserRegex.GroupMapping[ParserRegex.GroupType.Damage]
|
||||
? int.Parse(log[parserRegex.GroupMapping[ParserRegex.GroupType.Damage]])
|
||||
: 0);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
var hitInfo = new HitInfo()
|
||||
{
|
||||
EntityId = entityId,
|
||||
IsVictim = isVictim,
|
||||
HitType = hitType,
|
||||
Damage = Math.Min(MaximumDamage,
|
||||
log.Length > (uint) ParserRegex.GroupType.Damage
|
||||
? int.Parse(log[(uint) ParserRegex.GroupType.Damage])
|
||||
: 0),
|
||||
Location = log.Length > (uint) ParserRegex.GroupType.HitLocation
|
||||
? log[(uint) ParserRegex.GroupType.HitLocation]
|
||||
Damage = damage,
|
||||
Location = log.Length > parserRegex.GroupMapping[ParserRegex.GroupType.HitLocation]
|
||||
? log[parserRegex.GroupMapping[ParserRegex.GroupType.HitLocation]]
|
||||
: "Unknown",
|
||||
Weapon = log.Length == 10 ? _weaponNameParser.Parse(log[8], gameName)
|
||||
: _weaponNameParser.Parse(log[(uint) ParserRegex.GroupType.Weapon], gameName),
|
||||
MeansOfDeath = log.Length > (uint) ParserRegex.GroupType.MeansOfDeath
|
||||
? log[(uint) ParserRegex.GroupType.MeansOfDeath]
|
||||
Weapon = log.Length > parserRegex.GroupMapping[ParserRegex.GroupType.Weapon]
|
||||
? _weaponNameParser.Parse(log[parserRegex.GroupMapping[ParserRegex.GroupType.Weapon]], gameName)
|
||||
: new WeaponInfo {Name = "Unknown"},
|
||||
MeansOfDeath = log.Length > parserRegex.GroupMapping[ParserRegex.GroupType.MeansOfDeath]
|
||||
? log[parserRegex.GroupMapping[ParserRegex.GroupType.MeansOfDeath]]
|
||||
: "Unknown",
|
||||
Game = (Reference.Game) gameName
|
||||
};
|
||||
|
||||
//_logger.LogDebug("Generated new hitInfo {@hitInfo}", hitInfo);
|
||||
return hitInfo;
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ namespace Stats.Client
|
||||
public async Task Initialize()
|
||||
{
|
||||
await LoadServers();
|
||||
_distributionCache.SetCacheItem((async set =>
|
||||
_distributionCache.SetCacheItem((async (set, token) =>
|
||||
{
|
||||
var validPlayTime = _configurationHandler.Configuration()?.TopPlayersMinPlayTime ?? 3600 * 3;
|
||||
|
||||
@ -70,7 +70,7 @@ namespace Stats.Client
|
||||
return distributions;
|
||||
}), DistributionCacheKey, Utilities.IsDevelopment ? TimeSpan.FromMinutes(5) : TimeSpan.FromHours(1));
|
||||
|
||||
_maxZScoreCache.SetCacheItem(async set =>
|
||||
_maxZScoreCache.SetCacheItem(async (set, token) =>
|
||||
{
|
||||
var validPlayTime = _configurationHandler.Configuration()?.TopPlayersMinPlayTime ?? 3600 * 3;
|
||||
|
||||
@ -78,7 +78,7 @@ namespace Stats.Client
|
||||
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(validPlayTime))
|
||||
.Where(s => s.Skill > 0)
|
||||
.Where(s => s.EloRating > 0)
|
||||
.MaxAsync(s => (double?)s.ZScore);
|
||||
.MaxAsync(s => (double?)s.ZScore, token);
|
||||
return zScore ?? 0;
|
||||
}, MaxZScoreCacheKey, Utilities.IsDevelopment ? TimeSpan.FromMinutes(5) : TimeSpan.FromMinutes(30));
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user