Mounts gmCharGenMainUI (enum 0x10000039, root 0x100003CC) via CharacterCreationUiController/CharacterCreationUiMountCoordinator, cloning CharacterManagementUiController's recipe. Master shell ports SetProgressState @0x004e7a10 (Olthoi tab-hide + redirect) and ListenToElementMessage @0x004e9450 (Back/Next/Finish/Help/Exit/Random nav) verbatim, with free tab navigation over all six pages. Heritage, Profession, Skills, and Town pages bind to CC3's RuntimeCharacterCreationState commands; Appearance and Summary mount as content-inert placeholders for CC6b/CC5. Live-DAT probing (CharacterCreationLiveDatTests) found two widget- mapping surprises the decomp's DynamicCast hints don't predict: the Profession slider's value field imports as an editable UiField (wired for direct numeric entry), and the avail/health/stamina/mana/credits displays author as UIElement_Button hosts whose Type-12 value child is swallowed by UiButton.ConsumesDatChildren — substituted with the button's own Label. No new DatWidgetFactory widget types were needed. Threads the installed DAT's real ChargenOptions into Runtime via the new RuntimeCharacterCreationState.InstallOptions, called from ContentEffectsAudioCompositionPhase.Compose (mirrors InstallSpellMetadata's pattern); headless keeps ChargenOptions.Empty unchanged. Wires CC3's F14 status-hook gap (ApplyCharacterCreated/ ApplyCreationFailed) to SessionStatusWriter for both graphical and headless hosts, and adds the CharacterCreation view/command seam through CurrentGameRuntimeAdapter and DeferredGameRuntimeStateCommands alongside CharacterSelection's existing shape. Register: AD-101/102/103, AP-212/213, TS-82 filed for the auto-gender- select interim default, the omitted ToD-account gate, the button-Label widget substitution, the Random-button approximation, the flat-listbox Skills simplification, and the Appearance/Summary placeholders. Runtime 1713/0 (was 1707), App 5117/13 skips (was 5101/6), Headless 165/0 unaffected, full solution Release build green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
302 lines
8.7 KiB
C#
302 lines
8.7 KiB
C#
using AcDream.Core.Physics;
|
|
using AcDream.Runtime.World;
|
|
using AcDream.Runtime.Session;
|
|
|
|
namespace AcDream.Runtime;
|
|
|
|
public readonly record struct RuntimeEntityIdentity(
|
|
uint ServerGuid,
|
|
uint LocalEntityId,
|
|
ushort Incarnation);
|
|
|
|
public readonly record struct RuntimeEntitySnapshot(
|
|
RuntimeEntityIdentity Identity,
|
|
uint CellId,
|
|
uint PhysicsState,
|
|
Position? Position);
|
|
|
|
public interface IRuntimeEntityVisitor
|
|
{
|
|
void Visit(in RuntimeEntitySnapshot entity);
|
|
}
|
|
|
|
public interface IRuntimeEntityView
|
|
{
|
|
int Count { get; }
|
|
|
|
int MaterializedCount { get; }
|
|
|
|
bool TryGet(uint serverGuid, out RuntimeEntitySnapshot entity);
|
|
|
|
/// <summary>
|
|
/// Visits the canonical owner synchronously without copying its mutable
|
|
/// collection. The visitor must not retain references or mutate runtime
|
|
/// ownership during the call.
|
|
/// </summary>
|
|
void Visit(IRuntimeEntityVisitor visitor);
|
|
}
|
|
|
|
public readonly record struct RuntimeInventoryItemSnapshot(
|
|
uint ObjectId,
|
|
ushort Incarnation,
|
|
string Name,
|
|
uint ContainerId,
|
|
int ContainerSlot,
|
|
uint WielderId,
|
|
uint EquipLocation,
|
|
int StackSize,
|
|
int Value);
|
|
|
|
public interface IRuntimeInventoryVisitor
|
|
{
|
|
void Visit(in RuntimeInventoryItemSnapshot item);
|
|
}
|
|
|
|
public interface IRuntimeInventoryView
|
|
{
|
|
int ObjectCount { get; }
|
|
|
|
int ContainerCount { get; }
|
|
|
|
bool TryGet(uint objectId, out RuntimeInventoryItemSnapshot item);
|
|
|
|
/// <inheritdoc cref="IRuntimeEntityView.Visit"/>
|
|
void Visit(IRuntimeInventoryVisitor visitor);
|
|
}
|
|
|
|
public interface IRuntimeChatView
|
|
{
|
|
long Revision { get; }
|
|
|
|
int Count { get; }
|
|
}
|
|
|
|
public readonly record struct RuntimeMovementSnapshot(
|
|
bool HasController,
|
|
uint LocalEntityId,
|
|
Position Position,
|
|
System.Numerics.Vector3 Velocity,
|
|
bool IsAirborne,
|
|
double SimulationTimeSeconds,
|
|
long Revision = 0,
|
|
bool AutoRunActive = false,
|
|
bool HasCommandInput = false,
|
|
Gameplay.MovementInput CommandInput = default);
|
|
|
|
public interface IRuntimeMovementView
|
|
{
|
|
RuntimeMovementSnapshot Snapshot { get; }
|
|
}
|
|
|
|
public enum RuntimePortalKind
|
|
{
|
|
None,
|
|
Login,
|
|
Portal,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Immutable, presentation-independent destination from one canonical
|
|
/// accepted local-player Position packet. The frame remains cell-local;
|
|
/// graphical hosts may translate it into their current render origin, while
|
|
/// headless hosts consume the same full retail cell identity directly.
|
|
/// </summary>
|
|
public readonly record struct RuntimeTeleportDestination(
|
|
uint EntityGuid,
|
|
ushort InstanceSequence,
|
|
ushort PositionSequence,
|
|
ushort TeleportSequence,
|
|
ushort ForcePositionSequence,
|
|
Position Position)
|
|
{
|
|
public uint CellId => Position.ObjCellId;
|
|
}
|
|
|
|
public readonly record struct RuntimeDestinationReadiness(
|
|
long Generation,
|
|
uint DestinationCell,
|
|
bool IsIndoor,
|
|
bool IsUnhydratable,
|
|
int RequiredRenderRadius,
|
|
bool IsRenderNeighborhoodReady,
|
|
bool AreCompositeTexturesReady,
|
|
bool IsCollisionReady)
|
|
{
|
|
public bool HasDestination => DestinationCell != 0u;
|
|
|
|
public bool IsReady => HasDestination
|
|
&& (IsUnhydratable
|
|
|| (IsRenderNeighborhoodReady
|
|
&& AreCompositeTexturesReady
|
|
&& IsCollisionReady));
|
|
}
|
|
|
|
[Flags]
|
|
public enum RuntimeWorldHostAcknowledgementStage
|
|
{
|
|
None = 0,
|
|
ProjectionRegistered = 1 << 0,
|
|
SimulationReleaseProjected = 1 << 1,
|
|
DestinationReservationReleased = 1 << 2,
|
|
TerminalProjected = 1 << 3,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Exact identity of one graphical or direct host projection attached to a
|
|
/// Runtime reveal generation. It contains no presentation object.
|
|
/// </summary>
|
|
public readonly record struct RuntimeWorldHostProjectionToken(
|
|
long Generation,
|
|
uint DestinationCell)
|
|
{
|
|
public bool IsValid => Generation != 0 && DestinationCell != 0u;
|
|
}
|
|
|
|
public readonly record struct RuntimeWorldHostAcknowledgement(
|
|
RuntimeWorldHostProjectionToken Projection,
|
|
RuntimeWorldHostAcknowledgementStage Stage);
|
|
|
|
public readonly record struct RuntimeWorldHostProjectionSnapshot(
|
|
RuntimeWorldHostProjectionToken Token,
|
|
RuntimeWorldHostAcknowledgementStage PendingAcknowledgements,
|
|
bool ProjectionRegistered,
|
|
bool SimulationReleaseProjected,
|
|
bool DestinationReservationReleased,
|
|
bool IsSuperseding)
|
|
{
|
|
public int PendingAcknowledgementCount =>
|
|
Count(PendingAcknowledgements);
|
|
|
|
private static int Count(RuntimeWorldHostAcknowledgementStage stages)
|
|
{
|
|
int value = (int)stages;
|
|
int count = 0;
|
|
while (value != 0)
|
|
{
|
|
value &= value - 1;
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
}
|
|
|
|
public readonly record struct RuntimePortalSnapshot(
|
|
long Generation,
|
|
RuntimePortalKind Kind,
|
|
RuntimeDestinationReadiness Readiness,
|
|
bool Materialized,
|
|
bool Completed,
|
|
bool Cancelled,
|
|
bool WorldViewportObserved,
|
|
bool WorldSimulationAvailable,
|
|
int InvariantFailureCount,
|
|
bool WaitCueShown,
|
|
int PortalMaterializationCount)
|
|
{
|
|
public static RuntimePortalSnapshot Idle { get; } = new(
|
|
Generation: 0,
|
|
RuntimePortalKind.None,
|
|
Readiness: default,
|
|
Materialized: false,
|
|
Completed: false,
|
|
Cancelled: false,
|
|
WorldViewportObserved: false,
|
|
WorldSimulationAvailable: true,
|
|
InvariantFailureCount: 0,
|
|
WaitCueShown: false,
|
|
PortalMaterializationCount: 0);
|
|
|
|
public uint DestinationCell => Readiness.DestinationCell;
|
|
public bool IsReady => Readiness.IsReady;
|
|
public bool IsMaterialized => Materialized;
|
|
public bool IsCompleted => Completed;
|
|
public bool IsCancelled => Cancelled;
|
|
public bool IsWorldVisible => WorldViewportObserved;
|
|
public bool IsActive => Generation != 0 && !Cancelled;
|
|
}
|
|
|
|
public interface IRuntimePortalView
|
|
{
|
|
RuntimePortalSnapshot Snapshot { get; }
|
|
RuntimeWorldTransitOwnershipSnapshot Ownership { get; }
|
|
}
|
|
|
|
public readonly record struct RuntimeStateCheckpoint(
|
|
RuntimeGenerationToken Generation,
|
|
RuntimeLifecycleState Lifecycle,
|
|
ulong FrameNumber,
|
|
int EntityCount,
|
|
int MaterializedEntityCount,
|
|
int InventoryObjectCount,
|
|
int InventoryContainerCount,
|
|
RuntimeInventoryStateSnapshot InventoryState,
|
|
RuntimeCharacterSnapshot Character,
|
|
RuntimeSocialSnapshot Social,
|
|
long ChatRevision,
|
|
int ChatCount,
|
|
RuntimeActionSnapshot Actions,
|
|
RuntimeMovementSnapshot Movement,
|
|
RuntimeWorldEnvironmentSnapshot Environment,
|
|
RuntimeWorldEnvironmentOwnershipSnapshot EnvironmentOwnership,
|
|
RuntimePortalSnapshot Portal,
|
|
RuntimeWorldTransitOwnershipSnapshot TransitOwnership,
|
|
// Campaign FA slice FA2 (2026-08-12): originally trailing-optional
|
|
// (`= default`) so every existing positional construction site (tests)
|
|
// compiled unchanged. FA2 fix-round SHOULD-FIX 6 (blast review) made
|
|
// both parameters REQUIRED instead: `default(RuntimeFellowshipSnapshot)`/
|
|
// `default(RuntimeAllegianceSnapshot)` zero-init every field —
|
|
// including Name/AllegianceName to null, not string.Empty — and C#
|
|
// does not allow a non-constant expression (a real `new(...)` call) as
|
|
// an optional-parameter default, so there is no way to give a
|
|
// TRAILING-OPTIONAL parameter here a non-null default. Both snapshot
|
|
// types still declare an explicit parameterless constructor
|
|
// (`RuntimeFellowshipSnapshot()`/`RuntimeAllegianceSnapshot()`,
|
|
// GameRuntimeGameplayViews.cs) so callers that want an empty-but-safe
|
|
// snapshot can pass `new()` explicitly.
|
|
RuntimeFellowshipSnapshot Fellowship,
|
|
RuntimeAllegianceSnapshot Allegiance);
|
|
|
|
public interface IGameRuntimeView
|
|
{
|
|
RuntimeGenerationToken Generation { get; }
|
|
|
|
RuntimeLifecycleSnapshot Lifecycle { get; }
|
|
|
|
IGameRuntimeClock Clock { get; }
|
|
|
|
IRuntimeEntityView Entities { get; }
|
|
|
|
IRuntimeInventoryView Inventory { get; }
|
|
|
|
IRuntimeInventoryStateView InventoryState { get; }
|
|
|
|
IRuntimeCharacterView Character { get; }
|
|
|
|
IRuntimeSocialView Social { get; }
|
|
|
|
IRuntimeChatView Chat { get; }
|
|
|
|
IRuntimeCharacterSelectionView CharacterSelection =>
|
|
throw new NotSupportedException(
|
|
"This runtime view does not project character selection.");
|
|
|
|
/// <summary>Campaign CC slice CC4: mirrors <see cref="CharacterSelection"/>'s
|
|
/// default-throw shape.</summary>
|
|
IRuntimeCharacterCreationView CharacterCreation =>
|
|
throw new NotSupportedException(
|
|
"This runtime view does not project character creation.");
|
|
|
|
IRuntimeFellowshipView Fellowship { get; }
|
|
|
|
IRuntimeAllegianceView Allegiance { get; }
|
|
|
|
IRuntimeActionView Actions { get; }
|
|
|
|
IRuntimeMovementView Movement { get; }
|
|
|
|
IRuntimeWorldEnvironmentView Environment { get; }
|
|
|
|
IRuntimePortalView Portal { get; }
|
|
|
|
RuntimeStateCheckpoint CaptureCheckpoint();
|
|
}
|