add offline messaging feature
This commit is contained in:
parent
e2116712e7
commit
ed8067a4a2
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,7 @@ using Serilog.Context;
|
|||||||
using static SharedLibraryCore.Database.Models.EFClient;
|
using static SharedLibraryCore.Database.Models.EFClient;
|
||||||
using Data.Models;
|
using Data.Models;
|
||||||
using Data.Models.Server;
|
using Data.Models.Server;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using static Data.Models.Client.EFClient;
|
using static Data.Models.Client.EFClient;
|
||||||
|
|
||||||
namespace IW4MAdmin
|
namespace IW4MAdmin
|
||||||
@ -341,6 +342,25 @@ namespace IW4MAdmin
|
|||||||
E.Origin.Tag = clientTag.LinkedMeta.Value;
|
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);
|
await E.Origin.OnJoin(E.Origin.IPAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,6 +271,7 @@ namespace IW4MAdmin.Application
|
|||||||
|
|
||||||
// register the native commands
|
// register the native commands
|
||||||
foreach (var commandType in typeof(SharedLibraryCore.Commands.QuitCommand).Assembly.GetTypes()
|
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)))
|
.Where(_command => _command.BaseType == typeof(Command)))
|
||||||
{
|
{
|
||||||
defaultLogger.LogDebug("Registered native command type {name}", commandType.Name);
|
defaultLogger.LogDebug("Registered native command type {name}", commandType.Name);
|
||||||
|
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;
|
||||||
using Data.Models.Client.Stats;
|
using Data.Models.Client.Stats;
|
||||||
using Data.Models.Client.Stats.Reference;
|
using Data.Models.Client.Stats.Reference;
|
||||||
|
using Data.Models.Misc;
|
||||||
using Data.Models.Server;
|
using Data.Models.Server;
|
||||||
|
|
||||||
namespace Data.Context
|
namespace Data.Context
|
||||||
@ -38,6 +39,12 @@ namespace Data.Context
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region MISC
|
||||||
|
|
||||||
|
public DbSet<EFInboxMessage> InboxMessages { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
private void SetAuditColumns()
|
private void SetAuditColumns()
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<PackageId>RaidMax.IW4MAdmin.Data</PackageId>
|
<PackageId>RaidMax.IW4MAdmin.Data</PackageId>
|
||||||
<Title>RaidMax.IW4MAdmin.Data</Title>
|
<Title>RaidMax.IW4MAdmin.Data</Title>
|
||||||
<Authors />
|
<Authors />
|
||||||
<PackageVersion>1.0.3</PackageVersion>
|
<PackageVersion>1.0.4</PackageVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<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");
|
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 =>
|
modelBuilder.Entity("Data.Models.Server.EFServer", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("ServerId")
|
b.Property<long>("ServerId")
|
||||||
@ -1282,6 +1320,25 @@ namespace Data.Migrations.MySql
|
|||||||
.IsRequired();
|
.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 =>
|
modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
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");
|
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 =>
|
modelBuilder.Entity("Data.Models.Server.EFServer", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("ServerId")
|
b.Property<long>("ServerId")
|
||||||
@ -1307,6 +1346,25 @@ namespace Data.Migrations.Postgresql
|
|||||||
.IsRequired();
|
.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 =>
|
modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
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");
|
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 =>
|
modelBuilder.Entity("Data.Models.Server.EFServer", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("ServerId")
|
b.Property<long>("ServerId")
|
||||||
@ -1281,6 +1319,25 @@ namespace Data.Migrations.Sqlite
|
|||||||
.IsRequired();
|
.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 =>
|
modelBuilder.Entity("Data.Models.Server.EFServerStatistics", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Data.Abstractions;
|
||||||
|
|
||||||
namespace Stats.Models
|
namespace Stats.Models
|
||||||
{
|
{
|
||||||
public class AuditFields
|
public class AuditFields : IAuditFields
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
public DateTime CreatedDateTime { get; set; } = DateTime.UtcNow;
|
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; }
|
||||||
|
}
|
||||||
|
}
|
@ -529,6 +529,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
StrainAngleBetween = Strain.LastDistance,
|
StrainAngleBetween = Strain.LastDistance,
|
||||||
TimeSinceLastEvent = (int)Strain.LastDeltaTime,
|
TimeSinceLastEvent = (int)Strain.LastDeltaTime,
|
||||||
WeaponReference = hit.WeaponReference,
|
WeaponReference = hit.WeaponReference,
|
||||||
|
HitLocationReference = hit.GetAdditionalProperty<string>("HitLocationReference"),
|
||||||
SessionSnapHits = sessionSnapHits,
|
SessionSnapHits = sessionSnapHits,
|
||||||
SessionAverageSnapValue = sessionAverageSnapAmount
|
SessionAverageSnapValue = sessionAverageSnapAmount
|
||||||
};
|
};
|
||||||
|
@ -639,6 +639,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
TimeSinceLastAttack = long.Parse(lastAttackTime),
|
TimeSinceLastAttack = long.Parse(lastAttackTime),
|
||||||
GameName = (int) attacker.CurrentServer.GameName
|
GameName = (int) attacker.CurrentServer.GameName
|
||||||
};
|
};
|
||||||
|
|
||||||
|
hit.SetAdditionalProperty("HitLocationReference", hitLoc);
|
||||||
|
|
||||||
if (hit.HitLoc == (int) IW4Info.HitLocation.shield)
|
if (hit.HitLoc == (int) IW4Info.HitLocation.shield)
|
||||||
{
|
{
|
||||||
|
@ -35,7 +35,7 @@ namespace SharedLibraryCore.Commands
|
|||||||
|
|
||||||
if (!gameEvent.CanPerformActionOnTarget())
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,11 +53,18 @@ namespace SharedLibraryCore.Commands
|
|||||||
gameEvent.Owner.Manager.AddEvent(impersonatedCommandEvent);
|
gameEvent.Owner.Manager.AddEvent(impersonatedCommandEvent);
|
||||||
|
|
||||||
var result = await impersonatedCommandEvent.WaitAsync(Utilities.DefaultCommandTimeout, gameEvent.Owner.Manager.CancellationToken);
|
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
|
// 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -529,7 +529,7 @@ namespace SharedLibraryCore.Database.Models
|
|||||||
using (LogContext.PushProperty("Server", CurrentServer?.ToString()))
|
using (LogContext.PushProperty("Server", CurrentServer?.ToString()))
|
||||||
{
|
{
|
||||||
Utilities.DefaultLogger.LogInformation("Client {client} is joining the game from {source}", ToString(), ipAddress.HasValue ? "Status" : "Log");
|
Utilities.DefaultLogger.LogInformation("Client {client} is joining the game from {source}", ToString(), ipAddress.HasValue ? "Status" : "Log");
|
||||||
|
|
||||||
if (ipAddress != null)
|
if (ipAddress != null)
|
||||||
{
|
{
|
||||||
IPAddress = ipAddress;
|
IPAddress = ipAddress;
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.10" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.10" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.10" />
|
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.10" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<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="Serilog.AspNetCore" Version="3.4.0" />
|
||||||
<PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" />
|
<PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -1070,6 +1070,31 @@ namespace SharedLibraryCore
|
|||||||
return value?.ToNumericalString(precision);
|
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)
|
public static string FindRuleForReason(this string reason, ApplicationConfiguration appConfig, Server server)
|
||||||
{
|
{
|
||||||
// allow for penalty presets
|
// allow for penalty presets
|
||||||
|
Loading…
Reference in New Issue
Block a user