feat(chat): port retail client command families

Expand the typed client-command boundary across travel, character queries, local UI and layout controls, AFK and consent, emotes, friends, squelch and filters, and fill-components. Preserve retail packet layouts and queue ownership, import the confirmation dialog, and keep authoritative social state in Core.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-13 14:50:15 +02:00
parent 5a45a7ac7f
commit 9ea579bdd0
47 changed files with 6659 additions and 99 deletions

View file

@ -162,7 +162,8 @@ public sealed class SettingsStore
ShowHelm: ReadBool(gp, "showHelm", d.ShowHelm),
ShowCloak: ReadBool(gp, "showCloak", d.ShowCloak),
LockUI: ReadBool(gp, "lockUI", d.LockUI),
UseMouseTurning: ReadBool(gp, "useMouseTurning", d.UseMouseTurning));
UseMouseTurning: ReadBool(gp, "useMouseTurning", d.UseMouseTurning),
AcceptLootPermits: ReadBool(gp, "acceptLootPermits", d.AcceptLootPermits));
}
catch (Exception ex)
{
@ -419,6 +420,70 @@ public sealed class SettingsStore
WriteMutableRoot(root);
}
/// <summary>
/// Load one window from a user-named retail <c>@saveui</c> profile.
/// Named profiles are intentionally independent of character and screen
/// resolution, matching retail's portable UI files.
/// </summary>
public UiWindowLayout? LoadNamedWindowLayout(
string profileName,
string windowName,
UiWindowLayout fallback)
{
ArgumentNullException.ThrowIfNull(profileName);
ArgumentException.ThrowIfNullOrWhiteSpace(windowName);
if (!File.Exists(_path)) return null;
try
{
var root = JsonNode.Parse(File.ReadAllText(_path)) as JsonObject;
JsonObject? node = root?["namedWindowLayouts"]?[profileName]?[windowName] as JsonObject;
return node is null
? null
: new UiWindowLayout(
ReadNodeFloat(node, "x", fallback.X),
ReadNodeFloat(node, "y", fallback.Y),
ReadNodeFloat(node, "width", fallback.Width),
ReadNodeFloat(node, "height", fallback.Height),
ReadNodeBool(node, "visible", fallback.Visible),
ReadNodeBool(node, "collapsed", fallback.Collapsed),
ReadNodeBool(node, "maximized", fallback.Maximized));
}
catch (Exception ex)
{
Console.WriteLine($"settings: failed to load named window layout from {_path}: {ex.Message}");
return null;
}
}
/// <summary>Save one window to a user-named retail <c>@saveui</c> profile.</summary>
public void SaveNamedWindowLayout(
string profileName,
string windowName,
UiWindowLayout layout)
{
ArgumentNullException.ThrowIfNull(profileName);
ArgumentException.ThrowIfNullOrWhiteSpace(windowName);
JsonObject root = LoadMutableRoot();
JsonObject profiles = GetOrCreateObject(root, "namedWindowLayouts");
JsonObject profile = GetOrCreateObject(profiles, profileName);
profile[windowName] = SerializeWindowLayout(layout);
root["version"] = CurrentSchemaVersion;
WriteMutableRoot(root);
}
private static JsonObject SerializeWindowLayout(UiWindowLayout layout) => new()
{
["x"] = layout.X,
["y"] = layout.Y,
["width"] = layout.Width,
["height"] = layout.Height,
["visible"] = layout.Visible,
["collapsed"] = layout.Collapsed,
["maximized"] = layout.Maximized,
};
private JsonObject LoadMutableRoot()
{
var dir = Path.GetDirectoryName(_path);
@ -481,6 +546,7 @@ public sealed class SettingsStore
=> new(StringComparer.Ordinal)
{
["advancedCombatUI"] = g.AdvancedCombatUI,
["acceptLootPermits"] = g.AcceptLootPermits,
["allowGive"] = g.AllowGive,
["autoRepeatAttack"] = g.AutoRepeatAttack,
["autoTarget"] = g.AutoTarget,