mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab99d5be07 | ||
|
|
11c05509cb | ||
|
|
e1120b0d4c | ||
|
|
73c22ff6ac | ||
|
|
eb63809999 | ||
|
|
4c3c74865c | ||
|
|
b624f5b70f | ||
|
|
6566a7658a | ||
|
|
9e3e1cc929 | ||
|
|
4e87d93009 | ||
|
|
1031ae4cc5 | ||
|
|
73da147b88 | ||
|
|
0ab59d70b1 | ||
|
|
8e8470ac7e | ||
|
|
15f94bd094 | ||
|
|
68888c4370 | ||
|
|
19f87dfbb3 | ||
|
|
68e5b6924d | ||
|
|
9f913cd2d9 | ||
|
|
ec37d1c137 | ||
|
|
ea58924495 | ||
|
|
c5aa735506 | ||
|
|
f5a6e52c7f | ||
|
|
d5c4981648 | ||
|
|
8c6170661d | ||
|
|
1901059755 | ||
|
|
8cdec92be6 | ||
|
|
0a00e7ec29 | ||
|
|
8c25a83066 | ||
|
|
0fadfc2d9b | ||
|
|
a6e7224672 | ||
|
|
37796f4806 | ||
|
|
a5494d1df2 | ||
|
|
e2525a2103 | ||
|
|
b50f68866f | ||
|
|
03a4d3e0a0 | ||
|
|
af8fb52a4f | ||
|
|
fd60dc2887 | ||
|
|
cd24fd46b6 | ||
|
|
44cc7127fa | ||
|
|
af36d24892 | ||
|
|
caa8ff0f2d | ||
|
|
cd67c67a5c | ||
|
|
57b328e8c2 | ||
|
|
4874b1db68 | ||
|
|
814ad08884 | ||
|
|
8c4deb2067 | ||
|
|
f6a5120e56 | ||
|
|
c1b8bf8e52 | ||
|
|
4b193bad26 | ||
|
|
8db3da4852 | ||
|
|
0c271fc2f8 | ||
|
|
ed406c06b7 | ||
|
|
b31940b489 | ||
|
|
84360c653d | ||
|
|
6764ed56b0 | ||
|
|
2b55d39e51 | ||
|
|
fdc1de2430 | ||
|
|
99c5b0ad08 | ||
|
|
9e2ab2a917 | ||
|
|
36eb857b55 | ||
|
|
92d7f2723a | ||
|
|
91d3f67a94 | ||
|
|
b28b5ed09b | ||
|
|
30eed7957f | ||
|
|
73c1449811 | ||
|
|
958b5dd06d |
5
.github/workflows/build-test.yml
vendored
5
.github/workflows/build-test.yml
vendored
@@ -27,7 +27,8 @@ jobs:
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --no-restore /p:WarningsAsErrors=nullable
|
||||
- name: Test Engine
|
||||
- name: Robust.UnitTesting
|
||||
run: dotnet test --no-build Robust.UnitTesting/Robust.UnitTesting.csproj -- NUnit.ConsoleOut=0
|
||||
|
||||
- name: Robust.Analyzers.Tests
|
||||
run: dotnet test --no-build Robust.Analyzers.Tests/Robust.Analyzers.Tests.csproj -- NUnit.ConsoleOut=0
|
||||
|
||||
|
||||
6
.github/workflows/publish-client.yml
vendored
6
.github/workflows/publish-client.yml
vendored
@@ -33,10 +33,10 @@ jobs:
|
||||
mkdir "release/${{ steps.parse_version.outputs.version }}"
|
||||
mv release/*.zip "release/${{ steps.parse_version.outputs.version }}"
|
||||
|
||||
- name: Upload files to centcomm
|
||||
- name: Upload files to Suns
|
||||
uses: appleboy/scp-action@master
|
||||
with:
|
||||
host: centcomm.spacestation14.io
|
||||
host: suns.spacestation14.com
|
||||
username: robust-build-push
|
||||
key: ${{ secrets.CENTCOMM_ROBUST_BUILDS_PUSH_KEY }}
|
||||
source: "release/${{ steps.parse_version.outputs.version }}"
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
- name: Update manifest JSON
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: centcomm.spacestation14.io
|
||||
host: suns.spacestation14.com
|
||||
username: robust-build-push
|
||||
key: ${{ secrets.CENTCOMM_ROBUST_BUILDS_PUSH_KEY }}
|
||||
script: /home/robust-build-push/push.ps1 ${{ steps.parse_version.outputs.version }}
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<!--
|
||||
We actually set ManagePackageVersionsCentrally manually in another import file.
|
||||
Since .NET SDK 8.0.300, ManagePackageVersionsCentrally is automatically set if Directory.Packages.props exists.
|
||||
https://github.com/NuGet/NuGet.Client/pull/5572
|
||||
We actively negate this here, as we have some packages in tree we don't want such automatic behavior for.
|
||||
We use Directory.Build.props to get copy the state *after* our MSBuild config but before Nuget's config.
|
||||
-->
|
||||
<ManagePackageVersionsCentrally />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
|
||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||
@@ -45,7 +55,7 @@
|
||||
<PackageVersion Include="Serilog" Version="3.1.1" />
|
||||
<PackageVersion Include="Serilog.Sinks.Loki" Version="4.0.0-beta3" />
|
||||
<PackageVersion Include="SharpZstd.Interop" Version="1.5.2-beta2" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.3" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.5" />
|
||||
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.0" />
|
||||
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.0.2" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
170
RELEASE-NOTES.md
170
RELEASE-NOTES.md
@@ -1,4 +1,4 @@
|
||||
# Release notes for RobustToolbox.
|
||||
# Release notes for RobustToolbox.
|
||||
|
||||
<!--
|
||||
NOTE: automatically updated sometimes by version.py.
|
||||
@@ -54,6 +54,174 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 219.2.2
|
||||
|
||||
|
||||
## 219.2.1
|
||||
|
||||
|
||||
## 219.2.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add SetMapCoordinates to TransformSystem.
|
||||
* Improve YAML Linter and validation of static fields.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix DebugCoordsPanel freezing when hovering a control.
|
||||
|
||||
### Other
|
||||
|
||||
* Optimise physics networking to not dirty every tick of movement.
|
||||
|
||||
|
||||
## 219.1.3
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix map-loader not pausing pre-init maps when not actively overwriting an existing map.
|
||||
|
||||
|
||||
## 219.1.2
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix map-loader not map-initialising grids when loading into a post-init map.
|
||||
|
||||
|
||||
## 219.1.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix map-loader not map-initialising maps when overwriting a post-init map.
|
||||
|
||||
|
||||
## 219.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Added a new optional arguments to various entity spawning methods, including a new argument to set the entity's rotation.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixes map initialisation not always initialising all entities on a map.
|
||||
|
||||
### Other
|
||||
|
||||
* The default value of the `auth.mode` cvar has changed
|
||||
|
||||
|
||||
## 219.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Move most IMapManager functionality to SharedMapSystem.
|
||||
|
||||
|
||||
## 218.2.0
|
||||
|
||||
### New features
|
||||
|
||||
* Control layout properties such as `Margin` can now be set via style sheets.
|
||||
* Expose worldposition in SpriteComponent.Render
|
||||
* Network audio entity Play/Pause/Stop states and playback position.
|
||||
* Add `Disabled` functionality to `Slider` control.
|
||||
|
||||
|
||||
## 218.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add IEquatable.Equals to the sandbox.
|
||||
* Enable roslyn extensions tests in CI.
|
||||
* Add a VerticalTabContainer control to match the horizontal one.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix divison remainder issue for Colors, fixing purples.
|
||||
|
||||
### Other
|
||||
|
||||
* Default hub address (`hub.hub_urls`) has been changed to `https://hub.spacestation14.com/`.
|
||||
|
||||
|
||||
## 218.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `Robust.Shared.Configuration.EnvironmentVariables` is now internal and no longer usable by content.
|
||||
|
||||
### New features
|
||||
|
||||
* Add TryGetRandom to EntityManager to get a random entity with the specified component and TryGetRandom to IPrototypeManager to return a random prototype of the specified type.
|
||||
* Add CopyData to AppearanceSystem.
|
||||
* Update UI themes on prototype reloads.
|
||||
* Allow scaling the line height of a RichTextLabel.
|
||||
* You can now specify CVar overrides via environment variable with the `ROBUST_CVAR_*` prefix. For example `ROBUST_CVAR_game__hostname=foobar` would set the appropriate CVar. Double underscores in the environment variable name are replaced with ".".
|
||||
* Added non-generic variant of `GetCVar` to `IConfigurationManager`.
|
||||
* Add type tracking to FieldNotFoundErrorNode for serialization.
|
||||
* Distance between lines of a `RichTextLabel` can now be modified with `LineHeightScale`.
|
||||
* UI theme prototypes are now updated when reloaded.
|
||||
* New `RA0025` analyzer diagnostic warns for manual assignment to `[Dependency]` fields.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Request headers in `IStatusHandlerContext` are now case-insensitive.
|
||||
* SetWorldPosition rotation now more closely aligns with prior behavior.
|
||||
* Fix exception when inspecting elements in some cases.
|
||||
* Fix HTTP errors on watchdog ping not being reported.
|
||||
|
||||
### Other
|
||||
|
||||
* Add an analyzer for redundantly assigning to dependency fields.
|
||||
|
||||
### Internal
|
||||
|
||||
* Remove redundant Exists checks in ContainerSystem.
|
||||
* Improve logging on watchdog pings.
|
||||
|
||||
|
||||
## 217.2.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix LineEdit tests on engine.
|
||||
|
||||
### Internal
|
||||
|
||||
* Make various ValueList enumerators access the span directly for performance.
|
||||
|
||||
|
||||
## 217.2.0
|
||||
|
||||
### New features
|
||||
|
||||
* Added `AddComponents` and `RemoveComponents` methods to EntityManager that handle EntityPrototype / ComponentRegistry bulk component changes.
|
||||
* Add double-clicking to LineEdit.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Properly ignore non-hard fixtures for IntersectRayWithPredicate.
|
||||
* Fix nullable TimeSpan addition on some platforms.
|
||||
|
||||
|
||||
## 217.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Added `IRobustRandom.GetItems` extension methods for randomly picking multiple items from a collections.
|
||||
* Added `SharedPhysicsSystem.EffectiveCurTime`. This is effectively a variation of `IGameTiming.CurTime` that takes into account the current physics sub-step.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix `MapComponent.LightingEnabled` not leaving FOV rendering in a broken state.
|
||||
|
||||
### Internal
|
||||
|
||||
* `Shuffle<T>(Span<T>, System.Random)` has been removed, just use the builtin method.
|
||||
|
||||
|
||||
## 217.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
@@ -20,11 +20,16 @@ public sealed class AccessAnalyzer_Test
|
||||
{
|
||||
TestState =
|
||||
{
|
||||
AdditionalReferences = { typeof(AccessAnalyzer).Assembly },
|
||||
Sources = { code }
|
||||
},
|
||||
};
|
||||
|
||||
TestHelper.AddEmbeddedSources(
|
||||
test.TestState,
|
||||
"Robust.Shared.Analyzers.AccessAttribute.cs",
|
||||
"Robust.Shared.Analyzers.AccessPermissions.cs"
|
||||
);
|
||||
|
||||
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
|
||||
test.TestState.ExpectedDiagnostics.AddRange(expected);
|
||||
|
||||
|
||||
58
Robust.Analyzers.Tests/DependencyAssignAnalyzerTest.cs
Normal file
58
Robust.Analyzers.Tests/DependencyAssignAnalyzerTest.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.CSharp.Testing;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
using Microsoft.CodeAnalysis.Testing.Verifiers;
|
||||
using NUnit.Framework;
|
||||
using VerifyCS =
|
||||
Microsoft.CodeAnalysis.CSharp.Testing.NUnit.AnalyzerVerifier<Robust.Analyzers.DependencyAssignAnalyzer>;
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
|
||||
[TestFixture]
|
||||
public sealed class DependencyAssignAnalyzerTest
|
||||
{
|
||||
private static Task Verifier(string code, params DiagnosticResult[] expected)
|
||||
{
|
||||
var test = new CSharpAnalyzerTest<DependencyAssignAnalyzer, NUnitVerifier>()
|
||||
{
|
||||
TestState =
|
||||
{
|
||||
Sources = { code }
|
||||
},
|
||||
};
|
||||
|
||||
TestHelper.AddEmbeddedSources(
|
||||
test.TestState,
|
||||
"Robust.Shared.IoC.DependencyAttribute.cs"
|
||||
);
|
||||
|
||||
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
|
||||
test.TestState.ExpectedDiagnostics.AddRange(expected);
|
||||
|
||||
return test.RunAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test()
|
||||
{
|
||||
const string code = """
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
public sealed class Foo
|
||||
{
|
||||
[Dependency]
|
||||
private object? Field;
|
||||
|
||||
public Foo()
|
||||
{
|
||||
Field = "A";
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
await Verifier(code,
|
||||
// /0/Test0.cs(10,9): warning RA0025: Tried to assign to [Dependency] field 'Field'. Remove [Dependency] or inject it via field injection instead.
|
||||
VerifyCS.Diagnostic().WithSpan(10, 9, 10, 20).WithArguments("Field"));
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,13 @@
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets"/>
|
||||
<Import Project="..\MSBuild\Robust.Engine.props"/>
|
||||
|
||||
<!-- Engine source files needed to make the tests work -->
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\AccessAttribute.cs" LogicalName="Robust.Shared.Analyzers.AccessAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\AccessPermissions.cs" LogicalName="Robust.Shared.Analyzers.AccessPermissions.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\IoC\DependencyAttribute.cs" LogicalName="Robust.Shared.IoC.DependencyAttribute.cs" LinkBase="Implementations" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
22
Robust.Analyzers.Tests/TestHelper.cs
Normal file
22
Robust.Analyzers.Tests/TestHelper.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
public static class TestHelper
|
||||
{
|
||||
public static void AddEmbeddedSources(SolutionState state, params string[] embeddedFiles)
|
||||
{
|
||||
AddEmbeddedSources(state, (IEnumerable<string>) embeddedFiles);
|
||||
}
|
||||
|
||||
public static void AddEmbeddedSources(SolutionState state, IEnumerable<string> embeddedFiles)
|
||||
{
|
||||
foreach (var fileName in embeddedFiles)
|
||||
{
|
||||
using var stream = typeof(AccessAnalyzer_Test).Assembly.GetManifestResourceStream(fileName)!;
|
||||
state.Sources.Add((fileName, SourceText.From(stream)));
|
||||
}
|
||||
}
|
||||
}
|
||||
61
Robust.Analyzers/DependencyAssignAnalyzer.cs
Normal file
61
Robust.Analyzers/DependencyAssignAnalyzer.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class DependencyAssignAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string DependencyAttributeType = "Robust.Shared.IoC.DependencyAttribute";
|
||||
|
||||
private static readonly DiagnosticDescriptor Rule = new (
|
||||
Diagnostics.IdDependencyFieldAssigned,
|
||||
"Assignment to dependency field",
|
||||
"Tried to assign to [Dependency] field '{0}'. Remove [Dependency] or inject it via field injection instead.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Warning,
|
||||
true);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.EnableConcurrentExecution();
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
context.RegisterOperationAction(CheckAssignment, OperationKind.SimpleAssignment);
|
||||
}
|
||||
|
||||
private static void CheckAssignment(OperationAnalysisContext context)
|
||||
{
|
||||
if (context.Operation is not ISimpleAssignmentOperation assignment)
|
||||
return;
|
||||
|
||||
if (assignment.Target is not IFieldReferenceOperation fieldRef)
|
||||
return;
|
||||
|
||||
var field = fieldRef.Field;
|
||||
var attributes = field.GetAttributes();
|
||||
if (attributes.Length == 0)
|
||||
return;
|
||||
|
||||
var depAttribute = context.Compilation.GetTypeByMetadataName(DependencyAttributeType);
|
||||
if (!HasAttribute(attributes, depAttribute))
|
||||
return;
|
||||
|
||||
context.ReportDiagnostic(Diagnostic.Create(Rule, assignment.Syntax.GetLocation(), field.Name));
|
||||
}
|
||||
|
||||
private static bool HasAttribute(ImmutableArray<AttributeData> attributes, ISymbol symbol)
|
||||
{
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, symbol))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2,24 +2,29 @@
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Needed for NotNullableFlagAnalyzer. -->
|
||||
<Compile Include="..\Robust.Shared\Analyzers\NotNullableFlagAttribute.cs" />
|
||||
<Compile Include="..\Robust.Shared\Analyzers\NotNullableFlagAttribute.cs" LinkBase="Implementations" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Needed for FriendAnalyzer. -->
|
||||
<Compile Include="..\Robust.Shared\Analyzers\AccessAttribute.cs" />
|
||||
<Compile Include="..\Robust.Shared\Analyzers\AccessPermissions.cs" />
|
||||
<Compile Include="..\Robust.Shared\Analyzers\AccessAttribute.cs" LinkBase="Implementations" />
|
||||
<Compile Include="..\Robust.Shared\Analyzers\AccessPermissions.cs" LinkBase="Implementations" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Needed for PreferGenericVariantAnalyzer. -->
|
||||
<Compile Include="..\Robust.Shared\Analyzers\PreferGenericVariantAttribute.cs" />
|
||||
<Compile Include="..\Robust.Shared\Analyzers\PreferGenericVariantAttribute.cs" LinkBase="Implementations" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="../Robust.Roslyn.Shared/Robust.Roslyn.Shared.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Nullable>disable</Nullable>
|
||||
<!--
|
||||
Rider seems to get really confused with hot reload if we directly compile in the above-linked classes.
|
||||
As such, they have an #if to change their namespace in this project.
|
||||
-->
|
||||
<DefineConstants>$(DefineConstants);ROBUST_ANALYZERS_IMPL</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Collections;
|
||||
|
||||
namespace Robust.Benchmarks.Collections;
|
||||
|
||||
[Virtual]
|
||||
public class ValueListEnumerationBenchmarks
|
||||
{
|
||||
[Params(4, 16, 64)]
|
||||
public int N { get; set; }
|
||||
|
||||
private sealed class Data(int i)
|
||||
{
|
||||
public readonly int I = i;
|
||||
}
|
||||
|
||||
private ValueList<Data> _valueList;
|
||||
private Data[] _array = default!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
var list = new List<Data>(N);
|
||||
for (var i = 0; i < N; i++)
|
||||
{
|
||||
list.Add(new(i));
|
||||
}
|
||||
|
||||
_array = list.ToArray();
|
||||
_valueList = new(list.ToArray());
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int ValueList()
|
||||
{
|
||||
var total = 0;
|
||||
foreach (var ev in _valueList)
|
||||
{
|
||||
total += ev.I;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int ValueListSpan()
|
||||
{
|
||||
var total = 0;
|
||||
foreach (var ev in _valueList.Span)
|
||||
{
|
||||
total += ev.I;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int Array()
|
||||
{
|
||||
var total = 0;
|
||||
foreach (var ev in _array)
|
||||
{
|
||||
total += ev.I;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int Span()
|
||||
{
|
||||
var total = 0;
|
||||
foreach (var ev in _array.AsSpan())
|
||||
{
|
||||
total += ev.I;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,8 @@ public partial class AddRemoveComponentBenchmark
|
||||
.InitializeInstance();
|
||||
|
||||
_entityManager = _simulation.Resolve<IEntityManager>();
|
||||
|
||||
var coords = new MapCoordinates(0, 0, new MapId(1));
|
||||
_simulation.AddMap(coords.MapId);
|
||||
var map = _simulation.CreateMap().Uid;
|
||||
var coords = new EntityCoordinates(map, default);
|
||||
|
||||
for (var i = 0; i < N; i++)
|
||||
{
|
||||
|
||||
@@ -29,8 +29,8 @@ public partial class ComponentIteratorBenchmark
|
||||
|
||||
Comps = new A[N+2];
|
||||
|
||||
var coords = new MapCoordinates(0, 0, new MapId(1));
|
||||
_simulation.AddMap(coords.MapId);
|
||||
var map = _simulation.CreateMap().MapId;
|
||||
var coords = new MapCoordinates(default, map);
|
||||
|
||||
for (var i = 0; i < N; i++)
|
||||
{
|
||||
|
||||
@@ -31,8 +31,8 @@ public partial class GetComponentBenchmark
|
||||
|
||||
Comps = new A[N+2];
|
||||
|
||||
var coords = new MapCoordinates(0, 0, new MapId(1));
|
||||
_simulation.AddMap(coords.MapId);
|
||||
var map = _simulation.CreateMap().Uid;
|
||||
var coords = new EntityCoordinates(map, default);
|
||||
|
||||
for (var i = 0; i < N; i++)
|
||||
{
|
||||
|
||||
@@ -29,10 +29,9 @@ public partial class SpawnDeleteEntityBenchmark
|
||||
.InitializeInstance();
|
||||
|
||||
_entityManager = _simulation.Resolve<IEntityManager>();
|
||||
|
||||
_mapCoords = new MapCoordinates(0, 0, new MapId(1));
|
||||
var uid = _simulation.AddMap(_mapCoords.MapId);
|
||||
_entCoords = new EntityCoordinates(uid, 0, 0);
|
||||
var (map, mapId) = _simulation.CreateMap();
|
||||
_mapCoords = new MapCoordinates(default, mapId);
|
||||
_entCoords = new EntityCoordinates(map, 0, 0);
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
|
||||
@@ -91,8 +91,7 @@ public class RecursiveMoveBenchmark : RobustIntegrationTest
|
||||
// Set up map and spawn player
|
||||
server.WaitPost(() =>
|
||||
{
|
||||
var mapId = mapMan.CreateMap();
|
||||
var map = mapMan.GetMapEntityId(mapId);
|
||||
var map = server.ResolveDependency<SharedMapSystem>().CreateMap(out var mapId);
|
||||
var gridComp = mapMan.CreateGridEntity(mapId);
|
||||
var grid = gridComp.Owner;
|
||||
mapSys.SetTile(grid, gridComp, Vector2i.Zero, new Tile(1));
|
||||
|
||||
@@ -74,11 +74,13 @@ public sealed class AudioOverlay : Overlay
|
||||
output.Clear();
|
||||
output.AppendLine("Audio Source");
|
||||
output.AppendLine("Runtime:");
|
||||
output.AppendLine($"- Distance: {_audio.GetAudioDistance(distance.Length()):0.00}");
|
||||
output.AppendLine($"- Occlusion: {posOcclusion:0.0000}");
|
||||
output.AppendLine("Params:");
|
||||
output.AppendLine($"- RolloffFactor: {comp.RolloffFactor:0.0000}");
|
||||
output.AppendLine($"- Volume: {comp.Volume:0.0000}");
|
||||
output.AppendLine($"- Reference distance: {comp.ReferenceDistance}");
|
||||
output.AppendLine($"- Max distance: {comp.MaxDistance}");
|
||||
output.AppendLine($"- Reference distance: {comp.ReferenceDistance:0.00}");
|
||||
output.AppendLine($"- Max distance: {comp.MaxDistance:0.00}");
|
||||
var outputText = output.ToString().Trim();
|
||||
var dimensions = screenHandle.GetDimensions(_font, outputText, 1f);
|
||||
var buffer = new Vector2(3f, 3f);
|
||||
|
||||
@@ -126,6 +126,33 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
{
|
||||
component.Source.SetAuxiliary(null);
|
||||
}
|
||||
|
||||
switch (component.State)
|
||||
{
|
||||
case AudioState.Playing:
|
||||
component.StartPlaying();
|
||||
break;
|
||||
case AudioState.Paused:
|
||||
component.Pause();
|
||||
break;
|
||||
case AudioState.Stopped:
|
||||
component.StopPlaying();
|
||||
component.PlaybackPosition = 0f;
|
||||
break;
|
||||
}
|
||||
|
||||
// If playback position changed then update it.
|
||||
if (!string.IsNullOrEmpty(component.FileName))
|
||||
{
|
||||
var position = (float) ((component.PauseTime ?? Timing.CurTime) - component.AudioStart).TotalSeconds;
|
||||
var currentPosition = component.Source.PlaybackPosition;
|
||||
var diff = Math.Abs(position - currentPosition);
|
||||
|
||||
if (diff > 0.1f)
|
||||
{
|
||||
component.PlaybackPosition = position;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -361,7 +388,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
var distance = delta.Length();
|
||||
|
||||
// Out of range so just clip it for us.
|
||||
if (distance > component.MaxDistance)
|
||||
if (GetAudioDistance(distance) > component.MaxDistance)
|
||||
{
|
||||
// Still keeps the source playing, just with no volume.
|
||||
component.Gain = 0f;
|
||||
|
||||
@@ -314,6 +314,8 @@ public abstract class BaseAudioSource : IAudioSource
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
|
||||
value = MathF.Max(value, 0f);
|
||||
AL.Source(SourceHandle, ALSourcef.SecOffset, value);
|
||||
Master._checkAlError($"Tried to set invalid playback position of {value:0.00}");
|
||||
}
|
||||
|
||||
@@ -285,6 +285,7 @@ namespace Robust.Client
|
||||
/// <summary>
|
||||
/// Enumeration of the run levels of the BaseClient.
|
||||
/// </summary>
|
||||
/// <seealso cref="ClientRunLevelExt"/>
|
||||
public enum ClientRunLevel : byte
|
||||
{
|
||||
Error = 0,
|
||||
@@ -315,6 +316,21 @@ namespace Robust.Client
|
||||
SinglePlayerGame,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper functions for working with <see cref="ClientRunLevel"/>.
|
||||
/// </summary>
|
||||
public static class ClientRunLevelExt
|
||||
{
|
||||
/// <summary>
|
||||
/// Check if a <see cref="ClientRunLevel"/> is <see cref="ClientRunLevel.InGame"/>
|
||||
/// or <see cref="ClientRunLevel.SinglePlayerGame"/>.
|
||||
/// </summary>
|
||||
public static bool IsInGameLike(this ClientRunLevel runLevel)
|
||||
{
|
||||
return runLevel is ClientRunLevel.InGame or ClientRunLevel.SinglePlayerGame;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event arguments for when something changed with the player.
|
||||
/// </summary>
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var type = Type.GetType(args[0]);
|
||||
var type = GetType(args[0]);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
@@ -25,6 +25,17 @@ namespace Robust.Client.Console.Commands
|
||||
shell.WriteLine(sig);
|
||||
}
|
||||
}
|
||||
|
||||
private Type? GetType(string name)
|
||||
{
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
if (assembly.GetType(name) is { } type)
|
||||
return type;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -8,11 +8,9 @@ using System.Text;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.ComponentTrees;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Graphics.RSI;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
@@ -1237,9 +1235,9 @@ namespace Robust.Client.GameObjects
|
||||
public IEnumerable<ISpriteLayer> AllLayers => Layers;
|
||||
|
||||
// Lobby SpriteView rendering path
|
||||
public void Render(DrawingHandleWorld drawingHandle, Angle eyeRotation, Angle worldRotation, Direction? overrideDirection = null)
|
||||
public void Render(DrawingHandleWorld drawingHandle, Angle eyeRotation, Angle worldRotation, Direction? overrideDirection = null, Vector2 position = default)
|
||||
{
|
||||
RenderInternal(drawingHandle, eyeRotation, worldRotation, Vector2.Zero, overrideDirection);
|
||||
RenderInternal(drawingHandle, eyeRotation, worldRotation, position, overrideDirection);
|
||||
}
|
||||
|
||||
[DataField("noRot")] private bool _screenLock = false;
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace Robust.Client.GameObjects
|
||||
toDelete.Add(id);
|
||||
}
|
||||
|
||||
foreach (var dead in toDelete)
|
||||
foreach (var dead in toDelete.Span)
|
||||
{
|
||||
component.Containers.Remove(dead);
|
||||
}
|
||||
@@ -142,7 +142,7 @@ namespace Robust.Client.GameObjects
|
||||
toRemove.Add(entity);
|
||||
}
|
||||
|
||||
foreach (var entity in toRemove)
|
||||
foreach (var entity in toRemove.Span)
|
||||
{
|
||||
Remove(
|
||||
(entity, TransformQuery.GetComponent(entity), MetaQuery.GetComponent(entity)),
|
||||
@@ -162,7 +162,7 @@ namespace Robust.Client.GameObjects
|
||||
removedExpected.Add(netEntity);
|
||||
}
|
||||
|
||||
foreach (var entityUid in removedExpected)
|
||||
foreach (var entityUid in removedExpected.Span)
|
||||
{
|
||||
RemoveExpectedEntity(entityUid, out _);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Map;
|
||||
using Robust.Client.Physics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
@@ -16,6 +13,17 @@ public sealed class MapSystem : SharedMapSystem
|
||||
[Dependency] private readonly IResourceCache _resource = default!;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
||||
|
||||
protected override MapId GetNextMapId()
|
||||
{
|
||||
// Client-side map entities use negative map Ids to avoid conflict with server-side maps.
|
||||
var id = new MapId(--LastMapId);
|
||||
while (MapManager.MapExists(id))
|
||||
{
|
||||
id = new MapId(--LastMapId);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -27,9 +35,4 @@ public sealed class MapSystem : SharedMapSystem
|
||||
base.Shutdown();
|
||||
_overlayManager.RemoveOverlay<TileEdgeOverlay>();
|
||||
}
|
||||
|
||||
protected override void OnMapAdd(EntityUid uid, MapComponent component, ComponentAdd args)
|
||||
{
|
||||
EnsureComp<PhysicsMapComponent>(uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1138,7 +1138,7 @@ namespace Robust.Client.GameStates
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) != 0 &&
|
||||
metas.TryGetComponent(xform.ParentUid, out var containerMeta) &&
|
||||
(containerMeta.Flags & MetaDataFlags.Detached) == 0 &&
|
||||
containerSys.TryGetContainingContainer(xform.ParentUid, ent.Value, out container, null, true))
|
||||
containerSys.TryGetContainingContainer(xform.ParentUid, ent.Value, out container))
|
||||
{
|
||||
containerSys.Remove((ent.Value, xform, meta), container, false, true);
|
||||
}
|
||||
@@ -1329,23 +1329,8 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var (comp, cur, next) in _compStateWork.Values)
|
||||
{
|
||||
try
|
||||
{
|
||||
var handleState = new ComponentHandleState(cur, next);
|
||||
bus.RaiseComponentEvent(comp, ref handleState);
|
||||
}
|
||||
#pragma warning disable CS0168 // Variable is declared but never used
|
||||
catch (Exception e)
|
||||
#pragma warning restore CS0168 // Variable is declared but never used
|
||||
{
|
||||
#if EXCEPTION_TOLERANCE
|
||||
_sawmill.Error($"Failed to apply comp state: entity={_entities.ToPrettyString(uid)}, comp={comp.GetType()}");
|
||||
_runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(HandleEntityState)}");
|
||||
#else
|
||||
_sawmill.Error($"Failed to apply comp state: entity={_entities.ToPrettyString(uid)}, comp={comp.GetType()}");
|
||||
throw;
|
||||
#endif
|
||||
}
|
||||
var handleState = new ComponentHandleState(cur, next);
|
||||
bus.RaiseComponentEvent(comp, ref handleState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1414,7 +1399,7 @@ namespace Robust.Client.GameStates
|
||||
_entities.TryGetComponent(xform.ParentUid, out MetaDataComponent? containerMeta) &&
|
||||
(containerMeta.Flags & MetaDataFlags.Detached) == 0)
|
||||
{
|
||||
containerSys.TryGetContainingContainer(xform.ParentUid, uid, out container, null, true);
|
||||
containerSys.TryGetContainingContainer(xform.ParentUid, uid, out container);
|
||||
}
|
||||
|
||||
_entities.EntitySysManager.GetEntitySystem<TransformSystem>().DetachParentToNull(uid, xform);
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
foreach (var (grid, chunks) in _mapChunkData)
|
||||
{
|
||||
var gridComp = _mapManager.GetGridComp(grid);
|
||||
var gridComp = _entityManager.GetComponent<MapGridComponent>(grid);
|
||||
foreach (var (index, chunk) in chunks)
|
||||
{
|
||||
if (!chunk.Dirty || gridComp.Chunks.ContainsKey(index))
|
||||
|
||||
@@ -11,6 +11,7 @@ using Robust.Shared;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Profiling;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -250,10 +251,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private void DrawEntities(Viewport viewport, Box2Rotated worldBounds, Box2 worldAABB, IEye eye)
|
||||
{
|
||||
var mapId = eye.Position.MapId;
|
||||
if (mapId == MapId.Nullspace || !_mapManager.HasMapEntity(mapId))
|
||||
{
|
||||
if (mapId == MapId.Nullspace)
|
||||
return;
|
||||
}
|
||||
|
||||
RenderOverlays(viewport, OverlaySpace.WorldSpaceBelowEntities, worldAABB, worldBounds);
|
||||
var worldOverlays = GetOverlaysForSpace(OverlaySpace.WorldSpaceEntities);
|
||||
@@ -514,7 +513,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
if (_lightManager.Enabled && _lightManager.DrawHardFov && eye.DrawLight && eye.DrawFov)
|
||||
{
|
||||
ApplyFovToBuffer(viewport, eye);
|
||||
var mapUid = _mapManager.GetMapEntityId(eye.Position.MapId);
|
||||
if (_entityManager.GetComponent<MapComponent>(mapUid).LightingEnabled)
|
||||
ApplyFovToBuffer(viewport, eye);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
[Prototype("shader")]
|
||||
public sealed class ShaderPrototype : IPrototype, ISerializationHooks
|
||||
public sealed partial class ShaderPrototype : IPrototype, ISerializationHooks
|
||||
{
|
||||
[ViewVariables]
|
||||
[IdDataField]
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Robust.Client.UserInterface
|
||||
toRemove.Add(key);
|
||||
}
|
||||
|
||||
foreach (var key in toRemove)
|
||||
foreach (var key in toRemove.Span)
|
||||
{
|
||||
_playingAnimations.Remove(key);
|
||||
AnimationCompleted?.Invoke(key);
|
||||
|
||||
132
Robust.Client/UserInterface/Control.Layout.Styling.cs
Normal file
132
Robust.Client/UserInterface/Control.Layout.Styling.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Robust.Client.UserInterface;
|
||||
|
||||
public partial class Control
|
||||
{
|
||||
private LayoutStyleProperties _layoutStyleOverride;
|
||||
private LayoutStyleProperties _layoutStyleSheet;
|
||||
|
||||
private void UpdateLayoutStyleProperties()
|
||||
{
|
||||
var propertiesSet = LayoutStyleProperties.None;
|
||||
|
||||
// Assumed most controls will have little or no style properties,
|
||||
// so iterating once is less expensive overall then checking 10+ properties.
|
||||
// C# switch statements are compiled efficiently anyways.
|
||||
foreach (var (key, value) in _styleProperties)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case nameof(SizeFlagsStretchRatio):
|
||||
UpdateField(ref _sizeFlagsStretchRatio, value, LayoutStyleProperties.StretchRatio);
|
||||
break;
|
||||
case nameof(MinWidth):
|
||||
UpdateField(ref _minWidth, value, LayoutStyleProperties.MinWidth);
|
||||
break;
|
||||
case nameof(MinHeight):
|
||||
UpdateField(ref _minHeight, value, LayoutStyleProperties.MinHeight);
|
||||
break;
|
||||
case nameof(SetWidth):
|
||||
UpdateField(ref _setWidth, value, LayoutStyleProperties.SetWidth);
|
||||
break;
|
||||
case nameof(SetHeight):
|
||||
UpdateField(ref _setHeight, value, LayoutStyleProperties.SetHeight);
|
||||
break;
|
||||
case nameof(MaxWidth):
|
||||
UpdateField(ref _maxWidth, value, LayoutStyleProperties.MaxWidth);
|
||||
break;
|
||||
case nameof(MaxHeight):
|
||||
UpdateField(ref _maxHeight, value, LayoutStyleProperties.MaxHeight);
|
||||
break;
|
||||
case nameof(HorizontalExpand):
|
||||
UpdateField(ref _horizontalExpand, value, LayoutStyleProperties.HorizontalExpand);
|
||||
break;
|
||||
case nameof(VerticalExpand):
|
||||
UpdateField(ref _verticalExpand, value, LayoutStyleProperties.VerticalExpand);
|
||||
break;
|
||||
case nameof(HorizontalAlignment):
|
||||
UpdateField(ref _horizontalAlignment, value, LayoutStyleProperties.HorizontalAlignment);
|
||||
break;
|
||||
case nameof(VerticalAlignment):
|
||||
UpdateField(ref _verticalAlignment, value, LayoutStyleProperties.VerticalAlignment);
|
||||
break;
|
||||
case nameof(Margin):
|
||||
UpdateField(ref _margin, value, LayoutStyleProperties.Margin);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset cleared properties back to defaults.
|
||||
var toClear = _layoutStyleSheet & ~propertiesSet;
|
||||
if (toClear != 0)
|
||||
{
|
||||
ClearField(ref _sizeFlagsStretchRatio, DefaultStretchRatio, LayoutStyleProperties.StretchRatio);
|
||||
ClearField(ref _minWidth, 0, LayoutStyleProperties.MinWidth);
|
||||
ClearField(ref _minHeight, 0, LayoutStyleProperties.MinHeight);
|
||||
ClearField(ref _setWidth, DefaultSetSize, LayoutStyleProperties.SetWidth);
|
||||
ClearField(ref _setHeight, DefaultSetSize, LayoutStyleProperties.SetHeight);
|
||||
ClearField(ref _maxWidth, DefaultMaxSize, LayoutStyleProperties.MaxWidth);
|
||||
ClearField(ref _maxHeight, DefaultMaxSize, LayoutStyleProperties.MaxHeight);
|
||||
ClearField(ref _horizontalExpand, false, LayoutStyleProperties.HorizontalExpand);
|
||||
ClearField(ref _verticalExpand, false, LayoutStyleProperties.VerticalExpand);
|
||||
ClearField(ref _horizontalAlignment, DefaultHAlignment, LayoutStyleProperties.HorizontalAlignment);
|
||||
ClearField(ref _verticalAlignment, DefaultVAlignment, LayoutStyleProperties.VerticalAlignment);
|
||||
ClearField(ref _margin, default, LayoutStyleProperties.Margin);
|
||||
}
|
||||
|
||||
_layoutStyleSheet = propertiesSet;
|
||||
|
||||
return;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void UpdateField<T>(ref T field, object value, LayoutStyleProperties flag)
|
||||
{
|
||||
if ((_layoutStyleOverride & flag) != 0)
|
||||
return;
|
||||
|
||||
// TODO: Probably need better error handling...
|
||||
if (value is not T valueCast)
|
||||
return;
|
||||
|
||||
field = valueCast;
|
||||
propertiesSet |= flag;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void ClearField<T>(ref T field, T defaultValue, LayoutStyleProperties flag)
|
||||
{
|
||||
if ((toClear & flag) == 0)
|
||||
return;
|
||||
|
||||
field = defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void SetLayoutStyleProp(LayoutStyleProperties flag)
|
||||
{
|
||||
_layoutStyleOverride |= flag;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum LayoutStyleProperties : short
|
||||
{
|
||||
// @formatter:off
|
||||
None = 0,
|
||||
Margin = 1 << 0,
|
||||
MinWidth = 1 << 1,
|
||||
MinHeight = 1 << 2,
|
||||
SetWidth = 1 << 3,
|
||||
SetHeight = 1 << 4,
|
||||
MaxWidth = 1 << 5,
|
||||
MaxHeight = 1 << 6,
|
||||
StretchRatio = 1 << 7,
|
||||
HorizontalExpand = 1 << 8,
|
||||
VerticalExpand = 1 << 9,
|
||||
HorizontalAlignment = 1 << 10,
|
||||
VerticalAlignment = 1 << 11,
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
@@ -12,24 +12,30 @@ namespace Robust.Client.UserInterface
|
||||
|
||||
public partial class Control
|
||||
{
|
||||
private const float DefaultStretchRatio = 1;
|
||||
private const float DefaultSetSize = float.NaN;
|
||||
private const float DefaultMaxSize = float.PositiveInfinity;
|
||||
private const HAlignment DefaultHAlignment = HAlignment.Stretch;
|
||||
private const VAlignment DefaultVAlignment = VAlignment.Stretch;
|
||||
|
||||
private Vector2 _size;
|
||||
|
||||
[ViewVariables] internal Vector2? PreviousMeasure;
|
||||
[ViewVariables] internal UIBox2? PreviousArrange;
|
||||
|
||||
private float _sizeFlagsStretchRatio = 1;
|
||||
private float _sizeFlagsStretchRatio = DefaultStretchRatio;
|
||||
|
||||
private float _minWidth;
|
||||
private float _minHeight;
|
||||
private float _setWidth = float.NaN;
|
||||
private float _setHeight = float.NaN;
|
||||
private float _maxWidth = float.PositiveInfinity;
|
||||
private float _maxHeight = float.PositiveInfinity;
|
||||
private float _setWidth = DefaultSetSize;
|
||||
private float _setHeight = DefaultSetSize;
|
||||
private float _maxWidth = DefaultMaxSize;
|
||||
private float _maxHeight = DefaultMaxSize;
|
||||
|
||||
private bool _horizontalExpand;
|
||||
private bool _verticalExpand;
|
||||
private HAlignment _horizontalAlignment = HAlignment.Stretch;
|
||||
private VAlignment _verticalAlignment = VAlignment.Stretch;
|
||||
private HAlignment _horizontalAlignment = DefaultHAlignment;
|
||||
private VAlignment _verticalAlignment = DefaultVAlignment;
|
||||
private Thickness _margin;
|
||||
private bool _measuring;
|
||||
private bool _arranging;
|
||||
@@ -53,6 +59,7 @@ namespace Robust.Client.UserInterface
|
||||
set
|
||||
{
|
||||
_margin = value;
|
||||
SetLayoutStyleProp(LayoutStyleProperties.Margin);
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
@@ -242,6 +249,7 @@ namespace Robust.Client.UserInterface
|
||||
set
|
||||
{
|
||||
_horizontalAlignment = value;
|
||||
SetLayoutStyleProp(LayoutStyleProperties.HorizontalAlignment);
|
||||
InvalidateArrange();
|
||||
}
|
||||
}
|
||||
@@ -258,6 +266,7 @@ namespace Robust.Client.UserInterface
|
||||
set
|
||||
{
|
||||
_verticalAlignment = value;
|
||||
SetLayoutStyleProp(LayoutStyleProperties.VerticalAlignment);
|
||||
InvalidateArrange();
|
||||
}
|
||||
}
|
||||
@@ -276,6 +285,7 @@ namespace Robust.Client.UserInterface
|
||||
set
|
||||
{
|
||||
_horizontalExpand = value;
|
||||
SetLayoutStyleProp(LayoutStyleProperties.HorizontalExpand);
|
||||
Parent?.InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
@@ -294,6 +304,7 @@ namespace Robust.Client.UserInterface
|
||||
set
|
||||
{
|
||||
_verticalExpand = value;
|
||||
SetLayoutStyleProp(LayoutStyleProperties.VerticalExpand);
|
||||
Parent?.InvalidateArrange();
|
||||
}
|
||||
}
|
||||
@@ -318,6 +329,7 @@ namespace Robust.Client.UserInterface
|
||||
|
||||
_sizeFlagsStretchRatio = value;
|
||||
|
||||
SetLayoutStyleProp(LayoutStyleProperties.StretchRatio);
|
||||
Parent?.InvalidateArrange();
|
||||
}
|
||||
}
|
||||
@@ -394,6 +406,7 @@ namespace Robust.Client.UserInterface
|
||||
set
|
||||
{
|
||||
_minWidth = value;
|
||||
SetLayoutStyleProp(LayoutStyleProperties.MinWidth);
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
@@ -408,6 +421,7 @@ namespace Robust.Client.UserInterface
|
||||
set
|
||||
{
|
||||
_minHeight = value;
|
||||
SetLayoutStyleProp(LayoutStyleProperties.MinHeight);
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
@@ -422,6 +436,7 @@ namespace Robust.Client.UserInterface
|
||||
set
|
||||
{
|
||||
_setWidth = value;
|
||||
SetLayoutStyleProp(LayoutStyleProperties.SetWidth);
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
@@ -436,6 +451,7 @@ namespace Robust.Client.UserInterface
|
||||
set
|
||||
{
|
||||
_setHeight = value;
|
||||
SetLayoutStyleProp(LayoutStyleProperties.SetHeight);
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
@@ -450,6 +466,7 @@ namespace Robust.Client.UserInterface
|
||||
set
|
||||
{
|
||||
_maxWidth = value;
|
||||
SetLayoutStyleProp(LayoutStyleProperties.MaxWidth);
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
@@ -464,6 +481,7 @@ namespace Robust.Client.UserInterface
|
||||
set
|
||||
{
|
||||
_maxHeight = value;
|
||||
SetLayoutStyleProp(LayoutStyleProperties.MaxHeight);
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +239,7 @@ namespace Robust.Client.UserInterface
|
||||
|
||||
protected virtual void StylePropertiesChanged()
|
||||
{
|
||||
UpdateLayoutStyleProperties();
|
||||
InvalidateMeasure();
|
||||
}
|
||||
|
||||
|
||||
@@ -641,7 +641,11 @@ namespace Robust.Client.UserInterface
|
||||
|
||||
foreach (var child in Children.ToArray())
|
||||
{
|
||||
RemoveChild(child);
|
||||
// This checks fails in some obscure cases like using the element inspector in the dev window.
|
||||
// Why? Well I could probably spend 15 minutes in a debugger to find out,
|
||||
// but I'd probably still end up with this fix.
|
||||
if (child.Parent == this)
|
||||
RemoveChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controllers;
|
||||
|
||||
// Notices your UIController, *UwU Whats this?*
|
||||
/// <summary>
|
||||
/// Each <see cref="UIController"/> is instantiated as a singleton by <see cref="UserInterfaceManager"/>
|
||||
/// <see cref="UIController"/> can use <see cref="DependencyAttribute"/> for regular IoC dependencies
|
||||
|
||||
@@ -4,6 +4,8 @@ using System.Numerics;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -20,6 +22,8 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public class LineEdit : Control
|
||||
{
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private const float MouseScrollDelay = 0.001f;
|
||||
|
||||
@@ -46,6 +50,9 @@ namespace Robust.Client.UserInterface.Controls
|
||||
private bool _mouseSelectingText;
|
||||
private float _lastMousePosition;
|
||||
|
||||
private TimeSpan? _lastClickTime;
|
||||
private Vector2? _lastClickPosition;
|
||||
|
||||
private bool IsPlaceHolderVisible => string.IsNullOrEmpty(_text) && _placeHolder != null;
|
||||
|
||||
public event Action<LineEditEventArgs>? OnTextChanged;
|
||||
@@ -685,8 +692,26 @@ namespace Robust.Client.UserInterface.Controls
|
||||
args.Handle();
|
||||
}
|
||||
}
|
||||
// Double-clicking. Clicks delay should be <= 250ms and the distance < 10 pixels.
|
||||
else if (args.Function == EngineKeyFunctions.UIClick && _lastClickPosition != null && _lastClickTime != null
|
||||
&& _timing.RealTime - _lastClickTime <= TimeSpan.FromMilliseconds(_cfgManager.GetCVar(CVars.DoubleClickDelay))
|
||||
&& (_lastClickPosition.Value - args.PointerLocation.Position).IsShorterThan(_cfgManager.GetCVar(CVars.DoubleClickRange)))
|
||||
{
|
||||
_lastClickTime = _timing.RealTime;
|
||||
_lastClickPosition = args.PointerLocation.Position;
|
||||
|
||||
_lastMousePosition = args.RelativePosition.X;
|
||||
|
||||
_selectionStart = TextEditShared.PrevWordPosition(_text, GetIndexAtPos(args.RelativePosition.X));
|
||||
_cursorPosition = TextEditShared.EndWordPosition(_text, GetIndexAtPos(args.RelativePosition.X));
|
||||
|
||||
args.Handle();
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastClickTime = _timing.RealTime;
|
||||
_lastClickPosition = args.PointerLocation.Position;
|
||||
|
||||
_mouseSelectingText = true;
|
||||
_lastMousePosition = args.RelativePosition.X;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using Robust.Client.UserInterface.RichText;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
@@ -16,6 +17,26 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
private FormattedMessage? _message;
|
||||
private RichTextEntry _entry;
|
||||
private float _lineHeightScale = 1;
|
||||
private bool _lineHeightOverride;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float LineHeightScale
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_lineHeightOverride && TryGetStyleProperty(nameof(LineHeightScale), out float value))
|
||||
return value;
|
||||
|
||||
return _lineHeightScale;
|
||||
}
|
||||
set
|
||||
{
|
||||
_lineHeightScale = value;
|
||||
_lineHeightOverride = true;
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
|
||||
public RichTextLabel()
|
||||
{
|
||||
@@ -47,7 +68,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
|
||||
var font = _getFont();
|
||||
_entry.Update(font, availableSize.X * UIScale, UIScale);
|
||||
_entry.Update(font, availableSize.X * UIScale, UIScale, LineHeightScale);
|
||||
|
||||
return new Vector2(_entry.Width / UIScale, _entry.Height / UIScale);
|
||||
}
|
||||
@@ -61,7 +82,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
return;
|
||||
}
|
||||
|
||||
_entry.Draw(handle, _getFont(), SizeBox, 0, new MarkupDrawingContext(), UIScale);
|
||||
_entry.Draw(handle, _getFont(), SizeBox, 0, new MarkupDrawingContext(), UIScale, LineHeightScale);
|
||||
}
|
||||
|
||||
[Pure]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using static Robust.Client.UserInterface.Controls.LayoutContainer;
|
||||
|
||||
@@ -32,6 +33,11 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
public bool Grabbed => _grabbed;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the slider can be adjusted.
|
||||
/// </summary>
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
public StyleBox? ForegroundStyleBoxOverride
|
||||
{
|
||||
get => _foregroundStyleBoxOverride;
|
||||
@@ -132,7 +138,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
base.KeyBindDown(args);
|
||||
|
||||
if (args.Function != EngineKeyFunctions.UIClick)
|
||||
if (args.Function != EngineKeyFunctions.UIClick || Disabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -146,7 +152,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
base.KeyBindUp(args);
|
||||
|
||||
if (args.Function != EngineKeyFunctions.UIClick) return;
|
||||
if (args.Function != EngineKeyFunctions.UIClick || !_grabbed) return;
|
||||
|
||||
_grabbed = false;
|
||||
OnReleased?.Invoke(this);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<!-- Organised with tabs in a vertical list to the left and the contents to the right -->
|
||||
<BoxContainer Name="Container" xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:graphics="clr-namespace:Robust.Client.Graphics"
|
||||
Orientation="Horizontal"
|
||||
MouseFilter="Pass">
|
||||
<ScrollContainer VerticalExpand="True"
|
||||
HScrollEnabled="False"
|
||||
ReturnMeasure="True"
|
||||
Margin="5 5 0 5">
|
||||
<PanelContainer Margin="5" VerticalExpand="False"
|
||||
VerticalAlignment="Top">
|
||||
<PanelContainer.PanelOverride>
|
||||
<graphics:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Name="TabContainer" Margin="5" Orientation="Vertical"/>
|
||||
</PanelContainer>
|
||||
</ScrollContainer>
|
||||
<ScrollContainer VerticalExpand="True"
|
||||
HScrollEnabled="False"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalExpand="True"
|
||||
ReturnMeasure="True"
|
||||
Margin="0 5 5 5">
|
||||
<PanelContainer Margin="5" HorizontalExpand="True" HorizontalAlignment="Stretch">
|
||||
<PanelContainer.PanelOverride>
|
||||
<graphics:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Margin="5"
|
||||
Orientation="Vertical"
|
||||
Name="ContentsContainer"/>
|
||||
</PanelContainer>
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
@@ -0,0 +1,97 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class VerticalTabContainer : BoxContainer
|
||||
{
|
||||
private readonly Dictionary<Control, BaseButton> _tabs = new();
|
||||
|
||||
// Just used to order controls in case one gets removed.
|
||||
private readonly List<Control> _controls = new();
|
||||
|
||||
private readonly ButtonGroup _tabGroup = new(false);
|
||||
|
||||
private Control? _currentControl;
|
||||
|
||||
public VerticalTabContainer()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public int AddTab(Control control, string title)
|
||||
{
|
||||
var button = new Button()
|
||||
{
|
||||
Text = title,
|
||||
Group = _tabGroup,
|
||||
};
|
||||
|
||||
TabContainer.AddChild(button);
|
||||
ContentsContainer.AddChild(control);
|
||||
var index = ChildCount - 1;
|
||||
button.OnPressed += args =>
|
||||
{
|
||||
SelectTab(control);
|
||||
};
|
||||
|
||||
_controls.Add(control);
|
||||
_tabs.Add(control, button);
|
||||
|
||||
// Existing tabs
|
||||
if (ContentsContainer.ChildCount > 1)
|
||||
{
|
||||
control.Visible = false;
|
||||
}
|
||||
// First tab
|
||||
else
|
||||
{
|
||||
SelectTab(control);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
protected override void ChildRemoved(Control child)
|
||||
{
|
||||
if (_tabs.Remove(child, out var button))
|
||||
{
|
||||
button.Dispose();
|
||||
}
|
||||
|
||||
// Set the current tab to a different control
|
||||
if (_currentControl == child)
|
||||
{
|
||||
var previous = _controls.IndexOf(child) - 1;
|
||||
|
||||
if (previous > -1)
|
||||
{
|
||||
var setControl = _controls[previous];
|
||||
SelectTab(setControl);
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentControl = null;
|
||||
}
|
||||
}
|
||||
|
||||
_controls.Remove(child);
|
||||
base.ChildRemoved(child);
|
||||
}
|
||||
|
||||
private void SelectTab(Control control)
|
||||
{
|
||||
if (_currentControl != null)
|
||||
{
|
||||
_currentControl.Visible = false;
|
||||
}
|
||||
|
||||
var button = _tabs[control];
|
||||
button.Pressed = true;
|
||||
control.Visible = true;
|
||||
_currentControl = control;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ namespace Robust.Client.UserInterface.CustomControls.DebugMonitorControls
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IClyde _displayManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IBaseClient _baseClient = default!;
|
||||
|
||||
private readonly StringBuilder _textBuilder = new();
|
||||
private readonly char[] _textBuffer = new char[1024];
|
||||
@@ -58,30 +59,36 @@ namespace Robust.Client.UserInterface.CustomControls.DebugMonitorControls
|
||||
|
||||
_textBuilder.Clear();
|
||||
|
||||
var isInGame = _baseClient.RunLevel.IsInGameLike();
|
||||
var mouseScreenPos = _inputManager.MouseScreenPosition;
|
||||
var screenSize = _displayManager.ScreenSize;
|
||||
var screenScale = _displayManager.MainWindow.ContentScale;
|
||||
|
||||
EntityCoordinates mouseGridPos;
|
||||
TileRef tile;
|
||||
EntityCoordinates mouseGridPos = default;
|
||||
TileRef tile = default;
|
||||
MapCoordinates mouseWorldMap = default;
|
||||
|
||||
var mouseWorldMap = _eyeManager.PixelToMap(mouseScreenPos);
|
||||
if (mouseWorldMap == MapCoordinates.Nullspace)
|
||||
return;
|
||||
|
||||
var mapSystem = _entityManager.System<SharedMapSystem>();
|
||||
var xformSystem = _entityManager.System<SharedTransformSystem>();
|
||||
|
||||
if (_mapManager.TryFindGridAt(mouseWorldMap, out var mouseGridUid, out var mouseGrid))
|
||||
if (isInGame)
|
||||
{
|
||||
mouseGridPos = mapSystem.MapToGrid(mouseGridUid, mouseWorldMap);
|
||||
tile = mapSystem.GetTileRef(mouseGridUid, mouseGrid, mouseGridPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
mouseGridPos = new EntityCoordinates(_mapManager.GetMapEntityId(mouseWorldMap.MapId),
|
||||
mouseWorldMap.Position);
|
||||
tile = new TileRef(EntityUid.Invalid, mouseGridPos.ToVector2i(_entityManager, _mapManager, xformSystem), Tile.Empty);
|
||||
mouseWorldMap = _eyeManager.PixelToMap(mouseScreenPos);
|
||||
if (mouseWorldMap != MapCoordinates.Nullspace)
|
||||
{
|
||||
var mapSystem = _entityManager.System<SharedMapSystem>();
|
||||
var xformSystem = _entityManager.System<SharedTransformSystem>();
|
||||
|
||||
if (_mapManager.TryFindGridAt(mouseWorldMap, out var mouseGridUid, out var mouseGrid))
|
||||
{
|
||||
mouseGridPos = mapSystem.MapToGrid(mouseGridUid, mouseWorldMap);
|
||||
tile = mapSystem.GetTileRef(mouseGridUid, mouseGrid, mouseGridPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
mouseGridPos = new EntityCoordinates(_mapManager.GetMapEntityId(mouseWorldMap.MapId),
|
||||
mouseWorldMap.Position);
|
||||
tile = new TileRef(EntityUid.Invalid,
|
||||
mouseGridPos.ToVector2i(_entityManager, _mapManager, xformSystem), Tile.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var controlHovered = UserInterfaceManager.CurrentlyHovered;
|
||||
@@ -95,32 +102,37 @@ Mouse Pos:
|
||||
{tile}
|
||||
GUI: {controlHovered}");
|
||||
|
||||
_textBuilder.AppendLine("\nAttached NetEntity:");
|
||||
var controlledEntity = _playerManager.LocalSession?.AttachedEntity ?? EntityUid.Invalid;
|
||||
|
||||
if (controlledEntity == EntityUid.Invalid)
|
||||
if (isInGame)
|
||||
{
|
||||
_textBuilder.AppendLine("No attached netentity.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var entityTransform = _entityManager.GetComponent<TransformComponent>(controlledEntity);
|
||||
var playerWorldOffset = xformSystem.GetMapCoordinates(entityTransform);
|
||||
var playerScreen = _eyeManager.WorldToScreen(playerWorldOffset.Position);
|
||||
var xformSystem = _entityManager.System<SharedTransformSystem>();
|
||||
|
||||
var playerCoordinates = entityTransform.Coordinates;
|
||||
var playerRotation = xformSystem.GetWorldRotation(entityTransform);
|
||||
var gridRotation = entityTransform.GridUid != null
|
||||
? xformSystem.GetWorldRotation(entityTransform.GridUid.Value)
|
||||
: Angle.Zero;
|
||||
_textBuilder.AppendLine("\nAttached NetEntity:");
|
||||
var controlledEntity = _playerManager.LocalSession?.AttachedEntity ?? EntityUid.Invalid;
|
||||
|
||||
_textBuilder.Append($@" Screen: {playerScreen}
|
||||
if (controlledEntity == EntityUid.Invalid)
|
||||
{
|
||||
_textBuilder.AppendLine("No attached netentity.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var entityTransform = _entityManager.GetComponent<TransformComponent>(controlledEntity);
|
||||
var playerWorldOffset = xformSystem.GetMapCoordinates(entityTransform);
|
||||
var playerScreen = _eyeManager.WorldToScreen(playerWorldOffset.Position);
|
||||
|
||||
var playerCoordinates = entityTransform.Coordinates;
|
||||
var playerRotation = xformSystem.GetWorldRotation(entityTransform);
|
||||
var gridRotation = entityTransform.GridUid != null
|
||||
? xformSystem.GetWorldRotation(entityTransform.GridUid.Value)
|
||||
: Angle.Zero;
|
||||
|
||||
_textBuilder.Append($@" Screen: {playerScreen}
|
||||
{playerWorldOffset}
|
||||
{_entityManager.GetNetCoordinates(playerCoordinates)}
|
||||
Rotation: {playerRotation.Degrees:F2}°
|
||||
NEntId: {_entityManager.GetNetEntity(controlledEntity)}
|
||||
Grid NEntId: {_entityManager.GetNetEntity(entityTransform.GridUid)}
|
||||
Grid Rotation: {gridRotation.Degrees:F2}°");
|
||||
}
|
||||
}
|
||||
|
||||
_contents.TextMemory = FormatHelpers.BuilderToMemory(_textBuilder, _textBuffer);
|
||||
|
||||
@@ -5,7 +5,7 @@ using Robust.Shared.Utility;
|
||||
namespace Robust.Client.UserInterface.RichText;
|
||||
|
||||
[Prototype("font")]
|
||||
public sealed class FontPrototype : IPrototype
|
||||
public sealed partial class FontPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
@@ -71,7 +71,8 @@ namespace Robust.Client.UserInterface
|
||||
/// <param name="defaultFont">The font being used for display.</param>
|
||||
/// <param name="maxSizeX">The maximum horizontal size of the container of this entry.</param>
|
||||
/// <param name="uiScale"></param>
|
||||
public void Update(Font defaultFont, float maxSizeX, float uiScale)
|
||||
/// <param name="lineHeightScale"></param>
|
||||
public void Update(Font defaultFont, float maxSizeX, float uiScale, float lineHeightScale = 1)
|
||||
{
|
||||
// This method is gonna suck due to complexity.
|
||||
// Bear with me here.
|
||||
@@ -159,7 +160,7 @@ namespace Robust.Client.UserInterface
|
||||
if (!context.Font.TryPeek(out var font))
|
||||
font = defaultFont;
|
||||
|
||||
src.Height += font.GetLineHeight(uiScale);
|
||||
src.Height += GetLineHeight(font, uiScale, lineHeightScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,7 +171,8 @@ namespace Robust.Client.UserInterface
|
||||
UIBox2 drawBox,
|
||||
float verticalOffset,
|
||||
MarkupDrawingContext context,
|
||||
float uiScale)
|
||||
float uiScale,
|
||||
float lineHeightScale = 1)
|
||||
{
|
||||
context.Clear();
|
||||
context.Color.Push(_defaultColor);
|
||||
@@ -197,7 +199,7 @@ namespace Robust.Client.UserInterface
|
||||
if (lineBreakIndex < LineBreaks.Count &&
|
||||
LineBreaks[lineBreakIndex] == globalBreakCounter)
|
||||
{
|
||||
baseLine = new Vector2(drawBox.Left, baseLine.Y + font.GetLineHeight(uiScale) + controlYAdvance);
|
||||
baseLine = new Vector2(drawBox.Left, baseLine.Y + GetLineHeight(font, uiScale, lineHeightScale) + controlYAdvance);
|
||||
controlYAdvance = 0;
|
||||
lineBreakIndex += 1;
|
||||
}
|
||||
@@ -216,7 +218,7 @@ namespace Robust.Client.UserInterface
|
||||
control.Position = new Vector2(baseLine.X * invertedScale, (baseLine.Y - defaultFont.GetAscent(uiScale)) * invertedScale);
|
||||
control.Measure(new Vector2(Width, Height));
|
||||
var advanceX = control.DesiredPixelSize.X;
|
||||
controlYAdvance = Math.Max(0f, (control.DesiredPixelSize.Y - font.GetLineHeight(uiScale)) * invertedScale);
|
||||
controlYAdvance = Math.Max(0f, (control.DesiredPixelSize.Y - GetLineHeight(font, uiScale, lineHeightScale)) * invertedScale);
|
||||
baseLine += new Vector2(advanceX, 0);
|
||||
}
|
||||
}
|
||||
@@ -242,5 +244,11 @@ namespace Robust.Client.UserInterface
|
||||
tag.PopDrawContext(node, context);
|
||||
return tag.TextAfter(node);
|
||||
}
|
||||
|
||||
private static int GetLineHeight(Font font, float uiScale, float lineHeightScale)
|
||||
{
|
||||
var height = font.GetLineHeight(uiScale);
|
||||
return (int)(height * lineHeightScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -18,7 +16,7 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Robust.Client.UserInterface.Themes;
|
||||
|
||||
[Prototype("uiTheme")]
|
||||
public sealed class UITheme : IPrototype
|
||||
public sealed partial class UITheme : IPrototype
|
||||
{
|
||||
private IResourceCache? _cache;
|
||||
private IUserInterfaceManager? _uiMan;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Robust.Client.UserInterface.Themes;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.Client.UserInterface;
|
||||
|
||||
@@ -18,11 +19,29 @@ internal partial class UserInterfaceManager
|
||||
{
|
||||
DefaultTheme = _protoManager.Index<UITheme>(UITheme.DefaultName);
|
||||
CurrentTheme = DefaultTheme;
|
||||
ReloadThemes();
|
||||
_configurationManager.OnValueChanged(CVars.InterfaceTheme, SetThemeOrPrevious, true);
|
||||
_protoManager.PrototypesReloaded += OnPrototypesReloaded;
|
||||
}
|
||||
|
||||
private void OnPrototypesReloaded(PrototypesReloadedEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.WasModified<UITheme>())
|
||||
{
|
||||
_sawmillUI.Debug("Reloading UI themes due to prototype reload");
|
||||
ReloadThemes();
|
||||
}
|
||||
}
|
||||
|
||||
private void ReloadThemes()
|
||||
{
|
||||
_themes.Clear();
|
||||
foreach (var proto in _protoManager.EnumeratePrototypes<UITheme>())
|
||||
{
|
||||
_themes.Add(proto.ID, proto);
|
||||
}
|
||||
_configurationManager.OnValueChanged(CVars.InterfaceTheme, SetThemeOrPrevious, true);
|
||||
|
||||
SetThemeOrPrevious(CurrentTheme.ID);
|
||||
}
|
||||
|
||||
//Try to set the current theme, if the theme is not found do nothing
|
||||
|
||||
@@ -28,6 +28,7 @@ public static class Diagnostics
|
||||
public const string IdComponentPauseNoFields = "RA0022";
|
||||
public const string IdComponentPauseNoParentAttribute = "RA0023";
|
||||
public const string IdComponentPauseWrongTypeAttribute = "RA0024";
|
||||
public const string IdDependencyFieldAssigned = "RA0025";
|
||||
|
||||
public static SuppressionDescriptor MeansImplicitAssignment =>
|
||||
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");
|
||||
|
||||
@@ -70,6 +70,11 @@ namespace Robust.Server.Console.Commands
|
||||
}
|
||||
|
||||
var mapId = new MapId(mapInt);
|
||||
if (!_map.MapExists(mapId))
|
||||
{
|
||||
shell.WriteError($"map {args[0]} does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell.Player == null)
|
||||
{
|
||||
@@ -110,13 +115,6 @@ namespace Robust.Server.Console.Commands
|
||||
|
||||
private void SetupPlayer(MapId mapId, IConsoleShell shell)
|
||||
{
|
||||
if (mapId == MapId.Nullspace) return;
|
||||
|
||||
if (!_map.MapExists(mapId))
|
||||
{
|
||||
_map.CreateMap(mapId);
|
||||
}
|
||||
|
||||
_map.SetMapPaused(mapId, false);
|
||||
var mapUid = _map.GetMapEntityIdOrThrow(mapId);
|
||||
_ent.System<Gravity2DController>().SetGravity(mapUid, new Vector2(0, -9.8f));
|
||||
|
||||
@@ -49,7 +49,6 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
private ISawmill _logLoader = default!;
|
||||
private ISawmill _logWriter = default!;
|
||||
|
||||
private static readonly MapLoadOptions DefaultLoadOptions = new();
|
||||
private const int MapFormatVersion = 6;
|
||||
private const int BackwardsVersion = 2;
|
||||
|
||||
@@ -132,7 +131,7 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
public bool TryLoad(MapId mapId, string path, [NotNullWhen(true)] out IReadOnlyList<EntityUid>? rootUids,
|
||||
MapLoadOptions? options = null)
|
||||
{
|
||||
options ??= DefaultLoadOptions;
|
||||
options ??= new();
|
||||
|
||||
var resPath = new ResPath(path).ToRootedPath();
|
||||
|
||||
@@ -658,11 +657,13 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
// We just need to cache the old mapuid and point to the new mapuid.
|
||||
|
||||
if (HasComp<MapComponent>(rootNode))
|
||||
if (TryComp(rootNode, out MapComponent? mapComp))
|
||||
{
|
||||
// If map exists swap out
|
||||
if (_mapManager.MapExists(data.TargetMap))
|
||||
if (_mapSystem.TryGetMap(data.TargetMap, out var existing))
|
||||
{
|
||||
data.Options.DoMapInit |= _mapSystem.IsInitialized(data.TargetMap);
|
||||
data.MapIsPaused = _mapSystem.IsPaused(existing.Value);
|
||||
// Map exists but we also have a map file with stuff on it soooo swap out the old map.
|
||||
if (data.Options.LoadMap)
|
||||
{
|
||||
@@ -675,26 +676,28 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
data.Options.Rotation = Angle.Zero;
|
||||
}
|
||||
|
||||
_mapManager.SetMapEntity(data.TargetMap, rootNode);
|
||||
Del(existing);
|
||||
EnsureComp<LoadedMapComponent>(rootNode);
|
||||
|
||||
mapComp.MapId = data.TargetMap;
|
||||
DebugTools.Assert(mapComp.LifeStage < ComponentLifeStage.Initializing);
|
||||
}
|
||||
// Otherwise just ignore the map in the file.
|
||||
else
|
||||
{
|
||||
var oldRootUid = data.Entities[0];
|
||||
var newRootUid = _mapManager.GetMapEntityId(data.TargetMap);
|
||||
data.Entities[0] = newRootUid;
|
||||
data.Entities[0] = existing.Value;
|
||||
|
||||
foreach (var ent in data.Entities)
|
||||
{
|
||||
if (ent == newRootUid)
|
||||
if (ent == existing)
|
||||
continue;
|
||||
|
||||
var xform = xformQuery.GetComponent(ent);
|
||||
|
||||
if (!xform.ParentUid.IsValid() || xform.ParentUid.Equals(oldRootUid))
|
||||
{
|
||||
_transform.SetParent(ent, xform, newRootUid);
|
||||
_transform.SetParent(ent, xform, existing.Value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -703,16 +706,9 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we're loading a file with a map then swap out the entityuid
|
||||
// TODO: Mapmanager nonsense
|
||||
var AAAAA = _mapManager.CreateMap(data.TargetMap);
|
||||
|
||||
if (!data.MapIsPostInit)
|
||||
{
|
||||
_mapManager.AddUninitializedMap(data.TargetMap);
|
||||
}
|
||||
|
||||
_mapManager.SetMapEntity(data.TargetMap, rootNode);
|
||||
data.MapIsPaused = !data.MapIsPostInit;
|
||||
mapComp.MapId = data.TargetMap;
|
||||
DebugTools.Assert(mapComp.LifeStage < ComponentLifeStage.Initializing);
|
||||
EnsureComp<LoadedMapComponent>(rootNode);
|
||||
|
||||
// Nothing should have invalid uid except for the root node.
|
||||
@@ -721,17 +717,15 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
else
|
||||
{
|
||||
// No map file root, in that case create a new map / get the one we're loading onto.
|
||||
var mapNode = _mapManager.GetMapEntityId(data.TargetMap);
|
||||
|
||||
if (!mapNode.IsValid())
|
||||
if (!_mapSystem.TryGetMap(data.TargetMap, out var mapNode))
|
||||
{
|
||||
// Map doesn't exist so we'll start it up now so we can re-attach the preinit entities to it for later.
|
||||
_mapManager.CreateMap(data.TargetMap);
|
||||
_mapManager.AddUninitializedMap(data.TargetMap);
|
||||
mapNode = _mapManager.GetMapEntityId(data.TargetMap);
|
||||
DebugTools.Assert(mapNode.IsValid());
|
||||
mapNode = _mapSystem.CreateMap(data.TargetMap, false);
|
||||
}
|
||||
|
||||
data.Options.DoMapInit |= _mapSystem.IsInitialized(data.TargetMap);
|
||||
data.MapIsPaused = _mapSystem.IsPaused(mapNode.Value);
|
||||
|
||||
// If anything has an invalid parent (e.g. it's some form of root node) then parent it to the map.
|
||||
foreach (var ent in data.Entities)
|
||||
{
|
||||
@@ -743,12 +737,11 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
|
||||
if (!xform.ParentUid.IsValid())
|
||||
{
|
||||
_transform.SetParent(ent, xform, mapNode);
|
||||
_transform.SetParent(ent, xform, mapNode.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.MapIsPaused = _mapManager.IsMapPaused(data.TargetMap);
|
||||
_logLoader.Debug($"Swapped out root node in {_stopwatch.Elapsed}");
|
||||
}
|
||||
|
||||
@@ -896,7 +889,7 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
{
|
||||
EntityManager.SetLifeStage(metadata, EntityLifeStage.MapInitialized);
|
||||
}
|
||||
else if (_mapManager.IsMapInitialized(data.TargetMap))
|
||||
else if (data.Options.DoMapInit)
|
||||
{
|
||||
_serverEntityManager.RunMapInit(uid, metadata);
|
||||
}
|
||||
@@ -964,7 +957,7 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
// Yes, post-init maps do not have EntityLifeStage >= EntityLifeStage.MapInitialized
|
||||
bool postInit;
|
||||
if (TryComp(uid, out MapComponent? mapComp))
|
||||
postInit = !mapComp.MapPreInit;
|
||||
postInit = mapComp.MapInitialized;
|
||||
else
|
||||
postInit = metadata.EntityLifeStage >= EntityLifeStage.MapInitialized;
|
||||
|
||||
@@ -1098,17 +1091,17 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsSaveable(EntityUid uid, EntityQuery<MetaDataComponent> metaQuery, EntityQuery<TransformComponent> transformQuery)
|
||||
private bool IsSaveable(EntityUid uid)
|
||||
{
|
||||
// Don't serialize things parented to un savable things.
|
||||
// For example clothes inside a person.
|
||||
while (uid.IsValid())
|
||||
{
|
||||
var meta = metaQuery.GetComponent(uid);
|
||||
var meta = MetaData(uid);
|
||||
|
||||
if (meta.EntityDeleted || meta.EntityPrototype?.MapSavable == false) break;
|
||||
|
||||
uid = transformQuery.GetComponent(uid).ParentUid;
|
||||
uid = Transform(uid).ParentUid;
|
||||
}
|
||||
|
||||
// If we manage to get up to the map (root node) then it's saveable.
|
||||
@@ -1123,7 +1116,7 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
EntityQuery<TransformComponent> transformQuery,
|
||||
EntityQuery<MapSaveIdComponent> saveCompQuery)
|
||||
{
|
||||
if (!IsSaveable(uid, metaQuery, transformQuery))
|
||||
if (!IsSaveable(uid))
|
||||
return;
|
||||
|
||||
entities.Add(uid);
|
||||
|
||||
@@ -5,9 +5,9 @@ using Robust.Shared.Collections;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Map.Events;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
|
||||
namespace Robust.Server.GameObjects
|
||||
{
|
||||
@@ -18,6 +18,16 @@ namespace Robust.Server.GameObjects
|
||||
|
||||
private bool _deleteEmptyGrids;
|
||||
|
||||
protected override MapId GetNextMapId()
|
||||
{
|
||||
var id = new MapId(++LastMapId);
|
||||
while (MapManager.MapExists(id))
|
||||
{
|
||||
id = new MapId(++LastMapId);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
protected override void UpdatePvsChunks(Entity<TransformComponent, MetaDataComponent> grid)
|
||||
{
|
||||
_pvs.GridParentChanged(grid);
|
||||
@@ -31,11 +41,6 @@ namespace Robust.Server.GameObjects
|
||||
Subs.CVar(_cfg, CVars.GameDeleteEmptyGrids, SetGridDeletion, true);
|
||||
}
|
||||
|
||||
protected override void OnMapAdd(EntityUid uid, MapComponent component, ComponentAdd args)
|
||||
{
|
||||
EnsureComp<PhysicsMapComponent>(uid);
|
||||
}
|
||||
|
||||
private void SetGridDeletion(bool value)
|
||||
{
|
||||
_deleteEmptyGrids = value;
|
||||
|
||||
@@ -53,5 +53,7 @@ namespace Robust.Server.Maps
|
||||
/// This should be set to false if you want to load a map file onto an existing map and do not wish to overwrite the existing entity.
|
||||
/// </remarks>
|
||||
public bool LoadMap { get; set; } = true;
|
||||
|
||||
public bool DoMapInit = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ namespace Robust.Server.ServerStatus
|
||||
_context = context;
|
||||
RequestMethod = new HttpMethod(context.Request.HttpMethod!);
|
||||
|
||||
var headers = new Dictionary<string, StringValues>();
|
||||
var headers = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (string? key in context.Request.Headers.Keys)
|
||||
{
|
||||
if (key == null)
|
||||
|
||||
@@ -157,11 +157,14 @@ namespace Robust.Server.ServerStatus
|
||||
try
|
||||
{
|
||||
// Passing null as content works so...
|
||||
await _httpClient.PostAsync(new Uri(_baseUri, $"server_api/{_watchdogKey}/ping"), null!);
|
||||
_sawmill.Debug("Sending ping to watchdog...");
|
||||
using var resp = await _httpClient.PostAsync(new Uri(_baseUri, $"server_api/{_watchdogKey}/ping"), null!);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
_sawmill.Debug("Succeeded in sending ping to watchdog");
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
_sawmill.Warning("Failed to send ping to watchdog:\n{0}", e);
|
||||
_sawmill.Error("Failed to send ping to watchdog:\n{0}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -592,7 +592,11 @@ namespace Robust.Shared.Maths
|
||||
if (c != 0)
|
||||
{
|
||||
if (max == rgb.R)
|
||||
{
|
||||
h = (rgb.G - rgb.B) / c % 6.0f;
|
||||
if (h < 0f)
|
||||
h += 6.0f;
|
||||
}
|
||||
else if (max == rgb.G)
|
||||
h = (rgb.B - rgb.R) / c + 2.0f;
|
||||
else if (max == rgb.B)
|
||||
@@ -774,7 +778,11 @@ namespace Robust.Shared.Maths
|
||||
|
||||
var h = 0.0f;
|
||||
if (max == rgb.R)
|
||||
{
|
||||
h = (rgb.G - rgb.B) / c % 6.0f;
|
||||
if (h < 0f)
|
||||
h += 6.0f;
|
||||
}
|
||||
else if (max == rgb.G)
|
||||
h = (rgb.B - rgb.R) / c + 2.0f;
|
||||
else if (max == rgb.B)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
#if NETSTANDARD2_0
|
||||
#if ROBUST_ANALYZERS_IMPL
|
||||
namespace Robust.Shared.Analyzers.Implementation;
|
||||
#else
|
||||
namespace Robust.Shared.Analyzers;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
#if NETSTANDARD2_0
|
||||
#if ROBUST_ANALYZERS_IMPL
|
||||
namespace Robust.Shared.Analyzers.Implementation;
|
||||
#else
|
||||
namespace Robust.Shared.Analyzers;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
#if NETSTANDARD2_0
|
||||
#if ROBUST_ANALYZERS_IMPL
|
||||
namespace Robust.Shared.Analyzers.Implementation;
|
||||
#else
|
||||
namespace Robust.Shared.Analyzers;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
#if NETSTANDARD2_0
|
||||
#if ROBUST_ANALYZERS_IMPL
|
||||
namespace Robust.Shared.Analyzers.Implementation;
|
||||
#else
|
||||
namespace Robust.Shared.Analyzers;
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Robust.Shared.Audio;
|
||||
/// to allow the server to know audio lengths without shipping the large audio files themselves.
|
||||
/// </summary>
|
||||
[Prototype(ProtoName)]
|
||||
public sealed class AudioMetadataPrototype : IPrototype
|
||||
public sealed partial class AudioMetadataPrototype : IPrototype
|
||||
{
|
||||
public const string ProtoName = "audioMetadata";
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Robust.Shared.Audio;
|
||||
/// This can be used by <see cref="Content.Shared.Audio.SharedContentAudioSystem"/> to apply an audio preset.
|
||||
/// </summary>
|
||||
[Prototype("audioPreset")]
|
||||
public sealed class AudioPresetPrototype : IPrototype
|
||||
public sealed partial class AudioPresetPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; } = default!;
|
||||
|
||||
@@ -6,6 +6,7 @@ using Robust.Shared.Audio.Sources;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -90,11 +91,24 @@ public sealed partial class AudioComponent : Component, IAudioSource
|
||||
public void StartPlaying() => Source.StartPlaying();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void StopPlaying() => Source.StopPlaying();
|
||||
public void StopPlaying()
|
||||
{
|
||||
PlaybackPosition = 0f;
|
||||
Source.StopPlaying();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Restart() => Source.Restart();
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public AudioState State = AudioState.Playing;
|
||||
|
||||
/// <summary>
|
||||
/// Time when the audio was paused so we can offset it later if relevant.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public TimeSpan? PauseTime;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="IAudioSource.Playing"/>
|
||||
/// </summary>
|
||||
@@ -208,6 +222,7 @@ public sealed partial class AudioComponent : Component, IAudioSource
|
||||
/// <see cref="IAudioSource.PlaybackPosition"/>
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[Access(Other = AccessPermissions.ReadWriteExecute)]
|
||||
public float PlaybackPosition
|
||||
{
|
||||
get => Source.PlaybackPosition;
|
||||
@@ -240,6 +255,14 @@ public sealed partial class AudioComponent : Component, IAudioSource
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum AudioState : byte
|
||||
{
|
||||
Stopped,
|
||||
Playing,
|
||||
Paused,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum AudioFlags : byte
|
||||
{
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Robust.Shared.Audio;
|
||||
|
||||
[Prototype("soundCollection")]
|
||||
public sealed class SoundCollectionPrototype : IPrototype
|
||||
public sealed partial class SoundCollectionPrototype : IPrototype
|
||||
{
|
||||
[ViewVariables]
|
||||
[IdDataField]
|
||||
|
||||
@@ -55,6 +55,141 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
SubscribeLocalEvent<AudioComponent, EntityUnpausedEvent>(OnAudioUnpaused);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the playback position of audio to the specified spot.
|
||||
/// </summary>
|
||||
public void SetPlaybackPosition(Entity<AudioComponent?>? nullEntity, float position)
|
||||
{
|
||||
if (nullEntity == null)
|
||||
return;
|
||||
|
||||
var entity = nullEntity.Value;
|
||||
|
||||
if (!Resolve(entity.Owner, ref entity.Comp, false))
|
||||
return;
|
||||
|
||||
var audioLength = GetAudioLength(entity.Comp.FileName);
|
||||
|
||||
if (audioLength.TotalSeconds < position)
|
||||
{
|
||||
// Just stop it and return
|
||||
if (!_netManager.IsClient)
|
||||
QueueDel(nullEntity.Value);
|
||||
|
||||
entity.Comp.StopPlaying();
|
||||
return;
|
||||
}
|
||||
|
||||
if (position < 0f)
|
||||
{
|
||||
Log.Error($"Tried to set playback position for {ToPrettyString(entity.Owner)} / {entity.Comp.FileName} outside of bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're paused then the current position is <pause time - start time>, else it's <cur time - start time>
|
||||
var currentPos = (entity.Comp.PauseTime ?? Timing.CurTime) - entity.Comp.AudioStart;
|
||||
var timeOffset = TimeSpan.FromSeconds(position - currentPos.TotalSeconds);
|
||||
|
||||
DebugTools.Assert(currentPos > TimeSpan.Zero);
|
||||
|
||||
// Rounding.
|
||||
if (Math.Abs(timeOffset.TotalSeconds) <= 0.01)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity.Comp.PauseTime != null)
|
||||
{
|
||||
entity.Comp.PauseTime = entity.Comp.PauseTime.Value + timeOffset;
|
||||
|
||||
// Paused audio doesn't have TimedDespawn so.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Bump it back so the actual playback positions moves forward
|
||||
entity.Comp.AudioStart -= timeOffset;
|
||||
|
||||
// need to ensure it doesn't despawn too early.
|
||||
if (TryComp(entity.Owner, out TimedDespawnComponent? despawn))
|
||||
{
|
||||
despawn.Lifetime -= (float) timeOffset.TotalSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
entity.Comp.PlaybackPosition = position;
|
||||
// Network the new playback position.
|
||||
Dirty(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates playback position considering length paused.
|
||||
/// </summary>
|
||||
/// <param name="component"></param>
|
||||
/// <returns></returns>
|
||||
private float GetPlaybackPosition(AudioComponent component)
|
||||
{
|
||||
return (float) (Timing.CurTime - (component.PauseTime ?? TimeSpan.Zero) - component.AudioStart).TotalSeconds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the shared state for an audio entity.
|
||||
/// </summary>
|
||||
public void SetState(EntityUid? entity, AudioState state, bool force = false, AudioComponent? component = null)
|
||||
{
|
||||
if (entity == null || !Resolve(entity.Value, ref component, false))
|
||||
return;
|
||||
|
||||
if (component.State == state && !force)
|
||||
return;
|
||||
|
||||
// Unpause it
|
||||
if (component.State == AudioState.Paused && state == AudioState.Playing)
|
||||
{
|
||||
var pauseOffset = Timing.CurTime - component.PauseTime;
|
||||
component.AudioStart += pauseOffset ?? TimeSpan.Zero;
|
||||
component.PlaybackPosition = (float) (Timing.CurTime - component.AudioStart).TotalSeconds;
|
||||
}
|
||||
|
||||
// If we were stopped then played then restart audiostart to now.
|
||||
if (component.State == AudioState.Stopped && state == AudioState.Playing)
|
||||
{
|
||||
component.AudioStart = Timing.CurTime;
|
||||
component.PauseTime = null;
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case AudioState.Stopped:
|
||||
component.AudioStart = Timing.CurTime;
|
||||
component.PauseTime = null;
|
||||
component.StopPlaying();
|
||||
RemComp<TimedDespawnComponent>(entity.Value);
|
||||
break;
|
||||
case AudioState.Paused:
|
||||
// Set it to current time so we can easily unpause it later.
|
||||
component.PauseTime = Timing.CurTime;
|
||||
component.Pause();
|
||||
RemComp<TimedDespawnComponent>(entity.Value);
|
||||
break;
|
||||
case AudioState.Playing:
|
||||
component.PauseTime = null;
|
||||
component.StartPlaying();
|
||||
|
||||
// Reset TimedDespawn so the audio still gets cleaned up.
|
||||
|
||||
if (!component.Looping)
|
||||
{
|
||||
var timed = EnsureComp<TimedDespawnComponent>(entity.Value);
|
||||
var audioLength = GetAudioLength(component.FileName);
|
||||
timed.Lifetime = (float) audioLength.TotalSeconds + 0.01f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
component.State = state;
|
||||
Dirty(entity.Value, component);
|
||||
}
|
||||
|
||||
protected void SetZOffset(float value)
|
||||
{
|
||||
ZOffset = value;
|
||||
@@ -505,4 +640,12 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
{
|
||||
public NetEntity NetEntity;
|
||||
}
|
||||
|
||||
public bool IsPlaying(EntityUid? stream, AudioComponent? component = null)
|
||||
{
|
||||
if (stream == null || !Resolve(stream.Value, ref component, false))
|
||||
return false;
|
||||
|
||||
return component.State == AudioState.Playing;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -851,7 +851,7 @@ namespace Robust.Shared
|
||||
/// See the documentation of the <see cref="Network.AuthMode"/> enum for values.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> AuthMode =
|
||||
CVarDef.Create("auth.mode", (int) Network.AuthMode.Optional, CVar.SERVERONLY);
|
||||
CVarDef.Create("auth.mode", (int) Network.AuthMode.Required, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Allow unauthenticated localhost connections, even if the auth mode is set to required.
|
||||
@@ -886,6 +886,22 @@ namespace Robust.Shared
|
||||
public static readonly CVarDef<string> RenderFOVColor =
|
||||
CVarDef.Create("render.fov_color", Color.Black.ToHex(), CVar.ARCHIVE | CVar.CLIENTONLY);
|
||||
|
||||
/*
|
||||
* CONTROLS
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Milliseconds to wait to consider double-click delays.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> DoubleClickDelay =
|
||||
CVarDef.Create("controls.double_click_delay", 250, CVar.ARCHIVE | CVar.CLIENTONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Range in pixels for double-clicks
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> DoubleClickRange =
|
||||
CVarDef.Create("controls.double_click_range", 10, CVar.ARCHIVE | CVar.CLIENTONLY);
|
||||
|
||||
/*
|
||||
* DISPLAY
|
||||
*/
|
||||
@@ -1429,7 +1445,7 @@ namespace Robust.Shared
|
||||
/// Comma-separated list of URLs of hub servers to advertise to.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<string> HubUrls =
|
||||
CVarDef.Create("hub.hub_urls", "https://central.spacestation14.io/hub/", CVar.SERVERONLY);
|
||||
CVarDef.Create("hub.hub_urls", "https://hub.spacestation14.com/", CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// URL of this server to advertise.
|
||||
|
||||
@@ -558,17 +558,21 @@ namespace Robust.Shared.Configuration
|
||||
OverrideDefault(def.Name, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public T GetCVar<T>(string name)
|
||||
public object GetCVar(string name)
|
||||
{
|
||||
using var _ = Lock.ReadGuard();
|
||||
if (_configVars.TryGetValue(name, out var cVar) && cVar.Registered)
|
||||
//TODO: Make flags work, required non-derpy net system.
|
||||
return (T)(GetConfigVarValue(cVar))!;
|
||||
return GetConfigVarValue(cVar);
|
||||
|
||||
throw new InvalidConfigurationException($"Trying to get unregistered variable '{name}'");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public T GetCVar<T>(string name)
|
||||
{
|
||||
return (T)GetCVar(name);
|
||||
}
|
||||
|
||||
public T GetCVar<T>(CVarDef<T> def) where T : notnull
|
||||
{
|
||||
return GetCVar<T>(def.Name);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Robust.Shared.Configuration
|
||||
{
|
||||
public static class EnvironmentVariables
|
||||
internal static class EnvironmentVariables
|
||||
{
|
||||
/// <summary>
|
||||
/// The environment variable for configuring CVar overrides. The value
|
||||
@@ -12,23 +13,38 @@ namespace Robust.Shared.Configuration
|
||||
/// </summary>
|
||||
public const string ConfigVarEnvironmentVariable = "ROBUST_CVARS";
|
||||
|
||||
public const string SingleVarPrefix = "ROBUST_CVAR_";
|
||||
|
||||
/// <summary>
|
||||
/// Get the CVar overrides defined in the relevant environment variable.
|
||||
/// </summary>
|
||||
public static IEnumerable<(string, string)> GetEnvironmentCVars()
|
||||
internal static IEnumerable<(string, string)> GetEnvironmentCVars()
|
||||
{
|
||||
var eVarString = Environment.GetEnvironmentVariable(ConfigVarEnvironmentVariable);
|
||||
|
||||
if (eVarString == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
// Handle ROBUST_CVARS.
|
||||
var eVarString = Environment.GetEnvironmentVariable(ConfigVarEnvironmentVariable) ?? "";
|
||||
|
||||
foreach (var cVarPair in eVarString.Split(';', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
var pairParts = cVarPair.Split('=', 2);
|
||||
yield return (pairParts[0], pairParts[1]);
|
||||
}
|
||||
|
||||
// Handle ROBUST_CVAR_*
|
||||
|
||||
foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables())
|
||||
{
|
||||
var key = (string)entry.Key;
|
||||
var value = (string?)entry.Value;
|
||||
|
||||
if (value == null)
|
||||
continue;
|
||||
|
||||
if (!key.StartsWith(SingleVarPrefix))
|
||||
continue;
|
||||
|
||||
var varName = key[SingleVarPrefix.Length..].Replace("__", ".");
|
||||
yield return (varName, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +118,13 @@ namespace Robust.Shared.Configuration
|
||||
/// <param name="value">The new default value of the CVar.</param>
|
||||
void OverrideDefault<T>(CVarDef<T> def, T value) where T : notnull;
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of a CVar.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the CVar.</param>
|
||||
/// <returns></returns>
|
||||
object GetCVar(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of a CVar.
|
||||
/// </summary>
|
||||
|
||||
@@ -10,7 +10,8 @@ namespace Robust.Shared.Console.Commands;
|
||||
|
||||
sealed class AddMapCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
[Dependency] private readonly IMapManagerInternal _map = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
|
||||
public override string Command => "addmap";
|
||||
public override bool RequireServerOrSingleplayer => true;
|
||||
@@ -24,11 +25,8 @@ sealed class AddMapCommand : LocalizedCommands
|
||||
|
||||
if (!_map.MapExists(mapId))
|
||||
{
|
||||
_map.CreateMap(mapId);
|
||||
if (args.Length >= 2 && args[1] == "false")
|
||||
{
|
||||
_map.AddUninitializedMap(mapId);
|
||||
}
|
||||
var init = args.Length < 2 || !bool.Parse(args[1]);
|
||||
_entMan.System<SharedMapSystem>().CreateMap(mapId, runMapInit: init);
|
||||
|
||||
shell.WriteLine($"Map with ID {mapId} created.");
|
||||
return;
|
||||
|
||||
@@ -135,6 +135,25 @@ public abstract partial class SharedContainerSystem
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to insert an entity into a container. If it fails, it will instead drop the entity next to the
|
||||
/// container entity.
|
||||
/// </summary>
|
||||
/// <returns>Whether or not the entity was successfully inserted</returns>
|
||||
public bool InsertOrDrop(Entity<TransformComponent?, MetaDataComponent?, PhysicsComponent?> toInsert,
|
||||
BaseContainer container,
|
||||
TransformComponent? containerXform = null)
|
||||
{
|
||||
if (!Resolve(toInsert.Owner, ref toInsert.Comp1) || !Resolve(container.Owner, ref containerXform))
|
||||
return false;
|
||||
|
||||
if (Insert(toInsert, container, containerXform))
|
||||
return true;
|
||||
|
||||
_transform.DropNextTo(toInsert, (container.Owner, containerXform));
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the entity can be inserted into the given container.
|
||||
/// </summary>
|
||||
|
||||
@@ -168,9 +168,16 @@ namespace Robust.Shared.Containers
|
||||
return containerManager.Containers.TryGetValue(id, out container);
|
||||
}
|
||||
|
||||
public bool TryGetContainingContainer(EntityUid uid, EntityUid containedUid, [NotNullWhen(true)] out BaseContainer? container, ContainerManagerComponent? containerManager = null, bool skipExistCheck = false)
|
||||
[Obsolete("Use variant without skipExistCheck argument")]
|
||||
public bool TryGetContainingContainer(EntityUid uid, EntityUid containedUid, [NotNullWhen(true)] out BaseContainer? container, bool skipExistCheck)
|
||||
{
|
||||
if (!Resolve(uid, ref containerManager, false) || !(skipExistCheck || Exists(containedUid)))
|
||||
return TryGetContainingContainer(uid, containedUid, out container);
|
||||
}
|
||||
|
||||
public bool TryGetContainingContainer(EntityUid uid, EntityUid containedUid, [NotNullWhen(true)] out BaseContainer? container, ContainerManagerComponent? containerManager = null)
|
||||
{
|
||||
DebugTools.Assert(Exists(containedUid));
|
||||
if (!Resolve(uid, ref containerManager, false))
|
||||
{
|
||||
container = null;
|
||||
return false;
|
||||
@@ -191,7 +198,8 @@ namespace Robust.Shared.Containers
|
||||
|
||||
public bool ContainsEntity(EntityUid uid, EntityUid containedUid, ContainerManagerComponent? containerManager = null)
|
||||
{
|
||||
if (!Resolve(uid, ref containerManager, false) || !Exists(containedUid))
|
||||
DebugTools.Assert(Exists(containedUid));
|
||||
if (!Resolve(uid, ref containerManager, false))
|
||||
return false;
|
||||
|
||||
foreach (var container in containerManager.Containers.Values)
|
||||
@@ -251,7 +259,7 @@ namespace Robust.Shared.Containers
|
||||
if (!Resolve(uid, ref transform, false))
|
||||
return false;
|
||||
|
||||
return TryGetContainingContainer(transform.ParentUid, uid, out container, skipExistCheck: true);
|
||||
return TryGetContainingContainer(transform.ParentUid, uid, out container);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Robust.Shared.ContentPack
|
||||
String("short").ThenReturn(PrimitiveTypeCode.Int16);
|
||||
|
||||
private static readonly Parser<char, PrimitiveTypeCode> UInt16TypeParser =
|
||||
String("ushort").ThenReturn(PrimitiveTypeCode.UInt32);
|
||||
String("ushort").ThenReturn(PrimitiveTypeCode.UInt16);
|
||||
|
||||
private static readonly Parser<char, PrimitiveTypeCode> Int32TypeParser =
|
||||
String("int").ThenReturn(PrimitiveTypeCode.Int32);
|
||||
|
||||
@@ -84,12 +84,146 @@ Types:
|
||||
- "bool get_HasContents()"
|
||||
Lidgren.Network:
|
||||
NetBuffer:
|
||||
All: True
|
||||
Methods:
|
||||
- "byte[] get_Data()"
|
||||
- "void set_Data(byte[])"
|
||||
- "int get_LengthBytes()"
|
||||
- "void set_LengthBytes(int)"
|
||||
- "int get_LengthBits()"
|
||||
- "void set_LengthBits(int)"
|
||||
- "long get_Position()"
|
||||
- "void set_Position(long)"
|
||||
- "int get_PositionInBytes()"
|
||||
- "byte[] PeekDataBuffer()"
|
||||
- "bool PeekBoolean()"
|
||||
- "byte PeekByte()"
|
||||
- "sbyte PeekSByte()"
|
||||
- "byte PeekByte(int)"
|
||||
- "System.Span`1<byte> PeekBytes(System.Span`1<byte>)"
|
||||
- "byte[] PeekBytes(int)"
|
||||
- "void PeekBytes(byte[], int, int)"
|
||||
- "short PeekInt16()"
|
||||
- "ushort PeekUInt16()"
|
||||
- "int PeekInt32()"
|
||||
- "int PeekInt32(int)"
|
||||
- "uint PeekUInt32()"
|
||||
- "uint PeekUInt32(int)"
|
||||
- "ulong PeekUInt64()"
|
||||
- "long PeekInt64()"
|
||||
- "ulong PeekUInt64(int)"
|
||||
- "long PeekInt64(int)"
|
||||
- "float PeekFloat()"
|
||||
- "System.Half PeekHalf()"
|
||||
- "float PeekSingle()"
|
||||
- "double PeekDouble()"
|
||||
- "string PeekString()"
|
||||
- "int PeekStringSize()"
|
||||
- "bool ReadBoolean()"
|
||||
- "byte ReadByte()"
|
||||
- "bool ReadByte(ref byte)"
|
||||
- "sbyte ReadSByte()"
|
||||
- "byte ReadByte(int)"
|
||||
- "System.Span`1<byte> ReadBytes(System.Span`1<byte>)"
|
||||
- "byte[] ReadBytes(int)"
|
||||
- "bool ReadBytes(int, ref byte[])"
|
||||
- "bool TryReadBytes(System.Span`1<byte>)"
|
||||
- "void ReadBytes(byte[], int, int)"
|
||||
- "void ReadBits(System.Span`1<byte>, int)"
|
||||
- "void ReadBits(byte[], int, int)"
|
||||
- "short ReadInt16()"
|
||||
- "ushort ReadUInt16()"
|
||||
- "int ReadInt32()"
|
||||
- "bool ReadInt32(ref int)"
|
||||
- "int ReadInt32(int)"
|
||||
- "uint ReadUInt32()"
|
||||
- "bool ReadUInt32(ref uint)"
|
||||
- "uint ReadUInt32(int)"
|
||||
- "ulong ReadUInt64()"
|
||||
- "long ReadInt64()"
|
||||
- "ulong ReadUInt64(int)"
|
||||
- "long ReadInt64(int)"
|
||||
- "float ReadFloat()"
|
||||
- "System.Half ReadHalf()"
|
||||
- "float ReadSingle()"
|
||||
- "bool ReadSingle(ref float)"
|
||||
- "double ReadDouble()"
|
||||
- "uint ReadVariableUInt32()"
|
||||
- "bool ReadVariableUInt32(ref uint)"
|
||||
- "int ReadVariableInt32()"
|
||||
- "long ReadVariableInt64()"
|
||||
- "ulong ReadVariableUInt64()"
|
||||
- "float ReadSignedSingle(int)"
|
||||
- "float ReadUnitSingle(int)"
|
||||
- "float ReadRangedSingle(float, float, int)"
|
||||
- "int ReadRangedInteger(int, int)"
|
||||
- "long ReadRangedInteger(long, long)"
|
||||
- "string ReadString()"
|
||||
- "bool ReadString(ref string)"
|
||||
- "double ReadTime(Lidgren.Network.NetConnection, bool)"
|
||||
- "System.Net.IPEndPoint ReadIPEndPoint()"
|
||||
- "void SkipPadBits()"
|
||||
- "void ReadPadBits()"
|
||||
- "void SkipPadBits(int)"
|
||||
- "void EnsureBufferSize(int)"
|
||||
- "void Write(bool)"
|
||||
- "void Write(byte)"
|
||||
- "void WriteAt(int, byte)"
|
||||
- "void Write(sbyte)"
|
||||
- "void Write(byte, int)"
|
||||
- "void Write(byte[])"
|
||||
- "void Write(System.ReadOnlySpan`1<byte>)"
|
||||
- "void Write(byte[], int, int)"
|
||||
- "void Write(ushort)"
|
||||
- "void WriteAt(int, ushort)"
|
||||
- "void Write(ushort, int)"
|
||||
- "void Write(short)"
|
||||
- "void WriteAt(int, short)"
|
||||
- "void Write(int)"
|
||||
- "void WriteAt(int, int)"
|
||||
- "void Write(uint)"
|
||||
- "void WriteAt(int, uint)"
|
||||
- "void Write(uint, int)"
|
||||
- "void Write(int, int)"
|
||||
- "void Write(ulong)"
|
||||
- "void WriteAt(int, ulong)"
|
||||
- "void Write(ulong, int)"
|
||||
- "void Write(long)"
|
||||
- "void Write(long, int)"
|
||||
- "void Write(System.Half)"
|
||||
- "void Write(float)"
|
||||
- "void Write(double)"
|
||||
- "int WriteVariableUInt32(uint)"
|
||||
- "int WriteVariableInt32(int)"
|
||||
- "int WriteVariableInt64(long)"
|
||||
- "int WriteVariableUInt64(ulong)"
|
||||
- "void WriteSignedSingle(float, int)"
|
||||
- "void WriteUnitSingle(float, int)"
|
||||
- "void WriteRangedSingle(float, float, float, int)"
|
||||
- "int WriteRangedInteger(int, int, int)"
|
||||
- "int WriteRangedInteger(long, long, long)"
|
||||
- "void Write(string)"
|
||||
- "void Write(System.Net.IPEndPoint)"
|
||||
- "void WriteTime(bool)"
|
||||
- "void WriteTime(double, bool)"
|
||||
- "void WritePadBits()"
|
||||
- "void WritePadBits(int)"
|
||||
- "void Write(Lidgren.Network.NetBuffer)"
|
||||
- "void Zero(int)"
|
||||
- "void .ctor()"
|
||||
NetDeliveryMethod: { }
|
||||
NetIncomingMessage:
|
||||
All: True
|
||||
Methods:
|
||||
- "Lidgren.Network.NetIncomingMessageType get_MessageType()"
|
||||
- "Lidgren.Network.NetDeliveryMethod get_DeliveryMethod()"
|
||||
- "int get_SequenceChannel()"
|
||||
- "System.Net.IPEndPoint get_SenderEndPoint()"
|
||||
- "Lidgren.Network.NetConnection get_SenderConnection()"
|
||||
- "double get_ReceiveTime()"
|
||||
- "double ReadTime(bool)"
|
||||
- "string ToString()"
|
||||
NetOutgoingMessage:
|
||||
All: True
|
||||
Methods:
|
||||
- "string ToString()"
|
||||
Nett:
|
||||
CommentLocation: { } # Enum
|
||||
Toml:
|
||||
@@ -951,7 +1085,9 @@ Types:
|
||||
IComparable: { All: True }
|
||||
IComparable`1: { All: True }
|
||||
IDisposable: { All: True }
|
||||
IEquatable`1: { }
|
||||
IEquatable`1:
|
||||
Methods:
|
||||
- "bool Equals(!0)"
|
||||
IFormatProvider: { All: True }
|
||||
IFormattable: { All: True }
|
||||
Index: { All: True }
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.ContentPack
|
||||
@@ -135,11 +136,37 @@ namespace Robust.Shared.ContentPack
|
||||
path = path.Directory;
|
||||
|
||||
var fullPath = GetFullPath(path);
|
||||
Process.Start(new ProcessStartInfo
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
UseShellExecute = true,
|
||||
FileName = fullPath,
|
||||
});
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "explorer.exe",
|
||||
Arguments = ".",
|
||||
WorkingDirectory = fullPath,
|
||||
});
|
||||
}
|
||||
else if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "open",
|
||||
Arguments = ".",
|
||||
WorkingDirectory = fullPath,
|
||||
});
|
||||
}
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD())
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "xdg-open",
|
||||
Arguments = ".",
|
||||
WorkingDirectory = fullPath,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("Opening OS windows not supported on this OS");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -101,7 +101,6 @@ namespace Robust.Shared.GameObjects
|
||||
internal bool _mapIdInitialized;
|
||||
internal bool _gridInitialized;
|
||||
|
||||
// TODO: Cache this.
|
||||
/// <summary>
|
||||
/// The EntityUid of the map which this object is on, if any.
|
||||
/// </summary>
|
||||
|
||||
@@ -329,7 +329,7 @@ namespace Robust.Shared.GameObjects
|
||||
ref Unit unitRef,
|
||||
EventData subs)
|
||||
{
|
||||
foreach (var handler in subs.BroadcastRegistrations)
|
||||
foreach (var handler in subs.BroadcastRegistrations.Span)
|
||||
{
|
||||
if ((handler.Mask & source) != 0)
|
||||
handler.Handler(ref unitRef);
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Robust.Shared.GameObjects
|
||||
EventData sub,
|
||||
ref ValueList<OrderedEventDispatch> found)
|
||||
{
|
||||
foreach (var handler in sub.BroadcastRegistrations)
|
||||
foreach (var handler in sub.BroadcastRegistrations.Span)
|
||||
{
|
||||
if ((handler.Mask & source) != 0)
|
||||
found.Add(new OrderedEventDispatch(handler.Handler, handler.Order));
|
||||
@@ -44,7 +44,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
found.Sort(OrderedEventDispatchComparer.Instance);
|
||||
|
||||
foreach (var (handler, _) in found)
|
||||
foreach (var (handler, _) in found.Span)
|
||||
{
|
||||
handler(ref eventArgs);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
#if EXCEPTION_TOLERANCE
|
||||
@@ -176,6 +177,60 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AddComponents(EntityUid target, EntityPrototype prototype, bool removeExisting = true)
|
||||
{
|
||||
AddComponents(target, prototype.Components, removeExisting);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AddComponents(EntityUid target, ComponentRegistry registry, bool removeExisting = true)
|
||||
{
|
||||
if (registry.Count == 0)
|
||||
return;
|
||||
|
||||
var metadata = MetaQuery.GetComponent(target);
|
||||
|
||||
foreach (var (name, entry) in registry)
|
||||
{
|
||||
var reg = _componentFactory.GetRegistration(name);
|
||||
|
||||
if (HasComponent(target, reg.Type))
|
||||
{
|
||||
if (!removeExisting)
|
||||
continue;
|
||||
|
||||
RemoveComponent(target, reg.Type, metadata);
|
||||
}
|
||||
|
||||
var comp = _componentFactory.GetComponent(reg);
|
||||
_serManager.CopyTo(entry.Component, ref comp, notNullableOverride: true);
|
||||
AddComponent(target, comp, metadata: metadata);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void RemoveComponents(EntityUid target, EntityPrototype prototype)
|
||||
{
|
||||
RemoveComponents(target, prototype.Components);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveComponents(EntityUid target, ComponentRegistry registry)
|
||||
{
|
||||
if (registry.Count == 0)
|
||||
return;
|
||||
|
||||
var metadata = MetaQuery.GetComponent(target);
|
||||
|
||||
foreach (var entry in registry.Values)
|
||||
{
|
||||
RemoveComponent(target, entry.Component.GetType(), metadata);
|
||||
}
|
||||
}
|
||||
|
||||
public IComponent AddComponent(EntityUid uid, ushort netId, MetaDataComponent? meta = null)
|
||||
{
|
||||
var newComponent = _componentFactory.GetComponent(netId);
|
||||
@@ -958,6 +1013,11 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal variant of <see cref="GetComponents"/> that directly returns the actual component set.
|
||||
/// </summary>
|
||||
internal IReadOnlyCollection<IComponent> GetComponentsInternal(EntityUid uid) => _entCompIndex[uid];
|
||||
|
||||
/// <inheritdoc />
|
||||
public int ComponentCount(EntityUid uid)
|
||||
{
|
||||
|
||||
@@ -5,16 +5,17 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public partial class EntityManager
|
||||
{
|
||||
// This method will soon be marked as obsolete.
|
||||
// This method will soon(TM) be marked as obsolete.
|
||||
public EntityUid SpawnEntity(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
=> SpawnAttachedTo(protoName, coordinates, overrides);
|
||||
|
||||
// This method will soon be marked as obsolete.
|
||||
// This method will soon(TM) be marked as obsolete.
|
||||
public EntityUid SpawnEntity(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
=> Spawn(protoName, coordinates, overrides);
|
||||
|
||||
@@ -83,12 +84,16 @@ public partial class EntityManager
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid Spawn(string? protoName = null, ComponentRegistry? overrides = null)
|
||||
=> Spawn(protoName, MapCoordinates.Nullspace, overrides);
|
||||
|
||||
public virtual EntityUid Spawn(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
public EntityUid Spawn(string? protoName = null, ComponentRegistry? overrides = null, bool doMapInit = true)
|
||||
{
|
||||
var entity = CreateEntityUninitialized(protoName, coordinates, overrides);
|
||||
var entity = CreateEntityUninitialized(protoName, MapCoordinates.Nullspace, overrides);
|
||||
InitializeAndStartEntity(entity, doMapInit);
|
||||
return entity;
|
||||
}
|
||||
|
||||
public virtual EntityUid Spawn(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default!)
|
||||
{
|
||||
var entity = CreateEntityUninitialized(protoName, coordinates, overrides, rotation);
|
||||
InitializeAndStartEntity(entity, coordinates.MapId);
|
||||
return entity;
|
||||
}
|
||||
@@ -111,32 +116,19 @@ public partial class EntityManager
|
||||
if (!xform.ParentUid.IsValid())
|
||||
return false;
|
||||
|
||||
if (!MetaQuery.TryGetComponent(target, out var meta))
|
||||
return false;
|
||||
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) == 0)
|
||||
if (!_containers.TryGetContainingContainer(target, out var container))
|
||||
{
|
||||
uid = SpawnAttachedTo(protoName, xform.Coordinates, overrides);
|
||||
uid = SpawnNextToOrDrop(protoName, target, xform, overrides);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!TryGetComponent(xform.ParentUid, out ContainerManagerComponent? containerComp))
|
||||
return false;
|
||||
|
||||
foreach (var container in containerComp.Containers.Values)
|
||||
{
|
||||
if (!container.Contains(target))
|
||||
continue;
|
||||
|
||||
uid = Spawn(protoName, overrides);
|
||||
if (_containers.Insert(uid.Value, container))
|
||||
return true;
|
||||
|
||||
DeleteEntity(uid.Value);
|
||||
uid = null;
|
||||
return false;
|
||||
}
|
||||
var doMapInit = _mapSystem.IsInitialized(xform.MapUid);
|
||||
uid = Spawn(protoName, overrides, doMapInit);
|
||||
if (_containers.Insert(uid.Value, container))
|
||||
return true;
|
||||
|
||||
DeleteEntity(uid.Value);
|
||||
uid = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -155,7 +147,8 @@ public partial class EntityManager
|
||||
if (!containerComp.Containers.TryGetValue(containerId, out var container))
|
||||
return false;
|
||||
|
||||
uid = Spawn(protoName, overrides);
|
||||
var doMapInit = _mapSystem.IsInitialized(TransformQuery.GetComponent(containerUid).MapUid);
|
||||
uid = Spawn(protoName, overrides, doMapInit);
|
||||
|
||||
if (_containers.Insert(uid.Value, container))
|
||||
return true;
|
||||
@@ -171,7 +164,8 @@ public partial class EntityManager
|
||||
if (!xform.ParentUid.IsValid())
|
||||
return Spawn(protoName);
|
||||
|
||||
var uid = Spawn(protoName, overrides);
|
||||
var doMapInit = _mapSystem.IsInitialized(xform.MapUid);
|
||||
var uid = Spawn(protoName, overrides, doMapInit);
|
||||
_xforms.DropNextTo(uid, target);
|
||||
return uid;
|
||||
}
|
||||
@@ -184,14 +178,28 @@ public partial class EntityManager
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
var uid = Spawn(protoName, overrides);
|
||||
return SpawnInContainerOrDrop(protoName, containerUid, containerId, out _, xform, containerComp, overrides);
|
||||
}
|
||||
|
||||
public EntityUid SpawnInContainerOrDrop(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
out bool inserted,
|
||||
TransformComponent? xform = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
inserted = true;
|
||||
xform ??= TransformQuery.GetComponent(containerUid);
|
||||
var doMapInit = _mapSystem.IsInitialized(xform.MapUid);
|
||||
var uid = Spawn(protoName, overrides, doMapInit);
|
||||
|
||||
if ((containerComp == null && !TryGetComponent(containerUid, out containerComp))
|
||||
|| !containerComp.Containers.TryGetValue(containerId, out var container)
|
||||
|| !_containers.Insert(uid, container))
|
||||
{
|
||||
|
||||
xform ??= TransformQuery.GetComponent(containerUid);
|
||||
inserted = false;
|
||||
if (xform.ParentUid.IsValid())
|
||||
_xforms.DropNextTo(uid, (containerUid, xform));
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Profiling;
|
||||
@@ -297,14 +298,13 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, MapCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default!)
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName, out _, overrides);
|
||||
var transform = TransformQuery.GetComponent(newEntity);
|
||||
|
||||
if (coordinates.MapId == MapId.Nullspace)
|
||||
{
|
||||
DebugTools.Assert(_mapManager.GetMapEntityId(coordinates.MapId) == EntityUid.Invalid);
|
||||
transform._parent = EntityUid.Invalid;
|
||||
transform.Anchored = false;
|
||||
return newEntity;
|
||||
@@ -323,7 +323,7 @@ namespace Robust.Shared.GameObjects
|
||||
else
|
||||
{
|
||||
coords = new EntityCoordinates(mapEnt, coordinates.Position);
|
||||
_xforms.SetCoordinates(newEntity, transform, coords, null, newParent: mapXform);
|
||||
_xforms.SetCoordinates(newEntity, transform, coords, rotation, newParent: mapXform);
|
||||
}
|
||||
|
||||
return newEntity;
|
||||
@@ -821,15 +821,22 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public void InitializeAndStartEntity(EntityUid entity, MapId? mapId = null)
|
||||
{
|
||||
var doMapInit = _mapManager.IsMapInitialized(mapId ?? TransformQuery.GetComponent(entity).MapID);
|
||||
InitializeAndStartEntity(entity, doMapInit);
|
||||
}
|
||||
|
||||
public void InitializeAndStartEntity(Entity<MetaDataComponent?> entity, bool doMapInit)
|
||||
{
|
||||
if (!MetaQuery.Resolve(entity.Owner, ref entity.Comp))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var meta = MetaQuery.GetComponent(entity);
|
||||
InitializeEntity(entity, meta);
|
||||
StartEntity(entity);
|
||||
InitializeEntity(entity.Owner, entity.Comp);
|
||||
StartEntity(entity.Owner);
|
||||
|
||||
// If the map we're initializing the entity on is initialized, run map init on it.
|
||||
if (_mapManager.IsMapInitialized(mapId ?? TransformQuery.GetComponent(entity).MapID))
|
||||
RunMapInit(entity, meta);
|
||||
if (doMapInit)
|
||||
RunMapInit(entity.Owner, entity.Comp);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -859,7 +866,7 @@ namespace Robust.Shared.GameObjects
|
||||
DebugTools.Assert(meta.EntityLifeStage == EntityLifeStage.Initialized, $"Expected entity {ToPrettyString(entity)} to be initialized, was {meta.EntityLifeStage}");
|
||||
SetLifeStage(meta, EntityLifeStage.MapInitialized);
|
||||
|
||||
EventBus.RaiseLocalEvent(entity, MapInitEventInstance, false);
|
||||
EventBus.RaiseLocalEvent(entity, MapInitEventInstance);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
namespace Robust.Shared.GameObjects
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public static class EntityManagerExt
|
||||
{
|
||||
@@ -19,5 +22,79 @@
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Picks an entity at random with the supplied component.
|
||||
/// </summary>
|
||||
public static bool TryGetRandom<TComp1>(this IEntityManager entManager, IRobustRandom random, out EntityUid entity, bool includePaused = false) where TComp1 : IComponent
|
||||
{
|
||||
var entities = new ValueList<EntityUid>();
|
||||
|
||||
if (includePaused)
|
||||
{
|
||||
var query = entManager.AllEntityQueryEnumerator<TComp1>();
|
||||
|
||||
while (query.MoveNext(out var uid, out _))
|
||||
{
|
||||
entities.Add(uid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var query = entManager.EntityQueryEnumerator<TComp1>();
|
||||
|
||||
while (query.MoveNext(out var uid, out _))
|
||||
{
|
||||
entities.Add(uid);
|
||||
}
|
||||
}
|
||||
|
||||
if (entities.Count == 0)
|
||||
{
|
||||
entity = EntityUid.Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
entity = random.Pick(entities);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Picks an entity at random with the supplied components.
|
||||
/// </summary>
|
||||
public static bool TryGetRandom<TComp1, TComp2>(this IEntityManager entManager, IRobustRandom random, out EntityUid entity, bool includePaused = false)
|
||||
where TComp1 : IComponent
|
||||
where TComp2 : IComponent
|
||||
{
|
||||
var entities = new ValueList<EntityUid>();
|
||||
|
||||
if (includePaused)
|
||||
{
|
||||
var query = entManager.AllEntityQueryEnumerator<TComp1, TComp2>();
|
||||
|
||||
while (query.MoveNext(out var uid, out _, out _))
|
||||
{
|
||||
entities.Add(uid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var query = entManager.EntityQueryEnumerator<TComp1, TComp2>();
|
||||
|
||||
while (query.MoveNext(out var uid, out _, out _))
|
||||
{
|
||||
entities.Add(uid);
|
||||
}
|
||||
}
|
||||
|
||||
if (entities.Count == 0)
|
||||
{
|
||||
entity = EntityUid.Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
entity = random.Pick(entities);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@ using System.Runtime.CompilerServices;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
@@ -699,32 +701,32 @@ public partial class EntitySystem
|
||||
|
||||
#region Entity Spawning
|
||||
|
||||
// This method will be obsoleted soon.
|
||||
// This method will be obsoleted soon(TM).
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid Spawn(string? prototype, EntityCoordinates coordinates)
|
||||
{
|
||||
return ((IEntityManager)EntityManager).SpawnEntity(prototype, coordinates);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.Spawn(string?, MapCoordinates, ComponentRegistry?)" />
|
||||
/// <inheritdoc cref="IEntityManager.Spawn(string?, MapCoordinates, ComponentRegistry?, Angle)" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid Spawn(string? prototype, MapCoordinates coordinates)
|
||||
=> EntityManager.Spawn(prototype, coordinates);
|
||||
protected EntityUid Spawn(string? prototype, MapCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default)
|
||||
=> EntityManager.Spawn(prototype, coordinates, overrides, rotation);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.Spawn(string?, ComponentRegistry?)" />
|
||||
/// <inheritdoc cref="IEntityManager.Spawn(string?, ComponentRegistry?, bool)" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid Spawn(string? prototype = null)
|
||||
=> EntityManager.Spawn(prototype);
|
||||
protected EntityUid Spawn(string? prototype = null, ComponentRegistry? overrides = null, bool doMapInit = true)
|
||||
=> EntityManager.Spawn(prototype, overrides, doMapInit);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnAttachedTo" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid SpawnAttachedTo(string? prototype, EntityCoordinates coordinates)
|
||||
=> EntityManager.SpawnAttachedTo(prototype, coordinates);
|
||||
protected EntityUid SpawnAttachedTo(string? prototype, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
=> EntityManager.SpawnAttachedTo(prototype, coordinates, overrides);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.SpawnAtPosition" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid SpawnAtPosition(string? prototype, EntityCoordinates coordinates)
|
||||
=> EntityManager.SpawnAtPosition(prototype, coordinates);
|
||||
protected EntityUid SpawnAtPosition(string? prototype, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
=> EntityManager.SpawnAtPosition(prototype, coordinates, overrides);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.TrySpawnInContainer" />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
@@ -38,6 +39,26 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
int Count(Type component);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified components from the <see cref="EntityPrototype"/>
|
||||
/// </summary>
|
||||
void AddComponents(EntityUid target, EntityPrototype prototype, bool removeExisting = true);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified registry components to the target entity.
|
||||
/// </summary>
|
||||
void AddComponents(EntityUid target, ComponentRegistry registry, bool removeExisting = true);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified entity prototype components from the target entity.
|
||||
/// </summary>
|
||||
void RemoveComponents(EntityUid target, EntityPrototype prototype);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified registry components from the target entity.
|
||||
/// </summary>
|
||||
void RemoveComponents(EntityUid target, ComponentRegistry registry);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Component type to an entity. If the entity is already Initialized, the component will
|
||||
/// automatically be Initialized and Started.
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
@@ -27,12 +28,12 @@ public partial interface IEntityManager
|
||||
/// <summary>
|
||||
/// Spawns an entity in nullspace.
|
||||
/// </summary>
|
||||
EntityUid Spawn(string? protoName = null, ComponentRegistry? overrides = null);
|
||||
EntityUid Spawn(string? protoName = null, ComponentRegistry? overrides = null, bool doMapInit = true);
|
||||
|
||||
/// <summary>
|
||||
/// Spawns an entity at a specific world position. The entity will either be parented to the map or a grid.
|
||||
/// </summary>
|
||||
EntityUid Spawn(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
EntityUid Spawn(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default!);
|
||||
|
||||
/// <summary>
|
||||
/// Spawns an entity and then parents it to the entity that the given entity coordinates are relative to.
|
||||
@@ -46,7 +47,7 @@ public partial interface IEntityManager
|
||||
EntityUid SpawnAtPosition(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to spawn an entity inside of a container.
|
||||
/// Attempt to spawn an entity and insert it into a container. If the insertion fails, the entity gets deleted.
|
||||
/// </summary>
|
||||
bool TrySpawnInContainer(
|
||||
string? protoName,
|
||||
@@ -58,9 +59,9 @@ public partial interface IEntityManager
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to spawn an entity inside of a container. If it fails to insert into the container, it will
|
||||
/// instead attempt to spawn the entity next to the target.
|
||||
/// instead drop the entity next to the target (see <see cref="SpawnNextToOrDrop"/>).
|
||||
/// </summary>
|
||||
public EntityUid SpawnInContainerOrDrop(
|
||||
EntityUid SpawnInContainerOrDrop(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
@@ -68,9 +69,20 @@ public partial interface IEntityManager
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null);
|
||||
|
||||
/// <inheritdoc cref="SpawnInContainerOrDrop(string?,Robust.Shared.GameObjects.EntityUid,string,Robust.Shared.GameObjects.TransformComponent?,Robust.Shared.Containers.ContainerManagerComponent?,Robust.Shared.Prototypes.ComponentRegistry?)"/>
|
||||
EntityUid SpawnInContainerOrDrop(
|
||||
string? protoName,
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
out bool inserted,
|
||||
TransformComponent? xform = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to spawn an entity adjacent to some other entity. If the other entity is in a container, this will
|
||||
/// attempt to insert the new entity into the same container.
|
||||
/// Attempts to spawn an entity adjacent to some other target entity. If the target entity is in
|
||||
/// a container, this will attempt to insert the spawned entity into the same container. If the insertion fails,
|
||||
/// the entity is deleted. If the entity is not in a container, this behaves like <see cref="SpawnNextToOrDrop"/>.
|
||||
/// </summary>
|
||||
bool TrySpawnNextTo(
|
||||
string? protoName,
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using JetBrains.Annotations;
|
||||
using Prometheus;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -76,10 +77,12 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
EntityUid CreateEntityUninitialized(string? prototypeName, EntityCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
|
||||
EntityUid CreateEntityUninitialized(string? prototypeName, MapCoordinates coordinates, ComponentRegistry? overrides = null);
|
||||
EntityUid CreateEntityUninitialized(string? prototypeName, MapCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default!);
|
||||
|
||||
void InitializeAndStartEntity(EntityUid entity, MapId? mapId = null);
|
||||
|
||||
void InitializeAndStartEntity(Entity<MetaDataComponent?> entity, bool doMapInit);
|
||||
|
||||
void InitializeEntity(EntityUid entity, MetaDataComponent? meta = null);
|
||||
|
||||
void StartEntity(EntityUid entity);
|
||||
|
||||
@@ -67,7 +67,7 @@ public sealed partial class EntityLookupSystem
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var uid in toAdd)
|
||||
foreach (var uid in toAdd.Span)
|
||||
{
|
||||
intersecting.Add(uid);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public sealed partial class EntityLookupSystem
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var uid in toAdd)
|
||||
foreach (var uid in toAdd.Span)
|
||||
{
|
||||
intersecting.Add(uid);
|
||||
}
|
||||
|
||||
@@ -74,6 +74,28 @@ public abstract class SharedAppearanceSystem : EntitySystem
|
||||
|
||||
return component.AppearanceData.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies appearance data from <c>src</c> to <c>dest</c>.
|
||||
/// If <c>src</c> has no <see cref="AppearanceComponent"/> nothing is done.
|
||||
/// If <c>dest</c> has no <c>AppearanceComponent</c> then it is created.
|
||||
/// </summary>
|
||||
public void CopyData(Entity<AppearanceComponent?> src, Entity<AppearanceComponent?> dest)
|
||||
{
|
||||
if (!Resolve(src, ref src.Comp, false))
|
||||
return;
|
||||
|
||||
dest.Comp ??= EnsureComp<AppearanceComponent>(dest);
|
||||
dest.Comp.AppearanceData.Clear();
|
||||
|
||||
foreach (var (key, value) in src.Comp.AppearanceData)
|
||||
{
|
||||
dest.Comp.AppearanceData[key] = value;
|
||||
}
|
||||
|
||||
Dirty(dest, dest.Comp);
|
||||
QueueUpdate(dest, dest.Comp);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
|
||||
@@ -180,7 +180,7 @@ namespace Robust.Shared.GameObjects
|
||||
toRemove.Add((oldId, oldFixture));
|
||||
}
|
||||
|
||||
foreach (var (id, fixture) in toRemove)
|
||||
foreach (var (id, fixture) in toRemove.Span)
|
||||
{
|
||||
// TODO add a DestroyFixture() override that takes in a list.
|
||||
// reduced broadphase lookups
|
||||
@@ -194,7 +194,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
// Anything remaining is a new fixture (or at least, may have not serialized onto the chunk yet).
|
||||
foreach (var (id, fixture) in newFixtures)
|
||||
foreach (var (id, fixture) in newFixtures.Span)
|
||||
{
|
||||
var existingFixture = _fixtures.GetFixtureOrNull(uid, id, manager: manager);
|
||||
// Check if it's the same (otherwise remove anyway).
|
||||
|
||||
@@ -1,68 +1,188 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public abstract partial class SharedMapSystem
|
||||
{
|
||||
protected int LastMapId;
|
||||
|
||||
private void InitializeMap()
|
||||
{
|
||||
SubscribeLocalEvent<MapComponent, ComponentAdd>(OnMapAdd);
|
||||
SubscribeLocalEvent<MapComponent, ComponentInit>(OnMapInit);
|
||||
SubscribeLocalEvent<MapComponent, ComponentAdd>(OnComponentAdd);
|
||||
SubscribeLocalEvent<MapComponent, ComponentInit>(OnCompInit);
|
||||
SubscribeLocalEvent<MapComponent, ComponentStartup>(OnCompStartup);
|
||||
SubscribeLocalEvent<MapComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<MapComponent, ComponentShutdown>(OnMapRemoved);
|
||||
SubscribeLocalEvent<MapComponent, ComponentHandleState>(OnMapHandleState);
|
||||
SubscribeLocalEvent<MapComponent, ComponentGetState>(OnMapGetState);
|
||||
}
|
||||
|
||||
public bool MapExists([NotNullWhen(true)] MapId? mapId)
|
||||
{
|
||||
return mapId != null && Maps.ContainsKey(mapId.Value);
|
||||
}
|
||||
|
||||
public EntityUid GetMap(MapId mapId)
|
||||
{
|
||||
return Maps[mapId];
|
||||
}
|
||||
|
||||
public bool TryGetMap([NotNullWhen(true)] MapId? mapId, [NotNullWhen(true)] out EntityUid? uid)
|
||||
{
|
||||
if (mapId == null || !Maps.TryGetValue(mapId.Value, out var map))
|
||||
{
|
||||
uid = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
uid = map;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnMapHandleState(EntityUid uid, MapComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not MapComponentState state)
|
||||
return;
|
||||
|
||||
component.MapId = state.MapId;
|
||||
|
||||
if (!MapManager.MapExists(state.MapId))
|
||||
if (component.MapId == MapId.Nullspace)
|
||||
{
|
||||
var mapInternal = (IMapManagerInternal)MapManager;
|
||||
mapInternal.CreateMap(state.MapId, uid);
|
||||
if (state.MapId == MapId.Nullspace)
|
||||
throw new Exception($"Received invalid map state? {ToPrettyString(uid)}");
|
||||
|
||||
component.MapId = state.MapId;
|
||||
Maps.Add(component.MapId, uid);
|
||||
RecursiveMapIdUpdate(uid, uid, component.MapId);
|
||||
}
|
||||
|
||||
DebugTools.AssertEqual(component.MapId, state.MapId);
|
||||
component.LightingEnabled = state.LightingEnabled;
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
component.MapInitialized = state.Initialized;
|
||||
|
||||
xformQuery.GetComponent(uid).ChangeMapId(state.MapId, xformQuery);
|
||||
if (LifeStage(uid) >= EntityLifeStage.Initialized)
|
||||
SetPaused(uid, state.MapPaused);
|
||||
else
|
||||
component.MapPaused = state.MapPaused;
|
||||
}
|
||||
|
||||
MapManager.SetMapPaused(state.MapId, state.MapPaused);
|
||||
private void RecursiveMapIdUpdate(EntityUid uid, EntityUid mapUid, MapId mapId)
|
||||
{
|
||||
// This is required only in the event where an entity becomes a map AFTER children have already been attached to it.
|
||||
// AFAIK, this currently only happens when the client applies entity states out of order (i.e., ignoring transform hierarchy),
|
||||
// which itself only happens if PVS is disabled.
|
||||
// TODO MAPS remove this
|
||||
|
||||
var xform = Transform(uid);
|
||||
xform.MapUid = mapUid;
|
||||
xform.MapID = mapId;
|
||||
xform._mapIdInitialized = true;
|
||||
foreach (var child in xform._children)
|
||||
{
|
||||
RecursiveMapIdUpdate(child, mapUid, mapId);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMapGetState(EntityUid uid, MapComponent component, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new MapComponentState(component.MapId, component.LightingEnabled, component.MapPaused);
|
||||
args.State = new MapComponentState(component.MapId, component.LightingEnabled, component.MapPaused, component.MapInitialized);
|
||||
}
|
||||
|
||||
protected abstract void OnMapAdd(EntityUid uid, MapComponent component, ComponentAdd args);
|
||||
protected abstract MapId GetNextMapId();
|
||||
|
||||
private void OnMapInit(EntityUid uid, MapComponent component, ComponentInit args)
|
||||
private void OnComponentAdd(EntityUid uid, MapComponent component, ComponentAdd args)
|
||||
{
|
||||
// ordered startups when
|
||||
EnsureComp<PhysicsMapComponent>(uid);
|
||||
EnsureComp<GridTreeComponent>(uid);
|
||||
EnsureComp<MovedGridsComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnCompInit(EntityUid uid, MapComponent component, ComponentInit args)
|
||||
{
|
||||
if (component.MapId == MapId.Nullspace)
|
||||
component.MapId = GetNextMapId();
|
||||
|
||||
DebugTools.AssertEqual(component.MapId.IsClientSide, IsClientSide(uid));
|
||||
if (!Maps.TryAdd(component.MapId, uid))
|
||||
{
|
||||
if (Maps[component.MapId] != uid)
|
||||
throw new Exception($"Attempted to initialize a map {ToPrettyString(uid)} with a duplicate map id {component.MapId}");
|
||||
}
|
||||
|
||||
var msg = new MapChangedEvent(uid, component.MapId, true);
|
||||
RaiseLocalEvent(uid, msg, true);
|
||||
}
|
||||
|
||||
private void OnCompStartup(EntityUid uid, MapComponent component, ComponentStartup args)
|
||||
{
|
||||
if (component.MapPaused)
|
||||
RecursiveSetPaused(uid, true);
|
||||
}
|
||||
|
||||
private void OnMapRemoved(EntityUid uid, MapComponent component, ComponentShutdown args)
|
||||
{
|
||||
DebugTools.Assert(component.MapId != MapId.Nullspace);
|
||||
Log.Info($"Deleting map {component.MapId}");
|
||||
|
||||
var iMap = (IMapManagerInternal)MapManager;
|
||||
iMap.RemoveMapId(component.MapId);
|
||||
Maps.Remove(component.MapId);
|
||||
|
||||
var msg = new MapChangedEvent(uid, component.MapId, false);
|
||||
RaiseLocalEvent(uid, msg, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new map, automatically assigning a map id.
|
||||
/// </summary>
|
||||
public EntityUid CreateMap(out MapId mapId, bool runMapInit = true)
|
||||
{
|
||||
mapId = GetNextMapId();
|
||||
var uid = CreateMap(mapId, runMapInit);
|
||||
return uid;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CreateMap(out Robust.Shared.Map.MapId,bool)"/>
|
||||
public EntityUid CreateMap(bool runMapInit = true) => CreateMap(out _, runMapInit);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new map with the specified map id.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentException">Throws if an invalid or already existing map id is provided.</exception>
|
||||
public EntityUid CreateMap(MapId mapId, bool runMapInit = true)
|
||||
{
|
||||
if (Maps.ContainsKey(mapId))
|
||||
throw new ArgumentException($"Map with id {mapId} already exists");
|
||||
|
||||
if (mapId == MapId.Nullspace)
|
||||
throw new ArgumentException($"Cannot create a null-space map");
|
||||
|
||||
if (_netManager.IsServer && mapId.IsClientSide)
|
||||
throw new ArgumentException($"Attempted to create a client-side map on the server?");
|
||||
|
||||
if (_netManager.IsClient && _netManager.IsConnected && !mapId.IsClientSide)
|
||||
throw new ArgumentException($"Attempted to create a client-side map entity with a non client-side map ID?");
|
||||
|
||||
var uid = EntityManager.CreateEntityUninitialized(null);
|
||||
var map = _factory.GetComponent<MapComponent>();
|
||||
map.MapId = mapId;
|
||||
AddComp(uid, map);
|
||||
|
||||
// Give the entity a name, mainly for debugging. Content can always override this with a localized name.
|
||||
var meta = MetaData(uid);
|
||||
_meta.SetEntityName(uid, $"Map Entity", meta);
|
||||
|
||||
// Initialize components. this should add the map id to the collections.
|
||||
EntityManager.InitializeComponents(uid, meta);
|
||||
EntityManager.StartComponents(uid);
|
||||
DebugTools.Assert(Maps[mapId] == uid);
|
||||
|
||||
if (runMapInit)
|
||||
InitializeMap((uid, map));
|
||||
else
|
||||
SetPaused((uid, map), true);
|
||||
|
||||
return uid;
|
||||
}
|
||||
}
|
||||
|
||||
83
Robust.Shared/GameObjects/Systems/SharedMapSystem.MapInit.cs
Normal file
83
Robust.Shared/GameObjects/Systems/SharedMapSystem.MapInit.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public abstract partial class SharedMapSystem
|
||||
{
|
||||
public bool IsInitialized(MapId mapId)
|
||||
{
|
||||
if (mapId == MapId.Nullspace)
|
||||
return true; // Nullspace is always initialized
|
||||
|
||||
if(!Maps.TryGetValue(mapId, out var uid))
|
||||
throw new ArgumentException($"Map {mapId} does not exist.");
|
||||
|
||||
return IsInitialized(uid);
|
||||
}
|
||||
public bool IsInitialized(EntityUid? map)
|
||||
{
|
||||
if (map == null)
|
||||
return true; // Nullspace is always initialized
|
||||
|
||||
return IsInitialized(map.Value);
|
||||
}
|
||||
|
||||
public bool IsInitialized(Entity<MapComponent?> map)
|
||||
{
|
||||
if (!_mapQuery.Resolve(map, ref map.Comp))
|
||||
return false;
|
||||
|
||||
return map.Comp.MapInitialized;
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, MapComponent component, MapInitEvent args)
|
||||
{
|
||||
DebugTools.Assert(!component.MapInitialized);
|
||||
component.MapInitialized = true;
|
||||
EntityManager.Dirty(uid, component);
|
||||
}
|
||||
|
||||
public void InitializeMap(MapId mapId, bool unpause = true)
|
||||
{
|
||||
if(!Maps.TryGetValue(mapId, out var uid))
|
||||
throw new ArgumentException($"Map {mapId} does not exist.");
|
||||
|
||||
InitializeMap(uid, unpause);
|
||||
}
|
||||
|
||||
public void InitializeMap(Entity<MapComponent?> map, bool unpause = true)
|
||||
{
|
||||
if (!_mapQuery.Resolve(map, ref map.Comp))
|
||||
return;
|
||||
|
||||
if (map.Comp.MapInitialized)
|
||||
throw new ArgumentException($"Map {ToPrettyString(map)} is already initialized.");
|
||||
|
||||
RecursiveMapInit(map.Owner);
|
||||
|
||||
if (unpause)
|
||||
SetPaused(map, false);
|
||||
}
|
||||
|
||||
private void RecursiveMapInit(EntityUid entity)
|
||||
{
|
||||
var toInitialize = new List<EntityUid> {entity};
|
||||
for (var i = 0; i < toInitialize.Count; i++)
|
||||
{
|
||||
var uid = toInitialize[i];
|
||||
// toInitialize might contain deleted entities.
|
||||
if(!_metaQuery.TryComp(uid, out var meta))
|
||||
continue;
|
||||
|
||||
if (meta.EntityLifeStage == EntityLifeStage.MapInitialized)
|
||||
continue;
|
||||
|
||||
toInitialize.AddRange(Transform(uid)._children);
|
||||
EntityManager.RunMapInit(uid, meta);
|
||||
}
|
||||
}
|
||||
}
|
||||
60
Robust.Shared/GameObjects/Systems/SharedMapSystem.Pause.cs
Normal file
60
Robust.Shared/GameObjects/Systems/SharedMapSystem.Pause.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public abstract partial class SharedMapSystem
|
||||
{
|
||||
public bool IsPaused(MapId mapId)
|
||||
{
|
||||
if (mapId == MapId.Nullspace)
|
||||
return false;
|
||||
|
||||
if(!Maps.TryGetValue(mapId, out var uid))
|
||||
throw new ArgumentException($"Map {mapId} does not exist.");
|
||||
|
||||
return IsPaused(uid);
|
||||
}
|
||||
|
||||
public bool IsPaused(Entity<MapComponent?> map)
|
||||
{
|
||||
if (!_mapQuery.Resolve(map, ref map.Comp))
|
||||
return false;
|
||||
|
||||
return map.Comp.MapPaused;
|
||||
}
|
||||
|
||||
public void SetPaused(MapId mapId, bool paused)
|
||||
{
|
||||
if(!Maps.TryGetValue(mapId, out var uid))
|
||||
throw new ArgumentException($"Map {mapId} does not exist.");
|
||||
|
||||
SetPaused(uid, paused);
|
||||
}
|
||||
|
||||
public void SetPaused(Entity<MapComponent?> map, bool paused)
|
||||
{
|
||||
if (!_mapQuery.Resolve(map, ref map.Comp))
|
||||
return;
|
||||
|
||||
if (map.Comp.MapPaused == paused)
|
||||
return;
|
||||
|
||||
map.Comp.MapPaused = paused;
|
||||
if (map.Comp.LifeStage < ComponentLifeStage.Initializing)
|
||||
return;
|
||||
|
||||
Dirty(map);
|
||||
RecursiveSetPaused(map, paused);
|
||||
}
|
||||
|
||||
private void RecursiveSetPaused(EntityUid entity, bool paused)
|
||||
{
|
||||
_meta.SetEntityPaused(entity, paused);
|
||||
foreach (var child in Transform(entity)._children)
|
||||
{
|
||||
RecursiveSetPaused(child, paused);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,17 +20,23 @@ namespace Robust.Shared.GameObjects
|
||||
[Dependency] private readonly FixtureSystem _fixtures = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly IComponentFactory _factory = default!;
|
||||
[Dependency] private readonly MetaDataSystem _meta = default!;
|
||||
|
||||
private EntityQuery<MapComponent> _mapQuery;
|
||||
private EntityQuery<MapGridComponent> _gridQuery;
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
internal Dictionary<MapId, EntityUid> Maps { get; } = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_mapQuery = GetEntityQuery<MapComponent>();
|
||||
_gridQuery = GetEntityQuery<MapGridComponent>();
|
||||
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
InitializeMap();
|
||||
|
||||
@@ -199,47 +199,34 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
#region Component Lifetime
|
||||
|
||||
private (EntityUid?, MapId) InitializeMapUid(EntityUid uid, TransformComponent xform)
|
||||
{
|
||||
if (xform._mapIdInitialized)
|
||||
return (xform.MapUid, xform.MapID);
|
||||
|
||||
if (xform.ParentUid.IsValid())
|
||||
{
|
||||
(xform.MapUid, xform.MapID) = InitializeMapUid(xform.ParentUid, Transform(xform.ParentUid));
|
||||
}
|
||||
else if (_mapQuery.TryComp(uid, out var mapComp))
|
||||
{
|
||||
DebugTools.AssertNotEqual(mapComp.MapId, MapId.Nullspace);
|
||||
xform.MapUid = uid;
|
||||
xform.MapID = mapComp.MapId;
|
||||
}
|
||||
else
|
||||
{
|
||||
xform.MapUid = null;
|
||||
xform.MapID = MapId.Nullspace;
|
||||
}
|
||||
|
||||
xform._mapIdInitialized = true;
|
||||
return (xform.MapUid, xform.MapID);
|
||||
}
|
||||
|
||||
private void OnCompInit(EntityUid uid, TransformComponent component, ComponentInit args)
|
||||
{
|
||||
// Children MAY be initialized here before their parents are.
|
||||
// We do this whole dance to handle this recursively,
|
||||
// setting _mapIdInitialized along the way to avoid going to the MapComponent every iteration.
|
||||
static MapId FindMapIdAndSet(EntityUid uid, TransformComponent xform, IEntityManager entMan, EntityQuery<TransformComponent> xformQuery, IMapManager mapManager)
|
||||
{
|
||||
if (xform._mapIdInitialized)
|
||||
return xform.MapID;
|
||||
|
||||
MapId value;
|
||||
|
||||
if (xform.ParentUid.IsValid())
|
||||
{
|
||||
value = FindMapIdAndSet(xform.ParentUid, xformQuery.GetComponent(xform.ParentUid), entMan, xformQuery, mapManager);
|
||||
}
|
||||
else
|
||||
{
|
||||
// second level node, terminates recursion up the branch of the tree
|
||||
if (entMan.TryGetComponent(uid, out MapComponent? mapComp))
|
||||
{
|
||||
value = mapComp.MapId;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We allow entities to be spawned directly into null-space.
|
||||
value = MapId.Nullspace;
|
||||
}
|
||||
}
|
||||
|
||||
xform.MapUid = value == MapId.Nullspace ? null : mapManager.GetMapEntityId(value);
|
||||
xform.MapID = value;
|
||||
xform._mapIdInitialized = true;
|
||||
return value;
|
||||
}
|
||||
|
||||
if (!component._mapIdInitialized)
|
||||
{
|
||||
FindMapIdAndSet(uid, component, EntityManager, XformQuery, _mapManager);
|
||||
component._mapIdInitialized = true;
|
||||
}
|
||||
InitializeMapUid(uid, component);
|
||||
|
||||
// Has to be done if _parent is set from ExposeData.
|
||||
if (component.ParentUid.IsValid())
|
||||
@@ -522,6 +509,8 @@ public abstract partial class SharedTransformSystem
|
||||
throw new InvalidOperationException($"Attempted to re-parent to a terminating object. Entity: {ToPrettyString(uid)}, new parent: {ToPrettyString(value.EntityId)}");
|
||||
}
|
||||
|
||||
InitializeMapUid(value.EntityId, newParent);
|
||||
|
||||
// Check for recursive/circular transform hierarchies.
|
||||
if (xform.MapUid == newParent.MapUid)
|
||||
{
|
||||
@@ -881,6 +870,28 @@ public abstract partial class SharedTransformSystem
|
||||
return GetMapCoordinates(entity.Comp);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetMapCoordinates(EntityUid entity, MapCoordinates coordinates)
|
||||
{
|
||||
var xform = XformQuery.GetComponent(entity);
|
||||
SetMapCoordinates((entity, xform), coordinates);
|
||||
}
|
||||
|
||||
public void SetMapCoordinates(Entity<TransformComponent> entity, MapCoordinates coordinates)
|
||||
{
|
||||
var mapUid = _map.GetMap(coordinates.MapId);
|
||||
if (!_gridQuery.HasComponent(entity) &&
|
||||
_mapManager.TryFindGridAt(mapUid, coordinates.Position, out var targetGrid, out _))
|
||||
{
|
||||
var invWorldMatrix = GetInvWorldMatrix(targetGrid);
|
||||
SetCoordinates(entity, new EntityCoordinates(targetGrid, invWorldMatrix.Transform(coordinates.Position)));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCoordinates(entity, new EntityCoordinates(mapUid, coordinates.Position));
|
||||
}
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public (Vector2 WorldPosition, Angle WorldRotation) GetWorldPositionRotation(EntityUid uid)
|
||||
{
|
||||
@@ -993,7 +1004,7 @@ public abstract partial class SharedTransformSystem
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetWorldPosition(Entity<TransformComponent> entity, Vector2 worldPos)
|
||||
{
|
||||
SetWorldPositionRotation(entity.Owner, worldPos, entity.Comp.LocalRotation, entity.Comp);
|
||||
SetWorldPositionRotationInternal(entity.Owner, worldPos, null, entity.Comp);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -1079,10 +1090,17 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetWorldPositionRotation(EntityUid uid, Vector2 worldPos, Angle worldRot, TransformComponent? component = null)
|
||||
{
|
||||
SetWorldPositionRotationInternal(uid, worldPos, worldRot, component);
|
||||
}
|
||||
|
||||
private void SetWorldPositionRotationInternal(EntityUid uid, Vector2 worldPos, Angle? worldRot = null, TransformComponent? component = null)
|
||||
{
|
||||
if (!XformQuery.Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
// If no worldRot supplied then default the new rotation to 0.
|
||||
|
||||
if (!component._parent.IsValid() || component.MapUid == null)
|
||||
{
|
||||
DebugTools.Assert("Parent is invalid while attempting to set WorldPosition - did you try to move root node?");
|
||||
@@ -1453,8 +1471,7 @@ public abstract partial class SharedTransformSystem
|
||||
while (targetXform.ParentUid.IsValid())
|
||||
{
|
||||
if (_container.IsEntityInContainer(targetUid)
|
||||
&& _container.TryGetContainingContainer(targetXform.ParentUid, targetUid, out var container,
|
||||
skipExistCheck: true)
|
||||
&& _container.TryGetContainingContainer(targetXform.ParentUid, targetUid, out var container)
|
||||
&& _container.Insert((entity, xform, null, null), container))
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Robust.Shared.GameObjects
|
||||
[Dependency] private readonly INetManager _netMan = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
|
||||
private EntityQuery<MapComponent> _mapQuery;
|
||||
private EntityQuery<MapGridComponent> _gridQuery;
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
protected EntityQuery<TransformComponent> XformQuery;
|
||||
@@ -50,6 +51,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
UpdatesOutsidePrediction = true;
|
||||
|
||||
_mapQuery = GetEntityQuery<MapComponent>();
|
||||
_gridQuery = GetEntityQuery<MapGridComponent>();
|
||||
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
XformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
@@ -12,35 +12,29 @@ namespace Robust.Shared.Map.Components
|
||||
public sealed partial class MapComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("lightingEnabled")]
|
||||
[DataField]
|
||||
public bool LightingEnabled { get; set; } = true;
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public MapId MapId { get; internal set; } = MapId.Nullspace;
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public bool MapPaused { get; set; } = false;
|
||||
[DataField, Access(typeof(SharedMapSystem), typeof(MapManager))]
|
||||
public bool MapPaused;
|
||||
|
||||
//TODO replace MapPreInit with the map's entity life stage
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public bool MapPreInit { get; set; } = false;
|
||||
[DataField, Access(typeof(SharedMapSystem), typeof(MapManager))]
|
||||
public bool MapInitialized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialized state of a <see cref="MapGridComponentState"/>.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class MapComponentState : ComponentState
|
||||
public sealed class MapComponentState(MapId mapId, bool lightingEnabled, bool paused, bool init)
|
||||
: ComponentState
|
||||
{
|
||||
public MapId MapId;
|
||||
public bool LightingEnabled;
|
||||
public bool MapPaused;
|
||||
|
||||
public MapComponentState(MapId mapId, bool lightingEnabled, bool paused)
|
||||
{
|
||||
MapId = mapId;
|
||||
LightingEnabled = lightingEnabled;
|
||||
MapPaused = paused;
|
||||
}
|
||||
public MapId MapId = mapId;
|
||||
public bool LightingEnabled = lightingEnabled;
|
||||
public bool MapPaused = paused;
|
||||
public bool Initialized = init;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,6 @@ namespace Robust.Shared.Map
|
||||
public const bool Approximate = false;
|
||||
public const bool IncludeMap = true;
|
||||
|
||||
[Obsolete("Use EntityQuery<MapGridComponent>")]
|
||||
IEnumerable<MapGridComponent> GetAllGrids();
|
||||
|
||||
/// <summary>
|
||||
/// Should the OnTileChanged event be suppressed? This is useful for initially loading the map
|
||||
/// so that you don't spam an event for each of the million station tiles.
|
||||
@@ -42,16 +39,7 @@ namespace Robust.Shared.Map
|
||||
|
||||
void Restart();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new map.
|
||||
/// </summary>
|
||||
/// <param name="mapId">
|
||||
/// If provided, the new map will use this ID. If not provided, a new ID will be selected automatically.
|
||||
/// </param>
|
||||
/// <returns>The new map.</returns>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Throw if an explicit ID for the map or default grid is passed and a map or grid with the specified ID already exists, respectively.
|
||||
/// </exception>
|
||||
[Obsolete("Use MapSystem")]
|
||||
MapId CreateMap(MapId? mapId = null);
|
||||
|
||||
/// <summary>
|
||||
@@ -59,24 +47,12 @@ namespace Robust.Shared.Map
|
||||
/// </summary>
|
||||
/// <param name="mapId">The map ID to check existence of.</param>
|
||||
/// <returns>True if the map exists, false otherwise.</returns>
|
||||
bool MapExists(MapId mapId);
|
||||
bool MapExists([NotNullWhen(true)] MapId? mapId);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new entity, then sets it as the map entity.
|
||||
/// </summary>
|
||||
/// <returns>Newly created entity.</returns>
|
||||
EntityUid CreateNewMapEntity(MapId mapId);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the MapEntity(root node) for a given map. If an entity is already set, it will be deleted
|
||||
/// before the new one is set.
|
||||
/// </summary>
|
||||
/// <param name="updateChildren">Should we re-parent children from the old map to the new one, or delete them.</param>
|
||||
void SetMapEntity(MapId mapId, EntityUid newMapEntityId, bool updateChildren = true);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the map entity ID for a given map.
|
||||
/// Returns the map entity ID for a given map, or an invalid entity Id if the map does not exist.
|
||||
/// </summary>
|
||||
[Obsolete("Use TryGetMap")]
|
||||
EntityUid GetMapEntityId(MapId mapId);
|
||||
|
||||
/// <summary>
|
||||
@@ -93,6 +69,7 @@ namespace Robust.Shared.Map
|
||||
MapGridComponent CreateGrid(MapId currentMapId, in GridCreateOptions options);
|
||||
MapGridComponent CreateGrid(MapId currentMapId);
|
||||
Entity<MapGridComponent> CreateGridEntity(MapId currentMapId, GridCreateOptions? options = null);
|
||||
Entity<MapGridComponent> CreateGridEntity(EntityUid map, GridCreateOptions? options = null);
|
||||
|
||||
[Obsolete("Use GetComponent<MapGridComponent>(uid)")]
|
||||
MapGridComponent GetGrid(EntityUid gridId);
|
||||
@@ -233,17 +210,13 @@ namespace Robust.Shared.Map
|
||||
|
||||
#endregion
|
||||
|
||||
void DeleteGrid(EntityUid euid);
|
||||
|
||||
bool HasMapEntity(MapId mapId);
|
||||
[Obsolete("Just delete the grid entity")]
|
||||
void DeleteGrid(EntityUid euid);
|
||||
|
||||
bool IsGrid(EntityUid uid);
|
||||
bool IsMap(EntityUid uid);
|
||||
|
||||
[Obsolete("Whatever this is used for, it is a terrible idea. Create a new map and get it's MapId.")]
|
||||
MapId NextMapId();
|
||||
MapGridComponent GetGridComp(EntityUid euid);
|
||||
|
||||
//
|
||||
// Pausing functions
|
||||
//
|
||||
@@ -252,14 +225,15 @@ namespace Robust.Shared.Map
|
||||
|
||||
void DoMapInitialize(MapId mapId);
|
||||
|
||||
// TODO rename this to actually be descriptive or just remove it.
|
||||
[Obsolete("Use CreateMap's runMapInit argument")]
|
||||
void AddUninitializedMap(MapId mapId);
|
||||
|
||||
[Pure]
|
||||
[Obsolete("Use MapSystem")]
|
||||
bool IsMapPaused(MapId mapId);
|
||||
|
||||
[Pure]
|
||||
[Obsolete("Use MapSystem")]
|
||||
bool IsMapInitialized(MapId mapId);
|
||||
|
||||
}
|
||||
|
||||
public struct GridCreateOptions
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Shared.Map
|
||||
{
|
||||
/// <inheritdoc />
|
||||
internal interface IMapManagerInternal : IMapManager
|
||||
{
|
||||
IGameTiming GameTiming { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Raises the OnTileChanged event.
|
||||
/// </summary>
|
||||
/// <param name="tileRef">A reference to the new tile.</param>
|
||||
/// <param name="oldTile">The old tile that got replaced.</param>
|
||||
void RaiseOnTileChanged(TileRef tileRef, Tile oldTile, Vector2i chunk);
|
||||
|
||||
MapId CreateMap(MapId? mapId, EntityUid euid);
|
||||
|
||||
void RemoveMapId(MapId mapId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,5 +53,7 @@ namespace Robust.Shared.Map
|
||||
{
|
||||
return Value.ToString();
|
||||
}
|
||||
|
||||
public bool IsClientSide => Value < 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,32 +12,15 @@ using Robust.Shared.Utility;
|
||||
namespace Robust.Shared.Map;
|
||||
internal partial class MapManager
|
||||
{
|
||||
[Obsolete("Use GetComponent<MapGridComponent>(uid)")]
|
||||
public MapGridComponent GetGridComp(EntityUid euid)
|
||||
{
|
||||
return EntityManager.GetComponent<MapGridComponent>(euid);
|
||||
}
|
||||
|
||||
[Obsolete("Use EntityQuery instead.")]
|
||||
public IEnumerable<MapGridComponent> GetAllGrids()
|
||||
{
|
||||
var compQuery = EntityManager.AllEntityQueryEnumerator<MapGridComponent>();
|
||||
|
||||
while (compQuery.MoveNext(out var comp))
|
||||
{
|
||||
yield return comp;
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once MethodOverloadWithOptionalParameter
|
||||
public MapGridComponent CreateGrid(MapId currentMapId, ushort chunkSize = 16)
|
||||
{
|
||||
return CreateGrid(currentMapId, chunkSize, default);
|
||||
return CreateGrid(GetMapEntityIdOrThrow(currentMapId), chunkSize, default);
|
||||
}
|
||||
|
||||
public MapGridComponent CreateGrid(MapId currentMapId, in GridCreateOptions options)
|
||||
{
|
||||
return CreateGrid(currentMapId, options.ChunkSize, default);
|
||||
return CreateGrid(GetMapEntityIdOrThrow(currentMapId), options.ChunkSize, default);
|
||||
}
|
||||
|
||||
public MapGridComponent CreateGrid(MapId currentMapId)
|
||||
@@ -46,18 +29,19 @@ internal partial class MapManager
|
||||
}
|
||||
|
||||
public Entity<MapGridComponent> CreateGridEntity(MapId currentMapId, GridCreateOptions? options = null)
|
||||
{
|
||||
return CreateGridEntity(GetMapEntityIdOrThrow(currentMapId), options);
|
||||
}
|
||||
|
||||
public Entity<MapGridComponent> CreateGridEntity(EntityUid map, GridCreateOptions? options = null)
|
||||
{
|
||||
options ??= GridCreateOptions.Default;
|
||||
return CreateGrid(currentMapId, options.Value.ChunkSize, default);
|
||||
return CreateGrid(map, options.Value.ChunkSize, default);
|
||||
}
|
||||
|
||||
[Obsolete("Use GetComponent<MapGridComponent>(uid)")]
|
||||
public MapGridComponent GetGrid(EntityUid gridId)
|
||||
{
|
||||
DebugTools.Assert(gridId.IsValid());
|
||||
|
||||
return GetGridComp(gridId);
|
||||
}
|
||||
=> EntityManager.GetComponent<MapGridComponent>(gridId);
|
||||
|
||||
[Obsolete("Use HasComponent<MapGridComponent>(uid)")]
|
||||
public bool IsGrid(EntityUid uid)
|
||||
@@ -108,10 +92,6 @@ internal partial class MapManager
|
||||
|
||||
public virtual void DeleteGrid(EntityUid euid)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugTools.Assert(_dbgGuardRunning);
|
||||
#endif
|
||||
|
||||
// Possible the grid was already deleted / is invalid
|
||||
if (!EntityManager.TryGetComponent<MapGridComponent>(euid, out var iGrid))
|
||||
{
|
||||
@@ -141,10 +121,6 @@ internal partial class MapManager
|
||||
/// <param name="oldTile">The old tile that got replaced.</param>
|
||||
public void RaiseOnTileChanged(TileRef tileRef, Tile oldTile, Vector2i chunk)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugTools.Assert(_dbgGuardRunning);
|
||||
#endif
|
||||
|
||||
if (SuppressOnTileChanged)
|
||||
return;
|
||||
|
||||
@@ -153,7 +129,7 @@ internal partial class MapManager
|
||||
EntityManager.EventBus.RaiseLocalEvent(euid, ref ev, true);
|
||||
}
|
||||
|
||||
protected Entity<MapGridComponent> CreateGrid(MapId currentMapId, ushort chunkSize, EntityUid forcedGridEuid)
|
||||
protected Entity<MapGridComponent> CreateGrid(EntityUid map, ushort chunkSize, EntityUid forcedGridEuid)
|
||||
{
|
||||
var gridEnt = EntityManager.CreateEntityUninitialized(null, forcedGridEuid);
|
||||
|
||||
@@ -166,8 +142,7 @@ internal partial class MapManager
|
||||
//are applied. After they are applied the parent may be different, but the MapId will
|
||||
//be the same. This causes TransformComponent.ParentUid of a grid to be unsafe to
|
||||
//use in transform states anytime before the state parent is properly set.
|
||||
var fallbackParentEuid = GetMapEntityIdOrThrow(currentMapId);
|
||||
EntityManager.GetComponent<TransformComponent>(gridEnt).AttachParent(fallbackParentEuid);
|
||||
EntityManager.GetComponent<TransformComponent>(gridEnt).AttachParent(map);
|
||||
|
||||
var meta = EntityManager.GetComponent<MetaDataComponent>(gridEnt);
|
||||
EntityManager.System<MetaDataSystem>().SetEntityName(gridEnt, $"grid", meta);
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace Robust.Shared.Map;
|
||||
|
||||
internal partial class MapManager
|
||||
{
|
||||
public void RemoveMapId(MapId mapId)
|
||||
{
|
||||
if (mapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
_mapEntities.Remove(mapId);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -27,131 +28,42 @@ public sealed class MapEventArgs : EventArgs
|
||||
|
||||
internal partial class MapManager
|
||||
{
|
||||
private readonly Dictionary<MapId, EntityUid> _mapEntities = new();
|
||||
private MapId _highestMapId = MapId.Nullspace;
|
||||
private Dictionary<MapId, EntityUid> _mapEntities => _mapSystem.Maps;
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void DeleteMap(MapId mapId)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugTools.Assert(_dbgGuardRunning);
|
||||
#endif
|
||||
|
||||
if (!_mapEntities.TryGetValue(mapId, out var ent) || !ent.IsValid())
|
||||
throw new InvalidOperationException($"Attempted to delete nonexistent map '{mapId}'");
|
||||
|
||||
EntityManager.DeleteEntity(ent);
|
||||
DebugTools.Assert(!_mapEntities.ContainsKey(mapId));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MapId CreateMap(MapId? mapId = null)
|
||||
{
|
||||
return ((IMapManagerInternal) this).CreateMap(mapId, default);
|
||||
if (mapId != null)
|
||||
{
|
||||
_mapSystem.CreateMap(mapId.Value);
|
||||
return mapId.Value;
|
||||
}
|
||||
|
||||
_mapSystem.CreateMap(out var map);
|
||||
return map;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool MapExists(MapId mapId)
|
||||
public bool MapExists([NotNullWhen(true)] MapId? mapId)
|
||||
{
|
||||
return _mapEntities.ContainsKey(mapId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EntityUid CreateNewMapEntity(MapId mapId)
|
||||
{
|
||||
DebugTools.Assert(mapId != MapId.Nullspace);
|
||||
#if DEBUG
|
||||
DebugTools.Assert(_dbgGuardRunning);
|
||||
#endif
|
||||
|
||||
var newEntity = EntityManager.CreateEntityUninitialized(null);
|
||||
SetMapEntity(mapId, newEntity);
|
||||
|
||||
EntityManager.InitializeComponents(newEntity);
|
||||
EntityManager.StartComponents(newEntity);
|
||||
|
||||
return newEntity;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetMapEntity(MapId mapId, EntityUid newMapEntity, bool updateChildren = true)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugTools.Assert(_dbgGuardRunning);
|
||||
#endif
|
||||
|
||||
if (!_mapEntities.ContainsKey(mapId))
|
||||
throw new InvalidOperationException($"Map {mapId} does not exist.");
|
||||
|
||||
foreach (var kvEntity in _mapEntities)
|
||||
{
|
||||
if (kvEntity.Value == newMapEntity)
|
||||
{
|
||||
if (mapId == kvEntity.Key)
|
||||
return;
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Entity {newMapEntity} is already the root node of another map {kvEntity.Key}.");
|
||||
}
|
||||
}
|
||||
|
||||
MapComponent? mapComp;
|
||||
// If this is being done as part of maploader then we want to copy the preinit state across mainly.
|
||||
bool preInit = false;
|
||||
bool paused = false;
|
||||
|
||||
// remove existing graph
|
||||
if (_mapEntities.TryGetValue(mapId, out var oldEntId))
|
||||
{
|
||||
if (EntityManager.TryGetComponent(oldEntId, out mapComp))
|
||||
{
|
||||
preInit = mapComp.MapPreInit;
|
||||
paused = mapComp.MapPaused;
|
||||
}
|
||||
|
||||
EntityManager.System<SharedTransformSystem>().ReparentChildren(oldEntId, newMapEntity);
|
||||
|
||||
//Note: EntityUid.Invalid gets passed in here
|
||||
//Note: This prevents setting a subgraph as the root, since the subgraph will be deleted
|
||||
EntityManager.DeleteEntity(oldEntId);
|
||||
}
|
||||
|
||||
var raiseEvent = false;
|
||||
|
||||
// re-use or add map component
|
||||
if (!EntityManager.TryGetComponent(newMapEntity, out mapComp))
|
||||
mapComp = EntityManager.AddComponent<MapComponent>(newMapEntity);
|
||||
else
|
||||
{
|
||||
raiseEvent = true;
|
||||
|
||||
if (mapComp.MapId != mapId)
|
||||
{
|
||||
_sawmill.Warning($"Setting map {mapId} root to entity {newMapEntity}, but entity thinks it is root node of map {mapComp.MapId}.");
|
||||
}
|
||||
}
|
||||
|
||||
_sawmill.Debug($"Setting map {mapId} entity to {newMapEntity}");
|
||||
|
||||
// set as new map entity
|
||||
mapComp.MapPreInit = preInit;
|
||||
mapComp.MapPaused = paused;
|
||||
|
||||
mapComp.MapId = mapId;
|
||||
_mapEntities[mapId] = newMapEntity;
|
||||
|
||||
// Yeah this sucks but I just want to save maps for now, deal.
|
||||
if (raiseEvent)
|
||||
{
|
||||
var ev = new MapChangedEvent(newMapEntity, mapId, true);
|
||||
EntityManager.EventBus.RaiseLocalEvent(newMapEntity, ev, true);
|
||||
}
|
||||
return _mapSystem.MapExists(mapId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EntityUid GetMapEntityId(MapId mapId)
|
||||
{
|
||||
if (_mapEntities.TryGetValue(mapId, out var entId))
|
||||
return entId;
|
||||
if (_mapSystem.TryGetMap(mapId, out var entId))
|
||||
return entId.Value;
|
||||
|
||||
return EntityUid.Invalid;
|
||||
}
|
||||
@@ -161,13 +73,12 @@ internal partial class MapManager
|
||||
/// </summary>
|
||||
public EntityUid GetMapEntityIdOrThrow(MapId mapId)
|
||||
{
|
||||
return _mapEntities[mapId];
|
||||
return _mapSystem.GetMap(mapId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasMapEntity(MapId mapId)
|
||||
public bool TryGetMap([NotNullWhen(true)] MapId? mapId, [NotNullWhen(true)] out EntityUid? uid)
|
||||
{
|
||||
return _mapEntities.ContainsKey(mapId);
|
||||
return _mapSystem.TryGetMap(mapId, out uid);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -181,67 +92,4 @@ internal partial class MapManager
|
||||
{
|
||||
return EntityManager.HasComponent<MapComponent>(uid);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MapId NextMapId()
|
||||
{
|
||||
return _highestMapId = new MapId(_highestMapId.Value + 1);
|
||||
}
|
||||
|
||||
MapId IMapManagerInternal.CreateMap(MapId? mapId, EntityUid entityUid)
|
||||
{
|
||||
if (mapId == MapId.Nullspace)
|
||||
throw new InvalidOperationException("Attempted to create a null-space map.");
|
||||
|
||||
#if DEBUG
|
||||
DebugTools.Assert(_dbgGuardRunning);
|
||||
#endif
|
||||
|
||||
var actualId = mapId ?? new MapId(_highestMapId.Value + 1);
|
||||
|
||||
if (MapExists(actualId))
|
||||
throw new InvalidOperationException($"A map with ID {actualId} already exists");
|
||||
|
||||
if (_highestMapId.Value < actualId.Value)
|
||||
_highestMapId = actualId;
|
||||
|
||||
_sawmill.Info($"Creating new map {actualId}");
|
||||
|
||||
if (actualId != MapId.Nullspace) // nullspace isn't bound to an entity
|
||||
{
|
||||
Entity<MapComponent> result = default;
|
||||
var query = EntityManager.AllEntityQueryEnumerator<MapComponent>();
|
||||
while (query.MoveNext(out var uid, out var map))
|
||||
{
|
||||
if (map.MapId != actualId)
|
||||
continue;
|
||||
|
||||
result = (uid, map);
|
||||
break;
|
||||
}
|
||||
|
||||
if (result != default)
|
||||
{
|
||||
DebugTools.Assert(mapId != null);
|
||||
_mapEntities.Add(actualId, result);
|
||||
_sawmill.Debug($"Rebinding map {actualId} to entity {result.Owner}");
|
||||
}
|
||||
else
|
||||
{
|
||||
var newEnt = EntityManager.CreateEntityUninitialized(null, entityUid);
|
||||
_mapEntities.Add(actualId, newEnt);
|
||||
|
||||
var mapComp = EntityManager.AddComponent<MapComponent>(newEnt);
|
||||
mapComp.MapId = actualId;
|
||||
var meta = EntityManager.GetComponent<MetaDataComponent>(newEnt);
|
||||
EntityManager.System<MetaDataSystem>().SetEntityName(newEnt, $"map {actualId}", meta);
|
||||
EntityManager.Dirty(newEnt, mapComp, meta);
|
||||
EntityManager.InitializeComponents(newEnt, meta);
|
||||
EntityManager.StartComponents(newEnt);
|
||||
_sawmill.Debug($"Binding map {actualId} to entity {newEnt}");
|
||||
}
|
||||
}
|
||||
|
||||
return actualId;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user