Add StorageInteractionTest (#28541)

This commit is contained in:
Leon Friedrich
2024-06-04 09:05:51 +12:00
committed by GitHub
parent 98ab251f92
commit 5e51a1d73c
18 changed files with 427 additions and 190 deletions

View File

@@ -33,7 +33,7 @@ public abstract partial class InteractionTest
public int Quantity;
/// <summary>
/// If true, a check has been performed to see if the prototype ia an entity prototype with a stack component,
/// If true, a check has been performed to see if the prototype is an entity prototype with a stack component,
/// in which case the specifier was converted into a stack-specifier
/// </summary>
public bool Converted;
@@ -100,7 +100,7 @@ public abstract partial class InteractionTest
if (!ProtoMan.TryIndex<EntityPrototype>(spec.Prototype, out var entProto))
{
Assert.Fail($"Unkown prototype: {spec.Prototype}");
Assert.Fail($"Unknown prototype: {spec.Prototype}");
return default;
}
@@ -120,7 +120,7 @@ public abstract partial class InteractionTest
/// <summary>
/// Convert an entity-uid to a matching entity specifier. Useful when doing entity lookups & checking that the
/// right quantity of entities/materials werre produced. Returns null if passed an entity with a null prototype.
/// right quantity of entities/materials were produced. Returns null if passed an entity with a null prototype.
/// </summary>
protected EntitySpecifier? ToEntitySpecifier(EntityUid uid)
{

View File

@@ -14,6 +14,7 @@ using Content.Shared.Construction.Prototypes;
using Content.Shared.Gravity;
using Content.Shared.Item;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.GameObjects;
using Robust.Shared.Input;
@@ -44,8 +45,9 @@ public abstract partial class InteractionTest
return;
var comp = CEntMan.GetComponent<ConstructionGhostComponent>(clientTarget!.Value);
ClientTarget = clientTarget;
ConstructionGhostId = comp.Owner.Id;
Target = CEntMan.GetNetEntity(clientTarget.Value);
Assert.That(Target.Value.IsClientSide());
ConstructionGhostId = clientTarget.Value.GetHashCode();
});
await RunTicks(1);
@@ -129,21 +131,20 @@ public abstract partial class InteractionTest
/// <summary>
/// Place an entity prototype into the players hand. Deletes any currently held entity.
/// </summary>
/// <remarks>
/// Automatically enables welders.
/// </remarks>
protected async Task<NetEntity> PlaceInHands(string id, int quantity = 1, bool enableWelder = true)
/// <param name="id">The entity or stack prototype to spawn and place into the users hand</param>
/// <param name="quantity">The number of entities to spawn. If the prototype is a stack, this sets the stack count.</param>
/// <param name="enableToggleable">Whether or not to automatically enable any toggleable items</param>
protected async Task<NetEntity> PlaceInHands(string id, int quantity = 1, bool enableToggleable = true)
{
return await PlaceInHands((id, quantity), enableWelder);
return await PlaceInHands((id, quantity), enableToggleable);
}
/// <summary>
/// Place an entity prototype into the players hand. Deletes any currently held entity.
/// </summary>
/// <remarks>
/// Automatically enables welders.
/// </remarks>
protected async Task<NetEntity> PlaceInHands(EntitySpecifier entity, bool enableWelder = true)
/// <param name="entity">The entity type & quantity to spawn and place into the users hand</param>
/// <param name="enableToggleable">Whether or not to automatically enable any toggleable items</param>
protected async Task<NetEntity> PlaceInHands(EntitySpecifier entity, bool enableToggleable = true)
{
if (Hands.ActiveHand == null)
{
@@ -165,7 +166,7 @@ public abstract partial class InteractionTest
Assert.That(HandSys.TryPickup(playerEnt, item, Hands.ActiveHand, false, false, Hands));
// turn on welders
if (enableWelder && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated)
if (enableToggleable && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated)
{
Assert.That(ItemToggleSys.TryActivate(item, playerEnt, itemToggle: itemToggle));
}
@@ -173,7 +174,7 @@ public abstract partial class InteractionTest
await RunTicks(1);
Assert.That(Hands.ActiveHandEntity, Is.EqualTo(item));
if (enableWelder && itemToggle != null)
if (enableToggleable && itemToggle != null)
Assert.That(itemToggle.Activated);
return SEntMan.GetNetEntity(item);
@@ -254,21 +255,20 @@ public abstract partial class InteractionTest
/// <summary>
/// Place an entity prototype into the players hand and interact with the given entity (or target position)
/// </summary>
/// <remarks>
/// Empty strings imply empty hands.
/// </remarks>
protected async Task Interact(string id, int quantity = 1, bool shouldSucceed = true, bool awaitDoAfters = true)
/// <param name="id">The entity or stack prototype to spawn and place into the users hand</param>
/// <param name="quantity">The number of entities to spawn. If the prototype is a stack, this sets the stack count.</param>
/// <param name="awaitDoAfters">Whether or not to wait for any do-afters to complete</param>
protected async Task InteractUsing(string id, int quantity = 1, bool awaitDoAfters = true)
{
await Interact((id, quantity), shouldSucceed, awaitDoAfters);
await InteractUsing((id, quantity), awaitDoAfters);
}
/// <summary>
/// Place an entity prototype into the players hand and interact with the given entity (or target position)
/// Place an entity prototype into the players hand and interact with the given entity (or target position).
/// </summary>
/// <remarks>
/// Empty strings imply empty hands.
/// </remarks>
protected async Task Interact(EntitySpecifier entity, bool shouldSucceed = true, bool awaitDoAfters = true)
/// <param name="entity">The entity type & quantity to spawn and place into the users hand</param>
/// <param name="awaitDoAfters">Whether or not to wait for any do-afters to complete</param>
protected async Task InteractUsing(EntitySpecifier entity, bool awaitDoAfters = true)
{
// For every interaction, we will also examine the entity, just in case this breaks something, somehow.
// (e.g., servers attempt to assemble construction examine hints).
@@ -278,38 +278,80 @@ public abstract partial class InteractionTest
}
await PlaceInHands(entity);
await Interact(shouldSucceed, awaitDoAfters);
await Interact(awaitDoAfters);
}
/// <summary>
/// Interact with an entity using the currently held entity.
/// </summary>
protected async Task Interact(bool shouldSucceed = true, bool awaitDoAfters = true)
/// <param name="awaitDoAfters">Whether or not to wait for any do-afters to complete</param>
protected async Task Interact(bool awaitDoAfters = true)
{
var clientTarget = ClientTarget;
if ((clientTarget?.IsValid() != true || CEntMan.Deleted(clientTarget)) && (Target == null || Target.Value.IsValid()))
if (Target == null || !Target.Value.IsClientSide())
{
await Server.WaitPost(() => InteractSys.UserInteraction(SEntMan.GetEntity(Player), SEntMan.GetCoordinates(TargetCoords), SEntMan.GetEntity(Target)));
await RunTicks(1);
await Interact(Target, TargetCoords, awaitDoAfters);
return;
}
else
{
// The entity is client-side, so attempt to start construction
var clientEnt = ClientTarget ?? CEntMan.GetEntity(Target);
await Client.WaitPost(() => CConSys.TryStartConstruction(clientEnt!.Value));
await RunTicks(5);
}
// The target is a client-side entity, so we will just attempt to start construction under the assumption that
// it is a construction ghost.
await Client.WaitPost(() => CConSys.TryStartConstruction(CTarget!.Value));
await RunTicks(5);
if (awaitDoAfters)
await AwaitDoAfters(shouldSucceed);
await AwaitDoAfters();
await CheckTargetChange(shouldSucceed && awaitDoAfters);
await CheckTargetChange();
}
/// <inheritdoc cref="Interact(EntityUid?,EntityCoordinates,bool)"/>
protected async Task Interact(NetEntity? target, NetCoordinates coordinates, bool awaitDoAfters = true)
{
Assert.That(SEntMan.TryGetEntity(target, out var sTarget) || target == null);
var coords = SEntMan.GetCoordinates(coordinates);
Assert.That(coords.IsValid(SEntMan));
await Interact(sTarget, coords, awaitDoAfters);
}
/// <summary>
/// Variant of <see cref="InteractUsing"/> that performs several interactions using different entities.
/// Interact with an entity using the currently held entity.
/// </summary>
protected async Task Interact(EntityUid? target, EntityCoordinates coordinates, bool awaitDoAfters = true)
{
Assert.That(SEntMan.TryGetEntity(Player, out var player));
await Server.WaitPost(() => InteractSys.UserInteraction(player!.Value, coordinates, target));
await RunTicks(1);
if (awaitDoAfters)
await AwaitDoAfters();
await CheckTargetChange();
}
/// <summary>
/// Activate an entity.
/// </summary>
protected async Task Activate(NetEntity? target = null, bool awaitDoAfters = true)
{
target ??= Target;
Assert.That(target, Is.Not.Null);
Assert.That(SEntMan.TryGetEntity(target!.Value, out var sTarget));
Assert.That(SEntMan.TryGetEntity(Player, out var player));
await Server.WaitPost(() => InteractSys.InteractionActivate(player!.Value, sTarget!.Value));
await RunTicks(1);
if (awaitDoAfters)
await AwaitDoAfters();
await CheckTargetChange();
}
/// <summary>
/// Variant of <see cref="InteractUsing(string,int,bool)"/> that performs several interactions using different entities.
/// Useful for quickly finishing multiple construction steps.
/// </summary>
/// <remarks>
/// Empty strings imply empty hands.
@@ -318,7 +360,7 @@ public abstract partial class InteractionTest
{
foreach (var spec in specifiers)
{
await Interact(spec);
await InteractUsing(spec);
}
}
@@ -338,7 +380,7 @@ public abstract partial class InteractionTest
/// <summary>
/// Wait for any currently active DoAfters to finish.
/// </summary>
protected async Task AwaitDoAfters(bool shouldSucceed = true, int maxExpected = 1)
protected async Task AwaitDoAfters(int maxExpected = 1)
{
if (!ActiveDoAfters.Any())
return;
@@ -353,13 +395,12 @@ public abstract partial class InteractionTest
await RunTicks(10);
}
if (!shouldSucceed)
return;
foreach (var doAfter in doAfters)
{
Assert.That(!doAfter.Cancelled);
}
await RunTicks(5);
}
/// <summary>
@@ -398,39 +439,28 @@ public abstract partial class InteractionTest
/// Check if the test's target entity has changed. E.g., construction interactions will swap out entities while
/// a structure is being built.
/// </summary>
protected async Task CheckTargetChange(bool shouldSucceed)
protected async Task CheckTargetChange()
{
if (Target == null)
return;
var target = Target.Value;
var originalTarget = Target.Value;
await RunTicks(5);
if (ClientTarget != null && CEntMan.IsClientSide(ClientTarget.Value))
if (Target.Value.IsClientSide() && CTestSystem.Ghosts.TryGetValue(ConstructionGhostId, out var newWeh))
{
Assert.That(CEntMan.Deleted(ClientTarget.Value), Is.EqualTo(shouldSucceed),
$"Construction ghost was {(shouldSucceed ? "not deleted" : "deleted")}.");
if (shouldSucceed)
{
Assert.That(CTestSystem.Ghosts.TryGetValue(ConstructionGhostId, out var newWeh),
$"Failed to get construction entity from ghost Id");
await Client.WaitPost(() => CLogger.Debug($"Construction ghost {ConstructionGhostId} became entity {newWeh}"));
Target = newWeh;
}
CLogger.Debug($"Construction ghost {ConstructionGhostId} became entity {newWeh}");
Target = newWeh;
}
if (STestSystem.EntChanges.TryGetValue(Target.Value, out var newServerWeh))
{
await Server.WaitPost(
() => SLogger.Debug($"Construction entity {Target.Value} changed to {newServerWeh}"));
SLogger.Debug($"Construction entity {Target.Value} changed to {newServerWeh}");
Target = newServerWeh;
}
if (Target != target)
await CheckTargetChange(shouldSucceed);
if (Target != originalTarget)
await CheckTargetChange();
}
#region Asserts
@@ -444,16 +474,10 @@ public abstract partial class InteractionTest
return;
}
var meta = SEntMan.GetComponent<MetaDataComponent>(SEntMan.GetEntity(target.Value));
var meta = CEntMan.GetComponent<MetaDataComponent>(CEntMan.GetEntity(target.Value));
Assert.That(meta.EntityPrototype?.ID, Is.EqualTo(prototype));
}
protected void ClientAssertPrototype(string? prototype, EntityUid? target)
{
var netEnt = CTestSystem.Ghosts[target.GetHashCode()];
AssertPrototype(prototype, netEnt);
}
protected void AssertPrototype(string? prototype, NetEntity? target = null)
{
target ??= Target;
@@ -699,6 +723,8 @@ public abstract partial class InteractionTest
protected IEnumerable<Shared.DoAfter.DoAfter> ActiveDoAfters
=> DoAfters.DoAfters.Values.Where(x => !x.Cancelled && !x.Completed);
#region Component
/// <summary>
/// Convenience method to get components on the target. Returns SERVER-SIDE components.
/// </summary>
@@ -708,9 +734,23 @@ public abstract partial class InteractionTest
if (target == null)
Assert.Fail("No target specified");
return SEntMan.GetComponent<T>(SEntMan.GetEntity(target!.Value));
return SEntMan.GetComponent<T>(ToServer(target!.Value));
}
/// <inheritdoc cref="Comp{T}"/>
protected bool TryComp<T>(NetEntity? target, [NotNullWhen(true)] out T? comp) where T : IComponent
{
return SEntMan.TryGetComponent(ToServer(target), out comp);
}
/// <inheritdoc cref="Comp{T}"/>
protected bool TryComp<T>([NotNullWhen(true)] out T? comp) where T : IComponent
{
return SEntMan.TryGetComponent(STarget, out comp);
}
#endregion
/// <summary>
/// Set the tile at the target position to some prototype.
/// </summary>
@@ -833,23 +873,70 @@ public abstract partial class InteractionTest
return true;
}
protected bool IsUiOpen(Enum key)
{
if (!TryComp(Player, out UserInterfaceUserComponent? user))
return false;
foreach (var keys in user.OpenInterfaces.Values)
{
if (keys.Contains(key))
return true;
}
return false;
}
#endregion
#region UI
/// <summary>
/// Presses and releases a button on some client-side window. Will fail if the button cannot be found.
/// Attempts to find, and then presses and releases a control on some client-side window.
/// Will fail if the control cannot be found.
/// </summary>
protected async Task ClickControl<TWindow>(string name) where TWindow : BaseWindow
protected async Task ClickControl<TWindow, TControl>(string name, BoundKeyFunction? function = null)
where TWindow : BaseWindow
where TControl : Control
{
await ClickControl(GetControl<TWindow, Control>(name));
var window = GetWindow<TWindow>();
var control = GetControlFromField<TControl>(name, window);
await ClickControl(control, function);
}
/// <summary>
/// Simulates a click and release at the center of some UI Constrol.
/// Attempts to find, and then presses and releases a control on some client-side widget.
/// Will fail if the control cannot be found.
/// </summary>
protected async Task ClickControl(Control control)
protected async Task ClickWidgetControl<TWidget, TControl>(string name, BoundKeyFunction? function = null)
where TWidget : UIWidget, new()
where TControl : Control
{
var widget = GetWidget<TWidget>();
var control = GetControlFromField<TControl>(name, widget);
await ClickControl(control, function);
}
/// <inheritdoc cref="ClickControl{TWindow,TControl}"/>
protected async Task ClickControl<TWindow>(string name, BoundKeyFunction? function = null)
where TWindow : BaseWindow
{
await ClickControl<TWindow, Control>(name, function);
}
/// <inheritdoc cref="ClickWidgetControl{TWidget,TControl}"/>
protected async Task ClickWidgetControl<TWidget>(string name, BoundKeyFunction? function = null)
where TWidget : UIWidget, new()
{
await ClickWidgetControl<TWidget, Control>(name, function);
}
/// <summary>
/// Simulates a click and release at the center of some UI control.
/// </summary>
protected async Task ClickControl(Control control, BoundKeyFunction? function = null)
{
function ??= EngineKeyFunctions.UIClick;
var screenCoords = new ScreenCoordinates(
control.GlobalPixelPosition + control.PixelSize / 2,
control.Window?.Id ?? default);
@@ -858,7 +945,7 @@ public abstract partial class InteractionTest
var relativePixelPos = screenCoords.Position - control.GlobalPixelPosition;
var args = new GUIBoundKeyEventArgs(
EngineKeyFunctions.UIClick,
function.Value,
BoundKeyState.Down,
screenCoords,
default,
@@ -869,7 +956,7 @@ public abstract partial class InteractionTest
await RunTicks(1);
args = new GUIBoundKeyEventArgs(
EngineKeyFunctions.UIClick,
function.Value,
BoundKeyState.Up,
screenCoords,
default,
@@ -881,31 +968,26 @@ public abstract partial class InteractionTest
}
/// <summary>
/// Attempts to find a control on some client-side window. Will fail if the control cannot be found.
/// Attempt to retrieve a control by looking for a field on some other control.
/// </summary>
protected TControl GetControl<TWindow, TControl>(string name)
where TWindow : BaseWindow
/// <remarks>
/// Will fail if the control cannot be found.
/// </remarks>
protected TControl GetControlFromField<TControl>(string name, Control parent)
where TControl : Control
{
var control = GetControl<TWindow>(name);
Assert.That(control.GetType().IsAssignableTo(typeof(TControl)));
return (TControl) control;
}
protected Control GetControl<TWindow>(string name) where TWindow : BaseWindow
{
const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
var field = typeof(TWindow).GetField(name, flags);
var prop = typeof(TWindow).GetProperty(name, flags);
var parentType = parent.GetType();
var field = parentType.GetField(name, flags);
var prop = parentType.GetProperty(name, flags);
if (field == null && prop == null)
{
Assert.Fail($"Window {typeof(TWindow).Name} does not have a field or property named {name}");
Assert.Fail($"Window {parentType.Name} does not have a field or property named {name}");
return default!;
}
var window = GetWindow<TWindow>();
var fieldOrProp = field?.GetValue(window) ?? prop?.GetValue(window);
var fieldOrProp = field?.GetValue(parent) ?? prop?.GetValue(parent);
if (fieldOrProp is not Control control)
{
@@ -913,7 +995,59 @@ public abstract partial class InteractionTest
return default!;
}
return control;
Assert.That(control.GetType().IsAssignableTo(typeof(TControl)));
return (TControl) control;
}
/// <summary>
/// Attempt to retrieve a control that matches some predicate by iterating through a control's children.
/// </summary>
/// <remarks>
/// Will fail if the control cannot be found.
/// </remarks>
protected TControl GetControlFromChildren<TControl>(Func<TControl, bool> predicate, Control parent, bool recursive = true)
where TControl : Control
{
if (TryGetControlFromChildren(predicate, parent, out var control, recursive))
return control;
Assert.Fail($"Failed to find a {nameof(TControl)} that satisfies the predicate in {parent.Name}");
return default!;
}
/// <summary>
/// Attempt to retrieve a control of a given type by iterating through a control's children.
/// </summary>
protected TControl GetControlFromChildren<TControl>(Control parent, bool recursive = false)
where TControl : Control
{
return GetControlFromChildren<TControl>(static _ => true, parent, recursive);
}
/// <summary>
/// Attempt to retrieve a control that matches some predicate by iterating through a control's children.
/// </summary>
protected bool TryGetControlFromChildren<TControl>(
Func<TControl, bool> predicate,
Control parent,
[NotNullWhen(true)] out TControl? control,
bool recursive = true)
where TControl : Control
{
foreach (var ctrl in parent.Children)
{
if (ctrl is TControl cast && predicate(cast))
{
control = cast;
return true;
}
if (recursive && TryGetControlFromChildren(predicate, ctrl, out control))
return true;
}
control = null;
return false;
}
/// <summary>
@@ -944,7 +1078,6 @@ public abstract partial class InteractionTest
return window != null;
}
/// <summary>
/// Attempts to find a currently open client-side window.
/// </summary>
@@ -962,6 +1095,34 @@ public abstract partial class InteractionTest
return window != null;
}
/// <summary>
/// Attempts to find client-side UI widget.
/// </summary>
protected UIWidget GetWidget<TWidget>()
where TWidget : UIWidget, new()
{
if (TryFindWidget(out TWidget? widget))
return widget;
Assert.Fail($"Could not find a {typeof(TWidget).Name} widget");
return default!;
}
/// <summary>
/// Attempts to find client-side UI widget.
/// </summary>
private bool TryFindWidget<TWidget>([NotNullWhen(true)] out TWidget? uiWidget)
where TWidget : UIWidget, new()
{
uiWidget = null;
var screen = UiMan.ActiveScreen;
if (screen == null)
return false;
return screen.TryGetWidget(out uiWidget);
}
#endregion
#region Power

