mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36b278d8b9 | ||
|
|
fc454d55d9 | ||
|
|
b422d3fb3e | ||
|
|
e4101aae8b | ||
|
|
74fe177985 | ||
|
|
bb90d79a3f | ||
|
|
92b0e7f1a8 | ||
|
|
47d1c372b2 | ||
|
|
453f763128 | ||
|
|
65a7942d63 | ||
|
|
f1f3c60d1f | ||
|
|
d4e8a27c23 | ||
|
|
18a17da8fa | ||
|
|
90a8c66e96 | ||
|
|
45c14b2bc3 | ||
|
|
d227613997 | ||
|
|
7557cc703c | ||
|
|
7b81d0d881 | ||
|
|
b59f7801ac | ||
|
|
d724c5b3eb | ||
|
|
f812dc4dac | ||
|
|
2a1bcb6f1e | ||
|
|
fa9030e59c | ||
|
|
8dcae8631b | ||
|
|
21c3535486 | ||
|
|
4e100d96bc | ||
|
|
14d3699ae2 | ||
|
|
350fa8736d | ||
|
|
5a82df216d |
@@ -58,7 +58,7 @@
|
||||
<PackageVersion Include="SharpZstd.Interop" Version="1.5.2-beta2" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.5" />
|
||||
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.2.2" />
|
||||
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.0.2" />
|
||||
<PackageVersion Include="SpaceWizards.Sodium" Version="0.2.1" />
|
||||
<PackageVersion Include="System.Numerics.Vectors" Version="4.5.0" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
Submodule NetSerializer updated: 7f51deaeca...4882400f2c
@@ -54,6 +54,49 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 237.2.3
|
||||
|
||||
|
||||
## 237.2.2
|
||||
|
||||
|
||||
## 237.2.1
|
||||
|
||||
|
||||
## 237.2.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `SharedEyeSystem..SetTarget()` will now also automatically remove the old target from the session's ViewSubscriptions
|
||||
|
||||
### New features
|
||||
|
||||
* `ImmutableArray<T>` can now be serialized by `RobustSerializer`.
|
||||
* `RequiresLocationAttribute`, used by `ref readonly`, is now allowed by the sandbox.
|
||||
* Added `DAT-OBJ()` localization function, for the dative case in certain languages.
|
||||
* Client builds for FreeBSD are now made.
|
||||
* Added `FormattedMessage.TrimEnd()`.
|
||||
* Added Toolshed `with` for `ProtoId<T>`.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix `UniqueIndex<,>.RemoveRange()` and`UniqueIndexHkm<,>.RemoveRange()` clearing the whole set instead of just removing the specified values.
|
||||
* Avoid server crashes on some weird console setups (notably Pterodactyl).
|
||||
* Avoid unhandled exceptions during server shutdown getting swallowed due logging into a disposed logger.
|
||||
* Fix sandbox definitions for `Regex` functions returning `MatchCollection`.
|
||||
* Fix minor layout bugs with `SplitContainer` and `BoxContainer`.
|
||||
|
||||
### Other
|
||||
|
||||
* Changed how multi-window rendering presents to the screen with a new CVar `display.thread_unlock_before_swap`. This is an experiment to see if it solves some synchronization issues.
|
||||
* View Variables no longer clears the window on refresh while waiting on response from server.
|
||||
* `SpinBox` buttons now have a `+` prefix for the positive ones.
|
||||
* Improve Toolshed type intersection mechanism
|
||||
|
||||
### Internal
|
||||
|
||||
* Warning cleanup.
|
||||
|
||||
## 237.1.0
|
||||
|
||||
### New features
|
||||
|
||||
@@ -20,6 +20,15 @@ zzzz-object-pronoun = { GENDER($ent) ->
|
||||
*[neuter] it
|
||||
}
|
||||
|
||||
# Used internally by the DAT-OBJ() function.
|
||||
# Not used in en-US. Created for supporting other languages.
|
||||
zzzz-dat-object = { GENDER($ent) ->
|
||||
[male] him
|
||||
[female] her
|
||||
[epicene] them
|
||||
*[neuter] it
|
||||
}
|
||||
|
||||
# Used internally by the POSS-PRONOUN() function.
|
||||
zzzz-possessive-pronoun = { GENDER($ent) ->
|
||||
[male] his
|
||||
|
||||
@@ -6,7 +6,7 @@ using Xilium.CefGlue;
|
||||
|
||||
namespace Robust.Client.WebView.Cef
|
||||
{
|
||||
public static class Program
|
||||
internal static class Program
|
||||
{
|
||||
// This was supposed to be the main entry for the subprocess program... It doesn't work.
|
||||
public static int Main(string[] args)
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -24,6 +25,7 @@ namespace Robust.Client.WebView.Cef
|
||||
|
||||
[Dependency] private readonly IDependencyCollection _dependencyCollection = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IGameControllerInternal _gameController = default!;
|
||||
[Dependency] private readonly IResourceManagerInternal _resourceManager = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
@@ -61,7 +63,10 @@ namespace Robust.Client.WebView.Cef
|
||||
|
||||
var cachePath = "";
|
||||
if (_resourceManager.UserData is WritableDirProvider userData)
|
||||
cachePath = userData.GetFullPath(new ResPath("/cef_cache"));
|
||||
{
|
||||
var rootDir = UserDataDir.GetRootUserDataDir(_gameController);
|
||||
cachePath = Path.Combine(rootDir, "cef_cache", "0");
|
||||
}
|
||||
|
||||
var settings = new CefSettings()
|
||||
{
|
||||
|
||||
@@ -669,7 +669,8 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
|
||||
// TODO clamp the offset inside of SetPlaybackPosition() itself.
|
||||
var offset = audioP.PlayOffsetSeconds;
|
||||
offset = Math.Clamp(offset, 0f, (float) stream.Length.TotalSeconds - 0.01f);
|
||||
var maxOffset = Math.Max((float) stream.Length.TotalSeconds - 0.01f, 0f);
|
||||
offset = Math.Clamp(offset, 0f, maxOffset);
|
||||
source.PlaybackPosition = offset;
|
||||
|
||||
// For server we will rely on the adjusted one but locally we will have to adjust it ourselves.
|
||||
|
||||
@@ -382,7 +382,7 @@ namespace Robust.Client
|
||||
|
||||
_prof.Initialize();
|
||||
|
||||
_resManager.Initialize(Options.LoadConfigAndUserData ? userDataDir : null);
|
||||
_resManager.Initialize(Options.LoadConfigAndUserData ? userDataDir : null, hideUserDataDir: true);
|
||||
|
||||
var mountOptions = _commandLineArgs != null
|
||||
? MountOptions.Merge(_commandLineArgs.MountOptions, Options.MountOptions)
|
||||
|
||||
@@ -46,7 +46,6 @@ namespace Robust.Client.GameStates
|
||||
|
||||
// sum of all data point sizes in bytes
|
||||
private int _totalHistoryPayload;
|
||||
private int _totalUncompressed;
|
||||
|
||||
public EntityUid WatchEntId { get; set; }
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
@@ -176,6 +176,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
window.BlitDoneEvent!.Reset();
|
||||
window.BlitStartEvent!.Set();
|
||||
window.BlitDoneEvent.Wait();
|
||||
window.UnlockBeforeSwap = Clyde._cfg.GetCVar(CVars.DisplayThreadUnlockBeforeSwap);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -212,8 +213,15 @@ namespace Robust.Client.Graphics.Clyde
|
||||
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
|
||||
Clyde.CheckGlError();
|
||||
|
||||
window.BlitDoneEvent?.Set();
|
||||
if (window.UnlockBeforeSwap)
|
||||
{
|
||||
window.BlitDoneEvent?.Set();
|
||||
}
|
||||
Clyde._windowing!.WindowSwapBuffers(window.Reg);
|
||||
if (!window.UnlockBeforeSwap)
|
||||
{
|
||||
window.BlitDoneEvent?.Set();
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void BlitThreadInit(WindowData reg)
|
||||
@@ -336,6 +344,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
public Thread? BlitThread;
|
||||
public ManualResetEventSlim? BlitStartEvent;
|
||||
public ManualResetEventSlim? BlitDoneEvent;
|
||||
public bool UnlockBeforeSwap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
|
||||
@@ -14,16 +15,16 @@ namespace Robust.Client.Placement.Modes
|
||||
public override void AlignPlacementMode(ScreenCoordinates mouseScreen)
|
||||
{
|
||||
// Go over diagonal size so when placing in a line it doesn't stop snapping.
|
||||
const float SearchBoxSize = 2f; // size of search box in meters
|
||||
const float searchBoxSize = 2f; // size of search box in meters
|
||||
|
||||
MouseCoords = ScreenToCursorGrid(mouseScreen).AlignWithClosestGridTile(SearchBoxSize, pManager.EntityManager, pManager.MapManager);
|
||||
MouseCoords = ScreenToCursorGrid(mouseScreen).AlignWithClosestGridTile(searchBoxSize, pManager.EntityManager, pManager.MapManager);
|
||||
|
||||
var gridId = MouseCoords.GetGridUid(pManager.EntityManager);
|
||||
var gridId = pManager.EntityManager.System<SharedTransformSystem>().GetGrid(MouseCoords);
|
||||
|
||||
if (!pManager.EntityManager.TryGetComponent<MapGridComponent>(gridId, out var mapGrid))
|
||||
return;
|
||||
|
||||
CurrentTile = mapGrid.GetTileRef(MouseCoords);
|
||||
CurrentTile = pManager.EntityManager.System<SharedMapSystem>().GetTileRef(gridId.Value, mapGrid, MouseCoords);
|
||||
float tileSize = mapGrid.TileSize; //convert from ushort to float
|
||||
GridDistancing = tileSize;
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -19,12 +18,12 @@ namespace Robust.Client.Placement.Modes
|
||||
MouseCoords = ScreenToCursorGrid(mouseScreen);
|
||||
|
||||
var tileSize = 1f;
|
||||
var gridIdOpt = MouseCoords.GetGridUid(pManager.EntityManager);
|
||||
var gridIdOpt = pManager.EntityManager.System<SharedTransformSystem>().GetGrid(MouseCoords);
|
||||
|
||||
if (gridIdOpt is EntityUid gridId && gridId.IsValid())
|
||||
if (gridIdOpt is { } gridId && gridId.IsValid())
|
||||
{
|
||||
var mapGrid = pManager.EntityManager.GetComponent<MapGridComponent>(gridId);
|
||||
CurrentTile = mapGrid.GetTileRef(MouseCoords);
|
||||
CurrentTile = pManager.EntityManager.System<SharedMapSystem>().GetTileRef(gridId, mapGrid ,MouseCoords);
|
||||
tileSize = mapGrid.TileSize; //convert from ushort to float
|
||||
}
|
||||
|
||||
@@ -50,12 +49,12 @@ namespace Robust.Client.Placement.Modes
|
||||
return false;
|
||||
}
|
||||
|
||||
var map = MouseCoords.GetMapId(pManager.EntityManager);
|
||||
var map = pManager.EntityManager.System<SharedTransformSystem>().GetMapId(MouseCoords);
|
||||
var bottomLeft = new Vector2(CurrentTile.X, CurrentTile.Y);
|
||||
var topRight = new Vector2(CurrentTile.X + 0.99f, CurrentTile.Y + 0.99f);
|
||||
var box = new Box2(bottomLeft, topRight);
|
||||
|
||||
return !EntitySystem.Get<EntityLookupSystem>().AnyEntitiesIntersecting(map, box);
|
||||
return !pManager.EntityManager.System<EntityLookupSystem>().AnyEntitiesIntersecting(map, box);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -754,14 +754,14 @@ namespace Robust.Client.Placement
|
||||
|
||||
if (CurrentPermission.IsTile)
|
||||
{
|
||||
var gridIdOpt = coordinates.GetGridUid(EntityManager);
|
||||
var gridIdOpt = EntityManager.System<SharedTransformSystem>().GetGrid(coordinates);
|
||||
// If we have actually placed something on a valid grid...
|
||||
if (gridIdOpt is EntityUid gridId && gridId.IsValid())
|
||||
if (gridIdOpt is { } gridId && gridId.IsValid())
|
||||
{
|
||||
var grid = EntityManager.GetComponent<MapGridComponent>(gridId);
|
||||
|
||||
// no point changing the tile to the same thing.
|
||||
if (grid.GetTileRef(coordinates).Tile.TypeId == CurrentPermission.TileType)
|
||||
if (EntityManager.System<SharedMapSystem>().GetTileRef(gridId, grid, coordinates).Tile.TypeId == CurrentPermission.TileType)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -197,8 +197,9 @@ namespace Robust.Client.Placement
|
||||
/// </summary>
|
||||
public TileRef GetTileRef(EntityCoordinates coordinates)
|
||||
{
|
||||
var gridUidOpt = coordinates.GetGridUid(pManager.EntityManager);
|
||||
return gridUidOpt is EntityUid gridUid && gridUid.IsValid() ? pManager.EntityManager.GetComponent<MapGridComponent>(gridUid).GetTileRef(MouseCoords)
|
||||
var gridUidOpt = pManager.EntityManager.System<SharedTransformSystem>().GetGrid(coordinates);
|
||||
return gridUidOpt is { } gridUid && gridUid.IsValid()
|
||||
? pManager.EntityManager.System<SharedMapSystem>().GetTileRef(gridUid, pManager.EntityManager.GetComponent<MapGridComponent>(gridUid), MouseCoords)
|
||||
: new TileRef(gridUidOpt ?? EntityUid.Invalid,
|
||||
MouseCoords.ToVector2i(pManager.EntityManager, pManager.MapManager, pManager.EntityManager.System<SharedTransformSystem>()), Tile.Empty);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -56,7 +57,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
protected override Vector2 MeasureOverride(Vector2 availableSize)
|
||||
{
|
||||
// Account for separation.
|
||||
var separation = ActualSeparation * (ChildCount - 1);
|
||||
var separation = ActualSeparation * (Children.Where(c => c.Visible).Count() - 1);
|
||||
var desiredSize = Vector2.Zero;
|
||||
if (Vertical)
|
||||
{
|
||||
@@ -136,13 +137,14 @@ namespace Robust.Client.UserInterface.Controls
|
||||
protected override Vector2 ArrangeOverride(Vector2 finalSize)
|
||||
{
|
||||
var separation = ActualSeparation;
|
||||
var visibleChildCount = Children.Where(c => c.Visible).Count();
|
||||
|
||||
var stretchAvail = Vertical ? finalSize.Y : finalSize.X;
|
||||
stretchAvail -= separation * (ChildCount - 1);
|
||||
stretchAvail -= separation * (visibleChildCount - 1);
|
||||
stretchAvail = Math.Max(0, stretchAvail);
|
||||
|
||||
// Step one: figure out the sizes of all our children and whether they want to stretch.
|
||||
var sizeList = new List<(Control control, float size, bool stretch)>(ChildCount);
|
||||
var sizeList = new List<(Control control, float size, bool stretch)>(visibleChildCount);
|
||||
var totalStretchRatio = 0f;
|
||||
foreach (var child in Children)
|
||||
{
|
||||
|
||||
@@ -127,11 +127,11 @@ namespace Robust.Client.UserInterface.Controls
|
||||
ClearButtons();
|
||||
foreach (var num in leftButtons)
|
||||
{
|
||||
AddLeftButton(num, num.ToString());
|
||||
AddLeftButton(num, num.ToString("+#;-#;0"));
|
||||
}
|
||||
foreach (var num in rightButtons)
|
||||
{
|
||||
AddRightButton(num, num.ToString());
|
||||
AddRightButton(num, num.ToString("+#;-#;0"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -268,12 +268,15 @@ namespace Robust.Client.UserInterface.Controls
|
||||
var first = GetChild(0);
|
||||
var second = GetChild(1);
|
||||
|
||||
firstMinSize ??= (Vertical ? first.DesiredSize.Y : first.DesiredSize.X);
|
||||
secondMinSize ??= (Vertical ? second.DesiredSize.Y : second.DesiredSize.X);
|
||||
var size = Vertical ? controlSize.Y : controlSize.X;
|
||||
if (first.IsMeasureValid && second.IsMeasureValid)
|
||||
{
|
||||
firstMinSize ??= (Vertical ? first.DesiredSize.Y : first.DesiredSize.X);
|
||||
secondMinSize ??= (Vertical ? second.DesiredSize.Y : second.DesiredSize.X);
|
||||
var size = Vertical ? controlSize.Y : controlSize.X;
|
||||
|
||||
_splitStart = MathHelper.Clamp(_splitStart, firstMinSize.Value,
|
||||
size - (secondMinSize.Value + _splitWidth));
|
||||
_splitStart = MathHelper.Clamp(_splitStart, firstMinSize.Value,
|
||||
size - (secondMinSize.Value + _splitWidth));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,11 +131,6 @@ internal sealed class TextEditRopeViz : OSWindow
|
||||
throw new ArgumentOutOfRangeException(nameof(node));
|
||||
}
|
||||
}
|
||||
|
||||
static UIBox2 Around(Vector2 vec, float size)
|
||||
{
|
||||
return new UIBox2(vec - new Vector2(size, size), vec + new Vector2(size, size));
|
||||
}
|
||||
}
|
||||
|
||||
private static Color[] CalcLeafColors()
|
||||
|
||||
@@ -35,6 +35,7 @@ internal partial class UserInterfaceManager
|
||||
return;
|
||||
_controlFocused?.ControlFocusExited();
|
||||
_controlFocused = value;
|
||||
_needUpdateActiveCursor = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Numerics;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.ViewVariables.Instances;
|
||||
@@ -36,7 +37,7 @@ namespace Robust.Client.ViewVariables.Traits
|
||||
|
||||
public override async void Refresh()
|
||||
{
|
||||
_memberList.DisposeAllChildren();
|
||||
List<Control> replacementControls = [];
|
||||
|
||||
if (Instance.Object != null)
|
||||
{
|
||||
@@ -51,7 +52,7 @@ namespace Robust.Client.ViewVariables.Traits
|
||||
|
||||
foreach (var control in group)
|
||||
{
|
||||
_memberList.AddChild(control);
|
||||
replacementControls.Add(control);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,10 +83,16 @@ namespace Robust.Client.ViewVariables.Traits
|
||||
selectorChain, o, r);
|
||||
};
|
||||
|
||||
_memberList.AddChild(propertyEdit);
|
||||
replacementControls.Add(propertyEdit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_memberList.DisposeAllChildren();
|
||||
foreach (var item in replacementControls)
|
||||
{
|
||||
_memberList.AddChild(item);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void CreateMemberGroupHeader(ref bool first, string groupName, Control container)
|
||||
|
||||
@@ -297,7 +297,7 @@ namespace Robust.Server
|
||||
: null;
|
||||
|
||||
// Set up the VFS
|
||||
_resources.Initialize(dataDir);
|
||||
_resources.Initialize(dataDir, hideUserDataDir: false);
|
||||
|
||||
var mountOptions = _commandLineArgs != null
|
||||
? MountOptions.Merge(_commandLineArgs.MountOptions, Options.MountOptions) : Options.MountOptions;
|
||||
|
||||
@@ -174,7 +174,8 @@ namespace Robust.Server.Console
|
||||
while (Con.KeyAvailable)
|
||||
{
|
||||
ConsoleKeyInfo key = Con.ReadKey(true);
|
||||
Con.SetCursorPosition(0, Con.CursorTop);
|
||||
if (Con.WindowWidth > 0)
|
||||
Con.SetCursorPosition(0, Con.CursorTop);
|
||||
if (!Char.IsControl(key.KeyChar))
|
||||
{
|
||||
currentBuffer = currentBuffer.Insert(internalCursor++, key.KeyChar.ToString());
|
||||
@@ -277,6 +278,7 @@ namespace Robust.Server.Console
|
||||
|
||||
public void DrawCommandLine()
|
||||
{
|
||||
if (Con.WindowWidth <= 0) return;
|
||||
ClearCurrentLine();
|
||||
Con.SetCursorPosition(0, Con.CursorTop);
|
||||
Con.Write("> " + currentBuffer);
|
||||
|
||||
@@ -184,10 +184,9 @@ internal sealed partial class PvsSystem
|
||||
return;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
var i = 0;
|
||||
if (session.AttachedEntity is { } local)
|
||||
{
|
||||
DebugTools.Assert(!session.ViewSubscriptions.Contains(local));
|
||||
Array.Resize(ref pvsSession.Viewers, session.ViewSubscriptions.Count + 1);
|
||||
pvsSession.Viewers[i++] = (local, Transform(local), _eyeQuery.CompOrNull(local));
|
||||
}
|
||||
@@ -198,7 +197,8 @@ internal sealed partial class PvsSystem
|
||||
|
||||
foreach (var ent in session.ViewSubscriptions)
|
||||
{
|
||||
pvsSession.Viewers[i++] = (ent, Transform(ent), _eyeQuery.CompOrNull(ent));
|
||||
if (ent != session.AttachedEntity)
|
||||
pvsSession.Viewers[i++] = (ent, Transform(ent), _eyeQuery.CompOrNull(ent));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ internal sealed partial class PvsSystem
|
||||
{
|
||||
chunk.Initialize(location, _metaQuery, _xformQuery);
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception)
|
||||
{
|
||||
_chunks.Remove(location);
|
||||
throw;
|
||||
|
||||
@@ -126,13 +126,29 @@ namespace Robust.Server
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
|
||||
{
|
||||
var message = ((Exception) args.ExceptionObject).ToString();
|
||||
uh.Log(args.IsTerminating ? LogLevel.Fatal : LogLevel.Error, message);
|
||||
try
|
||||
{
|
||||
uh.Log(args.IsTerminating ? LogLevel.Fatal : LogLevel.Error, message);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Avoid eating the exception if it's during shutdown and the sawmill is already gone.
|
||||
System.Console.WriteLine($"UnhandledException but sawmill is disposed! {message}");
|
||||
}
|
||||
};
|
||||
|
||||
var uo = mgr.GetSawmill("unobserved");
|
||||
TaskScheduler.UnobservedTaskException += (sender, args) =>
|
||||
{
|
||||
uo.Error(args.Exception!.ToString());
|
||||
try
|
||||
{
|
||||
uo.Error(args.Exception!.ToString());
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Avoid eating the exception if it's during shutdown and the sawmill is already gone.
|
||||
System.Console.WriteLine($"UnobservedTaskException but sawmill is disposed! {args.Exception}");
|
||||
}
|
||||
#if EXCEPTION_TOLERANCE
|
||||
args.SetObserved(); // don't crash
|
||||
#endif
|
||||
|
||||
@@ -6,4 +6,3 @@
|
||||
|
||||
[assembly: InternalsVisibleTo("Robust.Client")]
|
||||
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
|
||||
[assembly: InternalsVisibleTo("Content.Benchmarks")]
|
||||
|
||||
@@ -1105,6 +1105,14 @@ namespace Robust.Shared
|
||||
public static readonly CVarDef<bool> DisplayThreadWindowBlit =
|
||||
CVarDef.Create("display.thread_window_blit", true, CVar.CLIENTONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Diagnostic flag for testing. When using a separate thread for multi-window blitting,
|
||||
/// should the worker be unblocked before the SwapBuffers(). Setting to true may improve
|
||||
/// performance but may cause crashes or rendering errors.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> DisplayThreadUnlockBeforeSwap =
|
||||
CVarDef.Create("display.thread_unlock_before_swap", false, CVar.CLIENTONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Buffer size of input command channel from windowing thread to main game thread.
|
||||
/// </summary>
|
||||
|
||||
@@ -88,6 +88,7 @@ namespace Robust.Shared.ContentPack
|
||||
public string SystemAssemblyName = default!;
|
||||
public HashSet<VerifierError> AllowedVerifierErrors = default!;
|
||||
public List<string> WhitelistedNamespaces = default!;
|
||||
public List<string> AllowedAssemblyPrefixes = default!;
|
||||
public Dictionary<string, Dictionary<string, TypeConfig>> Types = default!;
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +131,16 @@ namespace Robust.Shared.ContentPack
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma warning disable RA0004
|
||||
var loadedConfig = _config.Result;
|
||||
#pragma warning restore RA0004
|
||||
|
||||
if (!loadedConfig.AllowedAssemblyPrefixes.Any(allowedNamePrefix => asmName.StartsWith(allowedNamePrefix)))
|
||||
{
|
||||
_sawmill.Error($"Assembly name '{asmName}' is not allowed for a content assembly");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (VerifyIL)
|
||||
{
|
||||
if (!DoVerifyIL(asmName, resolver, peReader, reader))
|
||||
@@ -179,10 +189,6 @@ namespace Robust.Shared.ContentPack
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma warning disable RA0004
|
||||
var loadedConfig = _config.Result;
|
||||
#pragma warning restore RA0004
|
||||
|
||||
var badRefs = new ConcurrentBag<EntityHandle>();
|
||||
|
||||
// We still do explicit type reference scanning, even though the actual whitelists work with raw members.
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
internal string GetPath(ResPath relPath)
|
||||
{
|
||||
return Path.GetFullPath(Path.Combine(_directory.FullName, relPath.ToRelativeSystemPath()));
|
||||
return PathHelpers.SafeGetResourcePath(_directory.FullName, relPath);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -14,7 +14,11 @@ namespace Robust.Shared.ContentPack
|
||||
/// The directory to use for user data.
|
||||
/// If null, a virtual temporary file system is used instead.
|
||||
/// </param>
|
||||
void Initialize(string? userData);
|
||||
/// <param name="hideUserDataDir">
|
||||
/// If true, <see cref="IWritableDirProvider.RootDir"/> will be hidden on
|
||||
/// <see cref="IResourceManager.UserData"/>.
|
||||
/// </param>
|
||||
void Initialize(string? userData, bool hideUserDataDir);
|
||||
|
||||
/// <summary>
|
||||
/// Mounts a single stream as a content file. Useful for unit testing.
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
/// <summary>
|
||||
/// The root path of this provider.
|
||||
/// Can be null if it's a virtual provider.
|
||||
/// Can be null if it's a virtual provider or the path is protected (e.g. on the client).
|
||||
/// </summary>
|
||||
string? RootDir { get; }
|
||||
|
||||
|
||||
@@ -93,19 +93,23 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
Sawmill.Debug("LOADING modules");
|
||||
var files = new Dictionary<string, (ResPath Path, string[] references)>();
|
||||
var files = new Dictionary<string, (ResPath Path, MemoryStream data, string[] references)>();
|
||||
|
||||
// Find all modules we want to load.
|
||||
foreach (var fullPath in paths)
|
||||
{
|
||||
using var asmFile = _res.ContentFileRead(fullPath);
|
||||
var refData = GetAssemblyReferenceData(asmFile);
|
||||
var ms = new MemoryStream();
|
||||
asmFile.CopyTo(ms);
|
||||
|
||||
ms.Position = 0;
|
||||
var refData = GetAssemblyReferenceData(ms);
|
||||
if (refData == null)
|
||||
continue;
|
||||
|
||||
var (asmRefs, asmName) = refData.Value;
|
||||
|
||||
if (!files.TryAdd(asmName, (fullPath, asmRefs)))
|
||||
if (!files.TryAdd(asmName, (fullPath, ms, asmRefs)))
|
||||
{
|
||||
Sawmill.Error("Found multiple modules with the same assembly name " +
|
||||
$"'{asmName}', A: {files[asmName].Path}, B: {fullPath}.");
|
||||
@@ -122,10 +126,10 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
Parallel.ForEach(files, pair =>
|
||||
{
|
||||
var (name, (path, _)) = pair;
|
||||
var (name, (_, data, _)) = pair;
|
||||
|
||||
using var stream = _res.ContentFileRead(path);
|
||||
if (!typeChecker.CheckAssembly(stream, resolver))
|
||||
data.Position = 0;
|
||||
if (!typeChecker.CheckAssembly(data, resolver))
|
||||
{
|
||||
throw new TypeCheckFailedException($"Assembly {name} failed type checks.");
|
||||
}
|
||||
@@ -137,14 +141,15 @@ namespace Robust.Shared.ContentPack
|
||||
var nodes = TopologicalSort.FromBeforeAfter(
|
||||
files,
|
||||
kv => kv.Key,
|
||||
kv => kv.Value.Path,
|
||||
kv => kv.Value,
|
||||
_ => Array.Empty<string>(),
|
||||
kv => kv.Value.references,
|
||||
allowMissing: true); // missing refs would be non-content assemblies so allow that.
|
||||
|
||||
// Actually load them in the order they depend on each other.
|
||||
foreach (var path in TopologicalSort.Sort(nodes))
|
||||
foreach (var item in TopologicalSort.Sort(nodes))
|
||||
{
|
||||
var (path, memory, _) = item;
|
||||
Sawmill.Debug($"Loading module: '{path}'");
|
||||
try
|
||||
{
|
||||
@@ -156,9 +161,9 @@ namespace Robust.Shared.ContentPack
|
||||
}
|
||||
else
|
||||
{
|
||||
using var assemblyStream = _res.ContentFileRead(path);
|
||||
memory.Position = 0;
|
||||
using var symbolsStream = _res.ContentFileReadOrNull(path.WithExtension("pdb"));
|
||||
LoadGameAssembly(assemblyStream, symbolsStream, skipVerify: true);
|
||||
LoadGameAssembly(memory, symbolsStream, skipVerify: true);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -174,7 +179,7 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
private (string[] refs, string name)? GetAssemblyReferenceData(Stream stream)
|
||||
{
|
||||
using var reader = ModLoader.MakePEReader(stream);
|
||||
using var reader = ModLoader.MakePEReader(stream, leaveOpen: true);
|
||||
var metaReader = reader.GetMetadataReader();
|
||||
|
||||
var name = metaReader.GetString(metaReader.GetAssemblyDefinition().Name);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.ContentPack
|
||||
{
|
||||
@@ -63,5 +64,27 @@ namespace Robust.Shared.ContentPack
|
||||
!OperatingSystem.IsWindows()
|
||||
&& !OperatingSystem.IsMacOS();
|
||||
|
||||
|
||||
internal static string SafeGetResourcePath(string baseDir, ResPath path)
|
||||
{
|
||||
var relSysPath = path.ToRelativeSystemPath();
|
||||
if (relSysPath.Contains("\\..") || relSysPath.Contains("/.."))
|
||||
{
|
||||
// Hard cap on any exploit smuggling a .. in there.
|
||||
// Since that could allow leaving sandbox.
|
||||
throw new InvalidOperationException($"This branch should never be reached. Path: {path}");
|
||||
}
|
||||
|
||||
var retPath = Path.GetFullPath(Path.Join(baseDir, relSysPath));
|
||||
// better safe than sorry check
|
||||
if (!retPath.StartsWith(baseDir))
|
||||
{
|
||||
// Allow path to match if it's just missing the directory separator at the end.
|
||||
if (retPath != baseDir.TrimEnd(Path.DirectorySeparatorChar))
|
||||
throw new InvalidOperationException($"This branch should never be reached. Path: {path}");
|
||||
}
|
||||
|
||||
return retPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,13 +41,13 @@ namespace Robust.Shared.ContentPack
|
||||
public IWritableDirProvider UserData { get; private set; } = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Initialize(string? userData)
|
||||
public virtual void Initialize(string? userData, bool hideRootDir)
|
||||
{
|
||||
Sawmill = _logManager.GetSawmill("res");
|
||||
|
||||
if (userData != null)
|
||||
{
|
||||
UserData = new WritableDirProvider(Directory.CreateDirectory(userData));
|
||||
UserData = new WritableDirProvider(Directory.CreateDirectory(userData), hideRootDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -379,7 +379,13 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
if (root is DirLoader loader)
|
||||
{
|
||||
yield return new ResPath(loader.GetPath(new ResPath(@"/")));
|
||||
var rootDir = loader.GetPath(new ResPath(@"/"));
|
||||
|
||||
// TODO: GET RID OF THIS.
|
||||
// This code shouldn't be passing OS disk paths through ResPath.
|
||||
rootDir = rootDir.Replace(Path.DirectorySeparatorChar, '/');
|
||||
|
||||
yield return new ResPath(rootDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ WhitelistedNamespaces:
|
||||
- Content
|
||||
- OpenDreamShared
|
||||
|
||||
AllowedAssemblyPrefixes:
|
||||
- OpenDream
|
||||
- Content
|
||||
|
||||
# The type whitelist does NOT care about which assembly types come from.
|
||||
# This is because types switch assembly all the time.
|
||||
# Just look up stuff like StreamReader on https://apisof.net.
|
||||
@@ -654,6 +658,7 @@ Types:
|
||||
PreserveBaseOverridesAttribute: { All: True }
|
||||
RefSafetyRulesAttribute: { All: True }
|
||||
RequiredMemberAttribute: { All: True }
|
||||
RequiresLocationAttribute: { All: True }
|
||||
RuntimeCompatibilityAttribute: { All: True }
|
||||
RuntimeHelpers: { All: True }
|
||||
SwitchExpressionException: { All: True }
|
||||
@@ -722,11 +727,11 @@ Types:
|
||||
- "System.Text.RegularExpressions.Match Match(string, string)"
|
||||
- "System.Text.RegularExpressions.Match Match(string, string, System.Text.RegularExpressions.RegexOptions)"
|
||||
- "System.Text.RegularExpressions.Match Match(string, string, System.Text.RegularExpressions.RegexOptions, System.TimeSpan)"
|
||||
- "System.Text.RegularExpressions.Match[] Matches(string)"
|
||||
- "System.Text.RegularExpressions.Match[] Matches(string, int)"
|
||||
- "System.Text.RegularExpressions.Match[] Matches(string, string)"
|
||||
- "System.Text.RegularExpressions.Match[] Matches(string, string, System.Text.RegularExpressions.RegexOptions)"
|
||||
- "System.Text.RegularExpressions.Match[] Matches(string, string, System.Text.RegularExpressions.RegexOptions, System.TimeSpan)"
|
||||
- "System.Text.RegularExpressions.MatchCollection Matches(string)"
|
||||
- "System.Text.RegularExpressions.MatchCollection Matches(string, int)"
|
||||
- "System.Text.RegularExpressions.MatchCollection Matches(string, string)"
|
||||
- "System.Text.RegularExpressions.MatchCollection Matches(string, string, System.Text.RegularExpressions.RegexOptions)"
|
||||
- "System.Text.RegularExpressions.MatchCollection Matches(string, string, System.Text.RegularExpressions.RegexOptions, System.TimeSpan)"
|
||||
- "System.Text.RegularExpressions.RegexOptions get_Options()"
|
||||
- "System.TimeSpan get_MatchTimeout()"
|
||||
- "void .ctor()"
|
||||
|
||||
@@ -10,17 +10,22 @@ namespace Robust.Shared.ContentPack
|
||||
/// <inheritdoc />
|
||||
internal sealed class WritableDirProvider : IWritableDirProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
private readonly bool _hideRootDir;
|
||||
|
||||
public string RootDir { get; }
|
||||
|
||||
string? IWritableDirProvider.RootDir => _hideRootDir ? null : RootDir;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an instance of <see cref="WritableDirProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="rootDir">Root file system directory to allow writing.</param>
|
||||
public WritableDirProvider(DirectoryInfo rootDir)
|
||||
/// <param name="hideRootDir">If true, <see cref="IWritableDirProvider.RootDir"/> is reported as null.</param>
|
||||
public WritableDirProvider(DirectoryInfo rootDir, bool hideRootDir)
|
||||
{
|
||||
// FullName does not have a trailing separator, and we MUST have a separator.
|
||||
RootDir = rootDir.FullName + Path.DirectorySeparatorChar.ToString();
|
||||
_hideRootDir = hideRootDir;
|
||||
}
|
||||
|
||||
#region File Access
|
||||
@@ -119,7 +124,7 @@ namespace Robust.Shared.ContentPack
|
||||
throw new FileNotFoundException();
|
||||
|
||||
var dirInfo = new DirectoryInfo(GetFullPath(path));
|
||||
return new WritableDirProvider(dirInfo);
|
||||
return new WritableDirProvider(dirInfo, _hideRootDir);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -180,20 +185,7 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
path = path.Clean();
|
||||
|
||||
return GetFullPath(RootDir, path);
|
||||
}
|
||||
|
||||
private static string GetFullPath(string root, ResPath path)
|
||||
{
|
||||
var relPath = path.ToRelativeSystemPath();
|
||||
if (relPath.Contains("\\..") || relPath.Contains("/.."))
|
||||
{
|
||||
// Hard cap on any exploit smuggling a .. in there.
|
||||
// Since that could allow leaving sandbox.
|
||||
throw new InvalidOperationException($"This branch should never be reached. Path: {path}");
|
||||
}
|
||||
|
||||
return Path.GetFullPath(Path.Combine(root, relPath));
|
||||
return PathHelpers.SafeGetResourcePath(RootDir, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,14 +120,10 @@ public abstract class SharedEyeSystem : EntitySystem
|
||||
if (TryComp(uid, out ActorComponent? actorComp))
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
_views.AddViewSubscriber(value.Value, actorComp.PlayerSession);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Should never be null here
|
||||
_views.RemoveViewSubscriber(eyeComponent.Target!.Value, actorComp.PlayerSession);
|
||||
}
|
||||
|
||||
if (eyeComponent.Target is { } old)
|
||||
_views.RemoveViewSubscriber(old, actorComp.PlayerSession);
|
||||
}
|
||||
|
||||
eyeComponent.Target = value;
|
||||
|
||||
@@ -1064,8 +1064,8 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
|
||||
/// </summary>
|
||||
private record struct ActorRangeCheckJob() : IParallelRobustJob
|
||||
{
|
||||
public EntityQuery<TransformComponent> XformQuery;
|
||||
public SharedUserInterfaceSystem System;
|
||||
public required EntityQuery<TransformComponent> XformQuery;
|
||||
public required SharedUserInterfaceSystem System;
|
||||
public readonly List<(EntityUid Ui, Enum Key, InterfaceData Data, EntityUid Actor, bool Result)> ActorRanges = new();
|
||||
|
||||
public void Execute(int index)
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Robust.Shared.Localization
|
||||
AddCtxFunction(bundle, "GENDER", FuncGender);
|
||||
AddCtxFunction(bundle, "SUBJECT", FuncSubject);
|
||||
AddCtxFunction(bundle, "OBJECT", FuncObject);
|
||||
AddCtxFunction(bundle, "DAT-OBJ", FuncDatObj);
|
||||
AddCtxFunction(bundle, "POSS-ADJ", FuncPossAdj);
|
||||
AddCtxFunction(bundle, "POSS-PRONOUN", FuncPossPronoun);
|
||||
AddCtxFunction(bundle, "REFLEXIVE", FuncReflexive);
|
||||
@@ -203,6 +204,16 @@ namespace Robust.Shared.Localization
|
||||
return new LocValueString(GetString("zzzz-object-pronoun", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the dative form pronoun for the entity's gender.
|
||||
/// This method is intended for languages with a dative case, where indirect objects
|
||||
/// (e.g., "to him," "for her") require specific forms. Not applicable for en-US locale.
|
||||
/// </summary>
|
||||
private ILocValue FuncDatObj(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-dat-object", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective possessive adjective (his, her, their, its) for the entity's gender.
|
||||
/// </summary>
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
|
||||
[assembly: InternalsVisibleTo("OpenToolkit.GraphicsLibraryFramework")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] // Gives access to Castle(Moq)
|
||||
[assembly: InternalsVisibleTo("Content.Benchmarks")]
|
||||
[assembly: InternalsVisibleTo("Robust.Benchmarks")]
|
||||
[assembly: InternalsVisibleTo("Robust.Client.WebView")]
|
||||
[assembly: InternalsVisibleTo("Robust.Packaging")]
|
||||
|
||||
@@ -67,10 +67,11 @@ public sealed class CommandInvocationContextAttribute : Attribute
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks a command implementation as taking the type of the previous command in sequence as a generic argument.
|
||||
/// Marks a command implementation as taking the type of the previous command in sequence as a generic argument. Supports only one generic type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the argument marked with <see cref="PipedArgumentAttribute"/> is not <c>T</c> but instead a pattern like <c>IEnumerable<T></c>, Toolshed will account for this.
|
||||
/// If the argument marked with <see cref="PipedArgumentAttribute"/> is not <c>T</c> but instead a pattern like <c>IEnumerable<T></c>,
|
||||
/// Toolshed will account for this by using <see cref="ReflectionExtensions.IntersectWithGeneric"/>. It's not very precise.
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class TakesPipedTypeAsGenericAttribute : Attribute
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Robust.Shared.Toolshed.Commands.Entities;
|
||||
internal sealed class WithCommand : ToolshedCommand
|
||||
{
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
[CommandImplementation]
|
||||
public IEnumerable<EntityUid> With(
|
||||
@@ -32,4 +33,14 @@ internal sealed class WithCommand : ToolshedCommand
|
||||
var name = _componentFactory.GetComponentName(ty.Ty);
|
||||
return input.Where(x => x.Components.ContainsKey(name) ^ inverted);
|
||||
}
|
||||
|
||||
[CommandImplementation, TakesPipedTypeAsGeneric]
|
||||
public IEnumerable<ProtoId<T>> With<T>(
|
||||
[PipedArgument] IEnumerable<ProtoId<T>> input,
|
||||
[CommandArgument] ProtoId<T> protoId,
|
||||
[CommandInverted] bool inverted
|
||||
) where T : class, IPrototype
|
||||
{
|
||||
return input.Where(x => (x == protoId) ^ inverted);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,12 @@ namespace Robust.Shared.Toolshed.Commands.Generic;
|
||||
[ToolshedCommand]
|
||||
public sealed class AsCommand : ToolshedCommand
|
||||
{
|
||||
public override Type[] TypeParameterParsers => new[] {typeof(Type)};
|
||||
public override Type[] TypeParameterParsers => [ typeof(Type) ];
|
||||
|
||||
/// <summary>
|
||||
/// Uses a typecast to convert a type. It does not handle implicit casts, nor explicit ones.
|
||||
/// If you're thinking to extend this, you probably want to look into making a <see cref="TypeParser{T}"/>
|
||||
/// </summary>
|
||||
[CommandImplementation, TakesPipedTypeAsGeneric]
|
||||
public TOut? As<TOut, TIn>([PipedArgument] TIn value)
|
||||
=> (TOut?)(object?)value;
|
||||
|
||||
@@ -159,18 +159,6 @@ internal static class ReflectionExtensions
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
// IEnumerable<EntityUid> ^ IEnumerable<T> -> EntityUid
|
||||
public static Type Intersect(this Type left, Type right)
|
||||
{
|
||||
if (!left.IsGenericType)
|
||||
return left;
|
||||
|
||||
if (!right.IsGenericType)
|
||||
return left;
|
||||
|
||||
return left.GetGenericArguments().First();
|
||||
}
|
||||
|
||||
public static void DumpGenericInfo(this Type t)
|
||||
{
|
||||
Logger.Debug($"Info for {t.PrettyName()}");
|
||||
@@ -187,9 +175,18 @@ internal static class ReflectionExtensions
|
||||
}
|
||||
|
||||
public static bool IsAssignableToGeneric(this Type left, Type right, ToolshedManager toolshed, bool recursiveDescent = true)
|
||||
{
|
||||
return left.IntersectWithGeneric(right, toolshed, recursiveDescent) is not null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hopefully allows to figure out all the relevant type arguments when intersecting concrete with a generic one. Returns null if no intersection is possible
|
||||
/// Pseudocode: <c>IEnumerable<EntityUid> ^ IEnumerable<T> -> [EntityUid]</c>
|
||||
/// </summary>
|
||||
public static Type[]? IntersectWithGeneric(this Type left, Type right, ToolshedManager toolshed, bool recursiveDescent)
|
||||
{
|
||||
if (left.IsAssignableTo(right))
|
||||
return true;
|
||||
return [left];
|
||||
|
||||
if (right.IsInterface && !left.IsInterface)
|
||||
{
|
||||
@@ -197,15 +194,15 @@ internal static class ReflectionExtensions
|
||||
{
|
||||
if (right.GetMostGenericPossible() != i.GetMostGenericPossible())
|
||||
continue;
|
||||
if (right.IsAssignableToGeneric(i, toolshed, recursiveDescent))
|
||||
return true;
|
||||
if (right.IntersectWithGeneric(i, toolshed, recursiveDescent) is var outType && outType is not null)
|
||||
return outType;
|
||||
}
|
||||
}
|
||||
|
||||
if (left.Constructable() && right.IsGenericParameter)
|
||||
{
|
||||
// TODO: We need a constraint solver and a general overhaul of how toolshed constructs implementations.
|
||||
return true;
|
||||
return [left];
|
||||
}
|
||||
|
||||
if (left.IsGenericType && right.IsGenericType && left.GenericTypeArguments.Length == right.GenericTypeArguments.Length)
|
||||
@@ -215,10 +212,11 @@ internal static class ReflectionExtensions
|
||||
if (!equal)
|
||||
goto next;
|
||||
|
||||
var res = true;
|
||||
Type[]? res = null;
|
||||
foreach (var (leftTy, rightTy) in left.GenericTypeArguments.Zip(right.GenericTypeArguments))
|
||||
{
|
||||
res &= leftTy.IsAssignableToGeneric(rightTy, toolshed, false);
|
||||
if (leftTy.IntersectWithGeneric(rightTy, toolshed, false) is var outType && outType is not null)
|
||||
res = [ .. res ?? [], .. outType ];
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -229,14 +227,14 @@ internal static class ReflectionExtensions
|
||||
{
|
||||
foreach (var leftSubTy in toolshed.AllSteppedTypes(left))
|
||||
{
|
||||
if (leftSubTy.IsAssignableToGeneric(right, toolshed, false))
|
||||
if (leftSubTy.IntersectWithGeneric(right, toolshed, false) is var outType && outType is not null)
|
||||
{
|
||||
return true;
|
||||
return outType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool IsGenericRelated(this Type t)
|
||||
|
||||
@@ -29,6 +29,9 @@ public abstract partial class ToolshedCommand
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does its best to find an implementation that can deal with the given types
|
||||
/// </summary>
|
||||
internal List<MethodInfo> GetConcreteImplementations(Type? pipedType, Type[] typeArguments,
|
||||
string? subCommand)
|
||||
{
|
||||
@@ -97,8 +100,8 @@ public abstract partial class ToolshedCommand
|
||||
if (x.HasCustomAttribute<TakesPipedTypeAsGenericAttribute>())
|
||||
{
|
||||
var paramT = x.ConsoleGetPipedArgument()!.ParameterType;
|
||||
var t = pipedType!.Intersect(paramT);
|
||||
return x.MakeGenericMethod(typeArguments.Append(t).ToArray());
|
||||
var t = pipedType!.IntersectWithGeneric(paramT, Toolshed, true);
|
||||
return x.MakeGenericMethod([.. typeArguments, .. t!]);
|
||||
}
|
||||
else
|
||||
return x.MakeGenericMethod(typeArguments);
|
||||
|
||||
@@ -182,6 +182,31 @@ public sealed partial class FormattedMessage : IReadOnlyList<MarkupNode>
|
||||
AddText("\n");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes extraneous whitespace from the end of the message.
|
||||
/// </summary>
|
||||
public void TrimEnd()
|
||||
{
|
||||
while (_nodes.Count > 1)
|
||||
{
|
||||
var last = _nodes[^1];
|
||||
if (last.Name == null && last.Value.TryGetString(out var text))
|
||||
{
|
||||
string trimmed = text.TrimEnd();
|
||||
if (trimmed.Length == 0)
|
||||
{
|
||||
_nodes.Pop();
|
||||
continue;
|
||||
}
|
||||
else if (trimmed != text)
|
||||
{
|
||||
_nodes[^1] = new MarkupNode(trimmed);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new open node to the formatted message.
|
||||
/// The method for inserting closed nodes: <see cref="Pop"/>. It needs to be
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace Robust.Shared.Utility
|
||||
return false;
|
||||
}
|
||||
|
||||
var newIndex = _index.SetItem(key, new HashSet<TValue>());
|
||||
var newIndex = _index.Remove(key);
|
||||
|
||||
if (_index != newIndex)
|
||||
{
|
||||
@@ -126,7 +126,7 @@ namespace Robust.Shared.Utility
|
||||
|
||||
var c = set.Count;
|
||||
|
||||
set.ExceptWith(set);
|
||||
set.ExceptWith(values);
|
||||
|
||||
return c - set.Count;
|
||||
}
|
||||
|
||||
@@ -89,14 +89,11 @@ namespace Robust.Shared.Utility
|
||||
{
|
||||
InitializedCheck();
|
||||
|
||||
if (_index.Count == 0) return 0;
|
||||
|
||||
if (!_index.TryGetValue(key, out var set)) return 0;
|
||||
if (!_index.TryGetValue(key, out var set))
|
||||
return 0;
|
||||
|
||||
var c = set.Count;
|
||||
|
||||
set.ExceptWith(set);
|
||||
|
||||
set.ExceptWith(values);
|
||||
return c - set.Count;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ public sealed class DeletionNetworkingTests : RobustIntegrationTest
|
||||
var cPlayerMan = client.ResolveDependency<ISharedPlayerManager>();
|
||||
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
|
||||
var xformSys = sEntMan.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
|
||||
var mapSys = sEntMan.EntitySysManager.GetEntitySystem<SharedMapSystem>();
|
||||
|
||||
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
||||
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
||||
@@ -56,18 +57,18 @@ public sealed class DeletionNetworkingTests : RobustIntegrationTest
|
||||
EntityUid grid2 = default;
|
||||
NetEntity grid1Net = default;
|
||||
NetEntity grid2Net = default;
|
||||
server.System<SharedMapSystem>().CreateMap(out var mapId);
|
||||
mapSys.CreateMap(out var mapId);
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var gridComp = mapMan.CreateGridEntity(mapId);
|
||||
gridComp.Comp.SetTile(Vector2i.Zero, new Tile(1));
|
||||
mapSys.SetTile(gridComp, Vector2i.Zero, new Tile(1));
|
||||
grid1 = gridComp.Owner;
|
||||
xformSys.SetLocalPosition(grid1, new Vector2(-2,0));
|
||||
grid1Net = sEntMan.GetNetEntity(grid1);
|
||||
|
||||
gridComp = mapMan.CreateGridEntity(mapId);
|
||||
gridComp.Comp.SetTile(Vector2i.Zero, new Tile(1));
|
||||
mapSys.SetTile(gridComp, Vector2i.Zero, new Tile(1));
|
||||
grid2 = gridComp.Owner;
|
||||
xformSys.SetLocalPosition(grid2, new Vector2(2,0));
|
||||
grid2Net = sEntMan.GetNetEntity(grid2);
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var physSystem = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<SharedPhysicsSystem>();
|
||||
var mapSystem = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<SharedMapSystem>();
|
||||
|
||||
MapId mapId;
|
||||
Entity<MapGridComponent>? gridId1 = null;
|
||||
@@ -69,8 +70,8 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
gridId1?.Comp.SetTile(new Vector2i(0, 0), new Tile(1));
|
||||
gridId2?.Comp.SetTile(new Vector2i(0, 0), new Tile(1));
|
||||
mapSystem.SetTile(gridId1!.Value, new Vector2i(0, 0), new Tile(1));
|
||||
mapSystem.SetTile(gridId2!.Value, new Vector2i(0, 0), new Tile(1));
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(1);
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var mapSystem = entManager.EntitySysManager.GetEntitySystem<SharedMapSystem>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
@@ -27,12 +28,12 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
grid.Comp.SetTile(new Vector2i(i, 0), new Tile(1));
|
||||
mapSystem.SetTile(grid, new Vector2i(i, 0), new Tile(1));
|
||||
}
|
||||
|
||||
for (var i = 10; i >= 0; i--)
|
||||
{
|
||||
grid.Comp.SetTile(new Vector2i(i, 0), Tile.Empty);
|
||||
mapSystem.SetTile(grid, new Vector2i(i, 0), Tile.Empty);
|
||||
}
|
||||
|
||||
Assert.That(entManager.Deleted(gridEntity));
|
||||
@@ -56,6 +57,7 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
@@ -64,12 +66,12 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
grid.Comp.SetTile(new Vector2i(i, 0), new Tile(1));
|
||||
mapSystem.SetTile(grid, new Vector2i(i, 0), new Tile(1));
|
||||
}
|
||||
|
||||
for (var i = 10; i >= 0; i--)
|
||||
{
|
||||
grid.Comp.SetTile(new Vector2i(i, 0), Tile.Empty);
|
||||
mapSystem.SetTile(grid, new Vector2i(i, 0), Tile.Empty);
|
||||
}
|
||||
|
||||
Assert.That(!((!entManager.EntityExists(grid) ? EntityLifeStage.Deleted : entManager.GetComponent<MetaDataComponent>(grid).EntityLifeStage) >= EntityLifeStage.Deleted));
|
||||
|
||||
@@ -59,6 +59,7 @@ public sealed class GridFixtures_Tests : RobustIntegrationTest
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var physSystem = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<SharedPhysicsSystem>();
|
||||
var mapSystem = entManager.EntitySysManager.GetEntitySystem<SharedMapSystem>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
@@ -72,7 +73,7 @@ public sealed class GridFixtures_Tests : RobustIntegrationTest
|
||||
Assert.That(gridBody!.BodyType, Is.EqualTo(BodyType.Static));
|
||||
|
||||
// 1 fixture if we only ever update the 1 chunk
|
||||
grid.Comp.SetTile(Vector2i.Zero, new Tile(1));
|
||||
mapSystem.SetTile(grid, Vector2i.Zero, new Tile(1));
|
||||
|
||||
Assert.That(manager.FixtureCount, Is.EqualTo(1));
|
||||
// Also should only be a single tile.
|
||||
@@ -81,7 +82,7 @@ public sealed class GridFixtures_Tests : RobustIntegrationTest
|
||||
Assert.That(MathHelper.CloseToPercent(Box2.Area(bounds), 1.0f, 0.1f));
|
||||
|
||||
// Now do 2 tiles (same chunk)
|
||||
grid.Comp.SetTile(new Vector2i(0, 1), new Tile(1));
|
||||
mapSystem.SetTile(grid, new Vector2i(0, 1), new Tile(1));
|
||||
|
||||
Assert.That(manager.FixtureCount, Is.EqualTo(1));
|
||||
bounds = manager.Fixtures.First().Value.Shape.ComputeAABB(new Transform(Vector2.Zero, (float) Angle.Zero.Theta), 0);
|
||||
@@ -90,7 +91,7 @@ public sealed class GridFixtures_Tests : RobustIntegrationTest
|
||||
Assert.That(MathHelper.CloseToPercent(Box2.Area(bounds), 2.0f, 0.1f));
|
||||
|
||||
// If we add a new chunk should be 2 now
|
||||
grid.Comp.SetTile(new Vector2i(0, -1), new Tile(1));
|
||||
mapSystem.SetTile(grid, new Vector2i(0, -1), new Tile(1));
|
||||
Assert.That(manager.FixtureCount, Is.EqualTo(2));
|
||||
|
||||
physSystem.SetLinearVelocity(grid, Vector2.One, manager: manager, body: gridBody);
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
entMan.System<SharedMapSystem>().CreateMap(out var mapId);
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
var grid = mapMan.CreateGridEntity(mapId);
|
||||
var gridEnt = grid.Owner;
|
||||
var coordinates = new EntityCoordinates(gridEnt, new Vector2(10, 0));
|
||||
@@ -41,18 +41,18 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
Assert.That(mapSystem.WorldToLocal(grid.Owner, grid.Comp, coordinates.Position), Is.EqualTo(coordinates.Position));
|
||||
|
||||
// Rotate 180 degrees should show -10, 0 for the position in map-terms and 10, 0 for the position in entity terms (i.e. no change).
|
||||
entMan.GetComponent<TransformComponent>(gridEnt).WorldRotation += new Angle(MathF.PI);
|
||||
transformSystem.SetWorldRotation(gridEnt, transformSystem.GetWorldRotation(gridEnt) + new Angle(MathF.PI));
|
||||
Assert.That(transformSystem.GetWorldRotation(gridEnt), Is.EqualTo(new Angle(MathF.PI)));
|
||||
// Check the map coordinate rotates correctly
|
||||
Assert.That(mapSystem.WorldToLocal(grid.Owner, grid.Comp, new Vector2(10, 0)).EqualsApprox(new Vector2(-10, 0), 0.01f));
|
||||
Assert.That(mapSystem.LocalToWorld(grid.Owner, grid.Comp, coordinates.Position).EqualsApprox(new Vector2(-10, 0), 0.01f));
|
||||
Assert.That(mapSystem.WorldToLocal(gridEnt, grid.Comp, new Vector2(10, 0)).EqualsApprox(new Vector2(-10, 0), 0.01f));
|
||||
Assert.That(mapSystem.LocalToWorld(gridEnt, grid.Comp, coordinates.Position).EqualsApprox(new Vector2(-10, 0), 0.01f));
|
||||
|
||||
// Now we'll do the same for 180 degrees.
|
||||
entMan.GetComponent<TransformComponent>(gridEnt).WorldRotation += MathF.PI / 2f;
|
||||
transformSystem.SetWorldRotation(gridEnt, transformSystem.GetWorldRotation(gridEnt) + MathF.PI / 2f);
|
||||
// If grid facing down then worldpos of 10, 0 gets rotated 90 degrees CCW and hence should be 0, 10
|
||||
Assert.That(mapSystem.WorldToLocal(grid.Owner, grid.Comp, new Vector2(10, 0)).EqualsApprox(new Vector2(0, 10), 0.01f));
|
||||
Assert.That(mapSystem.WorldToLocal(gridEnt, grid.Comp, new Vector2(10, 0)).EqualsApprox(new Vector2(0, 10), 0.01f));
|
||||
// If grid facing down then local 10,0 pos should just return 0, -10 given it's aligned with the rotation.
|
||||
Assert.That(mapSystem.LocalToWorld(grid.Owner, grid.Comp, coordinates.Position).EqualsApprox(new Vector2(0, -10), 0.01f));
|
||||
Assert.That(mapSystem.LocalToWorld(gridEnt, grid.Comp, coordinates.Position).EqualsApprox(new Vector2(0, -10), 0.01f));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
entMan.System<SharedMapSystem>().CreateMap(out var mapId);
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
var grid = mapMan.CreateGridEntity(mapId);
|
||||
var gridEnt = grid.Owner;
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
}
|
||||
}
|
||||
|
||||
var chunks = mapSystem.GetMapChunks(grid.Owner, grid.Comp).Select(c => c.Value).ToList();
|
||||
var chunks = mapSystem.GetMapChunks(gridEnt, grid.Comp).Select(c => c.Value).ToList();
|
||||
|
||||
Assert.That(chunks.Count, Is.EqualTo(1));
|
||||
var chunk = chunks[0];
|
||||
|
||||
@@ -42,6 +42,7 @@ public sealed class BroadphaseNetworkingTest : RobustIntegrationTest
|
||||
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
|
||||
var fixturesSystem = sEntMan.EntitySysManager.GetEntitySystem<FixtureSystem>();
|
||||
var physicsSystem = sEntMan.EntitySysManager.GetEntitySystem<SharedPhysicsSystem>();
|
||||
var mapSystem = sEntMan.EntitySysManager.GetEntitySystem<SharedMapSystem>();
|
||||
|
||||
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
||||
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
||||
@@ -58,9 +59,9 @@ public sealed class BroadphaseNetworkingTest : RobustIntegrationTest
|
||||
EntityUid map1 = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
map1 = sEntMan.System<SharedMapSystem>().CreateMap(out var mapId);
|
||||
map1 = mapSystem.CreateMap(out var mapId);
|
||||
var gridEnt = mapMan.CreateGridEntity(mapId);
|
||||
gridEnt.Comp.SetTile(Vector2i.Zero, new Tile(1));
|
||||
mapSystem.SetTile(gridEnt, Vector2i.Zero, new Tile(1));
|
||||
grid1 = gridEnt.Owner;
|
||||
});
|
||||
|
||||
@@ -129,9 +130,9 @@ public sealed class BroadphaseNetworkingTest : RobustIntegrationTest
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
// Create grid
|
||||
map2 = sEntMan.System<SharedMapSystem>().CreateMap(out var mapId);
|
||||
map2 = mapSystem.CreateMap(out var mapId);
|
||||
var gridEnt = mapMan.CreateGridEntity(mapId);
|
||||
gridEnt.Comp.SetTile(Vector2i.Zero, new Tile(1));
|
||||
mapSystem.SetTile(gridEnt, Vector2i.Zero, new Tile(1));
|
||||
grid2 = gridEnt.Owner;
|
||||
|
||||
// Move player
|
||||
|
||||
@@ -42,6 +42,8 @@ namespace Robust.UnitTesting.Shared.Physics
|
||||
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
var transformSystem = entManager.System<SharedTransformSystem>();
|
||||
|
||||
Entity<MapGridComponent> grid = default!;
|
||||
MapId mapId = default!;
|
||||
@@ -51,14 +53,15 @@ namespace Robust.UnitTesting.Shared.Physics
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
entManager.System<SharedMapSystem>().CreateMap(out mapId);
|
||||
mapSystem.CreateMap(out mapId);
|
||||
grid = mapManager.CreateGridEntity(mapId);
|
||||
grid.Comp.SetTile(Vector2i.Zero, new Tile(1));
|
||||
mapSystem.SetTile(grid, Vector2i.Zero, new Tile(1));
|
||||
|
||||
var entityOne = entManager.SpawnEntity("CollisionWakeTestItem", new MapCoordinates(Vector2.One * 2f, mapId));
|
||||
entityOnePhysics = entManager.GetComponent<PhysicsComponent>(entityOne);
|
||||
xform = entManager.GetComponent<TransformComponent>(entityOne);
|
||||
Assert.That(xform.ParentUid == mapManager.GetMapEntityId(mapId));
|
||||
mapSystem.TryGetMap(mapId, out var mapUid);
|
||||
Assert.That(xform.ParentUid == mapUid);
|
||||
|
||||
var entityTwo = entManager.SpawnEntity("CollisionWakeTestItem", new EntityCoordinates(grid, new Vector2(0.5f, 0.5f)));
|
||||
entityTwoPhysics = entManager.GetComponent<PhysicsComponent>(entityTwo);
|
||||
|
||||
@@ -28,6 +28,8 @@ public sealed class GridMovement_Test : RobustIntegrationTest
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var physSystem = systems.GetEntitySystem<SharedPhysicsSystem>();
|
||||
var transformSystem = entManager.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
|
||||
var mapSystem = entManager.EntitySysManager.GetEntitySystem<SharedMapSystem>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
@@ -35,7 +37,7 @@ public sealed class GridMovement_Test : RobustIntegrationTest
|
||||
var grid = mapManager.CreateGridEntity(mapId);
|
||||
|
||||
// Setup 1 body on grid, 1 body off grid, and assert that it's all gucci.
|
||||
grid.Comp.SetTile(Vector2i.Zero, new Tile(1));
|
||||
mapSystem.SetTile(grid, Vector2i.Zero, new Tile(1));
|
||||
var fixtures = entManager.GetComponent<FixturesComponent>(grid);
|
||||
Assert.That(fixtures.FixtureCount, Is.EqualTo(1));
|
||||
|
||||
@@ -67,7 +69,7 @@ public sealed class GridMovement_Test : RobustIntegrationTest
|
||||
Assert.That(onGridBody.ContactCount, Is.EqualTo(0));
|
||||
|
||||
// Alright now move the grid on top of the off grid body, run physics for a frame and see if they contact
|
||||
entManager.GetComponent<TransformComponent>(grid.Owner).LocalPosition = new Vector2(10f, 10f);
|
||||
transformSystem.SetLocalPosition(grid.Owner, new Vector2(10f, 10f));
|
||||
physSystem.Update(0.001f);
|
||||
|
||||
Assert.That(onGridBody.ContactCount, Is.EqualTo(1));
|
||||
|
||||
@@ -23,12 +23,13 @@ public sealed class RecursiveUpdateTest
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapManager = sim.Resolve<IMapManager>();
|
||||
var xforms = entManager.System<SharedTransformSystem>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
var containers = entManager.System<ContainerSystem>();
|
||||
|
||||
var mapId = sim.CreateMap().MapId;
|
||||
var grid = mapManager.CreateGridEntity(mapId);
|
||||
var guid = grid.Owner;
|
||||
grid.Comp.SetTile(Vector2i.Zero, new Tile(1));
|
||||
mapSystem.SetTile(grid, Vector2i.Zero, new Tile(1));
|
||||
Assert.That(entManager.HasComponent<BroadphaseComponent>(guid));
|
||||
|
||||
var broadphase = entManager.GetComponent<BroadphaseComponent>(guid);
|
||||
@@ -161,11 +162,12 @@ public sealed class RecursiveUpdateTest
|
||||
var sim = RobustServerSimulation.NewSimulation().InitializeInstance();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapManager = sim.Resolve<IMapManager>();
|
||||
var mapSystem = entManager.EntitySysManager.GetEntitySystem<SharedMapSystem>();
|
||||
var transforms = entManager.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
|
||||
var lookup = entManager.EntitySysManager.GetEntitySystem<EntityLookupSystem>();
|
||||
|
||||
var mapId = sim.CreateMap().MapId;
|
||||
var map = mapManager.GetMapEntityId(mapId);
|
||||
var map = mapSystem.GetMapOrInvalid(mapId);
|
||||
var mapBroadphase = entManager.GetComponent<BroadphaseComponent>(map);
|
||||
|
||||
var coords = new EntityCoordinates(map, new Vector2(0.5f, 0.5f));
|
||||
@@ -216,7 +218,7 @@ public sealed class RecursiveUpdateTest
|
||||
// Try again, but this time with a parent change.
|
||||
var grid = mapManager.CreateGridEntity(mapId);
|
||||
var guid = grid.Owner;
|
||||
grid.Comp.SetTile(Vector2i.Zero, new Tile(1));
|
||||
mapSystem.SetTile(grid, Vector2i.Zero, new Tile(1));
|
||||
var gridBroadphase = entManager.GetComponent<BroadphaseComponent>(guid);
|
||||
var gridBroadData = new BroadphaseData(guid, EntityUid.Invalid, false, false);
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Robust.UnitTesting.Shared.Resources
|
||||
_testDir = Directory.CreateDirectory(_testDirPath);
|
||||
var subDir = Path.Combine(_testDirPath, "writable");
|
||||
|
||||
_dirProvider = new WritableDirProvider(Directory.CreateDirectory(subDir));
|
||||
_dirProvider = new WritableDirProvider(Directory.CreateDirectory(subDir), hideRootDir: false);
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
|
||||
@@ -32,6 +32,7 @@ PLATFORM_WINDOWS = "windows"
|
||||
PLATFORM_LINUX = "linux"
|
||||
PLATFORM_LINUX_ARM64 = "linux-arm64"
|
||||
PLATFORM_MACOS = "mac"
|
||||
PLATFORM_FREEBSD = "freebsd"
|
||||
|
||||
IGNORED_RESOURCES = {
|
||||
".gitignore",
|
||||
@@ -85,7 +86,7 @@ def main() -> None:
|
||||
parser.add_argument("--platform",
|
||||
"-p",
|
||||
action="store",
|
||||
choices=[PLATFORM_WINDOWS, PLATFORM_MACOS, PLATFORM_LINUX],
|
||||
choices=[PLATFORM_WINDOWS, PLATFORM_MACOS, PLATFORM_LINUX, PLATFORM_FREEBSD],
|
||||
nargs="*",
|
||||
help="Which platform to build for. If not provided, all platforms will be built")
|
||||
|
||||
@@ -98,7 +99,7 @@ def main() -> None:
|
||||
skip_build = args.skip_build
|
||||
|
||||
if not platforms:
|
||||
platforms = [PLATFORM_WINDOWS, PLATFORM_MACOS, PLATFORM_LINUX]
|
||||
platforms = [PLATFORM_WINDOWS, PLATFORM_MACOS, PLATFORM_LINUX, PLATFORM_FREEBSD]
|
||||
|
||||
if os.path.exists("release"):
|
||||
print(Fore.BLUE + Style.DIM +
|
||||
@@ -117,7 +118,7 @@ def main() -> None:
|
||||
if PLATFORM_LINUX in platforms:
|
||||
if not skip_build:
|
||||
wipe_bin()
|
||||
build_linux(skip_build)
|
||||
build_linux(skip_build, "linux", "Linux")
|
||||
|
||||
if PLATFORM_LINUX_ARM64 in platforms:
|
||||
if not skip_build:
|
||||
@@ -129,6 +130,11 @@ def main() -> None:
|
||||
wipe_bin()
|
||||
build_macos(skip_build)
|
||||
|
||||
if PLATFORM_FREEBSD in platforms:
|
||||
if not skip_build:
|
||||
wipe_bin()
|
||||
build_linux(skip_build, "freebsd", "FreeBSD")
|
||||
|
||||
|
||||
def wipe_bin():
|
||||
print(Fore.BLUE + Style.DIM +
|
||||
@@ -177,20 +183,22 @@ def build_macos(skip_build: bool) -> None:
|
||||
client_zip.close()
|
||||
|
||||
|
||||
def build_linux(skip_build: bool) -> None:
|
||||
def build_linux(skip_build: bool, platform, name) -> None:
|
||||
"""Build on Unix-like platforms including Linux and FreeBSD."""
|
||||
# Run a full build.
|
||||
print(Fore.GREEN + "Building project for Linux x64..." + Style.RESET_ALL)
|
||||
rid = "%s-x64" % platform
|
||||
print(Fore.GREEN + "Building project for %s..." % rid + Style.RESET_ALL)
|
||||
|
||||
if not skip_build:
|
||||
publish_client("linux-x64", "Linux")
|
||||
publish_client(rid, name)
|
||||
|
||||
print(Fore.GREEN + "Packaging Linux x64 client..." + Style.RESET_ALL)
|
||||
print(Fore.GREEN + "Packaging %s client..." % rid + Style.RESET_ALL)
|
||||
|
||||
client_zip = zipfile.ZipFile(
|
||||
p("release", "Robust.Client_linux-x64.zip"), "w",
|
||||
p("release", "Robust.Client_%s.zip" % rid), "w",
|
||||
compression=zipfile.ZIP_DEFLATED)
|
||||
|
||||
copy_dir_into_zip(p("bin", "Client", "linux-x64", "publish"), "", client_zip, IGNORED_FILES_LINUX)
|
||||
copy_dir_into_zip(p("bin", "Client", rid, "publish"), "", client_zip, IGNORED_FILES_LINUX)
|
||||
copy_resources("Resources", client_zip)
|
||||
# Cool we're done.
|
||||
client_zip.close()
|
||||
|
||||
Reference in New Issue
Block a user