Merge branch 'master' into icarus

This commit is contained in:
Arthur
2022-06-24 06:33:45 +03:00
678 changed files with 161405 additions and 103984 deletions

View File

@@ -62,3 +62,8 @@ jobs:
- name: Run Content.Tests
run: dotnet test --no-build Content.Tests/Content.Tests.csproj -- NUnit.ConsoleOut=0
- name: Run Content.IntegrationTests
shell: pwsh
run: |
$env:DOTNET_gcServer=1
dotnet test --no-build Content.IntegrationTests/Content.IntegrationTests.csproj -- NUnit.ConsoleOut=0

View File

@@ -9,10 +9,9 @@ on:
jobs:
Label:
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
steps:
- name: Check for Merge Conflicts
uses: eps1lon/actions-label-merge-conflict@513a24fc7dca40990863be2935e059e650728400
uses: ike709/actions-label-merge-conflict@9eefdd17e10566023c46d2dc6dc04fcb8ec76142
with:
dirtyLabel: "Merge Conflict"
repoToken: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -294,7 +294,7 @@ namespace Content.Client.AI
private void DrawCachedRegions(DrawingHandleScreen screenHandle, Box2 viewport)
{
var transform = _entities.GetComponentOrNull<TransformComponent>(_playerManager.LocalPlayer?.ControlledEntity);
if (transform == null || !CachedRegions.TryGetValue(transform.GridEntityId, out var entityRegions))
if (transform == null || transform.GridUid == null || !CachedRegions.TryGetValue(transform.GridUid.Value, out var entityRegions))
{
return;
}
@@ -312,7 +312,7 @@ namespace Content.Client.AI
screenTile.X + 15.0f,
screenTile.Y + 15.0f);
screenHandle.DrawRect(box, _cachedRegionColors[transform.GridEntityId][region]);
screenHandle.DrawRect(box, _cachedRegionColors[transform.GridUid.Value][region]);
}
}
}
@@ -344,7 +344,8 @@ namespace Content.Client.AI
{
var attachedEntity = _playerManager.LocalPlayer?.ControlledEntity;
if (!_entities.TryGetComponent(attachedEntity, out TransformComponent? transform) ||
!Regions.TryGetValue(transform.GridEntityId, out var entityRegions))
transform.GridUid == null ||
!Regions.TryGetValue(transform.GridUid.Value, out var entityRegions))
{
return;
}
@@ -364,7 +365,7 @@ namespace Content.Client.AI
screenTile.X + 15.0f,
screenTile.Y + 15.0f);
screenHandle.DrawRect(box, _regionColors[_entities.GetComponent<TransformComponent>(attachedEntity.Value).GridEntityId][chunk][region]);
screenHandle.DrawRect(box, _regionColors[transform.GridUid.Value][chunk][region]);
}
}
}

View File

@@ -0,0 +1,61 @@
using Content.Client.AME.Components;
using Robust.Client.GameObjects;
using static Content.Shared.AME.SharedAMEControllerComponent;
namespace Content.Client.AME;
public sealed class AMEControllerVisualizerSystem : VisualizerSystem<AMEControllerVisualsComponent>
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AMEControllerVisualsComponent, ComponentInit>(OnComponentInit);
}
private void OnComponentInit(EntityUid uid, AMEControllerVisualsComponent component, ComponentInit args)
{
if(TryComp<SpriteComponent>(uid, out var sprite))
{
sprite.LayerMapSet(AMEControllerVisualLayers.Display, sprite.AddLayerState("control_on"));
sprite.LayerSetVisible(AMEControllerVisualLayers.Display, false);
}
}
protected override void OnAppearanceChange(EntityUid uid, AMEControllerVisualsComponent component, ref AppearanceChangeEvent args)
{
base.OnAppearanceChange(uid, component, ref args);
if(args.Sprite != null
&& args.Component.TryGetData<string>(AMEControllerVisuals.DisplayState, out var state))
{
switch(state)
{
case "on":
args.Sprite.LayerSetState(AMEControllerVisualLayers.Display, "control_on");
args.Sprite.LayerSetVisible(AMEControllerVisualLayers.Display, true);
break;
case "critical":
args.Sprite.LayerSetState(AMEControllerVisualLayers.Display, "control_critical");
args.Sprite.LayerSetVisible(AMEControllerVisualLayers.Display, true);
break;
case "fuck":
args.Sprite.LayerSetState(AMEControllerVisualLayers.Display, "control_fuck");
args.Sprite.LayerSetVisible(AMEControllerVisualLayers.Display, true);
break;
case "off":
args.Sprite.LayerSetVisible(AMEControllerVisualLayers.Display, false);
break;
default:
args.Sprite.LayerSetVisible(AMEControllerVisualLayers.Display, false);
break;
}
}
}
}
public enum AMEControllerVisualLayers : byte
{
Display
}

View File

@@ -0,0 +1,70 @@
using Content.Client.AME.Components;
using Robust.Client.GameObjects;
using static Content.Shared.AME.SharedAMEShieldComponent;
namespace Content.Client.AME;
public sealed class AMEShieldingVisualizerSystem : VisualizerSystem<AMEShieldingVisualsComponent>
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AMEShieldingVisualsComponent, ComponentInit>(OnComponentInit);
}
private void OnComponentInit(EntityUid uid, AMEShieldingVisualsComponent component, ComponentInit args)
{
if(TryComp<SpriteComponent>(uid, out var sprite))
{
sprite.LayerMapSet(AMEShieldingVisualsLayer.Core, sprite.AddLayerState("core"));
sprite.LayerSetVisible(AMEShieldingVisualsLayer.Core, false);
sprite.LayerMapSet(AMEShieldingVisualsLayer.CoreState, sprite.AddLayerState("core_weak"));
sprite.LayerSetVisible(AMEShieldingVisualsLayer.CoreState, false);
}
}
protected override void OnAppearanceChange(EntityUid uid, AMEShieldingVisualsComponent component, ref AppearanceChangeEvent args)
{
if(args.Sprite == null)
return;
if(args.Component.TryGetData<string>(AMEShieldVisuals.Core, out var core))
{
if (core == "isCore")
{
args.Sprite.LayerSetState(AMEShieldingVisualsLayer.Core, "core");
args.Sprite.LayerSetVisible(AMEShieldingVisualsLayer.Core, true);
}
else
{
args.Sprite.LayerSetVisible(AMEShieldingVisualsLayer.Core, false);
}
}
if(args.Component.TryGetData<string>(AMEShieldVisuals.CoreState, out var coreState))
{
switch(coreState)
{
case "weak":
args.Sprite.LayerSetState(AMEShieldingVisualsLayer.CoreState, "core_weak");
args.Sprite.LayerSetVisible(AMEShieldingVisualsLayer.CoreState, true);
break;
case "strong":
args.Sprite.LayerSetState(AMEShieldingVisualsLayer.CoreState, "core_strong");
args.Sprite.LayerSetVisible(AMEShieldingVisualsLayer.CoreState, true);
break;
case "off":
args.Sprite.LayerSetVisible(AMEShieldingVisualsLayer.CoreState, false);
break;
}
}
}
}
public enum AMEShieldingVisualsLayer : byte
{
Core,
CoreState,
}

View File

@@ -0,0 +1,9 @@
using System.ComponentModel.DataAnnotations;
using Robust.Shared.GameObjects;
namespace Content.Client.AME.Components;
[RegisterComponent]
public sealed class AMEControllerVisualsComponent : Component
{
}

View File

@@ -0,0 +1,9 @@
using System.ComponentModel.DataAnnotations;
using Robust.Shared.GameObjects;
namespace Content.Client.AME.Components;
[RegisterComponent]
public sealed class AMEShieldingVisualsComponent : Component
{
}

View File

@@ -1,56 +0,0 @@
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using static Content.Shared.AME.SharedAMEControllerComponent;
namespace Content.Client.AME.Visualizers
{
[UsedImplicitly]
public sealed class AMEControllerVisualizer : AppearanceVisualizer
{
public override void InitializeEntity(EntityUid entity)
{
base.InitializeEntity(entity);
var sprite = IoCManager.Resolve<IEntityManager>().GetComponent<ISpriteComponent>(entity);
sprite.LayerMapSet(Layers.Display, sprite.AddLayerState("control_on"));
sprite.LayerSetVisible(Layers.Display, false);
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var sprite = IoCManager.Resolve<IEntityManager>().GetComponent<ISpriteComponent>(component.Owner);
if (component.TryGetData<string>(AMEControllerVisuals.DisplayState, out var state))
{
switch (state)
{
case "on":
sprite.LayerSetState(Layers.Display, "control_on");
sprite.LayerSetVisible(Layers.Display, true);
break;
case "critical":
sprite.LayerSetState(Layers.Display, "control_critical");
sprite.LayerSetVisible(Layers.Display, true);
break;
case "fuck":
sprite.LayerSetState(Layers.Display, "control_fuck");
sprite.LayerSetVisible(Layers.Display, true);
break;
case "off":
sprite.LayerSetVisible(Layers.Display, false);
break;
default:
sprite.LayerSetVisible(Layers.Display, false);
break;
}
}
}
enum Layers : byte
{
Display,
}
}
}

View File

@@ -1,62 +0,0 @@
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using static Content.Shared.AME.SharedAMEShieldComponent;
namespace Content.Client.AME.Visualizers
{
[UsedImplicitly]
public sealed class AMEVisualizer : AppearanceVisualizer
{
public override void InitializeEntity(EntityUid entity)
{
base.InitializeEntity(entity);
var sprite = IoCManager.Resolve<IEntityManager>().GetComponent<ISpriteComponent>(entity);
sprite.LayerMapSet(Layers.Core, sprite.AddLayerState("core"));
sprite.LayerSetVisible(Layers.Core, false);
sprite.LayerMapSet(Layers.CoreState, sprite.AddLayerState("core_weak"));
sprite.LayerSetVisible(Layers.CoreState, false);
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var sprite = IoCManager.Resolve<IEntityManager>().GetComponent<ISpriteComponent>(component.Owner);
if (component.TryGetData<string>(AMEShieldVisuals.Core, out var core))
{
if (core == "isCore")
{
sprite.LayerSetState(Layers.Core, "core");
sprite.LayerSetVisible(Layers.Core, true);
}
else
{
sprite.LayerSetVisible(Layers.Core, false);
}
}
if (component.TryGetData<string>(AMEShieldVisuals.CoreState, out var coreState))
switch (coreState)
{
case "weak":
sprite.LayerSetState(Layers.CoreState, "core_weak");
sprite.LayerSetVisible(Layers.CoreState, true);
break;
case "strong":
sprite.LayerSetState(Layers.CoreState, "core_strong");
sprite.LayerSetVisible(Layers.CoreState, true);
break;
case "off":
sprite.LayerSetVisible(Layers.CoreState, false);
break;
}
}
}
enum Layers : byte
{
Core,
CoreState,
}
}

View File

@@ -24,7 +24,7 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
foreach (var grid in _data)
{
var player = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.ControlledEntity;
var playerGrid = IoCManager.Resolve<IEntityManager>().GetComponentOrNull<TransformComponent>(player)?.GridEntityId;
var playerGrid = IoCManager.Resolve<IEntityManager>().GetComponentOrNull<TransformComponent>(player)?.GridUid;
GridOptions.AddItem($"{grid.GridEntityId} {(playerGrid == grid.GridEntityId ? " (Current)" : "")}");
}

View File

@@ -28,7 +28,7 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
foreach (var grid in _gridData)
{
var player = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.ControlledEntity;
var playerGrid = IoCManager.Resolve<IEntityManager>().GetComponentOrNull<TransformComponent>(player)?.GridEntityId;
var playerGrid = IoCManager.Resolve<IEntityManager>().GetComponentOrNull<TransformComponent>(player)?.GridUid;
GridOptions.AddItem($"{grid.GridEntityId} {(playerGrid == grid.GridEntityId ? " (Current)" : "")}");
}

View File

@@ -28,7 +28,7 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
foreach (var grid in _gridData)
{
var player = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.ControlledEntity;
var playerGrid = IoCManager.Resolve<IEntityManager>().GetComponentOrNull<TransformComponent>(player)?.GridEntityId;
var playerGrid = IoCManager.Resolve<IEntityManager>().GetComponentOrNull<TransformComponent>(player)?.GridUid;
GridOptions.AddItem($"{grid.GridEntityId} {(playerGrid == grid.GridEntityId ? " (Current)" : "")}");
}

View File

@@ -24,7 +24,7 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
foreach (var grid in _data)
{
var player = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.ControlledEntity;
var playerGrid = IoCManager.Resolve<IEntityManager>().GetComponentOrNull<TransformComponent>(player)?.GridEntityId;
var playerGrid = IoCManager.Resolve<IEntityManager>().GetComponentOrNull<TransformComponent>(player)?.GridUid;
GridOptions.AddItem($"{grid.GridEntityId} {(playerGrid == grid.GridEntityId ? " (Current)" : "")}");
}

View File

@@ -145,7 +145,7 @@ namespace Content.Client.Atmos.UI
PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#525252ff") }
});
CloseButton.OnPressed += _ => Close();
SetSize = (300, 200);
SetSize = (300, 420);
}

View File

@@ -89,7 +89,7 @@ namespace Content.Client.Audio
{
if(_playMan.LocalPlayer is null || _playMan.LocalPlayer.ControlledEntity != message.Entity) return;
if (!TryComp<TransformComponent>(message.Entity, out var xform) ||
!_mapManager.TryGetGrid(xform.GridEntityId, out var grid)) return;
!_mapManager.TryGetGrid(xform.GridUid, out var grid)) return;
var tileDef = (ContentTileDefinition) _tileDefMan[grid.GetTileRef(xform.Coordinates).Tile.TypeId];

View File

@@ -41,7 +41,7 @@ namespace Content.Client.Buckle
return;
}
if (!_entMan.TryGetComponent(Owner, out RiderComponent? rider))
if (LastEntityBuckledTo != null && _entMan.HasComponent<VehicleComponent>(LastEntityBuckledTo))
{
return;
}

View File

@@ -1,45 +1,46 @@
using Content.Client.Cargo.Components;
using Content.Client.Cargo.UI;
using Content.Shared.Access.Systems;
using Content.Shared.Cargo;
using Content.Shared.Cargo.BUI;
using Content.Shared.Cargo.Components;
using Content.Shared.Cargo.Events;
using Content.Shared.Cargo.Prototypes;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
using static Content.Shared.Cargo.Components.SharedCargoConsoleComponent;
using Robust.Client.Player;
using Robust.Shared.Prototypes;
using static Robust.Client.UserInterface.Controls.BaseButton;
namespace Content.Client.Cargo
namespace Content.Client.Cargo.BUI
{
public sealed class CargoConsoleBoundUserInterface : BoundUserInterface
public sealed class CargoOrderConsoleBoundUserInterface : BoundUserInterface
{
[ViewVariables]
private CargoConsoleMenu? _menu;
/// <summary>
/// This is the separate popup window for individual orders.
/// </summary>
[ViewVariables]
private CargoConsoleOrderMenu? _orderMenu;
[ViewVariables]
public CargoOrderDatabaseComponent? Orders { get; private set; }
[ViewVariables]
public bool RequestOnly { get; private set; }
[ViewVariables]
public int BankId { get; private set; }
[ViewVariables]
public string? BankName { get; private set; }
public string? AccountName { get; private set; }
[ViewVariables]
public int BankBalance { get; private set; }
[ViewVariables]
public (int CurrentCapacity, int MaxCapacity) ShuttleCapacity { get; private set; }
public int OrderCapacity { get; private set; }
[ViewVariables]
public int OrderCount { get; private set; }
/// <summary>
/// Currently selected product
/// </summary>
private CargoProductPrototype? _product;
public CargoConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
public CargoOrderConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
}
@@ -47,30 +48,29 @@ namespace Content.Client.Cargo
{
base.Open();
var entMan = IoCManager.Resolve<IEntityManager>();
if (!entMan.TryGetComponent(Owner.Owner, out CargoOrderDatabaseComponent? orders)) return;
var entityManager = IoCManager.Resolve<IEntityManager>();
var sysManager = entityManager.EntitySysManager;
var spriteSystem = sysManager.GetEntitySystem<SpriteSystem>();
_menu = new CargoConsoleMenu(IoCManager.Resolve<IPrototypeManager>(), spriteSystem);
var localPlayer = IoCManager.Resolve<IPlayerManager>()?.LocalPlayer?.ControlledEntity;
Orders = orders;
string orderRequester;
if (entityManager.TryGetComponent<MetaDataComponent>(localPlayer, out var metadata))
orderRequester = metadata.EntityName;
else
orderRequester = string.Empty;
_menu = new CargoConsoleMenu(this);
_orderMenu = new CargoConsoleOrderMenu();
_menu.OnClose += Close;
_menu.Populate();
Orders.OnDatabaseUpdated += _menu.PopulateOrders;
_menu.CallShuttleButton.OnPressed += (_) =>
{
SendMessage(new CargoConsoleShuttleMessage());
};
_menu.OnItemSelected += (args) =>
{
if (args.Button.Parent is not CargoProductRow row)
return;
_product = row.Product;
_orderMenu.Requester.Text = "";
_orderMenu.Requester.Text = orderRequester;
_orderMenu.Reason.Text = "";
_orderMenu.Amount.Value = 1;
_orderMenu.OpenCentered();
@@ -86,7 +86,15 @@ namespace Content.Client.Cargo
};
_menu.OpenCentered();
}
private void Populate(List<CargoOrderData> orders)
{
if (_menu == null) return;
_menu.PopulateProducts();
_menu.PopulateCategories();
_menu.PopulateOrders(orders);
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -95,17 +103,16 @@ namespace Content.Client.Cargo
if (state is not CargoConsoleInterfaceState cState)
return;
if (RequestOnly != cState.RequestOnly)
{
RequestOnly = cState.RequestOnly;
_menu?.UpdateRequestOnly();
}
BankId = cState.BankId;
BankName = cState.BankName;
BankBalance = cState.BankBalance;
ShuttleCapacity = cState.ShuttleCapacity;
_menu?.UpdateCargoCapacity();
_menu?.UpdateBankData();
OrderCapacity = cState.Capacity;
OrderCount = cState.Count;
BankBalance = cState.Balance;
AccountName = cState.Name;
Populate(cState.Orders);
_menu?.UpdateCargoCapacity(OrderCount, OrderCapacity);
_menu?.UpdateBankData(AccountName, BankBalance);
}
protected override void Dispose(bool disposing)
@@ -114,11 +121,6 @@ namespace Content.Client.Cargo
if (!disposing) return;
if (Orders != null && _menu != null)
{
Orders.OnDatabaseUpdated -= _menu.PopulateOrders;
}
_menu?.Dispose();
_orderMenu?.Dispose();
}
@@ -126,7 +128,7 @@ namespace Content.Client.Cargo
private bool AddOrder()
{
int orderAmt = _orderMenu?.Amount.Value ?? 0;
if (orderAmt < 1 || orderAmt > ShuttleCapacity.MaxCapacity)
if (orderAmt < 1 || orderAmt > OrderCapacity)
{
return false;
}
@@ -153,11 +155,12 @@ namespace Content.Client.Cargo
if (args.Button.Parent?.Parent is not CargoOrderRow row || row.Order == null)
return;
if (ShuttleCapacity.CurrentCapacity == ShuttleCapacity.MaxCapacity)
if (OrderCount >= OrderCapacity)
return;
SendMessage(new CargoConsoleApproveOrderMessage(row.Order.OrderNumber));
_menu?.UpdateCargoCapacity();
// Most of the UI isn't predicted anyway so.
// _menu?.UpdateCargoCapacity(OrderCount + row.Order.Amount, OrderCapacity);
}
}
}

View File

@@ -0,0 +1,57 @@
using Content.Client.Cargo.UI;
using Content.Shared.Cargo.BUI;
using Content.Shared.Cargo.Events;
using Robust.Client.GameObjects;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Client.Cargo.BUI;
public sealed class CargoShuttleConsoleBoundUserInterface : BoundUserInterface
{
private CargoShuttleMenu? _menu;
public CargoShuttleConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) {}
protected override void Open()
{
base.Open();
_menu = new CargoShuttleMenu(IoCManager.Resolve<IGameTiming>(), IoCManager.Resolve<IPrototypeManager>(), EntitySystem.Get<SpriteSystem>());
_menu.ShuttleCallRequested += OnShuttleCall;
_menu.ShuttleRecallRequested += OnShuttleRecall;
_menu.OnClose += Close;
_menu.OpenCentered();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_menu?.Dispose();
}
}
private void OnShuttleRecall()
{
SendMessage(new CargoRecallShuttleMessage());
}
private void OnShuttleCall()
{
SendMessage(new CargoCallShuttleMessage());
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (state is not CargoShuttleConsoleBoundUserInterfaceState cargoState) return;
_menu?.SetAccountName(cargoState.AccountName);
_menu?.SetShuttleName(cargoState.ShuttleName);
_menu?.SetShuttleETA(cargoState.ShuttleETA);
_menu?.SetOrders(cargoState.Orders);
_menu?.SetCanRecall(cargoState.CanRecall);
}
}

View File

@@ -1,56 +0,0 @@
using System;
using System.Collections.Generic;
using Content.Shared.Cargo;
using Content.Shared.Cargo.Components;
using Robust.Shared.GameObjects;
namespace Content.Client.Cargo.Components
{
[RegisterComponent]
public sealed class CargoOrderDatabaseComponent : SharedCargoOrderDatabaseComponent
{
private readonly List<CargoOrderData> _orders = new();
public IReadOnlyList<CargoOrderData> Orders => _orders;
/// <summary>
/// Event called when the database is updated.
/// </summary>
public event Action? OnDatabaseUpdated;
// TODO add account selector menu
/// <summary>
/// Removes all orders from the database.
/// </summary>
public void Clear()
{
_orders.Clear();
}
/// <summary>
/// Adds an order to the database.
/// </summary>
/// <param name="order">The order to be added.</param>
public void AddOrder(CargoOrderData order)
{
if (!_orders.Contains(order))
_orders.Add(order);
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
base.HandleComponentState(curState, nextState);
if (curState is not CargoOrderDatabaseState state)
return;
Clear();
if (state.Orders == null)
return;
foreach (var order in state.Orders)
{
AddOrder(order);
}
OnDatabaseUpdated?.Invoke();
}
}
}

View File

@@ -3,7 +3,7 @@ using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
namespace Content.Client.Cargo;
namespace Content.Client.Cargo.Systems;
public sealed partial class CargoSystem
{

View File

@@ -1,7 +1,7 @@
using Content.Shared.Cargo;
using Robust.Client.GameObjects;
namespace Content.Client.Cargo;
namespace Content.Client.Cargo.Systems;
public sealed partial class CargoSystem : SharedCargoSystem
{

View File

@@ -1,7 +1,8 @@
<DefaultWindow xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
SetSize="400 600"
MinSize="400 600">
<userInterface:FancyWindow xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:userInterface="clr-namespace:Content.Client.UserInterface"
SetSize="600 600"
MinSize="600 600">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'cargo-console-menu-account-name-label'}"
@@ -15,28 +16,12 @@
<Label Name="PointsLabel"
Text="0" />
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'cargo-console-menu-shuttle-status-label'}"
StyleClasses="LabelKeyText" />
<Label Name="ShuttleStatusLabel"
Text="{Loc 'cargo-console-menu-shuttle-status-away-text'}" />
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'cargo-console-menu-order-capacity-label'}"
StyleClasses="LabelKeyText" />
<Label Name="ShuttleCapacityLabel"
Text="0/20" />
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Button Name="CallShuttleButton"
Access="Public"
Text="{Loc 'cargo-console-menu-call-shuttle-button'}"
TextAlign="Center"
HorizontalExpand="True"/>
<Button Name="PermissionsButton"
Text="{Loc 'cargo-console-menu-permissions-button'}"
TextAlign="Center" />
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<OptionButton Name="Categories"
Prefix="{Loc 'cargo-console-menu-categories-label'}"
@@ -80,4 +65,4 @@
</PanelContainer>
<TextureButton VerticalExpand="True" />
</BoxContainer>
</DefaultWindow>
</userInterface:FancyWindow>

