Merge branch 'master' of https://github.com/space-wizards/space-station-14 into pr/34711
This commit is contained in:
45
.github/workflows/publish-testing.yml
vendored
Normal file
45
.github/workflows/publish-testing.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
name: Publish Testing
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: publish-testing
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 10 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3.6.0
|
||||||
|
with:
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: Setup .NET Core
|
||||||
|
uses: actions/setup-dotnet@v3.2.0
|
||||||
|
with:
|
||||||
|
dotnet-version: 9.0.x
|
||||||
|
|
||||||
|
- name: Get Engine Tag
|
||||||
|
run: |
|
||||||
|
cd RobustToolbox
|
||||||
|
git fetch --depth=1
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: dotnet restore
|
||||||
|
|
||||||
|
- name: Build Packaging
|
||||||
|
run: dotnet build Content.Packaging --configuration Release --no-restore /m
|
||||||
|
|
||||||
|
- name: Package server
|
||||||
|
run: dotnet run --project Content.Packaging server --platform win-x64 --platform linux-x64 --platform osx-x64 --platform linux-arm64
|
||||||
|
|
||||||
|
- name: Package client
|
||||||
|
run: dotnet run --project Content.Packaging client --no-wipe-release
|
||||||
|
|
||||||
|
- name: Publish version
|
||||||
|
run: Tools/publish_multi_request.py --fork-id wizards-testing
|
||||||
|
env:
|
||||||
|
PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
|
||||||
|
GITHUB_REPOSITORY: ${{ vars.GITHUB_REPOSITORY }}
|
||||||
@@ -12,12 +12,12 @@ You want to handle the Build, Clean and Rebuild tasks to prevent missing task er
|
|||||||
If you want to learn more about these kinds of things, check out Microsoft's official documentation about MSBuild:
|
If you want to learn more about these kinds of things, check out Microsoft's official documentation about MSBuild:
|
||||||
https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild
|
https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild
|
||||||
-->
|
-->
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Python>python3</Python>
|
<Python>python3</Python>
|
||||||
<Python Condition="'$(OS)'=='Windows_NT' Or '$(OS)'=='Windows'">py -3</Python>
|
<Python Condition="'$(OS)'=='Windows_NT' Or '$(OS)'=='Windows'">py -3</Python>
|
||||||
<ProjectGuid>{C899FCA4-7037-4E49-ABC2-44DE72487110}</ProjectGuid>
|
<ProjectGuid>{C899FCA4-7037-4E49-ABC2-44DE72487110}</ProjectGuid>
|
||||||
<TargetFrameworkMoniker>.NETFramework, Version=v4.7.2</TargetFrameworkMoniker>
|
<TargetFramework>net4.7.2</TargetFramework>
|
||||||
<RestorePackages>false</RestorePackages>
|
<RestorePackages>false</RestorePackages>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ using Content.IntegrationTests.Pair;
|
|||||||
using Content.Shared.Clothing.Components;
|
using Content.Shared.Clothing.Components;
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
using Content.Shared.Item;
|
using Content.Shared.Item;
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared;
|
using Robust.Shared;
|
||||||
using Robust.Shared.Analyzers;
|
using Robust.Shared.Analyzers;
|
||||||
|
using Robust.Shared.EntitySerialization;
|
||||||
|
using Robust.Shared.EntitySerialization.Systems;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Benchmarks;
|
namespace Content.Benchmarks;
|
||||||
|
|
||||||
@@ -32,7 +33,6 @@ public class ComponentQueryBenchmark
|
|||||||
|
|
||||||
private TestPair _pair = default!;
|
private TestPair _pair = default!;
|
||||||
private IEntityManager _entMan = default!;
|
private IEntityManager _entMan = default!;
|
||||||
private MapId _mapId = new(10);
|
|
||||||
private EntityQuery<ItemComponent> _itemQuery;
|
private EntityQuery<ItemComponent> _itemQuery;
|
||||||
private EntityQuery<ClothingComponent> _clothingQuery;
|
private EntityQuery<ClothingComponent> _clothingQuery;
|
||||||
private EntityQuery<MapComponent> _mapQuery;
|
private EntityQuery<MapComponent> _mapQuery;
|
||||||
@@ -54,10 +54,10 @@ public class ComponentQueryBenchmark
|
|||||||
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
||||||
_pair.Server.WaitPost(() =>
|
_pair.Server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
var success = _entMan.System<MapLoaderSystem>().TryLoad(_mapId, Map, out _);
|
var map = new ResPath(Map);
|
||||||
if (!success)
|
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||||
|
if (!_entMan.System<MapLoaderSystem>().TryLoadMap(map, out _, out _, opts))
|
||||||
throw new Exception("Map load failed");
|
throw new Exception("Map load failed");
|
||||||
_pair.Server.MapMan.DoMapInitialize(_mapId);
|
|
||||||
}).GetAwaiter().GetResult();
|
}).GetAwaiter().GetResult();
|
||||||
|
|
||||||
_items = new EntityUid[_entMan.Count<ItemComponent>()];
|
_items = new EntityUid[_entMan.Count<ItemComponent>()];
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ using BenchmarkDotNet.Attributes;
|
|||||||
using Content.IntegrationTests;
|
using Content.IntegrationTests;
|
||||||
using Content.IntegrationTests.Pair;
|
using Content.IntegrationTests.Pair;
|
||||||
using Content.Server.Maps;
|
using Content.Server.Maps;
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared;
|
using Robust.Shared;
|
||||||
using Robust.Shared.Analyzers;
|
using Robust.Shared.Analyzers;
|
||||||
|
using Robust.Shared.EntitySerialization.Systems;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Benchmarks;
|
namespace Content.Benchmarks;
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ public class MapLoadBenchmark
|
|||||||
{
|
{
|
||||||
private TestPair _pair = default!;
|
private TestPair _pair = default!;
|
||||||
private MapLoaderSystem _mapLoader = default!;
|
private MapLoaderSystem _mapLoader = default!;
|
||||||
private IMapManager _mapManager = default!;
|
private SharedMapSystem _mapSys = default!;
|
||||||
|
|
||||||
[GlobalSetup]
|
[GlobalSetup]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
@@ -36,7 +37,7 @@ public class MapLoadBenchmark
|
|||||||
.ToDictionary(x => x.ID, x => x.MapPath.ToString());
|
.ToDictionary(x => x.ID, x => x.MapPath.ToString());
|
||||||
|
|
||||||
_mapLoader = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>();
|
_mapLoader = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>();
|
||||||
_mapManager = server.ResolveDependency<IMapManager>();
|
_mapSys = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<SharedMapSystem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[GlobalCleanup]
|
[GlobalCleanup]
|
||||||
@@ -46,23 +47,25 @@ public class MapLoadBenchmark
|
|||||||
PoolManager.Shutdown();
|
PoolManager.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly string[] MapsSource = { "Empty", "Satlern", "Box", "Bagel", "Dev", "CentComm", "Core", "TestTeg", "Packed", "Omega", "Reach", "Meta", "Marathon", "MeteorArena", "Fland", "Oasis", "Cog", "Convex"};
|
public static readonly string[] MapsSource = { "Empty", "Satlern", "Box", "Bagel", "Dev", "CentComm", "Core", "TestTeg", "Packed", "Omega", "Reach", "Meta", "Marathon", "MeteorArena", "Fland", "Oasis", "Convex"};
|
||||||
|
|
||||||
[ParamsSource(nameof(MapsSource))]
|
[ParamsSource(nameof(MapsSource))]
|
||||||
public string Map;
|
public string Map;
|
||||||
|
|
||||||
public Dictionary<string, string> Paths;
|
public Dictionary<string, string> Paths;
|
||||||
|
private MapId _mapId;
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public async Task LoadMap()
|
public async Task LoadMap()
|
||||||
{
|
{
|
||||||
var mapPath = Paths[Map];
|
var mapPath = new ResPath(Paths[Map]);
|
||||||
var server = _pair.Server;
|
var server = _pair.Server;
|
||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
var success = _mapLoader.TryLoad(new MapId(10), mapPath, out _);
|
var success = _mapLoader.TryLoadMap(mapPath, out var map, out _);
|
||||||
if (!success)
|
if (!success)
|
||||||
throw new Exception("Map load failed");
|
throw new Exception("Map load failed");
|
||||||
|
_mapId = map.Value.Comp.MapId;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,9 +73,7 @@ public class MapLoadBenchmark
|
|||||||
public void IterationCleanup()
|
public void IterationCleanup()
|
||||||
{
|
{
|
||||||
var server = _pair.Server;
|
var server = _pair.Server;
|
||||||
server.WaitPost(() =>
|
server.WaitPost(() => _mapSys.DeleteMap(_mapId))
|
||||||
{
|
.Wait();
|
||||||
_mapManager.DeleteMap(new MapId(10));
|
|
||||||
}).Wait();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,15 @@ using Content.IntegrationTests;
|
|||||||
using Content.IntegrationTests.Pair;
|
using Content.IntegrationTests.Pair;
|
||||||
using Content.Server.Mind;
|
using Content.Server.Mind;
|
||||||
using Content.Server.Warps;
|
using Content.Server.Warps;
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared;
|
using Robust.Shared;
|
||||||
using Robust.Shared.Analyzers;
|
using Robust.Shared.Analyzers;
|
||||||
|
using Robust.Shared.EntitySerialization;
|
||||||
|
using Robust.Shared.EntitySerialization.Systems;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Benchmarks;
|
namespace Content.Benchmarks;
|
||||||
|
|
||||||
@@ -34,7 +36,6 @@ public class PvsBenchmark
|
|||||||
|
|
||||||
private TestPair _pair = default!;
|
private TestPair _pair = default!;
|
||||||
private IEntityManager _entMan = default!;
|
private IEntityManager _entMan = default!;
|
||||||
private MapId _mapId = new(10);
|
|
||||||
private ICommonSession[] _players = default!;
|
private ICommonSession[] _players = default!;
|
||||||
private EntityCoordinates[] _spawns = default!;
|
private EntityCoordinates[] _spawns = default!;
|
||||||
public int _cycleOffset = 0;
|
public int _cycleOffset = 0;
|
||||||
@@ -65,10 +66,10 @@ public class PvsBenchmark
|
|||||||
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
||||||
await _pair.Server.WaitPost(() =>
|
await _pair.Server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
var success = _entMan.System<MapLoaderSystem>().TryLoad(_mapId, Map, out _);
|
var path = new ResPath(Map);
|
||||||
if (!success)
|
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||||
|
if (!_entMan.System<MapLoaderSystem>().TryLoadMap(path, out _, out _, opts))
|
||||||
throw new Exception("Map load failed");
|
throw new Exception("Map load failed");
|
||||||
_pair.Server.MapMan.DoMapInitialize(_mapId);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get list of ghost warp positions
|
// Get list of ghost warp positions
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ namespace Content.Client.Actions
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
component.Whitelist = state.Whitelist;
|
component.Whitelist = state.Whitelist;
|
||||||
|
component.Blacklist = state.Blacklist;
|
||||||
component.CanTargetSelf = state.CanTargetSelf;
|
component.CanTargetSelf = state.CanTargetSelf;
|
||||||
BaseHandleState<EntityTargetActionComponent>(uid, component, state);
|
BaseHandleState<EntityTargetActionComponent>(uid, component, state);
|
||||||
}
|
}
|
||||||
@@ -137,6 +138,7 @@ namespace Content.Client.Actions
|
|||||||
component.Priority = state.Priority;
|
component.Priority = state.Priority;
|
||||||
component.AttachedEntity = EnsureEntity<T>(state.AttachedEntity, uid);
|
component.AttachedEntity = EnsureEntity<T>(state.AttachedEntity, uid);
|
||||||
component.RaiseOnUser = state.RaiseOnUser;
|
component.RaiseOnUser = state.RaiseOnUser;
|
||||||
|
component.RaiseOnAction = state.RaiseOnAction;
|
||||||
component.AutoPopulate = state.AutoPopulate;
|
component.AutoPopulate = state.AutoPopulate;
|
||||||
component.Temporary = state.Temporary;
|
component.Temporary = state.Temporary;
|
||||||
component.ItemIconStyle = state.ItemIconStyle;
|
component.ItemIconStyle = state.ItemIconStyle;
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ public sealed class AtmosAlertsComputerBoundUserInterface : BoundUserInterface
|
|||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
_menu = new AtmosAlertsComputerWindow(this, Owner);
|
_menu = new AtmosAlertsComputerWindow(this, Owner);
|
||||||
_menu.OpenCentered();
|
_menu.OpenCentered();
|
||||||
_menu.OnClose += Close;
|
_menu.OnClose += Close;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ namespace Content.Client.Atmos.EntitySystems
|
|||||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||||
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
||||||
[Dependency] private readonly SpriteSystem _spriteSys = default!;
|
[Dependency] private readonly SpriteSystem _spriteSys = default!;
|
||||||
|
[Dependency] private readonly SharedTransformSystem _xformSys = default!;
|
||||||
|
|
||||||
private GasTileOverlay _overlay = default!;
|
private GasTileOverlay _overlay = default!;
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ namespace Content.Client.Atmos.EntitySystems
|
|||||||
SubscribeNetworkEvent<GasOverlayUpdateEvent>(HandleGasOverlayUpdate);
|
SubscribeNetworkEvent<GasOverlayUpdateEvent>(HandleGasOverlayUpdate);
|
||||||
SubscribeLocalEvent<GasTileOverlayComponent, ComponentHandleState>(OnHandleState);
|
SubscribeLocalEvent<GasTileOverlayComponent, ComponentHandleState>(OnHandleState);
|
||||||
|
|
||||||
_overlay = new GasTileOverlay(this, EntityManager, _resourceCache, ProtoMan, _spriteSys);
|
_overlay = new GasTileOverlay(this, EntityManager, _resourceCache, ProtoMan, _spriteSys, _xformSys);
|
||||||
_overlayMan.AddOverlay(_overlay);
|
_overlayMan.AddOverlay(_overlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ namespace Content.Client.Atmos.Overlays
|
|||||||
{
|
{
|
||||||
private readonly IEntityManager _entManager;
|
private readonly IEntityManager _entManager;
|
||||||
private readonly IMapManager _mapManager;
|
private readonly IMapManager _mapManager;
|
||||||
|
private readonly SharedTransformSystem _xformSys;
|
||||||
|
|
||||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceEntities | OverlaySpace.WorldSpaceBelowWorld;
|
public override OverlaySpace Space => OverlaySpace.WorldSpaceEntities | OverlaySpace.WorldSpaceBelowWorld;
|
||||||
private readonly ShaderInstance _shader;
|
private readonly ShaderInstance _shader;
|
||||||
@@ -46,10 +47,11 @@ namespace Content.Client.Atmos.Overlays
|
|||||||
|
|
||||||
public const int GasOverlayZIndex = (int) Shared.DrawDepth.DrawDepth.Effects; // Under ghosts, above mostly everything else
|
public const int GasOverlayZIndex = (int) Shared.DrawDepth.DrawDepth.Effects; // Under ghosts, above mostly everything else
|
||||||
|
|
||||||
public GasTileOverlay(GasTileOverlaySystem system, IEntityManager entManager, IResourceCache resourceCache, IPrototypeManager protoMan, SpriteSystem spriteSys)
|
public GasTileOverlay(GasTileOverlaySystem system, IEntityManager entManager, IResourceCache resourceCache, IPrototypeManager protoMan, SpriteSystem spriteSys, SharedTransformSystem xformSys)
|
||||||
{
|
{
|
||||||
_entManager = entManager;
|
_entManager = entManager;
|
||||||
_mapManager = IoCManager.Resolve<IMapManager>();
|
_mapManager = IoCManager.Resolve<IMapManager>();
|
||||||
|
_xformSys = xformSys;
|
||||||
_shader = protoMan.Index<ShaderPrototype>("unshaded").Instance();
|
_shader = protoMan.Index<ShaderPrototype>("unshaded").Instance();
|
||||||
ZIndex = GasOverlayZIndex;
|
ZIndex = GasOverlayZIndex;
|
||||||
|
|
||||||
@@ -158,7 +160,8 @@ namespace Content.Client.Atmos.Overlays
|
|||||||
_fireFrameCounter,
|
_fireFrameCounter,
|
||||||
_shader,
|
_shader,
|
||||||
overlayQuery,
|
overlayQuery,
|
||||||
xformQuery);
|
xformQuery,
|
||||||
|
_xformSys);
|
||||||
|
|
||||||
var mapUid = _mapManager.GetMapEntityId(args.MapId);
|
var mapUid = _mapManager.GetMapEntityId(args.MapId);
|
||||||
|
|
||||||
@@ -180,7 +183,8 @@ namespace Content.Client.Atmos.Overlays
|
|||||||
int[] fireFrameCounter,
|
int[] fireFrameCounter,
|
||||||
ShaderInstance shader,
|
ShaderInstance shader,
|
||||||
EntityQuery<GasTileOverlayComponent> overlayQuery,
|
EntityQuery<GasTileOverlayComponent> overlayQuery,
|
||||||
EntityQuery<TransformComponent> xformQuery) state) =>
|
EntityQuery<TransformComponent> xformQuery,
|
||||||
|
SharedTransformSystem xformSys) state) =>
|
||||||
{
|
{
|
||||||
if (!state.overlayQuery.TryGetComponent(uid, out var comp) ||
|
if (!state.overlayQuery.TryGetComponent(uid, out var comp) ||
|
||||||
!state.xformQuery.TryGetComponent(uid, out var gridXform))
|
!state.xformQuery.TryGetComponent(uid, out var gridXform))
|
||||||
@@ -188,7 +192,7 @@ namespace Content.Client.Atmos.Overlays
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var (_, _, worldMatrix, invMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv();
|
var (_, _, worldMatrix, invMatrix) = state.xformSys.GetWorldPositionRotationMatrixWithInv(gridXform);
|
||||||
state.drawHandle.SetTransform(worldMatrix);
|
state.drawHandle.SetTransform(worldMatrix);
|
||||||
var floatBounds = invMatrix.TransformBox(state.WorldBounds).Enlarged(grid.TileSize);
|
var floatBounds = invMatrix.TransformBox(state.WorldBounds).Enlarged(grid.TileSize);
|
||||||
var localBounds = new Box2i(
|
var localBounds = new Box2i(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<DefaultWindow xmlns="https://spacestation14.io"
|
<DefaultWindow xmlns="https://spacestation14.io"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
MinSize="280 160" Title="Temperature Control Unit">
|
MinSize="280 160" Title="{Loc comp-space-heater-ui-title}">
|
||||||
|
|
||||||
<BoxContainer Name="VboxContainer" Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
|
<BoxContainer Name="VboxContainer" Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,15 @@ using Content.Shared.Buckle;
|
|||||||
using Content.Shared.Buckle.Components;
|
using Content.Shared.Buckle.Components;
|
||||||
using Content.Shared.Rotation;
|
using Content.Shared.Rotation;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Client.Graphics;
|
||||||
|
|
||||||
namespace Content.Client.Buckle;
|
namespace Content.Client.Buckle;
|
||||||
|
|
||||||
internal sealed class BuckleSystem : SharedBuckleSystem
|
internal sealed class BuckleSystem : SharedBuckleSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly RotationVisualizerSystem _rotationVisualizerSystem = default!;
|
[Dependency] private readonly RotationVisualizerSystem _rotationVisualizerSystem = default!;
|
||||||
|
[Dependency] private readonly IEyeManager _eye = default!;
|
||||||
|
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -17,6 +19,8 @@ internal sealed class BuckleSystem : SharedBuckleSystem
|
|||||||
|
|
||||||
SubscribeLocalEvent<BuckleComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
SubscribeLocalEvent<BuckleComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||||
SubscribeLocalEvent<StrapComponent, MoveEvent>(OnStrapMoveEvent);
|
SubscribeLocalEvent<StrapComponent, MoveEvent>(OnStrapMoveEvent);
|
||||||
|
SubscribeLocalEvent<BuckleComponent, BuckledEvent>(OnBuckledEvent);
|
||||||
|
SubscribeLocalEvent<BuckleComponent, UnbuckledEvent>(OnUnbuckledEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStrapMoveEvent(EntityUid uid, StrapComponent component, ref MoveEvent args)
|
private void OnStrapMoveEvent(EntityUid uid, StrapComponent component, ref MoveEvent args)
|
||||||
@@ -28,13 +32,21 @@ internal sealed class BuckleSystem : SharedBuckleSystem
|
|||||||
// This code is garbage, it doesn't work with rotated viewports. I need to finally get around to reworking
|
// This code is garbage, it doesn't work with rotated viewports. I need to finally get around to reworking
|
||||||
// sprite rendering for entity layers & direction dependent sorting.
|
// sprite rendering for entity layers & direction dependent sorting.
|
||||||
|
|
||||||
|
// Future notes:
|
||||||
|
// Right now this doesn't handle: other grids, other grids rotating, the camera rotation changing, and many other fun rotation specific things
|
||||||
|
// The entire thing should be a concern of the engine, or something engine helps to implement properly.
|
||||||
|
// Give some of the sprite rotations their own drawdepth, maybe as an offset within the rsi, or something like this
|
||||||
|
// And we won't ever need to set the draw depth manually
|
||||||
|
|
||||||
if (args.NewRotation == args.OldRotation)
|
if (args.NewRotation == args.OldRotation)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!TryComp<SpriteComponent>(uid, out var strapSprite))
|
if (!TryComp<SpriteComponent>(uid, out var strapSprite))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var isNorth = Transform(uid).LocalRotation.GetCardinalDir() == Direction.North;
|
var angle = _xformSystem.GetWorldRotation(uid) + _eye.CurrentEye.Rotation; // Get true screen position, or close enough
|
||||||
|
|
||||||
|
var isNorth = angle.GetCardinalDir() == Direction.North;
|
||||||
foreach (var buckledEntity in component.BuckledEntities)
|
foreach (var buckledEntity in component.BuckledEntities)
|
||||||
{
|
{
|
||||||
if (!TryComp<BuckleComponent>(buckledEntity, out var buckle))
|
if (!TryComp<BuckleComponent>(buckledEntity, out var buckle))
|
||||||
@@ -45,6 +57,7 @@ internal sealed class BuckleSystem : SharedBuckleSystem
|
|||||||
|
|
||||||
if (isNorth)
|
if (isNorth)
|
||||||
{
|
{
|
||||||
|
// This will only assign if empty, it won't get overwritten by new depth on multiple calls, which do happen easily
|
||||||
buckle.OriginalDrawDepth ??= buckledSprite.DrawDepth;
|
buckle.OriginalDrawDepth ??= buckledSprite.DrawDepth;
|
||||||
buckledSprite.DrawDepth = strapSprite.DrawDepth - 1;
|
buckledSprite.DrawDepth = strapSprite.DrawDepth - 1;
|
||||||
}
|
}
|
||||||
@@ -56,6 +69,42 @@ internal sealed class BuckleSystem : SharedBuckleSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lower the draw depth of the buckled entity without needing for the strap entity to rotate/move.
|
||||||
|
/// Only do so when the entity is facing screen-local north
|
||||||
|
/// </summary>
|
||||||
|
private void OnBuckledEvent(Entity<BuckleComponent> ent, ref BuckledEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp<SpriteComponent>(args.Strap, out var strapSprite))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!TryComp<SpriteComponent>(ent.Owner, out var buckledSprite))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var angle = _xformSystem.GetWorldRotation(args.Strap) + _eye.CurrentEye.Rotation; // Get true screen position, or close enough
|
||||||
|
|
||||||
|
if (angle.GetCardinalDir() != Direction.North)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent.Comp.OriginalDrawDepth ??= buckledSprite.DrawDepth;
|
||||||
|
buckledSprite.DrawDepth = strapSprite.DrawDepth - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Was the draw depth of the buckled entity lowered? Reset it upon unbuckling.
|
||||||
|
/// </summary>
|
||||||
|
private void OnUnbuckledEvent(Entity<BuckleComponent> ent, ref UnbuckledEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp<SpriteComponent>(ent.Owner, out var buckledSprite))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!ent.Comp.OriginalDrawDepth.HasValue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
buckledSprite.DrawDepth = ent.Comp.OriginalDrawDepth.Value;
|
||||||
|
ent.Comp.OriginalDrawDepth = null;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnAppearanceChange(EntityUid uid, BuckleComponent component, ref AppearanceChangeEvent args)
|
private void OnAppearanceChange(EntityUid uid, BuckleComponent component, ref AppearanceChangeEvent args)
|
||||||
{
|
{
|
||||||
if (!TryComp<RotationVisualsComponent>(uid, out var rotVisuals))
|
if (!TryComp<RotationVisualsComponent>(uid, out var rotVisuals))
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace Content.Client.Clickable
|
|||||||
"/Textures/Logo",
|
"/Textures/Logo",
|
||||||
};
|
};
|
||||||
|
|
||||||
private const float Threshold = 0.25f;
|
private const float Threshold = 0.1f;
|
||||||
private const int ClickRadius = 2;
|
private const int ClickRadius = 2;
|
||||||
|
|
||||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||||
|
|||||||
@@ -58,9 +58,7 @@
|
|||||||
StyleClasses="LabelBig" />
|
StyleClasses="LabelBig" />
|
||||||
<BoxContainer Orientation="Horizontal"
|
<BoxContainer Orientation="Horizontal"
|
||||||
Margin="0 0 0 5">
|
Margin="0 0 0 5">
|
||||||
<Label Text="{Loc 'crew-monitoring-user-interface-job'}:"
|
<Label Text="{Loc 'crew-monitoring-user-interface-job'}"
|
||||||
FontColorOverride="DarkGray" />
|
|
||||||
<Label Text=":"
|
|
||||||
FontColorOverride="DarkGray" />
|
FontColorOverride="DarkGray" />
|
||||||
<TextureRect Name="PersonJobIcon"
|
<TextureRect Name="PersonJobIcon"
|
||||||
TextureScale="2 2"
|
TextureScale="2 2"
|
||||||
|
|||||||
@@ -5,17 +5,24 @@ namespace Content.Client.Dice;
|
|||||||
|
|
||||||
public sealed class DiceSystem : SharedDiceSystem
|
public sealed class DiceSystem : SharedDiceSystem
|
||||||
{
|
{
|
||||||
protected override void UpdateVisuals(EntityUid uid, DiceComponent? die = null)
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref die) || !TryComp(uid, out SpriteComponent? sprite))
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<DiceComponent, AfterAutoHandleStateEvent>(OnDiceAfterHandleState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDiceAfterHandleState(Entity<DiceComponent> entity, ref AfterAutoHandleStateEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp<SpriteComponent>(entity, out var sprite))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO maybe just move each diue to its own RSI?
|
// TODO maybe just move each die to its own RSI?
|
||||||
var state = sprite.LayerGetState(0).Name;
|
var state = sprite.LayerGetState(0).Name;
|
||||||
if (state == null)
|
if (state == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var prefix = state.Substring(0, state.IndexOf('_'));
|
var prefix = state.Substring(0, state.IndexOf('_'));
|
||||||
sprite.LayerSetState(0, $"{prefix}_{die.CurrentValue}");
|
sprite.LayerSetState(0, $"{prefix}_{entity.Comp.CurrentValue}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ public sealed class DoAfterSystem : SharedDoAfterSystem
|
|||||||
_overlay.RemoveOverlay<DoAfterOverlay>();
|
_overlay.RemoveOverlay<DoAfterOverlay>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma warning disable RA0028 // No base call in overriden function
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
|
#pragma warning restore RA0028 // No base call in overriden function
|
||||||
{
|
{
|
||||||
// Currently this only predicts do afters initiated by the player.
|
// Currently this only predicts do afters initiated by the player.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Content.Client.Wires.Visualizers;
|
using Content.Client.Wires.Visualizers;
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
using Content.Shared.Doors.Systems;
|
using Content.Shared.Doors.Systems;
|
||||||
|
using Content.Shared.Power;
|
||||||
using Robust.Client.Animations;
|
using Robust.Client.Animations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
|
|
||||||
@@ -84,7 +85,8 @@ public sealed class AirlockSystem : SharedAirlockSystem
|
|||||||
if (!_appearanceSystem.TryGetData<DoorState>(uid, DoorVisuals.State, out var state, args.Component))
|
if (!_appearanceSystem.TryGetData<DoorState>(uid, DoorVisuals.State, out var state, args.Component))
|
||||||
state = DoorState.Closed;
|
state = DoorState.Closed;
|
||||||
|
|
||||||
if (_appearanceSystem.TryGetData<bool>(uid, DoorVisuals.Powered, out var powered, args.Component) && powered)
|
if (_appearanceSystem.TryGetData<bool>(uid, PowerDeviceVisuals.Powered, out var powered, args.Component)
|
||||||
|
&& powered)
|
||||||
{
|
{
|
||||||
boltedVisible = _appearanceSystem.TryGetData<bool>(uid, DoorVisuals.BoltLights, out var lights, args.Component)
|
boltedVisible = _appearanceSystem.TryGetData<bool>(uid, DoorVisuals.BoltLights, out var lights, args.Component)
|
||||||
&& lights && (state == DoorState.Closed || state == DoorState.Welded);
|
&& lights && (state == DoorState.Closed || state == DoorState.Welded);
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ namespace Content.Client.GameTicking.Managers
|
|||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeNetworkEvent<TickerJoinLobbyEvent>(JoinLobby);
|
SubscribeNetworkEvent<TickerJoinLobbyEvent>(JoinLobby);
|
||||||
SubscribeNetworkEvent<TickerJoinGameEvent>(JoinGame);
|
SubscribeNetworkEvent<TickerJoinGameEvent>(JoinGame);
|
||||||
SubscribeNetworkEvent<TickerConnectionStatusEvent>(ConnectionStatus);
|
SubscribeNetworkEvent<TickerConnectionStatusEvent>(ConnectionStatus);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ namespace Content.Client.Ghost
|
|||||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||||
|
[Dependency] private readonly PointLightSystem _pointLightSystem = default!;
|
||||||
[Dependency] private readonly ContentEyeSystem _contentEye = default!;
|
[Dependency] private readonly ContentEyeSystem _contentEye = default!;
|
||||||
|
|
||||||
public int AvailableGhostRoleCount { get; private set; }
|
public int AvailableGhostRoleCount { get; private set; }
|
||||||
@@ -79,8 +80,27 @@ namespace Content.Client.Ghost
|
|||||||
if (args.Handled)
|
if (args.Handled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Popup.PopupEntity(Loc.GetString("ghost-gui-toggle-lighting-manager-popup"), args.Performer);
|
TryComp<PointLightComponent>(uid, out var light);
|
||||||
_contentEye.RequestToggleLight(uid, component);
|
|
||||||
|
if (!component.DrawLight)
|
||||||
|
{
|
||||||
|
// normal lighting
|
||||||
|
Popup.PopupEntity(Loc.GetString("ghost-gui-toggle-lighting-manager-popup-normal"), args.Performer);
|
||||||
|
_contentEye.RequestEye(component.DrawFov, true);
|
||||||
|
}
|
||||||
|
else if (!light?.Enabled ?? false) // skip this option if we have no PointLightComponent
|
||||||
|
{
|
||||||
|
// enable personal light
|
||||||
|
Popup.PopupEntity(Loc.GetString("ghost-gui-toggle-lighting-manager-popup-personal-light"), args.Performer);
|
||||||
|
_pointLightSystem.SetEnabled(uid, true, light);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// fullbright mode
|
||||||
|
Popup.PopupEntity(Loc.GetString("ghost-gui-toggle-lighting-manager-popup-fullbright"), args.Performer);
|
||||||
|
_contentEye.RequestEye(component.DrawFov, false);
|
||||||
|
_pointLightSystem.SetEnabled(uid, false, light);
|
||||||
|
}
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Content.Shared.ActionBlocker;
|
|||||||
using Content.Shared.Instruments.UI;
|
using Content.Shared.Instruments.UI;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Robust.Client.Audio.Midi;
|
using Robust.Client.Audio.Midi;
|
||||||
using Robust.Client.GameObjects;
|
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
|
|
||||||
@@ -13,7 +12,6 @@ namespace Content.Client.Instruments.UI
|
|||||||
public IEntityManager Entities => EntMan;
|
public IEntityManager Entities => EntMan;
|
||||||
[Dependency] public readonly IMidiManager MidiManager = default!;
|
[Dependency] public readonly IMidiManager MidiManager = default!;
|
||||||
[Dependency] public readonly IFileDialogManager FileDialogManager = default!;
|
[Dependency] public readonly IFileDialogManager FileDialogManager = default!;
|
||||||
[Dependency] public readonly IPlayerManager PlayerManager = default!;
|
|
||||||
[Dependency] public readonly ILocalizationManager Loc = default!;
|
[Dependency] public readonly ILocalizationManager Loc = default!;
|
||||||
|
|
||||||
public readonly InstrumentSystem Instruments;
|
public readonly InstrumentSystem Instruments;
|
||||||
@@ -41,6 +39,8 @@ namespace Content.Client.Instruments.UI
|
|||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
_instrumentMenu = this.CreateWindow<InstrumentMenu>();
|
_instrumentMenu = this.CreateWindow<InstrumentMenu>();
|
||||||
_instrumentMenu.Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName;
|
_instrumentMenu.Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName;
|
||||||
|
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ public sealed class DragDropSystem : SharedDragDropSystem
|
|||||||
dragSprite.DrawDepth = (int) DrawDepth.Overlays;
|
dragSprite.DrawDepth = (int) DrawDepth.Overlays;
|
||||||
if (!dragSprite.NoRotation)
|
if (!dragSprite.NoRotation)
|
||||||
{
|
{
|
||||||
Transform(_dragShadow.Value).WorldRotation = Transform(_draggedEntity.Value).WorldRotation;
|
_transformSystem.SetWorldRotationNoLerp(_dragShadow.Value, _transformSystem.GetWorldRotation(_draggedEntity.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// drag initiated
|
// drag initiated
|
||||||
|
|||||||
11
Content.Client/ItemRecall/ItemRecallSystem.cs
Normal file
11
Content.Client/ItemRecall/ItemRecallSystem.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Content.Shared.ItemRecall;
|
||||||
|
|
||||||
|
namespace Content.Client.ItemRecall;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// System for handling the ItemRecall ability for wizards.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class ItemRecallSystem : SharedItemRecallSystem
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -33,8 +33,13 @@ namespace Content.Client.Labels.UI
|
|||||||
_focused = false;
|
_focused = false;
|
||||||
LabelLineEdit.Text = _label;
|
LabelLineEdit.Text = _label;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Give the editor keybard focus, since that's the only
|
protected override void Opened()
|
||||||
|
{
|
||||||
|
base.Opened();
|
||||||
|
|
||||||
|
// Give the editor keyboard focus, since that's the only
|
||||||
// thing the user will want to be doing with this UI
|
// thing the user will want to be doing with this UI
|
||||||
LabelLineEdit.GrabKeyboardFocus();
|
LabelLineEdit.GrabKeyboardFocus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public sealed partial class LatheMenu : DefaultWindow
|
|||||||
|
|
||||||
if (_entityManager.TryGetComponent<LatheComponent>(Entity, out var latheComponent))
|
if (_entityManager.TryGetComponent<LatheComponent>(Entity, out var latheComponent))
|
||||||
{
|
{
|
||||||
if (!latheComponent.DynamicRecipes.Any())
|
if (!latheComponent.DynamicPacks.Any())
|
||||||
{
|
{
|
||||||
ServerListButton.Visible = false;
|
ServerListButton.Visible = false;
|
||||||
}
|
}
|
||||||
|
|||||||
58
Content.Client/Light/AfterLightTargetOverlay.cs
Normal file
58
Content.Client/Light/AfterLightTargetOverlay.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Shared.Enums;
|
||||||
|
|
||||||
|
namespace Content.Client.Light;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This exists just to copy <see cref="BeforeLightTargetOverlay"/> to the light render target
|
||||||
|
/// </summary>
|
||||||
|
public sealed class AfterLightTargetOverlay : Overlay
|
||||||
|
{
|
||||||
|
public override OverlaySpace Space => OverlaySpace.BeforeLighting;
|
||||||
|
|
||||||
|
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||||
|
|
||||||
|
public const int ContentZIndex = LightBlurOverlay.ContentZIndex + 1;
|
||||||
|
|
||||||
|
public AfterLightTargetOverlay()
|
||||||
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
ZIndex = ContentZIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
|
{
|
||||||
|
var viewport = args.Viewport;
|
||||||
|
var worldHandle = args.WorldHandle;
|
||||||
|
|
||||||
|
if (viewport.Eye == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var lightOverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
|
||||||
|
var bounds = args.WorldBounds;
|
||||||
|
|
||||||
|
// at 1-1 render scale it's mostly fine but at 4x4 it's way too fkn big
|
||||||
|
var newScale = viewport.RenderScale / 2f;
|
||||||
|
|
||||||
|
var localMatrix =
|
||||||
|
viewport.LightRenderTarget.GetWorldToLocalMatrix(viewport.Eye, newScale);
|
||||||
|
var diff = (lightOverlay.EnlargedLightTarget.Size - viewport.LightRenderTarget.Size);
|
||||||
|
var halfDiff = diff / 2;
|
||||||
|
|
||||||
|
// Pixels -> Metres -> Half distance.
|
||||||
|
// If we're zoomed in need to enlarge the bounds further.
|
||||||
|
args.WorldHandle.RenderInRenderTarget(viewport.LightRenderTarget,
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
// We essentially need to draw the cropped version onto the lightrendertarget.
|
||||||
|
var subRegion = new UIBox2i(halfDiff.X,
|
||||||
|
halfDiff.Y,
|
||||||
|
viewport.LightRenderTarget.Size.X + halfDiff.X,
|
||||||
|
viewport.LightRenderTarget.Size.Y + halfDiff.Y);
|
||||||
|
|
||||||
|
worldHandle.SetTransform(localMatrix);
|
||||||
|
worldHandle.DrawTextureRectRegion(lightOverlay.EnlargedLightTarget.Texture, bounds, subRegion: subRegion);
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
51
Content.Client/Light/BeforeLightTargetOverlay.cs
Normal file
51
Content.Client/Light/BeforeLightTargetOverlay.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Shared.Enums;
|
||||||
|
|
||||||
|
namespace Content.Client.Light;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles an enlarged lighting target so content can use large blur radii.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class BeforeLightTargetOverlay : Overlay
|
||||||
|
{
|
||||||
|
public override OverlaySpace Space => OverlaySpace.BeforeLighting;
|
||||||
|
|
||||||
|
[Dependency] private readonly IClyde _clyde = default!;
|
||||||
|
|
||||||
|
public IRenderTexture EnlargedLightTarget = default!;
|
||||||
|
public Box2Rotated EnlargedBounds;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// In metres
|
||||||
|
/// </summary>
|
||||||
|
private float _skirting = 1.5f;
|
||||||
|
|
||||||
|
public const int ContentZIndex = -10;
|
||||||
|
|
||||||
|
public BeforeLightTargetOverlay()
|
||||||
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
ZIndex = ContentZIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
|
{
|
||||||
|
// Code is weird but I don't think engine should be enlarging the lighting render target arbitrarily either, maybe via cvar?
|
||||||
|
// The problem is the blur has no knowledge of pixels outside the viewport so with a large enough blur radius you get sampling issues.
|
||||||
|
var size = args.Viewport.LightRenderTarget.Size + (int) (_skirting * EyeManager.PixelsPerMeter);
|
||||||
|
EnlargedBounds = args.WorldBounds.Enlarged(_skirting / 2f);
|
||||||
|
|
||||||
|
// This just exists to copy the lightrendertarget and write back to it.
|
||||||
|
if (EnlargedLightTarget?.Size != size)
|
||||||
|
{
|
||||||
|
EnlargedLightTarget = _clyde
|
||||||
|
.CreateRenderTarget(size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "enlarged-light-copy");
|
||||||
|
}
|
||||||
|
|
||||||
|
args.WorldHandle.RenderInRenderTarget(EnlargedLightTarget,
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
}, _clyde.GetClearColor(args.MapUid));
|
||||||
|
}
|
||||||
|
}
|
||||||
36
Content.Client/Light/EntitySystems/PlanetLightSystem.cs
Normal file
36
Content.Client/Light/EntitySystems/PlanetLightSystem.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using Robust.Client.Graphics;
|
||||||
|
|
||||||
|
namespace Content.Client.Light.EntitySystems;
|
||||||
|
|
||||||
|
public sealed class PlanetLightSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<GetClearColorEvent>(OnClearColor);
|
||||||
|
|
||||||
|
_overlayMan.AddOverlay(new BeforeLightTargetOverlay());
|
||||||
|
_overlayMan.AddOverlay(new RoofOverlay(EntityManager));
|
||||||
|
_overlayMan.AddOverlay(new TileEmissionOverlay(EntityManager));
|
||||||
|
_overlayMan.AddOverlay(new LightBlurOverlay());
|
||||||
|
_overlayMan.AddOverlay(new AfterLightTargetOverlay());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnClearColor(ref GetClearColorEvent ev)
|
||||||
|
{
|
||||||
|
ev.Color = Color.Transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Shutdown()
|
||||||
|
{
|
||||||
|
base.Shutdown();
|
||||||
|
_overlayMan.RemoveOverlay<BeforeLightTargetOverlay>();
|
||||||
|
_overlayMan.RemoveOverlay<RoofOverlay>();
|
||||||
|
_overlayMan.RemoveOverlay<TileEmissionOverlay>();
|
||||||
|
_overlayMan.RemoveOverlay<LightBlurOverlay>();
|
||||||
|
_overlayMan.RemoveOverlay<AfterLightTargetOverlay>();
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Content.Client/Light/EntitySystems/RoofSystem.cs
Normal file
9
Content.Client/Light/EntitySystems/RoofSystem.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using Content.Shared.Light.EntitySystems;
|
||||||
|
|
||||||
|
namespace Content.Client.Light.EntitySystems;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public sealed class RoofSystem : SharedRoofSystem
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
44
Content.Client/Light/LightBlurOverlay.cs
Normal file
44
Content.Client/Light/LightBlurOverlay.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Shared.Enums;
|
||||||
|
|
||||||
|
namespace Content.Client.Light;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Essentially handles blurring for content-side light overlays.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class LightBlurOverlay : Overlay
|
||||||
|
{
|
||||||
|
public override OverlaySpace Space => OverlaySpace.BeforeLighting;
|
||||||
|
|
||||||
|
[Dependency] private readonly IClyde _clyde = default!;
|
||||||
|
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||||
|
|
||||||
|
public const int ContentZIndex = TileEmissionOverlay.ContentZIndex + 1;
|
||||||
|
|
||||||
|
private IRenderTarget? _blurTarget;
|
||||||
|
|
||||||
|
public LightBlurOverlay()
|
||||||
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
ZIndex = ContentZIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
|
{
|
||||||
|
if (args.Viewport.Eye == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var beforeOverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
|
||||||
|
var size = beforeOverlay.EnlargedLightTarget.Size;
|
||||||
|
|
||||||
|
if (_blurTarget?.Size != size)
|
||||||
|
{
|
||||||
|
_blurTarget = _clyde
|
||||||
|
.CreateRenderTarget(size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "enlarged-light-blur");
|
||||||
|
}
|
||||||
|
|
||||||
|
var target = beforeOverlay.EnlargedLightTarget;
|
||||||
|
// Yeah that's all this does keep walkin.
|
||||||
|
_clyde.BlurRenderTarget(args.Viewport, target, _blurTarget, args.Viewport.Eye, 14f * 2f);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
Content.Client/Light/LightCycleSystem.cs
Normal file
33
Content.Client/Light/LightCycleSystem.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using Content.Client.GameTicking.Managers;
|
||||||
|
using Content.Shared;
|
||||||
|
using Content.Shared.Light.Components;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Client.Light;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public sealed class LightCycleSystem : SharedLightCycleSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly ClientGameTicker _ticker = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
var mapQuery = AllEntityQuery<LightCycleComponent, MapLightComponent>();
|
||||||
|
while (mapQuery.MoveNext(out var uid, out var cycle, out var map))
|
||||||
|
{
|
||||||
|
if (!cycle.Running)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var time = (float) _timing.CurTime
|
||||||
|
.Add(cycle.Offset)
|
||||||
|
.Subtract(_ticker.RoundStartTimeSpan)
|
||||||
|
.TotalSeconds;
|
||||||
|
|
||||||
|
var color = GetColor((uid, cycle), cycle.OriginalColor, time);
|
||||||
|
map.AmbientLightColor = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
100
Content.Client/Light/RoofOverlay.cs
Normal file
100
Content.Client/Light/RoofOverlay.cs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Shared.Light.Components;
|
||||||
|
using Content.Shared.Maps;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Shared.Enums;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
|
||||||
|
namespace Content.Client.Light;
|
||||||
|
|
||||||
|
public sealed class RoofOverlay : Overlay
|
||||||
|
{
|
||||||
|
private readonly IEntityManager _entManager;
|
||||||
|
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||||
|
|
||||||
|
private readonly EntityLookupSystem _lookup;
|
||||||
|
private readonly SharedMapSystem _mapSystem;
|
||||||
|
private readonly SharedTransformSystem _xformSystem;
|
||||||
|
|
||||||
|
private readonly HashSet<Entity<OccluderComponent>> _occluders = new();
|
||||||
|
|
||||||
|
public override OverlaySpace Space => OverlaySpace.BeforeLighting;
|
||||||
|
|
||||||
|
public const int ContentZIndex = BeforeLightTargetOverlay.ContentZIndex + 1;
|
||||||
|
|
||||||
|
public RoofOverlay(IEntityManager entManager)
|
||||||
|
{
|
||||||
|
_entManager = entManager;
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
|
_lookup = _entManager.System<EntityLookupSystem>();
|
||||||
|
_mapSystem = _entManager.System<SharedMapSystem>();
|
||||||
|
_xformSystem = _entManager.System<SharedTransformSystem>();
|
||||||
|
|
||||||
|
ZIndex = ContentZIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
|
{
|
||||||
|
if (args.Viewport.Eye == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var mapEnt = _mapSystem.GetMap(args.MapId);
|
||||||
|
|
||||||
|
if (!_entManager.TryGetComponent(mapEnt, out RoofComponent? roofComp) ||
|
||||||
|
!_entManager.TryGetComponent(mapEnt, out MapGridComponent? grid))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var viewport = args.Viewport;
|
||||||
|
var eye = args.Viewport.Eye;
|
||||||
|
|
||||||
|
var worldHandle = args.WorldHandle;
|
||||||
|
var lightoverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
|
||||||
|
var bounds = lightoverlay.EnlargedBounds;
|
||||||
|
var target = lightoverlay.EnlargedLightTarget;
|
||||||
|
|
||||||
|
worldHandle.RenderInRenderTarget(target,
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
var invMatrix = target.GetWorldToLocalMatrix(eye, viewport.RenderScale / 2f);
|
||||||
|
|
||||||
|
var gridMatrix = _xformSystem.GetWorldMatrix(mapEnt);
|
||||||
|
var matty = Matrix3x2.Multiply(gridMatrix, invMatrix);
|
||||||
|
|
||||||
|
worldHandle.SetTransform(matty);
|
||||||
|
|
||||||
|
var tileEnumerator = _mapSystem.GetTilesEnumerator(mapEnt, grid, bounds);
|
||||||
|
|
||||||
|
// Due to stencilling we essentially draw on unrooved tiles
|
||||||
|
while (tileEnumerator.MoveNext(out var tileRef))
|
||||||
|
{
|
||||||
|
if ((tileRef.Tile.Flags & (byte) TileFlag.Roof) == 0x0)
|
||||||
|
{
|
||||||
|
// Check if the tile is occluded in which case hide it anyway.
|
||||||
|
// This is to avoid lit walls bleeding over to unlit tiles.
|
||||||
|
_occluders.Clear();
|
||||||
|
_lookup.GetLocalEntitiesIntersecting(mapEnt, tileRef.GridIndices, _occluders);
|
||||||
|
var found = false;
|
||||||
|
|
||||||
|
foreach (var occluder in _occluders)
|
||||||
|
{
|
||||||
|
if (!occluder.Comp.Enabled)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var local = _lookup.GetLocalBounds(tileRef, grid.TileSize);
|
||||||
|
worldHandle.DrawRect(local, roofComp.Color);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
90
Content.Client/Light/TileEmissionOverlay.cs
Normal file
90
Content.Client/Light/TileEmissionOverlay.cs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Shared.Light.Components;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Shared.Enums;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
|
||||||
|
namespace Content.Client.Light;
|
||||||
|
|
||||||
|
public sealed class TileEmissionOverlay : Overlay
|
||||||
|
{
|
||||||
|
public override OverlaySpace Space => OverlaySpace.BeforeLighting;
|
||||||
|
|
||||||
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||||
|
|
||||||
|
private SharedMapSystem _mapSystem;
|
||||||
|
private SharedTransformSystem _xformSystem;
|
||||||
|
|
||||||
|
private readonly EntityLookupSystem _lookup;
|
||||||
|
|
||||||
|
private readonly EntityQuery<TransformComponent> _xformQuery;
|
||||||
|
private readonly HashSet<Entity<TileEmissionComponent>> _entities = new();
|
||||||
|
|
||||||
|
private List<Entity<MapGridComponent>> _grids = new();
|
||||||
|
|
||||||
|
public const int ContentZIndex = RoofOverlay.ContentZIndex + 1;
|
||||||
|
|
||||||
|
public TileEmissionOverlay(IEntityManager entManager)
|
||||||
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
|
_lookup = entManager.System<EntityLookupSystem>();
|
||||||
|
_mapSystem = entManager.System<SharedMapSystem>();
|
||||||
|
_xformSystem = entManager.System<SharedTransformSystem>();
|
||||||
|
|
||||||
|
_xformQuery = entManager.GetEntityQuery<TransformComponent>();
|
||||||
|
ZIndex = ContentZIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
|
{
|
||||||
|
if (args.Viewport.Eye == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var mapId = args.MapId;
|
||||||
|
var worldHandle = args.WorldHandle;
|
||||||
|
var lightoverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
|
||||||
|
var bounds = lightoverlay.EnlargedBounds;
|
||||||
|
var target = lightoverlay.EnlargedLightTarget;
|
||||||
|
var viewport = args.Viewport;
|
||||||
|
|
||||||
|
args.WorldHandle.RenderInRenderTarget(target,
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
var invMatrix = target.GetWorldToLocalMatrix(viewport.Eye, viewport.RenderScale / 2f);
|
||||||
|
_grids.Clear();
|
||||||
|
_mapManager.FindGridsIntersecting(mapId, bounds, ref _grids, approx: true);
|
||||||
|
|
||||||
|
foreach (var grid in _grids)
|
||||||
|
{
|
||||||
|
var gridInvMatrix = _xformSystem.GetInvWorldMatrix(grid);
|
||||||
|
var localBounds = gridInvMatrix.TransformBox(bounds);
|
||||||
|
_entities.Clear();
|
||||||
|
_lookup.GetLocalEntitiesIntersecting(grid.Owner, localBounds, _entities);
|
||||||
|
|
||||||
|
if (_entities.Count == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var gridMatrix = _xformSystem.GetWorldMatrix(grid.Owner);
|
||||||
|
|
||||||
|
foreach (var ent in _entities)
|
||||||
|
{
|
||||||
|
var xform = _xformQuery.Comp(ent);
|
||||||
|
|
||||||
|
var tile = _mapSystem.LocalToTile(grid.Owner, grid, xform.Coordinates);
|
||||||
|
var matty = Matrix3x2.Multiply(gridMatrix, invMatrix);
|
||||||
|
|
||||||
|
worldHandle.SetTransform(matty);
|
||||||
|
|
||||||
|
// Yes I am fully aware this leads to overlap. If you really want to have alpha then you'll need
|
||||||
|
// to turn the squares into polys.
|
||||||
|
// Additionally no shadows so if you make it too big it's going to go through a 1x wall.
|
||||||
|
var local = _lookup.GetLocalBounds(tile, grid.Comp.TileSize).Enlarged(ent.Comp.Range);
|
||||||
|
worldHandle.DrawRect(local, ent.Comp.Color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,7 +43,6 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
|||||||
[UISystemDependency] private readonly ClientInventorySystem _inventory = default!;
|
[UISystemDependency] private readonly ClientInventorySystem _inventory = default!;
|
||||||
[UISystemDependency] private readonly StationSpawningSystem _spawn = default!;
|
[UISystemDependency] private readonly StationSpawningSystem _spawn = default!;
|
||||||
[UISystemDependency] private readonly GuidebookSystem _guide = default!;
|
[UISystemDependency] private readonly GuidebookSystem _guide = default!;
|
||||||
[UISystemDependency] private readonly LoadoutSystem _loadouts = default!;
|
|
||||||
|
|
||||||
private CharacterSetupGui? _characterSetup;
|
private CharacterSetupGui? _characterSetup;
|
||||||
private HumanoidProfileEditor? _profileEditor;
|
private HumanoidProfileEditor? _profileEditor;
|
||||||
|
|||||||
@@ -1015,6 +1015,13 @@ namespace Content.Client.Lobby.UI
|
|||||||
_loadoutWindow.RefreshLoadouts(roleLoadout, session, collection);
|
_loadoutWindow.RefreshLoadouts(roleLoadout, session, collection);
|
||||||
_loadoutWindow.OpenCenteredLeft();
|
_loadoutWindow.OpenCenteredLeft();
|
||||||
|
|
||||||
|
_loadoutWindow.OnNameChanged += name =>
|
||||||
|
{
|
||||||
|
roleLoadout.EntityName = name;
|
||||||
|
Profile = Profile.WithLoadout(roleLoadout);
|
||||||
|
SetDirty();
|
||||||
|
};
|
||||||
|
|
||||||
_loadoutWindow.OnLoadoutPressed += (loadoutGroup, loadoutProto) =>
|
_loadoutWindow.OnLoadoutPressed += (loadoutGroup, loadoutProto) =>
|
||||||
{
|
{
|
||||||
roleLoadout.AddLoadout(loadoutGroup, loadoutProto, _prototypeManager);
|
roleLoadout.AddLoadout(loadoutGroup, loadoutProto, _prototypeManager);
|
||||||
|
|||||||
@@ -5,17 +5,15 @@
|
|||||||
SetSize="800 800"
|
SetSize="800 800"
|
||||||
MinSize="800 128">
|
MinSize="800 128">
|
||||||
<BoxContainer Orientation="Vertical" VerticalExpand="True">
|
<BoxContainer Orientation="Vertical" VerticalExpand="True">
|
||||||
<!--
|
|
||||||
<BoxContainer Name="RoleNameBox" Orientation="Vertical" Margin="10">
|
<BoxContainer Name="RoleNameBox" Orientation="Vertical" Margin="10">
|
||||||
<Label Name="LoadoutNameLabel" Text="{Loc 'loadout-name-edit-label'}"/>
|
<Label Name="LoadoutNameLabel" Text="{Loc 'loadout-name-edit-label'}"/>
|
||||||
<PanelContainer HorizontalExpand="True" SetHeight="24">
|
<PanelContainer HorizontalExpand="True" SetHeight="24">
|
||||||
<PanelContainer.PanelOverride>
|
<PanelContainer.PanelOverride>
|
||||||
<graphics:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
<graphics:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||||
</PanelContainer.PanelOverride>
|
</PanelContainer.PanelOverride>
|
||||||
<LineEdit Name="RoleNameEdit" ToolTip="{Loc 'loadout-name-edit-tooltip'}" VerticalExpand="True" HorizontalExpand="True"/>
|
<LineEdit Name="RoleNameEdit" VerticalExpand="True" HorizontalExpand="True"/>
|
||||||
</PanelContainer>
|
</PanelContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
-->
|
|
||||||
<VerticalTabContainer Name="LoadoutGroupsContainer"
|
<VerticalTabContainer Name="LoadoutGroupsContainer"
|
||||||
VerticalExpand="True"
|
VerticalExpand="True"
|
||||||
HorizontalExpand="True">
|
HorizontalExpand="True">
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Client.UserInterface.Controls;
|
using Content.Client.UserInterface.Controls;
|
||||||
|
using Content.Shared.Dataset;
|
||||||
using Content.Shared.Preferences;
|
using Content.Shared.Preferences;
|
||||||
using Content.Shared.Preferences.Loadouts;
|
using Content.Shared.Preferences.Loadouts;
|
||||||
|
using Content.Shared.Random.Helpers;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
@@ -13,6 +15,7 @@ namespace Content.Client.Lobby.UI.Loadouts;
|
|||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class LoadoutWindow : FancyWindow
|
public sealed partial class LoadoutWindow : FancyWindow
|
||||||
{
|
{
|
||||||
|
public event Action<string>? OnNameChanged;
|
||||||
public event Action<ProtoId<LoadoutGroupPrototype>, ProtoId<LoadoutPrototype>>? OnLoadoutPressed;
|
public event Action<ProtoId<LoadoutGroupPrototype>, ProtoId<LoadoutPrototype>>? OnLoadoutPressed;
|
||||||
public event Action<ProtoId<LoadoutGroupPrototype>, ProtoId<LoadoutPrototype>>? OnLoadoutUnpressed;
|
public event Action<ProtoId<LoadoutGroupPrototype>, ProtoId<LoadoutPrototype>>? OnLoadoutUnpressed;
|
||||||
|
|
||||||
@@ -25,6 +28,23 @@ public sealed partial class LoadoutWindow : FancyWindow
|
|||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
Profile = profile;
|
Profile = profile;
|
||||||
var protoManager = collection.Resolve<IPrototypeManager>();
|
var protoManager = collection.Resolve<IPrototypeManager>();
|
||||||
|
RoleNameEdit.IsValid = text => text.Length <= HumanoidCharacterProfile.MaxLoadoutNameLength;
|
||||||
|
|
||||||
|
// Hide if we can't edit the name.
|
||||||
|
if (!proto.CanCustomizeName)
|
||||||
|
{
|
||||||
|
RoleNameBox.Visible = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var name = loadout.EntityName;
|
||||||
|
|
||||||
|
RoleNameEdit.ToolTip = Loc.GetString(
|
||||||
|
"loadout-name-edit-tooltip",
|
||||||
|
("max", HumanoidCharacterProfile.MaxLoadoutNameLength));
|
||||||
|
RoleNameEdit.Text = name ?? string.Empty;
|
||||||
|
RoleNameEdit.OnTextChanged += args => OnNameChanged?.Invoke(args.Text);
|
||||||
|
}
|
||||||
|
|
||||||
// Hide if no groups
|
// Hide if no groups
|
||||||
if (proto.Groups.Count == 0)
|
if (proto.Groups.Count == 0)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Shared.Magic;
|
using Content.Shared.Magic;
|
||||||
using Content.Shared.Magic.Events;
|
using Content.Shared.Magic.Events;
|
||||||
|
|
||||||
namespace Content.Client.Magic;
|
namespace Content.Client.Magic;
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ public sealed class GridDraggingSystem : SharedGridDraggingSystem
|
|||||||
if (!_mapManager.TryFindGridAt(mousePos, out var gridUid, out var grid))
|
if (!_mapManager.TryFindGridAt(mousePos, out var gridUid, out var grid))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
StartDragging(gridUid, Vector2.Transform(mousePos.Position, Transform(gridUid).InvWorldMatrix));
|
StartDragging(gridUid, Vector2.Transform(mousePos.Position, _transformSystem.GetInvWorldMatrix(gridUid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryComp(_dragging, out TransformComponent? xform))
|
if (!TryComp(_dragging, out TransformComponent? xform))
|
||||||
@@ -117,11 +117,11 @@ public sealed class GridDraggingSystem : SharedGridDraggingSystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var localToWorld = Vector2.Transform(_localPosition, xform.WorldMatrix);
|
var localToWorld = Vector2.Transform(_localPosition, _transformSystem.GetWorldMatrix(xform));
|
||||||
|
|
||||||
if (localToWorld.EqualsApprox(mousePos.Position, 0.01f)) return;
|
if (localToWorld.EqualsApprox(mousePos.Position, 0.01f)) return;
|
||||||
|
|
||||||
var requestedGridOrigin = mousePos.Position - xform.WorldRotation.RotateVec(_localPosition);
|
var requestedGridOrigin = mousePos.Position - _transformSystem.GetWorldRotation(xform).RotateVec(_localPosition);
|
||||||
_lastMousePosition = new MapCoordinates(requestedGridOrigin, mousePos.MapId);
|
_lastMousePosition = new MapCoordinates(requestedGridOrigin, mousePos.MapId);
|
||||||
|
|
||||||
RaiseNetworkEvent(new GridDragRequestPosition()
|
RaiseNetworkEvent(new GridDragRequestPosition()
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ public sealed class NewsWriterBoundUserInterface : BoundUserInterface
|
|||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
_menu = this.CreateWindow<NewsWriterMenu>();
|
_menu = this.CreateWindow<NewsWriterMenu>();
|
||||||
|
|
||||||
_menu.ArticleEditorPanel.PublishButtonPressed += OnPublishButtonPressed;
|
_menu.ArticleEditorPanel.PublishButtonPressed += OnPublishButtonPressed;
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ public sealed class CrewMonitoringBoundUserInterface : BoundUserInterface
|
|||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
EntityUid? gridUid = null;
|
EntityUid? gridUid = null;
|
||||||
var stationName = string.Empty;
|
var stationName = string.Empty;
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ namespace Content.Client.Nuke
|
|||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
_menu = this.CreateWindow<NukeMenu>();
|
_menu = this.CreateWindow<NukeMenu>();
|
||||||
|
|
||||||
_menu.OnKeypadButtonPressed += i =>
|
_menu.OnKeypadButtonPressed += i =>
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ public sealed class TargetOutlineSystem : EntitySystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public EntityWhitelist? Whitelist = null;
|
public EntityWhitelist? Whitelist = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Blacklist that the target must satisfy.
|
||||||
|
/// </summary>
|
||||||
|
public EntityWhitelist? Blacklist = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Predicate the target must satisfy.
|
/// Predicate the target must satisfy.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -93,15 +98,16 @@ public sealed class TargetOutlineSystem : EntitySystem
|
|||||||
RemoveHighlights();
|
RemoveHighlights();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Enable(float range, bool checkObstructions, Func<EntityUid, bool>? predicate, EntityWhitelist? whitelist, CancellableEntityEventArgs? validationEvent)
|
public void Enable(float range, bool checkObstructions, Func<EntityUid, bool>? predicate, EntityWhitelist? whitelist, EntityWhitelist? blacklist, CancellableEntityEventArgs? validationEvent)
|
||||||
{
|
{
|
||||||
Range = range;
|
Range = range;
|
||||||
CheckObstruction = checkObstructions;
|
CheckObstruction = checkObstructions;
|
||||||
Predicate = predicate;
|
Predicate = predicate;
|
||||||
Whitelist = whitelist;
|
Whitelist = whitelist;
|
||||||
|
Blacklist = blacklist;
|
||||||
ValidationEvent = validationEvent;
|
ValidationEvent = validationEvent;
|
||||||
|
|
||||||
_enabled = Predicate != null || Whitelist != null || ValidationEvent != null;
|
_enabled = Predicate != null || Whitelist != null || Blacklist != null || ValidationEvent != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ namespace Content.Client.Pinpointer.UI;
|
|||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class StationMapBeaconControl : Control, IComparable<StationMapBeaconControl>
|
public sealed partial class StationMapBeaconControl : Control, IComparable<StationMapBeaconControl>
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
|
||||||
|
|
||||||
public readonly EntityCoordinates BeaconPosition;
|
public readonly EntityCoordinates BeaconPosition;
|
||||||
public Action<EntityCoordinates>? OnPressed;
|
public Action<EntityCoordinates>? OnPressed;
|
||||||
public string? Label => BeaconNameLabel.Text;
|
public string? Label => BeaconNameLabel.Text;
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ public sealed class PowerMonitoringConsoleBoundUserInterface : BoundUserInterfac
|
|||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
_menu = this.CreateWindow<PowerMonitoringWindow>();
|
_menu = this.CreateWindow<PowerMonitoringWindow>();
|
||||||
_menu.SetEntity(Owner);
|
_menu.SetEntity(Owner);
|
||||||
_menu.SendPowerMonitoringConsoleMessageAction += SendPowerMonitoringConsoleMessage;
|
_menu.SendPowerMonitoringConsoleMessageAction += SendPowerMonitoringConsoleMessage;
|
||||||
|
|||||||
@@ -4,14 +4,18 @@ using Robust.Client.GameObjects;
|
|||||||
|
|
||||||
namespace Content.Client.Power.Visualizers;
|
namespace Content.Client.Power.Visualizers;
|
||||||
|
|
||||||
public sealed class CableVisualizerSystem : VisualizerSystem<CableVisualizerComponent>
|
public sealed class CableVisualizerSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<CableVisualizerComponent, AppearanceChangeEvent>(OnAppearanceChange, after: new[] { typeof(SubFloorHideSystem) });
|
SubscribeLocalEvent<CableVisualizerComponent, AppearanceChangeEvent>(OnAppearanceChange, after: new[] { typeof(SubFloorHideSystem) });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAppearanceChange(EntityUid uid, CableVisualizerComponent component, ref AppearanceChangeEvent args)
|
private void OnAppearanceChange(EntityUid uid, CableVisualizerComponent component, ref AppearanceChangeEvent args)
|
||||||
{
|
{
|
||||||
if (args.Sprite == null)
|
if (args.Sprite == null)
|
||||||
return;
|
return;
|
||||||
@@ -23,7 +27,7 @@ public sealed class CableVisualizerSystem : VisualizerSystem<CableVisualizerComp
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!AppearanceSystem.TryGetData<WireVisDirFlags>(uid, WireVisVisuals.ConnectedMask, out var mask, args.Component))
|
if (!_appearanceSystem.TryGetData<WireVisDirFlags>(uid, WireVisVisuals.ConnectedMask, out var mask, args.Component))
|
||||||
mask = WireVisDirFlags.None;
|
mask = WireVisDirFlags.None;
|
||||||
|
|
||||||
args.Sprite.LayerSetState(0, $"{component.StatePrefix}{(int) mask}");
|
args.Sprite.LayerSetState(0, $"{component.StatePrefix}{(int) mask}");
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem
|
|||||||
|
|
||||||
if (_enableShuttlePosition)
|
if (_enableShuttlePosition)
|
||||||
{
|
{
|
||||||
_overlay = new EmergencyShuttleOverlay(EntityManager);
|
_overlay = new EmergencyShuttleOverlay(EntityManager.TransformQuery, XformSystem);
|
||||||
overlayManager.AddOverlay(_overlay);
|
overlayManager.AddOverlay(_overlay);
|
||||||
RaiseNetworkEvent(new EmergencyShuttleRequestPositionMessage());
|
RaiseNetworkEvent(new EmergencyShuttleRequestPositionMessage());
|
||||||
}
|
}
|
||||||
@@ -57,23 +57,26 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class EmergencyShuttleOverlay : Overlay
|
public sealed class EmergencyShuttleOverlay : Overlay
|
||||||
{
|
{
|
||||||
private IEntityManager _entManager;
|
private readonly EntityQuery<TransformComponent> _transformQuery;
|
||||||
|
private readonly SharedTransformSystem _transformSystem;
|
||||||
|
|
||||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||||
|
|
||||||
public EntityUid? StationUid;
|
public EntityUid? StationUid;
|
||||||
public Box2? Position;
|
public Box2? Position;
|
||||||
|
|
||||||
public EmergencyShuttleOverlay(IEntityManager entManager)
|
public EmergencyShuttleOverlay(EntityQuery<TransformComponent> transformQuery, SharedTransformSystem transformSystem)
|
||||||
{
|
{
|
||||||
_entManager = entManager;
|
_transformQuery = transformQuery;
|
||||||
|
_transformSystem = transformSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Draw(in OverlayDrawArgs args)
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
{
|
{
|
||||||
if (Position == null || !_entManager.TryGetComponent<TransformComponent>(StationUid, out var xform)) return;
|
if (Position == null || !_transformQuery.TryGetComponent(StationUid, out var xform))
|
||||||
|
return;
|
||||||
|
|
||||||
args.WorldHandle.SetTransform(xform.WorldMatrix);
|
args.WorldHandle.SetTransform(_transformSystem.GetWorldMatrix(xform));
|
||||||
args.WorldHandle.DrawRect(Position.Value, Color.Red.WithAlpha(100));
|
args.WorldHandle.DrawRect(Position.Value, Color.Red.WithAlpha(100));
|
||||||
args.WorldHandle.SetTransform(Matrix3x2.Identity);
|
args.WorldHandle.SetTransform(Matrix3x2.Identity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,14 @@
|
|||||||
using System.Text;
|
|
||||||
using Content.Shared.Shuttles.BUIStates;
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
using Content.Shared.Shuttles.Systems;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Map;
|
|
||||||
|
|
||||||
namespace Content.Client.Shuttles.UI;
|
namespace Content.Client.Shuttles.UI;
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class DockObject : PanelContainer
|
public sealed partial class DockObject : PanelContainer
|
||||||
{
|
{
|
||||||
[PublicAPI]
|
|
||||||
public event Action? UndockPressed;
|
|
||||||
|
|
||||||
[PublicAPI]
|
|
||||||
public event Action? ViewPressed;
|
|
||||||
|
|
||||||
public BoxContainer ContentsContainer => Contents;
|
public BoxContainer ContentsContainer => Contents;
|
||||||
|
|
||||||
public DockObject()
|
public DockObject()
|
||||||
|
|||||||
@@ -163,16 +163,6 @@ public sealed partial class DockingScreen : BoxContainer
|
|||||||
}
|
}
|
||||||
|
|
||||||
dockContainer.AddDock(dock, DockingControl);
|
dockContainer.AddDock(dock, DockingControl);
|
||||||
|
|
||||||
dockContainer.ViewPressed += () =>
|
|
||||||
{
|
|
||||||
OnDockPress(dock);
|
|
||||||
};
|
|
||||||
|
|
||||||
dockContainer.UndockPressed += () =>
|
|
||||||
{
|
|
||||||
UndockRequest?.Invoke(dock.Entity);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ using System.Numerics;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Client.Administration.Managers;
|
using Content.Client.Administration.Managers;
|
||||||
using Content.Shared.Database;
|
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Shared.ContentPack;
|
using Robust.Shared.ContentPack;
|
||||||
|
using Robust.Shared.Exceptions;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
@@ -24,6 +24,7 @@ public sealed class ContentSpriteSystem : EntitySystem
|
|||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] private readonly IResourceManager _resManager = default!;
|
[Dependency] private readonly IResourceManager _resManager = default!;
|
||||||
[Dependency] private readonly IUserInterfaceManager _ui = default!;
|
[Dependency] private readonly IUserInterfaceManager _ui = default!;
|
||||||
|
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
|
||||||
|
|
||||||
private ContentSpriteControl _control = new();
|
private ContentSpriteControl _control = new();
|
||||||
|
|
||||||
@@ -42,12 +43,12 @@ public sealed class ContentSpriteSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
base.Shutdown();
|
base.Shutdown();
|
||||||
|
|
||||||
foreach (var queued in _control._queuedTextures)
|
foreach (var queued in _control.QueuedTextures)
|
||||||
{
|
{
|
||||||
queued.Tcs.SetCanceled();
|
queued.Tcs.SetCanceled();
|
||||||
}
|
}
|
||||||
|
|
||||||
_control._queuedTextures.Clear();
|
_control.QueuedTextures.Clear();
|
||||||
|
|
||||||
_ui.RootControl.RemoveChild(_control);
|
_ui.RootControl.RemoveChild(_control);
|
||||||
}
|
}
|
||||||
@@ -103,7 +104,7 @@ public sealed class ContentSpriteSystem : EntitySystem
|
|||||||
var texture = _clyde.CreateRenderTarget(new Vector2i(size.X, size.Y), new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "export");
|
var texture = _clyde.CreateRenderTarget(new Vector2i(size.X, size.Y), new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "export");
|
||||||
var tcs = new TaskCompletionSource(cancelToken);
|
var tcs = new TaskCompletionSource(cancelToken);
|
||||||
|
|
||||||
_control._queuedTextures.Enqueue((texture, direction, entity, includeId, tcs));
|
_control.QueuedTextures.Enqueue((texture, direction, entity, includeId, tcs));
|
||||||
|
|
||||||
await tcs.Task;
|
await tcs.Task;
|
||||||
}
|
}
|
||||||
@@ -113,13 +114,21 @@ public sealed class ContentSpriteSystem : EntitySystem
|
|||||||
if (!_adminManager.IsAdmin())
|
if (!_adminManager.IsAdmin())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var target = ev.Target;
|
||||||
Verb verb = new()
|
Verb verb = new()
|
||||||
{
|
{
|
||||||
Text = Loc.GetString("export-entity-verb-get-data-text"),
|
Text = Loc.GetString("export-entity-verb-get-data-text"),
|
||||||
Category = VerbCategory.Debug,
|
Category = VerbCategory.Debug,
|
||||||
Act = () =>
|
Act = async () =>
|
||||||
{
|
{
|
||||||
Export(ev.Target);
|
try
|
||||||
|
{
|
||||||
|
await Export(target);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_runtimeLog.LogException(e, $"{nameof(ContentSpriteSystem)}.{nameof(Export)}");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -141,7 +150,7 @@ public sealed class ContentSpriteSystem : EntitySystem
|
|||||||
Direction Direction,
|
Direction Direction,
|
||||||
EntityUid Entity,
|
EntityUid Entity,
|
||||||
bool IncludeId,
|
bool IncludeId,
|
||||||
TaskCompletionSource Tcs)> _queuedTextures = new();
|
TaskCompletionSource Tcs)> QueuedTextures = new();
|
||||||
|
|
||||||
private ISawmill _sawmill;
|
private ISawmill _sawmill;
|
||||||
|
|
||||||
@@ -155,7 +164,7 @@ public sealed class ContentSpriteSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
base.Draw(handle);
|
base.Draw(handle);
|
||||||
|
|
||||||
while (_queuedTextures.TryDequeue(out var queued))
|
while (QueuedTextures.TryDequeue(out var queued))
|
||||||
{
|
{
|
||||||
if (queued.Tcs.Task.IsCanceled)
|
if (queued.Tcs.Task.IsCanceled)
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Content.Shared.StatusIcon;
|
|||||||
using Content.Shared.StatusIcon.Components;
|
using Content.Shared.StatusIcon.Components;
|
||||||
using Content.Shared.Stealth.Components;
|
using Content.Shared.Stealth.Components;
|
||||||
using Content.Shared.Whitelist;
|
using Content.Shared.Whitelist;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
@@ -85,6 +86,9 @@ public sealed class StatusIconSystem : SharedStatusIconSystem
|
|||||||
if (data.HideOnStealth && TryComp<StealthComponent>(ent, out var stealth) && stealth.Enabled)
|
if (data.HideOnStealth && TryComp<StealthComponent>(ent, out var stealth) && stealth.Enabled)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (TryComp<SpriteComponent>(ent, out var sprite) && !sprite.Visible)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (data.ShowTo != null && !_entityWhitelist.IsValid(data.ShowTo, viewer))
|
if (data.ShowTo != null && !_entityWhitelist.IsValid(data.ShowTo, viewer))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -74,10 +74,6 @@ public sealed class StorageSystem : SharedStorageSystem
|
|||||||
{
|
{
|
||||||
containerBui.Hide();
|
containerBui.Hide();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
storageBui.Show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ public sealed class StoreBoundUserInterface : BoundUserInterface
|
|||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
_menu = this.CreateWindow<StoreMenu>();
|
_menu = this.CreateWindow<StoreMenu>();
|
||||||
if (EntMan.TryGetComponent<StoreComponent>(Owner, out var store))
|
if (EntMan.TryGetComponent<StoreComponent>(Owner, out var store))
|
||||||
_menu.Title = Loc.GetString(store.Name);
|
_menu.Title = Loc.GetString(store.Name);
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ public sealed class SurveillanceCameraSetupBoundUi : BoundUserInterface
|
|||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
_window = new();
|
_window = new();
|
||||||
|
|
||||||
if (_type == SurveillanceCameraSetupUiKey.Router)
|
if (_type == SurveillanceCameraSetupUiKey.Router)
|
||||||
|
|||||||
@@ -87,9 +87,6 @@ public sealed partial class DialogWindow : FancyWindow
|
|||||||
Prompts.AddChild(box);
|
Prompts.AddChild(box);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grab keyboard focus for the first dialog entry
|
|
||||||
_promptLines[0].Item2.GrabKeyboardFocus();
|
|
||||||
|
|
||||||
OkButton.OnPressed += _ => Confirm();
|
OkButton.OnPressed += _ => Confirm();
|
||||||
|
|
||||||
CancelButton.OnPressed += _ =>
|
CancelButton.OnPressed += _ =>
|
||||||
@@ -110,6 +107,14 @@ public sealed partial class DialogWindow : FancyWindow
|
|||||||
OpenCentered();
|
OpenCentered();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Opened()
|
||||||
|
{
|
||||||
|
base.Opened();
|
||||||
|
|
||||||
|
// Grab keyboard focus for the first dialog entry
|
||||||
|
_promptLines[0].Item2.GrabKeyboardFocus();
|
||||||
|
}
|
||||||
|
|
||||||
private void Confirm()
|
private void Confirm()
|
||||||
{
|
{
|
||||||
var results = new Dictionary<string, string>();
|
var results = new Dictionary<string, string>();
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ using Robust.Client.ResourceManagement;
|
|||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Graphics;
|
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
@@ -96,7 +95,6 @@ public sealed partial class FancyTree : Control
|
|||||||
private void LoadIcons()
|
private void LoadIcons()
|
||||||
{
|
{
|
||||||
IconColor = TryGetStyleProperty(StylePropertyIconColor, out Color color) ? color : Color.White;
|
IconColor = TryGetStyleProperty(StylePropertyIconColor, out Color color) ? color : Color.White;
|
||||||
string? path;
|
|
||||||
|
|
||||||
if (!TryGetStyleProperty(StylePropertyIconExpanded, out IconExpanded))
|
if (!TryGetStyleProperty(StylePropertyIconExpanded, out IconExpanded))
|
||||||
IconExpanded = _resCache.GetTexture(DefaultIconExpanded);
|
IconExpanded = _resCache.GetTexture(DefaultIconExpanded);
|
||||||
|
|||||||
@@ -952,7 +952,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||||||
var range = entityAction.CheckCanAccess ? action.Range : -1;
|
var range = entityAction.CheckCanAccess ? action.Range : -1;
|
||||||
|
|
||||||
_interactionOutline?.SetEnabled(false);
|
_interactionOutline?.SetEnabled(false);
|
||||||
_targetOutline?.Enable(range, entityAction.CheckCanAccess, predicate, entityAction.Whitelist, null);
|
_targetOutline?.Enable(range, entityAction.CheckCanAccess, predicate, entityAction.Whitelist, entityAction.Blacklist, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -56,19 +56,20 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
|
|||||||
_window = UIManager.CreateWindow<CharacterWindow>();
|
_window = UIManager.CreateWindow<CharacterWindow>();
|
||||||
LayoutContainer.SetAnchorPreset(_window, LayoutContainer.LayoutPreset.CenterTop);
|
LayoutContainer.SetAnchorPreset(_window, LayoutContainer.LayoutPreset.CenterTop);
|
||||||
|
|
||||||
|
_window.OnClose += DeactivateButton;
|
||||||
|
_window.OnOpen += ActivateButton;
|
||||||
|
|
||||||
CommandBinds.Builder
|
CommandBinds.Builder
|
||||||
.Bind(ContentKeyFunctions.OpenCharacterMenu,
|
.Bind(ContentKeyFunctions.OpenCharacterMenu,
|
||||||
InputCmdHandler.FromDelegate(_ => ToggleWindow()))
|
InputCmdHandler.FromDelegate(_ => ToggleWindow()))
|
||||||
.Register<CharacterUIController>();
|
.Register<CharacterUIController>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnStateExited(GameplayState state)
|
public void OnStateExited(GameplayState state)
|
||||||
{
|
{
|
||||||
if (_window != null)
|
if (_window != null)
|
||||||
{
|
{
|
||||||
_window.Dispose();
|
_window.Close();
|
||||||
_window = null;
|
_window = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,18 +106,27 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
|
|||||||
}
|
}
|
||||||
|
|
||||||
CharacterButton.OnPressed += CharacterButtonPressed;
|
CharacterButton.OnPressed += CharacterButtonPressed;
|
||||||
|
}
|
||||||
|
|
||||||
if (_window == null)
|
private void DeactivateButton()
|
||||||
|
{
|
||||||
|
if (CharacterButton == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_window.OnClose += DeactivateButton;
|
CharacterButton.Pressed = false;
|
||||||
_window.OnOpen += ActivateButton;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeactivateButton() => CharacterButton!.Pressed = false;
|
private void ActivateButton()
|
||||||
private void ActivateButton() => CharacterButton!.Pressed = true;
|
{
|
||||||
|
if (CharacterButton == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterButton.Pressed = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void CharacterUpdated(CharacterData data)
|
private void CharacterUpdated(CharacterData data)
|
||||||
{
|
{
|
||||||
@@ -150,7 +160,7 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
|
|||||||
|
|
||||||
var objectiveLabel = new RichTextLabel
|
var objectiveLabel = new RichTextLabel
|
||||||
{
|
{
|
||||||
StyleClasses = {StyleNano.StyleClassTooltipActionTitle}
|
StyleClasses = { StyleNano.StyleClassTooltipActionTitle }
|
||||||
};
|
};
|
||||||
objectiveLabel.SetMessage(objectiveText);
|
objectiveLabel.SetMessage(objectiveText);
|
||||||
|
|
||||||
@@ -245,10 +255,7 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
|
|||||||
if (_window == null)
|
if (_window == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (CharacterButton != null)
|
CharacterButton?.SetClickPressed(!_window.IsOpen);
|
||||||
{
|
|
||||||
CharacterButton.SetClickPressed(!_window.IsOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_window.IsOpen)
|
if (_window.IsOpen)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -38,11 +38,6 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
|||||||
[Dependency] private readonly IPlayerManager _player = default!;
|
[Dependency] private readonly IPlayerManager _player = default!;
|
||||||
[UISystemDependency] private readonly StorageSystem _storage = default!;
|
[UISystemDependency] private readonly StorageSystem _storage = default!;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Cached positions for opening nested storage.
|
|
||||||
/// </summary>
|
|
||||||
private readonly Dictionary<EntityUid, Vector2> _reservedStorage = new();
|
|
||||||
|
|
||||||
private readonly DragDropHelper<ItemGridPiece> _menuDragHelper;
|
private readonly DragDropHelper<ItemGridPiece> _menuDragHelper;
|
||||||
|
|
||||||
public ItemGridPiece? DraggingGhost => _menuDragHelper.Dragged;
|
public ItemGridPiece? DraggingGhost => _menuDragHelper.Dragged;
|
||||||
@@ -133,11 +128,6 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
window.OpenCenteredLeft();
|
window.OpenCenteredLeft();
|
||||||
|
|
||||||
if (_reservedStorage.Remove(uid, out var pos))
|
|
||||||
{
|
|
||||||
LayoutContainer.SetPosition(window, pos);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return window;
|
return window;
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ using Content.Client.UserInterface.Systems.Gameplay;
|
|||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controllers;
|
using Robust.Client.UserInterface.Controllers;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Client.UserInterface.Systems.Viewport;
|
namespace Content.Client.UserInterface.Systems.Viewport;
|
||||||
@@ -15,6 +17,7 @@ public sealed class ViewportUIController : UIController
|
|||||||
[Dependency] private readonly IPlayerManager _playerMan = default!;
|
[Dependency] private readonly IPlayerManager _playerMan = default!;
|
||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||||
|
[UISystemDependency] private readonly SharedTransformSystem? _transformSystem = default!;
|
||||||
public static readonly Vector2i ViewportSize = (EyeManager.PixelsPerMeter * 21, EyeManager.PixelsPerMeter * 15);
|
public static readonly Vector2i ViewportSize = (EyeManager.PixelsPerMeter * 21, EyeManager.PixelsPerMeter * 15);
|
||||||
public const int ViewportHeight = 15;
|
public const int ViewportHeight = 15;
|
||||||
private MainViewport? Viewport => UIManager.ActiveScreen?.GetWidget<MainViewport>();
|
private MainViewport? Viewport => UIManager.ActiveScreen?.GetWidget<MainViewport>();
|
||||||
@@ -93,8 +96,11 @@ public sealed class ViewportUIController : UIController
|
|||||||
_entMan.TryGetComponent(ent, out EyeComponent? eye);
|
_entMan.TryGetComponent(ent, out EyeComponent? eye);
|
||||||
|
|
||||||
if (eye?.Eye == _eyeManager.CurrentEye
|
if (eye?.Eye == _eyeManager.CurrentEye
|
||||||
&& _entMan.GetComponent<TransformComponent>(ent.Value).WorldPosition == default)
|
&& _entMan.GetComponent<TransformComponent>(ent.Value).MapID == MapId.Nullspace)
|
||||||
return; // nothing to worry about, the player is just in null space... actually that is probably a problem?
|
{
|
||||||
|
// nothing to worry about, the player is just in null space... actually that is probably a problem?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Currently, this shouldn't happen. This likely happened because the main eye was set to null. When this
|
// Currently, this shouldn't happen. This likely happened because the main eye was set to null. When this
|
||||||
// does happen it can create hard to troubleshoot bugs, so lets print some helpful warnings:
|
// does happen it can create hard to troubleshoot bugs, so lets print some helpful warnings:
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
using Content.Client.Ghost;
|
|
||||||
using Content.Shared.Voting;
|
using Content.Shared.Voting;
|
||||||
|
|
||||||
namespace Content.Client.Voting;
|
namespace Content.Client.Voting;
|
||||||
|
|
||||||
public sealed class VotingSystem : EntitySystem
|
public sealed class VotingSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
|
||||||
public event Action<VotePlayerListResponseEvent>? VotePlayerListResponse; //Provides a list of players elligble for vote actions
|
public event Action<VotePlayerListResponseEvent>? VotePlayerListResponse; //Provides a list of players elligble for vote actions
|
||||||
|
|
||||||
[Dependency] private readonly GhostSystem _ghostSystem = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
|||||||
var targetCoordinates = xform.Coordinates;
|
var targetCoordinates = xform.Coordinates;
|
||||||
var targetLocalAngle = xform.LocalRotation;
|
var targetLocalAngle = xform.LocalRotation;
|
||||||
|
|
||||||
return Interaction.InRangeUnobstructed(user, target, targetCoordinates, targetLocalAngle, range);
|
return Interaction.InRangeUnobstructed(user, target, targetCoordinates, targetLocalAngle, range, overlapCheck: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void DoDamageEffect(List<EntityUid> targets, EntityUid? user, TransformComponent targetXform)
|
protected override void DoDamageEffect(List<EntityUid> targets, EntityUid? user, TransformComponent targetXform)
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ using Robust.Shared.Map;
|
|||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Robust.Shared.EntitySerialization.Systems;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Body
|
namespace Content.IntegrationTests.Tests.Body
|
||||||
{
|
{
|
||||||
@@ -57,7 +59,6 @@ namespace Content.IntegrationTests.Tests.Body
|
|||||||
|
|
||||||
await server.WaitIdleAsync();
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
var mapManager = server.ResolveDependency<IMapManager>();
|
|
||||||
var entityManager = server.ResolveDependency<IEntityManager>();
|
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||||
var mapLoader = entityManager.System<MapLoaderSystem>();
|
var mapLoader = entityManager.System<MapLoaderSystem>();
|
||||||
var mapSys = entityManager.System<SharedMapSystem>();
|
var mapSys = entityManager.System<SharedMapSystem>();
|
||||||
@@ -69,17 +70,13 @@ namespace Content.IntegrationTests.Tests.Body
|
|||||||
GridAtmosphereComponent relevantAtmos = default;
|
GridAtmosphereComponent relevantAtmos = default;
|
||||||
var startingMoles = 0.0f;
|
var startingMoles = 0.0f;
|
||||||
|
|
||||||
var testMapName = "Maps/Test/Breathing/3by3-20oxy-80nit.yml";
|
var testMapName = new ResPath("Maps/Test/Breathing/3by3-20oxy-80nit.yml");
|
||||||
|
|
||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
mapSys.CreateMap(out var mapId);
|
mapSys.CreateMap(out var mapId);
|
||||||
Assert.That(mapLoader.TryLoad(mapId, testMapName, out var roots));
|
Assert.That(mapLoader.TryLoadGrid(mapId, testMapName, out var gridEnt));
|
||||||
|
grid = gridEnt!.Value.Owner;
|
||||||
var query = entityManager.GetEntityQuery<MapGridComponent>();
|
|
||||||
var grids = roots.Where(x => query.HasComponent(x));
|
|
||||||
Assert.That(grids, Is.Not.Empty);
|
|
||||||
grid = grids.First();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Assert.That(grid, Is.Not.Null, $"Test blueprint {testMapName} not found.");
|
Assert.That(grid, Is.Not.Null, $"Test blueprint {testMapName} not found.");
|
||||||
@@ -148,18 +145,13 @@ namespace Content.IntegrationTests.Tests.Body
|
|||||||
RespiratorComponent respirator = null;
|
RespiratorComponent respirator = null;
|
||||||
EntityUid human = default;
|
EntityUid human = default;
|
||||||
|
|
||||||
var testMapName = "Maps/Test/Breathing/3by3-20oxy-80nit.yml";
|
var testMapName = new ResPath("Maps/Test/Breathing/3by3-20oxy-80nit.yml");
|
||||||
|
|
||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
mapSys.CreateMap(out var mapId);
|
mapSys.CreateMap(out var mapId);
|
||||||
|
Assert.That(mapLoader.TryLoadGrid(mapId, testMapName, out var gridEnt));
|
||||||
Assert.That(mapLoader.TryLoad(mapId, testMapName, out var ents), Is.True);
|
grid = gridEnt!.Value.Owner;
|
||||||
var query = entityManager.GetEntityQuery<MapGridComponent>();
|
|
||||||
grid = ents
|
|
||||||
.Select<EntityUid, EntityUid?>(x => x)
|
|
||||||
.FirstOrDefault((uid) => uid.HasValue && query.HasComponent(uid.Value), null);
|
|
||||||
Assert.That(grid, Is.Not.Null);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Assert.That(grid, Is.Not.Null, $"Test blueprint {testMapName} not found.");
|
Assert.That(grid, Is.Not.Null, $"Test blueprint {testMapName} not found.");
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Content.Shared.Body.Systems;
|
using Content.Shared.Body.Systems;
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.EntitySerialization.Systems;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Body;
|
namespace Content.IntegrationTests.Tests.Body;
|
||||||
|
|
||||||
@@ -111,13 +112,12 @@ public sealed class SaveLoadReparentTest
|
|||||||
Is.Not.Empty
|
Is.Not.Empty
|
||||||
);
|
);
|
||||||
|
|
||||||
const string mapPath = $"/{nameof(SaveLoadReparentTest)}{nameof(Test)}map.yml";
|
var mapPath = new ResPath($"/{nameof(SaveLoadReparentTest)}{nameof(Test)}map.yml");
|
||||||
|
|
||||||
mapLoader.SaveMap(mapId, mapPath);
|
Assert.That(mapLoader.TrySaveMap(mapId, mapPath));
|
||||||
maps.DeleteMap(mapId);
|
mapSys.DeleteMap(mapId);
|
||||||
|
|
||||||
mapSys.CreateMap(out mapId);
|
Assert.That(mapLoader.TryLoadMap(mapPath, out var map, out _), Is.True);
|
||||||
Assert.That(mapLoader.TryLoad(mapId, mapPath, out _), Is.True);
|
|
||||||
|
|
||||||
var query = EnumerateQueryEnumerator(
|
var query = EnumerateQueryEnumerator(
|
||||||
entities.EntityQueryEnumerator<BodyComponent>()
|
entities.EntityQueryEnumerator<BodyComponent>()
|
||||||
@@ -173,7 +173,7 @@ public sealed class SaveLoadReparentTest
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
maps.DeleteMap(mapId);
|
entities.DeleteEntity(map);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Content.Server.Nutrition.Components;
|
|||||||
using Content.Server.Nutrition.EntitySystems;
|
using Content.Server.Nutrition.EntitySystems;
|
||||||
using Content.Shared.Cargo.Prototypes;
|
using Content.Shared.Cargo.Prototypes;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
|
using Content.Shared.Prototypes;
|
||||||
using Content.Shared.Stacks;
|
using Content.Shared.Stacks;
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
using Content.Shared.Whitelist;
|
using Content.Shared.Whitelist;
|
||||||
@@ -104,51 +105,34 @@ public sealed class CargoTest
|
|||||||
await using var pair = await PoolManager.GetServerClient();
|
await using var pair = await PoolManager.GetServerClient();
|
||||||
var server = pair.Server;
|
var server = pair.Server;
|
||||||
|
|
||||||
var testMap = await pair.CreateTestMap();
|
var protoManager = server.ProtoMan;
|
||||||
|
var compFact = server.ResolveDependency<IComponentFactory>();
|
||||||
var entManager = server.ResolveDependency<IEntityManager>();
|
|
||||||
var mapManager = server.ResolveDependency<IMapManager>();
|
|
||||||
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
|
||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
var mapId = testMap.MapId;
|
|
||||||
var grid = mapManager.CreateGridEntity(mapId);
|
|
||||||
var coord = new EntityCoordinates(grid.Owner, 0, 0);
|
|
||||||
|
|
||||||
var protoIds = protoManager.EnumeratePrototypes<EntityPrototype>()
|
var protoIds = protoManager.EnumeratePrototypes<EntityPrototype>()
|
||||||
.Where(p => !p.Abstract)
|
.Where(p => !p.Abstract)
|
||||||
.Where(p => !pair.IsTestPrototype(p))
|
.Where(p => !pair.IsTestPrototype(p))
|
||||||
.Where(p => !p.Components.ContainsKey("MapGrid")) // Grids are not for sale.
|
.Where(p => p.Components.ContainsKey("StaticPrice"))
|
||||||
.Select(p => p.ID)
|
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
foreach (var proto in protoIds)
|
foreach (var proto in protoIds)
|
||||||
{
|
{
|
||||||
var ent = entManager.SpawnEntity(proto, coord);
|
// Sanity check
|
||||||
|
Assert.That(proto.TryGetComponent<StaticPriceComponent>(out var staticPriceComp, compFact), Is.True);
|
||||||
|
|
||||||
if (entManager.TryGetComponent<StackPriceComponent>(ent, out var stackpricecomp)
|
if (proto.TryGetComponent<StackPriceComponent>(out var stackPriceComp, compFact) && stackPriceComp.Price > 0)
|
||||||
&& stackpricecomp.Price > 0)
|
|
||||||
{
|
{
|
||||||
if (entManager.TryGetComponent<StaticPriceComponent>(ent, out var staticpricecomp))
|
Assert.That(staticPriceComp.Price, Is.EqualTo(0),
|
||||||
{
|
$"The prototype {proto} has a StackPriceComponent and StaticPriceComponent whose values are not compatible with each other.");
|
||||||
Assert.That(staticpricecomp.Price, Is.EqualTo(0),
|
|
||||||
$"The prototype {proto} has a StackPriceComponent and StaticPriceComponent whose values are not compatible with each other.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entManager.HasComponent<StackComponent>(ent))
|
if (proto.HasComponent<StackComponent>(compFact))
|
||||||
{
|
{
|
||||||
if (entManager.TryGetComponent<StaticPriceComponent>(ent, out var staticpricecomp))
|
Assert.That(staticPriceComp.Price, Is.EqualTo(0),
|
||||||
{
|
$"The prototype {proto} has a StackComponent and StaticPriceComponent whose values are not compatible with each other.");
|
||||||
Assert.That(staticpricecomp.Price, Is.EqualTo(0),
|
|
||||||
$"The prototype {proto} has a StackComponent and StaticPriceComponent whose values are not compatible with each other.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entManager.DeleteEntity(ent);
|
|
||||||
}
|
}
|
||||||
mapManager.DeleteMap(mapId);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await pair.CleanReturnAsync();
|
await pair.CleanReturnAsync();
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
.Where(p => !p.Abstract)
|
.Where(p => !p.Abstract)
|
||||||
.Where(p => !pair.IsTestPrototype(p))
|
.Where(p => !pair.IsTestPrototype(p))
|
||||||
.Where(p => !p.Components.ContainsKey("MapGrid")) // This will smash stuff otherwise.
|
.Where(p => !p.Components.ContainsKey("MapGrid")) // This will smash stuff otherwise.
|
||||||
|
.Where(p => !p.Components.ContainsKey("RoomFill")) // This comp can delete all entities, and spawn others
|
||||||
.Select(p => p.ID)
|
.Select(p => p.ID)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
@@ -101,6 +102,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
.Where(p => !p.Abstract)
|
.Where(p => !p.Abstract)
|
||||||
.Where(p => !pair.IsTestPrototype(p))
|
.Where(p => !pair.IsTestPrototype(p))
|
||||||
.Where(p => !p.Components.ContainsKey("MapGrid")) // This will smash stuff otherwise.
|
.Where(p => !p.Components.ContainsKey("MapGrid")) // This will smash stuff otherwise.
|
||||||
|
.Where(p => !p.Components.ContainsKey("RoomFill")) // This comp can delete all entities, and spawn others
|
||||||
.Select(p => p.ID)
|
.Select(p => p.ID)
|
||||||
.ToList();
|
.ToList();
|
||||||
foreach (var protoId in protoIds)
|
foreach (var protoId in protoIds)
|
||||||
@@ -341,6 +343,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
"DebugExceptionInitialize",
|
"DebugExceptionInitialize",
|
||||||
"DebugExceptionStartup",
|
"DebugExceptionStartup",
|
||||||
"GridFill",
|
"GridFill",
|
||||||
|
"RoomFill",
|
||||||
"Map", // We aren't testing a map entity in this test
|
"Map", // We aren't testing a map entity in this test
|
||||||
"MapGrid",
|
"MapGrid",
|
||||||
"Broadphase",
|
"Broadphase",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public sealed class LatheTest
|
|||||||
var compFactory = server.ResolveDependency<IComponentFactory>();
|
var compFactory = server.ResolveDependency<IComponentFactory>();
|
||||||
var materialStorageSystem = server.System<SharedMaterialStorageSystem>();
|
var materialStorageSystem = server.System<SharedMaterialStorageSystem>();
|
||||||
var whitelistSystem = server.System<EntityWhitelistSystem>();
|
var whitelistSystem = server.System<EntityWhitelistSystem>();
|
||||||
|
var latheSystem = server.System<SharedLatheSystem>();
|
||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
@@ -74,14 +75,14 @@ public sealed class LatheTest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect all the recipes assigned to this lathe
|
// Collect all possible recipes assigned to this lathe
|
||||||
var recipes = new List<ProtoId<LatheRecipePrototype>>();
|
var recipes = new HashSet<ProtoId<LatheRecipePrototype>>();
|
||||||
recipes.AddRange(latheComp.StaticRecipes);
|
latheSystem.AddRecipesFromPacks(recipes, latheComp.StaticPacks);
|
||||||
recipes.AddRange(latheComp.DynamicRecipes);
|
latheSystem.AddRecipesFromPacks(recipes, latheComp.DynamicPacks);
|
||||||
if (latheProto.TryGetComponent<EmagLatheRecipesComponent>(out var emagRecipesComp, compFactory))
|
if (latheProto.TryGetComponent<EmagLatheRecipesComponent>(out var emagRecipesComp, compFactory))
|
||||||
{
|
{
|
||||||
recipes.AddRange(emagRecipesComp.EmagStaticRecipes);
|
latheSystem.AddRecipesFromPacks(recipes, emagRecipesComp.EmagStaticPacks);
|
||||||
recipes.AddRange(emagRecipesComp.EmagDynamicRecipes);
|
latheSystem.AddRecipesFromPacks(recipes, emagRecipesComp.EmagDynamicPacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check each recipe assigned to this lathe
|
// Check each recipe assigned to this lathe
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Reflection;
|
using Robust.Shared.Reflection;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
@@ -29,7 +30,9 @@ public sealed class StaticFieldValidationTest
|
|||||||
Assert.That(protoMan.ValidateStaticFields(typeof(StringValid), protos), Is.Empty);
|
Assert.That(protoMan.ValidateStaticFields(typeof(StringValid), protos), Is.Empty);
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(StringArrayValid), protos), Is.Empty);
|
Assert.That(protoMan.ValidateStaticFields(typeof(StringArrayValid), protos), Is.Empty);
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdValid), protos), Is.Empty);
|
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdValid), protos), Is.Empty);
|
||||||
|
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdTValid), protos), Is.Empty);
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdArrayValid), protos), Is.Empty);
|
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdArrayValid), protos), Is.Empty);
|
||||||
|
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdTArrayValid), protos), Is.Empty);
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdTestValid), protos), Is.Empty);
|
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdTestValid), protos), Is.Empty);
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdArrayValid), protos), Is.Empty);
|
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdArrayValid), protos), Is.Empty);
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListValid), protos), Is.Empty);
|
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListValid), protos), Is.Empty);
|
||||||
@@ -39,7 +42,9 @@ public sealed class StaticFieldValidationTest
|
|||||||
Assert.That(protoMan.ValidateStaticFields(typeof(StringInvalid), protos), Has.Count.EqualTo(1));
|
Assert.That(protoMan.ValidateStaticFields(typeof(StringInvalid), protos), Has.Count.EqualTo(1));
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(StringArrayInvalid), protos), Has.Count.EqualTo(2));
|
Assert.That(protoMan.ValidateStaticFields(typeof(StringArrayInvalid), protos), Has.Count.EqualTo(2));
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdInvalid), protos), Has.Count.EqualTo(1));
|
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdInvalid), protos), Has.Count.EqualTo(1));
|
||||||
|
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdTInvalid), protos), Has.Count.EqualTo(1));
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdArrayInvalid), protos), Has.Count.EqualTo(2));
|
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdArrayInvalid), protos), Has.Count.EqualTo(2));
|
||||||
|
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdTArrayInvalid), protos), Has.Count.EqualTo(2));
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdTestInvalid), protos), Has.Count.EqualTo(1));
|
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdTestInvalid), protos), Has.Count.EqualTo(1));
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdArrayInvalid), protos), Has.Count.EqualTo(2));
|
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdArrayInvalid), protos), Has.Count.EqualTo(2));
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListInvalid), protos), Has.Count.EqualTo(2));
|
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListInvalid), protos), Has.Count.EqualTo(2));
|
||||||
@@ -88,24 +93,48 @@ public sealed class StaticFieldValidationTest
|
|||||||
public static EntProtoId Tag = "StaticFieldTestEnt";
|
public static EntProtoId Tag = "StaticFieldTestEnt";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Reflect(false)]
|
||||||
|
private sealed class EntProtoIdTValid
|
||||||
|
{
|
||||||
|
public static EntProtoId<TransformComponent> Tag = "StaticFieldTestEnt";
|
||||||
|
}
|
||||||
|
|
||||||
[Reflect(false)]
|
[Reflect(false)]
|
||||||
private sealed class EntProtoIdInvalid
|
private sealed class EntProtoIdInvalid
|
||||||
{
|
{
|
||||||
public static EntProtoId Tag = string.Empty;
|
public static EntProtoId Tag = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Reflect(false)]
|
||||||
|
private sealed class EntProtoIdTInvalid
|
||||||
|
{
|
||||||
|
public static EntProtoId<TransformComponent> Tag = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
[Reflect(false)]
|
[Reflect(false)]
|
||||||
private sealed class EntProtoIdArrayValid
|
private sealed class EntProtoIdArrayValid
|
||||||
{
|
{
|
||||||
public static EntProtoId[] Tag = ["StaticFieldTestEnt", "StaticFieldTestEnt"];
|
public static EntProtoId[] Tag = ["StaticFieldTestEnt", "StaticFieldTestEnt"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Reflect(false)]
|
||||||
|
private sealed class EntProtoIdTArrayValid
|
||||||
|
{
|
||||||
|
public static EntProtoId<TransformComponent>[] Tag = ["StaticFieldTestEnt", "StaticFieldTestEnt"];
|
||||||
|
}
|
||||||
|
|
||||||
[Reflect(false)]
|
[Reflect(false)]
|
||||||
private sealed class EntProtoIdArrayInvalid
|
private sealed class EntProtoIdArrayInvalid
|
||||||
{
|
{
|
||||||
public static EntProtoId[] Tag = [string.Empty, "StaticFieldTestEnt", string.Empty];
|
public static EntProtoId[] Tag = [string.Empty, "StaticFieldTestEnt", string.Empty];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Reflect(false)]
|
||||||
|
private sealed class EntProtoIdTArrayInvalid
|
||||||
|
{
|
||||||
|
public static EntProtoId<TransformComponent>[] Tag = [string.Empty, "StaticFieldTestEnt", string.Empty];
|
||||||
|
}
|
||||||
|
|
||||||
[Reflect(false)]
|
[Reflect(false)]
|
||||||
private sealed class ProtoIdTestValid
|
private sealed class ProtoIdTestValid
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Minds;
|
namespace Content.IntegrationTests.Tests.Minds;
|
||||||
@@ -37,8 +38,8 @@ public sealed partial class MindTests
|
|||||||
Assert.That(pair.Client.EntMan.EntityCount, Is.EqualTo(0));
|
Assert.That(pair.Client.EntMan.EntityCount, Is.EqualTo(0));
|
||||||
|
|
||||||
// Create a new map.
|
// Create a new map.
|
||||||
int mapId = 1;
|
MapId mapId = default;
|
||||||
await pair.Server.WaitPost(() => conHost.ExecuteCommand($"addmap {mapId}"));
|
await pair.Server.WaitPost(() => pair.Server.System<SharedMapSystem>().CreateMap(out mapId));
|
||||||
await pair.RunTicksSync(5);
|
await pair.RunTicksSync(5);
|
||||||
|
|
||||||
// Client is not attached to anything
|
// Client is not attached to anything
|
||||||
@@ -54,7 +55,7 @@ public sealed partial class MindTests
|
|||||||
Assert.That(pair.Client.EntMan.EntityExists(pair.Client.AttachedEntity));
|
Assert.That(pair.Client.EntMan.EntityExists(pair.Client.AttachedEntity));
|
||||||
Assert.That(pair.Server.EntMan.EntityExists(pair.PlayerData?.Mind));
|
Assert.That(pair.Server.EntMan.EntityExists(pair.PlayerData?.Mind));
|
||||||
var xform = pair.Client.Transform(pair.Client.AttachedEntity!.Value);
|
var xform = pair.Client.Transform(pair.Client.AttachedEntity!.Value);
|
||||||
Assert.That(xform.MapID, Is.EqualTo(new MapId(mapId)));
|
Assert.That(xform.MapID, Is.EqualTo(mapId));
|
||||||
|
|
||||||
await pair.CleanReturnAsync();
|
await pair.CleanReturnAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ using Content.Server.Spawners.Components;
|
|||||||
using Content.Server.Station.Components;
|
using Content.Server.Station.Components;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.Roles;
|
using Content.Shared.Roles;
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.ContentPack;
|
using Robust.Shared.ContentPack;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
@@ -17,6 +16,9 @@ using Robust.Shared.Map;
|
|||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Content.Shared.Station.Components;
|
using Content.Shared.Station.Components;
|
||||||
|
using Robust.Shared.EntitySerialization;
|
||||||
|
using Robust.Shared.EntitySerialization.Systems;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using YamlDotNet.RepresentationModel;
|
using YamlDotNet.RepresentationModel;
|
||||||
|
|
||||||
@@ -36,10 +38,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
|
|
||||||
private static readonly string[] Grids =
|
private static readonly string[] Grids =
|
||||||
{
|
{
|
||||||
"/Maps/centcomm.yml",
|
"/Maps/centcomm.yml"
|
||||||
"/Maps/Shuttles/cargo.yml",
|
|
||||||
"/Maps/Shuttles/emergency.yml",
|
|
||||||
"/Maps/Shuttles/infiltrator.yml",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly string[] DoNotMapWhitelist =
|
private static readonly string[] DoNotMapWhitelist =
|
||||||
@@ -65,14 +64,13 @@ namespace Content.IntegrationTests.Tests
|
|||||||
"Reach",
|
"Reach",
|
||||||
"Train",
|
"Train",
|
||||||
"Oasis",
|
"Oasis",
|
||||||
"Cog",
|
|
||||||
"Gate",
|
"Gate",
|
||||||
"Amber",
|
"Amber",
|
||||||
"Loop",
|
"Loop",
|
||||||
"Plasma",
|
"Plasma",
|
||||||
"Elkridge",
|
"Elkridge",
|
||||||
"Convex"
|
"Convex",
|
||||||
|
"Relic"
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -87,33 +85,72 @@ namespace Content.IntegrationTests.Tests
|
|||||||
var entManager = server.ResolveDependency<IEntityManager>();
|
var entManager = server.ResolveDependency<IEntityManager>();
|
||||||
var mapLoader = entManager.System<MapLoaderSystem>();
|
var mapLoader = entManager.System<MapLoaderSystem>();
|
||||||
var mapSystem = entManager.System<SharedMapSystem>();
|
var mapSystem = entManager.System<SharedMapSystem>();
|
||||||
var mapManager = server.ResolveDependency<IMapManager>();
|
|
||||||
var cfg = server.ResolveDependency<IConfigurationManager>();
|
var cfg = server.ResolveDependency<IConfigurationManager>();
|
||||||
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
||||||
|
var path = new ResPath(mapFile);
|
||||||
|
|
||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
mapSystem.CreateMap(out var mapId);
|
mapSystem.CreateMap(out var mapId);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
#pragma warning disable NUnit2045
|
Assert.That(mapLoader.TryLoadGrid(mapId, path, out var grid));
|
||||||
Assert.That(mapLoader.TryLoad(mapId, mapFile, out var roots));
|
|
||||||
Assert.That(roots.Where(uid => entManager.HasComponent<MapGridComponent>(uid)), Is.Not.Empty);
|
|
||||||
#pragma warning restore NUnit2045
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
throw new Exception($"Failed to load map {mapFile}, was it saved as a map instead of a grid?", ex);
|
throw new Exception($"Failed to load map {mapFile}, was it saved as a map instead of a grid?", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
mapSystem.DeleteMap(mapId);
|
||||||
|
});
|
||||||
|
await server.WaitRunTicks(1);
|
||||||
|
|
||||||
|
await pair.CleanReturnAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asserts that shuttles are loadable and have been saved as grids and not maps.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public async Task ShuttlesLoadableTest()
|
||||||
|
{
|
||||||
|
await using var pair = await PoolManager.GetServerClient();
|
||||||
|
var server = pair.Server;
|
||||||
|
|
||||||
|
var entManager = server.ResolveDependency<IEntityManager>();
|
||||||
|
var resMan = server.ResolveDependency<IResourceManager>();
|
||||||
|
var mapLoader = entManager.System<MapLoaderSystem>();
|
||||||
|
var mapSystem = entManager.System<SharedMapSystem>();
|
||||||
|
var cfg = server.ResolveDependency<IConfigurationManager>();
|
||||||
|
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
||||||
|
|
||||||
|
var shuttleFolder = new ResPath("/Maps/Shuttles");
|
||||||
|
var shuttles = resMan
|
||||||
|
.ContentFindFiles(shuttleFolder)
|
||||||
|
.Where(filePath =>
|
||||||
|
filePath.Extension == "yml" && !filePath.Filename.StartsWith(".", StringComparison.Ordinal))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
await server.WaitPost(() =>
|
||||||
|
{
|
||||||
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
mapManager.DeleteMap(mapId);
|
foreach (var path in shuttles)
|
||||||
}
|
{
|
||||||
catch (Exception ex)
|
mapSystem.CreateMap(out var mapId);
|
||||||
{
|
try
|
||||||
throw new Exception($"Failed to delete map {mapFile}", ex);
|
{
|
||||||
}
|
Assert.That(mapLoader.TryLoadGrid(mapId, path, out _),
|
||||||
|
$"Failed to load shuttle {path}, was it saved as a map instead of a grid?");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failed to load shuttle {path}, was it saved as a map instead of a grid?",
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
mapSystem.DeleteMap(mapId);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
await server.WaitRunTicks(1);
|
await server.WaitRunTicks(1);
|
||||||
|
|
||||||
@@ -128,12 +165,15 @@ namespace Content.IntegrationTests.Tests
|
|||||||
|
|
||||||
var resourceManager = server.ResolveDependency<IResourceManager>();
|
var resourceManager = server.ResolveDependency<IResourceManager>();
|
||||||
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
||||||
|
var loader = server.System<MapLoaderSystem>();
|
||||||
|
|
||||||
var mapFolder = new ResPath("/Maps");
|
var mapFolder = new ResPath("/Maps");
|
||||||
var maps = resourceManager
|
var maps = resourceManager
|
||||||
.ContentFindFiles(mapFolder)
|
.ContentFindFiles(mapFolder)
|
||||||
.Where(filePath => filePath.Extension == "yml" && !filePath.Filename.StartsWith(".", StringComparison.Ordinal))
|
.Where(filePath => filePath.Extension == "yml" && !filePath.Filename.StartsWith(".", StringComparison.Ordinal))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
|
var v7Maps = new List<ResPath>();
|
||||||
foreach (var map in maps)
|
foreach (var map in maps)
|
||||||
{
|
{
|
||||||
var rootedPath = map.ToRootedPath();
|
var rootedPath = map.ToRootedPath();
|
||||||
@@ -156,8 +196,15 @@ namespace Content.IntegrationTests.Tests
|
|||||||
|
|
||||||
var root = yamlStream.Documents[0].RootNode;
|
var root = yamlStream.Documents[0].RootNode;
|
||||||
var meta = root["meta"];
|
var meta = root["meta"];
|
||||||
var postMapInit = meta["postmapinit"].AsBool();
|
var version = meta["format"].AsInt();
|
||||||
|
|
||||||
|
if (version >= 7)
|
||||||
|
{
|
||||||
|
v7Maps.Add(map);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var postMapInit = meta["postmapinit"].AsBool();
|
||||||
Assert.That(postMapInit, Is.False, $"Map {map.Filename} was saved postmapinit");
|
Assert.That(postMapInit, Is.False, $"Map {map.Filename} was saved postmapinit");
|
||||||
|
|
||||||
// testing that maps have nothing with the DoNotMap entity category
|
// testing that maps have nothing with the DoNotMap entity category
|
||||||
@@ -177,9 +224,58 @@ namespace Content.IntegrationTests.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var deps = server.ResolveDependency<IEntitySystemManager>().DependencyCollection;
|
||||||
|
foreach (var map in v7Maps)
|
||||||
|
{
|
||||||
|
Assert.That(IsPreInit(map, loader, deps));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the test actually does manage to catch post-init maps and isn't just blindly passing everything.
|
||||||
|
// To that end, create a new post-init map and try verify it.
|
||||||
|
var mapSys = server.System<SharedMapSystem>();
|
||||||
|
MapId id = default;
|
||||||
|
await server.WaitPost(() => mapSys.CreateMap(out id, runMapInit: false));
|
||||||
|
await server.WaitPost(() => server.EntMan.Spawn(null, new MapCoordinates(0, 0, id)));
|
||||||
|
|
||||||
|
// First check that a pre-init version passes
|
||||||
|
var path = new ResPath($"{nameof(NoSavedPostMapInitTest)}.yml");
|
||||||
|
Assert.That(loader.TrySaveMap(id, path));
|
||||||
|
Assert.That(IsPreInit(path, loader, deps));
|
||||||
|
|
||||||
|
// and the post-init version fails.
|
||||||
|
await server.WaitPost(() => mapSys.InitializeMap(id));
|
||||||
|
Assert.That(loader.TrySaveMap(id, path));
|
||||||
|
Assert.That(IsPreInit(path, loader, deps), Is.False);
|
||||||
|
|
||||||
await pair.CleanReturnAsync();
|
await pair.CleanReturnAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsPreInit(ResPath map, MapLoaderSystem loader, IDependencyCollection deps)
|
||||||
|
{
|
||||||
|
if (!loader.TryReadFile(map, out var data))
|
||||||
|
{
|
||||||
|
Assert.Fail($"Failed to read {map}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var reader = new EntityDeserializer(deps, data, DeserializationOptions.Default);
|
||||||
|
if (!reader.TryProcessData())
|
||||||
|
{
|
||||||
|
Assert.Fail($"Failed to process {map}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var mapId in reader.MapYamlIds)
|
||||||
|
{
|
||||||
|
var mapData = reader.YamlEntities[mapId];
|
||||||
|
if (mapData.PostInit)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
[Test, TestCaseSource(nameof(GameMaps))]
|
[Test, TestCaseSource(nameof(GameMaps))]
|
||||||
public async Task GameMapsLoadableTest(string mapProto)
|
public async Task GameMapsLoadableTest(string mapProto)
|
||||||
{
|
{
|
||||||
@@ -196,16 +292,16 @@ namespace Content.IntegrationTests.Tests
|
|||||||
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
||||||
var ticker = entManager.EntitySysManager.GetEntitySystem<GameTicker>();
|
var ticker = entManager.EntitySysManager.GetEntitySystem<GameTicker>();
|
||||||
var shuttleSystem = entManager.EntitySysManager.GetEntitySystem<ShuttleSystem>();
|
var shuttleSystem = entManager.EntitySysManager.GetEntitySystem<ShuttleSystem>();
|
||||||
var xformQuery = entManager.GetEntityQuery<TransformComponent>();
|
|
||||||
var cfg = server.ResolveDependency<IConfigurationManager>();
|
var cfg = server.ResolveDependency<IConfigurationManager>();
|
||||||
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
||||||
|
|
||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
mapSystem.CreateMap(out var mapId);
|
MapId mapId;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ticker.LoadGameMap(protoManager.Index<GameMapPrototype>(mapProto), mapId, null);
|
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||||
|
ticker.LoadGameMap(protoManager.Index<GameMapPrototype>(mapProto), out mapId, opts);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -242,21 +338,17 @@ namespace Content.IntegrationTests.Tests
|
|||||||
if (entManager.TryGetComponent<StationEmergencyShuttleComponent>(station, out var stationEvac))
|
if (entManager.TryGetComponent<StationEmergencyShuttleComponent>(station, out var stationEvac))
|
||||||
{
|
{
|
||||||
var shuttlePath = stationEvac.EmergencyShuttlePath;
|
var shuttlePath = stationEvac.EmergencyShuttlePath;
|
||||||
#pragma warning disable NUnit2045
|
Assert.That(mapLoader.TryLoadGrid(shuttleMap, shuttlePath, out var shuttle),
|
||||||
Assert.That(mapLoader.TryLoad(shuttleMap, shuttlePath.ToString(), out var roots));
|
$"Failed to load {shuttlePath}");
|
||||||
EntityUid shuttle = default!;
|
|
||||||
Assert.DoesNotThrow(() =>
|
|
||||||
{
|
|
||||||
shuttle = roots.First(uid => entManager.HasComponent<MapGridComponent>(uid));
|
|
||||||
}, $"Failed to load {shuttlePath}");
|
|
||||||
Assert.That(
|
Assert.That(
|
||||||
shuttleSystem.TryFTLDock(shuttle,
|
shuttleSystem.TryFTLDock(shuttle!.Value.Owner,
|
||||||
entManager.GetComponent<ShuttleComponent>(shuttle), targetGrid.Value),
|
entManager.GetComponent<ShuttleComponent>(shuttle!.Value.Owner),
|
||||||
|
targetGrid.Value),
|
||||||
$"Unable to dock {shuttlePath} to {mapProto}");
|
$"Unable to dock {shuttlePath} to {mapProto}");
|
||||||
#pragma warning restore NUnit2045
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mapManager.DeleteMap(shuttleMap);
|
mapSystem.DeleteMap(shuttleMap);
|
||||||
|
|
||||||
if (entManager.HasComponent<StationJobsComponent>(station))
|
if (entManager.HasComponent<StationJobsComponent>(station))
|
||||||
{
|
{
|
||||||
@@ -293,7 +385,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mapManager.DeleteMap(mapId);
|
mapSystem.DeleteMap(mapId);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -357,11 +449,9 @@ namespace Content.IntegrationTests.Tests
|
|||||||
var server = pair.Server;
|
var server = pair.Server;
|
||||||
|
|
||||||
var mapLoader = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>();
|
var mapLoader = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>();
|
||||||
var mapManager = server.ResolveDependency<IMapManager>();
|
|
||||||
var resourceManager = server.ResolveDependency<IResourceManager>();
|
var resourceManager = server.ResolveDependency<IResourceManager>();
|
||||||
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
||||||
var cfg = server.ResolveDependency<IConfigurationManager>();
|
var cfg = server.ResolveDependency<IConfigurationManager>();
|
||||||
var mapSystem = server.System<SharedMapSystem>();
|
|
||||||
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
||||||
|
|
||||||
var gameMaps = protoManager.EnumeratePrototypes<GameMapPrototype>().Select(o => o.MapPath).ToHashSet();
|
var gameMaps = protoManager.EnumeratePrototypes<GameMapPrototype>().Select(o => o.MapPath).ToHashSet();
|
||||||
@@ -372,7 +462,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
.Where(filePath => filePath.Extension == "yml" && !filePath.Filename.StartsWith(".", StringComparison.Ordinal))
|
.Where(filePath => filePath.Extension == "yml" && !filePath.Filename.StartsWith(".", StringComparison.Ordinal))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
var mapNames = new List<string>();
|
var mapPaths = new List<ResPath>();
|
||||||
foreach (var map in maps)
|
foreach (var map in maps)
|
||||||
{
|
{
|
||||||
if (gameMaps.Contains(map))
|
if (gameMaps.Contains(map))
|
||||||
@@ -383,32 +473,46 @@ namespace Content.IntegrationTests.Tests
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
mapNames.Add(rootedPath.ToString());
|
mapPaths.Add(rootedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
foreach (var mapName in mapNames)
|
// This bunch of files contains a random mixture of both map and grid files.
|
||||||
|
// TODO MAPPING organize files
|
||||||
|
var opts = MapLoadOptions.Default with
|
||||||
|
{
|
||||||
|
DeserializationOptions = DeserializationOptions.Default with
|
||||||
|
{
|
||||||
|
InitializeMaps = true,
|
||||||
|
LogOrphanedGrids = false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
HashSet<Entity<MapComponent>> maps;
|
||||||
|
foreach (var path in mapPaths)
|
||||||
{
|
{
|
||||||
mapSystem.CreateMap(out var mapId);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Assert.That(mapLoader.TryLoad(mapId, mapName, out _));
|
Assert.That(mapLoader.TryLoadGeneric(path, out maps, out _, opts));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
throw new Exception($"Failed to load map {mapName}", ex);
|
throw new Exception($"Failed to load map {path}", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mapManager.DeleteMap(mapId);
|
foreach (var map in maps)
|
||||||
|
{
|
||||||
|
server.EntMan.DeleteEntity(map);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
throw new Exception($"Failed to delete map {mapName}", ex);
|
throw new Exception($"Failed to delete map {path}", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -52,13 +52,16 @@ public sealed class ResearchTest
|
|||||||
await using var pair = await PoolManager.GetServerClient();
|
await using var pair = await PoolManager.GetServerClient();
|
||||||
var server = pair.Server;
|
var server = pair.Server;
|
||||||
|
|
||||||
|
var entMan = server.ResolveDependency<IEntityManager>();
|
||||||
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
||||||
var compFact = server.ResolveDependency<IComponentFactory>();
|
var compFact = server.ResolveDependency<IComponentFactory>();
|
||||||
|
|
||||||
|
var latheSys = entMan.System<SharedLatheSystem>();
|
||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
var allEnts = protoManager.EnumeratePrototypes<EntityPrototype>();
|
var allEnts = protoManager.EnumeratePrototypes<EntityPrototype>();
|
||||||
var allLathes = new HashSet<LatheComponent>();
|
var latheTechs = new HashSet<ProtoId<LatheRecipePrototype>>();
|
||||||
foreach (var proto in allEnts)
|
foreach (var proto in allEnts)
|
||||||
{
|
{
|
||||||
if (proto.Abstract)
|
if (proto.Abstract)
|
||||||
@@ -69,30 +72,31 @@ public sealed class ResearchTest
|
|||||||
|
|
||||||
if (!proto.TryGetComponent<LatheComponent>(out var lathe, compFact))
|
if (!proto.TryGetComponent<LatheComponent>(out var lathe, compFact))
|
||||||
continue;
|
continue;
|
||||||
allLathes.Add(lathe);
|
|
||||||
}
|
|
||||||
|
|
||||||
var latheTechs = new HashSet<string>();
|
latheSys.AddRecipesFromPacks(latheTechs, lathe.DynamicPacks);
|
||||||
foreach (var lathe in allLathes)
|
|
||||||
{
|
|
||||||
if (lathe.DynamicRecipes == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
foreach (var recipe in lathe.DynamicRecipes)
|
if (proto.TryGetComponent<EmagLatheRecipesComponent>(out var emag, compFact))
|
||||||
{
|
latheSys.AddRecipesFromPacks(latheTechs, emag.EmagDynamicPacks);
|
||||||
latheTechs.Add(recipe);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
|
// check that every recipe a tech adds can be made on some lathe
|
||||||
|
var unlockedTechs = new HashSet<ProtoId<LatheRecipePrototype>>();
|
||||||
foreach (var tech in protoManager.EnumeratePrototypes<TechnologyPrototype>())
|
foreach (var tech in protoManager.EnumeratePrototypes<TechnologyPrototype>())
|
||||||
{
|
{
|
||||||
|
unlockedTechs.UnionWith(tech.RecipeUnlocks);
|
||||||
foreach (var recipe in tech.RecipeUnlocks)
|
foreach (var recipe in tech.RecipeUnlocks)
|
||||||
{
|
{
|
||||||
Assert.That(latheTechs, Does.Contain(recipe), $"Recipe \"{recipe}\" cannot be unlocked on any lathes.");
|
Assert.That(latheTechs, Does.Contain(recipe), $"Recipe '{recipe}' from tech '{tech.ID}' cannot be unlocked on any lathes.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now check that every dynamic recipe a lathe lists can be unlocked
|
||||||
|
foreach (var recipe in latheTechs)
|
||||||
|
{
|
||||||
|
Assert.That(unlockedTechs, Does.Contain(recipe), $"Recipe '{recipe}' is dynamic on a lathe but cannot be unlocked by research.");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
using System.Linq;
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.CCVar;
|
|
||||||
using Content.Shared.Salvage;
|
using Content.Shared.Salvage;
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.EntitySerialization.Systems;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Map.Components;
|
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests;
|
namespace Content.IntegrationTests.Tests;
|
||||||
@@ -24,7 +21,6 @@ public sealed class SalvageTest
|
|||||||
|
|
||||||
var entManager = server.ResolveDependency<IEntityManager>();
|
var entManager = server.ResolveDependency<IEntityManager>();
|
||||||
var mapLoader = entManager.System<MapLoaderSystem>();
|
var mapLoader = entManager.System<MapLoaderSystem>();
|
||||||
var mapManager = server.ResolveDependency<IMapManager>();
|
|
||||||
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
|
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
|
||||||
var cfg = server.ResolveDependency<IConfigurationManager>();
|
var cfg = server.ResolveDependency<IConfigurationManager>();
|
||||||
var mapSystem = entManager.System<SharedMapSystem>();
|
var mapSystem = entManager.System<SharedMapSystem>();
|
||||||
@@ -34,13 +30,10 @@ public sealed class SalvageTest
|
|||||||
{
|
{
|
||||||
foreach (var salvage in prototypeManager.EnumeratePrototypes<SalvageMapPrototype>())
|
foreach (var salvage in prototypeManager.EnumeratePrototypes<SalvageMapPrototype>())
|
||||||
{
|
{
|
||||||
var mapFile = salvage.MapPath;
|
|
||||||
|
|
||||||
mapSystem.CreateMap(out var mapId);
|
mapSystem.CreateMap(out var mapId);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Assert.That(mapLoader.TryLoad(mapId, mapFile.ToString(), out var roots));
|
Assert.That(mapLoader.TryLoadGrid(mapId, salvage.MapPath, out var grid));
|
||||||
Assert.That(roots.Where(uid => entManager.HasComponent<MapGridComponent>(uid)), Is.Not.Empty);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -49,7 +42,7 @@ public sealed class SalvageTest
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mapManager.DeleteMap(mapId);
|
mapSystem.DeleteMap(mapId);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Content.Shared.CCVar;
|
|||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.ContentPack;
|
using Robust.Shared.ContentPack;
|
||||||
|
using Robust.Shared.EntitySerialization.Systems;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
@@ -16,7 +17,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public async Task SaveLoadMultiGridMap()
|
public async Task SaveLoadMultiGridMap()
|
||||||
{
|
{
|
||||||
const string mapPath = @"/Maps/Test/TestMap.yml";
|
var mapPath = new ResPath("/Maps/Test/TestMap.yml");
|
||||||
|
|
||||||
await using var pair = await PoolManager.GetServerClient();
|
await using var pair = await PoolManager.GetServerClient();
|
||||||
var server = pair.Server;
|
var server = pair.Server;
|
||||||
@@ -31,7 +32,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
var dir = new ResPath(mapPath).Directory;
|
var dir = mapPath.Directory;
|
||||||
resManager.UserData.CreateDir(dir);
|
resManager.UserData.CreateDir(dir);
|
||||||
|
|
||||||
mapSystem.CreateMap(out var mapId);
|
mapSystem.CreateMap(out var mapId);
|
||||||
@@ -39,23 +40,25 @@ namespace Content.IntegrationTests.Tests
|
|||||||
{
|
{
|
||||||
var mapGrid = mapManager.CreateGridEntity(mapId);
|
var mapGrid = mapManager.CreateGridEntity(mapId);
|
||||||
xformSystem.SetWorldPosition(mapGrid, new Vector2(10, 10));
|
xformSystem.SetWorldPosition(mapGrid, new Vector2(10, 10));
|
||||||
mapSystem.SetTile(mapGrid, new Vector2i(0, 0), new Tile(1, (TileRenderFlag) 1, 255));
|
mapSystem.SetTile(mapGrid, new Vector2i(0, 0), new Tile(typeId: 1, flags: 1, variant: 255));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var mapGrid = mapManager.CreateGridEntity(mapId);
|
var mapGrid = mapManager.CreateGridEntity(mapId);
|
||||||
xformSystem.SetWorldPosition(mapGrid, new Vector2(-8, -8));
|
xformSystem.SetWorldPosition(mapGrid, new Vector2(-8, -8));
|
||||||
mapSystem.SetTile(mapGrid, new Vector2i(0, 0), new Tile(2, (TileRenderFlag) 1, 254));
|
mapSystem.SetTile(mapGrid, new Vector2i(0, 0), new Tile(typeId: 2, flags: 1, variant: 254));
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.Multiple(() => mapLoader.SaveMap(mapId, mapPath));
|
Assert.That(mapLoader.TrySaveMap(mapId, mapPath));
|
||||||
Assert.Multiple(() => mapManager.DeleteMap(mapId));
|
mapSystem.DeleteMap(mapId);
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.WaitIdleAsync();
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
|
MapId newMap = default;
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
Assert.That(mapLoader.TryLoad(new MapId(10), mapPath, out _));
|
Assert.That(mapLoader.TryLoadMap(mapPath, out var map, out _));
|
||||||
|
newMap = map!.Value.Comp.MapId;
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.WaitIdleAsync();
|
await server.WaitIdleAsync();
|
||||||
@@ -63,7 +66,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
if (!mapManager.TryFindGridAt(new MapId(10), new Vector2(10, 10), out var gridUid, out var mapGrid) ||
|
if (!mapManager.TryFindGridAt(newMap, new Vector2(10, 10), out var gridUid, out var mapGrid) ||
|
||||||
!sEntities.TryGetComponent<TransformComponent>(gridUid, out var gridXform))
|
!sEntities.TryGetComponent<TransformComponent>(gridUid, out var gridXform))
|
||||||
{
|
{
|
||||||
Assert.Fail();
|
Assert.Fail();
|
||||||
@@ -73,11 +76,11 @@ namespace Content.IntegrationTests.Tests
|
|||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
Assert.That(xformSystem.GetWorldPosition(gridXform), Is.EqualTo(new Vector2(10, 10)));
|
Assert.That(xformSystem.GetWorldPosition(gridXform), Is.EqualTo(new Vector2(10, 10)));
|
||||||
Assert.That(mapSystem.GetTileRef(gridUid, mapGrid, new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(1, (TileRenderFlag) 1, 255)));
|
Assert.That(mapSystem.GetTileRef(gridUid, mapGrid, new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(typeId: 1, flags: 1, variant: 255)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
if (!mapManager.TryFindGridAt(new MapId(10), new Vector2(-8, -8), out var gridUid, out var mapGrid) ||
|
if (!mapManager.TryFindGridAt(newMap, new Vector2(-8, -8), out var gridUid, out var mapGrid) ||
|
||||||
!sEntities.TryGetComponent<TransformComponent>(gridUid, out var gridXform))
|
!sEntities.TryGetComponent<TransformComponent>(gridUid, out var gridXform))
|
||||||
{
|
{
|
||||||
Assert.Fail();
|
Assert.Fail();
|
||||||
@@ -87,7 +90,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
Assert.That(xformSystem.GetWorldPosition(gridXform), Is.EqualTo(new Vector2(-8, -8)));
|
Assert.That(xformSystem.GetWorldPosition(gridXform), Is.EqualTo(new Vector2(-8, -8)));
|
||||||
Assert.That(mapSystem.GetTileRef(gridUid, mapGrid, new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(2, (TileRenderFlag) 1, 254)));
|
Assert.That(mapSystem.GetTileRef(gridUid, mapGrid, new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(typeId: 2, flags: 1, variant: 254)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Server.Maps;
|
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.ContentPack;
|
using Robust.Shared.ContentPack;
|
||||||
|
using Robust.Shared.EntitySerialization.Systems;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Events;
|
||||||
|
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests
|
namespace Content.IntegrationTests.Tests
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests that a map's yaml does not change when saved consecutively.
|
/// Tests that a grid's yaml does not change when saved consecutively.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public sealed class SaveLoadSaveTest
|
public sealed class SaveLoadSaveTest
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public async Task SaveLoadSave()
|
public async Task CreateSaveLoadSaveGrid()
|
||||||
{
|
{
|
||||||
await using var pair = await PoolManager.GetServerClient();
|
await using var pair = await PoolManager.GetServerClient();
|
||||||
var server = pair.Server;
|
var server = pair.Server;
|
||||||
@@ -30,22 +30,21 @@ namespace Content.IntegrationTests.Tests
|
|||||||
var cfg = server.ResolveDependency<IConfigurationManager>();
|
var cfg = server.ResolveDependency<IConfigurationManager>();
|
||||||
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
||||||
|
|
||||||
|
var testSystem = server.System<SaveLoadSaveTestSystem>();
|
||||||
|
testSystem.Enabled = true;
|
||||||
|
|
||||||
|
var rp1 = new ResPath("/save load save 1.yml");
|
||||||
|
var rp2 = new ResPath("/save load save 2.yml");
|
||||||
|
|
||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
mapSystem.CreateMap(out var mapId0);
|
mapSystem.CreateMap(out var mapId0);
|
||||||
// TODO: Properly find the "main" station grid.
|
|
||||||
var grid0 = mapManager.CreateGridEntity(mapId0);
|
var grid0 = mapManager.CreateGridEntity(mapId0);
|
||||||
mapLoader.Save(grid0.Owner, "save load save 1.yml");
|
entManager.RunMapInit(grid0.Owner, entManager.GetComponent<MetaDataComponent>(grid0));
|
||||||
|
Assert.That(mapLoader.TrySaveGrid(grid0.Owner, rp1));
|
||||||
mapSystem.CreateMap(out var mapId1);
|
mapSystem.CreateMap(out var mapId1);
|
||||||
EntityUid grid1 = default!;
|
Assert.That(mapLoader.TryLoadGrid(mapId1, rp1, out var grid1));
|
||||||
#pragma warning disable NUnit2045
|
Assert.That(mapLoader.TrySaveGrid(grid1!.Value, rp2));
|
||||||
Assert.That(mapLoader.TryLoad(mapId1, "save load save 1.yml", out var roots, new MapLoadOptions() { LoadMap = false }), $"Failed to load test map {TestMap}");
|
|
||||||
Assert.DoesNotThrow(() =>
|
|
||||||
{
|
|
||||||
grid1 = roots.First(uid => entManager.HasComponent<MapGridComponent>(uid));
|
|
||||||
});
|
|
||||||
#pragma warning restore NUnit2045
|
|
||||||
mapLoader.Save(grid1, "save load save 2.yml");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.WaitIdleAsync();
|
await server.WaitIdleAsync();
|
||||||
@@ -54,14 +53,12 @@ namespace Content.IntegrationTests.Tests
|
|||||||
string one;
|
string one;
|
||||||
string two;
|
string two;
|
||||||
|
|
||||||
var rp1 = new ResPath("/save load save 1.yml");
|
|
||||||
await using (var stream = userData.Open(rp1, FileMode.Open))
|
await using (var stream = userData.Open(rp1, FileMode.Open))
|
||||||
using (var reader = new StreamReader(stream))
|
using (var reader = new StreamReader(stream))
|
||||||
{
|
{
|
||||||
one = await reader.ReadToEndAsync();
|
one = await reader.ReadToEndAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
var rp2 = new ResPath("/save load save 2.yml");
|
|
||||||
await using (var stream = userData.Open(rp2, FileMode.Open))
|
await using (var stream = userData.Open(rp2, FileMode.Open))
|
||||||
using (var reader = new StreamReader(stream))
|
using (var reader = new StreamReader(stream))
|
||||||
{
|
{
|
||||||
@@ -87,6 +84,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
TestContext.Error.WriteLine(twoTmp);
|
TestContext.Error.WriteLine(twoTmp);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
testSystem.Enabled = false;
|
||||||
await pair.CleanReturnAsync();
|
await pair.CleanReturnAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,8 +99,12 @@ namespace Content.IntegrationTests.Tests
|
|||||||
await using var pair = await PoolManager.GetServerClient();
|
await using var pair = await PoolManager.GetServerClient();
|
||||||
var server = pair.Server;
|
var server = pair.Server;
|
||||||
var mapLoader = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>();
|
var mapLoader = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>();
|
||||||
var mapManager = server.ResolveDependency<IMapManager>();
|
var mapSys = server.System<SharedMapSystem>();
|
||||||
var mapSystem = server.System<SharedMapSystem>();
|
var testSystem = server.System<SaveLoadSaveTestSystem>();
|
||||||
|
testSystem.Enabled = true;
|
||||||
|
|
||||||
|
var rp1 = new ResPath("/load save ticks save 1.yml");
|
||||||
|
var rp2 = new ResPath("/load save ticks save 2.yml");
|
||||||
|
|
||||||
MapId mapId = default;
|
MapId mapId = default;
|
||||||
var cfg = server.ResolveDependency<IConfigurationManager>();
|
var cfg = server.ResolveDependency<IConfigurationManager>();
|
||||||
@@ -111,10 +113,10 @@ namespace Content.IntegrationTests.Tests
|
|||||||
// Load bagel.yml as uninitialized map, and save it to ensure it's up to date.
|
// Load bagel.yml as uninitialized map, and save it to ensure it's up to date.
|
||||||
server.Post(() =>
|
server.Post(() =>
|
||||||
{
|
{
|
||||||
mapSystem.CreateMap(out mapId, runMapInit: false);
|
var path = new ResPath(TestMap);
|
||||||
mapManager.SetMapPaused(mapId, true);
|
Assert.That(mapLoader.TryLoadMap(path, out var map, out _), $"Failed to load test map {TestMap}");
|
||||||
Assert.That(mapLoader.TryLoad(mapId, TestMap, out _), $"Failed to load test map {TestMap}");
|
mapId = map!.Value.Comp.MapId;
|
||||||
mapLoader.SaveMap(mapId, "load save ticks save 1.yml");
|
Assert.That(mapLoader.TrySaveMap(mapId, rp1));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Run 5 ticks.
|
// Run 5 ticks.
|
||||||
@@ -122,7 +124,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
|
|
||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
mapLoader.SaveMap(mapId, "/load save ticks save 2.yml");
|
Assert.That(mapLoader.TrySaveMap(mapId, rp2));
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.WaitIdleAsync();
|
await server.WaitIdleAsync();
|
||||||
@@ -131,13 +133,13 @@ namespace Content.IntegrationTests.Tests
|
|||||||
string one;
|
string one;
|
||||||
string two;
|
string two;
|
||||||
|
|
||||||
await using (var stream = userData.Open(new ResPath("/load save ticks save 1.yml"), FileMode.Open))
|
await using (var stream = userData.Open(rp1, FileMode.Open))
|
||||||
using (var reader = new StreamReader(stream))
|
using (var reader = new StreamReader(stream))
|
||||||
{
|
{
|
||||||
one = await reader.ReadToEndAsync();
|
one = await reader.ReadToEndAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
await using (var stream = userData.Open(new ResPath("/load save ticks save 2.yml"), FileMode.Open))
|
await using (var stream = userData.Open(rp2, FileMode.Open))
|
||||||
using (var reader = new StreamReader(stream))
|
using (var reader = new StreamReader(stream))
|
||||||
{
|
{
|
||||||
two = await reader.ReadToEndAsync();
|
two = await reader.ReadToEndAsync();
|
||||||
@@ -163,7 +165,8 @@ namespace Content.IntegrationTests.Tests
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.WaitPost(() => mapManager.DeleteMap(mapId));
|
testSystem.Enabled = false;
|
||||||
|
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||||
await pair.CleanReturnAsync();
|
await pair.CleanReturnAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,29 +187,31 @@ namespace Content.IntegrationTests.Tests
|
|||||||
var server = pair.Server;
|
var server = pair.Server;
|
||||||
|
|
||||||
var mapLoader = server.System<MapLoaderSystem>();
|
var mapLoader = server.System<MapLoaderSystem>();
|
||||||
var mapSystem = server.System<SharedMapSystem>();
|
var mapSys = server.System<SharedMapSystem>();
|
||||||
var mapManager = server.ResolveDependency<IMapManager>();
|
|
||||||
var userData = server.ResolveDependency<IResourceManager>().UserData;
|
var userData = server.ResolveDependency<IResourceManager>().UserData;
|
||||||
var cfg = server.ResolveDependency<IConfigurationManager>();
|
var cfg = server.ResolveDependency<IConfigurationManager>();
|
||||||
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
||||||
|
var testSystem = server.System<SaveLoadSaveTestSystem>();
|
||||||
|
testSystem.Enabled = true;
|
||||||
|
|
||||||
MapId mapId = default;
|
MapId mapId1 = default;
|
||||||
const string fileA = "/load tick load a.yml";
|
MapId mapId2 = default;
|
||||||
const string fileB = "/load tick load b.yml";
|
var fileA = new ResPath("/load tick load a.yml");
|
||||||
|
var fileB = new ResPath("/load tick load b.yml");
|
||||||
string yamlA;
|
string yamlA;
|
||||||
string yamlB;
|
string yamlB;
|
||||||
|
|
||||||
// Load & save the first map
|
// Load & save the first map
|
||||||
server.Post(() =>
|
server.Post(() =>
|
||||||
{
|
{
|
||||||
mapSystem.CreateMap(out mapId, runMapInit: false);
|
var path = new ResPath(TestMap);
|
||||||
mapManager.SetMapPaused(mapId, true);
|
Assert.That(mapLoader.TryLoadMap(path, out var map, out _), $"Failed to load test map {TestMap}");
|
||||||
Assert.That(mapLoader.TryLoad(mapId, TestMap, out _), $"Failed to load test map {TestMap}");
|
mapId1 = map!.Value.Comp.MapId;
|
||||||
mapLoader.SaveMap(mapId, fileA);
|
Assert.That(mapLoader.TrySaveMap(mapId1, fileA));
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.WaitIdleAsync();
|
await server.WaitIdleAsync();
|
||||||
await using (var stream = userData.Open(new ResPath(fileA), FileMode.Open))
|
await using (var stream = userData.Open(fileA, FileMode.Open))
|
||||||
using (var reader = new StreamReader(stream))
|
using (var reader = new StreamReader(stream))
|
||||||
{
|
{
|
||||||
yamlA = await reader.ReadToEndAsync();
|
yamlA = await reader.ReadToEndAsync();
|
||||||
@@ -217,16 +222,15 @@ namespace Content.IntegrationTests.Tests
|
|||||||
// Load & save the second map
|
// Load & save the second map
|
||||||
server.Post(() =>
|
server.Post(() =>
|
||||||
{
|
{
|
||||||
mapManager.DeleteMap(mapId);
|
var path = new ResPath(TestMap);
|
||||||
mapSystem.CreateMap(out mapId, runMapInit: false);
|
Assert.That(mapLoader.TryLoadMap(path, out var map, out _), $"Failed to load test map {TestMap}");
|
||||||
mapManager.SetMapPaused(mapId, true);
|
mapId2 = map!.Value.Comp.MapId;
|
||||||
Assert.That(mapLoader.TryLoad(mapId, TestMap, out _), $"Failed to load test map {TestMap}");
|
Assert.That(mapLoader.TrySaveMap(mapId2, fileB));
|
||||||
mapLoader.SaveMap(mapId, fileB);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.WaitIdleAsync();
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
await using (var stream = userData.Open(new ResPath(fileB), FileMode.Open))
|
await using (var stream = userData.Open(fileB, FileMode.Open))
|
||||||
using (var reader = new StreamReader(stream))
|
using (var reader = new StreamReader(stream))
|
||||||
{
|
{
|
||||||
yamlB = await reader.ReadToEndAsync();
|
yamlB = await reader.ReadToEndAsync();
|
||||||
@@ -234,8 +238,32 @@ namespace Content.IntegrationTests.Tests
|
|||||||
|
|
||||||
Assert.That(yamlA, Is.EqualTo(yamlB));
|
Assert.That(yamlA, Is.EqualTo(yamlB));
|
||||||
|
|
||||||
await server.WaitPost(() => mapManager.DeleteMap(mapId));
|
testSystem.Enabled = false;
|
||||||
|
await server.WaitPost(() => mapSys.DeleteMap(mapId1));
|
||||||
|
await server.WaitPost(() => mapSys.DeleteMap(mapId2));
|
||||||
await pair.CleanReturnAsync();
|
await pair.CleanReturnAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simple system that modifies the data saved to a yaml file by removing the timestamp.
|
||||||
|
/// Required by some tests that validate that re-saving a map does not modify it.
|
||||||
|
/// </summary>
|
||||||
|
private sealed class SaveLoadSaveTestSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public bool Enabled;
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<AfterSerializationEvent>(OnAfterSave);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAfterSave(AfterSerializationEvent ev)
|
||||||
|
{
|
||||||
|
if (!Enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Remove timestamp.
|
||||||
|
((MappingDataNode)ev.Node["meta"]).Remove("time");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ using System.Numerics;
|
|||||||
using Content.Server.Shuttles.Systems;
|
using Content.Server.Shuttles.Systems;
|
||||||
using Content.Tests;
|
using Content.Tests;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Shared.EntitySerialization.Systems;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Shuttle;
|
namespace Content.IntegrationTests.Tests.Shuttle;
|
||||||
|
|
||||||
@@ -106,8 +108,9 @@ public sealed class DockTest : ContentUnitTest
|
|||||||
{
|
{
|
||||||
mapGrid = entManager.AddComponent<MapGridComponent>(map.MapUid);
|
mapGrid = entManager.AddComponent<MapGridComponent>(map.MapUid);
|
||||||
entManager.DeleteEntity(map.Grid);
|
entManager.DeleteEntity(map.Grid);
|
||||||
Assert.That(entManager.System<MapLoaderSystem>().TryLoad(otherMap.MapId, "/Maps/Shuttles/emergency.yml", out var rootUids));
|
var path = new ResPath("/Maps/Shuttles/emergency.yml");
|
||||||
shuttle = rootUids[0];
|
Assert.That(entManager.System<MapLoaderSystem>().TryLoadGrid(otherMap.MapId, path, out var grid));
|
||||||
|
shuttle = grid!.Value.Owner;
|
||||||
|
|
||||||
var dockingConfig = dockingSystem.GetDockingConfig(shuttle, map.MapUid);
|
var dockingConfig = dockingSystem.GetDockingConfig(shuttle, map.MapUid);
|
||||||
Assert.That(dockingConfig, Is.EqualTo(null));
|
Assert.That(dockingConfig, Is.EqualTo(null));
|
||||||
|
|||||||
2117
Content.Server.Database/Migrations/Postgres/20250211131539_LoadoutNames.Designer.cs
generated
Normal file
2117
Content.Server.Database/Migrations/Postgres/20250211131539_LoadoutNames.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Content.Server.Database.Migrations.Postgres
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class LoadoutNames : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "entity_name",
|
||||||
|
table: "profile_role_loadout",
|
||||||
|
type: "character varying(256)",
|
||||||
|
maxLength: 256,
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "entity_name",
|
||||||
|
table: "profile_role_loadout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -975,6 +975,11 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("EntityName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)")
|
||||||
|
.HasColumnName("entity_name");
|
||||||
|
|
||||||
b.Property<int>("ProfileId")
|
b.Property<int>("ProfileId")
|
||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
||||||
.HasColumnName("profile_id");
|
.HasColumnName("profile_id");
|
||||||
|
|||||||
2041
Content.Server.Database/Migrations/Sqlite/20250211131517_LoadoutNames.Designer.cs
generated
Normal file
2041
Content.Server.Database/Migrations/Sqlite/20250211131517_LoadoutNames.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Content.Server.Database.Migrations.Sqlite
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class LoadoutNames : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "entity_name",
|
||||||
|
table: "profile_role_loadout",
|
||||||
|
type: "TEXT",
|
||||||
|
maxLength: 256,
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "entity_name",
|
||||||
|
table: "profile_role_loadout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -921,6 +921,11 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||||||
.HasColumnType("INTEGER")
|
.HasColumnType("INTEGER")
|
||||||
.HasColumnName("profile_role_loadout_id");
|
.HasColumnName("profile_role_loadout_id");
|
||||||
|
|
||||||
|
b.Property<string>("EntityName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("entity_name");
|
||||||
|
|
||||||
b.Property<int>("ProfileId")
|
b.Property<int>("ProfileId")
|
||||||
.HasColumnType("INTEGER")
|
.HasColumnType("INTEGER")
|
||||||
.HasColumnName("profile_id");
|
.HasColumnName("profile_id");
|
||||||
|
|||||||
@@ -480,6 +480,12 @@ namespace Content.Server.Database
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string RoleName { get; set; } = string.Empty;
|
public string RoleName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom name of the role loadout if it supports it.
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(256)]
|
||||||
|
public string? EntityName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Store the saved loadout groups. These may get validated and removed when loaded at runtime.
|
/// Store the saved loadout groups. These may get validated and removed when loaded at runtime.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
215
Content.Server/Administration/Commands/ChangeCvarCommand.cs
Normal file
215
Content.Server/Administration/Commands/ChangeCvarCommand.cs
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Content.Server.Administration.Logs;
|
||||||
|
using Content.Server.Administration.Managers;
|
||||||
|
using Content.Shared.Administration;
|
||||||
|
using Content.Shared.Database;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.Console;
|
||||||
|
|
||||||
|
namespace Content.Server.Administration.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows admins to change certain CVars. This is different than the "cvar" command which is host only and can change any CVar.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Possible todo for future, store default values for cvars, and allow resetting to default.
|
||||||
|
/// </remarks>
|
||||||
|
[AnyCommand]
|
||||||
|
public sealed class ChangeCvarCommand : IConsoleCommand
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||||
|
[Dependency] private readonly IAdminLogManager _adminLogManager = default!;
|
||||||
|
[Dependency] private readonly CVarControlManager _cVarControlManager = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Searches the list of cvars for a cvar that matches the search string.
|
||||||
|
/// </summary>
|
||||||
|
private void SearchCVars(IConsoleShell shell, string argStr, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length < 2)
|
||||||
|
{
|
||||||
|
shell.WriteLine(Loc.GetString("cmd-changecvar-search-no-arguments"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cvars = _cVarControlManager.GetAllRunnableCvars(shell);
|
||||||
|
|
||||||
|
var matches = cvars
|
||||||
|
.Where(c =>
|
||||||
|
c.Name.Contains(args[1], StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| c.ShortHelp?.Contains(args[1], StringComparison.OrdinalIgnoreCase) == true
|
||||||
|
|| c.LongHelp?.Contains(args[1], StringComparison.OrdinalIgnoreCase) == true
|
||||||
|
) // Might be very slow and stupid, but eh.
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (matches.Count == 0)
|
||||||
|
{
|
||||||
|
shell.WriteLine(Loc.GetString("cmd-changecvar-search-no-matches"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
shell.WriteLine(Loc.GetString("cmd-changecvar-search-matches", ("count", matches.Count)));
|
||||||
|
shell.WriteLine(string.Join("\n", matches.Select(FormatCVarFullHelp)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Formats a CVar into a string for display.
|
||||||
|
/// </summary>
|
||||||
|
private string FormatCVarFullHelp(ChangableCVar cvar)
|
||||||
|
{
|
||||||
|
if (cvar.LongHelp != null && cvar.ShortHelp != null)
|
||||||
|
{
|
||||||
|
return $"{cvar.Name} - {cvar.LongHelp}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is no help, no one is coming. We are all doomed.
|
||||||
|
return cvar.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Command => "changecvar";
|
||||||
|
public string Description { get; } = Loc.GetString("cmd-changecvar-desc");
|
||||||
|
public string Help { get; } = Loc.GetString("cmd-changecvar-help");
|
||||||
|
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length == 0)
|
||||||
|
{
|
||||||
|
shell.WriteLine(Loc.GetString("cmd-changecvar-no-arguments"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cvars = _cVarControlManager.GetAllRunnableCvars(shell);
|
||||||
|
|
||||||
|
var cvar = args[0];
|
||||||
|
if (cvar == "?")
|
||||||
|
{
|
||||||
|
if (cvars.Count == 0)
|
||||||
|
{
|
||||||
|
shell.WriteLine(Loc.GetString("cmd-changecvar-no-cvars"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
shell.WriteLine(Loc.GetString("cmd-changecvar-available-cvars"));
|
||||||
|
shell.WriteLine(string.Join("\n", cvars.Select(FormatCVarFullHelp)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cvar == "search")
|
||||||
|
{
|
||||||
|
SearchCVars(shell, argStr, args);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_configurationManager.IsCVarRegistered(cvar)) // Might be a redunat check with the if statement below.
|
||||||
|
{
|
||||||
|
shell.WriteLine(Loc.GetString("cmd-changecvar-cvar-not-registered", ("cvar", cvar)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cvars.All(c => c.Name != cvar))
|
||||||
|
{
|
||||||
|
shell.WriteLine(Loc.GetString("cmd-changecvar-cvar-not-allowed"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Length == 1)
|
||||||
|
{
|
||||||
|
var value = _configurationManager.GetCVar<object>(cvar);
|
||||||
|
shell.WriteLine(value.ToString()!);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var value = args[1];
|
||||||
|
var type = _configurationManager.GetCVarType(cvar);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var parsed = CVarCommandUtil.ParseObject(type, value);
|
||||||
|
// Value check, is it in the min/max range?
|
||||||
|
var control = _cVarControlManager.GetCVar(cvar)!.Control; // Null check is done above.
|
||||||
|
var allowed = true;
|
||||||
|
if (control is { Min: not null, Max: not null })
|
||||||
|
{
|
||||||
|
switch (parsed) // This looks bad, and im not sorry.
|
||||||
|
{
|
||||||
|
case int intVal:
|
||||||
|
{
|
||||||
|
if (intVal < (int)control.Min || intVal > (int)control.Max)
|
||||||
|
{
|
||||||
|
allowed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case float floatVal:
|
||||||
|
{
|
||||||
|
if (floatVal < (float)control.Min || floatVal > (float)control.Max)
|
||||||
|
{
|
||||||
|
allowed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case long longVal:
|
||||||
|
{
|
||||||
|
if (longVal < (long)control.Min || longVal > (long)control.Max)
|
||||||
|
{
|
||||||
|
allowed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ushort ushortVal:
|
||||||
|
{
|
||||||
|
if (ushortVal < (ushort)control.Min || ushortVal > (ushort)control.Max)
|
||||||
|
{
|
||||||
|
allowed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allowed)
|
||||||
|
{
|
||||||
|
shell.WriteError(Loc.GetString("cmd-changecvar-value-out-of-range",
|
||||||
|
("min", control.Min ?? "-∞"),
|
||||||
|
("max", control.Max ?? "∞")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldValue = _configurationManager.GetCVar<object>(cvar);
|
||||||
|
_configurationManager.SetCVar(cvar, parsed);
|
||||||
|
_adminLogManager.Add(LogType.AdminCommands,
|
||||||
|
LogImpact.High,
|
||||||
|
$"{shell.Player!.Name} ({shell.Player!.UserId}) changed CVAR {cvar} from {oldValue.ToString()} to {parsed.ToString()}"
|
||||||
|
);
|
||||||
|
|
||||||
|
shell.WriteLine(Loc.GetString("cmd-changecvar-success", ("cvar", cvar), ("old", oldValue), ("value", parsed)));
|
||||||
|
}
|
||||||
|
catch (FormatException)
|
||||||
|
{
|
||||||
|
shell.WriteError(Loc.GetString("cmd-cvar-parse-error", ("type", type)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||||
|
{
|
||||||
|
var cvars = _cVarControlManager.GetAllRunnableCvars(shell);
|
||||||
|
|
||||||
|
if (args.Length == 1)
|
||||||
|
{
|
||||||
|
return CompletionResult.FromHintOptions(
|
||||||
|
cvars
|
||||||
|
.Select(c => new CompletionOption(c.Name, c.ShortHelp ?? c.Name)),
|
||||||
|
Loc.GetString("cmd-changecvar-arg-name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var cvar = args[0];
|
||||||
|
if (!_configurationManager.IsCVarRegistered(cvar))
|
||||||
|
return CompletionResult.Empty;
|
||||||
|
|
||||||
|
var type = _configurationManager.GetCVarType(cvar);
|
||||||
|
return CompletionResult.FromHint($"<{type.Name}>");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Server.GameTicking;
|
using Content.Server.GameTicking;
|
||||||
using Content.Server.Maps;
|
using Content.Server.Maps;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Robust.Server.Maps;
|
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
using Robust.Shared.ContentPack;
|
using Robust.Shared.EntitySerialization;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
@@ -25,6 +23,7 @@ namespace Content.Server.Administration.Commands
|
|||||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
var gameTicker = entityManager.EntitySysManager.GetEntitySystem<GameTicker>();
|
var gameTicker = entityManager.EntitySysManager.GetEntitySystem<GameTicker>();
|
||||||
|
var mapSys = entityManager.EntitySysManager.GetEntitySystem<SharedMapSystem>();
|
||||||
|
|
||||||
if (args.Length is not (2 or 4 or 5))
|
if (args.Length is not (2 or 4 or 5))
|
||||||
{
|
{
|
||||||
@@ -32,29 +31,28 @@ namespace Content.Server.Administration.Commands
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prototypeManager.TryIndex<GameMapPrototype>(args[1], out var gameMap))
|
if (!prototypeManager.TryIndex<GameMapPrototype>(args[1], out var gameMap))
|
||||||
{
|
|
||||||
if (!int.TryParse(args[0], out var mapId))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var loadOptions = new MapLoadOptions()
|
|
||||||
{
|
|
||||||
LoadMap = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
var stationName = args.Length == 5 ? args[4] : null;
|
|
||||||
|
|
||||||
if (args.Length >= 4 && int.TryParse(args[2], out var x) && int.TryParse(args[3], out var y))
|
|
||||||
{
|
|
||||||
loadOptions.Offset = new Vector2(x, y);
|
|
||||||
}
|
|
||||||
var grids = gameTicker.LoadGameMap(gameMap, new MapId(mapId), loadOptions, stationName);
|
|
||||||
shell.WriteLine($"Loaded {grids.Count} grids.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
shell.WriteError($"The given map prototype {args[0]} is invalid.");
|
shell.WriteError($"The given map prototype {args[0]} is invalid.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!int.TryParse(args[0], out var mapId))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var stationName = args.Length == 5 ? args[4] : null;
|
||||||
|
|
||||||
|
Vector2? offset = null;
|
||||||
|
if (args.Length >= 4)
|
||||||
|
offset = new Vector2(int.Parse(args[2]), int.Parse(args[3]));
|
||||||
|
|
||||||
|
var id = new MapId(mapId);
|
||||||
|
|
||||||
|
var grids = mapSys.MapExists(id)
|
||||||
|
? gameTicker.MergeGameMap(gameMap, id, stationName: stationName, offset: offset)
|
||||||
|
: gameTicker.LoadGameMapWithId(gameMap, id, stationName: stationName, offset: offset);
|
||||||
|
|
||||||
|
shell.WriteLine($"Loaded {grids.Count} grids.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using System.Linq;
|
using Robust.Shared.EntitySerialization.Systems;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Administration.Commands;
|
namespace Content.Server.Administration.Commands;
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ public sealed class PersistenceSave : IConsoleCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
var mapLoader = _system.GetEntitySystem<MapLoaderSystem>();
|
var mapLoader = _system.GetEntitySystem<MapLoaderSystem>();
|
||||||
mapLoader.SaveMap(mapId, saveFilePath);
|
mapLoader.TrySaveMap(mapId, new ResPath(saveFilePath));
|
||||||
shell.WriteLine(Loc.GetString("cmd-savemap-success"));
|
shell.WriteLine(Loc.GetString("cmd-savemap-success"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,8 +118,9 @@ namespace Content.Server.Administration.Commands
|
|||||||
}
|
}
|
||||||
|
|
||||||
var xform = _entManager.GetComponent<TransformComponent>(playerEntity);
|
var xform = _entManager.GetComponent<TransformComponent>(playerEntity);
|
||||||
|
var xformSystem = _entManager.System<SharedTransformSystem>();
|
||||||
xform.Coordinates = coords;
|
xform.Coordinates = coords;
|
||||||
xform.AttachToGridOrMap();
|
xformSystem.AttachToGridOrMap(playerEntity, xform);
|
||||||
if (_entManager.TryGetComponent(playerEntity, out PhysicsComponent? physics))
|
if (_entManager.TryGetComponent(playerEntity, out PhysicsComponent? physics))
|
||||||
{
|
{
|
||||||
_entManager.System<SharedPhysicsSystem>().SetLinearVelocity(playerEntity, Vector2.Zero, body: physics);
|
_entManager.System<SharedPhysicsSystem>().SetLinearVelocity(playerEntity, Vector2.Zero, body: physics);
|
||||||
|
|||||||
125
Content.Server/Administration/Managers/CVarControlManager.cs
Normal file
125
Content.Server/Administration/Managers/CVarControlManager.cs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using Content.Shared.CCVar.CVarAccess;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.Console;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Reflection;
|
||||||
|
|
||||||
|
namespace Content.Server.Administration.Managers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manages the control of CVars via the <see cref="Content.Shared.CCVar.CVarAccess.CVarControl"/> attribute.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CVarControlManager : IPostInjectInit
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||||
|
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||||
|
[Dependency] private readonly ILocalizationManager _localizationManager = default!;
|
||||||
|
[Dependency] private readonly ILogManager _logger = default!;
|
||||||
|
|
||||||
|
private readonly List<ChangableCVar> _changableCvars = new();
|
||||||
|
private ISawmill _sawmill = default!;
|
||||||
|
|
||||||
|
void IPostInjectInit.PostInject()
|
||||||
|
{
|
||||||
|
_sawmill = _logger.GetSawmill("cvarcontrol");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
RegisterCVars();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterCVars()
|
||||||
|
{
|
||||||
|
if (_changableCvars.Count != 0)
|
||||||
|
{
|
||||||
|
_sawmill.Warning("CVars already registered, overwriting.");
|
||||||
|
_changableCvars.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
var validCvarsDefs = _reflectionManager.FindTypesWithAttribute<CVarDefsAttribute>();
|
||||||
|
|
||||||
|
foreach (var type in validCvarsDefs)
|
||||||
|
{
|
||||||
|
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy))
|
||||||
|
{
|
||||||
|
var allowed = field.GetCustomAttribute<CVarControl>();
|
||||||
|
if (allowed == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cvarDef = (CVarDef)field.GetValue(null)!;
|
||||||
|
_changableCvars.Add(new ChangableCVar(cvarDef.Name, allowed, _localizationManager));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_sawmill.Info($"Registered {_changableCvars.Count} CVars.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all CVars that the player can change.
|
||||||
|
/// </summary>
|
||||||
|
public List<ChangableCVar> GetAllRunnableCvars(IConsoleShell shell)
|
||||||
|
{
|
||||||
|
// Not a player, running as server. We COULD return all cvars,
|
||||||
|
// but a check later down the line will prevent it from anyways. Use the "cvar" command instead.
|
||||||
|
if (shell.Player == null)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
return GetAllRunnableCvars(shell.Player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ChangableCVar> GetAllRunnableCvars(ICommonSession session)
|
||||||
|
{
|
||||||
|
var adminData = _adminManager.GetAdminData(session);
|
||||||
|
if (adminData == null)
|
||||||
|
return []; // Not an admin
|
||||||
|
|
||||||
|
return _changableCvars
|
||||||
|
.Where(cvar => adminData.HasFlag(cvar.Control.AdminFlags))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChangableCVar? GetCVar(string name)
|
||||||
|
{
|
||||||
|
return _changableCvars.FirstOrDefault(cvar => cvar.Name == name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ChangableCVar
|
||||||
|
{
|
||||||
|
private const string LocPrefix = "changecvar";
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
// Holding a reference to the attribute might be skrunkly? Not sure how much mem it eats up.
|
||||||
|
public CVarControl Control { get; }
|
||||||
|
|
||||||
|
public string? ShortHelp;
|
||||||
|
public string? LongHelp;
|
||||||
|
|
||||||
|
public ChangableCVar(string name, CVarControl control, ILocalizationManager loc)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Control = control;
|
||||||
|
|
||||||
|
if (loc.TryGetString($"{LocPrefix}-simple-{name.Replace('.', '_')}", out var simple))
|
||||||
|
{
|
||||||
|
ShortHelp = simple;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loc.TryGetString($"{LocPrefix}-full-{name.Replace('.', '_')}", out var longHelp))
|
||||||
|
{
|
||||||
|
LongHelp = longHelp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If one is set and the other is not, we throw
|
||||||
|
if (ShortHelp == null && LongHelp != null || ShortHelp != null && LongHelp == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Short and long help must both be set or both be null.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ using Content.Server.StationRecords.Systems;
|
|||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Administration.Events;
|
using Content.Shared.Administration.Events;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
|
using Content.Shared.Forensics.Components;
|
||||||
using Content.Shared.GameTicking;
|
using Content.Shared.GameTicking;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
using Robust.Server.GameObjects;
|
using Robust.Shared.EntitySerialization.Systems;
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Map.Components;
|
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Administration.Systems;
|
namespace Content.Server.Administration.Systems;
|
||||||
|
|
||||||
@@ -11,8 +10,7 @@ namespace Content.Server.Administration.Systems;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AdminTestArenaSystem : EntitySystem
|
public sealed class AdminTestArenaSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly MapLoaderSystem _loader = default!;
|
||||||
[Dependency] private readonly MapLoaderSystem _map = default!;
|
|
||||||
[Dependency] private readonly MetaDataSystem _metaDataSystem = default!;
|
[Dependency] private readonly MetaDataSystem _metaDataSystem = default!;
|
||||||
|
|
||||||
public const string ArenaMapPath = "/Maps/Test/admin_test_arena.yml";
|
public const string ArenaMapPath = "/Maps/Test/admin_test_arena.yml";
|
||||||
@@ -28,26 +26,24 @@ public sealed class AdminTestArenaSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
return (arenaMap, arenaGrid);
|
return (arenaMap, arenaGrid);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
ArenaGrid[admin.UserId] = null;
|
|
||||||
return (arenaMap, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ArenaMap[admin.UserId] = _mapManager.GetMapEntityId(_mapManager.CreateMap());
|
|
||||||
_metaDataSystem.SetEntityName(ArenaMap[admin.UserId], $"ATAM-{admin.Name}");
|
|
||||||
var grids = _map.LoadMap(Comp<MapComponent>(ArenaMap[admin.UserId]).MapId, ArenaMapPath);
|
|
||||||
if (grids.Count != 0)
|
|
||||||
{
|
|
||||||
_metaDataSystem.SetEntityName(grids[0], $"ATAG-{admin.Name}");
|
|
||||||
ArenaGrid[admin.UserId] = grids[0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ArenaGrid[admin.UserId] = null;
|
ArenaGrid[admin.UserId] = null;
|
||||||
|
return (arenaMap, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (ArenaMap[admin.UserId], ArenaGrid[admin.UserId]);
|
var path = new ResPath(ArenaMapPath);
|
||||||
|
if (!_loader.TryLoadMap(path, out var map, out var grids))
|
||||||
|
throw new Exception($"Failed to load admin arena");
|
||||||
|
|
||||||
|
ArenaMap[admin.UserId] = map.Value.Owner;
|
||||||
|
_metaDataSystem.SetEntityName(map.Value.Owner, $"ATAM-{admin.Name}");
|
||||||
|
|
||||||
|
var grid = grids.FirstOrNull();
|
||||||
|
ArenaGrid[admin.UserId] = grid?.Owner;
|
||||||
|
if (grid != null)
|
||||||
|
_metaDataSystem.SetEntityName(grid.Value.Owner, $"ATAG-{admin.Name}");
|
||||||
|
|
||||||
|
return (map.Value.Owner, grid?.Owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
var board = Spawn("ChessBoard", xform.Coordinates);
|
var board = Spawn("ChessBoard", xform.Coordinates);
|
||||||
var session = _tabletopSystem.EnsureSession(Comp<TabletopGameComponent>(board));
|
var session = _tabletopSystem.EnsureSession(Comp<TabletopGameComponent>(board));
|
||||||
xform.Coordinates = EntityCoordinates.FromMap(_mapManager, session.Position);
|
xform.Coordinates = EntityCoordinates.FromMap(_mapManager, session.Position);
|
||||||
xform.WorldRotation = Angle.Zero;
|
_transformSystem.SetWorldRotationNoLerp((args.Target, xform), Angle.Zero);
|
||||||
},
|
},
|
||||||
Impact = LogImpact.Extreme,
|
Impact = LogImpact.Extreme,
|
||||||
Message = string.Join(": ", chessName, Loc.GetString("admin-smite-chess-dimension-description"))
|
Message = string.Join(": ", chessName, Loc.GetString("admin-smite-chess-dimension-description"))
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ namespace Content.Server.Administration.Systems
|
|||||||
mark.Text = Loc.GetString("toolshed-verb-mark");
|
mark.Text = Loc.GetString("toolshed-verb-mark");
|
||||||
mark.Message = Loc.GetString("toolshed-verb-mark-description");
|
mark.Message = Loc.GetString("toolshed-verb-mark-description");
|
||||||
mark.Category = VerbCategory.Admin;
|
mark.Category = VerbCategory.Admin;
|
||||||
mark.Act = () => _toolshed.InvokeCommand(player, "=> $marked", Enumerable.Repeat(args.Target, 1), out _);
|
mark.Act = () => _toolshed.InvokeCommand(player, "=> $marked", new List<EntityUid> {args.Target}, out _);
|
||||||
mark.Impact = LogImpact.Low;
|
mark.Impact = LogImpact.Low;
|
||||||
args.Verbs.Add(mark);
|
args.Verbs.Add(mark);
|
||||||
|
|
||||||
@@ -370,7 +370,7 @@ namespace Content.Server.Administration.Systems
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lawBoundComponent != null && target != null)
|
if (lawBoundComponent != null && target != null && _adminManager.HasAdminFlag(player, AdminFlags.Moderator))
|
||||||
{
|
{
|
||||||
args.Verbs.Add(new Verb()
|
args.Verbs.Add(new Verb()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ public sealed class MarkedCommand : ToolshedCommand
|
|||||||
[CommandImplementation]
|
[CommandImplementation]
|
||||||
public IEnumerable<EntityUid> Marked(IInvocationContext ctx)
|
public IEnumerable<EntityUid> Marked(IInvocationContext ctx)
|
||||||
{
|
{
|
||||||
var res = (IEnumerable<EntityUid>?)ctx.ReadVar("marked");
|
var marked = ctx.ReadVar("marked") as IEnumerable<EntityUid>;
|
||||||
res ??= Array.Empty<EntityUid>();
|
return marked ?? Array.Empty<EntityUid>();
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,79 @@
|
|||||||
using Content.Server.Administration;
|
using Content.Server.Administration;
|
||||||
using Content.Server.Chat;
|
|
||||||
using Content.Server.Chat.Systems;
|
using Content.Server.Chat.Systems;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
|
using Robust.Shared.ContentPack;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.Announcements
|
namespace Content.Server.Announcements;
|
||||||
|
|
||||||
|
[AdminCommand(AdminFlags.Moderator)]
|
||||||
|
public sealed class AnnounceCommand : LocalizedEntityCommands
|
||||||
{
|
{
|
||||||
[AdminCommand(AdminFlags.Moderator)]
|
[Dependency] private readonly ChatSystem _chat = default!;
|
||||||
public sealed class AnnounceCommand : IConsoleCommand
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||||
{
|
[Dependency] private readonly IResourceManager _res = default!;
|
||||||
public string Command => "announce";
|
|
||||||
public string Description => "Send an in-game announcement.";
|
|
||||||
public string Help => $"{Command} <sender> <message> or {Command} <message> to send announcement as CentCom.";
|
|
||||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
|
||||||
{
|
|
||||||
var chat = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ChatSystem>();
|
|
||||||
|
|
||||||
if (args.Length == 0)
|
public override string Command => "announce";
|
||||||
|
public override string Description => Loc.GetString("cmd-announce-desc");
|
||||||
|
public override string Help => Loc.GetString("cmd-announce-help", ("command", Command));
|
||||||
|
|
||||||
|
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||||
|
{
|
||||||
|
switch (args.Length)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
shell.WriteError(Loc.GetString("shell-need-minimum-one-argument"));
|
||||||
|
return;
|
||||||
|
case > 4:
|
||||||
|
shell.WriteError(Loc.GetString("shell-wrong-arguments-number"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = args[0];
|
||||||
|
var sender = Loc.GetString("cmd-announce-sender");
|
||||||
|
var color = Color.Gold;
|
||||||
|
var sound = new SoundPathSpecifier("/Audio/Announcements/announce.ogg");
|
||||||
|
|
||||||
|
// Optional sender argument
|
||||||
|
if (args.Length >= 2)
|
||||||
|
sender = args[1];
|
||||||
|
|
||||||
|
// Optional color argument
|
||||||
|
if (args.Length >= 3)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
shell.WriteError("Not enough arguments! Need at least 1.");
|
color = Color.FromHex(args[2]);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
shell.WriteError(Loc.GetString("shell-invalid-color-hex"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.Length == 1)
|
|
||||||
{
|
|
||||||
chat.DispatchGlobalAnnouncement(args[0], colorOverride: Color.Gold);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Explicit IEnumerable<string> due to overload ambiguity on .NET 9
|
|
||||||
var message = string.Join(' ', (IEnumerable<string>)new ArraySegment<string>(args, 1, args.Length-1));
|
|
||||||
chat.DispatchGlobalAnnouncement(message, args[0], colorOverride: Color.Gold);
|
|
||||||
}
|
|
||||||
shell.WriteLine("Sent!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Optional sound argument
|
||||||
|
if (args.Length >= 4)
|
||||||
|
sound = new SoundPathSpecifier(args[3]);
|
||||||
|
|
||||||
|
_chat.DispatchGlobalAnnouncement(message, sender, true, sound, color);
|
||||||
|
shell.WriteLine(Loc.GetString("shell-command-success"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||||
|
{
|
||||||
|
return args.Length switch
|
||||||
|
{
|
||||||
|
1 => CompletionResult.FromHint(Loc.GetString("cmd-announce-arg-message")),
|
||||||
|
2 => CompletionResult.FromHint(Loc.GetString("cmd-announce-arg-sender")),
|
||||||
|
3 => CompletionResult.FromHint(Loc.GetString("cmd-announce-arg-color")),
|
||||||
|
4 => CompletionResult.FromHintOptions(
|
||||||
|
CompletionHelper.AudioFilePath(args[3], _proto, _res),
|
||||||
|
Loc.GetString("cmd-announce-arg-sound")
|
||||||
|
),
|
||||||
|
_ => CompletionResult.Empty
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using Content.Server.Chat.Managers;
|
|||||||
using Content.Server.Jittering;
|
using Content.Server.Jittering;
|
||||||
using Content.Server.Mind;
|
using Content.Server.Mind;
|
||||||
using Content.Server.Stunnable;
|
using Content.Server.Stunnable;
|
||||||
using Content.Shared.Actions;
|
|
||||||
using Content.Shared.Anomaly;
|
using Content.Shared.Anomaly;
|
||||||
using Content.Shared.Anomaly.Components;
|
using Content.Shared.Anomaly.Components;
|
||||||
using Content.Shared.Anomaly.Effects;
|
using Content.Shared.Anomaly.Effects;
|
||||||
@@ -24,8 +23,6 @@ public sealed class InnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IAdminLogManager _adminLog = default!;
|
[Dependency] private readonly IAdminLogManager _adminLog = default!;
|
||||||
[Dependency] private readonly AnomalySystem _anomaly = default!;
|
[Dependency] private readonly AnomalySystem _anomaly = default!;
|
||||||
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
|
||||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
[Dependency] private readonly BodySystem _body = default!;
|
[Dependency] private readonly BodySystem _body = default!;
|
||||||
[Dependency] private readonly IChatManager _chat = default!;
|
[Dependency] private readonly IChatManager _chat = default!;
|
||||||
|
|||||||
@@ -10,10 +10,8 @@ using Content.Shared.Atmos.Monitor;
|
|||||||
using Content.Shared.Atmos.Monitor.Components;
|
using Content.Shared.Atmos.Monitor.Components;
|
||||||
using Content.Shared.DeviceNetwork.Components;
|
using Content.Shared.DeviceNetwork.Components;
|
||||||
using Content.Shared.Pinpointer;
|
using Content.Shared.Pinpointer;
|
||||||
using Content.Shared.Tag;
|
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
using Robust.Shared.Timing;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
@@ -25,11 +23,9 @@ public sealed class AtmosAlertsComputerSystem : SharedAtmosAlertsComputerSystem
|
|||||||
[Dependency] private readonly AirAlarmSystem _airAlarmSystem = default!;
|
[Dependency] private readonly AirAlarmSystem _airAlarmSystem = default!;
|
||||||
[Dependency] private readonly AtmosDeviceNetworkSystem _atmosDevNet = default!;
|
[Dependency] private readonly AtmosDeviceNetworkSystem _atmosDevNet = default!;
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
|
||||||
[Dependency] private readonly MapSystem _mapSystem = default!;
|
[Dependency] private readonly MapSystem _mapSystem = default!;
|
||||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||||
[Dependency] private readonly NavMapSystem _navMapSystem = default!;
|
[Dependency] private readonly NavMapSystem _navMapSystem = default!;
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
||||||
[Dependency] private readonly DeviceListSystem _deviceListSystem = default!;
|
[Dependency] private readonly DeviceListSystem _deviceListSystem = default!;
|
||||||
|
|
||||||
private const float UpdateTime = 1.0f;
|
private const float UpdateTime = 1.0f;
|
||||||
@@ -54,7 +50,7 @@ public sealed class AtmosAlertsComputerSystem : SharedAtmosAlertsComputerSystem
|
|||||||
SubscribeLocalEvent<AtmosAlertsDeviceComponent, AnchorStateChangedEvent>(OnDeviceAnchorChanged);
|
SubscribeLocalEvent<AtmosAlertsDeviceComponent, AnchorStateChangedEvent>(OnDeviceAnchorChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Event handling
|
#region Event handling
|
||||||
|
|
||||||
private void OnConsoleInit(EntityUid uid, AtmosAlertsComputerComponent component, ComponentInit args)
|
private void OnConsoleInit(EntityUid uid, AtmosAlertsComputerComponent component, ComponentInit args)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -61,9 +61,9 @@ public sealed partial class AtmosphereSystem
|
|||||||
mixtures[5].Temperature = 5000f;
|
mixtures[5].Temperature = 5000f;
|
||||||
|
|
||||||
// 6: (Walk-In) Freezer
|
// 6: (Walk-In) Freezer
|
||||||
mixtures[6].AdjustMoles(Gas.Oxygen, Atmospherics.OxygenMolesStandard);
|
mixtures[6].AdjustMoles(Gas.Oxygen, Atmospherics.OxygenMolesFreezer);
|
||||||
mixtures[6].AdjustMoles(Gas.Nitrogen, Atmospherics.NitrogenMolesStandard);
|
mixtures[6].AdjustMoles(Gas.Nitrogen, Atmospherics.NitrogenMolesFreezer);
|
||||||
mixtures[6].Temperature = 235f; // Little colder than an actual freezer but gives a grace period to get e.g. themomachines set up, should keep warm for a few door openings
|
mixtures[6].Temperature = Atmospherics.FreezerTemp; // Little colder than an actual freezer but gives a grace period to get e.g. themomachines set up, should keep warm for a few door openings
|
||||||
|
|
||||||
// 7: Nitrogen (101kpa) for vox rooms
|
// 7: Nitrogen (101kpa) for vox rooms
|
||||||
mixtures[7].AdjustMoles(Gas.Nitrogen, Atmospherics.MolesCellStandard);
|
mixtures[7].AdjustMoles(Gas.Nitrogen, Atmospherics.MolesCellStandard);
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Used by ExperiencePressureDifference to correct push/throw directions from tile-relative to physics world.
|
// Used by ExperiencePressureDifference to correct push/throw directions from tile-relative to physics world.
|
||||||
var gridWorldRotation = xforms.GetComponent(gridAtmosphere).WorldRotation;
|
var gridWorldRotation = _transformSystem.GetWorldRotation(gridAtmosphere);
|
||||||
|
|
||||||
// If we're using monstermos, smooth out the yeet direction to follow the flow
|
// If we're using monstermos, smooth out the yeet direction to follow the flow
|
||||||
if (MonstermosEqualization)
|
if (MonstermosEqualization)
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
|
|
||||||
if (tile.Hotspot.Bypassing)
|
if (tile.Hotspot.Bypassing)
|
||||||
{
|
{
|
||||||
tile.Hotspot.Volume = tile.Air.ReactionResults[GasReaction.Fire] * Atmospherics.FireGrowthRate;
|
tile.Hotspot.Volume = tile.Air.ReactionResults[(byte)GasReaction.Fire] * Atmospherics.FireGrowthRate;
|
||||||
tile.Hotspot.Temperature = tile.Air.Temperature;
|
tile.Hotspot.Temperature = tile.Air.Temperature;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -187,7 +187,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
affected.Temperature = tile.Hotspot.Temperature;
|
affected.Temperature = tile.Hotspot.Temperature;
|
||||||
React(affected, tile);
|
React(affected, tile);
|
||||||
tile.Hotspot.Temperature = affected.Temperature;
|
tile.Hotspot.Temperature = affected.Temperature;
|
||||||
tile.Hotspot.Volume = affected.ReactionResults[GasReaction.Fire] * Atmospherics.FireGrowthRate;
|
tile.Hotspot.Volume = affected.ReactionResults[(byte)GasReaction.Fire] * Atmospherics.FireGrowthRate;
|
||||||
Merge(tile.Air, affected);
|
Merge(tile.Air, affected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ using Content.Server.Power.Components;
|
|||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Shared.Access.Components;
|
using Content.Shared.Access.Components;
|
||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Atmos.Monitor;
|
using Content.Shared.Atmos.Monitor;
|
||||||
using Content.Shared.Atmos.Monitor.Components;
|
using Content.Shared.Atmos.Monitor.Components;
|
||||||
using Content.Shared.Atmos.Piping.Unary.Components;
|
using Content.Shared.Atmos.Piping.Unary.Components;
|
||||||
|
using Content.Shared.Database;
|
||||||
using Content.Shared.DeviceLinking;
|
using Content.Shared.DeviceLinking;
|
||||||
using Content.Shared.DeviceNetwork;
|
using Content.Shared.DeviceNetwork;
|
||||||
using Content.Shared.DeviceNetwork.Systems;
|
using Content.Shared.DeviceNetwork.Systems;
|
||||||
@@ -37,6 +39,7 @@ namespace Content.Server.Atmos.Monitor.Systems;
|
|||||||
public sealed class AirAlarmSystem : EntitySystem
|
public sealed class AirAlarmSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly AccessReaderSystem _access = default!;
|
[Dependency] private readonly AccessReaderSystem _access = default!;
|
||||||
|
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||||
[Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!;
|
[Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!;
|
||||||
[Dependency] private readonly AtmosDeviceNetworkSystem _atmosDevNet = default!;
|
[Dependency] private readonly AtmosDeviceNetworkSystem _atmosDevNet = default!;
|
||||||
[Dependency] private readonly DeviceNetworkSystem _deviceNet = default!;
|
[Dependency] private readonly DeviceNetworkSystem _deviceNet = default!;
|
||||||
@@ -296,6 +299,7 @@ public sealed class AirAlarmSystem : EntitySystem
|
|||||||
addr = netConn.Address;
|
addr = netConn.Address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(args.Actor)} changed {ToPrettyString(uid)} mode to {args.Mode}");
|
||||||
SetMode(uid, addr, args.Mode, false);
|
SetMode(uid, addr, args.Mode, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -307,15 +311,26 @@ public sealed class AirAlarmSystem : EntitySystem
|
|||||||
private void OnUpdateAutoMode(EntityUid uid, AirAlarmComponent component, AirAlarmUpdateAutoModeMessage args)
|
private void OnUpdateAutoMode(EntityUid uid, AirAlarmComponent component, AirAlarmUpdateAutoModeMessage args)
|
||||||
{
|
{
|
||||||
component.AutoMode = args.Enabled;
|
component.AutoMode = args.Enabled;
|
||||||
|
|
||||||
|
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(args.Actor)} changed {ToPrettyString(uid)} auto mode to {args.Enabled}");
|
||||||
UpdateUI(uid, component);
|
UpdateUI(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUpdateThreshold(EntityUid uid, AirAlarmComponent component, AirAlarmUpdateAlarmThresholdMessage args)
|
private void OnUpdateThreshold(EntityUid uid, AirAlarmComponent component, AirAlarmUpdateAlarmThresholdMessage args)
|
||||||
{
|
{
|
||||||
if (AccessCheck(uid, args.Actor, component))
|
if (AccessCheck(uid, args.Actor, component))
|
||||||
|
{
|
||||||
|
if (args.Gas != null)
|
||||||
|
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(args.Actor)} changed {args.Address} {args.Gas} {args.Type} threshold using {ToPrettyString(uid)}");
|
||||||
|
else
|
||||||
|
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(args.Actor)} changed {args.Address} {args.Type} threshold using {ToPrettyString(uid)}");
|
||||||
|
|
||||||
SetThreshold(uid, args.Address, args.Type, args.Threshold, args.Gas);
|
SetThreshold(uid, args.Address, args.Type, args.Threshold, args.Gas);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
UpdateUI(uid, component);
|
UpdateUI(uid, component);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUpdateDeviceData(EntityUid uid, AirAlarmComponent component, AirAlarmUpdateDeviceDataMessage args)
|
private void OnUpdateDeviceData(EntityUid uid, AirAlarmComponent component, AirAlarmUpdateDeviceDataMessage args)
|
||||||
@@ -323,6 +338,8 @@ public sealed class AirAlarmSystem : EntitySystem
|
|||||||
if (AccessCheck(uid, args.Actor, component)
|
if (AccessCheck(uid, args.Actor, component)
|
||||||
&& _deviceList.ExistsInDeviceList(uid, args.Address))
|
&& _deviceList.ExistsInDeviceList(uid, args.Address))
|
||||||
{
|
{
|
||||||
|
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(args.Actor)} changed {args.Address} settings using {ToPrettyString(uid)}");
|
||||||
|
|
||||||
SetDeviceData(uid, args.Address, args.Data);
|
SetDeviceData(uid, args.Address, args.Data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -344,6 +361,7 @@ public sealed class AirAlarmSystem : EntitySystem
|
|||||||
case GasVentPumpData ventData:
|
case GasVentPumpData ventData:
|
||||||
foreach (string addr in component.VentData.Keys)
|
foreach (string addr in component.VentData.Keys)
|
||||||
{
|
{
|
||||||
|
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(args.Actor)} copied settings to vent {addr}");
|
||||||
SetData(uid, addr, args.Data);
|
SetData(uid, addr, args.Data);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -351,6 +369,7 @@ public sealed class AirAlarmSystem : EntitySystem
|
|||||||
case GasVentScrubberData scrubberData:
|
case GasVentScrubberData scrubberData:
|
||||||
foreach (string addr in component.ScrubberData.Keys)
|
foreach (string addr in component.ScrubberData.Keys)
|
||||||
{
|
{
|
||||||
|
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(args.Actor)} copied settings to scrubber {addr}");
|
||||||
SetData(uid, addr, args.Data);
|
SetData(uid, addr, args.Data);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -379,6 +398,7 @@ public sealed class AirAlarmSystem : EntitySystem
|
|||||||
if (!_access.IsAllowed(user.Value, uid, reader))
|
if (!_access.IsAllowed(user.Value, uid, reader))
|
||||||
{
|
{
|
||||||
_popup.PopupEntity(Loc.GetString("air-alarm-ui-access-denied"), user.Value, user.Value);
|
_popup.PopupEntity(Loc.GetString("air-alarm-ui-access-denied"), user.Value, user.Value);
|
||||||
|
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Low, $"{ToPrettyString(user)} attempted to access {ToPrettyString(uid)} without access");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ using Content.Server.NodeContainer.EntitySystems;
|
|||||||
using Content.Server.NodeContainer.Nodes;
|
using Content.Server.NodeContainer.Nodes;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Atmos.Monitor;
|
using Content.Shared.Atmos.Monitor;
|
||||||
using Content.Shared.Atmos.Piping.Components;
|
using Content.Shared.Atmos.Piping.Components;
|
||||||
|
using Content.Shared.Database;
|
||||||
using Content.Shared.DeviceNetwork;
|
using Content.Shared.DeviceNetwork;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
@@ -25,6 +27,7 @@ namespace Content.Server.Atmos.Monitor.Systems;
|
|||||||
// a danger), and atmos (which triggers based on set thresholds).
|
// a danger), and atmos (which triggers based on set thresholds).
|
||||||
public sealed class AtmosMonitorSystem : EntitySystem
|
public sealed class AtmosMonitorSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||||
[Dependency] private readonly AtmosDeviceSystem _atmosDeviceSystem = default!;
|
[Dependency] private readonly AtmosDeviceSystem _atmosDeviceSystem = default!;
|
||||||
[Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!;
|
[Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!;
|
||||||
@@ -393,21 +396,74 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
|||||||
if (!Resolve(uid, ref monitor))
|
if (!Resolve(uid, ref monitor))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Used for logging after the switch statement
|
||||||
|
string logPrefix = "";
|
||||||
|
string logValueSuffix = "";
|
||||||
|
AtmosAlarmThreshold? logPreviousThreshold = null;
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case AtmosMonitorThresholdType.Pressure:
|
case AtmosMonitorThresholdType.Pressure:
|
||||||
|
logPrefix = "pressure";
|
||||||
|
logValueSuffix = "kPa";
|
||||||
|
logPreviousThreshold = monitor.PressureThreshold;
|
||||||
|
|
||||||
monitor.PressureThreshold = threshold;
|
monitor.PressureThreshold = threshold;
|
||||||
break;
|
break;
|
||||||
case AtmosMonitorThresholdType.Temperature:
|
case AtmosMonitorThresholdType.Temperature:
|
||||||
|
logPrefix = "temperature";
|
||||||
|
logValueSuffix = "K";
|
||||||
|
logPreviousThreshold = monitor.TemperatureThreshold;
|
||||||
|
|
||||||
monitor.TemperatureThreshold = threshold;
|
monitor.TemperatureThreshold = threshold;
|
||||||
break;
|
break;
|
||||||
case AtmosMonitorThresholdType.Gas:
|
case AtmosMonitorThresholdType.Gas:
|
||||||
if (gas == null || monitor.GasThresholds == null)
|
if (gas == null || monitor.GasThresholds == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
logPrefix = ((Gas) gas).ToString();
|
||||||
|
logValueSuffix = "kPa";
|
||||||
|
monitor.GasThresholds.TryGetValue((Gas) gas, out logPreviousThreshold);
|
||||||
|
|
||||||
monitor.GasThresholds[(Gas) gas] = threshold;
|
monitor.GasThresholds[(Gas) gas] = threshold;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Admin log each change separately rather than logging the whole state
|
||||||
|
if (logPreviousThreshold != null)
|
||||||
|
{
|
||||||
|
if (threshold.Ignore != logPreviousThreshold.Ignore)
|
||||||
|
{
|
||||||
|
string enabled = threshold.Ignore ? "disabled" : "enabled";
|
||||||
|
_adminLogger.Add(
|
||||||
|
LogType.AtmosDeviceSetting,
|
||||||
|
LogImpact.Medium,
|
||||||
|
$"{ToPrettyString(uid)} {logPrefix} thresholds {enabled}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var change in threshold.GetChanges(logPreviousThreshold))
|
||||||
|
{
|
||||||
|
if (change.Current.Enabled != change.Previous?.Enabled)
|
||||||
|
{
|
||||||
|
string enabled = change.Current.Enabled ? "enabled" : "disabled";
|
||||||
|
_adminLogger.Add(
|
||||||
|
LogType.AtmosDeviceSetting,
|
||||||
|
LogImpact.Medium,
|
||||||
|
$"{ToPrettyString(uid)} {logPrefix} {change.Type} {enabled}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (change.Current.Value != change.Previous?.Value)
|
||||||
|
{
|
||||||
|
_adminLogger.Add(
|
||||||
|
LogType.AtmosDeviceSetting,
|
||||||
|
LogImpact.Medium,
|
||||||
|
$"{ToPrettyString(uid)} {logPrefix} {change.Type} changed from {change.Previous?.Value} {logValueSuffix} to {change.Current.Value} {logValueSuffix}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -11,8 +11,12 @@ namespace Content.Server.Atmos.Piping.Unary.Components
|
|||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed partial class GasVentPumpComponent : Component
|
public sealed partial class GasVentPumpComponent : Component
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies if the device is enabled by an air alarm. Does not indicate if the device is powered.
|
||||||
|
/// By default, all air vents start enabled, whether linked to an alarm or not.
|
||||||
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public bool Enabled { get; set; } = false;
|
public bool Enabled { get; set; } = true;
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public bool IsDirty { get; set; } = false;
|
public bool IsDirty { get; set; } = false;
|
||||||
|
|||||||
@@ -9,8 +9,12 @@ namespace Content.Server.Atmos.Piping.Unary.Components
|
|||||||
[Access(typeof(GasVentScrubberSystem))]
|
[Access(typeof(GasVentScrubberSystem))]
|
||||||
public sealed partial class GasVentScrubberComponent : Component
|
public sealed partial class GasVentScrubberComponent : Component
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies if the device is enabled by an air alarm. Does not indicate if the device is powered.
|
||||||
|
/// By default, all air scrubbers start enabled, whether linked to an alarm or not.
|
||||||
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public bool Enabled { get; set; } = false;
|
public bool Enabled { get; set; } = true;
|
||||||
|
|
||||||
[DataField]
|
[DataField]
|
||||||
public bool IsDirty { get; set; } = false;
|
public bool IsDirty { get; set; } = false;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user