Compare commits

..

2 Commits

Author SHA1 Message Date
DrSmugleaf
3ae65f9e3d Version: 248.0.2-source-gen-debug 2025-03-20 21:13:47 -07:00
DrSmugleaf
af6fc51def Source gen changes 2025-03-20 18:06:10 -07:00
186 changed files with 1565 additions and 4002 deletions

View File

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

View File

@@ -54,163 +54,7 @@ END TEMPLATE-->
*None yet*
## 254.1.0
### New features
* Add CC ND licences to the RGA validator.
* Add entity spawn prediction and entity deletion prediction. This is currently limited as you are unable to predict interactions with these entities. These are done via the new methods prefixed with "Predicted". You can also manually flag an entity as a predicted spawn with the `FlagPredicted` method which will clean it up when prediction is reset.
### Bugfixes
* Fix tile edge rendering for neighbor tiles being the same priority.
### Other
* Fix SpawnAttachedTo's system proxy method not the rotation arg like EntityManager.
## 254.0.0
### Breaking changes
* Yaml mappings/dictionaries now only support string keys instead of generic nodes
* Several MappingDataNode method arguments or return values now use strings instead of a DataNode object
* The MappingDataNode class has various helper methods that still accept a ValueDataNode, but these methods are marked as obsolete and may be removed in the future.
* yaml validators should use `MappingDataNode.GetKeyNode()` when validating mapping keys, so that errors can print node start & end information
* ValueTuple yaml serialization has changed
* Previously they would get serialized into a single mapping with one entry (i.e., `{foo : bar }`
* Now they serialize into a sequence (i.e., `[foo, bar]`
* The ValueTuple serializer will still try to read mappings, but due to the MappingDataNode this may fail if the previously serialized "key" can't be read as a simple string
### New features
* Add cvar to disable tile edges.
* Add GetContainingContainers method to ContainerSystem to recursively get containers upwards on an entity.
### Internal
* Make component lifecycle methods use generics.
## 253.0.0
### New features
* Add a new `SerializationManager.PushComposition()` overload that takes in a single parent instead of an array of parents.
* `BoundUserInterfaceMessageAttempt` once again gets raised as a broadcast event, in addition to being directed.
* This effectively reverts the breaking part of the changes made in v252.0.0
* Fix CreateDistanceJoint using an int instead of a float for minimum distance.
### Bugfixes
* Fix deferred component removal not setting the component's life stage to `ComponentLifeStage.Stopped` if the component has not yet been initialised.
* Fix some `EntitySystem.Resolve()` overloads not respecting the optional `logMissing` argument.
* Fix screen-space overlays not being useable without first initializing/starting entity manager & systems
* ItemList is now significantly optimized. VV's `AddComponent` window in particular should be much faster.
* Fix some more MapValidator fields.
* Fix popup text overflowing the sides of the screen.
* Improve location reporting for non-writeable datafields via analyzer.
### Other
* TestPoint now uses generics rather than IPhysShape directly.
## 252.0.0
### Breaking changes
* BoundUserInterfaceMessageAttempt is raised directed against entities and no longer broadcast.
## 251.0.0
### Breaking changes
* Localization is now separate between client and server and is handled via cvar.
* Contacting entities no longer can be disabled for CollisionWake to avoid destroying the contacts unnecessarily.
### New features
* Added `DirectionExtensions.AllDirections`, which contains a list of all `Direction`s for easy enumeration.
* Add ForbidLiteralAttribute.
* Log late MsgEntity again.
* Show entity name in `physics shapeinfo` output.
* Make SubscribeLocalEvent not require EntityEventArgs.
* Add autocomplete to `tp` command.
* Add button to jump to live chat when scrolled up.
* Add autocomplete to `savemap` and `savegrid`.
### Bugfixes
* Fix velocity not re-applying correctly on re-parenting.
* Fix Equatable on FormattedMessage.
* Fix SharedTransformSystem methods logging errors on resolves.
### Other
* Significantly optimized tile edge rendering.
### Internal
* Remove duplicate GetMassData method.
* Inline manifold points for physics.
## 250.0.0
### Breaking changes
* The default shader now interprets negative color modulation as a flag that indicates that the light map should be ignored.
* This can be used to avoid having to change the light map texture, thus reducing draw batches.
* Sprite layers that are set to use the "unshaded" shader prototype now use this.
* Any fragment shaders that previously the `VtxModulate` colour modulation variable should instead use the new `MODULATE` variable, as the former may now contain negative values.
### New features
* Add OtherBody API to contacts.
* Make FormattedMessages Equatable.
* AnimationCompletionEvent now has the AnimationPlayerComponent.
* Add entity description as a tooltip on the entity spawn panel.
### Bugfixes
* Fix serialization source generator breaking if a class has two partial locations.
* Fix map saving throwing a `DirectoryNotFoundException` when given a path with a non-existent directory. Now it once again creates any missing directories.
* Fix map loading taking a significant time due to MappingDataNode.Equals calls being slow.
### Other
* Add Pure to some Angle methods.
### Internal
* Cleanup some warnings in classes.
## 249.0.0
### Breaking changes
* Layer is now read-only on VisibilityComponent and isn't serialized.
### New features
* Added a debug overlay for the linear and angular velocity of all entities on the screen. Use the `showvel` and `showangvel` commands to toggle it.
* Add a GetWorldManifold overload that doesn't require a span of points.
* Added a GetVisMaskEvent. Calling `RefreshVisibilityMask` will raise it and subscribers can update the vismask via the event rather than subscribers having to each manually try and handle the vismask directly.
### Bugfixes
* `BoxContainer` no longer causes stretching children to go below their minimum size.
* Fix lights on other grids getting clipped due to ignoring the light range cvar.
* Fix the `showvelocities` command.
* Fix the DirtyFields overload not being sandbox safe for content.
### Internal
* Polygon vertices are now inlined with FixedArray8 and a separate SlimPolygon using FixedArray4 for hot paths rather than using pooled arrays.
## 248.0.2-source-gen-debug
## 248.0.2

View File

@@ -11,7 +11,6 @@ cmd-parse-failure-uid = {$arg} is not a valid entity UID.
cmd-parse-failure-mapid = {$arg} is not a valid MapId.
cmd-parse-failure-enum = {$arg} is not a {$enum} Enum.
cmd-parse-failure-grid = {$arg} is not a valid grid.
cmd-parse-failure-cultureinfo = "{$arg}" is not valid CultureInfo.
cmd-parse-failure-entity-exist = UID {$arg} does not correspond to an existing entity.
cmd-parse-failure-session = There is no session with username: {$username}
@@ -429,20 +428,11 @@ cmd-entfo-help = Usage: entfo <entityuid>
The entity UID can be prefixed with 'c' to convert it to a client entity UID.
cmd-fuck-desc = Throws an exception
cmd-fuck-help = Usage: fuck
cmd-fuck-help = Throws an exception
cmd-showpos-desc = Show the position of all entities on the screen.
cmd-showpos-desc = Enables debug drawing over all entity positions in the game.
cmd-showpos-help = Usage: showpos
cmd-showrot-desc = Show the rotation of all entities on the screen.
cmd-showrot-help = Usage: showrot
cmd-showvel-desc = Show the local velocity of all entites on the screen.
cmd-showvel-help = Usage: showvel
cmd-showangvel-desc = Show the angular velocity of all entities on the screen.
cmd-showangvel-help = Usage: showangvel
cmd-sggcell-desc = Lists entities on a snap grid cell.
cmd-sggcell-help = Usage: sggcell <gridID> <vector2i>\nThat vector2i param is in the form x<int>,y<int>.
@@ -573,8 +563,3 @@ cmd-pvs-override-info-desc = Prints information about any PVS overrides associat
cmd-pvs-override-info-empty = Entity {$nuid} has no PVS overrides.
cmd-pvs-override-info-global = Entity {$nuid} has a global override.
cmd-pvs-override-info-clients = Entity {$nuid} has a session override for {$clients}.
cmd-localization_set_culture-desc = Set DefaultCulture for the client LocalizationManager
cmd-localization_set_culture-help = Usage: localization_set_culture <cultureName>
cmd-localization_set_culture-culture-name = <cultureName>
cmd-localization_set_culture-changed = Localization changed to { $code } ({ $nativeName } / { $englishName })

View File

@@ -14,10 +14,6 @@ tile-spawn-window-title = Place Tiles
console-line-edit-placeholder = Command Here
## OutputPanel
output-panel-scroll-down-button-text = Scroll Down
## Common Used
window-erase-button-text = Erase Mode

View File

@@ -87,66 +87,4 @@ public sealed class DataDefinitionAnalyzerTest
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldNoVVReadWriteRule).WithSpan(35, 17, 35, 50).WithArguments("Bad", "Foo")
);
}
[Test]
public async Task ReadOnlyFieldTest()
{
const string code = """
using System;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Robust.Shared.Serialization.Manager.Attributes
{
public class DataFieldBaseAttribute : Attribute;
public class DataFieldAttribute : DataFieldBaseAttribute;
public sealed class DataDefinitionAttribute : Attribute;
}
[DataDefinition]
public sealed partial class Foo
{
[DataField]
public readonly int Bad;
[DataField]
public int Good;
}
""";
await Verifier(code,
// /0/Test0.cs(15,12): error RA0019: Data field Bad in data definition Foo is readonly
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldWritableRule).WithSpan(15, 12, 15, 20).WithArguments("Bad", "Foo")
);
}
[Test]
public async Task ReadOnlyPropertyTest()
{
const string code = """
using System;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Robust.Shared.Serialization.Manager.Attributes
{
public class DataFieldBaseAttribute : Attribute;
public class DataFieldAttribute : DataFieldBaseAttribute;
public sealed class DataDefinitionAttribute : Attribute;
}
[DataDefinition]
public sealed partial class Foo
{
[DataField]
public int Bad { get; }
[DataField]
public int Good { get; private set; }
}
""";
await Verifier(code,
// /0/Test0.cs(15,20): error RA0020: Data field property Bad in data definition Foo does not have a setter
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldPropertyWritableRule).WithSpan(15, 20, 15, 28).WithArguments("Bad", "Foo")
);
}
}

View File

@@ -1,189 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.ForbidLiteralAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
public sealed class ForbidLiteralAnalyzerTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<ForbidLiteralAnalyzer, DefaultVerifier>()
{
TestState =
{
Sources = { code },
},
};
TestHelper.AddEmbeddedSources(
test.TestState,
"Robust.Shared.Analyzers.ForbidLiteralAttribute.cs"
);
test.TestState.Sources.Add(("TestTypeDefs.cs", TestTypeDefs));
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
private const string TestTypeDefs = """
using System.Collections.Generic;
using Robust.Shared.Analyzers;
public sealed class TestClass
{
public static void OneParameterForbidden([ForbidLiteral] string value) { }
public static void TwoParametersFirstForbidden([ForbidLiteral] string first, string second) { }
public static void TwoParametersBothForbidden([ForbidLiteral] string first, [ForbidLiteral] string second) { }
public static void ListParameterForbidden([ForbidLiteral] List<string> values) { }
public static void ParamsListParameterForbidden([ForbidLiteral] params List<string> values) { }
}
public record struct StringWrapper(string value)
{
private readonly string _value = value;
public static implicit operator string(StringWrapper wrapper)
{
return wrapper._value;
}
}
""";
[Test]
public async Task TestOneParameter()
{
const string code = """
public sealed class Tester
{
private const string _constValue = "foo";
private static readonly string StaticValue = "bar";
private static readonly StringWrapper WrappedValue = new("biz");
public void Test()
{
TestClass.OneParameterForbidden(_constValue);
TestClass.OneParameterForbidden(StaticValue);
TestClass.OneParameterForbidden(WrappedValue);
TestClass.OneParameterForbidden("baz");
}
}
""";
await Verifier(code,
// /0/Test0.cs(12,41): error RA0033: The "value" parameter of OneParameterForbidden forbids literal values
VerifyCS.Diagnostic().WithSpan(12, 41, 12, 46).WithArguments("value", "OneParameterForbidden")
);
}
[Test]
public async Task TestTwoParametersFirstForbidden()
{
const string code = """
public sealed class Tester
{
private const string _constValue = "foo";
public void Test()
{
TestClass.TwoParametersFirstForbidden(_constValue, "whatever");
TestClass.TwoParametersFirstForbidden(_constValue, _constValue);
TestClass.TwoParametersFirstForbidden("foo", "whatever");
}
}
""";
await Verifier(code,
// /0/Test0.cs(9,47): error RA0033: The "first" parameter of TwoParametersFirstForbidden forbids literal values
VerifyCS.Diagnostic().WithSpan(9, 47, 9, 52).WithArguments("first", "TwoParametersFirstForbidden")
);
}
[Test]
public async Task TestTwoParametersBothForbidden()
{
const string code = """
public sealed class Tester
{
private const string _constValue = "foo";
private static readonly string StaticValue = "bar";
public void Test()
{
TestClass.TwoParametersBothForbidden(_constValue, _constValue);
TestClass.TwoParametersBothForbidden(_constValue, StaticValue);
TestClass.TwoParametersBothForbidden(_constValue, "whatever");
TestClass.TwoParametersBothForbidden("whatever", _constValue);
}
}
""";
await Verifier(code,
// /0/Test0.cs(10,59): error RA0033: The "second" parameter of TwoParametersBothForbidden forbids literal values
VerifyCS.Diagnostic().WithSpan(10, 59, 10, 69).WithArguments("second", "TwoParametersBothForbidden"),
// /0/Test0.cs(11,46): error RA0033: The "first" parameter of TwoParametersBothForbidden forbids literal values
VerifyCS.Diagnostic().WithSpan(11, 46, 11, 56).WithArguments("first", "TwoParametersBothForbidden")
);
}
[Test]
public async Task TestListParameter()
{
const string code = """
public sealed class Tester
{
private const string _constValue = "foo";
private static readonly string StaticValue = "bar";
private static readonly StringWrapper WrappedValue = new("biz");
public void Test()
{
TestClass.ListParameterForbidden([_constValue, StaticValue, WrappedValue]);
TestClass.ListParameterForbidden(["foo", _constValue, "bar"]);
}
}
""";
await Verifier(code,
// /0/Test0.cs(10,43): warning RA0033: The "values" parameter of ListParameterForbidden forbids literal values
VerifyCS.Diagnostic().WithSpan(10, 43, 10, 48).WithArguments("values", "ListParameterForbidden"),
// /0/Test0.cs(10,63): warning RA0033: The "values" parameter of ListParameterForbidden forbids literal values
VerifyCS.Diagnostic().WithSpan(10, 63, 10, 68).WithArguments("values", "ListParameterForbidden")
);
}
[Test]
public async Task TestParamsListParameter()
{
const string code = """
public sealed class Tester
{
private const string _constValue = "foo";
private static readonly string StaticValue = "bar";
private static readonly StringWrapper WrappedValue = new("biz");
public void Test()
{
TestClass.ParamsListParameterForbidden(_constValue, StaticValue, WrappedValue);
TestClass.ParamsListParameterForbidden("foo", _constValue, "bar");
}
}
""";
await Verifier(code,
// /0/Test0.cs(10,48): warning RA0033: The "values" parameter of ParamsListParameterForbidden forbids literal values
VerifyCS.Diagnostic().WithSpan(10, 48, 10, 53).WithArguments("values", "ParamsListParameterForbidden"),
// /0/Test0.cs(10,68): warning RA0033: The "values" parameter of ParamsListParameterForbidden forbids literal values
VerifyCS.Diagnostic().WithSpan(10, 68, 10, 73).WithArguments("values", "ParamsListParameterForbidden")
);
}
}

View File

@@ -13,7 +13,6 @@
<EmbeddedResource Include="..\Robust.Shared\Analyzers\MustCallBaseAttribute.cs" LogicalName="Robust.Shared.IoC.MustCallBaseAttribute.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Analyzers\PreferNonGenericVariantForAttribute.cs" LogicalName="Robust.Shared.Analyzers.PreferNonGenericVariantForAttribute.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Analyzers\PreferOtherTypeAttribute.cs" LogicalName="Robust.Shared.Analyzers.PreferOtherTypeAttribute.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Analyzers\ForbidLiteralAttribute.cs" LogicalName="Robust.Shared.Analyzers.ForbidLiteralAttribute.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\IoC\DependencyAttribute.cs" LogicalName="Robust.Shared.IoC.DependencyAttribute.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\GameObjects\EventBusAttributes.cs" LogicalName="Robust.Shared.GameObjects.EventBusAttributes.cs" LinkBase="Implementations" />
</ItemGroup>

View File

@@ -41,7 +41,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
"Make sure to mark any type containing a nested data definition as partial."
);
public static readonly DiagnosticDescriptor DataFieldWritableRule = new(
private static readonly DiagnosticDescriptor DataFieldWritableRule = new(
Diagnostics.IdDataFieldWritable,
"Data field must not be readonly",
"Data field {0} in data definition {1} is readonly",
@@ -51,7 +51,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
"Make sure to remove the readonly modifier."
);
public static readonly DiagnosticDescriptor DataFieldPropertyWritableRule = new(
private static readonly DiagnosticDescriptor DataFieldPropertyWritableRule = new(
Diagnostics.IdDataFieldPropertyWritable,
"Data field property must have a setter",
"Data field property {0} in data definition {1} does not have a setter",
@@ -149,8 +149,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
if (IsReadOnlyDataField(type, fieldSymbol))
{
TryGetModifierLocation(field, SyntaxKind.ReadOnlyKeyword, out var location);
context.ReportDiagnostic(Diagnostic.Create(DataFieldWritableRule, location, fieldSymbol.Name, type.Name));
context.ReportDiagnostic(Diagnostic.Create(DataFieldWritableRule, context.Node.GetLocation(), fieldSymbol.Name, type.Name));
}
if (HasRedundantTag(fieldSymbol))
@@ -186,8 +185,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
if (IsReadOnlyDataField(type, propertySymbol))
{
var location = property.AccessorList != null ? property.AccessorList.GetLocation() : property.GetLocation();
context.ReportDiagnostic(Diagnostic.Create(DataFieldPropertyWritableRule, location, propertySymbol.Name, type.Name));
context.ReportDiagnostic(Diagnostic.Create(DataFieldPropertyWritableRule, context.Node.GetLocation(), propertySymbol.Name, type.Name));
}
if (HasRedundantTag(propertySymbol))
@@ -287,20 +285,6 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
return false;
}
private static bool TryGetModifierLocation(MemberDeclarationSyntax syntax, SyntaxKind modifierKind, out Location location)
{
foreach (var modifier in syntax.Modifiers)
{
if (modifier.IsKind(modifierKind))
{
location = modifier.GetLocation();
return true;
}
}
location = syntax.GetLocation();
return false;
}
private static bool IsReadOnlyMember(ITypeSymbol type, ISymbol member)
{
if (member is IFieldSymbol field)

View File

@@ -1,101 +0,0 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class ForbidLiteralAnalyzer : DiagnosticAnalyzer
{
private const string ForbidLiteralType = "Robust.Shared.Analyzers.ForbidLiteralAttribute";
public static DiagnosticDescriptor ForbidLiteralRule = new(
Diagnostics.IdForbidLiteral,
"Parameter forbids literal values",
"The {0} parameter of {1} forbids literal values",
"Usage",
DiagnosticSeverity.Warning,
true,
"Pass in a validated wrapper type like ProtoId, or a const or static value."
);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [ForbidLiteralRule];
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();
context.RegisterOperationAction(AnalyzeOperation, OperationKind.Invocation);
}
private void AnalyzeOperation(OperationAnalysisContext context)
{
if (context.Operation is not IInvocationOperation invocationOperation)
return;
// Check each parameter of the method invocation
foreach (var argumentOperation in invocationOperation.Arguments)
{
// Check for our attribute on the parameter
if (!AttributeHelper.HasAttribute(argumentOperation.Parameter, ForbidLiteralType, out _))
continue;
// Handle parameters using the params keyword
if (argumentOperation.Syntax is InvocationExpressionSyntax subExpressionSyntax)
{
// Check each param value
foreach (var subArgument in subExpressionSyntax.ArgumentList.Arguments)
{
CheckArgumentSyntax(context, argumentOperation, subArgument);
}
continue;
}
// Not params, so just check the single parameter
if (argumentOperation.Syntax is not ArgumentSyntax argumentSyntax)
continue;
CheckArgumentSyntax(context, argumentOperation, argumentSyntax);
}
}
private void CheckArgumentSyntax(OperationAnalysisContext context, IArgumentOperation operation, ArgumentSyntax argumentSyntax)
{
// Handle collection types
if (argumentSyntax.Expression is CollectionExpressionSyntax collectionExpressionSyntax)
{
// Check each value of the collection
foreach (var elementSyntax in collectionExpressionSyntax.Elements)
{
if (elementSyntax is not ExpressionElementSyntax expressionSyntax)
continue;
// Check if a literal was passed in
if (expressionSyntax.Expression is not LiteralExpressionSyntax)
continue;
context.ReportDiagnostic(Diagnostic.Create(ForbidLiteralRule,
expressionSyntax.GetLocation(),
operation.Parameter.Name,
(context.Operation as IInvocationOperation).TargetMethod.Name
));
}
return;
}
// Not a collection, just a single value to check
// Check if it's a literal
if (argumentSyntax.Expression is not LiteralExpressionSyntax)
return;
context.ReportDiagnostic(Diagnostic.Create(ForbidLiteralRule,
argumentSyntax.GetLocation(),
operation.Parameter.Name,
(context.Operation as IInvocationOperation).TargetMethod.Name
));
}
}

View File

@@ -173,51 +173,29 @@ namespace Robust.Client.Console.Commands
}
}
internal sealed class ShowPositionsCommand : LocalizedEntityCommands
internal sealed class ShowPositionsCommand : LocalizedCommands
{
[Dependency] private readonly DebugDrawingSystem _debugDrawing = default!;
[Dependency] private readonly IEntitySystemManager _entitySystems = default!;
public override string Command => "showpos";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
_debugDrawing.DebugPositions = !_debugDrawing.DebugPositions;
var mgr = _entitySystems.GetEntitySystem<DebugDrawingSystem>();
mgr.DebugPositions = !mgr.DebugPositions;
}
}
internal sealed class ShowRotationsCommand : LocalizedEntityCommands
internal sealed class ShowRotationsCommand : LocalizedCommands
{
[Dependency] private readonly DebugDrawingSystem _debugDrawing = default!;
[Dependency] private readonly IEntitySystemManager _entitySystems = default!;
public override string Command => "showrot";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
_debugDrawing.DebugRotations = !_debugDrawing.DebugRotations;
}
}
internal sealed class ShowVelocitiesCommand : LocalizedEntityCommands
{
[Dependency] private readonly DebugDrawingSystem _debugDrawing = default!;
public override string Command => "showvel";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
_debugDrawing.DebugVelocities = !_debugDrawing.DebugVelocities;
}
}
internal sealed class ShowAngularVelocitiesCommand : LocalizedEntityCommands
{
[Dependency] private readonly DebugDrawingSystem _debugDrawing = default!;
public override string Command => "showangvel";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
_debugDrawing.DebugAngularVelocities = !_debugDrawing.DebugAngularVelocities;
var mgr = _entitySystems.GetEntitySystem<DebugDrawingSystem>();
mgr.DebugRotations = !mgr.DebugRotations;
}
}

