mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7c28992f8 | ||
|
|
920ae58019 | ||
|
|
5bb21e07de | ||
|
|
78ceaa50d5 | ||
|
|
7473b6dae1 | ||
|
|
c335170fc1 | ||
|
|
13e9fe12ce | ||
|
|
7ef2fd46da | ||
|
|
f048209bf5 | ||
|
|
1bf9e2e87a | ||
|
|
fd4f45e670 | ||
|
|
f15c1c7a95 | ||
|
|
50f0a4389e | ||
|
|
cab6277b2d | ||
|
|
797fa9cffa | ||
|
|
a20245d623 | ||
|
|
04cc1f616d | ||
|
|
8cd6f63f17 | ||
|
|
ad8b0b3c83 | ||
|
|
f157cdce02 | ||
|
|
2504a42f88 | ||
|
|
d0191e063a | ||
|
|
b96bcbd357 | ||
|
|
ae14031377 |
@@ -1,11 +1,11 @@
|
||||
root = true
|
||||
root = true
|
||||
|
||||
[*]
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
charset = utf-8-bom
|
||||
charset = utf-8
|
||||
|
||||
[*.{csproj,xml,yml,dll.config,targets,props}]
|
||||
indent_size = 2
|
||||
|
||||
2
.github/workflows/publish-client.yml
vendored
2
.github/workflows/publish-client.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
id: parse_version
|
||||
shell: pwsh
|
||||
run: |
|
||||
$ver = [regex]::Match($env:GITHUB_REF, "v?(.+)").Groups[1].Value
|
||||
$ver = [regex]::Match($env:GITHUB_REF, "refs/tags/v?(.+)").Groups[1].Value
|
||||
echo ("::set-output name=version::{0}" -f $ver)
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -4,3 +4,6 @@
|
||||
[submodule "Lidgren.Network"]
|
||||
path = Lidgren.Network/Lidgren.Network
|
||||
url = https://github.com/space-wizards/lidgren-network-gen3.git
|
||||
[submodule "Robust.LoaderApi"]
|
||||
path = Robust.LoaderApi
|
||||
url = https://github.com/space-wizards/Robust.LoaderApi.git
|
||||
|
||||
Submodule Lidgren.Network/Lidgren.Network updated: 11d3f497bc...4a5cedacb2
@@ -1,12 +1,2 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<PropertyGroup>
|
||||
<RobustToolsPath>$(MSBuildThisFileDirectory)/../Tools/</RobustToolsPath>
|
||||
</PropertyGroup>
|
||||
<Target Name="CopyClientNatives">
|
||||
<CombinePath BasePath="$(RobustToolsPath)" Paths="download_natives.py">
|
||||
<Output TaskParameter="CombinedPaths" PropertyName="ScriptPath" />
|
||||
</CombinePath>
|
||||
<Exec Command="$(Python) "$(ScriptPath)" $(Platform) $(TargetOS) Client $(OutputPath)" CustomErrorRegularExpression="^Error" />
|
||||
</Target>
|
||||
<Target Name="ClientAfterBuild" DependsOnTargets="CopyClientNatives" />
|
||||
</Project>
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// Defines event information for <see cref="GLFWCallbacks.KeyCallback"/>
|
||||
/// or <see cref="GLFWCallbacks.MouseButtonCallback"/>.
|
||||
/// </summary>
|
||||
public enum InputAction
|
||||
public enum InputAction : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The key or mouse button was released.
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// Key modifiers, such as Shift or CTRL.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum KeyModifiers
|
||||
public enum KeyModifiers : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// if one or more Shift keys were held down.
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// <summary>
|
||||
/// Specifies key codes and modifiers in US keyboard layout.
|
||||
/// </summary>
|
||||
public enum Keys
|
||||
public enum Keys : short
|
||||
{
|
||||
/// <summary>
|
||||
/// An unknown key.
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// <summary>
|
||||
/// Specifies the buttons of a mouse.
|
||||
/// </summary>
|
||||
public enum MouseButton
|
||||
public enum MouseButton : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The first button.
|
||||
|
||||
@@ -14,7 +14,7 @@ using MidiEvent = NFluidsynth.MidiEvent;
|
||||
|
||||
namespace Robust.Client.Audio.Midi
|
||||
{
|
||||
public enum MidiRendererStatus
|
||||
public enum MidiRendererStatus : byte
|
||||
{
|
||||
None,
|
||||
Input,
|
||||
|
||||
@@ -223,7 +223,7 @@ namespace Robust.Client
|
||||
/// <summary>
|
||||
/// Enumeration of the run levels of the BaseClient.
|
||||
/// </summary>
|
||||
public enum ClientRunLevel
|
||||
public enum ClientRunLevel : byte
|
||||
{
|
||||
Error = 0,
|
||||
|
||||
|
||||
@@ -231,6 +231,11 @@ namespace Robust.Client.Debugging
|
||||
_handle.DrawRect(box, color);
|
||||
}
|
||||
|
||||
public override void DrawRect(in Box2Rotated box, in Color color)
|
||||
{
|
||||
_handle.DrawRect(box, color);
|
||||
}
|
||||
|
||||
public override void DrawCircle(Vector2 origin, float radius, in Color color)
|
||||
{
|
||||
_handle.DrawCircle(origin, radius, color);
|
||||
|
||||
@@ -17,6 +17,7 @@ using Robust.Client.Interfaces.Utility;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Client.ViewVariables;
|
||||
using Robust.LoaderApi;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.Configuration;
|
||||
@@ -31,6 +32,7 @@ using Robust.Shared.Interfaces.Timers;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -64,9 +66,12 @@ namespace Robust.Client
|
||||
[Dependency] private readonly IScriptClient _scriptClient = default!;
|
||||
[Dependency] private readonly IComponentManager _componentManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IRobustMappedStringSerializer _stringSerializer = default!;
|
||||
|
||||
private CommandLineArgs? _commandLineArgs;
|
||||
private bool _disableAssemblyLoadContext;
|
||||
// Arguments for loader-load. Not used otherwise.
|
||||
private IMainArgs? _loaderArgs;
|
||||
|
||||
public InitialLaunchState LaunchState { get; private set; } = default!;
|
||||
|
||||
@@ -119,7 +124,13 @@ namespace Robust.Client
|
||||
|
||||
_resourceCache.Initialize(LoadConfigAndUserData ? userDataDir : null);
|
||||
|
||||
ProgramShared.DoMounts(_resourceCache, _commandLineArgs?.MountOptions, "Content.Client");
|
||||
ProgramShared.DoMounts(_resourceCache, _commandLineArgs?.MountOptions, "Content.Client", _loaderArgs != null);
|
||||
if (_loaderArgs != null)
|
||||
{
|
||||
_stringSerializer.EnableCaching = false;
|
||||
_resourceCache.MountLoaderApi(_loaderArgs.FileApi, "Resources/");
|
||||
_modLoader.VerifierExtraLoadHandler = VerifierExtraLoadHandler;
|
||||
}
|
||||
|
||||
// Bring display up as soon as resources are mounted.
|
||||
if (!_clyde.Initialize())
|
||||
@@ -185,6 +196,18 @@ namespace Robust.Client
|
||||
return true;
|
||||
}
|
||||
|
||||
private Stream? VerifierExtraLoadHandler(string arg)
|
||||
{
|
||||
DebugTools.AssertNotNull(_loaderArgs);
|
||||
|
||||
if (_loaderArgs!.FileApi.TryOpen(arg, out var stream))
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ReadInitialLaunchState()
|
||||
{
|
||||
if (_commandLineArgs == null)
|
||||
@@ -347,7 +370,7 @@ namespace Robust.Client
|
||||
}
|
||||
|
||||
|
||||
internal enum DisplayMode
|
||||
internal enum DisplayMode : byte
|
||||
{
|
||||
Headless,
|
||||
Clyde,
|
||||
|
||||
18
Robust.Client/GameController/GameController.Loader.cs
Normal file
18
Robust.Client/GameController/GameController.Loader.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Robust.Client;
|
||||
using Robust.LoaderApi;
|
||||
|
||||
[assembly: LoaderEntryPoint(typeof(GameController.LoaderEntryPoint))]
|
||||
|
||||
namespace Robust.Client
|
||||
{
|
||||
internal partial class GameController
|
||||
{
|
||||
internal class LoaderEntryPoint : ILoaderEntryPoint
|
||||
{
|
||||
public void Main(IMainArgs args)
|
||||
{
|
||||
GameController.Start(args.Args, contentStart: false, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Robust.Client.Interfaces;
|
||||
using Robust.LoaderApi;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
@@ -21,7 +21,7 @@ namespace Robust.Client
|
||||
Start(args);
|
||||
}
|
||||
|
||||
public static void Start(string[] args, bool contentStart = false)
|
||||
public static void Start(string[] args, bool contentStart = false, IMainArgs? loaderArgs=null)
|
||||
{
|
||||
if (_hasStarted)
|
||||
{
|
||||
@@ -32,11 +32,11 @@ namespace Robust.Client
|
||||
|
||||
if (CommandLineArgs.TryParse(args, out var parsed))
|
||||
{
|
||||
ParsedMain(parsed, contentStart);
|
||||
ParsedMain(parsed, contentStart, loaderArgs);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParsedMain(CommandLineArgs args, bool contentStart)
|
||||
private static void ParsedMain(CommandLineArgs args, bool contentStart, IMainArgs? loaderArgs)
|
||||
{
|
||||
IoCManager.InitThread();
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace Robust.Client
|
||||
|
||||
var gc = (GameController) IoCManager.Resolve<IGameController>();
|
||||
gc.SetCommandLineArgs(args);
|
||||
gc._loaderArgs = loaderArgs;
|
||||
|
||||
// When the game is ran with the startup executable being content,
|
||||
// we have to disable the separate load context.
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// To be clear: You shouldn't change this. This just helps with understanding where Primitive Restart is being used.
|
||||
private const ushort PrimitiveRestartIndex = ushort.MaxValue;
|
||||
|
||||
private enum Renderer
|
||||
private enum Renderer : short
|
||||
{
|
||||
// Default: Try all supported renderers (not necessarily the renderers shown here)
|
||||
Default = default,
|
||||
|
||||
@@ -970,7 +970,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
public Color Color;
|
||||
}
|
||||
|
||||
private enum RenderCommandType
|
||||
private enum RenderCommandType : byte
|
||||
{
|
||||
DrawBatch,
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
public enum WindowMode
|
||||
public enum WindowMode : byte
|
||||
{
|
||||
Windowed = 0,
|
||||
Fullscreen = 1,
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Robust.Client.Graphics.Drawing
|
||||
/// <remarks>
|
||||
/// See <see href="https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#drawing-point-lists">Vulkan's documentation</see> for descriptions of all these modes.
|
||||
/// </remarks>
|
||||
public enum DrawPrimitiveTopology
|
||||
public enum DrawPrimitiveTopology : byte
|
||||
{
|
||||
PointList,
|
||||
TriangleList,
|
||||
|
||||
@@ -318,7 +318,7 @@ namespace Robust.Client.Graphics.Drawing
|
||||
/// Describes margins of a style box.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum Margin
|
||||
public enum Margin : byte
|
||||
{
|
||||
None = 0,
|
||||
|
||||
|
||||
@@ -329,7 +329,7 @@ namespace Robust.Client.Graphics.Drawing
|
||||
/// <summary>
|
||||
/// Specifies how to stretch the sides and center of the style box.
|
||||
/// </summary>
|
||||
public enum StretchMode
|
||||
public enum StretchMode : byte
|
||||
{
|
||||
Stretch,
|
||||
Tile,
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace Robust.Client.Graphics.Overlays
|
||||
/// Determines in which canvas layers an overlay gets drawn.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum OverlaySpace
|
||||
public enum OverlaySpace : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Used for matching bit flags.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Robust.Client.Graphics.Shaders
|
||||
{
|
||||
public enum ShaderParamType
|
||||
public enum ShaderParamType : byte
|
||||
{
|
||||
// Can this even happen?
|
||||
Void = 0,
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace Robust.Client.Graphics.Shaders
|
||||
|
||||
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
internal enum ShaderDataType
|
||||
internal enum ShaderDataType : byte
|
||||
{
|
||||
Void,
|
||||
Bool,
|
||||
@@ -160,7 +160,7 @@ namespace Robust.Client.Graphics.Shaders
|
||||
}
|
||||
return Type.GetNativeType();
|
||||
}
|
||||
|
||||
|
||||
public bool TypePrecisionConsistent()
|
||||
{
|
||||
return Type.TypeHasPrecision() == (Precision != ShaderPrecisionQualifier.None);
|
||||
@@ -190,7 +190,7 @@ namespace Robust.Client.Graphics.Shaders
|
||||
throw new ArgumentOutOfRangeException(nameof(qualifier), qualifier, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static bool TypeHasPrecision(this ShaderDataType type)
|
||||
{
|
||||
return
|
||||
@@ -232,13 +232,13 @@ namespace Robust.Client.Graphics.Shaders
|
||||
};
|
||||
}
|
||||
|
||||
internal enum ShaderLightMode
|
||||
internal enum ShaderLightMode : byte
|
||||
{
|
||||
Default = 0,
|
||||
Unshaded = 1,
|
||||
}
|
||||
|
||||
internal enum ShaderBlendMode
|
||||
internal enum ShaderBlendMode : byte
|
||||
{
|
||||
None,
|
||||
Mix,
|
||||
@@ -247,7 +247,7 @@ namespace Robust.Client.Graphics.Shaders
|
||||
Multiply
|
||||
}
|
||||
|
||||
internal enum ShaderPreset
|
||||
internal enum ShaderPreset : byte
|
||||
{
|
||||
Default,
|
||||
Raw
|
||||
@@ -255,7 +255,7 @@ namespace Robust.Client.Graphics.Shaders
|
||||
|
||||
// Yeah I had no idea what to name this.
|
||||
[Flags]
|
||||
internal enum ShaderParameterQualifiers
|
||||
internal enum ShaderParameterQualifiers : byte
|
||||
{
|
||||
None = 0,
|
||||
In = 1,
|
||||
@@ -263,7 +263,7 @@ namespace Robust.Client.Graphics.Shaders
|
||||
Inout = 3,
|
||||
}
|
||||
|
||||
internal enum ShaderPrecisionQualifier
|
||||
internal enum ShaderPrecisionQualifier : byte
|
||||
{
|
||||
None = 0,
|
||||
Low = 1,
|
||||
|
||||
@@ -593,7 +593,7 @@ namespace Robust.Client.Graphics.Shaders
|
||||
public Symbols Symbol { get; }
|
||||
}
|
||||
|
||||
private enum Symbols
|
||||
private enum Symbols : byte
|
||||
{
|
||||
Semicolon,
|
||||
Comma,
|
||||
|
||||
@@ -296,7 +296,7 @@ namespace Robust.Client.Graphics.Shaders
|
||||
}
|
||||
}
|
||||
|
||||
private enum ShaderKind
|
||||
private enum ShaderKind : byte
|
||||
{
|
||||
Source,
|
||||
Canvas
|
||||
|
||||
@@ -189,7 +189,7 @@ namespace Robust.Client.Graphics
|
||||
/// Controls behavior when reading texture coordinates outside 0-1, which usually wraps the texture somehow.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public enum TextureWrapMode
|
||||
public enum TextureWrapMode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Do not wrap, instead clamp to edge.
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Robust.Client.Input
|
||||
/// <summary>
|
||||
/// Represents one of three mouse buttons.
|
||||
/// </summary>
|
||||
public enum Button
|
||||
public enum Button : byte
|
||||
{
|
||||
Left = 1,
|
||||
Middle = 2,
|
||||
|
||||
@@ -772,14 +772,14 @@ namespace Robust.Client.Input
|
||||
}
|
||||
}
|
||||
|
||||
public enum KeyBindingType
|
||||
public enum KeyBindingType : byte
|
||||
{
|
||||
Unknown = 0,
|
||||
State,
|
||||
Toggle,
|
||||
}
|
||||
|
||||
public enum CommandState
|
||||
public enum CommandState : byte
|
||||
{
|
||||
Unknown = 0,
|
||||
Enabled,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Robust.Client.Interfaces.Graphics
|
||||
{
|
||||
internal enum ClydeDebugLayers
|
||||
internal enum ClydeDebugLayers : byte
|
||||
{
|
||||
None,
|
||||
Fov,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Robust.Client.Interfaces.Graphics
|
||||
{
|
||||
internal enum ClydeStockTexture
|
||||
internal enum ClydeStockTexture : byte
|
||||
{
|
||||
White,
|
||||
Black,
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace Robust.Client.Interfaces.Graphics
|
||||
/// <summary>
|
||||
/// Formats for the color component of a render target.
|
||||
/// </summary>
|
||||
public enum RenderTargetColorFormat
|
||||
public enum RenderTargetColorFormat : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// 8 bits per channel linear RGBA.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Robust.Client.Interfaces.Graphics
|
||||
{
|
||||
public enum ScreenshotType
|
||||
public enum ScreenshotType : byte
|
||||
{
|
||||
BeforeUI,
|
||||
AfterUI
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace Robust.Client.Interfaces.Graphics
|
||||
/// <summary>
|
||||
/// OS-standard cursor shapes.
|
||||
/// </summary>
|
||||
public enum StandardCursorShape
|
||||
public enum StandardCursorShape : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The standard arrow shape. Used in almost all situations.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.LoaderApi;
|
||||
using Robust.Shared.Interfaces.Resources;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -47,5 +48,7 @@ namespace Robust.Client.Interfaces.ResourceManagement
|
||||
{
|
||||
void TextureLoaded(TextureLoadedEventArgs eventArgs);
|
||||
void RsiLoaded(RsiLoadedEventArgs eventArgs);
|
||||
|
||||
void MountLoaderApi(IFileApi api, string apiPrefix, ResourcePath? prefix=null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +138,13 @@ namespace Robust.Client.Interfaces.UserInterface
|
||||
/// Hides the tooltip for the indicated control, if tooltip for that control is currently showing.
|
||||
/// </summary>
|
||||
void HideTooltipFor(Control control);
|
||||
|
||||
/// <summary>
|
||||
/// If the control is currently showing a tooltip,
|
||||
/// gets the tooltip that was supplied via TooltipSupplier (null if tooltip
|
||||
/// was not supplied by tooltip supplier or tooltip is not showing for the control).
|
||||
/// </summary>
|
||||
Control? GetSuppliedTooltipFor(Control control);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -618,7 +618,7 @@ namespace Robust.Client.Placement
|
||||
NetworkManager.ClientSendMessage(message);
|
||||
}
|
||||
|
||||
public enum PlacementTypes
|
||||
public enum PlacementTypes : byte
|
||||
{
|
||||
None = 0,
|
||||
Line = 1,
|
||||
|
||||
58
Robust.Client/ResourceManagement/ResourceCache.LoaderApi.cs
Normal file
58
Robust.Client/ResourceManagement/ResourceCache.LoaderApi.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using Robust.LoaderApi;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.ResourceManagement
|
||||
{
|
||||
internal partial class ResourceCache
|
||||
{
|
||||
private sealed class LoaderApiLoader : IContentRoot
|
||||
{
|
||||
private readonly IFileApi _api;
|
||||
private readonly string _prefix;
|
||||
|
||||
public LoaderApiLoader(IFileApi api, string prefix)
|
||||
{
|
||||
_api = api;
|
||||
_prefix = prefix;
|
||||
}
|
||||
|
||||
public void Mount()
|
||||
{
|
||||
}
|
||||
|
||||
public bool TryGetFile(ResourcePath relPath, [NotNullWhen(true)] out Stream? stream)
|
||||
{
|
||||
if (_api.TryOpen($"{_prefix}{relPath}", out stream))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
stream = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public IEnumerable<ResourcePath> FindFiles(ResourcePath path)
|
||||
{
|
||||
foreach (var relPath in _api.AllFiles)
|
||||
{
|
||||
if (!relPath.StartsWith(_prefix))
|
||||
continue;
|
||||
|
||||
var resP = new ResourcePath(relPath[_prefix.Length..]);
|
||||
if (resP.TryRelativeTo(path, out _))
|
||||
{
|
||||
yield return resP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetRelativeFilePaths()
|
||||
{
|
||||
return _api.AllFiles;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,11 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.LoaderApi;
|
||||
|
||||
namespace Robust.Client.ResourceManagement
|
||||
{
|
||||
internal class ResourceCache : ResourceManager, IResourceCacheInternal, IDisposable
|
||||
internal partial class ResourceCache : ResourceManager, IResourceCacheInternal, IDisposable
|
||||
{
|
||||
private readonly Dictionary<Type, Dictionary<ResourcePath, BaseResource>> CachedResources =
|
||||
new();
|
||||
@@ -210,5 +211,12 @@ namespace Robust.Client.ResourceManagement
|
||||
{
|
||||
OnRsiLoaded?.Invoke(eventArgs);
|
||||
}
|
||||
|
||||
public void MountLoaderApi(IFileApi api, string apiPrefix, ResourcePath? prefix=null)
|
||||
{
|
||||
prefix ??= ResourcePath.Root;
|
||||
var root = new LoaderApiLoader(api, apiPrefix);
|
||||
AddRoot(prefix, root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<PackageReference Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" />
|
||||
<PackageReference Include="OpenToolkit.OpenAL" Version="4.0.0-pre9.1" />
|
||||
<PackageReference Include="SpaceWizards.SharpFont" Version="1.0.1" />
|
||||
<PackageReference Include="Robust.Natives" Version="0.1.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(EnableClientScripting)' == 'True'">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="3.8.0" />
|
||||
@@ -32,6 +33,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Lidgren.Network\Lidgren.Network.csproj" />
|
||||
<ProjectReference Include="..\OpenToolkit.GraphicsLibraryFramework\OpenToolkit.GraphicsLibraryFramework.csproj" />
|
||||
<ProjectReference Include="..\Robust.LoaderApi\Robust.LoaderApi\Robust.LoaderApi.csproj" />
|
||||
<ProjectReference Include="..\Robust.Physics\Robust.Physics.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
|
||||
@@ -43,9 +45,4 @@
|
||||
|
||||
<EmbeddedResource Include="Graphics\Clyde\Shaders\*" />
|
||||
</ItemGroup>
|
||||
<Import Project="..\MSBuild\Robust.Engine.targets" />
|
||||
<PropertyGroup>
|
||||
<RobustToolsPath>../Tools</RobustToolsPath>
|
||||
</PropertyGroup>
|
||||
<Target Name="RobustAfterBuild" DependsOnTargets="ClientAfterBuild" AfterTargets="Build" />
|
||||
</Project>
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Client.UserInterface
|
||||
/// <summary>
|
||||
/// Default common cursor shapes available in the UI.
|
||||
/// </summary>
|
||||
public enum CursorShape
|
||||
public enum CursorShape: byte
|
||||
{
|
||||
Arrow,
|
||||
IBeam,
|
||||
|
||||
@@ -193,7 +193,8 @@ namespace Robust.Client.UserInterface
|
||||
|
||||
/// <summary>
|
||||
/// Simple text tooltip that is shown when the mouse is hovered over this control for a bit.
|
||||
/// See <see cref="OnShowTooltip"/> for a more customizable alternative.
|
||||
/// See <see cref="TooltipSupplier"/> or <see cref="OnShowTooltip"/> for a more customizable alternative.
|
||||
/// No effect when TooltipSupplier is specified.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If empty or null, no tooltip is shown in the first place (but OnShowTooltip and OnHideTooltip
|
||||
@@ -201,9 +202,37 @@ namespace Robust.Client.UserInterface
|
||||
/// </remarks>
|
||||
public string? ToolTip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the global tooltip delay, showing the tooltip for this
|
||||
/// control within the specified number of seconds.
|
||||
/// </summary>
|
||||
public float? TooltipDelay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When a tooltip should be shown for this control, this will be invoked to
|
||||
/// produce a control which will serve as the tooltip (doing nothing if null is returned).
|
||||
/// This is the generally recommended way to implement custom tooltips for controls, as it takes
|
||||
/// care of the various edge cases for showing / hiding the tooltip.
|
||||
/// For an even more customizable approach, <see cref="OnShowTooltip"/>
|
||||
///
|
||||
/// The returned control will be added to PopupRoot, and positioned
|
||||
/// within the user interface under the current mouse position to avoid going off the edge of the
|
||||
/// screen. When the tooltip should be hidden, the control will be hidden by removing it from the tree.
|
||||
///
|
||||
/// It is expected that the returned control remains within PopupRoot. Other classes should
|
||||
/// not move it around in the tree or move it out of PopupRoot, but may access and modify
|
||||
/// the control and its children via <see cref="SuppliedTooltip"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Returning a new instance of a tooltip control every time is usually fine. If for some
|
||||
/// reason constructing the tooltip control is expensive, it MAY be fine to cache + reuse a single instance but this
|
||||
/// approach has not yet been tested.
|
||||
/// </remarks>
|
||||
public TooltipSupplier? TooltipSupplier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the mouse is hovered over this control for a bit and a tooltip
|
||||
/// should be shown. Can be used as an alternative to ToolTip to perform custom tooltip
|
||||
/// should be shown. Can be used as an alternative to ToolTip or TooltipSupplier to perform custom tooltip
|
||||
/// logic such as showing a more complex tooltip control.
|
||||
///
|
||||
/// Any custom tooltip controls should typically be added
|
||||
@@ -213,6 +242,23 @@ namespace Robust.Client.UserInterface
|
||||
/// </summary>
|
||||
public event EventHandler? OnShowTooltip;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// If this control is currently showing a tooltip provided via TooltipSupplier,
|
||||
/// returns that tooltip. Do not move this control within the tree, it should remain in PopupRoot.
|
||||
/// Also, as it may be hidden (removed from tree) at any time, saving a reference to this is a Bad Idea.
|
||||
/// </summary>
|
||||
public Control? SuppliedTooltip => UserInterfaceManagerInternal.GetSuppliedTooltipFor(this);
|
||||
|
||||
/// <summary>
|
||||
/// Manually hide the tooltip currently being shown for this control, if there is one.
|
||||
/// </summary>
|
||||
public void HideTooltip()
|
||||
{
|
||||
UserInterfaceManagerInternal.HideTooltipFor(this);
|
||||
}
|
||||
|
||||
internal void PerformShowTooltip()
|
||||
{
|
||||
OnShowTooltip?.Invoke(this, EventArgs.Empty);
|
||||
@@ -228,6 +274,7 @@ namespace Robust.Client.UserInterface
|
||||
OnHideTooltip?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The mode that controls how mouse filtering works. See the enum for how it functions.
|
||||
/// </summary>
|
||||
@@ -757,7 +804,7 @@ namespace Robust.Client.UserInterface
|
||||
/// <summary>
|
||||
/// Mode that will be tested when testing controls to invoke mouse button events on.
|
||||
/// </summary>
|
||||
public enum MouseFilterMode
|
||||
public enum MouseFilterMode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The control will be able to receive mouse buttons events.
|
||||
@@ -865,4 +912,6 @@ namespace Robust.Client.UserInterface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public delegate Control? TooltipSupplier(Control sender);
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
}
|
||||
|
||||
public enum DrawModeEnum
|
||||
public enum DrawModeEnum : byte
|
||||
{
|
||||
Normal = 0,
|
||||
Pressed = 1,
|
||||
@@ -361,7 +361,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// <summary>
|
||||
/// For use with <see cref="BaseButton.Mode"/>.
|
||||
/// </summary>
|
||||
public enum ActionMode
|
||||
public enum ActionMode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="BaseButton.OnPressed"/> fires when the mouse button causing them is pressed down.
|
||||
|
||||
@@ -210,7 +210,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
return new Vector2(minWidth, minHeight);
|
||||
}
|
||||
|
||||
public enum AlignMode
|
||||
public enum AlignMode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Controls are laid out from the begin of the box container.
|
||||
|
||||
@@ -557,13 +557,13 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
}
|
||||
|
||||
public enum Dimension
|
||||
public enum Dimension : byte
|
||||
{
|
||||
Column,
|
||||
Row
|
||||
}
|
||||
|
||||
public enum LimitType
|
||||
public enum LimitType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Defined number of rows or columns
|
||||
|
||||
@@ -570,7 +570,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
}
|
||||
|
||||
public enum ItemListSelectMode
|
||||
public enum ItemListSelectMode : byte
|
||||
{
|
||||
None,
|
||||
Single,
|
||||
|
||||
@@ -194,7 +194,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
}
|
||||
|
||||
public enum AlignMode
|
||||
public enum AlignMode : byte
|
||||
{
|
||||
Left = 0,
|
||||
Center = 1,
|
||||
@@ -202,7 +202,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
Fill = 3
|
||||
}
|
||||
|
||||
public enum VAlignMode
|
||||
public enum VAlignMode : byte
|
||||
{
|
||||
Top = 0,
|
||||
Center = 1,
|
||||
|
||||
@@ -126,7 +126,16 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
/// <summary>
|
||||
/// Sets an anchor AND a margin preset. This is most likely the method you want.
|
||||
///
|
||||
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that the current size and minimum size of the control affects how
|
||||
/// each of the margins will be set, so if your control needs to shrink beyond its
|
||||
/// current size / min size, you should either not call this method or only call it when your
|
||||
/// control has a size of (0, 0). Otherwise your control's size will never be able
|
||||
/// to go below the size implied by the margins set in this method.
|
||||
/// </remarks>
|
||||
public static void SetAnchorAndMarginPreset(Control control, LayoutPreset preset,
|
||||
LayoutPresetMode mode = LayoutPresetMode.MinSize,
|
||||
int margin = 0)
|
||||
@@ -274,6 +283,12 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// <summary>
|
||||
/// Changes all the margins of a control at once to common presets.
|
||||
/// The result is that the control is laid out as specified by the preset.
|
||||
///
|
||||
/// Note that the current size and minimum size of the control affects how
|
||||
/// each of the margins will be set, so if your control needs to shrink beyond its
|
||||
/// current size / min size, you should either not call this method or only call it when your
|
||||
/// control has a size of (0, 0). Otherwise your control's size will never be able
|
||||
/// to go below the size implied by the margins set in this method.
|
||||
/// </summary>
|
||||
/// <param name="preset"></param>
|
||||
/// <param name="resizeMode"></param>
|
||||
|
||||
@@ -711,7 +711,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
return CharClass.Other;
|
||||
}
|
||||
|
||||
private enum CharClass
|
||||
private enum CharClass : byte
|
||||
{
|
||||
Other,
|
||||
AlphaNumeric,
|
||||
|
||||
325
Robust.Client/UserInterface/Controls/MultiselectOptionButton.cs
Normal file
325
Robust.Client/UserInterface/Controls/MultiselectOptionButton.cs
Normal file
@@ -0,0 +1,325 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Option button which allows toggling multiple elements.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">type to use as the unique key for each option. Functions similarly
|
||||
/// to dictionary key, so the type should make sure to respect dictionary key semantics.</typeparam>
|
||||
public class MultiselectOptionButton<TKey> : ContainerButton where TKey : notnull
|
||||
{
|
||||
public const string StyleClassOptionButton = "optionButton";
|
||||
public const string StyleClassOptionTriangle = "optionTriangle";
|
||||
|
||||
private List<ButtonData> _buttonData = new();
|
||||
// map from key to buttondata index
|
||||
private Dictionary<TKey, int> _keyMap = new();
|
||||
private readonly Popup _popup;
|
||||
private readonly VBoxContainer _popupVBox;
|
||||
private readonly Label _label;
|
||||
|
||||
public event Action<ItemPressedEventArgs>? OnItemSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks the order in which items were selected, latest going at the end.
|
||||
/// </summary>
|
||||
private List<TKey> _selectedKeys = new();
|
||||
|
||||
/// <summary>
|
||||
/// Ids of all currently selected items, ordered by most recently selected = last
|
||||
/// </summary>
|
||||
public IReadOnlyList<TKey> SelectedKeys => _selectedKeys;
|
||||
|
||||
public int ItemCount => _buttonData.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Labels of all currently selected items, ordered by most recently selected = last
|
||||
/// </summary>
|
||||
public IEnumerable<string?> SelectedLabels => _selectedKeys
|
||||
.Select(key => _buttonData[_keyMap[key]].Button.Label.Text);
|
||||
|
||||
/// <summary>
|
||||
/// Metadata of all currently selected items, ordered by most recently selected = last
|
||||
/// </summary>
|
||||
public IEnumerable<object?> SelectedMetadata => _selectedKeys
|
||||
.Select(key => _buttonData[_keyMap[key]].Metadata);
|
||||
|
||||
public string? Label
|
||||
{
|
||||
get => _label.Text;
|
||||
set => _label.Text = value;
|
||||
}
|
||||
|
||||
public MultiselectOptionButton()
|
||||
{
|
||||
AddStyleClass(StyleClassButton);
|
||||
OnPressed += OnPressedInternal;
|
||||
|
||||
var hBox = new HBoxContainer();
|
||||
AddChild(hBox);
|
||||
|
||||
_popup = new Popup();
|
||||
_popupVBox = new VBoxContainer();
|
||||
_popup.AddChild(_popupVBox);
|
||||
_popup.OnPopupHide += OnPopupHide;
|
||||
|
||||
_label = new Label
|
||||
{
|
||||
StyleClasses = { StyleClassOptionButton },
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
};
|
||||
hBox.AddChild(_label);
|
||||
|
||||
var textureRect = new TextureRect
|
||||
{
|
||||
StyleClasses = { StyleClassOptionTriangle },
|
||||
SizeFlagsVertical = SizeFlags.ShrinkCenter,
|
||||
};
|
||||
hBox.AddChild(textureRect);
|
||||
}
|
||||
|
||||
public void AddItem(Texture icon, string label, TKey key)
|
||||
{
|
||||
AddItem(label, key);
|
||||
}
|
||||
|
||||
public void AddItem(string label, TKey key)
|
||||
{
|
||||
if (_keyMap.ContainsKey(key))
|
||||
{
|
||||
throw new ArgumentException("An item with the same key already exists.");
|
||||
}
|
||||
|
||||
var button = new Button
|
||||
{
|
||||
Text = label,
|
||||
ToggleMode = true
|
||||
};
|
||||
button.OnPressed += ButtonOnPressed;
|
||||
var data = new ButtonData(label, button, key);
|
||||
_keyMap.Add(key, _buttonData.Count);
|
||||
_buttonData.Add(data);
|
||||
_popupVBox.AddChild(button);
|
||||
}
|
||||
|
||||
private void TogglePopup(bool show)
|
||||
{
|
||||
if (show)
|
||||
{
|
||||
var globalPos = GlobalPosition;
|
||||
var (minX, minY) = _popupVBox.CombinedMinimumSize;
|
||||
var box = UIBox2.FromDimensions(globalPos, (Math.Max(minX, Width), minY));
|
||||
UserInterfaceManager.ModalRoot.AddChild(_popup);
|
||||
_popup.Open(box);
|
||||
}
|
||||
else
|
||||
{
|
||||
_popup.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPopupHide()
|
||||
{
|
||||
UserInterfaceManager.ModalRoot.RemoveChild(_popup);
|
||||
}
|
||||
|
||||
|
||||
private void ButtonOnPressed(ButtonEventArgs obj)
|
||||
{
|
||||
TogglePopup(false);
|
||||
foreach (var buttonData in _buttonData)
|
||||
{
|
||||
if (buttonData.Button == obj.Button)
|
||||
{
|
||||
if (obj.Button.Pressed)
|
||||
{
|
||||
_selectedKeys.Add(buttonData.Key);
|
||||
}
|
||||
else
|
||||
{
|
||||
_selectedKeys.Remove(buttonData.Key);
|
||||
}
|
||||
OnItemSelected?.Invoke(new ItemPressedEventArgs(buttonData.Key, obj.Button.Pressed, this));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Not reachable.
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_keyMap.Clear();
|
||||
foreach (var buttonDatum in _buttonData)
|
||||
{
|
||||
buttonDatum.Button.OnPressed -= ButtonOnPressed;
|
||||
}
|
||||
_buttonData.Clear();
|
||||
_popupVBox.DisposeAllChildren();
|
||||
_selectedKeys = new List<TKey>();
|
||||
}
|
||||
|
||||
public TKey GetItemKey(int idx)
|
||||
{
|
||||
return _buttonData[idx].Key;
|
||||
}
|
||||
|
||||
public object? GetItemMetadata(int idx)
|
||||
{
|
||||
return _buttonData[idx].Metadata;
|
||||
}
|
||||
|
||||
public bool IsItemDisabled(int idx)
|
||||
{
|
||||
return _buttonData[idx].Disabled;
|
||||
}
|
||||
|
||||
public void RemoveItem(int idx)
|
||||
{
|
||||
var data = _buttonData[idx];
|
||||
data.Button.OnPressed -= ButtonOnPressed;
|
||||
_keyMap.Remove(data.Key);
|
||||
_popupVBox.RemoveChild(data.Button);
|
||||
_buttonData.RemoveAt(idx);
|
||||
var newIdx = 0;
|
||||
foreach (var buttonData in _buttonData)
|
||||
{
|
||||
_keyMap[buttonData.Key] = newIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
public void Select(int idx)
|
||||
{
|
||||
var data = _buttonData[idx];
|
||||
if (data.Button.Pressed) return;
|
||||
_selectedKeys.Add(data.Key);
|
||||
data.Button.Pressed = true;
|
||||
}
|
||||
|
||||
public void SelectKey(TKey key)
|
||||
{
|
||||
Select(GetIdx(key));
|
||||
}
|
||||
|
||||
public void DeselectAll()
|
||||
{
|
||||
foreach (var buttonData in _buttonData)
|
||||
{
|
||||
Deselect(buttonData);
|
||||
}
|
||||
}
|
||||
|
||||
public void Deselect(int idx)
|
||||
{
|
||||
Deselect(_buttonData[idx]);
|
||||
}
|
||||
|
||||
public void DeselectKey(TKey key)
|
||||
{
|
||||
Deselect(GetIdx(key));
|
||||
}
|
||||
|
||||
private void Deselect(ButtonData data)
|
||||
{
|
||||
if (!data.Button.Pressed) return;
|
||||
_selectedKeys.Remove(data.Key);
|
||||
data.Button.Pressed = false;
|
||||
}
|
||||
|
||||
|
||||
public int GetIdx(TKey key)
|
||||
{
|
||||
return _keyMap[key];
|
||||
}
|
||||
|
||||
public void SetItemDisabled(int idx, bool disabled)
|
||||
{
|
||||
var data = _buttonData[idx];
|
||||
data.Disabled = disabled;
|
||||
data.Button.Disabled = disabled;
|
||||
}
|
||||
|
||||
public void SetItemKey(int idx, TKey key)
|
||||
{
|
||||
if (_keyMap.TryGetValue(key, out var existIdx) && existIdx != idx)
|
||||
{
|
||||
throw new InvalidOperationException("An item with said key already exists.");
|
||||
}
|
||||
|
||||
var data = _buttonData[idx];
|
||||
_keyMap.Remove(data.Key);
|
||||
_keyMap.Add(key, idx);
|
||||
data.Key = key;
|
||||
}
|
||||
|
||||
public void SetItemMetadata(int idx, object metadata)
|
||||
{
|
||||
_buttonData[idx].Metadata = metadata;
|
||||
}
|
||||
|
||||
public void SetItemText(int idx, string text)
|
||||
{
|
||||
var data = _buttonData[idx];
|
||||
data.Text = text;
|
||||
data.Button.Text = text;
|
||||
}
|
||||
|
||||
private void OnPressedInternal(ButtonEventArgs args)
|
||||
{
|
||||
TogglePopup(true);
|
||||
}
|
||||
|
||||
protected override void ExitedTree()
|
||||
{
|
||||
base.ExitedTree();
|
||||
TogglePopup(false);
|
||||
}
|
||||
|
||||
public class ItemPressedEventArgs : EventArgs
|
||||
{
|
||||
public readonly MultiselectOptionButton<TKey> Button;
|
||||
/// <summary>
|
||||
/// True if item is being selected, false if being unselected
|
||||
/// </summary>
|
||||
public readonly bool Selected;
|
||||
/// <summary>
|
||||
/// True if item is being deselected, false if being selected
|
||||
/// </summary>
|
||||
public bool Deselected => !Selected;
|
||||
|
||||
/// <summary>
|
||||
/// The key of the item that has been selected or deselected.
|
||||
/// </summary>
|
||||
public readonly TKey Key;
|
||||
|
||||
public ItemPressedEventArgs(TKey key, bool selected, MultiselectOptionButton<TKey> button)
|
||||
{
|
||||
Key = key;
|
||||
Selected = selected;
|
||||
Button = button;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ButtonData
|
||||
{
|
||||
public string Text;
|
||||
public bool Disabled;
|
||||
public object? Metadata;
|
||||
public TKey Key;
|
||||
public Button Button;
|
||||
|
||||
public ButtonData(string text, Button button, TKey key)
|
||||
{
|
||||
Text = text;
|
||||
Button = button;
|
||||
Key = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,29 +10,31 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public const string StyleClassOptionButton = "optionButton";
|
||||
public const string StyleClassOptionTriangle = "optionTriangle";
|
||||
|
||||
private List<ButtonData> _buttonData = new();
|
||||
private Dictionary<int, int> _idMap = new();
|
||||
private Popup _popup;
|
||||
private VBoxContainer _popupVBox;
|
||||
private Label _label;
|
||||
private readonly List<ButtonData> _buttonData = new();
|
||||
private readonly Dictionary<int, int> _idMap = new();
|
||||
private readonly Popup _popup;
|
||||
private readonly VBoxContainer _popupVBox;
|
||||
private readonly Label _label;
|
||||
|
||||
public int ItemCount => _buttonData.Count;
|
||||
|
||||
public event Action<ItemSelectedEventArgs>? OnItemSelected;
|
||||
|
||||
public string Prefix { get; set; }
|
||||
|
||||
public OptionButton() : base()
|
||||
public OptionButton()
|
||||
{
|
||||
AddStyleClass(StyleClassButton);
|
||||
Prefix = "";
|
||||
OnPressed += _onPressed;
|
||||
OnPressed += OnPressedInternal;
|
||||
|
||||
var hBox = new HBoxContainer();
|
||||
AddChild(hBox);
|
||||
|
||||
_popup = new Popup();
|
||||
UserInterfaceManager.ModalRoot.AddChild(_popup);
|
||||
_popupVBox = new VBoxContainer();
|
||||
_popup.AddChild(_popupVBox);
|
||||
_popup.OnPopupHide += OnPopupHide;
|
||||
|
||||
_label = new Label
|
||||
{
|
||||
@@ -85,10 +87,31 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
}
|
||||
|
||||
private void TogglePopup(bool show)
|
||||
{
|
||||
if (show)
|
||||
{
|
||||
var globalPos = GlobalPosition;
|
||||
var (minX, minY) = _popupVBox.CombinedMinimumSize;
|
||||
var box = UIBox2.FromDimensions(globalPos, (Math.Max(minX, Width), minY));
|
||||
UserInterfaceManager.ModalRoot.AddChild(_popup);
|
||||
_popup.Open(box);
|
||||
}
|
||||
else
|
||||
{
|
||||
_popup.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPopupHide()
|
||||
{
|
||||
UserInterfaceManager.ModalRoot.RemoveChild(_popup);
|
||||
}
|
||||
|
||||
private void ButtonOnPressed(ButtonEventArgs obj)
|
||||
{
|
||||
obj.Button.Pressed = false;
|
||||
_popup.Visible = false;
|
||||
TogglePopup(false);
|
||||
foreach (var buttonData in _buttonData)
|
||||
{
|
||||
if (buttonData.Button == obj.Button)
|
||||
@@ -105,13 +128,15 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public void Clear()
|
||||
{
|
||||
_idMap.Clear();
|
||||
foreach (var buttonDatum in _buttonData)
|
||||
{
|
||||
buttonDatum.Button.OnPressed -= ButtonOnPressed;
|
||||
}
|
||||
_buttonData.Clear();
|
||||
_popupVBox.DisposeAllChildren();
|
||||
SelectedId = 0;
|
||||
}
|
||||
|
||||
public int ItemCount => _buttonData.Count;
|
||||
|
||||
public int GetItemId(int idx)
|
||||
{
|
||||
return _buttonData[idx].Id;
|
||||
@@ -134,9 +159,15 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public void RemoveItem(int idx)
|
||||
{
|
||||
var data = _buttonData[idx];
|
||||
data.Button.OnPressed -= ButtonOnPressed;
|
||||
_idMap.Remove(data.Id);
|
||||
_popupVBox.RemoveChild(data.Button);
|
||||
_buttonData.RemoveAt(idx);
|
||||
var newIdx = 0;
|
||||
foreach (var buttonData in _buttonData)
|
||||
{
|
||||
_idMap[buttonData.Id] = newIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
public void Select(int idx)
|
||||
@@ -168,13 +199,9 @@ namespace Robust.Client.UserInterface.Controls
|
||||
data.Button.Disabled = disabled;
|
||||
}
|
||||
|
||||
public void SetItemIcon(int idx, Texture texture)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetItemId(int idx, int id)
|
||||
{
|
||||
if (_idMap.TryGetValue(id, out var existIdx) && existIdx != id)
|
||||
if (_idMap.TryGetValue(id, out var existIdx) && existIdx != idx)
|
||||
{
|
||||
throw new InvalidOperationException("An item with said ID already exists.");
|
||||
}
|
||||
@@ -202,19 +229,15 @@ namespace Robust.Client.UserInterface.Controls
|
||||
data.Button.Text = text;
|
||||
}
|
||||
|
||||
private void _onPressed(ButtonEventArgs args)
|
||||
private void OnPressedInternal(ButtonEventArgs args)
|
||||
{
|
||||
var globalPos = GlobalPosition;
|
||||
var (minX, minY) = _popupVBox.CombinedMinimumSize;
|
||||
var box = UIBox2.FromDimensions(globalPos, (Math.Max(minX, Width), minY));
|
||||
_popup.Open(box);
|
||||
TogglePopup(true);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
protected override void ExitedTree()
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
_popup?.Dispose();
|
||||
base.ExitedTree();
|
||||
TogglePopup(false);
|
||||
}
|
||||
|
||||
public class ItemSelectedEventArgs : EventArgs
|
||||
|
||||
@@ -21,7 +21,10 @@ namespace Robust.Client.UserInterface.Controls
|
||||
UserInterfaceManagerInternal.RemoveModal(this);
|
||||
}
|
||||
|
||||
if (box != null && _desiredSize != box.Value.Size)
|
||||
if (box != null &&
|
||||
(_desiredSize != box.Value.Size ||
|
||||
PopupContainer.GetPopupOrigin(this) != box.Value.TopLeft ||
|
||||
PopupContainer.GetAltOrigin(this) != altPos))
|
||||
{
|
||||
PopupContainer.SetPopupOrigin(this, box.Value.TopLeft);
|
||||
PopupContainer.SetAltOrigin(this, altPos);
|
||||
@@ -34,6 +37,13 @@ namespace Robust.Client.UserInterface.Controls
|
||||
UserInterfaceManagerInternal.PushModal(this);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (!Visible) return;
|
||||
UserInterfaceManagerInternal.RemoveModal(this);
|
||||
}
|
||||
|
||||
|
||||
protected internal override void ModalRemoved()
|
||||
{
|
||||
base.ModalRemoved();
|
||||
|
||||
@@ -35,6 +35,16 @@ namespace Robust.Client.UserInterface.Controls
|
||||
control.SetValue(PopupOriginProperty, origin);
|
||||
}
|
||||
|
||||
public static Vector2 GetPopupOrigin(Control control)
|
||||
{
|
||||
return control.GetValue<Vector2>(PopupOriginProperty);
|
||||
}
|
||||
|
||||
public static Vector2? GetAltOrigin(Control control)
|
||||
{
|
||||
return control.GetValue<Vector2?>(AltOriginProperty);
|
||||
}
|
||||
|
||||
public static void SetAltOrigin(Control control, Vector2? origin)
|
||||
{
|
||||
control.SetValue(AltOriginProperty, origin);
|
||||
|
||||
@@ -225,7 +225,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
return _getGrabberStyleBox()?.MinimumSize ?? Vector2.Zero;
|
||||
}
|
||||
|
||||
protected enum OrientationMode
|
||||
protected enum OrientationMode : byte
|
||||
{
|
||||
Horizontal,
|
||||
Vertical
|
||||
|
||||
@@ -230,7 +230,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// <summary>
|
||||
/// Defines how user-initiated moving of the split should work
|
||||
/// </summary>
|
||||
public enum SplitResizeMode
|
||||
public enum SplitResizeMode : sbyte
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't allow user to move the split.
|
||||
@@ -249,7 +249,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// <summary>
|
||||
/// Defines how the split position should be determined
|
||||
/// </summary>
|
||||
private enum SplitState
|
||||
private enum SplitState : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Automatically adjust the split based on the width of the children
|
||||
|
||||
@@ -173,7 +173,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
}
|
||||
|
||||
public enum StretchMode
|
||||
public enum StretchMode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The texture is stretched to fit the entire area of the control.
|
||||
|
||||
@@ -255,7 +255,7 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
}
|
||||
|
||||
[Flags]
|
||||
protected enum DragMode
|
||||
protected enum DragMode : byte
|
||||
{
|
||||
None = 0,
|
||||
Move = 1,
|
||||
|
||||
@@ -77,8 +77,11 @@ namespace Robust.Client.UserInterface
|
||||
|
||||
private bool _rendering = true;
|
||||
private float _tooltipTimer;
|
||||
// set to null when not counting down
|
||||
private float? _tooltipDelay;
|
||||
private Tooltip _tooltip = default!;
|
||||
private bool showingTooltip;
|
||||
private Control? _suppliedTooltip;
|
||||
private const float TooltipDelay = 1;
|
||||
|
||||
private readonly Queue<Control> _styleUpdateQueue = new();
|
||||
@@ -208,10 +211,15 @@ namespace Robust.Client.UserInterface
|
||||
control.DoLayoutUpdate();
|
||||
}
|
||||
|
||||
_tooltipTimer -= args.DeltaSeconds;
|
||||
if (_tooltipTimer <= 0)
|
||||
// count down tooltip delay if we're not showing one yet and
|
||||
// are hovering the mouse over a control without moving it
|
||||
if (_tooltipDelay != null && !showingTooltip)
|
||||
{
|
||||
_showTooltip();
|
||||
_tooltipTimer += args.DeltaSeconds;
|
||||
if (_tooltipTimer >= _tooltipDelay)
|
||||
{
|
||||
_showTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
if (_needUpdateActiveCursor)
|
||||
@@ -332,6 +340,14 @@ namespace Robust.Client.UserInterface
|
||||
CurrentlyHovered?.MouseExited();
|
||||
CurrentlyHovered = newHovered;
|
||||
CurrentlyHovered?.MouseEntered();
|
||||
if (CurrentlyHovered != null)
|
||||
{
|
||||
_tooltipDelay = CurrentlyHovered.TooltipDelay ?? TooltipDelay;
|
||||
}
|
||||
else
|
||||
{
|
||||
_tooltipDelay = null;
|
||||
}
|
||||
|
||||
_needUpdateActiveCursor = true;
|
||||
}
|
||||
@@ -509,6 +525,7 @@ namespace Robust.Client.UserInterface
|
||||
{
|
||||
control.MouseExited();
|
||||
CurrentlyHovered = null;
|
||||
_clearTooltip();
|
||||
}
|
||||
|
||||
if (control == _controlFocused)
|
||||
@@ -695,6 +712,11 @@ namespace Robust.Client.UserInterface
|
||||
{
|
||||
if (!showingTooltip) return;
|
||||
_tooltip.Visible = false;
|
||||
if (_suppliedTooltip != null)
|
||||
{
|
||||
PopupRoot.RemoveChild(_suppliedTooltip);
|
||||
_suppliedTooltip = null;
|
||||
}
|
||||
CurrentlyHovered?.PerformHideTooltip();
|
||||
_resetTooltipTimer();
|
||||
showingTooltip = false;
|
||||
@@ -709,9 +731,14 @@ namespace Robust.Client.UserInterface
|
||||
}
|
||||
}
|
||||
|
||||
public Control? GetSuppliedTooltipFor(Control control)
|
||||
{
|
||||
return CurrentlyHovered == control ? _suppliedTooltip : null;
|
||||
}
|
||||
|
||||
private void _resetTooltipTimer()
|
||||
{
|
||||
_tooltipTimer = TooltipDelay;
|
||||
_tooltipTimer = 0;
|
||||
}
|
||||
|
||||
private void _showTooltip()
|
||||
@@ -724,9 +751,19 @@ namespace Robust.Client.UserInterface
|
||||
return;
|
||||
}
|
||||
|
||||
// show simple tooltip if there is one
|
||||
if (!String.IsNullOrWhiteSpace(hovered.ToolTip))
|
||||
// show supplied tooltip if there is one
|
||||
if (hovered.TooltipSupplier != null)
|
||||
{
|
||||
_suppliedTooltip = hovered.TooltipSupplier.Invoke(hovered);
|
||||
if (_suppliedTooltip != null)
|
||||
{
|
||||
PopupRoot.AddChild(_suppliedTooltip);
|
||||
Tooltips.PositionTooltip(_suppliedTooltip);
|
||||
}
|
||||
}
|
||||
else if (!String.IsNullOrWhiteSpace(hovered.ToolTip))
|
||||
{
|
||||
// show simple tooltip if there is one
|
||||
_tooltip.Visible = true;
|
||||
_tooltip.Text = hovered.ToolTip;
|
||||
Tooltips.PositionTooltip(_tooltip);
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Robust.Client.ViewVariables.Editors
|
||||
return convert.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public enum NumberType
|
||||
public enum NumberType : byte
|
||||
{
|
||||
Byte,
|
||||
SByte,
|
||||
|
||||
@@ -174,7 +174,7 @@ namespace Robust.Client.ViewVariables.Editors
|
||||
return hBoxContainer;
|
||||
}
|
||||
|
||||
public enum BoxType
|
||||
public enum BoxType : byte
|
||||
{
|
||||
Box2,
|
||||
Box2i,
|
||||
|
||||
1
Robust.LoaderApi
Submodule
1
Robust.LoaderApi
Submodule
Submodule Robust.LoaderApi added at 0d5c015792
@@ -83,32 +83,29 @@ namespace Robust.Server.ServerStatus
|
||||
return true;
|
||||
}
|
||||
|
||||
var downloadUrlWindows = _configurationManager.GetCVar(CVars.BuildDownloadUrlWindows);
|
||||
var downloadUrl = _configurationManager.GetCVar(CVars.BuildDownloadUrl);
|
||||
|
||||
JObject? buildInfo;
|
||||
|
||||
if (string.IsNullOrEmpty(downloadUrlWindows))
|
||||
if (string.IsNullOrEmpty(downloadUrl))
|
||||
{
|
||||
buildInfo = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var hash = _configurationManager.GetCVar(CVars.BuildHash);
|
||||
if (hash == "")
|
||||
{
|
||||
hash = null;
|
||||
}
|
||||
|
||||
buildInfo = new JObject
|
||||
{
|
||||
["download_urls"] = new JObject
|
||||
{
|
||||
["Windows"] = downloadUrlWindows,
|
||||
["MacOS"] = _configurationManager.GetCVar(CVars.BuildDownloadUrlMacOS),
|
||||
["Linux"] = _configurationManager.GetCVar(CVars.BuildDownloadUrlLinux)
|
||||
},
|
||||
["engine_version"] = _configurationManager.GetCVar(CVars.BuildEngineVersion),
|
||||
["fork_id"] = _configurationManager.GetCVar(CVars.BuildForkId),
|
||||
["version"] = _configurationManager.GetCVar(CVars.BuildVersion),
|
||||
["hashes"] = new JObject
|
||||
{
|
||||
["Windows"] = _configurationManager.GetCVar(CVars.BuildHashWindows),
|
||||
["MacOS"] = _configurationManager.GetCVar(CVars.BuildHashMacOS),
|
||||
["Linux"] = _configurationManager.GetCVar(CVars.BuildHashLinux),
|
||||
},
|
||||
["download_url"] = downloadUrl,
|
||||
["hash"] = hash,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -141,24 +141,20 @@ namespace Robust.Server.ServerStatus
|
||||
|
||||
private void RegisterCVars()
|
||||
{
|
||||
BuildInfo? info = null;
|
||||
try
|
||||
{
|
||||
var buildInfo = File.ReadAllText(PathHelpers.ExecutableRelativeFile("build.json"));
|
||||
info = JsonConvert.DeserializeObject<BuildInfo>(buildInfo);
|
||||
var info = JsonConvert.DeserializeObject<BuildInfo>(buildInfo);
|
||||
|
||||
_configurationManager.SetCVar(CVars.BuildEngineVersion, info.EngineVersion);
|
||||
_configurationManager.SetCVar(CVars.BuildForkId, info.ForkId);
|
||||
_configurationManager.SetCVar(CVars.BuildVersion, info.Version);
|
||||
_configurationManager.SetCVar(CVars.BuildDownloadUrl, info.Download);
|
||||
_configurationManager.SetCVar(CVars.BuildHash, info.Hash ?? "");
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
}
|
||||
|
||||
_configurationManager.SetCVar(CVars.BuildForkId, info?.ForkId ?? "");
|
||||
_configurationManager.SetCVar(CVars.BuildVersion, info?.Version ?? "");
|
||||
_configurationManager.SetCVar(CVars.BuildDownloadUrlWindows, info?.Downloads.Windows ?? "");
|
||||
_configurationManager.SetCVar(CVars.BuildDownloadUrlMacOS, info?.Downloads.MacOS ?? "");
|
||||
_configurationManager.SetCVar(CVars.BuildDownloadUrlLinux, info?.Downloads.Linux ?? "");
|
||||
_configurationManager.SetCVar(CVars.BuildHashWindows, info?.Hashes.Windows ?? "");
|
||||
_configurationManager.SetCVar(CVars.BuildHashMacOS, info?.Hashes.MacOS ?? "");
|
||||
_configurationManager.SetCVar(CVars.BuildHashLinux, info?.Hashes.Linux ?? "");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -175,18 +171,11 @@ namespace Robust.Server.ServerStatus
|
||||
[JsonObject(ItemRequired = Required.DisallowNull)]
|
||||
private sealed class BuildInfo
|
||||
{
|
||||
[JsonProperty("hashes")] public PlatformData Hashes { get; set; } = default!;
|
||||
[JsonProperty("downloads")] public PlatformData Downloads { get; set; } = default!;
|
||||
[JsonProperty("fork_id")] public string ForkId { get; set; } = default!;
|
||||
[JsonProperty("version")] public string Version { get; set; } = default!;
|
||||
}
|
||||
|
||||
[JsonObject(ItemRequired = Required.DisallowNull)]
|
||||
private sealed class PlatformData
|
||||
{
|
||||
[JsonProperty("windows")] public string Windows { get; set; } = default!;
|
||||
[JsonProperty("linux")] public string Linux { get; set; } = default!;
|
||||
[JsonProperty("macos")] public string MacOS { get; set; } = default!;
|
||||
[JsonProperty("engine_version")] public string EngineVersion = default!;
|
||||
[JsonProperty("hashes")] public string? Hash;
|
||||
[JsonProperty("downloads")] public string Download = default!;
|
||||
[JsonProperty("fork_id")] public string ForkId = default!;
|
||||
[JsonProperty("version")] public string Version = default!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,8 +87,10 @@ namespace Robust.Shared.Maths
|
||||
public Vector2 RotateVec(in Vector2 vec)
|
||||
{
|
||||
var (x, y) = vec;
|
||||
var dx = Math.Cos(Theta) * x - Math.Sin(Theta) * y;
|
||||
var dy = Math.Sin(Theta) * x + Math.Cos(Theta) * y;
|
||||
var cos = Math.Cos(Theta);
|
||||
var sin = Math.Sin(Theta);
|
||||
var dx = cos * x - sin * y;
|
||||
var dy = sin * x + cos * y;
|
||||
|
||||
return new Vector2((float)dx, (float)dy);
|
||||
}
|
||||
@@ -184,6 +186,11 @@ namespace Robust.Shared.Maths
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
public Angle Opposite()
|
||||
{
|
||||
return new Angle(FlipPositive(Theta-Math.PI));
|
||||
}
|
||||
|
||||
public Angle FlipPositive()
|
||||
{
|
||||
return new(FlipPositive(Theta));
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Robust.Shared.Maths
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct Box2 : IEquatable<Box2>
|
||||
public struct Box2 : IEquatable<Box2>, IApproxEquatable<Box2>
|
||||
{
|
||||
/// <summary>
|
||||
/// The X coordinate of the left edge of the box.
|
||||
@@ -336,5 +336,21 @@ namespace Robust.Shared.Maths
|
||||
|
||||
return new Vector2(cx, cy);
|
||||
}
|
||||
|
||||
public bool EqualsApprox(Box2 other)
|
||||
{
|
||||
return MathHelper.CloseTo(Left, other.Left)
|
||||
&& MathHelper.CloseTo(Bottom, other.Bottom)
|
||||
&& MathHelper.CloseTo(Right, other.Right)
|
||||
&& MathHelper.CloseTo(Top, other.Top);
|
||||
}
|
||||
|
||||
public bool EqualsApprox(Box2 other, double tolerance)
|
||||
{
|
||||
return MathHelper.CloseTo(Left, other.Left, tolerance)
|
||||
&& MathHelper.CloseTo(Bottom, other.Bottom, tolerance)
|
||||
&& MathHelper.CloseTo(Right, other.Right, tolerance)
|
||||
&& MathHelper.CloseTo(Top, other.Top, tolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
namespace Robust.Shared.Maths
|
||||
{
|
||||
@@ -10,6 +13,7 @@ namespace Robust.Shared.Maths
|
||||
{
|
||||
public Box2 Box;
|
||||
public Angle Rotation;
|
||||
|
||||
/// <summary>
|
||||
/// The point about which the rotation occurs.
|
||||
/// </summary>
|
||||
@@ -26,13 +30,19 @@ namespace Robust.Shared.Maths
|
||||
public readonly Vector2 BottomLeft => Origin + Rotation.RotateVec(Box.BottomLeft - Origin);
|
||||
|
||||
public Box2Rotated(Vector2 bottomLeft, Vector2 topRight)
|
||||
: this(new Box2(bottomLeft, topRight)) { }
|
||||
: this(new Box2(bottomLeft, topRight))
|
||||
{
|
||||
}
|
||||
|
||||
public Box2Rotated(Box2 box)
|
||||
: this(box, 0) { }
|
||||
: this(box, 0)
|
||||
{
|
||||
}
|
||||
|
||||
public Box2Rotated(Box2 box, Angle rotation)
|
||||
: this(box, rotation, Vector2.Zero) { }
|
||||
: this(box, rotation, Vector2.Zero)
|
||||
{
|
||||
}
|
||||
|
||||
public Box2Rotated(Box2 box, Angle rotation, Vector2 origin)
|
||||
{
|
||||
@@ -46,24 +56,97 @@ namespace Robust.Shared.Maths
|
||||
/// </summary>
|
||||
public readonly Box2 CalcBoundingBox()
|
||||
{
|
||||
// https://stackoverflow.com/a/19830964
|
||||
if (Sse.IsSupported && NumericsHelpers.Enabled)
|
||||
{
|
||||
return CalcBoundingBoxSse();
|
||||
}
|
||||
|
||||
var (X0, Y0) = Box.BottomLeft;
|
||||
var (X1, Y1) = Box.TopRight;
|
||||
return CalcBoundingBoxSlow();
|
||||
}
|
||||
|
||||
var Fi = Rotation.Theta;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal readonly unsafe Box2 CalcBoundingBoxSse()
|
||||
{
|
||||
Vector128<float> boxVec;
|
||||
fixed (float* lPtr = &Box.Left)
|
||||
{
|
||||
boxVec = Sse.LoadVector128(lPtr);
|
||||
}
|
||||
|
||||
var CX = (X0 + X1) / 2; //Center point
|
||||
var CY = (Y0 + Y1) / 2;
|
||||
var WX = (X1 - X0) / 2; //Half-width
|
||||
var WY = (Y1 - Y0) / 2;
|
||||
var originX = Vector128.Create(Origin.X);
|
||||
var originY = Vector128.Create(Origin.Y);
|
||||
|
||||
var SF = Math.Sin(Fi);
|
||||
var CF = Math.Cos(Fi);
|
||||
var cos = Vector128.Create((float) Math.Cos(Rotation));
|
||||
var sin = Vector128.Create((float) Math.Sin(Rotation));
|
||||
|
||||
var NH = Math.Abs(WX * SF) + Math.Abs(WY * CF); //boundrect half-height
|
||||
var NW = Math.Abs(WX * CF) + Math.Abs(WY * SF); //boundrect half-width
|
||||
return new Box2((float) (CX - NW), (float) (CY - NH), (float) (CX + NW), (float) (CY + NH)); //draw bound rectangle
|
||||
var allX = Sse.Shuffle(boxVec, boxVec, 0b10_10_00_00);
|
||||
var allY = Sse.Shuffle(boxVec, boxVec, 0b01_11_11_01);
|
||||
|
||||
allX = Sse.Subtract(allX, originX);
|
||||
allY = Sse.Subtract(allY, originY);
|
||||
|
||||
var modX = Sse.Subtract(Sse.Multiply(allX, cos), Sse.Multiply(allY, sin));
|
||||
var modY = Sse.Add(Sse.Multiply(allX, sin), Sse.Multiply(allY, cos));
|
||||
|
||||
allX = Sse.Add(modX, originX);
|
||||
allY = Sse.Add(modY, originY);
|
||||
|
||||
var l = SimdHelpers.MinHorizontalSse(allX);
|
||||
var b = SimdHelpers.MinHorizontalSse(allY);
|
||||
var r = SimdHelpers.MaxHorizontalSse(allX);
|
||||
var t = SimdHelpers.MaxHorizontalSse(allY);
|
||||
|
||||
var lb = Sse.UnpackLow(l, b);
|
||||
var rt = Sse.UnpackLow(r, t);
|
||||
|
||||
var lbrt = Sse.Shuffle(lb, rt, 0b11_10_01_00);
|
||||
|
||||
return Unsafe.As<Vector128<float>, Box2>(ref lbrt);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal readonly unsafe Box2 CalcBoundingBoxSlow()
|
||||
{
|
||||
Span<float> allX = stackalloc float[4];
|
||||
Span<float> allY = stackalloc float[4];
|
||||
(allX[0], allY[0]) = BottomLeft;
|
||||
(allX[1], allY[1]) = TopRight;
|
||||
(allX[2], allY[2]) = TopLeft;
|
||||
(allX[3], allY[3]) = BottomRight;
|
||||
|
||||
var X0 = allX[0];
|
||||
var X1 = allX[0];
|
||||
for (int i = 1; i < allX.Length; i++)
|
||||
{
|
||||
if (allX[i] > X1)
|
||||
{
|
||||
X1 = allX[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (allX[i] < X0)
|
||||
{
|
||||
X0 = allX[i];
|
||||
}
|
||||
}
|
||||
|
||||
var Y0 = allY[0];
|
||||
var Y1 = allY[0];
|
||||
for (int i = 1; i < allY.Length; i++)
|
||||
{
|
||||
if (allY[i] > Y1)
|
||||
{
|
||||
Y1 = allY[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (allY[i] < Y0)
|
||||
{
|
||||
Y0 = allY[i];
|
||||
}
|
||||
}
|
||||
|
||||
return new Box2(X0, Y0, X1, Y1);
|
||||
}
|
||||
|
||||
#region Equality
|
||||
|
||||
@@ -1009,7 +1009,7 @@ namespace Robust.Shared.Maths
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public enum BlendFactor
|
||||
public enum BlendFactor : byte
|
||||
{
|
||||
Zero,
|
||||
One,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Robust.Shared.Maths
|
||||
{
|
||||
[Flags]
|
||||
public enum Direction
|
||||
public enum Direction : sbyte
|
||||
{
|
||||
Invalid = -1,
|
||||
East = 0,
|
||||
|
||||
@@ -3,3 +3,6 @@
|
||||
#if NET5_0
|
||||
[module: SkipLocalsInit]
|
||||
#endif
|
||||
|
||||
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
|
||||
[assembly: InternalsVisibleTo("Content.Benchmarks")]
|
||||
|
||||
29
Robust.Shared.Maths/SimdHelpers.cs
Normal file
29
Robust.Shared.Maths/SimdHelpers.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
namespace Robust.Shared.Maths
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper stuff for SIMD code.
|
||||
/// </summary>
|
||||
internal static class SimdHelpers
|
||||
{
|
||||
/// <returns>The min value is broadcast to the whole vector.</returns>
|
||||
public static Vector128<float> MinHorizontalSse(Vector128<float> v)
|
||||
{
|
||||
var b = Sse.Shuffle(v, v, 0b10_11_00_01);
|
||||
var m = Sse.Min(b, v);
|
||||
var c = Sse.Shuffle(m, m, 0b01_00_11_10);
|
||||
return Sse.Min(c, m);
|
||||
}
|
||||
|
||||
/// <returns>The max value is broadcast to the whole vector.</returns>
|
||||
public static Vector128<float> MaxHorizontalSse(Vector128<float> v)
|
||||
{
|
||||
var b = Sse.Shuffle(v, v, 0b10_11_00_01);
|
||||
var m = Sse.Max(b, v);
|
||||
var c = Sse.Shuffle(m, m, 0b01_00_11_10);
|
||||
return Sse.Max(c, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ namespace Robust.Shared.Animations
|
||||
/// <summary>
|
||||
/// Specifies how animated properties are interpolated between two keyframes.
|
||||
/// </summary>
|
||||
public enum AnimationInterpolationMode
|
||||
public enum AnimationInterpolationMode: byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Use a linear interpolation for supported values.
|
||||
|
||||
@@ -118,29 +118,20 @@ namespace Robust.Shared
|
||||
* BUILD
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<string> BuildEngineVersion =
|
||||
CVarDef.Create("build.engine_version", "", CVar.SERVERONLY);
|
||||
|
||||
public static readonly CVarDef<string> BuildForkId =
|
||||
CVarDef.Create("build.fork_id", "", CVar.ARCHIVE | CVar.SERVERONLY);
|
||||
CVarDef.Create("build.fork_id", "", CVar.SERVERONLY);
|
||||
|
||||
public static readonly CVarDef<string> BuildVersion =
|
||||
CVarDef.Create("build.version", "", CVar.ARCHIVE | CVar.SERVERONLY);
|
||||
CVarDef.Create("build.version", "", CVar.SERVERONLY);
|
||||
|
||||
public static readonly CVarDef<string> BuildDownloadUrlWindows =
|
||||
CVarDef.Create("build.download_url_windows", string.Empty, CVar.ARCHIVE | CVar.SERVERONLY);
|
||||
public static readonly CVarDef<string> BuildDownloadUrl =
|
||||
CVarDef.Create("build.download_url", string.Empty, CVar.SERVERONLY);
|
||||
|
||||
public static readonly CVarDef<string> BuildDownloadUrlMacOS =
|
||||
CVarDef.Create("build.download_url_macos", "", CVar.ARCHIVE | CVar.SERVERONLY);
|
||||
|
||||
public static readonly CVarDef<string> BuildDownloadUrlLinux =
|
||||
CVarDef.Create("build.download_url_linux", "", CVar.ARCHIVE | CVar.SERVERONLY);
|
||||
|
||||
public static readonly CVarDef<string> BuildHashWindows =
|
||||
CVarDef.Create("build.hash_windows", "", CVar.ARCHIVE | CVar.SERVERONLY);
|
||||
|
||||
public static readonly CVarDef<string> BuildHashMacOS =
|
||||
CVarDef.Create("build.hash_macos", "", CVar.ARCHIVE | CVar.SERVERONLY);
|
||||
|
||||
public static readonly CVarDef<string> BuildHashLinux =
|
||||
CVarDef.Create("build.hash_linux", "", CVar.ARCHIVE | CVar.SERVERONLY);
|
||||
public static readonly CVarDef<string> BuildHash =
|
||||
CVarDef.Create("build.hash", "", CVar.SERVERONLY);
|
||||
|
||||
/*
|
||||
* WATCHDOG
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Robust.Shared.Configuration
|
||||
/// Extra flags for changing the behavior of a config var.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum CVar
|
||||
public enum CVar : short
|
||||
{
|
||||
/// <summary>
|
||||
/// No special flags.
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Configuration
|
||||
{
|
||||
@@ -203,6 +204,9 @@ namespace Robust.Shared.Configuration
|
||||
|
||||
private void RegisterCVar(string name, Type type, object? defaultValue, CVar flags, Action<object>? onValueChanged)
|
||||
{
|
||||
DebugTools.Assert(!type.IsEnum || type.GetEnumUnderlyingType() == typeof(int),
|
||||
$"{name}: Enum cvars must have int as underlying type.");
|
||||
|
||||
var only = _isServer ? CVar.CLIENTONLY : CVar.SERVERONLY;
|
||||
|
||||
if ((flags & only) != 0)
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace Robust.Shared.ContentPack
|
||||
}
|
||||
#pragma warning restore 649
|
||||
|
||||
private enum InheritMode
|
||||
private enum InheritMode : byte
|
||||
{
|
||||
// Allow if All is set, block otherwise
|
||||
Default,
|
||||
|
||||
@@ -181,6 +181,7 @@ namespace Robust.Shared.ContentPack
|
||||
}
|
||||
}
|
||||
|
||||
// Normal single dimensional array with zero lower bound.
|
||||
internal sealed record MTypeSZArray(MType ElementType) : MType
|
||||
{
|
||||
public override string ToString()
|
||||
@@ -199,7 +200,8 @@ namespace Robust.Shared.ContentPack
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed record MTypeArray(MType ElementType, ArrayShape Shape) : MType
|
||||
// Multi-dimension arrays with funny lower and upper bounds.
|
||||
internal sealed record MTypeWackyArray(MType ElementType, ArrayShape Shape) : MType
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
@@ -213,7 +215,7 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
public override bool WhitelistEquals(MType other)
|
||||
{
|
||||
return other is MTypeArray arr && ShapesEqual(Shape, arr.Shape) && ElementType.WhitelistEquals(arr);
|
||||
return other is MTypeWackyArray arr && ShapesEqual(Shape, arr.Shape) && ElementType.WhitelistEquals(arr);
|
||||
}
|
||||
|
||||
private static bool ShapesEqual(in ArrayShape a, in ArrayShape b)
|
||||
|
||||
@@ -11,11 +11,17 @@ using System.Reflection.Metadata;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using System.Threading.Tasks;
|
||||
using ILVerify;
|
||||
using Pidgin;
|
||||
using Robust.Shared.Interfaces.Log;
|
||||
using Robust.Shared.Interfaces.Resources;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
// psst
|
||||
// You know ECMA-335 right? The specification for the CLI that .NET runs on?
|
||||
// Yeah, you need it to understand a lot of this code. So get a copy.
|
||||
// You know the cool thing?
|
||||
// ISO has a version that has correct PDF metadata so there's an actual table of contents.
|
||||
// Right here: https://standards.iso.org/ittf/PubliclyAvailableStandards/c058046_ISO_IEC_23271_2012(E).zip
|
||||
|
||||
namespace Robust.Shared.ContentPack
|
||||
{
|
||||
/// <summary>
|
||||
@@ -28,29 +34,46 @@ namespace Robust.Shared.ContentPack
|
||||
/// <summary>
|
||||
/// Completely disables type checking, allowing everything.
|
||||
/// </summary>
|
||||
public bool DisableTypeCheck { get; set; } = false;
|
||||
public bool DisableTypeCheck { get; init; }
|
||||
|
||||
public DumpFlags Dump { get; set; } = DumpFlags.None;
|
||||
public bool VerifyIL { get; set; } = true;
|
||||
public DumpFlags Dump { get; init; } = DumpFlags.None;
|
||||
public bool VerifyIL { get; init; } = true;
|
||||
|
||||
private bool WouldNoOp => Dump == DumpFlags.None && DisableTypeCheck && !VerifyIL;
|
||||
|
||||
public AssemblyTypeChecker(IResourceManager res)
|
||||
// Necessary for loads with launcher loader.
|
||||
public Func<string, Stream?>? ExtraRobustLoader { get; init; }
|
||||
private readonly ISawmill _sawmill;
|
||||
private readonly SandboxConfig _config;
|
||||
|
||||
public AssemblyTypeChecker(IResourceManager res, ISawmill sawmill)
|
||||
{
|
||||
_res = res;
|
||||
_sawmill = sawmill;
|
||||
_config = LoadConfig();
|
||||
}
|
||||
|
||||
private Resolver CreateResolver()
|
||||
{
|
||||
var dotnetDir = Path.GetDirectoryName(typeof(int).Assembly.Location)!;
|
||||
var ourDir = Path.GetDirectoryName(typeof(AssemblyTypeChecker).Assembly.Location)!;
|
||||
var ourPath = typeof(AssemblyTypeChecker).Assembly.Location;
|
||||
string[] loadDirs;
|
||||
if (string.IsNullOrEmpty(ourPath))
|
||||
{
|
||||
_sawmill.Debug("Robust directory not available");
|
||||
loadDirs = new[] {dotnetDir};
|
||||
}
|
||||
else
|
||||
{
|
||||
_sawmill.Debug("Robust directory is {0}", ourPath);
|
||||
loadDirs = new[] {dotnetDir, Path.GetDirectoryName(ourPath)!};
|
||||
}
|
||||
|
||||
Logger.DebugS("res.typecheck", ".NET runtime directory is {0}", dotnetDir);
|
||||
Logger.DebugS("res.typecheck", "Robust directory is {0}", ourDir);
|
||||
_sawmill.Debug(".NET runtime directory is {0}", dotnetDir);
|
||||
|
||||
return new Resolver(
|
||||
this,
|
||||
new[] {dotnetDir, ourDir},
|
||||
loadDirs,
|
||||
new[] {new ResourcePath("/Assemblies/")}
|
||||
);
|
||||
}
|
||||
@@ -69,17 +92,18 @@ namespace Robust.Shared.ContentPack
|
||||
return true;
|
||||
}
|
||||
|
||||
Logger.DebugS("res.typecheck", "Checking assembly...");
|
||||
_sawmill.Debug("Checking assembly...");
|
||||
var fullStopwatch = Stopwatch.StartNew();
|
||||
|
||||
var config = LoadConfig();
|
||||
var resolver = CreateResolver();
|
||||
using var peReader = new PEReader(assembly, PEStreamOptions.LeaveOpen);
|
||||
var reader = peReader.GetMetadataReader();
|
||||
|
||||
var asmName = reader.GetString(reader.GetAssemblyDefinition().Name);
|
||||
|
||||
if (VerifyIL)
|
||||
{
|
||||
if (!DoVerifyIL(resolver, config, peReader, reader))
|
||||
if (!DoVerifyIL(asmName, resolver, peReader, reader))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -90,13 +114,13 @@ namespace Robust.Shared.ContentPack
|
||||
var types = GetReferencedTypes(reader, errors);
|
||||
var members = GetReferencedMembers(reader, errors);
|
||||
var inherited = GetExternalInheritedTypes(reader, errors);
|
||||
Logger.DebugS("res.typecheck", $"References loaded... {fullStopwatch.ElapsedMilliseconds}ms");
|
||||
_sawmill.Debug($"References loaded... {fullStopwatch.ElapsedMilliseconds}ms");
|
||||
|
||||
if ((Dump & DumpFlags.Types) != 0)
|
||||
{
|
||||
foreach (var mType in types)
|
||||
{
|
||||
Logger.DebugS("res.typecheck", $"RefType: {mType}");
|
||||
_sawmill.Debug($"RefType: {mType}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +128,7 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
foreach (var memberRef in members)
|
||||
{
|
||||
Logger.DebugS("res.typecheck", $"RefMember: {memberRef}");
|
||||
_sawmill.Debug($"RefMember: {memberRef}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,10 +136,10 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
foreach (var (name, baseType, interfaces) in inherited)
|
||||
{
|
||||
Logger.DebugS("res.typecheck", $"Inherit: {name} -> {baseType}");
|
||||
_sawmill.Debug($"Inherit: {name} -> {baseType}");
|
||||
foreach (var @interface in interfaces)
|
||||
{
|
||||
Logger.DebugS("res.typecheck", $" Interface: {@interface}");
|
||||
_sawmill.Debug($" Interface: {@interface}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,46 +154,49 @@ namespace Robust.Shared.ContentPack
|
||||
// we won't have to check that any types in their type arguments are whitelisted.
|
||||
foreach (var type in types)
|
||||
{
|
||||
if (!IsTypeAccessAllowed(type, config, out _))
|
||||
if (!IsTypeAccessAllowed(type, out _))
|
||||
{
|
||||
errors.Add(new SandboxError($"Access to type not allowed: {type}"));
|
||||
}
|
||||
}
|
||||
|
||||
Logger.DebugS("res.typecheck", $"Types... {fullStopwatch.ElapsedMilliseconds}ms");
|
||||
_sawmill.Debug($"Types... {fullStopwatch.ElapsedMilliseconds}ms");
|
||||
|
||||
CheckInheritance(inherited, errors, config);
|
||||
CheckInheritance(inherited, errors);
|
||||
|
||||
Logger.DebugS("res.typecheck", $"Inheritance... {fullStopwatch.ElapsedMilliseconds}ms");
|
||||
_sawmill.Debug($"Inheritance... {fullStopwatch.ElapsedMilliseconds}ms");
|
||||
|
||||
CheckMemberReferences(members, config, errors);
|
||||
CheckMemberReferences(members, errors);
|
||||
|
||||
foreach (var error in errors)
|
||||
{
|
||||
Logger.ErrorS("res.typecheck", $"Sandbox violation: {error.Message}");
|
||||
_sawmill.Error($"Sandbox violation: {error.Message}");
|
||||
}
|
||||
|
||||
Logger.DebugS("res.typecheck", $"Checked assembly in {fullStopwatch.ElapsedMilliseconds}ms");
|
||||
_sawmill.Debug($"Checked assembly in {fullStopwatch.ElapsedMilliseconds}ms");
|
||||
|
||||
return errors.IsEmpty;
|
||||
}
|
||||
|
||||
private static bool DoVerifyIL(Resolver resolver, SandboxConfig config, PEReader peReader,
|
||||
private bool DoVerifyIL(
|
||||
string name,
|
||||
IResolver resolver,
|
||||
PEReader peReader,
|
||||
MetadataReader reader)
|
||||
{
|
||||
Logger.DebugS("res.typecheck", "Verifying IL...");
|
||||
_sawmill.Debug($"{name}: Verifying IL...");
|
||||
var sw = Stopwatch.StartNew();
|
||||
var ver = new Verifier(resolver);
|
||||
ver.SetSystemModuleName(new AssemblyName(config.SystemAssemblyName));
|
||||
ver.SetSystemModuleName(new AssemblyName(_config.SystemAssemblyName));
|
||||
var verifyErrors = false;
|
||||
foreach (var res in ver.Verify(peReader))
|
||||
{
|
||||
if (config.AllowedVerifierErrors.Contains(res.Code))
|
||||
if (_config.AllowedVerifierErrors.Contains(res.Code))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var msg = $"ILVerify: {res.Message}";
|
||||
var msg = $"{name}: ILVerify: {res.Message}";
|
||||
|
||||
try
|
||||
{
|
||||
@@ -193,14 +220,14 @@ namespace Robust.Shared.ContentPack
|
||||
}
|
||||
catch (UnsupportedMetadataException e)
|
||||
{
|
||||
Logger.ErrorS("res.typecheck", $"{e}");
|
||||
_sawmill.Error($"{e}");
|
||||
}
|
||||
|
||||
verifyErrors = true;
|
||||
Logger.ErrorS("res.typecheck", msg);
|
||||
_sawmill.Error(msg);
|
||||
}
|
||||
|
||||
Logger.DebugS("res.typecheck", $"Verified IL in {sw.Elapsed.TotalMilliseconds}ms");
|
||||
_sawmill.Debug($"{name}: Verified IL in {sw.Elapsed.TotalMilliseconds}ms");
|
||||
|
||||
if (verifyErrors)
|
||||
{
|
||||
@@ -210,7 +237,8 @@ namespace Robust.Shared.ContentPack
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void CheckMemberReferences(List<MMemberRef> members, SandboxConfig config,
|
||||
private void CheckMemberReferences(
|
||||
List<MMemberRef> members,
|
||||
ConcurrentBag<SandboxError> errors)
|
||||
{
|
||||
Parallel.ForEach(members, memberRef =>
|
||||
@@ -226,15 +254,11 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
break;
|
||||
}
|
||||
case MTypeArray array:
|
||||
case MTypeWackyArray:
|
||||
{
|
||||
// For this kind of array we just need access to the type itself.
|
||||
if (!IsTypeAccessAllowed((MTypeReferenced) array.ElementType, config, out _))
|
||||
{
|
||||
errors.Add(new SandboxError($"Access to type not allowed: {array}"));
|
||||
}
|
||||
|
||||
return; // Found
|
||||
// Members on arrays (not to be confused with vectors) are all fine.
|
||||
// See II.14.2 in ECMA-335.
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
@@ -245,8 +269,11 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
var baseTypeReferenced = (MTypeReferenced) baseType;
|
||||
|
||||
if (!IsTypeAccessAllowed(baseTypeReferenced, config, out var typeCfg))
|
||||
if (!IsTypeAccessAllowed(baseTypeReferenced, out var typeCfg))
|
||||
{
|
||||
// Technically this error isn't necessary since we have an earlier pass
|
||||
// checking all referenced types. That should have caught this
|
||||
// We still need the typeCfg so that's why we're checking. Might as well.
|
||||
errors.Add(new SandboxError($"Access to type not allowed: {baseTypeReferenced}"));
|
||||
return;
|
||||
}
|
||||
@@ -306,9 +333,9 @@ namespace Robust.Shared.ContentPack
|
||||
});
|
||||
}
|
||||
|
||||
private static void CheckInheritance(
|
||||
private void CheckInheritance(
|
||||
List<(MType type, MType parent, ArraySegment<MType> interfaceImpls)> inherited,
|
||||
ConcurrentBag<SandboxError> errors, SandboxConfig config)
|
||||
ConcurrentBag<SandboxError> errors)
|
||||
{
|
||||
// This inheritance whitelisting primarily serves to avoid content doing funny stuff
|
||||
// by e.g. inheriting Type.
|
||||
@@ -336,7 +363,7 @@ namespace Robust.Shared.ContentPack
|
||||
_ => throw new InvalidOperationException() // Can't happen.
|
||||
};
|
||||
|
||||
if (!IsTypeAccessAllowed(realBaseType, config, out var cfg))
|
||||
if (!IsTypeAccessAllowed(realBaseType, out var cfg))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -346,14 +373,13 @@ namespace Robust.Shared.ContentPack
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsTypeAccessAllowed(MTypeReferenced type, SandboxConfig config,
|
||||
[NotNullWhen(true)] out TypeConfig? cfg)
|
||||
private bool IsTypeAccessAllowed(MTypeReferenced type, [NotNullWhen(true)] out TypeConfig? cfg)
|
||||
{
|
||||
if (type.Namespace == null)
|
||||
{
|
||||
if (type.ResolutionScope is MResScopeType parentType)
|
||||
{
|
||||
if (!IsTypeAccessAllowed((MTypeReferenced) parentType.Type, config, out var parentCfg))
|
||||
if (!IsTypeAccessAllowed((MTypeReferenced) parentType.Type, out var parentCfg))
|
||||
{
|
||||
cfg = null;
|
||||
return false;
|
||||
@@ -383,7 +409,7 @@ namespace Robust.Shared.ContentPack
|
||||
}
|
||||
|
||||
// Check if in whitelisted namespaces.
|
||||
foreach (var whNamespace in config.WhitelistedNamespaces)
|
||||
foreach (var whNamespace in _config.WhitelistedNamespaces)
|
||||
{
|
||||
if (type.Namespace.StartsWith(whNamespace))
|
||||
{
|
||||
@@ -392,7 +418,7 @@ namespace Robust.Shared.ContentPack
|
||||
}
|
||||
}
|
||||
|
||||
if (!config.Types.TryGetValue(type.Namespace, out var nsDict))
|
||||
if (!_config.Types.TryGetValue(type.Namespace, out var nsDict))
|
||||
{
|
||||
cfg = null;
|
||||
return false;
|
||||
@@ -733,6 +759,12 @@ namespace Robust.Shared.ContentPack
|
||||
}
|
||||
}
|
||||
|
||||
var extraStream = _parent.ExtraRobustLoader?.Invoke(dllName);
|
||||
if (extraStream != null)
|
||||
{
|
||||
return new PEReader(extraStream);
|
||||
}
|
||||
|
||||
foreach (var resLoadPath in _resLoadPaths)
|
||||
{
|
||||
try
|
||||
@@ -758,7 +790,7 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
public MType GetArrayType(MType elementType, ArrayShape shape)
|
||||
{
|
||||
return new MTypeArray(elementType, shape);
|
||||
return new MTypeWackyArray(elementType, shape);
|
||||
}
|
||||
|
||||
public MType GetByReferenceType(MType elementType)
|
||||
@@ -839,7 +871,7 @@ namespace Robust.Shared.ContentPack
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum DumpFlags
|
||||
public enum DumpFlags : byte
|
||||
{
|
||||
None = 0,
|
||||
Types = 1,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
@@ -41,12 +42,13 @@ namespace Robust.Shared.ContentPack
|
||||
/// </summary>
|
||||
/// <param name="assembly">Byte array of the assembly.</param>
|
||||
/// <param name="symbols">Optional byte array of the debug symbols.</param>
|
||||
void LoadGameAssembly(Stream assembly, Stream? symbols = null);
|
||||
/// <param name="skipVerify">Whether to skip checking the loaded assembly for sandboxing.</param>
|
||||
void LoadGameAssembly(Stream assembly, Stream? symbols = null, bool skipVerify = false);
|
||||
|
||||
/// <summary>
|
||||
/// Loads an assembly into the current AppDomain.
|
||||
/// </summary>
|
||||
void LoadGameAssembly(string diskPath);
|
||||
void LoadGameAssembly(string diskPath, bool skipVerify = false);
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts a run level change to all loaded entry point.
|
||||
@@ -65,5 +67,7 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
void SetUseLoadContext(bool useLoadContext);
|
||||
void SetEnableSandboxing(bool sandboxing);
|
||||
|
||||
Func<string, Stream?>? VerifierExtraLoadHandler { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -8,6 +9,7 @@ using System.Reflection.PortableExecutable;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Runtime.Loader;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Shared.Interfaces.Log;
|
||||
using Robust.Shared.Interfaces.Resources;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -19,13 +21,11 @@ namespace Robust.Shared.ContentPack
|
||||
/// <summary>
|
||||
/// Class for managing the loading of assemblies into the engine.
|
||||
/// </summary>
|
||||
internal sealed class ModLoader : BaseModLoader, IModLoaderInternal, IDisposable, IPostInjectInit
|
||||
internal sealed class ModLoader : BaseModLoader, IModLoaderInternal, IDisposable
|
||||
{
|
||||
[Dependency] private readonly IResourceManagerInternal _res = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private AssemblyTypeChecker _typeChecker = default!;
|
||||
|
||||
// List of extra assemblies side-loaded from the /Assemblies/ mounted path.
|
||||
private readonly List<Assembly> _sideModules = new();
|
||||
|
||||
@@ -50,11 +50,6 @@ namespace Robust.Shared.ContentPack
|
||||
AssemblyLoadContext.Default.Resolving += DefaultOnResolving;
|
||||
}
|
||||
|
||||
void IPostInjectInit.PostInject()
|
||||
{
|
||||
_typeChecker = new AssemblyTypeChecker(_res);
|
||||
}
|
||||
|
||||
public void SetUseLoadContext(bool useLoadContext)
|
||||
{
|
||||
_useLoadContext = useLoadContext;
|
||||
@@ -64,13 +59,15 @@ namespace Robust.Shared.ContentPack
|
||||
public void SetEnableSandboxing(bool sandboxing)
|
||||
{
|
||||
_sandboxingEnabled = sandboxing;
|
||||
_typeChecker.VerifyIL = sandboxing;
|
||||
_typeChecker.DisableTypeCheck = !sandboxing;
|
||||
Logger.DebugS("res", "{0} sandboxing", sandboxing ? "ENABLING" : "DISABLING");
|
||||
}
|
||||
|
||||
public Func<string, Stream?>? VerifierExtraLoadHandler { get; set; }
|
||||
|
||||
public bool TryLoadModulesFrom(ResourcePath mountPath, string filterPrefix)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
Logger.DebugS("res.mod", "LOADING modules");
|
||||
var files = new Dictionary<string, (ResourcePath Path, string[] references)>();
|
||||
|
||||
// Find all modules we want to load.
|
||||
@@ -91,6 +88,22 @@ namespace Robust.Shared.ContentPack
|
||||
}
|
||||
}
|
||||
|
||||
if (_sandboxingEnabled)
|
||||
{
|
||||
var typeChecker = MakeTypeChecker();
|
||||
|
||||
Parallel.ForEach(files, pair =>
|
||||
{
|
||||
var (name, (path, _)) = pair;
|
||||
|
||||
using var stream = _res.ContentFileRead(path);
|
||||
if (!typeChecker.CheckAssembly(stream))
|
||||
{
|
||||
throw new TypeCheckFailedException($"Assembly {name} failed type checks.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Actually load them in the order they depend on each other.
|
||||
foreach (var path in TopologicalSortModules(files))
|
||||
{
|
||||
@@ -101,13 +114,13 @@ namespace Robust.Shared.ContentPack
|
||||
// This probably improves performance or something and makes debugging etc more reliable.
|
||||
if (_res.TryGetDiskFilePath(path, out var diskPath))
|
||||
{
|
||||
LoadGameAssembly(diskPath);
|
||||
LoadGameAssembly(diskPath, skipVerify: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
var assemblyStream = _res.ContentFileRead(path);
|
||||
var symbolsStream = _res.ContentFileReadOrNull(path.WithExtension("pdb"));
|
||||
LoadGameAssembly(assemblyStream, symbolsStream);
|
||||
LoadGameAssembly(assemblyStream, symbolsStream, skipVerify: true);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -116,6 +129,7 @@ namespace Robust.Shared.ContentPack
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Logger.DebugS("res.mod", $"DONE loading modules: {sw.Elapsed}");
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -164,9 +178,9 @@ namespace Robust.Shared.ContentPack
|
||||
.Select(a => metaReader.GetString(a.Name)).ToArray(), name);
|
||||
}
|
||||
|
||||
public void LoadGameAssembly(Stream assembly, Stream? symbols = null)
|
||||
public void LoadGameAssembly(Stream assembly, Stream? symbols = null, bool skipVerify = false)
|
||||
{
|
||||
if (!_typeChecker.CheckAssembly(assembly))
|
||||
if (!skipVerify && !MakeTypeChecker().CheckAssembly(assembly))
|
||||
{
|
||||
throw new TypeCheckFailedException();
|
||||
}
|
||||
@@ -186,9 +200,9 @@ namespace Robust.Shared.ContentPack
|
||||
InitMod(gameAssembly);
|
||||
}
|
||||
|
||||
public void LoadGameAssembly(string diskPath)
|
||||
public void LoadGameAssembly(string diskPath, bool skipVerify = false)
|
||||
{
|
||||
if (!_typeChecker.CheckAssembly(diskPath))
|
||||
if (!skipVerify && !MakeTypeChecker().CheckAssembly(diskPath))
|
||||
{
|
||||
throw new TypeCheckFailedException();
|
||||
}
|
||||
@@ -215,7 +229,7 @@ namespace Robust.Shared.ContentPack
|
||||
Logger.DebugS("srv", $"Loading {assemblyName} DLL");
|
||||
try
|
||||
{
|
||||
LoadGameAssembly(path);
|
||||
LoadGameAssembly(path, skipVerify: false);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -343,5 +357,15 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private AssemblyTypeChecker MakeTypeChecker()
|
||||
{
|
||||
return new(_res, Logger.GetSawmill("res.typecheck"))
|
||||
{
|
||||
VerifyIL = _sandboxingEnabled,
|
||||
DisableTypeCheck = !_sandboxingEnabled,
|
||||
ExtraRobustLoader = VerifierExtraLoadHandler
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// Run levels of the Content entry point.
|
||||
/// </summary>
|
||||
public enum ModRunLevel
|
||||
public enum ModRunLevel: byte
|
||||
{
|
||||
Error = 0,
|
||||
Init = 1,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// Levels at which point the content assemblies are getting updates.
|
||||
/// </summary>
|
||||
public enum ModUpdateLevel
|
||||
public enum ModUpdateLevel : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// This update is called before the main state manager on process frames.
|
||||
|
||||
@@ -70,7 +70,8 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
prefix = SanitizePrefix(prefix);
|
||||
|
||||
pack = PathHelpers.ExecutableRelativeFile(pack);
|
||||
if (!Path.IsPathRooted(pack))
|
||||
pack = PathHelpers.ExecutableRelativeFile(pack);
|
||||
|
||||
var packInfo = new FileInfo(pack);
|
||||
|
||||
@@ -93,7 +94,7 @@ namespace Robust.Shared.ContentPack
|
||||
AddRoot(prefix, loader);
|
||||
}
|
||||
|
||||
private void AddRoot(ResourcePath prefix, IContentRoot loader)
|
||||
protected void AddRoot(ResourcePath prefix, IContentRoot loader)
|
||||
{
|
||||
loader.Mount();
|
||||
_contentRootsLock.EnterWriteLock();
|
||||
@@ -126,7 +127,9 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
prefix = SanitizePrefix(prefix);
|
||||
|
||||
path = PathHelpers.ExecutableRelativeFile(path);
|
||||
if (!Path.IsPathRooted(path))
|
||||
path = PathHelpers.ExecutableRelativeFile(path);
|
||||
|
||||
var pathInfo = new DirectoryInfo(path);
|
||||
if (!pathInfo.Exists)
|
||||
{
|
||||
@@ -293,22 +296,8 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
public void MountStreamAt(MemoryStream stream, ResourcePath path)
|
||||
{
|
||||
if (!path.IsRooted)
|
||||
{
|
||||
throw new ArgumentException("Path must be rooted.", nameof(path));
|
||||
}
|
||||
|
||||
var loader = new SingleStreamLoader(stream, path.ToRelativePath());
|
||||
loader.Mount();
|
||||
_contentRootsLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
_contentRoots.Add((ResourcePath.Root, loader));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_contentRootsLock.ExitWriteLock();
|
||||
}
|
||||
AddRoot(ResourcePath.Root, loader);
|
||||
}
|
||||
|
||||
internal static bool IsPathValid(ResourcePath path)
|
||||
|
||||
@@ -886,6 +886,7 @@ Types:
|
||||
MulticastDelegate:
|
||||
Inherit: Allow
|
||||
NotSupportedException: { All: True }
|
||||
Nullable: { All: True }
|
||||
Nullable`1: { All: True }
|
||||
NullReferenceException: { All: True }
|
||||
Object: { All: True }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Robust.Shared.Enums
|
||||
{
|
||||
public enum PlacementManagerMessage
|
||||
public enum PlacementManagerMessage : byte
|
||||
{
|
||||
StartPlacement,
|
||||
CancelPlacement,
|
||||
@@ -18,7 +18,7 @@
|
||||
Disconnected
|
||||
}
|
||||
|
||||
public enum NetworkDataType
|
||||
public enum NetworkDataType: byte
|
||||
{
|
||||
d_enum,
|
||||
d_bool,
|
||||
|
||||
@@ -505,7 +505,7 @@ namespace Robust.Shared.GameObjects.Components
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum BodyStatus
|
||||
public enum BodyStatus: byte
|
||||
{
|
||||
OnGround,
|
||||
InAir
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace Robust.Shared.GameObjects.Components.Transform
|
||||
}
|
||||
}
|
||||
|
||||
public enum SnapGridOffset
|
||||
public enum SnapGridOffset: byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Center snap grid (wires, pipes, ...).
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum EventSource
|
||||
public enum EventSource : byte
|
||||
{
|
||||
None = 0b0000,
|
||||
Local = 0b0001,
|
||||
|
||||
@@ -672,7 +672,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
}
|
||||
|
||||
public enum EntityMessageType
|
||||
public enum EntityMessageType : byte
|
||||
{
|
||||
Error = 0,
|
||||
ComponentMessage,
|
||||
|
||||
@@ -427,6 +427,9 @@ namespace Robust.Shared.GameObjects.Systems
|
||||
continue;
|
||||
}
|
||||
|
||||
if (collision.A.Owner.Deleted || collision.B.Owner.Deleted)
|
||||
continue;
|
||||
|
||||
var penetration = _physicsManager.CalculatePenetration(collision.A, collision.B);
|
||||
|
||||
if (penetration <= allowance)
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Robust.Shared.Interfaces.GameObjects
|
||||
/// This distinction is important because prototypes are shared across client and server, but the two might have different components.
|
||||
/// </summary>
|
||||
/// <seealso cref="IComponentFactory" />
|
||||
public enum ComponentAvailability
|
||||
public enum ComponentAvailability : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The component is available and can be instantiated.
|
||||
@@ -213,7 +213,7 @@ namespace Robust.Shared.Interfaces.GameObjects
|
||||
/// <param name="registration">The registration if found, null otherwise.</param>
|
||||
/// <returns>true it found, false otherwise.</returns>
|
||||
bool TryGetRegistration(IComponent component, [NotNullWhen(true)] out IComponentRegistration? registration);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Automatically create registrations for all components with a <see cref="RegisterComponentAttribute" />
|
||||
/// </summary>
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Robust.Shared.Interfaces.Network
|
||||
void ClientDisconnect(string reason);
|
||||
}
|
||||
|
||||
public enum ClientConnectionState
|
||||
public enum ClientConnectionState : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// We are not connected and not trying to get connected either. Quite lonely huh.
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Robust.Shared.Interfaces.Network
|
||||
/// Defines on which side of the network a net message can be accepted.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum NetMessageAccept
|
||||
public enum NetMessageAccept : byte
|
||||
{
|
||||
None = 0,
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ namespace Robust.Shared.Serialization
|
||||
/// </exception>
|
||||
ReadOnlySpan<byte> MappedStringsHash { get; }
|
||||
|
||||
bool EnableCaching { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Add a string to the constant mapping.
|
||||
/// </summary>
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Robust.Shared.Localization.Macros
|
||||
/// <summary>
|
||||
/// Genders for grammatical usage only.
|
||||
/// </summary>
|
||||
public enum Gender
|
||||
public enum Gender : byte
|
||||
{
|
||||
Epicene,
|
||||
Female,
|
||||
|
||||
@@ -19,6 +19,13 @@ namespace Robust.Shared.Map
|
||||
|
||||
void NotifyTileChanged(in TileRef tileRef, in Tile oldTile);
|
||||
|
||||
/// <summary>
|
||||
/// Regenerates anything that is based on chunk collision data.
|
||||
/// This wouldn't even be separate if not for the whole "ability to suppress automatic collision regeneration" thing.
|
||||
/// As it is, YamlGridSerializer performs manual collision regeneration and that wasn't properly getting propagated to the grid. Thus, this needs to exist.
|
||||
/// </summary>
|
||||
void NotifyChunkCollisionRegenerated();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the chunk at the given indices. If the chunk does not exist,
|
||||
/// then a new one is generated that is filled with empty space.
|
||||
|
||||
@@ -247,6 +247,7 @@ namespace Robust.Shared.Map
|
||||
{
|
||||
// generate collision rects
|
||||
GridChunkPartition.PartitionChunk(this, ref _colBoxes, out _cachedBounds);
|
||||
_grid.NotifyChunkCollisionRegenerated();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -167,10 +167,15 @@ namespace Robust.Shared.Map
|
||||
public void NotifyTileChanged(in TileRef tileRef, in Tile oldTile)
|
||||
{
|
||||
LastModifiedTick = _mapManager.GameTiming.CurTick;
|
||||
UpdateAABB();
|
||||
_mapManager.RaiseOnTileChanged(tileRef, oldTile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void NotifyChunkCollisionRegenerated()
|
||||
{
|
||||
UpdateAABB();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool OnSnapCenter(Vector2 position)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Robust.Shared.Network
|
||||
{
|
||||
public enum AuthMode
|
||||
public enum AuthMode : byte
|
||||
{
|
||||
Optional = 0,
|
||||
Required = 1,
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Robust.Shared.Network
|
||||
/// <summary>
|
||||
/// The group the message belongs to, used for statistics and packet channels.
|
||||
/// </summary>
|
||||
public enum MsgGroups
|
||||
public enum MsgGroups : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Error state, the message needs to set a different one.
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Robust.Shared.Noise
|
||||
private const MethodImplOptions FN_INLINE = MethodImplOptions.AggressiveInlining;
|
||||
private const int FN_CELLULAR_INDEX_MAX = 3;
|
||||
|
||||
public enum NoiseType
|
||||
public enum NoiseType : byte
|
||||
{
|
||||
Value,
|
||||
ValueFractal,
|
||||
@@ -60,28 +60,28 @@ namespace Robust.Shared.Noise
|
||||
CubicFractal
|
||||
};
|
||||
|
||||
public enum Interp
|
||||
public enum Interp : byte
|
||||
{
|
||||
Linear,
|
||||
Hermite,
|
||||
Quintic
|
||||
};
|
||||
|
||||
public enum FractalType
|
||||
public enum FractalType : byte
|
||||
{
|
||||
FBM,
|
||||
Billow,
|
||||
RigidMulti
|
||||
};
|
||||
|
||||
public enum CellularDistanceFunction
|
||||
public enum CellularDistanceFunction : byte
|
||||
{
|
||||
Euclidean,
|
||||
Manhattan,
|
||||
Natural
|
||||
};
|
||||
|
||||
public enum CellularReturnType
|
||||
public enum CellularReturnType : byte
|
||||
{
|
||||
CellValue,
|
||||
NoiseLookup,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user