Compare commits

...

23 Commits

Author SHA1 Message Date
Pieter-Jan Briers
92b0e7f1a8 Version: 237.2.0 2024-11-21 00:03:19 +01:00
Pieter-Jan Briers
47d1c372b2 Mute null error from Rider 2024-11-20 23:45:47 +01:00
SpaceManiac
453f763128 Fix minor layout bugs in SplitContainer and BoxContainer (#5529)
* Fix SplitContainer using invalidated measures when clamping SplitCenter

* Fix BoxContainer adding separation for invisible children
2024-11-20 23:41:10 +01:00
Nikolai Korolev
65a7942d63 Remove unused variable, local function and private field (#5528)
* Remove unused local function

* Remove unused variable

* Remove private field
2024-11-20 02:51:46 +01:00
Saphire Lattice
f1f3c60d1f Improve Toolshed type intersection mechanism, add WithCommand for ProtoId (#5515) 2024-11-20 01:00:13 +01:00
SpaceManiac
d4e8a27c23 Add FormattedMessage.TrimEnd (#5524)
* Add FormattedMessage.TrimTrailingNewlines

* Trim all trailing whitespace
2024-11-19 19:55:19 +01:00
Pieter-Jan Briers
18a17da8fa .NET 9 forward compatibility changes
This doesn't switch the projects over to .NET 9, but it does make them work on .NET 9 when we decide to switch in the future.
2024-11-19 19:54:01 +01:00
Nikolai Korolev
90a8c66e96 Fix System.ArgumentException: '0' cannot be greater than -0.01 for very fast audios (#5521)
* Fix `System.ArgumentException: '0' cannot be greater than -0.01`

In case of playing empty audio files

* Add semicolon
2024-11-18 18:46:01 +01:00
MilenVolf
45c14b2bc3 Replace remaining obsolete TileAccess methods (#5519)
* Replace remaining TileAccess methods

* Small fix
2024-11-18 17:19:57 +01:00
SpaceManiac
d227613997 Fix cursor getting stuck when click-dragging off of a control (#5523) 2024-11-16 23:51:32 +01:00
Partmedia
7557cc703c Add FreeBSD packaging target (#5522) 2024-11-16 02:01:17 +01:00
Leon Friedrich
7b81d0d881 Make PVS ignore duplicate view subscriber (#5502) 2024-11-13 00:07:53 +01:00
Leon Friedrich
b59f7801ac More UniqueIndex fixes (#5501) 2024-11-12 23:40:01 +01:00
FluffMe
d724c5b3eb Add conditional formatting to SpinBox buttons text (#5511) 2024-11-12 23:32:14 +01:00
Saphire Lattice
f812dc4dac Hopefully fix the dreaded VV refresh blink (#5517) 2024-11-12 23:31:39 +01:00
MilenVolf
2a1bcb6f1e Replace some obsolete TileAccess methods (#5516)
* Replace some obsolete TileAccess methods

* Guh
2024-11-12 23:17:40 +01:00
slarticodefast
fa9030e59c correct sandbox whitelist for Regex.Matches Method (#5513) 2024-11-12 21:48:39 +01:00
Pieter-Jan Briers
8dcae8631b Update NetSerializer
This enables sending of ImmutableArray<T>
2024-11-11 21:36:41 +01:00
Pieter-Jan Briers
21c3535486 Avoid unhandled exception handlers logging into disposed sawmills on shutdown
This caused a crash & exception swallow on Salamander.
2024-11-11 16:26:04 +01:00
lzk
4e100d96bc Add dative case function to loc manager (#5510)
* dative

* slipped it

* slipped it twice
2024-11-05 19:55:30 +01:00
qrtDaniil
14d3699ae2 Fix for server consoles without width and length (#5507)
* Update SystemConsoleManager.cs

* Update SystemConsoleManager.cs
2024-11-01 23:39:30 +01:00
Amy
350fa8736d add ref readonly to sandbox (#5506) 2024-10-30 02:49:25 +01:00
eoineoineoin
5a82df216d Fixes for rendering in multiple windows (#5497)
* Fix race condition when swapping buffers of secondary windows

* Avoid creating opengl 3.3 windows, to avoid Steam overlay bug

* Revert "Avoid creating opengl 3.3 windows, to avoid Steam overlay bug"

This reverts commit 97b5e7f461.

* Add CVar to perform unlocking test
2024-10-19 16:13:29 +02:00
43 changed files with 292 additions and 137 deletions

View File

@@ -1,4 +1,4 @@
<Project>
<!-- This file automatically reset by Tools/version.py -->
<!-- This file automatically reset by Tools/version.py -->

View File

@@ -54,6 +54,40 @@ END TEMPLATE-->
*None yet*
## 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

View File

@@ -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

View File

@@ -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.

View File

@@ -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; }

View File

@@ -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;
}
}
}

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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)
{

View File

@@ -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"));
}
}

View File

@@ -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));
}
}
}

View File

@@ -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()

View File

@@ -35,6 +35,7 @@ internal partial class UserInterfaceManager
return;
_controlFocused?.ControlFocusExited();
_controlFocused = value;
_needUpdateActiveCursor = true;
}
}

View File

@@ -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)

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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>

View File

@@ -654,6 +654,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 +723,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()"

View File

@@ -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;

View File

@@ -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)

View File

@@ -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>

View File

@@ -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&lt;T&gt;</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&lt;T&gt;</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

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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&lt;EntityUid&gt; ^ IEnumerable&lt;T&gt; -&gt; [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)

View File

@@ -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);

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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));

View File

@@ -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);

View File

@@ -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];

View File

@@ -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

View File

@@ -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);

View File

@@ -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));

View File

@@ -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);

View File

@@ -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()