mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7f7030b89 | ||
|
|
54218fb40b | ||
|
|
7b2c1701f3 | ||
|
|
0e3930d7d7 | ||
|
|
a9aea7027f | ||
|
|
2a49c2d9b8 | ||
|
|
a0c069f1ea | ||
|
|
2c6fb95e53 | ||
|
|
afe337644e | ||
|
|
b8924f3ddf | ||
|
|
08970e745b | ||
|
|
0ba4a66787 | ||
|
|
75b3431ee6 | ||
|
|
c0ef976588 | ||
|
|
fe5cdf9e3c | ||
|
|
450349188b | ||
|
|
897ad998d9 | ||
|
|
635ae3c353 | ||
|
|
a4ea5a4620 | ||
|
|
90e87526d0 | ||
|
|
cd6576ddf9 | ||
|
|
e2cf4ee3db |
@@ -55,7 +55,7 @@
|
||||
<PackageVersion Include="Serilog" Version="3.1.1" />
|
||||
<PackageVersion Include="Serilog.Sinks.Loki" Version="4.0.0-beta3" />
|
||||
<PackageVersion Include="SharpZstd.Interop" Version="1.5.2-beta2" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.3" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.5" />
|
||||
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.0.2" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
@@ -54,6 +54,39 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 227.0.2
|
||||
|
||||
|
||||
## 227.0.1
|
||||
|
||||
|
||||
## 227.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Add a `loop` arg to SpriteSystem.GetFrame in case you don't want to get a looping animation.
|
||||
* Remove obsolete VisibileSystem methods.
|
||||
|
||||
### New features
|
||||
|
||||
* Added `LocalizedEntityCommands`, which are console commands that have the ability to take entity system dependencies.
|
||||
* Added `BeginRegistrationRegion` to `IConsoleHost` to allow efficient bulk-registration of console commands.
|
||||
* Added `IConsoleHost.RegisterCommand` overload that takes an `IConsoleCommand`.
|
||||
* Added a `Finished` boolean to `AnimationCompletedEvent` which allows distinguishing if an animation was removed prematurely or completed naturally.
|
||||
* Add GetLocalTilesIntersecting for MapSystem.
|
||||
* Add an analyzer for methods that should call the base implementation and use it for EntitySystems.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix loading replays if string package is compressed inside a zip.
|
||||
|
||||
### Other
|
||||
|
||||
* Tab completions containing spaces are now properly quoted, so the command will actually work properly once entered.
|
||||
* Mark EntityCoordinates.Offset as Pure so it shows as warnings if the variable is unused.
|
||||
* Networked events will always be processed in order even if late.
|
||||
|
||||
|
||||
## 226.3.0
|
||||
|
||||
### New features
|
||||
|
||||
92
Robust.Analyzers.Tests/MustCallBaseAnalyzerTest.cs
Normal file
92
Robust.Analyzers.Tests/MustCallBaseAnalyzerTest.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.CSharp.Testing;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
using Microsoft.CodeAnalysis.Testing.Verifiers;
|
||||
using NUnit.Framework;
|
||||
using VerifyCS =
|
||||
Microsoft.CodeAnalysis.CSharp.Testing.NUnit.AnalyzerVerifier<Robust.Analyzers.MustCallBaseAnalyzer>;
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
|
||||
[TestFixture]
|
||||
public sealed class MustCallBaseAnalyzerTest
|
||||
{
|
||||
private static Task Verifier(string code, params DiagnosticResult[] expected)
|
||||
{
|
||||
var test = new CSharpAnalyzerTest<MustCallBaseAnalyzer, NUnitVerifier>()
|
||||
{
|
||||
TestState =
|
||||
{
|
||||
Sources = { code }
|
||||
},
|
||||
};
|
||||
|
||||
TestHelper.AddEmbeddedSources(
|
||||
test.TestState,
|
||||
"Robust.Shared.IoC.MustCallBaseAttribute.cs"
|
||||
);
|
||||
|
||||
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
|
||||
test.TestState.ExpectedDiagnostics.AddRange(expected);
|
||||
|
||||
return test.RunAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test()
|
||||
{
|
||||
const string code = """
|
||||
using Robust.Shared.Analyzers;
|
||||
|
||||
public class Foo
|
||||
{
|
||||
[MustCallBase]
|
||||
public virtual void Function()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[MustCallBase(true)]
|
||||
public virtual void Function2()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class Bar : Foo
|
||||
{
|
||||
public override void Function()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void Function2()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class Baz : Foo
|
||||
{
|
||||
public override void Function()
|
||||
{
|
||||
base.Function();
|
||||
}
|
||||
}
|
||||
|
||||
public class Bal : Bar
|
||||
{
|
||||
public override void Function2()
|
||||
{
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
await Verifier(code,
|
||||
// /0/Test0.cs(20,26): warning RA0028: Overriders of this function must always call the base function
|
||||
VerifyCS.Diagnostic().WithSpan(20, 26, 20, 34),
|
||||
// /0/Test0.cs(41,26): warning RA0028: Overriders of this function must always call the base function
|
||||
VerifyCS.Diagnostic().WithSpan(41, 26, 41, 35));
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\AccessAttribute.cs" LogicalName="Robust.Shared.Analyzers.AccessAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\AccessPermissions.cs" LogicalName="Robust.Shared.Analyzers.AccessPermissions.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\MustCallBaseAttribute.cs" LogicalName="Robust.Shared.IoC.MustCallBaseAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\IoC\DependencyAttribute.cs" LogicalName="Robust.Shared.IoC.DependencyAttribute.cs" LinkBase="Implementations" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
111
Robust.Analyzers/MustCallBaseAnalyzer.cs
Normal file
111
Robust.Analyzers/MustCallBaseAnalyzer.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Enforces <c>MustCallBaseAttribute</c>.
|
||||
/// </summary>
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class MustCallBaseAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string Attribute = "Robust.Shared.Analyzers.MustCallBaseAttribute";
|
||||
|
||||
private static readonly DiagnosticDescriptor Rule = new(
|
||||
Diagnostics.IdMustCallBase,
|
||||
"No base call in overriden function",
|
||||
"Overriders of this function must always call the base function",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Warning,
|
||||
isEnabledByDefault: true);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method);
|
||||
}
|
||||
|
||||
private static void AnalyzeSymbol(SymbolAnalysisContext context)
|
||||
{
|
||||
if (context.Symbol is not IMethodSymbol { IsOverride: true } method)
|
||||
return;
|
||||
|
||||
var attrSymbol = context.Compilation.GetTypeByMetadataName(Attribute);
|
||||
if (attrSymbol == null)
|
||||
return;
|
||||
|
||||
if (DoesMethodOverriderHaveAttribute(method, attrSymbol) is not { } data)
|
||||
return;
|
||||
|
||||
if (data is { onlyOverrides: true, depth: < 2 })
|
||||
return;
|
||||
|
||||
var syntax = (MethodDeclarationSyntax) method.DeclaringSyntaxReferences[0].GetSyntax();
|
||||
if (HasBaseCall(syntax))
|
||||
return;
|
||||
|
||||
var diag = Diagnostic.Create(Rule, syntax.Identifier.GetLocation());
|
||||
context.ReportDiagnostic(diag);
|
||||
}
|
||||
|
||||
private static (int depth, bool onlyOverrides)? DoesMethodOverriderHaveAttribute(
|
||||
IMethodSymbol method,
|
||||
INamedTypeSymbol attributeSymbol)
|
||||
{
|
||||
var depth = 0;
|
||||
while (method.OverriddenMethod != null)
|
||||
{
|
||||
depth += 1;
|
||||
method = method.OverriddenMethod;
|
||||
if (GetAttribute(method, attributeSymbol) is not { } attribute)
|
||||
continue;
|
||||
|
||||
var onlyOverrides = attribute.ConstructorArguments is [{Kind: TypedConstantKind.Primitive, Value: true}];
|
||||
return (depth, onlyOverrides);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool HasBaseCall(MethodDeclarationSyntax syntax)
|
||||
{
|
||||
return syntax.Accept(new BaseCallLocator());
|
||||
}
|
||||
|
||||
private static AttributeData? GetAttribute(ISymbol namedTypeSymbol, INamedTypeSymbol attrSymbol)
|
||||
{
|
||||
return namedTypeSymbol.GetAttributes()
|
||||
.SingleOrDefault(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol));
|
||||
}
|
||||
|
||||
private sealed class BaseCallLocator : CSharpSyntaxVisitor<bool>
|
||||
{
|
||||
public override bool VisitBaseExpression(BaseExpressionSyntax node)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DefaultVisit(SyntaxNode node)
|
||||
{
|
||||
foreach (var childNode in node.ChildNodes())
|
||||
{
|
||||
if (childNode is not CSharpSyntaxNode cSharpSyntax)
|
||||
continue;
|
||||
|
||||
if (cSharpSyntax.Accept(this))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var type = Type.GetType(args[0]);
|
||||
var type = GetType(args[0]);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
@@ -25,6 +25,17 @@ namespace Robust.Client.Console.Commands
|
||||
shell.WriteLine(sig);
|
||||
}
|
||||
}
|
||||
|
||||
private Type? GetType(string name)
|
||||
{
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
if (assembly.GetType(name) is { } type)
|
||||
return type;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -76,7 +76,8 @@ namespace Robust.Client.GameObjects
|
||||
foreach (var key in remie)
|
||||
{
|
||||
component.PlayingAnimations.Remove(key);
|
||||
EntityManager.EventBus.RaiseLocalEvent(uid, new AnimationCompletedEvent {Uid = uid, Key = key}, true);
|
||||
var completedEvent = new AnimationCompletedEvent {Uid = uid, Key = key, Finished = true};
|
||||
EntityManager.EventBus.RaiseLocalEvent(uid, completedEvent, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -187,7 +188,8 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
EntityManager.EventBus.RaiseLocalEvent(entity.Owner, new AnimationCompletedEvent {Uid = entity.Owner, Key = key}, true);
|
||||
var completedEvent = new AnimationCompletedEvent {Uid = entity.Owner, Key = key, Finished = false};
|
||||
EntityManager.EventBus.RaiseLocalEvent(entity.Owner, completedEvent, true);
|
||||
}
|
||||
|
||||
public void Stop(EntityUid uid, AnimationPlayerComponent? component, string key)
|
||||
@@ -203,5 +205,11 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
public EntityUid Uid { get; init; }
|
||||
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(Robust.Client.GameObjects.AnimationPlayerComponent,string)"/> or similar overloads.
|
||||
/// </summary>
|
||||
public bool Finished { get; init; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +184,8 @@ namespace Robust.Client.GameObjects
|
||||
/// <summary>
|
||||
/// Gets the specified frame for this sprite at the specified time.
|
||||
/// </summary>
|
||||
public Texture GetFrame(SpriteSpecifier spriteSpec, TimeSpan curTime)
|
||||
/// <param name="loop">Should we clamp on the last frame and not loop</param>
|
||||
public Texture GetFrame(SpriteSpecifier spriteSpec, TimeSpan curTime, bool loop = true)
|
||||
{
|
||||
Texture? sprite = null;
|
||||
|
||||
@@ -196,19 +197,29 @@ namespace Robust.Client.GameObjects
|
||||
var frames = state!.GetFrames(RsiDirection.South);
|
||||
var delays = state.GetDelays();
|
||||
var totalDelay = delays.Sum();
|
||||
var time = curTime.TotalSeconds % totalDelay;
|
||||
var delaySum = 0f;
|
||||
|
||||
for (var i = 0; i < delays.Length; i++)
|
||||
// No looping
|
||||
if (!loop && curTime.TotalSeconds >= totalDelay)
|
||||
{
|
||||
var delay = delays[i];
|
||||
delaySum += delay;
|
||||
sprite = frames[^1];
|
||||
}
|
||||
// Loopable
|
||||
else
|
||||
{
|
||||
var time = curTime.TotalSeconds % totalDelay;
|
||||
var delaySum = 0f;
|
||||
|
||||
if (time > delaySum)
|
||||
continue;
|
||||
for (var i = 0; i < delays.Length; i++)
|
||||
{
|
||||
var delay = delays[i];
|
||||
delaySum += delay;
|
||||
|
||||
sprite = frames[i];
|
||||
break;
|
||||
if (time > delaySum)
|
||||
continue;
|
||||
|
||||
sprite = frames[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sprite ??= Frame0(spriteSpec);
|
||||
|
||||
@@ -162,9 +162,7 @@ public sealed partial class ReplayLoadManager
|
||||
}
|
||||
|
||||
using var stringFile = fileReader.Open(FileStrings);
|
||||
var stringData = new byte[stringFile.Length];
|
||||
stringFile.ReadExactly(stringData);
|
||||
_serializer.SetStringSerializerPackage(stringHash, stringData);
|
||||
_serializer.SetStringSerializerPackage(stringHash, stringFile.CopyToArray());
|
||||
|
||||
using var cvarsFile = fileReader.Open(FileCvars);
|
||||
// Note, this does not invoke the received-initial-cvars event. But at least currently, that doesn't matter
|
||||
|
||||
@@ -15,6 +15,36 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public Label Label { get; }
|
||||
public TextureRect TextureRect { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the checkbox be to the left or the right of the label.
|
||||
/// </summary>
|
||||
public bool LeftAlign
|
||||
{
|
||||
get => _leftAlign;
|
||||
set
|
||||
{
|
||||
if (_leftAlign == value)
|
||||
return;
|
||||
|
||||
_leftAlign = value;
|
||||
|
||||
if (value)
|
||||
{
|
||||
Label.HorizontalExpand = false;
|
||||
TextureRect.SetPositionFirst();
|
||||
Label.SetPositionInParent(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Label.HorizontalExpand = true;
|
||||
Label.SetPositionFirst();
|
||||
TextureRect.SetPositionInParent(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _leftAlign = true;
|
||||
|
||||
public CheckBox()
|
||||
{
|
||||
ToggleMode = true;
|
||||
@@ -31,10 +61,21 @@ namespace Robust.Client.UserInterface.Controls
|
||||
StyleClasses = { StyleClassCheckBox },
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
};
|
||||
hBox.AddChild(TextureRect);
|
||||
|
||||
Label = new Label();
|
||||
hBox.AddChild(Label);
|
||||
|
||||
if (LeftAlign)
|
||||
{
|
||||
Label.HorizontalExpand = false;
|
||||
hBox.AddChild(TextureRect);
|
||||
hBox.AddChild(Label);
|
||||
}
|
||||
else
|
||||
{
|
||||
Label.HorizontalExpand = true;
|
||||
hBox.AddChild(Label);
|
||||
hBox.AddChild(TextureRect);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DrawModeChanged()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Robust.Shared.Maths;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
@@ -15,9 +14,10 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public const string RightButtonStyle = "spinbox-right";
|
||||
public const string MiddleButtonStyle = "spinbox-middle";
|
||||
public LineEdit LineEditControl { get; }
|
||||
private List<Button> _leftButtons = new();
|
||||
private List<Button> _rightButtons = new();
|
||||
private List<SpinBoxButton> _leftButtons = new();
|
||||
private List<SpinBoxButton> _rightButtons = new();
|
||||
private int _stepSize = 1;
|
||||
private bool _buttonsDisabled;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the SpinBox value gets changed by the input text.
|
||||
@@ -30,12 +30,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
get => _value;
|
||||
set
|
||||
{
|
||||
if (IsValid != null && !IsValid(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
_value = value;
|
||||
LineEditControl.Text = value.ToString();
|
||||
OverrideValue(value);
|
||||
ValueChanged?.Invoke(new ValueChangedEventArgs(value));
|
||||
}
|
||||
}
|
||||
@@ -52,6 +47,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
return;
|
||||
}
|
||||
_value = value;
|
||||
UpdateButtonCanPress();
|
||||
LineEditControl.Text = value.ToString();
|
||||
}
|
||||
|
||||
@@ -87,6 +83,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
ClearButtons();
|
||||
AddLeftButton(-1, "-");
|
||||
AddRightButton(1, "+");
|
||||
UpdateButtonCanPress();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -94,8 +91,8 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// </summary>
|
||||
public void AddRightButton(int num, string text)
|
||||
{
|
||||
var button = new Button { Text = text };
|
||||
button.OnPressed += (args) => Value += num;
|
||||
var button = new SpinBoxButton(num) { Text = text };
|
||||
button.OnPressed += _ => Value += num;
|
||||
AddChild(button);
|
||||
button.AddStyleClass(RightButtonStyle);
|
||||
if (_rightButtons.Count > 0)
|
||||
@@ -111,8 +108,8 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// </summary>
|
||||
public void AddLeftButton(int num, string text)
|
||||
{
|
||||
var button = new Button { Text = text };
|
||||
button.OnPressed += (args) => Value += num;
|
||||
var button = new SpinBoxButton(num) { Text = text };
|
||||
button.OnPressed += _ => Value += num;
|
||||
AddChild(button);
|
||||
button.SetPositionInParent(_leftButtons.Count);
|
||||
button.AddStyleClass(_leftButtons.Count == 0 ? LeftButtonStyle : MiddleButtonStyle);
|
||||
@@ -162,6 +159,24 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
rightButton.Disabled = disabled;
|
||||
}
|
||||
|
||||
_buttonsDisabled = disabled;
|
||||
}
|
||||
|
||||
private void UpdateButtonCanPress()
|
||||
{
|
||||
if (IsValid == null)
|
||||
return;
|
||||
|
||||
foreach (var button in _leftButtons)
|
||||
{
|
||||
button.Disabled = !IsValid(_value + button.Value) || _buttonsDisabled;
|
||||
}
|
||||
|
||||
foreach (var button in _rightButtons)
|
||||
{
|
||||
button.Disabled = !IsValid(_value + button.Value) || _buttonsDisabled;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -195,6 +210,16 @@ namespace Robust.Client.UserInterface.Controls
|
||||
else if (args.Delta.Y < 0)
|
||||
Value -= _stepSize;
|
||||
}
|
||||
|
||||
private sealed class SpinBoxButton : Button
|
||||
{
|
||||
public readonly int Value;
|
||||
|
||||
public SpinBoxButton(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ValueChangedEventArgs : EventArgs
|
||||
|
||||
@@ -277,8 +277,21 @@ public sealed partial class DebugConsole
|
||||
CommandBar.CursorPosition = lastRange.end;
|
||||
CommandBar.SelectionStart = lastRange.start;
|
||||
var insertValue = CommandParsing.Escape(completion);
|
||||
|
||||
// If the replacement contains a space, we must quote it to treat it as a single argument.
|
||||
var mustQuote = insertValue.Contains(' ');
|
||||
if ((completionFlags & CompletionOptionFlags.PartialCompletion) == 0)
|
||||
{
|
||||
if (mustQuote)
|
||||
insertValue = $"\"{insertValue}\"";
|
||||
|
||||
insertValue += " ";
|
||||
}
|
||||
else if (mustQuote)
|
||||
{
|
||||
// If it's a partial completion, only quote the start.
|
||||
insertValue = '"' + insertValue;
|
||||
}
|
||||
|
||||
CommandBar.InsertAtCursor(insertValue);
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ public static class Diagnostics
|
||||
public const string IdDependencyFieldAssigned = "RA0025";
|
||||
public const string IdUncachedRegex = "RA0026";
|
||||
public const string IdDataFieldRedundantTag = "RA0027";
|
||||
public const string IdMustCallBase = "RA0028";
|
||||
|
||||
public static SuppressionDescriptor MeansImplicitAssignment =>
|
||||
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");
|
||||
|
||||
@@ -40,12 +40,6 @@ namespace Robust.Server.GameObjects
|
||||
EntityManager.EntityInitialized -= OnEntityInit;
|
||||
}
|
||||
|
||||
[Obsolete("Use Entity<T> variant")]
|
||||
public void AddLayer(EntityUid uid, VisibilityComponent component, int layer, bool refresh = true)
|
||||
{
|
||||
AddLayer((uid, component), (ushort)layer, refresh);
|
||||
}
|
||||
|
||||
public void AddLayer(Entity<VisibilityComponent?> ent, ushort layer, bool refresh = true)
|
||||
{
|
||||
ent.Comp ??= _visibilityQuery.CompOrNull(ent.Owner) ?? AddComp<VisibilityComponent>(ent.Owner);
|
||||
@@ -59,13 +53,6 @@ namespace Robust.Server.GameObjects
|
||||
RefreshVisibility(ent);
|
||||
}
|
||||
|
||||
|
||||
[Obsolete("Use Entity<T> variant")]
|
||||
public void RemoveLayer(EntityUid uid, VisibilityComponent component, int layer, bool refresh = true)
|
||||
{
|
||||
RemoveLayer((uid, component), (ushort)layer, refresh);
|
||||
}
|
||||
|
||||
public void RemoveLayer(Entity<VisibilityComponent?> ent, ushort layer, bool refresh = true)
|
||||
{
|
||||
if (!_visibilityQuery.Resolve(ent.Owner, ref ent.Comp, false))
|
||||
@@ -80,12 +67,6 @@ namespace Robust.Server.GameObjects
|
||||
RefreshVisibility(ent);
|
||||
}
|
||||
|
||||
[Obsolete("Use Entity<T> variant")]
|
||||
public void SetLayer(EntityUid uid, VisibilityComponent component, int layer, bool refresh = true)
|
||||
{
|
||||
SetLayer((uid, component), (ushort)layer, refresh);
|
||||
}
|
||||
|
||||
public void SetLayer(Entity<VisibilityComponent?> ent, ushort layer, bool refresh = true)
|
||||
{
|
||||
ent.Comp ??= _visibilityQuery.CompOrNull(ent.Owner) ?? AddComp<VisibilityComponent>(ent.Owner);
|
||||
|
||||
@@ -229,21 +229,6 @@ namespace Robust.Server.GameObjects
|
||||
|
||||
private void HandleEntityNetworkMessage(MsgEntity message)
|
||||
{
|
||||
var msgT = message.SourceTick;
|
||||
var cT = _gameTiming.CurTick;
|
||||
|
||||
if (msgT <= cT)
|
||||
{
|
||||
if (msgT < cT && _logLateMsgs)
|
||||
{
|
||||
_netEntSawmill.Warning("Got late MsgEntity! Diff: {0}, msgT: {2}, cT: {3}, player: {1}",
|
||||
(int) msgT.Value - (int) cT.Value, message.MsgChannel.UserName, msgT, cT);
|
||||
}
|
||||
|
||||
DispatchEntityNetworkMessage(message);
|
||||
return;
|
||||
}
|
||||
|
||||
_queue.Add(message);
|
||||
}
|
||||
|
||||
|
||||
@@ -80,14 +80,17 @@ internal sealed partial class PvsSystem
|
||||
// Update visibility masks & viewer positions
|
||||
// TODO PVS do this before sending state.
|
||||
// I,e, we already enumerate over all eyes when computing visible chunks.
|
||||
Span<MapCoordinates> positions = stackalloc MapCoordinates[session.Viewers.Length];
|
||||
Span<(MapCoordinates pos, float scale)> positions = stackalloc (MapCoordinates, float)[session.Viewers.Length];
|
||||
int i = 0;
|
||||
foreach (var viewer in session.Viewers)
|
||||
{
|
||||
if (viewer.Comp2 != null)
|
||||
session.VisMask |= viewer.Comp2.VisibilityMask;
|
||||
|
||||
positions[i++] = _transform.GetMapCoordinates(viewer.Owner, viewer.Comp1);
|
||||
var mapCoordinates = _transform.GetMapCoordinates(viewer.Owner, viewer.Comp1);
|
||||
mapCoordinates = mapCoordinates.Offset(viewer.Comp2?.Offset ?? Vector2.Zero);
|
||||
var scale = MathF.Max((viewer.Comp2?.PvsScale ?? 1), 0.1f);
|
||||
positions[i++] = (mapCoordinates, scale);
|
||||
}
|
||||
|
||||
if (!CullingEnabled || session.DisableCulling)
|
||||
@@ -112,7 +115,7 @@ internal sealed partial class PvsSystem
|
||||
DebugTools.Assert(!chunk.UpdateQueued);
|
||||
DebugTools.Assert(!chunk.Dirty);
|
||||
|
||||
foreach (var pos in positions)
|
||||
foreach (var (pos, scale) in positions)
|
||||
{
|
||||
if (pos.MapId != chunk.Position.MapId)
|
||||
continue;
|
||||
@@ -120,8 +123,9 @@ internal sealed partial class PvsSystem
|
||||
dist = Math.Min(dist, (pos.Position - chunk.Position.Position).LengthSquared());
|
||||
|
||||
var relative = Vector2.Transform(pos.Position, chunk.InvWorldMatrix) - chunk.Centre;
|
||||
|
||||
relative = Vector2.Abs(relative);
|
||||
chebDist = Math.Min(chebDist, Math.Max(relative.X, relative.Y));
|
||||
chebDist = Math.Min(chebDist, Math.Max(relative.X, relative.Y) / scale);
|
||||
}
|
||||
|
||||
distances.Add(dist);
|
||||
|
||||
@@ -362,8 +362,19 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
private (Vector2 worldPos, float range, EntityUid? map) CalcViewBounds(Entity<TransformComponent, EyeComponent?> eye)
|
||||
{
|
||||
var size = Math.Max(eye.Comp2?.PvsSize ?? _priorityViewSize, 1);
|
||||
return (_transform.GetWorldPosition(eye.Comp1), size / 2f, eye.Comp1.MapUid);
|
||||
var size = _priorityViewSize;
|
||||
var worldPos = _transform.GetWorldPosition(eye.Comp1);
|
||||
|
||||
if (eye.Comp2 is not null)
|
||||
{
|
||||
// not using EyeComponent.Eye.Position, because it's updated only on the client's side
|
||||
worldPos += eye.Comp2.Offset;
|
||||
size *= eye.Comp2.PvsScale;
|
||||
}
|
||||
|
||||
size = Math.Max(size, 1);
|
||||
|
||||
return (worldPos, size / 2f, eye.Comp1.MapUid);
|
||||
}
|
||||
|
||||
private void CullDeletionHistoryUntil(GameTick tick)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -35,11 +36,6 @@ using Robust.Shared.Utility;
|
||||
using SysVector3 = System.Numerics.Vector3;
|
||||
using SysVector4 = System.Numerics.Vector4;
|
||||
|
||||
#if NETCOREAPP
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
#endif
|
||||
|
||||
namespace Robust.Shared.Maths
|
||||
{
|
||||
/// <summary>
|
||||
@@ -50,37 +46,43 @@ namespace Robust.Shared.Maths
|
||||
public struct Color : IEquatable<Color>, ISpanFormattable
|
||||
{
|
||||
/// <summary>
|
||||
/// The red component of this Color4 structure.
|
||||
/// The red component of this Color structure.
|
||||
/// </summary>
|
||||
public float R;
|
||||
|
||||
/// <summary>
|
||||
/// The green component of this Color4 structure.
|
||||
/// The green component of this Color structure.
|
||||
/// </summary>
|
||||
public float G;
|
||||
|
||||
/// <summary>
|
||||
/// The blue component of this Color4 structure.
|
||||
/// The blue component of this Color structure.
|
||||
/// </summary>
|
||||
public float B;
|
||||
|
||||
/// <summary>
|
||||
/// The alpha component of this Color4 structure.
|
||||
/// The alpha component of this Color structure.
|
||||
/// </summary>
|
||||
public float A;
|
||||
|
||||
/// <summary>
|
||||
/// Vector representation, for easy SIMD operations.
|
||||
/// </summary>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public readonly SysVector4 RGBA => Unsafe.BitCast<Color, SysVector4>(this);
|
||||
|
||||
public readonly byte RByte => (byte) (R * byte.MaxValue);
|
||||
public readonly byte GByte => (byte) (G * byte.MaxValue);
|
||||
public readonly byte BByte => (byte) (B * byte.MaxValue);
|
||||
public readonly byte AByte => (byte) (A * byte.MaxValue);
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new Color4 structure from the specified components.
|
||||
/// Constructs a new <see cref="Color"/> structure from the specified components.
|
||||
/// </summary>
|
||||
/// <param name="r">The red component of the new Color4 structure.</param>
|
||||
/// <param name="g">The green component of the new Color4 structure.</param>
|
||||
/// <param name="b">The blue component of the new Color4 structure.</param>
|
||||
/// <param name="a">The alpha component of the new Color4 structure.</param>
|
||||
/// <param name="r">The red component of the new Color structure.</param>
|
||||
/// <param name="g">The green component of the new Color structure.</param>
|
||||
/// <param name="b">The blue component of the new Color structure.</param>
|
||||
/// <param name="a">The alpha component of the new Color structure.</param>
|
||||
public Color(float r, float g, float b, float a = 1)
|
||||
{
|
||||
R = r;
|
||||
@@ -90,14 +92,23 @@ namespace Robust.Shared.Maths
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new Color4 structure from the specified components.
|
||||
/// Constructs a new Color structure from the components in a <see cref="SysVector4"/>.
|
||||
/// </summary>
|
||||
/// <param name="r">The red component of the new Color4 structure.</param>
|
||||
/// <param name="g">The green component of the new Color4 structure.</param>
|
||||
/// <param name="b">The blue component of the new Color4 structure.</param>
|
||||
/// <param name="a">The alpha component of the new Color4 structure.</param>
|
||||
public Color(in SysVector4 vec)
|
||||
{
|
||||
this = Unsafe.BitCast<SysVector4, Color>(vec);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new Color structure from the specified components.
|
||||
/// </summary>
|
||||
/// <param name="r">The red component of the new Color structure.</param>
|
||||
/// <param name="g">The green component of the new Color structure.</param>
|
||||
/// <param name="b">The blue component of the new Color structure.</param>
|
||||
/// <param name="a">The alpha component of the new Color structure.</param>
|
||||
public Color(byte r, byte g, byte b, byte a = 255)
|
||||
{
|
||||
Unsafe.SkipInit(out this);
|
||||
R = r / (float) byte.MaxValue;
|
||||
G = g / (float) byte.MaxValue;
|
||||
B = b / (float) byte.MaxValue;
|
||||
@@ -124,7 +135,7 @@ namespace Robust.Shared.Maths
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the specified Color4 structures for equality.
|
||||
/// Compares the specified Color structures for equality.
|
||||
/// </summary>
|
||||
/// <param name="left">The left-hand side of the comparison.</param>
|
||||
/// <param name="right">The right-hand side of the comparison.</param>
|
||||
@@ -135,7 +146,7 @@ namespace Robust.Shared.Maths
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the specified Color4 structures for inequality.
|
||||
/// Compares the specified Color structures for inequality.
|
||||
/// </summary>
|
||||
/// <param name="left">The left-hand side of the comparison.</param>
|
||||
/// <param name="right">The right-hand side of the comparison.</param>
|
||||
@@ -146,10 +157,10 @@ namespace Robust.Shared.Maths
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified System.Drawing.Color to a Color4 structure.
|
||||
/// Converts the specified System.Drawing.Color to a Color structure.
|
||||
/// </summary>
|
||||
/// <param name="color">The System.Drawing.Color to convert.</param>
|
||||
/// <returns>A new Color4 structure containing the converted components.</returns>
|
||||
/// <returns>A new Color structure containing the converted components.</returns>
|
||||
public static implicit operator Color(System.Drawing.Color color)
|
||||
{
|
||||
return new(color.R, color.G, color.B, color.A);
|
||||
@@ -181,9 +192,9 @@ namespace Robust.Shared.Maths
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified Color4 to a System.Drawing.Color structure.
|
||||
/// Converts the specified Color to a System.Drawing.Color structure.
|
||||
/// </summary>
|
||||
/// <param name="color">The Color4 to convert.</param>
|
||||
/// <param name="color">The Color to convert.</param>
|
||||
/// <returns>A new System.Drawing.Color structure containing the converted components.</returns>
|
||||
public static explicit operator System.Drawing.Color(Color color)
|
||||
{
|
||||
@@ -210,11 +221,11 @@ namespace Robust.Shared.Maths
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares whether this Color4 structure is equal to the specified object.
|
||||
/// Compares whether this Color structure is equal to the specified object.
|
||||
/// </summary>
|
||||
/// <param name="obj">An object to compare to.</param>
|
||||
/// <returns>True obj is a Color4 structure with the same components as this Color4; false otherwise.</returns>
|
||||
public override readonly bool Equals(object? obj)
|
||||
/// <returns>True obj is a Color structure with the same components as this Color; false otherwise.</returns>
|
||||
public readonly override bool Equals(object? obj)
|
||||
{
|
||||
if (!(obj is Color))
|
||||
return false;
|
||||
@@ -223,19 +234,19 @@ namespace Robust.Shared.Maths
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the hash code for this Color4 structure.
|
||||
/// Calculates the hash code for this Color structure.
|
||||
/// </summary>
|
||||
/// <returns>A System.Int32 containing the hash code of this Color4 structure.</returns>
|
||||
public override readonly int GetHashCode()
|
||||
/// <returns>A System.Int32 containing the hash code of this Color structure.</returns>
|
||||
public readonly override int GetHashCode()
|
||||
{
|
||||
return ToArgb();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a System.String that describes this Color4 structure.
|
||||
/// Creates a System.String that describes this Color structure.
|
||||
/// </summary>
|
||||
/// <returns>A System.String that describes this Color4 structure.</returns>
|
||||
public override readonly string ToString()
|
||||
/// <returns>A System.String that describes this Color structure.</returns>
|
||||
public readonly override string ToString()
|
||||
{
|
||||
return $"{{(R, G, B, A) = ({R}, {G}, {B}, {A})}}";
|
||||
}
|
||||
@@ -309,7 +320,6 @@ namespace Robust.Shared.Maths
|
||||
public static Color FromSrgb(Color srgb)
|
||||
{
|
||||
float r, g, b;
|
||||
#if NETCOREAPP
|
||||
if (srgb.R <= 0.04045f)
|
||||
r = srgb.R / 12.92f;
|
||||
else
|
||||
@@ -324,22 +334,6 @@ namespace Robust.Shared.Maths
|
||||
b = srgb.B / 12.92f;
|
||||
else
|
||||
b = MathF.Pow((srgb.B + 0.055f) / (1.0f + 0.055f), 2.4f);
|
||||
#else
|
||||
if (srgb.R <= 0.04045f)
|
||||
r = srgb.R / 12.92f;
|
||||
else
|
||||
r = (float) Math.Pow((srgb.R + 0.055f) / (1.0f + 0.055f), 2.4f);
|
||||
|
||||
if (srgb.G <= 0.04045f)
|
||||
g = srgb.G / 12.92f;
|
||||
else
|
||||
g = (float) Math.Pow((srgb.G + 0.055f) / (1.0f + 0.055f), 2.4f);
|
||||
|
||||
if (srgb.B <= 0.04045f)
|
||||
b = srgb.B / 12.92f;
|
||||
else
|
||||
b = (float) Math.Pow((srgb.B + 0.055f) / (1.0f + 0.055f), 2.4f);
|
||||
#endif
|
||||
|
||||
return new Color(r, g, b, srgb.A);
|
||||
}
|
||||
@@ -355,7 +349,6 @@ namespace Robust.Shared.Maths
|
||||
{
|
||||
float r, g, b;
|
||||
|
||||
#if NETCOREAPP
|
||||
if (rgb.R <= 0.0031308)
|
||||
r = 12.92f * rgb.R;
|
||||
else
|
||||
@@ -370,22 +363,6 @@ namespace Robust.Shared.Maths
|
||||
b = 12.92f * rgb.B;
|
||||
else
|
||||
b = (1.0f + 0.055f) * MathF.Pow(rgb.B, 1.0f / 2.4f) - 0.055f;
|
||||
#else
|
||||
if (rgb.R <= 0.0031308)
|
||||
r = 12.92f * rgb.R;
|
||||
else
|
||||
r = (1.0f + 0.055f) * (float) Math.Pow(rgb.R, 1.0f / 2.4f) - 0.055f;
|
||||
|
||||
if (rgb.G <= 0.0031308)
|
||||
g = 12.92f * rgb.G;
|
||||
else
|
||||
g = (1.0f + 0.055f) * (float) Math.Pow(rgb.G, 1.0f / 2.4f) - 0.055f;
|
||||
|
||||
if (rgb.B <= 0.0031308)
|
||||
b = 12.92f * rgb.B;
|
||||
else
|
||||
b = (1.0f + 0.055f) * (float) Math.Pow(rgb.B, 1.0f / 2.4f) - 0.055f;
|
||||
#endif
|
||||
|
||||
return new Color(r, g, b, rgb.A);
|
||||
}
|
||||
@@ -471,6 +448,7 @@ namespace Robust.Shared.Maths
|
||||
/// Each has a range of 0.0 to 1.0.
|
||||
/// </returns>
|
||||
/// <param name="rgb">Color value to convert.</param>
|
||||
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")]
|
||||
public static Vector4 ToHsl(Color rgb)
|
||||
{
|
||||
var max = MathF.Max(rgb.R, MathF.Max(rgb.G, rgb.B));
|
||||
@@ -582,6 +560,7 @@ namespace Robust.Shared.Maths
|
||||
/// Each has a range of 0.0 to 1.0.
|
||||
/// </returns>
|
||||
/// <param name="rgb">Color value to convert.</param>
|
||||
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")]
|
||||
public static Vector4 ToHsv(Color rgb)
|
||||
{
|
||||
var max = MathF.Max(rgb.R, MathF.Max(rgb.G, rgb.B));
|
||||
@@ -770,6 +749,7 @@ namespace Robust.Shared.Maths
|
||||
/// Each has a range of 0.0 to 1.0.
|
||||
/// </returns>
|
||||
/// <param name="rgb">Color value to convert.</param>
|
||||
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")]
|
||||
public static Vector4 ToHcy(Color rgb)
|
||||
{
|
||||
var max = MathF.Max(rgb.R, MathF.Max(rgb.G, rgb.B));
|
||||
@@ -828,23 +808,10 @@ namespace Robust.Shared.Maths
|
||||
/// with 0.5 being 50% of both colors, 0.25 being 25% of <paramref name="β" /> and 75%
|
||||
/// <paramref name="α" />.
|
||||
/// </param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Color InterpolateBetween(Color α, Color β, float λ)
|
||||
{
|
||||
if (Sse.IsSupported && Fma.IsSupported)
|
||||
{
|
||||
var vecA = Unsafe.As<Color, Vector128<float>>(ref α);
|
||||
var vecB = Unsafe.As<Color, Vector128<float>>(ref β);
|
||||
|
||||
vecB = Fma.MultiplyAdd(Sse.Subtract(vecB, vecA), Vector128.Create(λ), vecA);
|
||||
|
||||
return Unsafe.As<Vector128<float>, Color>(ref vecB);
|
||||
}
|
||||
ref var svA = ref Unsafe.As<Color, SysVector4>(ref α);
|
||||
ref var svB = ref Unsafe.As<Color, SysVector4>(ref β);
|
||||
|
||||
var res = SysVector4.Lerp(svA, svB, λ);
|
||||
|
||||
return Unsafe.As<SysVector4, Color>(ref res);
|
||||
return new(SysVector4.Lerp(α.RGBA, β.RGBA, λ));
|
||||
}
|
||||
|
||||
public static Color? TryFromHex(ReadOnlySpan<char> hexColor)
|
||||
@@ -1000,13 +967,8 @@ namespace Robust.Shared.Maths
|
||||
/// <summary>
|
||||
/// Component wise multiplication of two colors.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static Color operator *(Color a, Color b)
|
||||
{
|
||||
return new(a.R * b.R, a.G * b.G, a.B * b.B, a.A * b.A);
|
||||
}
|
||||
public static Color operator *(in Color a, in Color b)
|
||||
=> new(a.RGBA * b.RGBA);
|
||||
|
||||
public readonly string ToHex()
|
||||
{
|
||||
@@ -1030,17 +992,16 @@ namespace Robust.Shared.Maths
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares whether this Color4 structure is equal to the specified Color4.
|
||||
/// Compares whether this Color structure is equal to the specified Color.
|
||||
/// </summary>
|
||||
/// <param name="other">The Color4 structure to compare to.</param>
|
||||
/// <returns>True if both Color4 structures contain the same components; false otherwise.</returns>
|
||||
/// <param name="other">The Color structure to compare to.</param>
|
||||
/// <returns>True if both Color structures contain the same components; false otherwise.</returns>
|
||||
public readonly bool Equals(Color other)
|
||||
{
|
||||
return
|
||||
MathHelper.CloseToPercent(R, other.R) &&
|
||||
MathHelper.CloseToPercent(G, other.G) &&
|
||||
MathHelper.CloseToPercent(B, other.B) &&
|
||||
MathHelper.CloseToPercent(A, other.A);
|
||||
// TODO COLOR why is this approximate
|
||||
// This method literally doesn't do what its docstring says it does.
|
||||
// If people wanted approximate equality, they can check that manually.
|
||||
return MathHelper.CloseToPercent(this, other);
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
@@ -1942,7 +1903,7 @@ namespace Robust.Shared.Maths
|
||||
|
||||
public readonly string? Name()
|
||||
{
|
||||
return DefaultColorsInverted.TryGetValue(this, out var name) ? name : null;
|
||||
return DefaultColorsInverted.GetValueOrDefault(this);
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, out Color color)
|
||||
|
||||
@@ -15,6 +15,7 @@ using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Vec4 = System.Numerics.Vector4;
|
||||
|
||||
namespace Robust.Shared.Maths
|
||||
{
|
||||
@@ -525,6 +526,27 @@ namespace Robust.Shared.Maths
|
||||
return Math.Abs(a - b) <= epsilon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether two vectors are within <paramref name="percentage"/> of each other
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool CloseToPercent(Vec4 a, Vec4 b, float percentage = .00001f)
|
||||
{
|
||||
a = Vec4.Abs(a);
|
||||
b = Vec4.Abs(b);
|
||||
var p = new Vec4(percentage);
|
||||
var epsilon = Vec4.Max(Vec4.Max(a, b) * p, p);
|
||||
var delta = Vec4.Abs(a - b);
|
||||
return delta.X <= epsilon.X && delta.Y <= epsilon.Y && delta.Z <= epsilon.Z && delta.W <= epsilon.W;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether two colours are within <paramref name="percentage"/> of each other
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool CloseToPercent(Color a, Color b, float percentage = .00001f)
|
||||
=> CloseToPercent(a.RGBA, b.RGBA, percentage);
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether two floating point numbers are within <paramref name="percentage"/> of eachother
|
||||
/// </summary>
|
||||
|
||||
17
Robust.Shared/Analyzers/MustCallBaseAttribute.cs
Normal file
17
Robust.Shared/Analyzers/MustCallBaseAttribute.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace Robust.Shared.Analyzers;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that overriders of this method must always call the base function.
|
||||
/// </summary>
|
||||
/// <param name="onlyOverrides">
|
||||
/// If true, only base calls to *overrides* are necessary.
|
||||
/// This is intended for base classes where the base function is always empty,
|
||||
/// so a base call from the first override may be ommitted.
|
||||
/// </param>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class MustCallBaseAttribute(bool onlyOverrides = false) : Attribute
|
||||
{
|
||||
public bool OnlyOverrides { get; } = onlyOverrides;
|
||||
}
|
||||
@@ -9,18 +9,17 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Console.Commands;
|
||||
|
||||
internal sealed class TeleportCommand : LocalizedCommands
|
||||
internal sealed class TeleportCommand : LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
public override string Command => "tp";
|
||||
public override bool RequireServerOrSingleplayer => true;
|
||||
@@ -36,11 +35,10 @@ internal sealed class TeleportCommand : LocalizedCommands
|
||||
return;
|
||||
}
|
||||
|
||||
var xformSystem = _entitySystem.GetEntitySystem<SharedTransformSystem>();
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(entity);
|
||||
var position = new Vector2(posX, posY);
|
||||
|
||||
xformSystem.AttachToGridOrMap(entity, transform);
|
||||
_transform.AttachToGridOrMap(entity, transform);
|
||||
|
||||
MapId mapId;
|
||||
if (args.Length == 3 && int.TryParse(args[2], out var intMapId))
|
||||
@@ -56,25 +54,26 @@ internal sealed class TeleportCommand : LocalizedCommands
|
||||
|
||||
if (_map.TryFindGridAt(mapId, position, out var gridUid, out var grid))
|
||||
{
|
||||
var gridPos = Vector2.Transform(position, xformSystem.GetInvWorldMatrix(gridUid));
|
||||
var gridPos = Vector2.Transform(position, _transform.GetInvWorldMatrix(gridUid));
|
||||
|
||||
xformSystem.SetCoordinates(entity, transform, new EntityCoordinates(gridUid, gridPos));
|
||||
_transform.SetCoordinates(entity, transform, new EntityCoordinates(gridUid, gridPos));
|
||||
}
|
||||
else
|
||||
{
|
||||
var mapEnt = _map.GetMapEntityIdOrThrow(mapId);
|
||||
xformSystem.SetWorldPosition(transform, position);
|
||||
xformSystem.SetParent(entity, transform, mapEnt);
|
||||
_transform.SetWorldPosition(transform, position);
|
||||
_transform.SetParent(entity, transform, mapEnt);
|
||||
}
|
||||
|
||||
shell.WriteLine($"Teleported {shell.Player} to {mapId}:{posX},{posY}.");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TeleportToCommand : LocalizedCommands
|
||||
public sealed class TeleportToCommand : LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly ISharedPlayerManager _players = default!;
|
||||
[Dependency] private readonly IEntityManager _entities = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
public override string Command => "tpto";
|
||||
public override bool RequireServerOrSingleplayer => true;
|
||||
@@ -89,7 +88,6 @@ public sealed class TeleportToCommand : LocalizedCommands
|
||||
if (!TryGetTransformFromUidOrUsername(target, shell, out var targetUid, out _))
|
||||
return;
|
||||
|
||||
var transformSystem = _entities.System<SharedTransformSystem>();
|
||||
var targetCoords = new EntityCoordinates(targetUid.Value, Vector2.Zero);
|
||||
|
||||
if (_entities.TryGetComponent(targetUid, out PhysicsComponent? targetPhysics))
|
||||
@@ -127,8 +125,8 @@ public sealed class TeleportToCommand : LocalizedCommands
|
||||
|
||||
foreach (var victim in victims)
|
||||
{
|
||||
transformSystem.SetCoordinates(victim.Entity, targetCoords);
|
||||
transformSystem.AttachToGridOrMap(victim.Entity, victim.Transform);
|
||||
_transform.SetCoordinates(victim.Entity, targetCoords);
|
||||
_transform.AttachToGridOrMap(victim.Entity, victim.Transform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,9 +176,10 @@ public sealed class TeleportToCommand : LocalizedCommands
|
||||
}
|
||||
}
|
||||
|
||||
sealed class LocationCommand : LocalizedCommands
|
||||
sealed class LocationCommand : LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _ent = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
public override string Command => "loc";
|
||||
|
||||
@@ -192,18 +191,19 @@ sealed class LocationCommand : LocalizedCommands
|
||||
var pt = _ent.GetComponent<TransformComponent>(entity);
|
||||
var pos = pt.Coordinates;
|
||||
|
||||
shell.WriteLine($"MapID:{pos.GetMapId(_ent)} GridUid:{pos.GetGridUid(_ent)} X:{pos.X:N2} Y:{pos.Y:N2}");
|
||||
var mapId = _transform.GetMapId(pos);
|
||||
var gridUid = _transform.GetGrid(pos);
|
||||
|
||||
shell.WriteLine($"MapID:{mapId} GridUid:{gridUid} X:{pos.X:N2} Y:{pos.Y:N2}");
|
||||
}
|
||||
}
|
||||
|
||||
sealed class TpGridCommand : LocalizedCommands
|
||||
sealed class TpGridCommand : LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _ent = default!;
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
|
||||
public override string Command => "tpgrid";
|
||||
public override string Description => Loc.GetString("cmd-tpgrid-desc");
|
||||
public override string Help => Loc.GetString("cmd-tpgrid-help");
|
||||
public override bool RequireServerOrSingleplayer => true;
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
@@ -246,14 +246,14 @@ sealed class TpGridCommand : LocalizedCommands
|
||||
mapId = new MapId(map);
|
||||
}
|
||||
|
||||
var id = _map.GetMapEntityId(mapId);
|
||||
var id = _map.GetMap(mapId);
|
||||
if (id == EntityUid.Invalid)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-parse-failure-mapid", ("arg", mapId.Value)));
|
||||
return;
|
||||
}
|
||||
|
||||
var pos = new EntityCoordinates(_map.GetMapEntityId(mapId), new Vector2(xPos, yPos));
|
||||
var pos = new EntityCoordinates(id, new Vector2(xPos, yPos));
|
||||
_ent.System<SharedTransformSystem>().SetCoordinates(uid.Value, pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ namespace Robust.Shared.Console
|
||||
[Dependency] protected readonly ILocalizationManager LocalizationManager = default!;
|
||||
|
||||
[ViewVariables] protected readonly Dictionary<string, IConsoleCommand> RegisteredCommands = new();
|
||||
[ViewVariables] private readonly HashSet<string> _autoRegisteredCommands = [];
|
||||
|
||||
private bool _isInRegistrationRegion;
|
||||
|
||||
private readonly CommandBuffer _commandBuffer = new CommandBuffer();
|
||||
|
||||
@@ -61,6 +64,11 @@ namespace Robust.Shared.Console
|
||||
// search for all client commands in all assemblies, and register them
|
||||
foreach (var type in ReflectionManager.GetAllChildren<IConsoleCommand>())
|
||||
{
|
||||
// This sucks but I can't come up with anything better
|
||||
// that won't just be 10x worse complexity for no gain.
|
||||
if (type.IsAssignableTo(typeof(IEntityConsoleCommand)))
|
||||
continue;
|
||||
|
||||
var instance = (IConsoleCommand)_typeFactory.CreateInstanceUnchecked(type, true);
|
||||
if (AvailableCommands.TryGetValue(instance.Command, out var duplicate))
|
||||
{
|
||||
@@ -69,6 +77,7 @@ namespace Robust.Shared.Console
|
||||
}
|
||||
|
||||
RegisteredCommands[instance.Command] = instance;
|
||||
_autoRegisteredCommands.Add(instance.Command);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +85,23 @@ namespace Robust.Shared.Console
|
||||
{
|
||||
}
|
||||
|
||||
public void BeginRegistrationRegion()
|
||||
{
|
||||
if (_isInRegistrationRegion)
|
||||
throw new InvalidOperationException("Cannot enter registration region twice!");
|
||||
|
||||
_isInRegistrationRegion = true;
|
||||
}
|
||||
|
||||
public void EndRegistrationRegion()
|
||||
{
|
||||
if (!_isInRegistrationRegion)
|
||||
throw new InvalidOperationException("Was not in registration region.");
|
||||
|
||||
_isInRegistrationRegion = false;
|
||||
UpdateAvailableCommands();
|
||||
}
|
||||
|
||||
#region RegisterCommand
|
||||
public void RegisterCommand(
|
||||
string command,
|
||||
@@ -88,8 +114,7 @@ namespace Robust.Shared.Console
|
||||
throw new InvalidOperationException($"Command already registered: {command}");
|
||||
|
||||
var newCmd = new RegisteredCommand(command, description, help, callback, requireServerOrSingleplayer);
|
||||
RegisteredCommands.Add(command, newCmd);
|
||||
UpdateAvailableCommands();
|
||||
RegisterCommand(newCmd);
|
||||
}
|
||||
|
||||
public void RegisterCommand(
|
||||
@@ -104,8 +129,7 @@ namespace Robust.Shared.Console
|
||||
throw new InvalidOperationException($"Command already registered: {command}");
|
||||
|
||||
var newCmd = new RegisteredCommand(command, description, help, callback, completionCallback, requireServerOrSingleplayer);
|
||||
RegisteredCommands.Add(command, newCmd);
|
||||
UpdateAvailableCommands();
|
||||
RegisterCommand(newCmd);
|
||||
}
|
||||
|
||||
public void RegisterCommand(
|
||||
@@ -120,8 +144,7 @@ namespace Robust.Shared.Console
|
||||
throw new InvalidOperationException($"Command already registered: {command}");
|
||||
|
||||
var newCmd = new RegisteredCommand(command, description, help, callback, completionCallback, requireServerOrSingleplayer);
|
||||
RegisteredCommands.Add(command, newCmd);
|
||||
UpdateAvailableCommands();
|
||||
RegisterCommand(newCmd);
|
||||
}
|
||||
|
||||
public void RegisterCommand(string command, ConCommandCallback callback,
|
||||
@@ -153,6 +176,15 @@ namespace Robust.Shared.Console
|
||||
var help = LocalizationManager.TryGetString($"cmd-{command}-help", out var val) ? val : "";
|
||||
RegisterCommand(command, description, help, callback, completionCallback, requireServerOrSingleplayer);
|
||||
}
|
||||
|
||||
public void RegisterCommand(IConsoleCommand command)
|
||||
{
|
||||
RegisteredCommands.Add(command.Command, command);
|
||||
|
||||
if (!_isInRegistrationRegion)
|
||||
UpdateAvailableCommands();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -161,12 +193,14 @@ namespace Robust.Shared.Console
|
||||
if (!RegisteredCommands.TryGetValue(command, out var cmd))
|
||||
throw new KeyNotFoundException($"Command {command} is not registered.");
|
||||
|
||||
if (cmd is not RegisteredCommand)
|
||||
if (_autoRegisteredCommands.Contains(command))
|
||||
throw new InvalidOperationException(
|
||||
"You cannot unregister commands that have been registered automatically.");
|
||||
|
||||
RegisteredCommands.Remove(command);
|
||||
UpdateAvailableCommands();
|
||||
|
||||
if (!_isInRegistrationRegion)
|
||||
UpdateAvailableCommands();
|
||||
}
|
||||
|
||||
//TODO: Pull up
|
||||
|
||||
54
Robust.Shared/Console/EntityConsoleHost.cs
Normal file
54
Robust.Shared/Console/EntityConsoleHost.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Console;
|
||||
|
||||
/// <summary>
|
||||
/// Manages registration for "entity" console commands.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See <see cref="LocalizedEntityCommands"/> for details on what "entity" console commands are.
|
||||
/// </remarks>
|
||||
internal sealed class EntityConsoleHost
|
||||
{
|
||||
[Dependency] private readonly IConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
|
||||
private readonly HashSet<string> _entityCommands = [];
|
||||
|
||||
public void Startup()
|
||||
{
|
||||
DebugTools.Assert(_entityCommands.Count == 0);
|
||||
|
||||
var deps = ((EntitySystemManager)_entitySystemManager).SystemDependencyCollection;
|
||||
|
||||
_consoleHost.BeginRegistrationRegion();
|
||||
|
||||
// search for all client commands in all assemblies, and register them
|
||||
foreach (var type in _reflectionManager.GetAllChildren<IEntityConsoleCommand>())
|
||||
{
|
||||
var instance = (IConsoleCommand)Activator.CreateInstance(type)!;
|
||||
deps.InjectDependencies(instance, oneOff: true);
|
||||
|
||||
_entityCommands.Add(instance.Command);
|
||||
_consoleHost.RegisterCommand(instance);
|
||||
}
|
||||
|
||||
_consoleHost.EndRegistrationRegion();
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
foreach (var command in _entityCommands)
|
||||
{
|
||||
_consoleHost.UnregisterCommand(command);
|
||||
}
|
||||
|
||||
_entityCommands.Clear();
|
||||
}
|
||||
}
|
||||
@@ -83,4 +83,11 @@ namespace Robust.Shared.Console
|
||||
return ValueTask.FromResult(GetCompletion(shell, args));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Special marker interface used to indicate "entity" commands.
|
||||
/// See <see cref="LocalizedEntityCommands"/> for an overview.
|
||||
/// </summary>
|
||||
/// <seealso cref="EntityConsoleHost"/>
|
||||
internal interface IEntityConsoleCommand : IConsoleCommand;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Console
|
||||
@@ -173,6 +174,33 @@ namespace Robust.Shared.Console
|
||||
ConCommandCallback callback,
|
||||
ConCommandCompletionAsyncCallback completionCallback,
|
||||
bool requireServerOrSingleplayer = false);
|
||||
|
||||
/// <summary>
|
||||
/// Register an existing console command instance directly.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For this to be useful, the command has to be somehow excluded from automatic registration,
|
||||
/// such as by using the <see cref="ReflectAttribute"/>.
|
||||
/// </remarks>
|
||||
/// <param name="command">The command to register.</param>
|
||||
/// <seealso cref="BeginRegistrationRegion"/>
|
||||
void RegisterCommand(IConsoleCommand command);
|
||||
|
||||
/// <summary>
|
||||
/// Begin a region for registering many console commands in one go.
|
||||
/// The region can be ended with <see cref="EndRegistrationRegion"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Commands registered inside this region temporarily suppress some updating
|
||||
/// logic that would cause significant wasted work. This logic runs when the region is ended instead.
|
||||
/// </remarks>
|
||||
void BeginRegistrationRegion();
|
||||
|
||||
/// <summary>
|
||||
/// End a registration region started with <see cref="BeginRegistrationRegion"/>.
|
||||
/// </summary>
|
||||
void EndRegistrationRegion();
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
@@ -34,3 +35,21 @@ public abstract class LocalizedCommands : IConsoleCommand
|
||||
return ValueTask.FromResult(GetCompletion(shell, args));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for localized console commands that run in "entity space".
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This type of command is registered only while the entity system is active.
|
||||
/// On the client this means that the commands are only available while connected to a server or in single player.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// These commands are allowed to take dependencies on entity systems, reducing boilerplate for many usages.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public abstract class LocalizedEntityCommands : LocalizedCommands, IEntityConsoleCommand
|
||||
{
|
||||
[Dependency]
|
||||
protected readonly EntityManager EntityManager = default!;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Robust.Shared.ContentPack
|
||||
String("short").ThenReturn(PrimitiveTypeCode.Int16);
|
||||
|
||||
private static readonly Parser<char, PrimitiveTypeCode> UInt16TypeParser =
|
||||
String("ushort").ThenReturn(PrimitiveTypeCode.UInt32);
|
||||
String("ushort").ThenReturn(PrimitiveTypeCode.UInt16);
|
||||
|
||||
private static readonly Parser<char, PrimitiveTypeCode> Int32TypeParser =
|
||||
String("int").ThenReturn(PrimitiveTypeCode.Int32);
|
||||
|
||||
@@ -84,12 +84,146 @@ Types:
|
||||
- "bool get_HasContents()"
|
||||
Lidgren.Network:
|
||||
NetBuffer:
|
||||
All: True
|
||||
Methods:
|
||||
- "byte[] get_Data()"
|
||||
- "void set_Data(byte[])"
|
||||
- "int get_LengthBytes()"
|
||||
- "void set_LengthBytes(int)"
|
||||
- "int get_LengthBits()"
|
||||
- "void set_LengthBits(int)"
|
||||
- "long get_Position()"
|
||||
- "void set_Position(long)"
|
||||
- "int get_PositionInBytes()"
|
||||
- "byte[] PeekDataBuffer()"
|
||||
- "bool PeekBoolean()"
|
||||
- "byte PeekByte()"
|
||||
- "sbyte PeekSByte()"
|
||||
- "byte PeekByte(int)"
|
||||
- "System.Span`1<byte> PeekBytes(System.Span`1<byte>)"
|
||||
- "byte[] PeekBytes(int)"
|
||||
- "void PeekBytes(byte[], int, int)"
|
||||
- "short PeekInt16()"
|
||||
- "ushort PeekUInt16()"
|
||||
- "int PeekInt32()"
|
||||
- "int PeekInt32(int)"
|
||||
- "uint PeekUInt32()"
|
||||
- "uint PeekUInt32(int)"
|
||||
- "ulong PeekUInt64()"
|
||||
- "long PeekInt64()"
|
||||
- "ulong PeekUInt64(int)"
|
||||
- "long PeekInt64(int)"
|
||||
- "float PeekFloat()"
|
||||
- "System.Half PeekHalf()"
|
||||
- "float PeekSingle()"
|
||||
- "double PeekDouble()"
|
||||
- "string PeekString()"
|
||||
- "int PeekStringSize()"
|
||||
- "bool ReadBoolean()"
|
||||
- "byte ReadByte()"
|
||||
- "bool ReadByte(ref byte)"
|
||||
- "sbyte ReadSByte()"
|
||||
- "byte ReadByte(int)"
|
||||
- "System.Span`1<byte> ReadBytes(System.Span`1<byte>)"
|
||||
- "byte[] ReadBytes(int)"
|
||||
- "bool ReadBytes(int, ref byte[])"
|
||||
- "bool TryReadBytes(System.Span`1<byte>)"
|
||||
- "void ReadBytes(byte[], int, int)"
|
||||
- "void ReadBits(System.Span`1<byte>, int)"
|
||||
- "void ReadBits(byte[], int, int)"
|
||||
- "short ReadInt16()"
|
||||
- "ushort ReadUInt16()"
|
||||
- "int ReadInt32()"
|
||||
- "bool ReadInt32(ref int)"
|
||||
- "int ReadInt32(int)"
|
||||
- "uint ReadUInt32()"
|
||||
- "bool ReadUInt32(ref uint)"
|
||||
- "uint ReadUInt32(int)"
|
||||
- "ulong ReadUInt64()"
|
||||
- "long ReadInt64()"
|
||||
- "ulong ReadUInt64(int)"
|
||||
- "long ReadInt64(int)"
|
||||
- "float ReadFloat()"
|
||||
- "System.Half ReadHalf()"
|
||||
- "float ReadSingle()"
|
||||
- "bool ReadSingle(ref float)"
|
||||
- "double ReadDouble()"
|
||||
- "uint ReadVariableUInt32()"
|
||||
- "bool ReadVariableUInt32(ref uint)"
|
||||
- "int ReadVariableInt32()"
|
||||
- "long ReadVariableInt64()"
|
||||
- "ulong ReadVariableUInt64()"
|
||||
- "float ReadSignedSingle(int)"
|
||||
- "float ReadUnitSingle(int)"
|
||||
- "float ReadRangedSingle(float, float, int)"
|
||||
- "int ReadRangedInteger(int, int)"
|
||||
- "long ReadRangedInteger(long, long)"
|
||||
- "string ReadString()"
|
||||
- "bool ReadString(ref string)"
|
||||
- "double ReadTime(Lidgren.Network.NetConnection, bool)"
|
||||
- "System.Net.IPEndPoint ReadIPEndPoint()"
|
||||
- "void SkipPadBits()"
|
||||
- "void ReadPadBits()"
|
||||
- "void SkipPadBits(int)"
|
||||
- "void EnsureBufferSize(int)"
|
||||
- "void Write(bool)"
|
||||
- "void Write(byte)"
|
||||
- "void WriteAt(int, byte)"
|
||||
- "void Write(sbyte)"
|
||||
- "void Write(byte, int)"
|
||||
- "void Write(byte[])"
|
||||
- "void Write(System.ReadOnlySpan`1<byte>)"
|
||||
- "void Write(byte[], int, int)"
|
||||
- "void Write(ushort)"
|
||||
- "void WriteAt(int, ushort)"
|
||||
- "void Write(ushort, int)"
|
||||
- "void Write(short)"
|
||||
- "void WriteAt(int, short)"
|
||||
- "void Write(int)"
|
||||
- "void WriteAt(int, int)"
|
||||
- "void Write(uint)"
|
||||
- "void WriteAt(int, uint)"
|
||||
- "void Write(uint, int)"
|
||||
- "void Write(int, int)"
|
||||
- "void Write(ulong)"
|
||||
- "void WriteAt(int, ulong)"
|
||||
- "void Write(ulong, int)"
|
||||
- "void Write(long)"
|
||||
- "void Write(long, int)"
|
||||
- "void Write(System.Half)"
|
||||
- "void Write(float)"
|
||||
- "void Write(double)"
|
||||
- "int WriteVariableUInt32(uint)"
|
||||
- "int WriteVariableInt32(int)"
|
||||
- "int WriteVariableInt64(long)"
|
||||
- "int WriteVariableUInt64(ulong)"
|
||||
- "void WriteSignedSingle(float, int)"
|
||||
- "void WriteUnitSingle(float, int)"
|
||||
- "void WriteRangedSingle(float, float, float, int)"
|
||||
- "int WriteRangedInteger(int, int, int)"
|
||||
- "int WriteRangedInteger(long, long, long)"
|
||||
- "void Write(string)"
|
||||
- "void Write(System.Net.IPEndPoint)"
|
||||
- "void WriteTime(bool)"
|
||||
- "void WriteTime(double, bool)"
|
||||
- "void WritePadBits()"
|
||||
- "void WritePadBits(int)"
|
||||
- "void Write(Lidgren.Network.NetBuffer)"
|
||||
- "void Zero(int)"
|
||||
- "void .ctor()"
|
||||
NetDeliveryMethod: { }
|
||||
NetIncomingMessage:
|
||||
All: True
|
||||
Methods:
|
||||
- "Lidgren.Network.NetIncomingMessageType get_MessageType()"
|
||||
- "Lidgren.Network.NetDeliveryMethod get_DeliveryMethod()"
|
||||
- "int get_SequenceChannel()"
|
||||
- "System.Net.IPEndPoint get_SenderEndPoint()"
|
||||
- "Lidgren.Network.NetConnection get_SenderConnection()"
|
||||
- "double get_ReceiveTime()"
|
||||
- "double ReadTime(bool)"
|
||||
- "string ToString()"
|
||||
NetOutgoingMessage:
|
||||
All: True
|
||||
Methods:
|
||||
- "string ToString()"
|
||||
Nett:
|
||||
CommentLocation: { } # Enum
|
||||
Toml:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.ContentPack
|
||||
@@ -135,11 +136,37 @@ namespace Robust.Shared.ContentPack
|
||||
path = path.Directory;
|
||||
|
||||
var fullPath = GetFullPath(path);
|
||||
Process.Start(new ProcessStartInfo
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
UseShellExecute = true,
|
||||
FileName = fullPath,
|
||||
});
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = $"{Environment.GetEnvironmentVariable("SystemRoot")}\\explorer.exe",
|
||||
Arguments = ".",
|
||||
WorkingDirectory = fullPath,
|
||||
});
|
||||
}
|
||||
else if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "open",
|
||||
Arguments = ".",
|
||||
WorkingDirectory = fullPath,
|
||||
});
|
||||
}
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD())
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "xdg-open",
|
||||
Arguments = ".",
|
||||
WorkingDirectory = fullPath,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("Opening OS windows not supported on this OS");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
@@ -41,6 +40,9 @@ namespace Robust.Shared.GameObjects
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("zoom")]
|
||||
public Vector2 Zoom = Vector2.One;
|
||||
|
||||
/// <summary>
|
||||
/// Eye offset, relative to the map, and not affected by <see cref="Rotation"/>
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("offset"), AutoNetworkedField]
|
||||
public Vector2 Offset;
|
||||
|
||||
@@ -52,9 +54,13 @@ namespace Robust.Shared.GameObjects
|
||||
public int VisibilityMask = DefaultVisibilityMask;
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the PVS view range of this eye, Effectively a per-eye <see cref="CVars.NetMaxUpdateRange"/> cvar.
|
||||
/// Scaling factor for the PVS view range of this eye. This effectively allows the
|
||||
/// <see cref="CVars.NetMaxUpdateRange"/> and <see cref="CVars.NetPvsPriorityRange"/> cvars to be configured per
|
||||
/// eye.
|
||||
/// </summary>
|
||||
[DataField] public float? PvsSize;
|
||||
[Access(typeof(SharedEyeSystem))]
|
||||
[DataField]
|
||||
public float PvsScale = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -23,7 +23,6 @@ namespace Robust.Shared.GameObjects
|
||||
public sealed partial class TransformComponent : Component, IComponentDebug
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
// Currently this field just exists for VV. In future, it might become a real field
|
||||
[ViewVariables, PublicAPI]
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Prometheus;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Log;
|
||||
@@ -42,6 +43,7 @@ namespace Robust.Shared.GameObjects
|
||||
[IoC.Dependency] private readonly ProfManager _prof = default!;
|
||||
[IoC.Dependency] private readonly INetManager _netMan = default!;
|
||||
[IoC.Dependency] private readonly IReflectionManager _reflection = default!;
|
||||
[IoC.Dependency] private readonly EntityConsoleHost _entityConsoleHost = default!;
|
||||
|
||||
// I feel like PJB might shed me for putting a system dependency here, but its required for setting entity
|
||||
// positions on spawn....
|
||||
@@ -216,6 +218,7 @@ namespace Robust.Shared.GameObjects
|
||||
TransformQuery = GetEntityQuery<TransformComponent>();
|
||||
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
_actorQuery = GetEntityQuery<ActorComponent>();
|
||||
_entityConsoleHost.Startup();
|
||||
}
|
||||
|
||||
public virtual void Shutdown()
|
||||
@@ -227,6 +230,7 @@ namespace Robust.Shared.GameObjects
|
||||
ClearComponents();
|
||||
ShuttingDown = false;
|
||||
Started = false;
|
||||
_entityConsoleHost.Shutdown();
|
||||
}
|
||||
|
||||
public virtual void Cleanup()
|
||||
|
||||
@@ -74,6 +74,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[MustCallBase(true)]
|
||||
public virtual void Initialize() { }
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -81,12 +82,15 @@ namespace Robust.Shared.GameObjects
|
||||
/// Not ran on the client if prediction is disabled and
|
||||
/// <see cref="UpdatesOutsidePrediction"/> is false (the default).
|
||||
/// </remarks>
|
||||
[MustCallBase(true)]
|
||||
public virtual void Update(float frameTime) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
[MustCallBase(true)]
|
||||
public virtual void FrameUpdate(float frameTime) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
[MustCallBase(true)]
|
||||
public virtual void Shutdown()
|
||||
{
|
||||
ShutdownSubscriptions();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -97,6 +98,23 @@ public abstract class SharedEyeSystem : EntitySystem
|
||||
eyeComponent.Eye.Zoom = value;
|
||||
}
|
||||
|
||||
public void SetPvsScale(Entity<EyeComponent?> eye, float scale)
|
||||
{
|
||||
if (!Resolve(eye.Owner, ref eye.Comp, false))
|
||||
return;
|
||||
|
||||
// Prevent a admin or some other fuck-up from causing exception spam in PVS system due to divide-by-zero or
|
||||
// other such issues
|
||||
if (!float.IsFinite(scale))
|
||||
{
|
||||
Log.Error($"Attempted to set pvs scale to invalid value: {scale}. Eye: {ToPrettyString(eye)}");
|
||||
SetPvsScale(eye, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
eye.Comp.PvsScale = Math.Clamp(scale, 0.1f, 100f);
|
||||
}
|
||||
|
||||
public void SetVisibilityMask(EntityUid uid, int value, EyeComponent? eyeComponent = null)
|
||||
{
|
||||
if (!Resolve(uid, ref eyeComponent))
|
||||
|
||||
@@ -909,6 +909,26 @@ public abstract partial class SharedMapSystem
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TileRef> GetLocalTilesIntersecting(EntityUid uid, MapGridComponent grid, Circle localCircle, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
var aabb = new Box2(localCircle.Position.X - localCircle.Radius, localCircle.Position.Y - localCircle.Radius,
|
||||
localCircle.Position.X + localCircle.Radius, localCircle.Position.Y + localCircle.Radius);
|
||||
|
||||
var tileEnumerator = GetLocalTilesEnumerator(uid, grid, aabb, ignoreEmpty, predicate);
|
||||
|
||||
while (tileEnumerator.MoveNext(out var tile))
|
||||
{
|
||||
var tileCenter = tile.GridIndices + grid.TileSizeHalfVector;
|
||||
var direction = tileCenter - localCircle.Position;
|
||||
|
||||
if (direction.IsShorterThanOrEqualTo(localCircle.Radius))
|
||||
{
|
||||
yield return tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TileRef> GetTilesIntersecting(EntityUid uid, MapGridComponent grid, Circle worldArea, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
|
||||
@@ -31,6 +31,9 @@ namespace Robust.Shared.Graphics
|
||||
set => _coords = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Eye offset, relative to the map, and not affected by <see cref="Rotation"/>
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Vector2 Offset { get; set; }
|
||||
|
||||
|
||||
@@ -248,6 +248,7 @@ namespace Robust.Shared.Map
|
||||
/// </summary>
|
||||
/// <param name="position">The vector to offset by local to the entity.</param>
|
||||
/// <returns>Newly offset coordinates.</returns>
|
||||
[Pure]
|
||||
public EntityCoordinates Offset(Vector2 position)
|
||||
{
|
||||
return new(EntityId, Position + position);
|
||||
|
||||
@@ -59,9 +59,16 @@ namespace Robust.Shared.Network.Messages
|
||||
buffer.ReadAlignedMemory(finalStream, uncompressedLength);
|
||||
}
|
||||
|
||||
serializer.DeserializeDirect(finalStream, out State);
|
||||
try
|
||||
{
|
||||
serializer.DeserializeDirect(finalStream, out State);
|
||||
}
|
||||
finally
|
||||
{
|
||||
finalStream.Dispose();
|
||||
}
|
||||
|
||||
State.PayloadSize = uncompressedLength;
|
||||
finalStream.Dispose();
|
||||
}
|
||||
|
||||
public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -50,6 +51,7 @@ namespace Robust.Shared
|
||||
deps.Register<ToolshedManager>();
|
||||
deps.Register<HttpClientHolder>();
|
||||
deps.Register<RobustMemoryManager>();
|
||||
deps.Register<EntityConsoleHost>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,6 +232,7 @@ namespace Robust.UnitTesting.Server
|
||||
container.Register<IParallelManagerInternal, TestingParallelManager>();
|
||||
// Needed for grid fixture debugging.
|
||||
container.Register<IConGroupController, ConGroupController>();
|
||||
container.Register<EntityConsoleHost>();
|
||||
|
||||
// I just wanted to load pvs system
|
||||
container.Register<IServerEntityManager, ServerEntityManager>();
|
||||
|
||||
@@ -44,7 +44,7 @@ public sealed partial class VisibilityTest : RobustIntegrationTest
|
||||
metaComp[i] = server.EntMan.GetComponent<MetaDataComponent>(ent);
|
||||
visComp[i] = server.EntMan.AddComponent<VisibilityComponent>(ent);
|
||||
|
||||
vis.AddLayer(ent, visComp[i], 1 << i);
|
||||
vis.AddLayer((ent, visComp[i]), (ushort)(1 << i));
|
||||
if (i > 0)
|
||||
xforms.SetParent(ent, ents[i - 1]);
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public sealed partial class VisibilityTest : RobustIntegrationTest
|
||||
// Adding a layer to the root entity's mask will apply it to all children
|
||||
var extraMask = 1 << (N + 1);
|
||||
mask = RequiredMask | extraMask;
|
||||
vis.AddLayer(ents[0], visComp[0], extraMask);
|
||||
vis.AddLayer((ents[0], visComp[0]), (ushort)extraMask);
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
mask |= 1 << i;
|
||||
@@ -71,7 +71,7 @@ public sealed partial class VisibilityTest : RobustIntegrationTest
|
||||
}
|
||||
|
||||
// Removing the removes it from all children.
|
||||
vis.RemoveLayer(ents[0], visComp[0], extraMask);
|
||||
vis.RemoveLayer((ents[0], visComp[0]), (ushort)extraMask);
|
||||
mask = RequiredMask;
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
@@ -101,7 +101,7 @@ public sealed partial class VisibilityTest : RobustIntegrationTest
|
||||
}
|
||||
|
||||
// Re-attaching the entity also updates the masks.
|
||||
await server.WaitPost(() => xforms.SetParent(ents[split], ents[split-1]));
|
||||
await server.WaitPost(() => xforms.SetParent(ents[split], ents[split - 1]));
|
||||
mask = RequiredMask;
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
@@ -111,7 +111,7 @@ public sealed partial class VisibilityTest : RobustIntegrationTest
|
||||
}
|
||||
|
||||
// Setting a mask on a child does not propagate upwards, only downwards
|
||||
vis.AddLayer(ents[split], visComp[split], extraMask);
|
||||
vis.AddLayer((ents[split], visComp[split]), (ushort)extraMask);
|
||||
mask = RequiredMask;
|
||||
for (int i = 0; i < split; i++)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user