View File

@@ -1,19 +1,17 @@
#if DEBUG
using Robust.Client.Debugging;
using Robust.Shared.Console;
using Robust.Shared.IoC;
using Robust.Shared.GameObjects;
namespace Robust.Client.Console.Commands
{
public sealed class DebugAnchoredCommand : LocalizedEntityCommands
public sealed class DebugAnchoredCommand : LocalizedCommands
{
[Dependency] private readonly DebugAnchoringSystem _system = default!;
public override string Command => "showanchored";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
_system.Enabled ^= true;
EntitySystem.Get<DebugAnchoringSystem>().Enabled ^= true;
}
}
}

View File

@@ -1,19 +1,17 @@
#if DEBUG
using Robust.Client.GameObjects;
using Robust.Shared.Console;
using Robust.Shared.IoC;
using Robust.Shared.GameObjects;
namespace Robust.Client.Console.Commands
{
internal sealed class LightDebugCommand : LocalizedEntityCommands
internal sealed class LightDebugCommand : LocalizedCommands
{
[Dependency] private readonly DebugLightTreeSystem _system = default!;
public override string Command => "lightbb";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
_system.Enabled ^= true;
EntitySystem.Get<DebugLightTreeSystem>().Enabled ^= true;
}
}
}

View File

@@ -1,67 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using JetBrains.Annotations;
using Robust.Shared.Console;
using Robust.Shared.Localization;
namespace Robust.Client.Console.Commands;
[UsedImplicitly]
internal sealed class LocalizationSetCulture : LocalizedCommands
{
private const string Name = "localization_set_culture";
private const int ArgumentCount = 1;
public override string Command => Name;
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != ArgumentCount)
{
shell.WriteError(Loc.GetString("cmd-invalid-arg-number-error"));
return;
}
CultureInfo culture;
try
{
culture = CultureInfo.GetCultureInfo(args[0], predefinedOnly: false);
}
catch (CultureNotFoundException)
{
shell.WriteError(Loc.GetString("cmd-parse-failure-cultureinfo", ("arg", args[0])));
return;
}
LocalizationManager.SetCulture(culture);
shell.WriteLine(LocalizationManager.GetString("cmd-localization_set_culture-changed",
("code", culture.Name),
("nativeName", culture.NativeName),
("englishName", culture.EnglishName)));
}
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
return args.Length switch
{
1 => CompletionResult.FromHintOptions(GetCultureNames(),
LocalizationManager.GetString("cmd-localization_set_culture-culture-name")),
_ => CompletionResult.Empty
};
}
private static HashSet<string> GetCultureNames()
{
var cultureInfos = CultureInfo.GetCultures(CultureTypes.AllCultures)
.Where(x => !string.IsNullOrEmpty(x.Name))
.ToArray();
var allNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
allNames.UnionWith(cultureInfos.Select(x => x.TwoLetterISOLanguageName));
allNames.UnionWith(cultureInfos.Select(x => x.Name));
return allNames;
}
}

View File

@@ -1,18 +0,0 @@
using Robust.Client.GameObjects;
using Robust.Shared.Console;
using Robust.Shared.IoC;
namespace Robust.Client.Console.Commands
{
public sealed class ShowPlayerVelocityCommand : LocalizedEntityCommands
{
[Dependency] private readonly ShowPlayerVelocityDebugSystem _system = default!;
public override string Command => "showplayervelocity";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
_system.Enabled ^= true;
}
}
}

View File

@@ -0,0 +1,19 @@
using Robust.Client.GameObjects;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Robust.Client.Console.Commands
{
public sealed class VelocitiesCommand : LocalizedCommands
{
[Dependency] private readonly IEntitySystemManager _entitySystems = default!;
public override string Command => "showvelocities";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
_entitySystems.GetEntitySystem<VelocityDebugSystem>().Enabled ^= true;
}
}
}

View File

@@ -1,221 +1,140 @@
using System.Numerics;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics.Components;
using System.Numerics;
namespace Robust.Client.Debugging;
/// <summary>
/// A collection of visual debug overlays for the client game.
/// </summary>
public sealed class DebugDrawingSystem : EntitySystem
namespace Robust.Client.Debugging
{
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
private bool _debugPositions;
private bool _debugRotations;
private bool _debugVelocities;
private bool _debugAngularVelocities;
/// <summary>
/// Toggles the visual overlay of the local origin for each entity on screen.
/// A collection of visual debug overlays for the client game.
/// </summary>
public bool DebugPositions
public sealed class DebugDrawingSystem : EntitySystem
{
get => _debugPositions;
set
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly TransformSystem _transform = default!;
private bool _debugPositions;
private bool _debugRotations;
/// <summary>
/// Toggles the visual overlay of the local origin for each entity on screen.
/// </summary>
public bool DebugPositions
{
if (value == DebugPositions)
get => _debugPositions;
set
{
return;
}
if (value == DebugPositions)
{
return;
}
_debugPositions = value;
_debugPositions = value;
if (value && !_overlayManager.HasOverlay<EntityPositionOverlay>())
{
_overlayManager.AddOverlay(new EntityPositionOverlay(_lookup, _transform));
}
else
{
_overlayManager.RemoveOverlay<EntityPositionOverlay>();
if (value && !_overlayManager.HasOverlay<EntityPositionOverlay>())
{
_overlayManager.AddOverlay(new EntityPositionOverlay(_lookup, EntityManager, _transform));
}
else
{
_overlayManager.RemoveOverlay<EntityPositionOverlay>();
}
}
}
}
/// <summary>
/// Toggles the visual overlay of the rotation for each entity on screen.
/// </summary>
public bool DebugRotations
{
get => _debugRotations;
set
/// <summary>
/// Toggles the visual overlay of the local rotation.
/// </summary>
public bool DebugRotations
{
if (value == DebugRotations)
get => _debugRotations;
set
{
return;
}
if (value == DebugRotations)
{
return;
}
_debugRotations = value;
_debugRotations = value;
if (value && !_overlayManager.HasOverlay<EntityRotationOverlay>())
{
_overlayManager.AddOverlay(new EntityRotationOverlay(_lookup, _transform));
}
else
{
_overlayManager.RemoveOverlay<EntityRotationOverlay>();
if (value && !_overlayManager.HasOverlay<EntityRotationOverlay>())
{
_overlayManager.AddOverlay(new EntityRotationOverlay(_lookup, EntityManager));
}
else
{
_overlayManager.RemoveOverlay<EntityRotationOverlay>();
}
}
}
}
/// <summary>
/// Toggles the visual overlay of the local velocity for each entity on screen.
/// </summary>
public bool DebugVelocities
{
get => _debugVelocities;
set
private sealed class EntityPositionOverlay : Overlay
{
if (value == DebugVelocities)
private readonly EntityLookupSystem _lookup;
private readonly IEntityManager _entityManager;
private readonly SharedTransformSystem _transform;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public EntityPositionOverlay(EntityLookupSystem lookup, IEntityManager entityManager, SharedTransformSystem transform)
{
return;
_lookup = lookup;
_entityManager = entityManager;
_transform = transform;
}
_debugVelocities = value;
protected internal override void Draw(in OverlayDrawArgs args)
{
const float stubLength = 0.25f;
if (value && !_overlayManager.HasOverlay<EntityVelocityOverlay>())
{
_overlayManager.AddOverlay(new EntityVelocityOverlay(EntityManager, _lookup, _transform));
}
else
{
_overlayManager.RemoveOverlay<EntityVelocityOverlay>();
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
foreach (var entity in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
{
var (center, worldRotation) = _transform.GetWorldPositionRotation(entity);
var xLine = worldRotation.RotateVec(Vector2.UnitX);
var yLine = worldRotation.RotateVec(Vector2.UnitY);
worldHandle.DrawLine(center, center + xLine * stubLength, Color.Red);
worldHandle.DrawLine(center, center + yLine * stubLength, Color.Green);
}
}
}
}
/// <summary>
/// Toggles the visual overlay of the angular velocity for each entity on screen.
/// </summary>
public bool DebugAngularVelocities
{
get => _debugAngularVelocities;
set
private sealed class EntityRotationOverlay : Overlay
{
if (value == DebugAngularVelocities)
private readonly EntityLookupSystem _lookup;
private readonly IEntityManager _entityManager;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public EntityRotationOverlay(EntityLookupSystem lookup, IEntityManager entityManager)
{
return;
_lookup = lookup;
_entityManager = entityManager;
}
_debugAngularVelocities = value;
if (value && !_overlayManager.HasOverlay<EntityAngularVelocityOverlay>())
protected internal override void Draw(in OverlayDrawArgs args)
{
_overlayManager.AddOverlay(new EntityAngularVelocityOverlay(EntityManager, _lookup, _transform));
}
else
{
_overlayManager.RemoveOverlay<EntityAngularVelocityOverlay>();
}
}
}
private sealed class EntityPositionOverlay(EntityLookupSystem _lookup, SharedTransformSystem _transform) : Overlay
{
public override OverlaySpace Space => OverlaySpace.WorldSpace;
const float stubLength = 0.25f;
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
var xformQuery = _entityManager.GetEntityQuery<TransformComponent>();
protected internal override void Draw(in OverlayDrawArgs args)
{
const float stubLength = 0.25f;
foreach (var entity in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
{
var (center, worldRotation) = xformQuery.GetComponent(entity).GetWorldPositionRotation();
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
var drawLine = worldRotation.RotateVec(-Vector2.UnitY);
foreach (var uid in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
{
var (center, worldRotation) = _transform.GetWorldPositionRotation(uid);
var xLine = worldRotation.RotateVec(Vector2.UnitX);
var yLine = worldRotation.RotateVec(Vector2.UnitY);
worldHandle.DrawLine(center, center + xLine * stubLength, Color.Red);
worldHandle.DrawLine(center, center + yLine * stubLength, Color.Green);
}
}
}
private sealed class EntityRotationOverlay(EntityLookupSystem _lookup, SharedTransformSystem _transform) : Overlay
{
public override OverlaySpace Space => OverlaySpace.WorldSpace;
protected internal override void Draw(in OverlayDrawArgs args)
{
const float stubLength = 0.25f;
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
foreach (var uid in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
{
var (center, worldRotation) = _transform.GetWorldPositionRotation(uid);
var drawLine = worldRotation.RotateVec(-Vector2.UnitY);
worldHandle.DrawLine(center, center + drawLine * stubLength, Color.Red);
}
}
}
private sealed class EntityVelocityOverlay(IEntityManager _entityManager, EntityLookupSystem _lookup, SharedTransformSystem _transform) : Overlay
{
public override OverlaySpace Space => OverlaySpace.WorldSpace;
protected internal override void Draw(in OverlayDrawArgs args)
{
const float multiplier = 0.2f;
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
var physicsQuery = _entityManager.GetEntityQuery<PhysicsComponent>();
foreach (var uid in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
{
if(!physicsQuery.TryGetComponent(uid, out var physicsComp))
continue;
var center = _transform.GetWorldPosition(uid);
var localVelocity = physicsComp.LinearVelocity;
if (localVelocity != Vector2.Zero)
worldHandle.DrawLine(center, center + localVelocity * multiplier, Color.Yellow);
}
}
}
private sealed class EntityAngularVelocityOverlay(IEntityManager _entityManager, EntityLookupSystem _lookup, SharedTransformSystem _transform) : Overlay
{
public override OverlaySpace Space => OverlaySpace.WorldSpace;
protected internal override void Draw(in OverlayDrawArgs args)
{
const float multiplier = (float)(0.2 / (2 * System.Math.PI));
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
var physicsQuery = _entityManager.GetEntityQuery<PhysicsComponent>();
foreach (var uid in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
{
if(!physicsQuery.TryGetComponent(uid, out var physicsComp))
continue;
var center = _transform.GetWorldPosition(uid);
var angularVelocity = physicsComp.AngularVelocity;
if (angularVelocity != 0.0f)
worldHandle.DrawCircle(center, angularVelocity * multiplier, angularVelocity > 0 ? Color.Magenta : Color.Blue, false);
worldHandle.DrawLine(center, center + drawLine * stubLength, Color.Red);
}
}
}
}
}

View File

@@ -413,9 +413,8 @@ namespace Robust.Client.Debugging
}
var body = bodyEnt.Comp;
var meta = _entityManager.GetComponent<MetaDataComponent>(bodyEnt);
screenHandle.DrawString(_font, drawPos + new Vector2(0, row * lineHeight), $"Ent: {bodyEnt.Owner} ({meta.EntityName})");
screenHandle.DrawString(_font, drawPos + new Vector2(0, row * lineHeight), $"Ent: {bodyEnt.Owner}");
row++;
screenHandle.DrawString(_font, drawPos + new Vector2(0, row * lineHeight), $"Layer: {Convert.ToString(body.CollisionLayer, 2)}");
row++;

View File

@@ -1,126 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Robust.Client.GameObjects;
public sealed partial class ClientEntityManager
{
public override EntityUid PredictedSpawnAttachedTo(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default)
{
var ent = SpawnAttachedTo(protoName, coordinates, overrides, rotation);
FlagPredicted(ent);
return ent;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override EntityUid PredictedSpawn(string? protoName = null, ComponentRegistry? overrides = null, bool doMapInit = true)
{
var ent = Spawn(protoName, overrides, doMapInit);
FlagPredicted(ent);
return ent;
}
public override EntityUid PredictedSpawn(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default!)
{
var ent = Spawn(protoName, coordinates, overrides, rotation);
FlagPredicted(ent);
return ent;
}
public override EntityUid PredictedSpawnAtPosition(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
{
var ent = SpawnAtPosition(protoName, coordinates, overrides);
FlagPredicted(ent);
return ent;
}
public override bool PredictedTrySpawnNextTo(
string? protoName,
EntityUid target,
[NotNullWhen(true)] out EntityUid? uid,
TransformComponent? xform = null,
ComponentRegistry? overrides = null)
{
if (!TrySpawnNextTo(protoName, target, out uid, xform, overrides))
return false;
FlagPredicted(uid.Value);
return true;
}
public override bool PredictedTrySpawnInContainer(
string? protoName,
EntityUid containerUid,
string containerId,
[NotNullWhen(true)] out EntityUid? uid,
ContainerManagerComponent? containerComp = null,
ComponentRegistry? overrides = null)
{
if (!TrySpawnInContainer(protoName, containerUid, containerId, out uid, containerComp, overrides))
return false;
FlagPredicted(uid.Value);
return true;
}
public override EntityUid PredictedSpawnNextToOrDrop(string? protoName, EntityUid target, TransformComponent? xform = null, ComponentRegistry? overrides = null)
{
var ent = SpawnNextToOrDrop(protoName, target, xform, overrides);
FlagPredicted(ent);
return ent;
}
public override EntityUid PredictedSpawnInContainerOrDrop(
string? protoName,
EntityUid containerUid,
string containerId,
TransformComponent? xform = null,
ContainerManagerComponent? containerComp = null,
ComponentRegistry? overrides = null)
{
var ent = SpawnInContainerOrDrop(protoName, containerUid, containerId, xform, containerComp, overrides);
FlagPredicted(ent);
return ent;
}
public override EntityUid PredictedSpawnInContainerOrDrop(
string? protoName,
EntityUid containerUid,
string containerId,
out bool inserted,
TransformComponent? xform = null,
ContainerManagerComponent? containerComp = null,
ComponentRegistry? overrides = null)
{
var ent = SpawnInContainerOrDrop(protoName,
containerUid,
containerId,
out inserted,
xform,
containerComp,
overrides);
FlagPredicted(ent);
return ent;
}
public override void FlagPredicted(Entity<MetaDataComponent?> ent)
{
if (!MetaQuery.Resolve(ent.Owner, ref ent.Comp))
return;
DebugTools.Assert(IsClientSide(ent.Owner, ent.Comp));
EnsureComponent<PredictedSpawnComponent>(ent.Owner);
// Server has no knowledge of the entity we are so we generate a clientside nentity and send it to server.
DebugTools.Assert(ent.Comp.NetEntity.IsClientSide());
// TODO: Need to map call site or something, needs to be consistent between client and server.
}
}

View File

@@ -117,7 +117,7 @@ namespace Robust.Client.GameObjects
base.DirtyField(uid, comp, fieldName, metadata);
}
public override void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params string[] fields)
public override void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params ReadOnlySpan<string> fields)
{
// TODO Prediction
// does the client actually need to dirty the field?
@@ -291,42 +291,5 @@ namespace Robust.Client.GameObjects
}
}
#endregion
/// <inheritdoc />
public override void PredictedDeleteEntity(Entity<MetaDataComponent?, TransformComponent?> ent)
{
if (Deleted(ent.Owner) || !TransformQuery.Resolve(ent.Owner, ref ent.Comp2))
return;
// So there's 3 scenarios:
// 1. Networked entity we just move to nullspace and rely on state handling.
// 2. Clientside predicted entity we delete and rely on state handling.
// 3. Clientside only entity that actually needs deleting here.
if (HasComponent<PredictedSpawnComponent>(ent.Owner))
{
DeleteEntity(ent);
}
else
{
_xforms.DetachEntity(ent, ent.Comp2);
}
}
/// <inheritdoc />
public override void PredictedQueueDeleteEntity(Entity<MetaDataComponent?, TransformComponent?> ent)
{
if (IsQueuedForDeletion(ent.Owner) || !TransformQuery.Resolve(ent.Owner, ref ent.Comp2))
return;
if (HasComponent<PredictedSpawnComponent>(ent.Owner))
{
QueueDeleteEntity(ent);
}
else
{
_xforms.DetachEntity(ent.Owner, ent.Comp2);
}
}
}
}

View File

@@ -10,21 +10,19 @@ using Robust.Shared.Utility;
namespace Robust.Client.GameObjects
{
public sealed class ShowSpriteBBCommand : LocalizedEntityCommands
public sealed class ShowSpriteBBCommand : LocalizedCommands
{
[Dependency] private readonly SpriteBoundsSystem _system = default!;
public override string Command => "showspritebb";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
_system.Enabled ^= true;
EntitySystem.Get<SpriteBoundsSystem>().Enabled ^= true;
}
}
public sealed class SpriteBoundsSystem : EntitySystem
{
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly SpriteTreeSystem _spriteTree = default!;
@@ -42,7 +40,7 @@ namespace Robust.Client.GameObjects
if (_enabled)
{
DebugTools.AssertNull(_overlay);
_overlay = new SpriteBoundsOverlay(_spriteTree, _xformSystem);
_overlay = new SpriteBoundsOverlay(_spriteTree, _entityManager);
_overlayManager.AddOverlay(_overlay);
}
else
@@ -61,13 +59,13 @@ namespace Robust.Client.GameObjects
{
public override OverlaySpace Space => OverlaySpace.WorldSpace;
private readonly SharedTransformSystem _xformSystem;
private readonly IEntityManager _entityManager;
private SpriteTreeSystem _renderTree;
public SpriteBoundsOverlay(SpriteTreeSystem renderTree, SharedTransformSystem xformSystem)
public SpriteBoundsOverlay(SpriteTreeSystem renderTree, IEntityManager entityManager)
{
_renderTree = renderTree;
_xformSystem = xformSystem;
_entityManager = entityManager;
}
protected internal override void Draw(in OverlayDrawArgs args)
@@ -78,7 +76,7 @@ namespace Robust.Client.GameObjects
foreach (var (sprite, xform) in _renderTree.QueryAabb(currentMap, viewport))
{
var (worldPos, worldRot) = _xformSystem.GetWorldPositionRotation(xform);
var (worldPos, worldRot) = xform.GetWorldPositionRotation();
var bounds = sprite.CalculateRotatedBoundingBox(worldPos, worldRot, args.Viewport.Eye?.Rotation ?? default);
// Get scaled down bounds used to indicate the "south" of a sprite.

View File

@@ -30,7 +30,6 @@ using DrawDepthTag = Robust.Shared.GameObjects.DrawDepth;
using static Robust.Shared.Serialization.TypeSerializers.Implementations.SpriteSpecifierSerializer;
using Direction = Robust.Shared.Maths.Direction;
using Vector4 = Robust.Shared.Maths.Vector4;
using SysVec4 = System.Numerics.Vector4;
namespace Robust.Client.GameObjects
{
@@ -754,20 +753,12 @@ namespace Robust.Client.GameObjects
if (layerDatum.Shader == string.Empty)
{
layer.ShaderPrototype = null;
layer.UnShaded = false;
layer.Shader = null;
}
else if (layerDatum.Shader == SpriteSystem.UnshadedId.Id)
{
layer.ShaderPrototype = SpriteSystem.UnshadedId;
layer.UnShaded = true;
layer.Shader = null;
}
else if (prototypes.TryIndex<ShaderPrototype>(layerDatum.Shader, out var prototype))
{
layer.ShaderPrototype = layerDatum.Shader;
layer.Shader = prototype.Instance();
layer.UnShaded = false;
}
else
{
@@ -844,28 +835,11 @@ namespace Robust.Client.GameObjects
if (!TryGetLayer(layer, out var theLayer, true))
return;
if (shader == null)
{
theLayer.UnShaded = false;
theLayer.Shader = null;
theLayer.ShaderPrototype = null;
return;
}
if (prototype == SpriteSystem.UnshadedId.Id)
{
theLayer.UnShaded = true;
theLayer.ShaderPrototype = SpriteSystem.UnshadedId;
theLayer.Shader = null;
return;
}
theLayer.UnShaded = false;
theLayer.Shader = shader;
theLayer.ShaderPrototype = prototype;
}
public void LayerSetShader(object layerKey, ShaderInstance? shader, string? prototype = null)
public void LayerSetShader(object layerKey, ShaderInstance shader, string? prototype = null)
{
if (!LayerMapTryGet(layerKey, out var layer, true))
return;
@@ -1519,18 +1493,10 @@ namespace Robust.Client.GameObjects
{
[ViewVariables] private readonly SpriteComponent _parent;
[ViewVariables] public ProtoId<ShaderPrototype>? ShaderPrototype;
[ViewVariables] public string? ShaderPrototype;
[ViewVariables] public ShaderInstance? Shader;
[ViewVariables] public Texture? Texture;
/// <summary>
/// If true, then this layer is drawn without lighting applied.
/// Unshaded layers are given special treatment and don't just use the unshaded-shader to avoid having to
/// unnecessarily swap out the light texture. This helps the number of batches that need to be sent to the
/// GPU while drawing sprites.
/// </summary>
[ViewVariables] internal bool UnShaded;
private RSI? _rsi;
[ViewVariables] public RSI? RSI
{
@@ -1697,7 +1663,6 @@ namespace Robust.Client.GameObjects
if (toClone.Shader != null)
{
Shader = toClone.Shader.Mutable ? toClone.Shader.Duplicate() : toClone.Shader;
UnShaded = toClone.UnShaded;
ShaderPrototype = toClone.ShaderPrototype;
}
Texture = toClone.Texture;
@@ -2113,20 +2078,6 @@ namespace Robust.Client.GameObjects
drawingHandle.UseShader(Shader);
var layerColor = _parent.color * Color;
DebugTools.Assert(layerColor is {R: >= 0, G: >= 0, B: >= 0, A: >= 0}, "Negative colour modulation");
if (UnShaded)
{
DebugTools.AssertNull(Shader);
// Negative modulation values are used to disable light shading in the default shader.
// Specifically we set colour = -1 - colour
// This ensures that non-negative values become negative & is trivially invertible.
// Alternatively we could just clamp the colour to [0,1] and subtract a constant.
layerColor = new(new SysVec4(-1) - layerColor.RGBA);
}
var textureSize = texture.Size / (float)EyeManager.PixelsPerMeter;
var quad = Box2.FromDimensions(textureSize/-2, textureSize);

View File

@@ -76,7 +76,7 @@ namespace Robust.Client.GameObjects
foreach (var key in remie)
{
component.PlayingAnimations.Remove(key);
var completedEvent = new AnimationCompletedEvent(uid, component, key, true);
var completedEvent = new AnimationCompletedEvent {Uid = uid, Key = key, Finished = true};
EntityManager.EventBus.RaiseLocalEvent(uid, completedEvent, true);
}
@@ -187,7 +187,7 @@ namespace Robust.Client.GameObjects
return;
}
var completedEvent = new AnimationCompletedEvent(entity.Owner, entity.Comp, key, false);
var completedEvent = new AnimationCompletedEvent {Uid = entity.Owner, Key = key, Finished = false};
EntityManager.EventBus.RaiseLocalEvent(entity.Owner, completedEvent, true);
}
@@ -202,33 +202,13 @@ namespace Robust.Client.GameObjects
/// </summary>
public sealed class AnimationCompletedEvent : EntityEventArgs
{
/// <summary>
/// The entity associated with the event.
/// </summary>
public EntityUid Uid { get; init; }
/// <summary>
/// The animation player component associated with the entity this event was raised on.
/// </summary>
public AnimationPlayerComponent AnimationPlayer { get; init; }
/// <summary>
/// The key associated with the animation that was completed.
/// </summary>
public string Key { get; init; } = string.Empty;
/// <summary>
/// If true, the animation finished by getting to its natural end.
/// If false, it was removed prematurely via <see cref="AnimationPlayerSystem.Stop(EntityUid,AnimationPlayerComponent,string)"/> or similar overloads.
/// If false, it was removed prematurely via <see cref="AnimationPlayerSystem.Stop(Robust.Client.GameObjects.AnimationPlayerComponent,string)"/> or similar overloads.
/// </summary>
public bool Finished { get; init; }
public AnimationCompletedEvent(EntityUid uid, AnimationPlayerComponent animationPlayer, string key, bool finished = true)
{
Uid = uid;
AnimationPlayer = animationPlayer;
Key = key;
Finished = finished;
}
}
}

View File

@@ -303,7 +303,7 @@ namespace Robust.Client.GameObjects
while (parent.IsValid() && (!spriteOccluded || !lightOccluded))
{
var parentXform = TransformQuery.GetComponent(parent);
if (TryComp<ContainerManagerComponent>(parent, out var manager) && TryGetContainingContainer(parent, child, out var container, manager))
if (TryComp<ContainerManagerComponent>(parent, out var manager) && manager.TryGetContainer(child, out var container))
{
spriteOccluded = spriteOccluded || !container.ShowContents;
lightOccluded = lightOccluded || container.OccludesLight;
@@ -344,7 +344,7 @@ namespace Robust.Client.GameObjects
var childLightOccluded = lightOccluded;
// We already know either sprite or light is not occluding so need to check container.
if (TryGetContainingContainer(entity, child, out var container, manager))
if (manager.TryGetContainer(child, out var container))
{
childSpriteOccluded = childSpriteOccluded || !container.ShowContents;
childLightOccluded = childLightOccluded || container.OccludesLight;

View File

@@ -2,24 +2,24 @@ using System.Collections.Generic;
using System.Numerics;
using Robust.Client.Graphics;
using Robust.Shared.Console;
using Robust.Shared.Containers;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Utility;
using Color = Robust.Shared.Maths.Color;
namespace Robust.Client.GameObjects;
public sealed class DebugEntityLookupCommand : LocalizedEntityCommands
public sealed class DebugEntityLookupCommand : LocalizedCommands
{
[Dependency] private readonly DebugEntityLookupSystem _system = default!;
public override string Command => "togglelookup";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
_system.Enabled ^= true;
EntitySystem.Get<DebugEntityLookupSystem>().Enabled ^= true;
}
}

View File

@@ -26,10 +26,10 @@ namespace Robust.Client.GameObjects
if (_enabled)
{
_lightOverlay = new DebugLightOverlay(
EntityManager.System<EntityLookupSystem>(),
EntitySystem.Get<EntityLookupSystem>(),
IoCManager.Resolve<IEyeManager>(),
IoCManager.Resolve<IMapManager>(),
EntityManager.System<LightTreeSystem>());
Get<LightTreeSystem>());
overlayManager.AddOverlay(_lightOverlay);
}

View File

@@ -62,7 +62,7 @@ public sealed class EyeSystem : SharedEyeSystem
eyeComponent.Target = null;
}
eyeComponent.Eye.Position = TransformSystem.GetMapCoordinates(xform);
eyeComponent.Eye.Position = xform.MapPosition;
}
}
}

View File

@@ -9,6 +9,10 @@ namespace Robust.Client.GameObjects;
public sealed class MapSystem : SharedMapSystem
{
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[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.
@@ -19,4 +23,16 @@ public sealed class MapSystem : SharedMapSystem
}
return id;
}
public override void Initialize()
{
base.Initialize();
_overlayManager.AddOverlay(new TileEdgeOverlay(EntityManager, _resource, _tileDefinitionManager));
}
public override void Shutdown()
{
base.Shutdown();
_overlayManager.RemoveOverlay<TileEdgeOverlay>();
}
}

View File

@@ -1,66 +0,0 @@
using System.Numerics;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Physics.Components;
namespace Robust.Client.GameObjects;
public sealed class ShowPlayerVelocityDebugSystem : EntitySystem
{
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
internal bool Enabled
{
get => _label.Parent != null;
set
{
if (value)
{
_uiManager.WindowRoot.AddChild(_label);
}
else
{
_label.Orphan();
}
}
}
private Label _label = default!;
public override void Initialize()
{
base.Initialize();
_label = new Label();
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
if (!Enabled)
{
_label.Visible = false;
return;
}
var player = _playerManager.LocalEntity;
if (player == null || !EntityManager.TryGetComponent(player.Value, out PhysicsComponent? body))
{
_label.Visible = false;
return;
}
var screenPos = _eyeManager.WorldToScreen(_transform.GetWorldPosition(Transform(player.Value)));
LayoutContainer.SetPosition(_label, screenPos + new Vector2(0, 50));
_label.Visible = true;
_label.Text = $"Speed: {body.LinearVelocity.Length():0.00}\nLinear: {body.LinearVelocity.X:0.00}, {body.LinearVelocity.Y:0.00}\nAngular: {body.AngularVelocity}";
}
}

View File

@@ -37,7 +37,6 @@ namespace Robust.Client.GameObjects
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly SharedTransformSystem _xforms = default!;
public static readonly ProtoId<ShaderPrototype> UnshadedId = "unshaded";
private readonly Queue<SpriteComponent> _inertUpdateQueue = new();
/// <summary>

View File

@@ -0,0 +1,54 @@
using System.Numerics;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics.Components;
namespace Robust.Client.GameObjects
{
public sealed class VelocityDebugSystem : EntitySystem
{
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly TransformSystem _transform = default!;
internal bool Enabled { get; set; }
private Label _label = default!;
public override void Initialize()
{
base.Initialize();
_label = new Label();
IoCManager.Resolve<IUserInterfaceManager>().StateRoot.AddChild(_label);
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
if (!Enabled)
{
_label.Visible = false;
return;
}
var player = _playerManager.LocalEntity;
if (player == null || !EntityManager.TryGetComponent(player.Value, out PhysicsComponent? body))
{
_label.Visible = false;
return;
}
var screenPos = _eyeManager.WorldToScreen(_transform.GetWorldPosition(Transform(player.Value)));
LayoutContainer.SetPosition(_label, screenPos + new Vector2(0, 50));
_label.Visible = true;
_label.Text = $"Speed: {body.LinearVelocity.Length():0.00}\nLinear: {body.LinearVelocity.X:0.00}, {body.LinearVelocity.Y:0.00}\nAngular: {body.AngularVelocity}";
}
}
}

View File

@@ -13,7 +13,6 @@ using Robust.Client.Physics;
using Robust.Client.Player;
using Robust.Client.Timing;
using Robust.Shared;
using Robust.Shared.Collections;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.Containers;
@@ -385,6 +384,7 @@ namespace Robust.Client.GameStates
_processor.UpdateFullRep(curState);
}
IEnumerable<NetEntity> createdEntities;
using (_prof.Group("ApplyGameState"))
{
if (_timing.LastProcessedTick < targetProcessedTick && nextState != null)
@@ -564,21 +564,6 @@ namespace Robust.Client.GameStates
var metaQuery = _entities.GetEntityQuery<MetaDataComponent>();
RemQueue<IComponent> toRemove = new();
// Handle predicted entity spawns.
var predicted = new ValueList<EntityUid>();
var predictedQuery = _entities.AllEntityQueryEnumerator<PredictedSpawnComponent>();
while (predictedQuery.MoveNext(out var uid, out var _))
{
predicted.Add(uid);
}
// Entity will get re-created as part of the tick.
foreach (var ent in predicted)
{
_entities.DeleteEntity(ent);
}
foreach (var entity in system.DirtyEntities)
{
DebugTools.Assert(toRemove.Count == 0);
@@ -714,9 +699,8 @@ namespace Robust.Client.GameStates
#if !EXCEPTION_TOLERANCE
throw new KeyNotFoundException();
#else
continue;
#endif
continue;
}
var compData = _compDataPool.Get();
@@ -977,9 +961,8 @@ namespace Robust.Client.GameStates
RequestFullState();
#if !EXCEPTION_TOLERANCE
throw;
#else
return;
#endif
return;
}
if (data.Created)
@@ -997,9 +980,8 @@ namespace Robust.Client.GameStates
RequestFullState();
#if !EXCEPTION_TOLERANCE
throw;
#else
return;
#endif
return;
}
}

