106 lines
3.8 KiB
C#
106 lines
3.8 KiB
C#
namespace AcDream.App.UI.Layout;
|
|
|
|
internal sealed record CharacterManagementUiMountResources(
|
|
uint LayoutId,
|
|
ImportedLayout Layout,
|
|
Func<uint, uint, UiElement?> TemplateResolver,
|
|
CharacterManagementUiController.DialogStrings Strings);
|
|
|
|
/// <summary>
|
|
/// Retryable, idempotent composition edge for the pre-world character screen.
|
|
/// DATs can become readable after the graphical runtime starts (installer copy,
|
|
/// mapped-file replacement, or a transient catalog miss), so an unavailable
|
|
/// dialog catalog, root, template, or string must not permanently suppress the
|
|
/// screen. Once bound, later ticks are no-ops and cannot duplicate the root or
|
|
/// controller lifetime.
|
|
/// </summary>
|
|
internal sealed class CharacterManagementUiMountCoordinator : IDisposable
|
|
{
|
|
private readonly UiRoot _host;
|
|
private readonly CharacterSelectionRuntimeBindings _bindings;
|
|
private readonly Func<RetailDialogFactory?> _ensureDialogs;
|
|
private readonly Func<CharacterManagementUiMountResources?> _loadResources;
|
|
private bool _disposed;
|
|
|
|
public CharacterManagementUiMountCoordinator(
|
|
UiRoot host,
|
|
CharacterSelectionRuntimeBindings bindings,
|
|
Func<RetailDialogFactory?> ensureDialogs,
|
|
Func<CharacterManagementUiMountResources?> loadResources)
|
|
{
|
|
_host = host ?? throw new ArgumentNullException(nameof(host));
|
|
_bindings = bindings ?? throw new ArgumentNullException(nameof(bindings));
|
|
_ensureDialogs = ensureDialogs
|
|
?? throw new ArgumentNullException(nameof(ensureDialogs));
|
|
_loadResources = loadResources
|
|
?? throw new ArgumentNullException(nameof(loadResources));
|
|
}
|
|
|
|
public CharacterManagementUiController? Controller { get; private set; }
|
|
|
|
public void Tick()
|
|
{
|
|
if (_disposed || Controller is not null)
|
|
return;
|
|
|
|
try
|
|
{
|
|
RetailDialogFactory? dialogs = _ensureDialogs();
|
|
if (dialogs is null)
|
|
return;
|
|
|
|
CharacterManagementUiMountResources? resources = _loadResources();
|
|
if (resources is null)
|
|
return;
|
|
|
|
CharacterManagementUiController? candidate =
|
|
CharacterManagementUiController.CreateDetached(
|
|
_host,
|
|
resources.Layout,
|
|
resources.TemplateResolver,
|
|
dialogs,
|
|
_bindings,
|
|
resources.Strings);
|
|
if (candidate is null)
|
|
return;
|
|
|
|
// Take ownership before the first attach/tick. Template resolution
|
|
// happens inside that tick and can throw after the root and button
|
|
// handlers are live; the catch below can therefore always retire
|
|
// the exact partial controller before a later retry.
|
|
Controller = candidate;
|
|
candidate.AttachAndTick();
|
|
Console.WriteLine(
|
|
$"[UI] retail character management from enum table 5 "
|
|
+ $"(0x10000005 -> 0x{resources.LayoutId:X8}, "
|
|
+ "root 0x1000039A; flat list, no viewport).");
|
|
}
|
|
catch (Exception error)
|
|
{
|
|
CharacterManagementUiController? partial = Controller;
|
|
Controller = null;
|
|
try
|
|
{
|
|
partial?.Dispose();
|
|
}
|
|
catch (Exception cleanupError)
|
|
{
|
|
Console.WriteLine(
|
|
"[UI] character management partial-mount cleanup failed: "
|
|
+ cleanupError.Message);
|
|
}
|
|
Console.WriteLine(
|
|
"[UI] character management mount will retry after resource "
|
|
+ $"recovery: {error.Message}");
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (_disposed)
|
|
return;
|
|
_disposed = true;
|
|
Controller?.Dispose();
|
|
Controller = null;
|
|
}
|
|
}
|