mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5adf0cdfa3 | ||
|
|
320543c2a6 | ||
|
|
0d0d949752 | ||
|
|
d43fc89055 |
34
.github/workflows/build-all-configurations.yml
vendored
34
.github/workflows/build-all-configurations.yml
vendored
@@ -1,34 +0,0 @@
|
||||
name: Build All Configurations
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
targetOS: [Windows, Linux, MacOS]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Build Debug
|
||||
run: dotnet build --no-restore --configuration Debug /p:WarningsAsErrors=nullable /p:TargetOS=${{ matrix.targetOS }}
|
||||
- name: Build Tools
|
||||
run: dotnet build --no-restore --configuration Tools /p:WarningsAsErrors=nullable /p:TargetOS=${{ matrix.targetOS }}
|
||||
- name: Build Release
|
||||
run: dotnet build --no-restore --configuration Release /p:WarningsAsErrors=nullable /p:TargetOS=${{ matrix.targetOS }}
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
Submodule NetSerializer updated: ced82e76ad...4882400f2c
@@ -54,101 +54,10 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 265.0.3
|
||||
## 263.0.2
|
||||
|
||||
|
||||
## 265.0.2
|
||||
|
||||
|
||||
## 265.0.1
|
||||
|
||||
|
||||
## 265.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* More members in `IntegrationInstance` now enforce that the instance is idle before accessing it.
|
||||
* `Prototype.ValidateDirectory` now requires that prototype IDs have no spaces or periods in them.
|
||||
* `IPrototypeManager.TryIndex` no longer logs errors unless using the overload with an optional parameter. Use `Resolve()` instead if error logging is desired.
|
||||
* `LocalizedCommands` now has a `Loc` property that refers to `LocalizationManager`. This can cause compile failures if you have static methods in child types that referenced static `Loc`.
|
||||
* `[AutoGenerateComponentState]` now works on parent members for inherited classes. This can cause compile failures in certain formerly silently broken cases with overriden properties.
|
||||
* `Vector3`, `Vector4`, `Quaternion`, and `Matrix4` have been removed from `Robust.Shared.Maths`. Use the `System.Numerics` types instead.
|
||||
|
||||
### New features
|
||||
|
||||
* `RobustClientPackaging.WriteClientResources()` and `RobustServerPackaging.WriteServerResources()` now have an overload taking in a set of things to ignore in the content resources directory.
|
||||
* Added `IPrototypeManager.Resolve()`, which logs an error if the resolved prototype does not exist. This is effectively the previous (but not original) default behavior of `IPrototypeManager.TryIndex`.
|
||||
* There's now a ViewVariables property editor for tuples.
|
||||
* Added `ColorNaming` helper functions for getting textual descriptions of color values.
|
||||
* Added Oklab/Oklch conversion functions for `Color`.
|
||||
* `ColorSelectorSliders` now displays textual descriptions of color values.
|
||||
* Added `TimeSpanExt.TryTimeSpan` to parse `TimeSpan`s with the `1.5h` format available in YAML.
|
||||
* Added `ITestContextLike` and related classes to allow controlling pooled integration instances better.
|
||||
* `EntProtoId` VV prop editors now don't allow setting invalid prototype IDs, inline with `ProtoId<T>`.
|
||||
* Custom VV controls can now be registered using `IViewVariableControlFactory`.
|
||||
* The entity spawn window now shows all placement modes registered with `IPlacementManager`.
|
||||
* Added `VectorHelpers.InterpolateCubic` for `System.Numerics` `Vector3` and `Vector4`.
|
||||
* Added deconstruct helpers for `System.Numerics` `Vector3` and `Vector4`.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Pooled integration instances returned by `RobustIntegrationTest` are now treated as non-idle, for consistency with non-pooled startups.
|
||||
* `SharedAudioSystem.SetState` no longer calls `DirtyField` on `PlaybackPosition`, an unnetworked field.
|
||||
* Fix loading texture files from the root directory.
|
||||
* Fix integration test pooling leaking non-reusable instances.
|
||||
* Fix multiple bugs where VV displayed the wrong property editor for remote values.
|
||||
* VV displays group headings again in member list.
|
||||
* Fix a stack overflow that could occur with `ColorSelectorSliders`.
|
||||
* `MidiRenderer` now properly handles `NoteOn` events with 0 velocity (which should actually be treated as `NoteOff` events).
|
||||
|
||||
### Other
|
||||
|
||||
* The debug assert for `RobustRandom.Next(TimeSpan, TimeSpan)` now allows for the two arguments to be equal.
|
||||
* The configuration system will now report an error instead of warning if it fails to load the config file.
|
||||
* Members in `IntegrationInstance` that enforce the instance is idle now always allow access from the instance's thread (e.g. from a callback).
|
||||
* `IPrototypeManager` methods now have `[ForbidLiteral]` where appropriate.
|
||||
* Performance improvements to physics system.
|
||||
* `[ValidatePrototypeIdAttribute]` has been marked as obsolete.
|
||||
* `ParallelManager` no longer cuts out exception information for caught job exceptions.
|
||||
* Improved logging for PVS uninitialized/deleted entity errors.
|
||||
|
||||
### Internal
|
||||
|
||||
* General code & warning cleanup.
|
||||
* Fix `VisibilityTest` being unreliable.
|
||||
* `ColorSelectorSliders` has been internally refactored.
|
||||
* Added CI workflows that test all RT build configurations.
|
||||
|
||||
## 264.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `IPrototypeManager.Index(Type kind, string id)` now throws `UnknownPrototypeException` instead of `KeyNotFoundException`, for consistency with `IPrototypeManager.Index<T>`.
|
||||
|
||||
### New features
|
||||
|
||||
* Types can now implement the new interface `IRobustCloneable<T>` to be cloned by the component state source generator.
|
||||
* Added extra Roslyn Analyzers to detect some misuse of prototypes:
|
||||
* Network serializing prototypes (tagging them with `[Serializable, NetSerializable]`).
|
||||
* Constructing new instances of prototypes directly.
|
||||
* Add `PrototypeManagerExt.Index` helper function that takes a nullable `ProtoId<T>`, returning null if the ID is null.
|
||||
* Added an `AlwaysActive` field to `WebViewControl` to make a browser window active even when not in the UI tree.
|
||||
* Made some common dependencies accessible through `IPlacementManager`.
|
||||
* Added a new `GENITIVE()` localization helper function, which is useful for certain languages.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Sprite scale is now correctly applied to sprite boundaries in `SpriteSystem.GetLocalBounds`.
|
||||
* Fixed documentation for `IPrototypeManager.Index<T>` stating that `KeyNotFoundException` gets thrown, when in actuality `UnknownPrototypeException` gets thrown.
|
||||
|
||||
### Other
|
||||
|
||||
* More tiny optimizations to `DataDefinitionAnalyzer`.
|
||||
* NetSerializer has been updated. On debug, it will now report *where* a type that can't be serialized is referenced from.
|
||||
|
||||
### Internal
|
||||
|
||||
* Minor internal code cleanup.
|
||||
## 263.0.1
|
||||
|
||||
|
||||
## 263.0.0
|
||||
|
||||
@@ -21,8 +21,7 @@ zzzz-object-pronoun = { GENDER($ent) ->
|
||||
}
|
||||
|
||||
# Used internally by the DAT-OBJ() function.
|
||||
# Not used in en-US. Created to support other languages.
|
||||
# (e.g., "to him," "for her")
|
||||
# Not used in en-US. Created for supporting other languages.
|
||||
zzzz-dat-object = { GENDER($ent) ->
|
||||
[male] him
|
||||
[female] her
|
||||
@@ -30,16 +29,6 @@ zzzz-dat-object = { GENDER($ent) ->
|
||||
*[neuter] it
|
||||
}
|
||||
|
||||
# Used internally by the GENITIVE() function.
|
||||
# Not used in en-US. Created to support other languages.
|
||||
# e.g., "у него" (Russian), "seines Vaters" (German).
|
||||
zzzz-genitive = { GENDER($ent) ->
|
||||
[male] his
|
||||
[female] her
|
||||
[epicene] their
|
||||
*[neuter] its
|
||||
}
|
||||
|
||||
# Used internally by the POSS-PRONOUN() function.
|
||||
zzzz-possessive-pronoun = { GENDER($ent) ->
|
||||
[male] his
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
color-hue-chroma-lightness = {$lightness} {$chroma} {$hue}
|
||||
color-hue-chroma = {$chroma} {$hue}
|
||||
color-hue-lightness = {$lightness} {$hue}
|
||||
color-very-dark = very dark
|
||||
color-dark = dark
|
||||
color-light = light
|
||||
color-very-light = very light
|
||||
color-mixed-hue = {$a} {$b}
|
||||
color-pale = pale
|
||||
color-gray-adjective = gray
|
||||
color-strong = strong
|
||||
color-pink = pink
|
||||
color-red = red
|
||||
color-orange = orange
|
||||
color-yellow = yellow
|
||||
color-green = green
|
||||
color-cyan = cyan
|
||||
color-blue = blue
|
||||
color-purple = purple
|
||||
color-brown = brown
|
||||
color-white = white
|
||||
color-gray = gray
|
||||
color-black = black
|
||||
|
||||
color-pink-color-red = pinkish red
|
||||
color-red-color-orange = reddish orange
|
||||
color-orange-color-yellow = orangeish yellow
|
||||
color-yellow-color-green = yellowish green
|
||||
color-green-color-cyan = greenish cyan
|
||||
color-cyan-color-blue = cyanish blue
|
||||
color-blue-color-purple = blueish purple
|
||||
color-purple-color-pink = purpleish pink
|
||||
@@ -1,64 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
using NUnit.Framework;
|
||||
using VerifyCS =
|
||||
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.PrototypeInstantiationAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
|
||||
[TestFixture]
|
||||
[TestOf(typeof(PrototypeInstantiationAnalyzer))]
|
||||
public sealed class PrototypeInstantiationAnalyzerTest
|
||||
{
|
||||
private static Task Verifier(string code, params DiagnosticResult[] expected)
|
||||
{
|
||||
var test = new RTAnalyzerTest<PrototypeInstantiationAnalyzer>()
|
||||
{
|
||||
TestState =
|
||||
{
|
||||
Sources = { code }
|
||||
},
|
||||
};
|
||||
|
||||
TestHelper.AddEmbeddedSources(
|
||||
test.TestState,
|
||||
"Robust.Shared.Prototypes.Attributes.cs",
|
||||
"Robust.Shared.Prototypes.IPrototype.cs",
|
||||
"Robust.Shared.Serialization.Manager.Attributes.DataFieldAttribute.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.Serialization;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
[Prototype]
|
||||
public sealed class FooPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
|
||||
public static class Bad
|
||||
{
|
||||
public static FooPrototype Real()
|
||||
{
|
||||
return new FooPrototype();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
await Verifier(code,
|
||||
// /0/Test0.cs(15,16): warning RA0039: Do not instantiate prototypes directly. Prototypes should always be instantiated by the prototype manager.
|
||||
VerifyCS.Diagnostic().WithSpan(15, 16, 15, 34));
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
using NUnit.Framework;
|
||||
using VerifyCS =
|
||||
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.PrototypeNetSerializableAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
|
||||
[TestFixture]
|
||||
[TestOf(typeof(PrototypeNetSerializableAnalyzer))]
|
||||
public sealed class PrototypeNetSerializableAnalyzerTest
|
||||
{
|
||||
private static Task Verifier(string code, params DiagnosticResult[] expected)
|
||||
{
|
||||
var test = new RTAnalyzerTest<PrototypeNetSerializableAnalyzer>()
|
||||
{
|
||||
TestState =
|
||||
{
|
||||
Sources = { code }
|
||||
},
|
||||
};
|
||||
|
||||
TestHelper.AddEmbeddedSources(
|
||||
test.TestState,
|
||||
"Robust.Shared.Serialization.NetSerializableAttribute.cs",
|
||||
"Robust.Shared.Prototypes.Attributes.cs",
|
||||
"Robust.Shared.Prototypes.IPrototype.cs",
|
||||
"Robust.Shared.Serialization.Manager.Attributes.DataFieldAttribute.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 System;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
[Prototype]
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class FooPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
""";
|
||||
|
||||
await Verifier(code,
|
||||
// /0/Test0.cs(7,21): warning RA0037: Type FooPrototype is a prototype and marked as [NetSerializable]. Prototypes should not be directly sent over the network, send their IDs instead.
|
||||
VerifyCS.Diagnostic(PrototypeNetSerializableAnalyzer.RuleNetSerializable).WithSpan(7, 21, 7, 33).WithArguments("FooPrototype"),
|
||||
// /0/Test0.cs(7,21): warning RA0038: Type FooPrototype is a prototype and marked as [Serializable]. Prototypes should not be directly sent over the network, send their IDs instead.
|
||||
VerifyCS.Diagnostic(PrototypeNetSerializableAnalyzer.RuleSerializable).WithSpan(7, 21, 7, 33).WithArguments("FooPrototype"));
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Testing;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
public sealed class RTAnalyzerTest<TAnalyzer> : CSharpAnalyzerTest<TAnalyzer, DefaultVerifier>
|
||||
where TAnalyzer : DiagnosticAnalyzer, new()
|
||||
{
|
||||
protected override ParseOptions CreateParseOptions()
|
||||
{
|
||||
var baseOptions = (CSharpParseOptions) base.CreateParseOptions();
|
||||
return baseOptions.WithPreprocessorSymbols("ROBUST_ANALYZERS_TEST");
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,6 @@
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\ObsoleteInheritanceAttribute.cs" LogicalName="Robust.Shared.Analyzers.ObsoleteInheritanceAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\IoC\DependencyAttribute.cs" LogicalName="Robust.Shared.IoC.DependencyAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\GameObjects\EventBusAttributes.cs" LogicalName="Robust.Shared.GameObjects.EventBusAttributes.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Serialization\NetSerializableAttribute.cs" LogicalName="Robust.Shared.Serialization.NetSerializableAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Prototypes\Attributes.cs" LogicalName="Robust.Shared.Prototypes.Attributes.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Prototypes\IPrototype.cs" LogicalName="Robust.Shared.Prototypes.IPrototype.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Serialization\Manager\Attributes\DataFieldAttribute.cs" LogicalName="Robust.Shared.Serialization.Manager.Attributes.DataFieldAttribute.cs" LinkBase="Implementations" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -121,7 +121,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
}, SymbolKind.NamedType);
|
||||
}
|
||||
|
||||
private static void AnalyzeDataDefinition(SyntaxNodeAnalysisContext context)
|
||||
private void AnalyzeDataDefinition(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not TypeDeclarationSyntax declaration)
|
||||
return;
|
||||
@@ -147,7 +147,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
}
|
||||
}
|
||||
|
||||
private static void AnalyzeDataField(SyntaxNodeAnalysisContext context)
|
||||
private void AnalyzeDataField(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not FieldDeclarationSyntax field)
|
||||
return;
|
||||
@@ -198,7 +198,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
}
|
||||
}
|
||||
|
||||
private static void AnalyzeDataFieldProperty(SyntaxNodeAnalysisContext context)
|
||||
private void AnalyzeDataFieldProperty(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not PropertyDeclarationSyntax property)
|
||||
return;
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class PrototypeInstantiationAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string PrototypeInterfaceType = "Robust.Shared.Prototypes.IPrototype";
|
||||
|
||||
public static readonly DiagnosticDescriptor Rule = new(
|
||||
Diagnostics.IdPrototypeInstantiation,
|
||||
"Do not instantiate prototypes directly",
|
||||
"Do not instantiate prototypes directly. Prototypes should always be instantiated by the prototype manager.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Warning,
|
||||
true);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Rule];
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.EnableConcurrentExecution();
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
context.RegisterCompilationStartAction(static ctx =>
|
||||
{
|
||||
var prototypeInterface = ctx.Compilation.GetTypeByMetadataName(PrototypeInterfaceType);
|
||||
if (prototypeInterface == null)
|
||||
return;
|
||||
|
||||
ctx.RegisterOperationAction(symContext => Check(prototypeInterface, symContext), OperationKind.ObjectCreation);
|
||||
});
|
||||
}
|
||||
|
||||
private static void Check(INamedTypeSymbol prototypeInterface, OperationAnalysisContext ctx)
|
||||
{
|
||||
if (ctx.Operation is not IObjectCreationOperation { Type: { } resultType } creationOp)
|
||||
return;
|
||||
|
||||
if (!TypeSymbolHelper.ImplementsInterface(resultType, prototypeInterface))
|
||||
return;
|
||||
|
||||
ctx.ReportDiagnostic(Diagnostic.Create(Rule, creationOp.Syntax.GetLocation()));
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class PrototypeNetSerializableAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string PrototypeInterfaceType = "Robust.Shared.Prototypes.IPrototype";
|
||||
private const string NetSerializableAttributeType = "Robust.Shared.Serialization.NetSerializableAttribute";
|
||||
|
||||
public static readonly DiagnosticDescriptor RuleNetSerializable = new(
|
||||
Diagnostics.IdPrototypeNetSerializable,
|
||||
"Prototypes should not be [NetSerializable]",
|
||||
"Type {0} is a prototype and marked as [NetSerializable]. Prototypes should not be directly sent over the network, send their IDs instead.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Warning,
|
||||
true);
|
||||
|
||||
|
||||
public static readonly DiagnosticDescriptor RuleSerializable = new(
|
||||
Diagnostics.IdPrototypeSerializable,
|
||||
"Prototypes should not be [Serializable]",
|
||||
"Type {0} is a prototype and marked as [Serializable]. Prototypes should not be directly sent over the network, send their IDs instead.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Warning,
|
||||
true);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [
|
||||
RuleNetSerializable,
|
||||
RuleSerializable
|
||||
];
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.EnableConcurrentExecution();
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
|
||||
context.RegisterCompilationStartAction(static ctx =>
|
||||
{
|
||||
var prototypeInterface = ctx.Compilation.GetTypeByMetadataName(PrototypeInterfaceType);
|
||||
var netSerializableAttribute = ctx.Compilation.GetTypeByMetadataName(NetSerializableAttributeType);
|
||||
|
||||
if (prototypeInterface == null || netSerializableAttribute == null)
|
||||
return;
|
||||
|
||||
ctx.RegisterSymbolAction(symbolContext => CheckClass(prototypeInterface, netSerializableAttribute, symbolContext), SymbolKind.NamedType);
|
||||
});
|
||||
}
|
||||
|
||||
private static void CheckClass(
|
||||
INamedTypeSymbol prototypeInterface,
|
||||
INamedTypeSymbol netSerializableAttribute,
|
||||
SymbolAnalysisContext symbolContext)
|
||||
{
|
||||
if (symbolContext.Symbol is not INamedTypeSymbol symbol)
|
||||
return;
|
||||
|
||||
if (!TypeSymbolHelper.ImplementsInterface(symbol, prototypeInterface))
|
||||
return;
|
||||
|
||||
if (AttributeHelper.HasAttribute(symbol, netSerializableAttribute, out _))
|
||||
{
|
||||
symbolContext.ReportDiagnostic(
|
||||
Diagnostic.Create(RuleNetSerializable, symbol.Locations[0], symbol.ToDisplayString()));
|
||||
}
|
||||
|
||||
if (symbol.IsSerializable)
|
||||
{
|
||||
symbolContext.ReportDiagnostic(
|
||||
Diagnostic.Create(RuleSerializable, symbol.Locations[0], symbol.ToDisplayString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,10 +162,9 @@ namespace Robust.Client.WebView.Cef
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsOpen => _data != null;
|
||||
public bool IsLoading => _data?.Browser.IsLoading ?? false;
|
||||
|
||||
public void StartBrowser()
|
||||
public void EnteredTree()
|
||||
{
|
||||
DebugTools.AssertNull(_data);
|
||||
|
||||
@@ -196,7 +195,7 @@ namespace Robust.Client.WebView.Cef
|
||||
_data = new LiveData(texture, client, browser, renderer);
|
||||
}
|
||||
|
||||
public void CloseBrowser()
|
||||
public void ExitedTree()
|
||||
{
|
||||
DebugTools.AssertNotNull(_data);
|
||||
|
||||
|
||||
@@ -81,13 +81,11 @@ namespace Robust.Client.WebView.Headless
|
||||
|
||||
private sealed class WebViewControlImplDummy : DummyBase, IWebViewControlImpl
|
||||
{
|
||||
public bool IsOpen => false;
|
||||
|
||||
public void StartBrowser()
|
||||
public void EnteredTree()
|
||||
{
|
||||
}
|
||||
|
||||
public void CloseBrowser()
|
||||
public void ExitedTree()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,8 @@ namespace Robust.Client.WebView
|
||||
/// </summary>
|
||||
internal interface IWebViewControlImpl : IWebViewControl
|
||||
{
|
||||
public bool IsOpen { get; }
|
||||
|
||||
void StartBrowser();
|
||||
void CloseBrowser();
|
||||
void EnteredTree();
|
||||
void ExitedTree();
|
||||
void MouseMove(GUIMouseMoveEventArgs args);
|
||||
void MouseExited();
|
||||
void MouseWheel(GUIMouseWheelEventArgs args);
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace Robust.Client.WebView
|
||||
[Dependency] private readonly IWebViewManagerInternal _webViewManager = default!;
|
||||
|
||||
private readonly IWebViewControlImpl _controlImpl;
|
||||
private bool _alwaysActive;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string Url
|
||||
@@ -23,21 +22,6 @@ namespace Robust.Client.WebView
|
||||
set => _controlImpl.Url = value;
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool AlwaysActive
|
||||
{
|
||||
get => _alwaysActive;
|
||||
set
|
||||
{
|
||||
_alwaysActive = value;
|
||||
|
||||
if (_alwaysActive && !_controlImpl.IsOpen)
|
||||
_controlImpl.StartBrowser();
|
||||
else if (!_alwaysActive && _controlImpl.IsOpen && !IsInsideTree)
|
||||
_controlImpl.CloseBrowser();
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables] public bool IsLoading => _controlImpl.IsLoading;
|
||||
|
||||
public WebViewControl()
|
||||
@@ -55,16 +39,14 @@ namespace Robust.Client.WebView
|
||||
{
|
||||
base.EnteredTree();
|
||||
|
||||
if (!_controlImpl.IsOpen)
|
||||
_controlImpl.StartBrowser();
|
||||
_controlImpl.EnteredTree();
|
||||
}
|
||||
|
||||
protected override void ExitedTree()
|
||||
{
|
||||
base.ExitedTree();
|
||||
|
||||
if (!_alwaysActive)
|
||||
_controlImpl.CloseBrowser();
|
||||
_controlImpl.ExitedTree();
|
||||
}
|
||||
|
||||
protected internal override void MouseMove(GUIMouseMoveEventArgs args)
|
||||
|
||||
@@ -3,6 +3,8 @@ using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.Maths;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Animations
|
||||
{
|
||||
@@ -120,9 +122,9 @@ namespace Robust.Client.Animations
|
||||
case Vector2 vector2:
|
||||
return Vector2Helpers.InterpolateCubic((Vector2) preA, vector2, (Vector2) b, (Vector2) postB, t);
|
||||
case Vector3 vector3:
|
||||
return VectorHelpers.InterpolateCubic((Vector3) preA, vector3, (Vector3) b, (Vector3) postB, t);
|
||||
return Vector3.InterpolateCubic((Vector3) preA, vector3, (Vector3) b, (Vector3) postB, t);
|
||||
case Vector4 vector4:
|
||||
return VectorHelpers.InterpolateCubic((Vector4) preA, vector4, (Vector4) b, (Vector4) postB, t);
|
||||
return Vector4.InterpolateCubic((Vector4) preA, vector4, (Vector4) b, (Vector4) postB, t);
|
||||
case float f:
|
||||
return MathHelper.InterpolateCubic((float) preA, f, (float) b, (float) postB, t);
|
||||
case double d:
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using OpenTK.Audio.OpenAL.Extensions.Creative.EFX;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Effects;
|
||||
|
||||
@@ -575,28 +575,18 @@ internal sealed partial class MidiRenderer : IMidiRenderer
|
||||
case RobustMidiCommand.NoteOff:
|
||||
_rendererState.NoteVelocities.AsSpan[midiEvent.Channel].AsSpan[midiEvent.Key] = 0;
|
||||
_synth.NoteOff(midiEvent.Channel, midiEvent.Key);
|
||||
|
||||
break;
|
||||
|
||||
case RobustMidiCommand.NoteOn:
|
||||
// Velocity 0 *can* represent a NoteOff event.
|
||||
var velocity = midiEvent.Velocity;
|
||||
if (velocity == 0)
|
||||
{
|
||||
_rendererState.NoteVelocities.AsSpan[midiEvent.Channel].AsSpan[midiEvent.Key] = 0;
|
||||
_synth.NoteOn(midiEvent.Channel, midiEvent.Key, velocity);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (FilteredChannels[midiEvent.Channel])
|
||||
break;
|
||||
|
||||
velocity = VelocityOverride ?? midiEvent.Velocity;
|
||||
var velocity = VelocityOverride ?? midiEvent.Velocity;
|
||||
|
||||
_rendererState.NoteVelocities.AsSpan[midiEvent.Channel].AsSpan[midiEvent.Key] = velocity;
|
||||
_synth.NoteOn(midiEvent.Channel, midiEvent.Key, velocity);
|
||||
|
||||
break;
|
||||
|
||||
case RobustMidiCommand.AfterTouch:
|
||||
_rendererState.NoteVelocities.AsSpan[midiEvent.Channel].AsSpan[midiEvent.Key] = midiEvent.Value;
|
||||
_synth.KeyPressure(midiEvent.Channel, midiEvent.Key, midiEvent.Value);
|
||||
|
||||
@@ -144,7 +144,6 @@ namespace Robust.Client
|
||||
deps.Register<IViewVariablesManager, ClientViewVariablesManager>();
|
||||
deps.Register<IClientViewVariablesManager, ClientViewVariablesManager>();
|
||||
deps.Register<IClientViewVariablesManagerInternal, ClientViewVariablesManager>();
|
||||
deps.Register<IViewVariableControlFactory, ViewVariableControlFactory>();
|
||||
deps.Register<IClientConGroupController, ClientConGroupController>();
|
||||
deps.Register<IScriptClient, ScriptClient>();
|
||||
deps.Register<IRobustSerializer, ClientRobustSerializer>();
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace Robust.Client.Console
|
||||
MouseFilter = MouseFilterMode.Stop;
|
||||
Result = result;
|
||||
var compl = new FormattedMessage();
|
||||
var dim = Color.FromHsl(new Vector4(0f, 0f, 0.8f, 1f));
|
||||
var dim = Color.FromHsl((0f, 0f, 0.8f, 1f));
|
||||
|
||||
// warning: ew ahead
|
||||
string basen = "default";
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
foreach (var ent in _mapSystem.GetAnchoredEntities(gridUid, grid, spot))
|
||||
{
|
||||
if (TryComp(ent, out MetaDataComponent? meta))
|
||||
if (TryComp<MetaDataComponent>(ent, out var meta))
|
||||
{
|
||||
text.AppendLine($"uid: {ent}, {meta.EntityName}");
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ using Robust.Shared.ViewVariables;
|
||||
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;
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
|
||||
@@ -32,7 +32,7 @@ public sealed partial class SpriteSystem
|
||||
bounds = bounds.Union(GetLocalBounds(layer));
|
||||
}
|
||||
|
||||
sprite.Comp._bounds = bounds.Scale(sprite.Comp.Scale);
|
||||
sprite.Comp._bounds = bounds;
|
||||
sprite.Comp.BoundsDirty = false;
|
||||
return sprite.Comp._bounds;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ using Robust.Shared.Graphics.RSI;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.Client.GameObjects.SpriteComponent;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
using SysVec4 = System.Numerics.Vector4;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
@@ -155,7 +157,7 @@ public sealed partial class SpriteSystem
|
||||
// Negative color modulation values are by the default shader to disable light shading.
|
||||
// Specifically we set colour = - 1 - colour
|
||||
// This is good enough to ensure that non-negative values become negative & is trivially invertible.
|
||||
layerColor = new(new Vector4(-1) - layerColor.RGBA);
|
||||
layerColor = new(new SysVec4(-1) - layerColor.RGBA);
|
||||
}
|
||||
|
||||
drawingHandle.DrawTextureRectRegion(texture, quad, layerColor);
|
||||
|
||||
@@ -13,8 +13,6 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
internal sealed class NetInterpOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
|
||||
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
@@ -34,7 +32,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_lookup = lookup;
|
||||
_shader = _prototypeManager.Index(UnshadedShader).Instance();
|
||||
_shader = _prototypeManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
_container = _entityManager.System<SharedContainerSystem>();
|
||||
_xform = _entityManager.System<SharedTransformSystem>();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Enums;
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Maths;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
|
||||
@@ -18,6 +18,7 @@ using Robust.Shared.Graphics;
|
||||
using static Robust.Shared.GameObjects.OccluderComponent;
|
||||
using Robust.Shared.Utility;
|
||||
using TextureWrapMode = Robust.Shared.Graphics.TextureWrapMode;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
|
||||
@@ -11,6 +11,8 @@ using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using TKStencilOp = OpenToolkit.Graphics.OpenGL4.StencilOp;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -539,7 +541,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
case Matrix3x2 matrix3:
|
||||
program.SetUniform(name, matrix3);
|
||||
break;
|
||||
case Matrix4x4 matrix4:
|
||||
case Matrix4 matrix4:
|
||||
program.SetUniform(name, matrix4);
|
||||
break;
|
||||
case ClydeTexture clydeTexture:
|
||||
|
||||
@@ -10,6 +10,8 @@ using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -526,7 +528,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
data.Parameters[name] = value;
|
||||
}
|
||||
|
||||
private protected override void SetParameterImpl(string name, in Matrix4x4 value)
|
||||
private protected override void SetParameterImpl(string name, in Matrix4 value)
|
||||
{
|
||||
var data = Parent._shaderInstances[Handle];
|
||||
data.ParametersDirty = true;
|
||||
|
||||
@@ -17,6 +17,8 @@ using Robust.Shared.Timing;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using Color = Robust.Shared.Maths.Color;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -396,7 +398,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
}
|
||||
|
||||
private protected override void SetParameterImpl(string name, in Matrix4x4 value)
|
||||
private protected override void SetParameterImpl(string name, in Matrix4 value)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ using System.Runtime.CompilerServices;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -275,20 +277,20 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_clyde.CheckGlError();
|
||||
}
|
||||
|
||||
public void SetUniform(string uniformName, in Matrix4x4 matrix, bool transpose=true)
|
||||
public void SetUniform(string uniformName, in Matrix4 matrix, bool transpose=true)
|
||||
{
|
||||
var uniformId = GetUniform(uniformName);
|
||||
SetUniformDirect(uniformId, matrix, transpose);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private unsafe void SetUniformDirect(int uniformId, in Matrix4x4 value, bool transpose=true)
|
||||
private unsafe void SetUniformDirect(int uniformId, in Matrix4 value, bool transpose=true)
|
||||
{
|
||||
Matrix4x4 tmpTranspose = value;
|
||||
Matrix4 tmpTranspose = value;
|
||||
if (transpose)
|
||||
{
|
||||
// transposition not supported on GLES2, & no access to _hasGLES
|
||||
tmpTranspose = Matrix4x4.Transpose(value);
|
||||
tmpTranspose.Transpose();
|
||||
}
|
||||
GL.UniformMatrix4(uniformId, 1, false, (float*) &tmpTranspose);
|
||||
_clyde.CheckGlError();
|
||||
@@ -549,7 +551,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUniformMaybe(string uniformName, in Matrix4x4 value, bool transpose=true)
|
||||
public void SetUniformMaybe(string uniformName, in Matrix4 value, bool transpose=true)
|
||||
{
|
||||
if (TryGetUniform(uniformName, out var slot))
|
||||
{
|
||||
|
||||
@@ -4,6 +4,8 @@ using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
@@ -167,7 +169,7 @@ namespace Robust.Client.Graphics
|
||||
SetParameterImpl(name, value);
|
||||
}
|
||||
|
||||
public void SetParameter(string name, in Matrix4x4 value)
|
||||
public void SetParameter(string name, in Matrix4 value)
|
||||
{
|
||||
EnsureAlive();
|
||||
EnsureMutable();
|
||||
@@ -234,7 +236,7 @@ namespace Robust.Client.Graphics
|
||||
private protected abstract void SetParameterImpl(string name, bool value);
|
||||
private protected abstract void SetParameterImpl(string name, bool[] value);
|
||||
private protected abstract void SetParameterImpl(string name, in Matrix3x2 value);
|
||||
private protected abstract void SetParameterImpl(string name, in Matrix4x4 value);
|
||||
private protected abstract void SetParameterImpl(string name, in Matrix4 value);
|
||||
private protected abstract void SetParameterImpl(string name, Texture value);
|
||||
private protected abstract void SetStencilImpl(StencilParameters value);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
@@ -220,7 +222,7 @@ namespace Robust.Client.Graphics
|
||||
case Matrix3x2 i:
|
||||
instance.SetParameter(key, i);
|
||||
break;
|
||||
case Matrix4x4 i:
|
||||
case Matrix4 i:
|
||||
instance.SetParameter(key, i);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using System;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -21,10 +18,6 @@ namespace Robust.Client.Placement
|
||||
PlacementMode? CurrentMode { get; set; }
|
||||
PlacementInformation? CurrentPermission { get; set; }
|
||||
|
||||
IEntityManager EntityManager { get; }
|
||||
IEyeManager EyeManager { get; }
|
||||
IMapManager MapManager { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The direction to spawn the entity in (presently exposed for EntitySpawnWindow UI)
|
||||
/// </summary>
|
||||
@@ -56,15 +49,5 @@ namespace Robust.Client.Placement
|
||||
void ToggleEraserHijacked(PlacementHijack hijack);
|
||||
|
||||
void FrameUpdate(FrameEventArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// The name of the placement mode option to just use the default for the selected entity.
|
||||
/// </summary>
|
||||
const string DefaultModeName = "Default";
|
||||
|
||||
/// <summary>
|
||||
/// An array of the names of all available placement modes.
|
||||
/// </summary>
|
||||
string[] AllModeNames { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Map;
|
||||
using Vector2 = System.Numerics.Vector2;
|
||||
|
||||
namespace Robust.Client.Placement.Modes
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Map;
|
||||
using Vector2 = System.Numerics.Vector2;
|
||||
|
||||
namespace Robust.Client.Placement.Modes
|
||||
{
|
||||
|
||||
@@ -22,7 +22,6 @@ using Robust.Shared.Utility;
|
||||
using Robust.Shared.Log;
|
||||
using Direction = Robust.Shared.Maths.Direction;
|
||||
using Robust.Shared.Map.Components;
|
||||
using System.Linq;
|
||||
|
||||
namespace Robust.Client.Placement
|
||||
{
|
||||
@@ -33,23 +32,17 @@ namespace Robust.Client.Placement
|
||||
[Dependency] internal readonly IPlayerManager PlayerManager = default!;
|
||||
[Dependency] internal readonly IResourceCache ResourceCache = default!;
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] internal readonly IMapManager MapManager = default!;
|
||||
[Dependency] private readonly IGameTiming _time = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] internal readonly IEyeManager EyeManager = default!;
|
||||
[Dependency] internal readonly IInputManager InputManager = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] internal readonly IEntityManager EntityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IBaseClient _baseClient = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] internal readonly IClyde Clyde = default!;
|
||||
|
||||
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
|
||||
|
||||
public IEntityManager EntityManager => _entityManager;
|
||||
public IEyeManager EyeManager => _eyeManager;
|
||||
public IMapManager MapManager => _mapManager;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
private SharedMapSystem Maps => EntityManager.System<SharedMapSystem>();
|
||||
@@ -205,15 +198,6 @@ namespace Robust.Client.Placement
|
||||
}
|
||||
}
|
||||
|
||||
private string[]? _allModeNames;
|
||||
public string[] AllModeNames
|
||||
{
|
||||
get
|
||||
{
|
||||
return _allModeNames ??= [IPlacementManager.DefaultModeName, .. _modeDictionary.Keys.Order()];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler? DirectionChanged;
|
||||
|
||||
@@ -225,7 +209,7 @@ namespace Robust.Client.Placement
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_drawingShader = _prototypeManager.Index(UnshadedShader).Instance();
|
||||
_drawingShader = _prototypeManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
_sawmill = _logManager.GetSawmill("placement");
|
||||
|
||||
_networkManager.RegisterNetMessage<MsgPlacement>(HandlePlacementMessage);
|
||||
|
||||
@@ -22,10 +22,10 @@ namespace Robust.Client.ResourceManagement
|
||||
|
||||
public override void Load(IDependencyCollection dependencies, ResPath path)
|
||||
{
|
||||
if (IsInRsi(path))
|
||||
if (path.Directory.Filename.EndsWith(".rsi"))
|
||||
{
|
||||
var sawmill = dependencies.Resolve<ILogManager>().GetSawmill("res");
|
||||
sawmill.Warning(
|
||||
Logger.WarningS(
|
||||
"res",
|
||||
"Loading raw texture inside RSI: {Path}. Refer to the RSI state instead of the raw PNG.",
|
||||
path);
|
||||
}
|
||||
@@ -38,15 +38,6 @@ namespace Robust.Client.ResourceManagement
|
||||
LoadFinish(dependencies.Resolve<IResourceCache>(), data);
|
||||
}
|
||||
|
||||
private static bool IsInRsi(ResPath path)
|
||||
{
|
||||
var dir = path.Directory;
|
||||
if (dir == ResPath.Root)
|
||||
return false;
|
||||
|
||||
return dir.Filename.EndsWith(".rsi");
|
||||
}
|
||||
|
||||
internal static void LoadPreTextureData(IResourceManager cache, LoadStepData data)
|
||||
{
|
||||
using (var stream = cache.ContentFileRead(data.Path))
|
||||
|
||||
@@ -151,7 +151,7 @@ public sealed class EntitySpawningUIController : UIController
|
||||
{
|
||||
var newObjInfo = new PlacementInformation
|
||||
{
|
||||
PlacementOption = _placement.AllModeNames[args.Id],
|
||||
PlacementOption = EntitySpawnWindow.InitOpts[args.Id],
|
||||
EntityType = _placement.CurrentPermission!.EntityType,
|
||||
Range = 2,
|
||||
IsTile = _placement.CurrentPermission.IsTile
|
||||
@@ -364,11 +364,10 @@ public sealed class EntitySpawningUIController : UIController
|
||||
_window.SelectedButton = null;
|
||||
_window.SelectedPrototype = null;
|
||||
|
||||
|
||||
var overrideMode = _placement.AllModeNames[_window.OverrideMenu.SelectedId];
|
||||
var overrideMode = EntitySpawnWindow.InitOpts[_window.OverrideMenu.SelectedId];
|
||||
var newObjInfo = new PlacementInformation
|
||||
{
|
||||
PlacementOption = overrideMode != IPlacementManager.DefaultModeName ? overrideMode : item.Prototype.PlacementMode,
|
||||
PlacementOption = overrideMode != "Default" ? overrideMode : item.Prototype.PlacementMode,
|
||||
EntityType = item.PrototypeID,
|
||||
Range = 2,
|
||||
IsTile = false
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.ColorNaming;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -11,17 +8,22 @@ namespace Robust.Client.UserInterface.Controls;
|
||||
// condensed version of the original ColorSlider set
|
||||
public sealed class ColorSelectorSliders : Control
|
||||
{
|
||||
[Dependency] private readonly ILocalizationManager _localization = default!;
|
||||
|
||||
public Color Color
|
||||
{
|
||||
get => _currentColor;
|
||||
set
|
||||
{
|
||||
_currentColor = value;
|
||||
_colorData = GetStrategy().ToColorData(value);
|
||||
|
||||
UpdateAllSliders();
|
||||
switch (SelectorType)
|
||||
{
|
||||
case ColorSelectorType.Rgb:
|
||||
_colorData = new Vector4(_currentColor.R, _currentColor.G, _currentColor.B, _currentColor.A);
|
||||
break;
|
||||
case ColorSelectorType.Hsv:
|
||||
_colorData = Color.ToHsv(value);
|
||||
break;
|
||||
}
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,12 +32,19 @@ public sealed class ColorSelectorSliders : Control
|
||||
get => _currentType;
|
||||
set
|
||||
{
|
||||
switch ((_currentType, value))
|
||||
{
|
||||
case (ColorSelectorType.Rgb, ColorSelectorType.Hsv):
|
||||
_colorData = Color.ToHsv(Color);
|
||||
break;
|
||||
case (ColorSelectorType.Hsv, ColorSelectorType.Rgb):
|
||||
_colorData = new Vector4(_currentColor.R, _currentColor.G, _currentColor.B, _currentColor.A);
|
||||
break;
|
||||
}
|
||||
_currentType = value;
|
||||
_typeSelector.Select(_types.IndexOf(value));
|
||||
_colorData = GetStrategy().ToColorData(_currentColor);
|
||||
|
||||
UpdateType();
|
||||
UpdateAllSliders();
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,11 +61,7 @@ public sealed class ColorSelectorSliders : Control
|
||||
|
||||
public Action<Color>? OnColorChanged;
|
||||
|
||||
private readonly static HsvSliderStrategy _hsvStrategy = new();
|
||||
private readonly static RgbSliderStrategy _rgbStrategy = new();
|
||||
|
||||
private const float AlphaDivisor = 100.0f;
|
||||
|
||||
private bool _updating = false;
|
||||
private Color _currentColor = Color.White;
|
||||
private Vector4 _colorData;
|
||||
private ColorSelectorType _currentType = ColorSelectorType.Rgb;
|
||||
@@ -78,7 +83,6 @@ public sealed class ColorSelectorSliders : Control
|
||||
private Label _middleSliderLabel = new();
|
||||
private Label _bottomSliderLabel = new();
|
||||
private Label _alphaSliderLabel = new();
|
||||
private Label _colorDescriptionLabel = new();
|
||||
|
||||
private OptionButton _typeSelector;
|
||||
private List<ColorSelectorType> _types = new();
|
||||
@@ -89,8 +93,6 @@ public sealed class ColorSelectorSliders : Control
|
||||
|
||||
public ColorSelectorSliders()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_topColorSlider = new ColorableSlider
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
@@ -122,10 +124,10 @@ public sealed class ColorSelectorSliders : Control
|
||||
MaxValue = 1.0f,
|
||||
};
|
||||
|
||||
_topColorSlider.OnValueChanged += r => { OnSliderValueChanged(ColorSliderOrder.Top); };
|
||||
_middleColorSlider.OnValueChanged += r => { OnSliderValueChanged(ColorSliderOrder.Middle); };
|
||||
_bottomColorSlider.OnValueChanged += r => { OnSliderValueChanged(ColorSliderOrder.Bottom); };
|
||||
_alphaSlider.OnValueChanged += r => { OnSliderValueChanged(ColorSliderOrder.Alpha); };
|
||||
_topColorSlider.OnValueChanged += _ => { OnColorSet(); };
|
||||
_middleColorSlider.OnValueChanged += _ => { OnColorSet(); };
|
||||
_bottomColorSlider.OnValueChanged += _ => { OnColorSet(); };
|
||||
_alphaSlider.OnValueChanged += _ => { OnColorSet(); };
|
||||
|
||||
_topInputBox = new SpinBox
|
||||
{
|
||||
@@ -151,10 +153,25 @@ public sealed class ColorSelectorSliders : Control
|
||||
};
|
||||
_alphaInputBox.InitDefaultButtons();
|
||||
|
||||
_topInputBox.ValueChanged += value => { OnInputBoxValueChanged(value, ColorSliderOrder.Top); };
|
||||
_middleInputBox.ValueChanged += value => { OnInputBoxValueChanged(value, ColorSliderOrder.Middle); };
|
||||
_bottomInputBox.ValueChanged += value => { OnInputBoxValueChanged(value, ColorSliderOrder.Bottom); };
|
||||
_alphaInputBox.ValueChanged += value => { OnInputBoxValueChanged(value, ColorSliderOrder.Alpha); };
|
||||
_topInputBox.ValueChanged += value =>
|
||||
{
|
||||
_topColorSlider.Value = value.Value / GetColorValueDivisor(ColorSliderOrder.Top);
|
||||
};
|
||||
|
||||
_middleInputBox.ValueChanged += value =>
|
||||
{
|
||||
_middleColorSlider.Value = value.Value / GetColorValueDivisor(ColorSliderOrder.Middle);
|
||||
};
|
||||
|
||||
_bottomInputBox.ValueChanged += value =>
|
||||
{
|
||||
_bottomColorSlider.Value = value.Value / GetColorValueDivisor(ColorSliderOrder.Bottom);
|
||||
};
|
||||
|
||||
_alphaInputBox.ValueChanged += value =>
|
||||
{
|
||||
_alphaSlider.Value = value.Value / GetColorValueDivisor(ColorSliderOrder.Alpha);
|
||||
};
|
||||
|
||||
_alphaSliderLabel.Text = Loc.GetString("color-selector-sliders-alpha");
|
||||
|
||||
@@ -171,8 +188,6 @@ public sealed class ColorSelectorSliders : Control
|
||||
_typeSelector.Select(args.Id);
|
||||
};
|
||||
|
||||
_colorDescriptionLabel.Text = ColorNaming.Describe(_currentColor, _localization);
|
||||
|
||||
// TODO: Maybe some engine widgets could be laid out in XAML?
|
||||
|
||||
var rootBox = new BoxContainer
|
||||
@@ -185,7 +200,6 @@ public sealed class ColorSelectorSliders : Control
|
||||
rootBox.AddChild(headerBox);
|
||||
|
||||
headerBox.AddChild(_typeSelector);
|
||||
headerBox.AddChild(_colorDescriptionLabel);
|
||||
|
||||
var bodyBox = new BoxContainer()
|
||||
{
|
||||
@@ -227,114 +241,165 @@ public sealed class ColorSelectorSliders : Control
|
||||
Color = _currentColor;
|
||||
}
|
||||
|
||||
private ColorSliderStrategy GetStrategy()
|
||||
{
|
||||
return SelectorType switch
|
||||
{
|
||||
ColorSelectorType.Rgb => _rgbStrategy,
|
||||
ColorSelectorType.Hsv => _hsvStrategy,
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
}
|
||||
|
||||
private (Slider slider, SpinBox inputBox) GetSliderByOrder(ColorSliderOrder order)
|
||||
{
|
||||
return order switch
|
||||
{
|
||||
ColorSliderOrder.Top => (_topColorSlider, _topInputBox),
|
||||
ColorSliderOrder.Middle => (_middleColorSlider, _middleInputBox),
|
||||
ColorSliderOrder.Bottom => (_bottomColorSlider, _bottomInputBox),
|
||||
ColorSliderOrder.Alpha => (_alphaSlider, _alphaInputBox),
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
}
|
||||
|
||||
private float GetColorValueDivisor(ColorSliderOrder order)
|
||||
{
|
||||
return order == ColorSliderOrder.Alpha
|
||||
? AlphaDivisor
|
||||
: GetStrategy().GetColorValueDivisor(order);
|
||||
}
|
||||
|
||||
private void UpdateType()
|
||||
{
|
||||
var strategy = GetStrategy();
|
||||
var labels = strategy.GetSliderLabelTexts();
|
||||
_topSliderLabel.Text = labels.top;
|
||||
_middleSliderLabel.Text = labels.middle;
|
||||
_bottomSliderLabel.Text = labels.bottom;
|
||||
(string topLabel, string middleLabel, string bottomLabel) labels = GetSliderLabels();
|
||||
|
||||
_topStyle.ConfigureSlider(strategy.TopSliderStyle);
|
||||
_middleStyle.ConfigureSlider(strategy.MiddleSliderStyle);
|
||||
_bottomStyle.ConfigureSlider(strategy.BottomSliderStyle);
|
||||
_topSliderLabel.Text = labels.topLabel;
|
||||
_middleSliderLabel.Text = labels.middleLabel;
|
||||
_bottomSliderLabel.Text = labels.bottomLabel;
|
||||
|
||||
bool hsv = SelectorType == ColorSelectorType.Hsv;
|
||||
_topStyle.ConfigureSlider( hsv ? ColorSelectorStyleBox.ColorSliderPreset.Hue : ColorSelectorStyleBox.ColorSliderPreset.Red);
|
||||
_middleStyle.ConfigureSlider( hsv ? ColorSelectorStyleBox.ColorSliderPreset.Saturation : ColorSelectorStyleBox.ColorSliderPreset.Green);
|
||||
_bottomStyle.ConfigureSlider( hsv ? ColorSelectorStyleBox.ColorSliderPreset.Value : ColorSelectorStyleBox.ColorSliderPreset.Blue);
|
||||
}
|
||||
|
||||
private void UpdateSlider(ColorSliderOrder order)
|
||||
private void Update()
|
||||
{
|
||||
var (slider, inputBox) = GetSliderByOrder(order);
|
||||
var divisor = GetColorValueDivisor(order);
|
||||
// This code is a mess of UI events causing stack overflows. Also, updating one slider triggers all sliders to
|
||||
// update, which due to rounding errors causes them to actually change values, specifically for HSV sliders.
|
||||
if (_updating)
|
||||
return;
|
||||
|
||||
var dataValue = order switch
|
||||
{
|
||||
ColorSliderOrder.Top => _colorData.X,
|
||||
ColorSliderOrder.Middle => _colorData.Y,
|
||||
ColorSliderOrder.Bottom => _colorData.Z,
|
||||
ColorSliderOrder.Alpha => _colorData.W,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(order))
|
||||
};
|
||||
|
||||
slider.SetValueWithoutEvent(dataValue);
|
||||
inputBox.OverrideValue((int)(dataValue * divisor));
|
||||
}
|
||||
|
||||
private void UpdateSliderVisuals()
|
||||
{
|
||||
_updating = true;
|
||||
_topStyle.SetBaseColor(_colorData);
|
||||
_middleStyle.SetBaseColor(_colorData);
|
||||
_bottomStyle.SetBaseColor(_colorData);
|
||||
_colorDescriptionLabel.Text = ColorNaming.Describe(Color, _localization);
|
||||
}
|
||||
|
||||
private void UpdateAllSliders()
|
||||
{
|
||||
UpdateSliderVisuals();
|
||||
UpdateSlider(ColorSliderOrder.Top);
|
||||
UpdateSlider(ColorSliderOrder.Middle);
|
||||
UpdateSlider(ColorSliderOrder.Bottom);
|
||||
UpdateSlider(ColorSliderOrder.Alpha);
|
||||
switch (SelectorType)
|
||||
{
|
||||
case ColorSelectorType.Rgb:
|
||||
_topColorSlider.Value = _colorData.X;
|
||||
_middleColorSlider.Value = _colorData.Y;
|
||||
_bottomColorSlider.Value = _colorData.Z;
|
||||
|
||||
_topInputBox.Value = (int)(_colorData.X * 255.0f);
|
||||
_middleInputBox.Value = (int)(_colorData.Y * 255.0f);
|
||||
_bottomInputBox.Value = (int)(_colorData.Z * 255.0f);
|
||||
|
||||
break;
|
||||
case ColorSelectorType.Hsv:
|
||||
// dumb workaround because the formula for
|
||||
// HSV calculation results in a negative
|
||||
// number in any value past 300 degrees
|
||||
if (_colorData.X > 0)
|
||||
{
|
||||
_topColorSlider.Value = _colorData.X;
|
||||
_topInputBox.Value = (int)(_colorData.X * 360.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
_topInputBox.Value = (int)(_topColorSlider.Value * 360.0f);
|
||||
}
|
||||
|
||||
_middleColorSlider.Value = _colorData.Y;
|
||||
_bottomColorSlider.Value = _colorData.Z;
|
||||
|
||||
_middleInputBox.Value = (int)(_colorData.Y * 100.0f);
|
||||
_bottomInputBox.Value = (int)(_colorData.Z * 100.0f);
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_alphaSlider.Value = Color.A;
|
||||
_alphaInputBox.Value = (int)(Color.A * 100.0f);
|
||||
_updating = false;
|
||||
}
|
||||
|
||||
private bool IsSpinBoxValid(int value, ColorSliderOrder ordering)
|
||||
{
|
||||
var divisor = GetColorValueDivisor(ordering);
|
||||
var channelValue = value / divisor;
|
||||
if (value < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return channelValue >= 0.0f && channelValue <= 1.0f;
|
||||
if (ordering == ColorSliderOrder.Alpha)
|
||||
{
|
||||
return value <= 100;
|
||||
}
|
||||
|
||||
switch (SelectorType)
|
||||
{
|
||||
case ColorSelectorType.Rgb:
|
||||
return value <= byte.MaxValue;
|
||||
case ColorSelectorType.Hsv:
|
||||
switch (ordering)
|
||||
{
|
||||
case ColorSliderOrder.Top:
|
||||
return value <= 360;
|
||||
default:
|
||||
return value <= 100;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnInputBoxValueChanged(ValueChangedEventArgs args, ColorSliderOrder order)
|
||||
private (string, string, string) GetSliderLabels()
|
||||
{
|
||||
var (slider, _) = GetSliderByOrder(order);
|
||||
var value = args.Value / GetColorValueDivisor(order);
|
||||
switch (SelectorType)
|
||||
{
|
||||
case ColorSelectorType.Rgb:
|
||||
return (
|
||||
Loc.GetString("color-selector-sliders-red"),
|
||||
Loc.GetString("color-selector-sliders-green"),
|
||||
Loc.GetString("color-selector-sliders-blue")
|
||||
);
|
||||
case ColorSelectorType.Hsv:
|
||||
return (
|
||||
Loc.GetString("color-selector-sliders-hue"),
|
||||
Loc.GetString("color-selector-sliders-saturation"),
|
||||
Loc.GetString("color-selector-sliders-value")
|
||||
);
|
||||
}
|
||||
|
||||
// We are intentionally triggering the slider OnValueChanged event here.
|
||||
// This is so that the color data values of the sliders are updated accordingly.
|
||||
slider.Value = value;
|
||||
return ("ERR", "ERR", "ERR");
|
||||
}
|
||||
|
||||
private void OnSliderValueChanged(ColorSliderOrder order)
|
||||
private float GetColorValueDivisor(ColorSliderOrder order)
|
||||
{
|
||||
_colorData = new Vector4(
|
||||
_topColorSlider.Value,
|
||||
_middleColorSlider.Value,
|
||||
_bottomColorSlider.Value,
|
||||
_alphaSlider.Value);
|
||||
if (order == ColorSliderOrder.Alpha)
|
||||
{
|
||||
return 100.0f;
|
||||
}
|
||||
|
||||
_currentColor = GetStrategy().FromColorData(_colorData);
|
||||
switch (SelectorType)
|
||||
{
|
||||
case ColorSelectorType.Rgb:
|
||||
return 255.0f;
|
||||
case ColorSelectorType.Hsv:
|
||||
switch (order)
|
||||
{
|
||||
case ColorSliderOrder.Top:
|
||||
return 360.0f;
|
||||
default:
|
||||
return 100.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
private void OnColorSet()
|
||||
{
|
||||
// stack overflow otherwise due to value sets
|
||||
if (_updating)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_colorData = new Vector4(_topColorSlider.Value, _middleColorSlider.Value, _bottomColorSlider.Value, _alphaSlider.Value);
|
||||
|
||||
_currentColor = SelectorType switch
|
||||
{
|
||||
ColorSelectorType.Hsv => Color.FromHsv(_colorData),
|
||||
_ => new Color(_colorData.X, _colorData.Y, _colorData.Z, _colorData.W)
|
||||
};
|
||||
|
||||
Update();
|
||||
OnColorChanged?.Invoke(_currentColor);
|
||||
|
||||
UpdateSliderVisuals();
|
||||
UpdateSlider(order);
|
||||
}
|
||||
|
||||
private enum ColorSliderOrder
|
||||
@@ -350,121 +415,4 @@ public sealed class ColorSelectorSliders : Control
|
||||
Rgb,
|
||||
Hsv,
|
||||
}
|
||||
|
||||
private abstract class ColorSliderStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// The style preset used by the top slider.
|
||||
/// </summary>
|
||||
public abstract ColorSelectorStyleBox.ColorSliderPreset TopSliderStyle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The style preset used by the middle slider.
|
||||
/// </summary>
|
||||
public abstract ColorSelectorStyleBox.ColorSliderPreset MiddleSliderStyle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The style preset used by the bottom slider.
|
||||
/// </summary>
|
||||
public abstract ColorSelectorStyleBox.ColorSliderPreset BottomSliderStyle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Color to a Vector4 representation of its components.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Each value in the Vector4 must be between 0.0f and 1.0f; this is used in the
|
||||
/// context of slider values, which are between these ranges.
|
||||
/// </remarks>
|
||||
/// <param name="color">A Color to convert into Vector4 slider values.</param>
|
||||
/// <returns>A Vector4 representation of a Color's slider values.</returns>
|
||||
public abstract Vector4 ToColorData(Color color);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Vector4 representation of color slider values into a Color.
|
||||
/// </summary>
|
||||
/// <param name="colorData">A Vector4 representation of color slider values.</param>
|
||||
/// <returns>A color generated from slider values.</returns>
|
||||
public abstract Color FromColorData(Vector4 colorData);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a color component divisor for the given slider.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is used for converting slider values to/from color component values.
|
||||
/// For example, in RGB coloration, each channel ranges from 0 to 255,
|
||||
/// so if you had a slider value of 0.2, you would multiply 0.2 * 255 = 51
|
||||
/// for the "channel" value.
|
||||
///
|
||||
/// This does not apply to the Alpha channel, as the Alpha channel
|
||||
/// always uses the same divisor; this is defined in ColorSelectorSliders.
|
||||
/// </remarks>
|
||||
/// <param name="order">The slider to retrieve a divisor for.</param>
|
||||
/// <returns>The divisor for the given slider.</returns>
|
||||
public abstract float GetColorValueDivisor(ColorSliderOrder order);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a label text string for the first three color sliders.
|
||||
/// </summary>
|
||||
/// <returns>Label text strings for the top, middle, and bottom sliders.</returns>
|
||||
public abstract (string top, string middle, string bottom) GetSliderLabelTexts();
|
||||
}
|
||||
|
||||
private sealed class RgbSliderStrategy : ColorSliderStrategy
|
||||
{
|
||||
private const float ChannelMaxValue = byte.MaxValue;
|
||||
|
||||
public override ColorSelectorStyleBox.ColorSliderPreset TopSliderStyle
|
||||
=> ColorSelectorStyleBox.ColorSliderPreset.Red;
|
||||
public override ColorSelectorStyleBox.ColorSliderPreset MiddleSliderStyle
|
||||
=> ColorSelectorStyleBox.ColorSliderPreset.Green;
|
||||
public override ColorSelectorStyleBox.ColorSliderPreset BottomSliderStyle
|
||||
=> ColorSelectorStyleBox.ColorSliderPreset.Blue;
|
||||
|
||||
public override Vector4 ToColorData(Color color) => new(color.R, color.G, color.B, color.A);
|
||||
public override Color FromColorData(Vector4 colorData)
|
||||
=> new(colorData.X, colorData.Y, colorData.Z, colorData.W);
|
||||
|
||||
public override float GetColorValueDivisor(ColorSliderOrder order) => ChannelMaxValue;
|
||||
|
||||
public override (string top, string middle, string bottom) GetSliderLabelTexts()
|
||||
{
|
||||
return (
|
||||
Loc.GetString("color-selector-sliders-red"),
|
||||
Loc.GetString("color-selector-sliders-green"),
|
||||
Loc.GetString("color-selector-sliders-blue"));
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class HsvSliderStrategy : ColorSliderStrategy
|
||||
{
|
||||
private const float HueMaxValue = 360.0f;
|
||||
private const float SliderMaxValue = 100.0f;
|
||||
|
||||
public override ColorSelectorStyleBox.ColorSliderPreset TopSliderStyle
|
||||
=> ColorSelectorStyleBox.ColorSliderPreset.Hue;
|
||||
public override ColorSelectorStyleBox.ColorSliderPreset MiddleSliderStyle
|
||||
=> ColorSelectorStyleBox.ColorSliderPreset.Saturation;
|
||||
public override ColorSelectorStyleBox.ColorSliderPreset BottomSliderStyle
|
||||
=> ColorSelectorStyleBox.ColorSliderPreset.Value;
|
||||
|
||||
public override Vector4 ToColorData(Color color) => Color.ToHsv(color);
|
||||
public override Color FromColorData(Vector4 colorData) => Color.FromHsv(colorData);
|
||||
|
||||
public override float GetColorValueDivisor(ColorSliderOrder order)
|
||||
{
|
||||
return order switch
|
||||
{
|
||||
ColorSliderOrder.Top => HueMaxValue,
|
||||
_ => SliderMaxValue,
|
||||
};
|
||||
}
|
||||
|
||||
public override (string top, string middle, string bottom) GetSliderLabelTexts()
|
||||
{
|
||||
return (
|
||||
Loc.GetString("color-selector-sliders-hue"),
|
||||
Loc.GetString("color-selector-sliders-saturation"),
|
||||
Loc.GetString("color-selector-sliders-value"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,19 +21,19 @@ public sealed class ColorSelectorStyleBox : StyleBoxTexture
|
||||
/// <summary>
|
||||
/// Base background colour.
|
||||
/// </summary>
|
||||
public Vector4 BaseColor;
|
||||
public Robust.Shared.Maths.Vector4 BaseColor;
|
||||
|
||||
/// <summary>
|
||||
/// Colour to add to the background colour along the X-axis.
|
||||
/// I.e., from left to right the background colour will vary from (BaseColour) to (BaseColour + XAxis)
|
||||
/// </summary>
|
||||
public Vector4 XAxis;
|
||||
public Robust.Shared.Maths.Vector4 XAxis;
|
||||
|
||||
/// <summary>
|
||||
/// Colour to add to the background colour along the y-axis.
|
||||
/// I.e., from left to right the background colour will vary from (BaseColour) to (BaseColour + XAxis)
|
||||
/// </summary>
|
||||
public Vector4 YAxis;
|
||||
public Robust.Shared.Maths.Vector4 YAxis;
|
||||
|
||||
/// <summary>
|
||||
/// If true, then <see cref="BaseColor"/>, <see cref="XAxis"/>, and <see cref="YAxis"/> will be interpreted as HSVa
|
||||
@@ -93,14 +93,14 @@ public sealed class ColorSelectorStyleBox : StyleBoxTexture
|
||||
{
|
||||
var colorData = Hsv
|
||||
? Color.ToHsv(color)
|
||||
: new Vector4(color.R, color.G, color.B, color.A);
|
||||
: new Robust.Shared.Maths.Vector4(color.R, color.G, color.B, color.A);
|
||||
SetBaseColor(colorData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that sets the base color by taking in some color and removing the components that are controlled by the x and y axes.
|
||||
/// </summary>
|
||||
public void SetBaseColor(Vector4 colorData)
|
||||
public void SetBaseColor(Robust.Shared.Maths.Vector4 colorData)
|
||||
{
|
||||
BaseColor = colorData - colorData * XAxis - colorData * YAxis;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Vector2 = System.Numerics.Vector2;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls;
|
||||
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Placement;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
@@ -16,24 +12,34 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class EntitySpawnWindow : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly IPlacementManager _placement = default!;
|
||||
public static readonly string[] InitOpts =
|
||||
{
|
||||
"Default",
|
||||
"PlaceFree",
|
||||
"PlaceNearby",
|
||||
"SnapgridCenter",
|
||||
"SnapgridBorder",
|
||||
"AlignSimilar",
|
||||
"AlignTileAny",
|
||||
"AlignTileEmpty",
|
||||
"AlignTileNonDense",
|
||||
"AlignTileDense",
|
||||
"AlignWall",
|
||||
"AlignWallProper",
|
||||
};
|
||||
|
||||
public EntitySpawnButton? SelectedButton;
|
||||
public EntityPrototype? SelectedPrototype;
|
||||
|
||||
[Obsolete("Use IPlacementManager.AllModeNames")]
|
||||
public static string[] InitOpts =>IoCManager.Resolve<IPlacementManager>().AllModeNames;
|
||||
public EntitySpawnWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
MeasureButton.Measure(Vector2Helpers.Infinity);
|
||||
|
||||
var modes = _placement.AllModeNames;
|
||||
for (var i = 0; i < modes.Length; i++)
|
||||
for (var i = 0; i < InitOpts.Length; i++)
|
||||
{
|
||||
OverrideMenu.AddItem(modes[i], i);
|
||||
OverrideMenu.AddItem(InitOpts[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,27 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.ViewVariables.Editors;
|
||||
using Robust.Client.ViewVariables.Instances;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.ViewVariables.Editors.VVPropEditorNumeric;
|
||||
|
||||
namespace Robust.Client.ViewVariables
|
||||
{
|
||||
@@ -24,7 +31,8 @@ namespace Robust.Client.ViewVariables
|
||||
[Dependency] private readonly IClientNetManager _netManager = default!;
|
||||
[Dependency] private readonly IRobustSerializer _robustSerializer = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IViewVariableControlFactory _controlFactory = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
[Dependency] private readonly IResourceManager _resManager = default!;
|
||||
|
||||
private uint _nextReqId = 1;
|
||||
private readonly Vector2i _defaultWindowSize = (640, 420);
|
||||
@@ -56,7 +64,190 @@ namespace Robust.Client.ViewVariables
|
||||
|
||||
public VVPropEditor PropertyFor(Type? type)
|
||||
{
|
||||
return _controlFactory.CreateFor(type);
|
||||
// TODO: make this more flexible.
|
||||
if (type == null)
|
||||
{
|
||||
return new VVPropEditorDummy();
|
||||
}
|
||||
|
||||
if (type == typeof(sbyte))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.SByte);
|
||||
}
|
||||
|
||||
if (type == typeof(byte))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.Byte);
|
||||
}
|
||||
|
||||
if (type == typeof(ushort))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.UShort);
|
||||
}
|
||||
|
||||
if (type == typeof(short))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.Short);
|
||||
}
|
||||
|
||||
if (type == typeof(uint))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.UInt);
|
||||
}
|
||||
|
||||
if (type == typeof(int))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.Int);
|
||||
}
|
||||
|
||||
if (type == typeof(ulong))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.ULong);
|
||||
}
|
||||
|
||||
if (type == typeof(long))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.Long);
|
||||
}
|
||||
|
||||
if (type == typeof(float))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.Float);
|
||||
}
|
||||
|
||||
if (type == typeof(double))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.Double);
|
||||
}
|
||||
|
||||
if (type == typeof(decimal))
|
||||
{
|
||||
return new VVPropEditorNumeric(NumberType.Decimal);
|
||||
}
|
||||
|
||||
if (type == typeof(string))
|
||||
{
|
||||
return new VVPropEditorString();
|
||||
}
|
||||
|
||||
if (type == typeof(EntProtoId?))
|
||||
{
|
||||
return new VVPropEditorNullableEntProtoId();
|
||||
}
|
||||
|
||||
if (type == typeof(EntProtoId))
|
||||
{
|
||||
return new VVPropEditorEntProtoId();
|
||||
}
|
||||
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ProtoId<>))
|
||||
{
|
||||
var editor =
|
||||
(VVPropEditor)Activator.CreateInstance(
|
||||
typeof(VVPropEditorProtoId<>).MakeGenericType(type.GenericTypeArguments[0]))!;
|
||||
|
||||
IoCManager.InjectDependencies(editor);
|
||||
return editor;
|
||||
}
|
||||
|
||||
if (typeof(IPrototype).IsAssignableFrom(type) || typeof(ViewVariablesBlobMembers.PrototypeReferenceToken).IsAssignableFrom(type))
|
||||
{
|
||||
return (VVPropEditor)Activator.CreateInstance(typeof(VVPropEditorIPrototype<>).MakeGenericType(type))!;
|
||||
}
|
||||
|
||||
if (typeof(ISelfSerialize).IsAssignableFrom(type))
|
||||
{
|
||||
return (VVPropEditor)Activator.CreateInstance(typeof(VVPropEditorISelfSerializable<>).MakeGenericType(type))!;
|
||||
}
|
||||
|
||||
if (type.IsEnum)
|
||||
{
|
||||
return new VVPropEditorEnum();
|
||||
}
|
||||
|
||||
if (type == typeof(Vector2))
|
||||
{
|
||||
return new VVPropEditorVector2(intVec: false);
|
||||
}
|
||||
|
||||
if (type == typeof(Vector2i))
|
||||
{
|
||||
return new VVPropEditorVector2(intVec: true);
|
||||
}
|
||||
|
||||
if (type == typeof(bool))
|
||||
{
|
||||
return new VVPropEditorBoolean();
|
||||
}
|
||||
|
||||
if (type == typeof(Angle))
|
||||
{
|
||||
return new VVPropEditorAngle();
|
||||
}
|
||||
|
||||
if (type == typeof(Box2))
|
||||
{
|
||||
return new VVPropEditorUIBox2(VVPropEditorUIBox2.BoxType.Box2);
|
||||
}
|
||||
|
||||
if (type == typeof(Box2i))
|
||||
{
|
||||
return new VVPropEditorUIBox2(VVPropEditorUIBox2.BoxType.Box2i);
|
||||
}
|
||||
|
||||
if (type == typeof(UIBox2))
|
||||
{
|
||||
return new VVPropEditorUIBox2(VVPropEditorUIBox2.BoxType.UIBox2);
|
||||
}
|
||||
|
||||
if (type == typeof(UIBox2i))
|
||||
{
|
||||
return new VVPropEditorUIBox2(VVPropEditorUIBox2.BoxType.UIBox2i);
|
||||
}
|
||||
|
||||
if (type == typeof(EntityCoordinates))
|
||||
{
|
||||
return new VVPropEditorEntityCoordinates();
|
||||
}
|
||||
|
||||
if (type == typeof(EntityUid))
|
||||
{
|
||||
return new VVPropEditorEntityUid();
|
||||
}
|
||||
|
||||
if (type == typeof(NetEntity))
|
||||
{
|
||||
return new VVPropEditorNetEntity();
|
||||
}
|
||||
|
||||
if (type == typeof(Color))
|
||||
{
|
||||
return new VVPropEditorColor();
|
||||
}
|
||||
|
||||
if (type == typeof(TimeSpan))
|
||||
{
|
||||
return new VVPropEditorTimeSpan();
|
||||
}
|
||||
|
||||
if (typeof(SoundSpecifier).IsAssignableFrom(type))
|
||||
{
|
||||
var control = new VVPropEditorSoundSpecifier(_protoManager, _resManager);
|
||||
return control;
|
||||
}
|
||||
|
||||
if (type == typeof(ViewVariablesBlobMembers.ServerKeyValuePairToken) ||
|
||||
type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
|
||||
{
|
||||
return new VVPropEditorKeyValuePair();
|
||||
}
|
||||
|
||||
if (type != typeof(ViewVariablesBlobMembers.ServerValueTypeToken) && !type.IsValueType)
|
||||
{
|
||||
return new VVPropEditorReference();
|
||||
}
|
||||
|
||||
return new VVPropEditorDummy();
|
||||
}
|
||||
|
||||
public void OpenVV(object obj)
|
||||
@@ -72,7 +263,7 @@ namespace Robust.Client.ViewVariables
|
||||
instance = new ViewVariablesInstanceObject(this, _robustSerializer);
|
||||
}
|
||||
|
||||
var window = new DefaultWindow { Title = Loc.GetString("view-variables") };
|
||||
var window = new DefaultWindow {Title = Loc.GetString("view-variables")};
|
||||
instance.Initialize(window, obj);
|
||||
window.OnClose += () => _closeInstance(instance, false);
|
||||
_windows.Add(instance, window);
|
||||
@@ -82,7 +273,7 @@ namespace Robust.Client.ViewVariables
|
||||
|
||||
public void OpenVV(string path)
|
||||
{
|
||||
if (ReadPath(path) is { } obj)
|
||||
if (ReadPath(path) is {} obj)
|
||||
OpenVV(obj);
|
||||
}
|
||||
|
||||
@@ -93,7 +284,7 @@ namespace Robust.Client.ViewVariables
|
||||
Title = Loc.GetString("view-variables"),
|
||||
SetSize = _defaultWindowSize
|
||||
};
|
||||
var loadingLabel = new Label { Text = "Retrieving remote object data from server..." };
|
||||
var loadingLabel = new Label {Text = "Retrieving remote object data from server..."};
|
||||
window.Contents.AddChild(loadingLabel);
|
||||
|
||||
// We need to request the data, THEN create an instance.
|
||||
@@ -161,7 +352,7 @@ namespace Robust.Client.ViewVariables
|
||||
|
||||
public async Task<T> RequestData<T>(ViewVariablesRemoteSession session, ViewVariablesRequest meta) where T : ViewVariablesBlob
|
||||
{
|
||||
return (T)await RequestData(session, meta);
|
||||
return (T) await RequestData(session, meta);
|
||||
}
|
||||
|
||||
public void CloseSession(ViewVariablesRemoteSession session)
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Editors;
|
||||
|
||||
internal sealed class VVPropEditorEntProtoId : VVPropEditor
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||
|
||||
public VVPropEditorEntProtoId()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override Control MakeUI(object? value)
|
||||
{
|
||||
var lineEdit = new LineEdit
|
||||
{
|
||||
Text = (EntProtoId)(value ?? ""),
|
||||
Text = (EntProtoId) (value ?? ""),
|
||||
Editable = !ReadOnly,
|
||||
HorizontalExpand = true,
|
||||
};
|
||||
@@ -27,9 +19,7 @@ internal sealed class VVPropEditorEntProtoId : VVPropEditor
|
||||
{
|
||||
lineEdit.OnTextEntered += e =>
|
||||
{
|
||||
var id = (EntProtoId)e.Text;
|
||||
if (_protoMan.HasIndex(id))
|
||||
ValueChanged(id);
|
||||
ValueChanged((EntProtoId) e.Text);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,7 @@ namespace Robust.Client.ViewVariables.Editors
|
||||
{
|
||||
var hBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
MinWidth = 350
|
||||
Orientation = LayoutOrientation.Horizontal
|
||||
};
|
||||
|
||||
dynamic d = value!;
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Editors;
|
||||
|
||||
internal sealed class VVPropEditorNullableEntProtoId : VVPropEditor
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||
|
||||
public VVPropEditorNullableEntProtoId()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override Control MakeUI(object? value)
|
||||
{
|
||||
var lineEdit = new LineEdit
|
||||
{
|
||||
Text = value is EntProtoId protoId ? protoId.Id : "",
|
||||
Text = value is EntProtoId protoId ? protoId.Id : "",
|
||||
Editable = !ReadOnly,
|
||||
HorizontalExpand = true,
|
||||
};
|
||||
@@ -33,9 +25,7 @@ internal sealed class VVPropEditorNullableEntProtoId : VVPropEditor
|
||||
}
|
||||
else
|
||||
{
|
||||
var id = (EntProtoId)e.Text;
|
||||
if (_protoMan.HasIndex(id))
|
||||
ValueChanged(id);
|
||||
ValueChanged((EntProtoId) e.Text);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
using CS = System.Runtime.CompilerServices;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Editors;
|
||||
|
||||
internal sealed class VVPropEditorTuple : VVPropEditor
|
||||
{
|
||||
[Dependency] private readonly IClientViewVariablesManagerInternal _viewVariables = default!;
|
||||
|
||||
private bool _readOnly;
|
||||
private readonly List<object?> _tuple = [];
|
||||
private readonly List<VVPropEditor> _editors = [];
|
||||
private Type? _actualType;
|
||||
|
||||
public VVPropEditorTuple()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override Control MakeUI(object? value)
|
||||
{
|
||||
var vBoxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
MinSize = new Vector2(240, 0),
|
||||
};
|
||||
|
||||
if (value is not CS.ITuple tuple)
|
||||
return vBoxContainer;
|
||||
|
||||
// Zero-tuples exist?? I'm just not going to bother with that.
|
||||
if (tuple.Length == 0)
|
||||
return vBoxContainer;
|
||||
|
||||
_actualType = value.GetType();
|
||||
|
||||
// We disallow editing tuples with arity more than 7 since they would
|
||||
// a pain to construct via reflection. And no one should have tuples
|
||||
// that large. (8 is bad because last element becomes a ValueTuple<>)
|
||||
_readOnly = ReadOnly
|
||||
|| tuple.Length >= 8
|
||||
|| !IsValueTuple(_actualType); // ToTuple only supports ValueTuples
|
||||
|
||||
for (var i = 0; i < tuple.Length; i++)
|
||||
{
|
||||
var editor = CreateBox(tuple[i], vBoxContainer);
|
||||
var index = i; // thanks C#
|
||||
editor.OnValueChanged += (o, reinterpret) => ValueChanged(ToTuple(o, index), reinterpret);
|
||||
|
||||
_tuple.Add(tuple[i]);
|
||||
_editors.Add(editor);
|
||||
}
|
||||
return vBoxContainer;
|
||||
}
|
||||
|
||||
private bool IsValueTuple(Type actualType)
|
||||
{
|
||||
if (!actualType.IsGenericType)
|
||||
return false;
|
||||
|
||||
Type[] valueTupleTypes =
|
||||
[
|
||||
typeof(ValueTuple<>), typeof(ValueTuple<,>), typeof(ValueTuple<,,>), typeof(ValueTuple<,,,>),
|
||||
typeof(ValueTuple<,,,,>), typeof(ValueTuple<,,,,,>), typeof(ValueTuple<,,,,,,>), typeof(ValueTuple<,,,,,,,>)
|
||||
];
|
||||
return valueTupleTypes.Contains(actualType.GetGenericTypeDefinition());
|
||||
}
|
||||
|
||||
private CS.ITuple ToTuple(object? changed, int index)
|
||||
{
|
||||
_tuple[index] = changed;
|
||||
|
||||
// I can't seem to make this work using .GetMethod.
|
||||
// If you know of a better way of doing this... please do.
|
||||
return (CS.ITuple)typeof(ValueTuple).GetMethods()
|
||||
.First(x => x is { Name: nameof(ValueTuple.Create), IsGenericMethod: true }
|
||||
&& x.GetParameters().Length == _tuple.Count)
|
||||
.MakeGenericMethod(_actualType!.GenericTypeArguments)
|
||||
.Invoke(null, _tuple.ToArray())!;
|
||||
}
|
||||
|
||||
private VVPropEditor CreateBox<T>(T? entry, BoxContainer parent)
|
||||
{
|
||||
var editor = _viewVariables.PropertyFor(entry?.GetType());
|
||||
// We disallow editing of serverside-only tuples because, uh, I don't
|
||||
// know how to make it work. Presumably it'd have to be something
|
||||
// similarly cursed to what I did in ToTuple above.
|
||||
parent.AddChild(editor.Initialize(entry, _readOnly));
|
||||
return editor;
|
||||
}
|
||||
|
||||
// Allow selecting, for example, dictionaries within the tuple.
|
||||
// Wait, why do you have a field with a tuple that holds a dictionary??
|
||||
public override void WireNetworkSelector(uint sessionId, object[] selectorChain)
|
||||
{
|
||||
for (var i = 0; i < _editors.Count; i++)
|
||||
{
|
||||
object[] chain = [..selectorChain, new ViewVariablesTupleIndexSelector(i)];
|
||||
_editors[i].WireNetworkSelector(sessionId, chain);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
using System;
|
||||
using Robust.Client.ViewVariables.Editors;
|
||||
|
||||
namespace Robust.Client.ViewVariables;
|
||||
|
||||
/// <summary>
|
||||
/// Factory that creates UI controls for viewing variables based on provided property type.
|
||||
/// </summary>
|
||||
public interface IViewVariableControlFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates UI control for viewing variable of type <paramref name="type"/>.
|
||||
/// Returns <see cref="VVPropEditorDummy"/> if fails to find proper control.
|
||||
/// First will look for control factory by type match (<see cref="RegisterForType{T}"/>),
|
||||
/// then will try to check each registered conditional factory
|
||||
/// (<see cref="RegisterWithCondition"/>, <see cref="RegisterForAssignableFrom{T}"/> and similar methods).
|
||||
/// </summary>
|
||||
VVPropEditor CreateFor(Type? type);
|
||||
|
||||
/// <summary>
|
||||
/// Registers factory method for vv control. This factory method will be used if provided type will be exactly equal to <typeparamref name="T"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of property, for which this control factory should be used. Will only be used in case of exact match.</typeparam>
|
||||
/// <param name="factoryMethod">Factory method that creates VV control.</param>
|
||||
void RegisterForType<T>(Func<Type, VVPropEditor> factoryMethod);
|
||||
|
||||
/// <summary>
|
||||
/// Registers factory method for vv control. This factory method will be used if provided type will be assignable to <typeparamref name="T"/>.
|
||||
/// Conditions will be checked if none of <see cref="RegisterForType{T}"/> registrations were fitting.
|
||||
/// </summary>
|
||||
/// <param name="factoryMethod">Factory method that creates VV control.</param>
|
||||
/// <param name="insertPosition">Where new condition should be inserted - at start or at the end of the list.</param>
|
||||
void RegisterForAssignableFrom<T>(Func<Type, VVPropEditor> factoryMethod, InsertPosition insertPosition = InsertPosition.First);
|
||||
|
||||
/// <summary>
|
||||
/// Registers factory method for vv control. This factory method will be used if <paramref name="condition"/> will return true for provided type.
|
||||
/// Conditions will be checked if none of <see cref="RegisterForType{T}"/> registrations were fitting.
|
||||
/// </summary>
|
||||
/// <param name="condition">Condition, that will decide, if factory should be used for provided type.</param>
|
||||
/// <param name="factory">Factory method that creates VV control.</param>
|
||||
/// <param name="insertPosition">Where new condition should be inserted - at start or at the end of the list. </param>
|
||||
void RegisterWithCondition(Func<Type, bool> condition, Func<Type, VVPropEditor> factory, InsertPosition insertPosition = InsertPosition.First);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicator, where item should be inserted in list.
|
||||
/// </summary>
|
||||
public enum InsertPosition
|
||||
{
|
||||
/// <summary>
|
||||
/// Item will be inserted as first in list.
|
||||
/// </summary>
|
||||
First,
|
||||
/// <summary>
|
||||
/// Item will be inserted as last in list.
|
||||
/// </summary>
|
||||
Last
|
||||
}
|
||||
@@ -151,7 +151,7 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
ViewVariablesTraitMembers.CreateMemberGroupHeader(
|
||||
ref first,
|
||||
PrettyPrint.PrintUserFacingTypeShort(group.Key, 2),
|
||||
clientVBox.Children);
|
||||
clientVBox);
|
||||
|
||||
foreach (var control in group)
|
||||
{
|
||||
@@ -525,7 +525,7 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
var first = true;
|
||||
foreach (var (groupName, groupMembers) in _membersBlob!.MemberGroups)
|
||||
{
|
||||
ViewVariablesTraitMembers.CreateMemberGroupHeader(ref first, groupName, _serverVariables.Children);
|
||||
ViewVariablesTraitMembers.CreateMemberGroupHeader(ref first, groupName, _serverVariables);
|
||||
|
||||
foreach (var propertyData in groupMembers)
|
||||
{
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Robust.Client.ViewVariables.Traits
|
||||
CreateMemberGroupHeader(
|
||||
ref first,
|
||||
PrettyPrint.PrintUserFacingTypeShort(group.Key, 2),
|
||||
replacementControls);
|
||||
_memberList);
|
||||
|
||||
foreach (var control in group)
|
||||
{
|
||||
@@ -67,7 +67,7 @@ namespace Robust.Client.ViewVariables.Traits
|
||||
var first = true;
|
||||
foreach (var (groupName, groupMembers) in blob.MemberGroups)
|
||||
{
|
||||
CreateMemberGroupHeader(ref first, groupName, replacementControls);
|
||||
CreateMemberGroupHeader(ref first, groupName, _memberList);
|
||||
|
||||
foreach (var propertyData in groupMembers)
|
||||
{
|
||||
@@ -95,15 +95,15 @@ namespace Robust.Client.ViewVariables.Traits
|
||||
}
|
||||
}
|
||||
|
||||
internal static void CreateMemberGroupHeader(ref bool first, string groupName, ICollection<Control> container)
|
||||
internal static void CreateMemberGroupHeader(ref bool first, string groupName, Control container)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
container.Add(new Control {MinSize = new Vector2(0, 16)});
|
||||
container.AddChild(new Control {MinSize = new Vector2(0, 16)});
|
||||
}
|
||||
|
||||
first = false;
|
||||
container.Add(new Label {Text = groupName, FontColorOverride = Color.DarkGray});
|
||||
container.AddChild(new Label {Text = groupName, FontColorOverride = Color.DarkGray});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Client.ViewVariables.Editors;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using CS = System.Runtime.CompilerServices;
|
||||
|
||||
namespace Robust.Client.ViewVariables;
|
||||
|
||||
internal sealed class ViewVariableControlFactory : IViewVariableControlFactory
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
[Dependency] private readonly IResourceManager _resManager = default!;
|
||||
[Dependency] private readonly IDependencyCollection _dependencyManager = default!;
|
||||
|
||||
private readonly Dictionary<Type, Func<Type, VVPropEditor>> _factoriesByType = new();
|
||||
private readonly List<ConditionalViewVariableFactoryMethodContainer> _factoriesWithCondition = new();
|
||||
|
||||
public ViewVariableControlFactory()
|
||||
{
|
||||
RegisterForType<sbyte>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.SByte));
|
||||
RegisterForType<byte>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.Byte));
|
||||
RegisterForType<ushort>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.UShort));
|
||||
RegisterForType<short>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.Short));
|
||||
RegisterForType<uint>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.UInt));
|
||||
RegisterForType<int>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.Int));
|
||||
RegisterForType<ulong>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.ULong));
|
||||
RegisterForType<long>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.Long));
|
||||
RegisterForType<float>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.Float));
|
||||
RegisterForType<float>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.Float));
|
||||
RegisterForType<double>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.Double));
|
||||
RegisterForType<decimal>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.Decimal));
|
||||
RegisterForType<string>(_ => new VVPropEditorString());
|
||||
RegisterForType<EntProtoId?>(_ => new VVPropEditorNullableEntProtoId());
|
||||
RegisterForType<EntProtoId>(_ => new VVPropEditorEntProtoId());
|
||||
RegisterForType<Vector2>(_ => new VVPropEditorVector2(intVec: false));
|
||||
RegisterForType<Vector2i>(_ => new VVPropEditorVector2(intVec: true));
|
||||
RegisterForType<bool>(_ => new VVPropEditorBoolean());
|
||||
RegisterForType<Angle>(_ => new VVPropEditorAngle());
|
||||
RegisterForType<Box2>(_ => new VVPropEditorUIBox2(VVPropEditorUIBox2.BoxType.Box2));
|
||||
RegisterForType<Box2i>(_ => new VVPropEditorUIBox2(VVPropEditorUIBox2.BoxType.Box2i));
|
||||
RegisterForType<UIBox2>(_ => new VVPropEditorUIBox2(VVPropEditorUIBox2.BoxType.UIBox2));
|
||||
RegisterForType<UIBox2i>(_ => new VVPropEditorUIBox2(VVPropEditorUIBox2.BoxType.UIBox2i));
|
||||
RegisterForType<EntityCoordinates>(_ => new VVPropEditorEntityCoordinates());
|
||||
RegisterForType<EntityUid>(_ => new VVPropEditorEntityUid());
|
||||
RegisterForType<NetEntity>(_ => new VVPropEditorNetEntity());
|
||||
RegisterForType<Color>(_ => new VVPropEditorColor());
|
||||
RegisterForType<TimeSpan>(_ => new VVPropEditorTimeSpan());
|
||||
|
||||
RegisterWithCondition(
|
||||
type => type != typeof(ViewVariablesBlobMembers.ServerValueTypeToken) && !type.IsValueType,
|
||||
_ => new VVPropEditorReference()
|
||||
);
|
||||
RegisterWithCondition(
|
||||
type => type == typeof(ViewVariablesBlobMembers.ServerKeyValuePairToken)
|
||||
|| type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>),
|
||||
_ => new VVPropEditorKeyValuePair()
|
||||
);
|
||||
|
||||
RegisterForAssignableFrom<CS.ITuple>(_ => new VVPropEditorTuple());
|
||||
RegisterForAssignableFrom<SoundSpecifier>(_ => new VVPropEditorSoundSpecifier(_protoManager, _resManager));
|
||||
RegisterForAssignableFrom<ISelfSerialize>(type => CreateGenericEditor(type, typeof(VVPropEditorISelfSerializable<>)));
|
||||
RegisterForAssignableFrom<ViewVariablesBlobMembers.PrototypeReferenceToken>(type => CreateGenericEditor(type, typeof(VVPropEditorIPrototype<>)));
|
||||
RegisterForAssignableFrom<IPrototype>(type => CreateGenericEditor(type, typeof(VVPropEditorIPrototype<>)));
|
||||
RegisterWithCondition(
|
||||
type => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ProtoId<>),
|
||||
type =>
|
||||
{
|
||||
var typeArgumentType = type.GenericTypeArguments[0];
|
||||
var editor = CreateGenericEditor(typeArgumentType, typeof(VVPropEditorProtoId<>));
|
||||
|
||||
_dependencyManager.InjectDependencies(editor);
|
||||
return editor;
|
||||
}
|
||||
);
|
||||
RegisterWithCondition(type => type.IsEnum, _ => new VVPropEditorEnum());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterForType<T>(Func<Type, VVPropEditor> factoryMethod)
|
||||
{
|
||||
_factoriesByType[typeof(T)] = factoryMethod;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterForAssignableFrom<T>(Func<Type, VVPropEditor> factoryMethod, InsertPosition insertPosition = InsertPosition.First)
|
||||
{
|
||||
int insertIndex = insertPosition == InsertPosition.Last && _factoriesWithCondition.Count > 0
|
||||
? _factoriesWithCondition.Count - 1
|
||||
: 0;
|
||||
_factoriesWithCondition.Insert(insertIndex, new(type => typeof(T).IsAssignableFrom(type), factoryMethod));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterWithCondition(Func<Type, bool> condition, Func<Type, VVPropEditor> factory, InsertPosition insertPosition = InsertPosition.First)
|
||||
{
|
||||
int insertIndex = insertPosition == InsertPosition.Last && _factoriesWithCondition.Count > 0
|
||||
? _factoriesWithCondition.Count - 1
|
||||
: 0;
|
||||
_factoriesWithCondition.Insert(insertIndex, new(condition, factory));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public VVPropEditor CreateFor(Type? type)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
return new VVPropEditorDummy();
|
||||
}
|
||||
|
||||
if (_factoriesByType.TryGetValue(type, out var factory))
|
||||
{
|
||||
return factory(type);
|
||||
}
|
||||
|
||||
foreach (var factoryWithCondition in _factoriesWithCondition)
|
||||
{
|
||||
if (factoryWithCondition.CanExecute(type))
|
||||
{
|
||||
return factoryWithCondition.Create(type);
|
||||
}
|
||||
}
|
||||
|
||||
return new VVPropEditorDummy();
|
||||
}
|
||||
|
||||
private static VVPropEditor CreateGenericEditor(Type typeArgumentType, Type genericConrolType)
|
||||
{
|
||||
var typeToCreate = genericConrolType.MakeGenericType(typeArgumentType);
|
||||
return (VVPropEditor)Activator.CreateInstance(typeToCreate)!;
|
||||
}
|
||||
|
||||
private sealed record ConditionalViewVariableFactoryMethodContainer(
|
||||
Func<Type, bool> CanExecute,
|
||||
Func<Type, VVPropEditor> Create
|
||||
);
|
||||
}
|
||||
@@ -73,10 +73,30 @@ namespace Robust.Client.ViewVariables
|
||||
public VVPropEditor SetProperty(ViewVariablesBlobMembers.MemberData member)
|
||||
{
|
||||
NameLabel.Text = member.Name;
|
||||
var type = member.Value?.GetType();
|
||||
var type = Type.GetType(member.Type);
|
||||
|
||||
_bottomLabel.Text = $"Type: {member.TypePretty}";
|
||||
var editor = _viewVariablesManager.PropertyFor(type);
|
||||
VVPropEditor editor;
|
||||
if (type == null || !_robustSerializer.CanSerialize(type))
|
||||
{
|
||||
// Type is server-side only.
|
||||
// Info whether it's reference or value type can be figured out from the sent value.
|
||||
if (type?.IsValueType == true || member.Value is ViewVariablesBlobMembers.ServerValueTypeToken)
|
||||
{
|
||||
// Value type, just display it stringified read-only.
|
||||
editor = new VVPropEditorDummy();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Has to be a reference type at this point.
|
||||
DebugTools.Assert(member.Value is ViewVariablesBlobMembers.ReferenceToken || member.Value == null || type?.IsClass == true || type?.IsInterface == true);
|
||||
editor = _viewVariablesManager.PropertyFor(type ?? typeof(object));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
editor = _viewVariablesManager.PropertyFor(type);
|
||||
}
|
||||
|
||||
var view = editor.Initialize(member.Value, !member.Editable);
|
||||
if (!view.HorizontalExpand)
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Robust.Packaging.AssetProcessing.Passes;
|
||||
/// </summary>
|
||||
public sealed class AssetPassAudioMetadata : AssetPass
|
||||
{
|
||||
private readonly List<(string ID, TimeSpan Length)> _audioMetadata = new();
|
||||
private readonly List<AudioMetadataPrototype> _audioMetadata = new();
|
||||
private readonly string _metadataPath;
|
||||
|
||||
public AssetPassAudioMetadata(string metadataPath = "Prototypes/_audio_metadata.yml")
|
||||
@@ -32,7 +32,11 @@ public sealed class AssetPassAudioMetadata : AssetPass
|
||||
|
||||
lock (_audioMetadata)
|
||||
{
|
||||
_audioMetadata.Add(("/" + file.Path, metadata.Length));
|
||||
_audioMetadata.Add(new AudioMetadataPrototype()
|
||||
{
|
||||
ID = "/" + file.Path,
|
||||
Length = metadata.Length,
|
||||
});
|
||||
}
|
||||
|
||||
return AssetFileAcceptResult.Consumed;
|
||||
|
||||
@@ -19,19 +19,7 @@ public sealed class RobustClientPackaging
|
||||
AssetPass pass,
|
||||
CancellationToken cancel = default)
|
||||
{
|
||||
await WriteClientResources(contentDir, pass, new HashSet<string>(), cancel);
|
||||
}
|
||||
|
||||
public static async Task WriteClientResources(
|
||||
string contentDir,
|
||||
AssetPass pass,
|
||||
IReadOnlySet<string> additionalIgnoredResources,
|
||||
CancellationToken cancel = default)
|
||||
{
|
||||
var ignoreSet = ClientIgnoredResources
|
||||
.Union(RobustSharedPackaging.SharedIgnoredResources)
|
||||
.Union(additionalIgnoredResources)
|
||||
.ToHashSet();
|
||||
var ignoreSet = ClientIgnoredResources.Union(RobustSharedPackaging.SharedIgnoredResources).ToHashSet();
|
||||
|
||||
await RobustSharedPackaging.DoResourceCopy(Path.Combine(contentDir, "Resources"), pass, ignoreSet, cancel: cancel);
|
||||
}
|
||||
|
||||
@@ -15,22 +15,13 @@ public sealed class RobustServerPackaging
|
||||
string contentDir,
|
||||
AssetPass pass,
|
||||
CancellationToken cancel = default)
|
||||
{
|
||||
await WriteServerResources(contentDir, pass, new HashSet<string>(), cancel);
|
||||
}
|
||||
|
||||
public static async Task WriteServerResources(
|
||||
string contentDir,
|
||||
AssetPass pass,
|
||||
IReadOnlySet<string> additionalIgnoredResources,
|
||||
CancellationToken cancel = default)
|
||||
{
|
||||
var ignoreSet = ServerIgnoresResources.Union(RobustSharedPackaging.SharedIgnoredResources).ToHashSet();
|
||||
|
||||
await RobustSharedPackaging.DoResourceCopy(
|
||||
Path.Combine(contentDir, "Resources"),
|
||||
pass,
|
||||
ignoreSet.Union(additionalIgnoredResources).ToHashSet(),
|
||||
ignoreSet,
|
||||
cancel: cancel);
|
||||
|
||||
await RobustSharedPackaging.DoResourceCopy(
|
||||
|
||||
@@ -43,22 +43,4 @@ public static class AttributeHelper
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static bool HasAttribute(
|
||||
INamedTypeSymbol symbol,
|
||||
INamedTypeSymbol attribute,
|
||||
[NotNullWhen(true)] out AttributeData? matchedAttribute)
|
||||
{
|
||||
matchedAttribute = null;
|
||||
foreach (var typeAttribute in symbol.GetAttributes())
|
||||
{
|
||||
if (SymbolEqualityComparer.Default.Equals(typeAttribute.AttributeClass, attribute))
|
||||
{
|
||||
matchedAttribute = typeAttribute;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,9 +40,6 @@ public static class Diagnostics
|
||||
public const string IdObsoleteInheritance = "RA0034";
|
||||
public const string IdObsoleteInheritanceWithMessage = "RA0035";
|
||||
public const string IdDataFieldYamlSerializable = "RA0036";
|
||||
public const string IdPrototypeNetSerializable = "RA0037";
|
||||
public const string IdPrototypeSerializable = "RA0038";
|
||||
public const string IdPrototypeInstantiation = "RA0039";
|
||||
|
||||
public static SuppressionDescriptor MeansImplicitAssignment =>
|
||||
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Robust.Roslyn.Shared;
|
||||
|
||||
public static class TypeSymbolHelper
|
||||
{
|
||||
public static bool ShittyTypeMatch(ITypeSymbol type, string attributeMetadataName)
|
||||
public static bool ShittyTypeMatch(INamedTypeSymbol type, string attributeMetadataName)
|
||||
{
|
||||
// Doing it like this only allocates when the type actually matches, which is good enough for me right now.
|
||||
if (!attributeMetadataName.EndsWith(type.Name))
|
||||
@@ -15,7 +15,7 @@ public static class TypeSymbolHelper
|
||||
return type.ToDisplayString() == attributeMetadataName;
|
||||
}
|
||||
|
||||
public static bool ImplementsInterface(ITypeSymbol type, string interfaceTypeName)
|
||||
public static bool ImplementsInterface(INamedTypeSymbol type, string interfaceTypeName)
|
||||
{
|
||||
foreach (var interfaceType in type.AllInterfaces)
|
||||
{
|
||||
@@ -25,33 +25,4 @@ public static class TypeSymbolHelper
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ImplementsInterface(ITypeSymbol type, INamedTypeSymbol interfaceType)
|
||||
{
|
||||
foreach (var @interface in type.AllInterfaces)
|
||||
{
|
||||
if (SymbolEqualityComparer.Default.Equals(@interface, interfaceType))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all Members of a symbol, including those that are inherited.
|
||||
/// We need this because sometimes Components have abstract parents with autonetworked datafields.
|
||||
/// </summary>
|
||||
public static IEnumerable<ISymbol> GetAllMembersIncludingInherited(INamedTypeSymbol type)
|
||||
{
|
||||
var current = type;
|
||||
while (current != null)
|
||||
{
|
||||
foreach (var member in current.GetMembers())
|
||||
{
|
||||
yield return member;
|
||||
}
|
||||
|
||||
current = current.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ namespace Robust.Server.Console.Commands
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
return LoadMap.GetCompletionResult(shell, args, _resource, Loc);
|
||||
return LoadMap.GetCompletionResult(shell, args, _resource);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,24 +235,24 @@ namespace Robust.Server.Console.Commands
|
||||
|
||||
public override string Command => "loadmap";
|
||||
|
||||
public static CompletionResult GetCompletionResult(IConsoleShell shell, string[] args, IResourceManager resource, ILocalizationManager loc)
|
||||
public static CompletionResult GetCompletionResult(IConsoleShell shell, string[] args, IResourceManager resource)
|
||||
{
|
||||
switch (args.Length)
|
||||
{
|
||||
case 1:
|
||||
return CompletionResult.FromHint(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)
|
||||
.Concat(CompletionHelper.ContentFilePath(args[1], resource));
|
||||
return CompletionResult.FromHintOptions(opts, loc.GetString("cmd-hint-savemap-path"));
|
||||
return CompletionResult.FromHintOptions(opts, Loc.GetString("cmd-hint-savemap-path"));
|
||||
case 3:
|
||||
return CompletionResult.FromHint(loc.GetString("cmd-hint-loadmap-x-position"));
|
||||
return CompletionResult.FromHint(Loc.GetString("cmd-hint-loadmap-x-position"));
|
||||
case 4:
|
||||
return CompletionResult.FromHint(loc.GetString("cmd-hint-loadmap-y-position"));
|
||||
return CompletionResult.FromHint(Loc.GetString("cmd-hint-loadmap-y-position"));
|
||||
case 5:
|
||||
return CompletionResult.FromHint(loc.GetString("cmd-hint-loadmap-rotation"));
|
||||
return CompletionResult.FromHint(Loc.GetString("cmd-hint-loadmap-rotation"));
|
||||
case 6:
|
||||
return CompletionResult.FromHint(loc.GetString("cmd-hint-loadmap-uids"));
|
||||
return CompletionResult.FromHint(Loc.GetString("cmd-hint-loadmap-uids"));
|
||||
}
|
||||
|
||||
return CompletionResult.Empty;
|
||||
@@ -260,7 +260,7 @@ namespace Robust.Server.Console.Commands
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
return GetCompletionResult(shell, args, _resource, Loc);
|
||||
return GetCompletionResult(shell, args, _resource);
|
||||
}
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
|
||||
@@ -32,7 +32,7 @@ internal sealed partial class PvsSystem
|
||||
|
||||
if (component.Deleted || !component.Initialized)
|
||||
{
|
||||
Log.Error($"Entity manager returned deleted or uninitialized component of type {component.GetType()} on entity {ToPrettyString(entityUid)} while generating entity state data for {player?.Name ?? "replay"}");
|
||||
Log.Error("Entity manager returned deleted or uninitialized components while sending entity data");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Server.ViewVariables.Traits;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Log;
|
||||
@@ -136,10 +135,6 @@ namespace Robust.Server.ViewVariables
|
||||
dynamic kv = value;
|
||||
value = kvPair.Key ? kv.Key : kv.Value;
|
||||
break;
|
||||
case ViewVariablesTupleIndexSelector indexSelector
|
||||
when value is ITuple tuple:
|
||||
value = indexSelector.Index <= tuple.Length - 1 ? tuple[indexSelector.Index] : null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Server.ViewVariables.Traits;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -142,19 +141,6 @@ namespace Robust.Server.ViewVariables
|
||||
};
|
||||
}
|
||||
|
||||
// Handle ValueTuples
|
||||
if (typeof(ITuple).IsAssignableFrom(valType))
|
||||
{
|
||||
var tuple = (ITuple)value;
|
||||
var items = new object?[tuple.Length];
|
||||
for (var i = 0; i < tuple.Length; i++)
|
||||
{
|
||||
items[i] = MakeValueNetSafe(tuple[i]);
|
||||
}
|
||||
|
||||
return new ViewVariablesBlobMembers.ServerTupleToken { Items = items };
|
||||
}
|
||||
|
||||
// Can't send this value type over the wire.
|
||||
return new ViewVariablesBlobMembers.ServerValueTypeToken
|
||||
{
|
||||
|
||||
@@ -5,7 +5,6 @@ using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using static Microsoft.CodeAnalysis.SymbolDisplayFormat;
|
||||
using static Microsoft.CodeAnalysis.SymbolDisplayMiscellaneousOptions;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
// Yes dude I know this source generator isn't incremental, I'll fix it eventually.
|
||||
#pragma warning disable RS1035
|
||||
@@ -36,7 +35,6 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
private const string GlobalDictionaryName = "global::System.Collections.Generic.Dictionary<TKey, TValue>";
|
||||
private const string GlobalHashSetName = "global::System.Collections.Generic.HashSet<T>";
|
||||
private const string GlobalListName = "global::System.Collections.Generic.List<T>";
|
||||
private const string GlobalIRobustCloneableName = "global::Robust.Shared.Serialization.IRobustCloneable";
|
||||
|
||||
private static readonly SymbolDisplayFormat FullNullableFormat =
|
||||
FullyQualifiedFormat.WithMiscellaneousOptions(IncludeNullableReferenceTypeModifier);
|
||||
@@ -49,7 +47,7 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
var componentName = classSymbol.Name;
|
||||
var stateName = $"{componentName}_AutoState";
|
||||
|
||||
var members = TypeSymbolHelper.GetAllMembersIncludingInherited(classSymbol);
|
||||
var members = classSymbol.GetMembers();
|
||||
var fields = new List<(ITypeSymbol Type, string FieldName)>();
|
||||
var fieldAttr = comp.GetTypeByMetadataName(MemberAttributeName);
|
||||
|
||||
@@ -377,39 +375,7 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
stateFields.Append($@"
|
||||
public {networkedType} {name} = default!;");
|
||||
|
||||
if (ImplementsInterface(type, GlobalIRobustCloneableName))
|
||||
{
|
||||
getField = $"component.{name}";
|
||||
cast = $"({castString})";
|
||||
|
||||
var nullCast = nullable ? castString.Substring(0, castString.Length - 1) : castString;
|
||||
|
||||
if (nullable)
|
||||
{
|
||||
handleStateSetters.Append($@"
|
||||
component.{name} = state.{name} == null ? null! : state.{name}.Clone();");
|
||||
deltaHandleFields.Append($@"
|
||||
var {name}Value = {cast} {fieldHandleValue};
|
||||
if ({name}Value == null)
|
||||
component.{name} = null!;
|
||||
else
|
||||
component.{name} = {nullCast}({name}Value.Clone());");
|
||||
shallowClone.Append($@"
|
||||
{name} = this.{name},");
|
||||
deltaApply.Add($"fullState.{name} = {name} == null ? null! : {name}.Clone();");
|
||||
}
|
||||
else
|
||||
{
|
||||
handleStateSetters.Append($@"
|
||||
component.{name} = state.{name}.Clone();");
|
||||
deltaHandleFields.Append($@"
|
||||
component.{name} = {cast}({fieldHandleValue}.Clone());");
|
||||
shallowClone.Append($@"
|
||||
{name} = this.{name},");
|
||||
deltaApply.Add($"fullState.{name} = {name}.Clone();");
|
||||
}
|
||||
}
|
||||
else if (IsCloneType(type))
|
||||
if (IsCloneType(type))
|
||||
{
|
||||
getField = $"component.{name}";
|
||||
cast = $"({castString})";
|
||||
@@ -716,7 +682,7 @@ public partial class {componentName}{deltaInterface}
|
||||
|
||||
if (relevantAttribute == null)
|
||||
{
|
||||
foreach (var mem in TypeSymbolHelper.GetAllMembersIncludingInherited(typeSymbol))
|
||||
foreach (var mem in typeSymbol.GetMembers())
|
||||
{
|
||||
var attribute = mem.GetAttributes().FirstOrDefault(a =>
|
||||
a.AttributeClass != null &&
|
||||
@@ -792,19 +758,5 @@ public partial class {componentName}{deltaInterface}
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
private static bool ImplementsInterface(ITypeSymbol type, string interfaceName)
|
||||
{
|
||||
foreach (var interfaceType in type.AllInterfaces)
|
||||
{
|
||||
if (interfaceType.ToDisplayString(FullyQualifiedFormat).Contains(interfaceName)
|
||||
|| interfaceType.ConstructedFrom.ToDisplayString(FullyQualifiedFormat).Contains(interfaceName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,11 +29,12 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Utility;
|
||||
using SysVector3 = System.Numerics.Vector3;
|
||||
using SysVector4 = System.Numerics.Vector4;
|
||||
|
||||
namespace Robust.Shared.Maths
|
||||
{
|
||||
@@ -68,7 +69,7 @@ namespace Robust.Shared.Maths
|
||||
/// Vector representation, for easy SIMD operations.
|
||||
/// </summary>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public readonly Vector4 RGBA => Unsafe.BitCast<Color, Vector4>(this);
|
||||
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);
|
||||
@@ -93,9 +94,9 @@ namespace Robust.Shared.Maths
|
||||
/// <summary>
|
||||
/// Constructs a new Color structure from the components in a <see cref="SysVector4"/>.
|
||||
/// </summary>
|
||||
public Color(in Vector4 vec)
|
||||
public Color(in SysVector4 vec)
|
||||
{
|
||||
this = Unsafe.BitCast<Vector4, Color>(vec);
|
||||
this = Unsafe.BitCast<SysVector4, Color>(vec);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -590,125 +591,6 @@ namespace Robust.Shared.Maths
|
||||
return new Vector4(hue, saturation, max, rgb.A);
|
||||
}
|
||||
|
||||
#region Oklab/Oklch
|
||||
/*
|
||||
|
||||
The code in this region is based off of https://bottosson.github.io/posts/oklab/, available under public domain or the MIT license.
|
||||
|
||||
Copyright (c) 2020 Björn Ottosson
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Converts linear sRGB color values to Oklab color values.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns the converted color value.
|
||||
/// The X element is L (lightness), the Y element is a (green-red), the Z element is b (blue-yellow), and the W element is Alpha
|
||||
/// (a copy of the input's Alpha value).
|
||||
/// L and Alpha have a range of 0 to 1, while a and b are unbounded, but roughly -0.5 to 0.5
|
||||
/// </returns>
|
||||
/// <param name="srgb">Linear sRGB color value to convert. <see cref="FromSrgb"/> to convert an sRGB color into linear sRGB.</param>
|
||||
public static Vector4 ToLab(Color srgb)
|
||||
{
|
||||
// convert from srgb to linear lms
|
||||
|
||||
var l = 0.4122214708f * srgb.R + 0.5363325363f * srgb.G + 0.0514459929f * srgb.B;
|
||||
var m = 0.2119034982f * srgb.R + 0.6806995451f * srgb.G + 0.1073969566f * srgb.B;
|
||||
var s = 0.0883024619f * srgb.R + 0.2817188376f * srgb.G + 0.6299787005f * srgb.B;
|
||||
|
||||
// convert from linear lms to non-linear lms
|
||||
|
||||
var l_ = MathF.Cbrt(l);
|
||||
var m_ = MathF.Cbrt(m);
|
||||
var s_ = MathF.Cbrt(s);
|
||||
|
||||
// convert from non-linear lms to lab
|
||||
|
||||
return new Vector4(
|
||||
0.2104542553f * l_ + 0.7936177850f * m_ - 0.0040720468f * s_,
|
||||
1.9779984951f * l_ - 2.4285922050f * m_ + 0.4505937099f * s_,
|
||||
0.0259040371f * l_ + 0.7827717662f * m_ - 0.8086757660f * s_,
|
||||
srgb.A
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts Oklab color values to linear sRGB color values.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns the converted color value. <see cref="ToSrgb"/> to convert an sRGB color into linear sRGB.
|
||||
/// </returns>
|
||||
/// <param name="oklab">Oklab color value to convert.</param>
|
||||
public static Color FromLab(Vector4 oklab)
|
||||
{
|
||||
var l_ = oklab.X + 0.3963377774f * oklab.Y + 0.2158037573f * oklab.Z;
|
||||
var m_ = oklab.X - 0.1055613458f * oklab.Y - 0.0638541728f * oklab.Z;
|
||||
var s_ = oklab.X - 0.0894841775f * oklab.Y - 1.2914855480f * oklab.Z;
|
||||
|
||||
// convert from non-linear lms to linear lms
|
||||
|
||||
var l = l_ * l_ * l_;
|
||||
var m = m_ * m_ * m_;
|
||||
var s = s_ * s_ * s_;
|
||||
|
||||
// convert from linear lms to linear srgb
|
||||
|
||||
var r = +4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s;
|
||||
var g = -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s;
|
||||
var b = -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s;
|
||||
|
||||
return new Color(r, g, b, oklab.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts cartesian Oklab color values to polar Oklch color values.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns the converted color value.
|
||||
/// </returns>
|
||||
/// <param name="oklab">Oklab color value to convert.</param>
|
||||
public static Vector4 ToLch(Vector4 oklab)
|
||||
{
|
||||
var c = MathF.Sqrt(oklab.Y * oklab.Y + oklab.Z * oklab.Z);
|
||||
var h = MathF.Atan2(oklab.Z, oklab.Y);
|
||||
if (h < 0)
|
||||
h += 2 * MathF.PI;
|
||||
|
||||
return new Vector4(oklab.X, c, h, oklab.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts polar Oklch color values to cartesian Oklab color values.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns the converted color value.
|
||||
/// </returns>
|
||||
/// <param name="oklch">Oklch color value to convert.</param>
|
||||
public static Vector4 FromLch(Vector4 oklch)
|
||||
{
|
||||
var a = oklch.Y * MathF.Cos(oklch.Z);
|
||||
var b = oklch.Y * MathF.Sin(oklch.Z);
|
||||
|
||||
return new Vector4(oklch.X, a, b, oklch.W);
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Converts XYZ color values to RGB color values.
|
||||
/// </summary>
|
||||
@@ -904,7 +786,7 @@ namespace Robust.Shared.Maths
|
||||
var m = (1 - g - k) / (1 - k);
|
||||
var y = (1 - b - k) / (1 - k);
|
||||
|
||||
return new Vector4(c, m, y, k);
|
||||
return (c, m, y, k);
|
||||
}
|
||||
|
||||
public static Color FromCmyk(Vector4 cmyk)
|
||||
@@ -931,7 +813,7 @@ namespace Robust.Shared.Maths
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Color InterpolateBetween(Color α, Color β, float λ)
|
||||
{
|
||||
return new(Vector4.Lerp(α.RGBA, β.RGBA, λ));
|
||||
return new(SysVector4.Lerp(α.RGBA, β.RGBA, λ));
|
||||
}
|
||||
|
||||
public static Color? TryFromHex(ReadOnlySpan<char> hexColor)
|
||||
@@ -1006,10 +888,10 @@ namespace Robust.Shared.Maths
|
||||
|
||||
public static Color Blend(Color dstColor, Color srcColor, BlendFactor dstFactor, BlendFactor srcFactor)
|
||||
{
|
||||
var dst = new Vector3(dstColor.R, dstColor.G, dstColor.B);
|
||||
var src = new Vector3(srcColor.R, srcColor.G, srcColor.B);
|
||||
var dst = new SysVector3(dstColor.R, dstColor.G, dstColor.B);
|
||||
var src = new SysVector3(srcColor.R, srcColor.G, srcColor.B);
|
||||
|
||||
var ret = new Vector3();
|
||||
var ret = new SysVector3();
|
||||
|
||||
switch (dstFactor)
|
||||
{
|
||||
@@ -1022,13 +904,13 @@ namespace Robust.Shared.Maths
|
||||
ret = dst * src;
|
||||
break;
|
||||
case BlendFactor.OneMinusSrcColor:
|
||||
ret = dst * (Vector3.One - src);
|
||||
ret = dst * (SysVector3.One - src);
|
||||
break;
|
||||
case BlendFactor.DstColor:
|
||||
ret = dst * dst;
|
||||
break;
|
||||
case BlendFactor.OneMinusDstColor:
|
||||
ret = dst * (Vector3.One - dst);
|
||||
ret = dst * (SysVector3.One - dst);
|
||||
break;
|
||||
case BlendFactor.SrcAlpha:
|
||||
ret = dst * srcColor.A;
|
||||
@@ -1057,13 +939,13 @@ namespace Robust.Shared.Maths
|
||||
ret += src * src;
|
||||
break;
|
||||
case BlendFactor.OneMinusSrcColor:
|
||||
ret += src * (Vector3.One - src);
|
||||
ret += src * (SysVector3.One - src);
|
||||
break;
|
||||
case BlendFactor.DstColor:
|
||||
ret += src * dst;
|
||||
break;
|
||||
case BlendFactor.OneMinusDstColor:
|
||||
ret += src * (Vector3.One - dst);
|
||||
ret += src * (SysVector3.One - dst);
|
||||
break;
|
||||
case BlendFactor.SrcAlpha:
|
||||
ret += src * srcColor.A;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -529,13 +530,13 @@ namespace Robust.Shared.Maths
|
||||
/// Returns whether two vectors are within <paramref name="percentage"/> of each other
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool CloseToPercent(Vector4 a, Vector4 b, float percentage = .00001f)
|
||||
public static bool CloseToPercent(Vec4 a, Vec4 b, float percentage = .00001f)
|
||||
{
|
||||
a = Vector4.Abs(a);
|
||||
b = Vector4.Abs(b);
|
||||
var p = new Vector4(percentage);
|
||||
var epsilon = Vector4.Max(Vector4.Max(a, b) * p, p);
|
||||
var delta = Vector4.Abs(a - b);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
1239
Robust.Shared.Maths/Matrix4.cs
Normal file
1239
Robust.Shared.Maths/Matrix4.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,3 +6,4 @@
|
||||
|
||||
[assembly: InternalsVisibleTo("Robust.Client")]
|
||||
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
|
||||
[assembly: InternalsVisibleTo("Content.Benchmarks")]
|
||||
|
||||
978
Robust.Shared.Maths/Quaternion.cs
Normal file
978
Robust.Shared.Maths/Quaternion.cs
Normal file
@@ -0,0 +1,978 @@
|
||||
#region --- License ---
|
||||
|
||||
/*
|
||||
Copyright (c) 2006 - 2008 The Open Toolkit library.
|
||||
Copyright 2013 Xamarin Inc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#endregion --- License ---
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Xml.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Maths
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Quaternion.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Quaternion : IEquatable<Quaternion>, ISpanFormattable
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private Vector3 xyz;
|
||||
private float w;
|
||||
|
||||
#endregion Fields
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new Quaternion from vector and w components
|
||||
/// </summary>
|
||||
/// <param name="v">The vector part</param>
|
||||
/// <param name="w">The w part</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Quaternion(Vector3 v, float w)
|
||||
{
|
||||
xyz = v;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new Quaternion
|
||||
/// </summary>
|
||||
/// <param name="x">The x component</param>
|
||||
/// <param name="y">The y component</param>
|
||||
/// <param name="z">The z component</param>
|
||||
/// <param name="w">The w component</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Quaternion(float x, float y, float z, float w)
|
||||
: this(new Vector3(x, y, z), w) { }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Quaternion(ref Matrix3x2 matrix)
|
||||
{
|
||||
var scale = Math.Pow(matrix.GetDeterminant(), 1.0d / 3.0d);
|
||||
float x, y, z;
|
||||
|
||||
w = (float) (Math.Sqrt(Math.Max(0, scale + matrix[0, 0] + matrix[1, 1] + matrix[2, 2])) / 2);
|
||||
x = (float) (Math.Sqrt(Math.Max(0, scale + matrix[0, 0] - matrix[1, 1] - matrix[2, 2])) / 2);
|
||||
y = (float) (Math.Sqrt(Math.Max(0, scale - matrix[0, 0] + matrix[1, 1] - matrix[2, 2])) / 2);
|
||||
z = (float) (Math.Sqrt(Math.Max(0, scale - matrix[0, 0] - matrix[1, 1] + matrix[2, 2])) / 2);
|
||||
|
||||
xyz = new Vector3(x, y, z);
|
||||
|
||||
if (matrix[2, 1] - matrix[1, 2] < 0) X = -X;
|
||||
if (matrix[0, 2] - matrix[2, 0] < 0) Y = -Y;
|
||||
if (matrix[1, 0] - matrix[0, 1] < 0) Z = -Z;
|
||||
}
|
||||
|
||||
#endregion Constructors
|
||||
|
||||
#region Public Members
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an OpenTK.Vector3 with the X, Y and Z components of this instance.
|
||||
/// </summary>
|
||||
public Vector3 Xyz
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => xyz;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
set => xyz = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the X component of this instance.
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public float X
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => xyz.X;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
set => xyz.X = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Y component of this instance.
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public float Y
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => xyz.Y;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
set => xyz.Y = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Z component of this instance.
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public float Z
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => xyz.Z;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
set => xyz.Z = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the W component of this instance.
|
||||
/// </summary>
|
||||
public float W
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => w;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
set => w = value;
|
||||
}
|
||||
|
||||
public float x
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => xyz.X;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
set => xyz.X = value;
|
||||
}
|
||||
|
||||
public float y
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => xyz.Y;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
set => xyz.Y = value;
|
||||
}
|
||||
|
||||
public float z
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => xyz.Z;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
set => xyz.Z = value;
|
||||
}
|
||||
|
||||
#endregion Properties
|
||||
|
||||
#region Instance
|
||||
|
||||
#region ToAxisAngle
|
||||
|
||||
/// <summary>
|
||||
/// Convert the current quaternion to axis angle representation
|
||||
/// </summary>
|
||||
/// <param name="axis">The resultant axis</param>
|
||||
/// <param name="angle">The resultant angle</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ToAxisAngle(out Vector3 axis, out float angle)
|
||||
{
|
||||
var result = ToAxisAngle();
|
||||
axis = result.Xyz;
|
||||
angle = result.W;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert this instance to an axis-angle representation.
|
||||
/// </summary>
|
||||
/// <returns>A Vector4 that is the axis-angle representation of this quaternion.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Vector4 ToAxisAngle()
|
||||
{
|
||||
var q = this;
|
||||
if (Math.Abs(q.W) > 1.0f)
|
||||
q.Normalize();
|
||||
|
||||
var result = new Vector4();
|
||||
|
||||
result.W = 2.0f * (float) Math.Acos(q.W); // angle
|
||||
var den = (float) Math.Sqrt(1.0 - q.W * q.W);
|
||||
if (den > 0.0001f)
|
||||
{
|
||||
result.Xyz = q.Xyz / den;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This occurs when the angle is zero.
|
||||
// Not a problem: just set an arbitrary normalized axis.
|
||||
result.Xyz = Vector3.UnitX;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion ToAxisAngle
|
||||
|
||||
#region public float Length
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length (magnitude) of the quaternion.
|
||||
/// </summary>
|
||||
/// <seealso cref="LengthSquared"/>
|
||||
public float Length
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => (float) Math.Sqrt(W * W + Xyz.LengthSquared);
|
||||
}
|
||||
|
||||
#endregion public float Length
|
||||
|
||||
#region public float LengthSquared
|
||||
|
||||
/// <summary>
|
||||
/// Gets the square of the quaternion length (magnitude).
|
||||
/// </summary>
|
||||
public float LengthSquared
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => W * W + Xyz.LengthSquared;
|
||||
}
|
||||
|
||||
#endregion public float LengthSquared
|
||||
|
||||
#region public void Normalize()
|
||||
|
||||
/// <summary>
|
||||
/// Scales the Quaternion to unit length.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Normalize()
|
||||
{
|
||||
var scale = 1.0f / Length;
|
||||
Xyz *= scale;
|
||||
W *= scale;
|
||||
}
|
||||
|
||||
#endregion public void Normalize()
|
||||
|
||||
#region public void Conjugate()
|
||||
|
||||
/// <summary>
|
||||
/// Convert this quaternion to its conjugate
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Conjugate()
|
||||
{
|
||||
Xyz = -Xyz;
|
||||
}
|
||||
|
||||
#endregion public void Conjugate()
|
||||
|
||||
#endregion Instance
|
||||
|
||||
#region Static
|
||||
|
||||
#region Fields
|
||||
|
||||
private const float RadToDeg = (float) (180.0 / Math.PI);
|
||||
private const float DegToRad = (float) (Math.PI / 180.0);
|
||||
|
||||
/// <summary>
|
||||
/// Defines the identity quaternion.
|
||||
/// </summary>
|
||||
public static readonly Quaternion Identity = new(0, 0, 0, 1);
|
||||
|
||||
#endregion Fields
|
||||
|
||||
#region Add
|
||||
|
||||
/// <summary>
|
||||
/// Add two quaternions
|
||||
/// </summary>
|
||||
/// <param name="left">The first operand</param>
|
||||
/// <param name="right">The second operand</param>
|
||||
/// <returns>The result of the addition</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion Add(Quaternion left, Quaternion right)
|
||||
{
|
||||
return new(
|
||||
left.Xyz + right.Xyz,
|
||||
left.W + right.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add two quaternions
|
||||
/// </summary>
|
||||
/// <param name="left">The first operand</param>
|
||||
/// <param name="right">The second operand</param>
|
||||
/// <param name="result">The result of the addition</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Add(ref Quaternion left, ref Quaternion right, out Quaternion result)
|
||||
{
|
||||
result = new Quaternion(
|
||||
left.Xyz + right.Xyz,
|
||||
left.W + right.W);
|
||||
}
|
||||
|
||||
#endregion Add
|
||||
|
||||
#region Sub
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two instances.
|
||||
/// </summary>
|
||||
/// <param name="left">The left instance.</param>
|
||||
/// <param name="right">The right instance.</param>
|
||||
/// <returns>The result of the operation.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion Sub(Quaternion left, Quaternion right)
|
||||
{
|
||||
return new(
|
||||
left.Xyz - right.Xyz,
|
||||
left.W - right.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two instances.
|
||||
/// </summary>
|
||||
/// <param name="left">The left instance.</param>
|
||||
/// <param name="right">The right instance.</param>
|
||||
/// <param name="result">The result of the operation.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Sub(ref Quaternion left, ref Quaternion right, out Quaternion result)
|
||||
{
|
||||
result = new Quaternion(
|
||||
left.Xyz - right.Xyz,
|
||||
left.W - right.W);
|
||||
}
|
||||
|
||||
#endregion Sub
|
||||
|
||||
#region Mult
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two instances.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>A new instance containing the result of the calculation.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion Multiply(Quaternion left, Quaternion right)
|
||||
{
|
||||
Multiply(ref left, ref right, out var result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two instances.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <param name="result">A new instance containing the result of the calculation.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Multiply(ref Quaternion left, ref Quaternion right, out Quaternion result)
|
||||
{
|
||||
result = new Quaternion(
|
||||
right.W * left.Xyz + left.W * right.Xyz + Vector3.Cross(left.Xyz, right.Xyz),
|
||||
left.W * right.W - Vector3.Dot(left.Xyz, right.Xyz));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies an instance by a scalar.
|
||||
/// </summary>
|
||||
/// <param name="quaternion">The instance.</param>
|
||||
/// <param name="scale">The scalar.</param>
|
||||
/// <param name="result">A new instance containing the result of the calculation.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Multiply(ref Quaternion quaternion, float scale, out Quaternion result)
|
||||
{
|
||||
result = new Quaternion(quaternion.X * scale, quaternion.Y * scale, quaternion.Z * scale, quaternion.W * scale);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies an instance by a scalar.
|
||||
/// </summary>
|
||||
/// <param name="quaternion">The instance.</param>
|
||||
/// <param name="scale">The scalar.</param>
|
||||
/// <returns>A new instance containing the result of the calculation.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion Multiply(Quaternion quaternion, float scale)
|
||||
{
|
||||
return new(quaternion.X * scale, quaternion.Y * scale, quaternion.Z * scale, quaternion.W * scale);
|
||||
}
|
||||
|
||||
#endregion Mult
|
||||
|
||||
#region Dot
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the dot product between two Quaternions.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float Dot(Quaternion a, Quaternion b)
|
||||
{
|
||||
return a.X * b.X + a.Y * b.Y + a.Z * b.Z + a.W * b.W;
|
||||
}
|
||||
|
||||
#endregion Dot
|
||||
|
||||
#region Conjugate
|
||||
|
||||
/// <summary>
|
||||
/// Get the conjugate of the given quaternion
|
||||
/// </summary>
|
||||
/// <param name="q">The quaternion</param>
|
||||
/// <returns>The conjugate of the given quaternion</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion Conjugate(Quaternion q)
|
||||
{
|
||||
return new(-q.Xyz, q.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the conjugate of the given quaternion
|
||||
/// </summary>
|
||||
/// <param name="q">The quaternion</param>
|
||||
/// <param name="result">The conjugate of the given quaternion</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Conjugate(ref Quaternion q, out Quaternion result)
|
||||
{
|
||||
result = new Quaternion(-q.Xyz, q.W);
|
||||
}
|
||||
|
||||
#endregion Conjugate
|
||||
|
||||
#region Invert
|
||||
|
||||
/// <summary>
|
||||
/// Get the inverse of the given quaternion
|
||||
/// </summary>
|
||||
/// <param name="q">The quaternion to invert</param>
|
||||
/// <returns>The inverse of the given quaternion</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion Invert(Quaternion q)
|
||||
{
|
||||
Invert(ref q, out var result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the inverse of the given quaternion
|
||||
/// </summary>
|
||||
/// <param name="q">The quaternion to invert</param>
|
||||
/// <param name="result">The inverse of the given quaternion</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Invert(ref Quaternion q, out Quaternion result)
|
||||
{
|
||||
var lengthSq = q.LengthSquared;
|
||||
if (lengthSq != 0.0)
|
||||
{
|
||||
var i = 1.0f / lengthSq;
|
||||
result = new Quaternion(q.Xyz * -i, q.W * i);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = q;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Invert
|
||||
|
||||
#region Normalize
|
||||
|
||||
/// <summary>
|
||||
/// Scale the given quaternion to unit length
|
||||
/// </summary>
|
||||
/// <param name="q">The quaternion to normalize</param>
|
||||
/// <returns>The normalized quaternion</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion Normalize(Quaternion q)
|
||||
{
|
||||
Normalize(ref q, out var result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scale the given quaternion to unit length
|
||||
/// </summary>
|
||||
/// <param name="q">The quaternion to normalize</param>
|
||||
/// <param name="result">The normalized quaternion</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Normalize(ref Quaternion q, out Quaternion result)
|
||||
{
|
||||
var scale = 1.0f / q.Length;
|
||||
result = new Quaternion(q.Xyz * scale, q.W * scale);
|
||||
}
|
||||
|
||||
#endregion Normalize
|
||||
|
||||
#region FromAxisAngle
|
||||
|
||||
/// <summary>
|
||||
/// Build a quaternion from the given axis and angle
|
||||
/// </summary>
|
||||
/// <param name="axis">The axis to rotate about</param>
|
||||
/// <param name="angle">The rotation angle in radians</param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion FromAxisAngle(Vector3 axis, float angle)
|
||||
{
|
||||
if (axis.LengthSquared == 0.0f)
|
||||
return Identity;
|
||||
|
||||
var result = Identity;
|
||||
|
||||
angle *= 0.5f;
|
||||
axis.Normalize();
|
||||
result.Xyz = axis * (float) Math.Sin(angle);
|
||||
result.W = (float) Math.Cos(angle);
|
||||
|
||||
return Normalize(result);
|
||||
}
|
||||
|
||||
#endregion FromAxisAngle
|
||||
|
||||
#region Slerp
|
||||
|
||||
/// <summary>
|
||||
/// Do Spherical linear interpolation between two quaternions
|
||||
/// </summary>
|
||||
/// <param name="q1">The first quaternion</param>
|
||||
/// <param name="q2">The second quaternion</param>
|
||||
/// <param name="blend">The blend factor</param>
|
||||
/// <returns>A smooth blend between the given quaternions</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion Slerp(Quaternion q1, Quaternion q2, float blend)
|
||||
{
|
||||
// if either input is zero, return the other.
|
||||
if (q1.LengthSquared == 0.0f)
|
||||
{
|
||||
if (q2.LengthSquared == 0.0f)
|
||||
{
|
||||
return Identity;
|
||||
}
|
||||
|
||||
return q2;
|
||||
}
|
||||
|
||||
if (q2.LengthSquared == 0.0f)
|
||||
{
|
||||
return q1;
|
||||
}
|
||||
|
||||
var cosHalfAngle = q1.W * q2.W + Vector3.Dot(q1.Xyz, q2.Xyz);
|
||||
|
||||
if (cosHalfAngle >= 1.0f || cosHalfAngle <= -1.0f)
|
||||
{
|
||||
// angle = 0.0f, so just return one input.
|
||||
return q1;
|
||||
}
|
||||
|
||||
if (cosHalfAngle < 0.0f)
|
||||
{
|
||||
q2.Xyz = -q2.Xyz;
|
||||
q2.W = -q2.W;
|
||||
cosHalfAngle = -cosHalfAngle;
|
||||
}
|
||||
|
||||
float blendA;
|
||||
float blendB;
|
||||
if (cosHalfAngle < 0.99f)
|
||||
{
|
||||
// do proper slerp for big angles
|
||||
var halfAngle = (float) Math.Acos(cosHalfAngle);
|
||||
var sinHalfAngle = (float) Math.Sin(halfAngle);
|
||||
var oneOverSinHalfAngle = 1.0f / sinHalfAngle;
|
||||
blendA = (float) Math.Sin(halfAngle * (1.0f - blend)) * oneOverSinHalfAngle;
|
||||
blendB = (float) Math.Sin(halfAngle * blend) * oneOverSinHalfAngle;
|
||||
}
|
||||
else
|
||||
{
|
||||
// do lerp if angle is really small.
|
||||
blendA = 1.0f - blend;
|
||||
blendB = blend;
|
||||
}
|
||||
|
||||
var result = new Quaternion(blendA * q1.Xyz + blendB * q2.Xyz, blendA * q1.W + blendB * q2.W);
|
||||
if (result.LengthSquared > 0.0f)
|
||||
return Normalize(result);
|
||||
return Identity;
|
||||
}
|
||||
|
||||
#endregion Slerp
|
||||
|
||||
#region RotateTowards
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion RotateTowards(Quaternion from, Quaternion to, float maxDegreesDelta)
|
||||
{
|
||||
var num = Angle(from, to);
|
||||
if (num == 0f)
|
||||
{
|
||||
return to;
|
||||
}
|
||||
|
||||
var t = MathF.Min(1f, maxDegreesDelta / num);
|
||||
return Slerp(from, to, t);
|
||||
}
|
||||
|
||||
#endregion RotateTowards
|
||||
|
||||
#region Angle
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float Angle(Quaternion a, Quaternion b)
|
||||
{
|
||||
var f = Dot(a, b);
|
||||
return (float) (Math.Acos(Math.Min(Math.Abs(f), 1f)) * 2f * RadToDeg);
|
||||
}
|
||||
|
||||
#endregion Angle
|
||||
|
||||
#region LookRotation
|
||||
|
||||
// from http://answers.unity3d.com/questions/467614/what-is-the-source-code-of-quaternionlookrotation.html
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion LookRotation(ref Vector3 forward, ref Vector3 up)
|
||||
{
|
||||
forward = Vector3.Normalize(forward);
|
||||
var right = Vector3.Normalize(Vector3.Cross(up, forward));
|
||||
up = Vector3.Cross(forward, right);
|
||||
var m00 = right.X;
|
||||
var m01 = right.Y;
|
||||
var m02 = right.Z;
|
||||
var m10 = up.X;
|
||||
var m11 = up.Y;
|
||||
var m12 = up.Z;
|
||||
var m20 = forward.X;
|
||||
var m21 = forward.Y;
|
||||
var m22 = forward.Z;
|
||||
|
||||
var num8 = m00 + m11 + m22;
|
||||
var quaternion = new Quaternion();
|
||||
if (num8 > 0f)
|
||||
{
|
||||
var num = MathF.Sqrt(num8 + 1f);
|
||||
quaternion.w = num * 0.5f;
|
||||
num = 0.5f / num;
|
||||
quaternion.X = (m12 - m21) * num;
|
||||
quaternion.Y = (m20 - m02) * num;
|
||||
quaternion.Z = (m01 - m10) * num;
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
if (m00 >= m11 && m00 >= m22)
|
||||
{
|
||||
var num7 = MathF.Sqrt(1f + m00 - m11 - m22);
|
||||
var num4 = 0.5f / num7;
|
||||
quaternion.X = 0.5f * num7;
|
||||
quaternion.Y = (m01 + m10) * num4;
|
||||
quaternion.Z = (m02 + m20) * num4;
|
||||
quaternion.W = (m12 - m21) * num4;
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
if (m11 > m22)
|
||||
{
|
||||
var num6 = MathF.Sqrt(1f + m11 - m00 - m22);
|
||||
var num3 = 0.5f / num6;
|
||||
quaternion.X = (m10 + m01) * num3;
|
||||
quaternion.Y = 0.5f * num6;
|
||||
quaternion.Z = (m21 + m12) * num3;
|
||||
quaternion.W = (m20 - m02) * num3;
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
var num5 = MathF.Sqrt(1f + m22 - m00 - m11);
|
||||
var num2 = 0.5f / num5;
|
||||
quaternion.X = (m20 + m02) * num2;
|
||||
quaternion.Y = (m21 + m12) * num2;
|
||||
quaternion.Z = 0.5f * num5;
|
||||
quaternion.W = (m01 - m10) * num2;
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
#endregion LookRotation
|
||||
|
||||
#region Euler Angles
|
||||
|
||||
// from http://stackoverflow.com/questions/12088610/conversion-between-euler-quaternion-like-in-unity3d-engine
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 ToEulerRad(Quaternion rotation)
|
||||
{
|
||||
var sqw = rotation.w * rotation.w;
|
||||
var sqx = rotation.x * rotation.x;
|
||||
var sqy = rotation.y * rotation.y;
|
||||
var sqz = rotation.z * rotation.z;
|
||||
var unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor
|
||||
var test = rotation.x * rotation.w - rotation.y * rotation.z;
|
||||
Vector3 v;
|
||||
|
||||
if (test > 0.4995f * unit)
|
||||
{
|
||||
// singularity at north pole
|
||||
v.Y = 2f * MathF.Atan2(rotation.y, rotation.x);
|
||||
v.X = (float) (Math.PI / 2);
|
||||
v.Z = 0;
|
||||
return NormalizeAngles(v * RadToDeg);
|
||||
}
|
||||
|
||||
if (test < -0.4995f * unit)
|
||||
{
|
||||
// singularity at south pole
|
||||
v.Y = -2f * MathF.Atan2(rotation.y, rotation.x);
|
||||
v.X = (float) (-Math.PI / 2);
|
||||
v.Z = 0;
|
||||
return NormalizeAngles(v * RadToDeg);
|
||||
}
|
||||
|
||||
var q = new Quaternion(rotation.w, rotation.z, rotation.x, rotation.y);
|
||||
v.Y = MathF.Atan2(2f * q.x * q.w + 2f * q.y * q.z, 1 - 2f * (q.z * q.z + q.w * q.w)); // Yaw
|
||||
v.X = MathF.Asin(2f * (q.x * q.z - q.w * q.y)); // Pitch
|
||||
v.Z = MathF.Atan2(2f * q.x * q.y + 2f * q.z * q.w, 1 - 2f * (q.y * q.y + q.z * q.z)); // Roll
|
||||
return NormalizeAngles(v * RadToDeg);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Vector3 NormalizeAngles(Vector3 angles)
|
||||
{
|
||||
angles.X = NormalizeAngle(angles.X);
|
||||
angles.Y = NormalizeAngle(angles.Y);
|
||||
angles.Z = NormalizeAngle(angles.Z);
|
||||
return angles;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static float NormalizeAngle(float angle)
|
||||
{
|
||||
/*
|
||||
while (angle > 360)
|
||||
angle -= 360;
|
||||
while (angle < 0)
|
||||
angle += 360;
|
||||
return angle;
|
||||
|
||||
asm:
|
||||
|
||||
L0000: vzeroupper
|
||||
L0003: vucomiss xmm0, [fld 360f]
|
||||
L000b: jbe short L001f
|
||||
L000d: vsubss xmm0, xmm0, [fld 360f]
|
||||
L0015: vucomiss xmm0, [fld 0]
|
||||
L001d: ja short L000d
|
||||
L001f: vxorps xmm1, xmm1, xmm1
|
||||
L0023: vucomiss xmm1, xmm0
|
||||
L0027: jbe short L003b
|
||||
L0029: vaddss xmm0, xmm0, [fld 360f]
|
||||
L0031: vxorps xmm1, xmm1, xmm1
|
||||
L0035: vucomiss xmm1, xmm0
|
||||
L0039: ja short L0029
|
||||
L003b: ret
|
||||
|
||||
*/
|
||||
|
||||
return angle - MathF.Floor(angle * (1/360f)) * 360f;
|
||||
/* asm:
|
||||
L0000: vzeroupper
|
||||
L0003: vmovaps xmm1, xmm0
|
||||
L0007: vmulss xmm1, xmm1, [fld 1/360f]
|
||||
L000f: vroundss xmm1, xmm1, xmm1, 9
|
||||
L0015: vmulss xmm1, xmm1, [fld 360f]
|
||||
L001d: vsubss xmm0, xmm0, xmm1
|
||||
L0021: ret
|
||||
*/
|
||||
}
|
||||
|
||||
#endregion Euler Angles
|
||||
|
||||
#endregion Static
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Adds two instances.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>The result of the calculation.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion operator +(Quaternion left, Quaternion right)
|
||||
{
|
||||
left.Xyz += right.Xyz;
|
||||
left.W += right.W;
|
||||
return left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two instances.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>The result of the calculation.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion operator -(Quaternion left, Quaternion right)
|
||||
{
|
||||
left.Xyz -= right.Xyz;
|
||||
left.W -= right.W;
|
||||
return left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two instances.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>The result of the calculation.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion operator *(Quaternion left, Quaternion right)
|
||||
{
|
||||
Multiply(ref left, ref right, out left);
|
||||
return left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies an instance by a scalar.
|
||||
/// </summary>
|
||||
/// <param name="quaternion">The instance.</param>
|
||||
/// <param name="scale">The scalar.</param>
|
||||
/// <returns>A new instance containing the result of the calculation.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion operator *(Quaternion quaternion, float scale)
|
||||
{
|
||||
Multiply(ref quaternion, scale, out quaternion);
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies an instance by a scalar.
|
||||
/// </summary>
|
||||
/// <param name="quaternion">The instance.</param>
|
||||
/// <param name="scale">The scalar.</param>
|
||||
/// <returns>A new instance containing the result of the calculation.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion operator *(float scale, Quaternion quaternion)
|
||||
{
|
||||
return new(quaternion.X * scale, quaternion.Y * scale, quaternion.Z * scale, quaternion.W * scale);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two instances for equality.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>True, if left equals right; false otherwise.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(Quaternion left, Quaternion right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two instances for inequality.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance.</param>
|
||||
/// <param name="right">The second instance.</param>
|
||||
/// <returns>True, if left does not equal right; false otherwise.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(Quaternion left, Quaternion right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
#endregion Operators
|
||||
|
||||
#region Overrides
|
||||
|
||||
#region public override string ToString()
|
||||
|
||||
/// <summary>
|
||||
/// Returns a System.String that represents the current Quaternion.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public readonly override string ToString()
|
||||
{
|
||||
return $"V: {xyz}, W: {w}";
|
||||
}
|
||||
|
||||
public readonly string ToString(string? format, IFormatProvider? formatProvider)
|
||||
{
|
||||
return ToString();
|
||||
}
|
||||
|
||||
public readonly bool TryFormat(
|
||||
Span<char> destination,
|
||||
out int charsWritten,
|
||||
ReadOnlySpan<char> format,
|
||||
IFormatProvider? provider)
|
||||
{
|
||||
return FormatHelpers.TryFormatInto(
|
||||
destination,
|
||||
out charsWritten,
|
||||
$"V: {xyz}, W: {w}");
|
||||
}
|
||||
|
||||
#endregion public override string ToString()
|
||||
|
||||
#region public override bool Equals (object o)
|
||||
|
||||
/// <summary>
|
||||
/// Compares this object instance to another object for equality.
|
||||
/// </summary>
|
||||
/// <param name="obj">The other object to be used in the comparison.</param>
|
||||
/// <returns>True if both objects are Quaternions of equal value. Otherwise it returns false.</returns>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is Quaternion quaternion) return this == quaternion;
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion public override bool Equals (object o)
|
||||
|
||||
#region public override int GetHashCode ()
|
||||
|
||||
/// <summary>
|
||||
/// Provides the hash code for this object.
|
||||
/// </summary>
|
||||
/// <returns>A hash code formed from the bitwise XOR of this objects members.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Xyz.GetHashCode() ^ W.GetHashCode();
|
||||
}
|
||||
|
||||
#endregion public override int GetHashCode ()
|
||||
|
||||
#endregion Overrides
|
||||
|
||||
#endregion Public Members
|
||||
|
||||
#region IEquatable<Quaternion> Members
|
||||
|
||||
/// <summary>
|
||||
/// Compares this Quaternion instance to another Quaternion for equality.
|
||||
/// </summary>
|
||||
/// <param name="other">The other Quaternion to be used in the comparison.</param>
|
||||
/// <returns>True if both instances are equal; false otherwise.</returns>
|
||||
public bool Equals(Quaternion other)
|
||||
{
|
||||
return Xyz == other.Xyz && W == other.W;
|
||||
}
|
||||
|
||||
#endregion IEquatable<Quaternion> Members
|
||||
}
|
||||
}
|
||||
1235
Robust.Shared.Maths/Vector3.cs
Normal file
1235
Robust.Shared.Maths/Vector3.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Robust.Shared.Maths
|
||||
|
||||
1022
Robust.Shared.Maths/Vector4.cs
Normal file
1022
Robust.Shared.Maths/Vector4.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Robust.Shared.Maths
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Robust.Shared.Maths;
|
||||
|
||||
public static class VectorHelpers
|
||||
{
|
||||
public static Vector3 InterpolateCubic(Vector3 preA, Vector3 a, Vector3 b, Vector3 postB, float t)
|
||||
{
|
||||
return a + (b - preA + (preA * 2.0f - a * 5.0f + b * 4.0f - postB + ((a - b) * 3.0f + postB - preA) * t) * t) * t * 0.5f;
|
||||
}
|
||||
|
||||
public static Vector4 InterpolateCubic(Vector4 preA, Vector4 a, Vector4 b, Vector4 postB, float t)
|
||||
{
|
||||
return a + (b - preA + (preA * 2.0f - a * 5.0f + b * 4.0f - postB + ((a - b) * 3.0f + postB - preA) * t) * t) * t * 0.5f;
|
||||
}
|
||||
|
||||
public static void Deconstruct(this Vector3 vector, out float x, out float y, out float z)
|
||||
{
|
||||
x = vector.X;
|
||||
y = vector.Y;
|
||||
z = vector.Z;
|
||||
}
|
||||
|
||||
public static void Deconstruct(this Vector4 vector, out float x, out float y, out float z, out float w)
|
||||
{
|
||||
x = vector.X;
|
||||
y = vector.Y;
|
||||
z = vector.Z;
|
||||
w = vector.W;
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ internal sealed class AudioDebugCommands : LocalizedCommands
|
||||
}
|
||||
|
||||
var audioSystem = _entitySystem.GetEntitySystem<SharedAudioSystem>();
|
||||
var length = audioSystem.GetAudioLength(new ResolvedPathSpecifier(args[0]));
|
||||
var length = audioSystem.GetAudioLength(args[0]);
|
||||
shell.WriteLine(length.ToString());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System.Numerics;
|
||||
using System;
|
||||
using Robust.Shared.Audio.Effects;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Audio.Components;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Shared.Audio.Effects;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
//
|
||||
|
||||
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Shared.Audio.Effects;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
// of the MIT license. See the LICENSE file for details.
|
||||
//
|
||||
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Shared.Audio.Effects;
|
||||
|
||||
@@ -195,6 +195,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
component.PlaybackPosition = (float) (Timing.CurTime - component.AudioStart).TotalSeconds;
|
||||
|
||||
DirtyField(entity.Value, component, nameof(AudioComponent.AudioStart));
|
||||
DirtyField(entity.Value, component, nameof(AudioComponent.PlaybackPosition));
|
||||
}
|
||||
|
||||
// If we were stopped then played then restart audiostart to now.
|
||||
@@ -429,15 +430,11 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// Gets the timespan of the specified audio.
|
||||
/// </summary>
|
||||
public TimeSpan GetAudioLength(ResolvedSoundSpecifier specifier)
|
||||
=> GetAudioLength(GetAudioPath(specifier));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timespan of the specified filename.
|
||||
/// </summary>
|
||||
protected TimeSpan GetAudioLength(string filename)
|
||||
{
|
||||
if (!filename.StartsWith('/'))
|
||||
var filename = GetAudioPath(specifier) ?? string.Empty;
|
||||
if (!filename.StartsWith("/"))
|
||||
throw new ArgumentException("Path must be rooted");
|
||||
|
||||
return GetAudioLengthImpl(filename);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Shared.ColorNaming;
|
||||
|
||||
// color naming algorithim is inspired by https://react-spectrum.adobe.com/blog/accessible-color-descriptions.html
|
||||
|
||||
public static class ColorNaming
|
||||
{
|
||||
private static readonly (float Hue, string Loc)[] HueNames =
|
||||
{
|
||||
(float.DegreesToRadians(0f), "color-pink"),
|
||||
(float.DegreesToRadians(15f), "color-red"),
|
||||
(float.DegreesToRadians(45f), "color-orange"),
|
||||
(float.DegreesToRadians(90f), "color-yellow"),
|
||||
(float.DegreesToRadians(135f), "color-green"),
|
||||
(float.DegreesToRadians(180f), "color-cyan"),
|
||||
(float.DegreesToRadians(240f), "color-blue"),
|
||||
(float.DegreesToRadians(285f), "color-purple"),
|
||||
(float.DegreesToRadians(330f), "color-pink"),
|
||||
};
|
||||
private static readonly (float Hue, string Loc) HueFallback = (float.DegreesToRadians(360f), "color-pink");
|
||||
|
||||
private const float BrownLightnessThreshold = 0.675f;
|
||||
private static readonly LocId OrangeString = "color-orange";
|
||||
private static readonly LocId BrownString = "color-brown";
|
||||
|
||||
private const float VeryDarkLightnessThreshold = 0.25f;
|
||||
private const float DarkLightnessThreshold = 0.5f;
|
||||
private const float NeutralLightnessThreshold = 0.7f;
|
||||
private const float LightLightnessThreshold = 0.85f;
|
||||
|
||||
private static readonly LocId VeryDarkString = "color-very-dark";
|
||||
private static readonly LocId DarkString = "color-dark";
|
||||
private static readonly LocId LightString = "color-light";
|
||||
private static readonly LocId VeryLightString = "color-very-light";
|
||||
|
||||
private static readonly LocId MixedHueString = "color-mixed-hue";
|
||||
private static readonly LocId LightLowChromaString = "color-pale";
|
||||
private static readonly LocId DarkLowChromaString = "color-gray-adjective";
|
||||
private static readonly LocId HighChromaString = "color-strong";
|
||||
|
||||
private static readonly LocId WhiteString = "color-white";
|
||||
private static readonly LocId GrayString = "color-gray";
|
||||
private static readonly LocId BlackString = "color-black";
|
||||
|
||||
private const float LowChromaThreshold = 0.07f;
|
||||
private const float HighChromaThreshold = 0.16f;
|
||||
private const float LightLowChromaThreshold = 0.6f;
|
||||
|
||||
private const float WhiteLightnessThreshold = 0.99f;
|
||||
private const float BlackLightnessThreshold = 0.01f;
|
||||
private const float GrayChromaThreshold = 0.01f;
|
||||
|
||||
private static (string Loc, float AdjustedLightness) DescribeHue(Vector4 oklch, ILocalizationManager localization)
|
||||
{
|
||||
var (lightness, _, hue, _) = oklch;
|
||||
|
||||
for (var i = 0; i < HueNames.Length; i++)
|
||||
{
|
||||
var prevData = HueNames[i];
|
||||
var nextData = i+1 < HueNames.Length ? HueNames[i+1] : HueFallback;
|
||||
|
||||
if (prevData.Hue >= hue || hue > nextData.Hue)
|
||||
continue;
|
||||
|
||||
var loc = prevData.Loc;
|
||||
var adjustedLightness = lightness;
|
||||
|
||||
if (prevData.Loc == OrangeString && lightness <= BrownLightnessThreshold)
|
||||
loc = BrownString;
|
||||
else if (prevData.Loc == OrangeString)
|
||||
adjustedLightness = lightness - BrownLightnessThreshold + DarkLightnessThreshold;
|
||||
|
||||
if (hue >= (prevData.Hue + nextData.Hue)/2f && prevData.Loc != nextData.Loc)
|
||||
{
|
||||
if (localization.TryGetString($"{loc}-{nextData.Loc}", out var hueName))
|
||||
return (hueName!, adjustedLightness);
|
||||
else
|
||||
return (localization.GetString(MixedHueString, ("a", localization.GetString(loc)), ("b", localization.GetString(nextData.Loc))), adjustedLightness);
|
||||
}
|
||||
|
||||
return (localization.GetString(loc), adjustedLightness);
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException("oklch", $"colour ({oklch}) hue {hue} is outside of expected bounds");
|
||||
}
|
||||
|
||||
private static string? DescribeChroma(Vector4 oklch, ILocalizationManager localization)
|
||||
{
|
||||
var (lightness, chroma, _, _) = oklch;
|
||||
|
||||
if (chroma <= LowChromaThreshold)
|
||||
{
|
||||
if (lightness >= LightLowChromaThreshold)
|
||||
return localization.GetString(LightLowChromaString);
|
||||
else
|
||||
return localization.GetString(DarkLowChromaString);
|
||||
}
|
||||
else if (chroma >= HighChromaThreshold)
|
||||
{
|
||||
return localization.GetString(HighChromaString);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string? DescribeLightness(Vector4 oklch, ILocalizationManager localization)
|
||||
{
|
||||
return oklch.X switch
|
||||
{
|
||||
< VeryDarkLightnessThreshold => localization.GetString(VeryDarkString),
|
||||
< DarkLightnessThreshold => localization.GetString(DarkString),
|
||||
< NeutralLightnessThreshold => null,
|
||||
< LightLightnessThreshold => localization.GetString(LightString),
|
||||
_ => localization.GetString(VeryLightString)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Textually describes a color
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns a localized textual description of the provided color
|
||||
/// </returns>
|
||||
/// <param name="srgb">A Color that is assumed to be in SRGB (the default for most cases)</param>
|
||||
public static string Describe(Color srgb, ILocalizationManager localization)
|
||||
{
|
||||
var oklch = Color.ToLch(Color.ToLab(Color.FromSrgb(srgb)));
|
||||
|
||||
if (oklch.X >= WhiteLightnessThreshold)
|
||||
return localization.GetString(WhiteString);
|
||||
|
||||
if (oklch.X <= BlackLightnessThreshold)
|
||||
return localization.GetString(BlackString);
|
||||
|
||||
var (hueDescription, adjustedLightness) = DescribeHue(oklch, localization);
|
||||
oklch.X = adjustedLightness;
|
||||
var chromaDescription = DescribeChroma(oklch, localization);
|
||||
var lightnessDescription = DescribeLightness(oklch, localization);
|
||||
|
||||
if (oklch.Y <= GrayChromaThreshold)
|
||||
{
|
||||
hueDescription = localization.GetString(GrayString);
|
||||
chromaDescription = null;
|
||||
}
|
||||
|
||||
return (hueDescription, chromaDescription, lightnessDescription) switch
|
||||
{
|
||||
({ } hue, { } chroma, { } lightness) => localization.GetString("color-hue-chroma-lightness", ("hue", hue), ("chroma", chroma), ("lightness", lightness)),
|
||||
({ } hue, { } chroma, null) => localization.GetString("color-hue-chroma", ("hue", hue), ("chroma", chroma)),
|
||||
({ } hue, null, { } lightness) => localization.GetString("color-hue-lightness", ("hue", hue), ("lightness", lightness)),
|
||||
({ } hue, null, null) => hue,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -135,7 +135,7 @@ namespace Robust.Shared.Configuration
|
||||
return CompletionResult.FromHint($"<{type.Name}>");
|
||||
}
|
||||
|
||||
private string GetCVarValueHint(IConfigurationManager cfg, string cVar)
|
||||
private static string GetCVarValueHint(IConfigurationManager cfg, string cVar)
|
||||
{
|
||||
var flags = cfg.GetCVarFlags(cVar);
|
||||
if ((flags & CVar.CONFIDENTIAL) != 0)
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Robust.Shared.Configuration
|
||||
catch (Exception e)
|
||||
{
|
||||
loaded.Clear();
|
||||
_sawmill.Error("Unable to load configuration from stream:\n{0}", e);
|
||||
_sawmill.Warning("Unable to load configuration from stream:\n{0}", e);
|
||||
}
|
||||
|
||||
return loaded;
|
||||
@@ -188,7 +188,7 @@ namespace Robust.Shared.Configuration
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_sawmill.Error("Unable to load configuration file:\n{0}", e);
|
||||
_sawmill.Warning("Unable to load configuration file:\n{0}", e);
|
||||
return new HashSet<string>(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ internal sealed class HelpCommand : LocalizedCommands
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
// Not a toolshed command since it doesn't support optional arguments
|
||||
ExecuteStatic(shell, argStr, args, Loc);
|
||||
ExecuteStatic(shell, argStr, args);
|
||||
}
|
||||
|
||||
public static void ExecuteStatic(IConsoleShell shell, string argStr, string[] args, ILocalizationManager loc)
|
||||
public static void ExecuteStatic(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
switch (args.Length)
|
||||
{
|
||||
@@ -40,34 +40,34 @@ For help with old console commands, run [color={Gold}]oldhelp[/color].
|
||||
var commandName = args[0];
|
||||
if (!shell.ConsoleHost.AvailableCommands.TryGetValue(commandName, out var cmd))
|
||||
{
|
||||
shell.WriteError(loc.GetString("cmd-help-unknown", ("command", commandName)));
|
||||
shell.WriteError(Loc.GetString("cmd-help-unknown", ("command", commandName)));
|
||||
return;
|
||||
}
|
||||
|
||||
shell.WriteLine(loc.GetString("cmd-help-top", ("command", cmd.Command),
|
||||
shell.WriteLine(Loc.GetString("cmd-help-top", ("command", cmd.Command),
|
||||
("description", cmd.Description)));
|
||||
shell.WriteLine(cmd.Help);
|
||||
break;
|
||||
|
||||
default:
|
||||
shell.WriteError(loc.GetString("cmd-help-invalid-args"));
|
||||
shell.WriteError(Loc.GetString("cmd-help-invalid-args"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
return GetCompletionStatic(shell, args, Loc);
|
||||
return GetCompletionStatic(shell, args);
|
||||
}
|
||||
|
||||
public static CompletionResult GetCompletionStatic(IConsoleShell shell, string[] args, ILocalizationManager loc)
|
||||
public static CompletionResult GetCompletionStatic(IConsoleShell shell, string[] args)
|
||||
{
|
||||
if (args.Length == 1)
|
||||
{
|
||||
var host = shell.ConsoleHost;
|
||||
return CompletionResult.FromHintOptions(
|
||||
host.AvailableCommands.Values.OrderBy(c => c.Command).Select(c => new CompletionOption(c.Command, c.Description)).ToArray(),
|
||||
loc.GetString("cmd-help-arg-cmdname"));
|
||||
Loc.GetString("cmd-help-arg-cmdname"));
|
||||
}
|
||||
|
||||
return CompletionResult.Empty;
|
||||
|
||||
@@ -7,11 +7,11 @@ public sealed class OldHelpCommand : LocalizedCommands
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
// For the people that got used to oldhelp
|
||||
HelpCommand.ExecuteStatic(shell, argStr, args, Loc);
|
||||
HelpCommand.ExecuteStatic(shell, argStr, args);
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
return HelpCommand.GetCompletionStatic(shell, args, Loc);
|
||||
return HelpCommand.GetCompletionStatic(shell, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ public abstract class LocalizedCommands : IConsoleCommand
|
||||
{
|
||||
[Dependency] protected readonly ILocalizationManager LocalizationManager = default!;
|
||||
|
||||
public ILocalizationManager Loc => LocalizationManager;
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract string Command { get; }
|
||||
|
||||
|
||||
@@ -88,7 +88,6 @@ namespace Robust.Shared.ContentPack
|
||||
public string SystemAssemblyName = default!;
|
||||
public HashSet<VerifierError> AllowedVerifierErrors = default!;
|
||||
public List<string> WhitelistedNamespaces = default!;
|
||||
public List<string> AllowedAssemblyPrefixes = default!;
|
||||
public Dictionary<string, Dictionary<string, TypeConfig>> Types = default!;
|
||||
}
|
||||
|
||||
|
||||
@@ -131,16 +131,6 @@ namespace Robust.Shared.ContentPack
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma warning disable RA0004
|
||||
var loadedConfig = _config.Result;
|
||||
#pragma warning restore RA0004
|
||||
|
||||
if (!loadedConfig.AllowedAssemblyPrefixes.Any(allowedNamePrefix => asmName.StartsWith(allowedNamePrefix)))
|
||||
{
|
||||
_sawmill.Error($"Assembly name '{asmName}' is not allowed for a content assembly");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (VerifyIL)
|
||||
{
|
||||
if (!DoVerifyIL(asmName, resolver, peReader, reader))
|
||||
@@ -189,6 +179,10 @@ namespace Robust.Shared.ContentPack
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma warning disable RA0004
|
||||
var loadedConfig = _config.Result;
|
||||
#pragma warning restore RA0004
|
||||
|
||||
var badRefs = new ConcurrentBag<EntityHandle>();
|
||||
|
||||
// We still do explicit type reference scanning, even though the actual whitelists work with raw members.
|
||||
|
||||
@@ -93,23 +93,19 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
Sawmill.Debug("LOADING modules");
|
||||
var files = new Dictionary<string, (ResPath Path, MemoryStream data, string[] references)>();
|
||||
var files = new Dictionary<string, (ResPath Path, string[] references)>();
|
||||
|
||||
// Find all modules we want to load.
|
||||
foreach (var fullPath in paths)
|
||||
{
|
||||
using var asmFile = _res.ContentFileRead(fullPath);
|
||||
var ms = new MemoryStream();
|
||||
asmFile.CopyTo(ms);
|
||||
|
||||
ms.Position = 0;
|
||||
var refData = GetAssemblyReferenceData(ms);
|
||||
var refData = GetAssemblyReferenceData(asmFile);
|
||||
if (refData == null)
|
||||
continue;
|
||||
|
||||
var (asmRefs, asmName) = refData.Value;
|
||||
|
||||
if (!files.TryAdd(asmName, (fullPath, ms, asmRefs)))
|
||||
if (!files.TryAdd(asmName, (fullPath, asmRefs)))
|
||||
{
|
||||
Sawmill.Error("Found multiple modules with the same assembly name " +
|
||||
$"'{asmName}', A: {files[asmName].Path}, B: {fullPath}.");
|
||||
@@ -126,10 +122,10 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
Parallel.ForEach(files, pair =>
|
||||
{
|
||||
var (name, (_, data, _)) = pair;
|
||||
var (name, (path, _)) = pair;
|
||||
|
||||
data.Position = 0;
|
||||
if (!typeChecker.CheckAssembly(data, resolver))
|
||||
using var stream = _res.ContentFileRead(path);
|
||||
if (!typeChecker.CheckAssembly(stream, resolver))
|
||||
{
|
||||
throw new TypeCheckFailedException($"Assembly {name} failed type checks.");
|
||||
}
|
||||
@@ -141,15 +137,14 @@ namespace Robust.Shared.ContentPack
|
||||
var nodes = TopologicalSort.FromBeforeAfter(
|
||||
files,
|
||||
kv => kv.Key,
|
||||
kv => kv.Value,
|
||||
kv => kv.Value.Path,
|
||||
_ => Array.Empty<string>(),
|
||||
kv => kv.Value.references,
|
||||
allowMissing: true); // missing refs would be non-content assemblies so allow that.
|
||||
|
||||
// Actually load them in the order they depend on each other.
|
||||
foreach (var item in TopologicalSort.Sort(nodes))
|
||||
foreach (var path in TopologicalSort.Sort(nodes))
|
||||
{
|
||||
var (path, memory, _) = item;
|
||||
Sawmill.Debug($"Loading module: '{path}'");
|
||||
try
|
||||
{
|
||||
@@ -161,9 +156,9 @@ namespace Robust.Shared.ContentPack
|
||||
}
|
||||
else
|
||||
{
|
||||
memory.Position = 0;
|
||||
using var assemblyStream = _res.ContentFileRead(path);
|
||||
using var symbolsStream = _res.ContentFileReadOrNull(path.WithExtension("pdb"));
|
||||
LoadGameAssembly(memory, symbolsStream, skipVerify: true);
|
||||
LoadGameAssembly(assemblyStream, symbolsStream, skipVerify: true);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -179,7 +174,7 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
private (string[] refs, string name)? GetAssemblyReferenceData(Stream stream)
|
||||
{
|
||||
using var reader = ModLoader.MakePEReader(stream, leaveOpen: true);
|
||||
using var reader = ModLoader.MakePEReader(stream);
|
||||
var metaReader = reader.GetMetadataReader();
|
||||
|
||||
var name = metaReader.GetString(metaReader.GetAssemblyDefinition().Name);
|
||||
|
||||
@@ -17,10 +17,6 @@ WhitelistedNamespaces:
|
||||
- Content
|
||||
- OpenDreamShared
|
||||
|
||||
AllowedAssemblyPrefixes:
|
||||
- OpenDream
|
||||
- Content
|
||||
|
||||
# The type whitelist does NOT care about which assembly types come from.
|
||||
# This is because types switch assembly all the time.
|
||||
# Just look up stuff like StreamReader on https://apisof.net.
|
||||
|
||||
@@ -13,6 +13,7 @@ using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
using Robust.Shared.Utility;
|
||||
using Vector2 = System.Numerics.Vector2;
|
||||
|
||||
namespace Robust.Shared.EntitySerialization.Systems;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Vector2 = System.Numerics.Vector2;
|
||||
|
||||
namespace Robust.Shared.EntitySerialization.Systems;
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ namespace Robust.Shared.Localization
|
||||
AddCtxFunction(bundle, "SUBJECT", FuncSubject);
|
||||
AddCtxFunction(bundle, "OBJECT", FuncObject);
|
||||
AddCtxFunction(bundle, "DAT-OBJ", FuncDatObj);
|
||||
AddCtxFunction(bundle, "GENITIVE", FuncGenitive);
|
||||
AddCtxFunction(bundle, "POSS-ADJ", FuncPossAdj);
|
||||
AddCtxFunction(bundle, "POSS-PRONOUN", FuncPossPronoun);
|
||||
AddCtxFunction(bundle, "REFLEXIVE", FuncReflexive);
|
||||
@@ -213,15 +212,6 @@ namespace Robust.Shared.Localization
|
||||
return new LocValueString(GetString("zzzz-dat-object", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective genitive form (pronoun or possessive adjective) for the entity's gender.
|
||||
/// This is used in languages with a genitive case to indicate possession or related relationships,
|
||||
/// e.g., "у него" (Russian), "seines Vaters" (German).
|
||||
private ILocValue FuncGenitive(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-genitive", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective possessive adjective (his, her, their, its) for the entity's gender.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Shared.Noise
|
||||
{
|
||||
|
||||
@@ -30,6 +30,7 @@ using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
|
||||
namespace Robust.Shared.Physics.Dynamics.Joints;
|
||||
// Linear constraint (point-to-line)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user