View File

@@ -1,31 +1,24 @@
using System;
using System.Collections.Generic;
using Content.Client.Stylesheets;
using System.Linq;
using Content.Client.UserInterface;
using Content.Shared.Cargo;
using Content.Shared.Cargo.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Client.Utility;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using static Robust.Client.UserInterface.Controls.BaseButton;
using static Robust.Client.UserInterface.Controls.BoxContainer;
namespace Content.Client.Cargo.UI
{
[GenerateTypedNameReferences]
public sealed partial class CargoConsoleMenu : DefaultWindow
public sealed partial class CargoConsoleMenu : FancyWindow
{
[Dependency]
private IPrototypeManager _prototypeManager = default!;
public CargoConsoleBoundUserInterface Owner { get; private set; }
private IPrototypeManager _protoManager;
private SpriteSystem _spriteSystem;
public event Action<ButtonEventArgs>? OnItemSelected;
public event Action<ButtonEventArgs>? OnOrderApproved;
@@ -34,25 +27,18 @@ namespace Content.Client.Cargo.UI
private readonly List<string> _categoryStrings = new();
private string? _category;
public CargoConsoleMenu(CargoConsoleBoundUserInterface owner)
public CargoConsoleMenu(IPrototypeManager protoManager, SpriteSystem spriteSystem)
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
Owner = owner;
_protoManager = protoManager;
_spriteSystem = spriteSystem;
Title = Loc.GetString(Owner.RequestOnly
? "cargo-console-menu-request-only-title"
: "cargo-console-menu-title");
Title = Loc.GetString("cargo-console-menu-title");
CallShuttleButton.OnPressed += OnCallShuttleButtonPressed;
SearchBar.OnTextChanged += OnSearchBarTextChanged;
Categories.OnItemSelected += OnCategoryItemSelected;
}
private void OnCallShuttleButtonPressed(ButtonEventArgs args)
{
}
private void OnCategoryItemSelected(OptionButton.ItemSelectedEventArgs args)
{
SetCategoryText(args.Id);
@@ -70,7 +56,7 @@ namespace Content.Client.Cargo.UI
Categories.SelectId(id);
}
public IEnumerable<CargoProductPrototype> ProductPrototypes => _prototypeManager.EnumeratePrototypes<CargoProductPrototype>();
public IEnumerable<CargoProductPrototype> ProductPrototypes => _protoManager.EnumeratePrototypes<CargoProductPrototype>();
/// <summary>
/// Populates the list of products that will actually be shown, using the current filters.
@@ -78,9 +64,12 @@ namespace Content.Client.Cargo.UI
public void PopulateProducts()
{
Products.RemoveAllChildren();
var products = ProductPrototypes.ToList();
products.Sort((x, y) =>
string.Compare(x.Name, y.Name, StringComparison.Ordinal));
var search = SearchBar.Text.Trim().ToLowerInvariant();
foreach (var prototype in ProductPrototypes)
foreach (var prototype in products)
{
// if no search or category
// else if search
@@ -94,7 +83,7 @@ namespace Content.Client.Cargo.UI
Product = prototype,
ProductName = { Text = prototype.Name },
PointCost = { Text = prototype.PointCost.ToString() },
Icon = { Texture = prototype.Icon.Frame0() },
Icon = { Texture = _spriteSystem.Frame0(prototype.Icon) },
};
button.MainButton.OnPressed += args =>
{
@@ -132,31 +121,20 @@ namespace Content.Client.Cargo.UI
/// <summary>
/// Populates the list of orders and requests.
/// </summary>
public void PopulateOrders()
public void PopulateOrders(IEnumerable<CargoOrderData> orders)
{
Orders.RemoveAllChildren();
Requests.RemoveAllChildren();
Orders.DisposeAllChildren();
Requests.DisposeAllChildren();
if (Owner.Orders == null)
foreach (var order in orders)
{
return;
}
foreach (var order in Owner.Orders.Orders)
{
if (!_prototypeManager.TryIndex<CargoProductPrototype>(order.ProductId, out CargoProductPrototype? product))
{
DebugTools.Assert(false);
Logger.ErrorS("cargo", $"Unable to find product name for {order.ProductId}");
continue;
}
var product = _protoManager.Index<CargoProductPrototype>(order.ProductId);
var productName = product.Name;
var row = new CargoOrderRow
{
Order = order,
Icon = { Texture = product.Icon.Frame0() },
Icon = { Texture = _spriteSystem.Frame0(product.Icon) },
ProductName =
{
Text = Loc.GetString(
@@ -177,43 +155,23 @@ namespace Content.Client.Cargo.UI
}
else
{
if (Owner.RequestOnly)
row.Approve.Visible = false;
else
row.Approve.OnPressed += (args) => { OnOrderApproved?.Invoke(args); };
// TODO: Disable based on access.
row.Approve.OnPressed += (args) => { OnOrderApproved?.Invoke(args); };
Requests.AddChild(row);
}
}
}
public void Populate()
public void UpdateCargoCapacity(int count, int capacity)
{
PopulateProducts();
PopulateCategories();
PopulateOrders();
// TODO: Rename + Loc.
ShuttleCapacityLabel.Text = $"{count}/{capacity}";
}
public void UpdateCargoCapacity()
public void UpdateBankData(string name, int points)
{
ShuttleCapacityLabel.Text = $"{Owner.ShuttleCapacity.CurrentCapacity}/{Owner.ShuttleCapacity.MaxCapacity}";
}
public void UpdateBankData()
{
AccountNameLabel.Text = Owner.BankName;
PointsLabel.Text = Owner.BankBalance.ToString();
}
/// <summary>
/// Show/Hide Call Shuttle button and Approve buttons
/// </summary>
public void UpdateRequestOnly()
{
CallShuttleButton.Visible = !Owner.RequestOnly;
foreach (CargoOrderRow row in Requests.Children)
{
row.Approve.Visible = !Owner.RequestOnly;
}
AccountNameLabel.Text = name;
PointsLabel.Text = points.ToString();
}
}
}

View File

@@ -1,4 +1,5 @@
using Content.Shared.Cargo;
using Content.Shared.Cargo.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;

View File

@@ -0,0 +1,47 @@
<userInterface:FancyWindow xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:userInterface="clr-namespace:Content.Client.UserInterface"
SetSize="600 600"
MinSize="600 600">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'cargo-console-menu-account-name-label'}"
StyleClasses="LabelKeyText" />
<Label Name="AccountNameLabel"
Text="{Loc 'cargo-console-menu-account-name-none-text'}" />
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'cargo-console-menu-shuttle-name-label'}"
StyleClasses="LabelKeyText" />
<Label Name="ShuttleNameLabel"
Text="{Loc 'cargo-console-menu-shuttle-name-none-text'}" />
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'cargo-console-menu-shuttle-status-label'}"
StyleClasses="LabelKeyText" />
<Label Name="ShuttleStatusLabel"
Text="{Loc 'cargo-console-menu-shuttle-status-away-text'}" />
</BoxContainer>
<Button Name="ShuttleCallButton"
Text="Call Shuttle"/>
<Button Name="ShuttleRecallButton"
Text="Recall Shuttle"
ToolTip="Needs to be out of range to recall."
Visible="False"/>
<Label Text="{Loc 'cargo-console-menu-orders-label'}" />
<PanelContainer VerticalExpand="True"
SizeFlagsStretchRatio="6">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#000000" />
</PanelContainer.PanelOverride>
<ScrollContainer VerticalExpand="True">
<BoxContainer Name="Orders"
Orientation="Vertical"
StyleClasses="transparentItemList"
VerticalExpand="True">
</BoxContainer>
</ScrollContainer>
</PanelContainer>
<TextureButton VerticalExpand="True" />
</BoxContainer>
</userInterface:FancyWindow>

View File

@@ -0,0 +1,128 @@
using Content.Client.UserInterface;
using Content.Shared.Cargo;
using Content.Shared.Cargo.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Client.Cargo.UI
{
[GenerateTypedNameReferences]
public sealed partial class CargoShuttleMenu : FancyWindow
{
private readonly IGameTiming _timing;
private readonly IPrototypeManager _protoManager;
private readonly SpriteSystem _spriteSystem;
public Action? ShuttleCallRequested;
public Action? ShuttleRecallRequested;
private TimeSpan? _shuttleEta;
public CargoShuttleMenu(IGameTiming timing, IPrototypeManager protoManager, SpriteSystem spriteSystem)
{
RobustXamlLoader.Load(this);
_timing = timing;
_protoManager = protoManager;
_spriteSystem = spriteSystem;
ShuttleCallButton.OnPressed += OnCallPressed;
ShuttleRecallButton.OnPressed += OnRecallPressed;
Title = Loc.GetString("cargo-shuttle-console-menu-title");
}
public void SetAccountName(string name)
{
AccountNameLabel.Text = name;
}
public void SetShuttleName(string name)
{
ShuttleNameLabel.Text = name;
}
public void SetShuttleETA(TimeSpan? eta)
{
_shuttleEta = eta;
if (eta == null)
{
ShuttleCallButton.Visible = false;
ShuttleRecallButton.Visible = true;
}
else
{
ShuttleRecallButton.Visible = false;
ShuttleCallButton.Visible = true;
ShuttleCallButton.Disabled = true;
}
}
private void OnRecallPressed(BaseButton.ButtonEventArgs obj)
{
ShuttleRecallRequested?.Invoke();
}
private void OnCallPressed(BaseButton.ButtonEventArgs obj)
{
ShuttleCallRequested?.Invoke();
}
public void SetOrders(List<CargoOrderData> orders)
{
Orders.DisposeAllChildren();
foreach (var order in orders)
{
var product = _protoManager.Index<CargoProductPrototype>(order.ProductId);
var productName = product.Name;
var row = new CargoOrderRow
{
Order = order,
Icon = { Texture = _spriteSystem.Frame0(product.Icon) },
ProductName =
{
Text = Loc.GetString(
"cargo-console-menu-populate-orders-cargo-order-row-product-name-text",
("productName", productName),
("orderAmount", order.Amount),
("orderRequester", order.Requester))
},
Description = {Text = Loc.GetString("cargo-console-menu-order-reason-description",
("reason", order.Reason))}
};
row.Approve.Visible = false;
row.Cancel.Visible = false;
Orders.AddChild(row);
}
}
public void SetCanRecall(bool canRecall)
{
ShuttleRecallButton.Disabled = !canRecall;
}
protected override void Draw(DrawingHandleScreen handle)
{
base.Draw(handle);
var remaining = _shuttleEta - _timing.CurTime;
if (remaining == null || remaining <= TimeSpan.Zero)
{
ShuttleStatusLabel.Text = $"Available";
ShuttleCallButton.Disabled = false;
}
else
{
ShuttleStatusLabel.Text = $"Available in: {remaining.Value.TotalSeconds:0.0}";
}
}
}
}

View File

@@ -1,45 +0,0 @@
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
namespace Content.Client.Cargo.UI
{
public sealed class GalacticBankSelectionMenu : DefaultWindow
{
private readonly ItemList _accounts;
private int _accountCount;
private string[] _accountNames = System.Array.Empty<string>();
private int[] _accountIds = System.Array.Empty<int>();
private int _selectedAccountId = -1;
public GalacticBankSelectionMenu(CargoConsoleBoundUserInterface owner)
{
MinSize = SetSize = (300, 300);
IoCManager.InjectDependencies(this);
Title = Loc.GetString("galactic-bank-selection-menu-title");
_accounts = new ItemList { SelectMode = ItemList.ItemListSelectMode.Single };
Contents.AddChild(_accounts);
}
public void Populate(int accountCount, string[] accountNames, int[] accountIds, int selectedAccountId)
{
_accountCount = accountCount;
_accountNames = accountNames;
_accountIds = accountIds;
_selectedAccountId = selectedAccountId;
_accounts.Clear();
for (var i = 0; i < _accountCount; i++)
{
var id = _accountIds[i];
_accounts.AddItem($"ID: {id} || {_accountNames[i]}");
if (id == _selectedAccountId)
_accounts[id].Selected = true;
}
}
}
}

View File

@@ -128,11 +128,10 @@ namespace Content.Client.Chat.UI
Modulate = Color.White;
}
var worldPos = xform.WorldPosition;
var scale = _eyeManager.MainViewport.GetRenderScale();
var offset = new Vector2(0, EntityVerticalOffset * EyeManager.PixelsPerMeter * scale);
var lowerCenter = (_eyeManager.WorldToScreen(worldPos) - offset) / UIScale;
var offset = (-_eyeManager.CurrentEye.Rotation).ToWorldVec() * -EntityVerticalOffset;
var worldPos = xform.WorldPosition + offset;
var lowerCenter = _eyeManager.WorldToScreen(worldPos) / UIScale;
var screenPos = lowerCenter - (ContentSize.X / 2, ContentSize.Y + _verticalOffsetAchieved);
// Round to nearest 0.5
screenPos = (screenPos * 2).Rounded() / 2;

View File

@@ -251,14 +251,14 @@ public sealed class ClothingSystem : EntitySystem
if (ev.Layers.Count == 0)
{
RaiseLocalEvent(equipment, new EquipmentVisualsUpdatedEvent(equipee, slot, revealedLayers));
RaiseLocalEvent(equipment, new EquipmentVisualsUpdatedEvent(equipee, slot, revealedLayers), true);
return;
}
// temporary, until layer draw depths get added. Basically: a layer with the key "slot" is being used as a
// bookmark to determine where in the list of layers we should insert the clothing layers.
bool slotLayerExists = sprite.LayerMapTryGet(slot, out var index);
// add the new layers
foreach (var (key, layerData) in ev.Layers)
{
@@ -294,6 +294,6 @@ public sealed class ClothingSystem : EntitySystem
layer.Offset += slotDef.Offset;
}
RaiseLocalEvent(equipment, new EquipmentVisualsUpdatedEvent(equipee, slot, revealedLayers));
RaiseLocalEvent(equipment, new EquipmentVisualsUpdatedEvent(equipee, slot, revealedLayers), true);
}
}

View File

@@ -429,7 +429,7 @@ namespace Content.Client.DragDrop
// CanInteract() doesn't support checking a second "target" entity.
// Doing so manually:
var ev = new GettingInteractedWithAttemptEvent(eventArgs.User, eventArgs.Dragged);
RaiseLocalEvent(eventArgs.Dragged, ev);
RaiseLocalEvent(eventArgs.Dragged, ev, true);
if (ev.Cancelled)
return false;

View File

@@ -0,0 +1,58 @@
using Content.Shared.Drugs;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Player;
namespace Content.Client.Drugs;
/// <summary>
/// System to handle drug related overlays.
/// </summary>
public sealed class DrugOverlaySystem : EntitySystem
{
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IOverlayManager _overlayMan = default!;
private RainbowOverlay _overlay = default!;
public static string RainbowKey = "SeeingRainbows";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SeeingRainbowsComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<SeeingRainbowsComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<SeeingRainbowsComponent, PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<SeeingRainbowsComponent, PlayerDetachedEvent>(OnPlayerDetached);
_overlay = new();
}
private void OnPlayerAttached(EntityUid uid, SeeingRainbowsComponent component, PlayerAttachedEvent args)
{
_overlayMan.AddOverlay(_overlay);
}
private void OnPlayerDetached(EntityUid uid, SeeingRainbowsComponent component, PlayerDetachedEvent args)
{
_overlay.Intoxication = 0;
_overlayMan.RemoveOverlay(_overlay);
}
private void OnInit(EntityUid uid, SeeingRainbowsComponent component, ComponentInit args)
{
if (_player.LocalPlayer?.ControlledEntity == uid)
_overlayMan.AddOverlay(_overlay);
}
private void OnShutdown(EntityUid uid, SeeingRainbowsComponent component, ComponentShutdown args)
{
if (_player.LocalPlayer?.ControlledEntity == uid)
{
_overlay.Intoxication = 0;
_overlayMan.RemoveOverlay(_overlay);
}
}
}

View File

@@ -0,0 +1,70 @@
using Content.Shared.Drugs;
using Content.Shared.StatusEffect;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Client.Drugs;
public sealed class RainbowOverlay : Overlay
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public override bool RequestScreenTexture => true;
private readonly ShaderInstance _rainbowShader;
public float Intoxication = 0.0f;
private const float VisualThreshold = 10.0f;
private const float PowerDivisor = 250.0f;
private float EffectScale => Math.Clamp((Intoxication - VisualThreshold) / PowerDivisor, 0.0f, 1.0f);
public RainbowOverlay()
{
IoCManager.InjectDependencies(this);
_rainbowShader = _prototypeManager.Index<ShaderPrototype>("Rainbow").InstanceUnique();
}
protected override void FrameUpdate(FrameEventArgs args)
{
var playerEntity = _playerManager.LocalPlayer?.ControlledEntity;
if (playerEntity == null)
return;
if (!_entityManager.HasComponent<SeeingRainbowsComponent>(playerEntity)
|| !_entityManager.TryGetComponent<StatusEffectsComponent>(playerEntity, out var status))
return;
var statusSys = _sysMan.GetEntitySystem<StatusEffectsSystem>();
if (!statusSys.TryGetTime(playerEntity.Value, DrugOverlaySystem.RainbowKey, out var time, status))
return;
var timeLeft = (float) (time.Value.Item2 - time.Value.Item1).TotalSeconds;
Intoxication += (timeLeft - Intoxication) * args.DeltaSeconds / 16f;
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
return EffectScale > 0;
}
protected override void Draw(in OverlayDrawArgs args)
{
if (ScreenTexture == null)
return;
var handle = args.WorldHandle;
_rainbowShader.SetParameter("SCREEN_TEXTURE", ScreenTexture);
_rainbowShader.SetParameter("effectScale", EffectScale);
handle.UseShader(_rainbowShader);
handle.DrawRect(args.WorldBounds, Color.White);
}
}

View File

@@ -0,0 +1,81 @@
using Content.Shared.Drunk;
using Content.Shared.StatusEffect;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Client.Drunk;
public sealed class DrunkOverlay : Overlay
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public override bool RequestScreenTexture => true;
private readonly ShaderInstance _drunkShader;
public float CurrentBoozePower = 0.0f;
private const float VisualThreshold = 10.0f;
private const float PowerDivisor = 250.0f;
private float _visualScale = 0;
public DrunkOverlay()
{
IoCManager.InjectDependencies(this);
_drunkShader = _prototypeManager.Index<ShaderPrototype>("Drunk").InstanceUnique();
}
protected override void FrameUpdate(FrameEventArgs args)
{
var playerEntity = _playerManager.LocalPlayer?.ControlledEntity;
if (playerEntity == null)
return;
if (!_entityManager.HasComponent<DrunkComponent>(playerEntity)
|| !_entityManager.TryGetComponent<StatusEffectsComponent>(playerEntity, out var status))
return;
var statusSys = _sysMan.GetEntitySystem<StatusEffectsSystem>();
if (!statusSys.TryGetTime(playerEntity.Value, SharedDrunkSystem.DrunkKey, out var time, status))
return;
var timeLeft = (float) (time.Value.Item2 - time.Value.Item1).TotalSeconds;
CurrentBoozePower += (timeLeft - CurrentBoozePower) * args.DeltaSeconds / 16f;
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
_visualScale = BoozePowerToVisual(CurrentBoozePower);
return _visualScale > 0;
}
protected override void Draw(in OverlayDrawArgs args)
{
if (ScreenTexture == null)
return;
var handle = args.WorldHandle;
_drunkShader.SetParameter("SCREEN_TEXTURE", ScreenTexture);
_drunkShader.SetParameter("boozePower", _visualScale);
handle.UseShader(_drunkShader);
handle.DrawRect(args.WorldBounds, Color.White);
}
/// <summary>
/// Converts the # of seconds the drunk effect lasts for (booze power) to a percentage
/// used by the actual shader.
/// </summary>
/// <param name="boozePower"></param>
private float BoozePowerToVisual(float boozePower)
{
return Math.Clamp((boozePower - VisualThreshold) / PowerDivisor, 0.0f, 1.0f);
}
}

View File

@@ -0,0 +1,53 @@
using Content.Shared.Drunk;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Player;
namespace Content.Client.Drunk;
public sealed class DrunkSystem : SharedDrunkSystem
{
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IOverlayManager _overlayMan = default!;
private DrunkOverlay _overlay = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DrunkComponent, ComponentInit>(OnDrunkInit);
SubscribeLocalEvent<DrunkComponent, ComponentShutdown>(OnDrunkShutdown);
SubscribeLocalEvent<DrunkComponent, PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<DrunkComponent, PlayerDetachedEvent>(OnPlayerDetached);
_overlay = new();
}
private void OnPlayerAttached(EntityUid uid, DrunkComponent component, PlayerAttachedEvent args)
{
_overlayMan.AddOverlay(_overlay);
}
private void OnPlayerDetached(EntityUid uid, DrunkComponent component, PlayerDetachedEvent args)
{
_overlay.CurrentBoozePower = 0;
_overlayMan.RemoveOverlay(_overlay);
}
private void OnDrunkInit(EntityUid uid, DrunkComponent component, ComponentInit args)
{
if (_player.LocalPlayer?.ControlledEntity == uid)
_overlayMan.AddOverlay(_overlay);
}
private void OnDrunkShutdown(EntityUid uid, DrunkComponent component, ComponentShutdown args)
{
if (_player.LocalPlayer?.ControlledEntity == uid)
{
_overlay.CurrentBoozePower = 0;
_overlayMan.RemoveOverlay(_overlay);
}
}
}

View File

@@ -74,7 +74,6 @@ namespace Content.Client.Entry
factory.RegisterClass<SharedLatheComponent>();
factory.RegisterClass<SharedSpawnPointComponent>();
factory.RegisterClass<SharedVendingMachineComponent>();
factory.RegisterClass<SharedCargoConsoleComponent>();
factory.RegisterClass<SharedReagentDispenserComponent>();
factory.RegisterClass<SharedChemMasterComponent>();
factory.RegisterClass<SharedGravityGeneratorComponent>();

View File

