mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf574d7588 | ||
|
|
b6a4d17144 | ||
|
|
0a47ecc5b5 | ||
|
|
f694f0e311 | ||
|
|
cd90cc6e26 | ||
|
|
3923dd39ae | ||
|
|
3caffa04da | ||
|
|
06b377d1d5 | ||
|
|
41fb191dda | ||
|
|
d4bcc1dc05 | ||
|
|
84dcd658aa | ||
|
|
a634d6bd04 | ||
|
|
36f9df3079 | ||
|
|
824c018a69 | ||
|
|
4b6b688c72 | ||
|
|
71df25b251 | ||
|
|
be14a3c249 | ||
|
|
3c2a4d5c79 | ||
|
|
44180b3ee0 | ||
|
|
bb0e77e937 | ||
|
|
684b9bc852 | ||
|
|
9f3db6693e |
@@ -57,7 +57,7 @@
|
||||
<PackageVersion Include="SharpZstd.Interop" Version="1.5.2-beta2" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.7" />
|
||||
<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="TerraFX.Interop.Windows" Version="10.0.26100.1" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
@@ -54,6 +54,53 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 260.2.5
|
||||
|
||||
|
||||
## 260.2.4
|
||||
|
||||
|
||||
## 260.2.3
|
||||
|
||||
|
||||
## 260.2.2
|
||||
|
||||
|
||||
## 260.2.1
|
||||
|
||||
|
||||
## 260.2.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add `StringBuilder.Insert(int, string)` to sandbox.
|
||||
* Add the WorldNormal to the StartCollideEvent.
|
||||
|
||||
|
||||
## 260.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* `ComponentFactory` is now exposed to `EntitySystem` as `Factory`
|
||||
|
||||
### Other
|
||||
|
||||
* Cleanup warnings in PLacementManager
|
||||
* Cleanup warnings in Clide.Sprite
|
||||
|
||||
## 260.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Fix / change `StartCollideEvent.WorldPoint` to return all points for the collision which may be up to 2 instead of 1.
|
||||
|
||||
### New features
|
||||
|
||||
* Add SpriteSystem dependency to VisualizerSystem.
|
||||
* Add Vertical property to progress bars
|
||||
* Add some `EntProtoId` overloads for group entity spawn methods.
|
||||
|
||||
|
||||
## 259.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -387,7 +387,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)
|
||||
|
||||
@@ -11,6 +11,7 @@ public abstract class VisualizerSystem<T> : EntitySystem
|
||||
{
|
||||
[Dependency] protected readonly AppearanceSystem AppearanceSystem = default!;
|
||||
[Dependency] protected readonly AnimationPlayerSystem AnimationSystem = default!;
|
||||
[Dependency] protected readonly SpriteSystem SpriteSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
||||
@@ -153,7 +153,7 @@ internal partial class Clyde
|
||||
|
||||
// special casing angle = n*pi/2 to avoid box rotation & bounding calculations doesn't seem to give significant speedups.
|
||||
data.SpriteScreenBB = TransformCenteredBox(
|
||||
data.Sprite.Bounds,
|
||||
_spriteSystem.GetLocalBounds((data.Uid, data.Sprite)),
|
||||
finalRotation,
|
||||
pos + batch.PreScaleViewOffset,
|
||||
batch.ViewScale);
|
||||
|
||||
@@ -10,6 +10,7 @@ internal sealed partial class Clyde
|
||||
private MapSystem _mapSystem = default!;
|
||||
private LightTreeSystem _lightTreeSystem = default!;
|
||||
private TransformSystem _transformSystem = default!;
|
||||
private SpriteSystem _spriteSystem = default!;
|
||||
private SpriteTreeSystem _spriteTreeSystem = default!;
|
||||
private ClientOccluderSystem _occluderSystem = default!;
|
||||
|
||||
@@ -24,6 +25,7 @@ internal sealed partial class Clyde
|
||||
_mapSystem = _entitySystemManager.GetEntitySystem<MapSystem>();
|
||||
_lightTreeSystem = _entitySystemManager.GetEntitySystem<LightTreeSystem>();
|
||||
_transformSystem = _entitySystemManager.GetEntitySystem<TransformSystem>();
|
||||
_spriteSystem = _entitySystemManager.GetEntitySystem<SpriteSystem>();
|
||||
_spriteTreeSystem = _entitySystemManager.GetEntitySystem<SpriteTreeSystem>();
|
||||
_occluderSystem = _entitySystemManager.GetEntitySystem<ClientOccluderSystem>();
|
||||
}
|
||||
@@ -33,6 +35,7 @@ internal sealed partial class Clyde
|
||||
_mapSystem = null!;
|
||||
_lightTreeSystem = null!;
|
||||
_transformSystem = null!;
|
||||
_spriteSystem = null!;
|
||||
_spriteTreeSystem = null!;
|
||||
_occluderSystem = null!;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace Robust.Client.Placement
|
||||
|
||||
private SharedMapSystem Maps => EntityManager.System<SharedMapSystem>();
|
||||
private SharedTransformSystem XformSystem => EntityManager.System<SharedTransformSystem>();
|
||||
private SpriteSystem Sprite => EntityManager.System<SpriteSystem>();
|
||||
|
||||
/// <summary>
|
||||
/// How long before a pending tile change is dropped.
|
||||
@@ -711,11 +712,11 @@ namespace Robust.Client.Placement
|
||||
CurrentPlacementOverlayEntity = null;
|
||||
}
|
||||
|
||||
private SpriteComponent SetupPlacementOverlayEntity()
|
||||
private Entity<SpriteComponent> SetupPlacementOverlayEntity()
|
||||
{
|
||||
EnsureNoPlacementOverlayEntity();
|
||||
CurrentPlacementOverlayEntity = EntityManager.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
return EntityManager.EnsureComponent<SpriteComponent>(CurrentPlacementOverlayEntity.Value);
|
||||
return (CurrentPlacementOverlayEntity.Value, EntityManager.EnsureComponent<SpriteComponent>(CurrentPlacementOverlayEntity.Value));
|
||||
}
|
||||
|
||||
private void PreparePlacement(string templateName)
|
||||
@@ -732,10 +733,16 @@ namespace Robust.Client.Placement
|
||||
EntityManager.GetComponent<MetaDataComponent>(CurrentPlacementOverlayEntity.Value));
|
||||
}
|
||||
|
||||
public void PreparePlacementSprite(SpriteComponent sprite)
|
||||
public void PreparePlacementSprite(Entity<SpriteComponent> sprite)
|
||||
{
|
||||
var sc = SetupPlacementOverlayEntity();
|
||||
sc.CopyFrom(sprite);
|
||||
Sprite.CopySprite(sprite.AsNullable(), sc.AsNullable());
|
||||
}
|
||||
|
||||
[Obsolete("Use the Entity<SpriteComponent> overload.")]
|
||||
public void PreparePlacementSprite(SpriteComponent sprite)
|
||||
{
|
||||
PreparePlacementSprite((sprite.Owner, sprite));
|
||||
}
|
||||
|
||||
public void PreparePlacementTexList(List<IDirectionalTextureProvider>? texs, bool noRot, EntityPrototype? prototype)
|
||||
@@ -746,27 +753,27 @@ namespace Robust.Client.Placement
|
||||
// This one covers most cases (including Construction)
|
||||
foreach (var v in texs)
|
||||
{
|
||||
if (v is RSI.State)
|
||||
if (v is RSI.State st)
|
||||
{
|
||||
var st = (RSI.State) v;
|
||||
sc.AddLayer(st.StateId, st.RSI);
|
||||
Sprite.AddRsiLayer(sc.AsNullable(), st.StateId, st.RSI);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback
|
||||
sc.AddLayer(v.Default);
|
||||
Sprite.AddTextureLayer(sc.AsNullable(), v.Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.AddLayer(new ResPath("/Textures/Interface/tilebuildoverlay.png"));
|
||||
Sprite.AddTextureLayer(sc.AsNullable(), new ResPath("/Textures/Interface/tilebuildoverlay.png"));
|
||||
}
|
||||
sc.NoRotation = noRot;
|
||||
|
||||
sc.Comp.NoRotation = noRot;
|
||||
|
||||
if (prototype != null && prototype.TryGetComponent<SpriteComponent>("Sprite", out var spriteComp))
|
||||
{
|
||||
sc.Scale = spriteComp.Scale;
|
||||
Sprite.SetScale(sc.AsNullable(), spriteComp.Scale);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -774,7 +781,7 @@ namespace Robust.Client.Placement
|
||||
private void PreparePlacementTile()
|
||||
{
|
||||
var sc = SetupPlacementOverlayEntity();
|
||||
sc.AddLayer(new ResPath("/Textures/Interface/tilebuildoverlay.png"));
|
||||
Sprite.AddTextureLayer(sc.AsNullable(), new ResPath("/Textures/Interface/tilebuildoverlay.png"));
|
||||
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace Robust.Client.Player
|
||||
{
|
||||
if (_client.RunLevel != ClientRunLevel.SinglePlayerGame)
|
||||
Sawmill.Warning($"Attaching local player to an entity {EntManager.ToPrettyString(uid)} without an eye. This eye will not be netsynced and may cause issues.");
|
||||
var eye = (EyeComponent) Factory.GetComponent(typeof(EyeComponent));
|
||||
var eye = Factory.GetComponent<EyeComponent>();
|
||||
eye.NetSyncEnabled = false;
|
||||
EntManager.AddComponent(uid.Value, eye);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,27 @@ namespace Robust.Client.UserInterface.Controls
|
||||
private StyleBox? _backgroundStyleBoxOverride;
|
||||
private StyleBox? _foregroundStyleBoxOverride;
|
||||
|
||||
private bool _vertical;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the progress bar is oriented vertically.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A vertical progress bar fills from bottom to top.
|
||||
/// </remarks>
|
||||
public bool Vertical
|
||||
{
|
||||
get => _vertical;
|
||||
set
|
||||
{
|
||||
if (_vertical != value)
|
||||
{
|
||||
_vertical = value;
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public StyleBox? BackgroundStyleBoxOverride
|
||||
{
|
||||
get => _backgroundStyleBoxOverride;
|
||||
@@ -70,11 +91,23 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
return;
|
||||
}
|
||||
var minSize = fg.MinimumSize;
|
||||
var size = PixelWidth * GetAsRatio() - minSize.X;
|
||||
if (size > 0)
|
||||
|
||||
if (_vertical)
|
||||
{
|
||||
fg.Draw(handle, UIBox2.FromDimensions(0, 0, minSize.X + size, PixelHeight), UIScale);
|
||||
var size = PixelHeight * GetAsRatio();
|
||||
if (size > 0)
|
||||
{
|
||||
fg.Draw(handle, UIBox2.FromDimensions(0, PixelHeight - size, PixelWidth, size), UIScale);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var minSize = fg.MinimumSize;
|
||||
var size = PixelWidth * GetAsRatio() - minSize.X;
|
||||
if (size > 0)
|
||||
{
|
||||
fg.Draw(handle, UIBox2.FromDimensions(0, 0, minSize.X + size, PixelHeight), UIScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -6,4 +6,3 @@
|
||||
|
||||
[assembly: InternalsVisibleTo("Robust.Client")]
|
||||
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
|
||||
[assembly: InternalsVisibleTo("Content.Benchmarks")]
|
||||
|
||||
@@ -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,9 +60,7 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
internal string GetPath(ResPath relPath)
|
||||
{
|
||||
return Path.GetFullPath(Path.Combine(_directory.FullName, relPath.ToRelativeSystemPath()))
|
||||
// Sanitise platform-specific path and standardize it for engine use.
|
||||
.Replace(Path.DirectorySeparatorChar, '/');
|
||||
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,6 +379,10 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
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.
|
||||
@@ -875,6 +879,7 @@ Types:
|
||||
- "System.Text.StringBuilder Insert(int, object)"
|
||||
- "System.Text.StringBuilder Insert(int, sbyte)"
|
||||
- "System.Text.StringBuilder Insert(int, short)"
|
||||
- "System.Text.StringBuilder Insert(int, string)"
|
||||
- "System.Text.StringBuilder Insert(int, string, int)"
|
||||
- "System.Text.StringBuilder Insert(int, System.Decimal)"
|
||||
- "System.Text.StringBuilder Insert(int, System.ReadOnlySpan`1<char>)"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,11 +273,11 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public IComponent GetComponent(Type componentType)
|
||||
{
|
||||
if (!_types.ContainsKey(componentType))
|
||||
if (!_types.TryGetValue(componentType, out var value))
|
||||
{
|
||||
throw new InvalidOperationException($"{componentType} is not a registered component.");
|
||||
}
|
||||
return _typeFactory.CreateInstanceUnchecked<IComponent>(_types[componentType].Type);
|
||||
return _typeFactory.CreateInstanceUnchecked<IComponent>(value.Type);
|
||||
}
|
||||
|
||||
public IComponent GetComponent(CompIdx componentType)
|
||||
@@ -287,11 +287,11 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public T GetComponent<T>() where T : IComponent, new()
|
||||
{
|
||||
if (!_types.ContainsKey(typeof(T)))
|
||||
if (!_types.TryGetValue(typeof(T), out var reg))
|
||||
{
|
||||
throw new InvalidOperationException($"{typeof(T)} is not a registered component.");
|
||||
}
|
||||
return _typeFactory.CreateInstanceUnchecked<T>(_types[typeof(T)].Type);
|
||||
return _typeFactory.CreateInstanceUnchecked<T>(reg.Type);
|
||||
}
|
||||
|
||||
public IComponent GetComponent(ComponentRegistration reg)
|
||||
|
||||
@@ -52,6 +52,16 @@ public partial class EntityManager
|
||||
return ents;
|
||||
}
|
||||
|
||||
public EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, params EntProtoId[] protoNames)
|
||||
{
|
||||
var ents = new EntityUid[protoNames.Length];
|
||||
for (var i = 0; i < protoNames.Length; i++)
|
||||
{
|
||||
ents[i] = SpawnAttachedTo(protoNames[i], coordinates);
|
||||
}
|
||||
return ents;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, List<string?> protoNames)
|
||||
{
|
||||
@@ -74,6 +84,14 @@ public partial class EntityManager
|
||||
return ents;
|
||||
}
|
||||
|
||||
public void SpawnEntitiesAttachedTo(EntityCoordinates coordinates, IEnumerable<EntProtoId> protoNames)
|
||||
{
|
||||
foreach (var protoName in protoNames)
|
||||
{
|
||||
SpawnAttachedTo(protoName, coordinates);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual EntityUid SpawnAttachedTo(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default)
|
||||
{
|
||||
if (!coordinates.IsValid(this))
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace Robust.Shared.GameObjects
|
||||
[Dependency] private readonly IReplayRecordingManager _replayMan = default!;
|
||||
[Dependency] protected readonly ILocalizationManager Loc = default!;
|
||||
|
||||
protected IComponentFactory Factory => EntityManager.ComponentFactory;
|
||||
|
||||
public ISawmill Log { get; private set; } = default!;
|
||||
|
||||
protected virtual string SawmillName
|
||||
|
||||
@@ -22,6 +22,8 @@ public partial interface IEntityManager
|
||||
EntityUid[] SpawnEntities(MapCoordinates coordinates, params string?[] protoNames);
|
||||
EntityUid[] SpawnEntities(MapCoordinates coordinates, string? prototype, int count);
|
||||
EntityUid[] SpawnEntities(MapCoordinates coordinates, List<string?> protoNames);
|
||||
void SpawnEntitiesAttachedTo(EntityCoordinates coordinates, IEnumerable<EntProtoId> protoNames);
|
||||
EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, params EntProtoId[] protoNames);
|
||||
EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, List<string?> protoNames);
|
||||
EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, params string?[] protoNames);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Physics.Events;
|
||||
|
||||
@@ -20,9 +20,16 @@ public readonly struct StartCollideEvent
|
||||
|
||||
public readonly Fixture OurFixture;
|
||||
public readonly Fixture OtherFixture;
|
||||
public readonly Vector2 WorldPoint;
|
||||
|
||||
public StartCollideEvent(
|
||||
internal readonly FixedArray2<Vector2> _worldPoints;
|
||||
|
||||
public readonly int PointCount;
|
||||
public readonly Vector2 WorldNormal;
|
||||
|
||||
public Vector2[] WorldPoints => _worldPoints.AsSpan[..PointCount].ToArray();
|
||||
|
||||
|
||||
internal StartCollideEvent(
|
||||
EntityUid ourEntity,
|
||||
EntityUid otherEntity,
|
||||
string ourFixtureId,
|
||||
@@ -31,7 +38,9 @@ public readonly struct StartCollideEvent
|
||||
Fixture otherFixture,
|
||||
PhysicsComponent ourBody,
|
||||
PhysicsComponent otherBody,
|
||||
Vector2 worldPoint)
|
||||
FixedArray2<Vector2> worldPoints,
|
||||
int pointCount,
|
||||
Vector2 worldNormal)
|
||||
{
|
||||
OurEntity = ourEntity;
|
||||
OtherEntity = otherEntity;
|
||||
@@ -39,8 +48,10 @@ public readonly struct StartCollideEvent
|
||||
OtherFixtureId = otherFixtureId;
|
||||
OurFixture = ourFixture;
|
||||
OtherFixture = otherFixture;
|
||||
WorldPoint = worldPoint;
|
||||
OtherBody = otherBody;
|
||||
OurBody = ourBody;
|
||||
_worldPoints = worldPoints;
|
||||
PointCount = pointCount;
|
||||
WorldNormal = worldNormal;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,7 +551,7 @@ public abstract partial class SharedPhysicsSystem
|
||||
}
|
||||
|
||||
var status = ArrayPool<ContactStatus>.Shared.Rent(index);
|
||||
var worldPoints = ArrayPool<Vector2>.Shared.Rent(index);
|
||||
var worldPoints = ArrayPool<FixedArray4<Vector2>>.Shared.Rent(index);
|
||||
|
||||
// Update contacts all at once.
|
||||
BuildManifolds(contacts, index, status, worldPoints);
|
||||
@@ -586,9 +586,11 @@ public abstract partial class SharedPhysicsSystem
|
||||
var uidA = contact.EntityA;
|
||||
var uidB = contact.EntityB;
|
||||
var worldPoint = worldPoints[i];
|
||||
var points = new FixedArray2<Vector2>(worldPoint._00, worldPoint._01);
|
||||
var worldNormal = worldPoint._02;
|
||||
|
||||
var ev1 = new StartCollideEvent(uidA, uidB, contact.FixtureAId, contact.FixtureBId, fixtureA, fixtureB, bodyA, bodyB, worldPoint);
|
||||
var ev2 = new StartCollideEvent(uidB, uidA, contact.FixtureBId, contact.FixtureAId, fixtureB, fixtureA, bodyB, bodyA, worldPoint);
|
||||
var ev1 = new StartCollideEvent(uidA, uidB, contact.FixtureAId, contact.FixtureBId, fixtureA, fixtureB, bodyA, bodyB, points, contact.Manifold.PointCount, worldNormal);
|
||||
var ev2 = new StartCollideEvent(uidB, uidA, contact.FixtureBId, contact.FixtureAId, fixtureB, fixtureA, bodyB, bodyA, points, contact.Manifold.PointCount, worldNormal);
|
||||
|
||||
RaiseLocalEvent(uidA, ref ev1, true);
|
||||
RaiseLocalEvent(uidB, ref ev2, true);
|
||||
@@ -626,10 +628,10 @@ public abstract partial class SharedPhysicsSystem
|
||||
|
||||
ArrayPool<Contact>.Shared.Return(contacts);
|
||||
ArrayPool<ContactStatus>.Shared.Return(status);
|
||||
ArrayPool<Vector2>.Shared.Return(worldPoints);
|
||||
ArrayPool<FixedArray4<Vector2>>.Shared.Return(worldPoints);
|
||||
}
|
||||
|
||||
private void BuildManifolds(Contact[] contacts, int count, ContactStatus[] status, Vector2[] worldPoints)
|
||||
private void BuildManifolds(Contact[] contacts, int count, ContactStatus[] status, FixedArray4<Vector2>[] worldPoints)
|
||||
{
|
||||
if (count == 0)
|
||||
return;
|
||||
@@ -672,7 +674,7 @@ public abstract partial class SharedPhysicsSystem
|
||||
|
||||
public Contact[] Contacts;
|
||||
public ContactStatus[] Status;
|
||||
public Vector2[] WorldPoints;
|
||||
public FixedArray4<Vector2>[] WorldPoints;
|
||||
public bool[] Wake;
|
||||
|
||||
public void Execute(int index)
|
||||
@@ -681,7 +683,7 @@ public abstract partial class SharedPhysicsSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateContact(Contact[] contacts, int index, ContactStatus[] status, bool[] wake, Vector2[] worldPoints)
|
||||
private void UpdateContact(Contact[] contacts, int index, ContactStatus[] status, bool[] wake, FixedArray4<Vector2>[] worldPoints)
|
||||
{
|
||||
var contact = contacts[index];
|
||||
|
||||
@@ -706,7 +708,11 @@ public abstract partial class SharedPhysicsSystem
|
||||
|
||||
if (contactStatus == ContactStatus.StartTouching)
|
||||
{
|
||||
worldPoints[index] = Physics.Transform.Mul(bodyATransform, contacts[index].Manifold.LocalPoint);
|
||||
var points = new FixedArray4<Vector2>();
|
||||
contact.GetWorldManifold(bodyATransform, bodyBTransform, out var worldNormal, points.AsSpan);
|
||||
// Use the 3rd Vector2 as the world normal, 4th is blank.
|
||||
points._02 = worldNormal;
|
||||
worldPoints[index] = points;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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")]
|
||||
|
||||
100
Robust.Shared/Serialization/NetBitArraySerializer.cs
Normal file
100
Robust.Shared/Serialization/NetBitArraySerializer.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.Serialization;
|
||||
using JetBrains.Annotations;
|
||||
using NetSerializer;
|
||||
|
||||
namespace Robust.Shared.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Custom serializer implementation for <see cref="BitArray"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This type is necessary as, since .NET 10, the internal layout of <see cref="BitArray"/> was changed.
|
||||
/// The type now (internally) implements <see cref="ISerializable"/> for backwards compatibility with existing
|
||||
/// <c>BinaryFormatter</c> code, but NetSerializer does not support <see cref="ISerializable"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This code is designed to be backportable & network compatible with the previous behavior on .NET 9.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
internal sealed class NetBitArraySerializer : IDynamicTypeSerializer
|
||||
{
|
||||
// NOTE: MUST be a IDynamicTypeSerializer for compatibility!
|
||||
// Can be changed in the future.
|
||||
|
||||
// For reference, the layout of BitArray before .NET 10 was:
|
||||
// private int[] m_array;
|
||||
// private int m_length;
|
||||
// private int _version;
|
||||
// NetSerializer serialized these in the following order (sorted by name):
|
||||
// _version, m_array, m_length
|
||||
|
||||
public bool Handles(Type type)
|
||||
{
|
||||
return type == typeof(BitArray);
|
||||
}
|
||||
|
||||
public IEnumerable<Type> GetSubtypes(Type type)
|
||||
{
|
||||
return [typeof(int[]), typeof(int)];
|
||||
}
|
||||
|
||||
public void GenerateWriterMethod(Serializer serializer, Type type, ILGenerator il)
|
||||
{
|
||||
var method = typeof(NetBitArraySerializer).GetMethod("Write", BindingFlags.Static | BindingFlags.NonPublic)!;
|
||||
|
||||
// arg0: Serializer, arg1: Stream, arg2: value
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
il.Emit(OpCodes.Ldarg_2);
|
||||
|
||||
il.EmitCall(OpCodes.Call, method, null);
|
||||
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
public void GenerateReaderMethod(Serializer serializer, Type type, ILGenerator il)
|
||||
{
|
||||
var method = typeof(NetBitArraySerializer).GetMethod("Read", BindingFlags.Static | BindingFlags.NonPublic)!;
|
||||
|
||||
// arg0: Serializer, arg1: stream, arg2: out value
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
il.Emit(OpCodes.Ldarg_2);
|
||||
|
||||
il.EmitCall(OpCodes.Call, method, null);
|
||||
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
private static void Write(Serializer serializer, Stream stream, BitArray value)
|
||||
{
|
||||
var intCount = (31 + value.Length) >> 5;
|
||||
var ints = new int[intCount];
|
||||
value.CopyTo(ints, 0);
|
||||
|
||||
serializer.SerializeDirect(stream, 0); // _version
|
||||
serializer.SerializeDirect(stream, ints); // m_array
|
||||
serializer.SerializeDirect(stream, value.Length); // m_length
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
private static void Read(Serializer serializer, Stream stream, out BitArray value)
|
||||
{
|
||||
serializer.DeserializeDirect<int>(stream, out _); // _version
|
||||
serializer.DeserializeDirect<int[]>(stream, out var array); // m_array
|
||||
serializer.DeserializeDirect<int>(stream, out var length); // m_length
|
||||
|
||||
value = new BitArray(array)
|
||||
{
|
||||
Length = length
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -91,6 +91,7 @@ namespace Robust.Shared.Serialization
|
||||
MappedStringSerializer.TypeSerializer,
|
||||
new Vector2Serializer(),
|
||||
new Matrix3x2Serializer(),
|
||||
new NetBitArraySerializer()
|
||||
}
|
||||
};
|
||||
_serializer = new Serializer(types, settings);
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user