refactor(runtime): expose canonical gameplay state
Move character options and movement skills into the Runtime-owned character graph, expose borrowed inventory, character, and social views, and route retained UI state commands through generation-gated typed Runtime contracts. Preserve the existing synchronous wire path while deleting the App-owned option and skill mirrors and extending normalized parity checkpoints. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
9d0d9b07e0
commit
dcb61efb5a
34 changed files with 2076 additions and 103 deletions
|
|
@ -18,6 +18,7 @@ using AcDream.Core.Items;
|
|||
using AcDream.Core.Player;
|
||||
using AcDream.Core.Selection;
|
||||
using AcDream.Core.Spells;
|
||||
using AcDream.Runtime;
|
||||
using AcDream.Runtime.Gameplay;
|
||||
using AcDream.UI.Abstractions.Input;
|
||||
using AcDream.UI.Abstractions.Panels.Chat;
|
||||
|
|
@ -56,7 +57,6 @@ internal sealed record InteractionRetainedUiDependencies(
|
|||
ILocalPlayerIdentitySource PlayerIdentity,
|
||||
ILocalPlayerControllerSource PlayerController,
|
||||
ILocalPlayerModeSource PlayerMode,
|
||||
PlayerCharacterOptionsState CharacterOptions,
|
||||
Func<DeferredSelectionViewPlaneSource, SelectionCameraSource>
|
||||
SelectionCameraFactory,
|
||||
DeferredRenderFrameDiagnosticsSource FrameDiagnostics,
|
||||
|
|
@ -118,6 +118,7 @@ internal sealed class InteractionUiLateBindings : IDisposable
|
|||
private bool _deactivationStarted;
|
||||
|
||||
public DeferredLiveSessionUiAuthority Session { get; } = new();
|
||||
public DeferredGameRuntimeStateCommands GameRuntime { get; } = new();
|
||||
public DeferredSelectionUiAuthority Selection { get; } = new();
|
||||
public DeferredSelectionViewPlaneSource SelectionViewPlane { get; } = new();
|
||||
public DeferredRadarSnapshotSource Radar { get; } = new();
|
||||
|
|
@ -176,6 +177,7 @@ internal sealed class InteractionUiLateBindings : IDisposable
|
|||
Radar.Deactivate();
|
||||
SelectionViewPlane.Deactivate();
|
||||
Selection.Deactivate();
|
||||
GameRuntime.Deactivate();
|
||||
Session.Deactivate();
|
||||
InventoryContainer.Deactivate();
|
||||
for (int i = _lateOwnerBindings.Count - 1; i >= 0; i--)
|
||||
|
|
@ -294,7 +296,7 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
|||
sendGive: (target, item, amount) =>
|
||||
session.CurrentSession?.SendGiveObject(target, item, amount),
|
||||
dragOnPlayerOpensSecureTrade: () =>
|
||||
d.CharacterOptions.DragItemOnPlayerOpensSecureTrade,
|
||||
d.Character.Options.DragItemOnPlayerOpensSecureTrade,
|
||||
toast: d.Toast,
|
||||
readyForInventoryRequest: () => session.IsInWorld,
|
||||
playerOnGround: () =>
|
||||
|
|
@ -375,15 +377,27 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
|||
playerGuid: () => d.PlayerIdentity.ServerGuid,
|
||||
activeToonName: () => d.Settings.ActiveToonKey,
|
||||
fallbackSheet: Studio.SampleData.SampleCharacter,
|
||||
canSendRaise: () => late.Session.IsInWorld,
|
||||
canSendRaise: () => late.GameRuntime.IsInWorld,
|
||||
sendRaiseAttribute: (statId, cost) =>
|
||||
late.Session.CurrentSession?.SendRaiseAttribute(statId, cost),
|
||||
late.GameRuntime.Advance(
|
||||
RuntimeAdvancementKind.Attribute,
|
||||
statId,
|
||||
cost),
|
||||
sendRaiseVital: (statId, cost) =>
|
||||
late.Session.CurrentSession?.SendRaiseVital(statId, cost),
|
||||
late.GameRuntime.Advance(
|
||||
RuntimeAdvancementKind.Vital,
|
||||
statId,
|
||||
cost),
|
||||
sendRaiseSkill: (statId, cost) =>
|
||||
late.Session.CurrentSession?.SendRaiseSkill(statId, cost),
|
||||
late.GameRuntime.Advance(
|
||||
RuntimeAdvancementKind.Skill,
|
||||
statId,
|
||||
cost),
|
||||
sendTrainSkill: (statId, credits) =>
|
||||
late.Session.CurrentSession?.SendTrainSkill(statId, credits));
|
||||
late.GameRuntime.Advance(
|
||||
RuntimeAdvancementKind.TrainSkill,
|
||||
statId,
|
||||
credits));
|
||||
checkpoint(InteractionRetainedUiCompositionPoint.CharacterSheetCreated);
|
||||
|
||||
MagicRuntime magic = MagicRuntime.Create(
|
||||
|
|
@ -543,16 +557,13 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
|||
guid => d.Selection.Select(guid, SelectionChangeSource.Inventory),
|
||||
guid => late.Session.TryUseItem(guid, d.Log),
|
||||
(tab, position, spellId) =>
|
||||
late.Session.CurrentSession?.SendAddSpellFavorite(
|
||||
spellId,
|
||||
position,
|
||||
tab),
|
||||
late.GameRuntime.AddFavorite(tab, position, spellId),
|
||||
(tab, spellId) =>
|
||||
late.Session.CurrentSession?.SendRemoveSpellFavorite(spellId, tab),
|
||||
filters => late.Session.CurrentSession?.SendSpellbookFilter(filters),
|
||||
spellId => late.Session.CurrentSession?.SendRemoveSpell(spellId),
|
||||
late.GameRuntime.RemoveFavorite(tab, spellId),
|
||||
filters => late.GameRuntime.SetSpellbookFilter(filters),
|
||||
spellId => late.GameRuntime.ForgetSpell(spellId),
|
||||
(componentId, amount) =>
|
||||
late.Session.CurrentSession?.SendSetDesiredComponentLevel(
|
||||
late.GameRuntime.SetDesiredComponent(
|
||||
componentId,
|
||||
amount),
|
||||
d.ClientTime),
|
||||
|
|
@ -589,8 +600,8 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
|||
d.Inventory.ItemMana,
|
||||
d.CombatModeCommands.Toggle,
|
||||
itemInteraction,
|
||||
entry => late.Session.CurrentSession?.SendAddShortcut(entry),
|
||||
index => late.Session.CurrentSession?.SendRemoveShortcut(index),
|
||||
entry => late.GameRuntime.AddShortcut(entry),
|
||||
index => late.GameRuntime.RemoveShortcut(index),
|
||||
d.Selection,
|
||||
handler => d.Combat.HealthChanged += handler,
|
||||
handler => d.Combat.HealthChanged -= handler,
|
||||
|
|
|
|||
|
|
@ -9,11 +9,186 @@ using AcDream.App.UI.Testing;
|
|||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Net;
|
||||
using AcDream.Core.Net.Messages;
|
||||
using AcDream.Runtime;
|
||||
using AcDream.UI.Abstractions;
|
||||
using Silk.NET.Windowing;
|
||||
|
||||
namespace AcDream.App.Composition;
|
||||
|
||||
/// <summary>
|
||||
/// Early retained-UI projection over Slice J's later current-runtime seam.
|
||||
/// Every call captures the view and command owner together and supplies that
|
||||
/// exact generation, so a displaced session can never receive the command.
|
||||
/// </summary>
|
||||
internal sealed class DeferredGameRuntimeStateCommands
|
||||
{
|
||||
private readonly object _gate = new();
|
||||
private IGameRuntimeView? _view;
|
||||
private IGameRuntimeCommands? _commands;
|
||||
private bool _deactivated;
|
||||
|
||||
public bool IsInWorld
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_gate)
|
||||
return !_deactivated
|
||||
&& _view?.Lifecycle.State == RuntimeLifecycleState.InWorld;
|
||||
}
|
||||
}
|
||||
|
||||
public IDisposable Bind(
|
||||
IGameRuntimeView view,
|
||||
IGameRuntimeCommands commands)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(view);
|
||||
ArgumentNullException.ThrowIfNull(commands);
|
||||
lock (_gate)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_deactivated, this);
|
||||
if (_view is not null || _commands is not null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The retained-UI game-runtime command seam is already bound.");
|
||||
}
|
||||
_view = view;
|
||||
_commands = commands;
|
||||
}
|
||||
return new ExpectedRuntimeBinding(this, view, commands);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult AddShortcut(ShortcutEntry entry) =>
|
||||
Invoke((commands, generation) => commands.InventoryState.AddShortcut(
|
||||
generation,
|
||||
new RuntimeShortcutCommand(
|
||||
entry.Index,
|
||||
entry.ObjectId,
|
||||
entry.SpellId)));
|
||||
|
||||
public RuntimeCommandResult RemoveShortcut(uint index)
|
||||
{
|
||||
if (index > int.MaxValue)
|
||||
return CurrentResult(RuntimeCommandStatus.Rejected);
|
||||
return Invoke((commands, generation) =>
|
||||
commands.InventoryState.RemoveShortcut(
|
||||
generation,
|
||||
(int)index));
|
||||
}
|
||||
|
||||
public RuntimeCommandResult AddFavorite(
|
||||
int tab,
|
||||
int position,
|
||||
uint spellId) =>
|
||||
Invoke((commands, generation) => commands.Spellbook.AddFavorite(
|
||||
generation,
|
||||
tab,
|
||||
position,
|
||||
spellId));
|
||||
|
||||
public RuntimeCommandResult RemoveFavorite(int tab, uint spellId) =>
|
||||
Invoke((commands, generation) => commands.Spellbook.RemoveFavorite(
|
||||
generation,
|
||||
tab,
|
||||
spellId));
|
||||
|
||||
public RuntimeCommandResult SetSpellbookFilter(uint filters) =>
|
||||
Invoke((commands, generation) => commands.Spellbook.SetFilter(
|
||||
generation,
|
||||
filters));
|
||||
|
||||
public RuntimeCommandResult ForgetSpell(uint spellId) =>
|
||||
Invoke((commands, generation) => commands.Spellbook.ForgetSpell(
|
||||
generation,
|
||||
spellId));
|
||||
|
||||
public RuntimeCommandResult SetDesiredComponent(
|
||||
uint componentId,
|
||||
uint amount) =>
|
||||
Invoke((commands, generation) =>
|
||||
commands.Spellbook.SetDesiredComponent(
|
||||
generation,
|
||||
componentId,
|
||||
amount));
|
||||
|
||||
public RuntimeCommandResult Advance(
|
||||
RuntimeAdvancementKind kind,
|
||||
uint statId,
|
||||
ulong cost) =>
|
||||
Invoke((commands, generation) => commands.Character.Advance(
|
||||
generation,
|
||||
new RuntimeAdvancementCommand(kind, statId, cost)));
|
||||
|
||||
public void Deactivate()
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
_deactivated = true;
|
||||
_view = null;
|
||||
_commands = null;
|
||||
}
|
||||
}
|
||||
|
||||
private RuntimeCommandResult Invoke(
|
||||
Func<
|
||||
IGameRuntimeCommands,
|
||||
RuntimeGenerationToken,
|
||||
RuntimeCommandResult> invoke)
|
||||
{
|
||||
IGameRuntimeCommands commands;
|
||||
RuntimeGenerationToken generation;
|
||||
lock (_gate)
|
||||
{
|
||||
if (_deactivated || _view is null || _commands is null)
|
||||
{
|
||||
return new RuntimeCommandResult(
|
||||
RuntimeCommandStatus.Inactive,
|
||||
_view?.Generation ?? default);
|
||||
}
|
||||
commands = _commands;
|
||||
generation = _view.Generation;
|
||||
}
|
||||
return invoke(commands, generation);
|
||||
}
|
||||
|
||||
private RuntimeCommandResult CurrentResult(RuntimeCommandStatus status)
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
return new RuntimeCommandResult(
|
||||
status,
|
||||
_view?.Generation ?? default);
|
||||
}
|
||||
}
|
||||
|
||||
private void Release(
|
||||
IGameRuntimeView expectedView,
|
||||
IGameRuntimeCommands expectedCommands)
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
if (!ReferenceEquals(_view, expectedView)
|
||||
|| !ReferenceEquals(_commands, expectedCommands))
|
||||
{
|
||||
return;
|
||||
}
|
||||
_view = null;
|
||||
_commands = null;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ExpectedRuntimeBinding(
|
||||
DeferredGameRuntimeStateCommands owner,
|
||||
IGameRuntimeView view,
|
||||
IGameRuntimeCommands commands)
|
||||
: IDisposable
|
||||
{
|
||||
private DeferredGameRuntimeStateCommands? _owner = owner;
|
||||
|
||||
public void Dispose() =>
|
||||
Interlocked.Exchange(ref _owner, null)?.Release(view, commands);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Early UI view over the later live-session owner. A binding lease clears only
|
||||
/// the exact owner it installed, so rollback cannot withdraw a replacement.
|
||||
|
|
@ -394,19 +569,6 @@ internal sealed class DeferredInventoryContainerSource
|
|||
}
|
||||
}
|
||||
|
||||
internal sealed class PlayerCharacterOptionsState
|
||||
{
|
||||
public PlayerDescriptionParser.CharacterOptions1 Options { get; set; } =
|
||||
PlayerDescriptionParser.CharacterOptions1.Default;
|
||||
|
||||
public bool DragItemOnPlayerOpensSecureTrade =>
|
||||
(Options
|
||||
& PlayerDescriptionParser.CharacterOptions1
|
||||
.DragItemOnPlayerOpensSecureTrade) != 0;
|
||||
|
||||
public void Reset() => Options = PlayerDescriptionParser.CharacterOptions1.Default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Probe scripts are mounted in Phase 5; reveal/resource facts become valid in
|
||||
/// Phase 7. Calls remain inert and diagnostic until that exact owner binds.
|
||||
|
|
|
|||
|
|
@ -68,10 +68,8 @@ internal sealed record SessionPlayerDependencies(
|
|||
LiveWorldOriginState WorldOrigin,
|
||||
WorldRenderRangeState RenderRange,
|
||||
LocalPlayerShadowState PlayerShadow,
|
||||
LocalPlayerSkillState PlayerSkills,
|
||||
ViewportAspectState ViewportAspect,
|
||||
PlayerApproachCompletionState PlayerApproachCompletions,
|
||||
PlayerCharacterOptionsState CharacterOptions,
|
||||
RuntimeInventoryState Inventory,
|
||||
PointerPositionState PointerPosition,
|
||||
DispatcherMovementInputSource MovementInput,
|
||||
|
|
@ -710,7 +708,7 @@ internal sealed class SessionPlayerCompositionPhase
|
|||
gameplayInput,
|
||||
liveSessionSource,
|
||||
d.MovementDiagnostics,
|
||||
d.PlayerSkills,
|
||||
d.Character.MovementSkills,
|
||||
d.ViewportAspect);
|
||||
if (d.SettingsDevTools.DevTools is { } devTools)
|
||||
{
|
||||
|
|
@ -796,9 +794,7 @@ internal sealed class SessionPlayerCompositionPhase
|
|||
new LiveSessionPlayerRuntime(
|
||||
d.PlayerIdentity,
|
||||
d.PlayerController,
|
||||
d.PlayerSkills,
|
||||
d.WorldOrigin,
|
||||
d.CharacterOptions),
|
||||
d.WorldOrigin),
|
||||
new LiveSessionDomainRuntime(
|
||||
d.EntityObjects,
|
||||
d.Character,
|
||||
|
|
@ -876,6 +872,8 @@ internal sealed class SessionPlayerCompositionPhase
|
|||
d.PlayerIdentity,
|
||||
live.LiveEntities,
|
||||
d.EntityObjects,
|
||||
d.Inventory,
|
||||
d.Character,
|
||||
d.Communication,
|
||||
d.PlayerController,
|
||||
worldReveal,
|
||||
|
|
@ -885,6 +883,9 @@ internal sealed class SessionPlayerCompositionPhase
|
|||
gameplayInput,
|
||||
combatCommand);
|
||||
bindings.Adopt("current game runtime adapter", gameRuntime);
|
||||
bindings.Adopt(
|
||||
"retained-UI game runtime commands",
|
||||
interaction.LateBindings.GameRuntime.Bind(gameRuntime, gameRuntime));
|
||||
|
||||
var nearbyDiagnostics = new NearbyWorldDiagnosticDumper(
|
||||
new RuntimeNearbyWorldDiagnosticSource(
|
||||
|
|
|
|||
|
|
@ -140,38 +140,21 @@ internal sealed class LocalPlayerModeState : ILocalPlayerModeSource
|
|||
/// delivery and player-mode construction share this owner so rebuilding the
|
||||
/// local physics controller cannot fall back to stale defaults.
|
||||
/// </summary>
|
||||
internal sealed class LocalPlayerSkillState
|
||||
internal static class LocalPlayerSkillProjection
|
||||
{
|
||||
public int RunSkill { get; private set; } = -1;
|
||||
public int JumpSkill { get; private set; } = -1;
|
||||
public bool IsComplete => RunSkill >= 0 && JumpSkill >= 0;
|
||||
|
||||
public void Update(
|
||||
int runSkill,
|
||||
int jumpSkill,
|
||||
public static bool ApplyTo(
|
||||
AcDream.Runtime.Gameplay.RuntimeMovementSkillState skills,
|
||||
PlayerMovementController? controller)
|
||||
{
|
||||
if (runSkill >= 0)
|
||||
RunSkill = runSkill;
|
||||
if (jumpSkill >= 0)
|
||||
JumpSkill = jumpSkill;
|
||||
ApplyTo(controller);
|
||||
}
|
||||
|
||||
public bool ApplyTo(PlayerMovementController? controller)
|
||||
{
|
||||
if (controller is null || !IsComplete)
|
||||
ArgumentNullException.ThrowIfNull(skills);
|
||||
AcDream.Runtime.Gameplay.RuntimeMovementSkillSnapshot snapshot =
|
||||
skills.Snapshot;
|
||||
if (controller is null || !snapshot.IsComplete)
|
||||
return false;
|
||||
|
||||
controller.SetCharacterSkills(RunSkill, JumpSkill);
|
||||
controller.SetCharacterSkills(snapshot.RunSkill, snapshot.JumpSkill);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ResetSession()
|
||||
{
|
||||
RunSkill = -1;
|
||||
JumpSkill = -1;
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IViewportAspectSource
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using AcDream.Content;
|
|||
using AcDream.Core.Physics;
|
||||
using AcDream.Core.Physics.Motion;
|
||||
using AcDream.Core.World;
|
||||
using AcDream.Runtime.Gameplay;
|
||||
|
||||
namespace AcDream.App.Input;
|
||||
|
||||
|
|
@ -41,7 +42,7 @@ internal sealed class PlayerModeController :
|
|||
private readonly ILocalPlayerTeleportInputLifetime _input;
|
||||
private readonly ILiveInWorldSource _session;
|
||||
private readonly MovementTruthDiagnosticController _movementDiagnostics;
|
||||
private readonly LocalPlayerSkillState _skills;
|
||||
private readonly RuntimeMovementSkillState _skills;
|
||||
private readonly IViewportAspectSource _viewport;
|
||||
private PlayerModeAutoEntry? _autoEntry;
|
||||
private IPlayerApproachCompletionSink? _approachLifetime;
|
||||
|
|
@ -67,7 +68,7 @@ internal sealed class PlayerModeController :
|
|||
ILocalPlayerTeleportInputLifetime input,
|
||||
ILiveInWorldSource session,
|
||||
MovementTruthDiagnosticController movementDiagnostics,
|
||||
LocalPlayerSkillState skills,
|
||||
RuntimeMovementSkillState skills,
|
||||
IViewportAspectSource viewport)
|
||||
{
|
||||
_mode = mode ?? throw new ArgumentNullException(nameof(mode));
|
||||
|
|
@ -359,7 +360,7 @@ internal sealed class PlayerModeController :
|
|||
exactMovement.CancelMoveTo(WeenieError.ActionCancelled);
|
||||
};
|
||||
|
||||
if (_skills.ApplyTo(controller))
|
||||
if (LocalPlayerSkillProjection.ApplyTo(_skills, controller))
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"live: {loggingTag} — applied server skills "
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using AcDream.App.UI;
|
||||
using AcDream.Core.Chat;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Net.Messages;
|
||||
using AcDream.Runtime.Session;
|
||||
using AcDream.UI.Abstractions;
|
||||
|
|
@ -15,8 +16,64 @@ internal sealed record LiveSessionCommandBindings(
|
|||
Action<string, string> SendTell,
|
||||
Action<uint, string> SendChannel,
|
||||
Action<uint, uint, uint, uint, string, uint> SendTurbineChat,
|
||||
Action<ShortcutEntry> AddShortcut,
|
||||
Action<uint> RemoveShortcut,
|
||||
Action<uint, int, int> AddFavorite,
|
||||
Action<uint, int> RemoveFavorite,
|
||||
Action<uint> SetSpellbookFilter,
|
||||
Action<uint> ForgetSpell,
|
||||
Action<uint, uint> SetDesiredComponent,
|
||||
Action ClearDesiredComponents,
|
||||
Action<uint, ulong> RaiseAttribute,
|
||||
Action<uint, ulong> RaiseVital,
|
||||
Action<uint, ulong> RaiseSkill,
|
||||
Action<uint, uint> TrainSkill,
|
||||
Action<uint> SetCharacterOptions,
|
||||
Action<string> AddFriend,
|
||||
Action<uint> RemoveFriend,
|
||||
Action ClearFriends,
|
||||
Action RequestLegacyFriends,
|
||||
Action<bool, uint, string, uint> ModifyCharacterSquelch,
|
||||
Action<bool, string> ModifyAccountSquelch,
|
||||
Action<bool, uint> ModifyGlobalSquelch,
|
||||
Action<string>? Log = null);
|
||||
|
||||
internal readonly record struct AddShortcutRuntimeCmd(ShortcutEntry Entry);
|
||||
internal readonly record struct RemoveShortcutRuntimeCmd(uint Index);
|
||||
internal readonly record struct AddFavoriteRuntimeCmd(
|
||||
uint SpellId,
|
||||
int Position,
|
||||
int TabIndex);
|
||||
internal readonly record struct RemoveFavoriteRuntimeCmd(
|
||||
uint SpellId,
|
||||
int TabIndex);
|
||||
internal readonly record struct SetSpellbookFilterRuntimeCmd(uint Filters);
|
||||
internal readonly record struct ForgetSpellRuntimeCmd(uint SpellId);
|
||||
internal readonly record struct SetDesiredComponentRuntimeCmd(
|
||||
uint ComponentId,
|
||||
uint Amount);
|
||||
internal readonly record struct ClearDesiredComponentsRuntimeCmd;
|
||||
internal readonly record struct RaiseAttributeRuntimeCmd(uint StatId, ulong Cost);
|
||||
internal readonly record struct RaiseVitalRuntimeCmd(uint StatId, ulong Cost);
|
||||
internal readonly record struct RaiseSkillRuntimeCmd(uint StatId, ulong Cost);
|
||||
internal readonly record struct TrainSkillRuntimeCmd(uint StatId, uint Cost);
|
||||
internal readonly record struct SetCharacterOptionsRuntimeCmd(uint Options);
|
||||
internal readonly record struct AddFriendRuntimeCmd(string Name);
|
||||
internal readonly record struct RemoveFriendRuntimeCmd(uint CharacterId);
|
||||
internal readonly record struct ClearFriendsRuntimeCmd;
|
||||
internal readonly record struct RequestLegacyFriendsRuntimeCmd;
|
||||
internal readonly record struct ModifyCharacterSquelchRuntimeCmd(
|
||||
bool Add,
|
||||
uint CharacterId,
|
||||
string Name,
|
||||
uint MessageType);
|
||||
internal readonly record struct ModifyAccountSquelchRuntimeCmd(
|
||||
bool Add,
|
||||
string Name);
|
||||
internal readonly record struct ModifyGlobalSquelchRuntimeCmd(
|
||||
bool Add,
|
||||
uint MessageType);
|
||||
|
||||
/// <summary>
|
||||
/// Owns the command surface for one exact live-session generation. The router
|
||||
/// itself is the published bus, so a retained reference becomes inert before
|
||||
|
|
@ -52,6 +109,68 @@ internal sealed class LiveSessionCommandRouter : ILiveSessionCommandRouting
|
|||
SendIfActive(() => bindings.SendTalk(command.Text));
|
||||
});
|
||||
commands.Register<SendChatCmd>(command => RouteChat(bindings, command));
|
||||
commands.Register<AddShortcutRuntimeCmd>(
|
||||
command => SendIfActive(() => bindings.AddShortcut(command.Entry)));
|
||||
commands.Register<RemoveShortcutRuntimeCmd>(
|
||||
command => SendIfActive(() => bindings.RemoveShortcut(command.Index)));
|
||||
commands.Register<AddFavoriteRuntimeCmd>(
|
||||
command => SendIfActive(() => bindings.AddFavorite(
|
||||
command.SpellId,
|
||||
command.Position,
|
||||
command.TabIndex)));
|
||||
commands.Register<RemoveFavoriteRuntimeCmd>(
|
||||
command => SendIfActive(() => bindings.RemoveFavorite(
|
||||
command.SpellId,
|
||||
command.TabIndex)));
|
||||
commands.Register<SetSpellbookFilterRuntimeCmd>(
|
||||
command => SendIfActive(() =>
|
||||
bindings.SetSpellbookFilter(command.Filters)));
|
||||
commands.Register<ForgetSpellRuntimeCmd>(
|
||||
command => SendIfActive(() => bindings.ForgetSpell(command.SpellId)));
|
||||
commands.Register<SetDesiredComponentRuntimeCmd>(
|
||||
command => SendIfActive(() => bindings.SetDesiredComponent(
|
||||
command.ComponentId,
|
||||
command.Amount)));
|
||||
commands.Register<ClearDesiredComponentsRuntimeCmd>(
|
||||
_ => SendIfActive(bindings.ClearDesiredComponents));
|
||||
commands.Register<RaiseAttributeRuntimeCmd>(
|
||||
command => SendIfActive(() =>
|
||||
bindings.RaiseAttribute(command.StatId, command.Cost)));
|
||||
commands.Register<RaiseVitalRuntimeCmd>(
|
||||
command => SendIfActive(() =>
|
||||
bindings.RaiseVital(command.StatId, command.Cost)));
|
||||
commands.Register<RaiseSkillRuntimeCmd>(
|
||||
command => SendIfActive(() =>
|
||||
bindings.RaiseSkill(command.StatId, command.Cost)));
|
||||
commands.Register<TrainSkillRuntimeCmd>(
|
||||
command => SendIfActive(() =>
|
||||
bindings.TrainSkill(command.StatId, command.Cost)));
|
||||
commands.Register<SetCharacterOptionsRuntimeCmd>(
|
||||
command => SendIfActive(() =>
|
||||
bindings.SetCharacterOptions(command.Options)));
|
||||
commands.Register<AddFriendRuntimeCmd>(
|
||||
command => SendIfActive(() => bindings.AddFriend(command.Name)));
|
||||
commands.Register<RemoveFriendRuntimeCmd>(
|
||||
command => SendIfActive(() =>
|
||||
bindings.RemoveFriend(command.CharacterId)));
|
||||
commands.Register<ClearFriendsRuntimeCmd>(
|
||||
_ => SendIfActive(bindings.ClearFriends));
|
||||
commands.Register<RequestLegacyFriendsRuntimeCmd>(
|
||||
_ => SendIfActive(bindings.RequestLegacyFriends));
|
||||
commands.Register<ModifyCharacterSquelchRuntimeCmd>(
|
||||
command => SendIfActive(() => bindings.ModifyCharacterSquelch(
|
||||
command.Add,
|
||||
command.CharacterId,
|
||||
command.Name,
|
||||
command.MessageType)));
|
||||
commands.Register<ModifyAccountSquelchRuntimeCmd>(
|
||||
command => SendIfActive(() => bindings.ModifyAccountSquelch(
|
||||
command.Add,
|
||||
command.Name)));
|
||||
commands.Register<ModifyGlobalSquelchRuntimeCmd>(
|
||||
command => SendIfActive(() => bindings.ModifyGlobalSquelch(
|
||||
command.Add,
|
||||
command.MessageType)));
|
||||
_commands = commands;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,9 +36,7 @@ namespace AcDream.App.Net;
|
|||
internal sealed record LiveSessionPlayerRuntime(
|
||||
LocalPlayerIdentityState Identity,
|
||||
LocalPlayerControllerSlot Controller,
|
||||
LocalPlayerSkillState Skills,
|
||||
LiveWorldOriginState WorldOrigin,
|
||||
PlayerCharacterOptionsState CharacterOptions);
|
||||
LiveWorldOriginState WorldOrigin);
|
||||
|
||||
internal sealed record LiveSessionDomainRuntime(
|
||||
RuntimeEntityObjectLifetime EntityObjects,
|
||||
|
|
@ -215,8 +213,8 @@ internal sealed class LiveSessionRuntimeFactory
|
|||
_domain.Communication.ResetChatIdentity();
|
||||
EntityVanishProbe.PlayerGuid = 0u;
|
||||
_interaction.Settings.ResetActiveCharacterKey();
|
||||
_player.CharacterOptions.Reset();
|
||||
_player.Skills.ResetSession();
|
||||
_domain.Character.Options.ResetSession();
|
||||
_domain.Character.MovementSkills.ResetSession();
|
||||
_world.NetworkUpdates.ResetSessionState();
|
||||
_world.Hydration.ResetSessionState();
|
||||
_domain.Inventory.ResetPlayerSnapshots();
|
||||
|
|
@ -282,27 +280,22 @@ internal sealed class LiveSessionRuntimeFactory
|
|||
ResolveSkillFormulaBonus: skillCreditResolver.Resolve,
|
||||
OnSkillsUpdated: (runSkill, jumpSkill) =>
|
||||
{
|
||||
_player.Skills.Update(
|
||||
runSkill,
|
||||
jumpSkill,
|
||||
_player.Controller.Controller);
|
||||
if (_player.Skills.IsComplete
|
||||
&& _player.Controller.Controller is not null)
|
||||
if (LocalPlayerSkillProjection.ApplyTo(
|
||||
_domain.Character.MovementSkills,
|
||||
_player.Controller.Controller))
|
||||
{
|
||||
RuntimeMovementSkillSnapshot snapshot =
|
||||
_domain.Character.MovementSkills.Snapshot;
|
||||
_log(
|
||||
$"player: applied server skills " +
|
||||
$"run={_player.Skills.RunSkill} " +
|
||||
$"jump={_player.Skills.JumpSkill}");
|
||||
$"run={snapshot.RunSkill} " +
|
||||
$"jump={snapshot.JumpSkill}");
|
||||
}
|
||||
},
|
||||
OnConfirmationRequest: request =>
|
||||
_ui.RetailUi?.HandleConfirmationRequest(request),
|
||||
OnConfirmationDone: done =>
|
||||
_ui.RetailUi?.HandleConfirmationDone(done),
|
||||
OnCharacterOptions: (options1, _) =>
|
||||
_player.CharacterOptions.Options =
|
||||
(Core.Net.Messages.PlayerDescriptionParser.CharacterOptions1)
|
||||
options1,
|
||||
ClientTime: ClientTimerNow);
|
||||
}
|
||||
|
||||
|
|
@ -391,6 +384,26 @@ internal sealed class LiveSessionRuntimeFactory
|
|||
senderGuid,
|
||||
text,
|
||||
cookie),
|
||||
AddShortcut: session.SendAddShortcut,
|
||||
RemoveShortcut: session.SendRemoveShortcut,
|
||||
AddFavorite: session.SendAddSpellFavorite,
|
||||
RemoveFavorite: session.SendRemoveSpellFavorite,
|
||||
SetSpellbookFilter: session.SendSpellbookFilter,
|
||||
ForgetSpell: session.SendRemoveSpell,
|
||||
SetDesiredComponent: session.SendSetDesiredComponentLevel,
|
||||
ClearDesiredComponents: session.SendClearDesiredComponents,
|
||||
RaiseAttribute: session.SendRaiseAttribute,
|
||||
RaiseVital: session.SendRaiseVital,
|
||||
RaiseSkill: session.SendRaiseSkill,
|
||||
TrainSkill: session.SendTrainSkill,
|
||||
SetCharacterOptions: session.SendSetCharacterOptions,
|
||||
AddFriend: session.SendAddFriend,
|
||||
RemoveFriend: session.SendRemoveFriend,
|
||||
ClearFriends: session.SendClearFriends,
|
||||
RequestLegacyFriends: session.SendLegacyFriendsListRequest,
|
||||
ModifyCharacterSquelch: session.SendModifyCharacterSquelch,
|
||||
ModifyAccountSquelch: session.SendModifyAccountSquelch,
|
||||
ModifyGlobalSquelch: session.SendModifyGlobalSquelch,
|
||||
Log: _log);
|
||||
|
||||
private static double ClientTimerNow() =>
|
||||
|
|
|
|||
|
|
@ -440,12 +440,8 @@ public sealed class GameWindow :
|
|||
get => _localPlayerIdentity.ServerGuid;
|
||||
set => _localPlayerIdentity.ServerGuid = value;
|
||||
}
|
||||
// Retail Default_CharacterOption (acclient.h:3434). PlayerDescription
|
||||
// replaces this before any in-world drag can normally occur.
|
||||
private readonly PlayerCharacterOptionsState _characterOptions = new();
|
||||
private readonly AcDream.App.Physics.LocalPlayerShadowState _localPlayerShadow = new();
|
||||
|
||||
private readonly AcDream.App.Input.LocalPlayerSkillState _localPlayerSkills = new();
|
||||
private readonly AcDream.App.Input.ViewportAspectState _viewportAspect = new();
|
||||
private readonly FramebufferResizeController _framebufferResize;
|
||||
private AcDream.App.Input.PlayerModeController? _playerModeController;
|
||||
|
|
@ -1269,7 +1265,6 @@ public sealed class GameWindow :
|
|||
_localPlayerIdentity,
|
||||
_playerControllerSlot,
|
||||
_localPlayerMode,
|
||||
_characterOptions,
|
||||
viewPlane => new SelectionCameraSource(
|
||||
hostInputCamera.CameraController,
|
||||
_window!,
|
||||
|
|
@ -1387,10 +1382,8 @@ public sealed class GameWindow :
|
|||
_liveWorldOrigin,
|
||||
_renderRange,
|
||||
_localPlayerShadow,
|
||||
_localPlayerSkills,
|
||||
_viewportAspect,
|
||||
_playerApproachCompletions,
|
||||
_characterOptions,
|
||||
_runtimeInventory,
|
||||
_pointerPosition,
|
||||
_movementInput,
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ internal sealed class CurrentGameRuntimeAdapter
|
|||
LocalPlayerIdentityState playerIdentity,
|
||||
LiveEntityRuntime entities,
|
||||
RuntimeEntityObjectLifetime entityObjects,
|
||||
RuntimeInventoryState inventory,
|
||||
RuntimeCharacterState character,
|
||||
RuntimeCommunicationState communication,
|
||||
ILocalPlayerControllerSource playerController,
|
||||
WorldRevealCoordinator worldReveal,
|
||||
|
|
@ -51,6 +53,8 @@ internal sealed class CurrentGameRuntimeAdapter
|
|||
playerIdentity,
|
||||
entities,
|
||||
entityObjects,
|
||||
inventory,
|
||||
character,
|
||||
communication,
|
||||
playerController,
|
||||
worldReveal,
|
||||
|
|
@ -79,6 +83,9 @@ internal sealed class CurrentGameRuntimeAdapter
|
|||
public IGameRuntimeClock Clock => _view.Clock;
|
||||
public IRuntimeEntityView Entities => _view.Entities;
|
||||
public IRuntimeInventoryView Inventory => _view.Inventory;
|
||||
public IRuntimeInventoryStateView InventoryState => _view.InventoryState;
|
||||
public IRuntimeCharacterView Character => _view.Character;
|
||||
public IRuntimeSocialView Social => _view.Social;
|
||||
public IRuntimeChatView Chat => _view.Chat;
|
||||
public IRuntimeMovementView Movement => _view.Movement;
|
||||
public IRuntimePortalView Portal => _view.Portal;
|
||||
|
|
@ -92,6 +99,15 @@ internal sealed class CurrentGameRuntimeAdapter
|
|||
IRuntimeChatCommands IGameRuntimeCommands.Chat => _commands;
|
||||
public IRuntimePortalCommands PortalCommands => _commands;
|
||||
IRuntimePortalCommands IGameRuntimeCommands.Portal => _commands;
|
||||
public IRuntimeInventoryStateCommands InventoryCommands => _commands;
|
||||
IRuntimeInventoryStateCommands IGameRuntimeCommands.InventoryState =>
|
||||
_commands;
|
||||
public IRuntimeSpellbookCommands SpellbookCommands => _commands;
|
||||
IRuntimeSpellbookCommands IGameRuntimeCommands.Spellbook => _commands;
|
||||
public IRuntimeCharacterCommands CharacterCommands => _commands;
|
||||
IRuntimeCharacterCommands IGameRuntimeCommands.Character => _commands;
|
||||
public IRuntimeSocialCommands SocialCommands => _commands;
|
||||
IRuntimeSocialCommands IGameRuntimeCommands.Social => _commands;
|
||||
|
||||
public RuntimeStateCheckpoint CaptureCheckpoint() =>
|
||||
_view.CaptureCheckpoint();
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using AcDream.App.Input;
|
|||
using AcDream.App.Interaction;
|
||||
using AcDream.App.Net;
|
||||
using AcDream.Core.Selection;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Runtime;
|
||||
using AcDream.Runtime.Session;
|
||||
using AcDream.UI.Abstractions;
|
||||
|
|
@ -20,7 +21,11 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
IRuntimeCombatCommands,
|
||||
IRuntimeMovementCommands,
|
||||
IRuntimeChatCommands,
|
||||
IRuntimePortalCommands
|
||||
IRuntimePortalCommands,
|
||||
IRuntimeInventoryStateCommands,
|
||||
IRuntimeSpellbookCommands,
|
||||
IRuntimeCharacterCommands,
|
||||
IRuntimeSocialCommands
|
||||
{
|
||||
private readonly LiveSessionController _session;
|
||||
private readonly LiveSessionHost _sessionHost;
|
||||
|
|
@ -276,6 +281,340 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
return Result(RuntimeCommandStatus.Accepted);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult AddShortcut(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
in RuntimeShortcutCommand command)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (command.Index < 0
|
||||
|| (command.ObjectId == 0u && command.SpellId == 0u))
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.InventoryState,
|
||||
operation: 0,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
command.ObjectId);
|
||||
}
|
||||
|
||||
_commands.Publish(new AddShortcutRuntimeCmd(new ShortcutEntry(
|
||||
command.Index,
|
||||
command.ObjectId,
|
||||
command.SpellId)));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.InventoryState,
|
||||
operation: 0,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
command.ObjectId);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult RemoveShortcut(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
int index)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (index < 0)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.InventoryState,
|
||||
operation: 1,
|
||||
RuntimeCommandStatus.Rejected);
|
||||
}
|
||||
|
||||
_commands.Publish(new RemoveShortcutRuntimeCmd((uint)index));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.InventoryState,
|
||||
operation: 1,
|
||||
RuntimeCommandStatus.Accepted);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult AddFavorite(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
int tabIndex,
|
||||
int position,
|
||||
uint spellId)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if ((uint)tabIndex >= 8u
|
||||
|| position < 0
|
||||
|| spellId == 0u)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 0,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
spellId);
|
||||
}
|
||||
|
||||
_commands.Publish(new AddFavoriteRuntimeCmd(
|
||||
spellId,
|
||||
position,
|
||||
tabIndex));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 0,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
spellId);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult RemoveFavorite(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
int tabIndex,
|
||||
uint spellId)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if ((uint)tabIndex >= 8u || spellId == 0u)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 1,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
spellId);
|
||||
}
|
||||
|
||||
_commands.Publish(new RemoveFavoriteRuntimeCmd(spellId, tabIndex));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 1,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
spellId);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult SetFilter(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint filters)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
_commands.Publish(new SetSpellbookFilterRuntimeCmd(filters));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 2,
|
||||
RuntimeCommandStatus.Accepted);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult ForgetSpell(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint spellId)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (spellId == 0u)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 3,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
spellId);
|
||||
}
|
||||
|
||||
_commands.Publish(new ForgetSpellRuntimeCmd(spellId));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 3,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
spellId);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult SetDesiredComponent(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint componentId,
|
||||
uint amount)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (componentId == 0u)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 4,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
componentId);
|
||||
}
|
||||
|
||||
_commands.Publish(new SetDesiredComponentRuntimeCmd(
|
||||
componentId,
|
||||
amount));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 4,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
componentId);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult ClearDesiredComponents(
|
||||
RuntimeGenerationToken expectedGeneration)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
_commands.Publish(new ClearDesiredComponentsRuntimeCmd());
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 5,
|
||||
RuntimeCommandStatus.Accepted);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult Advance(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
in RuntimeAdvancementCommand command)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (command.StatId == 0u
|
||||
|| command.Cost == 0u)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Character,
|
||||
(int)command.Kind,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
command.StatId);
|
||||
}
|
||||
|
||||
switch (command.Kind)
|
||||
{
|
||||
case RuntimeAdvancementKind.Attribute:
|
||||
_commands.Publish(new RaiseAttributeRuntimeCmd(
|
||||
command.StatId,
|
||||
command.Cost));
|
||||
break;
|
||||
case RuntimeAdvancementKind.Vital:
|
||||
_commands.Publish(new RaiseVitalRuntimeCmd(
|
||||
command.StatId,
|
||||
command.Cost));
|
||||
break;
|
||||
case RuntimeAdvancementKind.Skill:
|
||||
_commands.Publish(new RaiseSkillRuntimeCmd(
|
||||
command.StatId,
|
||||
command.Cost));
|
||||
break;
|
||||
case RuntimeAdvancementKind.TrainSkill
|
||||
when command.Cost <= uint.MaxValue:
|
||||
_commands.Publish(new TrainSkillRuntimeCmd(
|
||||
command.StatId,
|
||||
(uint)command.Cost));
|
||||
break;
|
||||
default:
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Character,
|
||||
(int)command.Kind,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
command.StatId);
|
||||
}
|
||||
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Character,
|
||||
(int)command.Kind,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
command.StatId);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult SetOptions1(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint options)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
_commands.Publish(new SetCharacterOptionsRuntimeCmd(options));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Character,
|
||||
operation: 4,
|
||||
RuntimeCommandStatus.Accepted);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult Execute(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
in RuntimeFriendCommand command)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
RuntimeCommandStatus status = RuntimeCommandStatus.Accepted;
|
||||
switch (command.Kind)
|
||||
{
|
||||
case RuntimeFriendCommandKind.Add
|
||||
when !string.IsNullOrWhiteSpace(command.Name):
|
||||
_commands.Publish(new AddFriendRuntimeCmd(command.Name));
|
||||
break;
|
||||
case RuntimeFriendCommandKind.Remove
|
||||
when command.CharacterId != 0u:
|
||||
_commands.Publish(new RemoveFriendRuntimeCmd(
|
||||
command.CharacterId));
|
||||
break;
|
||||
case RuntimeFriendCommandKind.Clear:
|
||||
_commands.Publish(new ClearFriendsRuntimeCmd());
|
||||
break;
|
||||
case RuntimeFriendCommandKind.RequestLegacyList:
|
||||
_commands.Publish(new RequestLegacyFriendsRuntimeCmd());
|
||||
break;
|
||||
default:
|
||||
status = RuntimeCommandStatus.Rejected;
|
||||
break;
|
||||
}
|
||||
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Social,
|
||||
(int)command.Kind,
|
||||
status,
|
||||
command.CharacterId,
|
||||
command.Name);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult Execute(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
in RuntimeSquelchCommand command)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
RuntimeCommandStatus status = RuntimeCommandStatus.Accepted;
|
||||
switch (command.Scope)
|
||||
{
|
||||
case RuntimeSquelchScope.Character
|
||||
when command.CharacterId != 0u
|
||||
&& !string.IsNullOrWhiteSpace(command.Name):
|
||||
_commands.Publish(new ModifyCharacterSquelchRuntimeCmd(
|
||||
command.Add,
|
||||
command.CharacterId,
|
||||
command.Name,
|
||||
command.MessageType));
|
||||
break;
|
||||
case RuntimeSquelchScope.Account
|
||||
when !string.IsNullOrWhiteSpace(command.Name):
|
||||
_commands.Publish(new ModifyAccountSquelchRuntimeCmd(
|
||||
command.Add,
|
||||
command.Name));
|
||||
break;
|
||||
case RuntimeSquelchScope.Global:
|
||||
_commands.Publish(new ModifyGlobalSquelchRuntimeCmd(
|
||||
command.Add,
|
||||
command.MessageType));
|
||||
break;
|
||||
default:
|
||||
status = RuntimeCommandStatus.Rejected;
|
||||
break;
|
||||
}
|
||||
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Social,
|
||||
operation: 4 + (int)command.Scope,
|
||||
status,
|
||||
command.CharacterId,
|
||||
command.Name);
|
||||
}
|
||||
|
||||
private RuntimeCommandStatus Validate(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
bool requireWorld)
|
||||
|
|
@ -294,6 +633,17 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
uint objectId = 0u) =>
|
||||
new(status, _view.Generation, objectId);
|
||||
|
||||
private RuntimeCommandResult EmitResult(
|
||||
RuntimeCommandDomain domain,
|
||||
int operation,
|
||||
RuntimeCommandStatus status,
|
||||
uint objectId = 0u,
|
||||
string? text = null)
|
||||
{
|
||||
_events.EmitCommand(domain, operation, status, objectId, text);
|
||||
return Result(status, objectId);
|
||||
}
|
||||
|
||||
private RuntimeSessionStartResult RejectedStart(RuntimeCommandStatus status) =>
|
||||
new(
|
||||
status == RuntimeCommandStatus.StaleGeneration
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ internal sealed class CurrentGameRuntimeViewAdapter : IGameRuntimeView
|
|||
private readonly IGameRuntimeClock _clock;
|
||||
private readonly IRuntimeEntityView _entityView;
|
||||
private readonly IRuntimeInventoryView _inventoryView;
|
||||
private readonly IRuntimeInventoryStateView _inventoryStateView;
|
||||
private readonly IRuntimeCharacterView _characterView;
|
||||
private readonly IRuntimeSocialView _socialView;
|
||||
private readonly IRuntimeChatView _chatView;
|
||||
private readonly MovementView _movementView;
|
||||
private readonly PortalView _portalView;
|
||||
|
|
@ -34,6 +37,8 @@ internal sealed class CurrentGameRuntimeViewAdapter : IGameRuntimeView
|
|||
LocalPlayerIdentityState playerIdentity,
|
||||
LiveEntityRuntime entities,
|
||||
RuntimeEntityObjectLifetime entityObjects,
|
||||
RuntimeInventoryState inventory,
|
||||
RuntimeCharacterState character,
|
||||
RuntimeCommunicationState communication,
|
||||
ILocalPlayerControllerSource playerController,
|
||||
WorldRevealCoordinator worldReveal,
|
||||
|
|
@ -50,7 +55,12 @@ internal sealed class CurrentGameRuntimeViewAdapter : IGameRuntimeView
|
|||
|
||||
_entityView = entityObjects.EntityView;
|
||||
_inventoryView = entityObjects.InventoryView;
|
||||
_inventoryStateView = (
|
||||
inventory ?? throw new ArgumentNullException(nameof(inventory))).View;
|
||||
_characterView = (
|
||||
character ?? throw new ArgumentNullException(nameof(character))).View;
|
||||
_chatView = communication.View;
|
||||
_socialView = communication.SocialView;
|
||||
_movementView = new MovementView(
|
||||
playerController
|
||||
?? throw new ArgumentNullException(nameof(playerController)),
|
||||
|
|
@ -90,6 +100,9 @@ internal sealed class CurrentGameRuntimeViewAdapter : IGameRuntimeView
|
|||
public IGameRuntimeClock Clock => _clock;
|
||||
public IRuntimeEntityView Entities => _entityView;
|
||||
public IRuntimeInventoryView Inventory => _inventoryView;
|
||||
public IRuntimeInventoryStateView InventoryState => _inventoryStateView;
|
||||
public IRuntimeCharacterView Character => _characterView;
|
||||
public IRuntimeSocialView Social => _socialView;
|
||||
public IRuntimeChatView Chat => _chatView;
|
||||
public IRuntimeMovementView Movement => _movementView;
|
||||
public IRuntimePortalView Portal => _portalView;
|
||||
|
|
@ -103,6 +116,9 @@ internal sealed class CurrentGameRuntimeViewAdapter : IGameRuntimeView
|
|||
_entities.MaterializedCount,
|
||||
_objects.ObjectCount,
|
||||
_objects.ContainerCount,
|
||||
_inventoryStateView.Snapshot,
|
||||
_characterView.Snapshot,
|
||||
_socialView.Snapshot,
|
||||
_chatView.Revision,
|
||||
_chatView.Count,
|
||||
_movementView.Snapshot,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue