Harden retail character selection recovery

This commit is contained in:
Erik 2026-08-14 20:59:10 +02:00
parent 6cfab727f1
commit aeac874dab
8 changed files with 736 additions and 81 deletions

View file

@ -434,6 +434,7 @@ public sealed class RetailUiRuntime : IDisposable
private UiShortcutDigitGraphics? _shortcutDigitGraphics;
private ItemCooldownUiController? _itemCooldownController;
private VividTargetIndicatorController? _vividTargetIndicator;
private CharacterManagementUiMountCoordinator? _characterManagementMount;
private IDisposable? _characterSheetSubscription;
private ResourceShutdownTransaction? _shutdown;
private bool _disposed;
@ -500,7 +501,8 @@ public sealed class RetailUiRuntime : IDisposable
MountVendor();
MountSecureTrade();
MountItemCooldowns();
MountCharacterManagement();
ConfigureCharacterManagement();
_characterManagementMount?.Tick();
Host.WindowManager.WindowVisibilityChanged += OnWindowVisibilityChanged;
BindToolbarPanelButtons();
SyncToolbarWindowButtons();
@ -595,7 +597,8 @@ public sealed class RetailUiRuntime : IDisposable
public VendorUiController? VendorController { get; private set; }
public OptionsPanelController? OptionsPanelController { get; private set; }
public SocialPanelController? SocialPanelController { get; private set; }
internal CharacterManagementUiController? CharacterManagementController { get; private set; }
internal CharacterManagementUiController? CharacterManagementController =>
_characterManagementMount?.Controller;
public static RetailUiRuntime Mount(RetailUiRuntimeBindings bindings)
{
@ -641,6 +644,7 @@ public sealed class RetailUiRuntime : IDisposable
ExternalContainerController?.Tick();
SocialPanelController?.Tick();
_itemCooldownController?.Tick();
_characterManagementMount?.Tick();
CharacterManagementController?.Tick();
DialogFactory?.Tick();
Host.Tick(deltaSeconds);
@ -3051,13 +3055,29 @@ public sealed class RetailUiRuntime : IDisposable
private void MountDialogFactory()
{
if (DialogFactory is not null)
return;
uint layoutId;
lock (_bindings.Assets.DatLock)
try
{
// DialogFactory::CreateDialog_ @ 0x00477AD0 resolves the shared
// catalog through GetDIDByEnum(2, 5). Each shown DialogInfo then
// creates a fresh type-specific root from that catalog.
layoutId = RetailDataIdResolver.Resolve(_bindings.Assets.Dats, 2u, 5u);
lock (_bindings.Assets.DatLock)
{
// DialogFactory::CreateDialog_ @ 0x00477AD0 resolves the shared
// catalog through GetDIDByEnum(2, 5). Each shown DialogInfo then
// creates a fresh type-specific root from that catalog.
layoutId = RetailDataIdResolver.Resolve(
_bindings.Assets.Dats,
2u,
5u);
}
}
catch (Exception error)
{
Console.WriteLine(
"[UI] retail dialog catalog will retry after resource "
+ $"recovery: {error.Message}");
return;
}
if (layoutId == 0u)
@ -3690,19 +3710,28 @@ public sealed class RetailUiRuntime : IDisposable
"[M4] retail secure trade panel mounted from LayoutDesc 0x2100000D.");
}
private void MountCharacterManagement()
private void ConfigureCharacterManagement()
{
CharacterSelectionRuntimeBindings? bindings =
_bindings.CharacterSelection;
if (bindings is null)
if (bindings is null || _characterManagementMount is not null)
return;
if (DialogFactory is null)
{
Console.WriteLine(
"[UI] character management: retail DialogFactory is unavailable.");
return;
}
_characterManagementMount = new CharacterManagementUiMountCoordinator(
Host.Root,
bindings,
EnsureDialogFactory,
LoadCharacterManagementResources);
}
private RetailDialogFactory? EnsureDialogFactory()
{
MountDialogFactory();
return DialogFactory;
}
private CharacterManagementUiMountResources? LoadCharacterManagementResources()
{
const uint stringTableId = 0x23000002u;
uint layoutId;
ImportedLayout? layout;
@ -3730,7 +3759,7 @@ public sealed class RetailUiRuntime : IDisposable
{
Console.WriteLine(
"[UI] character management: enum-table-5 root could not be imported.");
return;
return null;
}
string? deleteResponse;
@ -3767,7 +3796,7 @@ public sealed class RetailUiRuntime : IDisposable
{
Console.WriteLine(
"[UI] character management: required retail strings are unavailable.");
return;
return null;
}
UiElement? ResolveTemplate(uint templateLayoutId, uint templateElementId)
@ -3798,23 +3827,15 @@ public sealed class RetailUiRuntime : IDisposable
}
}
CharacterManagementController = CharacterManagementUiController.Bind(
Host.Root,
return new CharacterManagementUiMountResources(
layoutId,
layout,
ResolveTemplate,
DialogFactory,
bindings,
new CharacterManagementUiController.DialogStrings(
ComposeDeleteConfirmation,
deleteResponse,
pleaseWait,
enteringWorld));
if (CharacterManagementController is null)
return;
Console.WriteLine(
$"[UI] retail character management from enum table 5 "
+ $"(0x10000005 -> 0x{layoutId:X8}, root 0x1000039A; flat list, no viewport).");
}
private static string? ResolveCharacterManagementString(
@ -3873,7 +3894,7 @@ public sealed class RetailUiRuntime : IDisposable
() => _itemConfirmationController?.Dispose(),
() =>
{
CharacterManagementController?.Dispose();
_characterManagementMount?.Dispose();
_gameplayConfirmationController?.Dispose();
},
() => DialogFactory?.Dispose(),