mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03f8d4d3e0 | ||
|
|
728d541ca5 | ||
|
|
4cbce064b8 | ||
|
|
93bb7b1532 | ||
|
|
3ba91d2ed0 | ||
|
|
8f9e0f6bab | ||
|
|
3ed408de5d | ||
|
|
2f85408f8f | ||
|
|
f49b01b1b7 | ||
|
|
3ce764311d | ||
|
|
adf0b6ae78 | ||
|
|
04406311dc | ||
|
|
ee45a608b9 | ||
|
|
077c91a54b | ||
|
|
2ac17009ee | ||
|
|
a73d1b6666 | ||
|
|
e5d6f194be | ||
|
|
72d893dec5 | ||
|
|
191d7ab81c | ||
|
|
65d2f2dd2f | ||
|
|
02b451db2a | ||
|
|
d5d4584e11 | ||
|
|
b146b1b82c | ||
|
|
2f8f4f2f7a | ||
|
|
7365a59bd9 | ||
|
|
37560f663b | ||
|
|
bd489e9218 | ||
|
|
33166e8866 | ||
|
|
ed16032280 | ||
|
|
cf785c886b |
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Release notes for RobustToolbox.
|
||||
# Release notes for RobustToolbox.
|
||||
|
||||
<!--
|
||||
NOTE: automatically updated sometimes by version.py.
|
||||
@@ -54,6 +54,64 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 255.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `RobustIntegrationTest` now pools server/client instances by default. If a custom settings class is provided, it will still disable pooling unless explicitly enabled.
|
||||
* Server/Client instances that are returned to the pool should be disconnected. This might require you to update some tests.
|
||||
* Pooled instances also require you to use `RobustIntegrationTest` methods like `WaitPost()` to ensure the correct thread is used.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix `EntityDeserializer` improperly setting entity lifestages when loading a post-mapinit map.
|
||||
* Fix `EntityManager.PredictedDeleteEntity()` not deleting pure client-side entities.
|
||||
* Fix grid fixtures using a locale dependent id. This could cause some clients to crash/freeze when connected to a server with a different locale.
|
||||
|
||||
### Other
|
||||
|
||||
* Add logic to block cycles in master MIDI renderers, which could otherwise cause client freezes.
|
||||
|
||||
|
||||
## 254.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add CC ND licences to the RGA validator.
|
||||
* Add entity spawn prediction and entity deletion prediction. This is currently limited as you are unable to predict interactions with these entities. These are done via the new methods prefixed with "Predicted". You can also manually flag an entity as a predicted spawn with the `FlagPredicted` method which will clean it up when prediction is reset.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix tile edge rendering for neighbor tiles being the same priority.
|
||||
|
||||
### Other
|
||||
|
||||
* Fix SpawnAttachedTo's system proxy method not the rotation arg like EntityManager.
|
||||
|
||||
|
||||
## 254.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Yaml mappings/dictionaries now only support string keys instead of generic nodes
|
||||
* Several MappingDataNode method arguments or return values now use strings instead of a DataNode object
|
||||
* The MappingDataNode class has various helper methods that still accept a ValueDataNode, but these methods are marked as obsolete and may be removed in the future.
|
||||
* yaml validators should use `MappingDataNode.GetKeyNode()` when validating mapping keys, so that errors can print node start & end information
|
||||
* ValueTuple yaml serialization has changed
|
||||
* Previously they would get serialized into a single mapping with one entry (i.e., `{foo : bar }`)
|
||||
* Now they serialize into a sequence (i.e., `[foo, bar]`)
|
||||
* The ValueTuple serializer will still try to read mappings, but due to the MappingDataNode this may fail if the previously serialized "key" can't be read as a simple string
|
||||
|
||||
### New features
|
||||
|
||||
* Add cvar to disable tile edges.
|
||||
* Add GetContainingContainers method to ContainerSystem to recursively get containers upwards on an entity.
|
||||
|
||||
### Internal
|
||||
|
||||
* Make component lifecycle methods use generics.
|
||||
|
||||
|
||||
## 253.0.0
|
||||
|
||||
### New features
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
### Localization for engine console commands
|
||||
|
||||
cmd-hint-float = [float]
|
||||
|
||||
## generic command errors
|
||||
|
||||
cmd-invalid-arg-number-error = Invalid number of arguments.
|
||||
|
||||
86
Robust.Analyzers.Tests/ObsoleteInheritanceAnalyzerTest.cs
Normal file
86
Robust.Analyzers.Tests/ObsoleteInheritanceAnalyzerTest.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.CSharp.Testing;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
using NUnit.Framework;
|
||||
using VerifyCS =
|
||||
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.ObsoleteInheritanceAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Analyzer that implements <c>[ObsoleteInheritance]</c> checking, to give obsoletion warnings for inheriting types
|
||||
/// that should never have been virtual.
|
||||
/// </summary>
|
||||
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
|
||||
[TestFixture]
|
||||
public sealed class ObsoleteInheritanceAnalyzerTest
|
||||
{
|
||||
private static Task Verifier(string code, params DiagnosticResult[] expected)
|
||||
{
|
||||
var test = new CSharpAnalyzerTest<ObsoleteInheritanceAnalyzer, DefaultVerifier>()
|
||||
{
|
||||
TestState =
|
||||
{
|
||||
Sources = { code },
|
||||
},
|
||||
};
|
||||
|
||||
TestHelper.AddEmbeddedSources(
|
||||
test.TestState,
|
||||
"Robust.Shared.Analyzers.ObsoleteInheritanceAttribute.cs"
|
||||
);
|
||||
|
||||
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
|
||||
test.TestState.ExpectedDiagnostics.AddRange(expected);
|
||||
|
||||
return test.RunAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestBasic()
|
||||
{
|
||||
const string code = """
|
||||
using Robust.Shared.Analyzers;
|
||||
|
||||
[ObsoleteInheritance]
|
||||
public class Base;
|
||||
|
||||
public class NotAllowed : Base;
|
||||
""";
|
||||
|
||||
await Verifier(code,
|
||||
// /0/Test0.cs(6,14): warning RA0034: Type 'NotAllowed' inherits from 'Base', which has obsoleted inheriting from itself
|
||||
VerifyCS.Diagnostic(ObsoleteInheritanceAnalyzer.Rule).WithSpan(6, 14, 6, 24).WithArguments("NotAllowed", "Base")
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestMessage()
|
||||
{
|
||||
const string code = """
|
||||
using Robust.Shared.Analyzers;
|
||||
|
||||
[ObsoleteInheritance("Sus")]
|
||||
public class Base;
|
||||
|
||||
public class NotAllowed : Base;
|
||||
""";
|
||||
|
||||
await Verifier(code,
|
||||
// /0/Test0.cs(6,14): warning RA0034: Type 'NotAllowed' inherits from 'Base', which has obsoleted inheriting from itself: "Sus"
|
||||
VerifyCS.Diagnostic(ObsoleteInheritanceAnalyzer.RuleWithMessage).WithSpan(6, 14, 6, 24).WithArguments("NotAllowed", "Base", "Sus")
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestNormal()
|
||||
{
|
||||
const string code = """
|
||||
public class Base;
|
||||
|
||||
public class AllowedAllowed : Base;
|
||||
""";
|
||||
|
||||
await Verifier(code);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\PreferNonGenericVariantForAttribute.cs" LogicalName="Robust.Shared.Analyzers.PreferNonGenericVariantForAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\PreferOtherTypeAttribute.cs" LogicalName="Robust.Shared.Analyzers.PreferOtherTypeAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\ForbidLiteralAttribute.cs" LogicalName="Robust.Shared.Analyzers.ForbidLiteralAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\ObsoleteInheritanceAttribute.cs" LogicalName="Robust.Shared.Analyzers.ObsoleteInheritanceAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\IoC\DependencyAttribute.cs" LogicalName="Robust.Shared.IoC.DependencyAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\GameObjects\EventBusAttributes.cs" LogicalName="Robust.Shared.GameObjects.EventBusAttributes.cs" LinkBase="Implementations" />
|
||||
</ItemGroup>
|
||||
|
||||
75
Robust.Analyzers/ObsoleteInheritanceAnalyzer.cs
Normal file
75
Robust.Analyzers/ObsoleteInheritanceAnalyzer.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
#nullable enable
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class ObsoleteInheritanceAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string Attribute = "Robust.Shared.Analyzers.ObsoleteInheritanceAttribute";
|
||||
|
||||
public static readonly DiagnosticDescriptor Rule = new(
|
||||
Diagnostics.IdObsoleteInheritance,
|
||||
"Parent type has obsoleted inheritance",
|
||||
"Type '{0}' inherits from '{1}', which has obsoleted inheriting from itself",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Warning,
|
||||
true);
|
||||
|
||||
public static readonly DiagnosticDescriptor RuleWithMessage = new(
|
||||
Diagnostics.IdObsoleteInheritanceWithMessage,
|
||||
"Parent type has obsoleted inheritance",
|
||||
"Type '{0}' inherits from '{1}', which has obsoleted inheriting from itself: \"{2}\"",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Warning,
|
||||
true);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Rule, RuleWithMessage];
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
context.RegisterSymbolAction(CheckClass, SymbolKind.NamedType);
|
||||
}
|
||||
|
||||
private static void CheckClass(SymbolAnalysisContext context)
|
||||
{
|
||||
if (context.Symbol is not INamedTypeSymbol typeSymbol)
|
||||
return;
|
||||
|
||||
if (typeSymbol.IsValueType || typeSymbol.BaseType is not { } baseType)
|
||||
return;
|
||||
|
||||
if (!AttributeHelper.HasAttribute(baseType, Attribute, out var data))
|
||||
return;
|
||||
|
||||
var location = context.Symbol.Locations[0];
|
||||
|
||||
if (GetMessageFromAttributeData(data) is { } message)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
RuleWithMessage,
|
||||
location,
|
||||
[typeSymbol.Name, baseType.Name, message]));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
Rule,
|
||||
location,
|
||||
[typeSymbol.Name, baseType.Name]));
|
||||
}
|
||||
}
|
||||
|
||||
private static string? GetMessageFromAttributeData(AttributeData data)
|
||||
{
|
||||
if (data.ConstructorArguments is not [var message, ..])
|
||||
return null;
|
||||
|
||||
return message.Value as string;
|
||||
}
|
||||
}
|
||||
@@ -42,8 +42,8 @@ public class RecursiveMoveBenchmark : RobustIntegrationTest
|
||||
public void GlobalSetup()
|
||||
{
|
||||
ProgramShared.PathOffset = "../../../../";
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
var server = StartServer(new() {Pool = false});
|
||||
var client = StartClient(new() {Pool = false});
|
||||
|
||||
Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync()).Wait();
|
||||
|
||||
|
||||
@@ -226,6 +226,9 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
if (value == _master)
|
||||
return;
|
||||
|
||||
if (CheckMasterCycle(value))
|
||||
throw new InvalidOperationException("Tried to set master to a child of this renderer!");
|
||||
|
||||
if (_master is { Disposed: false })
|
||||
{
|
||||
try
|
||||
@@ -729,4 +732,22 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
_synth?.Dispose();
|
||||
_player?.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that a given renderer is not already a child of this renderer, i.e. it would introduce a cycle if set as master of this renderer.
|
||||
/// </summary>
|
||||
private bool CheckMasterCycle(IMidiRenderer? otherRenderer)
|
||||
{
|
||||
// Doesn't inside drift, cringe.
|
||||
|
||||
while (otherRenderer != null)
|
||||
{
|
||||
if (otherRenderer == this)
|
||||
return true;
|
||||
|
||||
otherRenderer = otherRenderer.Master;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
#if DEBUG
|
||||
using Robust.Client.Debugging;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
public sealed class DebugAnchoredCommand : LocalizedCommands
|
||||
public sealed class DebugAnchoredCommand : LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly DebugAnchoringSystem _system = default!;
|
||||
|
||||
public override string Command => "showanchored";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntitySystem.Get<DebugAnchoringSystem>().Enabled ^= true;
|
||||
_system.Enabled ^= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
#if DEBUG
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
internal sealed class LightDebugCommand : LocalizedCommands
|
||||
internal sealed class LightDebugCommand : LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly DebugLightTreeSystem _system = default!;
|
||||
|
||||
public override string Command => "lightbb";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntitySystem.Get<DebugLightTreeSystem>().Enabled ^= true;
|
||||
_system.Enabled ^= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
public sealed class ShowPlayerVelocityCommand : LocalizedCommands
|
||||
public sealed class ShowPlayerVelocityCommand : LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystems = default!;
|
||||
[Dependency] private readonly ShowPlayerVelocityDebugSystem _system = default!;
|
||||
|
||||
public override string Command => "showplayervelocity";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
_entitySystems.GetEntitySystem<ShowPlayerVelocityDebugSystem>().Enabled ^= true;
|
||||
_system.Enabled ^= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
123
Robust.Client/GameObjects/ClientEntityManager.Spawn.cs
Normal file
123
Robust.Client/GameObjects/ClientEntityManager.Spawn.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
public sealed partial class ClientEntityManager
|
||||
{
|
||||
public override EntityUid PredictedSpawnAttachedTo(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default)
|
||||
{
|
||||
var ent = SpawnAttachedTo(protoName, coordinates, overrides, rotation);
|
||||
FlagPredicted(ent);
|
||||
return ent;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override EntityUid PredictedSpawn(string? protoName = null, ComponentRegistry? overrides = null, bool doMapInit = true)
|
||||
{
|
||||
var ent = Spawn(protoName, overrides, doMapInit);
|
||||
FlagPredicted(ent);
|
||||
return ent;
|
||||
}
|
||||
|
||||
public override EntityUid PredictedSpawn(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default!)
|
||||
{
|
||||
var ent = Spawn(protoName, coordinates, overrides, rotation);
|
||||
FlagPredicted(ent);
|
||||
return ent;
|
||||
}
|
||||
|
||||
public override EntityUid PredictedSpawnAtPosition(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var ent = SpawnAtPosition(protoName, coordinates, overrides);
|
||||
FlagPredicted(ent);
|
||||
return ent;
|
||||
}
|
||||
|
||||
public override bool PredictedTrySpawnNextTo(
|
||||
string? protoName,
|
||||
EntityUid target,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
TransformComponent? xform = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
if (!TrySpawnNextTo(protoName, target, out uid, xform, overrides))
|
||||
return false;
|
||||
|
||||
FlagPredicted(uid.Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool PredictedTrySpawnInContainer(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
if (!TrySpawnInContainer(protoName, containerUid, containerId, out uid, containerComp, overrides))
|
||||
return false;
|
||||
|
||||
FlagPredicted(uid.Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override EntityUid PredictedSpawnNextToOrDrop(string? protoName, EntityUid target, TransformComponent? xform = null, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var ent = SpawnNextToOrDrop(protoName, target, xform, overrides);
|
||||
FlagPredicted(ent);
|
||||
return ent;
|
||||
}
|
||||
|
||||
public override EntityUid PredictedSpawnInContainerOrDrop(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
TransformComponent? xform = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
var ent = SpawnInContainerOrDrop(protoName, containerUid, containerId, xform, containerComp, overrides);
|
||||
FlagPredicted(ent);
|
||||
return ent;
|
||||
}
|
||||
|
||||
public override EntityUid PredictedSpawnInContainerOrDrop(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
out bool inserted,
|
||||
TransformComponent? xform = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
var ent = SpawnInContainerOrDrop(protoName,
|
||||
containerUid,
|
||||
containerId,
|
||||
out inserted,
|
||||
xform,
|
||||
containerComp,
|
||||
overrides);
|
||||
|
||||
FlagPredicted(ent);
|
||||
return ent;
|
||||
}
|
||||
|
||||
public override void FlagPredicted(Entity<MetaDataComponent?> ent)
|
||||
{
|
||||
if (!MetaQuery.Resolve(ent.Owner, ref ent.Comp))
|
||||
return;
|
||||
|
||||
DebugTools.Assert(IsClientSide(ent.Owner, ent.Comp));
|
||||
EnsureComponent<PredictedSpawnComponent>(ent.Owner);
|
||||
|
||||
// TODO: Need to map call site or something, needs to be consistent between client and server.
|
||||
}
|
||||
}
|
||||
@@ -291,5 +291,54 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PredictedDeleteEntity(Entity<MetaDataComponent?, TransformComponent?> ent)
|
||||
{
|
||||
if (!MetaQuery.Resolve(ent.Owner, ref ent.Comp1)
|
||||
|| ent.Comp1.EntityDeleted
|
||||
|| !TransformQuery.Resolve(ent.Owner, ref ent.Comp2))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// So there's 3 scenarios:
|
||||
// 1. Networked entity we just move to nullspace and rely on state handling.
|
||||
// 2. Clientside predicted entity we delete and rely on state handling.
|
||||
// 3. Clientside only entity that actually needs deleting here.
|
||||
|
||||
if (ent.Comp1.NetEntity.IsClientSide())
|
||||
{
|
||||
DeleteEntity(ent, ent.Comp1, ent.Comp2);
|
||||
}
|
||||
else
|
||||
{
|
||||
_xforms.DetachEntity(ent, ent.Comp2);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PredictedQueueDeleteEntity(Entity<MetaDataComponent?, TransformComponent?> ent)
|
||||
{
|
||||
if (IsQueuedForDeletion(ent.Owner)
|
||||
|| !MetaQuery.Resolve(ent.Owner, ref ent.Comp1)
|
||||
|| ent.Comp1.EntityDeleted
|
||||
|| !TransformQuery.Resolve(ent.Owner, ref ent.Comp2))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent.Comp1.NetEntity.IsClientSide())
|
||||
{
|
||||
// client-side QueueDeleteEntity re-fetches MetadataComp and checks IsClientSide().
|
||||
// base call to skip that.
|
||||
// TODO create override that takes in metadata comp
|
||||
base.QueueDeleteEntity(ent);
|
||||
}
|
||||
else
|
||||
{
|
||||
_xforms.DetachEntity(ent.Owner, ent.Comp2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,19 +10,21 @@ using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public sealed class ShowSpriteBBCommand : LocalizedCommands
|
||||
public sealed class ShowSpriteBBCommand : LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly SpriteBoundsSystem _system = default!;
|
||||
|
||||
public override string Command => "showspritebb";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntitySystem.Get<SpriteBoundsSystem>().Enabled ^= true;
|
||||
_system.Enabled ^= true;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SpriteBoundsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] private readonly SpriteTreeSystem _spriteTree = default!;
|
||||
|
||||
@@ -40,7 +42,7 @@ namespace Robust.Client.GameObjects
|
||||
if (_enabled)
|
||||
{
|
||||
DebugTools.AssertNull(_overlay);
|
||||
_overlay = new SpriteBoundsOverlay(_spriteTree, _entityManager);
|
||||
_overlay = new SpriteBoundsOverlay(_spriteTree, _xformSystem);
|
||||
_overlayManager.AddOverlay(_overlay);
|
||||
}
|
||||
else
|
||||
@@ -59,13 +61,13 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly SharedTransformSystem _xformSystem;
|
||||
private SpriteTreeSystem _renderTree;
|
||||
|
||||
public SpriteBoundsOverlay(SpriteTreeSystem renderTree, IEntityManager entityManager)
|
||||
public SpriteBoundsOverlay(SpriteTreeSystem renderTree, SharedTransformSystem xformSystem)
|
||||
{
|
||||
_renderTree = renderTree;
|
||||
_entityManager = entityManager;
|
||||
_xformSystem = xformSystem;
|
||||
}
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
@@ -76,7 +78,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
foreach (var (sprite, xform) in _renderTree.QueryAabb(currentMap, viewport))
|
||||
{
|
||||
var (worldPos, worldRot) = xform.GetWorldPositionRotation();
|
||||
var (worldPos, worldRot) = _xformSystem.GetWorldPositionRotation(xform);
|
||||
var bounds = sprite.CalculateRotatedBoundingBox(worldPos, worldRot, args.Viewport.Eye?.Rotation ?? default);
|
||||
|
||||
// Get scaled down bounds used to indicate the "south" of a sprite.
|
||||
|
||||
@@ -303,7 +303,7 @@ namespace Robust.Client.GameObjects
|
||||
while (parent.IsValid() && (!spriteOccluded || !lightOccluded))
|
||||
{
|
||||
var parentXform = TransformQuery.GetComponent(parent);
|
||||
if (TryComp<ContainerManagerComponent>(parent, out var manager) && manager.TryGetContainer(child, out var container))
|
||||
if (TryComp<ContainerManagerComponent>(parent, out var manager) && TryGetContainingContainer(parent, child, out var container, manager))
|
||||
{
|
||||
spriteOccluded = spriteOccluded || !container.ShowContents;
|
||||
lightOccluded = lightOccluded || container.OccludesLight;
|
||||
@@ -344,7 +344,7 @@ namespace Robust.Client.GameObjects
|
||||
var childLightOccluded = lightOccluded;
|
||||
|
||||
// We already know either sprite or light is not occluding so need to check container.
|
||||
if (manager.TryGetContainer(child, out var container))
|
||||
if (TryGetContainingContainer(entity, child, out var container, manager))
|
||||
{
|
||||
childSpriteOccluded = childSpriteOccluded || !container.ShowContents;
|
||||
childLightOccluded = childLightOccluded || container.OccludesLight;
|
||||
|
||||
@@ -2,24 +2,24 @@ using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Utility;
|
||||
using Color = Robust.Shared.Maths.Color;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
public sealed class DebugEntityLookupCommand : LocalizedCommands
|
||||
public sealed class DebugEntityLookupCommand : LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly DebugEntityLookupSystem _system = default!;
|
||||
|
||||
public override string Command => "togglelookup";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntitySystem.Get<DebugEntityLookupSystem>().Enabled ^= true;
|
||||
_system.Enabled ^= true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,10 +26,10 @@ namespace Robust.Client.GameObjects
|
||||
if (_enabled)
|
||||
{
|
||||
_lightOverlay = new DebugLightOverlay(
|
||||
EntitySystem.Get<EntityLookupSystem>(),
|
||||
EntityManager.System<EntityLookupSystem>(),
|
||||
IoCManager.Resolve<IEyeManager>(),
|
||||
IoCManager.Resolve<IMapManager>(),
|
||||
Get<LightTreeSystem>());
|
||||
EntityManager.System<LightTreeSystem>());
|
||||
|
||||
overlayManager.AddOverlay(_lightOverlay);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public sealed class EyeSystem : SharedEyeSystem
|
||||
eyeComponent.Target = null;
|
||||
}
|
||||
|
||||
eyeComponent.Eye.Position = xform.MapPosition;
|
||||
eyeComponent.Eye.Position = TransformSystem.GetMapCoordinates(xform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ using Robust.Client.Physics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Containers;
|
||||
@@ -563,6 +564,21 @@ namespace Robust.Client.GameStates
|
||||
var metaQuery = _entities.GetEntityQuery<MetaDataComponent>();
|
||||
RemQueue<IComponent> toRemove = new();
|
||||
|
||||
// Handle predicted entity spawns.
|
||||
var predicted = new ValueList<EntityUid>();
|
||||
var predictedQuery = _entities.AllEntityQueryEnumerator<PredictedSpawnComponent>();
|
||||
|
||||
while (predictedQuery.MoveNext(out var uid, out var _))
|
||||
{
|
||||
predicted.Add(uid);
|
||||
}
|
||||
|
||||
// Entity will get re-created as part of the tick.
|
||||
foreach (var ent in predicted)
|
||||
{
|
||||
_entities.DeleteEntity(ent);
|
||||
}
|
||||
|
||||
foreach (var entity in system.DirtyEntities)
|
||||
{
|
||||
DebugTools.Assert(toRemove.Count == 0);
|
||||
|
||||
@@ -123,7 +123,8 @@ namespace Robust.Client.Graphics
|
||||
/// <inheritdoc />
|
||||
public ScreenCoordinates CoordinatesToScreen(EntityCoordinates point)
|
||||
{
|
||||
return MapToScreen(point.ToMap(_entityManager, _entityManager.System<SharedTransformSystem>()));
|
||||
var transformSystem = _entityManager.System<SharedTransformSystem>();
|
||||
return MapToScreen(transformSystem.ToMapCoordinates(point));
|
||||
}
|
||||
|
||||
public ScreenCoordinates MapToScreen(MapCoordinates point)
|
||||
|
||||
@@ -30,6 +30,23 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private int _indicesPerChunk(MapChunk chunk) => chunk.ChunkSize * chunk.ChunkSize * GetQuadBatchIndexCount();
|
||||
|
||||
private List<Entity<MapGridComponent>> _grids = new();
|
||||
private bool _drawTileEdges;
|
||||
|
||||
private void RenderTileEdgesChanges(bool value)
|
||||
{
|
||||
_drawTileEdges = value;
|
||||
if (!value)
|
||||
return;
|
||||
|
||||
// Dirty all Edges
|
||||
foreach (var gridData in _mapChunkData.Values)
|
||||
{
|
||||
foreach (var chunk in gridData.Values)
|
||||
{
|
||||
chunk.EdgeDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _drawGrids(Viewport viewport, Box2 worldAABB, Box2Rotated worldBounds, IEye eye)
|
||||
{
|
||||
@@ -81,6 +98,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
_updateChunkMesh(mapGrid, chunk, datum);
|
||||
|
||||
if (!_drawTileEdges)
|
||||
continue;
|
||||
|
||||
// Dirty edge tiles for next step.
|
||||
datum.EdgeDirty = true;
|
||||
|
||||
@@ -99,17 +119,16 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
enumerator = mapSystem.GetMapChunks(mapGrid.Owner, mapGrid.Comp, worldBounds);
|
||||
|
||||
// Handle edge sprites.
|
||||
while (enumerator.MoveNext(out var chunk))
|
||||
if (_drawTileEdges)
|
||||
{
|
||||
var datum = data[chunk.Indices];
|
||||
|
||||
if (!datum.EdgeDirty)
|
||||
continue;
|
||||
|
||||
_updateChunkEdges(mapGrid, chunk, datum);
|
||||
enumerator = mapSystem.GetMapChunks(mapGrid.Owner, mapGrid.Comp, worldBounds);
|
||||
while (enumerator.MoveNext(out var chunk))
|
||||
{
|
||||
var datum = data[chunk.Indices];
|
||||
if (datum.EdgeDirty)
|
||||
_updateChunkEdges(mapGrid, chunk, datum);
|
||||
}
|
||||
}
|
||||
|
||||
enumerator = mapSystem.GetMapChunks(mapGrid.Owner, mapGrid.Comp, worldBounds);
|
||||
@@ -129,7 +148,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
if (datum.EdgeCount > 0)
|
||||
if (_drawTileEdges && datum.EdgeCount > 0)
|
||||
{
|
||||
BindVertexArray(datum.EdgeVAO);
|
||||
CheckGlError();
|
||||
@@ -138,7 +157,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
GL.DrawElements(GetQuadGLPrimitiveType(), datum.EdgeCount * GetQuadBatchIndexCount(), DrawElementsType.UnsignedShort, 0);
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
requiresFlush = false;
|
||||
@@ -274,7 +292,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var gridX = x + chunkOriginScaled.X;
|
||||
var gridY = y + chunkOriginScaled.Y;
|
||||
var tile = chunk.GetTile(x, y);
|
||||
var tileDef = _tileDefinitionManager[tile.TypeId];
|
||||
if (!_tileDefinitionManager.TryGetDefinition(tile.TypeId, out var tileDef))
|
||||
continue;
|
||||
|
||||
// Edge render
|
||||
for (var nx = -1; nx <= 1; nx++)
|
||||
@@ -288,14 +307,15 @@ namespace Robust.Client.Graphics.Clyde
|
||||
if (!maps.TryGetTile(grid.Comp, neighborIndices, out var neighborTile))
|
||||
continue;
|
||||
|
||||
var neighborDef = _tileDefinitionManager[neighborTile.TypeId];
|
||||
if (!_tileDefinitionManager.TryGetDefinition(neighborTile.TypeId, out var neighborDef))
|
||||
continue;
|
||||
|
||||
// If it's the same tile then no edge to be drawn.
|
||||
if (tile.TypeId == neighborTile.TypeId || neighborDef.EdgeSprites.Count == 0)
|
||||
continue;
|
||||
|
||||
// If neighbor is a lower priority then us then don't draw on our tile.
|
||||
if (neighborDef.EdgeSpritePriority < tileDef.EdgeSpritePriority)
|
||||
// If neighbor is a lower or same priority then us then don't draw on our tile.
|
||||
if (neighborDef.EdgeSpritePriority <= tileDef.EdgeSpritePriority)
|
||||
continue;
|
||||
|
||||
var direction = new Vector2i(nx, ny).AsDirection().GetOpposite();
|
||||
|
||||
@@ -121,6 +121,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_cfg.OnValueChanged(CVars.LightSoftShadows, SoftShadowsChanged, true);
|
||||
_cfg.OnValueChanged(CVars.MaxLightCount, MaxLightsChanged, true);
|
||||
_cfg.OnValueChanged(CVars.MaxOccluderCount, MaxOccludersChanged, true);
|
||||
_cfg.OnValueChanged(CVars.RenderTileEdges, RenderTileEdgesChanges, true);
|
||||
// I can't be bothered to tear down and set these threads up in a cvar change handler.
|
||||
|
||||
// Windows and Linux can be trusted to not explode with threaded windowing,
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace Robust.Client.Input
|
||||
[Dependency] private readonly IUserInterfaceManagerInternal _uiMgr = default!;
|
||||
[Dependency] private readonly IConsoleHost _console = default!;
|
||||
[Dependency] private readonly ISerializationManager _serialization = default!;
|
||||
private ISawmill _logger = default!;
|
||||
|
||||
private bool _currentlyFindingViewport;
|
||||
|
||||
@@ -114,6 +115,8 @@ namespace Robust.Client.Input
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
_logger = Logger.GetSawmill("input");
|
||||
|
||||
NetworkBindMap = new BoundKeyMap(_reflectionManager);
|
||||
NetworkBindMap.PopulateKeyFunctionsMap();
|
||||
|
||||
@@ -130,7 +133,7 @@ namespace Robust.Client.Input
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorS("input", "Failed to load user keybindings: " + e);
|
||||
_logger.Error("Failed to load user keybindings: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -531,8 +534,7 @@ namespace Robust.Client.Input
|
||||
|
||||
if (reg.Type != KeyBindingType.Command && !NetworkBindMap.FunctionExists(reg.Function.FunctionName))
|
||||
{
|
||||
Logger.DebugS("input", "Key function in {0} does not exist: '{1}'.", file,
|
||||
reg.Function);
|
||||
_logger.Debug("Key function in {0} does not exist: '{1}'.", file, reg.Function);
|
||||
invalid = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,11 +30,13 @@ namespace Robust.Client.Placement.Modes
|
||||
return;
|
||||
}
|
||||
|
||||
var mapId = MouseCoords.GetMapId(pManager.EntityManager);
|
||||
var transformSys = pManager.EntityManager.System<SharedTransformSystem>();
|
||||
var mapId = transformSys.GetMapId(MouseCoords);
|
||||
|
||||
var snapToEntities = EntitySystem.Get<EntityLookupSystem>().GetEntitiesInRange(MouseCoords, SnapToRange)
|
||||
var snapToEntities = pManager.EntityManager.System<EntityLookupSystem>()
|
||||
.GetEntitiesInRange(MouseCoords, SnapToRange)
|
||||
.Where(entity => pManager.EntityManager.GetComponent<MetaDataComponent>(entity).EntityPrototype == pManager.CurrentPrototype && pManager.EntityManager.GetComponent<TransformComponent>(entity).MapID == mapId)
|
||||
.OrderBy(entity => (pManager.EntityManager.GetComponent<TransformComponent>(entity).WorldPosition - MouseCoords.ToMapPos(pManager.EntityManager, pManager.EntityManager.System<SharedTransformSystem>())).LengthSquared())
|
||||
.OrderBy(entity => (transformSys.GetWorldPosition(entity) - transformSys.ToMapCoordinates(MouseCoords).Position).LengthSquared())
|
||||
.ToList();
|
||||
|
||||
if (snapToEntities.Count == 0)
|
||||
@@ -51,10 +53,11 @@ namespace Robust.Client.Placement.Modes
|
||||
|
||||
var closestBounds = component.BaseRSI.Size;
|
||||
|
||||
var closestPos = transformSys.GetWorldPosition(closestTransform);
|
||||
var closestRect =
|
||||
Box2.FromDimensions(
|
||||
closestTransform.WorldPosition.X - closestBounds.X / 2f,
|
||||
closestTransform.WorldPosition.Y - closestBounds.Y / 2f,
|
||||
closestPos.X - closestBounds.X / 2f,
|
||||
closestPos.Y - closestBounds.Y / 2f,
|
||||
closestBounds.X, closestBounds.Y);
|
||||
|
||||
var sides = new[]
|
||||
|
||||
@@ -21,7 +21,8 @@ namespace Robust.Client.Placement.Modes
|
||||
{
|
||||
MouseCoords = ScreenToCursorGrid(mouseScreen);
|
||||
|
||||
var gridIdOpt = MouseCoords.GetGridUid(pManager.EntityManager);
|
||||
var transformSys = pManager.EntityManager.System<SharedTransformSystem>();
|
||||
var gridIdOpt = transformSys.GetGrid(MouseCoords);
|
||||
SnapSize = 1f;
|
||||
if (gridIdOpt is EntityUid gridId && gridId.IsValid())
|
||||
{
|
||||
|
||||
@@ -16,8 +16,10 @@ namespace Robust.Client.Placement.Modes
|
||||
{
|
||||
MouseCoords = ScreenToCursorGrid(mouseScreen);
|
||||
|
||||
var transformSys = pManager.EntityManager.System<SharedTransformSystem>();
|
||||
|
||||
var tileSize = 1f;
|
||||
var gridIdOpt = MouseCoords.GetGridUid(pManager.EntityManager);
|
||||
var gridIdOpt = transformSys.GetGrid(MouseCoords);
|
||||
|
||||
if (gridIdOpt is EntityUid gridId && gridId.IsValid())
|
||||
{
|
||||
|
||||
@@ -16,9 +16,11 @@ namespace Robust.Client.Placement.Modes
|
||||
{
|
||||
MouseCoords = ScreenToCursorGrid(mouseScreen);
|
||||
|
||||
var transformSys = pManager.EntityManager.System<SharedTransformSystem>();
|
||||
|
||||
var tileSize = 1f;
|
||||
|
||||
var gridIdOpt = MouseCoords.GetGridUid(pManager.EntityManager);
|
||||
var gridIdOpt = transformSys.GetGrid(MouseCoords);
|
||||
if (gridIdOpt is EntityUid gridId && gridId.IsValid())
|
||||
{
|
||||
var mapGrid = pManager.EntityManager.GetComponent<MapGridComponent>(gridId);
|
||||
|
||||
@@ -121,8 +121,8 @@ namespace Robust.Client.Placement
|
||||
{
|
||||
if (!coordinate.IsValid(pManager.EntityManager))
|
||||
return; // Just some paranoia just in case
|
||||
var worldPos = coordinate.ToMapPos(pManager.EntityManager, transformSys);
|
||||
var worldRot = pManager.EntityManager.GetComponent<TransformComponent>(coordinate.EntityId).WorldRotation + dirAng;
|
||||
var worldPos = transformSys.ToMapCoordinates(coordinate).Position;
|
||||
var worldRot = transformSys.GetWorldRotation(coordinate.EntityId) + dirAng;
|
||||
|
||||
sprite.Color = IsValidPosition(coordinate) ? ValidPlaceColor : InvalidPlaceColor;
|
||||
var rot = args.Viewport.Eye?.Rotation ?? default;
|
||||
@@ -230,7 +230,7 @@ namespace Robust.Client.Placement
|
||||
|
||||
var range = pManager.CurrentPermission!.Range;
|
||||
var transformSys = pManager.EntityManager.System<SharedTransformSystem>();
|
||||
if (range > 0 && !pManager.EntityManager.GetComponent<TransformComponent>(controlled).Coordinates.InRange(pManager.EntityManager, transformSys, coordinates, range))
|
||||
if (range > 0 && !transformSys.InRange(pManager.EntityManager.GetComponent<TransformComponent>(controlled).Coordinates, coordinates, range))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@@ -239,7 +239,7 @@ namespace Robust.Client.Placement
|
||||
{
|
||||
var bounds = pManager.ColliderAABB;
|
||||
var transformSys = pManager.EntityManager.System<SharedTransformSystem>();
|
||||
var mapCoords = coordinates.ToMap(pManager.EntityManager, transformSys);
|
||||
var mapCoords = transformSys.ToMapCoordinates(coordinates);
|
||||
var (x, y) = mapCoords.Position;
|
||||
|
||||
var collisionBox = Box2.FromDimensions(
|
||||
@@ -248,7 +248,9 @@ namespace Robust.Client.Placement
|
||||
bounds.Width,
|
||||
bounds.Height);
|
||||
|
||||
return EntitySystem.Get<SharedPhysicsSystem>().TryCollideRect(collisionBox, mapCoords.MapId);
|
||||
return pManager.EntityManager
|
||||
.System<SharedPhysicsSystem>()
|
||||
.TryCollideRect(collisionBox, mapCoords.MapId);
|
||||
}
|
||||
|
||||
protected Vector2 ScreenToWorld(Vector2 point)
|
||||
@@ -265,10 +267,8 @@ namespace Robust.Client.Placement
|
||||
{
|
||||
var mapCoords = pManager.EyeManager.PixelToMap(coords.Position);
|
||||
var transformSys = pManager.EntityManager.System<SharedTransformSystem>();
|
||||
|
||||
if (!pManager.MapManager.TryFindGridAt(mapCoords, out var gridUid, out var grid))
|
||||
if (!pManager.MapManager.TryFindGridAt(mapCoords, out var gridUid, out _))
|
||||
{
|
||||
|
||||
return transformSys.ToCoordinates(mapCoords);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -56,7 +54,7 @@ namespace Robust.Client.Prototypes
|
||||
using var _ = _timing.StartStateApplicationArea();
|
||||
ReloadPrototypes([file]);
|
||||
|
||||
Logger.Info($"Reloaded prototypes in {sw.ElapsedMilliseconds} ms");
|
||||
Sawmill.Info($"Reloaded prototypes in {sw.ElapsedMilliseconds} ms");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
7
Robust.Client/UserInterface/UIConstants.cs
Normal file
7
Robust.Client/UserInterface/UIConstants.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Robust.Client.UserInterface;
|
||||
|
||||
internal static class UIConstants
|
||||
{
|
||||
public const string ObsoleteInheritanceMessage =
|
||||
"Do not inherit from standard UI controls, compose via nesting instead";
|
||||
}
|
||||
@@ -103,7 +103,6 @@ internal partial class UserInterfaceManager
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var guiArgs = new GUIBoundKeyEventArgs(args.Function, args.State, args.PointerLocation, args.CanFocus,
|
||||
args.PointerLocation.Position / control.UIScale - control.GlobalPosition,
|
||||
args.PointerLocation.Position - control.GlobalPixelPosition);
|
||||
@@ -115,10 +114,6 @@ internal partial class UserInterfaceManager
|
||||
args.Handle();
|
||||
}
|
||||
|
||||
// Attempt to ensure that keybind-up events get raised after a keybind-down.
|
||||
DebugTools.Assert(!_focusedControls.TryGetValue(args.Function, out var existing)
|
||||
|| !existing.VisibleInTree
|
||||
|| args.IsRepeat && existing == control);
|
||||
_focusedControls[args.Function] = control;
|
||||
|
||||
OnKeyBindDown?.Invoke(control);
|
||||
|
||||
@@ -37,6 +37,8 @@ public static class Diagnostics
|
||||
public const string IdPreferOtherType = "RA0031";
|
||||
public const string IdDuplicateDependency = "RA0032";
|
||||
public const string IdForbidLiteral = "RA0033";
|
||||
public const string IdObsoleteInheritance = "RA0034";
|
||||
public const string IdObsoleteInheritanceWithMessage = "RA0035";
|
||||
|
||||
public static SuppressionDescriptor MeansImplicitAssignment =>
|
||||
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");
|
||||
|
||||
@@ -4,6 +4,7 @@ using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
@@ -16,6 +17,20 @@ public sealed class ScaleCommand : LocalizedCommands
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
public override string Command => "scale";
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
switch (args.Length)
|
||||
{
|
||||
case 1:
|
||||
return CompletionResult.FromOptions(CompletionHelper.NetEntities(args[0], entManager: _entityManager));
|
||||
case 2:
|
||||
return CompletionResult.FromHint(Loc.GetString("cmd-hint-float"));
|
||||
default:
|
||||
return CompletionResult.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
|
||||
@@ -204,7 +204,7 @@ internal struct PvsMetadata
|
||||
{
|
||||
DebugTools.AssertEqual(NetEntity, comp.NetEntity);
|
||||
DebugTools.AssertEqual(VisMask, comp.VisibilityMask);
|
||||
DebugTools.Assert(LifeStage == comp.EntityLifeStage);
|
||||
DebugTools.AssertEqual(LifeStage, comp.EntityLifeStage);
|
||||
DebugTools.Assert(LastModifiedTick == comp.EntityLastModifiedTick || LastModifiedTick.Value == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +268,6 @@ namespace Robust.Server.Physics
|
||||
newGrids[i] = newGridUid;
|
||||
|
||||
// Keep same origin / velocity etc; this makes updating a lot faster and easier.
|
||||
_xformSystem.SetWorldPosition(newGridXform, gridPos);
|
||||
_xformSystem.SetWorldPositionRotation(newGridUid, gridPos, gridRot, newGridXform);
|
||||
var splitBody = _bodyQuery.GetComponent(newGridUid);
|
||||
_physics.SetLinearVelocity(newGridUid, mapBody.LinearVelocity, body: splitBody);
|
||||
@@ -290,7 +289,7 @@ namespace Robust.Server.Physics
|
||||
}
|
||||
|
||||
_maps.SetTiles(newGrid.Owner, newGrid.Comp, tileData);
|
||||
DebugTools.Assert(_mapManager.IsGrid(newGridUid), "A split grid had no tiles?");
|
||||
DebugTools.Assert(_gridQuery.HasComp(newGridUid), "A split grid had no tiles?");
|
||||
|
||||
// Set tiles on new grid + update anchored entities
|
||||
foreach (var node in group)
|
||||
|
||||
28
Robust.Shared/Analyzers/ObsoleteInheritanceAttribute.cs
Normal file
28
Robust.Shared/Analyzers/ObsoleteInheritanceAttribute.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace Robust.Shared.Analyzers;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the ability to <i>inherit</i> this type is obsolete, and attempting to do so should give a warning.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is useful to gracefully deal with types that should never have had <see cref="VirtualAttribute"/>.
|
||||
/// </remarks>
|
||||
/// <seealso cref="VirtualAttribute"/>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class ObsoleteInheritanceAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// An optional message provided alongside this obsoletion.
|
||||
/// </summary>
|
||||
public string? Message { get; }
|
||||
|
||||
public ObsoleteInheritanceAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
public ObsoleteInheritanceAttribute(string message)
|
||||
{
|
||||
Message = message;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ namespace Robust.Shared.Analyzers;
|
||||
/// Robust uses analyzers to prevent accidental usage of non-sealed classes:
|
||||
/// a class must be either marked [Virtual], abstract, or sealed.
|
||||
/// </remarks>
|
||||
/// <seealso cref="ObsoleteInheritanceAttribute"/>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class VirtualAttribute : Attribute
|
||||
{
|
||||
|
||||
@@ -968,6 +968,13 @@ namespace Robust.Shared
|
||||
public static readonly CVarDef<string> RenderFOVColor =
|
||||
CVarDef.Create("render.fov_color", Color.Black.ToHex(), CVar.REPLICATED | CVar.SERVER);
|
||||
|
||||
/// <summary>
|
||||
/// Whether to render tile edges, which is where some tiles can partially overlap other adjacent tiles on a grid.
|
||||
/// E.g., snow tiles partly extending beyond their own tile to blend together with different adjacent tiles types.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> RenderTileEdges =
|
||||
CVarDef.Create("render.tile_edges", true, CVar.CLIENTONLY);
|
||||
|
||||
/*
|
||||
* CONTROLS
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.ComponentTrees;
|
||||
@@ -13,20 +14,23 @@ namespace Robust.Shared.ComponentTrees;
|
||||
/// </remarks>
|
||||
internal sealed class RecursiveMoveSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
public delegate void TreeRecursiveMoveEventHandler(EntityUid uid, TransformComponent xform);
|
||||
|
||||
public event TreeRecursiveMoveEventHandler? OnTreeRecursiveMove;
|
||||
|
||||
private EntityQuery<MapComponent> _mapQuery;
|
||||
private EntityQuery<MapGridComponent> _gridQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
bool _subscribed = false;
|
||||
private bool _subscribed = false;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_gridQuery = GetEntityQuery<MapGridComponent>();
|
||||
_mapQuery = GetEntityQuery<MapComponent>();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
}
|
||||
|
||||
@@ -52,8 +56,8 @@ internal sealed class RecursiveMoveSystem : EntitySystem
|
||||
if (args.Component.MapUid == args.Sender || args.Component.GridUid == args.Sender)
|
||||
return;
|
||||
|
||||
DebugTools.Assert(!_mapManager.IsMap(args.Sender));
|
||||
DebugTools.Assert(!_mapManager.IsGrid(args.Sender));
|
||||
DebugTools.Assert(!_mapQuery.HasComp(args.Sender));
|
||||
DebugTools.Assert(!_gridQuery.HasComp(args.Sender));
|
||||
|
||||
AnythingMovedSubHandler(args.Sender, args.Component);
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Robust.Shared.Containers
|
||||
|
||||
#if DEBUG
|
||||
// TODO make this a proper debug assert when gun code no longer fudges client-side spawn prediction.
|
||||
if (entMan.IsClientSide(toInsert) && !entMan.IsClientSide(Owner) && Manager.NetSyncEnabled)
|
||||
if (entMan.IsClientSide(toInsert) && !entMan.IsClientSide(Owner) && Manager.NetSyncEnabled && !entMan.HasComponent<PredictedSpawnComponent>(toInsert))
|
||||
Logger.Warning("Inserting a client-side entity into a networked container slot. This will block the container slot and may cause issues.");
|
||||
#endif
|
||||
ContainedEntity = toInsert;
|
||||
|
||||
@@ -530,6 +530,39 @@ namespace Robust.Shared.Containers
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the full chain of containers containing the entity passed in, from innermost to outermost.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The resulting collection includes the container directly containing the entity (if any),
|
||||
/// the container containing that container, and so on until reaching the outermost container.
|
||||
/// </remarks>
|
||||
public IEnumerable<BaseContainer> GetContainingContainers(Entity<TransformComponent?> ent)
|
||||
{
|
||||
if (!ent.Owner.IsValid())
|
||||
yield break;
|
||||
|
||||
if (!Resolve(ent, ref ent.Comp))
|
||||
yield break;
|
||||
|
||||
var child = ent.Owner;
|
||||
var parent = ent.Comp.ParentUid;
|
||||
|
||||
while (parent.IsValid())
|
||||
{
|
||||
if (((MetaQuery.GetComponent(child).Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer) &&
|
||||
_managerQuery.TryGetComponent(parent, out var conManager) &&
|
||||
TryGetContainingContainer(parent, child, out var parentContainer, conManager))
|
||||
{
|
||||
yield return parentContainer;
|
||||
}
|
||||
|
||||
var parentXform = TransformQuery.GetComponent(parent);
|
||||
child = parent;
|
||||
parent = parentXform.ParentUid;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the top-most container in the hierarchy for this entity, if it exists.
|
||||
/// </summary>
|
||||
|
||||
@@ -273,8 +273,6 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
public IEnumerable<string> ContentGetDirectoryEntries(ResPath path)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(path, nameof(path));
|
||||
|
||||
if (!path.IsRooted)
|
||||
throw new ArgumentException("Path is not rooted", nameof(path));
|
||||
|
||||
|
||||
@@ -497,6 +497,23 @@ Types:
|
||||
DebuggerVisualizerAttribute: { All: True }
|
||||
Stopwatch: { All: True }
|
||||
System.Globalization:
|
||||
# all the calendars are here, screw making our own.
|
||||
Calendar: { All: True }
|
||||
ChineseLunisolarCalendar: { All: True }
|
||||
GregorianCalendar: { All: True }
|
||||
HebrewCalendar: { All: True }
|
||||
HijriCalendar: { All: True }
|
||||
JapaneseCalendar: { All: True }
|
||||
JapaneseLunisolarCalendar: { All: True }
|
||||
JulianCalendar: { All: True }
|
||||
KoreanCalendar: { All: True }
|
||||
KoreanLunisolarCalendar: { All: True }
|
||||
PersianCalendar: { All: True }
|
||||
TaiwanCalendar: { All: True }
|
||||
TaiwanLunisolarCalendar: { All: True }
|
||||
ThaiBuddhistCalendar: { All: True }
|
||||
UmAlQuraCalendar: { All: True }
|
||||
#end calendars
|
||||
CompareOptions: { }
|
||||
CultureInfo: { All: True }
|
||||
DateTimeStyles: { All: True } # Enum
|
||||
@@ -1071,6 +1088,7 @@ Types:
|
||||
DateTime: { All: True }
|
||||
DateTimeKind: { } # Enum
|
||||
DateTimeOffset: { All: True }
|
||||
DayOfWeek: { } # Enum
|
||||
Decimal: { All: True }
|
||||
Delegate:
|
||||
Methods:
|
||||
|
||||
@@ -475,7 +475,7 @@ public sealed class EntityDeserializer :
|
||||
|
||||
foreach (var (key, value) in tileMap.Children)
|
||||
{
|
||||
var yamlTileId = ((ValueDataNode) key).AsInt();
|
||||
var yamlTileId = int.Parse(key, CultureInfo.InvariantCulture);
|
||||
var tileName = ((ValueDataNode) value).Value;
|
||||
if (migrations.TryGetValue(tileName, out var @new))
|
||||
tileName = @new;
|
||||
@@ -1000,7 +1000,7 @@ public sealed class EntityDeserializer :
|
||||
continue;
|
||||
|
||||
DebugTools.Assert(meta.EntityLifeStage == EntityLifeStage.Initialized);
|
||||
meta.EntityLifeStage = EntityLifeStage.MapInitialized;
|
||||
EntMan.SetLifeStage(meta, EntityLifeStage.MapInitialized);
|
||||
}
|
||||
|
||||
_log.Debug($"Finished flagging mapinit in {_stopwatch.Elapsed}");
|
||||
|
||||
@@ -69,7 +69,7 @@ internal sealed class MapChunkSerializer : ITypeSerializer<MapChunk, MappingData
|
||||
|
||||
var tileDefinitionManager = dependencies.Resolve<ITileDefinitionManager>();
|
||||
|
||||
node.TryGetValue(new ValueDataNode("version"), out var versionNode);
|
||||
node.TryGetValue("version", out var versionNode);
|
||||
var version = ((ValueDataNode?) versionNode)?.AsInt() ?? 1;
|
||||
|
||||
for (ushort y = 0; y < chunk.ChunkSize; y++)
|
||||
|
||||
@@ -147,7 +147,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// The current lifetime stage of this entity. You can use this to check
|
||||
/// if the entity is initialized or being deleted.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[ViewVariables, Access(typeof(EntityManager), Other = AccessPermissions.ReadExecute)]
|
||||
public EntityLifeStage EntityLifeStage { get; internal set; }
|
||||
|
||||
public MetaDataFlags Flags
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the attached entity was spawn predicted and should be reconciled when the server states comes in.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class PredictedSpawnComponent : Component;
|
||||
@@ -14,7 +14,7 @@ public partial class EntityManager
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.PreAdd" /> to <see cref="ComponentLifeStage.Added" />,
|
||||
/// after raising a <see cref="ComponentAdd"/> event.
|
||||
/// </summary>
|
||||
internal void LifeAddToEntity(EntityUid uid, IComponent component, CompIdx idx)
|
||||
internal void LifeAddToEntity<T>(EntityUid uid, T component, CompIdx idx) where T : IComponent
|
||||
{
|
||||
DebugTools.Assert(!_deleteSet.Contains(component));
|
||||
DebugTools.Assert(component.LifeStage == ComponentLifeStage.PreAdd);
|
||||
@@ -33,7 +33,7 @@ public partial class EntityManager
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Added" /> to <see cref="ComponentLifeStage.Initialized" />,
|
||||
/// calling <see cref="Initialize" />.
|
||||
/// </summary>
|
||||
internal void LifeInitialize(EntityUid uid, IComponent component, CompIdx idx)
|
||||
internal void LifeInitialize<T>(EntityUid uid, T component, CompIdx idx) where T : IComponent
|
||||
{
|
||||
DebugTools.Assert(!_deleteSet.Contains(component));
|
||||
DebugTools.Assert(component.LifeStage == ComponentLifeStage.Added);
|
||||
@@ -47,7 +47,7 @@ public partial class EntityManager
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Initialized" /> to
|
||||
/// <see cref="ComponentLifeStage.Running" />, calling <see cref="Startup" />.
|
||||
/// </summary>
|
||||
internal void LifeStartup(EntityUid uid, IComponent component, CompIdx idx)
|
||||
internal void LifeStartup<T>(EntityUid uid, T component, CompIdx idx) where T : IComponent
|
||||
{
|
||||
DebugTools.Assert(!_deleteSet.Contains(component));
|
||||
DebugTools.Assert(component.LifeStage == ComponentLifeStage.Initialized);
|
||||
@@ -64,7 +64,7 @@ public partial class EntityManager
|
||||
/// <remarks>
|
||||
/// Components are allowed to remove themselves in their own Startup function.
|
||||
/// </remarks>
|
||||
internal void LifeShutdown(EntityUid uid, IComponent component, CompIdx idx)
|
||||
internal void LifeShutdown<T>(EntityUid uid, T component, CompIdx idx) where T : IComponent
|
||||
{
|
||||
DebugTools.Assert(component.LifeStage is >= ComponentLifeStage.Initializing and < ComponentLifeStage.Stopping);
|
||||
|
||||
@@ -84,7 +84,7 @@ public partial class EntityManager
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Stopped" /> to <see cref="ComponentLifeStage.Deleted" />,
|
||||
/// calling <see cref="Component.OnRemove" />.
|
||||
/// </summary>
|
||||
internal void LifeRemoveFromEntity(EntityUid uid, IComponent component, CompIdx idx)
|
||||
internal void LifeRemoveFromEntity<T>(EntityUid uid, T component, CompIdx idx) where T : IComponent
|
||||
{
|
||||
// can be called at any time after PreAdd, including inside other life stage events.
|
||||
DebugTools.Assert(component.LifeStage != ComponentLifeStage.PreAdd);
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
@@ -79,7 +80,7 @@ public partial class EntityManager
|
||||
throw new InvalidOperationException($"Tried to spawn entity {protoName} on invalid coordinates {coordinates}.");
|
||||
|
||||
var entity = CreateEntityUninitialized(protoName, coordinates, overrides, rotation);
|
||||
InitializeAndStartEntity(entity, coordinates.GetMapId(this));
|
||||
InitializeAndStartEntity(entity, _xforms.GetMapId(coordinates));
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -100,7 +101,7 @@ public partial class EntityManager
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid SpawnAtPosition(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
=> Spawn(protoName, coordinates.ToMap(this, _xforms), overrides);
|
||||
=> Spawn(protoName, _xforms.ToMapCoordinates(coordinates), overrides);
|
||||
|
||||
public bool TrySpawnNextTo(
|
||||
string? protoName,
|
||||
@@ -206,4 +207,91 @@ public partial class EntityManager
|
||||
|
||||
return uid;
|
||||
}
|
||||
|
||||
#region Prediction
|
||||
|
||||
public virtual EntityUid PredictedSpawnAttachedTo(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default)
|
||||
{
|
||||
return SpawnAttachedTo(protoName, coordinates, overrides, rotation);
|
||||
}
|
||||
|
||||
public virtual EntityUid PredictedSpawn(string? protoName = null, ComponentRegistry? overrides = null, bool doMapInit = true)
|
||||
{
|
||||
return Spawn(protoName, overrides, doMapInit);
|
||||
}
|
||||
|
||||
public virtual EntityUid PredictedSpawn(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default!)
|
||||
{
|
||||
return Spawn(protoName, coordinates, overrides, rotation);
|
||||
}
|
||||
|
||||
public virtual EntityUid PredictedSpawnAtPosition(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
return SpawnAtPosition(protoName, coordinates, overrides);
|
||||
}
|
||||
|
||||
public virtual bool PredictedTrySpawnNextTo(
|
||||
string? protoName,
|
||||
EntityUid target,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
TransformComponent? xform = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
return TrySpawnNextTo(protoName, target, out uid, xform, overrides);
|
||||
}
|
||||
|
||||
public virtual bool PredictedTrySpawnInContainer(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
return TrySpawnInContainer(protoName, containerUid, containerId, out uid, containerComp, overrides);
|
||||
}
|
||||
|
||||
public virtual EntityUid PredictedSpawnNextToOrDrop(string? protoName, EntityUid target, TransformComponent? xform = null, ComponentRegistry? overrides = null)
|
||||
{
|
||||
return SpawnNextToOrDrop(protoName, target, xform, overrides);
|
||||
}
|
||||
|
||||
public virtual EntityUid PredictedSpawnInContainerOrDrop(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
TransformComponent? xform = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
return SpawnInContainerOrDrop(protoName, containerUid, containerId, xform, containerComp, overrides);
|
||||
}
|
||||
|
||||
public virtual EntityUid PredictedSpawnInContainerOrDrop(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
out bool inserted,
|
||||
TransformComponent? xform = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
return SpawnInContainerOrDrop(protoName,
|
||||
containerUid,
|
||||
containerId,
|
||||
out inserted,
|
||||
xform,
|
||||
containerComp,
|
||||
overrides);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flags an entity as being a predicted spawn and should be deleted when its corresponding tick comes in.
|
||||
/// </summary>
|
||||
public virtual void FlagPredicted(Entity<MetaDataComponent?> ent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
// I feel like PJB might shed me for putting a system dependency here, but its required for setting entity
|
||||
// positions on spawn....
|
||||
private SharedTransformSystem _xforms = default!;
|
||||
protected SharedTransformSystem _xforms = default!;
|
||||
private SharedContainerSystem _containers = default!;
|
||||
|
||||
public EntityQuery<MetaDataComponent> MetaQuery;
|
||||
@@ -693,6 +693,36 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public bool IsQueuedForDeletion(EntityUid uid) => QueuedDeletionsSet.Contains(uid);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void PredictedDeleteEntity(Entity<MetaDataComponent?, TransformComponent?> ent)
|
||||
{
|
||||
DeleteEntity(ent.Owner);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PredictedDeleteEntity(Entity<MetaDataComponent?, TransformComponent?>? ent)
|
||||
{
|
||||
if (ent == null)
|
||||
return;
|
||||
|
||||
PredictedDeleteEntity(ent.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void PredictedQueueDeleteEntity(Entity<MetaDataComponent?, TransformComponent?> ent)
|
||||
{
|
||||
QueueDeleteEntity(ent.Owner);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void PredictedQueueDeleteEntity(Entity<MetaDataComponent?, TransformComponent?>? ent)
|
||||
{
|
||||
if (ent == null)
|
||||
return;
|
||||
|
||||
PredictedQueueDeleteEntity(ent.Value);
|
||||
}
|
||||
|
||||
public bool EntityExists(EntityUid uid)
|
||||
{
|
||||
return MetaQuery.HasComponentInternal(uid);
|
||||
|
||||
@@ -782,6 +782,34 @@ public partial class EntitySystem
|
||||
EntityManager.QueueDeleteEntity(uid);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.DeleteEntity(EntityUid?)" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void PredictedDel(Entity<MetaDataComponent?, TransformComponent?> ent)
|
||||
{
|
||||
EntityManager.PredictedDeleteEntity(ent);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.DeleteEntity(EntityUid?)" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void PredictedDel(Entity<MetaDataComponent?, TransformComponent?>? ent)
|
||||
{
|
||||
EntityManager.PredictedDeleteEntity(ent);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.QueueDeleteEntity(EntityUid)" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void PredictedQueueDel(Entity<MetaDataComponent?, TransformComponent?> ent)
|
||||
{
|
||||
EntityManager.PredictedQueueDeleteEntity(ent);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.QueueDeleteEntity(EntityUid?)" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void PredictedQueueDel(Entity<MetaDataComponent?, TransformComponent?>? ent)
|
||||
{
|
||||
EntityManager.PredictedQueueDeleteEntity(ent);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.TryQueueDeleteEntity(EntityUid?)" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool TryQueueDel(EntityUid? uid)
|
||||
@@ -812,8 +840,8 @@ public partial class EntitySystem
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnAttachedTo" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid SpawnAttachedTo(string? prototype, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
=> EntityManager.SpawnAttachedTo(prototype, coordinates, overrides);
|
||||
protected EntityUid SpawnAttachedTo(string? prototype, EntityCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default)
|
||||
=> EntityManager.SpawnAttachedTo(prototype, coordinates, overrides, rotation);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnAtPosition" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -871,6 +899,74 @@ public partial class EntitySystem
|
||||
|
||||
#endregion
|
||||
|
||||
#region PredictedSpawning
|
||||
|
||||
protected void FlagPredicted(Entity<MetaDataComponent?> ent)
|
||||
{
|
||||
EntityManager.FlagPredicted(ent);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnAttachedTo" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid PredictedSpawnAttachedTo(string? prototype, EntityCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default)
|
||||
=> EntityManager.PredictedSpawnAttachedTo(prototype, coordinates, overrides, rotation);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnAtPosition" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid PredictedSpawnAtPosition(string? prototype, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
=> EntityManager.PredictedSpawnAtPosition(prototype, coordinates, overrides);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.TrySpawnInContainer" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool PredictedTrySpawnInContainer(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
return EntityManager.PredictedTrySpawnInContainer(protoName, containerUid, containerId, out uid, containerComp, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.TrySpawnNextTo" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool PredictedTrySpawnNextTo(
|
||||
string? protoName,
|
||||
EntityUid target,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
TransformComponent? xform = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
return EntityManager.PredictedTrySpawnNextTo(protoName, target, out uid, xform, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnNextToOrDrop" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid PredictedSpawnNextToOrDrop(
|
||||
string? protoName,
|
||||
EntityUid target,
|
||||
TransformComponent? xform = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
return EntityManager.PredictedSpawnNextToOrDrop(protoName, target, xform, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnInContainerOrDrop" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid PredictedSpawnInContainerOrDrop(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
TransformComponent? xform = null,
|
||||
ContainerManagerComponent? container = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
return EntityManager.PredictedSpawnInContainerOrDrop(protoName, containerUid, containerId, xform, container, overrides);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utils
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -162,6 +162,26 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public bool IsQueuedForDeletion(EntityUid uid);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to predict entity deletion. On the server it runs the normal code path and on the client the entity is detached.
|
||||
/// </summary>
|
||||
void PredictedDeleteEntity(Entity<MetaDataComponent?, TransformComponent?> ent);
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="PredictedDeleteEntity(Robust.Shared.GameObjects.Entity{Robust.Shared.GameObjects.MetaDataComponent?,Robust.Shared.GameObjects.TransformComponent?})"/>
|
||||
/// </summary>
|
||||
void PredictedDeleteEntity(Entity<MetaDataComponent?, TransformComponent?>? ent);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to predict entity deletion. On the server it runs the normal code path and on the client the entity is detached.
|
||||
/// </summary>
|
||||
void PredictedQueueDeleteEntity(Entity<MetaDataComponent?, TransformComponent?> ent);
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="PredictedQueueDeleteEntity(Robust.Shared.GameObjects.Entity{Robust.Shared.GameObjects.MetaDataComponent?,Robust.Shared.GameObjects.TransformComponent?})"/>
|
||||
/// </summary>
|
||||
void PredictedQueueDeleteEntity(Entity<MetaDataComponent?, TransformComponent?>? ent);
|
||||
|
||||
/// <summary>
|
||||
/// Shuts-down and removes the entity with the given <see cref="Robust.Shared.GameObjects.EntityUid"/>. This is also broadcast to all clients.
|
||||
/// </summary>
|
||||
|
||||
@@ -429,7 +429,7 @@ public sealed partial class EntityLookupSystem
|
||||
float arcWidth,
|
||||
LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
var position = coordinates.ToMap(EntityManager, _transform);
|
||||
var position = _transform.ToMapCoordinates(coordinates);
|
||||
|
||||
return GetEntitiesInArc(position, range, direction, arcWidth, flags);
|
||||
}
|
||||
@@ -628,7 +628,7 @@ public sealed partial class EntityLookupSystem
|
||||
if (!coordinates.IsValid(EntityManager))
|
||||
return false;
|
||||
|
||||
var mapPos = coordinates.ToMap(EntityManager, _transform);
|
||||
var mapPos = _transform.ToMapCoordinates(coordinates);
|
||||
return AnyEntitiesIntersecting(mapPos, flags);
|
||||
}
|
||||
|
||||
@@ -637,13 +637,13 @@ public sealed partial class EntityLookupSystem
|
||||
if (!coordinates.IsValid(EntityManager))
|
||||
return false;
|
||||
|
||||
var mapPos = coordinates.ToMap(EntityManager, _transform);
|
||||
var mapPos = _transform.ToMapCoordinates(coordinates);
|
||||
return AnyEntitiesInRange(mapPos, range, flags);
|
||||
}
|
||||
|
||||
public HashSet<EntityUid> GetEntitiesIntersecting(EntityCoordinates coordinates, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
var mapPos = coordinates.ToMap(EntityManager, _transform);
|
||||
var mapPos = _transform.ToMapCoordinates(coordinates);
|
||||
return GetEntitiesIntersecting(mapPos, flags);
|
||||
}
|
||||
|
||||
@@ -656,7 +656,7 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
public void GetEntitiesInRange(EntityCoordinates coordinates, float range, HashSet<EntityUid> entities, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
var mapPos = coordinates.ToMap(EntityManager, _transform);
|
||||
var mapPos = _transform.ToMapCoordinates(coordinates);
|
||||
|
||||
if (mapPos.MapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
@@ -621,7 +621,7 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
public void GetEntitiesInRange<T>(EntityCoordinates coordinates, float range, HashSet<Entity<T>> entities, LookupFlags flags = DefaultFlags) where T : IComponent
|
||||
{
|
||||
var mapPos = coordinates.ToMap(EntityManager, _transform);
|
||||
var mapPos = _transform.ToMapCoordinates(coordinates);
|
||||
GetEntitiesInRange(mapPos, range, entities, flags);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,11 +85,11 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
private EntityQuery<BroadphaseComponent> _broadQuery;
|
||||
private EntityQuery<ContainerManagerComponent> _containerQuery;
|
||||
private EntityQuery<FixturesComponent> _fixturesQuery;
|
||||
|
||||
private EntityQuery<MapComponent> _mapQuery;
|
||||
private EntityQuery<MapGridComponent> _gridQuery;
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
private EntityQuery<PhysicsComponent> _physicsQuery;
|
||||
private EntityQuery<PhysicsMapComponent> _mapQuery;
|
||||
private EntityQuery<PhysicsMapComponent> _physMapQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
/// <summary>
|
||||
@@ -115,10 +115,11 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
_broadQuery = GetEntityQuery<BroadphaseComponent>();
|
||||
_containerQuery = GetEntityQuery<ContainerManagerComponent>();
|
||||
_fixturesQuery = GetEntityQuery<FixturesComponent>();
|
||||
_mapQuery = GetEntityQuery<MapComponent>();
|
||||
_gridQuery = GetEntityQuery<MapGridComponent>();
|
||||
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
_mapQuery = GetEntityQuery<PhysicsMapComponent>();
|
||||
_physMapQuery = GetEntityQuery<PhysicsMapComponent>();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
SubscribeLocalEvent<BroadphaseComponent, EntityTerminatingEvent>(OnBroadphaseTerminating);
|
||||
@@ -153,7 +154,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
{
|
||||
var xform = _xformQuery.GetComponent(uid);
|
||||
var map = xform.MapUid;
|
||||
_mapQuery.TryGetComponent(map, out var physMap);
|
||||
_physMapQuery.TryGetComponent(map, out var physMap);
|
||||
RemoveChildrenFromTerminatingBroadphase(xform, component, physMap);
|
||||
RemComp(uid, component);
|
||||
}
|
||||
@@ -174,12 +175,12 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
continue;
|
||||
|
||||
DebugTools.Assert(childXform.Broadphase.Value.Uid == component.Owner);
|
||||
DebugTools.Assert(!_mapManager.IsGrid(child));
|
||||
DebugTools.Assert(!_gridQuery.HasComp(child));
|
||||
|
||||
if (childXform.Broadphase.Value.CanCollide && _fixturesQuery.TryGetComponent(child, out var fixtures))
|
||||
{
|
||||
if (map == null)
|
||||
_mapQuery.TryGetComponent(childXform.Broadphase.Value.PhysicsMap, out map);
|
||||
_physMapQuery.TryGetComponent(childXform.Broadphase.Value.PhysicsMap, out map);
|
||||
|
||||
DebugTools.Assert(map == null || childXform.Broadphase.Value.PhysicsMap == map.Owner);
|
||||
var tree = childXform.Broadphase.Value.Static ? component.StaticTree : component.DynamicTree;
|
||||
@@ -226,7 +227,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
if (xform.MapUid == null)
|
||||
return;
|
||||
|
||||
if (!_mapQuery.TryGetComponent(xform.MapUid, out var physMap))
|
||||
if (!_physMapQuery.TryGetComponent(xform.MapUid, out var physMap))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Broadphase's map is missing a physics map comp. Broadphase: {ToPrettyString(broadphase.Owner)}");
|
||||
@@ -257,7 +258,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
if (!xform.Broadphase.Value.IsValid())
|
||||
return; // Entity is intentionally not on a broadphase (deferred updating?).
|
||||
|
||||
_mapQuery.TryGetComponent(xform.Broadphase.Value.PhysicsMap, out var oldPhysMap);
|
||||
_physMapQuery.TryGetComponent(xform.Broadphase.Value.PhysicsMap, out var oldPhysMap);
|
||||
if (!_broadQuery.TryGetComponent(xform.Broadphase.Value.Uid, out var oldBroadphase))
|
||||
{
|
||||
DebugTools.Assert("Encountered deleted broadphase.");
|
||||
@@ -314,7 +315,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
if (!TryGetCurrentBroadphase(xform, out var broadphase))
|
||||
return;
|
||||
|
||||
if (!_mapQuery.TryGetComponent(xform.MapUid, out var physMap))
|
||||
if (!_physMapQuery.TryGetComponent(xform.MapUid, out var physMap))
|
||||
throw new InvalidOperationException();
|
||||
|
||||
var (worldPos, worldRot) = _transform.GetWorldPositionRotation(xform);
|
||||
@@ -380,7 +381,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
|
||||
if (xform.GridUid == uid)
|
||||
return;
|
||||
DebugTools.Assert(!_mapManager.IsGrid(uid));
|
||||
DebugTools.Assert(!HasComp<MapGridComponent>(uid));
|
||||
|
||||
if (xform.Broadphase is not { Valid: true } old)
|
||||
return; // entity is not on any broadphase
|
||||
@@ -394,7 +395,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
var fixtures = Comp<FixturesComponent>(uid);
|
||||
if (old.CanCollide)
|
||||
{
|
||||
_mapQuery.TryGetComponent(old.PhysicsMap, out var physicsMap);
|
||||
_physMapQuery.TryGetComponent(old.PhysicsMap, out var physicsMap);
|
||||
RemoveBroadTree(broadphase, fixtures, old.Static, physicsMap);
|
||||
}
|
||||
else
|
||||
@@ -437,7 +438,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
if (broadphaseXform.MapID == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
if (!_mapQuery.TryGetComponent(broadphaseXform.MapUid, out var physMap))
|
||||
if (!_physMapQuery.TryGetComponent(broadphaseXform.MapUid, out var physMap))
|
||||
throw new InvalidOperationException($"Physics Broadphase is missing physics map. {ToPrettyString(broadUid)}");
|
||||
|
||||
AddOrUpdatePhysicsTree(uid, broadUid, broadphase, broadphaseXform, physMap, xform, body, fixtures);
|
||||
@@ -525,7 +526,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
|
||||
private void OnEntityInit(Entity<MetaDataComponent> uid)
|
||||
{
|
||||
if (_container.IsEntityOrParentInContainer(uid, uid) || _mapManager.IsMap(uid) || _mapManager.IsGrid(uid))
|
||||
if (_container.IsEntityOrParentInContainer(uid, uid) || _mapQuery.HasComp(uid) || _gridQuery.HasComp(uid))
|
||||
return;
|
||||
|
||||
// TODO can this just be done implicitly via transform startup?
|
||||
@@ -541,11 +542,11 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
OnGridChangedMap(args);
|
||||
return;
|
||||
}
|
||||
DebugTools.Assert(!_mapManager.IsGrid(args.Sender));
|
||||
DebugTools.Assert(!_gridQuery.HasComp(args.Sender));
|
||||
|
||||
if (args.Component.MapUid == args.Sender)
|
||||
return;
|
||||
DebugTools.Assert(!_mapManager.IsMap(args.Sender));
|
||||
DebugTools.Assert(!_mapQuery.HasComp(args.Sender));
|
||||
|
||||
if (args.ParentChanged)
|
||||
UpdateParent(args.Sender, args.Component);
|
||||
@@ -562,12 +563,12 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
return;
|
||||
|
||||
// We need to recursively update the cached data and remove children from the move buffer
|
||||
DebugTools.Assert(HasComp<MapGridComponent>(args.Sender));
|
||||
DebugTools.Assert(!newMap.IsValid() || HasComp<MapComponent>(newMap));
|
||||
DebugTools.Assert(!oldMap.IsValid() || HasComp<MapComponent>(oldMap));
|
||||
DebugTools.Assert(_gridQuery.HasComp(args.Sender));
|
||||
DebugTools.Assert(!newMap.IsValid() || _mapQuery.HasComp(newMap));
|
||||
DebugTools.Assert(!oldMap.IsValid() || _mapQuery.HasComp(oldMap));
|
||||
|
||||
var oldBuffer = _mapQuery.CompOrNull(oldMap)?.MoveBuffer;
|
||||
var newBuffer = _mapQuery.CompOrNull(newMap)?.MoveBuffer;
|
||||
var oldBuffer = _physMapQuery.CompOrNull(oldMap)?.MoveBuffer;
|
||||
var newBuffer = _physMapQuery.CompOrNull(newMap)?.MoveBuffer;
|
||||
|
||||
foreach (var child in args.Component._children)
|
||||
{
|
||||
@@ -635,7 +636,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
if (!xform.Broadphase.Value.IsValid())
|
||||
return; // Entity is intentionally not on a broadphase (deferred updating?).
|
||||
|
||||
_mapQuery.TryGetComponent(xform.Broadphase.Value.PhysicsMap, out oldPhysMap);
|
||||
_physMapQuery.TryGetComponent(xform.Broadphase.Value.PhysicsMap, out oldPhysMap);
|
||||
|
||||
if (!_broadQuery.TryGetComponent(xform.Broadphase.Value.Uid, out oldBroadphase))
|
||||
{
|
||||
@@ -667,7 +668,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
return;
|
||||
|
||||
var newBroadphaseXform = _xformQuery.GetComponent(newBroadphase.Owner);
|
||||
if (!_mapQuery.TryGetComponent(newBroadphaseXform.MapUid, out var physMap))
|
||||
if (!_physMapQuery.TryGetComponent(newBroadphaseXform.MapUid, out var physMap))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Broadphase's map is missing a physics map comp. Broadphase: {ToPrettyString(newBroadphase.Owner)}");
|
||||
@@ -712,7 +713,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
bool recursive = true)
|
||||
{
|
||||
var broadphaseXform = _xformQuery.GetComponent(broadphase.Owner);
|
||||
if (!_mapQuery.TryGetComponent(broadphaseXform.MapUid, out var physMap))
|
||||
if (!_physMapQuery.TryGetComponent(broadphaseXform.MapUid, out var physMap))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Broadphase's map is missing a physics map comp. Broadphase: {ToPrettyString(broadphase.Owner)}");
|
||||
@@ -793,10 +794,10 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
if (!TryGetCurrentBroadphase(xform, out var broadphase))
|
||||
return;
|
||||
|
||||
DebugTools.Assert(!HasComp<MapGridComponent>(uid));
|
||||
DebugTools.Assert(!HasComp<MapComponent>(uid));
|
||||
DebugTools.Assert(!_gridQuery.HasComp(uid));
|
||||
DebugTools.Assert(!_mapQuery.HasComp(uid));
|
||||
PhysicsMapComponent? physMap = null;
|
||||
if (xform.Broadphase!.Value.PhysicsMap is { Valid: true } map && !_mapQuery.TryGetComponent(map, out physMap))
|
||||
if (xform.Broadphase!.Value.PhysicsMap is { Valid: true } map && !_physMapQuery.TryGetComponent(map, out physMap))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Broadphase's map is missing a physics map comp. Broadphase: {ToPrettyString(broadphase.Owner)}");
|
||||
@@ -834,7 +835,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
|
||||
if (old.PhysicsMap.IsValid() && physicsMap?.Owner != old.PhysicsMap)
|
||||
{
|
||||
if (!_mapQuery.TryGetComponent(old.PhysicsMap, out physicsMap))
|
||||
if (!_physMapQuery.TryGetComponent(old.PhysicsMap, out physicsMap))
|
||||
Log.Error($"Entity {ToPrettyString(uid)} has missing physics map?");
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Robust.Shared.GameObjects;
|
||||
public abstract class SharedEyeSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedViewSubscriberSystem _views = default!;
|
||||
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Map.Events;
|
||||
@@ -16,7 +16,6 @@ using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
@@ -150,7 +149,8 @@ namespace Robust.Shared.GameObjects
|
||||
};
|
||||
#pragma warning restore CS0618
|
||||
|
||||
newFixtures.Add(($"grid_chunk-{bounds.Left}-{bounds.Bottom}", newFixture));
|
||||
var key = string.Create(CultureInfo.InvariantCulture, $"grid_chunk-{bounds.Left}-{bounds.Bottom}");
|
||||
newFixtures.Add((key, newFixture));
|
||||
}
|
||||
|
||||
// Check if we even need to issue an eventbus event
|
||||
|
||||
@@ -23,7 +23,7 @@ public abstract partial class SharedMapSystem
|
||||
}
|
||||
|
||||
// Check if mappos intersects a grid.
|
||||
var mapPos = coordinates.ToMap(EntityManager, _transform);
|
||||
var mapPos = _transform.ToMapCoordinates(coordinates);
|
||||
|
||||
if (_mapInternal.TryFindGridAt(mapPos, out var gridUid, out gridComponent))
|
||||
{
|
||||
|
||||
@@ -1222,7 +1222,7 @@ public abstract partial class SharedMapSystem
|
||||
{
|
||||
#if DEBUG
|
||||
var mapId = _xformQuery.GetComponent(uid).MapID;
|
||||
DebugTools.Assert(mapId == coords.GetMapId(EntityManager));
|
||||
DebugTools.Assert(mapId == _transform.GetMapId(coords));
|
||||
#endif
|
||||
|
||||
return SnapGridLocalCellFor(uid, grid, LocalToGrid(uid, grid, coords));
|
||||
@@ -1432,7 +1432,7 @@ public abstract partial class SharedMapSystem
|
||||
{
|
||||
#if DEBUG
|
||||
var mapId = _xformQuery.GetComponent(uid).MapID;
|
||||
DebugTools.Assert(mapId == coords.GetMapId(EntityManager));
|
||||
DebugTools.Assert(mapId == _transform.GetMapId(coords));
|
||||
#endif
|
||||
var local = LocalToGrid(uid, grid, coords);
|
||||
|
||||
@@ -1454,7 +1454,7 @@ public abstract partial class SharedMapSystem
|
||||
{
|
||||
return position.EntityId == uid
|
||||
? position.Position
|
||||
: WorldToLocal(uid, grid, position.ToMapPos(EntityManager, _transform));
|
||||
: WorldToLocal(uid, grid, _transform.ToMapCoordinates(position).Position);
|
||||
}
|
||||
|
||||
public bool CollidesWithGrid(EntityUid uid, MapGridComponent grid, Vector2i indices)
|
||||
|
||||
@@ -5,7 +5,6 @@ using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Map.Components;
|
||||
@@ -1361,7 +1360,7 @@ public abstract partial class SharedTransformSystem
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_mapManager.IsMap(uid))
|
||||
if (!_mapQuery.HasComp(uid))
|
||||
Log.Warning($"Failed to attach entity to map or grid. Entity: ({ToPrettyString(uid)}). Trace: {Environment.StackTrace}");
|
||||
|
||||
DetachEntity(uid, xform);
|
||||
|
||||
@@ -133,7 +133,7 @@ namespace Robust.Shared.GameObjects
|
||||
if (xform.GridUid == xform.ParentUid)
|
||||
return xform.Coordinates;
|
||||
|
||||
DebugTools.Assert(!_mapManager.IsGrid(uid) && !_mapManager.IsMap(uid));
|
||||
DebugTools.Assert(!_gridQuery.HasComp(uid) && !_mapQuery.HasComp(uid));
|
||||
|
||||
// Not parented to grid so convert their pos back to the grid.
|
||||
var worldPos = GetWorldPosition(xform, XformQuery);
|
||||
@@ -174,7 +174,7 @@ namespace Robust.Shared.GameObjects
|
||||
if (mapId == parentUid)
|
||||
return coordinates;
|
||||
|
||||
DebugTools.Assert(!_mapManager.IsGrid(parentUid) && !_mapManager.IsMap(parentUid));
|
||||
DebugTools.Assert(!HasComp<MapGridComponent>(parentUid) && !HasComp<MapComponent>(parentUid));
|
||||
|
||||
// Not parented to grid so convert their pos back to the grid.
|
||||
var worldPos = Vector2.Transform(coordinates.Position, GetWorldMatrix(parentXform, XformQuery));
|
||||
|
||||
@@ -29,11 +29,9 @@ public sealed class FixtureSerializer : ITypeSerializer<Dictionary<string, Fixtu
|
||||
|
||||
foreach (var subNode in node)
|
||||
{
|
||||
var key = (ValueDataNode)subNode.Key;
|
||||
|
||||
if (!keys.Add(key.Value))
|
||||
if (!keys.Add(subNode.Key))
|
||||
{
|
||||
seq.Add(new ErrorNode(subNode.Key, $"Found duplicate fixture ID {key.Value}"));
|
||||
seq.Add(new ErrorNode(new ValueDataNode(subNode.Key), $"Found duplicate fixture ID {subNode.Key}"));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -50,10 +48,8 @@ public sealed class FixtureSerializer : ITypeSerializer<Dictionary<string, Fixtu
|
||||
|
||||
foreach (var subNode in node)
|
||||
{
|
||||
var key = (ValueDataNode)subNode.Key;
|
||||
|
||||
var fixture = serializationManager.Read<Fixture>(subNode.Value, hookCtx, context, notNullableOverride: true);
|
||||
value.Add(key.Value, fixture);
|
||||
value.Add(subNode.Key, fixture);
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
public sealed partial class FixtureSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly SharedBroadphaseSystem _broadphase = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
private EntityQuery<PhysicsMapComponent> _mapQuery;
|
||||
@@ -239,25 +240,31 @@ namespace Robust.Shared.Physics.Systems
|
||||
|
||||
private void OnGetState(EntityUid uid, FixturesComponent component, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new FixtureManagerComponentState
|
||||
var copied = new Dictionary<string, Fixture>(component.Fixtures.Count);
|
||||
|
||||
foreach (var (id, fixture) in component.Fixtures)
|
||||
{
|
||||
Fixtures = component.Fixtures,
|
||||
var copy = new Fixture();
|
||||
fixture.CopyTo(copy);
|
||||
copied[id] = copy;
|
||||
}
|
||||
|
||||
args.State = new FixtureManagerComponentState()
|
||||
{
|
||||
Fixtures = copied,
|
||||
};
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid uid, FixturesComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not FixtureManagerComponentState state) return;
|
||||
if (args.Current is not FixtureManagerComponentState state)
|
||||
return;
|
||||
|
||||
if (!EntityManager.TryGetComponent(uid, out PhysicsComponent? physics))
|
||||
{
|
||||
Log.Error($"Tried to apply fixture state for an entity without physics: {ToPrettyString(uid)}");
|
||||
return;
|
||||
}
|
||||
foreach (var fixture in component.Fixtures.Values)
|
||||
{
|
||||
fixture.Owner = uid;
|
||||
}
|
||||
|
||||
var toAddFixtures = new ValueList<(string Id, Fixture Fixture)>();
|
||||
var toRemoveFixtures = new ValueList<(string Id, Fixture Fixture)>();
|
||||
@@ -275,6 +282,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
}
|
||||
|
||||
TransformComponent? xform = null;
|
||||
var regenerate = false;
|
||||
|
||||
// Add / update new fixtures
|
||||
// FUTURE SLOTH
|
||||
@@ -287,10 +295,12 @@ namespace Robust.Shared.Physics.Systems
|
||||
{
|
||||
toAddFixtures.Add((id, fixture));
|
||||
}
|
||||
// Retained fixture but new data
|
||||
else if (!existing.Equivalent(fixture))
|
||||
{
|
||||
toRemoveFixtures.Add((id, existing));
|
||||
toAddFixtures.Add((id, fixture));
|
||||
fixture.CopyTo(existing);
|
||||
computeProperties = true;
|
||||
regenerate = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,6 +334,11 @@ namespace Robust.Shared.Physics.Systems
|
||||
{
|
||||
FixtureUpdate(uid, manager: component, body: physics);
|
||||
}
|
||||
|
||||
if (regenerate)
|
||||
{
|
||||
_broadphase.RegenerateContacts((uid, physics, component, xform));
|
||||
}
|
||||
}
|
||||
|
||||
#region Restitution
|
||||
|
||||
@@ -24,7 +24,6 @@ public abstract partial class SharedJointSystem : EntitySystem
|
||||
private EntityQuery<JointComponent> _jointsQuery;
|
||||
private EntityQuery<PhysicsComponent> _physicsQuery;
|
||||
private EntityQuery<JointRelayTargetComponent> _relayQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
// To avoid issues with component states we'll queue up all dirty joints and check it every tick to see if
|
||||
// we can delete the component.
|
||||
@@ -38,7 +37,6 @@ public abstract partial class SharedJointSystem : EntitySystem
|
||||
|
||||
_jointsQuery = GetEntityQuery<JointComponent>();
|
||||
_relayQuery = GetEntityQuery<JointRelayTargetComponent>();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
UpdatesOutsidePrediction = true;
|
||||
|
||||
@@ -348,7 +346,7 @@ public abstract partial class SharedJointSystem : EntitySystem
|
||||
if (!Resolve(uid, ref xform))
|
||||
return Vector2.Zero;
|
||||
|
||||
return Physics.Transform.MulT(new Quaternion2D((float) xform.WorldRotation.Theta), worldVector);
|
||||
return Physics.Transform.MulT(new Quaternion2D((float) _transform.GetWorldRotation(xform).Theta), worldVector);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -30,6 +30,7 @@ using Robust.Shared.Collections;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
@@ -535,7 +536,7 @@ public partial class SharedPhysicsSystem
|
||||
DirtyFields(uid, body, null, nameof(PhysicsComponent.Force), nameof(PhysicsComponent.Torque));
|
||||
}
|
||||
|
||||
_broadphase.RegenerateContacts(uid, body, manager, xform);
|
||||
_broadphase.RegenerateContacts((uid, body, manager, xform));
|
||||
|
||||
if (body.Initialized)
|
||||
{
|
||||
@@ -581,13 +582,13 @@ public partial class SharedPhysicsSystem
|
||||
if (_containerSystem.IsEntityOrParentInContainer(uid))
|
||||
return false;
|
||||
|
||||
if (!_fixturesQuery.Resolve(uid, ref manager) || manager.FixtureCount == 0 && !_mapManager.IsGrid(uid))
|
||||
if (!_fixturesQuery.Resolve(uid, ref manager) || manager.FixtureCount == 0 && !_gridQuery.HasComp(uid))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugTools.Assert(!_containerSystem.IsEntityOrParentInContainer(uid));
|
||||
DebugTools.Assert((Resolve(uid, ref manager) && manager.FixtureCount > 0) || _mapManager.IsGrid(uid));
|
||||
DebugTools.Assert((Resolve(uid, ref manager) && manager.FixtureCount > 0) || _gridQuery.HasComp(uid));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,12 +30,12 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Collision;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
@@ -424,6 +424,12 @@ public abstract partial class SharedPhysicsSystem
|
||||
var xformA = _xformQuery.GetComponent(uidA);
|
||||
var xformB = _xformQuery.GetComponent(uidB);
|
||||
|
||||
if (xformA.MapID == MapId.Nullspace || xformB.MapID == MapId.Nullspace)
|
||||
{
|
||||
DestroyContact(contact);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is this contact flagged for filtering?
|
||||
if ((contact.Flags & ContactFlags.Filter) != 0x0)
|
||||
{
|
||||
@@ -661,8 +667,8 @@ public abstract partial class SharedPhysicsSystem
|
||||
var aUid = contact.EntityA;
|
||||
var bUid = contact.EntityB;
|
||||
|
||||
SetAwake(aUid, bodyA, true);
|
||||
SetAwake(bUid, bodyB, true);
|
||||
SetAwake((aUid, bodyA), true);
|
||||
SetAwake((bUid, bodyB), true);
|
||||
}
|
||||
|
||||
ArrayPool<bool>.Shared.Return(wake);
|
||||
@@ -788,7 +794,7 @@ public abstract partial class SharedPhysicsSystem
|
||||
if (!PhysicsQuery.Resolve(entity.Owner, ref entity.Comp))
|
||||
return;
|
||||
|
||||
_broadphase.RegenerateContacts(entity.Owner, entity.Comp);
|
||||
_broadphase.RegenerateContacts(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1045,7 +1045,10 @@ public abstract partial class SharedPhysicsSystem
|
||||
// May reparent object and change body's velocity.
|
||||
if (!float.IsNaN(position.X) && !float.IsNaN(position.Y))
|
||||
{
|
||||
_transform.SetLocalPositionRotation(xform, xform.LocalPosition + position, xform.LocalRotation + angle);
|
||||
_transform.SetLocalPositionRotation(uid,
|
||||
xform.LocalPosition + position,
|
||||
xform.LocalRotation + angle,
|
||||
xform);
|
||||
}
|
||||
|
||||
if (physicsDirtied)
|
||||
|
||||
@@ -137,6 +137,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
/// <summary>
|
||||
/// Get all entities colliding with a certain body.
|
||||
/// </summary>
|
||||
[Obsolete("Use EntityLookupSystem")]
|
||||
public IEnumerable<PhysicsComponent> GetCollidingEntities(MapId mapId, in Box2 worldAABB)
|
||||
{
|
||||
if (mapId == MapId.Nullspace) return Array.Empty<PhysicsComponent>();
|
||||
@@ -169,6 +170,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
/// <summary>
|
||||
/// Get all entities colliding with a certain body.
|
||||
/// </summary>
|
||||
[Obsolete("Use EntityLookupSystem")]
|
||||
public IEnumerable<Entity<PhysicsComponent>> GetCollidingEntities(MapId mapId, in Box2Rotated worldBounds)
|
||||
{
|
||||
if (mapId == MapId.Nullspace)
|
||||
|
||||
@@ -45,7 +45,7 @@ public abstract partial class SharedPhysicsSystem
|
||||
if (!coordinates.IsValid(EntityManager))
|
||||
return Vector2.Zero;
|
||||
|
||||
var mapUid = coordinates.GetMapUid(EntityManager);
|
||||
var mapUid = _transform.GetMap(coordinates);
|
||||
var parent = coordinates.EntityId;
|
||||
var localPos = coordinates.Position;
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ namespace Robust.Shared.Physics.Systems
|
||||
});
|
||||
|
||||
[Dependency] private readonly IManifoldManager _manifoldManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IParallelManager _parallel = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IDependencyCollection _deps = default!;
|
||||
@@ -66,11 +65,12 @@ namespace Robust.Shared.Physics.Systems
|
||||
|
||||
public bool MetricsEnabled { get; protected set; }
|
||||
|
||||
private EntityQuery<FixturesComponent> _fixturesQuery;
|
||||
private EntityQuery<FixturesComponent> _fixturesQuery;
|
||||
protected EntityQuery<PhysicsComponent> PhysicsQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
private EntityQuery<CollideOnAnchorComponent> _anchorQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
private EntityQuery<CollideOnAnchorComponent> _anchorQuery;
|
||||
protected EntityQuery<PhysicsMapComponent> PhysMapQuery;
|
||||
private EntityQuery<MapGridComponent> _gridQuery;
|
||||
protected EntityQuery<MapComponent> MapQuery;
|
||||
|
||||
private ComponentRegistration _physicsReg = default!;
|
||||
@@ -111,6 +111,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
_anchorQuery = GetEntityQuery<CollideOnAnchorComponent>();
|
||||
PhysMapQuery = GetEntityQuery<PhysicsMapComponent>();
|
||||
_gridQuery = GetEntityQuery<MapGridComponent>();
|
||||
MapQuery = GetEntityQuery<MapComponent>();
|
||||
|
||||
SubscribeLocalEvent<GridAddEvent>(OnGridAdd);
|
||||
|
||||
@@ -342,8 +342,7 @@ public partial class PrototypeManager
|
||||
{
|
||||
if (abstractNode is not ValueDataNode abstractValueNode)
|
||||
{
|
||||
mapping.Remove(abstractNode);
|
||||
mapping.Add("abstract", "true");
|
||||
mapping["abstract"] = new ValueDataNode("true");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ public sealed class ReplayData
|
||||
ClientSideRecording = clientSideRecording;
|
||||
YamlData = yamlData;
|
||||
|
||||
if (YamlData.TryGet(new ValueDataNode(ReplayConstants.MetaKeyRecordedBy), out ValueDataNode? node)
|
||||
if (YamlData.TryGet(ReplayConstants.MetaKeyRecordedBy, out ValueDataNode? node)
|
||||
&& Guid.TryParse(node.Value, out var guid))
|
||||
{
|
||||
Recorder = new NetUserId(guid);
|
||||
|
||||
@@ -266,27 +266,21 @@ namespace Robust.Shared.Serialization.Manager.Definition
|
||||
|
||||
foreach (var (key, val) in mapping.Children)
|
||||
{
|
||||
if (key is not ValueDataNode valueDataNode)
|
||||
if (!TryGetIndex(key, out var idx))
|
||||
{
|
||||
validatedMapping.Add(new ErrorNode(key, "Key not ValueDataNode."), new InconclusiveNode(val));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TryGetIndex(valueDataNode.Value, out var idx))
|
||||
{
|
||||
if (TryGetIncludeMappingPair(includeValidations, valueDataNode.Value, out var validatedNotFoundPair))
|
||||
if (TryGetIncludeMappingPair(includeValidations, key, out var validatedNotFoundPair))
|
||||
{
|
||||
validatedMapping.Add(validatedNotFoundPair.Key, validatedNotFoundPair.Value);
|
||||
continue;
|
||||
}
|
||||
|
||||
var error = new FieldNotFoundErrorNode(valueDataNode, typeof(T));
|
||||
var error = new FieldNotFoundErrorNode(mapping.GetKeyNode(key), typeof(T));
|
||||
|
||||
validatedMapping.Add(error, new InconclusiveNode(val));
|
||||
continue;
|
||||
}
|
||||
|
||||
var keyValidated = serialization.ValidateNode<string>(key, context);
|
||||
var keyValidated = serialization.ValidateNode<string>(mapping.GetKeyNode(key), context);
|
||||
|
||||
ValidationNode valNode;
|
||||
if (IsNull(val))
|
||||
@@ -295,7 +289,7 @@ namespace Robust.Shared.Serialization.Manager.Definition
|
||||
{
|
||||
var error = new ErrorNode(
|
||||
val,
|
||||
$"Field \"{valueDataNode.Value}\" had null value despite not being annotated as nullable.");
|
||||
$"Field \"{key}\" had null value despite not being annotated as nullable.");
|
||||
|
||||
validatedMapping.Add(keyValidated, error);
|
||||
continue;
|
||||
@@ -309,7 +303,7 @@ namespace Robust.Shared.Serialization.Manager.Definition
|
||||
}
|
||||
|
||||
//include node errors override successful nodes on the main datadef
|
||||
if (valNode is not ErrorNode && TryGetIncludeMappingPair(includeValidations, valueDataNode.Value, out var validatedPair))
|
||||
if (valNode is not ErrorNode && TryGetIncludeMappingPair(includeValidations, key, out var validatedPair))
|
||||
{
|
||||
if (validatedPair.Value is ErrorNode)
|
||||
{
|
||||
|
||||
@@ -190,7 +190,8 @@ public partial class SerializationManager
|
||||
{
|
||||
// tag is set on data definition creation
|
||||
if(!processedTags.Add(dfa.Tag!)) continue; //tag was already processed, probably because we are using the same tag in an include
|
||||
var key = new ValueDataNode(dfa.Tag);
|
||||
|
||||
var key = dfa.Tag!;
|
||||
if (parent.TryGetValue(key, out var parentValue))
|
||||
{
|
||||
if (newMapping.TryGetValue(key, out var childValue))
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using YamlDotNet.Core;
|
||||
@@ -28,6 +29,7 @@ namespace Robust.Shared.Serialization.Markdown
|
||||
/// </summary>
|
||||
public abstract DataNode? Except(DataNode node);
|
||||
|
||||
[Obsolete("Use SerializationManager.PushComposition()")]
|
||||
public abstract DataNode PushInheritance(DataNode parent);
|
||||
|
||||
public T CopyCast<T>() where T : DataNode
|
||||
@@ -60,6 +62,7 @@ namespace Robust.Shared.Serialization.Markdown
|
||||
|
||||
public abstract T? Except(T node);
|
||||
|
||||
[Obsolete("Use SerializationManager.PushComposition()")]
|
||||
public abstract T PushInheritance(T node);
|
||||
|
||||
public override DataNode? Except(DataNode node)
|
||||
@@ -67,6 +70,7 @@ namespace Robust.Shared.Serialization.Markdown
|
||||
return node is not T tNode ? throw new InvalidNodeTypeException() : Except(tNode);
|
||||
}
|
||||
|
||||
[Obsolete("Use SerializationManager.PushComposition()")]
|
||||
public override DataNode PushInheritance(DataNode parent)
|
||||
{
|
||||
return parent is not T tNode ? throw new InvalidNodeTypeException() : PushInheritance(tNode);
|
||||
|
||||
@@ -25,10 +25,7 @@ public static class DataNodeHelpers
|
||||
|
||||
foreach (var (k, v) in node)
|
||||
{
|
||||
foreach (var child in GetAllNodes(k))
|
||||
{
|
||||
yield return child;
|
||||
}
|
||||
yield return node.GetKeyNode(k);
|
||||
|
||||
foreach (var child in GetAllNodes(v))
|
||||
{
|
||||
|
||||
@@ -85,6 +85,16 @@ public static class DataNodeParser
|
||||
return node;
|
||||
}
|
||||
|
||||
private static string ParseKey(Parser parser)
|
||||
{
|
||||
var ev = parser.Consume<Scalar>();
|
||||
|
||||
if (!ev.Anchor.IsEmpty)
|
||||
throw new NotSupportedException();
|
||||
|
||||
return ev.Value;
|
||||
}
|
||||
|
||||
private static SequenceDataNode ParseSequence(Parser parser, DocumentState state)
|
||||
{
|
||||
var ev = parser.Consume<SequenceStart>();
|
||||
@@ -124,12 +134,11 @@ public static class DataNodeParser
|
||||
MappingEnd mapEnd;
|
||||
while (!parser.TryConsume(out mapEnd))
|
||||
{
|
||||
var key = Parse(parser, state);
|
||||
var key = ParseKey(parser);
|
||||
var value = Parse(parser, state);
|
||||
|
||||
node.Add(key, value);
|
||||
|
||||
unresolvedAlias |= key is DataNodeAlias;
|
||||
unresolvedAlias |= value is DataNodeAlias;
|
||||
}
|
||||
|
||||
@@ -173,17 +182,16 @@ public static class DataNodeParser
|
||||
|
||||
private static void ResolveMappingAliases(MappingDataNode mapping, DocumentState state)
|
||||
{
|
||||
var swaps = new ValueList<(DataNode key, DataNode value)>();
|
||||
var swaps = new ValueList<(string key, DataNode value)>();
|
||||
|
||||
foreach (var (key, value) in mapping)
|
||||
{
|
||||
if (key is not DataNodeAlias && value is not DataNodeAlias)
|
||||
if (value is not DataNodeAlias valueAlias)
|
||||
return;
|
||||
|
||||
var newKey = key is DataNodeAlias keyAlias ? ResolveAlias(keyAlias, state) : key;
|
||||
var newValue = value is DataNodeAlias valueAlias ? ResolveAlias(valueAlias, state) : value;
|
||||
var newValue = ResolveAlias(valueAlias, state);
|
||||
|
||||
swaps.Add((newKey, newValue));
|
||||
swaps.Add((key, newValue));
|
||||
mapping.Remove(key);
|
||||
}
|
||||
|
||||
@@ -242,6 +250,7 @@ public static class DataNodeParser
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
[Obsolete("Use SerializationManager.PushComposition()")]
|
||||
public override DataNode PushInheritance(DataNode parent)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
|
||||
@@ -5,25 +5,30 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using Robust.Shared.Serialization.Markdown.Value;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
{
|
||||
public sealed class MappingDataNode : DataNode<MappingDataNode>, IDictionary<DataNode, DataNode>
|
||||
public sealed class MappingDataNode : DataNode<MappingDataNode>, IDictionary<string, DataNode>
|
||||
{
|
||||
// To fetch nodes by key name with YAML, we NEED a YamlScalarNode.
|
||||
// We use a thread local one to avoid allocating one every fetch, since we just replace the inner value.
|
||||
// Obviously thread local to avoid threading issues.
|
||||
private static readonly ThreadLocal<ValueDataNode> FetchNode =
|
||||
new(() => new ValueDataNode(""));
|
||||
private readonly Dictionary<string, DataNode> _children;
|
||||
private readonly List<KeyValuePair<string,DataNode>> _list;
|
||||
|
||||
private readonly Dictionary<DataNode, DataNode> _children;
|
||||
private readonly List<KeyValuePair<DataNode,DataNode>> _list;
|
||||
/// <summary>
|
||||
/// ValueDataNodes associated with each key. This is used for yaml validation / error reporting.
|
||||
/// I.e., if a key is meant to be an EntityPrototype ID, we want to print an error that points to the
|
||||
/// corresponding yaml lines.
|
||||
/// </summary>
|
||||
private IReadOnlyDictionary<string, ValueDataNode>? _keyNodes;
|
||||
// TODO avoid populating this unless we are running the yaml linter?
|
||||
|
||||
public IReadOnlyDictionary<DataNode, DataNode> Children => _children;
|
||||
public override bool IsEmpty => _children.Count == 0;
|
||||
public int Count => _children.Count;
|
||||
public bool IsReadOnly => false;
|
||||
public IReadOnlyDictionary<string, DataNode> Children => _children;
|
||||
|
||||
public MappingDataNode() : base(NodeMark.Invalid, NodeMark.Invalid)
|
||||
{
|
||||
@@ -41,113 +46,106 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
{
|
||||
_children = new(mapping.Children.Count);
|
||||
_list = new(mapping.Children.Count);
|
||||
foreach (var (key, val) in mapping.Children)
|
||||
var keyNodes = new Dictionary<string, ValueDataNode>(mapping.Children.Count);
|
||||
foreach (var (keyNode, val) in mapping.Children)
|
||||
{
|
||||
Add(key.ToDataNode(), val.ToDataNode());
|
||||
if (keyNode is not YamlScalarNode scalarNode)
|
||||
throw new NotSupportedException("Mapping data nodes must have a scalar keys");
|
||||
|
||||
var valueNode = new ValueDataNode(scalarNode);
|
||||
Add(valueNode.Value, val.ToDataNode());
|
||||
keyNodes.Add(valueNode.Value, valueNode);
|
||||
}
|
||||
|
||||
_keyNodes = keyNodes;
|
||||
Tag = mapping.Tag.IsEmpty ? null : mapping.Tag.Value;
|
||||
}
|
||||
|
||||
public MappingDataNode(Dictionary<DataNode, DataNode> nodes) : base(NodeMark.Invalid, NodeMark.Invalid)
|
||||
public MappingDataNode(Dictionary<string, DataNode> nodes) : base(NodeMark.Invalid, NodeMark.Invalid)
|
||||
{
|
||||
_children = new(nodes.Count);
|
||||
_list = new(nodes.Count);
|
||||
foreach (var (key, val) in nodes)
|
||||
{
|
||||
Add(key, val);
|
||||
}
|
||||
_children = new(nodes);
|
||||
_list = new(_children);
|
||||
}
|
||||
|
||||
public KeyValuePair<DataNode, DataNode> this[int key] => _list[key];
|
||||
|
||||
public DataNode this[string index]
|
||||
{
|
||||
get => Get(index);
|
||||
set => Add(index, value);
|
||||
}
|
||||
public KeyValuePair<string, DataNode> this[int key] => _list[key];
|
||||
|
||||
public MappingDataNode Add(string key, DataNode node)
|
||||
{
|
||||
Add(new ValueDataNode(key), node);
|
||||
return this;
|
||||
}
|
||||
|
||||
private static ValueDataNode GetFetchNode(string key)
|
||||
{
|
||||
var node = FetchNode.Value!;
|
||||
node.Value = key;
|
||||
return node;
|
||||
}
|
||||
|
||||
public MappingDataNode Add(DataNode key, DataNode node)
|
||||
{
|
||||
_children.Add(key, node);
|
||||
_list.Add(new(key, node));
|
||||
return this;
|
||||
}
|
||||
|
||||
public DataNode this[DataNode key]
|
||||
public DataNode this[string key]
|
||||
{
|
||||
get => _children[key];
|
||||
set
|
||||
{
|
||||
if (_children.TryAdd(key, value))
|
||||
{
|
||||
_list.Add(new( key, value));
|
||||
_list.Add(new(key, value));
|
||||
return;
|
||||
}
|
||||
|
||||
var i = _list.IndexOf(new(key, _children[key]));
|
||||
_list[i] = new(key, value);
|
||||
var index = IndexOf(key);
|
||||
if (index == -1)
|
||||
throw new Exception("Key exists in Children, but not list?");
|
||||
|
||||
_list[index] = new(key, value);
|
||||
_children[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void IDictionary<DataNode, DataNode>.Add(DataNode key, DataNode value) => Add(key, value);
|
||||
public int IndexOf(string key)
|
||||
{
|
||||
// TODO MappingDataNode
|
||||
// Consider having a Dictionary<string,int> for faster lookups?
|
||||
// IndexOf() gets called in Remove(), which itself gets called frequently (e.g., per serialized component,
|
||||
// per entity, when loading a map.
|
||||
//
|
||||
// Then again, if most mappings only contain 1-4 entries, this list search is comparable in speed, reduces
|
||||
// allocations, and makes adding/inserting entries faster.
|
||||
|
||||
public bool ContainsKey(DataNode key) => _children.ContainsKey(key);
|
||||
for (var index = 0; index < _list.Count; index++)
|
||||
{
|
||||
if (_list[index].Key == key)
|
||||
return index;
|
||||
}
|
||||
|
||||
bool IDictionary<DataNode, DataNode>.Remove(DataNode key)
|
||||
=> ((IDictionary<DataNode, DataNode>)this).Remove(key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
public bool TryGetValue(DataNode key, [NotNullWhen(true)] out DataNode? value) => TryGet(key, out value);
|
||||
void IDictionary<string, DataNode>.Add(string key, DataNode value) => Add(key, value);
|
||||
|
||||
public ICollection<DataNode> Keys => _list.Select(x => x.Key).ToArray();
|
||||
public bool ContainsKey(string key) => _children.ContainsKey(key);
|
||||
|
||||
bool IDictionary<string, DataNode>.Remove(string key)
|
||||
=> ((IDictionary<string, DataNode>)this).Remove(key);
|
||||
|
||||
public bool TryGetValue(string key, [NotNullWhen(true)] out DataNode? value)
|
||||
=> TryGet(key, out value);
|
||||
|
||||
// TODO consider changing these to unsorted collections
|
||||
// I.e., just redirect to _children.Keys to avoid hidden linq & allocations.
|
||||
public ICollection<string> Keys => _list.Select(x => x.Key).ToArray();
|
||||
public ICollection<DataNode> Values => _list.Select(x => x.Value).ToArray();
|
||||
|
||||
public DataNode Get(DataNode key)
|
||||
public DataNode Get(string key)
|
||||
{
|
||||
return _children[key];
|
||||
}
|
||||
|
||||
public T Get<T>(DataNode key) where T : DataNode
|
||||
public T Get<T>(string key) where T : DataNode
|
||||
{
|
||||
return (T) Get(key);
|
||||
}
|
||||
|
||||
public DataNode Get(string key)
|
||||
public bool TryGet(string key, [NotNullWhen(true)] out DataNode? node)
|
||||
{
|
||||
return Get(GetFetchNode(key));
|
||||
return _children.TryGetValue(key, out node);
|
||||
}
|
||||
|
||||
public T Get<T>(string key) where T : DataNode
|
||||
{
|
||||
return Get<T>(GetFetchNode(key));
|
||||
}
|
||||
|
||||
public bool TryGet(DataNode key, [NotNullWhen(true)] out DataNode? node)
|
||||
{
|
||||
if (_children.TryGetValue(key, out node))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
node = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGet<T>(DataNode key, [NotNullWhen(true)] out T? node) where T : DataNode
|
||||
public bool TryGet<T>(string key, [NotNullWhen(true)] out T? node) where T : DataNode
|
||||
{
|
||||
node = null;
|
||||
if (!TryGet(key, out var rawNode) || rawNode is not T castNode)
|
||||
@@ -156,41 +154,27 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGet(string key, [NotNullWhen(true)] out DataNode? node)
|
||||
{
|
||||
return TryGet(GetFetchNode(key), out node);
|
||||
}
|
||||
|
||||
public bool TryGet<T>(string key, [NotNullWhen(true)] out T? node) where T : DataNode
|
||||
{
|
||||
return TryGet(GetFetchNode(key), out node);
|
||||
}
|
||||
|
||||
public bool Has(DataNode key)
|
||||
public bool Has(string key)
|
||||
{
|
||||
return _children.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool Has(string key)
|
||||
public bool Remove(string key)
|
||||
{
|
||||
return Has(GetFetchNode(key));
|
||||
if (!_children.Remove(key))
|
||||
return false;
|
||||
|
||||
var index = IndexOf(key);
|
||||
if (index == -1)
|
||||
throw new Exception("Key exists in Children, but not list?");
|
||||
|
||||
_list.RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
public MappingDataNode Remove(DataNode key)
|
||||
public T Cast<T>(string key) where T : DataNode
|
||||
{
|
||||
if (_children.Remove(key, out var val))
|
||||
_list.Remove(new(key, val));
|
||||
return this;
|
||||
}
|
||||
|
||||
public MappingDataNode Remove(string key)
|
||||
{
|
||||
return Remove(GetFetchNode(key));
|
||||
}
|
||||
|
||||
public T Cast<T>(string index) where T : DataNode
|
||||
{
|
||||
return (T) this[index];
|
||||
return (T) this[key];
|
||||
}
|
||||
|
||||
public YamlMappingNode ToYaml()
|
||||
@@ -199,7 +183,23 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
|
||||
foreach (var (key, val) in _list)
|
||||
{
|
||||
mapping.Add(key.ToYamlNode(), val.ToYamlNode());
|
||||
YamlScalarNode yamlKeyNode;
|
||||
if (_keyNodes != null && _keyNodes.TryGetValue(key, out var keyNode))
|
||||
{
|
||||
yamlKeyNode = (YamlScalarNode)keyNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is matches the ValueDataNode -> YamlScalarNode cast operator
|
||||
yamlKeyNode = new(key)
|
||||
{
|
||||
Style = ValueDataNode.IsNullLiteral(key) || string.IsNullOrWhiteSpace(key)
|
||||
? ScalarStyle.DoubleQuoted
|
||||
: ScalarStyle.Any
|
||||
};
|
||||
}
|
||||
|
||||
mapping.Add(yamlKeyNode, val.ToYamlNode());
|
||||
}
|
||||
|
||||
mapping.Tag = Tag;
|
||||
@@ -207,6 +207,11 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
return mapping;
|
||||
}
|
||||
|
||||
public ValueDataNode GetKeyNode(string key)
|
||||
{
|
||||
return _keyNodes?.GetValueOrDefault(key) ?? new ValueDataNode(key);
|
||||
}
|
||||
|
||||
public MappingDataNode Merge(MappingDataNode otherMapping)
|
||||
{
|
||||
var newMapping = Copy();
|
||||
@@ -227,12 +232,12 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
if (!skipDuplicates || !Has(key))
|
||||
{
|
||||
// Intentionally raises an ArgumentException
|
||||
Add(key.Copy(), val.Copy());
|
||||
Add(key, val.Copy());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertAt(int index, DataNode key, DataNode value)
|
||||
public void InsertAt(int index, string key, DataNode value)
|
||||
{
|
||||
if (index > _list.Count || index < 0)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
@@ -243,20 +248,6 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
_list.Insert(index, new(key, value));
|
||||
}
|
||||
|
||||
public void InsertAt(int index, string key, DataNode value)
|
||||
{
|
||||
if (index > _list.Count || index < 0)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
var k = new ValueDataNode(key);
|
||||
if (!_children.TryAdd(k, value))
|
||||
throw new InvalidOperationException($"Already contains key {key}");
|
||||
|
||||
_list.Insert(index, new(k, value));
|
||||
}
|
||||
|
||||
public override bool IsEmpty => _children.Count == 0;
|
||||
|
||||
public override MappingDataNode Copy()
|
||||
{
|
||||
var newMapping = new MappingDataNode(_children.Count)
|
||||
@@ -268,9 +259,10 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
|
||||
foreach (var (key, val) in _list)
|
||||
{
|
||||
newMapping.Add(key.Copy(), val.Copy());
|
||||
newMapping.Add(key, val.Copy());
|
||||
}
|
||||
|
||||
newMapping._keyNodes = _keyNodes;
|
||||
return newMapping;
|
||||
}
|
||||
|
||||
@@ -308,17 +300,13 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
|
||||
foreach (var (key, val) in _list)
|
||||
{
|
||||
var other = node._list.FirstOrNull(p => p.Key.Equals(key));
|
||||
if (other == null)
|
||||
if (!node._children.TryGetValue(key, out var otherVal))
|
||||
{
|
||||
mappingNode.Add(key.Copy(), val.Copy());
|
||||
mappingNode.Add(key, val.Copy());
|
||||
}
|
||||
else
|
||||
else if (val.Except(otherVal) is { } newValue)
|
||||
{
|
||||
// We recursively call except on the values and keep only the differences.
|
||||
var newValue = val.Except(other.Value.Value);
|
||||
if (newValue == null) continue;
|
||||
mappingNode.Add(key.Copy(), newValue);
|
||||
mappingNode.Add(key, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,18 +324,8 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
|
||||
foreach (var (key, val) in _list)
|
||||
{
|
||||
var other = node._list.FirstOrNull(p => p.Key.Equals(key));
|
||||
|
||||
if (other == null)
|
||||
{
|
||||
mappingNode.Add(key.Copy(), val.Copy());
|
||||
}
|
||||
else
|
||||
{
|
||||
// We only keep the entry if the values are not equal
|
||||
if (!val.Equals(other.Value.Value))
|
||||
mappingNode.Add(key.Copy(), val.Copy());
|
||||
}
|
||||
if (!node._children.TryGetValue(key, out var otherVal) || !val.Equals(otherVal))
|
||||
mappingNode.Add(key, val.Copy());
|
||||
}
|
||||
|
||||
return mappingNode._children.Count == 0 ? null : mappingNode;
|
||||
@@ -384,34 +362,24 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
if (_children.Count != other._children.Count)
|
||||
return false;
|
||||
|
||||
if (Tag != other.Tag)
|
||||
return false;
|
||||
|
||||
foreach (var (key, otherValue) in other)
|
||||
{
|
||||
if (!_children.TryGetValue(key, out var ownValue) ||
|
||||
!otherValue.Equals(ownValue))
|
||||
if (!_children.TryGetValue(key, out var ownValue)
|
||||
|| !otherValue.Equals(ownValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return Tag == other.Tag;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override MappingDataNode PushInheritance(MappingDataNode node)
|
||||
{
|
||||
var newNode = Copy();
|
||||
foreach (var (key, val) in node)
|
||||
{
|
||||
if(_children.ContainsKey(key))
|
||||
continue;
|
||||
|
||||
newNode.Remove(key);
|
||||
newNode.Add(key.Copy(), val.Copy());
|
||||
}
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<DataNode, DataNode>> GetEnumerator() => _list.GetEnumerator();
|
||||
public List<KeyValuePair<string, DataNode>>.Enumerator GetEnumerator() => _list.GetEnumerator();
|
||||
IEnumerator<KeyValuePair<string, DataNode>> IEnumerable<KeyValuePair<string, DataNode>>.GetEnumerator() =>
|
||||
_list.GetEnumerator();
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
@@ -430,7 +398,7 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<DataNode, DataNode> item) => Add(item.Key, item.Value);
|
||||
public void Add(KeyValuePair<string, DataNode> item) => Add(item.Key, item.Value);
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
@@ -438,18 +406,31 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
_list.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<DataNode, DataNode> item) => _children.ContainsKey(item.Key);
|
||||
public bool Contains(KeyValuePair<string, DataNode> item) => _children.ContainsKey(item.Key);
|
||||
|
||||
public void CopyTo(KeyValuePair<DataNode, DataNode>[] array, int arrayIndex)
|
||||
[Obsolete("Use SerializationManager.PushComposition()")]
|
||||
public override MappingDataNode PushInheritance(MappingDataNode node)
|
||||
{
|
||||
var newNode = Copy();
|
||||
foreach (var (key, val) in node)
|
||||
{
|
||||
if (_children.ContainsKey(key))
|
||||
continue;
|
||||
|
||||
newNode.Remove(key);
|
||||
newNode.Add(key, val.Copy());
|
||||
}
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
public void CopyTo(KeyValuePair<string, DataNode>[] array, int arrayIndex)
|
||||
=> _list.CopyTo(array, arrayIndex);
|
||||
|
||||
public bool Remove(KeyValuePair<DataNode, DataNode> item)
|
||||
=> ((IDictionary<DataNode, DataNode>)this).Remove(item.Key);
|
||||
public bool Remove(KeyValuePair<string, DataNode> item)
|
||||
=> ((IDictionary<string, DataNode>) this).Remove(item.Key);
|
||||
|
||||
public int Count => _children.Count;
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public bool TryAdd(DataNode key, DataNode value)
|
||||
public bool TryAdd(string key, DataNode value)
|
||||
{
|
||||
if (!_children.TryAdd(key, value))
|
||||
return false;
|
||||
@@ -458,7 +439,7 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryAddCopy(DataNode key, DataNode value)
|
||||
public bool TryAddCopy(string key, DataNode value)
|
||||
{
|
||||
ref var entry = ref CollectionsMarshal.GetValueRefOrAddDefault(_children, key, out var exists);
|
||||
if (exists)
|
||||
@@ -468,5 +449,52 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
_list.Add(new(key, entry));
|
||||
return true;
|
||||
}
|
||||
|
||||
// These methods are probably fine to keep around as helper methods, but are currently marked as obsolete
|
||||
// so that people don't uneccesarily allocate a ValueDataNode. I.e., to prevent people from using code like
|
||||
// mapping.TryGet(new ValueDataNode("key"), ...)
|
||||
#region ValueDataNode Helpers
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public bool TryGet(ValueDataNode key, [NotNullWhen(true)] out DataNode? value)
|
||||
=> TryGet(key.Value, out value);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public DataNode this[ValueDataNode key]
|
||||
{
|
||||
get => this[key.Value];
|
||||
set => this[key.Value] = value;
|
||||
}
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public bool TryGetValue(ValueDataNode key, [NotNullWhen(true)] out DataNode? value)
|
||||
=> TryGet(key.Value, out value);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public bool TryGet<T>(ValueDataNode key, [NotNullWhen(true)] out T? node) where T : DataNode
|
||||
=> TryGet(key.Value, out node);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public bool Has(ValueDataNode key) => Has(key.Value);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public T Cast<T>(ValueDataNode key) where T : DataNode => Cast<T>(key.Value);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public void Add(KeyValuePair<ValueDataNode, DataNode> item) => Add(item.Key, item.Value);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public MappingDataNode Add(ValueDataNode key, DataNode node) => Add(key.Value, node);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public void InsertAt(int index, ValueDataNode key, DataNode value) => InsertAt(index, key.Value, value);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public bool Contains(KeyValuePair<ValueDataNode, DataNode> item) => _children.ContainsKey(item.Key.Value);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public bool Remove(ValueDataNode key) => Remove(key.Value);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,21 +6,15 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
{
|
||||
public static class MappingDataNodeExtensions
|
||||
{
|
||||
public static MappingDataNode Add(this MappingDataNode mapping, string key, DataNode node)
|
||||
{
|
||||
mapping.Add(new ValueDataNode(key), node);
|
||||
return mapping;
|
||||
}
|
||||
|
||||
public static MappingDataNode Add(this MappingDataNode mapping, string key, string value)
|
||||
{
|
||||
mapping.Add(new ValueDataNode(key), new ValueDataNode(value));
|
||||
mapping.Add(key, new ValueDataNode(value));
|
||||
return mapping;
|
||||
}
|
||||
|
||||
public static MappingDataNode Add(this MappingDataNode mapping, string key, List<string> sequence)
|
||||
{
|
||||
mapping.Add(new ValueDataNode(key), new SequenceDataNode(sequence));
|
||||
mapping.Add(key, new SequenceDataNode(sequence));
|
||||
return mapping;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,8 @@ namespace Robust.Shared.Serialization.Markdown.Sequence
|
||||
return newSequence;
|
||||
}
|
||||
|
||||
public IEnumerator<DataNode> GetEnumerator() => _nodes.GetEnumerator();
|
||||
public List<DataNode>.Enumerator GetEnumerator() => _nodes.GetEnumerator();
|
||||
IEnumerator<DataNode> IEnumerable<DataNode>.GetEnumerator() => _nodes.GetEnumerator();
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
@@ -192,6 +193,7 @@ namespace Robust.Shared.Serialization.Markdown.Sequence
|
||||
return true;
|
||||
}
|
||||
|
||||
[Obsolete("Use SerializationManager.PushComposition()")]
|
||||
public override SequenceDataNode PushInheritance(SequenceDataNode node)
|
||||
{
|
||||
var newNode = Copy();
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Robust.Shared.Serialization.Markdown.Value
|
||||
|
||||
public override bool IsEmpty => string.IsNullOrWhiteSpace(Value);
|
||||
|
||||
private static bool IsNullLiteral(string? value) => value != null && value.Trim().ToLower() is "null" ;
|
||||
public static bool IsNullLiteral(string? value) => value != null && value.Trim().ToLower() is "null" ;
|
||||
|
||||
public override ValueDataNode Copy()
|
||||
{
|
||||
@@ -76,6 +76,7 @@ namespace Robust.Shared.Serialization.Markdown.Value
|
||||
return node.Value == Value ? null : Copy();
|
||||
}
|
||||
|
||||
[Obsolete("Use SerializationManager.PushComposition()")]
|
||||
public override ValueDataNode PushInheritance(ValueDataNode node)
|
||||
{
|
||||
return Copy();
|
||||
|
||||
@@ -11,30 +11,25 @@ using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
/// <summary>
|
||||
/// A custom type serializer for reading a set of types that inherit from some base type.
|
||||
/// A custom type serializer for reading a set of types that inherit from some base type.
|
||||
/// </summary>
|
||||
public sealed class AbstractDictionarySerializer<TValue> : ITypeSerializer<Dictionary<Type, TValue>, MappingDataNode>
|
||||
public sealed class AbstractDictionarySerializer<TValue> : ITypeSerializer<Dictionary<Type, TValue>, MappingDataNode>
|
||||
where TValue : notnull
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, MappingDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
{
|
||||
var mapping = new Dictionary<ValidationNode, ValidationNode>();
|
||||
foreach (var (keyNode, valueNode) in node.Children)
|
||||
foreach (var (key, valueNode) in node.Children)
|
||||
{
|
||||
if (keyNode is not ValueDataNode key)
|
||||
{
|
||||
mapping.Add(new ErrorNode(keyNode, $"Expected {nameof(ValueDataNode)} but was {keyNode.GetType()}"), new ValidatedValueNode(valueNode));
|
||||
continue;
|
||||
}
|
||||
var type = serializationManager.ReflectionManager.YamlTypeTagLookup(typeof(TValue), key.Value);
|
||||
var type = serializationManager.ReflectionManager.YamlTypeTagLookup(typeof(TValue), key);
|
||||
if (type == null)
|
||||
{
|
||||
mapping.Add(new ErrorNode(keyNode, $"Could not resolve type: {key.Value}"), new ValidatedValueNode(valueNode));
|
||||
mapping.Add(new ErrorNode(node.GetKeyNode(key), $"Could not resolve type: {key}"), new ValidatedValueNode(valueNode));
|
||||
continue;
|
||||
}
|
||||
|
||||
mapping.Add(new ValidatedValueNode(key), serializationManager.ValidateNode(type, valueNode, context));
|
||||
|
||||
mapping.Add(new ValidatedValueNode(node.GetKeyNode(key)), serializationManager.ValidateNode(type, valueNode, context));
|
||||
}
|
||||
|
||||
return new ValidatedMappingNode(mapping);
|
||||
@@ -44,10 +39,9 @@ public sealed class AbstractDictionarySerializer<TValue> : ITypeSerializer<Dicti
|
||||
SerializationHookContext hookCtx, ISerializationContext? context = null, ISerializationManager.InstantiationDelegate<Dictionary<Type, TValue>>? instanceProvider = null)
|
||||
{
|
||||
var dict = instanceProvider != null ? instanceProvider() : new Dictionary<Type, TValue>();
|
||||
foreach (var (keyNode, valueNode) in node.Children)
|
||||
foreach (var (key, valueNode) in node.Children)
|
||||
{
|
||||
var key = (ValueDataNode) keyNode;
|
||||
var type = serializationManager.ReflectionManager.YamlTypeTagLookup(typeof(TValue), key.Value)!;
|
||||
var type = serializationManager.ReflectionManager.YamlTypeTagLookup(typeof(TValue), key)!;
|
||||
var value = (TValue) serializationManager.Read(type, valueNode, hookCtx, context, notNullableOverride:true)!;
|
||||
dict.Add(type, value);
|
||||
}
|
||||
@@ -62,8 +56,14 @@ public sealed class AbstractDictionarySerializer<TValue> : ITypeSerializer<Dicti
|
||||
|
||||
foreach (var (key, val) in value)
|
||||
{
|
||||
// TODO SERIALIZATION
|
||||
// Add some way to directly return a string w/o allocating a ValueDataNode
|
||||
var keyNode = serializationManager.WriteValue(key.Name, alwaysWrite, context, notNullableOverride: true);
|
||||
if (keyNode is not ValueDataNode valueNode)
|
||||
throw new NotSupportedException();
|
||||
|
||||
mappingNode.Add(
|
||||
serializationManager.WriteValue(key.Name, alwaysWrite, context, notNullableOverride:true),
|
||||
valueNode.Value,
|
||||
serializationManager.WriteValue(key, val, alwaysWrite, context));
|
||||
}
|
||||
|
||||
|
||||
@@ -32,13 +32,8 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Pro
|
||||
|
||||
foreach (var (key, val) in node.Children)
|
||||
{
|
||||
if (key is not ValueDataNode value)
|
||||
{
|
||||
mapping.Add(new ErrorNode(key, $"Cannot cast node {key} to ValueDataNode."), serializationManager.ValidateNode<TValue>(val, context));
|
||||
continue;
|
||||
}
|
||||
|
||||
mapping.Add(PrototypeSerializer.Validate(serializationManager, value, dependencies, context), serializationManager.ValidateNode<TValue>(val, context));
|
||||
var keyNode = new ValueDataNode(key);
|
||||
mapping.Add(PrototypeSerializer.Validate(serializationManager, keyNode, dependencies, context), serializationManager.ValidateNode<TValue>(val, context));
|
||||
}
|
||||
|
||||
return new ValidatedMappingNode(mapping);
|
||||
|
||||
@@ -30,8 +30,9 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Pro
|
||||
{
|
||||
var mapping = new Dictionary<ValidationNode, ValidationNode>();
|
||||
|
||||
foreach (var (key, val) in node.Children)
|
||||
foreach (var (k, val) in node.Children)
|
||||
{
|
||||
var key = node.GetKeyNode(k);
|
||||
if (val is not ValueDataNode value)
|
||||
{
|
||||
mapping.Add(new ErrorNode(val, $"Cannot cast node {val} to ValueDataNode."), serializationManager.ValidateNode<TValue>(key, context));
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -8,6 +9,7 @@ using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.Markdown.Value;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Generic;
|
||||
@@ -59,7 +61,7 @@ public sealed class DictionarySerializer<TKey, TValue> :
|
||||
var mapping = new Dictionary<ValidationNode, ValidationNode>();
|
||||
foreach (var (key, val) in node.Children)
|
||||
{
|
||||
mapping.Add(serializationManager.ValidateNode<TKey>(key, context),
|
||||
mapping.Add(serializationManager.ValidateNode<TKey>(node.GetKeyNode(key), context),
|
||||
serializationManager.ValidateNode<TValue>(val, context));
|
||||
}
|
||||
|
||||
@@ -79,8 +81,14 @@ public sealed class DictionarySerializer<TKey, TValue> :
|
||||
var mappingNode = new MappingDataNode();
|
||||
foreach (var (key, val) in value)
|
||||
{
|
||||
// TODO SERIALIZATION
|
||||
// Add some way to directly return a string w/o allocating a ValueDataNode
|
||||
var keyNode = serializationManager.WriteValue(key, alwaysWrite, context);
|
||||
if (keyNode is not ValueDataNode valueNode)
|
||||
throw new NotSupportedException("Yaml mapping keys must serialize to a ValueDataNode (i.e. a string)");
|
||||
|
||||
mappingNode.Add(
|
||||
serializationManager.WriteValue(key, alwaysWrite, context),
|
||||
valueNode.Value,
|
||||
serializationManager.WriteValue(val, alwaysWrite, context));
|
||||
}
|
||||
|
||||
@@ -128,9 +136,11 @@ public sealed class DictionarySerializer<TKey, TValue> :
|
||||
{
|
||||
var dict = instanceProvider != null ? instanceProvider() : new Dictionary<TKey, TValue>();
|
||||
|
||||
var keyNode = new ValueDataNode();
|
||||
foreach (var (key, value) in node.Children)
|
||||
{
|
||||
dict.Add(serializationManager.Read<TKey>(key, hookCtx, context),
|
||||
keyNode.Value = key;
|
||||
dict.Add(serializationManager.Read<TKey>(keyNode, hookCtx, context),
|
||||
serializationManager.Read<TValue>(value, hookCtx, context));
|
||||
}
|
||||
|
||||
@@ -149,9 +159,11 @@ public sealed class DictionarySerializer<TKey, TValue> :
|
||||
|
||||
var array = new KeyValuePair<TKey, TValue>[node.Children.Count];
|
||||
int i = 0;
|
||||
var keyNode = new ValueDataNode();
|
||||
foreach (var (key, value) in node.Children)
|
||||
{
|
||||
var k = serializationManager.Read<TKey>(key, hookCtx, context);
|
||||
keyNode.Value = key;
|
||||
var k = serializationManager.Read<TKey>(keyNode, hookCtx, context);
|
||||
var v = serializationManager.Read<TValue>(value, hookCtx, context);
|
||||
array[i++] = new(k,v);
|
||||
}
|
||||
@@ -174,9 +186,11 @@ public sealed class DictionarySerializer<TKey, TValue> :
|
||||
|
||||
var dict = new Dictionary<TKey, TValue>();
|
||||
|
||||
var keyNode = new ValueDataNode();
|
||||
foreach (var (key, value) in node.Children)
|
||||
{
|
||||
dict.Add(serializationManager.Read<TKey>(key, hookCtx, context),
|
||||
keyNode.Value = key;
|
||||
dict.Add(serializationManager.Read<TKey>(keyNode, hookCtx, context),
|
||||
serializationManager.Read<TValue>(value, hookCtx, context));
|
||||
}
|
||||
|
||||
@@ -190,10 +204,12 @@ public sealed class DictionarySerializer<TKey, TValue> :
|
||||
ISerializationManager.InstantiationDelegate<SortedDictionary<TKey, TValue>>? instanceProvider)
|
||||
{
|
||||
var dict = instanceProvider != null ? instanceProvider() : new SortedDictionary<TKey, TValue>();
|
||||
var keyNode = new ValueDataNode();
|
||||
|
||||
foreach (var (key, value) in node.Children)
|
||||
{
|
||||
dict.Add(serializationManager.Read<TKey>(key, hookCtx, context),
|
||||
keyNode.Value = key;
|
||||
dict.Add(serializationManager.Read<TKey>(keyNode, hookCtx, context),
|
||||
serializationManager.Read<TValue>(value, hookCtx, context));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,40 +6,67 @@ using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
using Robust.Shared.Serialization.Markdown.Sequence;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Generic
|
||||
{
|
||||
[TypeSerializer]
|
||||
public sealed class ValueTupleSerializer<T1, T2> : ITypeSerializer<ValueTuple<T1, T2>, MappingDataNode>, ITypeCopyCreator<ValueTuple<T1, T2>>
|
||||
public sealed class ValueTupleSerializer<T1, T2> :
|
||||
ITypeReader<ValueTuple<T1, T2>, MappingDataNode>,
|
||||
ITypeSerializer<ValueTuple<T1, T2>, SequenceDataNode>,
|
||||
ITypeCopyCreator<ValueTuple<T1, T2>>
|
||||
{
|
||||
public (T1, T2) Read(ISerializationManager serializationManager, MappingDataNode node,
|
||||
public (T1, T2) Read(
|
||||
ISerializationManager serializationManager,
|
||||
MappingDataNode node,
|
||||
IDependencyCollection dependencies,
|
||||
SerializationHookContext hookCtx,
|
||||
ISerializationContext? context = null, ISerializationManager.InstantiationDelegate<(T1, T2)>? val = null)
|
||||
ISerializationContext? context = null,
|
||||
ISerializationManager.InstantiationDelegate<(T1, T2)>? instanceProvider = null)
|
||||
{
|
||||
if (node.Children.Count != 1)
|
||||
throw new InvalidMappingException("Less than or more than 1 mappings provided to ValueTupleSerializer");
|
||||
|
||||
var entry = node.Children.First();
|
||||
var v1 = serializationManager.Read<T1>(entry.Key, hookCtx, context);
|
||||
var v1 = serializationManager.Read<T1>(node.GetKeyNode(entry.Key), hookCtx, context);
|
||||
var v2 = serializationManager.Read<T2>(entry.Value, hookCtx, context);
|
||||
|
||||
return (v1, v2);
|
||||
}
|
||||
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, MappingDataNode node,
|
||||
public (T1, T2) Read(
|
||||
ISerializationManager serializationManager,
|
||||
SequenceDataNode node,
|
||||
IDependencyCollection dependencies,
|
||||
SerializationHookContext hookCtx,
|
||||
ISerializationContext? context = null,
|
||||
ISerializationManager.InstantiationDelegate<(T1, T2)>? val = null)
|
||||
{
|
||||
if (node.Count != 2)
|
||||
throw new InvalidMappingException("Sequence must contain exactly 2 elements.");
|
||||
|
||||
var v1 = serializationManager.Read<T1>(node[0], hookCtx, context);
|
||||
var v2 = serializationManager.Read<T2>(node[1], hookCtx, context);
|
||||
|
||||
return (v1, v2);
|
||||
}
|
||||
|
||||
public ValidationNode Validate(
|
||||
ISerializationManager serializationManager,
|
||||
MappingDataNode node,
|
||||
IDependencyCollection dependencies,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
if (node.Children.Count != 1) return new ErrorNode(node, "More or less than 1 Mapping for ValueTuple found.");
|
||||
if (node.Children.Count != 1)
|
||||
return new ErrorNode(node, "More or less than 1 Mapping for ValueTuple found.");
|
||||
|
||||
var entry = node.Children.First();
|
||||
var dict = new Dictionary<ValidationNode, ValidationNode>
|
||||
{
|
||||
{
|
||||
serializationManager.ValidateNode<T1>(entry.Key, context),
|
||||
serializationManager.ValidateNode<T1>(node.GetKeyNode(entry.Key), context),
|
||||
serializationManager.ValidateNode<T2>(entry.Value, context)
|
||||
}
|
||||
};
|
||||
@@ -47,21 +74,44 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Generic
|
||||
return new ValidatedMappingNode(dict);
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, (T1, T2) value,
|
||||
IDependencyCollection dependencies, bool alwaysWrite = false,
|
||||
public ValidationNode Validate(
|
||||
ISerializationManager serializationManager,
|
||||
SequenceDataNode node,
|
||||
IDependencyCollection dependencies,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
var mapping = new MappingDataNode();
|
||||
if (node.Count != 2)
|
||||
throw new InvalidMappingException("Sequence must contain exactly 2 elements.");
|
||||
|
||||
mapping.Add(
|
||||
serializationManager.WriteValue<T1>(value.Item1, alwaysWrite, context),
|
||||
serializationManager.WriteValue<T2>(value.Item2, alwaysWrite, context));
|
||||
var seq = new List<ValidationNode>
|
||||
{
|
||||
serializationManager.ValidateNode<T1>(node[0], context),
|
||||
serializationManager.ValidateNode<T2>(node[1], context)
|
||||
};
|
||||
|
||||
return mapping;
|
||||
return new ValidatedSequenceNode(seq);
|
||||
}
|
||||
|
||||
public (T1, T2) CreateCopy(ISerializationManager serializationManager, (T1, T2) source,
|
||||
IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
public DataNode Write(
|
||||
ISerializationManager serializationManager,
|
||||
(T1, T2) value,
|
||||
IDependencyCollection dependencies,
|
||||
bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new SequenceDataNode(new List<DataNode>
|
||||
{
|
||||
serializationManager.WriteValue(value.Item1, alwaysWrite, context),
|
||||
serializationManager.WriteValue(value.Item2, alwaysWrite, context)
|
||||
});
|
||||
}
|
||||
|
||||
public (T1, T2) CreateCopy(
|
||||
ISerializationManager serializationManager,
|
||||
(T1, T2) source,
|
||||
IDependencyCollection dependencies,
|
||||
SerializationHookContext hookCtx,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return (serializationManager.CreateCopy(source.Item1, hookCtx, context),
|
||||
serializationManager.CreateCopy(source.Item2, hookCtx, context));
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.UnitTesting.Client.GameObjects.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that content can correctly override an occluder's directions instead of relying on the default anchoring behaviour.
|
||||
/// The directions are used for connecting occluders together.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public sealed class OccluderDirectionsTest : RobustIntegrationTest
|
||||
{
|
||||
/* See https://github.com/space-wizards/RobustToolbox/pull/2528 for why this is commented out as the technology isn't there yet.
|
||||
[Test]
|
||||
public async Task TestOccluderOverride()
|
||||
{
|
||||
var client = StartClient();
|
||||
|
||||
await client.WaitIdleAsync();
|
||||
|
||||
var entManager = client.ResolveDependency<IEntityManager>();
|
||||
var mapManager = client.ResolveDependency<IMapManager>();
|
||||
|
||||
var overrider = new OccluderOverrider();
|
||||
entManager.EventBus.SubscribeEvent<OccluderDirectionsEvent>(EventSource.Local, overrider, EventHandler);
|
||||
|
||||
await client.WaitAssertion(() =>
|
||||
{
|
||||
var mapId = mapManager.CreateMap();
|
||||
var occ = entManager.SpawnEntity(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
var occluder = entManager.AddComponent<ClientOccluderComponent>(occ);
|
||||
|
||||
Assert.That(occluder.Occluding, Is.EqualTo(OccluderDir.None));
|
||||
occluder.Update();
|
||||
Assert.That(occluder.Occluding, Is.EqualTo(OccluderDir.East));
|
||||
});
|
||||
}
|
||||
|
||||
private static void EventHandler(ref OccluderDirectionsEvent ev)
|
||||
{
|
||||
ev.Handled = true;
|
||||
ev.Directions = OccluderDir.East;
|
||||
}
|
||||
|
||||
private sealed class OccluderOverrider : IEntityEventSubscriber {}
|
||||
*/
|
||||
}
|
||||
@@ -33,6 +33,7 @@ using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -171,7 +172,19 @@ namespace Robust.UnitTesting
|
||||
|
||||
private bool ShouldPool(IntegrationOptions? options)
|
||||
{
|
||||
return options?.Pool ?? false;
|
||||
// If no options are provided, we assume we should pool
|
||||
if (options == null)
|
||||
return true;
|
||||
|
||||
// If custom options are provided without explicitly setting pool=true, we assume we shouldn't pool.
|
||||
if (options is not {Pool: true})
|
||||
return false;
|
||||
|
||||
if (!options.Asynchronous)
|
||||
throw new Exception("Invalid options. Pooled instances must be asynchronous");
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
protected virtual async Task OnInstanceReturn(IntegrationInstance instance)
|
||||
@@ -203,28 +216,14 @@ namespace Robust.UnitTesting
|
||||
{
|
||||
foreach (var client in _clientsRunning.Keys)
|
||||
{
|
||||
await client.WaitIdleAsync();
|
||||
|
||||
if (client.UnhandledException != null || !client.IsAlive)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ClientsReady.Enqueue(client);
|
||||
await ReturnToPool(client);
|
||||
}
|
||||
|
||||
_clientsRunning.Clear();
|
||||
|
||||
foreach (var server in _serversRunning.Keys)
|
||||
{
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
if (server.UnhandledException != null || !server.IsAlive)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ServersReady.Enqueue(server);
|
||||
await ReturnToPool(server);
|
||||
}
|
||||
|
||||
_serversRunning.Clear();
|
||||
@@ -234,6 +233,43 @@ namespace Robust.UnitTesting
|
||||
_notPooledInstances.Clear();
|
||||
}
|
||||
|
||||
public async Task ReturnToPool(ClientIntegrationInstance client)
|
||||
{
|
||||
if (!_clientsRunning.Remove(client, out _))
|
||||
return;
|
||||
|
||||
var res = await ReturnToPoolInternal(client);
|
||||
if (res)
|
||||
ClientsReady.Enqueue(client);
|
||||
}
|
||||
|
||||
public async Task ReturnToPool(ServerIntegrationInstance server)
|
||||
{
|
||||
if (!_serversRunning.Remove(server, out _))
|
||||
return;
|
||||
|
||||
var res = await ReturnToPoolInternal(server);
|
||||
if (res)
|
||||
ServersReady.Enqueue(server);
|
||||
}
|
||||
|
||||
public async Task<bool> ReturnToPoolInternal(IntegrationInstance instance)
|
||||
{
|
||||
await instance.WaitIdleAsync();
|
||||
if (instance.UnhandledException != null || !instance.IsAlive)
|
||||
return false;
|
||||
|
||||
var netMan = instance.ResolveDependency<INetManager>();
|
||||
Assert.That(netMan.IsConnected, Is.False);
|
||||
|
||||
// TODO Validate cvars and whatnot
|
||||
// Or just move content's PoolManager & TestPair over to engine.
|
||||
|
||||
await instance.WaitPost(() => instance.EntMan.FlushEntities());
|
||||
await instance.WaitIdleAsync();
|
||||
return instance.UnhandledException == null && instance.IsAlive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides control over a running instance of the client or server.
|
||||
/// </summary>
|
||||
@@ -414,7 +450,7 @@ namespace Robust.UnitTesting
|
||||
/// </exception>
|
||||
public Task WaitIdleAsync(bool throwOnUnhandled = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (Options?.Asynchronous == true)
|
||||
if (Options?.Asynchronous != false)
|
||||
{
|
||||
return WaitIdleImplAsync(throwOnUnhandled, cancellationToken);
|
||||
}
|
||||
@@ -624,7 +660,7 @@ namespace Robust.UnitTesting
|
||||
{
|
||||
ServerOptions = options;
|
||||
DependencyCollection = new DependencyCollection();
|
||||
if (options?.Asynchronous == true)
|
||||
if (options?.Asynchronous != false)
|
||||
{
|
||||
InstanceThread = new Thread(_serverMain)
|
||||
{
|
||||
@@ -839,7 +875,7 @@ namespace Robust.UnitTesting
|
||||
ClientOptions = options;
|
||||
DependencyCollection = new DependencyCollection();
|
||||
|
||||
if (options?.Asynchronous == true)
|
||||
if (options?.Asynchronous != false)
|
||||
{
|
||||
InstanceThread = new Thread(ThreadMain)
|
||||
{
|
||||
|
||||
@@ -29,7 +29,6 @@ public sealed class DefaultEntityTest : RobustIntegrationTest
|
||||
var netMan = client.ResolveDependency<IClientNetManager>();
|
||||
var playerMan = server.ResolveDependency<IPlayerManager>();
|
||||
var confMan = server.ResolveDependency<IConfigurationManager>();
|
||||
var mapMan = server.ResolveDependency<IMapManager>();
|
||||
|
||||
client.SetConnectTarget(server);
|
||||
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
||||
@@ -104,6 +103,10 @@ public sealed class DefaultEntityTest : RobustIntegrationTest
|
||||
|
||||
Assert.That(sEntMan.EntityExists(sEntMan.GetEntity(ent)));
|
||||
Assert.That(cEntMan.EntityExists(cEntMan.GetEntity(ent)));
|
||||
|
||||
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ public sealed class DetachedParentTest : RobustIntegrationTest
|
||||
[Test]
|
||||
public async Task TestDetachedParent()
|
||||
{
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
var server = StartServer(new() {Pool = false});
|
||||
var client = StartClient(new() {Pool = false});
|
||||
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
@@ -441,6 +441,10 @@ public sealed class DetachedParentTest : RobustIntegrationTest
|
||||
Assert.That(cParent3X.MapUid, Is.EqualTo(cMap3));
|
||||
Assert.That(cParent4X.MapUid, Is.EqualTo(cMap3));
|
||||
Assert.That(cGrid3X.MapUid, Is.EqualTo(cMap3));
|
||||
|
||||
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -164,6 +164,10 @@ public sealed class MissingParentTest : RobustIntegrationTest
|
||||
Assert.That(client.Transform(entity).ParentUid.IsValid(), Is.True);
|
||||
Assert.That(client.Transform(entity).ParentUid, Is.EqualTo(newParent));
|
||||
Assert.That(client.MetaData(entity).Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
|
||||
|
||||
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,6 +141,10 @@ public sealed class PvsChunkTest : RobustIntegrationTest
|
||||
Assert.That(cEntMan.TryGetEntity(nMap1, out _));
|
||||
Assert.That(!cEntMan.TryGetEntity(nMap2, out _));
|
||||
Assert.That(cEntMan.TryGetEntity(nGrid, out _));
|
||||
|
||||
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -204,6 +204,9 @@ public sealed class PvsReEntryTest : RobustIntegrationTest
|
||||
// If the test moves the entity instead of the player, then the test doesn't actually work.
|
||||
Assert.That(meta.LastModifiedTick, Is.EqualTo(lastDirty));
|
||||
|
||||
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -121,6 +121,10 @@ public sealed class PvsSystemTests : RobustIntegrationTest
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,8 +57,9 @@ namespace Robust.UnitTesting.Shared
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
// Connect client to the server...
|
||||
var netMan = client.ResolveDependency<IClientNetManager>();
|
||||
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
||||
client.Post(() => IoCManager.Resolve<IClientNetManager>().ClientConnect(null!, 0, null!));
|
||||
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
||||
|
||||
// Run 10 synced ticks...
|
||||
for (int i = 0; i < 10; i++)
|
||||
@@ -89,6 +90,10 @@ namespace Robust.UnitTesting.Shared
|
||||
Assert.That(netManager.ServerChannel, Is.Not.Null);
|
||||
Assert.That(netManager.ServerChannel!.IsConnected, Is.True);
|
||||
});
|
||||
|
||||
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public sealed partial class AutoIncludeSerializationTest : RobustIntegrationTest
|
||||
[Test]
|
||||
public async Task TestAutoIncludeSerialization()
|
||||
{
|
||||
var server = StartServer();
|
||||
var server = StartServer(new() {Pool = false}); // Pool=false due to TileDef registration
|
||||
await server.WaitIdleAsync();
|
||||
var entMan = server.EntMan;
|
||||
var mapSys = server.System<SharedMapSystem>();
|
||||
@@ -274,6 +274,7 @@ public sealed partial class AutoIncludeSerializationTest : RobustIntegrationTest
|
||||
await server.WaitPost(() => entMan.DeleteEntity(otherMap));
|
||||
await server.WaitPost(() => entMan.DeleteEntity(grid.Comp1.ParentUid));
|
||||
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
AssertCount(0);
|
||||
|
||||
// Check the map file
|
||||
@@ -302,5 +303,8 @@ public sealed partial class AutoIncludeSerializationTest : RobustIntegrationTest
|
||||
await server.WaitPost(() => entMan.DeleteEntity(otherMap));
|
||||
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
|
||||
AssertCount(0);
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using Robust.Shared.EntitySerialization.Components;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
|
||||
|
||||
@@ -51,7 +52,7 @@ public sealed partial class BackwardsCompatibilityTest
|
||||
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
await server.WaitPost(() => mapUid = mapSys.CreateMap(out mapId));
|
||||
await server.WaitPost(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
|
||||
@@ -77,7 +78,7 @@ public sealed partial class BackwardsCompatibilityTest
|
||||
|
||||
var mapPath = new ResPath($"{nameof(MapDataV3Map)}.yml");
|
||||
resourceManager.MountString(mapPath.ToString(), MapDataV3Map);
|
||||
await server.WaitPost(() => Assert.That(loader.TryLoadMap(mapPath, out _, out _)));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out _, out _)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
@@ -111,7 +112,7 @@ public sealed partial class BackwardsCompatibilityTest
|
||||
resourceManager.MountString(mapPath2.ToString(), MapDataV3Map);
|
||||
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
await server.WaitPost(() => Assert.That(loader.TryLoadMap(mapPath2, out _, out _, opts)));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath2, out _, out _, opts)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
@@ -137,6 +138,8 @@ public sealed partial class BackwardsCompatibilityTest
|
||||
|
||||
await server.WaitPost(() => entMan.DeleteEntity(map));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
private const string MapDataV3Grid = @"
|
||||
|
||||
@@ -6,6 +6,7 @@ using Robust.Shared.EntitySerialization.Components;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
|
||||
|
||||
@@ -47,7 +48,7 @@ public sealed partial class BackwardsCompatibilityTest
|
||||
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
await server.WaitPost(() => mapUid = mapSys.CreateMap(out mapId));
|
||||
await server.WaitPost(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
|
||||
@@ -73,7 +74,7 @@ public sealed partial class BackwardsCompatibilityTest
|
||||
|
||||
var mapPath = new ResPath($"{nameof(MapDataV4Map)}.yml");
|
||||
resourceManager.MountString(mapPath.ToString(), MapDataV4Map);
|
||||
await server.WaitPost(() => Assert.That(loader.TryLoadMap(mapPath, out _, out _)));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out _, out _)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
@@ -107,7 +108,7 @@ public sealed partial class BackwardsCompatibilityTest
|
||||
resourceManager.MountString(mapPath2.ToString(), MapDataV4Map);
|
||||
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
await server.WaitPost(() => Assert.That(loader.TryLoadMap(mapPath2, out _, out _, opts)));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath2, out _, out _, opts)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
@@ -133,6 +134,8 @@ public sealed partial class BackwardsCompatibilityTest
|
||||
|
||||
await server.WaitPost(() => entMan.DeleteEntity(map));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
private const string MapDataV4Grid = @"
|
||||
|
||||
@@ -6,6 +6,7 @@ using Robust.Shared.EntitySerialization.Components;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
|
||||
|
||||
@@ -47,7 +48,7 @@ public sealed partial class BackwardsCompatibilityTest
|
||||
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
await server.WaitPost(() => mapUid = mapSys.CreateMap(out mapId));
|
||||
await server.WaitPost(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
|
||||
@@ -73,7 +74,7 @@ public sealed partial class BackwardsCompatibilityTest
|
||||
|
||||
var mapPath = new ResPath($"{nameof(MapDataV5Map)}.yml");
|
||||
resourceManager.MountString(mapPath.ToString(), MapDataV5Map);
|
||||
await server.WaitPost(() => Assert.That(loader.TryLoadMap(mapPath, out _, out _)));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out _, out _)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
@@ -107,7 +108,7 @@ public sealed partial class BackwardsCompatibilityTest
|
||||
resourceManager.MountString(mapPath2.ToString(), MapDataV5Map);
|
||||
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
await server.WaitPost(() => Assert.That(loader.TryLoadMap(mapPath2, out _, out _, opts)));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath2, out _, out _, opts)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
@@ -133,6 +134,8 @@ public sealed partial class BackwardsCompatibilityTest
|
||||
|
||||
await server.WaitPost(() => entMan.DeleteEntity(map));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
private const string MapDataV5Grid = @"
|
||||
|
||||
@@ -6,6 +6,7 @@ using Robust.Shared.EntitySerialization.Components;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
|
||||
|
||||
@@ -47,7 +48,7 @@ public sealed partial class BackwardsCompatibilityTest
|
||||
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
await server.WaitPost(() => mapUid = mapSys.CreateMap(out mapId));
|
||||
await server.WaitPost(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
|
||||
@@ -73,7 +74,7 @@ public sealed partial class BackwardsCompatibilityTest
|
||||
|
||||
var mapPath = new ResPath($"{nameof(MapDataV6Map)}.yml");
|
||||
resourceManager.MountString(mapPath.ToString(), MapDataV6Map);
|
||||
await server.WaitPost(() => Assert.That(loader.TryLoadMap(mapPath, out _, out _)));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out _, out _)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
@@ -107,7 +108,7 @@ public sealed partial class BackwardsCompatibilityTest
|
||||
resourceManager.MountString(mapPath2.ToString(), MapDataV6Map);
|
||||
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
await server.WaitPost(() => Assert.That(loader.TryLoadMap(mapPath2, out _, out _, opts)));
|
||||
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath2, out _, out _, opts)));
|
||||
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
|
||||
@@ -133,6 +134,8 @@ public sealed partial class BackwardsCompatibilityTest
|
||||
|
||||
await server.WaitPost(() => entMan.DeleteEntity(map));
|
||||
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
|
||||
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
private const string MapDataV6Grid = @"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user