View File

@@ -123,8 +123,7 @@ namespace Robust.Client.Graphics
/// <inheritdoc />
public ScreenCoordinates CoordinatesToScreen(EntityCoordinates point)
{
var transformSystem = _entityManager.System<SharedTransformSystem>();
return MapToScreen(transformSystem.ToMapCoordinates(point));
return MapToScreen(point.ToMap(_entityManager, _entityManager.System<SharedTransformSystem>()));
}
public ScreenCoordinates MapToScreen(MapCoordinates point)

View File

@@ -21,7 +21,7 @@ namespace Robust.Client.Graphics.Clyde
private const string UniModelMatrix = "modelMatrix";
private const string UniTexturePixelSize = "TEXTURE_PIXEL_SIZE";
private const string UniMainTexture = "TEXTURE";
private const string UniLightTexture = "lightMap"; // TODO CLYDE consistent shader variable naming
private const string UniLightTexture = "lightMap";
private const string UniProjViewMatrices = "projectionViewMatrices";
private const string UniUniformConstants = "uniformConstants";

View File

@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenToolkit.Graphics.OpenGL4;
using Robust.Client.ResourceManagement;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.Graphics;
@@ -23,30 +22,10 @@ namespace Robust.Client.Graphics.Clyde
/// </summary>
private HashSet<Type> _erroredGridOverlays = new();
private Vertex2D[]? _chunkMeshBuilderVertexBuffer;
private ushort[]? _chunkMeshBuilderIndexBuffer;
private int _verticesPerChunk(MapChunk chunk) => chunk.ChunkSize * chunk.ChunkSize * 4;
private int _indicesPerChunk(MapChunk chunk) => chunk.ChunkSize * chunk.ChunkSize * GetQuadBatchIndexCount();
private List<Entity<MapGridComponent>> _grids = new();
private bool _drawTileEdges;
private void RenderTileEdgesChanges(bool value)
{
_drawTileEdges = value;
if (!value)
return;
// Dirty all Edges
foreach (var gridData in _mapChunkData.Values)
{
foreach (var chunk in gridData.Values)
{
chunk.EdgeDirty = true;
}
}
}
private void _drawGrids(Viewport viewport, Box2 worldAABB, Box2Rotated worldBounds, IEye eye)
{
@@ -84,79 +63,29 @@ namespace Robust.Client.Graphics.Clyde
gridProgram.SetUniform(UniIModUV, new Vector4(0, 0, 1, 1));
}
gridProgram.SetUniform(UniIModelMatrix, _transformSystem.GetWorldMatrix(mapGrid));
var transform = _entityManager.GetComponent<TransformComponent>(mapGrid);
gridProgram.SetUniform(UniIModelMatrix, _transformSystem.GetWorldMatrix(transform));
var enumerator = mapSystem.GetMapChunks(mapGrid.Owner, mapGrid.Comp, worldBounds);
// Handle base texture updates.
while (enumerator.MoveNext(out var chunk))
{
DebugTools.Assert(chunk.FilledTiles > 0);
var datum = EnsureChunkInitialized(data, chunk, mapGrid);
if (!data.TryGetValue(chunk.Indices, out MapChunkData? datum))
data[chunk.Indices] = datum = _initChunkBuffers(mapGrid, chunk);
if (!datum.Dirty)
continue;
if (datum.Dirty)
_updateChunkMesh(mapGrid, chunk, datum);
_updateChunkMesh(mapGrid, chunk, datum);
if (!_drawTileEdges)
continue;
// Dirty edge tiles for next step.
datum.EdgeDirty = true;
for (var x = -1; x <= 1; x++)
{
for (var y = -1; y <= 1; y++)
{
var neighbor = chunk.Indices + new Vector2i(x, y);
if (!mapGrid.Comp.Chunks.TryGetValue(neighbor, out var neighborChunk))
continue;
var neighborDatum = EnsureChunkInitialized(data, neighborChunk, mapGrid);
neighborDatum.EdgeDirty = true;
}
}
}
// Handle edge sprites.
if (_drawTileEdges)
{
enumerator = mapSystem.GetMapChunks(mapGrid.Owner, mapGrid.Comp, worldBounds);
while (enumerator.MoveNext(out var chunk))
{
var datum = data[chunk.Indices];
if (datum.EdgeDirty)
_updateChunkEdges(mapGrid, chunk, datum);
}
}
enumerator = mapSystem.GetMapChunks(mapGrid.Owner, mapGrid.Comp, worldBounds);
// Draw chunks
while (enumerator.MoveNext(out var chunk))
{
var datum = data[chunk.Indices];
DebugTools.Assert(datum.TileCount > 0);
if (datum.TileCount > 0)
{
BindVertexArray(datum.VAO);
CheckGlError();
if (datum.TileCount == 0)
continue;
_debugStats.LastGLDrawCalls += 1;
GL.DrawElements(GetQuadGLPrimitiveType(), datum.TileCount * GetQuadBatchIndexCount(), DrawElementsType.UnsignedShort, 0);
CheckGlError();
}
BindVertexArray(datum.VAO);
CheckGlError();
if (_drawTileEdges && datum.EdgeCount > 0)
{
BindVertexArray(datum.EdgeVAO);
CheckGlError();
_debugStats.LastGLDrawCalls += 1;
GL.DrawElements(GetQuadGLPrimitiveType(), datum.EdgeCount * GetQuadBatchIndexCount(), DrawElementsType.UnsignedShort, 0);
CheckGlError();
}
_debugStats.LastGLDrawCalls += 1;
GL.DrawElements(GetQuadGLPrimitiveType(), datum.TileCount * GetQuadBatchIndexCount(), DrawElementsType.UnsignedShort, 0);
CheckGlError();
}
requiresFlush = false;
@@ -188,17 +117,6 @@ namespace Robust.Client.Graphics.Clyde
CullEmptyChunks();
}
private MapChunkData EnsureChunkInitialized(Dictionary<Vector2i, MapChunkData> data, MapChunk chunk, Entity<MapGridComponent> mapGrid)
{
if (!data.TryGetValue(chunk.Indices, out var datum))
{
data[chunk.Indices] = datum = new MapChunkData();
_initChunkBuffers(mapGrid, chunk, datum);
}
return datum;
}
private void CullEmptyChunks()
{
foreach (var (grid, chunks) in _mapChunkData)
@@ -220,143 +138,66 @@ namespace Robust.Client.Graphics.Clyde
private void _updateChunkMesh(Entity<MapGridComponent> grid, MapChunk chunk, MapChunkData datum)
{
Span<ushort> indexBuffer = EnsureSize(ref _chunkMeshBuilderIndexBuffer, _indicesPerChunk(chunk));
Span<Vertex2D> vertexBuffer = EnsureSize(ref _chunkMeshBuilderVertexBuffer, _verticesPerChunk(chunk));
Span<ushort> indexBuffer = stackalloc ushort[_indicesPerChunk(chunk)];
Span<Vertex2D> vertexBuffer = stackalloc Vertex2D[_verticesPerChunk(chunk)];
var i = 0;
var chunkSize = grid.Comp.ChunkSize;
var chunkOriginScaled = chunk.Indices * chunkSize;
for (ushort x = 0; x < chunkSize; x++)
var cSz = grid.Comp.ChunkSize;
var cScaled = chunk.Indices * cSz;
for (ushort x = 0; x < cSz; x++)
{
for (ushort y = 0; y < chunkSize; y++)
for (ushort y = 0; y < cSz; y++)
{
var gridX = x + chunkOriginScaled.X;
var gridY = y + chunkOriginScaled.Y;
var tile = chunk.GetTile(x, y);
if (tile.IsEmpty)
continue;
// Tile render
if (x != chunkSize && y != chunkSize)
var regionMaybe = _tileDefinitionManager.TileAtlasRegion(tile);
Box2 region;
if (regionMaybe == null || regionMaybe.Length <= tile.Variant)
{
// ReSharper disable once IntVariableOverflowInUncheckedContext
if (tile.IsEmpty)
continue;
var regionMaybe = _tileDefinitionManager.TileAtlasRegion(tile);
Box2 region;
if (regionMaybe == null || regionMaybe.Length <= tile.Variant)
{
region = _tileDefinitionManager.ErrorTileRegion;
}
else
{
region = regionMaybe[tile.Variant];
}
WriteTileToBuffers(i, gridX, gridY, vertexBuffer, indexBuffer, region);
i += 1;
region = _tileDefinitionManager.ErrorTileRegion;
}
else
{
region = regionMaybe[tile.Variant];
}
var gx = x + cScaled.X;
var gy = y + cScaled.Y;
var vIdx = i * 4;
vertexBuffer[vIdx + 0] = new Vertex2D(gx, gy, region.Left, region.Bottom, Color.White);
vertexBuffer[vIdx + 1] = new Vertex2D(gx + 1, gy, region.Right, region.Bottom, Color.White);
vertexBuffer[vIdx + 2] = new Vertex2D(gx + 1, gy + 1, region.Right, region.Top, Color.White);
vertexBuffer[vIdx + 3] = new Vertex2D(gx, gy + 1, region.Left, region.Top, Color.White);
var nIdx = i * GetQuadBatchIndexCount();
var tIdx = (ushort)(i * 4);
QuadBatchIndexWrite(indexBuffer, ref nIdx, tIdx);
i += 1;
}
}
var indexSlice = indexBuffer[..(i * GetQuadBatchIndexCount())];
var vertSlice = vertexBuffer[..(i * 4)];
GL.BindVertexArray(datum.VAO);
CheckGlError();
datum.EBO.Use();
datum.VBO.Use();
datum.EBO.Reallocate(indexSlice);
datum.VBO.Reallocate(vertSlice);
datum.TileCount = i;
datum.EBO.Reallocate(indexBuffer[..(i * GetQuadBatchIndexCount())]);
datum.VBO.Reallocate(vertexBuffer[..(i * 4)]);
datum.Dirty = false;
datum.TileCount = i;
}
private void _updateChunkEdges(Entity<MapGridComponent> grid, MapChunk chunk, MapChunkData datum)
private unsafe MapChunkData _initChunkBuffers(Entity<MapGridComponent> grid, MapChunk chunk)
{
// Need a buffer that can potentially store all neighbor tiles
Span<ushort> indexBuffer = EnsureSize(ref _chunkMeshBuilderIndexBuffer, _indicesPerChunk(chunk) * 8);
Span<Vertex2D> vertexBuffer = EnsureSize(ref _chunkMeshBuilderVertexBuffer, _verticesPerChunk(chunk) * 8);
var i = 0;
var chunkSize = grid.Comp.ChunkSize;
var chunkOriginScaled = chunk.Indices * chunkSize;
var maps = _entityManager.System<SharedMapSystem>();
for (ushort x = 0; x < chunkSize; x++)
{
for (ushort y = 0; y < chunkSize; y++)
{
var gridX = x + chunkOriginScaled.X;
var gridY = y + chunkOriginScaled.Y;
var tile = chunk.GetTile(x, y);
if (!_tileDefinitionManager.TryGetDefinition(tile.TypeId, out var tileDef))
continue;
// Edge render
for (var nx = -1; nx <= 1; nx++)
{
for (var ny = -1; ny <= 1; ny++)
{
if (nx == 0 && ny == 0)
continue;
var neighborIndices = new Vector2i(gridX + nx, gridY + ny);
if (!maps.TryGetTile(grid.Comp, neighborIndices, out var neighborTile))
continue;
if (!_tileDefinitionManager.TryGetDefinition(neighborTile.TypeId, out var neighborDef))
continue;
// If it's the same tile then no edge to be drawn.
if (tile.TypeId == neighborTile.TypeId || neighborDef.EdgeSprites.Count == 0)
continue;
// If neighbor is a lower or same priority then us then don't draw on our tile.
if (neighborDef.EdgeSpritePriority <= tileDef.EdgeSpritePriority)
continue;
var direction = new Vector2i(nx, ny).AsDirection().GetOpposite();
var regionMaybe = _tileDefinitionManager.TileAtlasRegion(neighborTile.TypeId, direction);
if (regionMaybe == null)
continue;
var region = regionMaybe[0];
WriteTileToBuffers(i, gridX, gridY, vertexBuffer, indexBuffer, region);
i += 1;
}
}
}
}
// We don't save the edge buffers back because we might need to re-use it if a neighbor chunk updates.
var indexSlice = indexBuffer[..(i * GetQuadBatchIndexCount())];
var vertSlice = vertexBuffer[..(i * 4)];
GL.BindVertexArray(datum.EdgeVAO);
CheckGlError();
datum.EdgeEBO.Use();
datum.EdgeVBO.Use();
datum.EdgeEBO.Reallocate(indexSlice);
datum.EdgeVBO.Reallocate(vertSlice);
datum.EdgeCount = i;
datum.EdgeDirty = false;
}
private unsafe void _initChunkBuffers(Entity<MapGridComponent> grid, MapChunk chunk, MapChunkData datum)
{
var vboSize = _verticesPerChunk(chunk) * sizeof(Vertex2D);
var eboSize = _indicesPerChunk(chunk) * sizeof(ushort);
// Base VAO
var vao = GenVertexArray();
BindVertexArray(vao);
CheckGlError();
var vboSize = _verticesPerChunk(chunk) * sizeof(Vertex2D);
var eboSize = _indicesPerChunk(chunk) * sizeof(ushort);
var vbo = new GLBuffer(this, BufferTarget.ArrayBuffer, BufferUsageHint.DynamicDraw,
vboSize, $"Grid {grid.Owner} chunk {chunk.Indices} VBO");
var ebo = new GLBuffer(this, BufferTarget.ElementArrayBuffer, BufferUsageHint.DynamicDraw,
@@ -371,30 +212,12 @@ namespace Robust.Client.Graphics.Clyde
vbo.Use();
ebo.Use();
datum.EBO = ebo;
datum.VBO = vbo;
datum.VAO = vao;
var datum = new MapChunkData(vao, vbo, ebo)
{
Dirty = true
};
// EdgeVAO
var edgeVao = GenVertexArray();
BindVertexArray(edgeVao);
CheckGlError();
var edgeVbo = new GLBuffer(this, BufferTarget.ArrayBuffer, BufferUsageHint.DynamicDraw,
vboSize * 8, $"Grid {grid.Owner} chunk {chunk.Indices} EdgeVBO");
var edgeEbo = new GLBuffer(this, BufferTarget.ElementArrayBuffer, BufferUsageHint.DynamicDraw,
eboSize * 8, $"Grid {grid.Owner} chunk {chunk.Indices} EdgeEBO");
ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, vao, $"Grid {grid.Owner} chunk {chunk.Indices} EdgeVAO");
SetupVAOLayout();
CheckGlError();
edgeVbo.Use();
edgeEbo.Use();
datum.EdgeEBO = edgeEbo;
datum.EdgeVBO = edgeVbo;
datum.EdgeVAO = edgeVao;
return datum;
}
private void DeleteChunk(MapChunkData data)
@@ -431,49 +254,19 @@ namespace Robust.Client.Graphics.Clyde
_mapChunkData.Remove(gridId);
}
private static T[] EnsureSize<T>(ref T[]? field, int size)
{
if (field == null || field.Length < size)
field = new T[size];
return field;
}
private void WriteTileToBuffers(
int i,
int gridX,
int gridY,
Span<Vertex2D> vertexBuffer,
Span<ushort> indexBuffer,
Box2 region)
{
var vIdx = i * 4;
vertexBuffer[vIdx + 0] = new Vertex2D(gridX, gridY, region.Left, region.Bottom, Color.White);
vertexBuffer[vIdx + 1] = new Vertex2D(gridX + 1, gridY, region.Right, region.Bottom, Color.White);
vertexBuffer[vIdx + 2] = new Vertex2D(gridX + 1, gridY + 1, region.Right, region.Top, Color.White);
vertexBuffer[vIdx + 3] = new Vertex2D(gridX, gridY + 1, region.Left, region.Top, Color.White);
var nIdx = i * GetQuadBatchIndexCount();
var tIdx = (ushort)(i * 4);
QuadBatchIndexWrite(indexBuffer, ref nIdx, tIdx);
}
private sealed class MapChunkData
{
public bool EdgeDirty = true;
public bool Dirty = true;
public uint VAO;
public GLBuffer VBO = default!;
public GLBuffer EBO = default!;
public bool Dirty;
public readonly uint VAO;
public readonly GLBuffer VBO;
public readonly GLBuffer EBO;
public int TileCount;
public uint EdgeVAO;
public GLBuffer EdgeVBO = default!;
public GLBuffer EdgeEBO = default!;
public int EdgeCount;
public MapChunkData()
public MapChunkData(uint vao, GLBuffer vbo, GLBuffer ebo)
{
VAO = vao;
VBO = vbo;
EBO = ebo;
}
}
}

