Compare commits

..

No commits in common. "release/pre" and "2023.08.27.3-prerelease" have entirely different histories.

19 changed files with 26 additions and 190 deletions

View File

@ -1251,10 +1251,6 @@
"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"

View File

@ -433,11 +433,6 @@ namespace IW4MAdmin.Application
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")

View File

@ -8,11 +8,9 @@ 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;
@ -20,7 +18,6 @@ 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;
@ -30,10 +27,8 @@ 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;
} }
@ -80,9 +75,7 @@ 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 });
@ -210,7 +203,7 @@ public class ClientResourceQueryHelper : IResourceQueryHelper<ClientResourceRequ
} }
private static IQueryable<ClientAlias> SearchByIp(ClientResourceRequest query, private static IQueryable<ClientAlias> SearchByIp(ClientResourceRequest query,
IQueryable<ClientAlias> clientAliases, bool canSearchIP) IQueryable<ClientAlias> clientAliases)
{ {
var ipString = query.ClientIp.Trim(); var ipString = query.ClientIp.Trim();
var ipAddress = ipString.ConvertToIP(); var ipAddress = ipString.ConvertToIP();
@ -220,7 +213,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 if(canSearchIP) else
{ {
clientAliases = clientAliases.Where(clientAlias => clientAliases = clientAliases.Where(clientAlias =>
EF.Functions.Like(clientAlias.Alias.SearchableIPAddress, $"{ipString}%")); EF.Functions.Like(clientAlias.Alias.SearchableIPAddress, $"{ipString}%"));

View File

@ -194,14 +194,10 @@ 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))
{ {
continue;
}
value = regex.Groups[parserRegex.GroupMapping[groupType]].ToString(); value = regex.Groups[parserRegex.GroupMapping[groupType]].ToString();
break; }
} }
if (value == null) if (value == null)

View File

@ -16,8 +16,7 @@
T7 = 8, T7 = 8,
SHG1 = 9, SHG1 = 9,
CSGO = 10, CSGO = 10,
H1 = 11, H1 = 11
L4D2 = 12,
} }
public enum ConnectionType public enum ConnectionType

View File

@ -88,7 +88,7 @@ IsBotWrapper( client )
GetXuidWrapper() GetXuidWrapper()
{ {
return self GetGuid(); return self GetXUID();
} }
////////////////////////////////// //////////////////////////////////

View File

@ -53,7 +53,6 @@ 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}"

View File

@ -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.HostnameStatus.AddMapping(113, 1); rconParser.Configuration.MapStatus.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.MaxPlayersStatus.AddMapping(114, 1); rconParser.Configuration.MapStatus.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);

View File

@ -3,7 +3,7 @@ let eventParser;
const plugin = { const plugin = {
author: 'RaidMax', author: 'RaidMax',
version: 0.7, version: 0.6,
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.HostnameStatus.AddMapping(113, 1); rconParser.Configuration.MapStatus.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.MaxPlayersStatus.AddMapping(114, 1); rconParser.Configuration.MapStatus.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);

View File

@ -1,135 +0,0 @@
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) {
}
};

View File

@ -28,19 +28,18 @@ namespace Stats.Dtos
/// <summary> /// <summary>
/// The time associated with SentAfter date /// The time associated with SentAfter date
/// </summary> /// </summary>
public string SentAfterTime { get; set; } public string SentAfterTime { get; set; } = "00:00";
public DateTime? SentAfterDateTime => SentAfter?.Add(string.IsNullOrEmpty(SentAfterTime) ? TimeSpan.Zero : TimeSpan.Parse(SentAfterTime)); public DateTime? SentAfterDateTime => SentAfter?.Add(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.Date; public DateTime SentBefore { get; set; } = DateTime.UtcNow.Date;
public string SentBeforeTime { get; set; } public string SentBeforeTime { get; set; } = DateTime.UtcNow.ToString("HH:mm");
public DateTime? SentBeforeDateTime => public DateTime? SentBeforeDateTime => SentBefore.Add(TimeSpan.Parse(SentBeforeTime));
SentBefore.Add(string.IsNullOrEmpty(SentBeforeTime) ? TimeSpan.Zero : TimeSpan.Parse(SentBeforeTime));
public bool IsExactMatch { get; set; } public bool IsExactMatch { get; set; }

View File

@ -469,8 +469,6 @@ 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
}; };

View File

@ -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://git.rimmyscorner.com/Parasyn/IW4M-Admin/wiki) to find more information. #### Explore the [wiki](https://github.com/RaidMax/IW4M-Admin/wiki) to find more information.

View File

@ -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("https://master.iw4.zip"); [ConfigurationIgnore] public Uri MasterUrl { get; set; } = new("http://api.raidmax.org:5000");
public IBaseConfiguration Generate() public IBaseConfiguration Generate()
{ {

View File

@ -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 || _url.Scheme == Uri.UriSchemeHttps)); .Must(_url => _url != null && _url.Scheme == Uri.UriSchemeHttp);
RuleFor(_app => _app.CommandPrefix) RuleFor(_app => _app.CommandPrefix)
.NotEmpty(); .NotEmpty();

View File

@ -35,8 +35,7 @@ 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

View File

@ -250,7 +250,6 @@ 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)

View File

@ -17,8 +17,6 @@ public class ClientResourceRequest : ClientPaginationRequest
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) || public bool HasData => !string.IsNullOrEmpty(ClientName) || !string.IsNullOrEmpty(ClientIp) ||
!string.IsNullOrEmpty(ClientGuid) || ClientLevel is not null || GameName is not null; !string.IsNullOrEmpty(ClientGuid) || ClientLevel is not null || GameName is not null;
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB