Skip to content

Commit 7477db1

Browse files
feat: Add agent name configuration and improve UI for global settings
- Introduced a new property `AgentName` in `GlobalSettingsViewModel` with a default value. - Updated the agent configuration view to include an input for `AgentConfigName` with validation and help text. - Enhanced the game server edit view to support agent name input and preview in broadcast messages. - Improved layout and organization of global settings form, including agent-related settings and moderation defaults. - Added JavaScript functionality to dynamically update broadcast message previews based on agent name.
1 parent d65add0 commit 7477db1

7 files changed

Lines changed: 522 additions & 413 deletions

File tree

src/XtremeIdiots.Portal.Web/Controllers/GameServersController.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,7 @@ private void PopulateConfigFromNamespace(GameServerEditViewModel editModel, Conf
606606
case "agent":
607607
editModel.AgentConfigLogFilePath = GetStringProperty(root, "logFilePath");
608608
editModel.AgentConfigRconSyncEnabled = GetBoolProperty(root, "rconSyncEnabled", true);
609+
editModel.AgentConfigName = GetStringProperty(root, "agentName");
609610
break;
610611
case "banfiles":
611612
editModel.BanFileSyncConfigCheckIntervalSeconds = GetIntProperty(root, "checkIntervalSeconds", 60);
@@ -705,6 +706,13 @@ private static List<BroadcastMessageViewModel> GetBroadcastMessages(JsonElement
705706
return messages;
706707
}
707708

709+
private static string NormalizeAgentName(string? value)
710+
{
711+
return string.IsNullOrWhiteSpace(value)
712+
? GlobalSettingsViewModel.DefaultAgentName
713+
: value;
714+
}
715+
708716
private void PopulateGlobalDefaults(GameServerEditViewModel editModel, ConfigurationDto config)
709717
{
710718
try
@@ -717,6 +725,9 @@ private void PopulateGlobalDefaults(GameServerEditViewModel editModel, Configura
717725

718726
switch (config.Namespace)
719727
{
728+
case "agent":
729+
editModel.GlobalAgentName = NormalizeAgentName(GetStringProperty(root, "agentName"));
730+
break;
720731
case "moderation":
721732
var legacyThreshold = GetNullableIntProperty(root, "contentSafetySeverityThreshold");
722733
editModel.GlobalModerationHateSeverityThreshold = GetIntProperty(root, "contentSafetyHateSeverityThreshold", legacyThreshold ?? editModel.GlobalModerationHateSeverityThreshold);
@@ -831,7 +842,10 @@ await UpsertConfigSafeAsync(gameServerId, "rcon", JsonSerializer.Serialize(new
831842
await UpsertConfigSafeAsync(gameServerId, "agent", JsonSerializer.Serialize(new
832843
{
833844
logFilePath = model.AgentConfigLogFilePath,
834-
rconSyncEnabled = model.AgentConfigRconSyncEnabled
845+
rconSyncEnabled = model.AgentConfigRconSyncEnabled,
846+
agentName = string.IsNullOrWhiteSpace(model.AgentConfigName)
847+
? null
848+
: model.AgentConfigName
835849
}, configJsonOptions), serverTitle, errors, cancellationToken).ConfigureAwait(false);
836850
}
837851

src/XtremeIdiots.Portal.Web/Controllers/GlobalSettingsController.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,17 @@ public async Task<IActionResult> Index(GlobalSettingsViewModel model, Cancellati
7070
if (modelStateResult is not null)
7171
return modelStateResult;
7272

73+
model.AgentName = NormalizeAgentName(model.AgentName);
74+
7375
var errors = new List<string>();
7476

7577
await UpsertConfigSafeAsync("agent", JsonSerializer.Serialize(new
7678
{
7779
pollIntervalMs = model.AgentPollIntervalMs,
7880
statusPublishIntervalSeconds = model.AgentStatusPublishIntervalSeconds,
7981
rconSyncIntervalSeconds = model.AgentRconSyncIntervalSeconds,
80-
offsetSaveIntervalSeconds = model.AgentOffsetSaveIntervalSeconds
82+
offsetSaveIntervalSeconds = model.AgentOffsetSaveIntervalSeconds,
83+
agentName = model.AgentName
8184
}, configJsonOptions), errors, cancellationToken).ConfigureAwait(false);
8285

8386
await UpsertConfigSafeAsync("banfiles", JsonSerializer.Serialize(new
@@ -131,6 +134,7 @@ private void PopulateModelFromNamespace(GlobalSettingsViewModel model, Configura
131134
model.AgentStatusPublishIntervalSeconds = GetIntProperty(root, "statusPublishIntervalSeconds", model.AgentStatusPublishIntervalSeconds);
132135
model.AgentRconSyncIntervalSeconds = GetIntProperty(root, "rconSyncIntervalSeconds", model.AgentRconSyncIntervalSeconds);
133136
model.AgentOffsetSaveIntervalSeconds = GetIntProperty(root, "offsetSaveIntervalSeconds", model.AgentOffsetSaveIntervalSeconds);
137+
model.AgentName = NormalizeAgentName(GetStringProperty(root, "agentName"));
134138
break;
135139
case "banfiles":
136140
model.BanFileSyncCheckIntervalSeconds = GetIntProperty(root, "checkIntervalSeconds", model.BanFileSyncCheckIntervalSeconds);
@@ -167,6 +171,20 @@ private static int GetIntProperty(JsonElement root, string propertyName, int def
167171
: defaultValue;
168172
}
169173

174+
private static string? GetStringProperty(JsonElement root, string propertyName)
175+
{
176+
return root.TryGetProperty(propertyName, out var prop) && prop.ValueKind == JsonValueKind.String
177+
? prop.GetString()
178+
: null;
179+
}
180+
181+
private static string NormalizeAgentName(string? value)
182+
{
183+
return string.IsNullOrWhiteSpace(value)
184+
? GlobalSettingsViewModel.DefaultAgentName
185+
: value;
186+
}
187+
170188
private static int? GetNullableIntProperty(JsonElement root, string propertyName)
171189
{
172190
return root.TryGetProperty(propertyName, out var prop) &&

src/XtremeIdiots.Portal.Web/ViewModels/GameServerEditViewModel.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ public class GameServerEditViewModel : IValidatableObject
4343
[DisplayName("RCON Sync Enabled")]
4444
public bool AgentConfigRconSyncEnabled { get; set; } = true;
4545

46+
[DisplayName("Agent Name Override")]
47+
[MaxLength(120, ErrorMessage = "Agent name override must be 120 characters or fewer.")]
48+
public string? AgentConfigName { get; set; }
49+
4650
// Ban File Sync configuration (parsed from "banfiles" config namespace)
4751

4852
[DisplayName("Check Interval (seconds)")]
@@ -110,6 +114,7 @@ public class GameServerEditViewModel : IValidatableObject
110114
public int GlobalModerationMinMessageLength { get; set; } = 5;
111115
public int GlobalEventsStaleThresholdSeconds { get; set; } = 120;
112116
public int GlobalEventsPlayerCacheExpirationSeconds { get; set; } = 900;
117+
public string GlobalAgentName { get; set; } = GlobalSettingsViewModel.DefaultAgentName;
113118

114119
// Auth flags for tab visibility
115120

src/XtremeIdiots.Portal.Web/ViewModels/GlobalSettingsViewModel.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace XtremeIdiots.Portal.Web.ViewModels;
1010
public class GlobalSettingsViewModel
1111
{
1212
public const int DisabledSeverityThreshold = -1;
13+
public const string DefaultAgentName = "^4[^1>XI< BOT^4]^7";
1314

1415
// Agent defaults
1516
[DisplayName("Poll Interval (ms)")]
@@ -28,6 +29,10 @@ public class GlobalSettingsViewModel
2829
[Range(1, int.MaxValue, ErrorMessage = "Offset save interval must be at least 1 second.")]
2930
public int AgentOffsetSaveIntervalSeconds { get; set; } = 30;
3031

32+
[DisplayName("Agent Name")]
33+
[MaxLength(120, ErrorMessage = "Agent name must be 120 characters or fewer.")]
34+
public string AgentName { get; set; } = DefaultAgentName;
35+
3136
// Ban file sync defaults
3237
[DisplayName("Check Interval (s)")]
3338
[Range(1, int.MaxValue, ErrorMessage = "Check interval must be at least 1 second.")]

src/XtremeIdiots.Portal.Web/Views/GameServers/ConfigurationSections/_AgentConfiguration.cshtml

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,30 @@
1313
@if (Model.GameServer.FtpEnabled && Model.CanEditFtp)
1414
{
1515
<button class="btn btn-outline-secondary" type="button"
16-
onclick="openFtpBrowser('@Model.GameServer.GameServerId', 'AgentConfigLogFilePath', '.log')">
16+
onclick="openFtpBrowser('@Model.GameServer.GameServerId', 'AgentConfigLogFilePath', '.log')">
1717
<i class="fa-solid fa-fw fa-folder-open"></i>Browse
1818
</button>
1919
}
2020
</div>
2121
<span asp-validation-for="AgentConfigLogFilePath" class="text-danger"></span>
22-
<small class="form-text text-muted">The file system path to the game server log file that the agent monitors.</small>
23-
</div>
24-
<div class="form-check mb-3">
25-
<label class="form-check-label">
26-
<input class="form-check-input" asp-for="AgentConfigRconSyncEnabled" />
27-
@Html.DisplayNameFor(m => m.AgentConfigRconSyncEnabled)
28-
</label>
29-
<small class="form-text text-muted d-block">When enabled, the agent will synchronize RCON commands with the game server.</small>
30-
</div>
31-
</div>
32-
</div>
22+
<small class="form-text text-muted">The file system path to the game server log file that the agent
23+
monitors.</small>
24+
</div>
25+
<div class="form-check mb-3">
26+
<label class="form-check-label">
27+
<input class="form-check-input" asp-for="AgentConfigRconSyncEnabled" />
28+
@Html.DisplayNameFor(m => m.AgentConfigRconSyncEnabled)
29+
</label>
30+
<small class="form-text text-muted d-block">When enabled, the agent will synchronize RCON commands with the
31+
game server.</small>
32+
</div>
33+
<div class="mb-3">
34+
<label asp-for="AgentConfigName" class="form-label"></label>
35+
<input asp-for="AgentConfigName" class="form-control" maxlength="120" placeholder="@Model.GlobalAgentName"
36+
data-global-agent-name="@Model.GlobalAgentName" />
37+
<span asp-validation-for="AgentConfigName" class="text-danger"></span>
38+
<small class="form-text text-muted">Optional. Leave empty to use global default:
39+
@Model.GlobalAgentName</small>
40+
</div>
41+
</div>
42+
</div>

0 commit comments

Comments
 (0)