View File

@@ -123,13 +123,9 @@ namespace Robust.Client.Graphics.Clyde
private void RenderSingleWorldOverlay(Overlay overlay, Viewport vp, OverlaySpace space, in Box2 worldBox, in Box2Rotated worldBounds)
{
// Check that entity manager has started.
// This is required for us to be able to use MapSystem.
DebugTools.Assert(_entityManager.Started, "Entity manager should be started/initialized before rendering world-space overlays");
DebugTools.Assert(space != OverlaySpace.ScreenSpaceBelowWorld && space != OverlaySpace.ScreenSpace);
var mapId = vp.Eye?.Position.MapId ?? MapId.Nullspace;
var mapId = vp.Eye!.Position.MapId;
var args = new OverlayDrawArgs(space, null, vp, _renderHandle, new UIBox2i((0, 0), vp.Size), _mapSystem.GetMapOrInvalid(mapId), mapId, worldBox, worldBounds);
if (!overlay.BeforeDraw(args))
@@ -156,7 +152,6 @@ namespace Robust.Client.Graphics.Clyde
private void RenderOverlays(Viewport vp, OverlaySpace space, in Box2 worldBox, in Box2Rotated worldBounds)
{
DebugTools.Assert(space != OverlaySpace.ScreenSpaceBelowWorld && space != OverlaySpace.ScreenSpace);
using (DebugGroup($"Overlays: {space}"))
{
foreach (var overlay in GetOverlaysForSpace(space))
@@ -181,18 +176,9 @@ namespace Robust.Client.Graphics.Clyde
var worldBounds = CalcWorldBounds(vp);
var worldAABB = worldBounds.CalcBoundingBox();
var mapId = vp.Eye?.Position.MapId ?? MapId.Nullspace;
var mapUid = EntityUid.Invalid;
var mapId = vp.Eye!.Position.MapId;
// Screen space overlays may be getting used before entity manager & entity systems have been initialized.
// This might mean that _mapSystem is currently null.
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
if (_entityManager.Started && _mapSystem != null)
mapUid = _mapSystem.GetMapOrInvalid(mapId);
DebugTools.Assert(_mapSystem != null || !_entityManager.Started);
var args = new OverlayDrawArgs(space, vpControl, vp, handle, bounds, mapUid, mapId, worldAABB, worldBounds);
var args = new OverlayDrawArgs(space, vpControl, vp, handle, bounds, _mapSystem.GetMapOrInvalid(mapId), mapId, worldAABB, worldBounds);
foreach (var overlay in list)
{

View File

@@ -98,11 +98,9 @@ namespace Robust.Client.Graphics.Clyde
private LightCapacityComparer _lightCap = new();
private ShadowCapacityComparer _shadowCap = new ShadowCapacityComparer();
private float _maxLightRadius;
private unsafe void InitLighting()
{
_cfg.OnValueChanged(CVars.MaxLightRadius, val => { _maxLightRadius = val;}, true);
// Other...
LoadLightingShaders();
@@ -619,9 +617,8 @@ namespace Robust.Client.Graphics.Clyde
// Use worldbounds for this one as we only care if the light intersects our actual bounds
var xforms = _entityManager.GetEntityQuery<TransformComponent>();
var state = (this, count: 0, shadowCastingCount: 0, xforms, worldAABB);
var lightAabb = worldAABB.Enlarged(_maxLightRadius);
foreach (var (uid, comp) in _lightTreeSystem.GetIntersectingTrees(map, lightAabb))
foreach (var (uid, comp) in _lightTreeSystem.GetIntersectingTrees(map, worldAABB))
{
var bounds = _transformSystem.GetInvWorldMatrix(uid, xforms).TransformBox(worldBounds);
comp.Tree.QueryAabb(ref state, LightQuery, bounds);

View File

@@ -121,7 +121,6 @@ namespace Robust.Client.Graphics.Clyde
_cfg.OnValueChanged(CVars.LightSoftShadows, SoftShadowsChanged, true);
_cfg.OnValueChanged(CVars.MaxLightCount, MaxLightsChanged, true);
_cfg.OnValueChanged(CVars.MaxOccluderCount, MaxOccludersChanged, true);
_cfg.OnValueChanged(CVars.RenderTileEdges, RenderTileEdgesChanges, true);
// I can't be bothered to tear down and set these threads up in a cvar change handler.
// Windows and Linux can be trusted to not explode with threaded windowing,

View File

@@ -1,22 +1,8 @@
// UV coordinates in texture-space. I.e., (0,0) is the corner of the texture currently being used to draw.
// When drawing a sprite from a texture atlas, (0,0) is the corner of the atlas, not the specific sprite being drawn.
varying highp vec2 UV;
// UV coordinates in quad-space. I.e., when drawing a sprite from a texture atlas (0,0) is the corner of the sprite
// currently being drawn.
varying highp vec2 UV2;
// TBH I'm not sure what this is for. I think it is scree UV coordiantes, i.e., FRAGCOORD.xy * SCREEN_PIXEL_SIZE ?
// TODO CLYDE Is this still needed?
varying highp vec2 Pos;
// Vertex colour modulation. Note that negative values imply that the LIGHTMAP should be ignored. This is used to avoid
// having to set the texture to a white/blank texture for sprites that have no light shading applied.
varying highp vec4 VtxModulate;
// The current light map. Unless disabled, this is automatically sampled to create the LIGHT vector, which is then used
// to modulate the output colour.
// TODO CLYDE consistent shader variable naming
uniform sampler2D lightMap;
// [SHADER_HEADER_CODE]
@@ -25,37 +11,11 @@ void main()
{
highp vec4 FRAGCOORD = gl_FragCoord;
// The output colour. This should get set by the shader code block.
// This will get modified by the LIGHT and MODULATE vectors.
lowp vec4 COLOR;
// The light colour, usually sampled from the LIGHTMAP
lowp vec4 LIGHT;
// Colour modulation vector.
highp vec4 MODULATE;
// Sample the texture outside of the branch / with uniform control flow.
LIGHT = texture2D(lightMap, Pos);
if (VtxModulate.x < 0.0)
{
// Negative VtxModulate implies unshaded/no lighting.
MODULATE = -1.0 - VtxModulate;
LIGHT = vec4(1.0);
}
else
{
MODULATE = VtxModulate;
}
// TODO CLYDE consistent shader variable naming
// Requires breaking changes.
lowp vec3 lightSample = LIGHT.xyz;
lowp vec3 lightSample = texture2D(lightMap, Pos).rgb;
// [SHADER_CODE]
LIGHT.xyz = lightSample;
gl_FragColor = zAdjustResult(COLOR * MODULATE * LIGHT);
gl_FragColor = zAdjustResult(COLOR * VtxModulate * vec4(lightSample, 1.0));
}

View File

@@ -18,7 +18,6 @@ uniform mat3 modelMatrix;
// Allows us to do texture atlassing with texture coordinates 0->1
// Input texture coordinates get mapped to this range.
uniform vec4 modifyUV;
// TODO CLYDE Is this still needed?
// [SHADER_HEADER_CODE]
@@ -40,15 +39,5 @@ void main()
Pos = (VERTEX + 1.0) / 2.0;
UV = mix(modifyUV.xy, modifyUV.zw, tCoord);
UV2 = tCoord2;
// Negative modulation is being used as a hacky way to squeeze in lighting data.
// I.e., negative modulation implies we ignore the lighting.
if (modulate.x < 0.0)
{
VtxModulate = -1.0 - zFromSrgb(-1.0 - modulate);
}
else
{
VtxModulate = zFromSrgb(modulate);
}
VtxModulate = zFromSrgb(modulate);
}

View File

@@ -1,7 +1,6 @@
varying highp vec2 UV;
varying highp vec2 UV2;
// TODO CLYDE consistent shader variable naming
uniform sampler2D lightMap;
// [SHADER_HEADER_CODE]

View File

@@ -16,7 +16,7 @@ using Vector4 = Robust.Shared.Maths.Vector4;
namespace Robust.Client.Graphics
{
[Prototype]
[Prototype("shader")]
public sealed partial class ShaderPrototype : IPrototype, ISerializationHooks
{
[ViewVariables]

View File

@@ -48,7 +48,6 @@ namespace Robust.Client.Input
[Dependency] private readonly IUserInterfaceManagerInternal _uiMgr = default!;
[Dependency] private readonly IConsoleHost _console = default!;
[Dependency] private readonly ISerializationManager _serialization = default!;
private ISawmill _logger = default!;
private bool _currentlyFindingViewport;
@@ -115,8 +114,6 @@ namespace Robust.Client.Input
/// <inheritdoc />
public void Initialize()
{
_logger = Logger.GetSawmill("input");
NetworkBindMap = new BoundKeyMap(_reflectionManager);
NetworkBindMap.PopulateKeyFunctionsMap();
@@ -133,7 +130,7 @@ namespace Robust.Client.Input
}
catch (Exception e)
{
_logger.Error("Failed to load user keybindings: " + e);
Logger.ErrorS("input", "Failed to load user keybindings: " + e);
}
}
@@ -534,7 +531,8 @@ namespace Robust.Client.Input
if (reg.Type != KeyBindingType.Command && !NetworkBindMap.FunctionExists(reg.Function.FunctionName))
{
_logger.Debug("Key function in {0} does not exist: '{1}'.", file, reg.Function);
Logger.DebugS("input", "Key function in {0} does not exist: '{1}'.", file,
reg.Function);
invalid = true;
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Linq;
using Robust.Client.Graphics;
@@ -11,11 +10,9 @@ using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
namespace Robust.Client.Map
{
@@ -32,7 +29,7 @@ namespace Robust.Client.Map
public Texture TileTextureAtlas => _tileTextureAtlas ?? Texture.Transparent;
private FrozenDictionary<(int Id, Direction Direction), Box2[]> _tileRegions = FrozenDictionary<(int Id, Direction Direction), Box2[]>.Empty;
private readonly Dictionary<int, Box2[]> _tileRegions = new();
public Box2 ErrorTileRegion { get; private set; }
@@ -45,14 +42,7 @@ namespace Robust.Client.Map
/// <inheritdoc />
public Box2[]? TileAtlasRegion(int tileType)
{
return TileAtlasRegion(tileType, Direction.Invalid);
}
/// <inheritdoc />
public Box2[]? TileAtlasRegion(int tileType, Direction direction)
{
// ReSharper disable once CanSimplifyDictionaryTryGetValueWithGetValueOrDefault
if (_tileRegions.TryGetValue((tileType, direction), out var region))
if (_tileRegions.TryGetValue(tileType, out var region))
{
return region;
}
@@ -93,8 +83,7 @@ namespace Robust.Client.Map
internal void _genTextureAtlas()
{
var sw = RStopwatch.StartNew();
var tileRegs = new Dictionary<(int Id, Direction Direction), Box2[]>();
_tileRegions.Clear();
_tileTextureAtlas = null;
var defList = TileDefs.Where(t => t.Sprite != null).ToList();
@@ -105,7 +94,7 @@ namespace Robust.Client.Map
const int tileSize = EyeManager.PixelsPerMeter;
var tileCount = defList.Select(x => x.Variants + x.EdgeSprites.Count).Sum() + 1;
var tileCount = defList.Select(x => (int)x.Variants).Sum() + 1;
var dimensionX = (int) Math.Ceiling(Math.Sqrt(tileCount));
var dimensionY = (int) Math.Ceiling((float) tileCount / dimensionX);
@@ -113,11 +102,11 @@ namespace Robust.Client.Map
var imgWidth = dimensionX * tileSize;
var imgHeight = dimensionY * tileSize;
var sheet = new Image<Rgba32>(imgWidth, imgHeight);
var w = (float) sheet.Width;
var h = (float) sheet.Height;
// Add in the missing tile texture sprite as tile texture 0.
{
var w = (float) sheet.Width;
var h = (float) sheet.Height;
ErrorTileRegion = Box2.FromDimensions(
0, (h - EyeManager.PixelsPerMeter) / h,
tileSize / w, tileSize / h);
@@ -165,98 +154,25 @@ namespace Robust.Client.Map
var box = new UIBox2i(0, 0, tileSize, tileSize).Translated(new Vector2i(j * tileSize, 0));
image.Blit(box, sheet, point);
var w = (float) sheet.Width;
var h = (float) sheet.Height;
regionList[j] = Box2.FromDimensions(
point.X / w, (h - point.Y - EyeManager.PixelsPerMeter) / h,
tileSize / w, tileSize / h);
BumpColumn(ref row, ref column, dimensionX);
column++;
if (column >= dimensionX)
{
column = 0;
row++;
}
}
tileRegs.Add((def.TileId, Direction.Invalid), regionList);
// Edges
if (def.EdgeSprites.Count <= 0)
continue;
foreach (var direction in DirectionExtensions.AllDirections)
{
if (!def.EdgeSprites.TryGetValue(direction, out var edge))
continue;
using (var stream = _manager.ContentFileRead(edge))
{
image = Image.Load<Rgba32>(stream);
}
if (image.Width != tileSize || image.Height != tileSize)
{
throw new NotSupportedException(
$"Unable to load {path}, due to being unable to use tile textures with a dimension other than {tileSize}x{tileSize}.");
}
Angle angle = Angle.Zero;
switch (direction)
{
// Corner sprites
case Direction.SouthEast:
break;
case Direction.NorthEast:
angle = new Angle(MathF.PI / 2f);
break;
case Direction.NorthWest:
angle = new Angle(MathF.PI);
break;
case Direction.SouthWest:
angle = new Angle(MathF.PI * 1.5f);
break;
// Edge sprites
case Direction.South:
break;
case Direction.East:
angle = new Angle(MathF.PI / 2f);
break;
case Direction.North:
angle = new Angle(MathF.PI);
break;
case Direction.West:
angle = new Angle(MathF.PI * 1.5f);
break;
}
if (angle != Angle.Zero)
{
image.Mutate(o => o.Rotate((float)-angle.Degrees));
}
var point = new Vector2i(column * tileSize, row * tileSize);
var box = new UIBox2i(0, 0, tileSize, tileSize);
image.Blit(box, sheet, point);
// If you ever need edge variants then you could just bump this.
var edgeList = new Box2[1];
edgeList[0] = Box2.FromDimensions(
point.X / w, (h - point.Y - EyeManager.PixelsPerMeter) / h,
tileSize / w, tileSize / h);
tileRegs.Add((def.TileId, direction), edgeList);
BumpColumn(ref row, ref column, dimensionX);
}
_tileRegions.Add(def.TileId, regionList);
}
_tileRegions = tileRegs.ToFrozenDictionary();
_tileTextureAtlas = Texture.LoadFromImage(sheet, "Tile Atlas");
_sawmill.Debug($"Tile atlas took {sw.Elapsed} to build");
}
private void BumpColumn(ref int row, ref int column, int dimensionX)
{
column++;
if (column >= dimensionX)
{
column = 0;
row++;
}
}
void IPostInjectInit.PostInject()

View File

@@ -29,12 +29,5 @@ namespace Robust.Client.Map
/// </summary>
/// <returns>If null, do not draw the tile at all.</returns>
Box2[]? TileAtlasRegion(int tileType);
/// <summary>
/// Gets the region inside the texture atlas to use to draw a tile type.
/// Also handles edge sprites.
/// </summary>
/// <returns>If null, do not draw the tile at all.</returns>
public Box2[]? TileAtlasRegion(int tileType, Direction direction);
}
}

View File

@@ -0,0 +1,125 @@
using System;
using System.Numerics;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Robust.Client.Map;
/// <summary>
/// Draws border sprites for tiles that support them.
/// </summary>
public sealed class TileEdgeOverlay : GridOverlay
{
private readonly IEntityManager _entManager;
private readonly IResourceCache _resource;
private readonly ITileDefinitionManager _tileDefManager;
public TileEdgeOverlay(IEntityManager entManager, IResourceCache resource, ITileDefinitionManager tileDefManager)
{
_entManager = entManager;
_resource = resource;
_tileDefManager = tileDefManager;
ZIndex = -1;
}
protected internal override void Draw(in OverlayDrawArgs args)
{
if (args.MapId == MapId.Nullspace)
return;
var mapSystem = _entManager.System<SharedMapSystem>();
var xformSystem = _entManager.System<SharedTransformSystem>();
var tileSize = Grid.Comp.TileSize;
var tileDimensions = new Vector2(tileSize, tileSize);
var (_, _, worldMatrix, invMatrix) = xformSystem.GetWorldPositionRotationMatrixWithInv(Grid.Owner);
args.WorldHandle.SetTransform(worldMatrix);
var bounds = args.WorldBounds;
bounds = new Box2Rotated(bounds.Box.Enlarged(1), bounds.Rotation, bounds.Origin);
var localAABB = invMatrix.TransformBox(bounds);
var enumerator = mapSystem.GetLocalTilesEnumerator(Grid.Owner, Grid, localAABB, false);
while (enumerator.MoveNext(out var tileRef))
{
var tileDef = _tileDefManager[tileRef.Tile.TypeId];
if (tileDef.EdgeSprites.Count == 0)
continue;
// Get what tiles border us to determine what sprites we need to draw.
for (var x = -1; x <= 1; x++)
{
for (var y = -1; y <= 1; y++)
{
if (x == 0 && y == 0)
continue;
var neighborIndices = new Vector2i(tileRef.GridIndices.X + x, tileRef.GridIndices.Y + y);
var neighborTile = mapSystem.GetTileRef(Grid.Owner, Grid, neighborIndices);
var neighborDef = _tileDefManager[neighborTile.Tile.TypeId];
// If it's the same tile then no edge to be drawn.
if (tileRef.Tile.TypeId == neighborTile.Tile.TypeId)
continue;
// Don't draw if the the neighbor tile edges should draw over us (or if we have the same priority)
if (neighborDef.EdgeSprites.Count != 0 && neighborDef.EdgeSpritePriority >= tileDef.EdgeSpritePriority)
continue;
var direction = new Vector2i(x, y).AsDirection();
// No edge tile
if (!tileDef.EdgeSprites.TryGetValue(direction, out var edgePath))
continue;
var texture = _resource.GetResource<TextureResource>(edgePath);
var box = Box2.FromDimensions(neighborIndices, tileDimensions);
var angle = Angle.Zero;
// If we ever need one for both cardinals and corners then update this.
switch (direction)
{
// Corner sprites
case Direction.SouthEast:
break;
case Direction.NorthEast:
angle = new Angle(MathF.PI / 2f);
break;
case Direction.NorthWest:
angle = new Angle(MathF.PI);
break;
case Direction.SouthWest:
angle = new Angle(MathF.PI * 1.5f);
break;
// Edge sprites
case Direction.South:
break;
case Direction.East:
angle = new Angle(MathF.PI / 2f);
break;
case Direction.North:
angle = new Angle(MathF.PI);
break;
case Direction.West:
angle = new Angle(MathF.PI * 1.5f);
break;
}
if (angle == Angle.Zero)
args.WorldHandle.DrawTextureRect(texture.Texture, box);
else
args.WorldHandle.DrawTextureRect(texture.Texture, new Box2Rotated(box, angle, box.Center));
RequiresFlush = true;
}
}
}
args.WorldHandle.SetTransform(Matrix3x2.Identity);
}
}

View File

@@ -30,13 +30,11 @@ namespace Robust.Client.Placement.Modes
return;
}
var transformSys = pManager.EntityManager.System<SharedTransformSystem>();
var mapId = transformSys.GetMapId(MouseCoords);
var mapId = MouseCoords.GetMapId(pManager.EntityManager);
var snapToEntities = pManager.EntityManager.System<EntityLookupSystem>()
.GetEntitiesInRange(MouseCoords, SnapToRange)
var snapToEntities = EntitySystem.Get<EntityLookupSystem>().GetEntitiesInRange(MouseCoords, SnapToRange)
.Where(entity => pManager.EntityManager.GetComponent<MetaDataComponent>(entity).EntityPrototype == pManager.CurrentPrototype && pManager.EntityManager.GetComponent<TransformComponent>(entity).MapID == mapId)
.OrderBy(entity => (transformSys.GetWorldPosition(entity) - transformSys.ToMapCoordinates(MouseCoords).Position).LengthSquared())
.OrderBy(entity => (pManager.EntityManager.GetComponent<TransformComponent>(entity).WorldPosition - MouseCoords.ToMapPos(pManager.EntityManager, pManager.EntityManager.System<SharedTransformSystem>())).LengthSquared())
.ToList();
if (snapToEntities.Count == 0)
@@ -53,11 +51,10 @@ namespace Robust.Client.Placement.Modes
var closestBounds = component.BaseRSI.Size;
var closestPos = transformSys.GetWorldPosition(closestTransform);
var closestRect =
Box2.FromDimensions(
closestPos.X - closestBounds.X / 2f,
closestPos.Y - closestBounds.Y / 2f,
closestTransform.WorldPosition.X - closestBounds.X / 2f,
closestTransform.WorldPosition.Y - closestBounds.Y / 2f,
closestBounds.X, closestBounds.Y);
var sides = new[]

View File

@@ -21,8 +21,7 @@ namespace Robust.Client.Placement.Modes
{
MouseCoords = ScreenToCursorGrid(mouseScreen);
var transformSys = pManager.EntityManager.System<SharedTransformSystem>();
var gridIdOpt = transformSys.GetGrid(MouseCoords);
var gridIdOpt = MouseCoords.GetGridUid(pManager.EntityManager);
SnapSize = 1f;
if (gridIdOpt is EntityUid gridId && gridId.IsValid())
{

View File

@@ -16,10 +16,8 @@ namespace Robust.Client.Placement.Modes
{
MouseCoords = ScreenToCursorGrid(mouseScreen);
var transformSys = pManager.EntityManager.System<SharedTransformSystem>();
var tileSize = 1f;
var gridIdOpt = transformSys.GetGrid(MouseCoords);
var gridIdOpt = MouseCoords.GetGridUid(pManager.EntityManager);
if (gridIdOpt is EntityUid gridId && gridId.IsValid())
{

View File

@@ -16,11 +16,9 @@ namespace Robust.Client.Placement.Modes
{
MouseCoords = ScreenToCursorGrid(mouseScreen);
var transformSys = pManager.EntityManager.System<SharedTransformSystem>();
var tileSize = 1f;
var gridIdOpt = transformSys.GetGrid(MouseCoords);
var gridIdOpt = MouseCoords.GetGridUid(pManager.EntityManager);
if (gridIdOpt is EntityUid gridId && gridId.IsValid())
{
var mapGrid = pManager.EntityManager.GetComponent<MapGridComponent>(gridId);

View File

@@ -121,8 +121,8 @@ namespace Robust.Client.Placement
{
if (!coordinate.IsValid(pManager.EntityManager))
return; // Just some paranoia just in case
var worldPos = transformSys.ToMapCoordinates(coordinate).Position;
var worldRot = transformSys.GetWorldRotation(coordinate.EntityId) + dirAng;
var worldPos = coordinate.ToMapPos(pManager.EntityManager, transformSys);
var worldRot = pManager.EntityManager.GetComponent<TransformComponent>(coordinate.EntityId).WorldRotation + dirAng;
sprite.Color = IsValidPosition(coordinate) ? ValidPlaceColor : InvalidPlaceColor;
var rot = args.Viewport.Eye?.Rotation ?? default;
@@ -230,7 +230,7 @@ namespace Robust.Client.Placement
var range = pManager.CurrentPermission!.Range;
var transformSys = pManager.EntityManager.System<SharedTransformSystem>();
if (range > 0 && !transformSys.InRange(pManager.EntityManager.GetComponent<TransformComponent>(controlled).Coordinates, coordinates, range))
if (range > 0 && !pManager.EntityManager.GetComponent<TransformComponent>(controlled).Coordinates.InRange(pManager.EntityManager, transformSys, coordinates, range))
return false;
return true;
}
@@ -239,7 +239,7 @@ namespace Robust.Client.Placement
{
var bounds = pManager.ColliderAABB;
var transformSys = pManager.EntityManager.System<SharedTransformSystem>();
var mapCoords = transformSys.ToMapCoordinates(coordinates);
var mapCoords = coordinates.ToMap(pManager.EntityManager, transformSys);
var (x, y) = mapCoords.Position;
var collisionBox = Box2.FromDimensions(
@@ -248,9 +248,7 @@ namespace Robust.Client.Placement
bounds.Width,
bounds.Height);
return pManager.EntityManager
.System<SharedPhysicsSystem>()
.TryCollideRect(collisionBox, mapCoords.MapId);
return EntitySystem.Get<SharedPhysicsSystem>().TryCollideRect(collisionBox, mapCoords.MapId);
}
protected Vector2 ScreenToWorld(Vector2 point)
@@ -267,8 +265,10 @@ namespace Robust.Client.Placement
{
var mapCoords = pManager.EyeManager.PixelToMap(coords.Position);
var transformSys = pManager.EntityManager.System<SharedTransformSystem>();
if (!pManager.MapManager.TryFindGridAt(mapCoords, out var gridUid, out _))
if (!pManager.MapManager.TryFindGridAt(mapCoords, out var gridUid, out var grid))
{
return transformSys.ToCoordinates(mapCoords);
}

View File

@@ -2,7 +2,9 @@
using System.Collections.Generic;
using System.Diagnostics;
using Robust.Client.Timing;
using Robust.Client.Utility;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Prototypes;
@@ -54,7 +56,7 @@ namespace Robust.Client.Prototypes
using var _ = _timing.StartStateApplicationArea();
ReloadPrototypes([file]);
Sawmill.Info($"Reloaded prototypes in {sw.ElapsedMilliseconds} ms");
Logger.Info($"Reloaded prototypes in {sw.ElapsedMilliseconds} ms");
#endif
}
}

View File

@@ -71,11 +71,21 @@ namespace Robust.Client.UserInterface.Controls
}
// First, we measure non-stretching children.
var stretching = new List<Control>();
float totalStretchRatio = 0;
foreach (var child in Children)
{
if (!child.Visible)
continue;
var stretch = Vertical ? child.VerticalExpand : child.HorizontalExpand;
if (stretch)
{
totalStretchRatio += child.SizeFlagsStretchRatio;
stretching.Add(child);
continue;
}
child.Measure(availableSize);
if (Vertical)
@@ -92,6 +102,35 @@ namespace Robust.Client.UserInterface.Controls
}
}
if (stretching.Count == 0)
return desiredSize;
// Then we measure stretching children
foreach (var child in stretching)
{
var size = availableSize;
if (Vertical)
{
size.Y *= child.SizeFlagsStretchRatio / totalStretchRatio;
child.Measure(size);
desiredSize.Y += child.DesiredSize.Y;
desiredSize.X = Math.Max(desiredSize.X, child.DesiredSize.X);
}
else
{
size.X *= child.SizeFlagsStretchRatio / totalStretchRatio;
child.Measure(size);
desiredSize.X += child.DesiredSize.X;
desiredSize.Y = Math.Max(desiredSize.Y, child.DesiredSize.Y);
}
// TODO Maybe make BoxContainer.MeasureOverride more rigorous.
// This should check if size < desired size. If it is, treat child as non-stretching (see the code in
// ArrangeOverride). This requires remeasuring all stretching controls + the control that just became
// non-stretching. But the re-measured controls might then become smaller (e.g. rich text wrapping),
// leading to a recursion problem.
}
return desiredSize;
}

View File

@@ -4,7 +4,6 @@ using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Numerics;
using Robust.Client.Graphics;
using Robust.Shared.Collections;
using Robust.Shared.Graphics;
using Robust.Shared.Input;
using Robust.Shared.Maths;
@@ -84,23 +83,9 @@ namespace Robust.Client.UserInterface.Controls
_updateScrollbarVisibility();
}
public void Add(IEnumerable<Item> items)
{
foreach (var item in items)
{
if(item.Owner != this) throw new ArgumentException("Item is owned by another ItemList!");
_itemList.Add(item);
item.OnSelected += Select;
item.OnDeselected += Deselect;
}
Recalculate();
}
public void Add(Item item)
{
if (item == null) return;
if(item.Owner != this) throw new ArgumentException("Item is owned by another ItemList!");
_itemList.Add(item);
@@ -108,19 +93,9 @@ namespace Robust.Client.UserInterface.Controls
item.OnSelected += Select;
item.OnDeselected += Deselect;
Recalculate();
}
public void AddItems(IEnumerable<string> texts, Texture? icon = null, bool selectable = true, object? metadata = null)
{
var items = new ValueList<Item>();
foreach (var text in texts)
{
items.Add(new Item(this) {Text = text, Icon = icon, Selectable = selectable, Metadata = metadata});
}
Add(items);
RecalculateContentHeight();
if (_isAtBottom && ScrollFollowing)
_scrollBar.MoveToEnd();
}
public Item AddItem(string text, Texture? icon = null, bool selectable = true, object? metadata = null)
@@ -132,15 +107,11 @@ namespace Robust.Client.UserInterface.Controls
public void Clear()
{
// Handle this manually so we can just clear all at once.
foreach (var item in _itemList)
foreach (var item in _itemList.ToArray())
{
item.OnSelected -= Select;
item.OnDeselected -= Deselect;
Remove(item);
}
_itemList.Clear();
Recalculate();
_totalContentHeight = 0;
}
@@ -154,35 +125,25 @@ namespace Robust.Client.UserInterface.Controls
_itemList.CopyTo(array, arrayIndex);
}
private void InternalRemoveAt(int index)
{
if (_itemList.Count <= index)
return;
// If you modify this then also make sure to update Clear!
var item = _itemList[index];
_itemList.RemoveAt(index);
item.OnSelected -= Select;
item.OnDeselected -= Deselect;
}
public bool Remove(Item item)
{
if (item == null) return false;
var value = _itemList.Remove(item);
item.OnSelected -= Select;
item.OnDeselected -= Deselect;
Recalculate();
RecalculateContentHeight();
if (_isAtBottom && ScrollFollowing)
_scrollBar.MoveToEnd();
return value;
}
public void RemoveAt(int index)
{
InternalRemoveAt(index);
Recalculate();
Remove(this[index]);
}
public IEnumerator<Item> GetEnumerator()
@@ -200,24 +161,16 @@ namespace Robust.Client.UserInterface.Controls
return _itemList.IndexOf(item);
}
private void InternalInsert(int index, Item item)
public void Insert(int index, Item item)
{
if (item == null) return;
if(item.Owner != this) throw new ArgumentException("Item is owned by another ItemList!");
_itemList.Insert(index, item);
item.OnSelected += Select;
item.OnDeselected += Deselect;
}
public void Insert(int index, Item item)
{
InternalInsert(index, item);
Recalculate();
}
private void Recalculate()
{
RecalculateContentHeight();
if (_isAtBottom && ScrollFollowing)
_scrollBar.MoveToEnd();
@@ -238,6 +191,7 @@ namespace Robust.Client.UserInterface.Controls
SetItems(newItems, (a,b) => string.Compare(a.Text, b.Text));
}
/// <inheritdoc />
/// <summary>
/// This variant allows for a custom equality operator to compare items, when
/// comparing the Item text is not desired.
@@ -261,13 +215,13 @@ namespace Robust.Client.UserInterface.Controls
else if (cmpResult > 0)
{
// Item exists in our list, but not in `newItems`. Remove it.
InternalRemoveAt(i);
RemoveAt(i);
i--;
}
else if (cmpResult < 0)
{
// A new entry which doesn't exist in our list. Insert it.
InternalInsert(i + 1, newItems[j]);
Insert(i + 1, newItems[j]);
j--;
}
}
@@ -275,18 +229,16 @@ namespace Robust.Client.UserInterface.Controls
// Any remaining items in our list don't exist in `newItems` so remove them
while (i >= 0)
{
InternalRemoveAt(i);
RemoveAt(i);
i--;
}
// And finally, any remaining items in `newItems` don't exist in our list. Create them.
while (j >= 0)
{
InternalInsert(0, newItems[j]);
Insert(0, newItems[j]);
j--;
}
Recalculate();
}
// Without this attribute, this would compile into a property called "Item", causing problems with the Item class.
@@ -338,12 +290,9 @@ namespace Robust.Client.UserInterface.Controls
public void ClearSelected(int? except = null)
{
for (var i = 0; i < _itemList.Count; i++)
foreach (var item in GetSelected())
{
if (i == except)
continue;
var item = _itemList[i];
if(IndexOf(item) == except) continue;
item.Selected = false;
}
}

View File

@@ -4,7 +4,6 @@ using Robust.Client.Graphics;
using Robust.Client.UserInterface.RichText;
using Robust.Shared.Collections;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
@@ -16,23 +15,10 @@ namespace Robust.Client.UserInterface.Controls
[Virtual]
public class OutputPanel : Control
{
public const string StyleClassOutputPanelScrollDownButton = "outputPanelScrollDownButton";
[Dependency] private readonly MarkupTagManager _tagManager = default!;
public const string StylePropertyStyleBox = "stylebox";
public bool ShowScrollDownButton
{
get => _showScrollDownButton;
set
{
_showScrollDownButton = value;
_updateScrollButtonVisibility();
}
}
private bool _showScrollDownButton;
private readonly RingBufferList<RichTextEntry> _entries = new();
private bool _isAtBottom = true;
@@ -40,7 +26,6 @@ namespace Robust.Client.UserInterface.Controls
private bool _firstLine = true;
private StyleBox? _styleBoxOverride;
private VScrollBar _scrollBar;
private Button _scrollDownButton;
public bool ScrollFollowing { get; set; } = true;
@@ -58,25 +43,7 @@ namespace Robust.Client.UserInterface.Controls
HorizontalAlignment = HAlignment.Right
};
AddChild(_scrollBar);
AddChild(_scrollDownButton = new Button()
{
Name = "scrollLiveBtn",
StyleClasses = { StyleClassOutputPanelScrollDownButton },
VerticalAlignment = VAlignment.Bottom,
HorizontalAlignment = HAlignment.Center,
Text = String.Format("⬇ {0} ⬇", Loc.GetString("output-panel-scroll-down-button-text")),
MaxWidth = 300,
Visible = false,
});
_scrollDownButton.OnPressed += _ => ScrollToBottom();
_scrollBar.OnValueChanged += _ =>
{
_isAtBottom = _scrollBar.IsAtEnd;
_updateScrollButtonVisibility();
};
_scrollBar.OnValueChanged += _ => _isAtBottom = _scrollBar.IsAtEnd;
}
public int EntryCount => _entries.Count;
@@ -217,7 +184,6 @@ namespace Robust.Client.UserInterface.Controls
var styleBoxSize = _getStyleBox()?.MinimumSize.Y ?? 0;
_scrollBar.Page = UIScale * (Height - styleBoxSize);
_updateScrollButtonVisibility();
_invalidateEntries();
}
@@ -318,10 +284,5 @@ namespace Robust.Client.UserInterface.Controls
_invalidOnVisible = false;
}
}
private void _updateScrollButtonVisibility()
{
_scrollDownButton.Visible = ShowScrollDownButton && !_isAtBottom;
}
}
}