View File

@@ -1,8 +1,10 @@
#nullable enable
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using Content.Client.Construction;
using Content.Client.Examine;
using Content.Client.Gameplay;
using Content.IntegrationTests.Pair;
using Content.Server.Body.Systems;
using Content.Server.Hands.Systems;
@@ -24,6 +26,7 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.UnitTesting;
using Content.Shared.Item.ItemToggle;
using Robust.Client.State;
namespace Content.IntegrationTests.Tests.Interaction;
@@ -64,15 +67,12 @@ public abstract partial class InteractionTest
/// The player entity that performs all these interactions. Defaults to an admin-observer with 1 hand.
/// </summary>
protected NetEntity Player;
protected EntityUid SPlayer => ToServer(Player);
protected EntityUid CPlayer => ToClient(Player);
protected EntityUid SPlayer;
protected EntityUid CPlayer;
protected ICommonSession ClientSession = default!;
protected ICommonSession ServerSession = default!;
public EntityUid? ClientTarget;
/// <summary>
/// The current target entity. This is the default entity for various helper functions.
/// </summary>
@@ -108,6 +108,7 @@ public abstract partial class InteractionTest
protected InteractionTestSystem STestSystem = default!;
protected SharedTransformSystem Transform = default!;
protected ISawmill SLogger = default!;
protected SharedUserInterfaceSystem SUiSys = default!;
// CLIENT dependencies
protected IEntityManager CEntMan = default!;
@@ -119,6 +120,7 @@ public abstract partial class InteractionTest
protected ExamineSystem ExamineSys = default!;
protected InteractionTestSystem CTestSystem = default!;
protected ISawmill CLogger = default!;
protected SharedUserInterfaceSystem CUiSys = default!;
// player components
protected HandsComponent Hands = default!;
@@ -168,6 +170,7 @@ public abstract partial class InteractionTest
STestSystem = SEntMan.System<InteractionTestSystem>();
Stack = SEntMan.System<StackSystem>();
SLogger = Server.ResolveDependency<ILogManager>().RootSawmill;
SUiSys = Client.System<SharedUserInterfaceSystem>();
// client dependencies
CEntMan = Client.ResolveDependency<IEntityManager>();
@@ -179,6 +182,7 @@ public abstract partial class InteractionTest
CConSys = CEntMan.System<ConstructionSystem>();
ExamineSys = CEntMan.System<ExamineSystem>();
CLogger = Client.ResolveDependency<ILogManager>().RootSawmill;
CUiSys = Client.System<SharedUserInterfaceSystem>();
// Setup map.
await Pair.CreateTestMap();
@@ -204,15 +208,16 @@ public abstract partial class InteractionTest
old = cPlayerMan.LocalEntity;
Player = SEntMan.GetNetEntity(SEntMan.SpawnEntity(PlayerPrototype, SEntMan.GetCoordinates(PlayerCoords)));
var serverPlayerEnt = SEntMan.GetEntity(Player);
Server.PlayerMan.SetAttachedEntity(ServerSession, serverPlayerEnt);
Hands = SEntMan.GetComponent<HandsComponent>(serverPlayerEnt);
DoAfters = SEntMan.GetComponent<DoAfterComponent>(serverPlayerEnt);
SPlayer = SEntMan.GetEntity(Player);
Server.PlayerMan.SetAttachedEntity(ServerSession, SPlayer);
Hands = SEntMan.GetComponent<HandsComponent>(SPlayer);
DoAfters = SEntMan.GetComponent<DoAfterComponent>(SPlayer);
});
// Check player got attached.
await RunTicks(5);
Assert.That(CEntMan.GetNetEntity(cPlayerMan.LocalEntity), Is.EqualTo(Player));
CPlayer = ToClient(Player);
Assert.That(cPlayerMan.LocalEntity, Is.EqualTo(CPlayer));
// Delete old player entity.
await Server.WaitPost(() =>
@@ -235,6 +240,10 @@ public abstract partial class InteractionTest
}
});
// Change UI state to in-game.
var state = Client.ResolveDependency<IStateManager>();
await Client.WaitPost(() => state.RequestStateChange<GameplayState>());
// Final player asserts/checks.
await Pair.ReallyBeIdle(5);
Assert.Multiple(() =>