mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18c32a0258 | ||
|
|
72314a102d | ||
|
|
719ea26a31 | ||
|
|
5cb8fe1897 | ||
|
|
f35a52fc24 | ||
|
|
6bdb0cef47 | ||
|
|
fe3c9fe28f | ||
|
|
6085671f22 | ||
|
|
a2398da324 | ||
|
|
b27304cc58 | ||
|
|
3bf851a6cf | ||
|
|
cef92efd0f | ||
|
|
c5961a5ab1 | ||
|
|
8ddd92993d | ||
|
|
da253a5f34 | ||
|
|
ca9400a1ff | ||
|
|
f232195ceb | ||
|
|
b54a803519 | ||
|
|
a0d3d2108f | ||
|
|
977e4a017b | ||
|
|
2d8b159016 | ||
|
|
9caa0dde4b | ||
|
|
7a5a8c5eb1 | ||
|
|
95ba58f0a4 | ||
|
|
f780f04784 | ||
|
|
85782bda92 | ||
|
|
14a01df5b1 | ||
|
|
644da60bfc | ||
|
|
8c83999ad2 | ||
|
|
24b9fc9eec | ||
|
|
ba40185179 | ||
|
|
8b013cb424 |
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Net;
|
||||
using Robust.Client.Debugging;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
@@ -27,6 +27,7 @@ using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
@@ -536,7 +537,10 @@ namespace Robust.Client.Console.Commands
|
||||
var scroll = new ScrollContainer();
|
||||
tabContainer.AddChild(scroll);
|
||||
//scroll.SetAnchorAndMarginPreset(Control.LayoutPreset.Wide);
|
||||
var vBox = new VBoxContainer();
|
||||
var vBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
scroll.AddChild(vBox);
|
||||
|
||||
var progressBar = new ProgressBar { MaxValue = 10, Value = 5 };
|
||||
@@ -594,7 +598,10 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
|
||||
var group = new ButtonGroup();
|
||||
var vBoxRadioButtons = new VBoxContainer();
|
||||
var vBoxRadioButtons = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
vBoxRadioButtons.AddChild(new Button
|
||||
@@ -610,8 +617,9 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
TabContainer.SetTabTitle(vBoxRadioButtons, "Radio buttons!!");
|
||||
|
||||
tabContainer.AddChild(new VBoxContainer
|
||||
tabContainer.AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Name = "Slider",
|
||||
Children =
|
||||
{
|
||||
|
||||
@@ -13,10 +13,11 @@ using Microsoft.CodeAnalysis.Text;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.ViewVariables;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Scripting;
|
||||
using Robust.Shared.Utility;
|
||||
using SixLabors.ImageSharp;
|
||||
using Color = Robust.Shared.Maths.Color;
|
||||
|
||||
#nullable enable
|
||||
|
||||
@@ -116,7 +117,7 @@ namespace Robust.Client.Console
|
||||
}
|
||||
else
|
||||
{
|
||||
var options = ScriptInstanceShared.GetScriptOptions(_reflectionManager);
|
||||
var options = ScriptInstanceShared.GetScriptOptions(_reflectionManager).AddReferences(typeof(Image).Assembly);
|
||||
newScript = CSharpScript.Create(code, options, typeof(ScriptGlobals));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Scripting;
|
||||
using Robust.Shared.Timing;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.Console
|
||||
{
|
||||
@@ -18,7 +19,7 @@ namespace Robust.Client.Console
|
||||
{
|
||||
private readonly IReflectionManager _reflectionManager;
|
||||
|
||||
private readonly VBoxContainer _watchesVBox;
|
||||
private readonly BoxContainer _watchesVBox;
|
||||
private readonly LineEdit _addWatchEdit;
|
||||
private readonly Button _addWatchButton;
|
||||
|
||||
@@ -31,17 +32,20 @@ namespace Robust.Client.Console
|
||||
|
||||
Title = "Watch Window";
|
||||
|
||||
var mainVBox = new VBoxContainer
|
||||
var mainVBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
MinSize = (500, 300),
|
||||
Children =
|
||||
{
|
||||
(_watchesVBox = new VBoxContainer
|
||||
(_watchesVBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
VerticalExpand = true
|
||||
}),
|
||||
new HBoxContainer
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
(_addWatchEdit = new HistoryLineEdit
|
||||
@@ -105,8 +109,9 @@ namespace Robust.Client.Console
|
||||
Button delButton;
|
||||
_runner = runner;
|
||||
|
||||
AddChild(new HBoxContainer
|
||||
AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
(_outputLabel = new Label
|
||||
@@ -166,8 +171,9 @@ namespace Robust.Client.Console
|
||||
public CompilationErrorControl(string message)
|
||||
{
|
||||
Button delButton;
|
||||
AddChild(new HBoxContainer
|
||||
AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
|
||||
@@ -365,7 +365,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.ErrorS(LogCategory, "Unable to load RSI '{0}'. Trace:\n{1}", rsiPath);
|
||||
Logger.ErrorS(LogCategory, "Unable to load RSI '{0}'.", rsiPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,29 +15,43 @@ namespace Robust.Client.GameObjects
|
||||
private static Box2 SpriteAabbFunc(in SpriteComponent value)
|
||||
{
|
||||
var worldPos = value.Owner.Transform.WorldPosition;
|
||||
return new Box2(worldPos, worldPos);
|
||||
var tree = RenderingTreeSystem.GetRenderTree(value.Owner);
|
||||
|
||||
var pos = worldPos - tree?.Owner.Transform.WorldPosition ?? Vector2.Zero;
|
||||
|
||||
return new Box2(pos, pos);
|
||||
}
|
||||
|
||||
private static Box2 LightAabbFunc(in PointLightComponent value)
|
||||
{
|
||||
var worldPos = value.Owner.Transform.WorldPosition;
|
||||
|
||||
var tree = RenderingTreeSystem.GetRenderTree(value.Owner);
|
||||
var boxSize = value.Radius * 2;
|
||||
return Box2.CenteredAround(worldPos, (boxSize, boxSize));
|
||||
|
||||
var pos = worldPos - tree?.Owner.Transform.WorldPosition ?? Vector2.Zero;
|
||||
|
||||
return Box2.CenteredAround(pos, (boxSize, boxSize));
|
||||
}
|
||||
|
||||
internal static Box2 SpriteAabbFunc(SpriteComponent value, Vector2? worldPos = null)
|
||||
{
|
||||
worldPos ??= value.Owner.Transform.WorldPosition;
|
||||
var tree = RenderingTreeSystem.GetRenderTree(value.Owner);
|
||||
|
||||
return new Box2(worldPos.Value, worldPos.Value);
|
||||
var pos = worldPos - tree?.Owner.Transform.WorldPosition ?? Vector2.Zero;
|
||||
|
||||
return new Box2(pos, pos);
|
||||
}
|
||||
|
||||
internal static Box2 LightAabbFunc(PointLightComponent value, Vector2? worldPos = null)
|
||||
{
|
||||
worldPos ??= value.Owner.Transform.WorldPosition;
|
||||
var tree = RenderingTreeSystem.GetRenderTree(value.Owner);
|
||||
var boxSize = value.Radius * 2;
|
||||
return Box2.CenteredAround(worldPos.Value, (boxSize, boxSize));
|
||||
|
||||
var pos = worldPos - tree?.Owner.Transform.WorldPosition ?? Vector2.Zero;
|
||||
|
||||
return Box2.CenteredAround(pos, (boxSize, boxSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ namespace Robust.Client.GameObjects
|
||||
if (!source.SetPosition(coordinates.ToMapPos(EntityManager)))
|
||||
{
|
||||
source.Dispose();
|
||||
Logger.Warning("Can't play positional audio \"{stream.Name}\", can't set position.");
|
||||
Logger.Warning($"Can't play positional audio \"{stream.Name}\", can't set position.");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,22 +41,12 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
if (mapId == MapId.Nullspace) yield break;
|
||||
|
||||
var enclosed = false;
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldAABB))
|
||||
{
|
||||
yield return EntityManager.GetEntity(grid.GridEntityId).GetComponent<RenderingTreeComponent>();
|
||||
|
||||
// if we're enclosed then we know no other grids relevant + don't need the map's rendertree
|
||||
if (grid.WorldBounds.Encloses(in worldAABB))
|
||||
{
|
||||
enclosed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!enclosed)
|
||||
yield return _mapManager.GetMapEntity(mapId).GetComponent<RenderingTreeComponent>();
|
||||
yield return _mapManager.GetMapEntity(mapId).GetComponent<RenderingTreeComponent>();
|
||||
}
|
||||
|
||||
internal IEnumerable<DynamicTree<SpriteComponent>> GetSpriteTrees(MapId mapId, Box2 worldAABB)
|
||||
@@ -259,9 +249,8 @@ namespace Robust.Client.GameObjects
|
||||
EntityManager.GetEntity(_mapManager.GetGrid(gridId).GridEntityId).EnsureComponent<RenderingTreeComponent>();
|
||||
}
|
||||
|
||||
private RenderingTreeComponent? GetRenderTree(IEntity entity)
|
||||
internal static RenderingTreeComponent? GetRenderTree(IEntity entity)
|
||||
{
|
||||
|
||||
if (entity.Transform.MapID == MapId.Nullspace ||
|
||||
entity.HasComponent<RenderingTreeComponent>()) return null;
|
||||
|
||||
@@ -302,8 +291,7 @@ namespace Robust.Client.GameObjects
|
||||
continue;
|
||||
}
|
||||
|
||||
var treePos = newMapTree?.Owner.Transform.WorldPosition ?? Vector2.Zero;
|
||||
var aabb = RenderingTreeComponent.SpriteAabbFunc(sprite, worldPos).Translated(-treePos);
|
||||
var aabb = RenderingTreeComponent.SpriteAabbFunc(sprite, worldPos);
|
||||
|
||||
// If we're on a new map then clear the old one.
|
||||
if (oldMapTree != newMapTree)
|
||||
@@ -348,7 +336,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
var treePos = newMapTree?.Owner.Transform.WorldPosition ?? Vector2.Zero;
|
||||
var aabb = RenderingTreeComponent.LightAabbFunc(light, worldPos).Translated(-treePos);
|
||||
var aabb = RenderingTreeComponent.LightAabbFunc(light, worldPos);
|
||||
|
||||
// If we're on a new map then clear the old one.
|
||||
if (oldMapTree != newMapTree)
|
||||
|
||||
@@ -210,11 +210,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_windowing!.WindowRequestAttention(_windowing.MainWindow!);
|
||||
}
|
||||
|
||||
public async Task<IClydeWindow> CreateWindow(WindowCreateParameters parameters)
|
||||
public IClydeWindow CreateWindow(WindowCreateParameters parameters)
|
||||
{
|
||||
DebugTools.AssertNotNull(_windowing);
|
||||
|
||||
return await _windowing!.WindowCreate(parameters);
|
||||
return _windowing!.WindowCreate(parameters);
|
||||
}
|
||||
|
||||
private void DoDestroyWindow(WindowReg reg)
|
||||
|
||||
@@ -210,7 +210,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
yield break;
|
||||
}
|
||||
|
||||
public Task<IClydeWindow> CreateWindow(WindowCreateParameters parameters)
|
||||
public IClydeWindow CreateWindow(WindowCreateParameters parameters)
|
||||
{
|
||||
var window = new DummyWindow(CreateRenderTarget((123, 123), default))
|
||||
{
|
||||
@@ -218,7 +218,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
};
|
||||
_windows.Add(window);
|
||||
|
||||
return Task.FromResult<IClydeWindow>(window);
|
||||
return window;
|
||||
}
|
||||
|
||||
public ClydeHandle LoadShader(ParsedShader shader, string? name = null)
|
||||
|
||||
@@ -22,8 +22,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
internal partial class Clyde
|
||||
{
|
||||
// Wait for it.
|
||||
private sealed partial class GlfwWindowingImpl
|
||||
private unsafe sealed partial class GlfwWindowingImpl
|
||||
{
|
||||
private readonly List<GlfwWindowReg> _windows = new();
|
||||
|
||||
@@ -36,45 +35,23 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private int _nextWindowId = 1;
|
||||
private static bool _eglLoaded;
|
||||
|
||||
public async Task<WindowHandle> WindowCreate(WindowCreateParameters parameters)
|
||||
public WindowHandle WindowCreate(WindowCreateParameters parameters)
|
||||
{
|
||||
// tfw await not allowed in unsafe contexts
|
||||
|
||||
// GL APIs don't take kindly to making a new window without unbinding the main context. Great.
|
||||
// Leaving code for async path in, in case it works on like GLX.
|
||||
var unbindContextAndBlock = true;
|
||||
|
||||
DebugTools.AssertNotNull(_mainWindow);
|
||||
|
||||
Task<GlfwWindowCreateResult> task;
|
||||
unsafe
|
||||
{
|
||||
if (unbindContextAndBlock)
|
||||
GLFW.MakeContextCurrent(null);
|
||||
GLFW.MakeContextCurrent(null);
|
||||
|
||||
task = SharedWindowCreate(
|
||||
_clyde._chosenRenderer,
|
||||
parameters,
|
||||
_mainWindow!.GlfwWindow);
|
||||
}
|
||||
var task = SharedWindowCreate(
|
||||
_clyde._chosenRenderer,
|
||||
parameters,
|
||||
_mainWindow!.GlfwWindow);
|
||||
|
||||
if (unbindContextAndBlock)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
// Block the main thread (to avoid stuff like texture uploads being problematic).
|
||||
WaitWindowCreate(task);
|
||||
// Block the main thread (to avoid stuff like texture uploads being problematic).
|
||||
WaitWindowCreate(task);
|
||||
|
||||
if (unbindContextAndBlock)
|
||||
GLFW.MakeContextCurrent(_mainWindow.GlfwWindow);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await task;
|
||||
}
|
||||
GLFW.MakeContextCurrent(_mainWindow.GlfwWindow);
|
||||
|
||||
var (reg, error) = await task;
|
||||
var (reg, error) = task.Result;
|
||||
|
||||
if (reg == null)
|
||||
{
|
||||
@@ -85,18 +62,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_clyde.CreateWindowRenderTexture(reg);
|
||||
_clyde.InitWindowBlitThread(reg);
|
||||
|
||||
unsafe
|
||||
{
|
||||
GLFW.MakeContextCurrent(_mainWindow.GlfwWindow);
|
||||
}
|
||||
GLFW.MakeContextCurrent(_mainWindow.GlfwWindow);
|
||||
|
||||
return reg.Handle;
|
||||
}
|
||||
}
|
||||
|
||||
// Yes, you read that right.
|
||||
private sealed unsafe partial class GlfwWindowingImpl
|
||||
{
|
||||
public bool TryInitMainWindow(Renderer renderer, [NotNullWhen(false)] out string? error)
|
||||
{
|
||||
var width = _cfg.GetCVar(CVars.DisplayWidth);
|
||||
@@ -167,6 +137,21 @@ namespace Robust.Client.Graphics.Clyde
|
||||
WindowCreateParameters parameters,
|
||||
Window* share)
|
||||
{
|
||||
//
|
||||
// IF YOU'RE WONDERING WHY THIS IS TASK-BASED:
|
||||
// I originally wanted this to be async so we could avoid blocking the main thread
|
||||
// while the OS takes its stupid 100~ms just to initialize a fucking GL context.
|
||||
// This doesn't *work* because
|
||||
// we have to release the GL context while the shared context is being created.
|
||||
// (at least on WGL, I didn't test other platforms and I don't care to.)
|
||||
// Not worth it to avoid a main thread blockage by allowing Clyde to temporarily release the GL context,
|
||||
// because rendering would be locked up *anyways*.
|
||||
//
|
||||
// Basically what I'm saying is that everything about OpenGL is a fucking mistake
|
||||
// and I should get on either Veldrid or Vulkan some time.
|
||||
// Probably Veldrid tbh.
|
||||
//
|
||||
|
||||
// Yes we ping-pong this TCS through the window thread and back, deal with it.
|
||||
var tcs = new TaskCompletionSource<GlfwWindowCreateResult>();
|
||||
SendCmd(new CmdWinCreate(
|
||||
@@ -525,7 +510,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
|
||||
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
void WindowRequestAttention(WindowReg window);
|
||||
void WindowSwapBuffers(WindowReg window);
|
||||
uint? WindowGetX11Id(WindowReg window);
|
||||
Task<WindowHandle> WindowCreate(WindowCreateParameters parameters);
|
||||
WindowHandle WindowCreate(WindowCreateParameters parameters);
|
||||
void WindowDestroy(WindowReg reg);
|
||||
|
||||
string KeyGetName(Keyboard.Key key);
|
||||
|
||||
@@ -130,6 +130,6 @@ namespace Robust.Client.Graphics
|
||||
|
||||
IEnumerable<IClydeMonitor> EnumerateMonitors();
|
||||
|
||||
Task<IClydeWindow> CreateWindow(WindowCreateParameters parameters);
|
||||
IClydeWindow CreateWindow(WindowCreateParameters parameters);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using Robust.Client.Utility;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
|
||||
@@ -62,7 +63,7 @@ namespace Robust.Client.Map
|
||||
var row = i / dimensionX;
|
||||
|
||||
Image<Rgba32> image;
|
||||
using (var stream = _resourceCache.ContentFileRead(Path.Join(def.Path, $"{def.SpriteName}.png")))
|
||||
using (var stream = _resourceCache.ContentFileRead(new ResourcePath(def.Path) / $"{def.SpriteName}.png"))
|
||||
{
|
||||
image = Image.Load<Rgba32>(stream);
|
||||
}
|
||||
|
||||
@@ -504,8 +504,8 @@ namespace Robust.Client.Placement
|
||||
coordinates = new EntityCoordinates();
|
||||
return false;
|
||||
}
|
||||
coordinates = EntityCoordinates.FromMap(ent.EntityManager, MapManager,
|
||||
eyeManager.ScreenToMap(_inputManager.MouseScreenPosition));
|
||||
coordinates = EntityCoordinates.FromMap(MapManager,
|
||||
eyeManager.ScreenToMap(_inputManager.MouseScreenPosition));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ namespace Robust.Client.Placement
|
||||
var mapCoords = pManager.eyeManager.ScreenToMap(coords.Position);
|
||||
if (!pManager.MapManager.TryFindGridAt(mapCoords, out var grid))
|
||||
{
|
||||
return EntityCoordinates.FromMap(pManager.EntityManager, pManager.MapManager, mapCoords);
|
||||
return EntityCoordinates.FromMap(pManager.MapManager, mapCoords);
|
||||
}
|
||||
|
||||
return EntityCoordinates.FromMap(pManager.EntityManager, grid.GridEntityId, mapCoords);
|
||||
|
||||
@@ -6,20 +6,31 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A container that lays out its children sequentially.
|
||||
/// Use <see cref="VBoxContainer"/> or <see cref="HBoxContainer"/> for an implementation.
|
||||
/// </summary>
|
||||
public abstract class BoxContainer : Container
|
||||
public class BoxContainer : Container
|
||||
{
|
||||
private LayoutOrientation _orientation;
|
||||
public const string StylePropertySeparation = "separation";
|
||||
|
||||
private const int DefaultSeparation = 0;
|
||||
private protected abstract bool Vertical { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies "where" the controls should be laid out.
|
||||
/// </summary>
|
||||
public AlignMode Align { get; set; }
|
||||
|
||||
private bool Vertical => Orientation == LayoutOrientation.Vertical;
|
||||
|
||||
public LayoutOrientation Orientation
|
||||
{
|
||||
get => _orientation;
|
||||
set
|
||||
{
|
||||
_orientation = value;
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
|
||||
private int ActualSeparation
|
||||
{
|
||||
get
|
||||
@@ -237,5 +248,11 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// </summary>
|
||||
End = 2
|
||||
}
|
||||
|
||||
public enum LayoutOrientation : byte
|
||||
{
|
||||
Horizontal,
|
||||
Vertical
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,9 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
ToggleMode = true;
|
||||
|
||||
var hBox = new HBoxContainer
|
||||
var hBox = new BoxContainer
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||
StyleClasses = { StyleClassCheckBox },
|
||||
};
|
||||
AddChild(hBox);
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
using System;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Container that lays its children out horizontally: from left to right.
|
||||
/// </summary>
|
||||
[Obsolete("Use BoxContainer and set Orientation instead")]
|
||||
public class HBoxContainer : BoxContainer
|
||||
{
|
||||
private protected override bool Vertical => false;
|
||||
public HBoxContainer()
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
[Obsolete("Use SplitContainer directly and set Orientation")]
|
||||
public class HSplitContainer : SplitContainer
|
||||
{
|
||||
private protected sealed override bool Vertical => false;
|
||||
public HSplitContainer()
|
||||
{
|
||||
Orientation = SplitOrientation.Horizontal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Maths;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
@@ -13,9 +14,9 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
private readonly List<Menu> _menus = new();
|
||||
private readonly List<MenuBarTopButton> _buttons = new();
|
||||
private readonly HBoxContainer _hBox;
|
||||
private readonly BoxContainer _hBox;
|
||||
private readonly Popup _popup;
|
||||
private readonly VBoxContainer _popupVBox;
|
||||
private readonly BoxContainer _popupVBox;
|
||||
private bool _popupOpen;
|
||||
|
||||
public IList<Menu> Menus { get; }
|
||||
@@ -26,13 +27,21 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
Children =
|
||||
{
|
||||
(_popupVBox = new VBoxContainer {MinSize = (300, 0)})
|
||||
(_popupVBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
MinSize = (300, 0)
|
||||
})
|
||||
}
|
||||
};
|
||||
_popup.OnPopupHide += PopupHidden;
|
||||
UserInterfaceManager.ModalRoot.AddChild(_popup);
|
||||
Menus = new MenuCollection(this);
|
||||
AddChild(_hBox = new HBoxContainer {SeparationOverride = 8});
|
||||
AddChild(_hBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
SeparationOverride = 8
|
||||
});
|
||||
}
|
||||
|
||||
private void AddMenu(Menu menu)
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
@@ -20,7 +21,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
// map from key to buttondata index
|
||||
private Dictionary<TKey, int> _keyMap = new();
|
||||
private readonly Popup _popup;
|
||||
private readonly VBoxContainer _popupVBox;
|
||||
private readonly BoxContainer _popupVBox;
|
||||
private readonly Label _label;
|
||||
|
||||
public event Action<ItemPressedEventArgs>? OnItemSelected;
|
||||
@@ -60,11 +61,17 @@ namespace Robust.Client.UserInterface.Controls
|
||||
AddStyleClass(StyleClassButton);
|
||||
OnPressed += OnPressedInternal;
|
||||
|
||||
var hBox = new HBoxContainer();
|
||||
var hBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal
|
||||
};
|
||||
AddChild(hBox);
|
||||
|
||||
_popup = new Popup();
|
||||
_popupVBox = new VBoxContainer();
|
||||
_popupVBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
_popup.AddChild(_popupVBox);
|
||||
_popup.OnPopupHide += OnPopupHide;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
private readonly List<ButtonData> _buttonData = new();
|
||||
private readonly Dictionary<int, int> _idMap = new();
|
||||
private readonly Popup _popup;
|
||||
private readonly VBoxContainer _popupVBox;
|
||||
private readonly BoxContainer _popupVBox;
|
||||
private readonly Label _label;
|
||||
private readonly TextureRect _triangle;
|
||||
|
||||
@@ -49,11 +50,17 @@ namespace Robust.Client.UserInterface.Controls
|
||||
Prefix = "";
|
||||
OnPressed += OnPressedInternal;
|
||||
|
||||
var hBox = new HBoxContainer();
|
||||
var hBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal
|
||||
};
|
||||
AddChild(hBox);
|
||||
|
||||
_popup = new Popup();
|
||||
_popupVBox = new VBoxContainer();
|
||||
_popupVBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
_popup.AddChild(_popupVBox);
|
||||
_popup.OnPopupHide += OnPopupHide;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
@@ -34,11 +35,17 @@ namespace Robust.Client.UserInterface.Controls
|
||||
switch (layout)
|
||||
{
|
||||
case RadioOptionsLayout.Vertical:
|
||||
_container = new VBoxContainer();
|
||||
_container = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
break;
|
||||
case RadioOptionsLayout.Horizontal:
|
||||
default:
|
||||
_container = new HBoxContainer();
|
||||
_container = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
public abstract class SplitContainer : Container
|
||||
public class SplitContainer : Container
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines how user-initiated moving of the split should work. See documentation
|
||||
@@ -22,11 +22,10 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// </summary>
|
||||
public float SplitEdgeSeparation { get; set; }
|
||||
|
||||
private protected abstract bool Vertical { get; }
|
||||
|
||||
private float _splitCenter;
|
||||
private SplitState _splitState;
|
||||
private bool _dragging;
|
||||
private SplitOrientation _orientation;
|
||||
|
||||
// min / max x and y extents in relative virtual pixels of where the split can go regardless
|
||||
// of anything else.
|
||||
@@ -35,6 +34,18 @@ namespace Robust.Client.UserInterface.Controls
|
||||
private float SplitMax =>
|
||||
Vertical ? Height - (SplitWidth + SplitEdgeSeparation) : Width - (SplitWidth + SplitEdgeSeparation);
|
||||
|
||||
private bool Vertical => Orientation == SplitOrientation.Vertical;
|
||||
|
||||
public SplitOrientation Orientation
|
||||
{
|
||||
get => _orientation;
|
||||
set
|
||||
{
|
||||
_orientation = value;
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
|
||||
public SplitContainer()
|
||||
{
|
||||
MouseFilter = MouseFilterMode.Stop;
|
||||
@@ -264,5 +275,11 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// </summary>
|
||||
Manual = 1
|
||||
}
|
||||
|
||||
public enum SplitOrientation : byte
|
||||
{
|
||||
Horizontal,
|
||||
Vertical
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
using System;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Container that lays its children out vertically: from top to bottom.
|
||||
/// </summary>
|
||||
[Obsolete("Use BoxContainer and set Orientation instead")]
|
||||
public class VBoxContainer : BoxContainer
|
||||
{
|
||||
private protected override bool Vertical => true;
|
||||
public VBoxContainer()
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
[Obsolete("Use SplitContainer directly and set Orientation")]
|
||||
public class VSplitContainer : SplitContainer
|
||||
{
|
||||
private protected sealed override bool Vertical => true;
|
||||
public VSplitContainer()
|
||||
{
|
||||
Orientation = SplitOrientation.Vertical;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics">
|
||||
<VBoxContainer>
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<OutputPanel Name="Output" VerticalExpand="True">
|
||||
<OutputPanel.StyleBoxOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#25252add"
|
||||
@@ -9,5 +9,5 @@
|
||||
</OutputPanel.StyleBoxOverride>
|
||||
</OutputPanel>
|
||||
<HistoryLineEdit Name="CommandBar" PlaceHolder="{Loc 'console-line-edit-placeholder'}" />
|
||||
</VBoxContainer>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
||||
@@ -12,6 +12,7 @@ using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.UserInterface.CustomControls
|
||||
{
|
||||
@@ -21,7 +22,7 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
private readonly IPrototypeManager prototypeManager;
|
||||
private readonly IResourceCache resourceCache;
|
||||
|
||||
private VBoxContainer MainVBox;
|
||||
private BoxContainer MainVBox;
|
||||
private PrototypeListContainer PrototypeList;
|
||||
private LineEdit SearchBar;
|
||||
private OptionButton OverrideMenu;
|
||||
@@ -71,13 +72,15 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
SetSize = (250, 300);
|
||||
MinSize = (250, 200);
|
||||
|
||||
Contents.AddChild(MainVBox = new VBoxContainer
|
||||
Contents.AddChild(MainVBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Name = "AAAAAA",
|
||||
Children =
|
||||
{
|
||||
new HBoxContainer
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
(SearchBar = new LineEdit
|
||||
@@ -102,8 +105,9 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
(PrototypeList = new PrototypeListContainer())
|
||||
}
|
||||
},
|
||||
new HBoxContainer
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
(EraseButton = new Button
|
||||
@@ -472,8 +476,9 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
ToggleMode = true,
|
||||
});
|
||||
|
||||
AddChild(new HBoxContainer
|
||||
AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
(EntityTextureRects = new LayeredTextureRect
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
}
|
||||
|
||||
var fps = _gameTiming.FramesPerSecondAvg;
|
||||
Text = $"FPS: {fps:N1}";
|
||||
Text = $"FPS: {fps:N0}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<SS14Window xmlns="https://spacestation14.io" MinWidth="100" MinHeight="50">
|
||||
<PanelContainer StyleClasses="windowPanel" />
|
||||
<VBoxContainer SeparationOverride="0">
|
||||
<BoxContainer Orientation="Vertical" SeparationOverride="0">
|
||||
<PanelContainer Name="WindowHeader" StyleClasses="windowHeader">
|
||||
<HBoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Margin="5 0 0 0" HorizontalExpand="true" Name="TitleLabel" StyleIdentifier="foo" ClipText="True"
|
||||
Text="{Loc 'ss14window-placeholder-title'}" VAlign="Center" StyleClasses="windowTitle" />
|
||||
<TextureButton Name="CloseButton" StyleClasses="windowCloseButton" VerticalAlignment="Center" />
|
||||
</HBoxContainer>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
<Control Name="ContentsContainer" Margin="10" RectClipContent="True" VerticalExpand="true" />
|
||||
</VBoxContainer>
|
||||
</BoxContainer>
|
||||
</SS14Window>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Maths;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.UserInterface.CustomControls
|
||||
{
|
||||
@@ -12,8 +13,9 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
|
||||
protected ScriptConsole()
|
||||
{
|
||||
Contents.AddChild(new VBoxContainer
|
||||
Contents.AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Children =
|
||||
{
|
||||
new PanelContainer
|
||||
@@ -29,8 +31,9 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
},
|
||||
VerticalExpand = true,
|
||||
},
|
||||
new HBoxContainer
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
(InputBar = new HistoryLineEdit
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
using Robust.Shared.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Placement;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.UserInterface.CustomControls
|
||||
{
|
||||
@@ -32,9 +35,15 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
_placementManager = placementManager;
|
||||
_resourceCache = resourceCache;
|
||||
|
||||
var vBox = new VBoxContainer();
|
||||
var vBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
Contents.AddChild(vBox);
|
||||
var hBox = new HBoxContainer();
|
||||
var hBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal
|
||||
};
|
||||
vBox.AddChild(hBox);
|
||||
SearchBar = new LineEdit {PlaceHolder = "Search", HorizontalExpand = true};
|
||||
SearchBar.OnTextChanged += OnSearchBarTextChanged;
|
||||
@@ -105,7 +114,7 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
Texture? texture = null;
|
||||
if (!string.IsNullOrEmpty(entry.SpriteName))
|
||||
{
|
||||
texture = _resourceCache.GetResource<TextureResource>($"/Textures/Constructible/Tiles/{entry.SpriteName}.png");
|
||||
texture = _resourceCache.GetResource<TextureResource>(new ResourcePath(entry.Path) / $"{entry.SpriteName}.png");
|
||||
}
|
||||
TileList.AddItem(entry.DisplayName, texture);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Robust.Client.UserInterface
|
||||
monitor = clyde.EnumerateMonitors().Single(m => m.Id == id);
|
||||
}
|
||||
|
||||
var window = await clyde.CreateWindow(new WindowCreateParameters
|
||||
var window = clyde.CreateWindow(new WindowCreateParameters
|
||||
{
|
||||
Maximized = true,
|
||||
Title = "SS14 Debug Window",
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Globalization;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Maths;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Editors
|
||||
{
|
||||
@@ -9,8 +10,9 @@ namespace Robust.Client.ViewVariables.Editors
|
||||
{
|
||||
protected override Control MakeUI(object? value)
|
||||
{
|
||||
var hBox = new HBoxContainer
|
||||
var hBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
MinSize = new Vector2(200, 0)
|
||||
};
|
||||
var angle = (Angle) value!;
|
||||
|
||||
@@ -5,6 +5,7 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Editors
|
||||
{
|
||||
@@ -13,8 +14,9 @@ namespace Robust.Client.ViewVariables.Editors
|
||||
protected override Control MakeUI(object? value)
|
||||
{
|
||||
var coords = (EntityCoordinates) value!;
|
||||
var hBoxContainer = new HBoxContainer
|
||||
var hBoxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
MinSize = new Vector2(240, 0),
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Editors
|
||||
{
|
||||
@@ -9,8 +10,9 @@ namespace Robust.Client.ViewVariables.Editors
|
||||
{
|
||||
protected override Control MakeUI(object? value)
|
||||
{
|
||||
var hBox = new HBoxContainer
|
||||
var hBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
MinSize = new Vector2(200, 0)
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Editors
|
||||
{
|
||||
@@ -20,7 +21,11 @@ namespace Robust.Client.ViewVariables.Editors
|
||||
{
|
||||
_localValue = value;
|
||||
|
||||
var hbox = new HBoxContainer() { HorizontalExpand = true };
|
||||
var hbox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
HorizontalExpand = true
|
||||
};
|
||||
|
||||
_lineEdit = new LineEdit()
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Editors
|
||||
{
|
||||
@@ -20,7 +21,10 @@ namespace Robust.Client.ViewVariables.Editors
|
||||
|
||||
protected override Control MakeUI(object? value)
|
||||
{
|
||||
var hBox = new HBoxContainer();
|
||||
var hBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal
|
||||
};
|
||||
|
||||
dynamic d = value!;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Globalization;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Maths;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Editors
|
||||
{
|
||||
@@ -17,8 +18,9 @@ namespace Robust.Client.ViewVariables.Editors
|
||||
|
||||
protected override Control MakeUI(object? value)
|
||||
{
|
||||
var hBoxContainer = new HBoxContainer
|
||||
var hBoxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
MinSize = new Vector2(200, 0),
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Globalization;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Maths;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Editors
|
||||
{
|
||||
@@ -16,8 +17,9 @@ namespace Robust.Client.ViewVariables.Editors
|
||||
|
||||
protected override Control MakeUI(object? value)
|
||||
{
|
||||
var hBoxContainer = new HBoxContainer
|
||||
var hBoxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
MinSize = new Vector2(240, 0),
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.UserInterface.Control;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
using static Robust.Client.UserInterface.Controls.LineEdit;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Instances
|
||||
@@ -48,10 +49,10 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
|
||||
private ViewVariablesBlobMembers? _membersBlob;
|
||||
|
||||
private VBoxContainer _clientComponents = default!;
|
||||
private BoxContainer _clientComponents = default!;
|
||||
|
||||
private VBoxContainer _serverVariables = default!;
|
||||
private VBoxContainer _serverComponents = default!;
|
||||
private BoxContainer _serverVariables = default!;
|
||||
private BoxContainer _serverComponents = default!;
|
||||
|
||||
private Button _clientComponentsAddButton = default!;
|
||||
private Button _serverComponentsAddButton = default!;
|
||||
@@ -73,7 +74,10 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
var scrollContainer = new ScrollContainer();
|
||||
//scrollContainer.SetAnchorPreset(Control.LayoutPreset.Wide, true);
|
||||
window.Contents.AddChild(scrollContainer);
|
||||
var vBoxContainer = new VBoxContainer();
|
||||
var vBoxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
scrollContainer.AddChild(vBoxContainer);
|
||||
|
||||
// Handle top bar displaying type and ToString().
|
||||
@@ -84,7 +88,11 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
{
|
||||
//var smallFont = new VectorFont(_resourceCache.GetResource<FontResource>("/Fonts/CALIBRI.TTF"), 10);
|
||||
// Custom ToString() implementation.
|
||||
var headBox = new VBoxContainer {SeparationOverride = 0};
|
||||
var headBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
SeparationOverride = 0
|
||||
};
|
||||
headBox.AddChild(new Label {Text = stringified, ClipText = true});
|
||||
headBox.AddChild(new Label
|
||||
{
|
||||
@@ -102,7 +110,10 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
|
||||
if (_entity.TryGetComponent(out ISpriteComponent? sprite))
|
||||
{
|
||||
var hBox = new HBoxContainer();
|
||||
var hBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal
|
||||
};
|
||||
top.HorizontalExpand = true;
|
||||
hBox.AddChild(top);
|
||||
hBox.AddChild(new SpriteView {Sprite = sprite});
|
||||
@@ -118,7 +129,11 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
_tabs.OnTabChanged += _tabsOnTabChanged;
|
||||
vBoxContainer.AddChild(_tabs);
|
||||
|
||||
var clientVBox = new VBoxContainer {SeparationOverride = 0};
|
||||
var clientVBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
SeparationOverride = 0
|
||||
};
|
||||
_tabs.AddChild(clientVBox);
|
||||
_tabs.SetTabTitle(TabClientVars, Loc.GetString("view-variable-instance-entity-client-variables-tab-title"));
|
||||
|
||||
@@ -136,7 +151,11 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
}
|
||||
}
|
||||
|
||||
_clientComponents = new VBoxContainer {SeparationOverride = 0};
|
||||
_clientComponents = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
SeparationOverride = 0
|
||||
};
|
||||
_tabs.AddChild(_clientComponents);
|
||||
_tabs.SetTabTitle(TabClientComponents, Loc.GetString("view-variable-instance-entity-client-components-tab-title"));
|
||||
|
||||
@@ -144,11 +163,19 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
|
||||
if (!_entity.Uid.IsClientSide())
|
||||
{
|
||||
_serverVariables = new VBoxContainer {SeparationOverride = 0};
|
||||
_serverVariables = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
SeparationOverride = 0
|
||||
};
|
||||
_tabs.AddChild(_serverVariables);
|
||||
_tabs.SetTabTitle(TabServerVars, Loc.GetString("view-variable-instance-entity-server-variables-tab-title"));
|
||||
|
||||
_serverComponents = new VBoxContainer {SeparationOverride = 0};
|
||||
_serverComponents = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
SeparationOverride = 0
|
||||
};
|
||||
_tabs.AddChild(_serverComponents);
|
||||
_tabs.SetTabTitle(TabServerComponents, Loc.GetString("view-variable-instance-entity-server-components-tab-title"));
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ using Robust.Shared.Input;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Instances
|
||||
@@ -64,8 +65,9 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
var scrollContainer = new ScrollContainer();
|
||||
//scrollContainer.SetAnchorPreset(Control.LayoutPreset.Wide, true);
|
||||
window.Contents.AddChild(scrollContainer);
|
||||
var vBoxContainer = new VBoxContainer
|
||||
var vBoxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
HorizontalExpand = true,
|
||||
VerticalExpand = true,
|
||||
};
|
||||
@@ -73,7 +75,10 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
|
||||
// Handle top bar.
|
||||
{
|
||||
var headBox = new HBoxContainer();
|
||||
var headBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal
|
||||
};
|
||||
var name = MakeTopBar(top, bottom);
|
||||
name.HorizontalExpand = true;
|
||||
headBox.AddChild(name);
|
||||
|
||||
@@ -10,6 +10,7 @@ using Robust.Client.ViewVariables.Editors;
|
||||
using Robust.Client.ViewVariables.Instances;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Traits
|
||||
{
|
||||
@@ -25,8 +26,8 @@ namespace Robust.Client.ViewVariables.Traits
|
||||
private Button _leftButton = default!;
|
||||
private Button _rightButton = default!;
|
||||
private LineEdit _pageLabel = default!;
|
||||
private HBoxContainer _controlsHBox = default!;
|
||||
private VBoxContainer _elementsVBox = default!;
|
||||
private BoxContainer _controlsHBox = default!;
|
||||
private BoxContainer _elementsVBox = default!;
|
||||
|
||||
private int HighestKnownPage => Math.Max(0, ((_cache.Count + ElementsPerPage - 1) / ElementsPerPage) - 1);
|
||||
|
||||
@@ -47,9 +48,13 @@ namespace Robust.Client.ViewVariables.Traits
|
||||
_enumerator = enumerable.GetEnumerator();
|
||||
}
|
||||
|
||||
var outerVBox = new VBoxContainer();
|
||||
_controlsHBox = new HBoxContainer
|
||||
var outerVBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
_controlsHBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
HorizontalAlignment = Control.HAlignment.Center
|
||||
};
|
||||
|
||||
@@ -70,7 +75,10 @@ namespace Robust.Client.ViewVariables.Traits
|
||||
|
||||
outerVBox.AddChild(_controlsHBox);
|
||||
|
||||
_elementsVBox = new VBoxContainer();
|
||||
_elementsVBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
outerVBox.AddChild(_elementsVBox);
|
||||
|
||||
instance.AddTab("IEnumerable", outerVBox);
|
||||
|
||||
@@ -5,6 +5,7 @@ using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Traits
|
||||
{
|
||||
@@ -13,12 +14,16 @@ namespace Robust.Client.ViewVariables.Traits
|
||||
private readonly IViewVariablesManagerInternal _vvm;
|
||||
private readonly IRobustSerializer _robustSerializer;
|
||||
|
||||
private VBoxContainer _memberList = default!;
|
||||
private BoxContainer _memberList = default!;
|
||||
|
||||
public override void Initialize(ViewVariablesInstanceObject instance)
|
||||
{
|
||||
base.Initialize(instance);
|
||||
_memberList = new VBoxContainer {SeparationOverride = 0};
|
||||
_memberList = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
SeparationOverride = 0
|
||||
};
|
||||
instance.AddTab("Members", _memberList);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<cc:SS14Window xmlns:cc="clr-namespace:Robust.Client.UserInterface.CustomControls"
|
||||
xmlns:c="clr-namespace:Robust.Client.UserInterface.Controls">
|
||||
<c:VBoxContainer VerticalExpand="True">
|
||||
<c:BoxContainer Orientation="Vertical" VerticalExpand="True">
|
||||
<c:LineEdit Name="SearchLineEdit" PlaceHolder="Search..." HorizontalExpand="True" />
|
||||
<c:ItemList Name="EntryItemList" VerticalExpand="True" HorizontalExpand="True" SelectMode="Single" />
|
||||
<c:Button Name="AddButton" Text="Select" TextAlign="Center" HorizontalExpand="True"/>
|
||||
</c:VBoxContainer>
|
||||
</c:BoxContainer>
|
||||
</cc:SS14Window>
|
||||
|
||||
@@ -9,6 +9,7 @@ using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.ViewVariables
|
||||
{
|
||||
@@ -126,7 +127,11 @@ namespace Robust.Client.ViewVariables
|
||||
// 10);
|
||||
|
||||
// Custom ToString() implementation.
|
||||
var headBox = new VBoxContainer {SeparationOverride = 0};
|
||||
var headBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
SeparationOverride = 0
|
||||
};
|
||||
headBox.AddChild(new Label {Text = top, ClipText = true});
|
||||
headBox.AddChild(new Label
|
||||
{
|
||||
|
||||
@@ -8,14 +8,15 @@ using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.ViewVariables
|
||||
{
|
||||
internal class ViewVariablesPropertyControl : PanelContainer
|
||||
{
|
||||
public VBoxContainer VBox { get; }
|
||||
public HBoxContainer TopContainer { get; }
|
||||
public HBoxContainer BottomContainer { get; }
|
||||
public BoxContainer VBox { get; }
|
||||
public BoxContainer TopContainer { get; }
|
||||
public BoxContainer BottomContainer { get; }
|
||||
public Label NameLabel { get; }
|
||||
|
||||
private readonly Label _bottomLabel;
|
||||
@@ -34,14 +35,23 @@ namespace Robust.Client.ViewVariables
|
||||
ToolTip = "Click to expand";
|
||||
MinHeight = 25;
|
||||
|
||||
VBox = new VBoxContainer {SeparationOverride = 0};
|
||||
VBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
SeparationOverride = 0
|
||||
};
|
||||
AddChild(VBox);
|
||||
|
||||
TopContainer = new HBoxContainer {VerticalExpand = true};
|
||||
TopContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
VerticalExpand = true
|
||||
};
|
||||
VBox.AddChild(TopContainer);
|
||||
|
||||
BottomContainer = new HBoxContainer
|
||||
BottomContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Visible = false
|
||||
};
|
||||
VBox.AddChild(BottomContainer);
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
using JetBrains.Annotations;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Robust.Server.GameObjects
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class PhysicsSystem : SharedPhysicsSystem
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_mapManager.OnGridCreated += HandleGridCreated;
|
||||
SubscribeLocalEvent<GridInitializeEvent>(HandleGridInit);
|
||||
LoadMetricCVar();
|
||||
_configurationManager.OnValueChanged(CVars.MetricsEnabled, _ => LoadMetricCVar());
|
||||
}
|
||||
@@ -28,19 +26,14 @@ namespace Robust.Server.GameObjects
|
||||
MetricsEnabled = _configurationManager.GetCVar(CVars.MetricsEnabled);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
private void HandleGridInit(GridInitializeEvent ev)
|
||||
{
|
||||
base.Shutdown();
|
||||
_mapManager.OnGridCreated -= HandleGridCreated;
|
||||
}
|
||||
var guid = ev.EntityUid;
|
||||
|
||||
private void HandleGridCreated(MapId mapId, GridId gridId)
|
||||
{
|
||||
if (!EntityManager.TryGetEntity(_mapManager.GetGrid(gridId).GridEntityId, out var gridEntity)) return;
|
||||
var grid = _mapManager.GetGrid(gridId);
|
||||
var collideComp = gridEntity.AddComponent<PhysicsComponent>();
|
||||
if (!EntityManager.TryGetEntity(guid, out var gridEntity)) return;
|
||||
var collideComp = gridEntity.EnsureComponent<PhysicsComponent>();
|
||||
collideComp.CanCollide = true;
|
||||
collideComp.AddFixture(new Fixture(collideComp, new PhysShapeGrid(grid)) {CollisionMask = MapGridHelpers.CollisionGroup, CollisionLayer = MapGridHelpers.CollisionGroup});
|
||||
collideComp.BodyType = BodyType.Static;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
136
Robust.Server/Physics/GridFixtureSystem.cs
Normal file
136
Robust.Server/Physics/GridFixtureSystem.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
|
||||
namespace Robust.Server.Physics
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles generating fixtures for MapGrids.
|
||||
/// </summary>
|
||||
internal sealed class GridFixtureSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
private SharedBroadPhaseSystem _broadphase = default!;
|
||||
|
||||
// Is delaying fixture updates a good idea? IDEK. We definitely can't do them on every tile changed
|
||||
// because if someone changes 50 tiles that will kill perf. We could probably just run it every Update
|
||||
// (and at specific times due to race condition stuff).
|
||||
// At any rate, cooldown given here if someone wants it. CD of 0 just runs it every tick.
|
||||
private float _cooldown;
|
||||
private float _accumulator;
|
||||
|
||||
private HashSet<MapChunk> _queuedChunks = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
UpdatesBefore.Add(typeof(PhysicsSystem));
|
||||
SubscribeLocalEvent<RegenerateChunkCollisionEvent>(HandleCollisionRegenerate);
|
||||
_broadphase = Get<SharedBroadPhaseSystem>();
|
||||
|
||||
var configManager = IoCManager.Resolve<IConfigurationManager>();
|
||||
configManager.OnValueChanged(CVars.GridFixtureUpdateRate, value => _cooldown = value, true);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
_accumulator += frameTime;
|
||||
if (_accumulator < _cooldown) return;
|
||||
|
||||
_accumulator -= _cooldown;
|
||||
Process();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Go through every dirty chunk and re-generate their fixtures.
|
||||
/// </summary>
|
||||
public void Process()
|
||||
{
|
||||
foreach (var chunk in _queuedChunks)
|
||||
{
|
||||
RegenerateCollision(chunk);
|
||||
}
|
||||
|
||||
_queuedChunks.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue the chunk to generate (if cooldown > 0) or immediately process it.
|
||||
/// </summary>
|
||||
/// <param name="ev"></param>
|
||||
private void HandleCollisionRegenerate(RegenerateChunkCollisionEvent ev)
|
||||
{
|
||||
if (_cooldown <= 0f)
|
||||
{
|
||||
RegenerateCollision(ev.Chunk);
|
||||
return;
|
||||
}
|
||||
|
||||
_queuedChunks.Add(ev.Chunk);
|
||||
}
|
||||
|
||||
private void RegenerateCollision(MapChunk chunk)
|
||||
{
|
||||
// Currently this is gonna be hella simple.
|
||||
if (!_mapManager.TryGetGrid(chunk.GridId, out var grid) ||
|
||||
!EntityManager.TryGetEntity(grid.GridEntityId, out var gridEnt) ||
|
||||
!gridEnt.TryGetComponent(out PhysicsComponent? physicsComponent)) return;
|
||||
|
||||
// TODO: Lots of stuff here etc etc, make changes to mapgridchunk.
|
||||
var bounds = chunk.CalcLocalBounds();
|
||||
|
||||
// So something goes on with the chunk's internal bounds caching where if there's no data the bound is 0 or something?
|
||||
if (bounds.IsEmpty()) return;
|
||||
|
||||
var origin = chunk.Indices * chunk.ChunkSize;
|
||||
bounds = bounds.Translated(origin);
|
||||
|
||||
var oldFixture = chunk.Fixture;
|
||||
|
||||
var newFixture = new Fixture(
|
||||
new PolygonShape
|
||||
{
|
||||
Vertices = new List<Vector2>
|
||||
{
|
||||
bounds.BottomRight,
|
||||
bounds.TopRight,
|
||||
bounds.TopLeft,
|
||||
bounds.BottomLeft,
|
||||
}
|
||||
},
|
||||
MapGridHelpers.CollisionGroup,
|
||||
MapGridHelpers.CollisionGroup,
|
||||
true) {ID = $"grid-{grid.Index}_chunk-{chunk.Indices.X}-{chunk.Indices.Y}",
|
||||
Body = physicsComponent};
|
||||
|
||||
// TODO: Chunk will likely need multiple fixtures but future sloth problem lmao fucking dickhead
|
||||
if (oldFixture?.Equals(newFixture) == true) return;
|
||||
|
||||
if (oldFixture != null)
|
||||
physicsComponent.RemoveFixture(oldFixture);
|
||||
|
||||
physicsComponent.AddFixture(newFixture);
|
||||
chunk.Fixture = newFixture;
|
||||
|
||||
EntityManager.EventBus.RaiseLocalEvent(gridEnt.Uid,new GridFixtureChangeEvent {OldFixture = oldFixture, NewFixture = newFixture});
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class GridFixtureChangeEvent : EntityEventArgs
|
||||
{
|
||||
public Fixture? OldFixture { get; init; }
|
||||
public Fixture? NewFixture { get; init; }
|
||||
}
|
||||
}
|
||||
@@ -316,7 +316,7 @@ namespace Robust.Shared.Maths
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static float Area(in Box2 box)
|
||||
public static float Area(in Box2 box)
|
||||
=> box.Width * box.Height;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
@@ -66,6 +66,11 @@ namespace Robust.Shared.Maths
|
||||
return xOk && yOk;
|
||||
}
|
||||
|
||||
public readonly bool IsEmpty()
|
||||
{
|
||||
return Bottom == Top || Left == Right;
|
||||
}
|
||||
|
||||
/// <summary>Returns a UIBox2 translated by the given amount.</summary>
|
||||
public readonly Box2i Translated(Vector2i point)
|
||||
{
|
||||
|
||||
@@ -226,6 +226,17 @@ namespace Robust.Shared
|
||||
public static readonly CVarDef<float> MaxLightRadius =
|
||||
CVarDef.Create("light.max_radius", 20.0f, CVar.CLIENTONLY);
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Like MaxLightRadius this is how far we enlarge lookups to find intersecting components.
|
||||
/// This should be set to your maximum entity size.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> LookupEnlargementRange =
|
||||
CVarDef.Create("lookup.enlargement_range", 10.0f, CVar.ARCHIVE | CVar.REPLICATED | CVar.CHEAT);
|
||||
|
||||
/*
|
||||
* LOKI
|
||||
*/
|
||||
@@ -451,6 +462,13 @@ namespace Robust.Shared
|
||||
public static readonly CVarDef<float> MaxAngVelocity =
|
||||
CVarDef.Create("physics.maxangvelocity", 15f);
|
||||
|
||||
/// <summary>
|
||||
/// How frequently grid fixtures are updated. Given grid updates can be expensive they aren't run immediately.
|
||||
/// Set to 0 to run them immediately.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> GridFixtureUpdateRate =
|
||||
CVarDef.Create("physics.grid_fixture_update_rate", 0.2f);
|
||||
|
||||
/*
|
||||
* DISCORD
|
||||
*/
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Robust.Shared.Configuration
|
||||
{
|
||||
// overwrite the value with the saved one
|
||||
cfgVar.Value = tomlValue;
|
||||
cfgVar.ValueChanged?.Invoke(cfgVar.Value);
|
||||
InvokeValueChanged(cfgVar, cfgVar.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -189,16 +189,13 @@ namespace Robust.Shared.Configuration
|
||||
public void RegisterCVar<T>(string name, T defaultValue, CVar flags = CVar.NONE, Action<T>? onValueChanged = null)
|
||||
where T : notnull
|
||||
{
|
||||
Action<object>? valueChangedDelegate = null;
|
||||
if (onValueChanged != null)
|
||||
{
|
||||
valueChangedDelegate = v => onValueChanged((T) v);
|
||||
}
|
||||
RegisterCVar(name, typeof(T), defaultValue, flags);
|
||||
|
||||
RegisterCVar(name, typeof(T), defaultValue, flags, valueChangedDelegate);
|
||||
if (onValueChanged != null)
|
||||
OnValueChanged(name, onValueChanged);
|
||||
}
|
||||
|
||||
private void RegisterCVar(string name, Type type, object defaultValue, CVar flags, Action<object>? onValueChanged)
|
||||
private void RegisterCVar(string name, Type type, object defaultValue, CVar flags)
|
||||
{
|
||||
DebugTools.Assert(!type.IsEnum || type.GetEnumUnderlyingType() == typeof(int),
|
||||
$"{name}: Enum cvars must have int as underlying type.");
|
||||
@@ -219,7 +216,6 @@ namespace Robust.Shared.Configuration
|
||||
cVar.DefaultValue = defaultValue;
|
||||
cVar.Flags = flags;
|
||||
cVar.Registered = true;
|
||||
cVar.ValueChanged = onValueChanged;
|
||||
|
||||
if (cVar.OverrideValue != null)
|
||||
{
|
||||
@@ -233,7 +229,6 @@ namespace Robust.Shared.Configuration
|
||||
{
|
||||
Registered = true,
|
||||
Value = defaultValue,
|
||||
ValueChanged = onValueChanged
|
||||
});
|
||||
}
|
||||
|
||||
@@ -247,7 +242,11 @@ namespace Robust.Shared.Configuration
|
||||
where T : notnull
|
||||
{
|
||||
var reg = _configVars[name];
|
||||
reg.ValueChanged += o => onValueChanged((T) o);
|
||||
var exDel = (Action<T>?) reg.ValueChanged;
|
||||
exDel += onValueChanged;
|
||||
reg.ValueChanged = exDel;
|
||||
|
||||
reg.ValueChangedInvoker ??= (del, v) => ((Action<T>) del)((T) v);
|
||||
|
||||
if (invokeImmediately)
|
||||
{
|
||||
@@ -255,6 +254,19 @@ namespace Robust.Shared.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
public void UnsubValueChanged<T>(CVarDef<T> cVar, Action<T> onValueChanged) where T : notnull
|
||||
{
|
||||
UnsubValueChanged(cVar.Name, onValueChanged);
|
||||
}
|
||||
|
||||
public void UnsubValueChanged<T>(string name, Action<T> onValueChanged) where T : notnull
|
||||
{
|
||||
var reg = _configVars[name];
|
||||
var exDel = (Action<T>?) reg.ValueChanged;
|
||||
exDel -= onValueChanged;
|
||||
reg.ValueChanged = exDel;
|
||||
}
|
||||
|
||||
public void LoadCVarsFromAssembly(Assembly assembly)
|
||||
{
|
||||
foreach (var defField in assembly
|
||||
@@ -282,7 +294,7 @@ namespace Robust.Shared.Configuration
|
||||
throw new InvalidOperationException($"CVarDef '{defField.Name}' on '{defField.DeclaringType?.FullName}' is null.");
|
||||
}
|
||||
|
||||
RegisterCVar(def.Name, type, def.DefaultValue, def.Flags, null);
|
||||
RegisterCVar(def.Name, type, def.DefaultValue, def.Flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,7 +328,7 @@ namespace Robust.Shared.Configuration
|
||||
cVar.OverrideValueParsed = null;
|
||||
|
||||
cVar.Value = value;
|
||||
cVar.ValueChanged?.Invoke(value);
|
||||
InvokeValueChanged(cVar, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -362,7 +374,7 @@ namespace Robust.Shared.Configuration
|
||||
{
|
||||
cfgVar.OverrideValue = value;
|
||||
cfgVar.OverrideValueParsed = ParseOverrideValue(value, cfgVar.DefaultValue?.GetType());
|
||||
cfgVar.ValueChanged?.Invoke(cfgVar.OverrideValueParsed);
|
||||
InvokeValueChanged(cfgVar, cfgVar.OverrideValueParsed);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -422,6 +434,11 @@ namespace Robust.Shared.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
private static void InvokeValueChanged(ConfigVar var, object value)
|
||||
{
|
||||
var.ValueChangedInvoker?.Invoke(var.ValueChanged!, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds the data for a single configuration variable.
|
||||
/// </summary>
|
||||
@@ -476,7 +493,9 @@ namespace Robust.Shared.Configuration
|
||||
/// <summary>
|
||||
/// Invoked when the value of this CVar is changed.
|
||||
/// </summary>
|
||||
public Action<object>? ValueChanged { get; set; }
|
||||
public Delegate? ValueChanged { get; set; }
|
||||
|
||||
public Action<Delegate, object>? ValueChangedInvoker { get; set; }
|
||||
|
||||
// We don't know what the type of the var is until it's registered.
|
||||
// So we can't actually parse them until then.
|
||||
|
||||
@@ -63,10 +63,48 @@ namespace Robust.Shared.Configuration
|
||||
/// <param name="name">The name of the CVar</param>
|
||||
Type GetCVarType(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Listen for an event for if the config value changes.
|
||||
/// </summary>
|
||||
/// <param name="cVar">The CVar to listen for.</param>
|
||||
/// <param name="onValueChanged">The delegate to run when the value was changed.</param>
|
||||
/// <param name="invokeImmediately">
|
||||
/// Whether to run the callback immediately in this method. Can help reduce boilerplate
|
||||
/// </param>
|
||||
/// <typeparam name="T">The type of value contained in this CVar.</typeparam>
|
||||
/// <seealso cref="UnsubValueChanged{T}(Robust.Shared.Configuration.CVarDef{T},System.Action{T})"/>
|
||||
void OnValueChanged<T>(CVarDef<T> cVar, Action<T> onValueChanged, bool invokeImmediately = false)
|
||||
where T : notnull;
|
||||
|
||||
/// <summary>
|
||||
/// Listen for an event for if the config value changes.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the CVar to listen for.</param>
|
||||
/// <param name="onValueChanged">The delegate to run when the value was changed.</param>
|
||||
/// <param name="invokeImmediately">
|
||||
/// Whether to run the callback immediately in this method. Can help reduce boilerplate
|
||||
/// </param>
|
||||
/// <typeparam name="T">The type of value contained in this CVar.</typeparam>
|
||||
/// <seealso cref="UnsubValueChanged{T}(string,System.Action{T})"/>
|
||||
void OnValueChanged<T>(string name, Action<T> onValueChanged, bool invokeImmediately = false)
|
||||
where T : notnull;
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe an event previously registered with <see cref="OnValueChanged{T}(Robust.Shared.Configuration.CVarDef{T},System.Action{T},bool)"/>.
|
||||
/// </summary>
|
||||
/// <param name="cVar">The CVar to unsubscribe from.</param>
|
||||
/// <param name="onValueChanged">The delegate to unsubscribe.</param>
|
||||
/// <typeparam name="T">The type of value contained in this CVar.</typeparam>
|
||||
void UnsubValueChanged<T>(CVarDef<T> cVar, Action<T> onValueChanged)
|
||||
where T : notnull;
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe an event previously registered with <see cref="OnValueChanged{T}(string,System.Action{T},bool)"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the CVar to unsubscribe from.</param>
|
||||
/// <param name="onValueChanged">The delegate to unsubscribe.</param>
|
||||
/// <typeparam name="T">The type of value contained in this CVar.</typeparam>
|
||||
void UnsubValueChanged<T>(string name, Action<T> onValueChanged)
|
||||
where T : notnull;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ namespace Robust.Shared.Configuration
|
||||
|
||||
private void HandleNetVarMessage(MsgConVars message)
|
||||
{
|
||||
if(!_receivedInitialNwVars)
|
||||
if (_netManager.IsClient && !_receivedInitialNwVars)
|
||||
{
|
||||
_receivedInitialNwVars = true;
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace Robust.Shared.Configuration
|
||||
|
||||
ApplyNetVarChange(msg.MsgChannel, msg.NetworkedVars);
|
||||
|
||||
if(msg.Tick < _timing.LastRealTick)
|
||||
if(msg.Tick != default && msg.Tick < _timing.LastRealTick)
|
||||
Logger.WarningS("cfg", $"{msg.MsgChannel}: Received late nwVar message ({msg.Tick} < {_timing.LastRealTick} ).");
|
||||
|
||||
_netVarsMessages.RemoveSwap(i);
|
||||
@@ -148,45 +148,50 @@ namespace Robust.Shared.Configuration
|
||||
|
||||
private void ApplyNetVarChange(INetChannel msgChannel, List<(string name, object value)> networkedVars)
|
||||
{
|
||||
Logger.DebugS("cfg", "Handling replicated cvars...");
|
||||
Logger.DebugS("cfg", $"{msgChannel} Handling replicated cvars...");
|
||||
|
||||
foreach (var (name, value) in networkedVars)
|
||||
if (_netManager.IsClient)
|
||||
{
|
||||
if (_netManager.IsClient) // Server sent us a CVar update.
|
||||
// Server sent us a CVar update.
|
||||
foreach (var (name, value) in networkedVars)
|
||||
{
|
||||
// Actually set the CVar
|
||||
base.SetCVar(name, value);
|
||||
Logger.DebugS("cfg", $"name={name}, val={value}");
|
||||
}
|
||||
else // Client sent us a CVar update
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Client sent us a CVar update
|
||||
if (!_replicatedCVars.TryGetValue(msgChannel, out var clientCVars))
|
||||
{
|
||||
Logger.WarningS("cfg", $"{msgChannel} tried to replicate CVars but is not in _replicatedCVars.");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (name, value) in networkedVars)
|
||||
{
|
||||
if (!_configVars.TryGetValue(name, out var cVar))
|
||||
{
|
||||
if (!_configVars.TryGetValue(name, out var cVar))
|
||||
{
|
||||
Logger.WarningS("cfg", $"{msgChannel} tried to replicate an unknown CVar '{name}.'");
|
||||
continue;
|
||||
}
|
||||
Logger.WarningS("cfg", $"{msgChannel} tried to replicate an unknown CVar '{name}.'");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cVar.Registered)
|
||||
{
|
||||
Logger.WarningS("cfg", $"{msgChannel} tried to replicate an unregistered CVar '{name}.'");
|
||||
continue;
|
||||
}
|
||||
if (!cVar.Registered)
|
||||
{
|
||||
Logger.WarningS("cfg", $"{msgChannel} tried to replicate an unregistered CVar '{name}.'");
|
||||
continue;
|
||||
}
|
||||
|
||||
if((cVar.Flags & CVar.REPLICATED) != 0)
|
||||
{
|
||||
var clientCVars = _replicatedCVars[msgChannel];
|
||||
|
||||
if (clientCVars.ContainsKey(name))
|
||||
clientCVars[name] = value;
|
||||
else
|
||||
clientCVars.Add(name, value);
|
||||
|
||||
Logger.DebugS("cfg", $"name={name}, val={value}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.WarningS("cfg", $"{msgChannel} tried to replicate an un-replicated CVar '{name}.'");
|
||||
}
|
||||
if((cVar.Flags & CVar.REPLICATED) != 0)
|
||||
{
|
||||
clientCVars[name] = value;
|
||||
Logger.DebugS("cfg", $"name={name}, val={value}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.WarningS("cfg", $"{msgChannel} tried to replicate an un-replicated CVar '{name}.'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -290,7 +295,7 @@ namespace Robust.Shared.Configuration
|
||||
Logger.InfoS("cfg", "Sending client info...");
|
||||
|
||||
var msg = _netManager.CreateNetMessage<MsgConVars>();
|
||||
msg.Tick = _timing.CurTick;
|
||||
msg.Tick = default;
|
||||
msg.NetworkedVars = GetReplicatedVars();
|
||||
_netManager.ClientSendMessage(msg);
|
||||
}
|
||||
|
||||
@@ -871,6 +871,7 @@ Types:
|
||||
IEquatable`1: { }
|
||||
IFormatProvider: { All: True }
|
||||
IFormattable: { All: True }
|
||||
Index: { All: True }
|
||||
IndexOutOfRangeException: { All: True }
|
||||
Int16: { All: True }
|
||||
Int32: { All: True }
|
||||
@@ -912,6 +913,7 @@ Types:
|
||||
ParamArrayAttribute: { All: True }
|
||||
Predicate`1: { All: True } # Delegate
|
||||
Random: { All: True }
|
||||
Range: { All: True }
|
||||
ReadOnlyMemory`1:
|
||||
Methods:
|
||||
- "!0[] ToArray()"
|
||||
|
||||
@@ -18,18 +18,6 @@ namespace Robust.Shared.GameObjects
|
||||
void CollideWith(Fixture ourFixture, Fixture otherFixture, in Manifold manifold);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called once when a collision ends.
|
||||
/// </summary>
|
||||
public interface IEndCollide
|
||||
{
|
||||
/// <summary>
|
||||
/// Run behaviour after all other collision behaviors have run.
|
||||
/// </summary>
|
||||
[Obsolete("Use EndCollideEvent instead")]
|
||||
void CollideWith(Fixture ourFixture, Fixture otherFixture, in Manifold manifold);
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum BodyStatus: byte
|
||||
{
|
||||
|
||||
@@ -73,6 +73,23 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public bool IgnoreCCD { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
public int ContactCount
|
||||
{
|
||||
get
|
||||
{
|
||||
var count = 0;
|
||||
var edge = ContactEdges;
|
||||
while (edge != null)
|
||||
{
|
||||
edge = edge.Next;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Linked-list of all of our contacts.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class EntityLookupComponent : Component
|
||||
{
|
||||
public override string Name => "EntityLookup";
|
||||
|
||||
internal DynamicTree<IEntity> Tree = default!;
|
||||
}
|
||||
}
|
||||
@@ -400,7 +400,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
public void ClearEventTables()
|
||||
{
|
||||
_eventTables.ClearEntities();
|
||||
_eventTables.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -11,46 +11,74 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
protected void SubscribeNetworkEvent<T>(
|
||||
EntityEventHandler<T> handler,
|
||||
Type[]? before=null, Type[]? after=null)
|
||||
Type[]? before = null, Type[]? after = null)
|
||||
where T : notnull
|
||||
{
|
||||
EntityManager.EventBus.SubscribeEvent(EventSource.Network, this, handler, GetType(), before, after);
|
||||
|
||||
_subscriptions ??= new();
|
||||
_subscriptions.Add(new SubBroadcast<T>(EventSource.Network));
|
||||
}
|
||||
|
||||
protected void SubscribeNetworkEvent<T>(
|
||||
EntitySessionEventHandler<T> handler,
|
||||
Type[]? before=null, Type[]? after=null)
|
||||
where T : notnull
|
||||
{
|
||||
EntityManager.EventBus.SubscribeSessionEvent(EventSource.Network, this, handler);
|
||||
|
||||
_subscriptions ??= new();
|
||||
_subscriptions.Add(new SubBroadcast<EntitySessionMessage<T>>(EventSource.Network));
|
||||
SubEvent(EventSource.Network, handler, before, after);
|
||||
}
|
||||
|
||||
protected void SubscribeLocalEvent<T>(
|
||||
EntityEventHandler<T> handler,
|
||||
Type[]? before=null, Type[]? after=null)
|
||||
Type[]? before = null, Type[]? after = null)
|
||||
where T : notnull
|
||||
{
|
||||
EntityManager.EventBus.SubscribeEvent(EventSource.Local, this, handler, GetType(), before, after);
|
||||
SubEvent(EventSource.Local, handler, before, after);
|
||||
}
|
||||
|
||||
_subscriptions ??= new();
|
||||
_subscriptions.Add(new SubBroadcast<T>(EventSource.Local));
|
||||
protected void SubscribeAllEvent<T>(
|
||||
EntityEventHandler<T> handler,
|
||||
Type[]? before = null, Type[]? after = null)
|
||||
where T : notnull
|
||||
{
|
||||
SubEvent(EventSource.All, handler, before, after);
|
||||
}
|
||||
|
||||
protected void SubscribeNetworkEvent<T>(
|
||||
EntitySessionEventHandler<T> handler,
|
||||
Type[]? before = null, Type[]? after = null)
|
||||
where T : notnull
|
||||
{
|
||||
SubSessionEvent(EventSource.Network, handler, before, after);
|
||||
}
|
||||
|
||||
protected void SubscribeLocalEvent<T>(
|
||||
EntitySessionEventHandler<T> handler,
|
||||
Type[]? before=null, Type[]? after=null)
|
||||
Type[]? before = null, Type[]? after = null)
|
||||
where T : notnull
|
||||
{
|
||||
EntityManager.EventBus.SubscribeSessionEvent(EventSource.Local, this, handler);
|
||||
SubSessionEvent(EventSource.Local, handler, before, after);
|
||||
}
|
||||
|
||||
protected void SubscribeAllEvent<T>(
|
||||
EntitySessionEventHandler<T> handler,
|
||||
Type[]? before = null, Type[]? after = null)
|
||||
where T : notnull
|
||||
{
|
||||
SubSessionEvent(EventSource.All, handler, before, after);
|
||||
}
|
||||
|
||||
private void SubEvent<T>(
|
||||
EventSource src,
|
||||
EntityEventHandler<T> handler,
|
||||
Type[]? before, Type[]? after)
|
||||
where T : notnull
|
||||
{
|
||||
EntityManager.EventBus.SubscribeEvent(src, this, handler, GetType(), before, after);
|
||||
|
||||
_subscriptions ??= new();
|
||||
_subscriptions.Add(new SubBroadcast<EntitySessionMessage<T>>(EventSource.Local));
|
||||
_subscriptions.Add(new SubBroadcast<T>(src));
|
||||
}
|
||||
|
||||
private void SubSessionEvent<T>(
|
||||
EventSource src,
|
||||
EntitySessionEventHandler<T> handler,
|
||||
Type[]? before, Type[]? after)
|
||||
where T : notnull
|
||||
{
|
||||
EntityManager.EventBus.SubscribeSessionEvent(src, this, handler, GetType(), before, after);
|
||||
|
||||
_subscriptions ??= new();
|
||||
_subscriptions.Add(new SubBroadcast<EntitySessionMessage<T>>(src));
|
||||
}
|
||||
|
||||
[Obsolete("Unsubscribing of entity system events is now automatic")]
|
||||
@@ -69,7 +97,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
protected void SubscribeLocalEvent<TComp, TEvent>(
|
||||
ComponentEventHandler<TComp, TEvent> handler,
|
||||
Type[]? before=null, Type[]? after=null)
|
||||
Type[]? before = null, Type[]? after = null)
|
||||
where TComp : IComponent
|
||||
where TEvent : EntityEventArgs
|
||||
{
|
||||
|
||||
@@ -1,14 +1,37 @@
|
||||
using System;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public static class EventBusExt
|
||||
{
|
||||
public static void SubscribeSessionEvent<T>(this IEventBus eventBus, EventSource source,
|
||||
IEntityEventSubscriber subscriber, EntitySessionEventHandler<T> handler)
|
||||
public static void SubscribeSessionEvent<T>(
|
||||
this IEventBus eventBus,
|
||||
EventSource source,
|
||||
IEntityEventSubscriber subscriber,
|
||||
EntitySessionEventHandler<T> handler)
|
||||
{
|
||||
var wrapper = new HandlerWrapper<T>(handler);
|
||||
eventBus.SubscribeEvent<EntitySessionMessage<T>>(source, subscriber, wrapper.Invoke);
|
||||
}
|
||||
|
||||
public static void SubscribeSessionEvent<T>(
|
||||
this IEventBus eventBus,
|
||||
EventSource source,
|
||||
IEntityEventSubscriber subscriber,
|
||||
EntitySessionEventHandler<T> handler,
|
||||
Type orderType,
|
||||
Type[]? before=null,
|
||||
Type[]? after=null)
|
||||
{
|
||||
var wrapper = new HandlerWrapper<T>(handler);
|
||||
eventBus.SubscribeEvent<EntitySessionMessage<T>>(
|
||||
source,
|
||||
subscriber,
|
||||
wrapper.Invoke,
|
||||
orderType,
|
||||
before, after);
|
||||
}
|
||||
|
||||
private sealed class HandlerWrapper<T>
|
||||
{
|
||||
public HandlerWrapper(EntitySessionEventHandler<T> handler)
|
||||
|
||||
@@ -3,6 +3,8 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
@@ -66,15 +68,24 @@ namespace Robust.Shared.GameObjects
|
||||
[UsedImplicitly]
|
||||
public class EntityLookup : IEntityLookup, IEntityEventSubscriber
|
||||
{
|
||||
private readonly IComponentManager _compManager;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly IMapManager _mapManager;
|
||||
|
||||
private readonly Dictionary<MapId, DynamicTree<IEntity>> _entityTreesPerMap = new();
|
||||
private const int GrowthRate = 256;
|
||||
|
||||
private const float PointEnlargeRange = .00001f / 2;
|
||||
|
||||
// Using stacks so we always use latest data (given we only run it once per entity).
|
||||
private readonly Stack<MoveEvent> _moveQueue = new();
|
||||
private readonly Stack<RotateEvent> _rotateQueue = new();
|
||||
private readonly Queue<EntMapIdChangedMessage> _mapChangeQueue = new();
|
||||
private readonly Queue<EntParentChangedMessage> _parentChangeQueue = new();
|
||||
|
||||
/// <summary>
|
||||
/// Like RenderTree we need to enlarge our lookup range for EntityLookupComponent as an entity is only ever on
|
||||
/// 1 EntityLookupComponent at a time (hence it may overlap without another lookup).
|
||||
/// </summary>
|
||||
private float _lookupEnlargementRange;
|
||||
|
||||
/// <summary>
|
||||
/// Move and rotate events generate the same update so no point duplicating work in the same tick.
|
||||
@@ -83,10 +94,17 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
// TODO: Should combine all of the methods that check for IPhysBody and just use the one GetWorldAabbFromEntity method
|
||||
|
||||
// TODO: Combine GridTileLookupSystem and entity anchoring together someday.
|
||||
// Queries are a bit of spaghet rn but ideally you'd just have:
|
||||
// A) The fast tile-based one
|
||||
// B) The physics-only one (given physics needs it to be fast af)
|
||||
// C) A generic use one that covers anything not caught in the above.
|
||||
|
||||
public bool Started { get; private set; } = false;
|
||||
|
||||
public EntityLookup(IEntityManager entityManager, IMapManager mapManager)
|
||||
public EntityLookup(IComponentManager compManager, IEntityManager entityManager, IMapManager mapManager)
|
||||
{
|
||||
_compManager = compManager;
|
||||
_entityManager = entityManager;
|
||||
_mapManager = mapManager;
|
||||
}
|
||||
@@ -98,14 +116,21 @@ namespace Robust.Shared.GameObjects
|
||||
throw new InvalidOperationException("Startup() called multiple times.");
|
||||
}
|
||||
|
||||
var configManager = IoCManager.Resolve<IConfigurationManager>();
|
||||
configManager.OnValueChanged(CVars.LookupEnlargementRange, value => _lookupEnlargementRange = value, true);
|
||||
|
||||
var eventBus = _entityManager.EventBus;
|
||||
eventBus.SubscribeEvent<MoveEvent>(EventSource.Local, this, ev => _moveQueue.Push(ev));
|
||||
eventBus.SubscribeEvent<RotateEvent>(EventSource.Local, this, ev => _rotateQueue.Push(ev));
|
||||
eventBus.SubscribeEvent<EntMapIdChangedMessage>(EventSource.Local, this, ev => _mapChangeQueue.Enqueue(ev));
|
||||
eventBus.SubscribeEvent<EntParentChangedMessage>(EventSource.Local, this, ev => _parentChangeQueue.Enqueue(ev));
|
||||
|
||||
eventBus.SubscribeLocalEvent<EntityLookupComponent, ComponentInit>(HandleLookupInit);
|
||||
eventBus.SubscribeLocalEvent<EntityLookupComponent, ComponentShutdown>(HandleLookupShutdown);
|
||||
eventBus.SubscribeEvent<GridInitializeEvent>(EventSource.Local, this, HandleGridInit);
|
||||
|
||||
_entityManager.EntityDeleted += HandleEntityDeleted;
|
||||
_entityManager.EntityStarted += HandleEntityStarted;
|
||||
_mapManager.MapCreated += HandleMapCreated;
|
||||
_mapManager.MapDestroyed += HandleMapDestroyed;
|
||||
Started = true;
|
||||
}
|
||||
|
||||
@@ -118,17 +143,43 @@ namespace Robust.Shared.GameObjects
|
||||
_moveQueue.Clear();
|
||||
_rotateQueue.Clear();
|
||||
_handledThisTick.Clear();
|
||||
_mapChangeQueue.Clear();
|
||||
_entityTreesPerMap.Clear();
|
||||
_parentChangeQueue.Clear();
|
||||
|
||||
_entityManager.EventBus.UnsubscribeEvents(this);
|
||||
_entityManager.EntityDeleted -= HandleEntityDeleted;
|
||||
_entityManager.EntityStarted -= HandleEntityStarted;
|
||||
_mapManager.MapCreated -= HandleMapCreated;
|
||||
_mapManager.MapDestroyed -= HandleMapDestroyed;
|
||||
Started = false;
|
||||
}
|
||||
|
||||
private void HandleLookupShutdown(EntityUid uid, EntityLookupComponent component, ComponentShutdown args)
|
||||
{
|
||||
component.Tree.Clear();
|
||||
}
|
||||
|
||||
private void HandleGridInit(GridInitializeEvent ev)
|
||||
{
|
||||
_entityManager.GetEntity(ev.EntityUid).EnsureComponent<EntityLookupComponent>();
|
||||
}
|
||||
|
||||
private void HandleLookupInit(EntityUid uid, EntityLookupComponent component, ComponentInit args)
|
||||
{
|
||||
var capacity = (int) Math.Min(256, Math.Ceiling(component.Owner.Transform.ChildCount / (float) GrowthRate) * GrowthRate);
|
||||
|
||||
component.Tree = new DynamicTree<IEntity>(
|
||||
GetRelativeAABBFromEntity,
|
||||
capacity: capacity,
|
||||
growthFunc: x => x == GrowthRate ? GrowthRate * 8 : x + GrowthRate
|
||||
);
|
||||
}
|
||||
|
||||
private static Box2 GetRelativeAABBFromEntity(in IEntity entity)
|
||||
{
|
||||
var aabb = GetWorldAABB(entity);
|
||||
var tree = GetLookup(entity);
|
||||
|
||||
return aabb.Translated(-tree?.Owner.Transform.WorldPosition ?? Vector2.Zero);
|
||||
}
|
||||
|
||||
private void HandleEntityDeleted(object? sender, EntityUid uid)
|
||||
{
|
||||
RemoveFromEntityTrees(_entityManager.GetEntity(uid));
|
||||
@@ -141,63 +192,78 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
private void HandleMapCreated(object? sender, MapEventArgs eventArgs)
|
||||
{
|
||||
_entityTreesPerMap[eventArgs.Map] = new DynamicTree<IEntity>(
|
||||
GetWorldAabbFromEntity,
|
||||
capacity: 16,
|
||||
growthFunc: x => x == 16 ? 3840 : x + 256
|
||||
);
|
||||
}
|
||||
if (eventArgs.Map == MapId.Nullspace) return;
|
||||
|
||||
private void HandleMapDestroyed(object? sender, MapEventArgs eventArgs)
|
||||
{
|
||||
_entityTreesPerMap.Remove(eventArgs.Map);
|
||||
_mapManager.GetMapEntity(eventArgs.Map).EnsureComponent<EntityLookupComponent>();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Acruid said he'd deal with Update being called around IEntityManager later.
|
||||
_handledThisTick.Clear();
|
||||
|
||||
while (_mapChangeQueue.TryDequeue(out var mapChangeEvent))
|
||||
// Could be more efficient but essentially nuke their old lookup and add to new lookup if applicable.
|
||||
while (_parentChangeQueue.TryDequeue(out var mapChangeEvent))
|
||||
{
|
||||
if (mapChangeEvent.Entity.Deleted) continue;
|
||||
RemoveFromEntityTree(mapChangeEvent.Entity, mapChangeEvent.OldMapId);
|
||||
UpdateEntityTree(mapChangeEvent.Entity, GetWorldAabbFromEntity(mapChangeEvent.Entity));
|
||||
_handledThisTick.Add(mapChangeEvent.Entity.Uid);
|
||||
RemoveFromEntityTrees(mapChangeEvent.Entity);
|
||||
|
||||
if (mapChangeEvent.Entity.Deleted) continue;
|
||||
UpdateEntityTree(mapChangeEvent.Entity, GetWorldAabbFromEntity(mapChangeEvent.Entity));
|
||||
}
|
||||
|
||||
while (_moveQueue.TryPop(out var moveEvent))
|
||||
{
|
||||
if (moveEvent.Sender.Deleted || _handledThisTick.Contains(moveEvent.Sender.Uid)) continue;
|
||||
if (moveEvent.Sender.Deleted || !_handledThisTick.Add(moveEvent.Sender.Uid)) continue;
|
||||
|
||||
UpdateEntityTree(moveEvent.Sender, moveEvent.WorldAABB);
|
||||
_handledThisTick.Add(moveEvent.Sender.Uid);
|
||||
}
|
||||
|
||||
while (_rotateQueue.TryPop(out var rotateEvent))
|
||||
{
|
||||
if (rotateEvent.Sender.Deleted || _handledThisTick.Contains(rotateEvent.Sender.Uid)) continue;
|
||||
if (rotateEvent.Sender.Deleted || !_handledThisTick.Add(rotateEvent.Sender.Uid)) continue;
|
||||
|
||||
UpdateEntityTree(rotateEvent.Sender, rotateEvent.WorldAABB);
|
||||
}
|
||||
|
||||
_handledThisTick.Clear();
|
||||
}
|
||||
|
||||
#region Spatial Queries
|
||||
|
||||
private IEnumerable<EntityLookupComponent> GetLookupsIntersecting(MapId mapId, Box2 worldAABB)
|
||||
{
|
||||
if (mapId == MapId.Nullspace) yield break;
|
||||
|
||||
// TODO: Recursive and all that.
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldAABB.Enlarged(_lookupEnlargementRange)))
|
||||
{
|
||||
yield return _entityManager.GetEntity(grid.GridEntityId).GetComponent<EntityLookupComponent>();
|
||||
}
|
||||
|
||||
yield return _mapManager.GetMapEntity(mapId).GetComponent<EntityLookupComponent>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool AnyEntitiesIntersecting(MapId mapId, Box2 box, bool approximate = false)
|
||||
{
|
||||
var found = false;
|
||||
_entityTreesPerMap[mapId].QueryAabb(ref found, (ref bool found, in IEntity ent) =>
|
||||
{
|
||||
if (!ent.Deleted)
|
||||
{
|
||||
found = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}, box, approximate);
|
||||
foreach (var lookup in GetLookupsIntersecting(mapId, box))
|
||||
{
|
||||
var offsetBox = box.Translated(-lookup.Owner.Transform.WorldPosition);
|
||||
|
||||
lookup.Tree.QueryAabb(ref found, (ref bool found, in IEntity ent) =>
|
||||
{
|
||||
if (!ent.Deleted)
|
||||
{
|
||||
found = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}, offsetBox, approximate);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
@@ -205,31 +271,34 @@ namespace Robust.Shared.GameObjects
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
public void FastEntitiesIntersecting(in MapId mapId, ref Box2 position, EntityQueryCallback callback)
|
||||
{
|
||||
if (mapId == MapId.Nullspace)
|
||||
return;
|
||||
foreach (var lookup in GetLookupsIntersecting(mapId, position))
|
||||
{
|
||||
var offsetBox = position.Translated(-lookup.Owner.Transform.WorldPosition);
|
||||
|
||||
_entityTreesPerMap[mapId]._b2Tree
|
||||
.FastQuery(ref position, (ref IEntity data) => callback(data));
|
||||
lookup.Tree._b2Tree.FastQuery(ref offsetBox, (ref IEntity data) => callback(data));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IEntity> GetEntitiesIntersecting(MapId mapId, Box2 position, bool approximate = false)
|
||||
{
|
||||
if (!_entityTreesPerMap.TryGetValue(mapId, out var mapTree))
|
||||
{
|
||||
return Enumerable.Empty<IEntity>();
|
||||
}
|
||||
if (mapId == MapId.Nullspace) return Enumerable.Empty<IEntity>();
|
||||
|
||||
var list = new List<IEntity>();
|
||||
|
||||
mapTree.QueryAabb(ref list, (ref List<IEntity> list, in IEntity ent) =>
|
||||
foreach (var lookup in GetLookupsIntersecting(mapId, position))
|
||||
{
|
||||
if (!ent.Deleted)
|
||||
var offsetBox = position.Translated(-lookup.Owner.Transform.WorldPosition);
|
||||
|
||||
lookup.Tree.QueryAabb(ref list, (ref List<IEntity> list, in IEntity ent) =>
|
||||
{
|
||||
list.Add(ent);
|
||||
}
|
||||
return true;
|
||||
}, position, approximate);
|
||||
if (!ent.Deleted)
|
||||
{
|
||||
list.Add(ent);
|
||||
}
|
||||
return true;
|
||||
}, offsetBox, approximate);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
@@ -237,25 +306,25 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IEntity> GetEntitiesIntersecting(MapId mapId, Vector2 position, bool approximate = false)
|
||||
{
|
||||
const float range = .00001f / 2;
|
||||
var aabb = new Box2(position, position).Enlarged(range);
|
||||
|
||||
if (mapId == MapId.Nullspace)
|
||||
{
|
||||
return Enumerable.Empty<IEntity>();
|
||||
}
|
||||
if (mapId == MapId.Nullspace) return Enumerable.Empty<IEntity>();
|
||||
|
||||
var aabb = new Box2(position, position).Enlarged(PointEnlargeRange);
|
||||
var list = new List<IEntity>();
|
||||
var state = (list, position);
|
||||
|
||||
_entityTreesPerMap[mapId].QueryAabb(ref state, (ref (List<IEntity> list, Vector2 position) state, in IEntity ent) =>
|
||||
foreach (var lookup in GetLookupsIntersecting(mapId, aabb))
|
||||
{
|
||||
if (Intersecting(ent, state.position))
|
||||
var offsetBox = aabb.Translated(-lookup.Owner.Transform.WorldPosition);
|
||||
|
||||
lookup.Tree.QueryAabb(ref state, (ref (List<IEntity> list, Vector2 position) state, in IEntity ent) =>
|
||||
{
|
||||
state.list.Add(ent);
|
||||
}
|
||||
return true;
|
||||
}, aabb, approximate);
|
||||
if (Intersecting(ent, state.position))
|
||||
{
|
||||
state.list.Add(ent);
|
||||
}
|
||||
return true;
|
||||
}, offsetBox, approximate);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
@@ -365,34 +434,44 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IEntity> GetEntitiesInMap(MapId mapId)
|
||||
{
|
||||
if (!_entityTreesPerMap.TryGetValue(mapId, out var trees))
|
||||
yield break;
|
||||
|
||||
foreach (var entity in trees)
|
||||
foreach (EntityLookupComponent comp in _compManager.EntityQuery<EntityLookupComponent>(true))
|
||||
{
|
||||
if (!entity.Deleted)
|
||||
foreach (var entity in comp.Tree)
|
||||
{
|
||||
if (entity.Deleted) continue;
|
||||
|
||||
yield return entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IEntity> GetEntitiesAt(MapId mapId, Vector2 position, bool approximate = false)
|
||||
{
|
||||
if (mapId == MapId.Nullspace) return Enumerable.Empty<IEntity>();
|
||||
|
||||
var list = new List<IEntity>();
|
||||
|
||||
var state = (list, position);
|
||||
|
||||
_entityTreesPerMap[mapId].QueryPoint(ref state, (ref (List<IEntity> list, Vector2 position) state, in IEntity ent) =>
|
||||
{
|
||||
var transform = ent.Transform;
|
||||
if (MathHelper.CloseTo(transform.Coordinates.X, state.position.X) &&
|
||||
MathHelper.CloseTo(transform.Coordinates.Y, state.position.Y))
|
||||
{
|
||||
state.list.Add(ent);
|
||||
}
|
||||
var aabb = new Box2(position, position).Enlarged(PointEnlargeRange);
|
||||
|
||||
return true;
|
||||
}, position, approximate);
|
||||
foreach (var lookup in GetLookupsIntersecting(mapId, aabb))
|
||||
{
|
||||
var offsetPos = position -lookup.Owner.Transform.WorldPosition;
|
||||
|
||||
lookup.Tree.QueryPoint(ref state, (ref (List<IEntity> list, Vector2 position) state, in IEntity ent) =>
|
||||
{
|
||||
var transform = ent.Transform;
|
||||
if (MathHelper.CloseTo(transform.Coordinates.X, state.position.X) &&
|
||||
MathHelper.CloseTo(transform.Coordinates.Y, state.position.Y))
|
||||
{
|
||||
state.list.Add(ent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}, offsetPos, approximate);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
@@ -400,88 +479,119 @@ namespace Robust.Shared.GameObjects
|
||||
#endregion
|
||||
|
||||
#region Entity DynamicTree
|
||||
|
||||
private static EntityLookupComponent? GetLookup(IEntity entity)
|
||||
{
|
||||
if (entity.Transform.MapID == MapId.Nullspace)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// if it's map return null. Grids should return the map's broadphase.
|
||||
if (entity.HasComponent<EntityLookupComponent>() &&
|
||||
entity.Transform.Parent == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var parent = entity.Transform.Parent?.Owner;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (parent == null) break;
|
||||
|
||||
if (parent.TryGetComponent(out EntityLookupComponent? comp)) return comp;
|
||||
parent = parent.Transform.Parent?.Owner;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual bool UpdateEntityTree(IEntity entity, Box2? worldAABB = null)
|
||||
{
|
||||
// look there's JANK everywhere but I'm just bandaiding it for now for shuttles and we'll fix it later when
|
||||
// PVS is more stable and entity anchoring has been battle-tested.
|
||||
if (entity.Deleted)
|
||||
{
|
||||
RemoveFromEntityTrees(entity);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!entity.Initialized || !_entityManager.EntityExists(entity.Uid))
|
||||
if (!entity.Initialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var transform = entity.Transform;
|
||||
var lookup = GetLookup(entity);
|
||||
|
||||
if (lookup == null)
|
||||
{
|
||||
RemoveFromEntityTrees(entity);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Temp PVS guard for when we clear dynamictree for now.
|
||||
worldAABB ??= GetWorldAabbFromEntity(entity);
|
||||
var center = worldAABB.Value.Center;
|
||||
|
||||
if (float.IsNaN(center.X) || float.IsNaN(center.Y))
|
||||
{
|
||||
RemoveFromEntityTrees(entity);
|
||||
return true;
|
||||
}
|
||||
|
||||
var transform = entity.Transform;
|
||||
DebugTools.Assert(transform.Initialized);
|
||||
|
||||
var mapId = transform.MapID;
|
||||
|
||||
if (!_entityTreesPerMap.TryGetValue(mapId, out var entTree))
|
||||
{
|
||||
entTree = new DynamicTree<IEntity>(
|
||||
GetWorldAabbFromEntity,
|
||||
capacity: 16,
|
||||
growthFunc: x => x == 16 ? 3840 : x + 256
|
||||
);
|
||||
_entityTreesPerMap.Add(mapId, entTree);
|
||||
}
|
||||
var aabb = worldAABB.Value.Translated(-lookup.Owner.Transform.WorldPosition);
|
||||
|
||||
// for debugging
|
||||
var necessary = 0;
|
||||
|
||||
if (entTree.AddOrUpdate(entity, worldAABB))
|
||||
if (lookup.Tree.AddOrUpdate(entity, aabb))
|
||||
{
|
||||
++necessary;
|
||||
}
|
||||
|
||||
foreach (var childTx in entity.Transform.ChildEntityUids)
|
||||
if (!entity.HasComponent<EntityLookupComponent>())
|
||||
{
|
||||
if (UpdateEntityTree(_entityManager.GetEntity(childTx)))
|
||||
foreach (var childTx in entity.Transform.ChildEntityUids)
|
||||
{
|
||||
++necessary;
|
||||
if (!_handledThisTick.Add(childTx)) continue;
|
||||
|
||||
if (UpdateEntityTree(_entityManager.GetEntity(childTx)))
|
||||
{
|
||||
++necessary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return necessary > 0;
|
||||
}
|
||||
|
||||
public bool RemoveFromEntityTree(IEntity entity, MapId mapId)
|
||||
{
|
||||
if (_entityTreesPerMap.TryGetValue(mapId, out var tree))
|
||||
{
|
||||
return tree.Remove(entity);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveFromEntityTrees(IEntity entity)
|
||||
{
|
||||
foreach (var mapId in _mapManager.GetAllMapIds())
|
||||
foreach (var lookup in _compManager.EntityQuery<EntityLookupComponent>(true))
|
||||
{
|
||||
if (_entityTreesPerMap.TryGetValue(mapId, out var entTree))
|
||||
{
|
||||
entTree.Remove(entity);
|
||||
}
|
||||
lookup.Tree.Remove(entity);
|
||||
}
|
||||
}
|
||||
|
||||
public Box2 GetWorldAabbFromEntity(in IEntity ent)
|
||||
{
|
||||
if (ent.Deleted)
|
||||
return new Box2(0, 0, 0, 0);
|
||||
return GetWorldAABB(ent);
|
||||
}
|
||||
|
||||
private static Box2 GetWorldAABB(in IEntity ent)
|
||||
{
|
||||
var pos = ent.Transform.WorldPosition;
|
||||
|
||||
if (ent.TryGetComponent(out IPhysBody? collider))
|
||||
return collider.GetWorldAABB(pos);
|
||||
if (ent.Deleted || !ent.TryGetComponent(out PhysicsComponent? physics))
|
||||
return new Box2(pos, pos);
|
||||
|
||||
return new Box2(pos, pos);
|
||||
return physics.GetWorldAABB(pos);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Robust.Shared.GameObjects
|
||||
internal IReadOnlyList<VirtualController> Controllers => _controllers;
|
||||
private List<VirtualController> _controllers = new();
|
||||
|
||||
public Action<Fixture, Fixture, float, Manifold>? KinematicControllerCollision;
|
||||
public Action<Fixture, Fixture, float, Vector2>? KinematicControllerCollision;
|
||||
|
||||
public bool MetricsEnabled;
|
||||
private readonly Stopwatch _stopwatch = new();
|
||||
@@ -100,6 +100,7 @@ namespace Robust.Shared.GameObjects
|
||||
_mapManager.MapCreated += HandleMapCreated;
|
||||
_mapManager.MapDestroyed += HandleMapDestroyed;
|
||||
|
||||
SubscribeLocalEvent<GridInitializeEvent>(HandleGridInit);
|
||||
SubscribeLocalEvent<PhysicsUpdateMessage>(HandlePhysicsUpdateMessage);
|
||||
SubscribeLocalEvent<PhysicsWakeMessage>(HandleWakeMessage);
|
||||
SubscribeLocalEvent<PhysicsSleepMessage>(HandleSleepMessage);
|
||||
@@ -146,6 +147,13 @@ namespace Robust.Shared.GameObjects
|
||||
body.AngularVelocity += angularVelocityDiff;
|
||||
}
|
||||
|
||||
private void HandleGridInit(GridInitializeEvent ev)
|
||||
{
|
||||
if (!EntityManager.TryGetEntity(ev.EntityUid, out var gridEntity)) return;
|
||||
var collideComp = gridEntity.EnsureComponent<PhysicsComponent>();
|
||||
collideComp.BodyType = BodyType.Static;
|
||||
}
|
||||
|
||||
private void BuildControllers()
|
||||
{
|
||||
var reflectionManager = IoCManager.Resolve<IReflectionManager>();
|
||||
|
||||
@@ -365,10 +365,10 @@ namespace Robust.Shared.Map
|
||||
/// <summary>
|
||||
/// Creates a set of EntityCoordinates given some MapCoordinates.
|
||||
/// </summary>
|
||||
/// <param name="entityManager"></param>
|
||||
/// <param name="mapManager"></param>
|
||||
/// <param name="coordinates"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use FromMap(IMapManager mapManager, MapCoordinates coordinates) instead.")]
|
||||
public static EntityCoordinates FromMap(IEntityManager entityManager, IMapManager mapManager, MapCoordinates coordinates)
|
||||
{
|
||||
var mapId = coordinates.MapId;
|
||||
@@ -377,6 +377,19 @@ namespace Robust.Shared.Map
|
||||
return new EntityCoordinates(mapEntity.Uid, coordinates.Position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a set of EntityCoordinates given some MapCoordinates.
|
||||
/// </summary>
|
||||
/// <param name="mapManager"></param>
|
||||
/// <param name="coordinates"></param>
|
||||
public static EntityCoordinates FromMap(IMapManager mapManager, MapCoordinates coordinates)
|
||||
{
|
||||
var mapId = coordinates.MapId;
|
||||
var mapEntity = mapManager.GetMapEntity(mapId);
|
||||
|
||||
return new EntityCoordinates(mapEntity.Uid, coordinates.Position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this set of coordinates to Vector2i.
|
||||
/// </summary>
|
||||
|
||||
@@ -8,10 +8,8 @@ namespace Robust.Shared.Map
|
||||
/// </summary>
|
||||
internal static class GridChunkPartition
|
||||
{
|
||||
public static void PartitionChunk(IMapChunk chunk, ref IList<Box2> rectangles, out Box2i bounds)
|
||||
public static void PartitionChunk(IMapChunk chunk, out Box2i bounds)
|
||||
{
|
||||
rectangles.Clear();
|
||||
|
||||
var size = chunk.ChunkSize;
|
||||
|
||||
// copy 2d img
|
||||
@@ -37,8 +35,6 @@ namespace Robust.Shared.Map
|
||||
var bottom = block.x1;
|
||||
var top = block.x2 + 1;
|
||||
|
||||
rectangles.Add(new Box2(left, bottom, right, top));
|
||||
|
||||
if(bounds.Size.Equals(Vector2i.Zero))
|
||||
bounds = new Box2i(left, bottom, right, top);
|
||||
else
|
||||
|
||||
@@ -15,10 +15,5 @@ namespace Robust.Shared.Map
|
||||
/// The last game simulation tick that this chunk was modified.
|
||||
/// </summary>
|
||||
GameTick LastModifiedTick { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The physical collision boxes of this chunk.
|
||||
/// </summary>
|
||||
IEnumerable<Box2> CollisionBoxes { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,10 @@ namespace Robust.Shared.Map
|
||||
/// </summary>
|
||||
Vector2 WorldPosition { get; set; }
|
||||
|
||||
Matrix3 WorldMatrix { get; }
|
||||
|
||||
Matrix3 InvWorldMatrix { get; }
|
||||
|
||||
#region TileAccess
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Robust.Shared.Map
|
||||
/// 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();
|
||||
void NotifyChunkCollisionRegenerated(MapChunk chunk);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the chunk at the given indices. If the chunk does not exist,
|
||||
|
||||
@@ -2,7 +2,9 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -16,6 +18,8 @@ namespace Robust.Shared.Map
|
||||
/// </summary>
|
||||
private const int SnapCellStartingCapacity = 1;
|
||||
|
||||
public GridId GridId => _grid.Index;
|
||||
|
||||
private readonly IMapGridInternal _grid;
|
||||
private readonly Vector2i _gridIndices;
|
||||
|
||||
@@ -23,13 +27,12 @@ namespace Robust.Shared.Map
|
||||
private readonly SnapGridCell[,] _snapGrid;
|
||||
|
||||
private Box2i _cachedBounds;
|
||||
private IList<Box2> _colBoxes;
|
||||
|
||||
public Fixture? Fixture { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public GameTick LastModifiedTick { get; private set; }
|
||||
|
||||
public IEnumerable<Box2> CollisionBoxes => _colBoxes;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an instance of a MapGrid chunk.
|
||||
/// </summary>
|
||||
@@ -46,7 +49,6 @@ namespace Robust.Shared.Map
|
||||
|
||||
_tiles = new Tile[ChunkSize, ChunkSize];
|
||||
_snapGrid = new SnapGridCell[ChunkSize, ChunkSize];
|
||||
_colBoxes = new List<Box2>(0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -233,8 +235,8 @@ namespace Robust.Shared.Map
|
||||
public void RegenerateCollision()
|
||||
{
|
||||
// generate collision rects
|
||||
GridChunkPartition.PartitionChunk(this, ref _colBoxes, out _cachedBounds);
|
||||
_grid.NotifyChunkCollisionRegenerated();
|
||||
GridChunkPartition.PartitionChunk(this, out _cachedBounds);
|
||||
_grid.NotifyChunkCollisionRegenerated(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -264,12 +266,6 @@ namespace Robust.Shared.Map
|
||||
return _tiles[localIndices.X, localIndices.Y].TypeId != Tile.Empty.TypeId;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CollidesWithChunk(Box2 pos)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
@@ -281,4 +277,14 @@ namespace Robust.Shared.Map
|
||||
public List<EntityUid>? Center;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class RegenerateChunkCollisionEvent : EntityEventArgs
|
||||
{
|
||||
public MapChunk Chunk { get; }
|
||||
|
||||
public RegenerateChunkCollisionEvent(MapChunk chunk)
|
||||
{
|
||||
Chunk = chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -40,7 +41,7 @@ namespace Robust.Shared.Map
|
||||
private readonly Dictionary<Vector2i, IMapChunkInternal> _chunks = new();
|
||||
|
||||
private readonly IMapManagerInternal _mapManager;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly IEntityManager _entityManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MapGrid"/> class.
|
||||
@@ -111,6 +112,34 @@ namespace Robust.Shared.Map
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public Matrix3 WorldMatrix
|
||||
{
|
||||
get
|
||||
{
|
||||
//TODO: Make grids real parents of entities.
|
||||
if(GridEntityId.IsValid())
|
||||
return _mapManager.EntityManager.GetEntity(GridEntityId).Transform.WorldMatrix;
|
||||
|
||||
return Matrix3.Identity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public Matrix3 InvWorldMatrix
|
||||
{
|
||||
get
|
||||
{
|
||||
//TODO: Make grids real parents of entities.
|
||||
if(GridEntityId.IsValid())
|
||||
return _mapManager.EntityManager.GetEntity(GridEntityId).Transform.InvWorldMatrix;
|
||||
|
||||
return Matrix3.Identity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expands the AABB for this grid when a new tile is added. If the tile is already inside the existing AABB,
|
||||
/// nothing happens. If it is outside, the AABB is expanded to fit the new tile.
|
||||
@@ -146,8 +175,18 @@ namespace Robust.Shared.Map
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void NotifyChunkCollisionRegenerated()
|
||||
public void NotifyChunkCollisionRegenerated(MapChunk chunk)
|
||||
{
|
||||
// TODO: Ideally we wouldn't have LocalBounds on the grid and we could just treat it like a physics object
|
||||
// (eventually, well into the future).
|
||||
// For now we'll just attach a fixture to each chunk.
|
||||
|
||||
// Not raising directed because the grid's EntityUid isn't set yet.
|
||||
IoCManager
|
||||
.Resolve<IEntityManager>()
|
||||
.EventBus
|
||||
.RaiseEvent(EventSource.Local, new RegenerateChunkCollisionEvent(chunk));
|
||||
|
||||
UpdateAABB();
|
||||
}
|
||||
|
||||
@@ -478,7 +517,7 @@ namespace Robust.Shared.Map
|
||||
/// <inheritdoc />
|
||||
public Vector2 WorldToLocal(Vector2 posWorld)
|
||||
{
|
||||
return posWorld - WorldPosition;
|
||||
return InvWorldMatrix.Transform(posWorld);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -498,7 +537,7 @@ namespace Robust.Shared.Map
|
||||
/// <inheritdoc />
|
||||
public Vector2 LocalToWorld(Vector2 posLocal)
|
||||
{
|
||||
return posLocal + WorldPosition;
|
||||
return WorldMatrix.Transform(posLocal);
|
||||
}
|
||||
|
||||
public Vector2i WorldToTile(Vector2 posWorld)
|
||||
|
||||
@@ -6,6 +6,8 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -521,6 +523,7 @@ namespace Robust.Shared.Map
|
||||
/// <inheritdoc />
|
||||
public bool TryFindGridAt(MapId mapId, Vector2 worldPos, [NotNullWhen(true)] out IMapGrid? grid)
|
||||
{
|
||||
// TODO: this won't actually "work" but we need the broadphase refactor to finish it.
|
||||
foreach (var mapGrid in _grids.Values)
|
||||
{
|
||||
if (mapGrid.ParentMapId != mapId)
|
||||
|
||||
@@ -56,6 +56,11 @@ namespace Robust.Shared.Network
|
||||
|
||||
NetUserData UserData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Has the serializer handshake completed and <see cref="INetManager.Connected"/> been ran?
|
||||
/// </summary>
|
||||
bool IsHandshakeComplete { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new NetMessage to be filled up and sent.
|
||||
/// </summary>
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Robust.Shared.Network.Messages
|
||||
[UsedImplicitly]
|
||||
internal class MsgMapStrClientHandshake : NetMessage
|
||||
{
|
||||
public override MsgGroups MsgGroup => MsgGroups.Core;
|
||||
public override MsgGroups MsgGroup => MsgGroups.String;
|
||||
|
||||
/// <value>
|
||||
/// <c>true</c> if the client needs a new copy of the mapping,
|
||||
|
||||
@@ -216,7 +216,7 @@ namespace Robust.Shared.Network
|
||||
|
||||
private static byte[] MakeAuthHash(byte[] sharedSecret, byte[] pkBytes)
|
||||
{
|
||||
Logger.DebugS("auth", "shared: {0}, pk: {1}", Convert.ToBase64String(sharedSecret), Convert.ToBase64String(pkBytes));
|
||||
// Logger.DebugS("auth", "shared: {0}, pk: {1}", Convert.ToBase64String(sharedSecret), Convert.ToBase64String(pkBytes));
|
||||
|
||||
var incHash = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
|
||||
incHash.AppendData(sharedSecret);
|
||||
|
||||
@@ -46,6 +46,8 @@ namespace Robust.Shared.Network
|
||||
[ViewVariables] public NetUserId UserId => UserData.UserId;
|
||||
[ViewVariables] public NetUserData UserData { get; }
|
||||
|
||||
public bool IsHandshakeComplete { get; set; }
|
||||
|
||||
// Only used on server, contains the encryption to use for this channel.
|
||||
public NetEncryption? Encryption { get; set; }
|
||||
|
||||
@@ -88,6 +90,11 @@ namespace Robust.Shared.Network
|
||||
if (_connection.Status == NetConnectionStatus.Connected)
|
||||
_connection.Disconnect(reason);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{RemoteEndPoint}/{UserId}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ namespace Robust.Shared.Network
|
||||
|
||||
Logger.InfoS("net",
|
||||
"Approved {ConnectionEndpoint} with username {Username} user ID {userId} into the server",
|
||||
connection.RemoteEndPoint, userData.UserName, userData.UserName);
|
||||
connection.RemoteEndPoint, userData.UserName, userData.UserId);
|
||||
|
||||
// Handshake complete!
|
||||
HandleInitialHandshakeComplete(peer, connection, userData, encryption, type);
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace Robust.Shared.Network
|
||||
/// <summary>
|
||||
/// Holds lookup table for NetMessage.Id -> NetMessage.Type
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, Type> _messages = new();
|
||||
private readonly Dictionary<string, (Type type, bool isHandshake)> _messages = new();
|
||||
|
||||
/// <summary>
|
||||
/// The StringTable for transforming packet Ids to Packet name.
|
||||
@@ -204,7 +204,9 @@ namespace Robust.Shared.Network
|
||||
public IReadOnlyDictionary<Type, ProcessMessage> CallbackAudit => _callbacks;
|
||||
|
||||
/// <inheritdoc />
|
||||
public INetChannel? ServerChannel
|
||||
public INetChannel? ServerChannel => ServerChannelImpl;
|
||||
|
||||
private NetChannel? ServerChannelImpl
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -261,7 +263,7 @@ namespace Robust.Shared.Network
|
||||
_serializer.ClientHandshakeComplete += () =>
|
||||
{
|
||||
Logger.InfoS("net", "Client completed serializer handshake.");
|
||||
OnConnected(ServerChannel!);
|
||||
OnConnected(ServerChannelImpl!);
|
||||
};
|
||||
|
||||
_initialized = true;
|
||||
@@ -792,6 +794,7 @@ namespace Robust.Shared.Network
|
||||
var id = msg.ReadByte();
|
||||
|
||||
ref var entry = ref _netMsgFunctions[id];
|
||||
|
||||
if (entry.CreateFunction == null)
|
||||
{
|
||||
Logger.WarningS("net",
|
||||
@@ -801,6 +804,15 @@ namespace Robust.Shared.Network
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!channel.IsHandshakeComplete && !entry.IsHandshake)
|
||||
{
|
||||
Logger.WarningS("net",
|
||||
$"{msg.SenderConnection.RemoteEndPoint}: Got non-handshake message {entry.Type.Name} before handshake completion.");
|
||||
|
||||
channel.Disconnect("Got unacceptable net message before handshake completion");
|
||||
return true;
|
||||
}
|
||||
|
||||
var type = entry.Type;
|
||||
|
||||
var instance = entry.CreateFunction(channel);
|
||||
@@ -837,6 +849,7 @@ namespace Robust.Shared.Network
|
||||
// Callback must be available or else construction delegate will not be registered.
|
||||
var callback = _callbacks[type];
|
||||
|
||||
// Logger.DebugS("net", $"RECV: {instance.GetType().Name}");
|
||||
try
|
||||
{
|
||||
callback?.Invoke(instance);
|
||||
@@ -857,11 +870,13 @@ namespace Robust.Shared.Network
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_messages.TryGetValue(name, out var packetType))
|
||||
if (!_messages.TryGetValue(name, out var msgDat))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var (packetType, isHandshake) = msgDat;
|
||||
|
||||
if (!_callbacks.ContainsKey(packetType))
|
||||
{
|
||||
return;
|
||||
@@ -897,6 +912,7 @@ namespace Robust.Shared.Network
|
||||
ref var entry = ref _netMsgFunctions[id];
|
||||
entry.CreateFunction = @delegate;
|
||||
entry.Type = packetType;
|
||||
entry.IsHandshake = isHandshake;
|
||||
}
|
||||
|
||||
#region NetMessages
|
||||
@@ -909,7 +925,7 @@ namespace Robust.Shared.Network
|
||||
var name = new T().MsgName;
|
||||
var id = _strings.AddString(name);
|
||||
|
||||
_messages.Add(name, typeof(T));
|
||||
_messages.Add(name, (typeof(T), (accept & NetMessageAccept.Handshake) != 0));
|
||||
|
||||
var thisSide = IsServer ? NetMessageAccept.Server : NetMessageAccept.Client;
|
||||
|
||||
@@ -986,6 +1002,9 @@ namespace Robust.Shared.Network
|
||||
|
||||
foreach (var channel in _channels.Values)
|
||||
{
|
||||
if (!channel.IsHandshakeComplete)
|
||||
continue;
|
||||
|
||||
ServerSendMessage(message, channel);
|
||||
}
|
||||
}
|
||||
@@ -1006,6 +1025,12 @@ namespace Robust.Shared.Network
|
||||
|
||||
var method = message.DeliveryMethod;
|
||||
peer.SendMessage(packet, channel.Connection, method);
|
||||
LogSend(message, method, packet);
|
||||
}
|
||||
|
||||
private static void LogSend(NetMessage message, NetDeliveryMethod method, NetOutgoingMessage packet)
|
||||
{
|
||||
// Logger.DebugS("net", $"SEND: {message.GetType().Name} {method} {packet.LengthBytes}");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -1042,6 +1067,7 @@ namespace Robust.Shared.Network
|
||||
}
|
||||
|
||||
peer.Peer.SendMessage(packet, peer.ConnectionsWithChannels[0], method);
|
||||
LogSend(message, method, packet);
|
||||
}
|
||||
|
||||
#endregion NetMessages
|
||||
@@ -1068,8 +1094,10 @@ namespace Robust.Shared.Network
|
||||
ConnectFailed?.Invoke(this, args);
|
||||
}
|
||||
|
||||
private void OnConnected(INetChannel channel)
|
||||
private void OnConnected(NetChannel channel)
|
||||
{
|
||||
channel.IsHandshakeComplete = true;
|
||||
|
||||
Connected?.Invoke(this, new NetChannelArgs(channel));
|
||||
}
|
||||
|
||||
@@ -1151,6 +1179,7 @@ namespace Robust.Shared.Network
|
||||
private struct NetMsgEntry
|
||||
{
|
||||
public Func<INetChannel, NetMessage>? CreateFunction;
|
||||
public bool IsHandshake;
|
||||
public Type Type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,16 +13,26 @@ namespace Robust.Shared.Network
|
||||
/// <summary>
|
||||
/// Message can only be received on the server and it is an error to send it to a client.
|
||||
/// </summary>
|
||||
Server = 1,
|
||||
Server = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Message can only be received on the client and it is an error to send it to a server.
|
||||
/// </summary>
|
||||
Client = 2,
|
||||
Client = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Message can be received on both client and server.
|
||||
/// </summary>
|
||||
Both = Client | Server
|
||||
Both = Client | Server,
|
||||
|
||||
/// <summary>
|
||||
/// Message is used during connection handshake and may be sent before the initial handshake is completed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There is a window of time between the initial authentication handshake and serialization handshake,
|
||||
/// where the connection *does* have an INetChannel.
|
||||
/// During this handshake messages are still blocked however unless this flag is sent on the message type.
|
||||
/// </remarks>
|
||||
Handshake = 1 << 2,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace Robust.Shared.Network
|
||||
|
||||
_callback = callback;
|
||||
_updateCallback = updateCallback;
|
||||
_network.RegisterNetMessage<MsgStringTableEntries>(ReceiveEntries, NetMessageAccept.Client);
|
||||
_network.RegisterNetMessage<MsgStringTableEntries>(ReceiveEntries, NetMessageAccept.Client | NetMessageAccept.Handshake);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
@@ -114,17 +114,6 @@ namespace Robust.Shared.Physics.Collision
|
||||
|
||||
Radius = rectangle.Radius;
|
||||
break;
|
||||
case ShapeType.Grid:
|
||||
var grid = (PhysShapeGrid) shape;
|
||||
var gridBounds = grid.LocalBounds;
|
||||
|
||||
Vertices.Add(gridBounds.BottomRight);
|
||||
Vertices.Add(gridBounds.TopRight);
|
||||
Vertices.Add(gridBounds.TopLeft);
|
||||
Vertices.Add(gridBounds.BottomLeft);
|
||||
|
||||
Radius = grid.Radius;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid shapetype specified {shape.ShapeType}");
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
[DataDefinition]
|
||||
public sealed class EdgeShape : IPhysShape
|
||||
{
|
||||
internal Vector2 Centroid { get; set; } = Vector2.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Edge start vertex
|
||||
/// </summary>
|
||||
@@ -119,6 +121,12 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
Vertex3 == edge.Vertex3);
|
||||
}
|
||||
|
||||
public bool Intersects(Box2 worldAABB, Vector2 worldPos, Angle worldRot)
|
||||
{
|
||||
var bounds = CalculateLocalBounds(worldRot).Translated(worldPos);
|
||||
return bounds.Intersects(worldAABB);
|
||||
}
|
||||
|
||||
public Box2 CalculateLocalBounds(Angle rotation)
|
||||
{
|
||||
Vector2 lower = Vector2.ComponentMin(Vertex1, Vertex2);
|
||||
|
||||
@@ -12,8 +12,7 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
Chain = 3,
|
||||
Aabb = 4,
|
||||
Rectangle = 5, // Look you might be able to replace this with polys but for now I have done the thing
|
||||
Grid = 6,
|
||||
TypeCount = 7, // Obviously increment this if you add something
|
||||
TypeCount = 6, // Obviously increment this if you add something
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -37,6 +36,9 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
|
||||
ShapeType ShapeType { get; }
|
||||
|
||||
// TODO: Like raycasts these aren't using exact shapes so need to do that.
|
||||
bool Intersects(Box2 worldAABB, Vector2 worldPos, Angle worldRot);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the AABB of the shape.
|
||||
/// </summary>
|
||||
|
||||
@@ -38,6 +38,8 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
|
||||
private float _radius;
|
||||
|
||||
internal Vector2 Centroid { get; set; } = Vector2.Zero;
|
||||
|
||||
public ShapeType ShapeType => ShapeType.Aabb;
|
||||
|
||||
[DataField("bounds")]
|
||||
@@ -88,6 +90,12 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
[field: NonSerialized]
|
||||
public event Action? OnDataChanged;
|
||||
|
||||
public bool Intersects(Box2 worldAABB, Vector2 worldPos, Angle worldRot)
|
||||
{
|
||||
var bounds = CalculateLocalBounds(worldRot).Translated(worldPos);
|
||||
return bounds.Intersects(worldAABB);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Box2 CalculateLocalBounds(Angle rotation)
|
||||
{
|
||||
|
||||
@@ -58,6 +58,12 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
}
|
||||
}
|
||||
|
||||
public bool Intersects(Box2 worldAABB, Vector2 worldPos, Angle worldRot)
|
||||
{
|
||||
var bounds = CalculateLocalBounds(worldRot).Translated(worldPos);
|
||||
return bounds.Intersects(worldAABB);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Box2 CalculateLocalBounds(Angle rotation)
|
||||
{
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
using System;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Shared.Physics.Collision.Shapes
|
||||
{
|
||||
/// <summary>
|
||||
/// A physics shape that represents a <see cref="MapGrid"/>.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
[DataDefinition]
|
||||
public class PhysShapeGrid : IPhysShape, ISerializationHooks
|
||||
{
|
||||
public int ChildCount => 1;
|
||||
|
||||
public Box2 LocalBounds => _mapGrid.LocalBounds;
|
||||
|
||||
/// <summary>
|
||||
/// The radius of this AABB
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float Radius
|
||||
{
|
||||
get => _radius;
|
||||
set
|
||||
{
|
||||
if (MathHelper.CloseTo(_radius, value)) return;
|
||||
_radius = value;
|
||||
}
|
||||
}
|
||||
|
||||
private float _radius = IoCManager.Resolve<IConfigurationManager>().GetCVar(CVars.PolygonRadius);
|
||||
public ShapeType ShapeType => ShapeType.Grid;
|
||||
|
||||
[DataField("grid")]
|
||||
private GridId _gridId = GridId.Invalid;
|
||||
|
||||
[NonSerialized]
|
||||
private IMapGridInternal _mapGrid = default!;
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
var mapMan = IoCManager.Resolve<IMapManager>();
|
||||
_mapGrid = (IMapGridInternal)mapMan.GetGrid(_gridId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ApplyState()
|
||||
{
|
||||
var mapMan = IoCManager.Resolve<IMapManager>();
|
||||
_mapGrid = (IMapGridInternal)mapMan.GetGrid(_gridId);
|
||||
}
|
||||
|
||||
public void DebugDraw(DebugDrawingHandle handle, in Matrix3 modelMatrix, in Box2 worldViewport,
|
||||
float sleepPercent)
|
||||
{
|
||||
handle.SetTransform(modelMatrix);
|
||||
foreach (var chunk in _mapGrid.GetMapChunks().Values)
|
||||
{
|
||||
foreach (var box in chunk.CollisionBoxes)
|
||||
{
|
||||
var localChunkPos = new Vector2(chunk.Indices.X, chunk.Indices.Y) * _mapGrid.ChunkSize;
|
||||
var localBox = box.Translated(localChunkPos);
|
||||
|
||||
handle.DrawRect(localBox, handle.CalcWakeColor(handle.GridFillColor, sleepPercent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="PhysShapeGrid"/>.
|
||||
/// </summary>
|
||||
public PhysShapeGrid()
|
||||
{
|
||||
// Better hope ExposeData get called...
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="PhysShapeGrid"/>.
|
||||
/// </summary>
|
||||
public PhysShapeGrid(IMapGrid mapGrid)
|
||||
{
|
||||
_mapGrid = (IMapGridInternal)mapGrid;
|
||||
_gridId = _mapGrid.Index;
|
||||
}
|
||||
|
||||
public event Action? OnDataChanged { add { } remove { } }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Box2 CalculateLocalBounds(Angle rotation)
|
||||
{
|
||||
return new Box2Rotated(_mapGrid.LocalBounds, rotation).CalcBoundingBox().Scale(1 + Radius);
|
||||
}
|
||||
|
||||
public bool Equals(IPhysShape? other)
|
||||
{
|
||||
if (other is not PhysShapeGrid otherGrid) return false;
|
||||
return MathHelper.CloseTo(_radius, otherGrid._radius) &&
|
||||
_gridId == otherGrid._gridId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,8 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
|
||||
private float _radius = IoCManager.Resolve<IConfigurationManager>().GetCVar(CVars.PolygonRadius);
|
||||
|
||||
internal Vector2 Centroid { get; set; } = Vector2.Zero;
|
||||
|
||||
public ShapeType ShapeType => ShapeType.Rectangle;
|
||||
|
||||
/// <summary>
|
||||
@@ -65,6 +67,12 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
[field: NonSerialized]
|
||||
public event Action? OnDataChanged;
|
||||
|
||||
public bool Intersects(Box2 worldAABB, Vector2 worldPos, Angle worldRot)
|
||||
{
|
||||
var bounds = CalculateLocalBounds(worldRot).Translated(worldPos);
|
||||
return bounds.Intersects(worldAABB);
|
||||
}
|
||||
|
||||
public Box2 CalculateLocalBounds(Angle rotation)
|
||||
{
|
||||
_cachedBounds = new Box2Rotated(Rectangle, rotation.Opposite(), Vector2.Zero).CalcBoundingBox().Scale(1 + Radius);
|
||||
|
||||
@@ -75,6 +75,8 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
_normals.Add(temp.Normalized);
|
||||
}
|
||||
|
||||
Centroid = ComputeCentroid(_vertices);
|
||||
|
||||
// Compute the polygon mass data
|
||||
// TODO: Update fixture. Maybe use events for it? Who tf knows.
|
||||
// If we get grid polys then we'll actually need runtime updating of bbs.
|
||||
@@ -83,6 +85,8 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
|
||||
private List<Vector2> _vertices = new();
|
||||
|
||||
internal Vector2 Centroid { get; set; } = Vector2.Zero;
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public List<Vector2> Normals => _normals;
|
||||
|
||||
@@ -107,6 +111,44 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
|
||||
private float _radius;
|
||||
|
||||
public static Vector2 ComputeCentroid(List<Vector2> vertices)
|
||||
{
|
||||
DebugTools.Assert(vertices.Count >= 3);
|
||||
|
||||
var c = new Vector2(0.0f, 0.0f);
|
||||
float area = 0.0f;
|
||||
|
||||
// Get a reference point for forming triangles.
|
||||
// Use the first vertex to reduce round-off errors.
|
||||
var s = vertices[0];
|
||||
|
||||
const float inv3 = 1.0f / 3.0f;
|
||||
|
||||
for (var i = 0; i < vertices.Count; ++i)
|
||||
{
|
||||
// Triangle vertices.
|
||||
var p1 = vertices[0] - s;
|
||||
var p2 = vertices[i] - s;
|
||||
var p3 = i + 1 < vertices.Count ? vertices[i+1] - s : vertices[0] - s;
|
||||
|
||||
var e1 = p2 - p1;
|
||||
var e2 = p3 - p1;
|
||||
|
||||
float D = Vector2.Cross(e1, e2);
|
||||
|
||||
float triangleArea = 0.5f * D;
|
||||
area += triangleArea;
|
||||
|
||||
// Area weighted centroid
|
||||
c += (p1 + p2 + p3) * triangleArea * inv3;
|
||||
}
|
||||
|
||||
// Centroid
|
||||
DebugTools.Assert(area > float.Epsilon);
|
||||
c = c * (1.0f / area) + s;
|
||||
return c;
|
||||
}
|
||||
|
||||
public ShapeType ShapeType => ShapeType.Polygon;
|
||||
|
||||
public PolygonShape()
|
||||
@@ -157,6 +199,15 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Intersects(Box2 worldAABB, Vector2 worldPos, Angle worldRot)
|
||||
{
|
||||
var aabb = CalculateLocalBounds(worldRot).Translated(worldPos);
|
||||
if (!worldAABB.Intersects(aabb)) return false;
|
||||
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
public Box2 CalculateLocalBounds(Angle rotation)
|
||||
{
|
||||
if (Vertices.Count == 0) return new Box2();
|
||||
|
||||
@@ -34,6 +34,7 @@ using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Physics.Collision;
|
||||
using Robust.Shared.Physics.Dynamics.Contacts;
|
||||
@@ -63,7 +64,7 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
/// <summary>
|
||||
/// Invoked whenever a KinematicController body collides. The first body is always guaranteed to be a KinematicController
|
||||
/// </summary>
|
||||
internal event Action<Fixture, Fixture, float, Manifold>? KinematicControllerCollision;
|
||||
internal event Action<Fixture, Fixture, float, Vector2>? KinematicControllerCollision;
|
||||
|
||||
// TODO: Need to migrate the interfaces to comp bus when possible
|
||||
// TODO: Also need to clean the station up to not have 160 contacts on roundstart
|
||||
@@ -412,20 +413,6 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
|
||||
_entityManager.EventBus.RaiseLocalEvent(bodyA.Owner.Uid, new EndCollideEvent(fixtureA, fixtureB, manifold));
|
||||
_entityManager.EventBus.RaiseLocalEvent(bodyB.Owner.Uid, new EndCollideEvent(fixtureB, fixtureA, manifold));
|
||||
|
||||
#pragma warning disable 618
|
||||
foreach (var comp in bodyA.Owner.GetAllComponents<IEndCollide>().ToArray())
|
||||
{
|
||||
if (bodyB.Deleted) break;
|
||||
comp.CollideWith(fixtureA, fixtureB, manifold);
|
||||
}
|
||||
|
||||
foreach (var comp in bodyB.Owner.GetAllComponents<IEndCollide>().ToArray())
|
||||
{
|
||||
if (bodyA.Deleted) break;
|
||||
comp.CollideWith(fixtureB, fixtureA, manifold);
|
||||
}
|
||||
#pragma warning restore 618
|
||||
}
|
||||
|
||||
_startCollisions.Clear();
|
||||
@@ -434,26 +421,29 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
|
||||
public void PreSolve(float frameTime)
|
||||
{
|
||||
Span<Vector2> points = stackalloc Vector2[2];
|
||||
|
||||
// We'll do pre and post-solve around all islands rather than each specific island as it seems cleaner with race conditions.
|
||||
for (var contact = ContactList.Next; contact != ContactList; contact = contact?.Next)
|
||||
{
|
||||
if (contact == null || !contact.IsTouching || !contact.Enabled)
|
||||
if (contact is not {IsTouching: true, Enabled: true})
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var bodyA = contact.FixtureA!.Body;
|
||||
var bodyB = contact.FixtureB!.Body;
|
||||
contact.GetWorldManifold(out var worldNormal, points);
|
||||
|
||||
// Didn't use an EntitySystemMessage as this is called FOR EVERY COLLISION AND IS REALLY EXPENSIVE
|
||||
// so we just use the Action. Also we'll sort out BodyA / BodyB for anyone listening first.
|
||||
if (bodyA.BodyType == BodyType.KinematicController)
|
||||
{
|
||||
KinematicControllerCollision?.Invoke(contact.FixtureA!, contact.FixtureB!, frameTime, contact.Manifold);
|
||||
KinematicControllerCollision?.Invoke(contact.FixtureA!, contact.FixtureB!, frameTime, -worldNormal);
|
||||
}
|
||||
else if (bodyB.BodyType == BodyType.KinematicController)
|
||||
{
|
||||
KinematicControllerCollision?.Invoke(contact.FixtureB!, contact.FixtureA!, frameTime, contact.Manifold);
|
||||
KinematicControllerCollision?.Invoke(contact.FixtureB!, contact.FixtureA!, frameTime, worldNormal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -472,20 +462,18 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
{
|
||||
public Fixture OurFixture { get; }
|
||||
public Fixture OtherFixture { get; }
|
||||
public Manifold Manifold { get; }
|
||||
|
||||
public CollideEvent(Fixture ourFixture, Fixture otherFixture, Manifold manifold)
|
||||
public CollideEvent(Fixture ourFixture, Fixture otherFixture)
|
||||
{
|
||||
OurFixture = ourFixture;
|
||||
OtherFixture = otherFixture;
|
||||
Manifold = manifold;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class StartCollideEvent : CollideEvent
|
||||
{
|
||||
public StartCollideEvent(Fixture ourFixture, Fixture otherFixture, Manifold manifold)
|
||||
: base(ourFixture, otherFixture, manifold)
|
||||
: base(ourFixture, otherFixture)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -493,7 +481,7 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
public sealed class EndCollideEvent : CollideEvent
|
||||
{
|
||||
public EndCollideEvent(Fixture ourFixture, Fixture otherFixture, Manifold manifold)
|
||||
: base(ourFixture, otherFixture, manifold)
|
||||
: base(ourFixture, otherFixture)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
|
||||
|
||||
private ContactType _type;
|
||||
|
||||
// TODO: Jesus we should really have a test for this
|
||||
/// <summary>
|
||||
/// Ordering is under <see cref="ShapeType"/>
|
||||
/// uses enum to work out which collision evaluation to use.
|
||||
@@ -80,7 +81,6 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
|
||||
ContactType.ChainAndCircle,
|
||||
ContactType.AabbAndCircle,
|
||||
ContactType.RectAndCircle,
|
||||
ContactType.NotSupported,
|
||||
},
|
||||
{
|
||||
// Edge register
|
||||
@@ -90,7 +90,6 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
|
||||
ContactType.NotSupported, // Chain
|
||||
ContactType.NotSupported, // Aabb
|
||||
ContactType.NotSupported, // Rect
|
||||
ContactType.NotSupported,
|
||||
},
|
||||
{
|
||||
// Polygon register
|
||||
@@ -100,7 +99,6 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
|
||||
ContactType.ChainAndPolygon,
|
||||
ContactType.AabbAndPolygon,
|
||||
ContactType.RectAndPolygon,
|
||||
ContactType.NotSupported,
|
||||
},
|
||||
{
|
||||
// Chain register
|
||||
@@ -110,7 +108,6 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
|
||||
ContactType.NotSupported, // Chain
|
||||
ContactType.NotSupported, // Aabb - TODO Just cast to poly
|
||||
ContactType.NotSupported, // Rect - TODO Just cast to poly
|
||||
ContactType.NotSupported,
|
||||
},
|
||||
{
|
||||
// Aabb register
|
||||
@@ -120,7 +117,6 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
|
||||
ContactType.NotSupported, // Chain - TODO Just cast to poly
|
||||
ContactType.Aabb,
|
||||
ContactType.AabbAndRect,
|
||||
ContactType.NotSupported,
|
||||
},
|
||||
{
|
||||
// Rectangle register
|
||||
@@ -130,18 +126,7 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
|
||||
ContactType.NotSupported, // Chain - TODO Just cast to poly
|
||||
ContactType.AabbAndRect,
|
||||
ContactType.Rect,
|
||||
ContactType.NotSupported,
|
||||
},
|
||||
{
|
||||
// Grid register
|
||||
ContactType.NotSupported,
|
||||
ContactType.NotSupported,
|
||||
ContactType.NotSupported,
|
||||
ContactType.NotSupported,
|
||||
ContactType.NotSupported,
|
||||
ContactType.NotSupported,
|
||||
ContactType.Grids,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -494,9 +479,6 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
|
||||
case ContactType.RectAndPolygon:
|
||||
_collisionManager.CollideRectAndPolygon(ref manifold, (PhysShapeRect) FixtureA!.Shape, transformA, (PolygonShape) FixtureB!.Shape, transformB);
|
||||
break;
|
||||
case ContactType.Grids:
|
||||
// TODO: Dis
|
||||
throw new NotImplementedException();
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"Collision between {FixtureA!.Shape.GetType()} and {FixtureB!.Shape.GetType()} not supported");
|
||||
}
|
||||
@@ -531,7 +513,6 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
|
||||
Rect,
|
||||
RectAndCircle,
|
||||
RectAndPolygon,
|
||||
Grids,
|
||||
}
|
||||
|
||||
public bool Equals(Contact? other)
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Collision;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -32,8 +29,6 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
|
||||
{
|
||||
internal sealed class ContactSolver
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
|
||||
private bool _warmStarting;
|
||||
private float _velocityThreshold;
|
||||
private float _baumgarte;
|
||||
@@ -52,24 +47,13 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
|
||||
private ContactVelocityConstraint[] _velocityConstraints = Array.Empty<ContactVelocityConstraint>();
|
||||
private ContactPositionConstraint[] _positionConstraints = Array.Empty<ContactPositionConstraint>();
|
||||
|
||||
public void Initialize()
|
||||
public void LoadConfig(in IslandCfg cfg)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_warmStarting = _configManager.GetCVar(CVars.WarmStarting);
|
||||
_configManager.OnValueChanged(CVars.WarmStarting, value => _warmStarting = value);
|
||||
|
||||
_velocityThreshold = _configManager.GetCVar(CVars.VelocityThreshold);
|
||||
_configManager.OnValueChanged(CVars.VelocityThreshold, value => _velocityThreshold = value);
|
||||
|
||||
_baumgarte = _configManager.GetCVar(CVars.Baumgarte);
|
||||
_configManager.OnValueChanged(CVars.Baumgarte, value => _baumgarte = value);
|
||||
|
||||
_linearSlop = _configManager.GetCVar(CVars.LinearSlop);
|
||||
_configManager.OnValueChanged(CVars.LinearSlop, value => _linearSlop = value);
|
||||
|
||||
_maxLinearCorrection = _configManager.GetCVar(CVars.MaxLinearCorrection);
|
||||
_configManager.OnValueChanged(CVars.MaxLinearCorrection, value => _maxLinearCorrection = value);
|
||||
_warmStarting = cfg.WarmStarting;
|
||||
_velocityThreshold = cfg.VelocityThreshold;
|
||||
_baumgarte = cfg.Baumgarte;
|
||||
_linearSlop = cfg.LinearSlop;
|
||||
_maxLinearCorrection = cfg.MaxLinearCorrection;
|
||||
}
|
||||
|
||||
public void Reset(SolverData data, int contactCount, Contact[] contacts)
|
||||
|
||||
@@ -427,9 +427,6 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
case PhysShapeCircle circle:
|
||||
ComputeCircle(circle);
|
||||
break;
|
||||
case PhysShapeGrid grid:
|
||||
ComputeGrid(grid);
|
||||
break;
|
||||
case PhysShapeRect rect:
|
||||
ComputeRect(rect);
|
||||
break;
|
||||
@@ -580,23 +577,6 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
{
|
||||
_centroid = (edge.Vertex1 + edge.Vertex2) * 0.5f;
|
||||
}
|
||||
|
||||
private void ComputeGrid(PhysShapeGrid grid)
|
||||
{
|
||||
var area = grid.LocalBounds.Width * grid.LocalBounds.Height;
|
||||
float I = 0.0f;
|
||||
|
||||
// Probably nothing bad happening if the area is 0?
|
||||
|
||||
// Total mass
|
||||
var density = area > 0.0f ? Mass / area : 0.0f;
|
||||
|
||||
// Center of mass
|
||||
_centroid = Vector2.Zero;
|
||||
|
||||
// Inertia tensor relative to the local origin (point s).
|
||||
_inertia = density * I;
|
||||
}
|
||||
#endregion
|
||||
|
||||
// This is a crude equals mainly to avoid having to re-create the fixtures every time a state comes in.
|
||||
|
||||
@@ -22,15 +22,12 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Containers;
|
||||
using System.Diagnostics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Dynamics.Contacts;
|
||||
using Robust.Shared.Physics.Dynamics.Joints;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Physics.Dynamics
|
||||
{
|
||||
@@ -140,13 +137,13 @@ stored in a single array since multiple arrays lead to multiple misses.
|
||||
*/
|
||||
public sealed class PhysicsIsland
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
// if you add new deps to this, the IoCManager inject dependencies is behind #if too.
|
||||
#if DEBUG
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
private List<IPhysBody> _debugBodies = new(8);
|
||||
#endif
|
||||
|
||||
private ContactSolver _contactSolver = default!;
|
||||
private readonly ContactSolver _contactSolver = new();
|
||||
|
||||
internal int ID { get; set; } = -1;
|
||||
|
||||
@@ -211,53 +208,25 @@ stored in a single array since multiple arrays lead to multiple misses.
|
||||
/// </summary>
|
||||
public int JointCount { get; private set; }
|
||||
|
||||
public void Initialize()
|
||||
[Conditional("DEBUG")]
|
||||
internal void Initialize()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_contactSolver = new ContactSolver();
|
||||
_contactSolver.Initialize();
|
||||
}
|
||||
|
||||
// Values
|
||||
_angTolSqr = MathF.Pow(_configManager.GetCVar(CVars.AngularSleepTolerance), 2);
|
||||
_configManager.OnValueChanged(CVars.AngularSleepTolerance, value => _angTolSqr = MathF.Pow(value, 2));
|
||||
internal void LoadConfig(in IslandCfg cfg)
|
||||
{
|
||||
_angTolSqr = cfg.AngTolSqr;
|
||||
_linTolSqr = cfg.LinTolSqr;
|
||||
_warmStarting = cfg.WarmStarting;
|
||||
_velocityIterations = cfg.VelocityIterations;
|
||||
_maxLinearVelocity = cfg.MaxLinearVelocity;
|
||||
_maxAngularVelocity = cfg.MaxAngularVelocity;
|
||||
_positionIterations = cfg.PositionIterations;
|
||||
_sleepAllowed = cfg.SleepAllowed;
|
||||
_timeToSleep = cfg.TimeToSleep;
|
||||
|
||||
_linTolSqr = MathF.Pow(_configManager.GetCVar(CVars.LinearSleepTolerance), 2);
|
||||
_configManager.OnValueChanged(CVars.LinearSleepTolerance, value => _linTolSqr = MathF.Pow(value, 2));
|
||||
|
||||
_warmStarting = _configManager.GetCVar(CVars.WarmStarting);
|
||||
_configManager.OnValueChanged(CVars.WarmStarting, value => _warmStarting = value);
|
||||
|
||||
_velocityIterations = _configManager.GetCVar(CVars.VelocityIterations);
|
||||
_configManager.OnValueChanged(CVars.VelocityIterations, value => _velocityIterations = value);
|
||||
|
||||
_configManager.OnValueChanged(CVars.MaxLinVelocity, value => SetMaxLinearVelocity(value, null));
|
||||
_configManager.OnValueChanged(CVars.NetTickrate, value => SetMaxLinearVelocity(null, value));
|
||||
void SetMaxLinearVelocity(float? maxLinVelocity = null, int? tickrate = null)
|
||||
{
|
||||
maxLinVelocity ??= _configManager.GetCVar(CVars.MaxLinVelocity);
|
||||
tickrate ??= _configManager.GetCVar(CVars.NetTickrate);
|
||||
_maxLinearVelocity = (float)(maxLinVelocity / tickrate);
|
||||
}
|
||||
SetMaxLinearVelocity();
|
||||
|
||||
_configManager.OnValueChanged(CVars.MaxAngVelocity, value => SetMaxAngularVelocity(value, null));
|
||||
_configManager.OnValueChanged(CVars.NetTickrate, value => SetMaxAngularVelocity(null, value));
|
||||
void SetMaxAngularVelocity(float? maxAngVelocity = null, int? tickrate = null)
|
||||
{
|
||||
maxAngVelocity ??= _configManager.GetCVar(CVars.MaxAngVelocity);
|
||||
tickrate ??= _configManager.GetCVar(CVars.NetTickrate);
|
||||
_maxAngularVelocity = (float)((MathF.PI * 2 * maxAngVelocity) / tickrate);
|
||||
}
|
||||
SetMaxAngularVelocity();
|
||||
|
||||
_positionIterations = _configManager.GetCVar(CVars.PositionIterations);
|
||||
_configManager.OnValueChanged(CVars.PositionIterations, value => _positionIterations = value);
|
||||
|
||||
_sleepAllowed = _configManager.GetCVar(CVars.SleepAllowed);
|
||||
_configManager.OnValueChanged(CVars.SleepAllowed, value => _sleepAllowed = value);
|
||||
|
||||
_timeToSleep = _configManager.GetCVar(CVars.TimeToSleep);
|
||||
_configManager.OnValueChanged(CVars.TimeToSleep, value => _timeToSleep = value);
|
||||
_contactSolver.LoadConfig(cfg);
|
||||
}
|
||||
|
||||
public void Append(List<IPhysBody> bodies, List<Contact> contacts, List<Joint> joints)
|
||||
@@ -631,4 +600,24 @@ stored in a single array since multiple arrays lead to multiple misses.
|
||||
public Vector2[] Positions { get; set; } = default!;
|
||||
public float[] Angles { get; set; } = default!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains all configuration parameters that need to be passed to physics islands.
|
||||
/// </summary>
|
||||
internal struct IslandCfg
|
||||
{
|
||||
public float AngTolSqr;
|
||||
public float LinTolSqr;
|
||||
public bool SleepAllowed;
|
||||
public bool WarmStarting;
|
||||
public int VelocityIterations;
|
||||
public float MaxLinearVelocity;
|
||||
public float MaxAngularVelocity;
|
||||
public int PositionIterations;
|
||||
public float TimeToSleep;
|
||||
public float VelocityThreshold;
|
||||
public float Baumgarte;
|
||||
public float LinearSlop;
|
||||
public float MaxLinearCorrection;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,11 @@ subject to the following restrictions:
|
||||
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -31,6 +35,8 @@ namespace Robust.Shared.Physics
|
||||
/// </summary>
|
||||
internal sealed class IslandManager : IIslandManager
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
private static readonly IslandBodyCapacitySort CapacitySort = new();
|
||||
private static readonly IslandBodyCountSort CountSort = new();
|
||||
|
||||
@@ -67,13 +73,83 @@ namespace Robust.Shared.Physics
|
||||
}
|
||||
}
|
||||
|
||||
private int _tickRate;
|
||||
private float _maxLinearVelocityRaw;
|
||||
private float _maxAngularVelocityRaw;
|
||||
private IslandCfg _islandCfg;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
InitConfig();
|
||||
|
||||
_loneIsland.Initialize();
|
||||
// Set an initial size so we don't spam a bunch of array resizes at the start
|
||||
_loneIsland.Resize(64, 32, 8);
|
||||
}
|
||||
|
||||
private void InitConfig()
|
||||
{
|
||||
// Config stuff.
|
||||
CfgVar(CVars.AngularSleepTolerance, value => _islandCfg.AngTolSqr = value * value);
|
||||
CfgVar(CVars.LinearSleepTolerance, value => _islandCfg.LinTolSqr = value * value);
|
||||
CfgVar(CVars.WarmStarting, value => _islandCfg.WarmStarting = value);
|
||||
CfgVar(CVars.VelocityIterations, value => _islandCfg.VelocityIterations = value);
|
||||
CfgVar(CVars.PositionIterations, value => _islandCfg.PositionIterations = value);
|
||||
CfgVar(CVars.SleepAllowed, value => _islandCfg.SleepAllowed = value);
|
||||
CfgVar(CVars.TimeToSleep, value => _islandCfg.TimeToSleep = value);
|
||||
CfgVar(CVars.VelocityThreshold, value => _islandCfg.VelocityThreshold = value);
|
||||
CfgVar(CVars.Baumgarte, value => _islandCfg.Baumgarte = value);
|
||||
CfgVar(CVars.LinearSlop, value => _islandCfg.LinearSlop = value);
|
||||
CfgVar(CVars.MaxLinearCorrection, value => _islandCfg.MaxLinearCorrection = value);
|
||||
CfgVar(CVars.MaxLinVelocity, value =>
|
||||
{
|
||||
_maxLinearVelocityRaw = value;
|
||||
UpdateMaxLinearVelocity();
|
||||
});
|
||||
CfgVar(CVars.MaxAngVelocity, value =>
|
||||
{
|
||||
_maxAngularVelocityRaw = value;
|
||||
UpdateMaxAngularVelocity();
|
||||
});
|
||||
CfgVar(CVars.NetTickrate, value =>
|
||||
{
|
||||
_tickRate = value;
|
||||
UpdateMaxLinearVelocity();
|
||||
UpdateMaxAngularVelocity();
|
||||
});
|
||||
|
||||
void UpdateMaxLinearVelocity()
|
||||
{
|
||||
_islandCfg.MaxLinearVelocity = _maxLinearVelocityRaw / _tickRate;
|
||||
}
|
||||
|
||||
void UpdateMaxAngularVelocity()
|
||||
{
|
||||
_islandCfg.MaxAngularVelocity = (MathF.PI * 2 * _maxAngularVelocityRaw) / _tickRate;
|
||||
}
|
||||
|
||||
void CfgVar<T>(CVarDef<T> cVar, Action<T> callback) where T : notnull
|
||||
{
|
||||
_cfg.OnValueChanged(cVar, value =>
|
||||
{
|
||||
callback(value);
|
||||
UpdateIslandCfg();
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateIslandCfg()
|
||||
{
|
||||
// OOP bad
|
||||
|
||||
_loneIsland.LoadConfig(_islandCfg);
|
||||
|
||||
foreach (var island in _allocatedIslands)
|
||||
{
|
||||
island.LoadConfig(_islandCfg);
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializePools()
|
||||
{
|
||||
_loneIsland.Clear();
|
||||
@@ -159,6 +235,7 @@ namespace Robust.Shared.Physics
|
||||
{
|
||||
island = new PhysicsIsland();
|
||||
island.Initialize();
|
||||
island.LoadConfig(_islandCfg);
|
||||
_allocatedIslands.Add(island);
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +131,8 @@ namespace Robust.Shared.Physics
|
||||
public float C;
|
||||
public float S;
|
||||
|
||||
public float Angle => MathF.Atan2(S, C);
|
||||
|
||||
public Quaternion2D(float cos, float sin)
|
||||
{
|
||||
C = cos;
|
||||
|
||||
@@ -214,9 +214,9 @@ namespace Robust.Shared.Serialization
|
||||
/// <seealso cref="OnClientCompleteHandshake"/>
|
||||
private void NetworkInitialize()
|
||||
{
|
||||
_net.RegisterNetMessage<MsgMapStrServerHandshake>(HandleServerHandshake, NetMessageAccept.Client);
|
||||
_net.RegisterNetMessage<MsgMapStrClientHandshake>(HandleClientHandshake, NetMessageAccept.Server);
|
||||
_net.RegisterNetMessage<MsgMapStrStrings>(HandleStringsMessage, NetMessageAccept.Client);
|
||||
_net.RegisterNetMessage<MsgMapStrServerHandshake>(HandleServerHandshake, NetMessageAccept.Client | NetMessageAccept.Handshake);
|
||||
_net.RegisterNetMessage<MsgMapStrClientHandshake>(HandleClientHandshake, NetMessageAccept.Server | NetMessageAccept.Handshake);
|
||||
_net.RegisterNetMessage<MsgMapStrStrings>(HandleStringsMessage, NetMessageAccept.Client | NetMessageAccept.Handshake);
|
||||
|
||||
_net.Disconnect += NetOnDisconnect;
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Robust.Shared.Utility
|
||||
{
|
||||
public static class ProcessExt
|
||||
{
|
||||
public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
|
||||
void ProcessExited(object? sender, EventArgs e)
|
||||
{
|
||||
tcs.TrySetResult(true);
|
||||
}
|
||||
|
||||
process.EnableRaisingEvents = true;
|
||||
process.Exited += ProcessExited;
|
||||
|
||||
try
|
||||
{
|
||||
if (process.HasExited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (cancellationToken.Register(() => tcs.TrySetCanceled()))
|
||||
{
|
||||
await tcs.Task;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
process.Exited -= ProcessExited;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using NUnit.Framework;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Maths;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.UnitTesting.Client.UserInterface.Controls
|
||||
{
|
||||
@@ -15,7 +16,11 @@ namespace Robust.UnitTesting.Client.UserInterface.Controls
|
||||
public void TestLayoutBasic()
|
||||
{
|
||||
var root = new LayoutContainer();
|
||||
var boxContainer = new VBoxContainer {MinSize = (50, 60)};
|
||||
var boxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
MinSize = (50, 60)
|
||||
};
|
||||
var control1 = new Control {MinSize = (20, 20)};
|
||||
var control2 = new Control {MinSize = (30, 30)};
|
||||
|
||||
@@ -37,7 +42,11 @@ namespace Robust.UnitTesting.Client.UserInterface.Controls
|
||||
public void TestLayoutExpand()
|
||||
{
|
||||
var root = new LayoutContainer();
|
||||
var boxContainer = new VBoxContainer {MinSize = (50, 60)};
|
||||
var boxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
MinSize = (50, 60)
|
||||
};
|
||||
var control1 = new Control
|
||||
{
|
||||
VerticalExpand = true
|
||||
@@ -61,7 +70,10 @@ namespace Robust.UnitTesting.Client.UserInterface.Controls
|
||||
[Test]
|
||||
public void TestCalcMinSize()
|
||||
{
|
||||
var boxContainer = new VBoxContainer();
|
||||
var boxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
var control1 = new Control
|
||||
{
|
||||
MinSize = (50, 30)
|
||||
@@ -80,7 +92,11 @@ namespace Robust.UnitTesting.Client.UserInterface.Controls
|
||||
public void TestTwoExpand()
|
||||
{
|
||||
var root = new LayoutContainer();
|
||||
var boxContainer = new VBoxContainer {MinSize = (30, 80)};
|
||||
var boxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
MinSize = (30, 80)
|
||||
};
|
||||
var control1 = new Control
|
||||
{
|
||||
VerticalExpand = true,
|
||||
|
||||
@@ -387,6 +387,8 @@ namespace Robust.UnitTesting
|
||||
|
||||
public bool IsConnected { get; set; }
|
||||
public NetUserData UserData { get; }
|
||||
// integration tests don't simulate serializer handshake so this is always true.
|
||||
public bool IsHandshakeComplete => true;
|
||||
|
||||
// TODO: Should this port value make sense?
|
||||
public IPEndPoint RemoteEndPoint { get; } = new(IPAddress.Loopback, 1212);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user