View File

@@ -1,7 +1,7 @@
<Control xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics">
<BoxContainer Orientation="Vertical">
<OutputPanel Name="Output" VerticalExpand="True" StyleClasses="monospace" ShowScrollDownButton="True">
<OutputPanel Name="Output" VerticalExpand="True" StyleClasses="monospace">
<OutputPanel.StyleBoxOverride>
<gfx:StyleBoxFlat BackgroundColor="#25252add"
ContentMarginLeftOverride="3" ContentMarginRightOverride="3"

View File

@@ -59,7 +59,6 @@ namespace Robust.Client.UserInterface.CustomControls
}
button.EntityLabel.Text = entityLabelText;
button.ActualButton.ToolTip = prototype.Description;
if (prototype == SelectedPrototype)
{

View File

@@ -4,7 +4,7 @@ using Robust.Shared.Utility;
namespace Robust.Client.UserInterface.RichText;
[Prototype]
[Prototype("font")]
public sealed partial class FontPrototype : IPrototype
{
[IdDataField]

View File

@@ -5,9 +5,9 @@ using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Profiling;
using Robust.Shared.Utility;
namespace Robust.Client.UserInterface;
internal sealed partial class UserInterfaceManager
@@ -61,7 +61,7 @@ internal sealed partial class UserInterfaceManager
Title = string.IsNullOrEmpty(title) ? Loc.GetString("popup-title") : title,
};
var label = new RichTextLabel { Text = $"[color=white]{FormattedMessage.EscapeText(contents)}[/color]" };
var label = new Label { Text = contents };
var vBox = new BoxContainer
{

View File

@@ -2,15 +2,11 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Collections;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Timing;
namespace Robust.Client.ViewVariables
{
@@ -55,19 +51,15 @@ namespace Robust.Client.ViewVariables
_lastSearch = search;
EntryItemList.ClearSelected();
EntryItemList.Clear();
AddButton.Disabled = true;
var items = new ValueList<string>();
foreach (var component in _entries)
{
if(!string.IsNullOrEmpty(search) && !component.Contains(search, StringComparison.InvariantCultureIgnoreCase))
continue;
items.Add(component);
EntryItemList.AddItem(component);
}
EntryItemList.AddItems(items);
}
private void OnSearchTextChanged(LineEdit.LineEditEventArgs obj)

View File

@@ -36,7 +36,6 @@ public static class Diagnostics
public const string IdUseNonGenericVariant = "RA0030";
public const string IdPreferOtherType = "RA0031";
public const string IdDuplicateDependency = "RA0032";
public const string IdForbidLiteral = "RA0033";
public static SuppressionDescriptor MeansImplicitAssignment =>
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");

View File

@@ -14,7 +14,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ImplicitUsings>enable</ImplicitUsings>
<PolySharpIncludeGeneratedTypes>System.Index;System.Diagnostics.CodeAnalysis.NotNullWhenAttribute;System.Runtime.CompilerServices.IsExternalInit;System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute</PolySharpIncludeGeneratedTypes>
<NoWarn>RS2008;RS1038</NoWarn>
<NoWarn>RS2008</NoWarn>
</PropertyGroup>
<ItemGroup>

View File

@@ -19,119 +19,117 @@ public class Generator : IIncrementalGenerator
public void Initialize(IncrementalGeneratorInitializationContext initContext)
{
IncrementalValuesProvider<(string name, string code)?> dataDefinitions = initContext.SyntaxProvider
.CreateSyntaxProvider(
static (node, _) => node is TypeDeclarationSyntax,
static (context, _) =>
{
var type = (TypeDeclarationSyntax)context.Node;
var symbol = (ITypeSymbol)context.SemanticModel.GetDeclaredSymbol(type)!;
if (!IsDataDefinition(symbol))
return null;
return GenerateForDataDefinition(type, symbol);
}
)
.Where(static type => type != null);
initContext.RegisterSourceOutput(
dataDefinitions.Collect(),
static (sourceContext, sources) =>
IncrementalValuesProvider<TypeDeclarationSyntax> dataDefinitions = initContext.SyntaxProvider.CreateSyntaxProvider(
static (node, _) => node is TypeDeclarationSyntax,
static (context, _) =>
{
var done = new HashSet<string>();
var type = (TypeDeclarationSyntax) context.Node;
var symbol = (ITypeSymbol) context.SemanticModel.GetDeclaredSymbol(type)!;
return IsDataDefinition(symbol) ? type : null;
}
).Where(static type => type != null)!;
foreach (var source in sources)
var comparer = new DataDefinitionComparer();
initContext.RegisterSourceOutput(
initContext.CompilationProvider.Combine(dataDefinitions.WithComparer(comparer).Collect()),
static (sourceContext, source) =>
{
var (compilation, declarations) = source;
var builder = new StringBuilder();
var containingTypes = new Stack<INamedTypeSymbol>();
var declarationsGenerated = new HashSet<string>();
var deltaType = compilation.GetTypeByMetadataName(ComponentDeltaInterfaceName)!;
foreach (var declaration in declarations)
{
var (name, code) = source!.Value;
builder.Clear();
containingTypes.Clear();
if (!done.Add(name))
var type = compilation.GetSemanticModel(declaration.SyntaxTree).GetDeclaredSymbol(declaration)!;
var symbolName = type
.ToDisplayString()
.Replace('<', '{')
.Replace('>', '}');
if (!declarationsGenerated.Add(symbolName))
continue;
sourceContext.AddSource(name, code);
var nonPartial = !IsPartial(declaration);
var namespaceString = type.ContainingNamespace.IsGlobalNamespace
? string.Empty
: $"namespace {type.ContainingNamespace.ToDisplayString()};";
var containingType = type.ContainingType;
while (containingType != null)
{
containingTypes.Push(containingType);
containingType = containingType.ContainingType;
}
var containingTypesStart = new StringBuilder();
var containingTypesEnd = new StringBuilder();
foreach (var parent in containingTypes)
{
var syntax = (ClassDeclarationSyntax) parent.DeclaringSyntaxReferences[0].GetSyntax();
if (!IsPartial(syntax))
{
nonPartial = true;
continue;
}
containingTypesStart.AppendLine($"{GetPartialTypeDefinitionLine(parent)}\n{{");
containingTypesEnd.AppendLine("}");
}
var definition = GetDataDefinition(type);
if (nonPartial || definition.InvalidFields)
continue;
builder.AppendLine($$"""
#nullable enable
using System;
using Robust.Shared.Analyzers;
using Robust.Shared.IoC;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Exceptions;
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
#pragma warning disable CS0618 // Type or member is obsolete
#pragma warning disable CS0612 // Type or member is obsolete
#pragma warning disable CS0108 // Member hides inherited member; missing new keyword
#pragma warning disable RA0002 // Robust access analyzer
{{namespaceString}}
{{containingTypesStart}}
{{GetPartialTypeDefinitionLine(type)}} : ISerializationGenerated<{{definition.GenericTypeName}}>
{
{{GetConstructor(definition)}}
{{GetCopyMethods(definition, deltaType)}}
{{GetInstantiators(definition, deltaType)}}
}
{{containingTypesEnd}}
""");
var sourceText = CSharpSyntaxTree
.ParseText(builder.ToString())
.GetRoot()
.NormalizeWhitespace()
.ToFullString();
sourceContext.AddSource($"{symbolName}.g.cs", sourceText);
}
}
);
}
private static (string, string)? GenerateForDataDefinition(
TypeDeclarationSyntax declaration,
ITypeSymbol type)
{
var builder = new StringBuilder();
var containingTypes = new Stack<INamedTypeSymbol>();
containingTypes.Clear();
var symbolName = type
.ToDisplayString()
.Replace('<', '{')
.Replace('>', '}');
var nonPartial = !IsPartial(declaration);
var namespaceString = type.ContainingNamespace.IsGlobalNamespace
? string.Empty
: $"namespace {type.ContainingNamespace.ToDisplayString()};";
var containingType = type.ContainingType;
while (containingType != null)
{
containingTypes.Push(containingType);
containingType = containingType.ContainingType;
}
var containingTypesStart = new StringBuilder();
var containingTypesEnd = new StringBuilder();
foreach (var parent in containingTypes)
{
var syntax = (ClassDeclarationSyntax)parent.DeclaringSyntaxReferences[0].GetSyntax();
if (!IsPartial(syntax))
{
nonPartial = true;
continue;
}
containingTypesStart.AppendLine($"{GetPartialTypeDefinitionLine(parent)}\n{{");
containingTypesEnd.AppendLine("}");
}
var definition = GetDataDefinition(type);
if (nonPartial || definition.InvalidFields)
return null;
builder.AppendLine($$"""
#nullable enable
using System;
using Robust.Shared.Analyzers;
using Robust.Shared.IoC;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Exceptions;
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
#pragma warning disable CS0618 // Type or member is obsolete
#pragma warning disable CS0612 // Type or member is obsolete
#pragma warning disable CS0108 // Member hides inherited member; missing new keyword
#pragma warning disable RA0002 // Robust access analyzer
{{namespaceString}}
{{containingTypesStart}}
{{GetPartialTypeDefinitionLine(type)}} : ISerializationGenerated<{{definition.GenericTypeName}}>
{
{{GetConstructor(definition)}}
{{GetCopyMethods(definition)}}
{{GetInstantiators(definition)}}
}
{{containingTypesEnd}}
""");
return ($"{symbolName}.g.cs", builder.ToString());
}
private static DataDefinition GetDataDefinition(ITypeSymbol definition)
{
var fields = new List<DataField>();
@@ -198,7 +196,7 @@ public class Generator : IIncrementalGenerator
return builder.ToString();
}
private static string GetCopyMethods(DataDefinition definition)
private static string GetCopyMethods(DataDefinition definition, ITypeSymbol deltaType)
{
var builder = new StringBuilder();
@@ -269,36 +267,36 @@ public class Generator : IIncrementalGenerator
{{baseCopy}}
""");
foreach (var interfaceName in InternalGetImplicitDataDefinitionInterfaces(definition.Type, true))
foreach (var @interface in InternalGetImplicitDataDefinitionInterfaces(definition.Type, true, deltaType))
{
var interfaceModifiers = baseType != null &&
baseType.AllInterfaces.Any(i => i.ToDisplayString() == interfaceName)
var interfaceModifiers = baseType != null && baseType.AllInterfaces.Contains(@interface, SymbolEqualityComparer.Default)
? "override "
: modifiers;
var interfaceName = @interface.ToDisplayString();
builder.AppendLine($$"""
/// <seealso cref="ISerializationManager.CopyTo"/>
[Obsolete("Use ISerializationManager.CopyTo instead")]
public {{interfaceModifiers}} void InternalCopy(ref {{interfaceName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
{
var def = ({{definition.GenericTypeName}}) target;
Copy(ref def, serialization, hookCtx, context);
target = def;
}
/// <seealso cref="ISerializationManager.CopyTo"/>
[Obsolete("Use ISerializationManager.CopyTo instead")]
public {{interfaceModifiers}} void InternalCopy(ref {{interfaceName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
{
var def = ({{definition.GenericTypeName}}) target;
Copy(ref def, serialization, hookCtx, context);
target = def;
}
/// <seealso cref="ISerializationManager.CopyTo"/>
[Obsolete("Use ISerializationManager.CopyTo instead")]
public {{interfaceModifiers}} void Copy(ref {{interfaceName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
{
InternalCopy(ref target, serialization, hookCtx, context);
}
""");
/// <seealso cref="ISerializationManager.CopyTo"/>
[Obsolete("Use ISerializationManager.CopyTo instead")]
public {{interfaceModifiers}} void Copy(ref {{interfaceName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
{
InternalCopy(ref target, serialization, hookCtx, context);
}
""");
}
return builder.ToString();
}
private static string GetInstantiators(DataDefinition definition)
private static string GetInstantiators(DataDefinition definition, ITypeSymbol deltaType)
{
var builder = new StringBuilder();
var modifiers = string.Empty;
@@ -332,28 +330,27 @@ public class Generator : IIncrementalGenerator
""");
}
foreach (var interfaceName in InternalGetImplicitDataDefinitionInterfaces(definition.Type, false))
foreach (var @interface in InternalGetImplicitDataDefinitionInterfaces(definition.Type, false, deltaType))
{
var interfaceName = @interface.ToDisplayString();
builder.AppendLine($$"""
{{interfaceName}} {{interfaceName}}.Instantiate()
{
return Instantiate();
}
{{interfaceName}} {{interfaceName}}.Instantiate()
{
return Instantiate();
}
{{interfaceName}} ISerializationGenerated<{{interfaceName}}>.Instantiate()
{
return Instantiate();
}
""");
{{interfaceName}} ISerializationGenerated<{{interfaceName}}>.Instantiate()
{
return Instantiate();
}
""");
}
return builder.ToString();
}
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
private static IEnumerable<string> InternalGetImplicitDataDefinitionInterfaces(
ITypeSymbol type,
bool all)
private static IEnumerable<ITypeSymbol> InternalGetImplicitDataDefinitionInterfaces(ITypeSymbol type, bool all, ITypeSymbol deltaType)
{
var symbols = GetImplicitDataDefinitionInterfaces(type, all);
@@ -371,10 +368,10 @@ public class Generator : IIncrementalGenerator
return symbols;
}
if (symbols.Any(x => x == ComponentDeltaInterfaceName))
if (symbols.Any(x => x.ToDisplayString() == deltaType.ToDisplayString()))
return symbols;
return symbols.Append(ComponentDeltaInterfaceName);
return symbols.Append(deltaType);
}
// TODO serveronly? do we care? who knows!!

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -94,13 +93,13 @@ internal static class Types
return false;
}
internal static IEnumerable<string> GetImplicitDataDefinitionInterfaces(ITypeSymbol type, bool all)
internal static IEnumerable<ITypeSymbol> GetImplicitDataDefinitionInterfaces(ITypeSymbol type, bool all)
{
var interfaces = all ? type.AllInterfaces : type.Interfaces;
foreach (var @interface in interfaces)
{
if (IsImplicitDataDefinitionInterface(@interface))
yield return @interface.ToDisplayString();
yield return @interface;
}
}

View File

@@ -8,7 +8,6 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
@@ -47,11 +46,11 @@ namespace Robust.Server.Console.Commands
bool saveSuccess = _ent.System<MapLoaderSystem>().TrySaveGrid(uid, new ResPath(args[1]));
if(saveSuccess)
{
shell.WriteLine("Save successful. Look in the user data directory.");
shell.WriteLine("Save successful. Look in the user data directory.");
}
else
{
shell.WriteError("Save unsuccessful!");
shell.WriteError("Save unsuccessful!");
}
}
@@ -60,7 +59,7 @@ namespace Robust.Server.Console.Commands
switch (args.Length)
{
case 1:
return CompletionResult.FromHintOptions(CompletionHelper.Components<MapGridComponent>(args[0], _ent), Loc.GetString("cmd-hint-savebp-id"));
return CompletionResult.FromHint(Loc.GetString("cmd-hint-savebp-id"));
case 2:
var opts = CompletionHelper.UserFilePath(args[1], _resource.UserData);
return CompletionResult.FromHintOptions(opts, Loc.GetString("cmd-hint-savemap-path"));
@@ -160,7 +159,6 @@ namespace Robust.Server.Console.Commands
public sealed class SaveMap : LocalizedCommands
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IEntitySystemManager _system = default!;
[Dependency] private readonly IResourceManager _resource = default!;
@@ -171,7 +169,7 @@ namespace Robust.Server.Console.Commands
switch (args.Length)
{
case 1:
return CompletionResult.FromHintOptions(CompletionHelper.MapIds(_entManager), Loc.GetString("cmd-hint-savemap-id"));
return CompletionResult.FromHint(Loc.GetString("cmd-hint-savemap-id"));
case 2:
var opts = CompletionHelper.UserFilePath(args[1], _resource.UserData);
return CompletionResult.FromHintOptions(opts, Loc.GetString("cmd-hint-savemap-path"));

View File

@@ -204,23 +204,6 @@ namespace Robust.Server.GameObjects
private void HandleEntityNetworkMessage(MsgEntity message)
{
if (_logLateMsgs)
{
var msgT = message.SourceTick;
var cT = _gameTiming.CurTick;
if (msgT < cT)
{
_netEntSawmill.Warning(
"Got late MsgEntity! Diff: {0}, msgT: {2}, cT: {3}, player: {1}, msg: {4}",
(int) msgT.Value - (int) cT.Value,
message.MsgChannel.UserName,
msgT,
cT,
message.SystemMessage);
}
}
_queue.Add(message);
}

View File

@@ -268,6 +268,7 @@ namespace Robust.Server.Physics
newGrids[i] = newGridUid;
// Keep same origin / velocity etc; this makes updating a lot faster and easier.
_xformSystem.SetWorldPosition(newGridXform, gridPos);
_xformSystem.SetWorldPositionRotation(newGridUid, gridPos, gridRot, newGridXform);
var splitBody = _bodyQuery.GetComponent(newGridUid);
_physics.SetLinearVelocity(newGridUid, mapBody.LinearVelocity, body: splitBody);

View File

@@ -1,15 +0,0 @@
namespace Robust.Server.ServerStatus;
/// <summary>
/// Helper functions for working with <see cref="IStatusHandlerContext"/>.
/// </summary>
public static class StatusExt
{
/// <summary>
/// Add <c>Access-Control-Allow-Origin: *</c> to the response headers for this request.
/// </summary>
public static void AddAllowOriginAny(this IStatusHandlerContext context)
{
context.ResponseHeaders.Add("Access-Control-Allow-Origin", "*");
}
}

View File

@@ -61,7 +61,6 @@ namespace Robust.Server.ServerStatus
OnStatusRequest?.Invoke(jObject);
context.AddAllowOriginAny();
await context.RespondJsonAsync(jObject);
return true;
@@ -122,7 +121,6 @@ namespace Robust.Server.ServerStatus
OnInfoRequest?.Invoke(jObject);
context.AddAllowOriginAny();
await context.RespondJsonAsync(jObject);
return true;

View File

@@ -93,7 +93,6 @@ namespace Robust.Shared.Maths
private const double CardinalSegment = 2 * Math.PI / 4.0; // Cut the circle into 4 pieces
private const double CardinalOffset = CardinalSegment / 2.0; // offset the pieces by 1/2 their size
[Pure]
public readonly Direction GetCardinalDir()
{
var ang = Theta % (2 * Math.PI);
@@ -168,7 +167,6 @@ namespace Robust.Shared.Maths
/// <summary>
/// Removes revolutions from a positive or negative angle to make it as small as possible.
/// </summary>
[Pure]
public readonly Angle Reduced()
{
return new(Reduce(Theta));
@@ -215,13 +213,11 @@ namespace Robust.Shared.Maths
return !(a == b);
}
[Pure]
public readonly Angle Opposite()
{
return new Angle(FlipPositive(Theta-Math.PI));
}
[Pure]
public readonly Angle FlipPositive()
{
return new(FlipPositive(Theta));

View File

@@ -682,7 +682,6 @@ namespace Robust.Shared.Maths
/// (which is copied to the output's Alpha value).
/// Each has a range of 0.0 to 1.0.
/// </param>
[Obsolete("The HCY color space is mathematically incorrect and these functions are broken, use something else")]
public static Color FromHcy(Vector4 hcy)
{
var hue = hcy.X * 360.0f;
@@ -751,7 +750,6 @@ namespace Robust.Shared.Maths
/// </returns>
/// <param name="rgb">Color value to convert.</param>
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")]
[Obsolete("The HCY color space is mathematically incorrect and these functions are broken, use something else")]
public static Vector4 ToHcy(Color rgb)
{
var max = MathF.Max(rgb.R, MathF.Max(rgb.G, rgb.B));

View File

@@ -1,6 +1,4 @@
using System;
using System.Collections.Immutable;
using System.Diagnostics.Contracts;
using System.Numerics;
namespace Robust.Shared.Maths
@@ -38,21 +36,6 @@ namespace Robust.Shared.Maths
/// </summary>
public static class DirectionExtensions
{
/// <summary>
/// A list of all cardinal and diagonal <see cref="Direction"/>s.
/// </summary>
public static readonly ImmutableArray<Direction> AllDirections =
[
Direction.South,
Direction.SouthEast,
Direction.East,
Direction.NorthEast,
Direction.North,
Direction.NorthWest,
Direction.West,
Direction.SouthWest
];
private const double Segment = 2 * Math.PI / 8.0; // Cut the circle into 8 pieces
public static Direction AsDir(this DirectionFlag directionFlag)
@@ -255,7 +238,6 @@ namespace Robust.Shared.Maths
/// </summary>
/// <param name="dir"></param>
/// <returns></returns>
[Pure]
public static Angle ToAngle(this Direction dir)
{
var ang = Segment * (int) dir;

View File

@@ -1,11 +0,0 @@
using System;
namespace Robust.Shared.Analyzers;
/// <summary>
/// Marks that values used for this parameter should not be literal values.
/// This helps prevent magic numbers/strings/etc, by indicating that values
/// should either be wrapped (for validation) or defined as constants or readonly statics.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class ForbidLiteralAttribute : Attribute;

View File

@@ -8,7 +8,7 @@ namespace Robust.Shared.Audio;
/// Contains audio defaults to set for sounds.
/// This can be used by <see cref="Content.Shared.Audio.SharedContentAudioSystem"/> to apply an audio preset.
/// </summary>
[Prototype]
[Prototype("audioPreset")]
public sealed partial class AudioPresetPrototype : IPrototype
{
[IdDataField]

View File

@@ -6,7 +6,7 @@ using Robust.Shared.ViewVariables;
namespace Robust.Shared.Audio;
[Prototype]
[Prototype("soundCollection")]
public sealed partial class SoundCollectionPrototype : IPrototype
{
[ViewVariables]

View File

@@ -968,13 +968,6 @@ namespace Robust.Shared
public static readonly CVarDef<string> RenderFOVColor =
CVarDef.Create("render.fov_color", Color.Black.ToHex(), CVar.REPLICATED | CVar.SERVER);
/// <summary>
/// Whether to render tile edges, which is where some tiles can partially overlap other adjacent tiles on a grid.
/// E.g., snow tiles partly extending beyond their own tile to blend together with different adjacent tiles types.
/// </summary>
public static readonly CVarDef<bool> RenderTileEdges =
CVarDef.Create("render.tile_edges", true, CVar.CLIENTONLY);
/*
* CONTROLS
*/
@@ -1881,12 +1874,5 @@ namespace Robust.Shared
/// </summary>
public static readonly CVarDef<int> ToolshedNearbyEntitiesLimit =
CVarDef.Create("toolshed.nearby_entities_limit", 5, CVar.SERVER | CVar.REPLICATED);
/*
* Localization
*/
public static readonly CVarDef<string> LocCultureName =
CVarDef.Create("loc.culture_name", "en-US", CVar.ARCHIVE);
}
}

View File

@@ -37,7 +37,7 @@ sealed class AddMapCommand : LocalizedEntityCommands
sealed class RemoveMapCommand : LocalizedCommands
{
[Dependency] private readonly IEntitySystemManager _systems = default!;
[Dependency] private readonly IMapManager _map = default!;
public override string Command => "rmmap";
public override bool RequireServerOrSingleplayer => true;
@@ -51,15 +51,14 @@ sealed class RemoveMapCommand : LocalizedCommands
}
var mapId = new MapId(int.Parse(args[0]));
var mapSystem = _systems.GetEntitySystem<SharedMapSystem>();
if (!mapSystem.MapExists(mapId))
if (!_map.MapExists(mapId))
{
shell.WriteError($"Map {mapId.Value} does not exist.");
return;
}
mapSystem.DeleteMap(mapId);
_map.DeleteMap(mapId);
shell.WriteLine($"Map {mapId.Value} was removed.");
}
}

View File

@@ -20,7 +20,6 @@ internal sealed class TeleportCommand : LocalizedEntityCommands
[Dependency] private readonly IMapManager _map = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
public override string Command => "tp";
public override bool RequireServerOrSingleplayer => true;
@@ -47,7 +46,7 @@ internal sealed class TeleportCommand : LocalizedEntityCommands
else
mapId = transform.MapID;
if (!_mapSystem.MapExists(mapId))
if (!_map.MapExists(mapId))
{
shell.WriteError($"Map {mapId} doesn't exist!");
return;
@@ -61,26 +60,13 @@ internal sealed class TeleportCommand : LocalizedEntityCommands
}
else
{
if (_mapSystem.TryGetMap(mapId, out var mapEnt))
{
_transform.SetWorldPosition((entity, transform), position);
_transform.SetParent(entity, transform, mapEnt.Value);
}
var mapEnt = _map.GetMapEntityIdOrThrow(mapId);
_transform.SetWorldPosition((entity, transform), position);
_transform.SetParent(entity, transform, mapEnt);
}
shell.WriteLine($"Teleported {shell.Player} to {mapId}:{posX},{posY}.");
}
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
return args.Length switch
{
1 => CompletionResult.FromHint("<x>"),
2 => CompletionResult.FromHint("<y>"),
3 => CompletionResult.FromHintOptions(CompletionHelper.MapIds(_entityManager), "[MapId]"),
_ => CompletionResult.Empty
};
}
}
public sealed class TeleportToCommand : LocalizedEntityCommands

View File

@@ -530,39 +530,6 @@ namespace Robust.Shared.Containers
return false;
}
/// <summary>
/// Returns the full chain of containers containing the entity passed in, from innermost to outermost.
/// </summary>
/// <remarks>
/// The resulting collection includes the container directly containing the entity (if any),
/// the container containing that container, and so on until reaching the outermost container.
/// </remarks>
public IEnumerable<BaseContainer> GetContainingContainers(Entity<TransformComponent?> ent)
{
if (!ent.Owner.IsValid())
yield break;
if (!Resolve(ent, ref ent.Comp))
yield break;
var child = ent.Owner;
var parent = ent.Comp.ParentUid;
while (parent.IsValid())
{
if (((MetaQuery.GetComponent(child).Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer) &&
_managerQuery.TryGetComponent(parent, out var conManager) &&
TryGetContainingContainer(parent, child, out var parentContainer, conManager))
{
yield return parentContainer;
}
var parentXform = TransformQuery.GetComponent(parent);
child = parent;
parent = parentXform.ParentUid;
}
}
/// <summary>
/// Gets the top-most container in the hierarchy for this entity, if it exists.
/// </summary>

View File

@@ -475,7 +475,7 @@ public sealed class EntityDeserializer :
foreach (var (key, value) in tileMap.Children)
{
var yamlTileId = int.Parse(key, CultureInfo.InvariantCulture);
var yamlTileId = ((ValueDataNode) key).AsInt();
var tileName = ((ValueDataNode) value).Value;
if (migrations.TryGetValue(tileName, out var @new))
tileName = @new;

View File

@@ -155,9 +155,9 @@ public sealed class EntitySerializer : ISerializationContext,
public event IsSerializableDelegate? OnIsSerializeable;
public delegate void IsSerializableDelegate(Entity<MetaDataComponent> ent, ref bool serializable);
public EntitySerializer(IDependencyCollection dependency, SerializationOptions options)
public EntitySerializer(IDependencyCollection _dependency, SerializationOptions options)
{
dependency.InjectDependencies(this);
_dependency.InjectDependencies(this);
_log = _logMan.GetSawmill("entity_serializer");
SerializerProvider.RegisterSerializer(this);

View File

@@ -69,7 +69,7 @@ internal sealed class MapChunkSerializer : ITypeSerializer<MapChunk, MappingData
var tileDefinitionManager = dependencies.Resolve<ITileDefinitionManager>();
node.TryGetValue("version", out var versionNode);
node.TryGetValue(new ValueDataNode("version"), out var versionNode);
var version = ((ValueDataNode?) versionNode)?.AsInt() ?? 1;
for (ushort y = 0; y < chunk.ChunkSize; y++)

View File

@@ -47,7 +47,6 @@ public sealed partial class MapLoaderSystem : EntitySystem
Log.Info($"Saving serialized results to {path}");
path = path.ToRootedPath();
var document = new YamlDocument(data.ToYaml());
_resourceManager.UserData.CreateDir(path.Directory);
using var writer = _resourceManager.UserData.OpenWriteText(path);
{
var stream = new YamlStream {document};

View File

@@ -1,22 +1,20 @@
using Robust.Shared.Serialization.Manager.Attributes;
namespace Robust.Shared.GameObjects;
/// <summary>
/// Controls PVS visibility of entities. THIS COMPONENT CONTROLS WHETHER ENTITIES ARE NETWORKED TO PLAYERS
/// AND SHOULD NOT BE USED AS THE SOLE WAY TO HIDE AN ENTITY FROM A PLAYER.
/// </summary>
[RegisterComponent]
[Access(typeof(SharedVisibilitySystem))]
public sealed partial class VisibilityComponent : Component
namespace Robust.Shared.GameObjects
{
/// <summary>
/// The visibility layer for the entity.
/// Players whose visibility masks don't match this won't get state updates for it.
/// Controls PVS visibility of entities. THIS COMPONENT CONTROLS WHETHER ENTITIES ARE NETWORKED TO PLAYERS
/// AND SHOULD NOT BE USED AS THE SOLE WAY TO HIDE AN ENTITY FROM A PLAYER.
/// </summary>
/// <remarks>
/// Not serialized as visibility is normally immediate (i.e. prior to MapInit) and content should be handling it as such.
/// </remarks>
[DataField(readOnly: true)]
public ushort Layer = 1;
[RegisterComponent]
[Access(typeof(SharedVisibilitySystem))]
public sealed partial class VisibilityComponent : Component
{
/// <summary>
/// The visibility layer for the entity.
/// Players whose visibility masks don't match this won't get state updates for it.
/// </summary>
[DataField("layer")]
public ushort Layer = 1;
}
}

View File

@@ -1,7 +0,0 @@
namespace Robust.Shared.GameObjects;
/// <summary>
/// Indicates the attached entity was spawn predicted and should be reconciled when the server states comes in.
/// </summary>
[RegisterComponent]
public sealed partial class PredictedSpawnComponent : Component;

View File

@@ -59,7 +59,7 @@ public abstract partial class EntityManager
Dirty(uid, comp, metadata);
}
public virtual void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params string[] fields)
public virtual void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params ReadOnlySpan<string> fields)
where T : IComponentDelta
{
var compReg = ComponentFactory.GetRegistration(CompIdx.Index<T>());

View File

@@ -595,23 +595,12 @@ namespace Robust.Shared.GameObjects
if (!_deleteSet.Add(component))
{
// Already deferring deletion
DebugTools.Assert(component.LifeStage >= ComponentLifeStage.Stopped);
// already deferred deletion
return;
}
DebugTools.Assert(component.LifeStage >= ComponentLifeStage.Added);
if (component.LifeStage is >= ComponentLifeStage.Initialized and < ComponentLifeStage.Stopping)
if (component.LifeStage >= ComponentLifeStage.Initialized && component.LifeStage <= ComponentLifeStage.Running)
LifeShutdown(uid, component, _componentFactory.GetIndex(component.GetType()));
else if (component.LifeStage == ComponentLifeStage.Added)
{
// The component was added, but never initialized or started. It's kinda weird to add and then
// immediately defer-remove a component, but oh well. Let's just set the life stage directly and not
// raise shutdown events? The removal events will still get called later.
// This is also what LifeShutdown() would also do, albeit behind a DebugAssert.
component.LifeStage = ComponentLifeStage.Stopped;
}
#if EXCEPTION_TOLERANCE
}
catch (Exception e)

View File

@@ -14,9 +14,8 @@ public partial class EntityManager
/// Increases the life stage from <see cref="ComponentLifeStage.PreAdd" /> to <see cref="ComponentLifeStage.Added" />,
/// after raising a <see cref="ComponentAdd"/> event.
/// </summary>
internal void LifeAddToEntity<T>(EntityUid uid, T component, CompIdx idx) where T : IComponent
internal void LifeAddToEntity(EntityUid uid, IComponent component, CompIdx idx)
{
DebugTools.Assert(!_deleteSet.Contains(component));
DebugTools.Assert(component.LifeStage == ComponentLifeStage.PreAdd);
#pragma warning disable CS0618 // Type or member is obsolete
@@ -33,9 +32,8 @@ public partial class EntityManager
/// Increases the life stage from <see cref="ComponentLifeStage.Added" /> to <see cref="ComponentLifeStage.Initialized" />,
/// calling <see cref="Initialize" />.
/// </summary>
internal void LifeInitialize<T>(EntityUid uid, T component, CompIdx idx) where T : IComponent
internal void LifeInitialize(EntityUid uid, IComponent component, CompIdx idx)
{
DebugTools.Assert(!_deleteSet.Contains(component));
DebugTools.Assert(component.LifeStage == ComponentLifeStage.Added);
component.LifeStage = ComponentLifeStage.Initializing;
@@ -47,9 +45,8 @@ public partial class EntityManager
/// Increases the life stage from <see cref="ComponentLifeStage.Initialized" /> to
/// <see cref="ComponentLifeStage.Running" />, calling <see cref="Startup" />.
/// </summary>
internal void LifeStartup<T>(EntityUid uid, T component, CompIdx idx) where T : IComponent
internal void LifeStartup(EntityUid uid, IComponent component, CompIdx idx)
{
DebugTools.Assert(!_deleteSet.Contains(component));
DebugTools.Assert(component.LifeStage == ComponentLifeStage.Initialized);
component.LifeStage = ComponentLifeStage.Starting;
@@ -64,7 +61,7 @@ public partial class EntityManager
/// <remarks>
/// Components are allowed to remove themselves in their own Startup function.
/// </remarks>
internal void LifeShutdown<T>(EntityUid uid, T component, CompIdx idx) where T : IComponent
internal void LifeShutdown(EntityUid uid, IComponent component, CompIdx idx)
{
DebugTools.Assert(component.LifeStage is >= ComponentLifeStage.Initializing and < ComponentLifeStage.Stopping);
@@ -84,7 +81,7 @@ public partial class EntityManager
/// Increases the life stage from <see cref="ComponentLifeStage.Stopped" /> to <see cref="ComponentLifeStage.Deleted" />,
/// calling <see cref="Component.OnRemove" />.
/// </summary>
internal void LifeRemoveFromEntity<T>(EntityUid uid, T component, CompIdx idx) where T : IComponent
internal void LifeRemoveFromEntity(EntityUid uid, IComponent component, CompIdx idx)
{
// can be called at any time after PreAdd, including inside other life stage events.
DebugTools.Assert(component.LifeStage != ComponentLifeStage.PreAdd);

View File

@@ -6,7 +6,6 @@ using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Robust.Shared.Containers;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
namespace Robust.Shared.GameObjects;
@@ -80,7 +79,7 @@ public partial class EntityManager
throw new InvalidOperationException($"Tried to spawn entity {protoName} on invalid coordinates {coordinates}.");
var entity = CreateEntityUninitialized(protoName, coordinates, overrides, rotation);
InitializeAndStartEntity(entity, _xforms.GetMapId(coordinates));
InitializeAndStartEntity(entity, coordinates.GetMapId(this));
return entity;
}
@@ -101,7 +100,7 @@ public partial class EntityManager
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityUid SpawnAtPosition(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
=> Spawn(protoName, _xforms.ToMapCoordinates(coordinates), overrides);
=> Spawn(protoName, coordinates.ToMap(this, _xforms), overrides);
public bool TrySpawnNextTo(
string? protoName,
@@ -207,91 +206,4 @@ public partial class EntityManager
return uid;
}
#region Prediction
public virtual EntityUid PredictedSpawnAttachedTo(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default)
{
return SpawnAttachedTo(protoName, coordinates, overrides, rotation);
}
public virtual EntityUid PredictedSpawn(string? protoName = null, ComponentRegistry? overrides = null, bool doMapInit = true)
{
return Spawn(protoName, overrides, doMapInit);
}
public virtual EntityUid PredictedSpawn(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default!)
{
return Spawn(protoName, coordinates, overrides, rotation);
}
public virtual EntityUid PredictedSpawnAtPosition(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
{
return SpawnAtPosition(protoName, coordinates, overrides);
}
public virtual bool PredictedTrySpawnNextTo(
string? protoName,
EntityUid target,
[NotNullWhen(true)] out EntityUid? uid,
TransformComponent? xform = null,
ComponentRegistry? overrides = null)
{
return TrySpawnNextTo(protoName, target, out uid, xform, overrides);
}
public virtual bool PredictedTrySpawnInContainer(
string? protoName,
EntityUid containerUid,
string containerId,
[NotNullWhen(true)] out EntityUid? uid,
ContainerManagerComponent? containerComp = null,
ComponentRegistry? overrides = null)
{
return TrySpawnInContainer(protoName, containerUid, containerId, out uid, containerComp, overrides);
}
public virtual EntityUid PredictedSpawnNextToOrDrop(string? protoName, EntityUid target, TransformComponent? xform = null, ComponentRegistry? overrides = null)
{
return SpawnNextToOrDrop(protoName, target, xform, overrides);
}
public virtual EntityUid PredictedSpawnInContainerOrDrop(
string? protoName,
EntityUid containerUid,
string containerId,
TransformComponent? xform = null,
ContainerManagerComponent? containerComp = null,
ComponentRegistry? overrides = null)
{
return SpawnInContainerOrDrop(protoName, containerUid, containerId, xform, containerComp, overrides);
}
public virtual EntityUid PredictedSpawnInContainerOrDrop(
string? protoName,
EntityUid containerUid,
string containerId,
out bool inserted,
TransformComponent? xform = null,
ContainerManagerComponent? containerComp = null,
ComponentRegistry? overrides = null)
{
return SpawnInContainerOrDrop(protoName,
containerUid,
containerId,
out inserted,
xform,
containerComp,
overrides);
}
/// <summary>
/// Flags an entity as being a predicted spawn and should be deleted when its corresponding tick comes in.
/// </summary>
public virtual void FlagPredicted(Entity<MetaDataComponent?> ent)
{
}
#endregion
}

View File

@@ -48,7 +48,7 @@ namespace Robust.Shared.GameObjects
// I feel like PJB might shed me for putting a system dependency here, but its required for setting entity
// positions on spawn....
protected SharedTransformSystem _xforms = default!;
private SharedTransformSystem _xforms = default!;
private SharedContainerSystem _containers = default!;
public EntityQuery<MetaDataComponent> MetaQuery;
@@ -693,18 +693,6 @@ namespace Robust.Shared.GameObjects
public bool IsQueuedForDeletion(EntityUid uid) => QueuedDeletionsSet.Contains(uid);
/// <inheritdoc />
public virtual void PredictedDeleteEntity(Entity<MetaDataComponent?, TransformComponent?> ent)
{
DeleteEntity(ent.Owner);
}
/// <inheritdoc />
public virtual void PredictedQueueDeleteEntity(Entity<MetaDataComponent?, TransformComponent?> ent)
{
QueueDeleteEntity(ent.Owner);
}
public bool EntityExists(EntityUid uid)
{
return MetaQuery.HasComponentInternal(uid);

View File

@@ -172,22 +172,12 @@ public partial class EntitySystem
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params string[] fields)
protected void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params ReadOnlySpan<string> fields)
where T : IComponentDelta
{
EntityManager.DirtyFields(uid, comp, meta, fields);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void DirtyFields<T>(Entity<T?> ent, MetaDataComponent? meta, params string[] fields)
where T : IComponentDelta
{
if (!Resolve(ent, ref ent.Comp))
return;
EntityManager.DirtyFields(ent, ent.Comp, meta, fields);
}
/// <summary>
/// Marks a component as dirty. This also implicitly dirties the entity this component belongs to.
/// </summary>
@@ -782,20 +772,6 @@ public partial class EntitySystem
EntityManager.QueueDeleteEntity(uid);
}
/// <inheritdoc cref="IEntityManager.DeleteEntity(EntityUid)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void PredictedDel(Entity<MetaDataComponent?, TransformComponent?> ent)
{
EntityManager.PredictedDeleteEntity(ent);
}
/// <inheritdoc cref="IEntityManager.QueueDeleteEntity(EntityUid)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void PredictedQueueDel(Entity<MetaDataComponent?, TransformComponent?> ent)
{
EntityManager.PredictedQueueDeleteEntity(ent);
}
/// <inheritdoc cref="IEntityManager.TryQueueDeleteEntity(EntityUid?)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool TryQueueDel(EntityUid? uid)
@@ -826,8 +802,8 @@ public partial class EntitySystem
/// <inheritdoc cref="IEntityManager.SpawnAttachedTo" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityUid SpawnAttachedTo(string? prototype, EntityCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default)
=> EntityManager.SpawnAttachedTo(prototype, coordinates, overrides, rotation);
protected EntityUid SpawnAttachedTo(string? prototype, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
=> EntityManager.SpawnAttachedTo(prototype, coordinates, overrides);
/// <inheritdoc cref="IEntityManager.SpawnAtPosition" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -885,74 +861,6 @@ public partial class EntitySystem
#endregion
#region PredictedSpawning
protected void FlagPredicted(Entity<MetaDataComponent?> ent)
{
EntityManager.FlagPredicted(ent);
}
/// <inheritdoc cref="IEntityManager.SpawnAttachedTo" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityUid PredictedSpawnAttachedTo(string? prototype, EntityCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default)
=> EntityManager.PredictedSpawnAttachedTo(prototype, coordinates, overrides, rotation);
/// <inheritdoc cref="IEntityManager.SpawnAtPosition" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityUid PredictedSpawnAtPosition(string? prototype, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
=> EntityManager.PredictedSpawnAtPosition(prototype, coordinates, overrides);
/// <inheritdoc cref="IEntityManager.TrySpawnInContainer" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool PredictedTrySpawnInContainer(
string? protoName,
EntityUid containerUid,
string containerId,
[NotNullWhen(true)] out EntityUid? uid,
ContainerManagerComponent? containerComp = null,
ComponentRegistry? overrides = null)
{
return EntityManager.PredictedTrySpawnInContainer(protoName, containerUid, containerId, out uid, containerComp, overrides);
}
/// <inheritdoc cref="IEntityManager.TrySpawnNextTo" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool PredictedTrySpawnNextTo(
string? protoName,
EntityUid target,
[NotNullWhen(true)] out EntityUid? uid,
TransformComponent? xform = null,
ComponentRegistry? overrides = null)
{
return EntityManager.PredictedTrySpawnNextTo(protoName, target, out uid, xform, overrides);
}
/// <inheritdoc cref="IEntityManager.SpawnNextToOrDrop" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityUid PredictedSpawnNextToOrDrop(
string? protoName,
EntityUid target,
TransformComponent? xform = null,
ComponentRegistry? overrides = null)
{
return EntityManager.PredictedSpawnNextToOrDrop(protoName, target, xform, overrides);
}
/// <inheritdoc cref="IEntityManager.SpawnInContainerOrDrop" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityUid PredictedSpawnInContainerOrDrop(
string? protoName,
EntityUid containerUid,
string containerId,
TransformComponent? xform = null,
ContainerManagerComponent? container = null,
ComponentRegistry? overrides = null)
{
return EntityManager.PredictedSpawnInContainerOrDrop(protoName, containerUid, containerId, xform, container, overrides);
}
#endregion
#region Utils
/// <summary>

View File

@@ -37,22 +37,18 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc cref="Resolve{TComp}(Robust.Shared.GameObjects.EntityUid,ref TComp?,bool)"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool Resolve(
EntityUid uid,
[NotNullWhen(true)] ref MetaDataComponent? component,
protected bool Resolve(EntityUid uid, [NotNullWhen(true)] ref MetaDataComponent? component,
bool logMissing = true)
{
return EntityManager.MetaQuery.Resolve(uid, ref component, logMissing);
return EntityManager.MetaQuery.Resolve(uid, ref component);
}
/// <inheritdoc cref="Resolve{TComp}(Robust.Shared.GameObjects.EntityUid,ref TComp?,bool)"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool Resolve(
EntityUid uid,
[NotNullWhen(true)] ref TransformComponent? component,
protected bool Resolve(EntityUid uid, [NotNullWhen(true)] ref TransformComponent? component,
bool logMissing = true)
{
return EntityManager.TransformQuery.Resolve(uid, ref component, logMissing);
return EntityManager.TransformQuery.Resolve(uid, ref component);
}
/// <summary>

View File

@@ -193,7 +193,7 @@ namespace Robust.Shared.GameObjects
ComponentEventHandler<TComp, TEvent> handler,
Type[]? before = null, Type[]? after = null)
where TComp : IComponent
where TEvent : notnull
where TEvent : EntityEventArgs
{
System.SubscribeLocalEvent(handler, before, after);
}
@@ -206,7 +206,7 @@ namespace Robust.Shared.GameObjects
ComponentEventRefHandler<TComp, TEvent> handler,
Type[]? before = null, Type[]? after = null)
where TComp : IComponent
where TEvent : notnull
where TEvent : EntityEventArgs
{
System.SubscribeLocalEvent(handler, before, after);
}
@@ -219,7 +219,7 @@ namespace Robust.Shared.GameObjects
EntityEventRefHandler<TComp, TEvent> handler,
Type[]? before = null, Type[]? after = null)
where TComp : IComponent
where TEvent : notnull
where TEvent : EntityEventArgs
{
System.SubscribeLocalEvent(handler, before, after);
}
@@ -229,7 +229,7 @@ namespace Robust.Shared.GameObjects
/// </summary>
/// <remarks>
/// This can be used by extension methods for <see cref="Subscriptions"/>
/// to unsubscribe from external sources such as CVars.
/// to unsubscribe from from external sources such as CVars.
/// </remarks>
/// <param name="action">An action to be ran when the entity system is shut down.</param>
public void RegisterUnsubscription(Action action)

View File

@@ -162,16 +162,6 @@ namespace Robust.Shared.GameObjects
public bool IsQueuedForDeletion(EntityUid uid);
/// <summary>
/// Tries to predict entity deletion. On the server it runs the normal code path and on the client the entity is detached.
/// </summary>
void PredictedDeleteEntity(Entity<MetaDataComponent?, TransformComponent?> ent);
/// <summary>
/// Tries to predict entity deletion. On the server it runs the normal code path and on the client the entity is detached.
/// </summary>
void PredictedQueueDeleteEntity(Entity<MetaDataComponent?, TransformComponent?> ent);
/// <summary>
/// Shuts-down and removes the entity with the given <see cref="Robust.Shared.GameObjects.EntityUid"/>. This is also broadcast to all clients.
/// </summary>

View File

@@ -96,7 +96,6 @@ namespace Robust.Shared.GameObjects
// If we're attached to the map we'll also just never disable collision due to how grid movement works.
var canCollide = body.Awake ||
body.ContactCount > 0 ||
(TryComp(uid, out JointComponent? jointComponent) && jointComponent.JointCount > 0) ||
xform.GridUid == null;

View File

@@ -9,7 +9,6 @@ using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Shapes;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Utility;
@@ -407,7 +406,7 @@ public sealed partial class EntityLookupSystem
var broadphaseInv = _transform.GetInvWorldMatrix(lookupUid);
var localBounds = broadphaseInv.TransformBounds(worldBounds);
var polygon = new SlimPolygon(localBounds);
var polygon = _physics.GetPooled(localBounds);
var result = AnyEntitiesIntersecting(lookupUid,
polygon,
localBounds.CalcBoundingBox(),
@@ -415,6 +414,7 @@ public sealed partial class EntityLookupSystem
flags,
ignored);
_physics.ReturnPooled(polygon);
return result;
}
@@ -429,7 +429,7 @@ public sealed partial class EntityLookupSystem
float arcWidth,
LookupFlags flags = DefaultFlags)
{
var position = _transform.ToMapCoordinates(coordinates);
var position = coordinates.ToMap(EntityManager, _transform);
return GetEntitiesInArc(position, range, direction, arcWidth, flags);
}
@@ -458,8 +458,9 @@ public sealed partial class EntityLookupSystem
{
if (mapId == MapId.Nullspace) return false;
var polygon = new SlimPolygon(worldAABB);
var polygon = _physics.GetPooled(worldAABB);
var result = AnyEntitiesIntersecting(mapId, polygon, Physics.Transform.Empty, flags);
_physics.ReturnPooled(polygon);
return result;
}
@@ -474,8 +475,9 @@ public sealed partial class EntityLookupSystem
{
if (mapId == MapId.Nullspace) return;
var polygon = new SlimPolygon(worldAABB);
var polygon = _physics.GetPooled(worldAABB);
AddEntitiesIntersecting(mapId, intersecting, polygon, Physics.Transform.Empty, flags);
_physics.ReturnPooled(polygon);
}
#endregion
@@ -485,16 +487,18 @@ public sealed partial class EntityLookupSystem
public bool AnyEntitiesIntersecting(MapId mapId, Box2Rotated worldBounds, LookupFlags flags = DefaultFlags)
{
// Don't need to check contained entities as they have the same bounds as the parent.
var polygon = new SlimPolygon(worldBounds);
var polygon = _physics.GetPooled(worldBounds);
var result = AnyEntitiesIntersecting(mapId, polygon, Physics.Transform.Empty, flags);
_physics.ReturnPooled(polygon);
return result;
}
public HashSet<EntityUid> GetEntitiesIntersecting(MapId mapId, Box2Rotated worldBounds, LookupFlags flags = DefaultFlags)
{
var intersecting = new HashSet<EntityUid>();
var polygon = new SlimPolygon(worldBounds);
var polygon = _physics.GetPooled(worldBounds);
AddEntitiesIntersecting(mapId, intersecting, polygon, Physics.Transform.Empty, flags);
_physics.ReturnPooled(polygon);
return intersecting;
}
@@ -628,7 +632,7 @@ public sealed partial class EntityLookupSystem
if (!coordinates.IsValid(EntityManager))
return false;
var mapPos = _transform.ToMapCoordinates(coordinates);
var mapPos = coordinates.ToMap(EntityManager, _transform);
return AnyEntitiesIntersecting(mapPos, flags);
}
@@ -637,13 +641,13 @@ public sealed partial class EntityLookupSystem
if (!coordinates.IsValid(EntityManager))
return false;
var mapPos = _transform.ToMapCoordinates(coordinates);
var mapPos = coordinates.ToMap(EntityManager, _transform);
return AnyEntitiesInRange(mapPos, range, flags);
}
public HashSet<EntityUid> GetEntitiesIntersecting(EntityCoordinates coordinates, LookupFlags flags = DefaultFlags)
{
var mapPos = _transform.ToMapCoordinates(coordinates);
var mapPos = coordinates.ToMap(EntityManager, _transform);
return GetEntitiesIntersecting(mapPos, flags);
}
@@ -656,7 +660,7 @@ public sealed partial class EntityLookupSystem
public void GetEntitiesInRange(EntityCoordinates coordinates, float range, HashSet<EntityUid> entities, LookupFlags flags = DefaultFlags)
{
var mapPos = _transform.ToMapCoordinates(coordinates);
var mapPos = coordinates.ToMap(EntityManager, _transform);
if (mapPos.MapId == MapId.Nullspace)
return;
@@ -757,10 +761,11 @@ public sealed partial class EntityLookupSystem
return;
var localAABB = _transform.GetInvWorldMatrix(gridId).TransformBox(worldAABB);
var polygon = new SlimPolygon(localAABB);
var polygon = _physics.GetPooled(localAABB);
AddEntitiesIntersecting(gridId, intersecting, polygon, localAABB, Physics.Transform.Empty, flags, lookup);
AddContained(intersecting, flags);
_physics.ReturnPooled(polygon);
}
public void GetEntitiesIntersecting(EntityUid gridId, Box2Rotated worldBounds, HashSet<EntityUid> intersecting, LookupFlags flags = DefaultFlags)
@@ -769,10 +774,11 @@ public sealed partial class EntityLookupSystem
return;
var localBounds = _transform.GetInvWorldMatrix(gridId).TransformBounds(worldBounds);
var polygon = new SlimPolygon(localBounds);
var polygon = _physics.GetPooled(localBounds);
AddEntitiesIntersecting(gridId, intersecting, polygon, localBounds.CalcBoundingBox(), Physics.Transform.Empty, flags, lookup);
AddContained(intersecting, flags);
_physics.ReturnPooled(polygon);
}
#endregion

View File

@@ -121,8 +121,9 @@ public sealed partial class EntityLookupSystem
if (!_broadQuery.Resolve(lookupUid, ref lookup))
return;
var polygon = new SlimPolygon(localAABB);
var polygon = _physics.GetPooled(localAABB);
AddEntitiesIntersecting(lookupUid, intersecting, polygon, localAABB, Physics.Transform.Empty, flags, query, lookup);
_physics.ReturnPooled(polygon);
}
private void AddEntitiesIntersecting<T, TShape>(
@@ -251,10 +252,11 @@ public sealed partial class EntityLookupSystem
if (!_broadQuery.Resolve(lookupUid, ref lookup))
return false;
var polygon = new SlimPolygon(localAABB);
var polygon = _physics.GetPooled(localAABB);
var (lookupPos, lookupRot) = _transform.GetWorldPositionRotation(lookupUid);
var transform = new Transform(lookupPos, lookupRot);
var result = AnyComponentsIntersecting(lookupUid, polygon, localAABB, transform, flags, query, ignored, lookup);
_physics.ReturnPooled(polygon);
return result;
}
@@ -425,9 +427,10 @@ public sealed partial class EntityLookupSystem
public bool AnyComponentsIntersecting(Type type, MapId mapId, Box2 worldAABB, EntityUid? ignored = null, LookupFlags flags = DefaultFlags)
{
var polygon = new SlimPolygon(worldAABB);
var polygon = _physics.GetPooled(worldAABB);
var transform = Physics.Transform.Empty;
var result = AnyComponentsIntersecting(type, mapId, polygon, transform, ignored, flags);
_physics.ReturnPooled(polygon);
return result;
}
@@ -493,30 +496,33 @@ public sealed partial class EntityLookupSystem
if (mapId == MapId.Nullspace)
return;
var polygon = new SlimPolygon(worldAABB);
var polygon = _physics.GetPooled(worldAABB);
var transform = Physics.Transform.Empty;
GetEntitiesIntersecting(type, mapId, polygon, transform, intersecting, flags);
_physics.ReturnPooled(polygon);
}
public void GetEntitiesIntersecting<T>(MapId mapId, Box2Rotated worldBounds, HashSet<Entity<T>> entities, LookupFlags flags = DefaultFlags) where T : IComponent
{
if (mapId == MapId.Nullspace) return;
var polygon = new SlimPolygon(worldBounds);
var polygon = _physics.GetPooled(worldBounds);
var shapeTransform = Physics.Transform.Empty;
GetEntitiesIntersecting(mapId, polygon, shapeTransform, entities, flags);
_physics.ReturnPooled(polygon);
}
public void GetEntitiesIntersecting<T>(MapId mapId, Box2 worldAABB, HashSet<Entity<T>> entities, LookupFlags flags = DefaultFlags) where T : IComponent
{
if (mapId == MapId.Nullspace) return;
var polygon = new SlimPolygon(worldAABB);
var polygon = _physics.GetPooled(worldAABB);
var shapeTransform = Physics.Transform.Empty;
GetEntitiesIntersecting(mapId, polygon, shapeTransform, entities, flags);
_physics.ReturnPooled(polygon);
}
#endregion
@@ -621,7 +627,7 @@ public sealed partial class EntityLookupSystem
public void GetEntitiesInRange<T>(EntityCoordinates coordinates, float range, HashSet<Entity<T>> entities, LookupFlags flags = DefaultFlags) where T : IComponent
{
var mapPos = _transform.ToMapCoordinates(coordinates);
var mapPos = coordinates.ToMap(EntityManager, _transform);
GetEntitiesInRange(mapPos, range, entities, flags);
}