@@ -105,7 +105,7 @@ public sealed class EyeLerpingSystem : EntitySystem
if (!TryComp(uid, out TransformComponent? transform)
|| !TryComp(uid, out EyeComponent? eye)
|| eye.Eye == null
|| !_mapManager.TryGetGrid(transform.GridEntityId, out var grid))
|| !_mapManager.TryGetGrid(transform.GridUid, out var grid))
{
_toRemove.Add(uid);
return;

View File

@@ -18,6 +18,8 @@ namespace Content.Client.GameTicking.Managers
public sealed class ClientGameTicker : SharedGameTicker
{
[Dependency] private readonly IStateManager _stateManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[ViewVariables] private bool _initialized;
private Dictionary<EntityUid, Dictionary<string, uint?>> _jobsAvailable = new();
private Dictionary<EntityUid, string> _stationNames = new();
@@ -135,7 +137,7 @@ namespace Content.Client.GameTicking.Managers
RestartSound = message.RestartSound;
//This is not ideal at all, but I don't see an immediately better fit anywhere else.
var roundEnd = new RoundEndSummaryWindow(message.GamemodeTitle, message.RoundEndText, message.RoundDuration, message.RoundId, message.AllPlayersEndInfo);
var roundEnd = new RoundEndSummaryWindow(message.GamemodeTitle, message.RoundEndText, message.RoundDuration, message.RoundId, message.AllPlayersEndInfo, _entityManager);
}
private void RoundRestartCleanup(RoundRestartCleanupEvent ev)

View File

@@ -208,7 +208,7 @@ namespace Content.Client.Hands
if (hand.HeldEntity == null)
{
// the held item was removed.
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers));
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers), true);
return;
}
@@ -217,7 +217,7 @@ namespace Content.Client.Hands
if (ev.Layers.Count == 0)
{
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers));
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers), true);
return;
}
@@ -244,7 +244,7 @@ namespace Content.Client.Hands
sprite.LayerSetData(index, layerData);
}
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers));
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers), true);
}
private void OnVisualsChanged(EntityUid uid, HandsComponent component, VisualsChangedEvent args)

View File

@@ -13,7 +13,7 @@ namespace Content.Client.IconSmoothing
[RegisterComponent]
public sealed class IconSmoothComponent : Component
{
public (EntityUid, Vector2i)? LastPosition;
public (EntityUid?, Vector2i)? LastPosition;
/// <summary>
/// We will smooth with other objects with the same key.

View File

@@ -33,9 +33,9 @@ namespace Content.Client.IconSmoothing
var xform = Transform(uid);
if (xform.Anchored)
{
component.LastPosition = _mapManager.TryGetGrid(xform.GridEntityId, out var grid)
? (xform.GridEntityId, grid.TileIndicesFor(xform.Coordinates))
: (EntityUid.Invalid, new Vector2i(0, 0));
component.LastPosition = _mapManager.TryGetGrid(xform.GridUid, out var grid)
? (xform.GridUid.Value, grid.TileIndicesFor(xform.Coordinates))
: (null, new Vector2i(0, 0));
DirtyNeighbours(uid, component);
}
@@ -111,7 +111,7 @@ namespace Content.Client.IconSmoothing
Vector2i pos;
if (transform.Anchored && _mapManager.TryGetGrid(transform.GridEntityId, out var grid))
if (transform.Anchored && _mapManager.TryGetGrid(transform.GridUid, out var grid))
{
pos = grid.CoordinatesToTile(transform.Coordinates);
}
@@ -191,9 +191,9 @@ namespace Content.Client.IconSmoothing
if (xform.Anchored)
{
if (!_mapManager.TryGetGrid(xform.GridEntityId, out grid))
if (!_mapManager.TryGetGrid(xform.GridUid, out grid))
{
Logger.Error($"Failed to calculate IconSmoothComponent sprite in {uid} because grid {xform.GridEntityId} was missing.");
Logger.Error($"Failed to calculate IconSmoothComponent sprite in {uid} because grid {xform.GridUid} was missing.");
return;
}
}

View File

@@ -33,7 +33,7 @@ public sealed class ItemSystem : SharedItemSystem
// if the item is in a container, it might be equipped to hands or inventory slots --> update visuals.
if (_containerSystem.TryGetContainingContainer(uid, out var container))
RaiseLocalEvent(container.Owner, new VisualsChangedEvent(uid, container.ID));
RaiseLocalEvent(container.Owner, new VisualsChangedEvent(uid, container.ID), true);
}
/// <summary>

View File

@@ -194,7 +194,7 @@ namespace Content.Client.Items.UI
}
var collectMsg = new ItemStatusCollectMessage();
_entityManager.EventBus.RaiseLocalEvent(_entity!.Value, collectMsg);
_entityManager.EventBus.RaiseLocalEvent(_entity!.Value, collectMsg, true);
foreach (var control in collectMsg.Controls)
{

View File

@@ -1,30 +0,0 @@
using Content.Shared.Kudzu;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Client.Kudzu;
public sealed class KudzuVisualizer : AppearanceVisualizer
{
[DataField("layer")]
private int Layer { get; } = 0;
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var entities = IoCManager.Resolve<IEntityManager>();
if (!entities.TryGetComponent(component.Owner, out SpriteComponent? sprite))
{
return;
}
if (component.TryGetData(KudzuVisuals.Variant, out int var) && component.TryGetData(KudzuVisuals.GrowthLevel, out int level))
{
sprite.LayerMapReserveBlank(Layer);
sprite.LayerSetState(0, $"kudzu_{level}{var}");
}
}
}

View File

@@ -0,0 +1,10 @@
namespace Content.Client.Kudzu
{
[RegisterComponent]
public sealed class KudzuVisualsComponent : Component
{
[DataField("layer")]
public int Layer { get; } = 0;
}
}

View File

@@ -0,0 +1,22 @@
using Content.Shared.Kudzu;
using Robust.Client.GameObjects;
namespace Content.Client.Kudzu
{
public sealed class KudzuVisualsSystem : VisualizerSystem<KudzuVisualsComponent>
{
protected override void OnAppearanceChange(EntityUid uid, KudzuVisualsComponent component, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;
if (args.Component.TryGetData(KudzuVisuals.Variant, out int var)
&& args.Component.TryGetData(KudzuVisuals.GrowthLevel, out int level))
{
var index = args.Sprite.LayerMapReserveBlank(component.Layer);
args.Sprite.LayerSetState(index, $"kudzu_{level}{var}");
}
}
}
}

View File

@@ -76,9 +76,10 @@ namespace Content.Client.NodeContainer
var node = _system.NodeLookup[(groupId, nodeId)];
var gridId = _entityManager.GetComponent<TransformComponent>(node.Entity).GridEntityId;
var grid = _mapManager.GetGrid(gridId);
var gridTile = grid.TileIndicesFor(_entityManager.GetComponent<TransformComponent>(node.Entity).Coordinates);
var xform = _entityManager.GetComponent<TransformComponent>(node.Entity);
if (!_mapManager.TryGetGrid(xform.GridUid, out var grid))
return;
var gridTile = grid.TileIndicesFor(xform.Coordinates);
var sb = new StringBuilder();
sb.Append($"entity: {node.Entity}\n");

View File

@@ -30,6 +30,12 @@ public sealed class OrbitVisualsSystem : EntitySystem
component.OrbitLength = _robustRandom.NextFloat(0.5f * component.OrbitLength, 1.5f * component.OrbitLength);
if (TryComp<SpriteComponent>(uid, out var sprite))
{
sprite.EnableDirectionOverride = true;
sprite.DirectionOverride = Direction.South;
}
var animationPlayer = EntityManager.EnsureComponent<AnimationPlayerComponent>(uid);
if (animationPlayer.HasRunningAnimation(_orbitAnimationKey))
return;
@@ -44,9 +50,11 @@ public sealed class OrbitVisualsSystem : EntitySystem
private void OnComponentRemove(EntityUid uid, OrbitVisualsComponent component, ComponentRemove args)
{
if (!TryComp<ISpriteComponent>(uid, out var sprite))
if (!TryComp<SpriteComponent>(uid, out var sprite))
return;
sprite.EnableDirectionOverride = false;
var animationPlayer = EntityManager.EnsureComponent<AnimationPlayerComponent>(uid);
if (animationPlayer.HasRunningAnimation(_orbitAnimationKey))
{

View File

@@ -27,7 +27,7 @@ namespace Content.Client.Physics.Controllers
return;
}
if (xform.GridEntityId != EntityUid.Invalid)
if (xform.GridUid != null)
mover.LastGridAngle = GetParentGridAngle(xform, mover);
// Essentially we only want to set our mob to predicted so every other entity we just interpolate
@@ -66,7 +66,7 @@ namespace Content.Client.Physics.Controllers
// Server-side should just be handled on its own so we'll just do this shizznit
if (TryComp(player, out IMobMoverComponent? mobMover))
{
HandleMobMovement(mover, body, mobMover, xform);
HandleMobMovement(mover, body, mobMover, xform, frameTime);
return;
}

View File

@@ -1,6 +1,7 @@
using System.Linq;
using Content.Client.Message;
using Content.Shared.GameTicking;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Utility;
@@ -10,10 +11,13 @@ namespace Content.Client.RoundEnd
{
public sealed class RoundEndSummaryWindow : DefaultWindow
{
private readonly IEntityManager _entityManager;
public RoundEndSummaryWindow(string gm, string roundEnd, TimeSpan roundTimeSpan, int roundId,
RoundEndMessageEvent.RoundEndPlayerInfo[] info)
RoundEndMessageEvent.RoundEndPlayerInfo[] info, IEntityManager entityManager)
{
_entityManager = entityManager;
MinSize = SetSize = (520, 580);
Title = Loc.GetString("round-end-summary-window-title");
@@ -105,7 +109,27 @@ namespace Content.Client.RoundEnd
//Create labels for each player info.
foreach (var playerInfo in sortedPlayersInfo)
{
var playerInfoText = new RichTextLabel();
var hBox = new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
};
var playerInfoText = new RichTextLabel
{
VerticalAlignment = VAlignment.Center,
VerticalExpand = true,
};
if (_entityManager.TryGetComponent(playerInfo.PlayerEntityUid, out ISpriteComponent? sprite))
{
hBox.AddChild(new SpriteView
{
Sprite = sprite,
OverrideDirection = Direction.South,
VerticalAlignment = VAlignment.Center,
VerticalExpand = true,
});
}
if (playerInfo.PlayerICName != null)
{
@@ -129,7 +153,8 @@ namespace Content.Client.RoundEnd
("playerRole", Loc.GetString(playerInfo.Role))));
}
}
playerInfoContainer.AddChild(playerInfoText);
hBox.AddChild(playerInfoText);
playerInfoContainer.AddChild(hBox);
}
playerInfoContainerScrollbox.AddChild(playerInfoContainer);

View File

@@ -24,6 +24,7 @@ public sealed class RadarConsoleBoundUserInterface : BoundUserInterface
base.UpdateState(state);
if (state is not RadarConsoleBoundInterfaceState cState) return;
_window?.SetMatrix(cState.Coordinates, cState.Angle);
_window?.UpdateState(cState);
}
}

View File

@@ -65,6 +65,8 @@ public sealed class ShuttleConsoleBoundUserInterface : BoundUserInterface
{
base.UpdateState(state);
if (state is not ShuttleConsoleBoundInterfaceState cState) return;
_window?.SetMatrix(cState.Coordinates, cState.Angle);
_window?.UpdateState(cState);
}
}

View File

@@ -2,20 +2,4 @@ using Content.Shared.Shuttles.Events;
namespace Content.Client.Shuttles.Systems;
public sealed class DockingSystem : EntitySystem
{
public void StartAutodock(EntityUid uid)
{
RaiseNetworkEvent(new AutodockRequestMessage {Entity = uid});
}
public void StopAutodock(EntityUid uid)
{
RaiseNetworkEvent(new StopAutodockRequestMessage() {Entity = uid});
}
public void Undock(EntityUid uid)
{
RaiseNetworkEvent(new UndockRequestMessage() {Entity = uid});
}
}
public sealed class DockingSystem : EntitySystem {}

View File

@@ -31,6 +31,9 @@ public class DockingControl : Control
public EntityUid? ViewedDock;
public EntityUid? GridEntity;
public EntityCoordinates? Coordinates;
public Angle? Angle;
/// <summary>
/// Stored by GridID then by docks
/// </summary>
@@ -69,11 +72,12 @@ public class DockingControl : Control
handle.DrawLine((MidPoint, MidPoint) - aExtent, (MidPoint, MidPoint) + aExtent, gridLines);
}
if (!_entManager.TryGetComponent<TransformComponent>(ViewedDock, out var xform) ||
if (Coordinates == null ||
Angle == null ||
!_entManager.TryGetComponent<TransformComponent>(GridEntity, out var gridXform)) return;
var rotation = Matrix3.CreateRotation(xform.LocalRotation);
var matrix = Matrix3.CreateTranslation(-xform.LocalPosition);
var rotation = Matrix3.CreateRotation(Angle.Value);
var matrix = Matrix3.CreateTranslation(-Coordinates.Value.Position);
// Draw the fixtures around the dock before drawing it
if (_entManager.TryGetComponent<FixturesComponent>(GridEntity, out var fixtures))
@@ -128,7 +132,7 @@ public class DockingControl : Control
ScalePosition(rotation.Transform(new Vector2(0.5f, -0.5f)))), Color.Green);
// Draw nearby grids
var worldPos = gridXform.WorldMatrix.Transform(xform.LocalPosition);
var worldPos = gridXform.WorldMatrix.Transform(Coordinates.Value.Position);
var gridInvMatrix = gridXform.InvWorldMatrix;
Matrix3.Multiply(in gridInvMatrix, in matrix, out var invMatrix);

View File

@@ -3,6 +3,7 @@ using Content.Client.UserInterface;
using Content.Shared.Shuttles.BUIStates;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Map;
namespace Content.Client.Shuttles.UI;
@@ -19,4 +20,9 @@ public sealed partial class RadarConsoleWindow : FancyWindow,
{
RadarScreen.UpdateState(scc);
}
public void SetMatrix(EntityCoordinates? coordinates, Angle? angle)
{
RadarScreen.SetMatrix(coordinates, angle);
}
}

View File

@@ -25,9 +25,11 @@ public sealed class RadarControl : Control
private const float GridLinesDistance = 32f;
/// <summary>
/// Entity used to transform all of the radar objects.
/// Used to transform all of the radar objects. Typically is a shuttle console parented to a grid.
/// </summary>
private EntityUid? _entity;
private EntityCoordinates? _coordinates;
private Angle? _rotation;
private float _radarMinRange = 64f;
private float _radarMaxRange = 256f;
@@ -61,6 +63,12 @@ public sealed class RadarControl : Control
MinSize = (SizeFull, SizeFull);
}
public void SetMatrix(EntityCoordinates? coordinates, Angle? angle)
{
_coordinates = coordinates;
_rotation = angle;
}
public void UpdateState(RadarConsoleBoundInterfaceState ls)
{
_radarMaxRange = ls.MaxRange;
@@ -74,7 +82,6 @@ public sealed class RadarControl : Control
if (_radarMaxRange < _radarMinRange)
_radarMinRange = _radarMaxRange;
_entity = ls.Entity;
_docks.Clear();
foreach (var state in ls.Docks)
@@ -109,7 +116,7 @@ public sealed class RadarControl : Control
handle.DrawCircle((MidPoint, MidPoint), ScaledMinimapRadius, Color.Black);
// No data
if (_entity == null)
if (_coordinates == null || _rotation == null)
{
Clear();
return;
@@ -135,8 +142,8 @@ public sealed class RadarControl : Control
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
var fixturesQuery = _entManager.GetEntityQuery<FixturesComponent>();
var bodyQuery = _entManager.GetEntityQuery<PhysicsComponent>();
var xform = xformQuery.GetComponent(_entity.Value);
var mapPosition = xform.MapPosition;
var mapPosition = _coordinates.Value.ToMap(_entManager);
if (mapPosition.MapId == MapId.Nullspace)
{
@@ -144,28 +151,30 @@ public sealed class RadarControl : Control
return;
}
// Can also use ourGridBody.LocalCenter
var offset = xform.Coordinates.Position;
var offsetMatrix = Matrix3.CreateTranslation(-offset);
var offset = _coordinates.Value.Position;
Matrix3 matrix;
// Draw our grid in detail
var ourGridId = xform.GridID;
if (ourGridId != GridId.Invalid)
var ourGridId = _coordinates.Value.GetGridUid(_entManager);
if (ourGridId != null)
{
matrix = xform.InvWorldMatrix;
var ourGridFixtures = fixturesQuery.GetComponent(ourGridId);
var offsetMatrix = Matrix3.CreateInverseTransform(offset.X, offset.Y, (float) _rotation.Value.Theta);
var ourGridFixtures = fixturesQuery.GetComponent(ourGridId.Value);
// Draw our grid; use non-filled boxes so it doesn't look awful.
DrawGrid(handle, offsetMatrix, ourGridFixtures, Color.Yellow);
DrawDocks(handle, xform.GridEntityId, offsetMatrix);
DrawDocks(handle, ourGridId.Value, offsetMatrix);
var ourGridMatrix = xformQuery.GetComponent(ourGridId.Value).InvWorldMatrix;
Matrix3.Multiply(in ourGridMatrix, in offsetMatrix, out matrix);
}
else
{
matrix = Matrix3.CreateTranslation(-offset);
}
var invertedPosition = xform.Coordinates.Position - offset;
var invertedPosition = _coordinates.Value.Position - offset;
invertedPosition.Y = -invertedPosition.Y;
// Don't need to transform the InvWorldMatrix again as it's already offset to its position.

View File

@@ -7,6 +7,7 @@ using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Map;
using Robust.Shared.Utility;
namespace Content.Client.Shuttles.UI;
@@ -17,10 +18,7 @@ public sealed partial class ShuttleConsoleWindow : FancyWindow,
{
private readonly IEntityManager _entManager;
/// <summary>
/// EntityUid of the open console.
/// </summary>
private EntityUid? _entity;
private EntityUid? _shuttleUid;
/// <summary>
/// Currently selected dock button for camera.
@@ -84,9 +82,14 @@ public sealed partial class ShuttleConsoleWindow : FancyWindow,
UndockPressed?.Invoke(DockingScreen.ViewedDock.Value);
}
public void SetMatrix(EntityCoordinates? coordinates, Angle? angle)
{
_shuttleUid = coordinates?.EntityId;
RadarScreen.SetMatrix(coordinates, angle);
}
public void UpdateState(ShuttleConsoleBoundInterfaceState scc)
{
_entity = scc.Entity;
UpdateDocks(scc.Docks);
RadarScreen.UpdateState(scc);
MaxRadarRange.Text = $"{scc.MaxRange:0}";
@@ -110,20 +113,16 @@ public sealed partial class ShuttleConsoleWindow : FancyWindow,
DockPorts.DisposeAllChildren();
DockingScreen.Docks = _docks;
if (!_entManager.TryGetComponent<TransformComponent>(_entity, out var xform))
{
// TODO: Show Placeholder
return;
}
if (_docks.TryGetValue(xform.GridEntityId, out var gridDocks))
// TODO: Show Placeholder
if (_shuttleUid != null && _docks.TryGetValue(_shuttleUid.Value, out var gridDocks))
{
var index = 1;
foreach (var state in gridDocks)
{
var ent = state.Entity;
var pressed = ent == DockingScreen.ViewedDock;
var pressed = state.Entity == DockingScreen.ViewedDock;
string suffix;
if (state.Connected)
@@ -142,21 +141,26 @@ public sealed partial class ShuttleConsoleWindow : FancyWindow,
Pressed = pressed,
};
button.OnMouseEntered += args => OnDockMouseEntered(args, ent);
button.OnMouseExited += args => OnDockMouseExited(args, ent);
button.OnToggled += args => OnDockToggled(args, ent);
if (pressed)
{
_selectedDock = button;
}
button.OnMouseEntered += args => OnDockMouseEntered(args, state);
button.OnMouseExited += args => OnDockMouseExited(args, state);
button.OnToggled += args => OnDockToggled(args, state);
DockPorts.AddChild(button);
index++;
}
}
}
private void OnDockMouseEntered(GUIMouseHoverEventArgs obj, EntityUid uid)
private void OnDockMouseEntered(GUIMouseHoverEventArgs obj, DockingInterfaceState state)
{
RadarScreen.HighlightedDock = uid;
RadarScreen.HighlightedDock = state.Entity;
}
private void OnDockMouseExited(GUIMouseHoverEventArgs obj, EntityUid uid)
private void OnDockMouseExited(GUIMouseHoverEventArgs obj, DockingInterfaceState state)
{
RadarScreen.HighlightedDock = null;
}
@@ -164,10 +168,18 @@ public sealed partial class ShuttleConsoleWindow : FancyWindow,
/// <summary>
/// Shows a docking camera instead of radar screen.
/// </summary>
private void OnDockToggled(BaseButton.ButtonEventArgs obj, EntityUid ent)
private void OnDockToggled(BaseButton.ButtonEventArgs obj, DockingInterfaceState state)
{
var ent = state.Entity;
if (_selectedDock != null)
{
// If it got untoggled via other means then we'll stop viewing the old dock.
if (DockingScreen.ViewedDock != null && DockingScreen.ViewedDock != state.Entity)
{
StopAutodockPressed?.Invoke(DockingScreen.ViewedDock.Value);
}
_selectedDock.Pressed = false;
_selectedDock = null;
}
@@ -186,15 +198,23 @@ public sealed partial class ShuttleConsoleWindow : FancyWindow,
}
else
{
// DebugTools.Assert(DockingScreen.ViewedDock == null);
_entManager.TryGetComponent<TransformComponent>(_entity, out var xform);
if (_shuttleUid != null)
{
DockingScreen.Coordinates = state.Coordinates;
DockingScreen.Angle = state.Angle;
}
else
{
DockingScreen.Coordinates = null;
DockingScreen.Angle = null;
}
UndockButton.Disabled = false;
RadarScreen.Visible = false;
DockingScreen.Visible = true;
DockingScreen.ViewedDock = ent;
StartAutodockPressed?.Invoke(ent);
DockingScreen.GridEntity = xform?.GridEntityId;
DockingScreen.GridEntity = _shuttleUid;
_selectedDock = obj.Button;
}
}
@@ -214,9 +234,8 @@ public sealed partial class ShuttleConsoleWindow : FancyWindow,
{
base.Draw(handle);
if (!_entManager.TryGetComponent<TransformComponent>(_entity, out var entXform) ||
!_entManager.TryGetComponent<PhysicsComponent>(entXform.GridEntityId, out var gridBody) ||
!_entManager.TryGetComponent<TransformComponent>(entXform.GridEntityId, out var gridXform))
if (!_entManager.TryGetComponent<PhysicsComponent>(_shuttleUid, out var gridBody) ||
!_entManager.TryGetComponent<TransformComponent>(_shuttleUid, out var gridXform))
{
return;
}

View File

@@ -27,6 +27,46 @@ namespace Content.Client.Singularity
{
IoCManager.InjectDependencies(this);
_shader = _prototypeManager.Index<ShaderPrototype>("Singularity").Instance().Duplicate();
_shader.SetParameter("maxDistance", MaxDistance * EyeManager.PixelsPerMeter);
}
private Vector2[] _positions = new Vector2[MaxCount];
private float[] _intensities = new float[MaxCount];
private float[] _falloffPowers = new float[MaxCount];
private int _count = 0;
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
if (args.Viewport.Eye == null)
return false;
_count = 0;
foreach (var (distortion, xform) in _entMan.EntityQuery<SingularityDistortionComponent, TransformComponent>())
{
if (xform.MapID != args.MapId)
continue;
var mapPos = xform.WorldPosition;
// is the distortion in range?
if ((mapPos - args.WorldAABB.ClosestPoint(mapPos)).LengthSquared > MaxDistance * MaxDistance)
continue;
// To be clear, this needs to use "inside-viewport" pixels.
// In other words, specifically NOT IViewportControl.WorldToScreen (which uses outer coordinates).
var tempCoords = args.Viewport.WorldToLocal(mapPos);
tempCoords.Y = args.Viewport.Size.Y - tempCoords.Y;
_positions[_count] = tempCoords;
_intensities[_count] = distortion.Intensity;
_falloffPowers[_count] = distortion.FalloffPower;
_count++;
if (_count == MaxCount)
break;
}
return (_count > 0);
}
protected override void Draw(in OverlayDrawArgs args)
@@ -34,48 +74,11 @@ namespace Content.Client.Singularity
if (ScreenTexture == null || args.Viewport.Eye == null)
return;
// Has to be correctly handled because of the way intensity/falloff transform works so just do it.
_shader?.SetParameter("renderScale", args.Viewport.RenderScale);
var position = new Vector2[MaxCount];
var intensity = new float[MaxCount];
var falloffPower = new float[MaxCount];
int count = 0;
var mapId = args.Viewport.Eye.Position.MapId;
foreach (var distortion in _entMan.EntityQuery<SingularityDistortionComponent>())
{
var mapPos = _entMan.GetComponent<TransformComponent>(distortion.Owner).MapPosition;
if (mapPos.MapId != mapId)
continue;
// is the distortion in range?
if ((mapPos.Position - args.WorldAABB.ClosestPoint(mapPos.Position)).LengthSquared > MaxDistance * MaxDistance)
continue;
// To be clear, this needs to use "inside-viewport" pixels.
// In other words, specifically NOT IViewportControl.WorldToScreen (which uses outer coordinates).
var tempCoords = args.Viewport.WorldToLocal(mapPos.Position);
tempCoords.Y = args.Viewport.Size.Y - tempCoords.Y;
position[count] = tempCoords;
intensity[count] = distortion.Intensity;
falloffPower[count] = distortion.FalloffPower;
count++;
if (count == MaxCount)
break;
}
if (count == 0)
return;
_shader?.SetParameter("count", count);
_shader?.SetParameter("position", position);
_shader?.SetParameter("intensity", intensity);
_shader?.SetParameter("falloffPower", falloffPower);
_shader?.SetParameter("maxDistance", MaxDistance * EyeManager.PixelsPerMeter);
_shader?.SetParameter("count", _count);
_shader?.SetParameter("position", _positions);
_shader?.SetParameter("intensity", _intensities);
_shader?.SetParameter("falloffPower", _falloffPowers);
_shader?.SetParameter("SCREEN_TEXTURE", ScreenTexture);
var worldHandle = args.WorldHandle;

