Compare commits
7 Commits
2021.07.02
...
2021.07.12
Author | SHA1 | Date | |
---|---|---|---|
67c2406325 | |||
e2ea5c6ce0 | |||
5ef00d6dae | |||
5921098dce | |||
31ee71260a | |||
ed8067a4a2 | |||
e2116712e7 |
@ -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)
|
||||
{
|
||||
|
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -435,8 +435,8 @@
|
||||
"Name": "oilrig"
|
||||
},
|
||||
{
|
||||
"Name": "Village",
|
||||
"Alias": "co_hunted"
|
||||
"Alias": "Village",
|
||||
"Name": "co_hunted"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System.Text;
|
||||
using Integrations.Cod;
|
||||
@ -26,21 +27,15 @@ 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,
|
||||
"COD" => new CodRConConnection(ipEndpoint, password,
|
||||
_serviceProvider.GetRequiredService<ILogger<CodRConConnection>>(), GameEncoding),
|
||||
"Source" => new SourceRConConnection(_serviceProvider.GetRequiredService<ILogger<SourceRConConnection>>(),
|
||||
_serviceProvider.GetRequiredService<IRConClientFactory>(), ipAddress, port, password),
|
||||
_serviceProvider.GetRequiredService<IRConClientFactory>(), ipEndpoint, password),
|
||||
_ => throw new ArgumentException($"No supported RCon engine available for '{rconEngine}'")
|
||||
};
|
||||
}
|
||||
|
@ -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
|
||||
@ -341,6 +343,25 @@ namespace IW4MAdmin
|
||||
E.Origin.Tag = clientTag.LinkedMeta.Value;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -593,7 +614,7 @@ namespace IW4MAdmin
|
||||
{
|
||||
try
|
||||
{
|
||||
message = Manager.GetApplicationSettings().Configuration()
|
||||
message = _serviceProvider.GetRequiredService<DefaultSettings>()
|
||||
.QuickMessages
|
||||
.First(_qm => _qm.Game == GameName)
|
||||
.Messages[E.Data.Substring(1)];
|
||||
@ -1039,6 +1060,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);
|
||||
|
||||
@ -1048,7 +1079,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");
|
||||
@ -1118,8 +1149,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;
|
||||
|
@ -271,6 +271,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,6 +333,8 @@ 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")
|
||||
@ -360,6 +363,7 @@ namespace IW4MAdmin.Application
|
||||
|
||||
serviceCollection
|
||||
.AddBaseLogger(appConfig)
|
||||
.AddSingleton(defaultConfig)
|
||||
.AddSingleton<IServiceCollection>(_serviceProvider => serviceCollection)
|
||||
.AddSingleton<IConfigurationHandler<DefaultSettings>, BaseConfigurationHandler<DefaultSettings>>()
|
||||
.AddSingleton((IConfigurationHandler<ApplicationConfiguration>) appConfigHandler)
|
||||
|
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; }
|
||||
}
|
||||
}
|
@ -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
|
||||
@ -38,6 +39,12 @@ namespace Data.Context
|
||||
|
||||
#endregion
|
||||
|
||||
#region MISC
|
||||
|
||||
public DbSet<EFInboxMessage> InboxMessages { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
private void SetAuditColumns()
|
||||
{
|
||||
return;
|
||||
|
@ -8,7 +8,7 @@
|
||||
<PackageId>RaidMax.IW4MAdmin.Data</PackageId>
|
||||
<Title>RaidMax.IW4MAdmin.Data</Title>
|
||||
<Authors />
|
||||
<PackageVersion>1.0.3</PackageVersion>
|
||||
<PackageVersion>1.0.4</PackageVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
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");
|
||||
}
|
||||
}
|
||||
}
|
@ -935,6 +935,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")
|
||||
@ -1282,6 +1320,25 @@ 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.EFServerStatistics", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
|
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");
|
||||
}
|
||||
}
|
||||
}
|
@ -958,6 +958,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")
|
||||
@ -1307,6 +1346,25 @@ 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.EFServerStatistics", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
|
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");
|
||||
}
|
||||
}
|
||||
}
|
@ -934,6 +934,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")
|
||||
@ -1281,6 +1319,25 @@ 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.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;
|
||||
|
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; }
|
||||
}
|
||||
}
|
@ -32,12 +32,12 @@ namespace Integrations.Cod
|
||||
private readonly ILogger _log;
|
||||
private readonly Encoding _gameEncoding;
|
||||
|
||||
public CodRConConnection(string ipAddress, int port, string password, ILogger<CodRConConnection> log, Encoding gameEncoding)
|
||||
public CodRConConnection(IPEndPoint ipEndpoint, string password, ILogger<CodRConConnection> log, Encoding gameEncoding)
|
||||
{
|
||||
Endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
|
||||
_gameEncoding = gameEncoding;
|
||||
RConPassword = password;
|
||||
_gameEncoding = gameEncoding;
|
||||
_log = log;
|
||||
Endpoint = ipEndpoint;
|
||||
}
|
||||
|
||||
public void SetConfiguration(IRConParser parser)
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ let eventParser;
|
||||
|
||||
const plugin = {
|
||||
author: 'RaidMax',
|
||||
version: 0.2,
|
||||
version: 0.3,
|
||||
name: 'CS:GO Parser',
|
||||
engine: 'Source',
|
||||
isParser: true,
|
||||
@ -66,14 +66,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 +83,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,
|
||||
@ -66,14 +66,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 +83,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:';
|
||||
|
||||
|
@ -529,6 +529,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
StrainAngleBetween = Strain.LastDistance,
|
||||
TimeSinceLastEvent = (int)Strain.LastDeltaTime,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -24,14 +24,14 @@ namespace Stats.Helpers
|
||||
{
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
private readonly ILogger _logger;
|
||||
private readonly ApplicationConfiguration _appConfig;
|
||||
private readonly DefaultSettings _defaultSettings;
|
||||
private List<EFServer> serverCache;
|
||||
|
||||
public ChatResourceQueryHelper(ILogger<ChatResourceQueryHelper> logger, IDatabaseContextFactory contextFactory, ApplicationConfiguration appConfig)
|
||||
public ChatResourceQueryHelper(ILogger<ChatResourceQueryHelper> logger, IDatabaseContextFactory contextFactory, DefaultSettings defaultSettings)
|
||||
{
|
||||
_contextFactory = contextFactory;
|
||||
_logger = logger;
|
||||
_appConfig = appConfig;
|
||||
_defaultSettings = defaultSettings;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@ -106,20 +106,22 @@ namespace Stats.Helpers
|
||||
{
|
||||
message.IsHidden = serverCache.Any(server => server.ServerId == message.ServerId && server.IsPasswordProtected);
|
||||
|
||||
if (message.Message.IsQuickMessage())
|
||||
if (!message.Message.IsQuickMessage())
|
||||
{
|
||||
try
|
||||
{
|
||||
var quickMessages = _appConfig
|
||||
.QuickMessages
|
||||
.First(_qm => _qm.Game == message.GameName);
|
||||
message.Message = quickMessages.Messages[message.Message.Substring(1)];
|
||||
message.IsQuickMessage = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
message.Message = message.Message.Substring(1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var quickMessages = _defaultSettings
|
||||
.QuickMessages
|
||||
.First(_qm => _qm.Game == message.GameName);
|
||||
message.Message = quickMessages.Messages[message.Message.Substring(1)];
|
||||
message.IsQuickMessage = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
message.Message = message.Message.Substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -639,6 +639,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
TimeSinceLastAttack = long.Parse(lastAttackTime),
|
||||
GameName = (int) attacker.CurrentServer.GameName
|
||||
};
|
||||
|
||||
hit.SetAdditionalProperty("HitLocationReference", hitLoc);
|
||||
|
||||
if (hit.HitLoc == (int) IW4Info.HitLocation.shield)
|
||||
{
|
||||
|
@ -35,7 +35,7 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
if (!gameEvent.CanPerformActionOnTarget())
|
||||
{
|
||||
gameEvent.Origin.Tell(_translationLookup["COMMANDS_RUN_AS_FAIL_PERM"]);
|
||||
gameEvent.Origin.Tell(_translationLookup["COMMANDS_RUN_AS_FAIL_PERM"].FormatExt(gameEvent.Target.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -53,11 +53,18 @@ namespace SharedLibraryCore.Commands
|
||||
gameEvent.Owner.Manager.AddEvent(impersonatedCommandEvent);
|
||||
|
||||
var result = await impersonatedCommandEvent.WaitAsync(Utilities.DefaultCommandTimeout, gameEvent.Owner.Manager.CancellationToken);
|
||||
await result.WaitAsync(Utilities.DefaultCommandTimeout, gameEvent.Owner.Manager.CancellationToken);
|
||||
|
||||
// remove the added command response
|
||||
foreach (var output in result.Output)
|
||||
// todo: something weird happening making this change required
|
||||
var responses = gameEvent.Owner.Manager.ProcessingEvents
|
||||
.Where(ev => ev.Value.CorrelationId == impersonatedCommandEvent.CorrelationId)
|
||||
.SelectMany(ev => ev.Value.Output)
|
||||
.ToList();
|
||||
|
||||
foreach (var output in responses)
|
||||
{
|
||||
gameEvent.Origin.Tell(_translationLookup["COMMANDS_RUN_AS_SUCCESS"].FormatExt(output));
|
||||
await gameEvent.Origin.Tell(_translationLookup["COMMANDS_RUN_AS_SUCCESS"].FormatExt(output)).WaitAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using static Data.Models.Client.EFClient;
|
||||
|
||||
namespace SharedLibraryCore.Configuration
|
||||
@ -149,10 +150,13 @@ namespace SharedLibraryCore.Configuration
|
||||
[ConfigurationIgnore] public int MinimumNameLength { get; set; } = 3;
|
||||
[ConfigurationIgnore] public string Id { get; set; }
|
||||
[ConfigurationIgnore] public string SubscriptionId { get; set; }
|
||||
[Obsolete("Moved to DefaultSettings")]
|
||||
[ConfigurationIgnore] public MapConfiguration[] Maps { get; set; }
|
||||
[Obsolete("Moved to DefaultSettings")]
|
||||
[ConfigurationIgnore] public QuickMessageConfiguration[] QuickMessages { get; set; }
|
||||
|
||||
[ConfigurationIgnore]
|
||||
[JsonIgnore]
|
||||
public string WebfrontUrl => string.IsNullOrEmpty(ManualWebfrontUrl)
|
||||
? WebfrontBindUrl?.Replace("0.0.0.0", "127.0.0.1")
|
||||
: ManualWebfrontUrl;
|
||||
|
@ -87,12 +87,8 @@ namespace SharedLibraryCore.Configuration
|
||||
|
||||
while (string.IsNullOrEmpty(IPAddress))
|
||||
{
|
||||
string input = Utilities.PromptString(loc["SETUP_SERVER_IP"]);
|
||||
|
||||
if (System.Net.IPAddress.TryParse(input, out System.Net.IPAddress ip))
|
||||
{
|
||||
IPAddress = input;
|
||||
}
|
||||
var input = Utilities.PromptString(loc["SETUP_SERVER_IP"]);
|
||||
IPAddress = input;
|
||||
}
|
||||
|
||||
Port = Utilities.PromptInt(loc["SETUP_SERVER_PORT"], null, 1, ushort.MaxValue);
|
||||
|
@ -10,10 +10,6 @@ namespace SharedLibraryCore.Configuration.Validation
|
||||
{
|
||||
public ServerConfigurationValidator()
|
||||
{
|
||||
RuleFor(_server => _server.IPAddress)
|
||||
.NotEmpty()
|
||||
.Must(_address => IPAddress.TryParse(_address, out _));
|
||||
|
||||
RuleFor(_server => _server.Port)
|
||||
.InclusiveBetween(1, ushort.MaxValue);
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
namespace SharedLibraryCore.Interfaces
|
||||
using System.Net;
|
||||
|
||||
namespace SharedLibraryCore.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// defines the capabilities of an RCon connection factory
|
||||
@ -8,11 +10,10 @@
|
||||
/// <summary>
|
||||
/// creates an rcon connection instance
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">ip address of the server</param>
|
||||
/// <param name="port">port of the server</param>
|
||||
/// <param name="ipEndpoint">ip address and port of the server</param>
|
||||
/// <param name="password"> password of the server</param>
|
||||
/// <param name="rconEngine">engine to create the rcon connection to</param>
|
||||
/// <returns>instance of rcon connection</returns>
|
||||
IRConConnection CreateConnection(string ipAddress, int port, string password, string rconEngine);
|
||||
IRConConnection CreateConnection(IPEndPoint ipEndpoint, string password, string rconEngine);
|
||||
}
|
||||
}
|
||||
|
@ -529,7 +529,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
using (LogContext.PushProperty("Server", CurrentServer?.ToString()))
|
||||
{
|
||||
Utilities.DefaultLogger.LogInformation("Client {client} is joining the game from {source}", ToString(), ipAddress.HasValue ? "Status" : "Log");
|
||||
|
||||
|
||||
if (ipAddress != null)
|
||||
{
|
||||
IPAddress = ipAddress;
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@ -58,7 +59,9 @@ namespace SharedLibraryCore
|
||||
InitializeAutoMessages();
|
||||
}
|
||||
|
||||
public long EndPoint => Convert.ToInt64($"{IP.Replace(".", "")}{Port}");
|
||||
public long EndPoint => IPAddress.TryParse(IP, out _)
|
||||
? Convert.ToInt64($"{IP.Replace(".", "")}{Port}")
|
||||
: $"{IP.Replace(".", "")}{Port}".GetStableHashCode();
|
||||
|
||||
/// <summary>
|
||||
/// Returns list of all current players
|
||||
@ -251,17 +254,6 @@ namespace SharedLibraryCore
|
||||
/// </summary>
|
||||
abstract public void InitializeTokens();
|
||||
|
||||
/// <summary>
|
||||
/// Read the map configuration
|
||||
/// </summary>
|
||||
protected void InitializeMaps()
|
||||
{
|
||||
Maps = new List<Map>();
|
||||
var gameMaps = Manager.GetApplicationSettings().Configuration().Maps.FirstOrDefault(m => m.Game == GameName);
|
||||
if (gameMaps != null)
|
||||
Maps.AddRange(gameMaps.Maps);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the messages to be broadcasted
|
||||
/// </summary>
|
||||
@ -314,7 +306,7 @@ namespace SharedLibraryCore
|
||||
{
|
||||
get
|
||||
{
|
||||
return Clients.Where(p => p != null/* && !p.IsBot*/).Count();
|
||||
return Clients.Count(p => p != null && !p.IsBot);
|
||||
}
|
||||
}
|
||||
public int MaxClients { get; protected set; }
|
||||
@ -331,7 +323,11 @@ namespace SharedLibraryCore
|
||||
public SemaphoreSlim EventProcessing { get; private set; }
|
||||
|
||||
// Internal
|
||||
/// <summary>
|
||||
/// this is actually the hostname now
|
||||
/// </summary>
|
||||
public string IP { get; protected set; }
|
||||
public IPEndPoint ResolvedIpEndPoint { get; protected set; }
|
||||
public string Version { get; protected set; }
|
||||
public bool IsInitialized { get; set; }
|
||||
protected readonly ILogger ServerLogger;
|
||||
|
@ -44,7 +44,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.10" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.Data" Version="1.0.3" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.Data" Version="1.0.4" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
|
||||
<PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
@ -1070,6 +1070,31 @@ namespace SharedLibraryCore
|
||||
return value?.ToNumericalString(precision);
|
||||
}
|
||||
|
||||
public static string[] FragmentMessageForDisplay(this string message)
|
||||
{
|
||||
var messages = new List<string>();
|
||||
var length = 48;
|
||||
|
||||
if (message.Length <= length)
|
||||
{
|
||||
return new[] {message};
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < message.Length - length; i += length)
|
||||
{
|
||||
messages.Add(new string(message.Skip(i).Take(length).ToArray()));
|
||||
}
|
||||
|
||||
var left = message.Length - length;
|
||||
|
||||
if (left > 0)
|
||||
{
|
||||
messages.Add(new string(message.Skip(i).Take(left).ToArray()));
|
||||
}
|
||||
|
||||
return messages.ToArray();
|
||||
}
|
||||
|
||||
public static string FindRuleForReason(this string reason, ApplicationConfiguration appConfig, Server server)
|
||||
{
|
||||
// allow for penalty presets
|
||||
|
@ -125,7 +125,9 @@ namespace WebfrontCore.Controllers.API
|
||||
Origin = privilegedClient,
|
||||
Type = GameEvent.EventType.Login,
|
||||
Owner = Manager.GetServers().First(),
|
||||
Data = HttpContext.Connection.RemoteIpAddress.ToString()
|
||||
Data = HttpContext.Request.Headers.ContainsKey("X-Forwarded-For")
|
||||
? HttpContext.Request.Headers["X-Forwarded-For"].ToString()
|
||||
: HttpContext.Connection.RemoteIpAddress.ToString()
|
||||
});
|
||||
|
||||
return Ok();
|
||||
@ -152,7 +154,9 @@ namespace WebfrontCore.Controllers.API
|
||||
Origin = Client,
|
||||
Type = GameEvent.EventType.Logout,
|
||||
Owner = Manager.GetServers().First(),
|
||||
Data = HttpContext.Connection.RemoteIpAddress.ToString()
|
||||
Data = HttpContext.Request.Headers.ContainsKey("X-Forwarded-For")
|
||||
? HttpContext.Request.Headers["X-Forwarded-For"].ToString()
|
||||
: HttpContext.Connection.RemoteIpAddress.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace WebfrontCore.Controllers
|
||||
{
|
||||
@ -57,7 +58,9 @@ namespace WebfrontCore.Controllers
|
||||
Origin = privilegedClient,
|
||||
Type = GameEvent.EventType.Login,
|
||||
Owner = Manager.GetServers().First(),
|
||||
Data = HttpContext.Connection.RemoteIpAddress.ToString()
|
||||
Data = HttpContext.Request.Headers.ContainsKey("X-Forwarded-For")
|
||||
? HttpContext.Request.Headers["X-Forwarded-For"].ToString()
|
||||
: HttpContext.Connection.RemoteIpAddress.ToString()
|
||||
});
|
||||
|
||||
return Ok();
|
||||
@ -82,7 +85,9 @@ namespace WebfrontCore.Controllers
|
||||
Origin = Client,
|
||||
Type = GameEvent.EventType.Logout,
|
||||
Owner = Manager.GetServers().First(),
|
||||
Data = HttpContext.Connection.RemoteIpAddress.ToString()
|
||||
Data = HttpContext.Request.Headers.ContainsKey("X-Forwarded-For")
|
||||
? HttpContext.Request.Headers["X-Forwarded-For"].ToString()
|
||||
: HttpContext.Connection.RemoteIpAddress.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -113,6 +113,7 @@ namespace WebfrontCore
|
||||
services.AddSingleton<IResourceQueryHelper<StatsInfoRequest, AdvancedStatsInfo>, AdvancedClientStatsResourceQueryHelper>();
|
||||
services.AddSingleton(typeof(IDataValueCache<,>), typeof(DataValueCache<,>));
|
||||
// todo: this needs to be handled more gracefully
|
||||
services.AddSingleton(Program.ApplicationServiceProvider.GetRequiredService<DefaultSettings>());
|
||||
services.AddSingleton(Program.ApplicationServiceProvider.GetRequiredService<ILoggerFactory>());
|
||||
services.AddSingleton(Program.ApplicationServiceProvider.GetService<IConfigurationHandlerFactory>());
|
||||
services.AddSingleton(Program.ApplicationServiceProvider.GetService<IDatabaseContextFactory>());
|
||||
|
@ -12,7 +12,7 @@ namespace WebfrontCore.ViewComponents
|
||||
{
|
||||
public IViewComponentResult Invoke(Game? game)
|
||||
{
|
||||
var servers = Program.Manager.GetServers().Where(_server => !game.HasValue ? true : _server.GameName == game);
|
||||
var servers = Program.Manager.GetServers().Where(_server => !game.HasValue || _server.GameName == game);
|
||||
|
||||
var serverInfo = servers.Select(s => new ServerInfo()
|
||||
{
|
||||
@ -36,8 +36,8 @@ namespace WebfrontCore.ViewComponents
|
||||
}).ToList(),
|
||||
ChatHistory = s.ChatHistory.ToList(),
|
||||
Online = !s.Throttled,
|
||||
IPAddress = $"{(IPAddress.Parse(s.IP).IsInternal() ? Program.Manager.ExternalIPAddress : s.IP)}:{s.Port}",
|
||||
ConnectProtocolUrl = s.EventParser.URLProtocolFormat.FormatExt(IPAddress.Parse(s.IP).IsInternal() ? Program.Manager.ExternalIPAddress : s.IP, s.Port)
|
||||
IPAddress = $"{(s.ResolvedIpEndPoint.Address.IsInternal() ? Program.Manager.ExternalIPAddress : s.IP)}:{s.Port}",
|
||||
ConnectProtocolUrl = s.EventParser.URLProtocolFormat.FormatExt(s.ResolvedIpEndPoint.Address.IsInternal() ? Program.Manager.ExternalIPAddress : s.IP, s.Port)
|
||||
}).ToList();
|
||||
return View("_List", serverInfo);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
const int maxItems = 5;
|
||||
const string headshotKey = "MOD_HEAD_SHOT";
|
||||
const string headshotKey2 = "headshot";
|
||||
const string meleeKey = "MOD_MELEE";
|
||||
|
||||
var suicideKeys = new[] {"MOD_SUICIDE", "MOD_FALLING"};
|
||||
@ -138,7 +139,7 @@
|
||||
: null;
|
||||
|
||||
var headShots = allPerServer.Any()
|
||||
? allPerServer.Where(hit => hit.MeansOfDeath?.Name == headshotKey).Sum(hit => hit.HitCount)
|
||||
? allPerServer.Where(hit => hit.MeansOfDeath?.Name == headshotKey || hit.HitLocation?.Name == headshotKey2).Sum(hit => hit.HitCount)
|
||||
: (int?) null; // want to default to -- in ui instead of 0
|
||||
|
||||
var meleeKills = allPerServer.Any()
|
||||
|
Reference in New Issue
Block a user