View File

@@ -26,7 +26,7 @@ public sealed partial class EntityLookupSystem
if (!_broadQuery.Resolve(lookupUid, ref lookup))
return;
var lookupPoly = new SlimPolygon(localAABB);
var lookupPoly = new Polygon(localAABB);
AddEntitiesIntersecting(lookupUid, intersecting, lookupPoly, localAABB, Physics.Transform.Empty, flags, lookup);
}
@@ -40,7 +40,7 @@ public sealed partial class EntityLookupSystem
if (!_broadQuery.Resolve(lookupUid, ref lookup))
return;
var shape = new SlimPolygon(localBounds);
var shape = new Polygon(localBounds);
var localAABB = localBounds.CalcBoundingBox();
AddEntitiesIntersecting(lookupUid, intersecting, shape, localAABB, Physics.Transform.Empty, flags);
@@ -55,7 +55,7 @@ public sealed partial class EntityLookupSystem
if (!_broadQuery.Resolve(lookupUid, ref lookup))
return false;
var shape = new SlimPolygon(localAABB);
var shape = new Polygon(localAABB);
return AnyEntitiesIntersecting(lookupUid, shape, localAABB, Physics.Transform.Empty, flags, ignored, lookup);
}

View File

@@ -9,7 +9,6 @@ namespace Robust.Shared.GameObjects;
public abstract class SharedEyeSystem : EntitySystem
{
[Dependency] private readonly SharedViewSubscriberSystem _views = default!;
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
public override void Initialize()
{
@@ -160,10 +159,6 @@ public abstract class SharedEyeSystem : EntitySystem
eye.Comp.PvsScale = Math.Clamp(scale, 0.1f, 100f);
}
/// <summary>
/// Overwrites visibility mask of an entity's eye.
/// If you wish for other systems to potentially change it consider raising <see cref="RefreshVisibilityMask"/>.
/// </summary>
public void SetVisibilityMask(EntityUid uid, int value, EyeComponent? eyeComponent = null)
{
if (!Resolve(uid, ref eyeComponent))
@@ -175,32 +170,4 @@ public abstract class SharedEyeSystem : EntitySystem
eyeComponent.VisibilityMask = value;
DirtyField(uid, eyeComponent, nameof(EyeComponent.VisibilityMask));
}
/// <summary>
/// Updates the visibility mask for an entity by raising a <see cref="GetVisMaskEvent"/>
/// </summary>
public void RefreshVisibilityMask(Entity<EyeComponent?> entity)
{
if (!Resolve(entity.Owner, ref entity.Comp, false))
return;
var ev = new GetVisMaskEvent()
{
Entity = entity.Owner,
};
RaiseLocalEvent(entity.Owner, ref ev, true);
SetVisibilityMask(entity.Owner, ev.VisibilityMask, entity.Comp);
}
}
/// <summary>
/// Event raised to update the vismask of an entity's eye.
/// </summary>
[ByRefEvent]
public record struct GetVisMaskEvent()
{
public EntityUid Entity;
public int VisibilityMask = EyeComponent.DefaultVisibilityMask;
}