View File

@@ -0,0 +1,7 @@
using Content.Shared.Speech.EntitySystems;
namespace Content.Client.Speech.EntitySystems;
public sealed class SlurredSystem : SharedSlurredSystem
{
}

View File

@@ -34,13 +34,14 @@ namespace Content.Client.StationEvents
_baseShader = _prototypeManager.Index<ShaderPrototype>("Radiation").Instance().Duplicate();
}
protected override void Draw(in OverlayDrawArgs args)
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
RadiationQuery(args.Viewport.Eye);
return _pulses.Count > 0;
}
if (_pulses.Count == 0)
return;
protected override void Draw(in OverlayDrawArgs args)
{
if (ScreenTexture == null)
return;

View File

@@ -238,7 +238,7 @@ namespace Content.Client.Verbs
if (verb.ExecutionEventArgs != null)
{
if (verb.EventTarget.IsValid())
RaiseLocalEvent(verb.EventTarget, verb.ExecutionEventArgs);
RaiseLocalEvent(verb.EventTarget, verb.ExecutionEventArgs, true);
else
RaiseLocalEvent(verb.ExecutionEventArgs);
}

View File

@@ -1,440 +0,0 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Content.Client.Entry;
using Content.Client.IoC;
using Content.Client.Parallax.Managers;
using Content.Server.GameTicking;
using Content.Server.IoC;
using Content.Shared.CCVar;
using NUnit.Framework;
using Robust.Client;
using Robust.Server;
using Robust.Server.Player;
using Robust.Shared.ContentPack;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.UnitTesting;
namespace Content.IntegrationTests
{
[Parallelizable(ParallelScope.All)]
public abstract class ContentIntegrationTest : RobustIntegrationTest
{
private static readonly (string cvar, string value, bool)[] ServerTestCvars = {
// Avoid funny race conditions with the database.
(CCVars.DatabaseSynchronous.Name, "true", false),
// No artificial database delay, as it can make tests fail.
(CCVars.DatabaseSqliteDelay.Name, "0", false),
// Disable holidays as some of them might mess with the map at round start.
(CCVars.HolidaysEnabled.Name, "false", false),
// Avoid loading a large map by default for integration tests if none has been specified.
(CCVars.GameMap.Name, "empty", true),
// Makes sure IGameMapManager actually listens.
(CCVars.GameMapForced.Name, "true", true),
};
private static void SetServerTestCvars(IntegrationOptions options)
{
foreach (var (cvar, value, tryAdd) in ServerTestCvars)
{
if (tryAdd)
{
options.CVarOverrides.TryAdd(cvar, value);
}
else
{
options.CVarOverrides[cvar] = value;
}
}
}
protected sealed override ClientIntegrationInstance StartClient(ClientIntegrationOptions options = null)
{
options ??= new ClientContentIntegrationOption()
{
FailureLogLevel = LogLevel.Warning
};
options.Pool = ShouldPool(options, false);
// Load content resources, but not config and user data.
options.Options = new GameControllerOptions()
{
LoadContentResources = true,
LoadConfigAndUserData = false,
};
options.ContentStart = true;
options.ContentAssemblies = new[]
{
typeof(Shared.Entry.EntryPoint).Assembly,
typeof(EntryPoint).Assembly,
typeof(ContentIntegrationTest).Assembly
};
options.BeforeStart += () =>
{
IoCManager.Resolve<IModLoader>().SetModuleBaseCallbacks(new ClientModuleTestingCallbacks
{
ClientBeforeIoC = () =>
{
if (options is ClientContentIntegrationOption contentOptions)
{
contentOptions.ContentBeforeIoC?.Invoke();
}
IoCManager.Register<IParallaxManager, DummyParallaxManager>(true);
IoCManager.Resolve<ILogManager>().GetSawmill("loc").Level = LogLevel.Error;
}
});
};
return base.StartClient(options);
}
protected override ServerIntegrationInstance StartServer(ServerIntegrationOptions options = null)
{
options ??= new ServerContentIntegrationOption
{
FailureLogLevel = LogLevel.Warning,
};
SetServerTestCvars(options);
options.Pool = ShouldPool(options, true);
// Load content resources, but not config and user data.
options.Options = new ServerOptions()
{
LoadConfigAndUserData = false,
LoadContentResources = true,
};
options.ContentStart = true;
options.ContentAssemblies = new[]
{
typeof(Shared.Entry.EntryPoint).Assembly,
typeof(Server.Entry.EntryPoint).Assembly,
typeof(ContentIntegrationTest).Assembly
};
options.BeforeStart += () =>
{
IoCManager.Resolve<IModLoader>().SetModuleBaseCallbacks(new ServerModuleTestingCallbacks
{
ServerBeforeIoC = () =>
{
if (options is ServerContentIntegrationOption contentOptions)
{
contentOptions.ContentBeforeIoC?.Invoke();
}
}
});
IoCManager.Resolve<ILogManager>().GetSawmill("loc").Level = LogLevel.Error;
};
return base.StartServer(options);
}
protected ServerIntegrationInstance StartServerDummyTicker(ServerIntegrationOptions options = null)
{
options ??= new ServerContentIntegrationOption();
// Load content resources, but not config and user data.
options.Options = new ServerOptions()
{
LoadConfigAndUserData = false,
LoadContentResources = true,
};
// Dummy game ticker.
options.CVarOverrides[CCVars.GameDummyTicker.Name] = "true";
return StartServer(options);
}
protected async Task<(ClientIntegrationInstance client, ServerIntegrationInstance server)>
StartConnectedServerClientPair(ClientIntegrationOptions clientOptions = null,
ServerIntegrationOptions serverOptions = null)
{
var client = StartClient(clientOptions);
var server = StartServer(serverOptions);
await StartConnectedPairShared(client, server);
return (client, server);
}
protected async Task<(ClientIntegrationInstance client, ServerIntegrationInstance server)>
StartConnectedServerDummyTickerClientPair(ClientIntegrationOptions clientOptions = null,
ServerIntegrationOptions serverOptions = null)
{
var client = StartClient(clientOptions);
var server = StartServerDummyTicker(serverOptions);
await StartConnectedPairShared(client, server);
return (client, server);
}
private bool ShouldPool(IntegrationOptions options, bool server)
{
// TODO TEST POOLING client pooling
if (!server)
{
return false;
}
if (options.Pool.HasValue)
{
return options.Pool.Value;
}
if (server)
{
if (options.CVarOverrides.Count != 3)
{
return false;
}
foreach (var (cvar, value, _) in ServerTestCvars)
{
if (!options.CVarOverrides.TryGetValue(cvar, out var actualValue) ||
actualValue != value)
{
return false;
}
}
}
if (options.CVarOverrides.TryGetValue(CCVars.GameDummyTicker.Name, out var dummy) &&
dummy == "true")
{
return false;
}
if (options.CVarOverrides.TryGetValue(CCVars.GameLobbyEnabled.Name, out var lobby) &&
lobby == "true")
{
return false;
}
if (options is ClientContentIntegrationOption {ContentBeforeIoC: { }}
or ServerContentIntegrationOption {ContentBeforeIoC: { }})
{
return false;
}
return options.InitIoC == null &&
options.BeforeStart == null &&
options.ContentAssemblies == null;
}
protected override async Task OnClientReturn(ClientIntegrationInstance client)
{
await base.OnClientReturn(client);
await client.WaitIdleAsync();
var net = client.ResolveDependency<IClientNetManager>();
var prototypes = client.ResolveDependency<IPrototypeManager>();
await client.WaitPost(() =>
{
net.ClientDisconnect("Test pooling disconnect");
if (client.PreviousOptions?.ExtraPrototypes is { } oldExtra)
{
prototypes.RemoveString(oldExtra);
}
if (client.Options?.ExtraPrototypes is { } extra)
{
prototypes.LoadString(extra, true);
prototypes.ResolveResults();
}
});
await WaitUntil(client, () => !net.IsConnected);
}
protected override async Task OnServerReturn(ServerIntegrationInstance server)
{
await base.OnServerReturn(server);
await server.WaitIdleAsync();
if (server.Options != null)
{
SetServerTestCvars(server.Options);
}
var systems = server.ResolveDependency<IEntitySystemManager>();
var prototypes = server.ResolveDependency<IPrototypeManager>();
var net = server.ResolveDependency<IServerNetManager>();
var players = server.ResolveDependency<IPlayerManager>();
var gameTicker = systems.GetEntitySystem<GameTicker>();
await server.WaitPost(() =>
{
foreach (var channel in net.Channels)
{
net.DisconnectChannel(channel, "Test pooling disconnect");
}
});
await WaitUntil(server, () => players.PlayerCount == 0);
await server.WaitPost(() =>
{
gameTicker.RestartRound();
if (server.PreviousOptions?.ExtraPrototypes is { } oldExtra)
{
prototypes.RemoveString(oldExtra);
}
if (server.Options?.ExtraPrototypes is { } extra)
{
prototypes.LoadString(extra, true);
prototypes.ResolveResults();
}
});
if (!gameTicker.DummyTicker)
{
await WaitUntil(server, () => gameTicker.RunLevel == GameRunLevel.InRound);
}
}
protected async Task WaitUntil(IntegrationInstance instance, Func<bool> func, int maxTicks = 600,
int tickStep = 1)
{
await WaitUntil(instance, async () => await Task.FromResult(func()), maxTicks, tickStep);
}
protected async Task WaitUntil(IntegrationInstance instance, Func<Task<bool>> func, int maxTicks = 600,
int tickStep = 1)
{
var ticksAwaited = 0;
bool passed;
await instance.WaitIdleAsync();
while (!(passed = await func()) && ticksAwaited < maxTicks)
{
var ticksToRun = tickStep;
if (ticksAwaited + tickStep > maxTicks)
{
ticksToRun = maxTicks - ticksAwaited;
}
await instance.WaitRunTicks(ticksToRun);
ticksAwaited += ticksToRun;
}
if (!passed)
{
Assert.Fail($"Condition did not pass after {maxTicks} ticks.\n" +
$"Tests ran ({instance.TestsRan.Count}):\n" +
$"{string.Join('\n', instance.TestsRan)}");
}
Assert.That(passed);
}
private static async Task StartConnectedPairShared(ClientIntegrationInstance client,
ServerIntegrationInstance server)
{
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
client.SetConnectTarget(server);
client.Post(() => IoCManager.Resolve<IClientNetManager>().ClientConnect(null!, 0, null!));
await RunTicksSync(client, server, 10);
}
/// <summary>
/// Runs <paramref name="ticks"/> ticks on both server and client while keeping their main loop in sync.
/// </summary>
protected static async Task RunTicksSync(ClientIntegrationInstance client, ServerIntegrationInstance server,
int ticks)
{
for (var i = 0; i < ticks; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
}
protected MapId GetMainMapId(IMapManager manager)
{
// TODO a heuristic that is not this bad
return manager.GetAllMapIds().Last();
}
protected IMapGrid GetMainGrid(IMapManager manager)
{
// TODO a heuristic that is not this bad
return manager.GetAllGrids().First();
}
protected TileRef GetMainTile(IMapGrid grid)
{
// TODO a heuristic that is not this bad
return grid.GetAllTiles().First();
}
protected EntityCoordinates GetMainEntityCoordinates(IMapManager manager)
{
var gridId = GetMainGrid(manager).GridEntityId;
return new EntityCoordinates(gridId, -0.5f, -0.5f);
}
protected sealed class ClientContentIntegrationOption : ClientIntegrationOptions
{
public ClientContentIntegrationOption()
{
FailureLogLevel = LogLevel.Warning;
}
public override GameControllerOptions Options { get; set; } = new()
{
LoadContentResources = true,
LoadConfigAndUserData = false,
};
public Action ContentBeforeIoC { get; set; }
}
protected sealed class ServerContentIntegrationOption : ServerIntegrationOptions
{
public ServerContentIntegrationOption()
{
FailureLogLevel = LogLevel.Warning;
}
public override ServerOptions Options { get; set; } = new()
{
LoadContentResources = true,
LoadConfigAndUserData = false,
};
public Action ContentBeforeIoC { get; set; }
}
}
}

View File

@@ -1,15 +0,0 @@
using NUnit.Framework;
[SetUpFixture]
// ReSharper disable once CheckNamespace
public sealed class ContentIntegrationTestSetup
{
[OneTimeTearDown]
public void TearDown()
{
var robustSetup = new RobustIntegrationTestSetup();
robustSetup.Shutdown();
robustSetup.PrintTestPoolingInfo();
}
}

View File

@@ -0,0 +1,708 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Content.Client.IoC;
using Content.Client.Parallax.Managers;
using Content.IntegrationTests.Tests;
using Content.IntegrationTests.Tests.DeviceNetwork;
using Content.IntegrationTests.Tests.Interaction.Click;
using Content.IntegrationTests.Tests.Networking;
using Content.Server.GameTicking;
using Content.Shared.CCVar;
using Content.Shared.Maps;
using NUnit.Framework;
using Robust.Client;
using Robust.Server;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.Exceptions;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.UnitTesting;
[assembly: LevelOfParallelism(3)]
namespace Content.IntegrationTests;
public static class PoolManager
{
private static readonly (string cvar, string value, bool tryAdd)[] ServerTestCvars =
{
(CCVars.DatabaseSynchronous.Name, "true", false),
(CCVars.DatabaseSqliteDelay.Name, "0", false),
(CCVars.HolidaysEnabled.Name, "false", false),
(CCVars.GameMap.Name, "empty", true),
(CCVars.GameMapForced.Name, "true", true),
(CCVars.AdminLogsQueueSendDelay.Name, "0", true),
(CCVars.NetPVS.Name, "false", true),
(CCVars.NetInterp.Name, "false", true),
(CCVars.NPCMaxUpdates.Name, "999999", true),
(CCVars.GameMapForced.Name, "true", true),
(CCVars.SysWinTickPeriod.Name, "0", true),
(CCVars.ContactMinimumThreads.Name, "1", true),
(CCVars.ContactMultithreadThreshold.Name, "999", true),
(CCVars.PositionConstraintsMinimumThread.Name, "1", true),
(CCVars.PositionConstraintsPerThread.Name, "999", true),
(CCVars.VelocityConstraintMinimumThreads.Name, "1", true),
(CCVars.VelocityConstraintsPerThread.Name, "999", true),
(CCVars.ThreadParallelCount.Name, "1", true),
};
private static int PairId = 0;
private static object PairLock = new object();
private static List<Pair> Pairs = new();
private static async Task ConfigurePrototypes(RobustIntegrationTest.IntegrationInstance instance,
PoolSettings settings)
{
await instance.WaitPost(() =>
{
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
var changes = new Dictionary<Type, HashSet<string>>();
prototypeManager.LoadString(settings.ExtraPrototypes.Trim(), true, changes);
prototypeManager.ReloadPrototypes(changes);
});
}
private static async Task<RobustIntegrationTest.ServerIntegrationInstance> GenerateServer(PoolSettings poolSettings)
{
var options = new RobustIntegrationTest.ServerIntegrationOptions
{
ExtraPrototypes = poolSettings.ExtraPrototypes,
ContentStart = true,
Options = new ServerOptions()
{
LoadConfigAndUserData = false,
LoadContentResources = !poolSettings.NoLoadContent,
},
ContentAssemblies = new[]
{
typeof(Shared.Entry.EntryPoint).Assembly,
typeof(Server.Entry.EntryPoint).Assembly,
typeof(PoolManager).Assembly
}
};
options.BeforeStart += () =>
{
IoCManager.Resolve<IEntitySystemManager>()
.LoadExtraSystemType<SimplePredictReconcileTest.PredictionTestEntitySystem>();
IoCManager.Resolve<IComponentFactory>().RegisterClass<SimplePredictReconcileTest.PredictionTestComponent>();
IoCManager.Register<ResettingEntitySystemTests.TestRoundRestartCleanupEvent>();
IoCManager.Register<InteractionSystemTests.TestInteractionSystem>();
IoCManager.Register<DeviceNetworkTestSystem>();
IoCManager.Resolve<IEntitySystemManager>()
.LoadExtraSystemType<ResettingEntitySystemTests.TestRoundRestartCleanupEvent>();
IoCManager.Resolve<IEntitySystemManager>()
.LoadExtraSystemType<InteractionSystemTests.TestInteractionSystem>();
IoCManager.Resolve<IEntitySystemManager>().LoadExtraSystemType<DeviceNetworkTestSystem>();
IoCManager.Resolve<ILogManager>().GetSawmill("loc").Level = LogLevel.Error;
};
SetupCVars(poolSettings, options);
var server = new RobustIntegrationTest.ServerIntegrationInstance(options);
await server.WaitIdleAsync();
return server;
}
public static void Shutdown()
{
lock (PairLock)
{
var pairs = Pairs;
// We are trying to make things blow up if they are still happening after this method.
Pairs = null;
foreach (var pair in pairs)
{
pair.Client.Dispose();
pair.Server.Dispose();
}
}
}
private static async Task<RobustIntegrationTest.ClientIntegrationInstance> GenerateClient(PoolSettings poolSettings)
{
var options = new RobustIntegrationTest.ClientIntegrationOptions
{
FailureLogLevel = LogLevel.Warning,
ContentStart = true,
ExtraPrototypes = poolSettings.ExtraPrototypes,
ContentAssemblies = new[]
{
typeof(Shared.Entry.EntryPoint).Assembly,
typeof(Client.Entry.EntryPoint).Assembly,
typeof(PoolManager).Assembly
}
};
if (poolSettings.NoLoadContent)
{
Assert.Warn("NoLoadContent does not work on the client, ignoring");
}
options.Options = new GameControllerOptions()
{
LoadConfigAndUserData = false,
// LoadContentResources = !poolSettings.NoLoadContent
};
options.BeforeStart += () =>
{
IoCManager.Resolve<IModLoader>().SetModuleBaseCallbacks(new ClientModuleTestingCallbacks
{
ClientBeforeIoC = () =>
{
IoCManager.Resolve<IEntitySystemManager>()
.LoadExtraSystemType<SimplePredictReconcileTest.PredictionTestEntitySystem>();
IoCManager.Resolve<IComponentFactory>()
.RegisterClass<SimplePredictReconcileTest.PredictionTestComponent>();
IoCManager.Register<IParallaxManager, DummyParallaxManager>(true);
IoCManager.Resolve<ILogManager>().GetSawmill("loc").Level = LogLevel.Error;
}
});
};
SetupCVars(poolSettings, options);
var client = new RobustIntegrationTest.ClientIntegrationInstance(options);
await client.WaitIdleAsync();
return client;
}
private static void SetupCVars(PoolSettings poolSettings, RobustIntegrationTest.IntegrationOptions options)
{
foreach (var serverTestCvar in ServerTestCvars)
{
options.CVarOverrides[serverTestCvar.cvar] = serverTestCvar.value;
}
if (poolSettings.DummyTicker)
{
options.CVarOverrides[CCVars.GameDummyTicker.Name] = "true";
}
if (poolSettings.InLobby)
{
options.CVarOverrides[CCVars.GameLobbyEnabled.Name] = "true";
}
if (poolSettings.DisableInterpolate)
{
options.CVarOverrides[CCVars.NetInterp.Name] = "false";
}
if (poolSettings.Map != null)
{
options.CVarOverrides[CCVars.GameMap.Name] = poolSettings.Map;
}
}
public static async Task<PairTracker> GetServerClient(PoolSettings poolSettings = null,
[System.Runtime.CompilerServices.CallerFilePath] string testMethodFilePath = "",
[System.Runtime.CompilerServices.CallerMemberName] string testMethodName = "") =>
await GetServerClientPair(poolSettings ?? new PoolSettings(), $"{testMethodFilePath}, {testMethodName}");
private static async Task<PairTracker> GetServerClientPair(PoolSettings poolSettings, string testMethodName)
{
var poolRetrieveTimeWatch = new Stopwatch();
poolRetrieveTimeWatch.Start();
await TestContext.Out.WriteLineAsync("Getting server/client");
Pair pair;
if (poolSettings.MustBeNew)
{
await TestContext.Out.WriteLineAsync($"Creating, because must be new pair");
pair = await CreateServerClientPair(poolSettings);
}
else
{
pair = GrabOptimalPair(poolSettings);
if (pair != null)
{
var canSkip = pair.Settings.CanFastRecycle(poolSettings);
if (!canSkip)
{
await TestContext.Out.WriteLineAsync($"Cleaning existing pair");
await CleanPooledPair(poolSettings, pair);
}
else
{
await TestContext.Out.WriteLineAsync($"Skip cleanup pair");
}
}
else
{
await TestContext.Out.WriteLineAsync($"Creating, because pool empty");
pair = await CreateServerClientPair(poolSettings);
}
}
var poolRetrieveTime = poolRetrieveTimeWatch.Elapsed;
await TestContext.Out.WriteLineAsync($"Got server/client (id:{pair.PairId},uses:{pair.TestHistory.Count}) in {poolRetrieveTime.TotalMilliseconds} ms");
pair.Settings = poolSettings;
TestContext.Out.WriteLine($"Test History|\n{string.Join('\n', pair.TestHistory)}\n|Test History End");
pair.TestHistory.Add(testMethodName);
var usageWatch = new Stopwatch();
usageWatch.Start();
return new PairTracker()
{
Pair = pair,
UsageWatch = usageWatch
};
}
private static Pair GrabOptimalPair(PoolSettings poolSettings)
{
lock (PairLock)
{
if (Pairs.Count == 0) return null;
for (var i = 0; i < Pairs.Count; i++)
{
var pair = Pairs[i];
if (!pair.Settings.CanFastRecycle(poolSettings)) continue;
Pairs.RemoveAt(i);
return pair;
}
var defaultPair = Pairs[^1];
Pairs.RemoveAt(Pairs.Count - 1);
return defaultPair;
}
}
/// <summary>
/// Used after checking pairs, Don't use this directly
/// </summary>
/// <param name="pair"></param>
public static void NoCheckReturn(Pair pair)
{
lock (PairLock)
{
Pairs.Add(pair);
}
}
private static async Task CleanPooledPair(PoolSettings poolSettings, Pair pair)
{
var methodWatch = new Stopwatch();
methodWatch.Start();
await TestContext.Out.WriteLineAsync($"Recycling: {methodWatch.Elapsed.TotalMilliseconds} ms: Setting CVar ");
var configManager = pair.Server.ResolveDependency<IConfigurationManager>();
await pair.Server.WaitPost(() =>
{
configManager.SetCVar(CCVars.GameLobbyEnabled, poolSettings.InLobby);
});
var cNetMgr = pair.Client.ResolveDependency<IClientNetManager>();
if (!cNetMgr.IsConnected)
{
await TestContext.Out.WriteLineAsync($"Recycling: {methodWatch.Elapsed.TotalMilliseconds} ms: Connecting client, and restarting server");
pair.Client.SetConnectTarget(pair.Server);
await pair.Server.WaitPost(() =>
{
EntitySystem.Get<GameTicker>().RestartRound();
});
await pair.Client.WaitPost(() =>
{
cNetMgr.ClientConnect(null!, 0, null!);
});
await ReallyBeIdle(pair,11);
}
await TestContext.Out.WriteLineAsync($"Recycling: {methodWatch.Elapsed.TotalMilliseconds} ms: Disconnecting client, and restarting server");
await pair.Client.WaitPost(() =>
{
cNetMgr.ClientDisconnect("Test pooling cleanup disconnect");
});
await ReallyBeIdle(pair, 5);
if (!string.IsNullOrWhiteSpace(pair.Settings.ExtraPrototypes))
{
await TestContext.Out.WriteLineAsync($"Recycling: {methodWatch.Elapsed.TotalMilliseconds} ms: Removing prototypes");
if (!pair.Settings.NoServer)
{
var serverProtoManager = pair.Server.ResolveDependency<IPrototypeManager>();
await pair.Server.WaitPost(() =>
{
serverProtoManager.RemoveString(pair.Settings.ExtraPrototypes.Trim());
});
}
if(!pair.Settings.NoClient)
{
var clientProtoManager = pair.Client.ResolveDependency<IPrototypeManager>();
await pair.Client.WaitPost(() =>
{
clientProtoManager.RemoveString(pair.Settings.ExtraPrototypes.Trim());
});
}
await ReallyBeIdle(pair, 1);
}
if (poolSettings.ExtraPrototypes != null)
{
await TestContext.Out.WriteLineAsync($"Recycling: {methodWatch.Elapsed.TotalMilliseconds} ms: Adding prototypes");
if (!poolSettings.NoServer)
{
await ConfigurePrototypes(pair.Server, poolSettings);
}
if (!poolSettings.NoClient)
{
await ConfigurePrototypes(pair.Client, poolSettings);
}
}
await TestContext.Out.WriteLineAsync($"Recycling: {methodWatch.Elapsed.TotalMilliseconds} ms: Restarting server again");
await pair.Server.WaitPost(() =>
{
EntitySystem.Get<GameTicker>().RestartRound();
});
if (!poolSettings.NotConnected)
{
await TestContext.Out.WriteLineAsync($"Recycling: {methodWatch.Elapsed.TotalMilliseconds} ms: Connecting client");
await ReallyBeIdle(pair);
pair.Client.SetConnectTarget(pair.Server);
await pair.Client.WaitPost(() =>
{
var netMgr = IoCManager.Resolve<IClientNetManager>();
if (!netMgr.IsConnected)
{
netMgr.ClientConnect(null!, 0, null!);
}
});
}
await ReallyBeIdle(pair);
await TestContext.Out.WriteLineAsync($"Recycling: {methodWatch.Elapsed.TotalMilliseconds} ms: Done recycling");
}
private static async Task<Pair> CreateServerClientPair(PoolSettings poolSettings)
{
var client = await GenerateClient(poolSettings);
var server = await GenerateServer(poolSettings);
var pair = new Pair { Server = server, Client = client, PairId = Interlocked.Increment(ref PairId)};
if (!poolSettings.NotConnected)
{
pair.Client.SetConnectTarget(pair.Server);
await pair.Client.WaitPost(() =>
{
var netMgr = IoCManager.Resolve<IClientNetManager>();
if (!netMgr.IsConnected)
{
netMgr.ClientConnect(null!, 0, null!);
}
});
await ReallyBeIdle(pair, 10);
await client.WaitRunTicks(1);
}
return pair;
}
public static async Task<TestMapData> CreateTestMap(PairTracker pairTracker)
{
var server = pairTracker.Pair.Server;
var settings = pairTracker.Pair.Settings;
if (settings.NoServer) throw new Exception("Cannot setup test map without server");
var mapData = new TestMapData
{
};
await server.WaitPost(() =>
{
var mapManager = IoCManager.Resolve<IMapManager>();
mapData.MapId = mapManager.CreateMap();
mapData.MapGrid = mapManager.CreateGrid(mapData.MapId);
mapData.GridCoords = new EntityCoordinates(mapData.MapGrid.GridEntityId, 0, 0);
var tileDefinitionManager = IoCManager.Resolve<ITileDefinitionManager>();
var plating = tileDefinitionManager["plating"];
var platingTile = new Tile(plating.TileId);
mapData.MapGrid.SetTile(mapData.GridCoords, platingTile);
mapData.MapCoords = new MapCoordinates(0, 0, mapData.MapId);
mapData.Tile = mapData.MapGrid.GetAllTiles().First();
});
if (!settings.Disconnected)
{
await RunTicksSync(pairTracker.Pair, 10);
}
return mapData;
}
public static async Task RunTicksSync(Pair pair, int ticks)
{
for (var i = 0; i < ticks; i++)
{
await pair.Server.WaitRunTicks(1);
await pair.Client.WaitRunTicks(1);
}
}
public static async Task WaitUntil(RobustIntegrationTest.IntegrationInstance instance, Func<bool> func,
int maxTicks = 600,
int tickStep = 1)
{
await WaitUntil(instance, async () => await Task.FromResult(func()), maxTicks, tickStep);
}
public static async Task ReallyBeIdle(Pair pair, int runTicks = 25)
{
for (int i = 0; i < runTicks; i++)
{
await pair.Client.WaitRunTicks(1);
await pair.Server.WaitRunTicks(1);
for (int idleCycles = 0; idleCycles < 4; idleCycles++)
{
await pair.Client.WaitIdleAsync();
await pair.Server.WaitIdleAsync();
}
}
}
public static async Task WaitUntil(RobustIntegrationTest.IntegrationInstance instance, Func<Task<bool>> func,
int maxTicks = 600,
int tickStep = 1)
{
var ticksAwaited = 0;
bool passed;
await instance.WaitIdleAsync();
while (!(passed = await func()) && ticksAwaited < maxTicks)
{
var ticksToRun = tickStep;
if (ticksAwaited + tickStep > maxTicks)
{
ticksToRun = maxTicks - ticksAwaited;
}
await instance.WaitRunTicks(ticksToRun);
ticksAwaited += ticksToRun;
}
if (!passed)
{
Assert.Fail($"Condition did not pass after {maxTicks} ticks.\n" +
$"Tests ran ({instance.TestsRan.Count}):\n" +
$"{string.Join('\n', instance.TestsRan)}");
}
Assert.That(passed);
}
}
public sealed class PoolSettings
{
// Todo: We can make more of these pool-able, if we need enough of them for it to matter
public bool MustNotBeReused => Destructive || NoLoadContent || DisableInterpolate || DummyTicker;
public bool MustBeNew => Fresh || NoLoadContent || DisableInterpolate || DummyTicker;
public bool NotConnected => NoClient || NoServer || Disconnected;
/// <summary>
/// We are going to ruin this pair
/// </summary>
public bool Destructive { get; init; }
/// <summary>
/// We need a brand new pair
/// </summary>
public bool Fresh { get; init; }
/// <summary>
/// We need a pair that uses a dummy ticker
/// </summary>
public bool DummyTicker { get; init; }
/// <summary>
/// We need the client, and server to be disconnected
/// </summary>
public bool Disconnected { get; init; }
/// <summary>
/// We need the server to be in the lobby
/// </summary>
public bool InLobby { get; init; }
/// <summary>
/// We don't want content loaded
/// </summary>
public bool NoLoadContent { get; init; }
/// <summary>
/// We want to add some prototypes
/// </summary>
public string ExtraPrototypes { get; init; }
/// <summary>
/// Disables NetInterp
/// </summary>
public bool DisableInterpolate { get; init; }
/// <summary>
/// Tells the pool it has to clean up before the server/client can be used.
/// </summary>
public bool Dirty { get; init; }
/// <summary>
/// Sets the map Cvar, and loads the map
/// </summary>
public string Map { get; init; } // TODO for map painter
/// <summary>
/// The test won't use the client (so we can skip cleaning it)
/// </summary>
public bool NoClient { get; init; }
/// <summary>
/// The test won't use the client (so we can skip cleaning it)
/// </summary>
public bool NoServer { get; init; }
/// <summary>
/// Guess if skipping recycling is ok
/// </summary>
/// <param name="nextSettings">The next set of settings the old pair will be set to</param>
/// <returns></returns>
public bool CanFastRecycle(PoolSettings nextSettings)
{
if (Dirty) return false;
if (Destructive || nextSettings.Destructive) return false;
if (NotConnected != nextSettings.NotConnected) return false;
if (InLobby != nextSettings.InLobby) return false;
if (DisableInterpolate != nextSettings.DisableInterpolate) return false;
if (nextSettings.DummyTicker) return false;
if (Map != nextSettings.Map) return false;
if (NoLoadContent != nextSettings.NoLoadContent) return false;
if (nextSettings.Fresh) return false;
if (ExtraPrototypes != nextSettings.ExtraPrototypes) return false;
return true;
}
}
public sealed class TestMapData
{
public MapId MapId { get; set; }
public IMapGrid MapGrid { get; set; }
public EntityCoordinates GridCoords { get; set; }
public MapCoordinates MapCoords { get; set; }
public TileRef Tile { get; set; }
}
public sealed class Pair
{
public int PairId { get; init; }
public List<string> TestHistory { get; set; } = new();
public PoolSettings Settings { get; set; }
public RobustIntegrationTest.ServerIntegrationInstance Server { get; init; }
public RobustIntegrationTest.ClientIntegrationInstance Client { get; init; }
}
public sealed class PairTracker : IAsyncDisposable
{
private int _disposed;
public async Task OnDirtyDispose()
{
var usageTime = UsageWatch.Elapsed;
await TestContext.Out.WriteLineAsync($"Dirty: Test returned in {usageTime.TotalMilliseconds} ms");
var dirtyWatch = new Stopwatch();
dirtyWatch.Start();
Pair.Client.Dispose();
Pair.Server.Dispose();
var disposeTime = dirtyWatch.Elapsed;
await TestContext.Out.WriteLineAsync($"Dirty: Disposed in {disposeTime.TotalMilliseconds} ms");
}
public async Task OnCleanDispose()
{
var usageTime = UsageWatch.Elapsed;
await TestContext.Out.WriteLineAsync($"Clean: Test returned in {usageTime.TotalMilliseconds} ms");
var cleanWatch = new Stopwatch();
cleanWatch.Start();
// Let any last minute failures the test cause happen.
await PoolManager.ReallyBeIdle(Pair);
if (!Pair.Settings.Destructive)
{
if (Pair.Client.IsAlive == false)
{
throw new Exception("Test killed the client", Pair.Client.UnhandledException);
}
if (Pair.Server.IsAlive == false)
{
throw new Exception("Test killed the server", Pair.Server.UnhandledException);
}
}
if (Pair.Settings.MustNotBeReused)
{
Pair.Client.Dispose();
Pair.Server.Dispose();
var returnTime2 = cleanWatch.Elapsed;
await TestContext.Out.WriteLineAsync($"Clean: Clean disposed in {returnTime2.TotalMilliseconds} ms");
return;
}
var sRuntimeLog = Pair.Server.ResolveDependency<IRuntimeLog>();
if (sRuntimeLog.ExceptionCount > 0) throw new Exception("Server logged exceptions");
var cRuntimeLog = Pair.Client.ResolveDependency<IRuntimeLog>();
if (cRuntimeLog.ExceptionCount > 0) throw new Exception("Client logged exceptions");
PoolManager.NoCheckReturn(Pair);
var returnTime = cleanWatch.Elapsed;
await TestContext.Out.WriteLineAsync($"Clean: Clean returned to pool in {returnTime.TotalMilliseconds} ms");
}
public Stopwatch UsageWatch { get; set; }
public Pair Pair { get; init; }
public async ValueTask CleanReturnAsync()
{
var disposed = Interlocked.Exchange(ref _disposed, 1);
switch (disposed)
{
case 0:
await TestContext.Out.WriteLineAsync("Clean Return Start");
break;
case 1:
throw new Exception("Already called clean return before");
case 2:
throw new Exception("Already dirty disposed");
default:
throw new Exception("Unexpected disposed value");
}
await OnCleanDispose();
await TestContext.Out.WriteLineAsync($"Clean Return Exiting");
}
public async ValueTask DisposeAsync()
{
var disposed = Interlocked.Exchange(ref _disposed, 2);
switch (disposed)
{
case 0:
await TestContext.Out.WriteLineAsync("Dirty Return Start");
break;
case 1:
await TestContext.Out.WriteLineAsync("Dirty Return - Already Clean Disposed");
return;
case 2:
throw new Exception("Already called dirty return before");
default:
throw new Exception("Unexpected disposed value");
}
await OnDirtyDispose();
await TestContext.Out.WriteLineAsync($"Dirty Return Exiting");
}
}

View File

@@ -0,0 +1,15 @@
using NUnit.Framework;
[assembly: Parallelizable(ParallelScope.Children)]
namespace Content.IntegrationTests;
[SetUpFixture]
public sealed class PoolManagerTestEventHandler
{
[OneTimeTearDown]
public void TearDown()
{
PoolManager.Shutdown();
}
}

View File

@@ -12,13 +12,13 @@ namespace Content.IntegrationTests.Tests.AI
{
[TestFixture]
[TestOf(typeof(BehaviorSetPrototype))]
public sealed class BehaviorSetsTest : ContentIntegrationTest
public sealed class BehaviorSetsTest
{
[Test]
public async Task TestBehaviorSets()
{
var server = StartServer();
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
var protoManager = server.ResolveDependency<IPrototypeManager>();
var reflectionManager = server.ResolveDependency<IReflectionManager>();
@@ -56,6 +56,7 @@ namespace Content.IntegrationTests.Tests.AI
}
}
});
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -9,12 +9,14 @@ namespace Content.IntegrationTests.Tests.Access
{
[TestFixture]
[TestOf(typeof(AccessReaderComponent))]
public sealed class AccessReaderTest : ContentIntegrationTest
public sealed class AccessReaderTest
{
[Test]
public async Task TestTags()
{
var server = StartServer();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
await server.WaitAssertion(() =>
{
var system = EntitySystem.Get<AccessReaderSystem>();
@@ -69,6 +71,7 @@ namespace Content.IntegrationTests.Tests.Access
Assert.That(system.IsAllowed(new[] { "A", "B" }, reader), Is.False);
Assert.That(system.IsAllowed(new string[] { }, reader), Is.False);
});
await pairTracker.CleanReturnAsync();
}
}

View File

@@ -5,11 +5,13 @@ using System.Threading.Tasks;
using Content.Server.Administration.Logs;
using Content.Server.Database;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Commands;
using Content.Shared.Administration.Logs;
using Content.Shared.CCVar;
using Content.Shared.Database;
using NUnit.Framework;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
@@ -17,38 +19,31 @@ namespace Content.IntegrationTests.Tests.Administration.Logs;
[TestFixture]
[TestOf(typeof(AdminLogSystem))]
public sealed class AddTests : ContentIntegrationTest
public sealed class AddTests
{
[Test]
public async Task AddAndGetSingleLog()
{
var server = StartServer(new ServerContentIntegrationOption
{
CVarOverrides =
{
[CCVars.AdminLogsQueueSendDelay.Name] = "0"
},
Pool = true
});
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
var sEntities = server.ResolveDependency<IEntityManager>();
var sMaps = server.ResolveDependency<IMapManager>();
var sSystems = server.ResolveDependency<IEntitySystemManager>();
var sAdminLogSystem = server.ResolveDependency<IAdminLogManager>();
var guid = Guid.NewGuid();
var testMap = await PoolManager.CreateTestMap(pairTracker);
var coordinates = testMap.GridCoords;
await server.WaitPost(() =>
{
var coordinates = GetMainEntityCoordinates(sMaps);
var entity = sEntities.SpawnEntity(null, coordinates);
sAdminLogSystem.Add(LogType.Unknown, $"{entity:Entity} test log: {guid}");
});
await WaitUntil(server, async () =>
await PoolManager.WaitUntil(server, async () =>
{
var logs = sAdminLogSystem.CurrentRoundJson(new LogFilter
{
@@ -69,20 +64,15 @@ public sealed class AddTests : ContentIntegrationTest
return false;
});
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task AddAndGetUnformattedLog()
{
var server = StartServer(new ServerContentIntegrationOption
{
CVarOverrides =
{
[CCVars.AdminLogsQueueSendDelay.Name] = "0"
},
Pool = true
});
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
var sDatabase = server.ResolveDependency<IServerDbManager>();
var sEntities = server.ResolveDependency<IEntityManager>();
@@ -94,9 +84,10 @@ public sealed class AddTests : ContentIntegrationTest
var guid = Guid.NewGuid();
var testMap = await PoolManager.CreateTestMap(pairTracker);
var coordinates = testMap.GridCoords;
await server.WaitPost(() =>
{
var coordinates = GetMainEntityCoordinates(sMaps);
var entity = sEntities.SpawnEntity(null, coordinates);
sAdminLogSystem.Add(LogType.Unknown, $"{entity} test log: {guid}");
@@ -104,7 +95,7 @@ public sealed class AddTests : ContentIntegrationTest
SharedAdminLog log = default;
await WaitUntil(server, async () =>
await PoolManager.WaitUntil(server, async () =>
{
var logs = await sAdminLogSystem.CurrentRoundLogs(new LogFilter
{
@@ -136,31 +127,26 @@ public sealed class AddTests : ContentIntegrationTest
json.Dispose();
}
await pairTracker.CleanReturnAsync();
}
[Test]
[TestCase(500)]
public async Task BulkAddLogs(int amount)
{
var server = StartServer(new ServerContentIntegrationOption
{
CVarOverrides =
{
[CCVars.AdminLogsQueueSendDelay.Name] = "0"
},
Pool = true
});
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
var sEntities = server.ResolveDependency<IEntityManager>();
var sMaps = server.ResolveDependency<IMapManager>();
var sSystems = server.ResolveDependency<IEntitySystemManager>();
var sAdminLogSystem = server.ResolveDependency<IAdminLogManager>();
var testMap = await PoolManager.CreateTestMap(pairTracker);
var coordinates = testMap.GridCoords;
await server.WaitPost(() =>
{
var coordinates = GetMainEntityCoordinates(sMaps);
var entity = sEntities.SpawnEntity(null, coordinates);
for (var i = 0; i < amount; i++)
@@ -169,26 +155,20 @@ public sealed class AddTests : ContentIntegrationTest
}
});
await WaitUntil(server, async () =>
await PoolManager.WaitUntil(server, async () =>
{
var messages = await sAdminLogSystem.CurrentRoundLogs();
return messages.Count >= amount;
});
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task AddPlayerSessionLog()
{
var (client, server) = await StartConnectedServerClientPair(serverOptions: new ServerContentIntegrationOption
{
CVarOverrides =
{
[CCVars.AdminLogsQueueSendDelay.Name] = "0"
},
Pool = true
});
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
await using var pairTracker = await PoolManager.GetServerClient();
var server = pairTracker.Pair.Server;
var sPlayers = server.ResolveDependency<IPlayerManager>();
var sSystems = server.ResolveDependency<IEntitySystemManager>();
@@ -207,7 +187,7 @@ public sealed class AddTests : ContentIntegrationTest
});
});
await WaitUntil(server, async () =>
await PoolManager.WaitUntil(server, async () =>
{
var logs = await sAdminLogSystem.CurrentRoundLogs();
if (logs.Count == 0)
@@ -218,20 +198,22 @@ public sealed class AddTests : ContentIntegrationTest
Assert.That(logs.First().Players, Does.Contain(playerGuid));
return true;
});
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task PreRoundAddAndGetSingle()
{
var server = StartServer(new ServerContentIntegrationOption
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{Dirty = true});
var server = pairTracker.Pair.Server;
var configManager = server.ResolveDependency<IConfigurationManager>();
await server.WaitPost(() =>
{
CVarOverrides =
{
[CCVars.AdminLogsQueueSendDelay.Name] = "0",
[CCVars.GameLobbyEnabled.Name] = "true"
},
configManager.SetCVar(CCVars.GameLobbyEnabled, true);
var command = new RestartRoundNowCommand();
command.Execute(null, string.Empty, Array.Empty<string>());
});
await server.WaitIdleAsync();
var sDatabase = server.ResolveDependency<IServerDbManager>();
var sSystems = server.ResolveDependency<IEntitySystemManager>();
@@ -253,7 +235,7 @@ public sealed class AddTests : ContentIntegrationTest
SharedAdminLog log = default;
await WaitUntil(server, async () =>
await PoolManager.WaitUntil(server, async () =>
{
var logs = await sAdminLogSystem.CurrentRoundLogs(new LogFilter
{
@@ -284,20 +266,14 @@ public sealed class AddTests : ContentIntegrationTest
json.Dispose();
}
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task DuplicatePlayerDoesNotThrowTest()
{
var (client, server) = await StartConnectedServerClientPair(serverOptions: new ServerContentIntegrationOption
{
CVarOverrides =
{
[CCVars.AdminLogsQueueSendDelay.Name] = "0"
},
});
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
await using var pairTracker = await PoolManager.GetServerClient();
var server = pairTracker.Pair.Server;
var sPlayers = server.ResolveDependency<IPlayerManager>();
var sSystems = server.ResolveDependency<IEntitySystemManager>();
@@ -313,7 +289,7 @@ public sealed class AddTests : ContentIntegrationTest
sAdminLogSystem.Add(LogType.Unknown, $"{player} {player} test log: {guid}");
});
await WaitUntil(server, async () =>
await PoolManager.WaitUntil(server, async () =>
{
var logs = await sAdminLogSystem.CurrentRoundLogs(new LogFilter
{
@@ -328,24 +304,17 @@ public sealed class AddTests : ContentIntegrationTest
return true;
});
await pairTracker.CleanReturnAsync();
Assert.Pass();
}
[Test]
public async Task DuplicatePlayerIdDoesNotThrowTest()
{
var (client, server) = await StartConnectedServerClientPair(serverOptions: new ServerContentIntegrationOption
{
CVarOverrides =
{
[CCVars.AdminLogsQueueSendDelay.Name] = "0"
},
});
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
await using var pairTracker = await PoolManager.GetServerClient();
var server = pairTracker.Pair.Server;
var sPlayers = server.ResolveDependency<IPlayerManager>();
var sSystems = server.ResolveDependency<IEntitySystemManager>();
var sAdminLogSystem = server.ResolveDependency<IAdminLogManager>();
@@ -358,7 +327,7 @@ public sealed class AddTests : ContentIntegrationTest
sAdminLogSystem.Add(LogType.Unknown, $"{player:first} {player:second} test log: {guid}");
});
await WaitUntil(server, async () =>
await PoolManager.WaitUntil(server, async () =>
{
var logs = await sAdminLogSystem.CurrentRoundLogs(new LogFilter
{
@@ -373,6 +342,7 @@ public sealed class AddTests : ContentIntegrationTest
return true;
});
await pairTracker.CleanReturnAsync();
Assert.Pass();
}
}

View File

@@ -12,36 +12,28 @@ namespace Content.IntegrationTests.Tests.Administration.Logs;
[TestFixture]
[TestOf(typeof(AdminLogSystem))]
public sealed class FilterTests : ContentIntegrationTest
public sealed class FilterTests
{
[Test]
[TestCase(DateOrder.Ascending)]
[TestCase(DateOrder.Descending)]
public async Task Date(DateOrder order)
{
var server = StartServer(new ServerContentIntegrationOption
{
CVarOverrides =
{
[CCVars.AdminLogsQueueSendDelay.Name] = "0"
},
Pool = true
});
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
var sEntities = server.ResolveDependency<IEntityManager>();
var sMaps = server.ResolveDependency<IMapManager>();
var sSystems = server.ResolveDependency<IEntitySystemManager>();
var sAdminLogSystem = server.ResolveDependency<IAdminLogManager>();
var commonGuid = Guid.NewGuid();
var firstGuid = Guid.NewGuid();
var secondGuid = Guid.NewGuid();
var testMap = await PoolManager.CreateTestMap(pairTracker);
var coordinates = testMap.GridCoords;
await server.WaitPost(() =>
{
var coordinates = GetMainEntityCoordinates(sMaps);
var entity = sEntities.SpawnEntity(null, coordinates);
sAdminLogSystem.Add(LogType.Unknown, $"{entity:Entity} test log: {commonGuid} {firstGuid}");
@@ -51,13 +43,12 @@ public sealed class FilterTests : ContentIntegrationTest
await server.WaitPost(() =>
{
var coordinates = GetMainEntityCoordinates(sMaps);
var entity = sEntities.SpawnEntity(null, coordinates);
sAdminLogSystem.Add(LogType.Unknown, $"{entity:Entity} test log: {commonGuid} {secondGuid}");
});
await WaitUntil(server, async () =>
await PoolManager.WaitUntil(server, async () =>
{
var commonGuidStr = commonGuid.ToString();
@@ -110,5 +101,6 @@ public sealed class FilterTests : ContentIntegrationTest
return firstFound && secondFound;
});
await pairTracker.CleanReturnAsync();
}
}

View File

@@ -14,21 +14,13 @@ namespace Content.IntegrationTests.Tests.Administration.Logs;
[TestFixture]
[TestOf(typeof(AdminLogSystem))]
public sealed class QueryTests : ContentIntegrationTest
public sealed class QueryTests
{
[Test]
public async Task QuerySingleLog()
{
var serverOptions = new ServerContentIntegrationOption
{
CVarOverrides =
{
[CCVars.AdminLogsQueueSendDelay.Name] = "0"
}
};
var (client, server) = await StartConnectedServerClientPair(serverOptions: serverOptions);
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
await using var pairTracker = await PoolManager.GetServerClient();
var server = pairTracker.Pair.Server;
var sSystems = server.ResolveDependency<IEntitySystemManager>();
var sPlayers = server.ResolveDependency<IPlayerManager>();
@@ -57,7 +49,7 @@ public sealed class QueryTests : ContentIntegrationTest
AnyPlayers = new[] {player.UserId.UserId}
};
await WaitUntil(server, async () =>
await PoolManager.WaitUntil(server, async () =>
{
foreach (var _ in await sAdminLogSystem.All(filter))
{
@@ -66,5 +58,7 @@ public sealed class QueryTests : ContentIntegrationTest
return false;
});
await pairTracker.CleanReturnAsync();
}
}

View File

@@ -7,7 +7,7 @@ namespace Content.IntegrationTests.Tests.Atmos
{
[TestFixture]
[TestOf(typeof(AtmosAlarmThreshold))]
public sealed class AlarmThresholdTest : ContentIntegrationTest
public sealed class AlarmThresholdTest
{
private const string Prototypes = @"
- type: alarmThreshold
@@ -21,12 +21,8 @@ namespace Content.IntegrationTests.Tests.Atmos
[Test]
public async Task TestAlarmThreshold()
{
var server = StartServerDummyTicker(new ServerContentIntegrationOption
{
ExtraPrototypes = Prototypes
});
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
AtmosAlarmThreshold threshold = default!;
@@ -91,6 +87,7 @@ namespace Content.IntegrationTests.Tests.Atmos
Assert.That(threshold.UpperBound, Is.EqualTo(null));
Assert.That(threshold.LowerBound, Is.EqualTo(null));
});
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -10,14 +10,13 @@ namespace Content.IntegrationTests.Tests.Atmos
{
[TestFixture]
[TestOf(typeof(Atmospherics))]
public sealed class ConstantsTest : ContentIntegrationTest
public sealed class ConstantsTest
{
[Test]
public async Task TotalGasesTest()
{
var server = StartServer();
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
await server.WaitPost(() =>
{
@@ -27,6 +26,7 @@ namespace Content.IntegrationTests.Tests.Atmos
Assert.That(Enum.GetValues(typeof(Gas)).Length, Is.EqualTo(Atmospherics.TotalNumberOfGases));
});
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -9,18 +9,17 @@ namespace Content.IntegrationTests.Tests.Atmos
{
[TestFixture]
[TestOf(typeof(GasMixture))]
public sealed class GasMixtureTest : ContentIntegrationTest
public sealed class GasMixtureTest
{
[Test]
public async Task TestMerge()
{
var server = StartServer();
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
var atmosphereSystem = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<AtmosphereSystem>();
server.Assert(() =>
await server.WaitAssertion(() =>
{
var a = new GasMixture(10f);
var b = new GasMixture(10f);
@@ -48,7 +47,7 @@ namespace Content.IntegrationTests.Tests.Atmos
Assert.That(a.GetMoles(Gas.Oxygen), Is.EqualTo(50));
});
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
[Test]
@@ -60,9 +59,10 @@ namespace Content.IntegrationTests.Tests.Atmos
[TestCase(Atmospherics.BreathPercentage)]
public async Task RemoveRatio(float ratio)
{
var server = StartServer();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
server.Assert(() =>
await server.WaitAssertion(() =>
{
var a = new GasMixture(10f);
@@ -85,7 +85,7 @@ namespace Content.IntegrationTests.Tests.Atmos
Assert.That(a.GetMoles(Gas.Nitrogen), Is.EqualTo(100 - b.GetMoles(Gas.Nitrogen)));
});
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -14,7 +14,7 @@ namespace Content.IntegrationTests.Tests.Body
[TestFixture]
[TestOf(typeof(SharedBodyComponent))]
[TestOf(typeof(BodyComponent))]
public sealed class LegTest : ContentIntegrationTest
public sealed class LegTest
{
private const string Prototypes = @"
- type: entity
@@ -32,8 +32,8 @@ namespace Content.IntegrationTests.Tests.Body
[Test]
public async Task RemoveLegsFallTest()
{
var options = new ServerContentIntegrationOption{ExtraPrototypes = Prototypes};
var server = StartServer(options);
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
AppearanceComponent appearance = null;
@@ -64,6 +64,7 @@ namespace Content.IntegrationTests.Tests.Body
Assert.That(appearance.TryGetData(RotationVisuals.RotationState, out RotationState state));
Assert.That(state, Is.EqualTo(RotationState.Horizontal));
});
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -13,7 +13,7 @@ namespace Content.IntegrationTests.Tests.Body
{
[TestFixture]
[TestOf(typeof(LungSystem))]
public sealed class LungTest : ContentIntegrationTest
public sealed class LungTest
{
private const string Prototypes = @"
- type: entity
@@ -49,8 +49,8 @@ namespace Content.IntegrationTests.Tests.Body
public async Task AirConsistencyTest()
{
// --- Setup
var options = new ServerContentIntegrationOption{ExtraPrototypes = Prototypes};
var server = StartServer(options);
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
await server.WaitIdleAsync();
@@ -121,16 +121,14 @@ namespace Content.IntegrationTests.Tests.Body
});
}
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task NoSuffocationTest()
{
var options = new ServerContentIntegrationOption{ExtraPrototypes = Prototypes};
var server = StartServer(options);
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
var mapLoader = server.ResolveDependency<IMapLoader>();
var mapManager = server.ResolveDependency<IMapManager>();
@@ -173,7 +171,7 @@ namespace Content.IntegrationTests.Tests.Body
});
}
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -17,7 +17,7 @@ namespace Content.IntegrationTests.Tests.Buckle
[TestFixture]
[TestOf(typeof(BuckleComponent))]
[TestOf(typeof(StrapComponent))]
public sealed class BuckleTest : ContentIntegrationTest
public sealed class BuckleTest
{
private const string BuckleDummyId = "BuckleDummy";
private const string StrapDummyId = "StrapDummy";
@@ -51,9 +51,11 @@ namespace Content.IntegrationTests.Tests.Buckle
[Test]
public async Task BuckleUnbuckleCooldownRangeTest()
{
var cOptions = new ClientIntegrationOptions {ExtraPrototypes = Prototypes};
var sOptions = new ServerIntegrationOptions {ExtraPrototypes = Prototypes};
var (_, server) = await StartConnectedServerClientPair(cOptions, sOptions);
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
var testMap = await PoolManager.CreateTestMap(pairTracker);
var coordinates = testMap.GridCoords;
EntityUid human = default;
EntityUid chair = default;
@@ -68,9 +70,6 @@ namespace Content.IntegrationTests.Tests.Buckle
var actionBlocker = EntitySystem.Get<ActionBlockerSystem>();
var standingState = EntitySystem.Get<StandingStateSystem>();
var grid = GetMainGrid(mapManager);
var coordinates = new EntityCoordinates(grid.GridEntityId, 0, 0);
human = entityManager.SpawnEntity(BuckleDummyId, coordinates);
chair = entityManager.SpawnEntity(StrapDummyId, coordinates);
@@ -209,13 +208,18 @@ namespace Content.IntegrationTests.Tests.Buckle
Assert.Null(buckle.BuckledTo);
Assert.IsEmpty(strap.BuckledEntities);
});
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task BuckledDyingDropItemsTest()
{
var options = new ServerContentIntegrationOption {ExtraPrototypes = Prototypes};
var server = StartServer(options);
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
var testMap = await PoolManager.CreateTestMap(pairTracker);
var coordinates = testMap.GridCoords;
EntityUid human = default;
BuckleComponent buckle = null;
@@ -226,12 +230,8 @@ namespace Content.IntegrationTests.Tests.Buckle
await server.WaitAssertion(() =>
{
var mapManager = IoCManager.Resolve<IMapManager>();
var entityManager = IoCManager.Resolve<IEntityManager>();
var grid = GetMainGrid(mapManager);
var coordinates = new EntityCoordinates(grid.GridEntityId, 0, 0);
human = entityManager.SpawnEntity(BuckleDummyId, coordinates);
var chair = entityManager.SpawnEntity(StrapDummyId, coordinates);
@@ -292,16 +292,18 @@ namespace Content.IntegrationTests.Tests.Buckle
buckle.TryUnbuckle(human, true);
});
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task ForceUnbuckleBuckleTest()
{
var options = new ServerContentIntegrationOption
{
ExtraPrototypes = Prototypes
};
var server = StartServer(options);
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
var testMap = await PoolManager.CreateTestMap(pairTracker);
var coordinates = testMap.GridCoords;
EntityUid human = default;
EntityUid chair = default;
@@ -312,9 +314,6 @@ namespace Content.IntegrationTests.Tests.Buckle
var mapManager = IoCManager.Resolve<IMapManager>();
var entityManager = IoCManager.Resolve<IEntityManager>();
var grid = GetMainGrid(mapManager);
var coordinates = new EntityCoordinates(grid.GridEntityId, 0, 0);
human = entityManager.SpawnEntity(BuckleDummyId, coordinates);
chair = entityManager.SpawnEntity(StrapDummyId, coordinates);
@@ -331,7 +330,7 @@ namespace Content.IntegrationTests.Tests.Buckle
entityManager.GetComponent<TransformComponent>(human).WorldPosition += (100, 0);
});
await WaitUntil(server, () => !buckle.Buckled, 10);
await PoolManager.WaitUntil(server, () => !buckle.Buckled, 10);
Assert.False(buckle.Buckled);
@@ -356,6 +355,7 @@ namespace Content.IntegrationTests.Tests.Buckle
Assert.NotNull(buckle.BuckledTo);
Assert.True(buckle.Buckled);
});
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -0,0 +1,39 @@
using System.Threading.Tasks;
using Content.Server.Cargo.Systems;
using Content.Shared.Cargo.Prototypes;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests;
[TestFixture]
public sealed class CargoTest
{
[Test]
public async Task NoArbitrage()
{
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings() {NoClient = true});
var server = pairTracker.Pair.Server;
var entManager = server.ResolveDependency<IEntityManager>();
var mapManager = server.ResolveDependency<IMapManager>();
var protoManager = server.ResolveDependency<IPrototypeManager>();
var pricing = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<PricingSystem>();
await server.WaitAssertion(() =>
{
var mapId = mapManager.CreateMap();
foreach (var proto in protoManager.EnumeratePrototypes<CargoProductPrototype>())
{
var ent = entManager.SpawnEntity(proto.Product, new MapCoordinates(Vector2.Zero, mapId));
var price = pricing.GetPrice(ent);
Assert.That(price, Is.LessThan(proto.PointCost), $"Found arbitrage on {proto.ID} cargo product!");
}
});
}
}

View File

@@ -14,7 +14,7 @@ namespace Content.IntegrationTests.Tests.Chemistry;
// reactions can change this assumption
[TestFixture]
[TestOf(typeof(SolutionContainerSystem))]
public sealed class SolutionSystemTests : ContentIntegrationTest
public sealed class SolutionSystemTests
{
private const string Prototypes = @"
- type: entity
@@ -25,22 +25,19 @@ public sealed class SolutionSystemTests : ContentIntegrationTest
beaker:
maxVol: 50
";
[Test]
public async Task TryAddTwoNonReactiveReagent()
{
var options = new ServerContentIntegrationOption { ExtraPrototypes = Prototypes };
var server = StartServer(options);
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
var entityManager = server.ResolveDependency<IEntityManager>();
var mapManager = server.ResolveDependency<IMapManager>();
var coordinates = GetMainEntityCoordinates(mapManager);
var testMap = await PoolManager.CreateTestMap(pairTracker);
var coordinates = testMap.GridCoords;
EntityUid beaker;
server.Assert(() =>
await server.WaitAssertion(() =>
{
var oilQuantity = FixedPoint2.New(15);
var waterQuantity = FixedPoint2.New(10);
@@ -62,7 +59,7 @@ public sealed class SolutionSystemTests : ContentIntegrationTest
Assert.That(oil, Is.EqualTo(oilQuantity));
});
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
// This test mimics current behavior
@@ -70,18 +67,17 @@ public sealed class SolutionSystemTests : ContentIntegrationTest
[Test]
public async Task TryAddTooMuchNonReactiveReagent()
{
var options = new ServerContentIntegrationOption { ExtraPrototypes = Prototypes };
var server = StartServer(options);
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
await server.WaitIdleAsync();
var testMap = await PoolManager.CreateTestMap(pairTracker);
var entityManager = server.ResolveDependency<IEntityManager>();
var mapManager = server.ResolveDependency<IMapManager>();
var coordinates = GetMainEntityCoordinates(mapManager);
var coordinates = testMap.GridCoords;
EntityUid beaker;
server.Assert(() =>
await server.WaitAssertion(() =>
{
var oilQuantity = FixedPoint2.New(1500);
var waterQuantity = FixedPoint2.New(10);
@@ -103,25 +99,24 @@ public sealed class SolutionSystemTests : ContentIntegrationTest
Assert.That(oil, Is.EqualTo(FixedPoint2.Zero));
});
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
// Unlike TryAddSolution this adds and two solution without then splits leaving only threshold in original
[Test]
public async Task TryMixAndOverflowTooMuchReagent()
{
var options = new ServerContentIntegrationOption { ExtraPrototypes = Prototypes };
var server = StartServer(options);
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
await server.WaitIdleAsync();
var entityManager = server.ResolveDependency<IEntityManager>();
var mapManager = server.ResolveDependency<IMapManager>();
var coordinates = GetMainEntityCoordinates(mapManager);
var testMap = await PoolManager.CreateTestMap(pairTracker);
var coordinates = testMap.GridCoords;
EntityUid beaker;
server.Assert(() =>
await server.WaitAssertion(() =>
{
int ratio = 9;
int threshold = 20;
@@ -152,25 +147,23 @@ public sealed class SolutionSystemTests : ContentIntegrationTest
Assert.That(oilOverFlow, Is.EqualTo(oilQuantity - oilMix));
});
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
// TryMixAndOverflow will fail if Threshold larger than MaxVolume
[Test]
public async Task TryMixAndOverflowTooBigOverflow()
{
var options = new ServerContentIntegrationOption { ExtraPrototypes = Prototypes };
var server = StartServer(options);
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
var entityManager = server.ResolveDependency<IEntityManager>();
var mapManager = server.ResolveDependency<IMapManager>();
var coordinates = GetMainEntityCoordinates(mapManager);
var testMap = await PoolManager.CreateTestMap(pairTracker);
var coordinates = testMap.GridCoords;
EntityUid beaker;
server.Assert(() =>
await server.WaitAssertion(() =>
{
int ratio = 9;
int threshold = 60;
@@ -190,6 +183,6 @@ public sealed class SolutionSystemTests : ContentIntegrationTest
Is.False);
});
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
}

View File

@@ -14,7 +14,7 @@ namespace Content.IntegrationTests.Tests.Chemistry
{
[TestFixture]
[TestOf(typeof(ReactionPrototype))]
public sealed class TryAllReactionsTest : ContentIntegrationTest
public sealed class TryAllReactionsTest
{
private const string Prototypes = @"
- type: entity
@@ -24,19 +24,16 @@ namespace Content.IntegrationTests.Tests.Chemistry
solutions:
beaker:
maxVol: 50";
[Test]
public async Task TryAllTest()
{
var options = new ServerContentIntegrationOption{ExtraPrototypes = Prototypes};
var server = StartServer(options);
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
var entityManager = server.ResolveDependency<IEntityManager>();
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
var mapManager = server.ResolveDependency<IMapManager>();
var coordinates = GetMainEntityCoordinates(mapManager);
var testMap = await PoolManager.CreateTestMap(pairTracker);
var coordinates = testMap.GridCoords;
foreach (var reactionPrototype in prototypeManager.EnumeratePrototypes<ReactionPrototype>())
{
@@ -46,7 +43,7 @@ namespace Content.IntegrationTests.Tests.Chemistry
EntityUid beaker;
Solution component = null;
server.Assert(() =>
await server.WaitAssertion(() =>
{
beaker = entityManager.SpawnEntity("TestSolutionContainer", coordinates);
Assert.That(EntitySystem.Get<SolutionContainerSystem>()
@@ -63,7 +60,7 @@ namespace Content.IntegrationTests.Tests.Chemistry
await server.WaitIdleAsync();
server.Assert(() =>
await server.WaitAssertion(() =>
{
//you just got linq'd fool
//(i'm sorry)
@@ -78,8 +75,9 @@ namespace Content.IntegrationTests.Tests.Chemistry
Assert.That(foundProductsMap.All(x => x.Value));
});
}
}
await pairTracker.CleanReturnAsync();
}
}

View File

@@ -4,37 +4,20 @@ using Content.Client.Clickable;
using NUnit.Framework;
using Robust.Client.Graphics;
using Robust.Server.GameObjects;
using Robust.Shared;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
namespace Content.IntegrationTests.Tests
{
[TestFixture]
public sealed class ClickableTest : ContentIntegrationTest
public sealed class ClickableTest
{
private ClientIntegrationInstance _client;
private ServerIntegrationInstance _server;
private const double DirSouth = 0;
private const double DirNorth = Math.PI;
private const double DirEast = Math.PI / 2;
private const double DirSouthEast = Math.PI / 4;
private const double DirSouthEastJustShy = Math.PI / 4 - 0.1;
[OneTimeSetUp]
public async Task Setup()
{
(_client, _server) = await StartConnectedServerClientPair(serverOptions: new ServerContentIntegrationOption()
{
CVarOverrides =
{
[CVars.NetPVS.Name] = "false"
}
});
}
[Parallelizable(ParallelScope.None)]
[Test]
[TestCase("ClickTestRotatingCornerVisible", 0.25f, 0.25f, DirSouth, 1, ExpectedResult = true)]
[TestCase("ClickTestRotatingCornerVisible", 0.35f, 0.5f, DirSouth, 2, ExpectedResult = true)]
@@ -64,26 +47,29 @@ namespace Content.IntegrationTests.Tests
[TestCase("ClickTestRotatingCornerInvisibleNoRot", 0.25f, 0.25f, DirSouthEastJustShy, 1, ExpectedResult = true)]
public async Task<bool> Test(string prototype, float clickPosX, float clickPosY, double angle, float scale)
{
await using var pairTracker = await PoolManager.GetServerClient();
var server = pairTracker.Pair.Server;
var client = pairTracker.Pair.Client;
EntityUid entity = default;
var clientEntManager = _client.ResolveDependency<IEntityManager>();
var serverEntManager = _server.ResolveDependency<IEntityManager>();
var eyeManager = _client.ResolveDependency<IEyeManager>();
var mapManager = _server.ResolveDependency<IMapManager>();
var clientEntManager = client.ResolveDependency<IEntityManager>();
var serverEntManager = server.ResolveDependency<IEntityManager>();
var eyeManager = client.ResolveDependency<IEyeManager>();
await _server.WaitPost(() =>
var testMap = await PoolManager.CreateTestMap(pairTracker);
await server.WaitPost(() =>
{
var ent = serverEntManager.SpawnEntity(prototype, GetMainEntityCoordinates(mapManager));
var ent = serverEntManager.SpawnEntity(prototype, testMap.GridCoords);
serverEntManager.GetComponent<TransformComponent>(ent).WorldRotation = angle;
serverEntManager.GetComponent<SpriteComponent>(ent).Scale = (scale, scale);
entity = ent;
});
// Let client sync up.
await RunTicksSync(_client, _server, 5);
await PoolManager.RunTicksSync(pairTracker.Pair, 5);
var hit = false;
await _client.WaitPost(() =>
await client.WaitPost(() =>
{
// these tests currently all assume player eye is 0
eyeManager.CurrentEye.Rotation = 0;
@@ -94,11 +80,13 @@ namespace Content.IntegrationTests.Tests
hit = clickable.CheckClick((clickPosX, clickPosY) + pos, out _, out _);
});
await _server.WaitPost(() =>
await server.WaitPost(() =>
{
serverEntManager.DeleteEntity(entity);
});
await pairTracker.CleanReturnAsync();
return hit;
}
}

View File

@@ -10,16 +10,15 @@ namespace Content.IntegrationTests.Tests.Commands
{
[TestFixture]
[TestOf(typeof(PardonCommand))]
public sealed class PardonCommand : ContentIntegrationTest
public sealed class PardonCommand
{
private static readonly TimeSpan MarginOfError = TimeSpan.FromMinutes(1);
[Test]
public async Task PardonTest()
{
var (client, server) = await StartConnectedServerClientPair();
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
await using var pairTracker = await PoolManager.GetServerClient(new (){Destructive = true});
var server = pairTracker.Pair.Server;
var sPlayerManager = server.ResolveDependency<IPlayerManager>();
var sConsole = server.ResolveDependency<IServerConsoleHost>();
@@ -122,6 +121,7 @@ namespace Content.IntegrationTests.Tests.Commands
// The list is still returned since that ignores pardons
Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null), Has.Count.EqualTo(1));
});
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -14,7 +14,7 @@ namespace Content.IntegrationTests.Tests.Commands
{
[TestFixture]
[TestOf(typeof(RejuvenateCommand))]
public sealed class RejuvenateTest : ContentIntegrationTest
public sealed class RejuvenateTest
{
private const string Prototypes = @"
- type: entity
@@ -33,8 +33,8 @@ namespace Content.IntegrationTests.Tests.Commands
[Test]
public async Task RejuvenateDeadTest()
{
var options = new ServerIntegrationOptions{ExtraPrototypes = Prototypes};
var server = StartServer(options);
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
await server.WaitAssertion(() =>
{
@@ -78,6 +78,7 @@ namespace Content.IntegrationTests.Tests.Commands
Assert.That(damageable.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
});
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -12,33 +12,27 @@ namespace Content.IntegrationTests.Tests.Commands
{
[TestFixture]
[TestOf(typeof(RestartRoundNowCommand))]
public sealed class RestartRoundNowTest : ContentIntegrationTest
public sealed class RestartRoundNowTest
{
[Test]
[TestCase(true)]
[TestCase(false)]
public async Task RestartRoundAfterStart(bool lobbyEnabled)
{
var (_, server) = await StartConnectedServerClientPair(serverOptions: new ServerContentIntegrationOption
{
CVarOverrides =
{
[CCVars.GameMap.Name] = "saltern"
}
});
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings(){Dirty = true});
var server = pairTracker.Pair.Server;
var configManager = server.ResolveDependency<IConfigurationManager>();
var entityManager = server.ResolveDependency<IEntityManager>();
var gameTicker = entityManager.EntitySysManager.GetEntitySystem<GameTicker>();
await server.WaitRunTicks(30);
await PoolManager.RunTicksSync(pairTracker.Pair, 5);
GameTick tickBeforeRestart = default;
server.Assert(() =>
await server.WaitAssertion(() =>
{
Assert.That(configManager.GetCVar<bool>(CCVars.GameLobbyEnabled), Is.EqualTo(false));
configManager.SetCVar(CCVars.GameLobbyEnabled, lobbyEnabled);
Assert.That(gameTicker.RunLevel, Is.EqualTo(GameRunLevel.InRound));
@@ -54,17 +48,17 @@ namespace Content.IntegrationTests.Tests.Commands
}
});
await server.WaitIdleAsync();
await server.WaitRunTicks(5);
await PoolManager.RunTicksSync(pairTracker.Pair, 15);
server.Assert(() =>
await server.WaitAssertion(() =>
{
var tickAfterRestart = entityManager.CurrentTick;
Assert.That(tickBeforeRestart < tickAfterRestart);
});
await server.WaitRunTicks(60);
await PoolManager.RunTicksSync(pairTracker.Pair, 5);
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -9,7 +9,7 @@ using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests.Construction
{
[TestFixture]
public sealed class ConstructionActionValid : ContentIntegrationTest
public sealed class ConstructionActionValid
{
private bool IsValid(IGraphAction action, IPrototypeManager protoMan, out string prototype)
{
@@ -49,9 +49,8 @@ namespace Content.IntegrationTests.Tests.Construction
[Test]
public async Task ConstructionGraphSpawnPrototypeValid()
{
var server = StartServer();
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
var protoMan = server.ResolveDependency<IPrototypeManager>();
@@ -82,6 +81,7 @@ namespace Content.IntegrationTests.Tests.Construction
}
}
}
await pairTracker.CleanReturnAsync();
Assert.That(valid, Is.True, $"One or more SpawnPrototype actions specified invalid entity prototypes!\n{message}");
}
@@ -89,9 +89,8 @@ namespace Content.IntegrationTests.Tests.Construction
[Test]
public async Task ConstructionGraphNodeEntityPrototypeValid()
{
var server = StartServer();
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
var protoMan = server.ResolveDependency<IPrototypeManager>();
@@ -108,6 +107,7 @@ namespace Content.IntegrationTests.Tests.Construction
message.Append($"Invalid entity prototype \"{node.Entity}\" on node \"{node.Name}\" of graph \"{graph.ID}\"\n");
}
}
await pairTracker.CleanReturnAsync();
Assert.That(valid, Is.True, $"One or more nodes specified invalid entity prototypes!\n{message}");
}
@@ -115,9 +115,8 @@ namespace Content.IntegrationTests.Tests.Construction
[Test]
public async Task ConstructionGraphEdgeValid()
{
var server = StartServer();
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
var protoMan = server.ResolveDependency<IPrototypeManager>();
@@ -139,6 +138,7 @@ namespace Content.IntegrationTests.Tests.Construction
}
Assert.That(valid, Is.True, $"One or more edges specified invalid node targets!\n{message}");
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -6,7 +6,7 @@ using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests.Construction
{
[TestFixture]
public sealed class ConstructionPrototypeTest : ContentIntegrationTest
public sealed class ConstructionPrototypeTest
{
// discount linter for construction graphs
// TODO: Create serialization validators for these?
@@ -14,9 +14,8 @@ namespace Content.IntegrationTests.Tests.Construction
[Test]
public async Task TestStartIsValid()
{
var server = StartServer();
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
var protoMan = server.ResolveDependency<IPrototypeManager>();
@@ -27,14 +26,14 @@ namespace Content.IntegrationTests.Tests.Construction
Assert.That(graph.Nodes.ContainsKey(start), $"Found no startNode \"{start}\" on graph \"{graph.ID}\" for construction prototype \"{proto.ID}\"!");
}
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task TestTargetIsValid()
{
var server = StartServer();
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
var protoMan = server.ResolveDependency<IPrototypeManager>();
@@ -45,14 +44,14 @@ namespace Content.IntegrationTests.Tests.Construction
Assert.That(graph.Nodes.ContainsKey(target), $"Found no targetNode \"{target}\" on graph \"{graph.ID}\" for construction prototype \"{proto.ID}\"!");
}
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task TestStartReachesValidTarget()
{
var server = StartServer();
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
var protoMan = server.ResolveDependency<IPrototypeManager>();
@@ -68,6 +67,7 @@ namespace Content.IntegrationTests.Tests.Construction
Assert.That(protoMan.TryIndex(next.Entity, out EntityPrototype entity), $"The next node ({next.Name}) in the path from the start node ({start}) to the target node ({target}) specified an invalid entity prototype ({next.Entity})");
Assert.That(entity.Components.ContainsKey("Construction"), $"The next node ({next.Name}) in the path from the start node ({start}) to the target node ({target}) specified an entity prototype ({next.Entity}) without a ConstructionComponent.");
}
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -3,8 +3,6 @@ using System.Threading.Tasks;
using Content.Server.Storage.Components;
using NUnit.Framework;
using Robust.Client.GameObjects;
using Robust.Server.Player;
using Robust.Shared;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
@@ -12,9 +10,9 @@ using Robust.Shared.Maths;
namespace Content.IntegrationTests.Tests
{
public sealed class ContainerOcclusionTest : ContentIntegrationTest
public sealed class ContainerOcclusionTest
{
private const string ExtraPrototypes = @"
private const string Prototypes = @"
- type: entity
id: ContainerOcclusionA
components:
@@ -35,53 +33,21 @@ namespace Content.IntegrationTests.Tests
- type: PointLight
";
private async Task<(ClientIntegrationInstance c, ServerIntegrationInstance s)> Start()
{
var optsServer = new ServerIntegrationOptions
{
CVarOverrides =
{
{CVars.NetPVS.Name, "false"}
},
ExtraPrototypes = ExtraPrototypes
};
var optsClient = new ClientIntegrationOptions
{
CVarOverrides =
{
{CVars.NetPVS.Name, "false"}
},
ExtraPrototypes = ExtraPrototypes
};
var (c, s) = await StartConnectedServerDummyTickerClientPair(optsClient, optsServer);
s.Post(() =>
{
IoCManager.Resolve<IPlayerManager>().ServerSessions.Single().JoinGame();
var mapMan = IoCManager.Resolve<IMapManager>();
mapMan.CreateMap(new MapId(1));
});
return (c, s);
}
[Test]
public async Task TestA()
{
var (c, s) = await Start();
await c.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{ExtraPrototypes = Prototypes});
var s = pairTracker.Pair.Server;
var c = pairTracker.Pair.Client;
var cEntities = c.ResolveDependency<IEntityManager>();
EntityUid dummy = default;
s.Post(() =>
var ent2 = s.ResolveDependency<IMapManager>();
await s.WaitPost(() =>
{
var pos = new MapCoordinates(Vector2.Zero, new MapId(1));
var mapId = ent2.GetAllMapIds().Last();
var pos = new MapCoordinates(Vector2.Zero, mapId);
var ent = IoCManager.Resolve<IEntityManager>();
var container = ent.SpawnEntity("ContainerOcclusionA", pos);
dummy = ent.SpawnEntity("ContainerOcclusionDummy", pos);
@@ -89,9 +55,9 @@ namespace Content.IntegrationTests.Tests
ent.GetComponent<EntityStorageComponent>(container).Insert(dummy);
});
await RunTicksSync(c, s, 5);
await PoolManager.RunTicksSync(pairTracker.Pair, 5);
c.Assert(() =>
await c.WaitAssertion(() =>
{
var sprite = cEntities.GetComponent<SpriteComponent>(dummy);
var light = cEntities.GetComponent<PointLightComponent>(dummy);
@@ -99,22 +65,24 @@ namespace Content.IntegrationTests.Tests
Assert.True(light.ContainerOccluded);
});
await Task.WhenAll(c.WaitIdleAsync(), s.WaitIdleAsync());
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task TestB()
{
var (c, s) = await Start();
await c.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{ExtraPrototypes = Prototypes});
var s = pairTracker.Pair.Server;
var c = pairTracker.Pair.Client;
var cEntities = c.ResolveDependency<IEntityManager>();
var ent2 = s.ResolveDependency<IMapManager>();
EntityUid dummy = default;
s.Post(() =>
await s.WaitPost(() =>
{
var pos = new MapCoordinates(Vector2.Zero, new MapId(1));
var mapId = ent2.GetAllMapIds().Last();
var pos = new MapCoordinates(Vector2.Zero, mapId);
var ent = IoCManager.Resolve<IEntityManager>();
var container = ent.SpawnEntity("ContainerOcclusionB", pos);
dummy = ent.SpawnEntity("ContainerOcclusionDummy", pos);
@@ -122,9 +90,9 @@ namespace Content.IntegrationTests.Tests
ent.GetComponent<EntityStorageComponent>(container).Insert(dummy);
});
await RunTicksSync(c, s, 5);
await PoolManager.RunTicksSync(pairTracker.Pair, 5);
c.Assert(() =>
await c.WaitAssertion(() =>
{
var sprite = cEntities.GetComponent<SpriteComponent>(dummy);
var light = cEntities.GetComponent<PointLightComponent>(dummy);
@@ -132,22 +100,24 @@ namespace Content.IntegrationTests.Tests
Assert.False(light.ContainerOccluded);
});
await Task.WhenAll(c.WaitIdleAsync(), s.WaitIdleAsync());
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task TestAb()
{
var (c, s) = await Start();
await c.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{ExtraPrototypes = Prototypes});
var s = pairTracker.Pair.Server;
var c = pairTracker.Pair.Client;
var ent2 = s.ResolveDependency<IMapManager>();
var cEntities = c.ResolveDependency<IEntityManager>();
EntityUid dummy = default;
s.Post(() =>
await s.WaitPost(() =>
{
var pos = new MapCoordinates(Vector2.Zero, new MapId(1));
var mapId = ent2.GetAllMapIds().Last();
var pos = new MapCoordinates(Vector2.Zero, mapId);
var ent = IoCManager.Resolve<IEntityManager>();
var containerA = ent.SpawnEntity("ContainerOcclusionA", pos);
var containerB = ent.SpawnEntity("ContainerOcclusionB", pos);
@@ -157,9 +127,9 @@ namespace Content.IntegrationTests.Tests
ent.GetComponent<EntityStorageComponent>(containerB).Insert(dummy);
});
await RunTicksSync(c, s, 5);
await PoolManager.RunTicksSync(pairTracker.Pair, 5);
c.Assert(() =>
await c.WaitAssertion(() =>
{
var sprite = cEntities.GetComponent<SpriteComponent>(dummy);
var light = cEntities.GetComponent<PointLightComponent>(dummy);
@@ -167,7 +137,7 @@ namespace Content.IntegrationTests.Tests
Assert.True(light.ContainerOccluded);
});
await Task.WhenAll(c.WaitIdleAsync(), s.WaitIdleAsync());
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -7,7 +7,7 @@ namespace Content.IntegrationTests.Tests.Damageable;
[TestFixture]
[TestOf(typeof(DamageSpecifier))]
public sealed class DamageSpecifierTest : ContentIntegrationTest
public sealed class DamageSpecifierTest
{
[Test]
public void TestDamageSpecifierOperations()

View File

@@ -14,9 +14,9 @@ namespace Content.IntegrationTests.Tests.Damageable
[TestFixture]
[TestOf(typeof(DamageableComponent))]
[TestOf(typeof(DamageableSystem))]
public sealed class DamageableTest : ContentIntegrationTest
public sealed class DamageableTest
{
private const string Prototypes = @"
public const string Prototypes = @"
# Define some damage groups
- type: damageType
id: TestDamage1
@@ -71,30 +71,17 @@ namespace Content.IntegrationTests.Tests.Damageable
damageContainer: testDamageContainer
";
// public bool & function to determine whether dealing damage resulted in actual damage change
public bool DamageChanged = false;
public void DamageChangedListener(EntityUid _, DamageableComponent comp, DamageChangedEvent args)
{
DamageChanged = true;
}
[Test]
public async Task TestDamageableComponents()
{
var server = StartServerDummyTicker(new ServerContentIntegrationOption
{
ExtraPrototypes = Prototypes
});
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
var sEntityManager = server.ResolveDependency<IEntityManager>();
var sMapManager = server.ResolveDependency<IMapManager>();
var sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
var sEntitySystemManager = server.ResolveDependency<IEntitySystemManager>();
sEntityManager.EventBus.SubscribeLocalEvent<DamageableComponent, DamageChangedEvent>(DamageChangedListener);
EntityUid sDamageableEntity = default;
DamageableComponent sDamageableComponent = null;
DamageableSystem sDamageableSystem = null;
@@ -153,8 +140,7 @@ namespace Content.IntegrationTests.Tests.Damageable
DamageSpecifier damage = new(group3, damageToDeal);
sDamageableSystem.TryChangeDamage(uid, damage, true);
Assert.That(DamageChanged);
DamageChanged = false;
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal));
Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(damageToDeal));
foreach (var type in types)
@@ -165,8 +151,7 @@ namespace Content.IntegrationTests.Tests.Damageable
// Heal
sDamageableSystem.TryChangeDamage(uid, -damage);
Assert.That(DamageChanged);
DamageChanged = false;
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(FixedPoint2.Zero));
foreach (var type in types)
@@ -180,8 +165,7 @@ namespace Content.IntegrationTests.Tests.Damageable
damageToDeal = FixedPoint2.New(types.Count() * 5 - 1);
damage = new DamageSpecifier(group3, damageToDeal);
sDamageableSystem.TryChangeDamage(uid, damage, true);
Assert.That(DamageChanged);
DamageChanged = false;
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal));
Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(damageToDeal));
Assert.That(sDamageableComponent.Damage.DamageDict[type3a.ID], Is.EqualTo(damageToDeal / types.Count()));
@@ -192,8 +176,6 @@ namespace Content.IntegrationTests.Tests.Damageable
// Heal
sDamageableSystem.TryChangeDamage(uid, -damage);
Assert.That(DamageChanged);
DamageChanged = false;
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(FixedPoint2.Zero));
foreach (var type in types)
@@ -206,7 +188,6 @@ namespace Content.IntegrationTests.Tests.Damageable
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
damage = new DamageSpecifier(group1, FixedPoint2.New(10)) + new DamageSpecifier(type2b, FixedPoint2.New(10));
sDamageableSystem.TryChangeDamage(uid, damage, true);
Assert.That(DamageChanged, Is.False);
Assert.That(sDamageableComponent.DamagePerGroup.TryGetValue(group1.ID, out groupDamage), Is.False);
Assert.That(sDamageableComponent.Damage.DamageDict.TryGetValue(type1.ID, out typeDamage), Is.False);
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
@@ -227,15 +208,13 @@ namespace Content.IntegrationTests.Tests.Damageable
// Test Over-Healing
sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(group3, FixedPoint2.New(-100)));
Assert.That(DamageChanged);
DamageChanged = false;
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
// Test that if no health change occurred, returns false
sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(group3, -100));
Assert.That(DamageChanged, Is.False);
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
});
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -11,14 +11,15 @@ using Robust.Shared.Map;
namespace Content.IntegrationTests.Tests
{
[TestFixture]
public sealed class DeleteInventoryTest : ContentIntegrationTest
public sealed class DeleteInventoryTest
{
// Test that when deleting an entity with an InventoryComponent,
// any equipped items also get deleted.
[Test]
public async Task Test()
{
var server = StartServer();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
await server.WaitAssertion(() =>
{
@@ -46,6 +47,7 @@ namespace Content.IntegrationTests.Tests
// Assert that child item was also deleted.
Assert.That(item.Deleted, Is.True);
});
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -15,20 +15,17 @@ namespace Content.IntegrationTests.Tests.Destructible
[TestFixture]
[TestOf(typeof(DamageGroupTrigger))]
[TestOf(typeof(AndTrigger))]
public sealed class DestructibleDamageGroupTest : ContentIntegrationTest
public sealed class DestructibleDamageGroupTest
{
[Test]
public async Task AndTest()
{
var server = StartServer(new ServerContentIntegrationOption
{
ExtraPrototypes = Prototypes
});
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
await server.WaitIdleAsync();
var testMap = await PoolManager.CreateTestMap(pairTracker);
var sEntityManager = server.ResolveDependency<IEntityManager>();
var sMapManager = server.ResolveDependency<IMapManager>();
var sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
var sEntitySystemManager = server.ResolveDependency<IEntitySystemManager>();
@@ -39,8 +36,7 @@ namespace Content.IntegrationTests.Tests.Destructible
await server.WaitPost(() =>
{
var gridId = GetMainGrid(sMapManager).GridEntityId;
var coordinates = new EntityCoordinates(gridId, 0, 0);
var coordinates = testMap.GridCoords;
sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleDamageGroupEntityId, coordinates);
sDamageableComponent = IoCManager.Resolve<IEntityManager>().GetComponent<DamageableComponent>(sDestructibleEntity);
@@ -184,6 +180,7 @@ namespace Content.IntegrationTests.Tests.Destructible
// No new thresholds reached as triggers once is set to true and it already triggered before
Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached);
});
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -14,20 +14,17 @@ namespace Content.IntegrationTests.Tests.Destructible
[TestFixture]
[TestOf(typeof(DamageTypeTrigger))]
[TestOf(typeof(AndTrigger))]
public sealed class DestructibleDamageTypeTest : ContentIntegrationTest
public sealed class DestructibleDamageTypeTest
{
[Test]
public async Task Test()
{
var server = StartServer(new ServerContentIntegrationOption
{
ExtraPrototypes = Prototypes
});
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
await server.WaitIdleAsync();
var testMap = await PoolManager.CreateTestMap(pairTracker);
var sEntityManager = server.ResolveDependency<IEntityManager>();
var sMapManager = server.ResolveDependency<IMapManager>();
var sEntitySystemManager = server.ResolveDependency<IEntitySystemManager>();
EntityUid sDestructibleEntity = default;
@@ -37,8 +34,7 @@ namespace Content.IntegrationTests.Tests.Destructible
await server.WaitPost(() =>
{
var gridId = GetMainGrid(sMapManager).GridEntityId;
var coordinates = new EntityCoordinates(gridId, 0, 0);
var coordinates = testMap.GridCoords;
sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleDamageTypeEntityId, coordinates);
sDamageableComponent = IoCManager.Resolve<IEntityManager>().GetComponent<DamageableComponent>(sDestructibleEntity);
@@ -180,6 +176,7 @@ namespace Content.IntegrationTests.Tests.Destructible
// No new thresholds reached as triggers once is set to true and it already triggered before
Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached);
});
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -13,20 +13,17 @@ using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototy
namespace Content.IntegrationTests.Tests.Destructible
{
public sealed class DestructibleDestructionTest : ContentIntegrationTest
public sealed class DestructibleDestructionTest
{
[Test]
public async Task Test()
{
var server = StartServer(new ServerContentIntegrationOption
{
ExtraPrototypes = Prototypes
});
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
await server.WaitIdleAsync();
var testMap = await PoolManager.CreateTestMap(pairTracker);
var sEntityManager = server.ResolveDependency<IEntityManager>();
var sMapManager = server.ResolveDependency<IMapManager>();
var sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
var sEntitySystemManager = server.ResolveDependency<IEntitySystemManager>();
@@ -35,8 +32,7 @@ namespace Content.IntegrationTests.Tests.Destructible
await server.WaitPost(() =>
{
var gridId = GetMainGrid(sMapManager).GridEntityId;
var coordinates = new EntityCoordinates(gridId, 0, 0);
var coordinates = testMap.GridCoords;
sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleDestructionEntityId, coordinates);
sTestThresholdListenerSystem = sEntitySystemManager.GetEntitySystem<TestDestructibleListenerSystem>();
@@ -87,6 +83,7 @@ namespace Content.IntegrationTests.Tests.Destructible
Assert.That(found, Is.True);
});
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -19,23 +19,20 @@ namespace Content.IntegrationTests.Tests.Destructible
[TestFixture]
[TestOf(typeof(DestructibleComponent))]
[TestOf(typeof(DamageThreshold))]
public sealed class DestructibleThresholdActivationTest : ContentIntegrationTest
public sealed class DestructibleThresholdActivationTest
{
[Test]
public async Task Test()
{
var server = StartServer(new ServerContentIntegrationOption
{
ExtraPrototypes = Prototypes
});
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
var sEntityManager = server.ResolveDependency<IEntityManager>();
var sMapManager = server.ResolveDependency<IMapManager>();
var sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
var sEntitySystemManager = server.ResolveDependency<IEntitySystemManager>();
var testMap = await PoolManager.CreateTestMap(pairTracker);
EntityUid sDestructibleEntity = default;
DamageableComponent sDamageableComponent = null;
DestructibleComponent sDestructibleComponent = null;
@@ -44,8 +41,7 @@ namespace Content.IntegrationTests.Tests.Destructible
await server.WaitPost(() =>
{
var gridId = GetMainGrid(sMapManager).GridEntityId;
var coordinates = new EntityCoordinates(gridId, 0, 0);
var coordinates = testMap.GridCoords;
sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleEntityId, coordinates);
sDamageableComponent = IoCManager.Resolve<IEntityManager>().GetComponent<DamageableComponent>(sDestructibleEntity);
@@ -268,6 +264,7 @@ namespace Content.IntegrationTests.Tests.Destructible
// They shouldn't have been triggered by changing TriggersOnce
Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty);
});
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -13,7 +13,7 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork
[TestOf(typeof(DeviceNetworkComponent))]
[TestOf(typeof(WiredNetworkComponent))]
[TestOf(typeof(WirelessNetworkComponent))]
public sealed class DeviceNetworkTest : ContentIntegrationTest
public sealed class DeviceNetworkTest
{
private const string Prototypes = @"
- type: entity
@@ -50,17 +50,8 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork
[Test]
public async Task NetworkDeviceSendAndReceive()
{
var options = new ServerContentIntegrationOption
{
ExtraPrototypes = Prototypes,
ContentBeforeIoC = () => {
IoCManager.Resolve<IEntitySystemManager>().LoadExtraSystemType<DeviceNetworkTestSystem>();
}
};
var server = StartServer(options);
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>();
@@ -81,7 +72,7 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork
["testbool"] = true
};
server.Assert(() => {
await server.WaitAssertion(() => {
mapManager.CreateNewMapEntity(MapId.Nullspace);
device1 = entityManager.SpawnEntity("DummyNetworkDevice", MapCoordinates.Nullspace);
@@ -104,25 +95,17 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork
await server.WaitRunTicks(1);
await server.WaitIdleAsync();
server.Assert(() => {
await server.WaitAssertion(() => {
CollectionAssert.AreEquivalent(deviceNetTestSystem.LastPayload, payload);
});
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task WirelessNetworkDeviceSendAndReceive()
{
var options = new ServerContentIntegrationOption
{
ExtraPrototypes = Prototypes,
ContentBeforeIoC = () => {
IoCManager.Resolve<IEntitySystemManager>().LoadExtraSystemType<DeviceNetworkTestSystem>();
}
};
var server = StartServer(options);
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>();
@@ -144,7 +127,7 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork
["testbool"] = true
};
server.Assert(() => {
await server.WaitAssertion(() => {
mapManager.CreateNewMapEntity(MapId.Nullspace);
device1 = entityManager.SpawnEntity("DummyWirelessNetworkDevice", MapCoordinates.Nullspace);
@@ -168,7 +151,7 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork
await server.WaitRunTicks(1);
await server.WaitIdleAsync();
server.Assert(() => {
await server.WaitAssertion(() => {
CollectionAssert.AreEqual(deviceNetTestSystem.LastPayload, payload);
payload = new NetworkPayload
@@ -184,27 +167,18 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork
await server.WaitRunTicks(1);
await server.WaitIdleAsync();
server.Assert(() => {
await server.WaitAssertion(() => {
CollectionAssert.AreNotEqual(deviceNetTestSystem.LastPayload, payload);
});
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task WiredNetworkDeviceSendAndReceive()
{
var options = new ServerContentIntegrationOption
{
ExtraPrototypes = Prototypes,
ContentBeforeIoC = () => {
IoCManager.Resolve<IEntitySystemManager>().LoadExtraSystemType<DeviceNetworkTestSystem>();
}
};
var server = StartServer(options);
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>();
@@ -230,7 +204,7 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork
await server.WaitRunTicks(1);
await server.WaitIdleAsync();
server.Assert(() => {
await server.WaitAssertion(() => {
var map = mapManager.CreateNewMapEntity(MapId.Nullspace);
grid = mapManager.CreateGrid(MapId.Nullspace);
@@ -255,7 +229,7 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork
await server.WaitRunTicks(1);
await server.WaitIdleAsync();
server.Assert(() => {
await server.WaitAssertion(() => {
//CollectionAssert.AreNotEqual(deviceNetTestSystem.LastPayload, payload);
entityManager.SpawnEntity("CableApcExtension", grid.MapToGrid(new MapCoordinates(new Robust.Shared.Maths.Vector2(0, 1), MapId.Nullspace)));
@@ -266,11 +240,11 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork
await server.WaitRunTicks(1);
await server.WaitIdleAsync();
server.Assert(() => {
await server.WaitAssertion(() => {
CollectionAssert.AreEqual(deviceNetTestSystem.LastPayload, payload);
});
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -18,7 +18,7 @@ namespace Content.IntegrationTests.Tests.Disposal
[TestOf(typeof(DisposalHolderComponent))]
[TestOf(typeof(DisposalEntryComponent))]
[TestOf(typeof(DisposalUnitComponent))]
public sealed class DisposalUnitTest : ContentIntegrationTest
public sealed class DisposalUnitTest
{
[Reflect(false)]
private sealed class DisposalUnitTestSystem : EntitySystem
@@ -127,9 +127,10 @@ namespace Content.IntegrationTests.Tests.Disposal
[Test]
public async Task Test()
{
var options = new ServerIntegrationOptions { ExtraPrototypes = Prototypes };
var server = StartServer(options);
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
var testMap = await PoolManager.CreateTestMap(pairTracker);
EntityUid human = default!;
EntityUid wrench = default!;
@@ -137,13 +138,12 @@ namespace Content.IntegrationTests.Tests.Disposal
EntityUid disposalTrunk = default!;
DisposalUnitComponent unit = default!;
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>();
await server.WaitAssertion(() =>
{
// Spawn the entities
var coordinates = GetMainEntityCoordinates(mapManager);
var coordinates = testMap.GridCoords;
human = entityManager.SpawnEntity("HumanDummy", coordinates);
wrench = entityManager.SpawnEntity("WrenchDummy", coordinates);
disposalUnit = entityManager.SpawnEntity("DisposalUnitDummy", coordinates);
@@ -208,6 +208,7 @@ namespace Content.IntegrationTests.Tests.Disposal
// Re-pressurizing
Flush(unit, false);
});
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -11,7 +11,7 @@ namespace Content.IntegrationTests.Tests.DoAfter
{
[TestFixture]
[TestOf(typeof(DoAfterComponent))]
public sealed class DoAfterServerTest : ContentIntegrationTest
public sealed class DoAfterServerTest
{
private const string Prototypes = @"
- type: entity
@@ -25,11 +25,11 @@ namespace Content.IntegrationTests.Tests.DoAfter
public async Task TestFinished()
{
Task<DoAfterStatus> task = null;
var options = new ServerIntegrationOptions{ExtraPrototypes = Prototypes};
var server = StartServer(options);
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
// That it finishes successfully
server.Post(() =>
await server.WaitPost(() =>
{
var tickTime = 1.0f / IoCManager.Resolve<IGameTiming>().TickRate;
var mapManager = IoCManager.Resolve<IMapManager>();
@@ -43,16 +43,19 @@ namespace Content.IntegrationTests.Tests.DoAfter
await server.WaitRunTicks(1);
Assert.That(task.Result == DoAfterStatus.Finished);
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task TestCancelled()
{
Task<DoAfterStatus> task = null;
var options = new ServerIntegrationOptions{ExtraPrototypes = Prototypes};
var server = StartServer(options);
server.Post(() =>
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
await server.WaitPost(() =>
{
var tickTime = 1.0f / IoCManager.Resolve<IGameTiming>().TickRate;
var mapManager = IoCManager.Resolve<IMapManager>();
@@ -67,6 +70,8 @@ namespace Content.IntegrationTests.Tests.DoAfter
await server.WaitRunTicks(3);
Assert.That(task.Result == DoAfterStatus.Cancelled, $"Result was {task.Result}");
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -12,7 +12,7 @@ namespace Content.IntegrationTests.Tests.Doors
{
[TestFixture]
[TestOf(typeof(AirlockComponent))]
public sealed class AirlockTest : ContentIntegrationTest
public sealed class AirlockTest
{
private const string Prototypes = @"
- type: entity
@@ -48,10 +48,8 @@ namespace Content.IntegrationTests.Tests.Doors
[Test]
public async Task OpenCloseDestroyTest()
{
var options = new ServerIntegrationOptions {ExtraPrototypes = Prototypes};
var server = StartServer(options);
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>();
@@ -59,7 +57,7 @@ namespace Content.IntegrationTests.Tests.Doors
EntityUid airlock = default;
DoorComponent doorComponent = null;
server.Assert(() =>
await server.WaitAssertion(() =>
{
mapManager.CreateNewMapEntity(MapId.Nullspace);
@@ -71,7 +69,7 @@ namespace Content.IntegrationTests.Tests.Doors
await server.WaitIdleAsync();
server.Assert(() =>
await server.WaitAssertion(() =>
{
EntitySystem.Get<DoorSystem>().StartOpening(airlock);
Assert.That(doorComponent.State, Is.EqualTo(DoorState.Opening));
@@ -79,21 +77,21 @@ namespace Content.IntegrationTests.Tests.Doors
await server.WaitIdleAsync();
await WaitUntil(server, () => doorComponent.State == DoorState.Open);
await PoolManager.WaitUntil(server, () => doorComponent.State == DoorState.Open);
Assert.That(doorComponent.State, Is.EqualTo(DoorState.Open));
server.Assert(() =>
await server.WaitAssertion(() =>
{
EntitySystem.Get<DoorSystem>().TryClose((EntityUid) airlock);
Assert.That(doorComponent.State, Is.EqualTo(DoorState.Closing));
});
await WaitUntil(server, () => doorComponent.State == DoorState.Closed);
await PoolManager.WaitUntil(server, () => doorComponent.State == DoorState.Closed);
Assert.That(doorComponent.State, Is.EqualTo(DoorState.Closed));
server.Assert(() =>
await server.WaitAssertion(() =>
{
Assert.DoesNotThrow(() =>
{
@@ -103,17 +101,14 @@ namespace Content.IntegrationTests.Tests.Doors
server.RunTicks(5);
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task AirlockBlockTest()
{
var options = new ServerContentIntegrationOption
{
ExtraPrototypes = Prototypes
};
var server = StartServer(options);
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
await server.WaitIdleAsync();
@@ -127,7 +122,7 @@ namespace Content.IntegrationTests.Tests.Doors
var physicsDummyStartingX = -1;
server.Assert(() =>
await server.WaitAssertion(() =>
{
var mapId = mapManager.CreateMap();
@@ -151,7 +146,7 @@ namespace Content.IntegrationTests.Tests.Doors
for (var i = 0; i < 240; i += 10)
{
// Keep the airlock awake so they collide
server.Post(() => entityManager.GetComponent<IPhysBody>(airlock).WakeBody());
await server.WaitPost(() => entityManager.GetComponent<IPhysBody>(airlock).WakeBody());
await server.WaitRunTicks(10);
await server.WaitIdleAsync();
@@ -165,6 +160,7 @@ namespace Content.IntegrationTests.Tests.Doors
// Blocked by the airlock
await server.WaitAssertion(() => Assert.That(Math.Abs(entityManager.GetComponent<TransformComponent>(physicsDummy).MapPosition.X - 1) > 0.01f));
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -9,16 +9,17 @@ using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests
{
[TestFixture]
public sealed class DummyIconTest : ContentIntegrationTest
public sealed class DummyIconTest
{
[Test]
public async Task Test()
{
var (client, _) = await StartConnectedServerClientPair(new ClientContentIntegrationOption(){ Pool = false }, new ServerContentIntegrationOption() { Pool = false });
await using var pairTracker = await PoolManager.GetServerClient();
var client = pairTracker.Pair.Client;
var prototypeManager = client.ResolveDependency<IPrototypeManager>();
var resourceCache = client.ResolveDependency<IResourceCache>();
await client.WaitRunTicks(5);
await client.WaitAssertion(() =>
{
foreach (var proto in prototypeManager.EnumeratePrototypes<EntityPrototype>())
@@ -32,6 +33,8 @@ namespace Content.IntegrationTests.Tests
proto.ID);
}
});
await client.WaitRunTicks(5);
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -15,18 +15,13 @@ namespace Content.IntegrationTests.Tests
{
[TestFixture]
[TestOf(typeof(EntityUid))]
public sealed class EntityTest : ContentIntegrationTest
public sealed class EntityTest
{
[Test]
public async Task SpawnTest()
{
var options = new ServerContentIntegrationOption()
{
CVarOverrides = {{CCVars.NPCMaxUpdates.Name, int.MaxValue.ToString()}}
};
var server = StartServer(options);
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, Dirty = true});
var server = pairTracker.Pair.Server;
var mapManager = server.ResolveDependency<IMapManager>();
var entityMan = server.ResolveDependency<IEntityManager>();
@@ -38,7 +33,7 @@ namespace Content.IntegrationTests.Tests
EntityUid testEntity;
//Build up test environment
server.Post(() =>
await server.WaitPost(() =>
{
// Create a one tile grid to stave off the grid 0 monsters
var mapId = mapManager.CreateMap();
@@ -56,7 +51,9 @@ namespace Content.IntegrationTests.Tests
mapManager.DoMapInitialize(mapId);
});
server.Assert(() =>
await server.WaitRunTicks(5);
await server.WaitAssertion(() =>
{
var testLocation = grid.ToCoordinates();
@@ -80,7 +77,7 @@ namespace Content.IntegrationTests.Tests
{
Logger.LogS(LogLevel.Debug, "EntityTest", $"Testing: {prototype.ID}");
testEntity = entityMan.SpawnEntity(prototype.ID, testLocation);
server.RunTicks(2);
server.RunTicks(1);
if(!entityMan.Deleted(testEntity))
entityMan.DeleteEntity(testEntity);
}, "Entity '{0}' threw an exception.",
@@ -89,7 +86,7 @@ namespace Content.IntegrationTests.Tests
});
});
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
[Test]
@@ -110,12 +107,8 @@ namespace Content.IntegrationTests.Tests
- type: entity
id: AllComponentsOneToOneDeleteTestEntity";
var server = StartServer(new ServerContentIntegrationOption
{
ExtraPrototypes = testEntity,
FailureLogLevel = LogLevel.Error
});
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = testEntity});
var server = pairTracker.Pair.Server;
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>();
@@ -124,7 +117,7 @@ namespace Content.IntegrationTests.Tests
IMapGrid grid = default;
server.Post(() =>
await server.WaitPost(() =>
{
// Create a one tile grid to stave off the grid 0 monsters
var mapId = mapManager.CreateMap();
@@ -142,7 +135,9 @@ namespace Content.IntegrationTests.Tests
mapManager.DoMapInitialize(mapId);
});
server.Assert(() =>
await server.WaitRunTicks(5);
await server.WaitAssertion(() =>
{
Assert.Multiple(() =>
{
@@ -179,14 +174,12 @@ namespace Content.IntegrationTests.Tests
}, "Component '{0}' threw an exception.",
component.Name);
server.RunTicks(2);
entityManager.DeleteEntity(entity);
}
});
});
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
[Test]
@@ -207,13 +200,8 @@ namespace Content.IntegrationTests.Tests
- type: entity
id: AllComponentsOneEntityDeleteTestEntity";
var server = StartServer(new ServerContentIntegrationOption
{
ExtraPrototypes = testEntity,
FailureLogLevel = LogLevel.Error,
Pool = false
});
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = testEntity});
var server = pairTracker.Pair.Server;
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>();
@@ -222,7 +210,7 @@ namespace Content.IntegrationTests.Tests
IMapGrid grid = default;
server.Post(() =>
await server.WaitPost(() =>
{
// Create a one tile grid to stave off the grid 0 monsters
var mapId = mapManager.CreateMap();
@@ -237,6 +225,7 @@ namespace Content.IntegrationTests.Tests
grid.SetTile(Vector2i.Zero, tile);
mapManager.DoMapInitialize(mapId);
});
await server.WaitRunTicks(5);
var distinctComponents = new List<(List<CompIdx> components, List<CompIdx> references)>
{
@@ -272,7 +261,7 @@ namespace Content.IntegrationTests.Tests
// Sanity check
Assert.That(distinctComponents, Is.Not.Empty);
server.Assert(() =>
await server.WaitAssertion(() =>
{
Assert.Multiple(() =>
{
@@ -313,14 +302,12 @@ namespace Content.IntegrationTests.Tests
}, "Component '{0}' threw an exception.",
component.Name);
}
server.RunTicks(2);
entityManager.DeleteEntity(entity);
}
});
});
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
}
}

View File

@@ -15,7 +15,7 @@ namespace Content.IntegrationTests.Tests.Fluids;
[TestFixture]
[TestOf(typeof(FluidSpreaderSystem))]
public sealed class FluidSpill : ContentIntegrationTest
public sealed class FluidSpill
{
private static PuddleComponent? GetPuddle(IEntityManager entityManager, IMapGrid mapGrid, Vector2i pos)
{
@@ -46,9 +46,8 @@ public sealed class FluidSpill : ContentIntegrationTest
[Test]
public async Task SpillEvenlyTest()
{
// --- Setup
var server = StartServer();
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>();
@@ -85,7 +84,7 @@ public sealed class FluidSpill : ContentIntegrationTest
var sTimeToWait = (int) Math.Ceiling(2f * gameTiming.TickRate);
await server.WaitRunTicks(sTimeToWait);
server.Assert(() =>
await server.WaitAssertion(() =>
{
var grid = mapManager.GetGrid(gridId);
var puddle = GetPuddle(entityManager, grid, _origin);
@@ -102,16 +101,15 @@ public sealed class FluidSpill : ContentIntegrationTest
}
});
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task SpillSmallOverflowTest()
{
// --- Setup
var server = StartServer();
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient();
var server = pairTracker.Pair.Server;
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>();
@@ -147,9 +145,9 @@ public sealed class FluidSpill : ContentIntegrationTest
});
var sTimeToWait = (int) Math.Ceiling(2f * gameTiming.TickRate);
await server.WaitRunTicks(sTimeToWait);
await PoolManager.RunTicksSync(pairTracker.Pair, sTimeToWait);
server.Assert(() =>
await server.WaitAssertion(() =>
{
var grid = mapManager.GetGrid(gridId);
var puddle = GetPuddle(entityManager, grid, _origin);
@@ -178,6 +176,6 @@ public sealed class FluidSpill : ContentIntegrationTest
Assert.That(fullField, Is.EqualTo(1));
});
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
}

View File

@@ -14,50 +14,52 @@ namespace Content.IntegrationTests.Tests.Fluids
{
[TestFixture]
[TestOf(typeof(PuddleComponent))]
public sealed class PuddleTest : ContentIntegrationTest
public sealed class PuddleTest
{
[Test]
public async Task TilePuddleTest()
{
var server = StartServer();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
await server.WaitIdleAsync();
var testMap = await PoolManager.CreateTestMap(pairTracker);
var mapManager = server.ResolveDependency<IMapManager>();
var entitySystemManager = server.ResolveDependency<IEntitySystemManager>();
var spillSystem = entitySystemManager.GetEntitySystem<SpillableSystem>();
server.Assert(() =>
await server.WaitAssertion(() =>
{
var solution = new Solution("Water", FixedPoint2.New(20));
var grid = GetMainGrid(mapManager);
var (x, y) = GetMainTile(grid).GridIndices;
var coordinates = new EntityCoordinates(grid.GridEntityId, x, y);
var tile = testMap.Tile;
var gridUid = tile.GridUid;
var (x, y) = tile.GridIndices;
var coordinates = new EntityCoordinates(gridUid, x, y);
var puddle = spillSystem.SpillAt(solution, coordinates, "PuddleSmear");
Assert.NotNull(puddle);
});
await PoolManager.RunTicksSync(pairTracker.Pair, 5);
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task SpaceNoPuddleTest()
{
var server = StartServer();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
await server.WaitIdleAsync();
var testMap = await PoolManager.CreateTestMap(pairTracker);
var mapManager = server.ResolveDependency<IMapManager>();
var entitySystemManager = server.ResolveDependency<IEntitySystemManager>();
var spillSystem = entitySystemManager.GetEntitySystem<SpillableSystem>();
IMapGrid grid = null;
// Remove all tiles
server.Post(() =>
await server.WaitPost(() =>
{
grid = GetMainGrid(mapManager);
grid = testMap.MapGrid;
foreach (var tile in grid.GetAllTiles())
{
@@ -65,9 +67,9 @@ namespace Content.IntegrationTests.Tests.Fluids
}
});
await server.WaitIdleAsync();
await PoolManager.RunTicksSync(pairTracker.Pair, 5);
server.Assert(() =>
await server.WaitAssertion(() =>
{
var coordinates = grid.ToCoordinates();
var solution = new Solution("Water", FixedPoint2.New(20));
@@ -75,15 +77,14 @@ namespace Content.IntegrationTests.Tests.Fluids
Assert.Null(puddle);
});
await server.WaitIdleAsync();
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task PuddlePauseTest()
{
var server = StartServer();
await server.WaitIdleAsync();
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
var sMapManager = server.ResolveDependency<IMapManager>();
var sTileDefinitionManager = server.ResolveDependency<ITileDefinitionManager>();
@@ -154,8 +155,8 @@ namespace Content.IntegrationTests.Tests.Fluids
});
// Wait enough time for it to evaporate if it was unpaused
var sTimeToWait = (5 + (int)Math.Ceiling(amount * evaporateTime * sGameTiming.TickRate));
await server.WaitRunTicks(sTimeToWait);
var sTimeToWait = 5 + (int)Math.Ceiling(amount * evaporateTime * sGameTiming.TickRate);
await PoolManager.RunTicksSync(pairTracker.Pair, sTimeToWait);
// No evaporation due to being paused
await server.WaitAssertion(() =>
@@ -181,7 +182,7 @@ namespace Content.IntegrationTests.Tests.Fluids
});
// Wait enough time for it to evaporate
await server.WaitRunTicks(sTimeToWait);
await PoolManager.RunTicksSync(pairTracker.Pair, sTimeToWait);
// Puddle evaporation should have ticked
await server.WaitAssertion(() =>
@@ -189,6 +190,7 @@ namespace Content.IntegrationTests.Tests.Fluids
// Check that puddle has been deleted
Assert.True(puddle.Deleted);
});
await pairTracker.CleanReturnAsync();
}
}
}

Some files were not shown because too many files have changed in this diff Show More