Compare commits
28 Commits
2023.06.10
...
release/pr
Author | SHA1 | Date | |
---|---|---|---|
95eb73da6e | |||
6ec0a24ca2 | |||
|
03b5b8b143 | ||
|
005a8b050d | ||
|
2c99f7b48e | ||
|
13d4ec3033 | ||
|
e6cdae5a6b | ||
|
d69a9ecf56 | ||
|
b6c32181b0 | ||
|
2017eebeba | ||
|
3192fe35e6 | ||
|
c1dace4af6 | ||
|
5c6ae3146a | ||
|
2e99db2275 | ||
|
79eec08590 | ||
|
69691f75f4 | ||
|
648eec25f2 | ||
|
80774853b6 | ||
|
08edbf9bd4 | ||
|
e472468c02 | ||
|
d4e266ed94 | ||
|
4e02e7841f | ||
|
dc707f75b3 | ||
|
41efe26a48 | ||
|
4740479ace | ||
|
6f28bc5b0b | ||
|
47ed505fae | ||
|
e2c07daece |
@ -1251,6 +1251,10 @@
|
|||||||
"Alias": "Call of the Dead",
|
"Alias": "Call of the Dead",
|
||||||
"Name": "zombie_coast"
|
"Name": "zombie_coast"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Shangri-La",
|
||||||
|
"Name": "zombie_temple"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Alias": "Moon",
|
"Alias": "Moon",
|
||||||
"Name": "zombie_moon"
|
"Name": "zombie_moon"
|
||||||
|
@ -48,8 +48,8 @@ namespace IW4MAdmin.Application.Extensions
|
|||||||
loggerConfig = loggerConfig.WriteTo.Console(
|
loggerConfig = loggerConfig.WriteTo.Console(
|
||||||
outputTemplate:
|
outputTemplate:
|
||||||
"[{Timestamp:HH:mm:ss} {Server} {Level:u3}] {Message:lj}{NewLine}{Exception}")
|
"[{Timestamp:HH:mm:ss} {Server} {Level:u3}] {Message:lj}{NewLine}{Exception}")
|
||||||
; //.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
||||||
//.MinimumLevel.Debug();
|
.MinimumLevel.Debug();
|
||||||
}
|
}
|
||||||
|
|
||||||
_defaultLogger = loggerConfig.CreateLogger();
|
_defaultLogger = loggerConfig.CreateLogger();
|
||||||
|
@ -129,7 +129,7 @@ public class BaseConfigurationHandlerV2<TConfigurationType> : IConfigurationHand
|
|||||||
await _onIo.WaitAsync();
|
await _onIo.WaitAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
await using var fileStream = File.OpenWrite(_path);
|
await using var fileStream = File.Create(_path);
|
||||||
await JsonSerializer.SerializeAsync(fileStream, configuration, _serializerOptions);
|
await JsonSerializer.SerializeAsync(fileStream, configuration, _serializerOptions);
|
||||||
await fileStream.DisposeAsync();
|
await fileStream.DisposeAsync();
|
||||||
_configurationInstance = configuration;
|
_configurationInstance = configuration;
|
||||||
|
@ -432,7 +432,12 @@ namespace IW4MAdmin.Application
|
|||||||
appConfigHandler.BuildAsync().GetAwaiter().GetResult();
|
appConfigHandler.BuildAsync().GetAwaiter().GetResult();
|
||||||
var commandConfigHandler = new BaseConfigurationHandler<CommandConfiguration>("CommandConfiguration");
|
var commandConfigHandler = new BaseConfigurationHandler<CommandConfiguration>("CommandConfiguration");
|
||||||
commandConfigHandler.BuildAsync().GetAwaiter().GetResult();
|
commandConfigHandler.BuildAsync().GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
if (appConfigHandler.Configuration()?.MasterUrl == new Uri("http://api.raidmax.org:5000"))
|
||||||
|
{
|
||||||
|
appConfigHandler.Configuration().MasterUrl = new ApplicationConfiguration().MasterUrl;
|
||||||
|
}
|
||||||
|
|
||||||
var appConfig = appConfigHandler.Configuration();
|
var appConfig = appConfigHandler.Configuration();
|
||||||
var masterUri = Utilities.IsDevelopment
|
var masterUri = Utilities.IsDevelopment
|
||||||
? new Uri("http://127.0.0.1:8080")
|
? new Uri("http://127.0.0.1:8080")
|
||||||
|
@ -8,9 +8,11 @@ using Data.Abstractions;
|
|||||||
using Data.Models;
|
using Data.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Configuration;
|
||||||
using SharedLibraryCore.Dtos;
|
using SharedLibraryCore.Dtos;
|
||||||
using SharedLibraryCore.Helpers;
|
using SharedLibraryCore.Helpers;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using WebfrontCore.Permissions;
|
||||||
using WebfrontCore.QueryHelpers.Models;
|
using WebfrontCore.QueryHelpers.Models;
|
||||||
using EFClient = Data.Models.Client.EFClient;
|
using EFClient = Data.Models.Client.EFClient;
|
||||||
|
|
||||||
@ -18,6 +20,7 @@ namespace IW4MAdmin.Application.QueryHelpers;
|
|||||||
|
|
||||||
public class ClientResourceQueryHelper : IResourceQueryHelper<ClientResourceRequest, ClientResourceResponse>
|
public class ClientResourceQueryHelper : IResourceQueryHelper<ClientResourceRequest, ClientResourceResponse>
|
||||||
{
|
{
|
||||||
|
public ApplicationConfiguration _appConfig { get; }
|
||||||
private readonly IDatabaseContextFactory _contextFactory;
|
private readonly IDatabaseContextFactory _contextFactory;
|
||||||
private readonly IGeoLocationService _geoLocationService;
|
private readonly IGeoLocationService _geoLocationService;
|
||||||
|
|
||||||
@ -27,8 +30,10 @@ public class ClientResourceQueryHelper : IResourceQueryHelper<ClientResourceRequ
|
|||||||
public EFAlias Alias { get; set; }
|
public EFAlias Alias { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientResourceQueryHelper(IDatabaseContextFactory contextFactory, IGeoLocationService geoLocationService)
|
public ClientResourceQueryHelper(IDatabaseContextFactory contextFactory, IGeoLocationService geoLocationService,
|
||||||
|
ApplicationConfiguration appConfig)
|
||||||
{
|
{
|
||||||
|
_appConfig = appConfig;
|
||||||
_contextFactory = contextFactory;
|
_contextFactory = contextFactory;
|
||||||
_geoLocationService = geoLocationService;
|
_geoLocationService = geoLocationService;
|
||||||
}
|
}
|
||||||
@ -75,7 +80,9 @@ public class ClientResourceQueryHelper : IResourceQueryHelper<ClientResourceRequ
|
|||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(query.ClientIp))
|
if (!string.IsNullOrWhiteSpace(query.ClientIp))
|
||||||
{
|
{
|
||||||
clientAliases = SearchByIp(query, clientAliases);
|
clientAliases = SearchByIp(query, clientAliases,
|
||||||
|
_appConfig.HasPermission(query.RequesterPermission, WebfrontEntity.ClientIPAddress,
|
||||||
|
WebfrontPermission.Read));
|
||||||
}
|
}
|
||||||
|
|
||||||
var iqGroupedClientAliases = clientAliases.GroupBy(a => new { a.Client.ClientId, a.Client.LastConnection });
|
var iqGroupedClientAliases = clientAliases.GroupBy(a => new { a.Client.ClientId, a.Client.LastConnection });
|
||||||
@ -203,7 +210,7 @@ public class ClientResourceQueryHelper : IResourceQueryHelper<ClientResourceRequ
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static IQueryable<ClientAlias> SearchByIp(ClientResourceRequest query,
|
private static IQueryable<ClientAlias> SearchByIp(ClientResourceRequest query,
|
||||||
IQueryable<ClientAlias> clientAliases)
|
IQueryable<ClientAlias> clientAliases, bool canSearchIP)
|
||||||
{
|
{
|
||||||
var ipString = query.ClientIp.Trim();
|
var ipString = query.ClientIp.Trim();
|
||||||
var ipAddress = ipString.ConvertToIP();
|
var ipAddress = ipString.ConvertToIP();
|
||||||
@ -213,7 +220,7 @@ public class ClientResourceQueryHelper : IResourceQueryHelper<ClientResourceRequ
|
|||||||
clientAliases = clientAliases.Where(clientAlias =>
|
clientAliases = clientAliases.Where(clientAlias =>
|
||||||
clientAlias.Alias.IPAddress != null && clientAlias.Alias.IPAddress == ipAddress);
|
clientAlias.Alias.IPAddress != null && clientAlias.Alias.IPAddress == ipAddress);
|
||||||
}
|
}
|
||||||
else
|
else if(canSearchIP)
|
||||||
{
|
{
|
||||||
clientAliases = clientAliases.Where(clientAlias =>
|
clientAliases = clientAliases.Where(clientAlias =>
|
||||||
EF.Functions.Like(clientAlias.Alias.SearchableIPAddress, $"{ipString}%"));
|
EF.Functions.Like(clientAlias.Alias.SearchableIPAddress, $"{ipString}%"));
|
||||||
|
@ -194,10 +194,14 @@ namespace IW4MAdmin.Application.RConParsers
|
|||||||
foreach (var line in response)
|
foreach (var line in response)
|
||||||
{
|
{
|
||||||
var regex = Regex.Match(line, parserRegex.Pattern);
|
var regex = Regex.Match(line, parserRegex.Pattern);
|
||||||
if (regex.Success && parserRegex.GroupMapping.ContainsKey(groupType))
|
|
||||||
|
if (!regex.Success || !parserRegex.GroupMapping.ContainsKey(groupType))
|
||||||
{
|
{
|
||||||
value = regex.Groups[parserRegex.GroupMapping[groupType]].ToString();
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value = regex.Groups[parserRegex.GroupMapping[groupType]].ToString();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value == null)
|
if (value == null)
|
||||||
@ -304,7 +308,7 @@ namespace IW4MAdmin.Application.RConParsers
|
|||||||
{
|
{
|
||||||
networkIdString = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConNetworkId]];
|
networkIdString = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConNetworkId]];
|
||||||
|
|
||||||
networkId = networkIdString.IsBotGuid() || (ip == null && ping == 999) ?
|
networkId = networkIdString.IsBotGuid() || (ip == null && ping is 999 or 0) ?
|
||||||
name.GenerateGuidFromString() :
|
name.GenerateGuidFromString() :
|
||||||
networkIdString.ConvertGuidToLong(Configuration.GuidNumberStyle);
|
networkIdString.ConvertGuidToLong(Configuration.GuidNumberStyle);
|
||||||
}
|
}
|
||||||
|
@ -153,6 +153,8 @@ namespace Data.Context
|
|||||||
|
|
||||||
modelBuilder.Entity<EFClientConnectionHistory>(ent => ent.HasIndex(history => history.CreatedDateTime));
|
modelBuilder.Entity<EFClientConnectionHistory>(ent => ent.HasIndex(history => history.CreatedDateTime));
|
||||||
|
|
||||||
|
modelBuilder.Entity<EFServerSnapshot>(ent => ent.HasIndex(snapshot => snapshot.CapturedAt));
|
||||||
|
|
||||||
// force full name for database conversion
|
// force full name for database conversion
|
||||||
modelBuilder.Entity<EFClient>().ToTable("EFClients");
|
modelBuilder.Entity<EFClient>().ToTable("EFClients");
|
||||||
modelBuilder.Entity<EFAlias>().ToTable("EFAlias");
|
modelBuilder.Entity<EFAlias>().ToTable("EFAlias");
|
||||||
|
1644
Data/Migrations/MySql/20230705133025_AddIndexToEFServerSnapshotCapturedAt.Designer.cs
generated
Normal file
1644
Data/Migrations/MySql/20230705133025_AddIndexToEFServerSnapshotCapturedAt.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Data.Migrations.MySql
|
||||||
|
{
|
||||||
|
public partial class AddIndexToEFServerSnapshotCapturedAt : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_EFServerSnapshot_CapturedAt",
|
||||||
|
table: "EFServerSnapshot",
|
||||||
|
column: "CapturedAt");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_EFServerSnapshot_CapturedAt",
|
||||||
|
table: "EFServerSnapshot");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -814,6 +814,7 @@ namespace Data.Migrations.MySql
|
|||||||
|
|
||||||
b.Property<string>("SearchableIPAddress")
|
b.Property<string>("SearchableIPAddress")
|
||||||
.ValueGeneratedOnAddOrUpdate()
|
.ValueGeneratedOnAddOrUpdate()
|
||||||
|
.HasMaxLength(255)
|
||||||
.HasColumnType("varchar(255)")
|
.HasColumnType("varchar(255)")
|
||||||
.HasComputedColumnSql("((IPAddress & 255) || '.' || ((IPAddress >> 8) & 255)) || '.' || ((IPAddress >> 16) & 255) || '.' || ((IPAddress >> 24) & 255)", true);
|
.HasComputedColumnSql("((IPAddress & 255) || '.' || ((IPAddress >> 8) & 255)) || '.' || ((IPAddress >> 16) & 255) || '.' || ((IPAddress >> 24) & 255)", true);
|
||||||
|
|
||||||
@ -1110,6 +1111,8 @@ namespace Data.Migrations.MySql
|
|||||||
|
|
||||||
b.HasKey("ServerSnapshotId");
|
b.HasKey("ServerSnapshotId");
|
||||||
|
|
||||||
|
b.HasIndex("CapturedAt");
|
||||||
|
|
||||||
b.HasIndex("MapId");
|
b.HasIndex("MapId");
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
b.HasIndex("ServerId");
|
||||||
|
1701
Data/Migrations/Postgresql/20230705133135_AddIndexToEFServerSnapshotCapturedAt.Designer.cs
generated
Normal file
1701
Data/Migrations/Postgresql/20230705133135_AddIndexToEFServerSnapshotCapturedAt.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,52 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Data.Migrations.Postgresql
|
||||||
|
{
|
||||||
|
public partial class AddIndexToEFServerSnapshotCapturedAt : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "SearchableIPAddress",
|
||||||
|
table: "EFAlias",
|
||||||
|
type: "character varying(255)",
|
||||||
|
maxLength: 255,
|
||||||
|
nullable: true,
|
||||||
|
computedColumnSql: "((IPAddress & 255) || '.' || ((IPAddress >> 8) & 255)) || '.' || ((IPAddress >> 16) & 255) || '.' || ((IPAddress >> 24) & 255)",
|
||||||
|
stored: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "text",
|
||||||
|
oldNullable: true,
|
||||||
|
oldComputedColumnSql: "((IPAddress & 255) || '.' || ((IPAddress >> 8) & 255)) || '.' || ((IPAddress >> 16) & 255) || '.' || ((IPAddress >> 24) & 255)",
|
||||||
|
oldStored: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_EFServerSnapshot_CapturedAt",
|
||||||
|
table: "EFServerSnapshot",
|
||||||
|
column: "CapturedAt");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_EFServerSnapshot_CapturedAt",
|
||||||
|
table: "EFServerSnapshot");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "SearchableIPAddress",
|
||||||
|
table: "EFAlias",
|
||||||
|
type: "text",
|
||||||
|
nullable: true,
|
||||||
|
computedColumnSql: "((IPAddress & 255) || '.' || ((IPAddress >> 8) & 255)) || '.' || ((IPAddress >> 16) & 255) || '.' || ((IPAddress >> 24) & 255)",
|
||||||
|
stored: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(255)",
|
||||||
|
oldMaxLength: 255,
|
||||||
|
oldNullable: true,
|
||||||
|
oldComputedColumnSql: "((IPAddress & 255) || '.' || ((IPAddress >> 8) & 255)) || '.' || ((IPAddress >> 16) & 255) || '.' || ((IPAddress >> 24) & 255)",
|
||||||
|
oldStored: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -853,7 +853,8 @@ namespace Data.Migrations.Postgresql
|
|||||||
|
|
||||||
b.Property<string>("SearchableIPAddress")
|
b.Property<string>("SearchableIPAddress")
|
||||||
.ValueGeneratedOnAddOrUpdate()
|
.ValueGeneratedOnAddOrUpdate()
|
||||||
.HasColumnType("text")
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)")
|
||||||
.HasComputedColumnSql("((IPAddress & 255) || '.' || ((IPAddress >> 8) & 255)) || '.' || ((IPAddress >> 16) & 255) || '.' || ((IPAddress >> 24) & 255)", true);
|
.HasComputedColumnSql("((IPAddress & 255) || '.' || ((IPAddress >> 8) & 255)) || '.' || ((IPAddress >> 16) & 255) || '.' || ((IPAddress >> 24) & 255)", true);
|
||||||
|
|
||||||
b.Property<string>("SearchableName")
|
b.Property<string>("SearchableName")
|
||||||
@ -1163,6 +1164,8 @@ namespace Data.Migrations.Postgresql
|
|||||||
|
|
||||||
b.HasKey("ServerSnapshotId");
|
b.HasKey("ServerSnapshotId");
|
||||||
|
|
||||||
|
b.HasIndex("CapturedAt");
|
||||||
|
|
||||||
b.HasIndex("MapId");
|
b.HasIndex("MapId");
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
b.HasIndex("ServerId");
|
||||||
|
1642
Data/Migrations/Sqlite/20230705132822_AddIndexToEFServerSnapshotCapturedAt.Designer.cs
generated
Normal file
1642
Data/Migrations/Sqlite/20230705132822_AddIndexToEFServerSnapshotCapturedAt.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Data.Migrations.Sqlite
|
||||||
|
{
|
||||||
|
public partial class AddIndexToEFServerSnapshotCapturedAt : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_EFServerSnapshot_CapturedAt",
|
||||||
|
table: "EFServerSnapshot",
|
||||||
|
column: "CapturedAt");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_EFServerSnapshot_CapturedAt",
|
||||||
|
table: "EFServerSnapshot");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -812,6 +812,7 @@ namespace Data.Migrations.Sqlite
|
|||||||
|
|
||||||
b.Property<string>("SearchableIPAddress")
|
b.Property<string>("SearchableIPAddress")
|
||||||
.ValueGeneratedOnAddOrUpdate()
|
.ValueGeneratedOnAddOrUpdate()
|
||||||
|
.HasMaxLength(255)
|
||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasComputedColumnSql("((IPAddress & 255) || '.' || ((IPAddress >> 8) & 255)) || '.' || ((IPAddress >> 16) & 255) || '.' || ((IPAddress >> 24) & 255)", true);
|
.HasComputedColumnSql("((IPAddress & 255) || '.' || ((IPAddress >> 8) & 255)) || '.' || ((IPAddress >> 16) & 255) || '.' || ((IPAddress >> 24) & 255)", true);
|
||||||
|
|
||||||
@ -1108,6 +1109,8 @@ namespace Data.Migrations.Sqlite
|
|||||||
|
|
||||||
b.HasKey("ServerSnapshotId");
|
b.HasKey("ServerSnapshotId");
|
||||||
|
|
||||||
|
b.HasIndex("CapturedAt");
|
||||||
|
|
||||||
b.HasIndex("MapId");
|
b.HasIndex("MapId");
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
b.HasIndex("ServerId");
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
T7 = 8,
|
T7 = 8,
|
||||||
SHG1 = 9,
|
SHG1 = 9,
|
||||||
CSGO = 10,
|
CSGO = 10,
|
||||||
H1 = 11
|
H1 = 11,
|
||||||
|
L4D2 = 12,
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ConnectionType
|
public enum ConnectionType
|
||||||
|
@ -8,7 +8,7 @@ Init()
|
|||||||
Setup()
|
Setup()
|
||||||
{
|
{
|
||||||
level endon( "game_ended" );
|
level endon( "game_ended" );
|
||||||
|
|
||||||
// setup default vars
|
// setup default vars
|
||||||
level.eventBus = spawnstruct();
|
level.eventBus = spawnstruct();
|
||||||
level.eventBus.inVar = "sv_iw4madmin_in";
|
level.eventBus.inVar = "sv_iw4madmin_in";
|
||||||
@ -16,7 +16,7 @@ Setup()
|
|||||||
level.eventBus.failKey = "fail";
|
level.eventBus.failKey = "fail";
|
||||||
level.eventBus.timeoutKey = "timeout";
|
level.eventBus.timeoutKey = "timeout";
|
||||||
level.eventBus.timeout = 30;
|
level.eventBus.timeout = 30;
|
||||||
|
|
||||||
level.commonFunctions = spawnstruct();
|
level.commonFunctions = spawnstruct();
|
||||||
level.commonFunctions.setDvar = "SetDvarIfUninitialized";
|
level.commonFunctions.setDvar = "SetDvarIfUninitialized";
|
||||||
level.commonFunctions.getPlayerFromClientNum = "GetPlayerFromClientNum";
|
level.commonFunctions.getPlayerFromClientNum = "GetPlayerFromClientNum";
|
||||||
@ -46,12 +46,12 @@ Setup()
|
|||||||
level.commonKeys.busDir = "sv_iw4madmin_integration_busdir";
|
level.commonKeys.busDir = "sv_iw4madmin_integration_busdir";
|
||||||
level.eventBus.inLocation = "";
|
level.eventBus.inLocation = "";
|
||||||
level.eventBus.outLocation = "";
|
level.eventBus.outLocation = "";
|
||||||
|
|
||||||
level.notifyTypes = spawnstruct();
|
level.notifyTypes = spawnstruct();
|
||||||
level.notifyTypes.gameFunctionsInitialized = "GameFunctionsInitialized";
|
level.notifyTypes.gameFunctionsInitialized = "GameFunctionsInitialized";
|
||||||
level.notifyTypes.sharedFunctionsInitialized = "SharedFunctionsInitialized";
|
level.notifyTypes.sharedFunctionsInitialized = "SharedFunctionsInitialized";
|
||||||
level.notifyTypes.integrationBootstrapInitialized = "IntegrationBootstrapInitialized";
|
level.notifyTypes.integrationBootstrapInitialized = "IntegrationBootstrapInitialized";
|
||||||
|
|
||||||
level.clientDataKey = "clientData";
|
level.clientDataKey = "clientData";
|
||||||
|
|
||||||
level.eventTypes = spawnstruct();
|
level.eventTypes = spawnstruct();
|
||||||
@ -61,9 +61,9 @@ Setup()
|
|||||||
level.eventTypes.setClientDataRequested = "SetClientDataRequested";
|
level.eventTypes.setClientDataRequested = "SetClientDataRequested";
|
||||||
level.eventTypes.setClientDataCompleted = "SetClientDataCompleted";
|
level.eventTypes.setClientDataCompleted = "SetClientDataCompleted";
|
||||||
level.eventTypes.executeCommandRequested = "ExecuteCommandRequested";
|
level.eventTypes.executeCommandRequested = "ExecuteCommandRequested";
|
||||||
|
|
||||||
level.iw4madminIntegrationDebug = 0;
|
level.iw4madminIntegrationDebug = 0;
|
||||||
|
|
||||||
// map the event type to the handler
|
// map the event type to the handler
|
||||||
level.eventCallbacks = [];
|
level.eventCallbacks = [];
|
||||||
level.eventCallbacks[level.eventTypes.clientDataReceived] = ::OnClientDataReceived;
|
level.eventCallbacks[level.eventTypes.clientDataReceived] = ::OnClientDataReceived;
|
||||||
@ -76,14 +76,14 @@ Setup()
|
|||||||
|
|
||||||
level.iw4madminIntegrationDebug = GetDvarInt( "sv_iw4madmin_integration_debug" );
|
level.iw4madminIntegrationDebug = GetDvarInt( "sv_iw4madmin_integration_debug" );
|
||||||
InitializeLogger();
|
InitializeLogger();
|
||||||
|
|
||||||
wait ( 0.05 * 2 ); // needed to give script engine time to propagate notifies
|
wait ( 0.05 * 2 ); // needed to give script engine time to propagate notifies
|
||||||
|
|
||||||
level notify( level.notifyTypes.integrationBootstrapInitialized );
|
level notify( level.notifyTypes.integrationBootstrapInitialized );
|
||||||
level waittill( level.notifyTypes.gameFunctionsInitialized );
|
level waittill( level.notifyTypes.gameFunctionsInitialized );
|
||||||
|
|
||||||
LogDebug( "Integration received notify that game functions are initialized" );
|
LogDebug( "Integration received notify that game functions are initialized" );
|
||||||
|
|
||||||
_SetDvarIfUninitialized( level.eventBus.inVar, "" );
|
_SetDvarIfUninitialized( level.eventBus.inVar, "" );
|
||||||
_SetDvarIfUninitialized( level.eventBus.outVar, "" );
|
_SetDvarIfUninitialized( level.eventBus.outVar, "" );
|
||||||
_SetDvarIfUninitialized( level.commonKeys.enabled, 1 );
|
_SetDvarIfUninitialized( level.commonKeys.enabled, 1 );
|
||||||
@ -93,12 +93,12 @@ Setup()
|
|||||||
_SetDvarIfUninitialized( "GroupSeparatorChar", "" );
|
_SetDvarIfUninitialized( "GroupSeparatorChar", "" );
|
||||||
_SetDvarIfUninitialized( "RecordSeparatorChar", "" );
|
_SetDvarIfUninitialized( "RecordSeparatorChar", "" );
|
||||||
_SetDvarIfUninitialized( "UnitSeparatorChar", "" );
|
_SetDvarIfUninitialized( "UnitSeparatorChar", "" );
|
||||||
|
|
||||||
if ( GetDvarInt( level.commonKeys.enabled ) != 1 )
|
if ( GetDvarInt( level.commonKeys.enabled ) != 1 )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// start long running tasks
|
// start long running tasks
|
||||||
thread MonitorEvents();
|
thread MonitorEvents();
|
||||||
thread MonitorBus();
|
thread MonitorBus();
|
||||||
@ -113,7 +113,7 @@ MonitorEvents()
|
|||||||
level waittill( level.eventTypes.eventAvailable, event );
|
level waittill( level.eventTypes.eventAvailable, event );
|
||||||
|
|
||||||
LogDebug( "Processing Event " + event.type + "-" + event.subtype );
|
LogDebug( "Processing Event " + event.type + "-" + event.subtype );
|
||||||
|
|
||||||
eventHandler = level.eventCallbacks[event.type];
|
eventHandler = level.eventCallbacks[event.type];
|
||||||
|
|
||||||
if ( IsDefined( eventHandler ) )
|
if ( IsDefined( eventHandler ) )
|
||||||
@ -161,11 +161,13 @@ _SetDvarIfUninitialized( dvarName, dvarValue )
|
|||||||
|
|
||||||
_GetPlayerFromClientNum( clientNum )
|
_GetPlayerFromClientNum( clientNum )
|
||||||
{
|
{
|
||||||
|
assertEx( clientNum >= 0, "clientNum cannot be negative" );
|
||||||
|
|
||||||
if ( clientNum < 0 )
|
if ( clientNum < 0 )
|
||||||
{
|
{
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( i = 0; i < level.players.size; i++ )
|
for ( i = 0; i < level.players.size; i++ )
|
||||||
{
|
{
|
||||||
if ( level.players[i] getEntityNumber() == clientNum )
|
if ( level.players[i] getEntityNumber() == clientNum )
|
||||||
@ -329,7 +331,7 @@ BuildEventRequest( responseExpected, eventType, eventSubtype, entOrId, data )
|
|||||||
{
|
{
|
||||||
data = "";
|
data = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !IsDefined( eventSubtype ) )
|
if ( !IsDefined( eventSubtype ) )
|
||||||
{
|
{
|
||||||
eventSubtype = "None";
|
eventSubtype = "None";
|
||||||
@ -344,9 +346,9 @@ BuildEventRequest( responseExpected, eventType, eventSubtype, entOrId, data )
|
|||||||
{
|
{
|
||||||
entOrId = entOrId getEntityNumber();
|
entOrId = entOrId getEntityNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
request = "0";
|
request = "0";
|
||||||
|
|
||||||
if ( responseExpected )
|
if ( responseExpected )
|
||||||
{
|
{
|
||||||
request = "1";
|
request = "1";
|
||||||
@ -355,14 +357,14 @@ BuildEventRequest( responseExpected, eventType, eventSubtype, entOrId, data )
|
|||||||
data = BuildDataString( data );
|
data = BuildDataString( data );
|
||||||
groupSeparator = GetSubStr( GetDvar( "GroupSeparatorChar" ), 0, 1 );
|
groupSeparator = GetSubStr( GetDvar( "GroupSeparatorChar" ), 0, 1 );
|
||||||
request = request + groupSeparator + eventType + groupSeparator + eventSubtype + groupSeparator + entOrId + groupSeparator + data;
|
request = request + groupSeparator + eventType + groupSeparator + eventSubtype + groupSeparator + entOrId + groupSeparator + data;
|
||||||
|
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
MonitorBus()
|
MonitorBus()
|
||||||
{
|
{
|
||||||
level endon( level.eventTypes.gameEnd );
|
level endon( level.eventTypes.gameEnd );
|
||||||
|
|
||||||
level.eventBus.inLocation = level.eventBus.inVar + "_" + GetDvar( "net_port" );
|
level.eventBus.inLocation = level.eventBus.inVar + "_" + GetDvar( "net_port" );
|
||||||
level.eventBus.outLocation = level.eventBus.outVar + "_" + GetDvar( "net_port" );
|
level.eventBus.outLocation = level.eventBus.outVar + "_" + GetDvar( "net_port" );
|
||||||
|
|
||||||
@ -372,7 +374,7 @@ MonitorBus()
|
|||||||
for( ;; )
|
for( ;; )
|
||||||
{
|
{
|
||||||
wait ( 0.1 );
|
wait ( 0.1 );
|
||||||
|
|
||||||
// check to see if IW4MAdmin is ready to receive more data
|
// check to see if IW4MAdmin is ready to receive more data
|
||||||
inVal = [[level.busMethods[level.commonFunctions.getInboundData]]]( level.eventBus.inLocation );
|
inVal = [[level.busMethods[level.commonFunctions.getInboundData]]]( level.eventBus.inLocation );
|
||||||
|
|
||||||
@ -380,19 +382,19 @@ MonitorBus()
|
|||||||
{
|
{
|
||||||
level notify( "bus_ready" );
|
level notify( "bus_ready" );
|
||||||
}
|
}
|
||||||
|
|
||||||
eventString = [[level.busMethods[level.commonFunctions.getOutboundData]]]( level.eventBus.outLocation );
|
eventString = [[level.busMethods[level.commonFunctions.getOutboundData]]]( level.eventBus.outLocation );
|
||||||
|
|
||||||
if ( !IsDefined( eventString ) || eventString == "" )
|
if ( !IsDefined( eventString ) || eventString == "" )
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogDebug( "-> " + eventString );
|
LogDebug( "-> " + eventString );
|
||||||
|
|
||||||
groupSeparator = GetSubStr( GetDvar( "GroupSeparatorChar" ), 0, 1 );
|
groupSeparator = GetSubStr( GetDvar( "GroupSeparatorChar" ), 0, 1 );
|
||||||
NotifyEvent( strtok( eventString, groupSeparator ) );
|
NotifyEvent( strtok( eventString, groupSeparator ) );
|
||||||
|
|
||||||
[[level.busMethods[level.commonFunctions.SetOutboundData]]]( level.eventBus.outLocation, "" );
|
[[level.busMethods[level.commonFunctions.SetOutboundData]]]( level.eventBus.outLocation, "" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -404,7 +406,7 @@ QueueEvent( request, eventType, notifyEntity )
|
|||||||
start = GetTime();
|
start = GetTime();
|
||||||
maxWait = level.eventBus.timeout * 1000; // 30 seconds
|
maxWait = level.eventBus.timeout * 1000; // 30 seconds
|
||||||
timedOut = "";
|
timedOut = "";
|
||||||
|
|
||||||
while ( [[level.busMethods[level.commonFunctions.getInboundData]]]( level.eventBus.inLocation ) != "" && ( GetTime() - start ) < maxWait )
|
while ( [[level.busMethods[level.commonFunctions.getInboundData]]]( level.eventBus.inLocation ) != "" && ( GetTime() - start ) < maxWait )
|
||||||
{
|
{
|
||||||
level [[level.overrideMethods[level.commonFunctions.waittillNotifyOrTimeout]]]( "bus_ready", 1 );
|
level [[level.overrideMethods[level.commonFunctions.waittillNotifyOrTimeout]]]( "bus_ready", 1 );
|
||||||
@ -415,10 +417,10 @@ QueueEvent( request, eventType, notifyEntity )
|
|||||||
timedOut = "set";
|
timedOut = "set";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
timedOut = "unset";
|
timedOut = "unset";
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( timedOut == "set" )
|
if ( timedOut == "set" )
|
||||||
{
|
{
|
||||||
LogDebug( "Timed out waiting for response..." );
|
LogDebug( "Timed out waiting for response..." );
|
||||||
@ -432,9 +434,9 @@ QueueEvent( request, eventType, notifyEntity )
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogDebug( "<- " + request );
|
LogDebug( "<- " + request );
|
||||||
|
|
||||||
[[level.busMethods[level.commonFunctions.setInboundData]]]( level.eventBus.inLocation, request );
|
[[level.busMethods[level.commonFunctions.setInboundData]]]( level.eventBus.inLocation, request );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,10 +447,10 @@ ParseDataString( data )
|
|||||||
LogDebug( "No data to parse" );
|
LogDebug( "No data to parse" );
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
dataParts = strtok( data, GetSubStr( GetDvar( "RecordSeparatorChar" ), 0, 1 ) );
|
dataParts = strtok( data, GetSubStr( GetDvar( "RecordSeparatorChar" ), 0, 1 ) );
|
||||||
dict = [];
|
dict = [];
|
||||||
|
|
||||||
for ( i = 0; i < dataParts.size; i++ )
|
for ( i = 0; i < dataParts.size; i++ )
|
||||||
{
|
{
|
||||||
part = dataParts[i];
|
part = dataParts[i];
|
||||||
@ -458,7 +460,7 @@ ParseDataString( data )
|
|||||||
dict[key] = value;
|
dict[key] = value;
|
||||||
dict[i] = key;
|
dict[i] = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,14 +497,14 @@ NotifyEvent( eventInfo )
|
|||||||
{
|
{
|
||||||
origin = [[level.overrideMethods[level.commonFunctions.getPlayerFromClientNum]]]( int( eventInfo[3] ) );
|
origin = [[level.overrideMethods[level.commonFunctions.getPlayerFromClientNum]]]( int( eventInfo[3] ) );
|
||||||
target = [[level.overrideMethods[level.commonFunctions.getPlayerFromClientNum]]]( int( eventInfo[4] ) );
|
target = [[level.overrideMethods[level.commonFunctions.getPlayerFromClientNum]]]( int( eventInfo[4] ) );
|
||||||
|
|
||||||
event = spawnstruct();
|
event = spawnstruct();
|
||||||
event.type = eventInfo[1];
|
event.type = eventInfo[1];
|
||||||
event.subtype = eventInfo[2];
|
event.subtype = eventInfo[2];
|
||||||
event.data = ParseDataString( eventInfo[5] );
|
event.data = ParseDataString( eventInfo[5] );
|
||||||
event.origin = origin;
|
event.origin = origin;
|
||||||
event.target = target;
|
event.target = target;
|
||||||
|
|
||||||
if ( int( eventInfo[3] ) != -1 && !IsDefined( origin ) )
|
if ( int( eventInfo[3] ) != -1 && !IsDefined( origin ) )
|
||||||
{
|
{
|
||||||
LogDebug( "origin is null but the slot id is " + int( eventInfo[3] ) );
|
LogDebug( "origin is null but the slot id is " + int( eventInfo[3] ) );
|
||||||
@ -529,7 +531,7 @@ AddClientCommand( commandName, shouldRunAsTarget, callback, shouldOverwrite )
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
level.clientCommandCallbacks[commandName] = callback;
|
level.clientCommandCallbacks[commandName] = callback;
|
||||||
level.clientCommandRusAsTarget[commandName] = shouldRunAsTarget == true; //might speed up things later in case someone gives us a string or number instead of a boolean
|
level.clientCommandRusAsTarget[commandName] = shouldRunAsTarget == true; //might speed up things later in case someone gives us a string or number instead of a boolean
|
||||||
}
|
}
|
||||||
@ -540,6 +542,7 @@ AddClientCommand( commandName, shouldRunAsTarget, callback, shouldOverwrite )
|
|||||||
|
|
||||||
OnClientDataReceived( event )
|
OnClientDataReceived( event )
|
||||||
{
|
{
|
||||||
|
assertEx( isDefined( self ), "player entity is not defined");
|
||||||
clientData = self.pers[level.clientDataKey];
|
clientData = self.pers[level.clientDataKey];
|
||||||
|
|
||||||
if ( event.subtype == "Fail" )
|
if ( event.subtype == "Fail" )
|
||||||
@ -555,15 +558,15 @@ OnClientDataReceived( event )
|
|||||||
{
|
{
|
||||||
clientData.meta = [];
|
clientData.meta = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
metaKey = event.data[0];
|
metaKey = event.data[0];
|
||||||
clientData.meta[metaKey] = event.data[metaKey];
|
clientData.meta[metaKey] = event.data[metaKey];
|
||||||
|
|
||||||
LogDebug( "Meta Key=" + CoerceUndefined( metaKey ) + ", Meta Value=" + CoerceUndefined( event.data[metaKey] ) );
|
LogDebug( "Meta Key=" + CoerceUndefined( metaKey ) + ", Meta Value=" + CoerceUndefined( event.data[metaKey] ) );
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
clientData.permissionLevel = event.data["level"];
|
clientData.permissionLevel = event.data["level"];
|
||||||
clientData.clientId = event.data["clientId"];
|
clientData.clientId = event.data["clientId"];
|
||||||
clientData.lastConnection = event.data["lastConnection"];
|
clientData.lastConnection = event.data["lastConnection"];
|
||||||
@ -577,7 +580,7 @@ OnExecuteCommand( event )
|
|||||||
{
|
{
|
||||||
data = event.data;
|
data = event.data;
|
||||||
response = "";
|
response = "";
|
||||||
|
|
||||||
command = level.clientCommandCallbacks[event.subtype];
|
command = level.clientCommandCallbacks[event.subtype];
|
||||||
runAsTarget = level.clientCommandRusAsTarget[event.subtype];
|
runAsTarget = level.clientCommandRusAsTarget[event.subtype];
|
||||||
executionContextEntity = event.origin;
|
executionContextEntity = event.origin;
|
||||||
@ -586,7 +589,7 @@ OnExecuteCommand( event )
|
|||||||
{
|
{
|
||||||
executionContextEntity = event.target;
|
executionContextEntity = event.target;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( IsDefined( command ) )
|
if ( IsDefined( command ) )
|
||||||
{
|
{
|
||||||
if ( IsDefined( executionContextEntity ) )
|
if ( IsDefined( executionContextEntity ) )
|
||||||
@ -602,7 +605,7 @@ OnExecuteCommand( event )
|
|||||||
{
|
{
|
||||||
LogDebug( "Unknown Client command->" + event.subtype );
|
LogDebug( "Unknown Client command->" + event.subtype );
|
||||||
}
|
}
|
||||||
|
|
||||||
// send back the response to the origin, but only if they're not the target
|
// send back the response to the origin, but only if they're not the target
|
||||||
if ( IsDefined( response ) && response != "" && IsPlayer( event.origin ) && event.origin != event.target )
|
if ( IsDefined( response ) && response != "" && IsPlayer( event.origin ) && event.origin != event.target )
|
||||||
{
|
{
|
||||||
|
@ -37,6 +37,8 @@ Setup()
|
|||||||
RegisterClientCommands();
|
RegisterClientCommands();
|
||||||
|
|
||||||
level notify( level.notifyTypes.gameFunctionsInitialized );
|
level notify( level.notifyTypes.gameFunctionsInitialized );
|
||||||
|
|
||||||
|
scripts\_integration_base::_SetDvarIfUninitialized( level.commonKeys.busdir, GetDvar( "fs_homepath" ) + "userraw/" + "scriptdata" );
|
||||||
|
|
||||||
if ( GetDvarInt( level.commonKeys.enabled ) != 1 )
|
if ( GetDvarInt( level.commonKeys.enabled ) != 1 )
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#include common_scripts\utility;
|
#include common_scripts\utility;
|
||||||
|
|
||||||
|
#inline scripts\_integration_utility;
|
||||||
|
|
||||||
Init()
|
Init()
|
||||||
{
|
{
|
||||||
thread Setup();
|
thread Setup();
|
||||||
@ -9,12 +11,12 @@ Setup()
|
|||||||
{
|
{
|
||||||
level endon( "game_ended" );
|
level endon( "game_ended" );
|
||||||
waittillframeend;
|
waittillframeend;
|
||||||
|
|
||||||
level waittill( level.notifyTypes.sharedFunctionsInitialized );
|
level waittill( level.notifyTypes.sharedFunctionsInitialized );
|
||||||
level.eventBus.gamename = "IW5";
|
level.eventBus.gamename = "IW5";
|
||||||
|
|
||||||
scripts\_integration_base::RegisterLogger( ::Log2Console );
|
scripts\_integration_base::RegisterLogger( ::Log2Console );
|
||||||
|
|
||||||
level.overrideMethods[level.commonFunctions.getTotalShotsFired] = ::GetTotalShotsFired;
|
level.overrideMethods[level.commonFunctions.getTotalShotsFired] = ::GetTotalShotsFired;
|
||||||
level.overrideMethods[level.commonFunctions.setDvar] = ::SetDvarIfUninitializedWrapper;
|
level.overrideMethods[level.commonFunctions.setDvar] = ::SetDvarIfUninitializedWrapper;
|
||||||
level.overrideMethods[level.commonFunctions.waittillNotifyOrTimeout] = ::WaitillNotifyOrTimeoutWrapper;
|
level.overrideMethods[level.commonFunctions.waittillNotifyOrTimeout] = ::WaitillNotifyOrTimeoutWrapper;
|
||||||
@ -22,7 +24,7 @@ Setup()
|
|||||||
level.overrideMethods[level.commonFunctions.getXuid] = ::GetXuidWrapper;
|
level.overrideMethods[level.commonFunctions.getXuid] = ::GetXuidWrapper;
|
||||||
level.overrideMethods[level.commonFunctions.waitTillAnyTimeout] = ::WaitTillAnyTimeout;
|
level.overrideMethods[level.commonFunctions.waitTillAnyTimeout] = ::WaitTillAnyTimeout;
|
||||||
RegisterClientCommands();
|
RegisterClientCommands();
|
||||||
|
|
||||||
level notify( level.notifyTypes.gameFunctionsInitialized );
|
level notify( level.notifyTypes.gameFunctionsInitialized );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,45 +84,36 @@ WaitTillAnyTimeout( timeOut, string1, string2, string3, string4, string5 )
|
|||||||
|
|
||||||
GiveWeaponImpl( event, data )
|
GiveWeaponImpl( event, data )
|
||||||
{
|
{
|
||||||
if ( !IsAlive( self ) )
|
_IS_ALIVE( self );
|
||||||
{
|
|
||||||
return self.name + "^7 is not alive";
|
|
||||||
}
|
|
||||||
|
|
||||||
self IPrintLnBold( "You have been given a new weapon" );
|
self IPrintLnBold( "You have been given a new weapon" );
|
||||||
self GiveWeapon( data["weaponName"] );
|
self GiveWeapon( data["weaponName"] );
|
||||||
self SwitchToWeapon( data["weaponName"] );
|
self SwitchToWeapon( data["weaponName"] );
|
||||||
|
|
||||||
return self.name + "^7 has been given ^5" + data["weaponName"];
|
return self.name + "^7 has been given ^5" + data["weaponName"];
|
||||||
}
|
}
|
||||||
|
|
||||||
TakeWeaponsImpl()
|
TakeWeaponsImpl()
|
||||||
{
|
{
|
||||||
if ( !IsAlive( self ) )
|
_IS_ALIVE( self );
|
||||||
{
|
|
||||||
return self.name + "^7 is not alive";
|
|
||||||
}
|
|
||||||
|
|
||||||
self TakeAllWeapons();
|
self TakeAllWeapons();
|
||||||
self IPrintLnBold( "All your weapons have been taken" );
|
self IPrintLnBold( "All your weapons have been taken" );
|
||||||
|
|
||||||
return "Took weapons from " + self.name;
|
return "Took weapons from " + self.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
TeamSwitchImpl()
|
TeamSwitchImpl()
|
||||||
{
|
{
|
||||||
if ( !IsAlive( self ) )
|
_IS_ALIVE( self );
|
||||||
{
|
|
||||||
return self + "^7 is not alive";
|
|
||||||
}
|
|
||||||
|
|
||||||
team = level.allies;
|
team = level.allies;
|
||||||
|
|
||||||
if ( self.team == "allies" )
|
if ( self.team == "allies" )
|
||||||
{
|
{
|
||||||
team = level.axis;
|
team = level.axis;
|
||||||
}
|
}
|
||||||
|
|
||||||
self IPrintLnBold( "You are being team switched" );
|
self IPrintLnBold( "You are being team switched" );
|
||||||
wait( 2 );
|
wait( 2 );
|
||||||
self [[team]]();
|
self [[team]]();
|
||||||
@ -130,10 +123,7 @@ TeamSwitchImpl()
|
|||||||
|
|
||||||
LockControlsImpl()
|
LockControlsImpl()
|
||||||
{
|
{
|
||||||
if ( !IsAlive( self ) )
|
_IS_ALIVE( self );
|
||||||
{
|
|
||||||
return self.name + "^7 is not alive";
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !IsDefined ( self.isControlLocked ) )
|
if ( !IsDefined ( self.isControlLocked ) )
|
||||||
{
|
{
|
||||||
@ -149,11 +139,11 @@ LockControlsImpl()
|
|||||||
info = [];
|
info = [];
|
||||||
info[ "alertType" ] = "Alert!";
|
info[ "alertType" ] = "Alert!";
|
||||||
info[ "message" ] = "You have been frozen!";
|
info[ "message" ] = "You have been frozen!";
|
||||||
|
|
||||||
self AlertImpl( undefined, info );
|
self AlertImpl( undefined, info );
|
||||||
|
|
||||||
self.isControlLocked = true;
|
self.isControlLocked = true;
|
||||||
|
|
||||||
return self.name + "\'s controls are locked";
|
return self.name + "\'s controls are locked";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -170,11 +160,13 @@ LockControlsImpl()
|
|||||||
|
|
||||||
NoClipImpl()
|
NoClipImpl()
|
||||||
{
|
{
|
||||||
|
_VERIFY_PLAYER_ENT( self );
|
||||||
|
|
||||||
if ( !IsAlive( self ) )
|
if ( !IsAlive( self ) )
|
||||||
{
|
{
|
||||||
self IPrintLnBold( "You are not alive" );
|
self IPrintLnBold( "You are not alive" );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !IsDefined ( self.isNoClipped ) )
|
if ( !IsDefined ( self.isNoClipped ) )
|
||||||
{
|
{
|
||||||
self.isNoClipped = false;
|
self.isNoClipped = false;
|
||||||
@ -184,29 +176,29 @@ NoClipImpl()
|
|||||||
{
|
{
|
||||||
SetDvar( "sv_cheats", 1 );
|
SetDvar( "sv_cheats", 1 );
|
||||||
self SetClientDvar( "cg_thirdperson", 1 );
|
self SetClientDvar( "cg_thirdperson", 1 );
|
||||||
|
|
||||||
self God();
|
self God();
|
||||||
self Noclip();
|
self Noclip();
|
||||||
self Hide();
|
self Hide();
|
||||||
SetDvar( "sv_cheats", 0 );
|
SetDvar( "sv_cheats", 0 );
|
||||||
|
|
||||||
self.isNoClipped = true;
|
self.isNoClipped = true;
|
||||||
|
|
||||||
self IPrintLnBold( "NoClip enabled" );
|
self IPrintLnBold( "NoClip enabled" );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SetDvar( "sv_cheats", 1 );
|
SetDvar( "sv_cheats", 1 );
|
||||||
self SetClientDvar( "cg_thirdperson", 0 );
|
self SetClientDvar( "cg_thirdperson", 0 );
|
||||||
|
|
||||||
self God();
|
self God();
|
||||||
self Noclip();
|
self Noclip();
|
||||||
self Hide();
|
self Hide();
|
||||||
|
|
||||||
SetDvar( "sv_cheats", 0 );
|
SetDvar( "sv_cheats", 0 );
|
||||||
|
|
||||||
self.isNoClipped = false;
|
self.isNoClipped = false;
|
||||||
|
|
||||||
self IPrintLnBold( "NoClip disabled" );
|
self IPrintLnBold( "NoClip disabled" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,12 +207,13 @@ NoClipImpl()
|
|||||||
|
|
||||||
HideImpl()
|
HideImpl()
|
||||||
{
|
{
|
||||||
|
_VERIFY_PLAYER_ENT( self );
|
||||||
|
|
||||||
if ( !IsAlive( self ) )
|
if ( !IsAlive( self ) )
|
||||||
{
|
{
|
||||||
self IPrintLnBold( "You are not alive" );
|
self IPrintLnBold( "You are not alive" );
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !IsDefined ( self.isHidden ) )
|
if ( !IsDefined ( self.isHidden ) )
|
||||||
{
|
{
|
||||||
self.isHidden = false;
|
self.isHidden = false;
|
||||||
@ -230,26 +223,26 @@ HideImpl()
|
|||||||
{
|
{
|
||||||
SetDvar( "sv_cheats", 1 );
|
SetDvar( "sv_cheats", 1 );
|
||||||
self SetClientDvar( "cg_thirdperson", 1 );
|
self SetClientDvar( "cg_thirdperson", 1 );
|
||||||
|
|
||||||
self God();
|
self God();
|
||||||
self Hide();
|
self Hide();
|
||||||
SetDvar( "sv_cheats", 0 );
|
SetDvar( "sv_cheats", 0 );
|
||||||
|
|
||||||
self.isHidden = true;
|
self.isHidden = true;
|
||||||
|
|
||||||
self IPrintLnBold( "Hide enabled" );
|
self IPrintLnBold( "Hide enabled" );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SetDvar( "sv_cheats", 1 );
|
SetDvar( "sv_cheats", 1 );
|
||||||
self SetClientDvar( "cg_thirdperson", 0 );
|
self SetClientDvar( "cg_thirdperson", 0 );
|
||||||
|
|
||||||
self God();
|
self God();
|
||||||
self Show();
|
self Show();
|
||||||
SetDvar( "sv_cheats", 0 );
|
SetDvar( "sv_cheats", 0 );
|
||||||
|
|
||||||
self.isHidden = false;
|
self.isHidden = false;
|
||||||
|
|
||||||
self IPrintLnBold( "Hide disabled" );
|
self IPrintLnBold( "Hide disabled" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -274,6 +267,8 @@ GotoImpl( event, data )
|
|||||||
|
|
||||||
GotoCoordImpl( data )
|
GotoCoordImpl( data )
|
||||||
{
|
{
|
||||||
|
_VERIFY_PLAYER_ENT( self );
|
||||||
|
|
||||||
if ( !IsAlive( self ) )
|
if ( !IsAlive( self ) )
|
||||||
{
|
{
|
||||||
self IPrintLnBold( "You are not alive" );
|
self IPrintLnBold( "You are not alive" );
|
||||||
@ -287,6 +282,8 @@ GotoCoordImpl( data )
|
|||||||
|
|
||||||
GotoPlayerImpl( target )
|
GotoPlayerImpl( target )
|
||||||
{
|
{
|
||||||
|
_VERIFY_PLAYER_ENT( self );
|
||||||
|
|
||||||
if ( !IsAlive( target ) )
|
if ( !IsAlive( target ) )
|
||||||
{
|
{
|
||||||
self IPrintLnBold( target.name + " is not alive" );
|
self IPrintLnBold( target.name + " is not alive" );
|
||||||
@ -299,10 +296,7 @@ GotoPlayerImpl( target )
|
|||||||
|
|
||||||
PlayerToMeImpl( event )
|
PlayerToMeImpl( event )
|
||||||
{
|
{
|
||||||
if ( !IsAlive( self ) )
|
_IS_ALIVE( self );
|
||||||
{
|
|
||||||
return self.name + " is not alive";
|
|
||||||
}
|
|
||||||
|
|
||||||
self SetOrigin( event.origin GetOrigin() );
|
self SetOrigin( event.origin GetOrigin() );
|
||||||
return "Moved here " + self.name;
|
return "Moved here " + self.name;
|
||||||
@ -310,10 +304,7 @@ PlayerToMeImpl( event )
|
|||||||
|
|
||||||
KillImpl()
|
KillImpl()
|
||||||
{
|
{
|
||||||
if ( !IsAlive( self ) )
|
_IS_ALIVE( self );
|
||||||
{
|
|
||||||
return self.name + " is not alive";
|
|
||||||
}
|
|
||||||
|
|
||||||
self Suicide();
|
self Suicide();
|
||||||
self IPrintLnBold( "You were killed by " + self.name );
|
self IPrintLnBold( "You were killed by " + self.name );
|
||||||
@ -323,13 +314,15 @@ KillImpl()
|
|||||||
|
|
||||||
SetSpectatorImpl()
|
SetSpectatorImpl()
|
||||||
{
|
{
|
||||||
|
_VERIFY_PLAYER_ENT( self );
|
||||||
|
|
||||||
if ( self.pers["team"] == "spectator" )
|
if ( self.pers["team"] == "spectator" )
|
||||||
{
|
{
|
||||||
return self.name + " is already spectating";
|
return self.name + " is already spectating";
|
||||||
}
|
}
|
||||||
|
|
||||||
self [[level.spectator]]();
|
self [[level.spectator]]();
|
||||||
self IPrintLnBold( "You have been moved to spectator" );
|
self IPrintLnBold( "You have been moved to spectator" );
|
||||||
|
|
||||||
return self.name + " has been moved to spectator";
|
return self.name + " has been moved to spectator";
|
||||||
}
|
}
|
||||||
|
@ -212,7 +212,7 @@ OnBusModeRequestedCallback( event )
|
|||||||
|
|
||||||
scripts\_integration_base::LogDebug( "Bus mode updated" );
|
scripts\_integration_base::LogDebug( "Bus mode updated" );
|
||||||
|
|
||||||
if ( GetDvar( level.commonKeys.busMode ) == "file" || GetDvar( level.commonKeys.busDir ) != "" )
|
if ( GetDvar( level.commonKeys.busMode ) == "file" && GetDvar( level.commonKeys.busDir ) != "" )
|
||||||
{
|
{
|
||||||
level.busMethods[level.commonFunctions.getInboundData] = level.overrideMethods[level.commonFunctions.getInboundData];
|
level.busMethods[level.commonFunctions.getInboundData] = level.overrideMethods[level.commonFunctions.getInboundData];
|
||||||
level.busMethods[level.commonFunctions.getOutboundData] = level.overrideMethods[level.commonFunctions.getOutboundData];
|
level.busMethods[level.commonFunctions.getOutboundData] = level.overrideMethods[level.commonFunctions.getOutboundData];
|
||||||
|
@ -88,7 +88,7 @@ IsBotWrapper( client )
|
|||||||
|
|
||||||
GetXuidWrapper()
|
GetXuidWrapper()
|
||||||
{
|
{
|
||||||
return self GetXUID();
|
return self GetGuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
|
73
GameFiles/GameInterface/_integration_t6_file_bus.gsc
Normal file
73
GameFiles/GameInterface/_integration_t6_file_bus.gsc
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*********************************************************************************
|
||||||
|
* DISCLAIMER: *
|
||||||
|
* *
|
||||||
|
* This script is optional and not required for *
|
||||||
|
* standard functionality. To use this script, a third-party *
|
||||||
|
* plugin named "t6-gsc-utils" must be installed on the *
|
||||||
|
* game server in the "*\storage\t6\plugins" folder *
|
||||||
|
* *
|
||||||
|
* The "t6-gsc-utils" plugin can be obtained from the GitHub *
|
||||||
|
* repository at: *
|
||||||
|
* https://github.com/fedddddd/t6-gsc-utils *
|
||||||
|
* *
|
||||||
|
* Please make sure to install the plugin before running this *
|
||||||
|
* script. *
|
||||||
|
*********************************************************************************/
|
||||||
|
|
||||||
|
/*********************************************************************************
|
||||||
|
* FUNCTIONALITY: *
|
||||||
|
* *
|
||||||
|
* This script extends the game interface to support the "file" *
|
||||||
|
* bus mode for Plutonium T6, which allows the game server and IW4M-Admin *
|
||||||
|
* to communicate via files, rather than over rcon using *
|
||||||
|
* dvars. *
|
||||||
|
* *
|
||||||
|
* By enabling the "file" bus mode, IW4M-Admin can send *
|
||||||
|
* commands and receive responses from the game server by *
|
||||||
|
* reading and writing to specific files. This provides a *
|
||||||
|
* flexible and efficient communication channel. *
|
||||||
|
* *
|
||||||
|
* Make sure to configure the server to use the "file" bus *
|
||||||
|
* mode and set the appropriate file path to *
|
||||||
|
* establish the communication between IW4M-Admin and the *
|
||||||
|
* game server. *
|
||||||
|
* *
|
||||||
|
* The wiki page for the setup of the game interface, and the bus mode *
|
||||||
|
* can be found on GitHub at: *
|
||||||
|
* https://github.com/RaidMax/IW4M-Admin/wiki/GameInterface#configuring-bus-mode *
|
||||||
|
*********************************************************************************/
|
||||||
|
|
||||||
|
Init()
|
||||||
|
{
|
||||||
|
thread Setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
Setup()
|
||||||
|
{
|
||||||
|
level waittill( level.notifyTypes.sharedFunctionsInitialized );
|
||||||
|
level.overrideMethods[level.commonFunctions.getInboundData] = ::GetInboundData;
|
||||||
|
level.overrideMethods[level.commonFunctions.getOutboundData] = ::GetOutboundData;
|
||||||
|
level.overrideMethods[level.commonFunctions.setInboundData] = ::SetInboundData;
|
||||||
|
level.overrideMethods[level.commonFunctions.setOutboundData] = ::SetOutboundData;
|
||||||
|
scripts\_integration_base::_SetDvarIfUninitialized( level.commonKeys.busdir, GetDvar( "fs_homepath" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
GetInboundData( location )
|
||||||
|
{
|
||||||
|
return readFile( location );
|
||||||
|
}
|
||||||
|
|
||||||
|
GetOutboundData( location )
|
||||||
|
{
|
||||||
|
return readFile( location );
|
||||||
|
}
|
||||||
|
|
||||||
|
SetInboundData( location, data )
|
||||||
|
{
|
||||||
|
writeFile( location, data );
|
||||||
|
}
|
||||||
|
|
||||||
|
SetOutboundData( location, data )
|
||||||
|
{
|
||||||
|
writeFile( location, data );
|
||||||
|
}
|
40
GameFiles/GameInterface/_integration_utility.gsh
Normal file
40
GameFiles/GameInterface/_integration_utility.gsh
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* This file contains reusable preprocessor directives meant to be used on
|
||||||
|
* Plutonium & AlterWare clients that are up to date with the latest version.
|
||||||
|
* Older versions of Plutonium or other clients do not have support for loading
|
||||||
|
* or parsing "gsh" files.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Turn off assertions by removing the following define
|
||||||
|
* gsc-tool will only emit assertions if developer_script dvar is set to 1
|
||||||
|
* In short, you should not need to remove this define. Just turn them off
|
||||||
|
* by using the dvar
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _INTEGRATION_DEBUG
|
||||||
|
|
||||||
|
#ifdef _INTEGRATION_DEBUG
|
||||||
|
|
||||||
|
#define _VERIFY( cond, msg ) \
|
||||||
|
assertEx( cond, msg )
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// This works as an "empty" define here with gsc-tool
|
||||||
|
#define _VERIFY( cond, msg )
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This function is meant to be used inside "client commands"
|
||||||
|
// If the client is not alive it shall return an error message
|
||||||
|
#define _IS_ALIVE( ent ) \
|
||||||
|
_VERIFY( ent, "player entity is not defined" ); \
|
||||||
|
if ( !IsAlive( ent ) ) \
|
||||||
|
{ \
|
||||||
|
return ent.name + "^7 is not alive"; \
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function should be used to verify if a player entity is defined
|
||||||
|
#define _VERIFY_PLAYER_ENT( ent ) \
|
||||||
|
_VERIFY( ent, "player entity is not defined" )
|
@ -3,14 +3,7 @@
|
|||||||
Allows integration of IW4M-Admin to GSC, mainly used for special commands that need to use GSC in order to work.
|
Allows integration of IW4M-Admin to GSC, mainly used for special commands that need to use GSC in order to work.
|
||||||
But can also be used to read / write metadata from / to a profile and to get the player permission level.
|
But can also be used to read / write metadata from / to a profile and to get the player permission level.
|
||||||
|
|
||||||
|
## Installation Guide
|
||||||
## Installation Plutonium IW5
|
|
||||||
|
|
||||||
|
|
||||||
Move `_integration.gsc` to `%localappdata%\Plutonium\storage\iw5\scripts\`
|
The documentation can be found here: [GameInterface](https://github.com/RaidMax/IW4M-Admin/wiki/GameInterface)
|
||||||
|
|
||||||
|
|
||||||
## Installation IW4x
|
|
||||||
|
|
||||||
|
|
||||||
Move `_integration.gsc` to `IW4x/userraw/scripts`, `IW4x` being the root folder of your game server.
|
|
||||||
|
@ -4,6 +4,7 @@ ECHO "Pluto IW5"
|
|||||||
xcopy /y .\GameInterface\_integration_base.gsc "%LOCALAPPDATA%\Plutonium\storage\iw5\scripts"
|
xcopy /y .\GameInterface\_integration_base.gsc "%LOCALAPPDATA%\Plutonium\storage\iw5\scripts"
|
||||||
xcopy /y .\GameInterface\_integration_shared.gsc "%LOCALAPPDATA%\Plutonium\storage\iw5\scripts"
|
xcopy /y .\GameInterface\_integration_shared.gsc "%LOCALAPPDATA%\Plutonium\storage\iw5\scripts"
|
||||||
xcopy /y .\GameInterface\_integration_iw5.gsc "%LOCALAPPDATA%\Plutonium\storage\iw5\scripts"
|
xcopy /y .\GameInterface\_integration_iw5.gsc "%LOCALAPPDATA%\Plutonium\storage\iw5\scripts"
|
||||||
|
xcopy /y .\GameInterface\_integration_utility.gsh "%LOCALAPPDATA%\Plutonium\storage\iw5\scripts"
|
||||||
xcopy /y .\AntiCheat\IW5\storage\iw5\scripts\_customcallbacks.gsc "%LOCALAPPDATA%\Plutonium\storage\iw5\scripts\mp"
|
xcopy /y .\AntiCheat\IW5\storage\iw5\scripts\_customcallbacks.gsc "%LOCALAPPDATA%\Plutonium\storage\iw5\scripts\mp"
|
||||||
|
|
||||||
ECHO "Pluto T5"
|
ECHO "Pluto T5"
|
||||||
@ -13,8 +14,8 @@ xcopy /y .\GameInterface\_integration_t5.gsc "%LOCALAPPDATA%\Plutonium\storage\t
|
|||||||
xcopy /y .\GameInterface\_integration_t5zm.gsc "%LOCALAPPDATA%\Plutonium\storage\t5\scripts\sp\zom"
|
xcopy /y .\GameInterface\_integration_t5zm.gsc "%LOCALAPPDATA%\Plutonium\storage\t5\scripts\sp\zom"
|
||||||
|
|
||||||
ECHO "Pluto T6"
|
ECHO "Pluto T6"
|
||||||
xcopy /y .\AntiCheat\PT6\storage\t6\scripts\mp\_customcallbacks.gsc "%LOCALAPPDATA%\Plutonium\storage\t6\scripts\mp"
|
|
||||||
xcopy /y .\GameInterface\_integration_base.gsc "%LOCALAPPDATA%\Plutonium\storage\t6\scripts"
|
xcopy /y .\GameInterface\_integration_base.gsc "%LOCALAPPDATA%\Plutonium\storage\t6\scripts"
|
||||||
xcopy /y .\GameInterface\_integration_shared.gsc "%LOCALAPPDATA%\Plutonium\storage\t6\scripts"
|
xcopy /y .\GameInterface\_integration_shared.gsc "%LOCALAPPDATA%\Plutonium\storage\t6\scripts"
|
||||||
xcopy /y .\GameInterface\_integration_t6.gsc "%LOCALAPPDATA%\Plutonium\storage\t6\scripts"
|
xcopy /y .\GameInterface\_integration_t6.gsc "%LOCALAPPDATA%\Plutonium\storage\t6\scripts"
|
||||||
xcopy /y .\GameInterface\_integration_t6zm_helper.gsc "%LOCALAPPDATA%\Plutonium\storage\t6\scripts\zm"
|
xcopy /y .\GameInterface\_integration_t6zm_helper.gsc "%LOCALAPPDATA%\Plutonium\storage\t6\scripts\zm"
|
||||||
|
xcopy /y .\AntiCheat\PT6\storage\t6\scripts\mp\_customcallbacks.gsc "%LOCALAPPDATA%\Plutonium\storage\t6\scripts\mp"
|
||||||
|
@ -53,6 +53,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ScriptPlugins", "ScriptPlug
|
|||||||
Plugins\ScriptPlugins\ParserPlutoniumT5.js = Plugins\ScriptPlugins\ParserPlutoniumT5.js
|
Plugins\ScriptPlugins\ParserPlutoniumT5.js = Plugins\ScriptPlugins\ParserPlutoniumT5.js
|
||||||
Plugins\ScriptPlugins\ServerBanner.js = Plugins\ScriptPlugins\ServerBanner.js
|
Plugins\ScriptPlugins\ServerBanner.js = Plugins\ScriptPlugins\ServerBanner.js
|
||||||
Plugins\ScriptPlugins\ParserBOIII.js = Plugins\ScriptPlugins\ParserBOIII.js
|
Plugins\ScriptPlugins\ParserBOIII.js = Plugins\ScriptPlugins\ParserBOIII.js
|
||||||
|
Plugins\ScriptPlugins\ParserL4D2SM.js = Plugins\ScriptPlugins\ParserL4D2SM.js
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutomessageFeed", "Plugins\AutomessageFeed\AutomessageFeed.csproj", "{F5815359-CFC7-44B4-9A3B-C04BACAD5836}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutomessageFeed", "Plugins\AutomessageFeed\AutomessageFeed.csproj", "{F5815359-CFC7-44B4-9A3B-C04BACAD5836}"
|
||||||
@ -87,6 +88,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GameInterface", "GameInterf
|
|||||||
GameFiles\GameInterface\_integration_t6.gsc = GameFiles\GameInterface\_integration_t6.gsc
|
GameFiles\GameInterface\_integration_t6.gsc = GameFiles\GameInterface\_integration_t6.gsc
|
||||||
GameFiles\GameInterface\_integration_t6zm_helper.gsc = GameFiles\GameInterface\_integration_t6zm_helper.gsc
|
GameFiles\GameInterface\_integration_t6zm_helper.gsc = GameFiles\GameInterface\_integration_t6zm_helper.gsc
|
||||||
GameFiles\GameInterface\example_module.gsc = GameFiles\GameInterface\example_module.gsc
|
GameFiles\GameInterface\example_module.gsc = GameFiles\GameInterface\example_module.gsc
|
||||||
|
GameFiles\GameInterface\_integration_t6_file_bus.gsc = GameFiles\GameInterface\_integration_t6_file_bus.gsc
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AntiCheat", "AntiCheat", "{AB83BAC0-C539-424A-BF00-78487C10753C}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AntiCheat", "AntiCheat", "{AB83BAC0-C539-424A-BF00-78487C10753C}"
|
||||||
|
@ -8,6 +8,7 @@ using System.Text;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Integrations.Cod.SecureRcon;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Serilog.Context;
|
using Serilog.Context;
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
@ -24,6 +25,7 @@ namespace Integrations.Cod
|
|||||||
public class CodRConConnection : IRConConnection
|
public class CodRConConnection : IRConConnection
|
||||||
{
|
{
|
||||||
private static readonly ConcurrentDictionary<EndPoint, ConnectionState> ActiveQueries = new();
|
private static readonly ConcurrentDictionary<EndPoint, ConnectionState> ActiveQueries = new();
|
||||||
|
private const string PkPattern = "-----BEGIN PRIVATE KEY-----";
|
||||||
public IPEndPoint Endpoint { get; }
|
public IPEndPoint Endpoint { get; }
|
||||||
public string RConPassword { get; }
|
public string RConPassword { get; }
|
||||||
|
|
||||||
@ -147,49 +149,33 @@ namespace Integrations.Cod
|
|||||||
{
|
{
|
||||||
var convertedRConPassword = ConvertEncoding(RConPassword);
|
var convertedRConPassword = ConvertEncoding(RConPassword);
|
||||||
var convertedParameters = ConvertEncoding(parameters);
|
var convertedParameters = ConvertEncoding(parameters);
|
||||||
byte SafeConversion(char c)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Convert.ToByte(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return (byte)'.';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case StaticHelpers.QueryType.GET_DVAR:
|
case StaticHelpers.QueryType.GET_DVAR:
|
||||||
waitForResponse = true;
|
waitForResponse = true;
|
||||||
payload = string
|
payload = BuildPayload(_config.CommandPrefixes.RConGetDvar, convertedRConPassword,
|
||||||
.Format(_config.CommandPrefixes.RConGetDvar, convertedRConPassword,
|
convertedParameters);
|
||||||
convertedParameters + '\0').Select(SafeConversion).ToArray();
|
|
||||||
break;
|
break;
|
||||||
case StaticHelpers.QueryType.SET_DVAR:
|
case StaticHelpers.QueryType.SET_DVAR:
|
||||||
payload = string
|
payload = BuildPayload(_config.CommandPrefixes.RConSetDvar, convertedRConPassword,
|
||||||
.Format(_config.CommandPrefixes.RConSetDvar, convertedRConPassword,
|
convertedParameters);
|
||||||
convertedParameters + '\0').Select(SafeConversion).ToArray();
|
|
||||||
break;
|
break;
|
||||||
case StaticHelpers.QueryType.COMMAND:
|
case StaticHelpers.QueryType.COMMAND:
|
||||||
payload = string
|
payload = BuildPayload(_config.CommandPrefixes.RConCommand, convertedRConPassword,
|
||||||
.Format(_config.CommandPrefixes.RConCommand, convertedRConPassword,
|
convertedParameters);
|
||||||
convertedParameters + '\0').Select(SafeConversion).ToArray();
|
|
||||||
break;
|
break;
|
||||||
case StaticHelpers.QueryType.GET_STATUS:
|
case StaticHelpers.QueryType.GET_STATUS:
|
||||||
waitForResponse = true;
|
waitForResponse = true;
|
||||||
payload = (_config.CommandPrefixes.RConGetStatus + '\0').Select(SafeConversion).ToArray();
|
payload = (_config.CommandPrefixes.RConGetStatus + '\0').Select(Helpers.SafeConversion).ToArray();
|
||||||
break;
|
break;
|
||||||
case StaticHelpers.QueryType.GET_INFO:
|
case StaticHelpers.QueryType.GET_INFO:
|
||||||
waitForResponse = true;
|
waitForResponse = true;
|
||||||
payload = (_config.CommandPrefixes.RConGetInfo + '\0').Select(SafeConversion).ToArray();
|
payload = (_config.CommandPrefixes.RConGetInfo + '\0').Select(Helpers.SafeConversion).ToArray();
|
||||||
break;
|
break;
|
||||||
case StaticHelpers.QueryType.COMMAND_STATUS:
|
case StaticHelpers.QueryType.COMMAND_STATUS:
|
||||||
waitForResponse = true;
|
waitForResponse = true;
|
||||||
payload = string.Format(_config.CommandPrefixes.RConCommand, convertedRConPassword, "status\0")
|
payload = BuildPayload(_config.CommandPrefixes.RConCommand, convertedRConPassword, "status");
|
||||||
.Select(SafeConversion).ToArray();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -334,6 +320,27 @@ namespace Integrations.Cod
|
|||||||
return validatedResponse;
|
return validatedResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] BuildPayload(string queryTemplate, string convertedRConPassword, string convertedParameters)
|
||||||
|
{
|
||||||
|
byte[] payload;
|
||||||
|
if (!RConPassword.StartsWith(PkPattern))
|
||||||
|
{
|
||||||
|
payload = string
|
||||||
|
.Format(queryTemplate, convertedRConPassword,
|
||||||
|
convertedParameters + '\0').Select(Helpers.SafeConversion).ToArray();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var textContent = string
|
||||||
|
.Format(queryTemplate, "", convertedParameters)
|
||||||
|
.Replace("rcon", "rconSafe ")
|
||||||
|
.Replace(" ", "").Split(" ");
|
||||||
|
payload = Helpers.BuildSafeRconPayload(textContent[0], textContent[1], RConPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<byte[][]> SendPayloadAsync(Socket rconSocket, byte[] payload, bool waitForResponse,
|
private async Task<byte[][]> SendPayloadAsync(Socket rconSocket, byte[] payload, bool waitForResponse,
|
||||||
CancellationToken token = default)
|
CancellationToken token = default)
|
||||||
{
|
{
|
||||||
@ -480,7 +487,7 @@ namespace Integrations.Cod
|
|||||||
|
|
||||||
return string.Join("", splitStatusStrings);
|
return string.Join("", splitStatusStrings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Recombines multiple game messages into one
|
/// Recombines multiple game messages into one
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -513,7 +520,7 @@ namespace Integrations.Cod
|
|||||||
{
|
{
|
||||||
return connectionState.ReceivedBytes.ToArray();
|
return connectionState.ReceivedBytes.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,4 +16,8 @@
|
|||||||
<ProjectReference Include="..\..\SharedLibraryCore\SharedLibraryCore.csproj" />
|
<ProjectReference Include="..\..\SharedLibraryCore\SharedLibraryCore.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="protobuf-net" Version="3.2.26" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
57
Integrations/Cod/SecureRcon/Helpers.cs
Normal file
57
Integrations/Cod/SecureRcon/Helpers.cs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace Integrations.Cod.SecureRcon;
|
||||||
|
|
||||||
|
public static class Helpers
|
||||||
|
{
|
||||||
|
private static byte[] ToSerializedMessage(this SecureCommand command)
|
||||||
|
{
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
Serializer.Serialize(ms, command);
|
||||||
|
return ms.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] SignData(byte[] data, string privateKey)
|
||||||
|
{
|
||||||
|
using var rsa = new RSACryptoServiceProvider();
|
||||||
|
rsa.ImportFromPem(privateKey);
|
||||||
|
var rsaFormatter = new RSAPKCS1SignatureFormatter(rsa);
|
||||||
|
rsaFormatter.SetHashAlgorithm("SHA512");
|
||||||
|
var hash = SHA512.Create();
|
||||||
|
var hashedData = hash.ComputeHash(data);
|
||||||
|
var signature = rsaFormatter.CreateSignature(hashedData);
|
||||||
|
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte SafeConversion(char c)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Convert.ToByte(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return (byte)'.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] BuildSafeRconPayload(string prefix, string command, string signingKey)
|
||||||
|
{
|
||||||
|
var message = command.Select(SafeConversion).ToArray();
|
||||||
|
var header = (prefix + "\n").Select(SafeConversion).ToArray();
|
||||||
|
|
||||||
|
var secureCommand = new SecureCommand
|
||||||
|
{
|
||||||
|
SecMessage = message,
|
||||||
|
Signature = SignData(message, signingKey)
|
||||||
|
};
|
||||||
|
|
||||||
|
return header.Concat(secureCommand.ToSerializedMessage()).ToArray();
|
||||||
|
}
|
||||||
|
}
|
13
Integrations/Cod/SecureRcon/SecureCommand.cs
Normal file
13
Integrations/Cod/SecureRcon/SecureCommand.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace Integrations.Cod.SecureRcon;
|
||||||
|
|
||||||
|
[ProtoContract]
|
||||||
|
public class SecureCommand
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public byte[] SecMessage { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(2)]
|
||||||
|
public byte[] Signature { get; set; }
|
||||||
|
}
|
@ -104,7 +104,7 @@ namespace Integrations.Source
|
|||||||
}
|
}
|
||||||
|
|
||||||
var split = response.TrimEnd('\n').Split('\n');
|
var split = response.TrimEnd('\n').Split('\n');
|
||||||
return split.Take(split.Length - 1).ToArray();
|
return split.Take(Math.Max(1, split.Length - 1)).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (TaskCanceledException)
|
catch (TaskCanceledException)
|
||||||
|
@ -22,10 +22,10 @@ const plugin = {
|
|||||||
rconParser.Configuration.MapStatus.AddMapping(111, 1);
|
rconParser.Configuration.MapStatus.AddMapping(111, 1);
|
||||||
|
|
||||||
rconParser.Configuration.HostnameStatus.Pattern = '^hostname: +(.+)$';
|
rconParser.Configuration.HostnameStatus.Pattern = '^hostname: +(.+)$';
|
||||||
rconParser.Configuration.MapStatus.AddMapping(113, 1);
|
rconParser.Configuration.HostnameStatus.AddMapping(113, 1);
|
||||||
|
|
||||||
rconParser.Configuration.MaxPlayersStatus.Pattern = '^players *: +\\d+ humans, \\d+ bots \\((\\d+).+';
|
rconParser.Configuration.MaxPlayersStatus.Pattern = '^players *: +\\d+ humans, \\d+ bots \\((\\d+).+';
|
||||||
rconParser.Configuration.MapStatus.AddMapping(114, 1);
|
rconParser.Configuration.MaxPlayersStatus.AddMapping(114, 1);
|
||||||
|
|
||||||
rconParser.Configuration.Dvar.Pattern = '^"(.+)" = "(.+)" (?:\\( def. "(.*)" \\))?(?: |\\w)+- (.+)$';
|
rconParser.Configuration.Dvar.Pattern = '^"(.+)" = "(.+)" (?:\\( def. "(.*)" \\))?(?: |\\w)+- (.+)$';
|
||||||
rconParser.Configuration.Dvar.AddMapping(106, 1);
|
rconParser.Configuration.Dvar.AddMapping(106, 1);
|
||||||
|
@ -3,7 +3,7 @@ let eventParser;
|
|||||||
|
|
||||||
const plugin = {
|
const plugin = {
|
||||||
author: 'RaidMax',
|
author: 'RaidMax',
|
||||||
version: 0.6,
|
version: 0.7,
|
||||||
name: 'CS:GO (SourceMod) Parser',
|
name: 'CS:GO (SourceMod) Parser',
|
||||||
engine: 'Source',
|
engine: 'Source',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
@ -22,10 +22,10 @@ const plugin = {
|
|||||||
rconParser.Configuration.MapStatus.AddMapping(111, 1);
|
rconParser.Configuration.MapStatus.AddMapping(111, 1);
|
||||||
|
|
||||||
rconParser.Configuration.HostnameStatus.Pattern = '^hostname: +(.+)$';
|
rconParser.Configuration.HostnameStatus.Pattern = '^hostname: +(.+)$';
|
||||||
rconParser.Configuration.MapStatus.AddMapping(113, 1);
|
rconParser.Configuration.HostnameStatus.AddMapping(113, 1);
|
||||||
|
|
||||||
rconParser.Configuration.MaxPlayersStatus.Pattern = '^players *: +\\d+ humans, \\d+ bots \\((\\d+).+';
|
rconParser.Configuration.MaxPlayersStatus.Pattern = '^players *: +\\d+ humans, \\d+ bots \\((\\d+).+';
|
||||||
rconParser.Configuration.MapStatus.AddMapping(114, 1);
|
rconParser.Configuration.MaxPlayersStatus.AddMapping(114, 1);
|
||||||
|
|
||||||
rconParser.Configuration.Dvar.Pattern = '^"(.+)" = "(.+)" (?:\\( def. "(.*)" \\))?(?: |\\w)+- (.+)$';
|
rconParser.Configuration.Dvar.Pattern = '^"(.+)" = "(.+)" (?:\\( def. "(.*)" \\))?(?: |\\w)+- (.+)$';
|
||||||
rconParser.Configuration.Dvar.AddMapping(106, 1);
|
rconParser.Configuration.Dvar.AddMapping(106, 1);
|
||||||
|
135
Plugins/ScriptPlugins/ParserL4D2SM.js
Normal file
135
Plugins/ScriptPlugins/ParserL4D2SM.js
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
let rconParser;
|
||||||
|
let eventParser;
|
||||||
|
|
||||||
|
const plugin = {
|
||||||
|
author: 'RaidMax',
|
||||||
|
version: 0.1,
|
||||||
|
name: 'L4D2 (SourceMod) Parser',
|
||||||
|
engine: 'Source',
|
||||||
|
isParser: true,
|
||||||
|
|
||||||
|
onEventAsync: function (gameEvent, server) {
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadAsync: function (manager) {
|
||||||
|
rconParser = manager.GenerateDynamicRConParser(this.name);
|
||||||
|
eventParser = manager.GenerateDynamicEventParser(this.name);
|
||||||
|
rconParser.RConEngine = this.engine;
|
||||||
|
|
||||||
|
rconParser.Configuration.StatusHeader.Pattern = '# userid name uniqueid connected ping loss state rate adr';
|
||||||
|
|
||||||
|
rconParser.Configuration.MapStatus.Pattern = '^map *: +(.+)$';
|
||||||
|
rconParser.Configuration.MapStatus.AddMapping(111, 1);
|
||||||
|
|
||||||
|
rconParser.Configuration.HostnameStatus.Pattern = '^hostname: +(.+)$';
|
||||||
|
rconParser.Configuration.HostnameStatus.AddMapping(113, 1);
|
||||||
|
|
||||||
|
rconParser.Configuration.MaxPlayersStatus.Pattern = '^players *: +\\d+ humans, \\d+ bots \\((\\d+).+';
|
||||||
|
rconParser.Configuration.MaxPlayersStatus.AddMapping(114, 1);
|
||||||
|
|
||||||
|
rconParser.Configuration.Dvar.Pattern = '^\\"(.+)\\" (?:=|is) \\"(.+)\\"(?: (?:\\( def. \\"(.*)\\" \\)))?$';
|
||||||
|
rconParser.Configuration.Dvar.AddMapping(106, 1);
|
||||||
|
rconParser.Configuration.Dvar.AddMapping(107, 2);
|
||||||
|
rconParser.Configuration.Dvar.AddMapping(108, 3);
|
||||||
|
rconParser.Configuration.Dvar.AddMapping(109, 3);
|
||||||
|
|
||||||
|
rconParser.Configuration.Status.Pattern = '^#\\s*(\\d+) (\\d+) "(.+)" (\\S+) +(\\d+:\\d+(?::\\d+)?) (\\d+) (\\S+) (\\S+) (\\d+) (\\d+\\.\\d+\\.\\d+\\.\\d+:\\d+)$';
|
||||||
|
rconParser.Configuration.Status.AddMapping(100, 2);
|
||||||
|
rconParser.Configuration.Status.AddMapping(101, -1);
|
||||||
|
rconParser.Configuration.Status.AddMapping(102, 6);
|
||||||
|
rconParser.Configuration.Status.AddMapping(103, 4)
|
||||||
|
rconParser.Configuration.Status.AddMapping(104, 3);
|
||||||
|
rconParser.Configuration.Status.AddMapping(105, 10);
|
||||||
|
rconParser.Configuration.Status.AddMapping(200, 1);
|
||||||
|
|
||||||
|
rconParser.Configuration.DefaultDvarValues.Add('sv_running', '1');
|
||||||
|
rconParser.Configuration.DefaultDvarValues.Add('bugfix_no_version', this.engine);
|
||||||
|
rconParser.Configuration.DefaultDvarValues.Add('fs_basepath', '');
|
||||||
|
rconParser.Configuration.DefaultDvarValues.Add('fs_basegame', '');
|
||||||
|
rconParser.Configuration.DefaultDvarValues.Add('fs_homepath', '');
|
||||||
|
rconParser.Configuration.DefaultDvarValues.Add('g_log', '');
|
||||||
|
rconParser.Configuration.DefaultDvarValues.Add('net_ip', 'localhost');
|
||||||
|
rconParser.Configuration.DefaultDvarValues.Add('g_gametype', '');
|
||||||
|
rconParser.Configuration.DefaultDvarValues.Add('fs_game', '');
|
||||||
|
|
||||||
|
rconParser.Configuration.OverrideDvarNameMapping.Add('sv_hostname', 'hostname');
|
||||||
|
rconParser.Configuration.OverrideDvarNameMapping.Add('mapname', 'host_map');
|
||||||
|
rconParser.Configuration.OverrideDvarNameMapping.Add('sv_maxclients', 'maxplayers');
|
||||||
|
rconParser.Configuration.OverrideDvarNameMapping.Add('g_password', 'sv_password');
|
||||||
|
rconParser.Configuration.OverrideDvarNameMapping.Add('version', 'bugfix_no_version');
|
||||||
|
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Clear();
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('White', '\x01');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Red', '\x07');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('LightRed', '\x0F');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('DarkRed', '\x02');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Blue', '\x0B');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('DarkBlue', '\x0C');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Purple', '\x03');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Orchid', '\x0E');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Yellow', '\x09');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Gold', '\x10');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('LightGreen', '\x05');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Green', '\x04');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Lime', '\x06');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Grey', '\x08');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Grey2', '\x0D');
|
||||||
|
// only adding there here for the default accent color
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Cyan', '\x0B');
|
||||||
|
|
||||||
|
rconParser.Configuration.NoticeLineSeparator = '. ';
|
||||||
|
rconParser.Configuration.DefaultRConPort = 27015;
|
||||||
|
rconParser.CanGenerateLogPath = false;
|
||||||
|
|
||||||
|
rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined;
|
||||||
|
rconParser.Configuration.CommandPrefixes.Kick = 'sm_kick #{0} {1}';
|
||||||
|
rconParser.Configuration.CommandPrefixes.Ban = 'sm_kick #{0} {1}';
|
||||||
|
rconParser.Configuration.CommandPrefixes.TempBan = 'sm_kick #{0} {1}';
|
||||||
|
rconParser.Configuration.CommandPrefixes.Say = 'sm_say {0}';
|
||||||
|
rconParser.Configuration.CommandPrefixes.Tell = 'sm_psay #{0} "{1}"';
|
||||||
|
|
||||||
|
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*)" *(?:\\((\\w+)((?: ).+)?\\))?$';
|
||||||
|
eventParser.Configuration.Kill.AddMapping(5, 1);
|
||||||
|
eventParser.Configuration.Kill.AddMapping(3, 2);
|
||||||
|
eventParser.Configuration.Kill.AddMapping(1, 3);
|
||||||
|
eventParser.Configuration.Kill.AddMapping(7, 4);
|
||||||
|
eventParser.Configuration.Kill.AddMapping(6, 5);
|
||||||
|
eventParser.Configuration.Kill.AddMapping(4, 6);
|
||||||
|
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.MapEnd.Pattern = '^World triggered "Match_Start" on "(.+)"$';
|
||||||
|
|
||||||
|
eventParser.Configuration.JoinTeam.Pattern = '^"(.+)<(\\d+)><(.*)>" switched from team <(.+)> to <(.+)>$';
|
||||||
|
eventParser.Configuration.JoinTeam.AddMapping(5, 1);
|
||||||
|
eventParser.Configuration.JoinTeam.AddMapping(3, 2);
|
||||||
|
eventParser.Configuration.JoinTeam.AddMapping(1, 3);
|
||||||
|
eventParser.Configuration.JoinTeam.AddMapping(7, 5);
|
||||||
|
|
||||||
|
eventParser.Configuration.TeamMapping.Add('CT', 2);
|
||||||
|
eventParser.Configuration.TeamMapping.Add('TERRORIST', 3);
|
||||||
|
|
||||||
|
eventParser.Configuration.Time.Pattern = '^L [01]\\d/[0-3]\\d/\\d+ - [0-2]\\d:[0-5]\\d:[0-5]\\d:';
|
||||||
|
|
||||||
|
rconParser.Version = 'L4D2SM';
|
||||||
|
rconParser.GameName = 12; // L4D2
|
||||||
|
eventParser.Version = 'L4D2SM';
|
||||||
|
eventParser.GameName = 12; // L4D2
|
||||||
|
eventParser.URLProtocolFormat = 'steam://connect/{{ip}}:{{port}}';
|
||||||
|
},
|
||||||
|
|
||||||
|
onUnloadAsync: function () {
|
||||||
|
},
|
||||||
|
|
||||||
|
onTickAsync: function (server) {
|
||||||
|
}
|
||||||
|
};
|
@ -25,11 +25,25 @@ namespace Stats.Dtos
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime? SentAfter { get; set; }
|
public DateTime? SentAfter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time associated with SentAfter date
|
||||||
|
/// </summary>
|
||||||
|
public string SentAfterTime { get; set; }
|
||||||
|
|
||||||
|
public DateTime? SentAfterDateTime => SentAfter?.Add(string.IsNullOrEmpty(SentAfterTime) ? TimeSpan.Zero : TimeSpan.Parse(SentAfterTime));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// only look for messages sent before this date0
|
/// only look for messages sent before this date0
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime SentBefore { get; set; } = DateTime.UtcNow;
|
public DateTime SentBefore { get; set; } = DateTime.UtcNow.Date;
|
||||||
|
|
||||||
|
public string SentBeforeTime { get; set; }
|
||||||
|
|
||||||
|
public DateTime? SentBeforeDateTime =>
|
||||||
|
SentBefore.Add(string.IsNullOrEmpty(SentBeforeTime) ? TimeSpan.Zero : TimeSpan.Parse(SentBeforeTime));
|
||||||
|
|
||||||
|
public bool IsExactMatch { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// indicates if the chat is on the meta page
|
/// indicates if the chat is on the meta page
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -13,7 +13,6 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
{
|
{
|
||||||
private const int ZScoreRange = 3;
|
private const int ZScoreRange = 3;
|
||||||
private const int RankIconDivisions = 24;
|
private const int RankIconDivisions = 24;
|
||||||
private const int MaxMessages = 100;
|
|
||||||
|
|
||||||
public class LogParams
|
public class LogParams
|
||||||
{
|
{
|
||||||
@ -127,70 +126,5 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// todo: lets abstract this out to a generic buildable query
|
|
||||||
/// this is just a dirty PoC
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="query"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static ChatSearchQuery ParseSearchInfo(this string query, int count, int offset)
|
|
||||||
{
|
|
||||||
string[] filters = query.Split('|');
|
|
||||||
var searchRequest = new ChatSearchQuery
|
|
||||||
{
|
|
||||||
Filter = query,
|
|
||||||
Count = count,
|
|
||||||
Offset = offset
|
|
||||||
};
|
|
||||||
|
|
||||||
// sanity checks
|
|
||||||
searchRequest.Count = Math.Min(searchRequest.Count, MaxMessages);
|
|
||||||
searchRequest.Count = Math.Max(searchRequest.Count, 0);
|
|
||||||
searchRequest.Offset = Math.Max(searchRequest.Offset, 0);
|
|
||||||
|
|
||||||
if (filters.Length > 1)
|
|
||||||
{
|
|
||||||
if (filters[0].ToLower() != "chat")
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Query is not compatible with chat");
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string filter in filters.Skip(1))
|
|
||||||
{
|
|
||||||
string[] args = filter.Split(' ');
|
|
||||||
|
|
||||||
if (args.Length > 1)
|
|
||||||
{
|
|
||||||
string recombinedArgs = string.Join(' ', args.Skip(1));
|
|
||||||
switch (args[0].ToLower())
|
|
||||||
{
|
|
||||||
case "before":
|
|
||||||
searchRequest.SentBefore = DateTime.Parse(recombinedArgs);
|
|
||||||
break;
|
|
||||||
case "after":
|
|
||||||
searchRequest.SentAfter = DateTime.Parse(recombinedArgs);
|
|
||||||
break;
|
|
||||||
case "server":
|
|
||||||
searchRequest.ServerId = args[1];
|
|
||||||
break;
|
|
||||||
case "client":
|
|
||||||
searchRequest.ClientId = int.Parse(args[1]);
|
|
||||||
break;
|
|
||||||
case "contains":
|
|
||||||
searchRequest.MessageContains = string.Join(' ', args.Skip(1));
|
|
||||||
break;
|
|
||||||
case "sort":
|
|
||||||
searchRequest.Direction = Enum.Parse<SortDirection>(args[1], ignoreCase: true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return searchRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ArgumentException("No filters specified for chat search");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,11 +53,11 @@ namespace Stats.Helpers
|
|||||||
}
|
}
|
||||||
|
|
||||||
var iqMessages = context.Set<EFClientMessage>()
|
var iqMessages = context.Set<EFClientMessage>()
|
||||||
.Where(message => message.TimeSent < query.SentBefore);
|
.Where(message => message.TimeSent < query.SentBeforeDateTime);
|
||||||
|
|
||||||
if (query.SentAfter is not null)
|
if (query.SentAfterDateTime is not null)
|
||||||
{
|
{
|
||||||
iqMessages = iqMessages.Where(message => message.TimeSent >= query.SentAfter);
|
iqMessages = iqMessages.Where(message => message.TimeSent >= query.SentAfterDateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.ClientId is not null)
|
if (query.ClientId is not null)
|
||||||
@ -72,7 +72,10 @@ namespace Stats.Helpers
|
|||||||
|
|
||||||
if (!string.IsNullOrEmpty(query.MessageContains))
|
if (!string.IsNullOrEmpty(query.MessageContains))
|
||||||
{
|
{
|
||||||
iqMessages = iqMessages.Where(message => EF.Functions.Like(message.Message.ToLower(), $"%{query.MessageContains.ToLower()}%"));
|
iqMessages = query.IsExactMatch
|
||||||
|
? iqMessages.Where(message => message.Message.ToLower() == query.MessageContains.ToLower())
|
||||||
|
: iqMessages.Where(message =>
|
||||||
|
EF.Functions.Like(message.Message.ToLower(), $"%{query.MessageContains.ToLower()}%"));
|
||||||
}
|
}
|
||||||
|
|
||||||
var iqResponse = iqMessages
|
var iqResponse = iqMessages
|
||||||
|
@ -469,6 +469,8 @@ public class Plugin : IPluginV2
|
|||||||
ClientId = request.ClientId,
|
ClientId = request.ClientId,
|
||||||
Before = request.Before,
|
Before = request.Before,
|
||||||
SentBefore = request.Before ?? DateTime.UtcNow,
|
SentBefore = request.Before ?? DateTime.UtcNow,
|
||||||
|
SentAfter = request.After,
|
||||||
|
After = request.After,
|
||||||
Count = request.Count,
|
Count = request.Count,
|
||||||
IsProfileMeta = true
|
IsProfileMeta = true
|
||||||
};
|
};
|
||||||
|
@ -56,4 +56,4 @@ Feel free to join the **IW4MAdmin** [Discord](https://discord.gg/ZZFK5p3)
|
|||||||
If you come across an issue, bug, or feature request please post an [issue](https://github.com/RaidMax/IW4M-Admin/issues)
|
If you come across an issue, bug, or feature request please post an [issue](https://github.com/RaidMax/IW4M-Admin/issues)
|
||||||
|
|
||||||
|
|
||||||
#### Explore the [wiki](https://github.com/RaidMax/IW4M-Admin/wiki) to find more information.
|
#### Explore the [wiki](https://git.rimmyscorner.com/Parasyn/IW4M-Admin/wiki) to find more information.
|
||||||
|
@ -179,6 +179,7 @@ namespace SharedLibraryCore
|
|||||||
server.Reports.Count(report => DateTime.UtcNow - report.ReportedOn <= TimeSpan.FromHours(24)));
|
server.Reports.Count(report => DateTime.UtcNow - report.ReportedOn <= TimeSpan.FromHours(24)));
|
||||||
ViewBag.PermissionsSet = PermissionsSet;
|
ViewBag.PermissionsSet = PermissionsSet;
|
||||||
ViewBag.Alerts = AlertManager.RetrieveAlerts(Client);
|
ViewBag.Alerts = AlertManager.RetrieveAlerts(Client);
|
||||||
|
ViewBag.Manager = Manager;
|
||||||
|
|
||||||
base.OnActionExecuting(context);
|
base.OnActionExecuting(context);
|
||||||
}
|
}
|
||||||
|
@ -206,7 +206,7 @@ namespace SharedLibraryCore.Configuration
|
|||||||
: ManualWebfrontUrl;
|
: ManualWebfrontUrl;
|
||||||
|
|
||||||
[ConfigurationIgnore] public bool IgnoreServerConnectionLost { get; set; }
|
[ConfigurationIgnore] public bool IgnoreServerConnectionLost { get; set; }
|
||||||
[ConfigurationIgnore] public Uri MasterUrl { get; set; } = new("http://api.raidmax.org:5000");
|
[ConfigurationIgnore] public Uri MasterUrl { get; set; } = new("https://master.iw4.zip");
|
||||||
|
|
||||||
public IBaseConfiguration Generate()
|
public IBaseConfiguration Generate()
|
||||||
{
|
{
|
||||||
|
@ -67,7 +67,7 @@ namespace SharedLibraryCore.Configuration.Validation
|
|||||||
|
|
||||||
RuleFor(_app => _app.MasterUrl)
|
RuleFor(_app => _app.MasterUrl)
|
||||||
.NotNull()
|
.NotNull()
|
||||||
.Must(_url => _url != null && _url.Scheme == Uri.UriSchemeHttp);
|
.Must(_url => _url != null && (_url.Scheme == Uri.UriSchemeHttp || _url.Scheme == Uri.UriSchemeHttps));
|
||||||
|
|
||||||
RuleFor(_app => _app.CommandPrefix)
|
RuleFor(_app => _app.CommandPrefix)
|
||||||
.NotEmpty();
|
.NotEmpty();
|
||||||
@ -80,4 +80,4 @@ namespace SharedLibraryCore.Configuration.Validation
|
|||||||
.SetValidator(new ServerConfigurationValidator());
|
.SetValidator(new ServerConfigurationValidator());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,8 @@ namespace SharedLibraryCore
|
|||||||
T7 = 8,
|
T7 = 8,
|
||||||
SHG1 = 9,
|
SHG1 = 9,
|
||||||
CSGO = 10,
|
CSGO = 10,
|
||||||
H1 = 11
|
H1 = 11,
|
||||||
|
L4D2 = 12
|
||||||
}
|
}
|
||||||
|
|
||||||
// only here for performance
|
// only here for performance
|
||||||
|
@ -50,8 +50,8 @@ namespace SharedLibraryCore
|
|||||||
public static char[] DirectorySeparatorChars = { '\\', '/' };
|
public static char[] DirectorySeparatorChars = { '\\', '/' };
|
||||||
public static char CommandPrefix { get; set; } = '!';
|
public static char CommandPrefix { get; set; } = '!';
|
||||||
|
|
||||||
public static string ToStandardFormat(this DateTime? time) => time?.ToString("yyyy-MM-dd H:mm:ss UTC");
|
public static string ToStandardFormat(this DateTime? time) => time?.ToString("yyyy-MM-dd HH:mm:ss UTC");
|
||||||
public static string ToStandardFormat(this DateTime time) => time.ToString("yyyy-MM-dd H:mm:ss UTC");
|
public static string ToStandardFormat(this DateTime time) => time.ToString("yyyy-MM-dd HH:mm:ss UTC");
|
||||||
|
|
||||||
public static EFClient IW4MAdminClient(Server server = null)
|
public static EFClient IW4MAdminClient(Server server = null)
|
||||||
{
|
{
|
||||||
@ -195,7 +195,7 @@ namespace SharedLibraryCore
|
|||||||
}
|
}
|
||||||
|
|
||||||
var output = str;
|
var output = str;
|
||||||
var colorCodeMatches = Regex.Matches(output, @"\(Color::(.{1,16})\)",
|
var colorCodeMatches = Regex.Matches(output, @"\(Color::(\w{1,16})\)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
foreach (var match in colorCodeMatches.Where(m => m.Success))
|
foreach (var match in colorCodeMatches.Where(m => m.Success))
|
||||||
{
|
{
|
||||||
|
@ -249,7 +249,8 @@ namespace WebfrontCore.Controllers
|
|||||||
{
|
{
|
||||||
ViewBag.Title = Localization["WEBFRONT_SEARCH_RESULTS_TITLE"];
|
ViewBag.Title = Localization["WEBFRONT_SEARCH_RESULTS_TITLE"];
|
||||||
ViewBag.ClientResourceRequest = request;
|
ViewBag.ClientResourceRequest = request;
|
||||||
|
|
||||||
|
request.RequesterPermission = Client.Level;
|
||||||
var response = await _clientResourceHelper.QueryResource(request);
|
var response = await _clientResourceHelper.QueryResource(request);
|
||||||
return request.Offset > 0
|
return request.Offset > 0
|
||||||
? PartialView("Find/_AdvancedFindList", response.Results)
|
? PartialView("Find/_AdvancedFindList", response.Results)
|
||||||
|
@ -17,6 +17,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||||
using Data.Abstractions;
|
using Data.Abstractions;
|
||||||
using Stats.Config;
|
using Stats.Config;
|
||||||
|
using WebfrontCore.QueryHelpers.Models;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers
|
namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers
|
||||||
{
|
{
|
||||||
@ -121,7 +122,7 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("Message/Find")]
|
[HttpGet("Message/Find")]
|
||||||
public async Task<IActionResult> FindMessage([FromQuery] string query)
|
public async Task<IActionResult> FindMessage([FromQuery] ChatResourceRequest query)
|
||||||
{
|
{
|
||||||
ViewBag.Localization = _translationLookup;
|
ViewBag.Localization = _translationLookup;
|
||||||
ViewBag.EnableColorCodes = _manager.GetApplicationSettings().Configuration().EnableColorCodes;
|
ViewBag.EnableColorCodes = _manager.GetApplicationSettings().Configuration().EnableColorCodes;
|
||||||
@ -130,53 +131,15 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers
|
|||||||
ViewBag.Title = _translationLookup["WEBFRONT_STATS_MESSAGES_TITLE"];
|
ViewBag.Title = _translationLookup["WEBFRONT_STATS_MESSAGES_TITLE"];
|
||||||
ViewBag.Error = null;
|
ViewBag.Error = null;
|
||||||
ViewBag.IsFluid = true;
|
ViewBag.IsFluid = true;
|
||||||
ChatSearchQuery searchRequest = null;
|
|
||||||
|
var result = query != null ? await _chatResourceQueryHelper.QueryResource(query) : null;
|
||||||
try
|
|
||||||
{
|
|
||||||
searchRequest = query.ParseSearchInfo(int.MaxValue, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (ArgumentException e)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(e, "Could not parse chat message search query {query}", query);
|
|
||||||
ViewBag.Error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (FormatException e)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(e, "Could not parse chat message search query filter format {query}", query);
|
|
||||||
ViewBag.Error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = searchRequest != null ? await _chatResourceQueryHelper.QueryResource(searchRequest) : null;
|
|
||||||
return View("~/Views/Client/Message/Find.cshtml", result);
|
return View("~/Views/Client/Message/Find.cshtml", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("Message/FindNext")]
|
[HttpGet("Message/FindNext")]
|
||||||
public async Task<IActionResult> FindNextMessages([FromQuery] string query, [FromQuery] int count,
|
public async Task<IActionResult> FindNextMessages(ChatResourceRequest query)
|
||||||
[FromQuery] int offset)
|
|
||||||
{
|
{
|
||||||
ChatSearchQuery searchRequest;
|
var result = await _chatResourceQueryHelper.QueryResource(query);
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
searchRequest = query.ParseSearchInfo(count, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (ArgumentException e)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(e, "Could not parse chat message search query {query}", query);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (FormatException e)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(e, "Could not parse chat message search query filter format {query}", query);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = await _chatResourceQueryHelper.QueryResource(searchRequest);
|
|
||||||
return PartialView("~/Views/Client/Message/_Item.cshtml", result.Results);
|
return PartialView("~/Views/Client/Message/_Item.cshtml", result.Results);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
9
WebfrontCore/QueryHelpers/Models/ChatResourceRequest.cs
Normal file
9
WebfrontCore/QueryHelpers/Models/ChatResourceRequest.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using Stats.Dtos;
|
||||||
|
|
||||||
|
namespace WebfrontCore.QueryHelpers.Models;
|
||||||
|
|
||||||
|
public class ChatResourceRequest : ChatSearchQuery
|
||||||
|
{
|
||||||
|
public bool HasData => !string.IsNullOrEmpty(MessageContains) || !string.IsNullOrEmpty(ServerId) ||
|
||||||
|
ClientId is not null || SentAfterDateTime is not null;
|
||||||
|
}
|
@ -16,4 +16,9 @@ public class ClientResourceRequest : ClientPaginationRequest
|
|||||||
public EFClient.Permission? ClientLevel { get; set; }
|
public EFClient.Permission? ClientLevel { get; set; }
|
||||||
public Reference.Game? GameName { get; set; }
|
public Reference.Game? GameName { get; set; }
|
||||||
public bool IncludeGeolocationData { get; set; } = true;
|
public bool IncludeGeolocationData { get; set; } = true;
|
||||||
|
|
||||||
|
public EFClient.Permission RequesterPermission { get; set; } = EFClient.Permission.User;
|
||||||
|
|
||||||
|
public bool HasData => !string.IsNullOrEmpty(ClientName) || !string.IsNullOrEmpty(ClientIp) ||
|
||||||
|
!string.IsNullOrEmpty(ClientGuid) || ClientLevel is not null || GameName is not null;
|
||||||
}
|
}
|
||||||
|
@ -26,14 +26,14 @@ else
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div id="loaderLoad" class="mt-10 m-auto text-center d-none d-lg-block">
|
<div id="loaderLoad" class="mt-10 m-auto text-center">
|
||||||
<i class="loader-load-more oi oi-chevron-bottom"></i>
|
<i class="loader-load-more oi oi-chevron-bottom"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@section scripts {
|
@section scripts {
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
initLoader('/Message/FindNext?query=@ViewBag.Query', '#message_table_body', @Model.RetrievedResultCount, @ViewBag.QueryLimit);
|
initLoader(`/Message/FindNext${window.location.search}`, '#message_table_body', @Model.RetrievedResultCount, 30);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
}
|
}
|
||||||
|
88
WebfrontCore/Views/Shared/Partials/Search/_ChatSearch.cshtml
Normal file
88
WebfrontCore/Views/Shared/Partials/Search/_ChatSearch.cshtml
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
@using WebfrontCore.QueryHelpers.Models
|
||||||
|
@using SharedLibraryCore.Interfaces
|
||||||
|
@using System.Globalization
|
||||||
|
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
@model string
|
||||||
|
@{
|
||||||
|
var existingChatFilter = ViewBag.Query as ChatResourceRequest;
|
||||||
|
var manager = ViewBag.Manager as IManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
<div id="chatSearchWrapper@(Model)" data-has-data="@(existingChatFilter?.HasData ?? false)" >
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="messageContains@(Model)">@ViewBag.Localization["WEBFRONT_ACTION_LABEL_MESSAGE"]</label>
|
||||||
|
<div class="d-flex">
|
||||||
|
<input type="text" class="form-control" name="messageContains" id="messageContains@(Model)}"
|
||||||
|
placeholder="Contains" value="@existingChatFilter?.MessageContains"/>
|
||||||
|
<div class="custom-control ml-10 align-self-center">
|
||||||
|
<div class="custom-switch">
|
||||||
|
@if (existingChatFilter?.IsExactMatch ?? false)
|
||||||
|
{
|
||||||
|
<input type="checkbox" id="isExactMatch@(Model)" name="isExactMatch" value="true"
|
||||||
|
checked="checked">
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<input type="checkbox" id="isExactMatch@(Model)" name="isExactMatch" value="true">
|
||||||
|
}
|
||||||
|
|
||||||
|
<label for="isExactMatch@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_EXACT"]</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="serverId@(Model)" class="w-quarter">@ViewBag.Localization["WEBFRONT_BAN_MGMT_FORM_ID"]</label>
|
||||||
|
<label for="clientId@(Model)" class="">@ViewBag.Localization["WEBFRONT_CONTEXT_MENU_GLOBAL_SERVER"]</label>
|
||||||
|
|
||||||
|
<div class="d-flex">
|
||||||
|
<input type="text" class="form-control w-quarter" name="clientId" id="clientId@(Model)}"
|
||||||
|
placeholder="Id" value="@existingChatFilter?.ClientId"/>
|
||||||
|
<select class="form-control w-three-quarter ml-10" id="serverId@(Model)" name="serverId">
|
||||||
|
<option value="">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_SELECT_PERMISSIONS_ANY"]</option>
|
||||||
|
@foreach (var server in manager!.GetServers())
|
||||||
|
{
|
||||||
|
<option value="@server.Id" selected="@(server.Id == existingChatFilter?.ServerId)">
|
||||||
|
[@server.GameName.ToString()] @server.ServerName.StripColors()
|
||||||
|
</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="sentAfter@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_SENT_AFTER"]</label>
|
||||||
|
@{
|
||||||
|
var afterDate = existingChatFilter?.SentAfterDateTime ?? DateTime.UtcNow.AddHours(-1);
|
||||||
|
}
|
||||||
|
<div class="d-flex">
|
||||||
|
<input type="text" class="form-control date-picker-input w-half" name="sentAfter"
|
||||||
|
id="sentAfter@(Model)" data-date="@afterDate.ToString("s", CultureInfo.InvariantCulture)"
|
||||||
|
value="@afterDate.ToString("s", CultureInfo.InvariantCulture)"/>
|
||||||
|
<input type="time" class="form-control w-half ml-10" name="sentAfterTime"
|
||||||
|
id="sentAfterTime@(Model)"
|
||||||
|
style="color-scheme: dark;"
|
||||||
|
value="@afterDate.ToString("HH:mm")"/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="sentAfter@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_SENT_BEFORE"]</label>
|
||||||
|
@{
|
||||||
|
var beforeDate = existingChatFilter?.SentBeforeDateTime ?? DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
<div class="d-flex">
|
||||||
|
<input type="text" class="form-control date-picker-input w-half" name="sentBefore"
|
||||||
|
id="sentBefore@(Model)" data-date="@beforeDate.ToString("s", CultureInfo.InvariantCulture)"
|
||||||
|
value="@beforeDate.ToString("s", CultureInfo.InvariantCulture)"/>
|
||||||
|
<input type="time" class="form-control w-half ml-10" name="sentBeforeTime"
|
||||||
|
id="sentBeforeTime@(Model)"
|
||||||
|
style="color-scheme: dark;"
|
||||||
|
value="@beforeDate.ToString("HH:mm")"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="submit" class="btn btn-primary" value="@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_BUTTON_SUBMIT"]"/>
|
||||||
|
</div>
|
116
WebfrontCore/Views/Shared/Partials/Search/_ClientSearch.cshtml
Normal file
116
WebfrontCore/Views/Shared/Partials/Search/_ClientSearch.cshtml
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
@using WebfrontCore.QueryHelpers.Models
|
||||||
|
@using System.Globalization
|
||||||
|
@using Data.Models
|
||||||
|
@using Data.Models.Client
|
||||||
|
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
@using SharedLibraryCore.Dtos
|
||||||
|
@model string
|
||||||
|
@{
|
||||||
|
var existingClientFilter = ViewBag.ClientResourceRequest as ClientResourceRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
<div id="clientSearchWrapper@(Model)" data-has-data="@(existingClientFilter?.HasData ?? false)" >
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="clientName@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_NAME"]</label>
|
||||||
|
<div class="d-flex">
|
||||||
|
<input type="text" class="form-control" name="clientName" id="clientName@(Model)"
|
||||||
|
placeholder="Unknown Soldier" value="@existingClientFilter?.ClientName"/>
|
||||||
|
<div class="custom-control ml-10 align-self-center">
|
||||||
|
<div class="custom-switch">
|
||||||
|
@if (existingClientFilter?.IsExactClientName ?? false)
|
||||||
|
{
|
||||||
|
<input type="checkbox" id="isExactClientName@(Model)" name="isExactClientName" value="true"
|
||||||
|
checked="checked">
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<input type="checkbox" id="isExactClientName@(Model)" name="isExactClientName" value="true">
|
||||||
|
}
|
||||||
|
<label for="isExactClientName@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_EXACT"]</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="clientIP@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_IP"]</label>
|
||||||
|
<div class="d-flex">
|
||||||
|
<input type="text" class="form-control" name="clientIP" id="clientIP@(Model)" placeholder="1.1.1.1"
|
||||||
|
value="@existingClientFilter?.ClientIp">
|
||||||
|
<div class="custom-control ml-10 align-self-center">
|
||||||
|
<div class="custom-switch">
|
||||||
|
@if (existingClientFilter?.IsExactClientIp ?? false)
|
||||||
|
{
|
||||||
|
<input type="checkbox" id="isExactClientIP@(Model)" name="isExactClientIP" value="true"
|
||||||
|
checked="checked">
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<input type="checkbox" id="isExactClientIP@(Model)" name="isExactClientIP" value="true">
|
||||||
|
}
|
||||||
|
<label for="isExactClientIP@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_EXACT"]</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="clientGuid@(Model)">GUID <span class="text-primary">•</span> XUID <span class="text-primary">•</span> NetworkID</label>
|
||||||
|
<input type="text" class="form-control" name="clientGuid" id="clientGuid@(Model)"
|
||||||
|
placeholder="110000100000001" value="@existingClientFilter?.ClientGuid"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="clientLevel@(Model)" class="w-half">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_PERMISSION"]</label>
|
||||||
|
<label for="clientGameName@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_GAME"]</label>
|
||||||
|
|
||||||
|
<div class="d-flex">
|
||||||
|
<select class="form-control w-half" id="clientLevel@(Model)" name="clientLevel">
|
||||||
|
<option value="">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_SELECT_PERMISSIONS_ANY"]</option>
|
||||||
|
@foreach (EFClient.Permission permission in Enum.GetValues(typeof(EFClient.Permission)))
|
||||||
|
{
|
||||||
|
<option value="@((int)permission)" selected="@(permission == existingClientFilter?.ClientLevel)">
|
||||||
|
@permission.ToLocalizedLevelName()
|
||||||
|
</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
<select class="form-control w-half ml-10" id="clientGameName@(Model)" name="gameName">
|
||||||
|
<option value="">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_SELECT_PERMISSIONS_ANY"]</option>
|
||||||
|
@foreach (Reference.Game game in Enum.GetValues(typeof(Reference.Game)))
|
||||||
|
{
|
||||||
|
<option value="@((int)game)" selected="@(game == existingClientFilter?.GameName)">
|
||||||
|
@ViewBag.Localization["GAME_" + game]
|
||||||
|
</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="clientConnected@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_CONNECTED_SINCE"]</label>
|
||||||
|
<div class="d-flex">
|
||||||
|
@{ var presetDate = (existingClientFilter?.ClientConnected ?? DateTime.UtcNow.AddYears(-1)).ToString("s", CultureInfo.InvariantCulture); }
|
||||||
|
<input type="text" class="form-control date-picker-input w-half" name="clientConnected"
|
||||||
|
id="clientConnected@(Model)" data-date="@presetDate"
|
||||||
|
value="@presetDate"/>
|
||||||
|
|
||||||
|
<div class="custom-control ml-10 align-self-center">
|
||||||
|
<div class="custom-switch">
|
||||||
|
@if ((existingClientFilter?.Direction ?? SortDirection.Descending) is SortDirection.Descending)
|
||||||
|
{
|
||||||
|
<input type="checkbox" id="resultOrder@(Model)" name="direction"
|
||||||
|
value="@((int)SortDirection.Ascending)">
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<input type="checkbox" id="resultOrder@(Model)" name="direction"
|
||||||
|
value="@((int)SortDirection.Ascending)" checked="checked">
|
||||||
|
}
|
||||||
|
<label for="resultOrder@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_OLDEST_FIRST"]</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="submit" class="btn btn-primary" value="@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_BUTTON_SUBMIT"]"/>
|
||||||
|
</div>
|
@ -1,126 +1,21 @@
|
|||||||
@using Data.Models.Client
|
|
||||||
@using SharedLibraryCore.Dtos
|
|
||||||
@using WebfrontCore.QueryHelpers.Models
|
|
||||||
@using Data.Models
|
|
||||||
@using System.Globalization
|
|
||||||
@model string
|
@model string
|
||||||
@{
|
|
||||||
var existingClientFilter = ViewBag.ClientResourceRequest as ClientResourceRequest;
|
|
||||||
}
|
|
||||||
<h6 class="dropdown-header">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_TITLE"]</h6>
|
<h6 class="dropdown-header">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_TITLE"]</h6>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<div class="dropdown-content">
|
<div class="dropdown-content">
|
||||||
|
|
||||||
|
<div class="form-group" id="searchTypeSelectorParent">
|
||||||
|
<label for="searchType@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_FOR"]</label>
|
||||||
|
<br/>
|
||||||
|
<select class="form-control" id="searchType@(Model)" name="searchType">
|
||||||
|
<option value="client">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_SELECT_TYPE_PLAYERS"]</option>
|
||||||
|
<option value="chat">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_SELECT_TYPE_CHAT"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<form asp-controller="Client" asp-action="AdvancedFind" method="get" id="advancedSearchDropdownContent@(Model)" onsubmit="showLoader()">
|
<form asp-controller="Client" asp-action="AdvancedFind" method="get" id="advancedSearchDropdownContent@(Model)" onsubmit="showLoader()">
|
||||||
<div class="form-group">
|
<partial name="Search/_ClientSearch.cshtml" model="@Model"/>
|
||||||
<label for="searchType@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_FOR"]</label>
|
</form>
|
||||||
<br/>
|
<form asp-controller="Stats" asp-action="FindMessage" method="get" onsubmit="showLoader()">
|
||||||
<select class="form-control" id="searchType@(Model)" name="searchType" disabled="disabled">
|
<partial name="Search/_ChatSearch.cshtml" model="@Model"/>
|
||||||
<option value="client">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_SELECT_TYPE_PLAYERS"]</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="clientName@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_NAME"]</label>
|
|
||||||
<div class="d-flex">
|
|
||||||
<input type="text" class="form-control" name="clientName" id="clientName@(Model)"
|
|
||||||
placeholder="Unknown Soldier" value="@existingClientFilter?.ClientName"/>
|
|
||||||
<div class="custom-control ml-10 align-self-center">
|
|
||||||
<div class="custom-switch">
|
|
||||||
@if (existingClientFilter?.IsExactClientName ?? false)
|
|
||||||
{
|
|
||||||
<input type="checkbox" id="isExactClientName@(Model)" name="isExactClientName" value="true"
|
|
||||||
checked="checked">
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<input type="checkbox" id="isExactClientName@(Model)" name="isExactClientName" value="true">
|
|
||||||
}
|
|
||||||
<label for="isExactClientName@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_EXACT"]</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="clientIP@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_IP"]</label>
|
|
||||||
<div class="d-flex">
|
|
||||||
<input type="text" class="form-control" name="clientIP" id="clientIP@(Model)" placeholder="1.1.1.1"
|
|
||||||
value="@existingClientFilter?.ClientIp">
|
|
||||||
<div class="custom-control ml-10 align-self-center">
|
|
||||||
<div class="custom-switch">
|
|
||||||
@if (existingClientFilter?.IsExactClientIp ?? false)
|
|
||||||
{
|
|
||||||
<input type="checkbox" id="isExactClientIP@(Model)" name="isExactClientIP" value="true"
|
|
||||||
checked="checked">
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<input type="checkbox" id="isExactClientIP@(Model)" name="isExactClientIP" value="true">
|
|
||||||
}
|
|
||||||
<label for="isExactClientIP@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_EXACT"]</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="clientGuid@(Model)">GUID <span class="text-primary">•</span> XUID <span class="text-primary">•</span> NetworkID</label>
|
|
||||||
<input type="text" class="form-control" name="clientGuid" id="clientGuid@(Model)"
|
|
||||||
placeholder="110000100000001" value="@existingClientFilter?.ClientGuid"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="clientLevel@(Model)" class="w-half">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_PERMISSION"]</label>
|
|
||||||
<label for="clientGameName@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_GAME"]</label>
|
|
||||||
|
|
||||||
<div class="d-flex">
|
|
||||||
<select class="form-control w-half" id="clientLevel@(Model)" name="clientLevel">
|
|
||||||
<option value="">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_SELECT_PERMISSIONS_ANY"]</option>
|
|
||||||
@foreach (EFClient.Permission permission in Enum.GetValues(typeof(EFClient.Permission)))
|
|
||||||
{
|
|
||||||
<option value="@((int)permission)" selected="@(permission == existingClientFilter?.ClientLevel)">
|
|
||||||
@permission.ToLocalizedLevelName()
|
|
||||||
</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
<select class="form-control w-half ml-10" id="clientGameName@(Model)" name="gameName">
|
|
||||||
<option value="">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_SELECT_PERMISSIONS_ANY"]</option>
|
|
||||||
@foreach (Reference.Game game in Enum.GetValues(typeof(Reference.Game)))
|
|
||||||
{
|
|
||||||
<option value="@((int)game)" selected="@(game == existingClientFilter?.GameName)">
|
|
||||||
@ViewBag.Localization["GAME_" + game]
|
|
||||||
</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="clientConnected@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_CONNECTED_SINCE"]</label>
|
|
||||||
<div class="d-flex">
|
|
||||||
@{ var presetDate = (existingClientFilter?.ClientConnected ?? DateTime.UtcNow.AddYears(-1)).ToString("s", CultureInfo.InvariantCulture); }
|
|
||||||
<input type="text" class="form-control date-picker-input w-half" name="clientConnected"
|
|
||||||
id="clientConnected@(Model)" data-date="@presetDate"
|
|
||||||
value="@presetDate"/>
|
|
||||||
|
|
||||||
<div class="custom-control ml-10 align-self-center">
|
|
||||||
<div class="custom-switch">
|
|
||||||
@if ((existingClientFilter?.Direction ?? SortDirection.Descending) is SortDirection.Descending)
|
|
||||||
{
|
|
||||||
<input type="checkbox" id="resultOrder@(Model)" name="direction"
|
|
||||||
value="@((int)SortDirection.Ascending)">
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<input type="checkbox" id="resultOrder@(Model)" name="direction"
|
|
||||||
value="@((int)SortDirection.Ascending)" checked="checked">
|
|
||||||
}
|
|
||||||
<label for="resultOrder@(Model)">@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_LABEL_OLDEST_FIRST"]</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input type="submit" class="btn btn-primary" value="@ViewBag.Localization["WEBFRONT_ADVANCED_SEARCH_BUTTON_SUBMIT"]"/>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,28 +1,4 @@
|
|||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$('.form-inline').submit(function (e) {
|
|
||||||
const id = $(e.currentTarget).find('input');
|
|
||||||
if ($(id).val().length < 3) {
|
|
||||||
e.preventDefault();
|
|
||||||
$(id)
|
|
||||||
.addClass('input-text-danger')
|
|
||||||
.delay(25)
|
|
||||||
.queue(function () {
|
|
||||||
$(this).addClass('input-border-transition').dequeue();
|
|
||||||
})
|
|
||||||
.delay(1000)
|
|
||||||
.queue(function () {
|
|
||||||
$(this).removeClass('input-text-danger').dequeue();
|
|
||||||
})
|
|
||||||
.delay(500)
|
|
||||||
.queue(function () {
|
|
||||||
$(this).removeClass('input-border-transition').dequeue();
|
|
||||||
});
|
|
||||||
} else if ($(id).val().startsWith("chat|")) {
|
|
||||||
e.preventDefault();
|
|
||||||
window.location = "/Message/Find?query=" + $(id).val();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.date-picker-input').each((index, selector) => {
|
$('.date-picker-input').each((index, selector) => {
|
||||||
new Datepicker(selector, {
|
new Datepicker(selector, {
|
||||||
buttonClass: 'btn',
|
buttonClass: 'btn',
|
||||||
@ -31,5 +7,37 @@
|
|||||||
prevArrow: '<',
|
prevArrow: '<',
|
||||||
orientation: 'auto top'
|
orientation: 'auto top'
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
|
const clientSearchWrapper = $('*[id^="clientSearchWrapper"]');
|
||||||
|
const chatSearchWrapper = $('*[id^="chatSearchWrapper"]');
|
||||||
|
const searchTypeSelector = $('#searchTypeSelectorParent select');
|
||||||
|
let isClients = false;
|
||||||
|
|
||||||
|
searchTypeSelector.on('change', function () {
|
||||||
|
if (isClients) {
|
||||||
|
clientSearchWrapper.removeClass('d-none');
|
||||||
|
chatSearchWrapper.addClass('d-none');
|
||||||
|
} else {
|
||||||
|
chatSearchWrapper.removeClass('d-none');
|
||||||
|
clientSearchWrapper.addClass('d-none');
|
||||||
|
}
|
||||||
|
isClients = !isClients;
|
||||||
|
});
|
||||||
|
|
||||||
|
const isDefault = clientSearchWrapper.data('has-data') !== 'True' && chatSearchWrapper.data('has-data') !== 'True';
|
||||||
|
|
||||||
|
if (isDefault) {
|
||||||
|
isClients = false;
|
||||||
|
searchTypeSelector.val('client').change();
|
||||||
|
} else {
|
||||||
|
if (clientSearchWrapper.data('has-data') === 'True') {
|
||||||
|
isClients = false;
|
||||||
|
searchTypeSelector.val('client').change();
|
||||||
|
}
|
||||||
|
if (chatSearchWrapper.data('has-data') === 'True') {
|
||||||
|
isClients = true;
|
||||||
|
searchTypeSelector.val('chat').change();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
BIN
assets/github/icon.png
Normal file
BIN
assets/github/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
Loading…
Reference in New Issue
Block a user