View File

@@ -23,7 +23,7 @@ public abstract partial class SharedMapSystem
}
// Check if mappos intersects a grid.
var mapPos = _transform.ToMapCoordinates(coordinates);
var mapPos = coordinates.ToMap(EntityManager, _transform);
if (_mapInternal.TryFindGridAt(mapPos, out var gridUid, out gridComponent))
{

View File

@@ -1222,7 +1222,7 @@ public abstract partial class SharedMapSystem
{
#if DEBUG
var mapId = _xformQuery.GetComponent(uid).MapID;
DebugTools.Assert(mapId == _transform.GetMapId(coords));
DebugTools.Assert(mapId == coords.GetMapId(EntityManager));
#endif
return SnapGridLocalCellFor(uid, grid, LocalToGrid(uid, grid, coords));
@@ -1432,7 +1432,7 @@ public abstract partial class SharedMapSystem
{
#if DEBUG
var mapId = _xformQuery.GetComponent(uid).MapID;
DebugTools.Assert(mapId == _transform.GetMapId(coords));
DebugTools.Assert(mapId == coords.GetMapId(EntityManager));
#endif
var local = LocalToGrid(uid, grid, coords);
@@ -1454,7 +1454,7 @@ public abstract partial class SharedMapSystem
{
return position.EntityId == uid
? position.Position
: WorldToLocal(uid, grid, _transform.ToMapCoordinates(position).Position);
: WorldToLocal(uid, grid, position.ToMapPos(EntityManager, _transform));
}
public bool CollidesWithGrid(EntityUid uid, MapGridComponent grid, Vector2i indices)

Some files were not shown because too many files have changed in this diff Show More