mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c555dd294 | ||
|
|
03f5efc4ec | ||
|
|
7e19a77995 | ||
|
|
0b219299c6 | ||
|
|
93c887eec5 | ||
|
|
45a2895e4b | ||
|
|
7d6def6adf | ||
|
|
f733a9efa5 | ||
|
|
8a68dce987 | ||
|
|
b8d840437e | ||
|
|
2b8057acf0 | ||
|
|
bec3caa5da | ||
|
|
ea6126563b | ||
|
|
00494ad9eb | ||
|
|
6672b7b1bd | ||
|
|
8dc55e8748 | ||
|
|
44ea2cd396 | ||
|
|
2c5604432b | ||
|
|
c696466522 | ||
|
|
01bb98e400 | ||
|
|
af08e747de | ||
|
|
8c35c2c380 | ||
|
|
6d46d3f4a5 | ||
|
|
50e06e43fa | ||
|
|
986b0f979d |
@@ -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,60 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 261.2.5
|
||||
|
||||
|
||||
## 261.2.4
|
||||
|
||||
|
||||
## 261.2.3
|
||||
|
||||
|
||||
## 261.2.2
|
||||
|
||||
|
||||
## 261.2.1
|
||||
|
||||
|
||||
## 261.2.0
|
||||
|
||||
### New features
|
||||
|
||||
* Implement IEquatable for ResolvedPathSpecifier & ResolvedCollectionSpecifier.
|
||||
* Add NearestChunkEnumerator.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix static entities not having the center of mass updated.
|
||||
* Fix TryQueueDelete.
|
||||
* Fix tpto potentially parenting grids to non-map entities.
|
||||
|
||||
### Other
|
||||
|
||||
* TileChangedEvent is now raised once in clientside grid state handling rather than per tile.
|
||||
* Removed ITileDefinition.ID as it was redundant.
|
||||
* Change the lifestage checks on predicted entity deletion to check for terminating.
|
||||
|
||||
### Internal
|
||||
|
||||
* Update some `GetComponentName<T>` uses to generic.
|
||||
|
||||
|
||||
## 261.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Automatically create logger sawmills for `UIController`s similar to `EntitySystem`s.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix physics forces not auto-clearing / respecting the cvar.
|
||||
|
||||
### Internal
|
||||
|
||||
* Cleanup more compiler warnings in unit tests.
|
||||
|
||||
|
||||
## 261.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)
|
||||
|
||||
@@ -296,7 +296,7 @@ namespace Robust.Client.GameObjects
|
||||
public override void PredictedDeleteEntity(Entity<MetaDataComponent?, TransformComponent?> ent)
|
||||
{
|
||||
if (!MetaQuery.Resolve(ent.Owner, ref ent.Comp1)
|
||||
|| ent.Comp1.EntityDeleted
|
||||
|| ent.Comp1.EntityLifeStage >= EntityLifeStage.Terminating
|
||||
|| !TransformQuery.Resolve(ent.Owner, ref ent.Comp2))
|
||||
{
|
||||
return;
|
||||
@@ -322,7 +322,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
if (IsQueuedForDeletion(ent.Owner)
|
||||
|| !MetaQuery.Resolve(ent.Owner, ref ent.Comp1)
|
||||
|| ent.Comp1.EntityDeleted
|
||||
|| ent.Comp1.EntityLifeStage >= EntityLifeStage.Terminating
|
||||
|| !TransformQuery.Resolve(ent.Owner, ref ent.Comp2))
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controllers;
|
||||
@@ -11,11 +14,14 @@ namespace Robust.Client.UserInterface.Controllers;
|
||||
/// and <see cref="UISystemDependencyAttribute"/> to depend on <see cref="EntitySystem"/>s, which will be automatically
|
||||
/// injected once they are created.
|
||||
/// </summary>
|
||||
public abstract partial class UIController
|
||||
public abstract partial class UIController : IPostInjectInit
|
||||
{
|
||||
[Dependency] protected readonly IUserInterfaceManager UIManager = default!;
|
||||
[Dependency] protected readonly IEntitySystemManager EntitySystemManager = default!;
|
||||
[Dependency] protected readonly IEntityManager EntityManager = default!;
|
||||
[Dependency] protected readonly ILogManager LogManager = default!;
|
||||
|
||||
public ISawmill Log { get; protected set; } = default!;
|
||||
|
||||
public virtual void Initialize()
|
||||
{
|
||||
@@ -24,4 +30,39 @@ public abstract partial class UIController
|
||||
public virtual void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual string SawmillName
|
||||
{
|
||||
get
|
||||
{
|
||||
var name = GetType().Name;
|
||||
|
||||
// Strip trailing "UIController"
|
||||
if (name.EndsWith("UIController"))
|
||||
name = name.Substring(0, name.Length - "UIController".Length);
|
||||
|
||||
// Convert CamelCase to snake_case
|
||||
// Ignore if all uppercase, assume acronym (e.g. NPC or HTN)
|
||||
if (name.All(char.IsUpper))
|
||||
{
|
||||
name = name.ToLower(CultureInfo.InvariantCulture);
|
||||
}
|
||||
else
|
||||
{
|
||||
name = string.Concat(name.Select(x => char.IsUpper(x) ? $"_{char.ToLower(x)}" : x.ToString()));
|
||||
name = name.Trim('_');
|
||||
}
|
||||
|
||||
return $"ui.{name}";
|
||||
}
|
||||
}
|
||||
|
||||
public void PostInject()
|
||||
{
|
||||
Log = LogManager.GetSawmill(SawmillName);
|
||||
|
||||
#if !DEBUG
|
||||
Log.Level = LogLevel.Info;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace Robust.Server.Placement
|
||||
{
|
||||
// Replace existing entities if relevant.
|
||||
if (msg.Replacement && _prototype.Index<EntityPrototype>(entityTemplateName).Components.TryGetValue(
|
||||
_factory.GetComponentName(typeof(PlacementReplacementComponent)), out var compRegistry))
|
||||
_factory.GetComponentName<PlacementReplacementComponent>(), out var compRegistry))
|
||||
{
|
||||
var key = ((PlacementReplacementComponent)compRegistry.Component).Key;
|
||||
var gridUid = _xformSystem.GetGrid(coordinates);
|
||||
|
||||
@@ -6,4 +6,3 @@
|
||||
|
||||
[assembly: InternalsVisibleTo("Robust.Client")]
|
||||
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
|
||||
[assembly: InternalsVisibleTo("Content.Benchmarks")]
|
||||
|
||||
@@ -13,7 +13,8 @@ namespace Robust.Shared.Audio;
|
||||
/// <seealso cref="ResolvedPathSpecifier"/>
|
||||
/// <seealso cref="ResolvedCollectionSpecifier"/>
|
||||
[Serializable, NetSerializable]
|
||||
public abstract partial class ResolvedSoundSpecifier {
|
||||
public abstract partial class ResolvedSoundSpecifier
|
||||
{
|
||||
[Obsolete("String literals for sounds are deprecated, use a SoundSpecifier or ResolvedSoundSpecifier as appropriate instead")]
|
||||
public static implicit operator ResolvedSoundSpecifier(string s) => new ResolvedPathSpecifier(s);
|
||||
[Obsolete("String literals for sounds are deprecated, use a SoundSpecifier or ResolvedSoundSpecifier as appropriate instead")]
|
||||
@@ -22,8 +23,10 @@ public abstract partial class ResolvedSoundSpecifier {
|
||||
/// <summary>
|
||||
/// Returns whether <c>s</c> is null, or if it contains an empty path/collection ID.
|
||||
/// </summary>
|
||||
public static bool IsNullOrEmpty(ResolvedSoundSpecifier? s) {
|
||||
return s switch {
|
||||
public static bool IsNullOrEmpty(ResolvedSoundSpecifier? s)
|
||||
{
|
||||
return s switch
|
||||
{
|
||||
null => true,
|
||||
ResolvedPathSpecifier path => path.Path.ToString() == "",
|
||||
ResolvedCollectionSpecifier collection => string.IsNullOrEmpty(collection.Collection),
|
||||
@@ -37,7 +40,8 @@ public abstract partial class ResolvedSoundSpecifier {
|
||||
/// </summary>
|
||||
/// <seealso cref="ResolvedCollectionSpecifier"/>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class ResolvedPathSpecifier : ResolvedSoundSpecifier {
|
||||
public sealed partial class ResolvedPathSpecifier : ResolvedSoundSpecifier, IEquatable<ResolvedPathSpecifier>
|
||||
{
|
||||
/// <summary>
|
||||
/// The resource path of the sound.
|
||||
/// </summary>
|
||||
@@ -57,6 +61,21 @@ public sealed partial class ResolvedPathSpecifier : ResolvedSoundSpecifier {
|
||||
public ResolvedPathSpecifier(string path) : this(new ResPath(path))
|
||||
{
|
||||
}
|
||||
|
||||
public bool Equals(ResolvedPathSpecifier? other)
|
||||
{
|
||||
return Path.Equals(other?.Path);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return Equals(obj as ResolvedPathSpecifier);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Path.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -64,7 +83,9 @@ public sealed partial class ResolvedPathSpecifier : ResolvedSoundSpecifier {
|
||||
/// </summary>
|
||||
/// <seealso cref="ResolvedPathSpecifier"/>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class ResolvedCollectionSpecifier : ResolvedSoundSpecifier {
|
||||
public sealed partial class ResolvedCollectionSpecifier : ResolvedSoundSpecifier, IEquatable<ResolvedCollectionSpecifier>
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the <see cref="SoundCollectionPrototype">sound collection</see> to look up.
|
||||
/// </summary>
|
||||
@@ -87,4 +108,19 @@ public sealed partial class ResolvedCollectionSpecifier : ResolvedSoundSpecifier
|
||||
Collection = collection;
|
||||
Index = index;
|
||||
}
|
||||
|
||||
public bool Equals(ResolvedCollectionSpecifier? other)
|
||||
{
|
||||
return Collection.Equals(other?.Collection) && Index.Equals(other?.Index);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return Equals(obj as ResolvedCollectionSpecifier);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Collection, Index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,9 +137,10 @@ public sealed class TeleportToCommand : LocalizedEntityCommands
|
||||
}
|
||||
}
|
||||
|
||||
var targetMapCoords = _transform.ToMapCoordinates(targetCoords);
|
||||
foreach (var victim in victims)
|
||||
{
|
||||
_transform.SetCoordinates(victim.Entity, targetCoords);
|
||||
_transform.SetMapCoordinates(victim.Entity, targetMapCoords);
|
||||
_transform.AttachToGridOrMap(victim.Entity, victim.Transform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,8 +162,8 @@ public sealed class EntitySerializer : ISerializationContext,
|
||||
_log = _logMan.GetSawmill("entity_serializer");
|
||||
SerializerProvider.RegisterSerializer(this);
|
||||
|
||||
_metaName = _factory.GetComponentName(typeof(MetaDataComponent));
|
||||
_xformName = _factory.GetComponentName(typeof(TransformComponent));
|
||||
_metaName = _factory.GetComponentName<MetaDataComponent>();
|
||||
_xformName = _factory.GetComponentName<TransformComponent>();
|
||||
_emptyMetaNode = _serialization.WriteValueAs<MappingDataNode>(typeof(MetaDataComponent), new MetaDataComponent(), alwaysWrite: true, context: this);
|
||||
|
||||
CurrentComponent = _xformName;
|
||||
|
||||
@@ -498,7 +498,7 @@ namespace Robust.Shared.GameObjects
|
||||
if (Deleted(uid.Value))
|
||||
return false;
|
||||
|
||||
if (!QueuedDeletionsSet.Add(uid.Value))
|
||||
if (QueuedDeletionsSet.Contains(uid.Value))
|
||||
return false;
|
||||
|
||||
QueueDeleteEntity(uid);
|
||||
|
||||
@@ -213,7 +213,7 @@ public abstract partial class SharedMapSystem
|
||||
|
||||
if (xform.ParentUid != xform.MapUid && meta.EntityLifeStage < EntityLifeStage.Terminating && _netManager.IsServer)
|
||||
{
|
||||
Log.Error($"Grid {ToPrettyString(uid, meta)} is not parented to {ToPrettyString(xform._parent)} which is not a map. y'all need jesus. {Environment.StackTrace}");
|
||||
Log.Error($"Grid {ToPrettyString(uid, meta)} is parented to {ToPrettyString(xform._parent)} which is not a map. y'all need jesus. {Environment.StackTrace}");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -338,6 +338,8 @@ public abstract partial class SharedMapSystem
|
||||
chunk.SuppressCollisionRegeneration = true;
|
||||
DebugTools.Assert(data.TileData.Any(x => !x.IsEmpty));
|
||||
DebugTools.Assert(data.TileData.Length == component.ChunkSize * component.ChunkSize);
|
||||
var changedEntry = new ValueList<TileChangedEntry>();
|
||||
|
||||
for (ushort x = 0; x < component.ChunkSize; x++)
|
||||
{
|
||||
for (ushort y = 0; y < component.ChunkSize; y++)
|
||||
@@ -346,12 +348,15 @@ public abstract partial class SharedMapSystem
|
||||
if (!chunk.TrySetTile(x, y, tile, out var oldTile, out _))
|
||||
continue;
|
||||
|
||||
var gridIndices = chunk.ChunkTileToGridTile((x, y));
|
||||
var newTileRef = new TileRef(uid, gridIndices, tile);
|
||||
_mapInternal.RaiseOnTileChanged(gridEnt, newTileRef, oldTile, index);
|
||||
|
||||
var chunkIndex = new Vector2i(x, y);
|
||||
var gridIndices = chunk.ChunkTileToGridTile(chunkIndex);
|
||||
changedEntry.Add(new TileChangedEntry(tile, oldTile, chunk.Indices, gridIndices));
|
||||
}
|
||||
}
|
||||
|
||||
var ev = new TileChangedEvent(gridEnt, changedEntry.ToArray());
|
||||
EntityManager.EventBus.RaiseLocalEvent(gridEnt.Owner, ref ev, true);
|
||||
DebugTools.Assert(chunk.Fixtures.SetEquals(data.Fixtures));
|
||||
|
||||
// These should never refer to the same object
|
||||
|
||||
64
Robust.Shared/Map/Enumerators/NearestChunkEnumerator.cs
Normal file
64
Robust.Shared/Map/Enumerators/NearestChunkEnumerator.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Shared.Map.Enumerators;
|
||||
|
||||
/// <summary>
|
||||
/// Iterates chunk indices but prefers ones closer towards the center first.
|
||||
/// </summary>
|
||||
public record struct NearestChunkEnumerator
|
||||
{
|
||||
private readonly Vector2i _chunkLB;
|
||||
private readonly Vector2i _chunkRT;
|
||||
|
||||
private ValueList<Vector2i> _chunks = new();
|
||||
|
||||
private int _n;
|
||||
|
||||
public NearestChunkEnumerator(Box2 localAABB, int chunkSize)
|
||||
{
|
||||
_chunkLB = (localAABB.BottomLeft / chunkSize).Floored();
|
||||
_chunkRT = (localAABB.TopRight / chunkSize).Floored();
|
||||
InitializeChunks(new Vector2i(chunkSize, chunkSize));
|
||||
}
|
||||
|
||||
public NearestChunkEnumerator(Box2 localAABB, Vector2i chunkSize)
|
||||
{
|
||||
_chunkLB = (localAABB.BottomLeft / chunkSize).Floored();
|
||||
_chunkRT = (localAABB.TopRight / chunkSize).Floored();
|
||||
InitializeChunks(chunkSize);
|
||||
}
|
||||
|
||||
private void InitializeChunks(Vector2i chunkSize)
|
||||
{
|
||||
var bl = (Vector2)_chunkLB * chunkSize;
|
||||
var tr = (Vector2)_chunkRT * chunkSize;
|
||||
var halfChunk = new Vector2(chunkSize.X / 2f, chunkSize.Y / 2f);
|
||||
|
||||
var center = (tr - bl) / 2 + bl;
|
||||
|
||||
for (var x = _chunkLB.X; x < _chunkRT.X; x++)
|
||||
{
|
||||
for (var y = _chunkLB.Y; y < _chunkRT.Y; y++)
|
||||
{
|
||||
_chunks.Add(new Vector2i(x, y) * chunkSize);
|
||||
}
|
||||
}
|
||||
|
||||
_chunks.Sort((c1, c2) => ((c1 + halfChunk) - center).LengthSquared().CompareTo(((c2 + halfChunk) - center).LengthSquared()));
|
||||
}
|
||||
|
||||
public bool MoveNext([NotNullWhen(true)] out Vector2i? indices)
|
||||
{
|
||||
if (_n >= _chunks.Count)
|
||||
{
|
||||
indices = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
indices = _chunks[_n++] ;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -20,11 +20,6 @@ namespace Robust.Shared.Map
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal name of the definition.
|
||||
/// </summary>
|
||||
string ID { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of the sprite to draw.
|
||||
/// </summary>
|
||||
|
||||
@@ -312,13 +312,6 @@ public partial class SharedPhysicsSystem
|
||||
body._inertia += data.I;
|
||||
}
|
||||
|
||||
// Update this after re-calculating mass as content may want to use the sum of fixture masses instead.
|
||||
if (((int) body.BodyType & (int) (BodyType.Kinematic | BodyType.Static)) != 0)
|
||||
{
|
||||
body._localCenter = Vector2.Zero;
|
||||
return;
|
||||
}
|
||||
|
||||
if (body._mass > 0.0f)
|
||||
{
|
||||
body._invMass = 1.0f / body._mass;
|
||||
@@ -348,18 +341,23 @@ public partial class SharedPhysicsSystem
|
||||
var oldCenter = body._localCenter;
|
||||
body._localCenter = localCenter;
|
||||
|
||||
// Update center of mass velocity.
|
||||
var comVelocityDiff = Vector2Helpers.Cross(body.AngularVelocity, localCenter - oldCenter);
|
||||
if (((int) body.BodyType & (int) (BodyType.Kinematic | BodyType.Static)) == 0)
|
||||
{
|
||||
// Update center of mass velocity.
|
||||
var comVelocityDiff = Vector2Helpers.Cross(body.AngularVelocity, localCenter - oldCenter);
|
||||
|
||||
if (comVelocityDiff != Vector2.Zero)
|
||||
{
|
||||
body.LinearVelocity += comVelocityDiff;
|
||||
DirtyField(uid, body, nameof(PhysicsComponent.LinearVelocity));
|
||||
}
|
||||
if (comVelocityDiff != Vector2.Zero)
|
||||
{
|
||||
body.LinearVelocity += comVelocityDiff;
|
||||
DirtyField(uid, body, nameof(PhysicsComponent.LinearVelocity));
|
||||
}
|
||||
}
|
||||
|
||||
if (body._mass == oldMass && body._inertia == oldInertia && oldCenter == localCenter)
|
||||
return;
|
||||
|
||||
// we always do the full mass and COM calculation and raise this, even for static bodies as content may need the info
|
||||
// examples are stations anchored with the station anchor, shuttles landed on planets, or grids getting an atmosphere above a certain mass threshold
|
||||
var ev = new MassDataChangedEvent((uid, body, manager), oldMass, oldInertia, oldCenter);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
}
|
||||
@@ -512,7 +510,6 @@ public partial class SharedPhysicsSystem
|
||||
|
||||
var oldType = body.BodyType;
|
||||
body.BodyType = value;
|
||||
ResetMassData(uid, manager, body);
|
||||
|
||||
body.Force = Vector2.Zero;
|
||||
body.Torque = 0f;
|
||||
|
||||
@@ -277,7 +277,7 @@ public abstract partial class SharedPhysicsSystem
|
||||
// TODO: SolveTOI
|
||||
|
||||
// Box2d recommends clearing (if you are) during fixed updates rather than variable if you are using it
|
||||
if (!prediction && _autoClearForces)
|
||||
if (_autoClearForces)
|
||||
ClearForces();
|
||||
|
||||
_invDt0 = invDt;
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
InitializeIsland();
|
||||
InitializeContacts();
|
||||
|
||||
Subs.CVar(_cfg, CVars.AutoClearForces, OnAutoClearChange);
|
||||
Subs.CVar(_cfg, CVars.AutoClearForces, OnAutoClearChange, true);
|
||||
Subs.CVar(_cfg, CVars.NetTickrate, UpdateSubsteps, true);
|
||||
Subs.CVar(_cfg, CVars.TargetMinimumTickrate, UpdateSubsteps, true);
|
||||
}
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -170,9 +170,9 @@ namespace Robust.Shared.Prototypes
|
||||
|
||||
[Obsolete("Pass in IComponentFactory")]
|
||||
public bool TryGetComponent<T>([NotNullWhen(true)] out T? component)
|
||||
where T : IComponent
|
||||
where T : IComponent, new()
|
||||
{
|
||||
var compName = IoCManager.Resolve<IComponentFactory>().GetComponentName(typeof(T));
|
||||
var compName = IoCManager.Resolve<IComponentFactory>().GetComponentName<T>();
|
||||
return TryGetComponent(compName, out component);
|
||||
}
|
||||
|
||||
@@ -182,9 +182,9 @@ namespace Robust.Shared.Prototypes
|
||||
return TryGetComponent(compName, out component);
|
||||
}
|
||||
|
||||
public bool TryGetComponent<T>(string name, [NotNullWhen(true)] out T? component) where T : IComponent
|
||||
public bool TryGetComponent<T>(string name, [NotNullWhen(true)] out T? component) where T : IComponent, new()
|
||||
{
|
||||
DebugTools.AssertEqual(IoCManager.Resolve<IComponentFactory>().GetComponentName(typeof(T)), name);
|
||||
DebugTools.AssertEqual(IoCManager.Resolve<IComponentFactory>().GetComponentName<T>(), name);
|
||||
|
||||
if (!Components.TryGetValue(name, out var componentUnCast))
|
||||
{
|
||||
|
||||
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);
|
||||
|
||||
@@ -33,8 +33,8 @@ public sealed partial class DeferredEntityDeletionTest : RobustIntegrationTest
|
||||
var server = StartServer(options);
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
EntityUid uid1 = default, uid2 = default, uid3 = default;
|
||||
DeferredDeletionTestComponent comp1 = default!, comp2 = default!, comp3 = default!;
|
||||
EntityUid uid1 = default, uid2 = default, uid3 = default, uid4 = default;
|
||||
DeferredDeletionTestComponent comp1 = default!, comp2 = default!, comp3 = default!, comp4 = default!;
|
||||
IEntityManager entMan = default!;
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
@@ -46,14 +46,17 @@ public sealed partial class DeferredEntityDeletionTest : RobustIntegrationTest
|
||||
uid1 = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
uid2 = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
uid3 = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
uid4 = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
|
||||
comp1 = entMan.AddComponent<DeferredDeletionTestComponent>(uid1);
|
||||
comp2 = entMan.AddComponent<DeferredDeletionTestComponent>(uid2);
|
||||
comp3 =entMan.AddComponent<DeferredDeletionTestComponent>(uid3);
|
||||
comp3 = entMan.AddComponent<DeferredDeletionTestComponent>(uid3);
|
||||
comp4 = entMan.AddComponent<DeferredDeletionTestComponent>(uid4);
|
||||
|
||||
entMan.AddComponent<OtherDeferredDeletionTestComponent>(uid1);
|
||||
entMan.AddComponent<OtherDeferredDeletionTestComponent>(uid2);
|
||||
entMan.AddComponent<OtherDeferredDeletionTestComponent>(uid3);
|
||||
entMan.AddComponent<OtherDeferredDeletionTestComponent>(uid4);
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(1);
|
||||
@@ -76,17 +79,23 @@ public sealed partial class DeferredEntityDeletionTest : RobustIntegrationTest
|
||||
var ev = new DeferredDeletionTestEvent();
|
||||
entMan.EventBus.RaiseLocalEvent(uid2, ev);
|
||||
entMan.EventBus.RaiseLocalEvent(uid3, ev);
|
||||
entMan.EventBus.RaiseLocalEvent(uid4, ev);
|
||||
entMan.DeleteEntity(uid2);
|
||||
entMan.QueueDeleteEntity(uid3);
|
||||
entMan.TryQueueDeleteEntity(uid4);
|
||||
Assert.That(entMan.Deleted(uid2));
|
||||
Assert.That(!entMan.Deleted(uid3));
|
||||
Assert.That(!entMan.Deleted(uid4));
|
||||
Assert.That(comp2.LifeStage == ComponentLifeStage.Deleted);
|
||||
Assert.That(comp3.LifeStage == ComponentLifeStage.Stopped);
|
||||
Assert.That(comp4.LifeStage == ComponentLifeStage.Stopped);
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(1);
|
||||
Assert.That(comp3.LifeStage == ComponentLifeStage.Deleted);
|
||||
Assert.That(comp4.LifeStage == ComponentLifeStage.Deleted);
|
||||
Assert.That(entMan.Deleted(uid3));
|
||||
Assert.That(entMan.Deleted(uid4));
|
||||
await server.WaitIdleAsync();
|
||||
}
|
||||
|
||||
|
||||
@@ -133,7 +133,9 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
|
||||
var entManMock = new Mock<IEntityManager>();
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
compInstance.Owner = entUid;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
var compRegistration = new ComponentRegistration(
|
||||
"MetaData",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -21,12 +22,16 @@ public sealed class MapGridMap_Tests
|
||||
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapManager = sim.Resolve<IMapManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
var mapId = sim.CreateMap().MapId;
|
||||
Assert.That(!mapManager.FindGridsIntersecting(mapId, Box2.UnitCentered).Any());
|
||||
List<Entity<MapGridComponent>> grids = [];
|
||||
mapManager.FindGridsIntersecting(mapId, Box2.UnitCentered, ref grids);
|
||||
Assert.That(grids, Is.Empty);
|
||||
|
||||
entManager.AddComponent<MapGridComponent>(mapManager.GetMapEntityId(mapId));
|
||||
Assert.That(mapManager.FindGridsIntersecting(mapId, Box2.UnitCentered).Count() == 1);
|
||||
entManager.AddComponent<MapGridComponent>(mapSystem.GetMapOrInvalid(mapId));
|
||||
mapManager.FindGridsIntersecting(mapId, Box2.UnitCentered, ref grids);
|
||||
Assert.That(grids, Has.Count.EqualTo(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -46,7 +51,7 @@ public sealed class MapGridMap_Tests
|
||||
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
entManager.AddComponent<MapGridComponent>(mapManager.GetMapEntityId(mapId));
|
||||
entManager.AddComponent<MapGridComponent>(mapSystem.GetMapOrInvalid(mapId));
|
||||
entManager.TickUpdate(0.016f, false);
|
||||
});
|
||||
|
||||
|
||||
@@ -27,12 +27,14 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var mapMan = sim.Resolve<IMapManager>();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var mapSys = entMan.System<SharedMapSystem>();
|
||||
|
||||
var mapID = sim.CreateMap().MapId;
|
||||
|
||||
mapMan.Restart();
|
||||
|
||||
Assert.That(mapMan.MapExists(mapID), Is.False);
|
||||
Assert.That(mapSys.MapExists(mapID), Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -62,7 +64,7 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
var sim = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var oldEntity = entMan.CreateEntityUninitialized(null, MapCoordinates.Nullspace);
|
||||
entMan.InitializeComponents(oldEntity);
|
||||
entMan.InitializeEntity(oldEntity);
|
||||
entMan.FlushEntities();
|
||||
Assert.That(entMan.Deleted(oldEntity), Is.True);
|
||||
}
|
||||
|
||||
@@ -150,19 +150,17 @@ internal sealed class MapPauseTests
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var mapMan = sim.Resolve<IMapManager>();
|
||||
|
||||
// arrange
|
||||
var map1 = sim.CreateMap().Uid;
|
||||
entMan.System<SharedMapSystem>().SetPaused(map1, true);
|
||||
var newEnt = entMan.SpawnEntity(null, new EntityCoordinates(map1, default));
|
||||
var xform = entMan.GetComponent<TransformComponent>(newEnt);
|
||||
|
||||
var map2 = sim.CreateMap().Uid;
|
||||
entMan.System<SharedMapSystem>().SetPaused(map2, false);
|
||||
|
||||
// Act
|
||||
entMan.EntitySysManager.GetEntitySystem<SharedTransformSystem>().SetParent(xform.Owner, map2);
|
||||
entMan.EntitySysManager.GetEntitySystem<SharedTransformSystem>().SetParent(newEnt, map2);
|
||||
|
||||
var metaData = entMan.GetComponent<MetaDataComponent>(newEnt);
|
||||
Assert.That(metaData.EntityPaused, Is.False);
|
||||
@@ -177,19 +175,17 @@ internal sealed class MapPauseTests
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var mapMan = sim.Resolve<IMapManager>();
|
||||
|
||||
// arrange
|
||||
var map1 = sim.CreateMap().Uid;
|
||||
entMan.System<SharedMapSystem>().SetPaused(map1, false);
|
||||
var newEnt = entMan.SpawnEntity(null, new EntityCoordinates(map1, default));
|
||||
var xform = entMan.GetComponent<TransformComponent>(newEnt);
|
||||
|
||||
var map2 = sim.CreateMap().Uid;
|
||||
entMan.System<SharedMapSystem>().SetPaused(map2, true);
|
||||
|
||||
// Act
|
||||
entMan.EntitySysManager.GetEntitySystem<SharedTransformSystem>().SetParent(xform.Owner, map2);
|
||||
entMan.EntitySysManager.GetEntitySystem<SharedTransformSystem>().SetParent(newEnt, map2);
|
||||
|
||||
var metaData = entMan.GetComponent<MetaDataComponent>(newEnt);
|
||||
Assert.That(metaData.EntityPaused, Is.True);
|
||||
|
||||
@@ -396,17 +396,6 @@ namespace Robust.UnitTesting.Shared.Maths
|
||||
Assert.That(MathHelper.CloseToPercent(color, controlColor));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToFromHcy([ValueSource(nameof(FourFloatsSource))] (float, float, float, float) floats)
|
||||
{
|
||||
var (rf, gf, bf, af) = floats;
|
||||
|
||||
var controlColor = new Color(rf, gf, bf, af);
|
||||
var color = Color.FromHcy(Color.ToHcy(controlColor));
|
||||
|
||||
Assert.That(MathHelper.CloseToPercent(color, controlColor));
|
||||
}
|
||||
|
||||
static IEnumerable<float> InterpolationValues => new float[]
|
||||
{
|
||||
0f,
|
||||
|
||||
@@ -41,7 +41,7 @@ internal sealed class NetDisconnectMessageTest
|
||||
};
|
||||
|
||||
var encoded = value.Encode();
|
||||
TestContext.Write($"Encoded: {encoded}\n");
|
||||
TestContext.Out.Write($"Encoded: {encoded}\n");
|
||||
var decodedAgain = NetDisconnectMessage.Decode(encoded);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
|
||||
@@ -122,10 +122,10 @@ public sealed class Collision_Test
|
||||
{
|
||||
var sim = RobustServerSimulation.NewSimulation().InitializeInstance();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapManager = sim.Resolve<IMapManager>();
|
||||
var fixtures = entManager.System<FixtureSystem>();
|
||||
var physics = entManager.System<SharedPhysicsSystem>();
|
||||
var xformSystem = entManager.System<SharedTransformSystem>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
var mapId = sim.CreateMap().MapId;
|
||||
var mapId2 = sim.CreateMap().MapId;
|
||||
|
||||
@@ -151,7 +151,7 @@ public sealed class Collision_Test
|
||||
Assert.That(body1.ContactCount == 1 && body2.ContactCount == 1);
|
||||
|
||||
// Reparent body2 and assert the contact is destroyed
|
||||
xformSystem.SetParent(ent2, mapManager.GetMapEntityId(mapId2));
|
||||
xformSystem.SetParent(ent2, mapSystem.GetMapOrInvalid(mapId2));
|
||||
physics.Update(0.01f);
|
||||
|
||||
Assert.That(body1.ContactCount == 0 && body2.ContactCount == 0);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
@@ -52,12 +53,11 @@ public sealed class GridDeletion_Test : RobustIntegrationTest
|
||||
Assert.That(physics.LinearVelocity.Length, NUnit.Framework.Is.GreaterThan(0f));
|
||||
entManager.DeleteEntity(grid);
|
||||
|
||||
List<Entity<MapGridComponent>> grids = [];
|
||||
// So if gridtree is fucky then this SHOULD throw.
|
||||
foreach (var _ in mapManager.FindGridsIntersecting(mapId,
|
||||
mapManager.FindGridsIntersecting(mapId,
|
||||
new Box2(new Vector2(float.MinValue, float.MinValue),
|
||||
new Vector2(float.MaxValue, float.MaxValue))))
|
||||
{
|
||||
}
|
||||
new Vector2(float.MaxValue, float.MaxValue)), ref grids);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,18 +87,6 @@ public sealed class GridReparentVelocity_Test
|
||||
return obj;
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void Teardown()
|
||||
{
|
||||
_entManager.DeleteEntity(_gridUid);
|
||||
_gridUid = default!;
|
||||
_entManager.DeleteEntity(_objUid);
|
||||
_objUid = default!;
|
||||
_mapSystem.DeleteMap(_mapId);
|
||||
_mapId = default!;
|
||||
_entManager.DeleteEntity(_mapUid);
|
||||
}
|
||||
|
||||
// Moves an object off of a moving grid, checks for conservation of linear velocity.
|
||||
[Test]
|
||||
public void TestLinearVelocityOnlyMoveOffGrid()
|
||||
|
||||
@@ -35,11 +35,12 @@ public sealed class JointDeletion_Test : RobustIntegrationTest
|
||||
EntityUid ent2 = default!;
|
||||
PhysicsComponent body1;
|
||||
PhysicsComponent body2 = default!;
|
||||
EntityUid mapEnt = default!;
|
||||
MapId mapId = default!;
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
entManager.System<SharedMapSystem>().CreateMap(out mapId);
|
||||
mapEnt = entManager.System<SharedMapSystem>().CreateMap(out mapId);
|
||||
ent1 = entManager.SpawnEntity(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
ent2 = entManager.SpawnEntity(null, new MapCoordinates(Vector2.One, mapId));
|
||||
|
||||
@@ -67,7 +68,7 @@ public sealed class JointDeletion_Test : RobustIntegrationTest
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(joint.Enabled);
|
||||
physicsSystem.SetAwake(ent2, body2, false);
|
||||
physicsSystem.SetAwake((ent2, body2), false);
|
||||
Assert.That(!body2.Awake);
|
||||
|
||||
entManager.DeleteEntity(ent2);
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace Robust.UnitTesting.Shared.Physics
|
||||
Assert.That(velocities.Item2, Is.Approximately(angularVelocity, 1e-6));
|
||||
|
||||
// but not if we update the local position:
|
||||
xformSystem.SetWorldPosition(xform2!, Vector2.Zero);
|
||||
xformSystem.SetWorldPosition((dummy2, xform2!), Vector2.Zero);
|
||||
linearVelocity = physicsSys.GetMapLinearVelocity(dummy2, body2);
|
||||
angularVelocity = physicsSys.GetMapAngularVelocity(dummy2, body2);
|
||||
velocities = physicsSys.GetMapVelocities(dummy2, body2);
|
||||
|
||||
@@ -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