mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
5 Commits
reactjs-su
...
2022_03_30
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e2063268f | ||
|
|
d2f5b05503 | ||
|
|
bf8c972eb2 | ||
|
|
1af1e3f768 | ||
|
|
1dab3cc567 |
6
.github/workflows/benchmarks.yml
vendored
6
.github/workflows/benchmarks.yml
vendored
@@ -1,6 +1,6 @@
|
||||
name: Benchmarks
|
||||
#on:
|
||||
# push
|
||||
on:
|
||||
push
|
||||
#schedule:
|
||||
# - cron: '0 5 * * *'
|
||||
#push:
|
||||
@@ -30,4 +30,4 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Run benchmark
|
||||
run: cd Robust.Benchmarks && sudo dotnet run --filter '*' --configuration Release
|
||||
run: cd Robust.Benchmarks && sudo -E dotnet run --filter 'Robust.Benchmarks.NumericsHelpers.AddBenchmark*' --configuration Release
|
||||
|
||||
Submodule Lidgren.Network/Lidgren.Network updated: b723fc532e...1dd5c1f333
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<PropertyGroup><Version>0.8.86</Version></PropertyGroup>
|
||||
<PropertyGroup><Version>0.8.82</Version></PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -18,8 +18,12 @@ namespace Robust.Benchmarks
|
||||
Console.WriteLine("THE DEBUG BUILD IS ONLY GOOD FOR FIXING A CRASHING BENCHMARK\n");
|
||||
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new DebugInProcessConfig());
|
||||
#else
|
||||
var config = Environment.GetEnvironmentVariable("ROBUST_BENCHMARKS_ENABLE_SQL") != null ? DefaultSQLConfig.Instance : null;
|
||||
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config);
|
||||
var val = Environment.GetEnvironmentVariable("ROBUST_BENCHMARKS_ENABLE_SQL");
|
||||
Console.WriteLine($"Value of env var: '{val}'");
|
||||
var sqlBenchmarks = val != null;
|
||||
if(sqlBenchmarks)
|
||||
Console.WriteLine("Found ROBUST_BENCHMARKS_ENABLE_SQL, using SQLExporter.");
|
||||
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, sqlBenchmarks ? DefaultSQLConfig.Instance : null);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace Robust.Client.GameObjects
|
||||
public override void Initialize()
|
||||
{
|
||||
SetupNetworking();
|
||||
ReceivedComponentMessage += (_, compMsg) => DispatchComponentMessage(compMsg);
|
||||
ReceivedSystemMessage += (_, systemMsg) => EventBus.RaiseEvent(EventSource.Network, systemMsg);
|
||||
|
||||
base.Initialize();
|
||||
@@ -51,6 +52,9 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public override IEntityNetworkManager EntityNetManager => this;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<NetworkComponentMessage>? ReceivedComponentMessage;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<object>? ReceivedSystemMessage;
|
||||
|
||||
@@ -101,6 +105,26 @@ namespace Robust.Client.GameObjects
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
||||
public void SendComponentNetworkMessage(INetChannel? channel, EntityUid entity, IComponent component, ComponentMessage message)
|
||||
{
|
||||
var componentType = component.GetType();
|
||||
var netId = ComponentFactory.GetRegistration(componentType).NetID;
|
||||
|
||||
if (!netId.HasValue)
|
||||
throw new ArgumentException($"Component {componentType} does not have a NetID.", nameof(component));
|
||||
|
||||
var msg = _networkManager.CreateNetMessage<MsgEntity>();
|
||||
msg.Type = EntityMessageType.ComponentMessage;
|
||||
msg.EntityUid = entity;
|
||||
msg.NetId = netId.Value;
|
||||
msg.ComponentMessage = message;
|
||||
msg.SourceTick = _gameTiming.CurTick;
|
||||
|
||||
_networkManager.ClientSendMessage(msg);
|
||||
}
|
||||
|
||||
private void HandleEntityNetworkMessage(MsgEntity message)
|
||||
{
|
||||
if (message.SourceTick <= _gameStateManager.CurServerTick)
|
||||
@@ -119,6 +143,10 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
switch (message.Type)
|
||||
{
|
||||
case EntityMessageType.ComponentMessage:
|
||||
ReceivedComponentMessage?.Invoke(this, new NetworkComponentMessage(message));
|
||||
return;
|
||||
|
||||
case EntityMessageType.SystemMessage:
|
||||
var msg = message.SystemMessage;
|
||||
var sessionType = typeof(EntitySessionMessage<>).MakeGenericType(msg.GetType());
|
||||
|
||||
@@ -25,8 +25,6 @@ namespace Robust.Client.GameObjects
|
||||
[Animatable]
|
||||
Vector2 Scale { get; set; }
|
||||
|
||||
Box2 Bounds { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A rotation applied to all layers.
|
||||
/// </summary>
|
||||
|
||||
@@ -35,6 +35,6 @@ namespace Robust.Client.GameObjects
|
||||
/// Calculate layer bounding box in sprite local-space coordinates.
|
||||
/// </summary>
|
||||
/// <returns>Bounding box in sprite local-space coordinates.</returns>
|
||||
Box2 CalculateBoundingBox();
|
||||
Box2 CalculateBoundingBox(Angle worldAngle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using TerraFX.Interop.Windows;
|
||||
using DrawDepthTag = Robust.Shared.GameObjects.DrawDepth;
|
||||
using RSIDirection = Robust.Client.Graphics.RSI.State.Direction;
|
||||
|
||||
@@ -218,10 +217,6 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private bool _containerOccluded;
|
||||
|
||||
private Box2 _bounds;
|
||||
|
||||
public Box2 Bounds => _bounds;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool TreeUpdateQueued { get; set; }
|
||||
|
||||
@@ -304,7 +299,6 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
//deep copying things to avoid entanglement
|
||||
_baseRsi = other._baseRsi;
|
||||
_bounds = other._bounds;
|
||||
_visible = other._visible;
|
||||
_layerMapShared = other._layerMapShared;
|
||||
color = other.color;
|
||||
@@ -554,7 +548,6 @@ namespace Robust.Client.GameObjects
|
||||
index = Layers.Count - 1;
|
||||
}
|
||||
|
||||
RebuildBounds();
|
||||
QueueUpdateIsInert();
|
||||
return index;
|
||||
}
|
||||
@@ -582,7 +575,6 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
RebuildBounds();
|
||||
QueueUpdateIsInert();
|
||||
}
|
||||
|
||||
@@ -598,15 +590,6 @@ namespace Robust.Client.GameObjects
|
||||
RemoveLayer(layer);
|
||||
}
|
||||
|
||||
private void RebuildBounds()
|
||||
{
|
||||
_bounds = new Box2();
|
||||
foreach (var layer in Layers.Where(layer => layer.Visible))
|
||||
{
|
||||
_bounds = _bounds.Union(layer.CalculateBoundingBox());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills in a layer's values using some <see cref="PrototypeLayerData"/>.
|
||||
/// </summary>
|
||||
@@ -730,8 +713,6 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
// If neither state: nor texture: were provided we assume that they want a blank invisible layer.
|
||||
layer.Visible = anyTextureAttempted && layerDatum.Visible;
|
||||
|
||||
RebuildBounds();
|
||||
}
|
||||
|
||||
public void LayerSetData(object layerKey, PrototypeLayerData data)
|
||||
@@ -818,8 +799,6 @@ namespace Robust.Client.GameObjects
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
RebuildBounds();
|
||||
}
|
||||
|
||||
public void LayerSetSprite(object layerKey, SpriteSpecifier specifier)
|
||||
@@ -845,7 +824,6 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
var theLayer = Layers[layer];
|
||||
theLayer.SetTexture(texture);
|
||||
RebuildBounds();
|
||||
}
|
||||
|
||||
public void LayerSetTexture(object layerKey, Texture texture)
|
||||
@@ -911,7 +889,6 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
var theLayer = Layers[layer];
|
||||
theLayer.SetState(stateId);
|
||||
RebuildBounds();
|
||||
}
|
||||
|
||||
public void LayerSetState(object layerKey, RSI.StateId stateId)
|
||||
@@ -959,8 +936,6 @@ namespace Robust.Client.GameObjects
|
||||
theLayer.Texture = null;
|
||||
}
|
||||
}
|
||||
|
||||
RebuildBounds();
|
||||
}
|
||||
|
||||
public void LayerSetState(object layerKey, RSI.StateId stateId, RSI rsi)
|
||||
@@ -1018,7 +993,6 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
var theLayer = Layers[layer];
|
||||
theLayer.SetRsi(rsi);
|
||||
RebuildBounds();
|
||||
}
|
||||
|
||||
public void LayerSetRSI(object layerKey, RSI rsi)
|
||||
@@ -1076,7 +1050,6 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
var theLayer = Layers[layer];
|
||||
theLayer.Scale = scale;
|
||||
RebuildBounds();
|
||||
}
|
||||
|
||||
public void LayerSetScale(object layerKey, Vector2 scale)
|
||||
@@ -1103,7 +1076,6 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
var theLayer = Layers[layer];
|
||||
theLayer.Rotation = rotation;
|
||||
RebuildBounds();
|
||||
}
|
||||
|
||||
public void LayerSetRotation(object layerKey, Angle rotation)
|
||||
@@ -1153,8 +1125,6 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
var theLayer = Layers[layer];
|
||||
theLayer.Color = color;
|
||||
|
||||
RebuildBounds();
|
||||
}
|
||||
|
||||
public void LayerSetColor(object layerKey, Color color)
|
||||
@@ -1180,8 +1150,6 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
var theLayer = Layers[layer];
|
||||
theLayer.DirOffset = offset;
|
||||
|
||||
RebuildBounds();
|
||||
}
|
||||
|
||||
public void LayerSetDirOffset(object layerKey, DirectionOffset offset)
|
||||
@@ -1233,8 +1201,6 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
Layers[layer].SetAutoAnimated(autoAnimated);
|
||||
|
||||
RebuildBounds();
|
||||
}
|
||||
|
||||
public void LayerSetAutoAnimated(object layerKey, bool autoAnimated)
|
||||
@@ -1260,8 +1226,6 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
Layers[layer].Offset = layerOffset;
|
||||
|
||||
RebuildBounds();
|
||||
}
|
||||
|
||||
public void LayerSetOffset(object layerKey, Vector2 layerOffset)
|
||||
@@ -1572,10 +1536,24 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
eye ??= eyeManager.CurrentEye;
|
||||
|
||||
// Need relative angle on screen for determining the sprite rsi direction.
|
||||
Angle relativeRotation = NoRotation
|
||||
? Angle.Zero
|
||||
: worldRotation + eye.Rotation;
|
||||
|
||||
// we need to calculate bounding box taking into account all nested layers
|
||||
// because layers can have offsets, scale or rotation, we need to calculate a new BB
|
||||
// based on lowest bottomLeft and highest topRight points from all layers
|
||||
var box = Bounds;
|
||||
var box = Layers[0].CalculateBoundingBox(relativeRotation);
|
||||
|
||||
for (int i = 1; i < Layers.Count; i++)
|
||||
{
|
||||
var layer = Layers[i];
|
||||
if (!layer.Visible) continue;
|
||||
var layerBB = layer.CalculateBoundingBox(relativeRotation);
|
||||
|
||||
box = box.Union(layerBB);
|
||||
}
|
||||
|
||||
// Next, what we do is take the box2 and apply the sprite's transform, and then the entity's transform. We
|
||||
// could do this via Matrix3.TransformBox, but that only yields bounding boxes. So instead we manually
|
||||
@@ -1871,7 +1849,6 @@ namespace Robust.Client.GameObjects
|
||||
Visible = value;
|
||||
|
||||
_parent.QueueUpdateIsInert();
|
||||
_parent.RebuildBounds();
|
||||
}
|
||||
|
||||
public void SetRsi(RSI? rsi)
|
||||
@@ -1966,40 +1943,39 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Box2 CalculateBoundingBox()
|
||||
public Box2 CalculateBoundingBox(Angle angle)
|
||||
{
|
||||
var textureSize = PixelSize / EyeManager.PixelsPerMeter;
|
||||
|
||||
// If the parent has locked rotation and we don't have any rotation,
|
||||
// we can take the quick path of just making a box the size of the texture.
|
||||
if (_parent.NoRotation && _rotation != 0)
|
||||
{
|
||||
return Box2.CenteredAround(Offset, textureSize).Scale(_scale);
|
||||
}
|
||||
// Other than some special cases for simple layers, this will basically just apply the matrix obtained
|
||||
// via GetLayerDrawMatrix() to this layer's bounding box.
|
||||
|
||||
var rsiState = GetActualState();
|
||||
|
||||
var longestSide = MathF.Max(textureSize.X, textureSize.Y);
|
||||
var longestRotatedSide = Math.Max(longestSide, (textureSize.X + textureSize.Y) / MathF.Sqrt(2));
|
||||
var dir = (rsiState == null || rsiState.Directions == RSI.State.DirectionType.Dir1)
|
||||
? RSIDirection.South
|
||||
: angle.ToRsiDirection(rsiState.Directions);
|
||||
|
||||
// Build the bounding box based on how many directions the sprite has
|
||||
var box = (_rotation != 0, rsiState) switch
|
||||
// special case for simple layers. The vast majority of layers are like this.
|
||||
if (_rotation == Angle.Zero)
|
||||
{
|
||||
// If this layer has any form of arbitrary rotation, return a bounding box big enough to cover
|
||||
// any possible rotation.
|
||||
(true, _) => Box2.CenteredAround(Offset, new Vector2(longestRotatedSide, longestRotatedSide)),
|
||||
var textureSize = PixelSize / EyeManager.PixelsPerMeter;
|
||||
|
||||
// Otherwise...
|
||||
// If we have only one direction or an invalid RSI state, create a simple bounding box with the size of the texture.
|
||||
(_, {Directions: RSI.State.DirectionType.Dir1} or null) => Box2.CenteredAround(Offset, textureSize),
|
||||
// If we have four cardinal directions, take the longest side of our texture and square it, then turn that into our bounding box.
|
||||
// This accounts for all possible rotations.
|
||||
(_, {Directions: RSI.State.DirectionType.Dir4}) => Box2.CenteredAround(Offset, new Vector2(longestSide, longestSide)),
|
||||
// If we have eight directions, find the maximum length of the texture (accounting for rotation), then square it to make
|
||||
// our bounding box.
|
||||
(_, {Directions: RSI.State.DirectionType.Dir8}) => Box2.CenteredAround(Offset, new Vector2(longestRotatedSide, longestRotatedSide)),
|
||||
};
|
||||
return _scale == Vector2.One ? box : box.Scale(_scale);
|
||||
// this switch block is basically an explicit version of the `rsiDirectionMatrix` in `GetLayerDrawMatrix()`.
|
||||
var box = dir switch
|
||||
{
|
||||
// No rotation:
|
||||
RSIDirection.South or RSIDirection.North => Box2.CenteredAround(Offset, textureSize),
|
||||
// rotate 90 degrees:
|
||||
RSIDirection.East or RSIDirection.West => Box2.CenteredAround(Offset, (textureSize.Y, textureSize.X)),
|
||||
// rotated 45 degrees (any 45 degree rotated rectangle has a square bounding box with sides of length (x+y)/sqrt(2) )
|
||||
_ => Box2.CenteredAround(Offset, Vector2.One * (textureSize.X + textureSize.Y) / MathF.Sqrt(2))
|
||||
};
|
||||
|
||||
return _scale == Vector2.One ? box : box.Scale(_scale);
|
||||
}
|
||||
|
||||
// Welp we have some non-zero _rotation, so lets just apply the generalized layer transform and get the bounding box from where;
|
||||
GetLayerDrawMatrix(dir, out var layerDrawMatrix);
|
||||
return layerDrawMatrix.TransformBox(Box2.CentredAroundZero(PixelSize / EyeManager.PixelsPerMeter));
|
||||
}
|
||||
|
||||
internal RSI.State? GetActualState()
|
||||
@@ -2023,27 +1999,15 @@ namespace Robust.Client.GameObjects
|
||||
/// </summary>
|
||||
public void GetLayerDrawMatrix(RSIDirection dir, out Matrix3 layerDrawMatrix)
|
||||
{
|
||||
if (_parent.NoRotation || dir == RSIDirection.South)
|
||||
if (_parent.NoRotation)
|
||||
layerDrawMatrix = LocalMatrix;
|
||||
else
|
||||
{
|
||||
Matrix3.Multiply(ref _rsiDirectionMatrices[(int)dir], ref LocalMatrix, out layerDrawMatrix);
|
||||
var rsiDirectionMatrix = Matrix3.CreateTransform(Vector2.Zero, -dir.Convert().ToAngle());
|
||||
Matrix3.Multiply(ref rsiDirectionMatrix, ref LocalMatrix, out layerDrawMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
private static Matrix3[] _rsiDirectionMatrices = new Matrix3[]
|
||||
{
|
||||
// array order chosen such that this array can be indexed by casing an RSI direction to an int
|
||||
Matrix3.Identity, // should probably just avoid matrix multiplication altogether if the direction is south.
|
||||
Matrix3.CreateRotation(-Direction.North.ToAngle()),
|
||||
Matrix3.CreateRotation(-Direction.East.ToAngle()),
|
||||
Matrix3.CreateRotation(-Direction.West.ToAngle()),
|
||||
Matrix3.CreateRotation(-Direction.SouthEast.ToAngle()),
|
||||
Matrix3.CreateRotation(-Direction.SouthWest.ToAngle()),
|
||||
Matrix3.CreateRotation(-Direction.NorthEast.ToAngle()),
|
||||
Matrix3.CreateRotation(-Direction.NorthWest.ToAngle())
|
||||
};
|
||||
|
||||
internal void Render(DrawingHandleWorld drawingHandle, ref Matrix3 spriteMatrix, Angle angle, Direction? overrideDirection)
|
||||
{
|
||||
if (!Visible)
|
||||
|
||||
@@ -342,6 +342,7 @@ namespace Robust.Client.GameObjects
|
||||
var map = _owner.eyeManager.CurrentMap;
|
||||
|
||||
var worldHandle = args.WorldHandle;
|
||||
ShaderInstance? currentShader = null;
|
||||
|
||||
if (_playerManager.LocalPlayer?.ControlledEntity is not {} playerEnt)
|
||||
return;
|
||||
@@ -361,8 +362,13 @@ namespace Robust.Client.GameObjects
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!effect.Shaded)
|
||||
worldHandle.UseShader(_unshadedShader);
|
||||
var newShader = effect.Shaded ? null : _unshadedShader;
|
||||
|
||||
if (newShader != currentShader)
|
||||
{
|
||||
worldHandle.UseShader(newShader);
|
||||
currentShader = newShader;
|
||||
}
|
||||
|
||||
// TODO: Should be doing matrix transformations
|
||||
var effectSprite = effect.EffectSprite;
|
||||
@@ -383,9 +389,6 @@ namespace Robust.Client.GameObjects
|
||||
var rotatedBox = new Box2Rotated(effectArea, effect.Rotation + rotation, effectOrigin);
|
||||
|
||||
worldHandle.DrawTextureRect(effectSprite, rotatedBox, ToColor(effect.Color));
|
||||
|
||||
if (!effect.Shaded)
|
||||
worldHandle.UseShader(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,12 +152,9 @@ namespace Robust.Client.Graphics
|
||||
/// <summary>
|
||||
/// Specifies a direction in an RSI state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Value of the enum here matches the index used to store it in the icons array. If this ever changes, then
|
||||
/// <see cref="GameObjects.SpriteComponent.Layer._rsiDirectionMatrices"/> also needs to be updated.
|
||||
/// </remarks>
|
||||
public enum Direction : byte
|
||||
{
|
||||
// Value of the enum here matches the index used to store it in the icons array.
|
||||
South = 0,
|
||||
North = 1,
|
||||
East = 2,
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace Robust.Server.GameObjects
|
||||
public override void Initialize()
|
||||
{
|
||||
SetupNetworking();
|
||||
ReceivedComponentMessage += (_, compMsg) => DispatchComponentMessage(compMsg);
|
||||
ReceivedSystemMessage += (_, systemMsg) => EventBus.RaiseEvent(EventSource.Network, systemMsg);
|
||||
|
||||
base.Initialize();
|
||||
@@ -97,6 +98,9 @@ namespace Robust.Server.GameObjects
|
||||
|
||||
public override IEntityNetworkManager EntityNetManager => this;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<NetworkComponentMessage>? ReceivedComponentMessage;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<object>? ReceivedSystemMessage;
|
||||
|
||||
@@ -203,6 +207,35 @@ namespace Robust.Server.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
||||
public void SendComponentNetworkMessage(INetChannel? channel, EntityUid entity, IComponent component,
|
||||
ComponentMessage message)
|
||||
{
|
||||
if (_networkManager.IsClient)
|
||||
return;
|
||||
|
||||
var netId = ComponentFactory.GetRegistration(component.GetType()).NetID;
|
||||
|
||||
if (!netId.HasValue)
|
||||
throw new ArgumentException($"Component {component.GetType()} does not have a NetID.", nameof(component));
|
||||
|
||||
var msg = _networkManager.CreateNetMessage<MsgEntity>();
|
||||
msg.Type = EntityMessageType.ComponentMessage;
|
||||
msg.EntityUid = entity;
|
||||
msg.NetId = netId.Value;
|
||||
msg.ComponentMessage = message;
|
||||
msg.SourceTick = _gameTiming.CurTick;
|
||||
|
||||
// Logger.DebugS("net.ent", "Sending: {0}", msg);
|
||||
|
||||
//Send the message
|
||||
if (channel == null)
|
||||
_networkManager.ServerSendToAll(msg);
|
||||
else
|
||||
_networkManager.ServerSendMessage(msg, channel);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SendSystemNetworkMessage(EntityEventArgs message)
|
||||
{
|
||||
@@ -269,6 +302,10 @@ namespace Robust.Server.GameObjects
|
||||
{
|
||||
switch (message.Type)
|
||||
{
|
||||
case EntityMessageType.ComponentMessage:
|
||||
ReceivedComponentMessage?.Invoke(this, new NetworkComponentMessage(message, player));
|
||||
return;
|
||||
|
||||
case EntityMessageType.SystemMessage:
|
||||
var msg = message.SystemMessage;
|
||||
var sessionType = typeof(EntitySessionMessage<>).MakeGenericType(msg.GetType());
|
||||
|
||||
@@ -8,7 +8,6 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Server.GameStates;
|
||||
|
||||
@@ -33,13 +32,6 @@ public interface IPVSCollection
|
||||
/// </summary>
|
||||
/// <param name="tick">The <see cref="GameTick"/> before which all deletions should be removed.</param>
|
||||
public void CullDeletionHistoryUntil(GameTick tick);
|
||||
|
||||
public bool IsDirty(IChunkIndexLocation location);
|
||||
|
||||
public bool MarkDirty(IChunkIndexLocation location);
|
||||
|
||||
public void ClearDirty();
|
||||
|
||||
}
|
||||
|
||||
public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : IComparable<TIndex>, IEquatable<TIndex>
|
||||
@@ -118,6 +110,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
|
||||
public void Process()
|
||||
{
|
||||
_dirtyChunks.Clear();
|
||||
_changedIndices.EnsureCapacity(_locationChangeBuffer.Count);
|
||||
|
||||
foreach (var (key, loc) in _locationChangeBuffer)
|
||||
@@ -146,16 +139,12 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
switch (chunkLocation)
|
||||
{
|
||||
case GridChunkLocation gridChunkLocation:
|
||||
if(!_gridChunkContents.TryGetValue(gridChunkLocation.GridId, out var gridChunks)) continue;
|
||||
if(!gridChunks.TryGetValue(gridChunkLocation.ChunkIndices, out var chunk)) continue;
|
||||
if(chunk.Count == 0)
|
||||
gridChunks.Remove(gridChunkLocation.ChunkIndices);
|
||||
if (_gridChunkContents[gridChunkLocation.GridId][gridChunkLocation.ChunkIndices].Count == 0)
|
||||
_gridChunkContents[gridChunkLocation.GridId].Remove(gridChunkLocation.ChunkIndices);
|
||||
break;
|
||||
case MapChunkLocation mapChunkLocation:
|
||||
if(!_mapChunkContents.TryGetValue(mapChunkLocation.MapId, out var mapChunks)) continue;
|
||||
if(!mapChunks.TryGetValue(mapChunkLocation.ChunkIndices, out chunk)) continue;
|
||||
if(chunk.Count == 0)
|
||||
mapChunks.Remove(mapChunkLocation.ChunkIndices);
|
||||
if (_mapChunkContents[mapChunkLocation.MapId][mapChunkLocation.ChunkIndices].Count == 0)
|
||||
_mapChunkContents[mapChunkLocation.MapId].Remove(mapChunkLocation.ChunkIndices);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -176,10 +165,6 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
|
||||
public bool IsDirty(IChunkIndexLocation location) => _dirtyChunks.Contains(location);
|
||||
|
||||
public bool MarkDirty(IChunkIndexLocation location) => _dirtyChunks.Add(location);
|
||||
|
||||
public void ClearDirty() => _dirtyChunks.Clear();
|
||||
|
||||
public bool TryGetChunk(MapId mapId, Vector2i chunkIndices, [NotNullWhen(true)] out HashSet<TIndex>? indices) =>
|
||||
_mapChunkContents[mapId].TryGetValue(chunkIndices, out indices);
|
||||
|
||||
@@ -197,9 +182,10 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
break;
|
||||
case GridChunkLocation gridChunkLocation:
|
||||
// might be gone due to grid-deletions
|
||||
if(!_gridChunkContents.TryGetValue(gridChunkLocation.GridId, out var gridChunk)) return;
|
||||
var gridLoc = gridChunk.GetOrNew(gridChunkLocation.ChunkIndices);
|
||||
gridLoc.Add(index);
|
||||
if(!_gridChunkContents.ContainsKey(gridChunkLocation.GridId)) return;
|
||||
if(!_gridChunkContents[gridChunkLocation.GridId].ContainsKey(gridChunkLocation.ChunkIndices))
|
||||
_gridChunkContents[gridChunkLocation.GridId][gridChunkLocation.ChunkIndices] = new();
|
||||
_gridChunkContents[gridChunkLocation.GridId][gridChunkLocation.ChunkIndices].Add(index);
|
||||
dirtyChunks.Add(gridChunkLocation);
|
||||
break;
|
||||
case LocalOverride localOverride:
|
||||
@@ -209,9 +195,10 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
break;
|
||||
case MapChunkLocation mapChunkLocation:
|
||||
// might be gone due to map-deletions
|
||||
if(!_mapChunkContents.TryGetValue(mapChunkLocation.MapId, out var mapChunk)) return;
|
||||
var mapLoc = mapChunk.GetOrNew(mapChunkLocation.ChunkIndices);
|
||||
mapLoc.Add(index);
|
||||
if(!_mapChunkContents.ContainsKey(mapChunkLocation.MapId)) return;
|
||||
if(!_mapChunkContents[mapChunkLocation.MapId].ContainsKey(mapChunkLocation.ChunkIndices))
|
||||
_mapChunkContents[mapChunkLocation.MapId][mapChunkLocation.ChunkIndices] = new();
|
||||
_mapChunkContents[mapChunkLocation.MapId][mapChunkLocation.ChunkIndices].Add(index);
|
||||
dirtyChunks.Add(mapChunkLocation);
|
||||
break;
|
||||
}
|
||||
@@ -403,20 +390,6 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
UpdateIndex(index, mapCoordinates.MapId, mapIndices, true); //skip overridecheck bc we already did it (saves some dict lookups)
|
||||
}
|
||||
|
||||
public IChunkIndexLocation GetChunkIndex(EntityCoordinates coordinates)
|
||||
{
|
||||
var gridId = coordinates.GetGridId(_entityManager);
|
||||
if (gridId != GridId.Invalid)
|
||||
{
|
||||
var gridIndices = GetChunkIndices(coordinates.Position);
|
||||
return new GridChunkLocation(gridId, gridIndices);
|
||||
}
|
||||
|
||||
var mapCoordinates = coordinates.ToMap(_entityManager);
|
||||
var mapIndices = GetChunkIndices(coordinates.Position);
|
||||
return new MapChunkLocation(mapCoordinates.MapId, mapIndices);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an <see cref="TIndex"/> using the provided <see cref="gridId"/> and <see cref="chunkIndices"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -113,7 +114,6 @@ internal sealed partial class PVSSystem : EntitySystem
|
||||
|
||||
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
SubscribeLocalEvent<MoveEvent>(OnEntityMove);
|
||||
SubscribeLocalEvent<EntParentChangedMessage>(OnParentChange);
|
||||
SubscribeLocalEvent<TransformComponent, ComponentStartup>(OnTransformStartup);
|
||||
EntityManager.EntityDeleted += OnEntityDeleted;
|
||||
|
||||
@@ -123,17 +123,6 @@ internal sealed partial class PVSSystem : EntitySystem
|
||||
InitializeDirty();
|
||||
}
|
||||
|
||||
private void OnParentChange(ref EntParentChangedMessage ev)
|
||||
{
|
||||
if (_mapManager.IsGrid(ev.Entity) || _mapManager.IsMap(ev.Entity)) return;
|
||||
|
||||
// If parent changes then the RobustTree for that chunk will no longer be valid and we need to force it as dirty.
|
||||
// Should still be at its old location as moveevent is called after.
|
||||
var xform = Transform(ev.Entity);
|
||||
var coordinates = _transform.GetMoverCoordinates(xform);
|
||||
var index = _entityPvsCollection.GetChunkIndex(coordinates);
|
||||
_entityPvsCollection.MarkDirty(index);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
@@ -185,11 +174,6 @@ internal sealed partial class PVSSystem : EntitySystem
|
||||
}
|
||||
|
||||
CleanupDirty(playerSessions);
|
||||
|
||||
foreach (var collection in _pvsCollections)
|
||||
{
|
||||
collection.ClearDirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void CullDeletionHistory(GameTick oldestAck)
|
||||
|
||||
@@ -87,8 +87,8 @@ namespace Robust.Server.ServerStatus
|
||||
var authInfo = new JsonObject
|
||||
{
|
||||
["mode"] = _netManager.Auth.ToString(),
|
||||
["public_key"] = _netManager.CryptoPublicKey != null
|
||||
? Convert.ToBase64String(_netManager.CryptoPublicKey)
|
||||
["public_key"] = _netManager.RsaPublicKey != null
|
||||
? Convert.ToBase64String(_netManager.RsaPublicKey)
|
||||
: null
|
||||
};
|
||||
|
||||
|
||||
@@ -55,27 +55,24 @@ namespace Robust.Shared.Containers
|
||||
protected BaseContainer() { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Insert(EntityUid toinsert, IEntityManager? entMan = null, TransformComponent? transform = null, TransformComponent? ownerTransform = null, MetaDataComponent? meta = null)
|
||||
public bool Insert(EntityUid toinsert, IEntityManager? entMan = null)
|
||||
{
|
||||
DebugTools.Assert(!Deleted);
|
||||
DebugTools.Assert(transform == null || transform.Owner == toinsert);
|
||||
DebugTools.Assert(ownerTransform == null || ownerTransform.Owner == Owner);
|
||||
IoCManager.Resolve(ref entMan);
|
||||
|
||||
//Verify we can insert into this container
|
||||
if (!CanInsert(toinsert, entMan))
|
||||
return false;
|
||||
|
||||
transform ??= entMan.GetComponent<TransformComponent>(toinsert);
|
||||
var transform = entMan.GetComponent<TransformComponent>(toinsert);
|
||||
|
||||
// CanInsert already checks nullability of Parent (or container forgot to call base that does)
|
||||
if (toinsert.TryGetContainerMan(out var containerManager, entMan) && !containerManager.Remove(toinsert))
|
||||
return false; // Can't remove from existing container, can't insert.
|
||||
|
||||
// Attach to parent first so we can check IsInContainer more easily.
|
||||
ownerTransform ??= entMan.GetComponent<TransformComponent>(Owner);
|
||||
transform.AttachParent(ownerTransform);
|
||||
InternalInsert(toinsert, entMan, meta);
|
||||
transform.AttachParent(entMan.GetComponent<TransformComponent>(Owner));
|
||||
InternalInsert(toinsert, entMan);
|
||||
|
||||
// This is an edge case where the parent grid is the container being inserted into, so AttachParent would not unanchor.
|
||||
if (transform.Anchored)
|
||||
@@ -126,25 +123,23 @@ namespace Robust.Shared.Containers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Remove(EntityUid toremove, IEntityManager? entMan = null, TransformComponent? xform = null, MetaDataComponent? meta = null)
|
||||
public bool Remove(EntityUid toremove, IEntityManager? entMan = null)
|
||||
{
|
||||
DebugTools.Assert(!Deleted);
|
||||
DebugTools.AssertNotNull(Manager);
|
||||
DebugTools.AssertNotNull(toremove);
|
||||
IoCManager.Resolve(ref entMan);
|
||||
DebugTools.Assert(entMan.EntityExists(toremove));
|
||||
DebugTools.Assert(xform == null || xform.Owner == toremove);
|
||||
|
||||
if (!CanRemove(toremove, entMan)) return false;
|
||||
InternalRemove(toremove, entMan, meta);
|
||||
InternalRemove(toremove, entMan);
|
||||
|
||||
xform ??= entMan.GetComponent<TransformComponent>(toremove);
|
||||
xform.AttachParentToContainerOrGrid(entMan);
|
||||
entMan.GetComponent<TransformComponent>(toremove).AttachParentToContainerOrGrid(entMan);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ForceRemove(EntityUid toRemove, IEntityManager? entMan = null, MetaDataComponent? meta = null)
|
||||
public void ForceRemove(EntityUid toRemove, IEntityManager? entMan = null)
|
||||
{
|
||||
DebugTools.Assert(!Deleted);
|
||||
DebugTools.AssertNotNull(Manager);
|
||||
@@ -152,7 +147,7 @@ namespace Robust.Shared.Containers
|
||||
IoCManager.Resolve(ref entMan);
|
||||
DebugTools.Assert(entMan.EntityExists(toRemove));
|
||||
|
||||
InternalRemove(toRemove, entMan, meta);
|
||||
InternalRemove(toRemove, entMan);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -194,13 +189,10 @@ namespace Robust.Shared.Containers
|
||||
/// </summary>
|
||||
/// <param name="toinsert"></param>
|
||||
/// <param name="entMan"></param>
|
||||
protected virtual void InternalInsert(EntityUid toinsert, IEntityManager entMan, MetaDataComponent? meta = null)
|
||||
protected virtual void InternalInsert(EntityUid toinsert, IEntityManager entMan)
|
||||
{
|
||||
DebugTools.Assert(!Deleted);
|
||||
DebugTools.Assert(meta == null || meta.Owner == toinsert);
|
||||
|
||||
meta ??= entMan.GetComponent<MetaDataComponent>(toinsert);
|
||||
meta.Flags |= MetaDataFlags.InContainer;
|
||||
entMan.EventBus.RaiseLocalEvent(Owner, new EntInsertedIntoContainerMessage(toinsert, this));
|
||||
entMan.EventBus.RaiseEvent(EventSource.Local, new UpdateContainerOcclusionMessage(toinsert));
|
||||
Manager.Dirty(entMan);
|
||||
@@ -211,16 +203,13 @@ namespace Robust.Shared.Containers
|
||||
/// </summary>
|
||||
/// <param name="toremove"></param>
|
||||
/// <param name="entMan"></param>
|
||||
protected virtual void InternalRemove(EntityUid toremove, IEntityManager entMan, MetaDataComponent? meta = null)
|
||||
protected virtual void InternalRemove(EntityUid toremove, IEntityManager entMan)
|
||||
{
|
||||
DebugTools.Assert(!Deleted);
|
||||
DebugTools.AssertNotNull(Manager);
|
||||
DebugTools.AssertNotNull(toremove);
|
||||
DebugTools.Assert(entMan.EntityExists(toremove));
|
||||
DebugTools.Assert(meta == null || meta.Owner == toremove);
|
||||
|
||||
meta ??= entMan.GetComponent<MetaDataComponent>(toremove);
|
||||
meta.Flags &= ~MetaDataFlags.InContainer;
|
||||
entMan.EventBus.RaiseLocalEvent(Owner, new EntRemovedFromContainerMessage(toremove, this));
|
||||
entMan.EventBus.RaiseEvent(EventSource.Local, new UpdateContainerOcclusionMessage(toremove));
|
||||
Manager.Dirty(entMan);
|
||||
|
||||
@@ -36,17 +36,17 @@ namespace Robust.Shared.Containers
|
||||
public override string ContainerType => ClassName;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void InternalInsert(EntityUid toinsert, IEntityManager entMan, MetaDataComponent? meta = null)
|
||||
protected override void InternalInsert(EntityUid toinsert, IEntityManager entMan)
|
||||
{
|
||||
_containerList.Add(toinsert);
|
||||
base.InternalInsert(toinsert, entMan, meta);
|
||||
base.InternalInsert(toinsert, entMan);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void InternalRemove(EntityUid toremove, IEntityManager entMan, MetaDataComponent? meta = null)
|
||||
protected override void InternalRemove(EntityUid toremove, IEntityManager entMan)
|
||||
{
|
||||
_containerList.Remove(toremove);
|
||||
base.InternalRemove(toremove, entMan, meta);
|
||||
base.InternalRemove(toremove, entMan);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -16,17 +16,24 @@ namespace Robust.Shared.Containers
|
||||
public static class ContainerHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Am I inside a container? Only checks the direct parent. To see if the entity, or any parent entity, is
|
||||
/// inside a container, use <see cref="ContainerSystem.IsEntityOrParentInContainer"/>
|
||||
/// Am I inside a container?
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity that might be inside a container.</param>
|
||||
/// <returns>If the entity is inside of a container.</returns>
|
||||
[Obsolete("Use ContainerSystem.IsEntityInContainer() instead")]
|
||||
public static bool IsInContainer(this EntityUid entity,
|
||||
IEntityManager? entMan = null)
|
||||
public static bool IsInContainer(this EntityUid entity, IEntityManager? entMan = null)
|
||||
{
|
||||
IoCManager.Resolve(ref entMan);
|
||||
return (entMan.GetComponent<MetaDataComponent>(entity).Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer;
|
||||
DebugTools.Assert(entMan.EntityExists(entity));
|
||||
|
||||
// Notice the recursion starts at the Owner of the passed in entity, this
|
||||
// allows containers inside containers (toolboxes in lockers).
|
||||
if (entMan.GetComponent<TransformComponent>(entity).ParentUid is not EntityUid { Valid: true} parent)
|
||||
return false;
|
||||
|
||||
if (TryGetManagerComp(parent, out var containerComp, entMan))
|
||||
return containerComp.ContainsEntity(entity);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -54,7 +61,6 @@ namespace Robust.Shared.Containers
|
||||
/// <param name="entity">Entity that might be inside a container.</param>
|
||||
/// <param name="container">The container that this entity is inside of.</param>
|
||||
/// <returns>If a container was found.</returns>
|
||||
[Obsolete("Use ContainerSystem.TryGetContainingContainer() instead")]
|
||||
public static bool TryGetContainer(this EntityUid entity, [NotNullWhen(true)] out IContainer? container, IEntityManager? entMan = null)
|
||||
{
|
||||
IoCManager.Resolve(ref entMan);
|
||||
@@ -189,7 +195,6 @@ namespace Robust.Shared.Containers
|
||||
/// <returns>The new container.</returns>
|
||||
/// <exception cref="ArgumentException">Thrown if there already is a container with the specified ID.</exception>
|
||||
/// <seealso cref="IContainerManager.MakeContainer{T}(string)" />
|
||||
[Obsolete("Use ContainerSystem.MakeContainer() instead")]
|
||||
public static T CreateContainer<T>(this EntityUid entity, string containerId, IEntityManager? entMan = null)
|
||||
where T : IContainer
|
||||
{
|
||||
@@ -198,7 +203,6 @@ namespace Robust.Shared.Containers
|
||||
return containermanager.MakeContainer<T>(containerId);
|
||||
}
|
||||
|
||||
[Obsolete("Use ContainerSystem.EnsureContainer() instead")]
|
||||
public static T EnsureContainer<T>(this EntityUid entity, string containerId, IEntityManager? entMan = null)
|
||||
where T : IContainer
|
||||
{
|
||||
@@ -206,7 +210,6 @@ namespace Robust.Shared.Containers
|
||||
return EnsureContainer<T>(entity, containerId, out _, entMan);
|
||||
}
|
||||
|
||||
[Obsolete("Use ContainerSystem.EnsureContainer() instead")]
|
||||
public static T EnsureContainer<T>(this EntityUid entity, string containerId, out bool alreadyExisted, IEntityManager? entMan = null)
|
||||
where T : IContainer
|
||||
{
|
||||
|
||||
@@ -79,17 +79,17 @@ namespace Robust.Shared.Containers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void InternalInsert(EntityUid toinsert, IEntityManager entMan, MetaDataComponent? meta = null)
|
||||
protected override void InternalInsert(EntityUid toinsert, IEntityManager entMan)
|
||||
{
|
||||
ContainedEntity = toinsert;
|
||||
base.InternalInsert(toinsert, entMan, meta);
|
||||
base.InternalInsert(toinsert, entMan);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void InternalRemove(EntityUid toremove, IEntityManager entMan, MetaDataComponent? meta = null)
|
||||
protected override void InternalRemove(EntityUid toremove, IEntityManager entMan)
|
||||
{
|
||||
ContainedEntity = null;
|
||||
base.InternalRemove(toremove, entMan, meta);
|
||||
base.InternalRemove(toremove, entMan);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace Robust.Shared.Containers
|
||||
/// Thrown if this container is a child of the entity,
|
||||
/// which would cause infinite loops.
|
||||
/// </exception>
|
||||
bool Insert(EntityUid toinsert, IEntityManager? entMan = null, TransformComponent? transform = null, TransformComponent? ownerTransform = null, MetaDataComponent? meta = null);
|
||||
bool Insert(EntityUid toinsert, IEntityManager? entMan = null);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the entity can be removed from this container.
|
||||
@@ -110,7 +110,7 @@ namespace Robust.Shared.Containers
|
||||
/// <param name="toremove">The entity to attempt to remove.</param>
|
||||
/// <param name="entMan"></param>
|
||||
/// <returns>True if the entity was removed, false otherwise.</returns>
|
||||
bool Remove(EntityUid toremove, IEntityManager? entMan = null, TransformComponent? xform = null, MetaDataComponent? meta = null);
|
||||
bool Remove(EntityUid toremove, IEntityManager? entMan = null);
|
||||
|
||||
/// <summary>
|
||||
/// Forcefully removes an entity from the container. Normally you would want to use <see cref="Remove" />,
|
||||
@@ -118,7 +118,7 @@ namespace Robust.Shared.Containers
|
||||
/// </summary>
|
||||
/// <param name="toRemove">The entity to attempt to remove.</param>
|
||||
/// <param name="entMan"></param>
|
||||
void ForceRemove(EntityUid toRemove, IEntityManager? entMan = null, MetaDataComponent? meta = null);
|
||||
void ForceRemove(EntityUid toRemove, IEntityManager? entMan = null);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the entity is contained in this container.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Shared.Containers
|
||||
{
|
||||
@@ -28,32 +29,18 @@ namespace Robust.Shared.Containers
|
||||
return containerManager.MakeContainer<T>(id);
|
||||
}
|
||||
|
||||
public T EnsureContainer<T>(EntityUid uid, string id, out bool alreadyExisted, ContainerManagerComponent? containerManager = null)
|
||||
public T EnsureContainer<T>(EntityUid uid, string id, ContainerManagerComponent? containerManager = null)
|
||||
where T : IContainer
|
||||
{
|
||||
if (!Resolve(uid, ref containerManager, false))
|
||||
containerManager = EntityManager.AddComponent<ContainerManagerComponent>(uid);
|
||||
|
||||
if (TryGetContainer(uid, id, out var container, containerManager))
|
||||
{
|
||||
alreadyExisted = true;
|
||||
if (container is T cast)
|
||||
return cast;
|
||||
return (T)container;
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"The container exists but is of a different type: {container.GetType()}");
|
||||
}
|
||||
|
||||
alreadyExisted = false;
|
||||
return MakeContainer<T>(uid, id, containerManager);
|
||||
}
|
||||
|
||||
public T EnsureContainer<T>(EntityUid uid, string id, ContainerManagerComponent? containerManager = null)
|
||||
where T : IContainer
|
||||
{
|
||||
return EnsureContainer<T>(uid, id, out _, containerManager);
|
||||
}
|
||||
|
||||
public IContainer GetContainer(EntityUid uid, string id, ContainerManagerComponent? containerManager = null)
|
||||
{
|
||||
if (!Resolve(uid, ref containerManager))
|
||||
@@ -79,9 +66,9 @@ namespace Robust.Shared.Containers
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetContainingContainer(EntityUid uid, EntityUid containedUid, [NotNullWhen(true)] out IContainer? container, ContainerManagerComponent? containerManager = null, bool skipExistCheck = false)
|
||||
public bool TryGetContainingContainer(EntityUid uid, EntityUid containedUid, [NotNullWhen(true)] out IContainer? container, ContainerManagerComponent? containerManager = null)
|
||||
{
|
||||
if (Resolve(uid, ref containerManager, false) && (skipExistCheck || EntityManager.EntityExists(containedUid)))
|
||||
if (Resolve(uid, ref containerManager, false) && EntityManager.EntityExists(containedUid))
|
||||
return containerManager.TryGetContainer(containedUid, out container);
|
||||
|
||||
container = null;
|
||||
@@ -119,69 +106,21 @@ namespace Robust.Shared.Containers
|
||||
|
||||
#region Container Helpers
|
||||
|
||||
public bool TryGetContainingContainer(EntityUid uid, [NotNullWhen(true)] out IContainer? container, MetaDataComponent? meta = null, TransformComponent? transform = null)
|
||||
public bool TryGetContainingContainer(EntityUid uid, [NotNullWhen(true)] out IContainer? container, TransformComponent? transform = null)
|
||||
{
|
||||
container = null;
|
||||
|
||||
if (!Resolve(uid, ref meta, false))
|
||||
return false;
|
||||
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) == MetaDataFlags.None)
|
||||
return false;
|
||||
|
||||
if (!Resolve(uid, ref transform, false))
|
||||
return false;
|
||||
|
||||
return TryGetContainingContainer(transform.ParentUid, uid, out container, skipExistCheck: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given entity is inside of a container. This will only check if this entity's direct
|
||||
/// parent is containing it. To recursively if the entity, or any parent, is inside a container, use <see
|
||||
/// cref="IsEntityOrParentInContainer"/>
|
||||
/// </summary>
|
||||
/// <returns>If the entity is inside of a container.</returns>
|
||||
public bool IsEntityInContainer(EntityUid uid, MetaDataComponent? meta = null)
|
||||
{
|
||||
if (!Resolve(uid, ref meta, false))
|
||||
if (!transform.ParentUid.IsValid())
|
||||
return false;
|
||||
|
||||
return (meta.Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer;
|
||||
return TryGetContainingContainer(transform.ParentUid, uid, out container);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively if the entity, or any parent entity, is inside of a container.
|
||||
/// </summary>
|
||||
/// <returns>If the entity is inside of a container.</returns>
|
||||
public bool IsEntityOrParentInContainer(
|
||||
EntityUid uid,
|
||||
MetaDataComponent? meta = null,
|
||||
TransformComponent? xform = null,
|
||||
EntityQuery<MetaDataComponent>? metas = null,
|
||||
EntityQuery<TransformComponent>? xforms = null)
|
||||
public bool IsEntityInContainer(EntityUid uid, TransformComponent? transform = null)
|
||||
{
|
||||
DebugTools.Assert(meta == null || meta.Owner == uid);
|
||||
DebugTools.Assert(xform == null || xform.Owner == uid);
|
||||
|
||||
if (meta == null)
|
||||
{
|
||||
metas ??= EntityManager.GetEntityQuery<MetaDataComponent>();
|
||||
meta = metas.Value.GetComponent(uid);
|
||||
}
|
||||
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer)
|
||||
return true;
|
||||
|
||||
if (xform == null)
|
||||
{
|
||||
xforms ??= EntityManager.GetEntityQuery<TransformComponent>();
|
||||
xform = xforms.Value.GetComponent(uid);
|
||||
}
|
||||
|
||||
if (!xform.ParentUid.Valid)
|
||||
return false;
|
||||
|
||||
return IsEntityOrParentInContainer(xform.ParentUid, metas: metas, xforms: xforms);
|
||||
return TryGetContainingContainer(uid, out _, transform);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -288,14 +227,12 @@ namespace Robust.Shared.Containers
|
||||
return false;
|
||||
|
||||
var conQuery = EntityManager.GetEntityQuery<ContainerManagerComponent>();
|
||||
var metaQuery = EntityManager.GetEntityQuery<MetaDataComponent>();
|
||||
var child = uid;
|
||||
var parent = xform.ParentUid;
|
||||
|
||||
while (parent.IsValid())
|
||||
{
|
||||
if (((metaQuery.GetComponent(child).Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer) &&
|
||||
conQuery.TryGetComponent(parent, out var conManager) &&
|
||||
if (conQuery.TryGetComponent(parent, out var conManager) &&
|
||||
conManager.TryGetContainer(child, out var parentContainer))
|
||||
{
|
||||
container = parentContainer;
|
||||
|
||||
@@ -219,6 +219,22 @@ namespace Robust.Shared.GameObjects
|
||||
entManager.Dirty(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message over the network to all other components on the networked entity. This works both ways.
|
||||
/// This is an alias of 'Owner.SendNetworkMessage(this, message);'
|
||||
/// </summary>
|
||||
/// <param name="message">Message to send.</param>
|
||||
/// <param name="channel">Network channel to send the message over. If null, broadcast to all channels.</param>
|
||||
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
||||
protected void SendNetworkMessage(ComponentMessage message, INetChannel? channel = null)
|
||||
{
|
||||
IoCManager.Resolve<IEntityManager>().EntityNetManager?.SendComponentNetworkMessage(channel, Owner, this, message);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
||||
public virtual void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null) { }
|
||||
|
||||
private static readonly ComponentState DefaultComponentState = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
24
Robust.Shared/GameObjects/ComponentMessage.cs
Normal file
24
Robust.Shared/GameObjects/ComponentMessage.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// A message containing info to send through the component message system.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
[Obsolete("Component messages are deprecated. Use directed local events instead.")]
|
||||
public abstract class ComponentMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Was this message raised from a remote location over the network?
|
||||
/// </summary>
|
||||
public bool Remote { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// If this is a remote message, will it only be sent to the corresponding component?
|
||||
/// If this is not a remote message, this flag does nothing.
|
||||
/// </summary>
|
||||
public bool Directed { get; protected set; }
|
||||
}
|
||||
}
|
||||
@@ -182,9 +182,5 @@ namespace Robust.Shared.GameObjects
|
||||
/// Whether the entity has states specific to a particular player.
|
||||
/// </summary>
|
||||
EntitySpecific = 1 << 0,
|
||||
/// <summary>
|
||||
/// Whether the entity is currently inside of a container.
|
||||
/// </summary>
|
||||
InContainer = 1 << 1,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +355,6 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
// offset position from world to parent
|
||||
_parent = value.EntityId;
|
||||
var oldMapId = MapID;
|
||||
ChangeMapId(newParent.MapID, xformQuery);
|
||||
|
||||
// preserve world rotation
|
||||
@@ -365,7 +364,7 @@ namespace Robust.Shared.GameObjects
|
||||
// Cache new GridID before raising the event.
|
||||
GridID = GetGridIndex(xformQuery);
|
||||
|
||||
var entParentChangedMessage = new EntParentChangedMessage(Owner, oldParent?.Owner, oldMapId);
|
||||
var entParentChangedMessage = new EntParentChangedMessage(Owner, oldParent?.Owner);
|
||||
_entMan.EventBus.RaiseLocalEvent(Owner, ref entParentChangedMessage);
|
||||
}
|
||||
|
||||
@@ -382,7 +381,7 @@ namespace Robust.Shared.GameObjects
|
||||
//TODO: This is a hack, look into WHY we can't call GridPosition before the comp is Running
|
||||
if (Running)
|
||||
{
|
||||
if (!oldPosition.Position.Equals(Coordinates.Position))
|
||||
if(!oldPosition.Position.Equals(Coordinates.Position))
|
||||
{
|
||||
var moveEvent = new MoveEvent(Owner, oldPosition, Coordinates, this);
|
||||
_entMan.EventBus.RaiseLocalEvent(Owner, ref moveEvent);
|
||||
@@ -734,8 +733,8 @@ namespace Robust.Shared.GameObjects
|
||||
oldConcrete._children.Remove(uid);
|
||||
|
||||
_parent = EntityUid.Invalid;
|
||||
var entParentChangedMessage = new EntParentChangedMessage(Owner, oldParent, MapID);
|
||||
MapID = MapId.Nullspace;
|
||||
var entParentChangedMessage = new EntParentChangedMessage(Owner, oldParent);
|
||||
_entMan.EventBus.RaiseLocalEvent(Owner, ref entParentChangedMessage);
|
||||
|
||||
// Does it even make sense to call these since this is called purely from OnRemove right now?
|
||||
@@ -932,7 +931,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the given entity is a child of this transform or one of its descendants.
|
||||
/// Returns whether the entity of this transform contains the entity argument
|
||||
/// </summary>
|
||||
public bool ContainsEntity(TransformComponent entityTransform)
|
||||
{
|
||||
@@ -941,7 +940,7 @@ namespace Robust.Shared.GameObjects
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == entityTransform.Parent) //Is this the direct parent of the entity
|
||||
if (this == entityTransform.Parent) //Is this the direct container of the entity
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -949,7 +948,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
return
|
||||
ContainsEntity(entityTransform
|
||||
.Parent); //Recursively search up the parents for this object
|
||||
.Parent); //Recursively search up the entities containers for this object
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -478,7 +478,31 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
#endregion Entity Management
|
||||
|
||||
/// <summary>
|
||||
protected void DispatchComponentMessage(NetworkComponentMessage netMsg)
|
||||
{
|
||||
var compMsg = netMsg.Message;
|
||||
var compChannel = netMsg.Channel;
|
||||
var session = netMsg.Session;
|
||||
compMsg.Remote = true;
|
||||
|
||||
#pragma warning disable 618
|
||||
var uid = netMsg.EntityUid;
|
||||
if (compMsg.Directed)
|
||||
{
|
||||
if (TryGetComponent(uid, (ushort) netMsg.NetId, out var component))
|
||||
component.HandleNetworkMessage(compMsg, compChannel, session);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var component in GetComponents(uid))
|
||||
{
|
||||
component.HandleNetworkMessage(compMsg, compChannel, session);
|
||||
}
|
||||
}
|
||||
#pragma warning restore 618
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory for generating a new EntityUid for an entity currently being created.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
@@ -491,6 +515,7 @@ namespace Robust.Shared.GameObjects
|
||||
public enum EntityMessageType : byte
|
||||
{
|
||||
Error = 0,
|
||||
ComponentMessage,
|
||||
SystemMessage
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ public partial class EntitySystem
|
||||
if(!Resolve(uid, ref metaData, false))
|
||||
throw CompNotFound<MetaDataComponent>(uid);
|
||||
|
||||
return metaData.EntityDescription;
|
||||
return metaData.EntityName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Raised when an entity parent is changed.
|
||||
@@ -18,26 +16,15 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
public EntityUid? OldParent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The map Id that the entity was on before its parent changed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the old parent was detached to null without manually updating the map ID of its children, then this
|
||||
/// is required as we cannot simply use the old parent's map ID. Also avoids having to fetch the old
|
||||
/// parent's transform component.
|
||||
/// </remarks>
|
||||
public MapId OldMapId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="EntParentChangedMessage"/>.
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="oldParent"></param>
|
||||
public EntParentChangedMessage(EntityUid entity, EntityUid? oldParent, MapId oldMapId)
|
||||
public EntParentChangedMessage(EntityUid entity, EntityUid? oldParent)
|
||||
{
|
||||
Entity = entity;
|
||||
OldParent = oldParent;
|
||||
OldMapId = oldMapId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,15 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
GameTick LastModifiedTick { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Handles an incoming component message from the server.
|
||||
/// </summary>
|
||||
/// <param name="message">Incoming event message.</param>
|
||||
/// <param name="netChannel">The channel of the remote client that sent the message.</param>
|
||||
/// <param name="session">The session data for the player who sent this message. Null if this is a client.</param>
|
||||
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
||||
void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null);
|
||||
|
||||
/// <summary>
|
||||
/// Get the component's state for replicating on the client.
|
||||
/// </summary>
|
||||
|
||||
@@ -9,7 +9,12 @@ namespace Robust.Shared.GameObjects
|
||||
public interface IEntityNetworkManager
|
||||
{
|
||||
/// <summary>
|
||||
/// This event is raised when a system message comes in from the network.
|
||||
/// This event is raised when a component message comes in from the network.
|
||||
/// </summary>
|
||||
event EventHandler<NetworkComponentMessage> ReceivedComponentMessage;
|
||||
|
||||
/// <summary>
|
||||
/// This event is raised when a component message comes in from the network.
|
||||
/// </summary>
|
||||
event EventHandler<object> ReceivedSystemMessage;
|
||||
|
||||
@@ -18,6 +23,21 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
void SetupNetworking();
|
||||
|
||||
/// <summary>
|
||||
/// Allows a component owned by this entity to send a message to a counterpart component on the
|
||||
/// counterpart entities on clients.
|
||||
/// </summary>
|
||||
/// <param name="channel">
|
||||
/// Intended recipient of the message. On Server, if this is null, broadcast the message to all clients.
|
||||
/// On clients, this should always be null.
|
||||
/// </param>
|
||||
/// <param name="entity">Entity sending the message (also entity to send to).</param>
|
||||
/// <param name="component">Component that sent the message.</param>
|
||||
/// <param name="message">Message to send.</param>
|
||||
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
||||
void SendComponentNetworkMessage(INetChannel? channel, EntityUid entity, IComponent component,
|
||||
ComponentMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// Sends an Entity System Message to relevant System(s).
|
||||
/// Client: Sends the message to the relevant server side System(s).
|
||||
|
||||
59
Robust.Shared/GameObjects/NetworkComponentMessage.cs
Normal file
59
Robust.Shared/GameObjects/NetworkComponentMessage.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// A container that holds a component message and network info.
|
||||
/// </summary>
|
||||
public readonly struct NetworkComponentMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Network channel this message came from.
|
||||
/// </summary>
|
||||
public readonly INetChannel Channel;
|
||||
|
||||
/// <summary>
|
||||
/// Entity Uid this message is associated with.
|
||||
/// </summary>
|
||||
public readonly EntityUid EntityUid;
|
||||
|
||||
/// <summary>
|
||||
/// If the Message is Directed, Component net Uid this message is being sent to.
|
||||
/// </summary>
|
||||
public readonly uint NetId;
|
||||
|
||||
/// <summary>
|
||||
/// The message payload.
|
||||
/// </summary>
|
||||
#pragma warning disable 618
|
||||
public readonly ComponentMessage Message;
|
||||
#pragma warning restore 618
|
||||
|
||||
/// <summary>
|
||||
/// If the message is from the client, the client's session.
|
||||
/// </summary>
|
||||
public readonly ICommonSession? Session;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="NetworkComponentMessage"/>.
|
||||
/// </summary>
|
||||
/// <param name="netMsg">Raw network message containing the component message.</param>
|
||||
public NetworkComponentMessage(MsgEntity netMsg, ICommonSession? session = null)
|
||||
{
|
||||
DebugTools.Assert(netMsg.Type == EntityMessageType.ComponentMessage);
|
||||
|
||||
Channel = netMsg.MsgChannel;
|
||||
EntityUid = netMsg.EntityUid;
|
||||
NetId = netMsg.NetId;
|
||||
Message = netMsg.ComponentMessage;
|
||||
Session = session;
|
||||
}
|
||||
}
|
||||
|
||||
#nullable restore
|
||||
}
|
||||
@@ -72,15 +72,21 @@ namespace Robust.Shared.GameObjects
|
||||
/// Updates the entity's AABB. Uses <see cref="ILookupWorldBox2Component"/>
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public void UpdateBounds(EntityUid uid, TransformComponent? xform = null, MetaDataComponent? meta = null)
|
||||
public void UpdateBounds(EntityUid uid, TransformComponent? xform = null)
|
||||
{
|
||||
if (_container.IsEntityInContainer(uid, meta))
|
||||
return;
|
||||
|
||||
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
if (!xformQuery.Resolve(uid, ref xform) || xform.Anchored)
|
||||
if (xform == null)
|
||||
xformQuery.TryGetComponent(uid, out xform);
|
||||
|
||||
if (xform == null)
|
||||
{
|
||||
Logger.Error($"Unable to resolve transform on {EntityManager.ToPrettyString(uid)}");
|
||||
DebugTools.Assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (xform.Anchored || _container.IsEntityInContainer(uid, xform)) return;
|
||||
|
||||
var lookup = GetLookup(uid, xform, xformQuery);
|
||||
|
||||
@@ -225,7 +231,7 @@ namespace Robust.Shared.GameObjects
|
||||
private void UpdatePosition(EntityUid uid, TransformComponent xform)
|
||||
{
|
||||
// Even if the entity is contained it may have children that aren't so we still need to update.
|
||||
if (!CanMoveUpdate(uid)) return;
|
||||
if (!CanMoveUpdate(uid, xform)) return;
|
||||
|
||||
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
var lookup = GetLookup(uid, xform, xformQuery);
|
||||
@@ -238,11 +244,11 @@ namespace Robust.Shared.GameObjects
|
||||
AddToEntityTree(lookup, xform, aabb, xformQuery);
|
||||
}
|
||||
|
||||
private bool CanMoveUpdate(EntityUid uid)
|
||||
private bool CanMoveUpdate(EntityUid uid, TransformComponent xform)
|
||||
{
|
||||
return !_mapManager.IsMap(uid) &&
|
||||
!_mapManager.IsGrid(uid) &&
|
||||
!_container.IsEntityInContainer(uid);
|
||||
!_container.IsEntityInContainer(uid, xform);
|
||||
}
|
||||
|
||||
private void OnParentChange(ref EntParentChangedMessage args)
|
||||
|
||||
@@ -44,17 +44,15 @@ public sealed class MetaDataSystem : EntitySystem
|
||||
/// </summary>
|
||||
public void RemoveFlag(EntityUid uid, MetaDataFlags flags, MetaDataComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
if (!Resolve(uid, ref component) ||
|
||||
(component.Flags & flags) == 0x0) return;
|
||||
|
||||
var toRemove = component.Flags & flags;
|
||||
if (toRemove == 0x0)
|
||||
return;
|
||||
|
||||
var ev = new MetaFlagRemoveAttemptEvent(toRemove);
|
||||
var ev = new MetaFlagRemoveAttemptEvent();
|
||||
EntityManager.EventBus.RaiseLocalEvent(component.Owner, ref ev);
|
||||
|
||||
component.Flags &= ~ev.ToRemove;
|
||||
if (ev.Cancelled) return;
|
||||
|
||||
component.Flags &= ~flags;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,10 +62,9 @@ public sealed class MetaDataSystem : EntitySystem
|
||||
[ByRefEvent]
|
||||
public struct MetaFlagRemoveAttemptEvent
|
||||
{
|
||||
public MetaDataFlags ToRemove;
|
||||
public bool Cancelled = false;
|
||||
|
||||
public MetaFlagRemoveAttemptEvent(MetaDataFlags toRemove)
|
||||
public MetaFlagRemoveAttemptEvent()
|
||||
{
|
||||
ToRemove = toRemove;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,13 +42,11 @@ namespace Robust.Shared.GameObjects
|
||||
var grids = EntityManager.GetEntityQuery<MapGridComponent>();
|
||||
var bodies = GetEntityQuery<PhysicsComponent>();
|
||||
var xforms = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
var metas = EntityManager.GetEntityQuery<MetaDataComponent>();
|
||||
|
||||
while (_queuedEvents.TryPop(out var moveEvent))
|
||||
{
|
||||
if (!_handledThisTick.Add(moveEvent.Sender)) continue;
|
||||
|
||||
HandleMove(ref moveEvent, xforms, bodies, maps, grids, metas);
|
||||
HandleMove(ref moveEvent, xforms, bodies, maps, grids);
|
||||
}
|
||||
|
||||
_handledThisTick.Clear();
|
||||
@@ -59,13 +57,11 @@ namespace Robust.Shared.GameObjects
|
||||
EntityQuery<TransformComponent> xforms,
|
||||
EntityQuery<PhysicsComponent> bodies,
|
||||
EntityQuery<MapComponent> maps,
|
||||
EntityQuery<MapGridComponent> grids,
|
||||
EntityQuery<MetaDataComponent> metas)
|
||||
EntityQuery<MapGridComponent> grids)
|
||||
{
|
||||
var entity = moveEvent.Sender;
|
||||
|
||||
if (!metas.TryGetComponent(entity, out MetaDataComponent? meta) ||
|
||||
meta.EntityDeleted ||
|
||||
if (Deleted(entity) ||
|
||||
maps.HasComponent(entity) ||
|
||||
grids.HasComponent(entity))
|
||||
{
|
||||
@@ -75,7 +71,7 @@ namespace Robust.Shared.GameObjects
|
||||
var xform = xforms.GetComponent(entity);
|
||||
DebugTools.Assert(!float.IsNaN(moveEvent.NewPosition.X) && !float.IsNaN(moveEvent.NewPosition.Y));
|
||||
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer) return;
|
||||
if (_container.IsEntityInContainer(entity, xform)) return;
|
||||
|
||||
var mapPos = moveEvent.NewPosition.ToMapPos(EntityManager);
|
||||
_gridBuffer.Clear();
|
||||
|
||||
@@ -35,7 +35,7 @@ public abstract partial class SharedPhysicsSystem
|
||||
EntityManager.EventBus.RaiseEvent(EventSource.Local, new PhysicsWakeMessage(component));
|
||||
}
|
||||
|
||||
if (!_containerSystem.IsEntityInContainer(uid))
|
||||
if (!_containerSystem.IsEntityInContainer(uid, xform))
|
||||
{
|
||||
// TODO: Probably a bad idea but ehh future sloth's problem; namely that we have to duplicate code between here and CanCollide.
|
||||
EntityManager.EventBus.RaiseLocalEvent(uid, new CollisionChangeMessage(component, uid, component._canCollide));
|
||||
|
||||
@@ -94,45 +94,6 @@ namespace Robust.Shared.GameObjects
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all the entities whose fixtures intersect the fixtures of the given entity. Basically a variant of
|
||||
/// <see cref="GetCollidingEntities(PhysicsComponent, Vector2, bool)"/> that allows the user to specify
|
||||
/// their own collision mask.
|
||||
/// </summary>
|
||||
public HashSet<EntityUid> GetEntitiesIntersectingBody(
|
||||
EntityUid uid,
|
||||
int collisionMask,
|
||||
bool approximate = true,
|
||||
PhysicsComponent? body = null,
|
||||
FixturesComponent? fixtureComp = null)
|
||||
{
|
||||
var entities = new HashSet<EntityUid>();
|
||||
|
||||
if (!Resolve(uid, ref body, ref fixtureComp, false) || body.Broadphase == null)
|
||||
return entities;
|
||||
|
||||
var state = (body, entities);
|
||||
|
||||
foreach (var (_, fixture) in fixtureComp.Fixtures)
|
||||
{
|
||||
foreach (var proxy in fixture.Proxies)
|
||||
{
|
||||
body.Broadphase.Tree.QueryAabb(ref state,
|
||||
(ref (PhysicsComponent body, HashSet<EntityUid> entities) state,
|
||||
in FixtureProxy other) =>
|
||||
{
|
||||
if (other.Fixture.Body.Deleted || other.Fixture.Body == body) return true;
|
||||
if ((collisionMask & other.Fixture.CollisionLayer) == 0x0) return true;
|
||||
|
||||
state.entities.Add(other.Fixture.Body.Owner);
|
||||
return true;
|
||||
}, proxy.AABB, approximate);
|
||||
}
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all entities colliding with a certain body.
|
||||
/// </summary>
|
||||
|
||||
@@ -142,6 +142,9 @@ public abstract partial class SharedPhysicsSystem
|
||||
if (physics.LifeStage != ComponentLifeStage.Running)
|
||||
return;
|
||||
|
||||
if (_container.IsEntityInContainer(uid, xform))
|
||||
return;
|
||||
|
||||
// When transferring bodies, we will preserve map angular and linear velocities. For this purpose, we simply
|
||||
// modify the velocities so that the map value remains unchanged.
|
||||
|
||||
|
||||
@@ -114,9 +114,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
private void OnParentChange(EntityUid uid, PhysicsComponent body, ref EntParentChangedMessage args)
|
||||
{
|
||||
var meta = MetaData(uid);
|
||||
|
||||
if (meta.EntityLifeStage < EntityLifeStage.Initialized || !TryComp(uid, out TransformComponent? xform))
|
||||
if (LifeStage(uid) < EntityLifeStage.Initialized || !TryComp(uid, out TransformComponent? xform))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -125,13 +123,15 @@ namespace Robust.Shared.GameObjects
|
||||
_broadphase.UpdateBroadphase(body, xform: xform);
|
||||
|
||||
// Handle map change
|
||||
var oldMapId = _transform.GetMapId(args.OldParent);
|
||||
var mapId = _transform.GetMapId(args.Entity);
|
||||
|
||||
if (args.OldMapId != mapId)
|
||||
HandleMapChange(body, xform, args.OldMapId, mapId);
|
||||
if (oldMapId != mapId)
|
||||
{
|
||||
HandleMapChange(body, xform, oldMapId, mapId);
|
||||
}
|
||||
|
||||
if (mapId != MapId.Nullspace && !_container.IsEntityInContainer(uid, meta))
|
||||
HandleParentChangeVelocity(uid, body, ref args, xform);
|
||||
HandleParentChangeVelocity(uid, body, ref args, xform);
|
||||
}
|
||||
|
||||
private void HandleMapChange(PhysicsComponent body, TransformComponent xform, MapId oldMapId, MapId mapId)
|
||||
@@ -159,10 +159,10 @@ namespace Robust.Shared.GameObjects
|
||||
map.AddBody(body);
|
||||
}
|
||||
|
||||
if (xform.ChildCount == 0 ||
|
||||
(oldMap == null && map == null) ||
|
||||
_mapManager.IsGrid(body.Owner) ||
|
||||
_mapManager.IsMap(body.Owner)) return;
|
||||
if (_mapManager.IsGrid(body.Owner) ||
|
||||
_mapManager.IsMap(body.Owner) ||
|
||||
xform.ChildCount == 0 ||
|
||||
(oldMap == null && map == null)) return;
|
||||
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var bodyQuery = GetEntityQuery<PhysicsComponent>();
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Robust.Shared.Network
|
||||
{
|
||||
public delegate Task<NetApproval> NetApprovalDelegate(NetApprovalEventArgs eventArgs);
|
||||
|
||||
byte[]? CryptoPublicKey { get; }
|
||||
byte[]? RsaPublicKey { get; }
|
||||
AuthMode Auth { get; }
|
||||
Func<string, Task<NetUserId?>>? AssignUserIdCallback { get; set; }
|
||||
NetApprovalDelegate? HandleApprovalCallback { get; set; }
|
||||
|
||||
@@ -12,20 +12,25 @@ namespace Robust.Shared.Network.Messages.Handshake
|
||||
public override MsgGroups MsgGroup => MsgGroups.Core;
|
||||
|
||||
public Guid UserId;
|
||||
public byte[] SealedData;
|
||||
public byte[] SharedSecret;
|
||||
public byte[] VerifyToken;
|
||||
|
||||
public override void ReadFromBuffer(NetIncomingMessage buffer)
|
||||
{
|
||||
UserId = buffer.ReadGuid();
|
||||
var keyLength = buffer.ReadVariableInt32();
|
||||
SealedData = buffer.ReadBytes(keyLength);
|
||||
SharedSecret = buffer.ReadBytes(keyLength);
|
||||
var tokenLength = buffer.ReadVariableInt32();
|
||||
VerifyToken = buffer.ReadBytes(tokenLength);
|
||||
}
|
||||
|
||||
public override void WriteToBuffer(NetOutgoingMessage buffer)
|
||||
{
|
||||
buffer.Write(UserId);
|
||||
buffer.WriteVariableInt32(SealedData.Length);
|
||||
buffer.Write(SealedData);
|
||||
buffer.WriteVariableInt32(SharedSecret.Length);
|
||||
buffer.Write(SharedSecret);
|
||||
buffer.WriteVariableInt32(VerifyToken.Length);
|
||||
buffer.Write(VerifyToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Lidgren.Network;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -17,6 +19,10 @@ namespace Robust.Shared.Network.Messages
|
||||
public EntityMessageType Type { get; set; }
|
||||
|
||||
public EntityEventArgs SystemMessage { get; set; }
|
||||
#pragma warning disable 618
|
||||
public ComponentMessage ComponentMessage { get; set; }
|
||||
#pragma warning restore 618
|
||||
|
||||
public EntityUid EntityUid { get; set; }
|
||||
public uint NetId { get; set; }
|
||||
public uint Sequence { get; set; }
|
||||
@@ -38,6 +44,20 @@ namespace Robust.Shared.Network.Messages
|
||||
SystemMessage = serializer.Deserialize<EntityEventArgs>(stream);
|
||||
}
|
||||
break;
|
||||
|
||||
case EntityMessageType.ComponentMessage:
|
||||
{
|
||||
EntityUid = new EntityUid(buffer.ReadInt32());
|
||||
NetId = buffer.ReadUInt32();
|
||||
|
||||
var serializer = IoCManager.Resolve<IRobustSerializer>();
|
||||
int length = buffer.ReadVariableInt32();
|
||||
using var stream = buffer.ReadAlignedMemory(length);
|
||||
#pragma warning disable 618
|
||||
ComponentMessage = serializer.Deserialize<ComponentMessage>(stream);
|
||||
#pragma warning restore 618
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +81,22 @@ namespace Robust.Shared.Network.Messages
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EntityMessageType.ComponentMessage:
|
||||
{
|
||||
buffer.Write((int)EntityUid);
|
||||
buffer.Write(NetId);
|
||||
|
||||
var serializer = IoCManager.Resolve<IRobustSerializer>();
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
serializer.Serialize(stream, ComponentMessage);
|
||||
buffer.WriteVariableInt32((int)stream.Length);
|
||||
stream.TryGetBuffer(out var segment);
|
||||
buffer.Write(segment);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +107,8 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
case EntityMessageType.Error:
|
||||
return "MsgEntity Error";
|
||||
case EntityMessageType.ComponentMessage:
|
||||
return $"MsgEntity Comp, {timingData}, {EntityUid}/{NetId}: {ComponentMessage}";
|
||||
case EntityMessageType.SystemMessage:
|
||||
return $"MsgEntity Comp, {timingData}, {SystemMessage}";
|
||||
default:
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using Lidgren.Network;
|
||||
using SpaceWizards.Sodium;
|
||||
|
||||
namespace Robust.Shared.Network;
|
||||
|
||||
internal sealed class NetEncryption
|
||||
{
|
||||
// Use a counter for nonces. The counter is 64-bit, I will be impressed if you ever manage to run it out.
|
||||
// 64-bit counter (incl over the wire) is fine, don't need the whole 192-bit.
|
||||
// Server starts at 0, client starts at 1, increment by two.
|
||||
// This means server and client never use eachother's nonces (one side odd, one side even).
|
||||
// Keep in mind, our keys are only valid for one session.
|
||||
private ulong _nonce;
|
||||
private readonly byte[] _key;
|
||||
|
||||
public NetEncryption(byte[] key, bool isServer)
|
||||
{
|
||||
if (key.Length != CryptoAeadXChaCha20Poly1305Ietf.KeyBytes)
|
||||
throw new ArgumentException("Key is of wrong size!");
|
||||
|
||||
_nonce = isServer ? 0ul : 1ul;
|
||||
_key = key;
|
||||
}
|
||||
|
||||
public unsafe void Encrypt(NetOutgoingMessage message)
|
||||
{
|
||||
var nonce = Interlocked.Add(ref _nonce, 2);
|
||||
|
||||
var lengthBytes = message.LengthBytes;
|
||||
var encryptedSize = CryptoAeadXChaCha20Poly1305Ietf.AddBytes + lengthBytes + sizeof(ulong);
|
||||
|
||||
var data = message.Data.AsSpan(0, lengthBytes);
|
||||
|
||||
Span<byte> plaintext;
|
||||
Span<byte> ciphertext;
|
||||
byte[]? returnPool = null;
|
||||
|
||||
if (message.Data.Length >= encryptedSize)
|
||||
{
|
||||
// Since we have enough space in the existing message data,
|
||||
// we copy plaintext to an ArrayPool buffer and write ciphertext into existing message.
|
||||
// This avoids an allocation at the cost of an extra copy operation.
|
||||
|
||||
returnPool = ArrayPool<byte>.Shared.Rent(lengthBytes);
|
||||
plaintext = returnPool.AsSpan(0, lengthBytes);
|
||||
data.CopyTo(plaintext);
|
||||
|
||||
ciphertext = message.Data.AsSpan(0, encryptedSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, an allocation is unavoidable,
|
||||
// so we swap the data buffer in the message with a fresh allocation and don't do an extra copy of the data.
|
||||
|
||||
plaintext = data;
|
||||
ciphertext = message.Data = new byte[encryptedSize];
|
||||
}
|
||||
|
||||
// TODO: this is probably broken for big-endian machines.
|
||||
Span<byte> nonceData = stackalloc byte[CryptoAeadXChaCha20Poly1305Ietf.NoncePublicBytes];
|
||||
nonceData.Fill(0);
|
||||
MemoryMarshal.Write(nonceData, ref nonce);
|
||||
MemoryMarshal.Write(ciphertext, ref nonce);
|
||||
|
||||
CryptoAeadXChaCha20Poly1305Ietf.Encrypt(
|
||||
// ciphertext
|
||||
ciphertext[sizeof(ulong)..],
|
||||
out _,
|
||||
// plaintext
|
||||
plaintext,
|
||||
// additional data (unused)
|
||||
ReadOnlySpan<byte>.Empty,
|
||||
// nonce
|
||||
nonceData,
|
||||
// key
|
||||
_key);
|
||||
|
||||
message.LengthBytes = encryptedSize;
|
||||
|
||||
if (returnPool != null)
|
||||
ArrayPool<byte>.Shared.Return(returnPool);
|
||||
}
|
||||
|
||||
public unsafe void Decrypt(NetIncomingMessage message)
|
||||
{
|
||||
var nonce = message.ReadUInt64();
|
||||
var cipherText = message.Data.AsSpan(sizeof(ulong), message.LengthBytes - sizeof(ulong));
|
||||
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(cipherText.Length);
|
||||
cipherText.CopyTo(buffer);
|
||||
|
||||
// TODO: this is probably broken for big-endian machines.
|
||||
Span<byte> nonceData = stackalloc byte[CryptoAeadXChaCha20Poly1305Ietf.NoncePublicBytes];
|
||||
nonceData.Fill(0);
|
||||
MemoryMarshal.Write(nonceData, ref nonce);
|
||||
|
||||
var result = CryptoAeadXChaCha20Poly1305Ietf.Decrypt(
|
||||
// plaintext
|
||||
message.Data,
|
||||
out var messageLength,
|
||||
// ciphertext
|
||||
buffer.AsSpan(0, cipherText.Length),
|
||||
// additional data (unused)
|
||||
ReadOnlySpan<byte>.Empty,
|
||||
// nonce
|
||||
nonceData,
|
||||
// key
|
||||
_key);
|
||||
|
||||
message.Position = 0;
|
||||
message.LengthBytes = messageLength;
|
||||
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
|
||||
if (!result)
|
||||
throw new SodiumException("Decryption operation failed!");
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ using Lidgren.Network;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network.Messages.Handshake;
|
||||
using Robust.Shared.Utility;
|
||||
using SpaceWizards.Sodium;
|
||||
|
||||
namespace Robust.Shared.Network
|
||||
{
|
||||
@@ -155,11 +154,11 @@ namespace Robust.Shared.Network
|
||||
var encRequest = new MsgEncryptionRequest();
|
||||
encRequest.ReadFromBuffer(response);
|
||||
|
||||
var sharedSecret = new byte[SharedKeyLength];
|
||||
var sharedSecret = new byte[AesKeyLength];
|
||||
RandomNumberGenerator.Fill(sharedSecret);
|
||||
|
||||
if (encrypt)
|
||||
encryption = new NetEncryption(sharedSecret, isServer: false);
|
||||
encryption = new NetAESEncryption(peer.Peer, sharedSecret, 0, sharedSecret.Length);
|
||||
|
||||
byte[] keyBytes;
|
||||
if (hasPubKey)
|
||||
@@ -173,18 +172,11 @@ namespace Robust.Shared.Network
|
||||
keyBytes = encRequest.PublicKey;
|
||||
}
|
||||
|
||||
if (keyBytes.Length != CryptoBox.PublicKeyBytes)
|
||||
{
|
||||
connection.Disconnect("Invalid public key length");
|
||||
return;
|
||||
}
|
||||
var rsaKey = RSA.Create();
|
||||
rsaKey.ImportRSAPublicKey(keyBytes, out _);
|
||||
|
||||
// Data is [shared]+[verify]
|
||||
var data = new byte[sharedSecret.Length + encRequest.VerifyToken.Length];
|
||||
sharedSecret.CopyTo(data.AsSpan());
|
||||
encRequest.VerifyToken.CopyTo(data.AsSpan(sharedSecret.Length));
|
||||
|
||||
var sealedData = CryptoBox.Seal(data, keyBytes);
|
||||
var encryptedSecret = rsaKey.Encrypt(sharedSecret, RSAEncryptionPadding.OaepSHA256);
|
||||
var encryptedVerifyToken = rsaKey.Encrypt(encRequest.VerifyToken, RSAEncryptionPadding.OaepSHA256);
|
||||
|
||||
var authHashBytes = MakeAuthHash(sharedSecret, keyBytes);
|
||||
var authHash = Convert.ToBase64String(authHashBytes);
|
||||
@@ -198,7 +190,8 @@ namespace Robust.Shared.Network
|
||||
|
||||
var encryptionResponse = new MsgEncryptionResponse
|
||||
{
|
||||
SealedData = sealedData,
|
||||
SharedSecret = encryptedSecret,
|
||||
VerifyToken = encryptedVerifyToken,
|
||||
UserId = userId!.Value.UserId
|
||||
};
|
||||
|
||||
|
||||
@@ -23,24 +23,22 @@ Note that the S->C packet AFTER `MsgLoginStart` is preceded with a bool (+pad) t
|
||||
|
||||
A more detailed overview is here:
|
||||
|
||||
First the client sends `MsgLoginStart`. This contains the client's username, whether it wants to authenticate, and whether it needs the server's public encryption key sent (when authenticating && it doesn't have it yet from the launcher).
|
||||
First the client sends `MsgLoginStart`. This contains the client's username, whether it wants to authenticate, and whether it needs the server's public RSA key sent (when authenticating && it doesn't have it yet from the launcher).
|
||||
|
||||
The server can then choose to do block the client, let the client authenticate, or let the client in as guest. If it lets the client in as guest it skips straight to sending `MsgLoginSuccess` (see below). Otherwise it will send an `MsgEncryptionRequest` to the client to initiate authentication.
|
||||
|
||||
`MsgEncryptionRequest` contains a random verify token sent by the server, as well as the server's public encryption key (if requested).
|
||||
`MsgEncryptionRequest` contains a random verify token sent by the server, as well as the server's public RSA key (if requested).
|
||||
|
||||
When the client receives `MsgEncryptionRequest`, it will generate a 32-byte random secret. It will then generate an SHA-256 hash of this secret and the server's public key. This hash is POSTed to `api/session/join` (along with login token in `Authorization` header) on the auth server. The shared secret and verify token are separately encrypted with the server's encryption key, then sent along with the client's account GUID to the server in `MsgEncryptionResponse`.
|
||||
When the client receives `MsgEncryptionRequest`, it will generate a 32-byte random secret. It will then generate an SHA-256 hash of this secret and the server's public key. This hash is POSTed to `api/session/join` (along with login token in `Authorization` header) on the auth server. The shared secret and verify token are separately encrypted with the server's RSA key, then sent along with the client's account GUID to the server in `MsgEncryptionResponse`.
|
||||
|
||||
The server will then decrypt the verify token and shared secret with its private encryption key. If the verify token does not match then drop the client (to check if the client is using the correct key). Then the server will generate the same hash as mentioned earlier and GET it to `api/session/hasJoined?hash=<hash>&userId=<userId>` to check if the user did indeed authenticate correctly. And also gets the user's username and GUID again because why not.
|
||||
The server will then decrypt the verify token and shared secret with its private RSA key. If the verify token does not match then drop the client (to check if the client is using the correct key). Then the server will generate the same hash as mentioned earlier and GET it to `api/session/hasJoined?hash=<hash>&userId=<userId>` to check if the user did indeed authenticate correctly. And also gets the user's username and GUID again because why not.
|
||||
|
||||
From this point on, if authenticating, all messages sent between client and server will be encrypted with the shared secret generated earlier.
|
||||
From this point on, if authenticating, all messages sent between client and server will be AES encrypted with the shared secret generated earlier.
|
||||
|
||||
Then the server shall reply with `MsgLoginSuccess` with the assigned username/userID if login is successful.
|
||||
|
||||
I think that was everything.
|
||||
|
||||
Oh yeah, the server generates a new encryption key every startup and exposes it via its status API on `/info`.
|
||||
|
||||
We use libsodium for most of the crypto stuff. The public/private key stuff is with [Sealed box](https://doc.libsodium.org/public-key_cryptography/sealed_boxes). Packet encryption for game packets is done with [AEAD XChaCha20-Poly1305](https://doc.libsodium.org/secret-key_cryptography/aead/chacha20-poly1305/xchacha20-poly1305_construction).
|
||||
Oh yeah, the server generates a new 2048-bit RSA key every startup and exposes it via its status API on `/info`.
|
||||
|
||||
This is a rough outline. If you want complete gritty details just check the damn code.
|
||||
|
||||
@@ -10,25 +10,31 @@ using Robust.Shared.AuthLib;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network.Messages.Handshake;
|
||||
using Robust.Shared.Utility;
|
||||
using SpaceWizards.Sodium;
|
||||
|
||||
namespace Robust.Shared.Network
|
||||
{
|
||||
partial class NetManager
|
||||
{
|
||||
private readonly byte[] _cryptoPrivateKey = new byte[CryptoBox.SecretKeyBytes];
|
||||
private const int RsaKeySize = 2048;
|
||||
|
||||
public byte[] CryptoPublicKey { get; } = new byte[CryptoBox.PublicKeyBytes];
|
||||
private RSA? _authRsaPrivateKey;
|
||||
|
||||
public byte[]? RsaPublicKey { get; private set; }
|
||||
public AuthMode Auth { get; private set; }
|
||||
|
||||
public Func<string, Task<NetUserId?>>? AssignUserIdCallback { get; set; }
|
||||
public IServerNetManager.NetApprovalDelegate? HandleApprovalCallback { get; set; }
|
||||
|
||||
private void SAGenerateKeys()
|
||||
private void SAGenerateRsaKeys()
|
||||
{
|
||||
CryptoBox.KeyPair(CryptoPublicKey, _cryptoPrivateKey);
|
||||
_authRsaPrivateKey = RSA.Create(RsaKeySize);
|
||||
RsaPublicKey = _authRsaPrivateKey.ExportRSAPublicKey();
|
||||
|
||||
Logger.DebugS("auth", "Public key is {0}", Convert.ToBase64String(CryptoPublicKey));
|
||||
/*
|
||||
Logger.DebugS("auth", "Private RSA key is {0}",
|
||||
Convert.ToBase64String(_authRsaPrivateKey.ExportRSAPrivateKey()));
|
||||
*/
|
||||
Logger.DebugS("auth", "Public RSA key is {0}", Convert.ToBase64String(RsaPublicKey));
|
||||
}
|
||||
|
||||
private async void HandleHandshake(NetPeerData peer, NetConnection connection)
|
||||
@@ -66,7 +72,7 @@ namespace Robust.Shared.Network
|
||||
RandomNumberGenerator.Fill(verifyToken);
|
||||
var msgEncReq = new MsgEncryptionRequest
|
||||
{
|
||||
PublicKey = needPk ? CryptoPublicKey : Array.Empty<byte>(),
|
||||
PublicKey = needPk ? RsaPublicKey : Array.Empty<byte>(),
|
||||
VerifyToken = verifyToken
|
||||
};
|
||||
|
||||
@@ -81,14 +87,18 @@ namespace Robust.Shared.Network
|
||||
var msgEncResponse = new MsgEncryptionResponse();
|
||||
msgEncResponse.ReadFromBuffer(incPacket);
|
||||
|
||||
var encResp = new byte[verifyToken.Length + SharedKeyLength];
|
||||
var ret = CryptoBox.SealOpen(
|
||||
encResp,
|
||||
msgEncResponse.SealedData,
|
||||
CryptoPublicKey,
|
||||
_cryptoPrivateKey);
|
||||
|
||||
if (!ret)
|
||||
byte[] verifyTokenCheck;
|
||||
byte[] sharedSecret;
|
||||
try
|
||||
{
|
||||
verifyTokenCheck = _authRsaPrivateKey!.Decrypt(
|
||||
msgEncResponse.VerifyToken,
|
||||
RSAEncryptionPadding.OaepSHA256);
|
||||
sharedSecret = _authRsaPrivateKey!.Decrypt(
|
||||
msgEncResponse.SharedSecret,
|
||||
RSAEncryptionPadding.OaepSHA256);
|
||||
}
|
||||
catch (CryptographicException)
|
||||
{
|
||||
// Launcher gives the client the public RSA key of the server BUT
|
||||
// that doesn't persist if the server restarts.
|
||||
@@ -98,20 +108,16 @@ namespace Robust.Shared.Network
|
||||
return;
|
||||
}
|
||||
|
||||
// Data is [shared]+[verify]
|
||||
var verifyTokenCheck = encResp[SharedKeyLength..];
|
||||
var sharedSecret = encResp[..SharedKeyLength];
|
||||
|
||||
if (!verifyToken.AsSpan().SequenceEqual(verifyTokenCheck))
|
||||
if (!verifyToken.SequenceEqual(verifyTokenCheck))
|
||||
{
|
||||
connection.Disconnect("Verify token is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msgLogin.Encrypt)
|
||||
encryption = new NetEncryption(sharedSecret, isServer: true);
|
||||
encryption = new NetAESEncryption(peer.Peer, sharedSecret, 0, sharedSecret.Length);
|
||||
|
||||
var authHashBytes = MakeAuthHash(sharedSecret, CryptoPublicKey!);
|
||||
var authHashBytes = MakeAuthHash(sharedSecret, RsaPublicKey!);
|
||||
var authHash = Base64Helpers.ConvertToBase64Url(authHashBytes);
|
||||
|
||||
var client = new HttpClient();
|
||||
|
||||
@@ -18,7 +18,6 @@ using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using SpaceWizards.Sodium;
|
||||
|
||||
namespace Robust.Shared.Network
|
||||
{
|
||||
@@ -39,7 +38,7 @@ namespace Robust.Shared.Network
|
||||
/// </summary>
|
||||
public sealed partial class NetManager : IClientNetManager, IServerNetManager
|
||||
{
|
||||
internal const int SharedKeyLength = CryptoAeadXChaCha20Poly1305Ietf.KeyBytes; // 32 bytes
|
||||
internal const int AesKeyLength = 32;
|
||||
|
||||
[Dependency] private readonly IRobustSerializer _serializer = default!;
|
||||
|
||||
@@ -267,7 +266,7 @@ namespace Robust.Shared.Network
|
||||
|
||||
if (IsServer)
|
||||
{
|
||||
SAGenerateKeys();
|
||||
SAGenerateRsaKeys();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -831,7 +830,10 @@ namespace Robust.Shared.Network
|
||||
|
||||
var encryption = IsServer ? channel.Encryption : _clientEncryption;
|
||||
|
||||
encryption?.Decrypt(msg);
|
||||
if (encryption != null)
|
||||
{
|
||||
msg.Decrypt(encryption);
|
||||
}
|
||||
|
||||
var id = msg.ReadByte();
|
||||
|
||||
@@ -1060,8 +1062,10 @@ namespace Robust.Shared.Network
|
||||
|
||||
var peer = channel.Connection.Peer;
|
||||
var packet = BuildMessage(message, peer);
|
||||
|
||||
channel.Encryption?.Encrypt(packet);
|
||||
if (channel.Encryption != null)
|
||||
{
|
||||
packet.Encrypt(channel.Encryption);
|
||||
}
|
||||
|
||||
var method = message.DeliveryMethod;
|
||||
peer.SendMessage(packet, channel.Connection, method);
|
||||
@@ -1101,8 +1105,10 @@ namespace Robust.Shared.Network
|
||||
var peer = _netPeers[0];
|
||||
var packet = BuildMessage(message, peer.Peer);
|
||||
var method = message.DeliveryMethod;
|
||||
|
||||
_clientEncryption?.Encrypt(packet);
|
||||
if (_clientEncryption != null)
|
||||
{
|
||||
packet.Encrypt(_clientEncryption);
|
||||
}
|
||||
|
||||
peer.Peer.SendMessage(packet, peer.ConnectionsWithChannels[0], method);
|
||||
LogSend(message, method, packet);
|
||||
|
||||
@@ -302,14 +302,6 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
{
|
||||
// TODO: When this gets ECSd add a helper and remove
|
||||
|
||||
if (seed.Deleted)
|
||||
{
|
||||
// This should never happen. Yet it does.
|
||||
Logger.Error($"Deleted physics component in awake bodies set. Owner Uid: {seed.Owner}. Physics map: {_entityManager.ToPrettyString(Owner)}");
|
||||
RemoveBody(seed);
|
||||
continue;
|
||||
}
|
||||
|
||||
// I tried not running prediction for non-contacted entities but unfortunately it looked like shit
|
||||
// when contact broke so if you want to try that then GOOD LUCK.
|
||||
if (seed.Island ||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
<PackageReference Include="YamlDotNet" Version="9.1.4" />
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageReference Include="Linguini.Bundle" Version="0.1.3" />
|
||||
<PackageReference Include="SpaceWizards.Sodium" Version="0.1.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Lidgren.Network\Lidgren.Network.csproj" />
|
||||
|
||||
@@ -302,7 +302,7 @@ namespace Robust.UnitTesting
|
||||
}
|
||||
}
|
||||
|
||||
public byte[]? CryptoPublicKey => null;
|
||||
public byte[]? RsaPublicKey => null;
|
||||
public AuthMode Auth => AuthMode.Disabled;
|
||||
public Func<string, Task<NetUserId?>>? AssignUserIdCallback { get; set; }
|
||||
public IServerNetManager.NetApprovalDelegate? HandleApprovalCallback { get; set; }
|
||||
|
||||
@@ -292,17 +292,17 @@ namespace Robust.UnitTesting.Server.GameObjects.Components
|
||||
public override List<EntityUid> ExpectedEntities => _expectedEntities;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void InternalInsert(EntityUid toinsert, IEntityManager entMan, MetaDataComponent? meta = null)
|
||||
protected override void InternalInsert(EntityUid toinsert, IEntityManager entMan)
|
||||
{
|
||||
_containerList.Add(toinsert);
|
||||
base.InternalInsert(toinsert, entMan, meta);
|
||||
base.InternalInsert(toinsert, entMan);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void InternalRemove(EntityUid toremove, IEntityManager entMan, MetaDataComponent? meta = null)
|
||||
protected override void InternalRemove(EntityUid toremove, IEntityManager entMan)
|
||||
{
|
||||
_containerList.Remove(toremove);
|
||||
base.InternalRemove(toremove, entMan, meta);
|
||||
base.InternalRemove(toremove, entMan);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
Reference in New Issue
Block a user