Compare commits

..

4 Commits

Author SHA1 Message Date
Pieter-Jan Briers
8d926d0f97 Version: 220.2.2 2024-08-11 19:32:34 +02:00
Pieter-Jan Briers
4cbbb6f848 Compile compat fixes
(cherry picked from commit 025d90d281)
(cherry picked from commit 799702b814)
(cherry picked from commit 4600ee8e5788891f1b610e2d5141fb4e1228d323)
2024-08-11 19:32:34 +02:00
Pieter-Jan Briers
d9a4e0d628 Version: 220.2.1 2024-08-11 17:56:08 +02:00
Pieter-Jan Briers
d282c46d44 Security updates (#5353)
* Fix security bug in WritableDirProvider.OpenOsWindow()

Reported by @NarryG and @nyeogmi

* Sandbox updates

* Update ImageSharp again

(cherry picked from commit 7d778248ee)
(cherry picked from commit f66cda74e95619ddba2221bda644bf4394619805)
(cherry picked from commit db8ba83866c523e08e4fba0b80cd954f4f190613)
2024-08-11 17:56:08 +02:00
68 changed files with 570 additions and 1185 deletions

View File

@@ -7,18 +7,6 @@ indent_size = 4
trim_trailing_whitespace = true
charset = utf-8
max_line_length = 120
# ReSharper properties
resharper_csharp_max_line_length = 120
resharper_csharp_wrap_after_declaration_lpar = true
resharper_csharp_wrap_arguments_style = chop_if_long
resharper_csharp_wrap_parameters_style = chop_if_long
resharper_keep_existing_attribute_arrangement = true
resharper_place_field_attribute_on_same_line = if_owner_is_single_line
resharper_wrap_chained_binary_patterns = chop_if_long
resharper_wrap_chained_method_calls = chop_if_long
[*.{csproj,xml,yml,dll.config,targets,props}]
indent_size = 2

View File

@@ -10,7 +10,7 @@ jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest ] # , macos-latest] - temporarily disabled due to libfreetype.dll errors.
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}

View File

@@ -1,4 +1,14 @@
<Project>
<PropertyGroup>
<!--
We actually set ManagePackageVersionsCentrally manually in another import file.
Since .NET SDK 8.0.300, ManagePackageVersionsCentrally is automatically set if Directory.Packages.props exists.
https://github.com/NuGet/NuGet.Client/pull/5572
We actively negate this here, as we have some packages in tree we don't want such automatic behavior for.
We use Directory.Build.props to get copy the state *after* our MSBuild config but before Nuget's config.
-->
<ManagePackageVersionsCentrally />
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
@@ -45,7 +55,7 @@
<PackageVersion Include="Serilog" Version="3.1.1" />
<PackageVersion Include="Serilog.Sinks.Loki" Version="4.0.0-beta3" />
<PackageVersion Include="SharpZstd.Interop" Version="1.5.2-beta2" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.3" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.5" />
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.0" />
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.1.1" />
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.0.2" />

View File

@@ -1,4 +1,4 @@
<Project>
<!-- This file automatically reset by Tools/version.py -->
<!-- This file automatically reset by Tools/version.py -->

View File

@@ -54,108 +54,10 @@ END TEMPLATE-->
*None yet*
## 222.2.0
### New features
* Added `EntityQuery.Comp()` (abbreviation of `GetComponent()`)
### Bugfixes
* Fix `SerializationManager.TryGetVariableType` checking the wrong property.
* Fixed GrammarSystem mispredicting a character's gender
### Other
* User interface system now performs range checks in parallel
## 220.2.2
## 222.1.1
### Bugfixes
* Fixed never setting BoundUserInterface.State.
### Other
* Add truncate for filesaving.
* Add method for getting the type of a data field by name from ISerializationManager.
## 222.1.0
### New features
* Added `BoundKeyEventArgs.IsRepeat`.
* Added `net.lidgren_log_warning` and `net.lidgren_log_error` CVars.
### Bugfixes
* Fix assert trip when holding repeatable keybinds.
### Other
* Updated Lidgren to v0.3.1. This should provide performance improvements if warning/error logs are disabled.
## 222.0.0
### Breaking changes
* Mark IComponentFactory argument in EntityPrototype as mandatory.
### New features
* Add `EntProtoId<T>` to check for components on the attached entity as well.
### Bugfixes
* Fix PVS iterating duplicate chunks for multiple viewsubscriptions.
### Other
* Defer clientside BUI opens if it's the first state that comes in.
## 221.2.0
### New features
* Add SetMapAudio helper to SharedAudioSystem to setup map-wide audio entities.
* Add SetWorldRotNoLerp method to SharedTransformSystem to avoid client lerping.
### Bugfixes
* `SpriteComponent.CopyFrom` now copies `CopyToShaderParameters` configuration.
## 221.1.0
## 221.0.0
### Breaking changes
* `EntParentChangedMessage.OldMapId` is now an `EntityUid` instead of `MapId`
* `TransformSystem.DetachParentToNull()` is being renamed to `DetachEntity`
* The order in which `MoveEvent` handlers are invoked has been changed to prioritise engine subscriptions
### New features
* Added `UpdateHovered()` and `SetHovered()` to `IUserInterfaceManager`, for updating or modifying the currently hovered control.
* Add SwapPositions to TransformSystem to swap two entity's transforms.
### Bugfixes
* Improve client gamestate exception tolerance.
### Other
* If the currently hovered control is disposed, `UserInterfaceManager` will now look for a new control, rather than just setting the hovered control to null.
### Internal
* Use more `EntityQuery<T>` internally in EntityManager and PhysicsSystem.
## 220.2.1
## 220.2.0
@@ -183,7 +85,7 @@ END TEMPLATE-->
### Breaking changes
* Refactor UserInterfaceSystem.
* Refactor UserInterfaceSystem.
- The API has been significantly cleaned up and standardised, most noticeably callers don't need to worry about TryGetUi and can rely on either HasUi, SetUiState, CloseUi, or OpenUi to handle their code as appropriate.
- Interface data is now stored via key rather than as a flat list which is a breaking change for YAML.
- BoundUserInterfaces can now be completely handled via Shared code. Existing Server-side callers will behave similarly to before.

View File

@@ -1,57 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Testing.Verifiers;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.NUnit.AnalyzerVerifier<Robust.Analyzers.NoUncachedRegexAnalyzer>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
public sealed class NoUncachedRegexAnalyzerTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<NoUncachedRegexAnalyzer, NUnitVerifier>()
{
TestState =
{
Sources = { code }
},
};
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task Test()
{
const string code = """
using System.Text.RegularExpressions;
public static class Foo
{
public static void Bad()
{
Regex.Replace("foo", "bar", "baz");
}
public static void Good()
{
var r = new Regex("bar");
r.Replace("foo", "baz");
}
}
""";
await Verifier(code,
// /0/Test0.cs(7,9): warning RA0026: Usage of a static Regex function that takes in a pattern string. This can cause constant re-parsing of the pattern.
VerifyCS.Diagnostic().WithSpan(7, 9, 7, 43)
);
}
}

View File

@@ -1,66 +0,0 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class NoUncachedRegexAnalyzer : DiagnosticAnalyzer
{
private const string RegexTypeName = "Regex";
private const string RegexType = $"System.Text.RegularExpressions.{RegexTypeName}";
private static readonly DiagnosticDescriptor Rule = new (
Diagnostics.IdUncachedRegex,
"Use of uncached static Regex function",
"Usage of a static Regex function that takes in a pattern string. This can cause constant re-parsing of the pattern.",
"Usage",
DiagnosticSeverity.Warning,
true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public static readonly HashSet<string> BadFunctions =
[
"Count",
"EnumerateMatches",
"IsMatch",
"Match",
"Matches",
"Replace",
"Split"
];
public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.RegisterOperationAction(CheckInvocation, OperationKind.Invocation);
}
private static void CheckInvocation(OperationAnalysisContext context)
{
if (context.Operation is not IInvocationOperation invocation)
return;
// All Regex functions we care about are static.
var targetMethod = invocation.TargetMethod;
if (!targetMethod.IsStatic)
return;
// Bail early.
if (targetMethod.ContainingType.Name != "Regex")
return;
var regexType = context.Compilation.GetTypeByMetadataName(RegexType);
if (!SymbolEqualityComparer.Default.Equals(regexType, targetMethod.ContainingType))
return;
if (!BadFunctions.Contains(targetMethod.Name))
return;
context.ReportDiagnostic(Diagnostic.Create(Rule, invocation.Syntax.GetLocation()));
}
}

View File

@@ -239,7 +239,6 @@ namespace Robust.Build.Tasks
engine.LogErrorEvent(new BuildErrorEventArgs("XAMLIL", "", res.FilePath, 0, 0, 0, 0,
$"{res.FilePath}: {e.Message}", "", "CompileRobustXaml"));
}
res.Remove();
}
return true;
}

View File

@@ -11,7 +11,7 @@ namespace Robust.Client.Console.Commands
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
var type = Type.GetType(args[0]);
var type = GetType(args[0]);
if (type == null)
{
@@ -25,6 +25,17 @@ namespace Robust.Client.Console.Commands
shell.WriteLine(sig);
}
}
private Type? GetType(string name)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (assembly.GetType(name) is { } type)
return type;
}
return null;
}
}
#endif
}

View File

@@ -1681,8 +1681,6 @@ namespace Robust.Client.GameObjects
DirOffset = toClone.DirOffset;
_autoAnimated = toClone._autoAnimated;
RenderingStrategy = toClone.RenderingStrategy;
if (toClone.CopyToShaderParameters is { } copyToShaderParameters)
CopyToShaderParameters = new CopyToShaderParameters(copyToShaderParameters);
}
void ISerializationHooks.AfterDeserialization()
@@ -2157,12 +2155,6 @@ namespace Robust.Client.GameObjects
public object LayerKey = layerKey;
public string? ParameterTexture;
public string? ParameterUV;
public CopyToShaderParameters(CopyToShaderParameters toClone) : this(toClone.LayerKey)
{
ParameterTexture = toClone.ParameterTexture;
ParameterUV = toClone.ParameterUV;
}
}
void IAnimationProperties.SetAnimatableProperty(string name, object value)

View File

@@ -125,8 +125,6 @@ namespace Robust.Client.GameStates
#endif
private bool _resettingPredictedEntities;
private readonly List<EntityUid> _brokenEnts = new();
private readonly List<(EntityUid, NetEntity)> _toStart = new();
/// <inheritdoc />
public void Initialize()
@@ -669,16 +667,7 @@ namespace Robust.Client.GameStates
foreach (var netEntity in createdEntities)
{
#if EXCEPTION_TOLERANCE
if (!_entityManager.TryGetEntityData(netEntity, out _, out var meta))
{
_sawmill.Error($"Encountered deleted entity while merging implicit data! NetEntity: {netEntity}");
continue;
}
#else
var (_, meta) = _entityManager.GetEntityData(netEntity);
#endif
var compData = _compDataPool.Get();
_outputData.Add(netEntity, compData);
@@ -887,22 +876,9 @@ namespace Robust.Client.GameStates
{
foreach (var (entity, data) in _toApply)
{
#if EXCEPTION_TOLERANCE
try
{
#endif
HandleEntityState(entity, data.NetEntity, data.Meta, _entities.EventBus, data.curState,
data.nextState, data.LastApplied, curState.ToSequence, data.EnteringPvs);
#if EXCEPTION_TOLERANCE
}
catch (Exception e)
{
_sawmill.Error($"Caught exception while applying entity state. Entity: {_entities.ToPrettyString(entity)}. Exception: {e}");
_entityManager.DeleteEntity(entity);
RequestFullState();
continue;
}
#endif
data.nextState, data.LastApplied, curState.ToSequence, data.EnteringPvs);
if (!data.EnteringPvs)
continue;
@@ -941,7 +917,7 @@ namespace Robust.Client.GameStates
{
try
{
ProcessDeletions(delSpan, xforms, metas, xformSys);
ProcessDeletions(delSpan, xforms, xformSys);
}
catch (Exception e)
{
@@ -986,7 +962,6 @@ namespace Robust.Client.GameStates
}
var xforms = _entities.GetEntityQuery<TransformComponent>();
var metas = _entities.GetEntityQuery<MetaDataComponent>();
var xformSys = _entitySystemManager.GetEntitySystem<SharedTransformSystem>();
_toDelete.Clear();
@@ -1015,12 +990,12 @@ namespace Robust.Client.GameStates
// This entity is going to get deleted, but maybe some if its children won't be, so lets detach them to
// null. First we will detach the parent in order to reduce the number of broadphase/lookup updates.
xformSys.DetachEntity(ent, xform);
xformSys.DetachParentToNull(ent, xform);
// Then detach all children.
foreach (var child in xform._children)
{
xformSys.DetachEntity(child, xforms.Get(child), metas.Get(child), xform);
xformSys.DetachParentToNull(child, xforms.GetComponent(child), xform);
if (deleteClientChildren
&& !deleteClientEntities // don't add duplicates
@@ -1039,9 +1014,9 @@ namespace Robust.Client.GameStates
}
}
private void ProcessDeletions(ReadOnlySpan<NetEntity> delSpan,
private void ProcessDeletions(
ReadOnlySpan<NetEntity> delSpan,
EntityQuery<TransformComponent> xforms,
EntityQuery<MetaDataComponent> metas,
SharedTransformSystem xformSys)
{
// Processing deletions is non-trivial, because by default deletions will also delete all child entities.
@@ -1068,13 +1043,13 @@ namespace Robust.Client.GameStates
continue; // Already deleted? or never sent to us?
// First, a single recursive map change
xformSys.DetachEntity(id.Value, xform);
xformSys.DetachParentToNull(id.Value, xform);
// Then detach all children.
var childEnumerator = xform.ChildEnumerator;
while (childEnumerator.MoveNext(out var child))
{
xformSys.DetachEntity(child, xforms.Get(child), metas.Get(child), xform);
xformSys.DetachParentToNull(child, xforms.GetComponent(child), xform);
}
// Finally, delete the entity.
@@ -1169,7 +1144,7 @@ namespace Robust.Client.GameStates
}
meta._flags |= MetaDataFlags.Detached;
xformSys.DetachEntity(ent.Value, xform);
xformSys.DetachParentToNull(ent.Value, xform);
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) == 0);
if (container != null)
@@ -1182,58 +1157,63 @@ namespace Robust.Client.GameStates
private void InitializeAndStart(Dictionary<NetEntity, EntityState> toCreate)
{
_toStart.Clear();
var metaQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
#if EXCEPTION_TOLERANCE
var brokenEnts = new List<EntityUid>();
#endif
using (_prof.Group("Initialize Entity"))
{
EntityUid entity = default;
foreach (var netEntity in toCreate.Keys)
{
var entity = _entityManager.GetEntity(netEntity);
#if EXCEPTION_TOLERANCE
try
{
(entity, var meta) = _entityManager.GetEntityData(netEntity);
_entities.InitializeEntity(entity, meta);
_toStart.Add((entity, netEntity));
#endif
_entities.InitializeEntity(entity, metaQuery.GetComponent(entity));
#if EXCEPTION_TOLERANCE
}
catch (Exception e)
{
_sawmill.Error($"Server entity threw in Init: nent={netEntity}, ent={_entities.ToPrettyString(entity)}");
_sawmill.Error($"Server entity threw in Init: ent={_entities.ToPrettyString(entity)}");
_runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(InitializeAndStart)}");
_toCreate.Remove(netEntity);
_brokenEnts.Add(entity);
#if !EXCEPTION_TOLERANCE
throw;
#endif
brokenEnts.Add(entity);
toCreate.Remove(netEntity);
}
#endif
}
}
using (_prof.Group("Start Entity"))
{
foreach (var (entity, netEntity) in _toStart)
foreach (var netEntity in toCreate.Keys)
{
var entity = _entityManager.GetEntity(netEntity);
#if EXCEPTION_TOLERANCE
try
{
_entities.StartEntity(entity);
#endif
_entities.StartEntity(entity);
#if EXCEPTION_TOLERANCE
}
catch (Exception e)
{
_sawmill.Error($"Server entity threw in Start: nent={netEntity}, ent={_entityManager.ToPrettyString(entity)}");
_sawmill.Error($"Server entity threw in Start: ent={_entityManager.ToPrettyString(entity)}");
_runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(InitializeAndStart)}");
_toCreate.Remove(netEntity);
_brokenEnts.Add(entity);
#if !EXCEPTION_TOLERANCE
throw;
#endif
brokenEnts.Add(entity);
toCreate.Remove(netEntity);
}
#endif
}
}
foreach (var entity in _brokenEnts)
#if EXCEPTION_TOLERANCE
foreach (var entity in brokenEnts)
{
_entityManager.DeleteEntity(entity);
}
_brokenEnts.Clear();
#endif
}
private void HandleEntityState(EntityUid uid, NetEntity netEntity, MetaDataComponent meta, IEventBus bus, EntityState? curState,
@@ -1422,7 +1402,7 @@ namespace Robust.Client.GameStates
containerSys.TryGetContainingContainer(xform.ParentUid, uid, out container);
}
_entities.EntitySysManager.GetEntitySystem<TransformSystem>().DetachEntity(uid, xform);
_entities.EntitySysManager.GetEntitySystem<TransformSystem>().DetachParentToNull(uid, xform);
if (container != null)
containerSys.AddExpectedEntity(_entities.GetNetEntity(uid), container);

View File

@@ -346,7 +346,7 @@ namespace Robust.Client.Input
{
if (binding.CanRepeat)
{
return SetBindState(binding, BoundKeyState.Down, uiOnly, isRepeat);
return SetBindState(binding, BoundKeyState.Down, uiOnly);
}
return true;
@@ -375,7 +375,7 @@ namespace Robust.Client.Input
SetBindState(binding, BoundKeyState.Up);
}
private bool SetBindState(KeyBinding binding, BoundKeyState state, bool uiOnly = false, bool isRepeat = false)
private bool SetBindState(KeyBinding binding, BoundKeyState state, bool uiOnly = false)
{
if (binding.BindingType == KeyBindingType.Command && state == BoundKeyState.Down)
{
@@ -387,7 +387,6 @@ namespace Robust.Client.Input
// I honestly have no idea what the best solution here is.
// note from the future: context switches won't cause re-entrancy anymore because InputContextContainer defers context switches
DebugTools.Assert(!_currentlyFindingViewport, "Re-entrant key events??");
DebugTools.Assert(!isRepeat || binding.CanRepeat);
try
{
@@ -400,7 +399,7 @@ namespace Robust.Client.Input
binding.State = state;
var eventArgs = new BoundKeyEventArgs(binding.Function, binding.State,
MouseScreenPosition, binding.CanFocus, isRepeat);
MouseScreenPosition, binding.CanFocus);
// UI returns true here into blockPass if it wants to prevent us from giving input events
// to the viewport, but doesn't want it hard-handled so we keep processing possible key actions.

View File

@@ -52,10 +52,6 @@ namespace Robust.Client.UserInterface
[ViewVariables] public bool IsMeasureValid { get; private set; }
[ViewVariables] public bool IsArrangeValid { get; private set; }
/// <summary>
/// Controls the amount of empty space in virtual pixels around the control.
/// </summary>
/// <remarks>Values can be provided as "All" or "Horizontal, Vertical" or "Left, Top, Right, Bottom"</remarks>
[ViewVariables]
public Thickness Margin
{

View File

@@ -13,7 +13,7 @@ namespace Robust.Client.UserInterface
return Task.FromResult<Stream?>(null);
}
public Task<(Stream fileStream, bool alreadyExisted)?> SaveFile(FileDialogFilters? filters = null, bool truncate = true)
public Task<(Stream fileStream, bool alreadyExisted)?> SaveFile(FileDialogFilters? filters = null)
{
return Task.FromResult<(Stream fileStream, bool alreadyExisted)?>(null);
}

View File

@@ -51,7 +51,7 @@ namespace Robust.Client.UserInterface
return await OpenFileNfd(filters);
}
public async Task<(Stream, bool)?> SaveFile(FileDialogFilters? filters, bool truncate = true)
public async Task<(Stream, bool)?> SaveFile(FileDialogFilters? filters)
{
var name = await GetSaveFileName(filters);
if (name == null)
@@ -61,7 +61,7 @@ namespace Robust.Client.UserInterface
try
{
return (File.Open(name, truncate ? FileMode.Truncate : FileMode.Open), true);
return (File.Open(name, FileMode.Open), true);
}
catch (FileNotFoundException)
{

View File

@@ -28,7 +28,6 @@ namespace Robust.Client.UserInterface
/// The file stream the user chose to save to, and whether the file already existed.
/// Null if the user cancelled the action.
/// </returns>
/// <param name="truncate">Should we truncate an existing file to 0-size then write or append.</param>
Task<(Stream fileStream, bool alreadyExisted)?> SaveFile(FileDialogFilters? filters = null, bool truncate = true);
Task<(Stream fileStream, bool alreadyExisted)?> SaveFile(FileDialogFilters? filters = null);
}
}

View File

@@ -135,17 +135,6 @@ namespace Robust.Client.UserInterface
/// Plays the UI hover sound if relevant.
/// </summary>
void HoverSound();
/// <summary>
/// Sets <see cref="CurrentlyHovered"/> to the given control.
/// </summary>
void SetHovered(Control? control);
/// <summary>
/// Forces <see cref="CurrentlyHovered"/> to get updated. This is done automatically when the mouse is moved,
/// but not necessarily a new or existing control is rearranged.
/// </summary>
void UpdateHovered();
}
public readonly struct PostDrawUIRootEventArgs

View File

@@ -9,7 +9,6 @@ using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Input;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Robust.Client.UserInterface;
@@ -21,10 +20,9 @@ internal partial class UserInterfaceManager
private bool _needUpdateActiveCursor;
[ViewVariables] public Control? KeyboardFocused { get; private set; }
[ViewVariables] public Control? CurrentlyHovered { get; private set; }
[ViewVariables] public Control? CurrentlyHovered { get; private set; } = default!;
private Control? _controlFocused;
[ViewVariables]
public Control? ControlFocused
{
@@ -102,7 +100,6 @@ internal partial class UserInterfaceManager
return;
}
var guiArgs = new GUIBoundKeyEventArgs(args.Function, args.State, args.PointerLocation, args.CanFocus,
args.PointerLocation.Position / control.UIScale - control.GlobalPosition,
args.PointerLocation.Position - control.GlobalPixelPosition);
@@ -114,20 +111,16 @@ internal partial class UserInterfaceManager
args.Handle();
}
// Attempt to ensure that keybind-up events get raised after a keybind-down.
DebugTools.Assert(!_focusedControls.TryGetValue(args.Function, out var existing)
|| !existing.VisibleInTree
|| args.IsRepeat && existing == control);
_focusedControls[args.Function] = control;
OnKeyBindDown?.Invoke(control);
}
public void KeyBindUp(BoundKeyEventArgs args)
{
// Only raise keybind-up for the control on which we previously raised keybind-down
if (!_focusedControls.Remove(args.Function, out var control) || !control.VisibleInTree)
if (!_focusedControls.TryGetValue(args.Function, out var control))
{
return;
}
var guiArgs = new GUIBoundKeyEventArgs(args.Function, args.State, args.PointerLocation, args.CanFocus,
args.PointerLocation.Position / control.UIScale - control.GlobalPosition,
@@ -138,6 +131,7 @@ internal partial class UserInterfaceManager
// Always mark this as handled.
// The only case it should not be is if we do not have a control to click on,
// in which case we never reach this.
_focusedControls.Remove(args.Function);
args.Handle();
}
@@ -146,7 +140,23 @@ internal partial class UserInterfaceManager
_resetTooltipTimer();
// Update which control is considered hovered.
var newHovered = MouseGetControl(mouseMoveEventArgs.Position);
SetHovered(newHovered);
if (newHovered != CurrentlyHovered)
{
_clearTooltip();
CurrentlyHovered?.MouseExited();
CurrentlyHovered = newHovered;
CurrentlyHovered?.MouseEntered();
if (CurrentlyHovered != null)
{
_tooltipDelay = CurrentlyHovered.TooltipDelay ?? TooltipDelay;
}
else
{
_tooltipDelay = null;
}
_needUpdateActiveCursor = true;
}
var target = ControlFocused ?? newHovered;
if (target != null)
@@ -162,33 +172,6 @@ internal partial class UserInterfaceManager
}
}
public void UpdateHovered()
{
var ctrl = MouseGetControl(_inputManager.MouseScreenPosition);
SetHovered(ctrl);
}
public void SetHovered(Control? control)
{
if (control == CurrentlyHovered)
return;
_clearTooltip();
CurrentlyHovered?.MouseExited();
CurrentlyHovered = control;
CurrentlyHovered?.MouseEntered();
if (CurrentlyHovered != null)
{
_tooltipDelay = CurrentlyHovered.TooltipDelay ?? TooltipDelay;
}
else
{
_tooltipDelay = null;
}
_needUpdateActiveCursor = true;
}
private void UpdateActiveCursor()
{
// Consider mouse input focus first so that dragging windows don't act up etc.

View File

@@ -77,12 +77,15 @@ internal sealed partial class UserInterfaceManager
ReleaseKeyboardFocus(control);
RemoveModal(control);
if (control == ControlFocused)
ControlFocused = null;
if (control == CurrentlyHovered)
UpdateHovered();
{
control.MouseExited();
CurrentlyHovered = null;
_clearTooltip();
}
if (control != ControlFocused) return;
ControlFocused = null;
}
public void PushModal(Control modal)

View File

@@ -29,7 +29,6 @@ public static class Diagnostics
public const string IdComponentPauseNoParentAttribute = "RA0023";
public const string IdComponentPauseWrongTypeAttribute = "RA0024";
public const string IdDependencyFieldAssigned = "RA0025";
public const string IdUncachedRegex = "RA0026";
public static SuppressionDescriptor MeansImplicitAssignment =>
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");

View File

@@ -42,17 +42,6 @@ public sealed partial class AudioSystem : SharedAudioSystem
component.Source = new DummyAudioSource();
}
public override void SetMapAudio(Entity<AudioComponent>? audio)
{
if (audio == null)
return;
base.SetMapAudio(audio);
// Also need a global override because clients not near 0,0 won't get the audio.
_pvs.AddGlobalOverride(audio.Value);
}
private void AddAudioFilter(EntityUid uid, AudioComponent component, Filter filter)
{
var count = filter.Count;

View File

@@ -51,15 +51,10 @@ internal sealed class PvsSession(ICommonSession session, ResizableMemoryRegion<P
public (GameTick ToTick, List<PvsIndex> PreviouslySent)? LastSent;
/// <summary>
/// Visible chunks, sorted by proximity to the client's viewers.
/// Visible chunks, sorted by proximity to the clients's viewers;
/// </summary>
public readonly List<(PvsChunk Chunk, float ChebyshevDistance)> Chunks = new();
/// <summary>
/// Unsorted set of visible chunks. Used to construct the <see cref="Chunks"/> list.
/// </summary>
public readonly HashSet<PvsChunk> ChunkSet = new();
/// <summary>
/// Squared distance ta all of the visible chunks.
/// </summary>
@@ -122,7 +117,6 @@ internal sealed class PvsSession(ICommonSession session, ResizableMemoryRegion<P
{
PlayerStates.Clear();
Chunks.Clear();
ChunkSet.Clear();
States.Clear();
State = null;
}

View File

@@ -90,13 +90,15 @@ internal sealed partial class PvsSystem
foreach (var session in _sessions)
{
session.Chunks.Clear();
session.ChunkSet.Clear();
GetSessionViewers(session);
foreach (var eye in session.Viewers)
{
GetVisibleChunks(eye, session.ChunkSet);
GetVisibleChunks(eye, session.Chunks);
}
// The list of visible chunks should be unique.
DebugTools.Assert(session.Chunks.Select(x => x.Chunk).ToHashSet().Count == session.Chunks.Count);
}
DebugTools.Assert(_dirtyChunks.ToHashSet().Count == _dirtyChunks.Count);
DebugTools.Assert(_cleanChunks.ToHashSet().Count == _cleanChunks.Count);
@@ -106,7 +108,7 @@ internal sealed partial class PvsSystem
/// Get the chunks visible to a single entity and add them to a player's set of visible chunks.
/// </summary>
private void GetVisibleChunks(Entity<TransformComponent, EyeComponent?> eye,
HashSet<PvsChunk> chunks)
List<(PvsChunk Chunk, float ChebyshevDistance)> playerChunks)
{
var (viewPos, range, mapUid) = CalcViewBounds(eye);
if (mapUid is not {} map)
@@ -119,7 +121,7 @@ internal sealed partial class PvsSystem
if (!_chunks.TryGetValue(loc, out var chunk))
continue;
chunks.Add(chunk);
playerChunks.Add((chunk, default));
if (chunk.UpdateQueued)
continue;
@@ -145,7 +147,7 @@ internal sealed partial class PvsSystem
if (!_chunks.TryGetValue(loc, out var chunk))
continue;
chunks.Add(chunk);
playerChunks.Add((chunk, default));
if (chunk.UpdateQueued)
continue;

View File

@@ -55,10 +55,13 @@ internal sealed partial class PvsSystem
return;
}
if (xform.ParentUid != xform.GridUid && xform.ParentUid != xform.MapUid)
var root = (xform.GridUid ?? xform.MapUid);
DebugTools.AssertNotNull(root);
if (xform.ParentUid != root)
return;
var location = new PvsChunkLocation(xform.ParentUid, GetChunkIndices(xform._localPosition));
var location = new PvsChunkLocation(root.Value, GetChunkIndices(xform._localPosition));
if (meta.LastPvsLocation == location)
return;

View File

@@ -137,19 +137,15 @@ internal sealed partial class PvsSystem
if (!CullingEnabled || session.DisableCulling)
return;
var chunkSet = session.ChunkSet;
var chunks = session.Chunks;
var distances = session.ChunkDistanceSq;
DebugTools.AssertEqual(chunks.Count, 0);
distances.Clear();
distances.EnsureCapacity(chunkSet.Count);
chunks.EnsureCapacity(chunkSet.Count);
distances.EnsureCapacity(chunks.Count);
// Assemble list of chunks and their distances to the nearest eye.
foreach(var chunk in chunkSet)
foreach (ref var tuple in CollectionsMarshal.AsSpan(chunks))
{
var chunk = tuple.Chunk;
var dist = float.MaxValue;
var chebDist = float.MaxValue;
@@ -169,7 +165,7 @@ internal sealed partial class PvsSystem
}
distances.Add(dist);
chunks.Add((chunk, chebDist));
tuple.ChebyshevDistance = chebDist;
}
// Sort chunks based on distances

View File

@@ -134,7 +134,7 @@ internal sealed partial class PvsSystem : EntitySystem
SubscribeLocalEvent<TransformComponent, TransformStartupEvent>(OnTransformStartup);
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
_transform.OnBeforeMoveEvent += OnEntityMove;
_transform.OnGlobalMoveEvent += OnEntityMove;
EntityManager.EntityAdded += OnEntityAdded;
EntityManager.EntityDeleted += OnEntityDeleted;
EntityManager.AfterEntityFlush += AfterEntityFlush;
@@ -159,7 +159,7 @@ internal sealed partial class PvsSystem : EntitySystem
base.Shutdown();
_playerManager.PlayerStatusChanged -= OnPlayerStatusChanged;
_transform.OnBeforeMoveEvent -= OnEntityMove;
_transform.OnGlobalMoveEvent -= OnEntityMove;
EntityManager.EntityAdded -= OnEntityAdded;
EntityManager.EntityDeleted -= OnEntityDeleted;
EntityManager.AfterEntityFlush -= AfterEntityFlush;

View File

@@ -68,7 +68,7 @@ namespace Robust.Server.ServerStatus
if (auth != _watchdogToken)
{
// Holy shit nobody read these logs please.
_sawmill.Verbose(@"Failed auth: ""{0}"" vs ""{1}""", auth, _watchdogToken);
_sawmill.Info(@"Failed auth: ""{0}"" vs ""{1}""", auth, _watchdogToken);
await context.RespondErrorAsync(HttpStatusCode.Unauthorized);
return true;
}

View File

@@ -8,7 +8,6 @@ using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
@@ -33,12 +32,11 @@ public abstract partial class SharedAudioSystem : EntitySystem
[Dependency] private readonly INetManager _netManager = default!;
[Dependency] protected readonly IPrototypeManager ProtoMan = default!;
[Dependency] protected readonly IRobustRandom RandMan = default!;
[Dependency] protected readonly MetaDataSystem MetadataSys = default!;
/// <summary>
/// Default max range at which the sound can be heard.
/// </summary>
public const float DefaultSoundRange = 15;
public const float DefaultSoundRange = 20;
/// <summary>
/// Used in the PAS to designate the physics collision mask of occluders.
@@ -133,18 +131,6 @@ public abstract partial class SharedAudioSystem : EntitySystem
return (float) (Timing.CurTime - (component.PauseTime ?? TimeSpan.Zero) - component.AudioStart).TotalSeconds;
}
/// <summary>
/// Marks this audio as being map-based.
/// </summary>
public virtual void SetMapAudio(Entity<AudioComponent>? audio)
{
if (audio == null)
return;
audio.Value.Comp.Global = true;
MetadataSys.AddFlag(audio.Value.Owner, MetaDataFlags.Undetachable);
}
/// <summary>
/// Sets the shared state for an audio entity.
/// </summary>

View File

@@ -368,21 +368,6 @@ namespace Robust.Shared
public static readonly CVarDef<float> NetHappyEyeballsDelay =
CVarDef.Create("net.happy_eyeballs_delay", 0.025f, CVar.CLIENTONLY);
/// <summary>
/// Controls whether the networking library will log warning messages.
/// </summary>
/// <remarks>
/// Disabling this should make the networking layer more resilient against some DDoS attacks.
/// </remarks>
public static readonly CVarDef<bool> NetLidgrenLogWarning =
CVarDef.Create("net.lidgren_log_warning", true);
/// <summary>
/// Controls whether the networking library will log error messages.
/// </summary>
public static readonly CVarDef<bool> NetLidgrenLogError =
CVarDef.Create("net.lidgren_log_error", true);
/**
* SUS
*/

View File

@@ -294,9 +294,6 @@ public struct ValueList<T> : IEnumerable<T>
if (Capacity < capacity)
Grow(capacity);
if (capacity == 0)
return capacity;
return _items!.Length;
}

View File

@@ -71,8 +71,6 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
}
else
{
// TODO EXCEPTION TOLERANCE
// Ensure lookup trees update before content code handles move events.
SubscribeLocalEvent<TComp, MoveEvent>(HandleMove);
}

View File

@@ -33,7 +33,7 @@ internal sealed class RecursiveMoveSystem : EntitySystem
public override void Shutdown()
{
if (_subscribed)
_transform.OnBeforeMoveEvent -= AnythingMoved;
_transform.OnGlobalMoveEvent -= AnythingMoved;
_subscribed = false;
}
@@ -44,7 +44,7 @@ internal sealed class RecursiveMoveSystem : EntitySystem
return;
_subscribed = true;
_transform.OnBeforeMoveEvent += AnythingMoved;
_transform.OnGlobalMoveEvent += AnythingMoved;
}
private void AnythingMoved(ref MoveEvent args)

View File

@@ -32,7 +32,7 @@ namespace Robust.Shared.ContentPack
String("short").ThenReturn(PrimitiveTypeCode.Int16);
private static readonly Parser<char, PrimitiveTypeCode> UInt16TypeParser =
String("ushort").ThenReturn(PrimitiveTypeCode.UInt32);
String("ushort").ThenReturn(PrimitiveTypeCode.UInt16);
private static readonly Parser<char, PrimitiveTypeCode> Int32TypeParser =
String("int").ThenReturn(PrimitiveTypeCode.Int32);

View File

@@ -84,12 +84,146 @@ Types:
- "bool get_HasContents()"
Lidgren.Network:
NetBuffer:
All: True
Methods:
- "byte[] get_Data()"
- "void set_Data(byte[])"
- "int get_LengthBytes()"
- "void set_LengthBytes(int)"
- "int get_LengthBits()"
- "void set_LengthBits(int)"
- "long get_Position()"
- "void set_Position(long)"
- "int get_PositionInBytes()"
- "byte[] PeekDataBuffer()"
- "bool PeekBoolean()"
- "byte PeekByte()"
- "sbyte PeekSByte()"
- "byte PeekByte(int)"
- "System.Span`1<byte> PeekBytes(System.Span`1<byte>)"
- "byte[] PeekBytes(int)"
- "void PeekBytes(byte[], int, int)"
- "short PeekInt16()"
- "ushort PeekUInt16()"
- "int PeekInt32()"
- "int PeekInt32(int)"
- "uint PeekUInt32()"
- "uint PeekUInt32(int)"
- "ulong PeekUInt64()"
- "long PeekInt64()"
- "ulong PeekUInt64(int)"
- "long PeekInt64(int)"
- "float PeekFloat()"
- "System.Half PeekHalf()"
- "float PeekSingle()"
- "double PeekDouble()"
- "string PeekString()"
- "int PeekStringSize()"
- "bool ReadBoolean()"
- "byte ReadByte()"
- "bool ReadByte(ref byte)"
- "sbyte ReadSByte()"
- "byte ReadByte(int)"
- "System.Span`1<byte> ReadBytes(System.Span`1<byte>)"
- "byte[] ReadBytes(int)"
- "bool ReadBytes(int, ref byte[])"
- "bool TryReadBytes(System.Span`1<byte>)"
- "void ReadBytes(byte[], int, int)"
- "void ReadBits(System.Span`1<byte>, int)"
- "void ReadBits(byte[], int, int)"
- "short ReadInt16()"
- "ushort ReadUInt16()"
- "int ReadInt32()"
- "bool ReadInt32(ref int)"
- "int ReadInt32(int)"
- "uint ReadUInt32()"
- "bool ReadUInt32(ref uint)"
- "uint ReadUInt32(int)"
- "ulong ReadUInt64()"
- "long ReadInt64()"
- "ulong ReadUInt64(int)"
- "long ReadInt64(int)"
- "float ReadFloat()"
- "System.Half ReadHalf()"
- "float ReadSingle()"
- "bool ReadSingle(ref float)"
- "double ReadDouble()"
- "uint ReadVariableUInt32()"
- "bool ReadVariableUInt32(ref uint)"
- "int ReadVariableInt32()"
- "long ReadVariableInt64()"
- "ulong ReadVariableUInt64()"
- "float ReadSignedSingle(int)"
- "float ReadUnitSingle(int)"
- "float ReadRangedSingle(float, float, int)"
- "int ReadRangedInteger(int, int)"
- "long ReadRangedInteger(long, long)"
- "string ReadString()"
- "bool ReadString(ref string)"
- "double ReadTime(Lidgren.Network.NetConnection, bool)"
- "System.Net.IPEndPoint ReadIPEndPoint()"
- "void SkipPadBits()"
- "void ReadPadBits()"
- "void SkipPadBits(int)"
- "void EnsureBufferSize(int)"
- "void Write(bool)"
- "void Write(byte)"
- "void WriteAt(int, byte)"
- "void Write(sbyte)"
- "void Write(byte, int)"
- "void Write(byte[])"
- "void Write(System.ReadOnlySpan`1<byte>)"
- "void Write(byte[], int, int)"
- "void Write(ushort)"
- "void WriteAt(int, ushort)"
- "void Write(ushort, int)"
- "void Write(short)"
- "void WriteAt(int, short)"
- "void Write(int)"
- "void WriteAt(int, int)"
- "void Write(uint)"
- "void WriteAt(int, uint)"
- "void Write(uint, int)"
- "void Write(int, int)"
- "void Write(ulong)"
- "void WriteAt(int, ulong)"
- "void Write(ulong, int)"
- "void Write(long)"
- "void Write(long, int)"
- "void Write(System.Half)"
- "void Write(float)"
- "void Write(double)"
- "int WriteVariableUInt32(uint)"
- "int WriteVariableInt32(int)"
- "int WriteVariableInt64(long)"
- "int WriteVariableUInt64(ulong)"
- "void WriteSignedSingle(float, int)"
- "void WriteUnitSingle(float, int)"
- "void WriteRangedSingle(float, float, float, int)"
- "int WriteRangedInteger(int, int, int)"
- "int WriteRangedInteger(long, long, long)"
- "void Write(string)"
- "void Write(System.Net.IPEndPoint)"
- "void WriteTime(bool)"
- "void WriteTime(double, bool)"
- "void WritePadBits()"
- "void WritePadBits(int)"
- "void Write(Lidgren.Network.NetBuffer)"
- "void Zero(int)"
- "void .ctor()"
NetDeliveryMethod: { }
NetIncomingMessage:
All: True
Methods:
- "Lidgren.Network.NetIncomingMessageType get_MessageType()"
- "Lidgren.Network.NetDeliveryMethod get_DeliveryMethod()"
- "int get_SequenceChannel()"
- "System.Net.IPEndPoint get_SenderEndPoint()"
- "Lidgren.Network.NetConnection get_SenderConnection()"
- "double get_ReceiveTime()"
- "double ReadTime(bool)"
- "string ToString()"
NetOutgoingMessage:
All: True
Methods:
- "string ToString()"
Nett:
CommentLocation: { } # Enum
Toml:
@@ -770,7 +904,7 @@ Types:
Array:
Methods:
- "!!0 Find<>(!!0[], System.Predicate`1<!!0>)"
- "void Resize<>(ref !!0[], int)"
- "!!0 Resize<>(!!0[], int)"
- "!!1 ConvertAll<,>(!!0[], System.Converter`2<!!0, !!1>)"
- "!!0[] Empty<>()"
- "!!0[] FindAll<>(!!0[], System.Predicate`1<!!0>)"

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using Robust.Shared.Utility;
namespace Robust.Shared.ContentPack
@@ -135,11 +136,37 @@ namespace Robust.Shared.ContentPack
path = path.Directory;
var fullPath = GetFullPath(path);
Process.Start(new ProcessStartInfo
if (OperatingSystem.IsWindows())
{
UseShellExecute = true,
FileName = fullPath,
});
Process.Start(new ProcessStartInfo
{
FileName = "explorer.exe",
Arguments = ".",
WorkingDirectory = fullPath,
});
}
else if (OperatingSystem.IsMacOS())
{
Process.Start(new ProcessStartInfo
{
FileName = "open",
Arguments = ".",
WorkingDirectory = fullPath,
});
}
else if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD())
{
Process.Start(new ProcessStartInfo
{
FileName = "xdg-open",
Arguments = ".",
WorkingDirectory = fullPath,
});
}
else
{
throw new NotSupportedException("Opening OS windows not supported on this OS");
}
}
#endregion

View File

@@ -289,12 +289,6 @@ namespace Robust.Shared.GameObjects
return GetRegistration(componentType).Name;
}
[Pure]
public string GetComponentName<T>() where T : IComponent, new()
{
return GetRegistration<T>().Name;
}
[Pure]
public string GetComponentName(ushort netID)
{
@@ -330,7 +324,7 @@ namespace Robust.Shared.GameObjects
public ComponentRegistration GetRegistration<T>() where T : IComponent, new()
{
return GetRegistration(CompIdx.Index<T>());
return GetRegistration(typeof(T));
}
public ComponentRegistration GetRegistration(IComponent component)

View File

@@ -2,35 +2,45 @@ using System;
using System.Collections.Generic;
using Robust.Shared.Enums;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Robust.Shared.GameObjects.Components.Localization;
/// <summary>
/// Overrides grammar attributes specified in prototypes or localization files.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
// [Access(typeof(GrammarSystem))] TODO access
public sealed partial class GrammarComponent : Component
namespace Robust.Shared.GameObjects.Components.Localization
{
[DataField, AutoNetworkedField]
public Dictionary<string, string> Attributes = new();
[ViewVariables]
public Gender? Gender
/// <summary>
/// Overrides grammar attributes specified in prototypes or localization files.
/// </summary>
[RegisterComponent]
[NetworkedComponent()]
public sealed partial class GrammarComponent : Component
{
get => Attributes.TryGetValue("gender", out var g) ? Enum.Parse<Gender>(g, true) : null;
[Obsolete("Use GrammarSystem.SetGender instead")]
set => IoCManager.Resolve<IEntityManager>().System<GrammarSystem>().SetGender((Owner, this), value);
}
[DataField("attributes")]
public Dictionary<string, string> Attributes { get; private set; } = new();
[ViewVariables]
public bool? ProperNoun
{
get => Attributes.TryGetValue("proper", out var g) ? bool.Parse(g) : null;
[Obsolete("Use GrammarSystem.SetProperNoun instead")]
set => IoCManager.Resolve<IEntityManager>().System<GrammarSystem>().SetProperNoun((Owner, this), value);
[ViewVariables]
public Gender? Gender
{
get => Attributes.TryGetValue("gender", out var g) ? Enum.Parse<Gender>(g, true) : null;
set
{
if (value.HasValue)
Attributes["gender"] = value.Value.ToString();
else
Attributes.Remove("gender");
}
}
[ViewVariables]
public bool? ProperNoun
{
get => Attributes.TryGetValue("proper", out var g) ? bool.Parse(g) : null;
set
{
if (value.HasValue)
Attributes["proper"] = value.Value.ToString();
else
Attributes.Remove("proper");
}
}
}
}

View File

@@ -1,39 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects.Components.Localization;
namespace Robust.Shared.GameObjects;
public sealed class GrammarSystem : EntitySystem
{
public void Clear(Entity<GrammarComponent> grammar)
{
grammar.Comp.Attributes.Clear();
Dirty(grammar);
}
public bool TryGet(Entity<GrammarComponent> grammar, string key, [NotNullWhen(true)] out string? value)
{
return grammar.Comp.Attributes.TryGetValue(key, out value);
}
public void Set(Entity<GrammarComponent> grammar, string key, string? value)
{
if (value == null)
grammar.Comp.Attributes.Remove(key);
else
grammar.Comp.Attributes[key] = value;
Dirty(grammar);
}
public void SetGender(Entity<GrammarComponent> grammar, Gender? gender)
{
Set(grammar, "gender", gender?.ToString());
}
public void SetProperNoun(Entity<GrammarComponent> grammar, bool? proper)
{
Set(grammar, "proper", proper?.ToString());
}
}

View File

@@ -157,7 +157,9 @@ namespace Robust.Shared.GameObjects
if (!Initialized)
return;
_entMan.System<SharedTransformSystem>().RaiseMoveEvent((Owner, this, meta), _parent, _localPosition, oldRotation, MapUid);
var moveEvent = new MoveEvent((Owner, this, meta), Coordinates, Coordinates, oldRotation, _localRotation, _gameTiming.ApplyingState);
_entMan.EventBus.RaiseLocalEvent(Owner, ref moveEvent);
_entMan.System<SharedTransformSystem>().InvokeGlobalMoveEvent(ref moveEvent);
}
}
@@ -332,9 +334,7 @@ namespace Robust.Shared.GameObjects
if (_localPosition.EqualsApprox(value))
return;
var oldParent = _parent;
var oldPos = _localPosition;
var oldGridPos = Coordinates;
_localPosition = value;
var meta = _entMan.GetComponent<MetaDataComponent>(Owner);
_entMan.Dirty(Owner, this, meta);
@@ -343,7 +343,9 @@ namespace Robust.Shared.GameObjects
if (!Initialized)
return;
_entMan.System<SharedTransformSystem>().RaiseMoveEvent((Owner, this, meta), oldParent, oldPos, _localRotation, MapUid);
var moveEvent = new MoveEvent((Owner, this, meta), oldGridPos, Coordinates, _localRotation, _localRotation, _gameTiming.ApplyingState);
_entMan.EventBus.RaiseLocalEvent(Owner, ref moveEvent);
_entMan.System<SharedTransformSystem>().InvokeGlobalMoveEvent(ref moveEvent);
}
}
@@ -600,12 +602,8 @@ namespace Robust.Shared.GameObjects
/// move events, subscribe to the <see cref="SharedTransformSystem.OnGlobalMoveEvent"/>.
/// </summary>
[ByRefEvent]
public readonly struct MoveEvent(
Entity<TransformComponent, MetaDataComponent> entity,
EntityCoordinates oldPos,
EntityCoordinates newPos,
Angle oldRotation,
Angle newRotation)
public readonly struct MoveEvent(Entity<TransformComponent, MetaDataComponent> entity, EntityCoordinates oldPos,
EntityCoordinates newPos, Angle oldRotation, Angle newRotation, bool stateHandling = false)
{
public readonly Entity<TransformComponent, MetaDataComponent> Entity = entity;
public readonly EntityCoordinates OldPosition = oldPos;
@@ -617,6 +615,15 @@ namespace Robust.Shared.GameObjects
public TransformComponent Component => Entity.Comp1;
public bool ParentChanged => NewPosition.EntityId != OldPosition.EntityId;
[Obsolete("Check IGameTiming.ApplyingState")]
public readonly bool FromStateHandling = stateHandling;
[Obsolete]
public MoveEvent(EntityUid uid, EntityCoordinates oldPos, EntityCoordinates newPos, Angle oldRot, Angle newRot, TransformComponent xform, bool state)
: this((uid, xform, default!), oldPos, newPos, oldRot, newRot)
{
}
}
public struct TransformChildrenEnumerator : IDisposable

View File

@@ -19,7 +19,7 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// The last received state object sent from the server.
/// </summary>
protected internal BoundUserInterfaceState? State { get; internal set; }
protected BoundUserInterfaceState? State { get; private set; }
protected BoundUserInterface(EntityUid owner, Enum uiKey)
{

View File

@@ -114,7 +114,7 @@ namespace Robust.Shared.GameObjects
public void InitializeComponents(EntityUid uid, MetaDataComponent? metadata = null)
{
DebugTools.AssertOwner(uid, metadata);
metadata ??= MetaQuery.GetComponent(uid);
metadata ??= GetComponent<MetaDataComponent>(uid);
DebugTools.Assert(metadata.EntityLifeStage == EntityLifeStage.PreInit);
SetLifeStage(metadata, EntityLifeStage.Initializing);
@@ -158,12 +158,13 @@ namespace Robust.Shared.GameObjects
// TODO: please for the love of god remove these initialization order hacks.
// Init transform first, we always have it.
var transform = TransformQuery.GetComponent(uid);
var transform = GetComponent<TransformComponent>(uid);
if (transform.LifeStage == ComponentLifeStage.Initialized)
LifeStartup(transform);
// Init physics second if it exists.
if (_physicsQuery.TryComp(uid, out var phys) && phys.LifeStage == ComponentLifeStage.Initialized)
if (TryGetComponent<PhysicsComponent>(uid, out var phys)
&& phys.LifeStage == ComponentLifeStage.Initialized)
{
LifeStartup(phys);
}
@@ -293,7 +294,7 @@ namespace Robust.Shared.GameObjects
if (!uid.IsValid() || !EntityExists(uid))
throw new ArgumentException($"Entity {uid} is not valid.", nameof(uid));
AddComponentInternal(uid, newComponent, false, true, null);
AddComponentInternal(uid, newComponent, false, true);
return new CompInitializeHandle<T>(this, uid, newComponent, reg.Idx);
}
@@ -301,11 +302,10 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public void AddComponent<T>(EntityUid uid, T component, bool overwrite = false, MetaDataComponent? metadata = null) where T : IComponent
{
if (!MetaQuery.Resolve(uid, ref metadata, false))
if (!uid.IsValid() || !EntityExists(uid))
throw new ArgumentException($"Entity {uid} is not valid.", nameof(uid));
if (component == null)
throw new ArgumentNullException(nameof(component));
if (component == null) throw new ArgumentNullException(nameof(component));
#pragma warning disable CS0618 // Type or member is obsolete
if (component.Owner == default)
@@ -321,17 +321,14 @@ namespace Robust.Shared.GameObjects
AddComponentInternal(uid, component, overwrite, false, metadata);
}
private void AddComponentInternal<T>(EntityUid uid, T component, bool overwrite, bool skipInit, MetaDataComponent? metadata) where T : IComponent
private void AddComponentInternal<T>(EntityUid uid, T component, bool overwrite, bool skipInit, MetaDataComponent? metadata = null) where T : IComponent
{
if (!MetaQuery.ResolveInternal(uid, ref metadata, false))
throw new ArgumentException($"Entity {uid} is not valid.", nameof(uid));
// get interface aliases for mapping
var reg = _componentFactory.GetRegistration(component);
AddComponentInternal(uid, component, reg, overwrite, skipInit, metadata);
}
private void AddComponentInternal<T>(EntityUid uid, T component, ComponentRegistration reg, bool overwrite, bool skipInit, MetaDataComponent metadata) where T : IComponent
private void AddComponentInternal<T>(EntityUid uid, T component, ComponentRegistration reg, bool overwrite, bool skipInit, MetaDataComponent? metadata = null) where T : IComponent
{
// We can't use typeof(T) here in case T is just Component
DebugTools.Assert(component is MetaDataComponent ||
@@ -645,14 +642,13 @@ namespace Robust.Shared.GameObjects
_runtimeLog.LogException(e, nameof(CullRemovedComponents));
}
#endif
var meta = MetaQuery.GetComponent(uid);
DeleteComponent(uid, component, false, meta);
DeleteComponent(uid, component, false);
}
_deleteSet.Clear();
}
private void DeleteComponent(EntityUid entityUid, IComponent component, bool terminating, MetaDataComponent? metadata)
private void DeleteComponent(EntityUid entityUid, IComponent component, bool terminating, MetaDataComponent? metadata = null)
{
if (!MetaQuery.ResolveInternal(entityUid, ref metadata))
return;
@@ -1523,7 +1519,7 @@ namespace Robust.Shared.GameObjects
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[Pure]
public bool TryComp([NotNullWhen(true)] EntityUid? uid, [NotNullWhen(true)] out TComp1? component)
public bool TryComp(EntityUid? uid, [NotNullWhen(true)] out TComp1? component)
=> TryGetComponent(uid, out component);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -1586,13 +1582,6 @@ namespace Robust.Shared.GameObjects
return default;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[Pure]
public TComp1 Comp(EntityUid uid)
{
return GetComponent(uid);
}
#region Internal
/// <summary>

View File

@@ -11,7 +11,6 @@ using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Physics.Components;
using Robust.Shared.Player;
using Robust.Shared.Profiling;
using Robust.Shared.Prototypes;
@@ -48,7 +47,6 @@ namespace Robust.Shared.GameObjects
public EntityQuery<MetaDataComponent> MetaQuery;
public EntityQuery<TransformComponent> TransformQuery;
private EntityQuery<PhysicsComponent> _physicsQuery;
private EntityQuery<ActorComponent> _actorQuery;
#endregion Dependencies
@@ -212,7 +210,6 @@ namespace Robust.Shared.GameObjects
_containers = System<SharedContainerSystem>();
MetaQuery = GetEntityQuery<MetaDataComponent>();
TransformQuery = GetEntityQuery<TransformComponent>();
_physicsQuery = GetEntityQuery<PhysicsComponent>();
_actorQuery = GetEntityQuery<ActorComponent>();
}
@@ -281,7 +278,6 @@ namespace Robust.Shared.GameObjects
#region Entity Management
/// <inheritdoc />
public EntityUid CreateEntityUninitialized(string? prototypeName, EntityUid euid, ComponentRegistry? overrides = null)
{
return CreateEntity(prototypeName, out _, overrides);
@@ -555,7 +551,17 @@ namespace Robust.Shared.GameObjects
// Detach the base entity to null before iterating over children
// This also ensures that the entity-lookup updates don't have to be re-run for every child (which recurses up the transform hierarchy).
_xforms.DetachEntity(uid, transform, metadata, parentXform, true);
if (transform.ParentUid != EntityUid.Invalid)
{
try
{
_xforms.DetachParentToNull((uid, transform, metadata), parentXform, true);
}
catch (Exception e)
{
_sawmill.Error($"Caught exception while trying to detach parent of entity '{ToPrettyString(uid, metadata)}' to null.\n{e}");
}
}
foreach (var child in transform._children)
{

View File

@@ -19,26 +19,26 @@ namespace Robust.Shared.GameObjects
public EntityUid? OldParent { get; }
/// <summary>
/// The map that the entity was on before its parent changed.
/// 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 readonly EntityUid? OldMapId;
public MapId OldMapId { get; }
public TransformComponent Transform { get; }
/// <summary>
/// Creates a new instance of <see cref="EntParentChangedMessage"/>.
/// </summary>
public EntParentChangedMessage(EntityUid entity, EntityUid? oldParent, EntityUid? oldMapId, TransformComponent xform)
public EntParentChangedMessage(EntityUid entity, EntityUid? oldParent, MapId oldMapId, TransformComponent xform)
{
Entity = entity;
OldParent = oldParent;
Transform = xform;
OldMapId = oldMapId;
Transform = xform;
}
}
}

View File

@@ -160,9 +160,6 @@ namespace Robust.Shared.GameObjects
[Pure]
string GetComponentName(Type componentType);
[Pure]
string GetComponentName<T>() where T : IComponent, new();
/// <summary>
/// Gets the name of a component, throwing an exception if it does not exist.
/// </summary>

View File

@@ -71,43 +71,12 @@ namespace Robust.Shared.GameObjects
/// </summary>
public event Action? AfterEntityFlush;
/// <summary>
/// Creates an uninitialized entity.
/// </summary>
/// <param name="prototypeName"><inheritdoc cref="CreateEntityUninitialized(string?, MapCoordinates , ComponentRegistry?, Angle)"/></param>
/// <param name="euid">Does nothing. Used to be the forced EntityUid of the new entity.</param>
/// <param name="overrides"><inheritdoc cref="CreateEntityUninitialized(string?, MapCoordinates , ComponentRegistry?, Angle)"/></param>
/// <returns><inheritdoc cref="CreateEntityUninitialized(string?, MapCoordinates , ComponentRegistry?, Angle)"/></returns>
[Obsolete($"Use one of the other {nameof(CreateEntityUninitialized)} overloads. euid no longer does anything.")]
EntityUid CreateEntityUninitialized(string? prototypeName, EntityUid euid, ComponentRegistry? overrides = null);
/// <summary>
/// Creates an uninitialized entity.
/// </summary>
/// <param name="prototypeName"><inheritdoc cref="CreateEntityUninitialized(string?, MapCoordinates , ComponentRegistry?, Angle)"/></param>
/// <param name="overrides"><inheritdoc cref="CreateEntityUninitialized(string?, MapCoordinates , ComponentRegistry?, Angle)"/></param>
/// <returns><inheritdoc cref="CreateEntityUninitialized(string?, MapCoordinates , ComponentRegistry?, Angle)"/></returns>
EntityUid CreateEntityUninitialized(string? prototypeName, ComponentRegistry? overrides = null);
/// <summary>
/// Creates an uninitialized entity and sets its position to the EntityCoordinates provided.
/// </summary>
/// <param name="prototypeName"><inheritdoc cref="CreateEntityUninitialized(string?, MapCoordinates , ComponentRegistry?, Angle)"/></param>
/// <param name="coordinates">Coordinates to set position and parent of the newly spawned entity to.</param>
/// <param name="overrides"><inheritdoc cref="CreateEntityUninitialized(string?, MapCoordinates , ComponentRegistry?, Angle)"/></param>
/// <returns><inheritdoc cref="CreateEntityUninitialized(string?, MapCoordinates , ComponentRegistry?, Angle)"/></returns>
EntityUid CreateEntityUninitialized(string? prototypeName, EntityCoordinates coordinates, ComponentRegistry? overrides = null);
/// <summary>
/// Creates an uninitialized entity and puts it on the grid or map at the MapCoordinates provided.
/// </summary>
/// <param name="prototypeName">Name of the <see cref="EntityPrototype"/> to spawn.</param>
/// <param name="coordinates">Coordinates to place the newly spawned entity.</param>
/// <param name="overrides">Overrides to add or remove components that differ from the prototype.</param>
/// <param name="rotation">Map rotation to set the newly spawned entity to.</param>
/// <returns>A new uninitialized entity.</returns>
/// <remarks>If there is a grid at the <paramref name="coordinates"/>, the entity will be parented to the grid.
/// Otherwise, it will be parented to the map.</remarks>
EntityUid CreateEntityUninitialized(string? prototypeName, MapCoordinates coordinates, ComponentRegistry? overrides = null, Angle rotation = default!);
void InitializeAndStartEntity(EntityUid entity, MapId? mapId = null);

View File

@@ -125,7 +125,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
SubscribeLocalEvent<GridAddEvent>(OnGridAdd);
SubscribeLocalEvent<MapChangedEvent>(OnMapChange);
_transform.OnBeforeMoveEvent += OnMove;
_transform.OnGlobalMoveEvent += OnMove;
EntityManager.EntityInitialized += OnEntityInit;
SubscribeLocalEvent<TransformComponent, PhysicsBodyTypeChangedEvent>(OnBodyTypeChange);
@@ -142,7 +142,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
{
base.Shutdown();
EntityManager.EntityInitialized -= OnEntityInit;
_transform.OnBeforeMoveEvent -= OnMove;
_transform.OnGlobalMoveEvent -= OnMove;
}
#region DynamicTree

View File

@@ -26,6 +26,7 @@ internal sealed class SharedGridTraversalSystem : EntitySystem
{
base.Initialize();
SubscribeLocalEvent<TransformStartupEvent>(OnStartup);
_transform.OnGlobalMoveEvent += OnMove;
}
private void OnStartup(ref TransformStartupEvent ev)
@@ -33,6 +34,17 @@ internal sealed class SharedGridTraversalSystem : EntitySystem
CheckTraverse(ev.Entity.Owner, ev.Entity.Comp);
}
public override void Shutdown()
{
_transform.OnGlobalMoveEvent -= OnMove;
}
private void OnMove(ref MoveEvent moveEv)
{
CheckTraverse(moveEv.Sender, moveEv.Component);
}
internal void CheckTraverse(EntityUid uid, TransformComponent xform)
{
if (!Enabled || _timing.ApplyingState)

View File

@@ -145,11 +145,6 @@ namespace Robust.Shared.GameObjects
ChunkIndex = chunkIndex;
}
/// <summary>
/// Was the tile previously empty or is it now empty.
/// </summary>
public bool EmptyChanged => OldTile.IsEmpty != NewTile.Tile.IsEmpty;
/// <summary>
/// EntityUid of the grid with the tile-change. TileRef stores the GridId.
/// </summary>

View File

@@ -5,7 +5,6 @@ using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Utility;
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using Robust.Shared.Map.Components;
@@ -44,13 +43,21 @@ public abstract partial class SharedTransformSystem
xform._anchored = true;
var oldPos = xform._localPosition;
var oldRot = xform._localRotation;
var oldMap = xform.MapUid;
xform._localPosition = tilePos + newGrid.TileSizeHalfVector;
xform._localRotation += rotation;
SetGridId(uid, xform, newGridUid, XformQuery);
var reParent = new EntParentChangedMessage(uid, oldGridUid, xform.MapID, xform);
RaiseLocalEvent(uid, ref reParent, true);
var meta = MetaData(uid);
RaiseMoveEvent((uid, xform, meta), oldGridUid, oldPos, oldRot, oldMap);
var movEevee = new MoveEvent((uid, xform, meta),
new EntityCoordinates(oldGridUid, oldPos),
new EntityCoordinates(newGridUid, xform._localPosition),
oldRot,
xform.LocalRotation,
_gameTiming.ApplyingState);
RaiseLocalEvent(uid, ref movEevee);
InvokeGlobalMoveEvent(ref movEevee);
DebugTools.Assert(XformQuery.GetComponent(oldGridUid).MapID == XformQuery.GetComponent(newGridUid).MapID);
DebugTools.Assert(xform._anchored);
@@ -314,7 +321,7 @@ public abstract partial class SharedTransformSystem
// I hate this too. Once again, required for shit like containers because they CBF doing their own init logic
// and rely on parent changed messages instead. Might also be used by broadphase stuff?
var parentEv = new EntParentChangedMessage(uid, null, null, xform);
var parentEv = new EntParentChangedMessage(uid, null, MapId.Nullspace, xform);
RaiseLocalEvent(uid, ref parentEv, true);
var ev = new TransformStartupEvent((uid, xform));
@@ -442,6 +449,9 @@ public abstract partial class SharedTransformSystem
return;
}
var oldPosition = xform._parent.IsValid() ? new EntityCoordinates(xform._parent, xform._localPosition) : default;
var oldRotation = xform._localRotation;
if (xform.Anchored && unanchor)
Unanchor(uid, xform);
@@ -460,11 +470,6 @@ public abstract partial class SharedTransformSystem
}
}
var oldParentUid = xform._parent;
var oldPosition = xform._localPosition;
var oldRotation = xform._localRotation;
var oldMap = xform.MapUid;
// Set new values
Dirty(uid, xform, meta);
xform.MatricesDirty = true;
@@ -480,7 +485,7 @@ public abstract partial class SharedTransformSystem
{
if (value.EntityId == uid)
{
DetachEntity(uid, xform);
DetachParentToNull(uid, xform);
if (_netMan.IsServer || IsClientSide(uid))
QueueDel(uid);
throw new InvalidOperationException($"Attempted to parent an entity to itself: {ToPrettyString(uid)}");
@@ -490,7 +495,7 @@ public abstract partial class SharedTransformSystem
{
if (!XformQuery.Resolve(value.EntityId, ref newParent, false))
{
DetachEntity(uid, xform);
DetachParentToNull(uid, xform);
if (_netMan.IsServer || IsClientSide(uid))
QueueDel(uid);
throw new InvalidOperationException($"Attempted to parent entity {ToPrettyString(uid)} to non-existent entity {value.EntityId}");
@@ -498,7 +503,7 @@ public abstract partial class SharedTransformSystem
if (newParent.LifeStage >= ComponentLifeStage.Stopping || LifeStage(value.EntityId) >= EntityLifeStage.Terminating)
{
DetachEntity(uid, xform);
DetachParentToNull(uid, xform);
if (_netMan.IsServer || IsClientSide(uid))
QueueDel(uid);
throw new InvalidOperationException($"Attempted to re-parent to a terminating object. Entity: {ToPrettyString(uid)}, new parent: {ToPrettyString(value.EntityId)}");
@@ -523,7 +528,7 @@ public abstract partial class SharedTransformSystem
// Even though its temporary, this can still cause the client to get stuck in infinite loops while applying the game state.
// So we will just break the loop by detaching to null and just trusting that the loop wasn't actually a real feature of the server state.
Log.Warning($"Encountered circular transform hierarchy while applying state for entity: {ToPrettyString(uid)}. Detaching child to null: {ToPrettyString(recursiveUid)}");
DetachEntity(recursiveUid, recursiveXform);
DetachParentToNull(recursiveUid, recursiveXform);
break;
}
@@ -540,6 +545,7 @@ public abstract partial class SharedTransformSystem
newParent?._children.Add(uid);
xform._parent = value.EntityId;
var oldMapId = xform.MapID;
if (newParent != null)
{
@@ -570,18 +576,24 @@ public abstract partial class SharedTransformSystem
xform._localRotation += GetWorldRotation(oldParent) - GetWorldRotation(newParent);
DebugTools.Assert(!xform.NoLocalRotation || xform.LocalRotation == 0);
var entParentChangedMessage = new EntParentChangedMessage(uid, oldParent?.Owner, oldMapId, xform);
RaiseLocalEvent(uid, ref entParentChangedMessage, true);
}
}
if (!xform.Initialized)
return;
var newPosition = xform._parent.IsValid() ? new EntityCoordinates(xform._parent, xform._localPosition) : default;
#if DEBUG
// If an entity is parented to the map, its grid uid should be null (unless it is itself a grid or we have a map-grid)
if (xform.ParentUid == xform.MapUid)
DebugTools.Assert(xform.GridUid == null || xform.GridUid == uid || xform.GridUid == xform.MapUid);
#endif
RaiseMoveEvent(entity, oldParentUid, oldPosition, oldRotation, oldMap);
var moveEvent = new MoveEvent((uid, xform, meta), oldPosition, newPosition, oldRotation, xform._localRotation, _gameTiming.ApplyingState);
RaiseLocalEvent(uid, ref moveEvent);
InvokeGlobalMoveEvent(ref moveEvent);
}
public void SetCoordinates(
@@ -656,13 +668,13 @@ public abstract partial class SharedTransformSystem
public void SetParent(EntityUid uid, TransformComponent xform, EntityUid parent, EntityQuery<TransformComponent> xformQuery, TransformComponent? parentXform = null)
{
DebugTools.AssertOwner(uid, xform);
DebugTools.Assert(uid == xform.Owner);
if (xform.ParentUid == parent)
return;
if (!parent.IsValid())
{
DetachEntity(uid, xform);
DetachParentToNull(uid, xform);
return;
}
@@ -964,7 +976,7 @@ public abstract partial class SharedTransformSystem
// Entity was not actually in the transform hierarchy. This is probably a sign that something is wrong, or that the function is being misused.
Log.Warning($"Target entity ({ToPrettyString(relative)}) not in transform hierarchy while calling {nameof(GetRelativePositionRotation)}.");
var relXform = query.GetComponent(relative);
pos = GetInvWorldMatrix(relXform).Transform(pos);
pos = relXform.InvWorldMatrix.Transform(pos);
break;
}
@@ -978,6 +990,7 @@ public abstract partial class SharedTransformSystem
SetWorldPosition(xform, worldPos);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetWorldPosition(TransformComponent component, Vector2 worldPos)
{
@@ -1035,16 +1048,6 @@ public abstract partial class SharedTransformSystem
return rotation;
}
public void SetWorldRotationNoLerp(Entity<TransformComponent?> entity, Angle angle)
{
if (!XformQuery.Resolve(entity.Owner, ref entity.Comp))
return;
var current = GetWorldRotation(entity.Comp);
var diff = angle - current;
SetLocalRotationNoLerp(entity, entity.Comp.LocalRotation + diff);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetWorldRotation(EntityUid uid, Angle angle)
{
@@ -1140,8 +1143,7 @@ public abstract partial class SharedTransformSystem
if (xform._localPosition.EqualsApprox(pos) && xform.LocalRotation.EqualsApprox(rot))
return;
var oldParent = xform._parent;
var oldPosition = xform._localPosition;
var oldPosition = xform.Coordinates;
var oldRotation = xform.LocalRotation;
if (!xform.Anchored)
@@ -1159,7 +1161,9 @@ public abstract partial class SharedTransformSystem
if (!xform.Initialized)
return;
RaiseMoveEvent((uid, xform, meta), oldParent, oldPosition, oldRotation, xform.MapUid);
var moveEvent = new MoveEvent((uid, xform, meta), oldPosition, xform.Coordinates, oldRotation, rot, _gameTiming.ApplyingState);
RaiseLocalEvent(uid, ref moveEvent);
InvokeGlobalMoveEvent(ref moveEvent);
}
#endregion
@@ -1322,7 +1326,7 @@ public abstract partial class SharedTransformSystem
if (!_mapManager.IsMap(uid))
Log.Warning($"Failed to attach entity to map or grid. Entity: ({ToPrettyString(uid)}). Trace: {Environment.StackTrace}");
DetachEntity(uid, xform);
DetachParentToNull(uid, xform);
return;
}
@@ -1358,50 +1362,21 @@ public abstract partial class SharedTransformSystem
#region State Handling
[Obsolete("Use DetachEntity")]
public void DetachParentToNull(EntityUid uid, TransformComponent xform)
=> DetachEntity(uid, xform);
/// <inheritdoc cref="DetachEntityInternal"/>
public void DetachEntity(EntityUid uid, TransformComponent xform)
{
XformQuery.TryGetComponent(xform.ParentUid, out var oldXform);
DetachEntity(uid, xform, MetaData(uid), oldXform);
DetachParentToNull(uid, xform, oldXform);
}
/// <inheritdoc cref="DetachEntityInternal"/>
public void DetachEntity(
EntityUid uid,
TransformComponent xform,
MetaDataComponent meta,
TransformComponent? oldXform,
bool terminating = false)
public void DetachParentToNull(EntityUid uid, TransformComponent xform, TransformComponent? oldXform)
{
#if !EXCEPTION_TOLERANCE
DetachEntityInternal(uid, xform, meta, oldXform, terminating);
#else
try
{
DetachEntityInternal(uid, xform, meta, oldXform, terminating);
}
catch (Exception e)
{
Log.Error($"Caught exception while attempting to detach an entity to nullspace. Entity: {ToPrettyString(uid, meta)}. Exception: {e}");
// TODO detach without content event handling.
}
#endif
DetachParentToNull((uid, xform, MetaData(uid)), oldXform);
}
/// <summary>
/// Remove an entity from the transform hierarchy and send it to null space
/// </summary>
internal void DetachEntityInternal(
EntityUid uid,
TransformComponent xform,
MetaDataComponent meta,
TransformComponent? oldXform,
bool terminating = false)
public void DetachParentToNull(Entity<TransformComponent,MetaDataComponent> entity, TransformComponent? oldXform, bool terminating = false)
{
var (uid, xform, meta) = entity;
if (!terminating && meta.EntityLifeStage >= EntityLifeStage.Terminating)
{
// Something is attempting to remove the entity from this entity's parent while it is in the process of being deleted.
@@ -1418,14 +1393,15 @@ public abstract partial class SharedTransformSystem
DebugTools.Assert((MetaData(uid).Flags & MetaDataFlags.InContainer) == 0x0,
$"Entity is in a container but has no parent? Entity: {ToPrettyString(uid)}");
DebugTools.Assert(
xform.Broadphase == null
|| xform.Broadphase == BroadphaseData.Invalid
|| xform.Broadphase.Value.Uid == uid
|| Deleted(xform.Broadphase.Value.Uid)
|| Terminating(xform.Broadphase.Value.Uid),
$"Entity has no parent but is on some broadphase? Entity: {ToPrettyString(uid)}. Broadphase: {ToPrettyString(xform.Broadphase!.Value.Uid)}");
if (xform.Broadphase != null)
{
DebugTools.Assert(
xform.Broadphase == BroadphaseData.Invalid
|| xform.Broadphase.Value.Uid == uid
|| Deleted(xform.Broadphase.Value.Uid)
|| Terminating(xform.Broadphase.Value.Uid),
$"Entity has no parent but is on some broadphase? Entity: {ToPrettyString(uid)}. Broadphase: {ToPrettyString(xform.Broadphase.Value.Uid)}");
}
return;
}
@@ -1449,7 +1425,7 @@ public abstract partial class SharedTransformSystem
RaiseLocalEvent(uid, ref anchorStateChangedEvent, true);
}
SetCoordinates((uid, xform, meta), default, Angle.Zero, oldParent: oldXform);
SetCoordinates(entity, default, Angle.Zero, oldParent: oldXform);
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) == 0x0,
$"Entity is in a container after having been detached to null-space? Entity: {ToPrettyString(uid)}");
@@ -1484,7 +1460,7 @@ public abstract partial class SharedTransformSystem
var targetXform = target.Comp;
if (!XformQuery.Resolve(target, ref targetXform) || !targetXform.ParentUid.IsValid())
{
DetachEntity(entity, xform);
DetachParentToNull(entity, xform);
return;
}
@@ -1522,7 +1498,7 @@ public abstract partial class SharedTransformSystem
var targetXform = target.Comp;
if (!XformQuery.Resolve(target, ref targetXform) || !targetXform.ParentUid.IsValid())
{
DetachEntity(entity, xform);
DetachParentToNull(entity, xform);
return;
}
@@ -1542,90 +1518,4 @@ public abstract partial class SharedTransformSystem
PlaceNextTo((entity, xform), targetXform.ParentUid);
}
}
/// <summary>
/// Swaps the position of two entities, placing them inside of containers when applicable.
/// </summary>
/// <returns>Returns if the entities can have their positions swapped. Fails if the entities are parented to one another</returns>
/// <exception cref="InvalidOperationException"></exception>
public bool SwapPositions(Entity<TransformComponent?> entity1, Entity<TransformComponent?> entity2)
{
if (!XformQuery.Resolve(entity1, ref entity1.Comp) || !XformQuery.Resolve(entity2, ref entity2.Comp))
return false;
// save ourselves the hassle and just don't move anything.
if (entity1 == entity2)
return true;
// don't parent things to each other by accident
if (IsParentOf(entity1.Comp, entity2) || IsParentOf(entity2.Comp, entity1))
return false;
MapCoordinates? pos1 = null;
MapCoordinates? pos2 = null;
if (_container.TryGetContainingContainer(entity1, out var container1))
_container.Remove(entity1, container1, force: true);
else
pos1 = GetMapCoordinates(entity1.Comp);
if (_container.TryGetContainingContainer(entity2, out var container2))
_container.Remove(entity2, container2, force: true);
else
pos2 = GetMapCoordinates(entity2.Comp);
// making sure we don't accidentally place something inside of itself
if (container1 != null && container1.Owner == entity2.Owner)
return false;
if (container2 != null && container2.Owner == entity1.Owner)
return false;
if (container2 != null)
{
_container.Insert(entity1, container2);
}
else if (pos2 != null)
{
var mapUid = _mapManager.GetMapEntityId(pos2.Value.MapId);
if (!_gridQuery.HasComponent(entity1) && _mapManager.TryFindGridAt(mapUid, pos2.Value.Position, out var targetGrid, out _))
{
var invWorldMatrix = GetInvWorldMatrix(targetGrid);
SetCoordinates(entity1, new EntityCoordinates(targetGrid, invWorldMatrix.Transform(pos2.Value.Position)));
}
else
{
SetCoordinates(entity1, new EntityCoordinates(mapUid, pos2.Value.Position));
}
}
else
{
throw new InvalidOperationException();
}
if (container1 != null)
{
_container.Insert(entity2, container1);
}
else if (pos1 != null)
{
var mapUid = _mapManager.GetMapEntityId(pos1.Value.MapId);
if (!_gridQuery.HasComponent(entity1) && _mapManager.TryFindGridAt(mapUid, pos1.Value.Position, out var targetGrid, out _))
{
var invWorldMatrix = GetInvWorldMatrix(targetGrid);
SetCoordinates(entity2, new EntityCoordinates(targetGrid, invWorldMatrix.Transform(pos1.Value.Position)));
}
else
{
SetCoordinates(entity2, new EntityCoordinates(mapUid, pos1.Value.Position));
}
}
else
{
throw new InvalidOperationException();
}
return true;
}
}

View File

@@ -25,7 +25,6 @@ namespace Robust.Shared.GameObjects
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly INetManager _netMan = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedGridTraversalSystem _traversal = default!;
private EntityQuery<MapComponent> _mapQuery;
private EntityQuery<MapGridComponent> _gridQuery;
@@ -41,12 +40,10 @@ namespace Robust.Shared.GameObjects
/// </summary>
public event MoveEventHandler? OnGlobalMoveEvent;
/// <summary>
/// Internal move event handlers. This gets invoked before the global & directed move events. This is mainly
/// for exception tolerance, we want to ensure that PVS, physics & entity lookups get updated before some
/// content code throws an exception.
/// </summary>
internal event MoveEventHandler? OnBeforeMoveEvent;
public void InvokeGlobalMoveEvent(ref MoveEvent ev)
{
OnGlobalMoveEvent?.Invoke(ref ev);
}
public override void Initialize()
{
@@ -107,7 +104,7 @@ namespace Robust.Shared.GameObjects
// If a tile is being removed due to an explosion or somesuch, some entities are likely being deleted.
// Avoid unnecessary entity updates.
if (EntityManager.IsQueuedForDeletion(entity))
DetachEntity(entity, xform, MetaData(entity), gridXform);
DetachParentToNull(entity, xform, gridXform);
else
SetParent(entity, xform, gridXform.MapUid.Value, mapTransform);
}
@@ -258,44 +255,6 @@ namespace Robust.Shared.GameObjects
indices = _map.CoordinatesToTile(xform.GridUid.Value, grid, xform.Coordinates);
return true;
}
public void RaiseMoveEvent(
Entity<TransformComponent, MetaDataComponent> ent,
EntityUid oldParent,
Vector2 oldPosition,
Angle oldRotation,
EntityUid? oldMap)
{
var pos = ent.Comp1._parent == EntityUid.Invalid
? default
: new EntityCoordinates(ent.Comp1._parent, ent.Comp1._localPosition);
var oldPos = oldParent == EntityUid.Invalid
? default
: new EntityCoordinates(oldParent, oldPosition);
var ev = new MoveEvent(ent, oldPos, pos, oldRotation, ent.Comp1._localRotation);
if (oldParent != ent.Comp1._parent)
{
_physics.OnParentChange(ent, oldParent, oldMap);
OnBeforeMoveEvent?.Invoke(ref ev);
var entParentChangedMessage = new EntParentChangedMessage(ev.Sender, oldParent, oldMap, ev.Component);
RaiseLocalEvent(ev.Sender, ref entParentChangedMessage, true);
}
else
{
OnBeforeMoveEvent?.Invoke(ref ev);
}
RaiseLocalEvent(ev.Sender, ref ev);
OnGlobalMoveEvent?.Invoke(ref ev);
// Finally, handle grid traversal. This is handled separately to avoid out-of-order move events.
// I.e., if the traversal raises its own move event, this ensures that all the old move event handlers
// have finished running first. Ideally this shouldn't be required, but this is here just in case
_traversal.CheckTraverse(ent.Owner, ent.Comp1);
}
}
[ByRefEvent]

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.InteropServices;
using JetBrains.Annotations;
using Robust.Shared.Collections;
@@ -10,7 +11,6 @@ using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Reflection;
using Robust.Shared.Threading;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -21,7 +21,6 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
[Dependency] private readonly IDynamicTypeFactory _factory = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly INetManager _netManager = default!;
[Dependency] private readonly IParallelManager _parallel = default!;
[Dependency] private readonly IReflectionManager _reflection = default!;
[Dependency] private readonly ISharedPlayerManager _player = default!;
[Dependency] private readonly SharedTransformSystem _transforms = default!;
@@ -31,8 +30,6 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
private EntityQuery<UserInterfaceComponent> _uiQuery;
private EntityQuery<UserInterfaceUserComponent> _userQuery;
private ActorRangeCheckJob _rangeJob;
public override void Initialize()
{
base.Initialize();
@@ -42,12 +39,6 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
_uiQuery = GetEntityQuery<UserInterfaceComponent>();
_userQuery = GetEntityQuery<UserInterfaceUserComponent>();
_rangeJob = new()
{
System = this,
XformQuery = _xformQuery,
};
SubscribeAllEvent<BoundUIWrapMessage>((msg, args) =>
{
if (args.SenderSession.AttachedEntity is not { } player)
@@ -58,7 +49,6 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
SubscribeLocalEvent<UserInterfaceComponent, OpenBoundInterfaceMessage>(OnUserInterfaceOpen);
SubscribeLocalEvent<UserInterfaceComponent, CloseBoundInterfaceMessage>(OnUserInterfaceClosed);
SubscribeLocalEvent<UserInterfaceComponent, ComponentStartup>(OnUserInterfaceStartup);
SubscribeLocalEvent<UserInterfaceComponent, ComponentShutdown>(OnUserInterfaceShutdown);
SubscribeLocalEvent<UserInterfaceComponent, ComponentGetState>(OnUserInterfaceGetState);
SubscribeLocalEvent<UserInterfaceComponent, ComponentHandleState>(OnUserInterfaceHandleState);
@@ -66,10 +56,28 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
SubscribeLocalEvent<PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<PlayerDetachedEvent>(OnPlayerDetached);
SubscribeLocalEvent<UserInterfaceUserComponent, ComponentShutdown>(OnActorShutdown);
SubscribeLocalEvent<UserInterfaceUserComponent, ComponentGetStateAttemptEvent>(OnGetStateAttempt);
SubscribeLocalEvent<UserInterfaceUserComponent, ComponentGetState>(OnActorGetState);
SubscribeLocalEvent<UserInterfaceUserComponent, ComponentHandleState>(OnActorHandleState);
_player.PlayerStatusChanged += OnStatusChange;
}
private void OnStatusChange(object? sender, SessionStatusEventArgs e)
{
var attachedEnt = e.Session.AttachedEntity;
if (attachedEnt == null)
return;
// Content can't handle it yet sadly :(
CloseUserUis(attachedEnt.Value);
}
public override void Shutdown()
{
base.Shutdown();
_player.PlayerStatusChanged -= OnStatusChange;
}
/// <summary>
@@ -128,11 +136,6 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
#region User
private void OnActorShutdown(Entity<UserInterfaceUserComponent> ent, ref ComponentShutdown args)
{
CloseUserUis((ent.Owner, ent.Comp));
}
private void OnGetStateAttempt(Entity<UserInterfaceUserComponent> ent, ref ComponentGetStateAttemptEvent args)
{
if (args.Cancelled || args.Player?.AttachedEntity != ent.Owner)
@@ -205,10 +208,11 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
foreach (var key in keys)
{
if (!uiComp.ClientOpenInterfaces.Remove(key, out var cBui))
if (!uiComp.ClientOpenInterfaces.TryGetValue(key, out var cBui))
continue;
cBui.Dispose();
uiComp.ClientOpenInterfaces.Remove(key);
}
}
}
@@ -230,8 +234,10 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
Dirty(ent);
// If the actor is also deleting then don't worry about updating what they have open.
if (!TerminatingOrDeleted(actor) && _userQuery.TryComp(actor, out var actorComp))
if (!TerminatingOrDeleted(actor))
{
var actorComp = EnsureComp<UserInterfaceUserComponent>(actor);
if (actorComp.OpenInterfaces.TryGetValue(ent.Owner, out var keys))
{
keys.Remove(args.UiKey);
@@ -277,20 +283,6 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
EnsureClientBui(ent, args.UiKey, ent.Comp.Interfaces[args.UiKey]);
}
private void OnUserInterfaceStartup(Entity<UserInterfaceComponent> ent, ref ComponentStartup args)
{
// PlayerAttachedEvent will catch some of these.
foreach (var (key, bui) in ent.Comp.ClientOpenInterfaces)
{
bui.Open();
if (ent.Comp.States.TryGetValue(key, out var state))
{
bui.UpdateState(state);
}
}
}
private void OnUserInterfaceShutdown(EntityUid uid, UserInterfaceComponent component, ComponentShutdown args)
{
foreach (var bui in component.ClientOpenInterfaces.Values)
@@ -396,21 +388,17 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
if (!ent.Comp.ClientOpenInterfaces.TryGetValue(key, out var cBui))
continue;
cBui.State = buiState;
cBui.UpdateState(buiState);
}
// If UI not open then open it
var attachedEnt = _player.LocalEntity;
// If we get the first state for an ent coming in then don't open BUIs yet, just defer it until later.
var open = ent.Comp.LifeStage > ComponentLifeStage.Added;
if (attachedEnt != null)
{
foreach (var (key, value) in ent.Comp.Interfaces)
{
EnsureClientBui(ent, key, value, open);
EnsureClientBui(ent, key, value);
}
}
}
@@ -418,7 +406,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
/// <summary>
/// Opens a client's BUI if not already open and applies the state to it.
/// </summary>
private void EnsureClientBui(Entity<UserInterfaceComponent> entity, Enum key, InterfaceData data, bool open = true)
private void EnsureClientBui(Entity<UserInterfaceComponent> entity, Enum key, InterfaceData data)
{
// If it's out BUI open it up and apply the state, otherwise do nothing.
var player = _player.LocalEntity;
@@ -441,16 +429,10 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
var boundUserInterface = (BoundUserInterface) _factory.CreateInstance(type, [entity.Owner, key]);
entity.Comp.ClientOpenInterfaces[key] = boundUserInterface;
// This is just so we don't open while applying UI states.
if (!open)
return;
boundUserInterface.Open();
if (entity.Comp.States.TryGetValue(key, out var buiState))
{
boundUserInterface.State = buiState;
boundUserInterface.UpdateState(buiState);
}
}
@@ -896,8 +878,6 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
public override void Update(float frameTime)
{
var query = AllEntityQuery<ActiveUserInterfaceComponent, UserInterfaceComponent>();
// Run these in parallel because it's expensive.
_rangeJob.ActorRanges.Clear();
// Handles closing the BUI if actors move out of range of them.
while (query.MoveNext(out var uid, out _, out var uiComp))
@@ -911,26 +891,24 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
if (data.InteractionRange <= 0f || actors.Count == 0)
continue;
foreach (var actor in actors)
// Okay so somehow UISystem is high up on the server profile
// If that's actually still a problem turn this into an IParallelRobustJob and slam all the UIs in parallel.
var xform = _xformQuery.GetComponent(uid);
var coordinates = xform.Coordinates;
var mapId = xform.MapID;
for (var i = actors.Count - 1; i >= 0; i--)
{
_rangeJob.ActorRanges.Add((uid, key, data, actor, false));
var actor = actors[i];
if (CheckRange(uid, key, data, actor, coordinates, mapId))
continue;
// Using the non-predicted one here seems fine?
CloseUi((uid, uiComp), key, actor);
}
}
}
_parallel.ProcessNow(_rangeJob, _rangeJob.ActorRanges.Count);
foreach (var data in _rangeJob.ActorRanges)
{
var uid = data.Ui;
var actor = data.Actor;
var key = data.Key;
if (data.Result || Deleted(uid) || Deleted(actor) || !_uiQuery.TryComp(uid, out var uiComp))
continue;
CloseUi((uid, uiComp), key, actor);
}
}
/// <summary>
@@ -967,32 +945,6 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
return uiCoordinates.InRange(EntityManager, _transforms, actorXform.Coordinates, data.InteractionRange);
}
/// <summary>
/// Used for running UI raycast checks in parallel.
/// </summary>
private record struct ActorRangeCheckJob() : IParallelRobustJob
{
public EntityQuery<TransformComponent> XformQuery;
public SharedUserInterfaceSystem System;
public readonly List<(EntityUid Ui, Enum Key, InterfaceData Data, EntityUid Actor, bool Result)> ActorRanges = new();
public void Execute(int index)
{
var data = ActorRanges[index];
if (!XformQuery.TryComp(data.Ui, out var uiXform))
{
data.Result = false;
}
else
{
data.Result = System.CheckRange(data.Ui, data.Key, data.Data, data.Actor, uiXform.Coordinates, uiXform.MapID);
}
ActorRanges[index] = data;
}
}
}
/// <summary>

View File

@@ -33,11 +33,6 @@ namespace Robust.Shared.Input
public bool Handled { get; private set; }
/// <summary>
/// Is this a repeated keypress (i.e., are they holding down the key)?
/// </summary>
public readonly bool IsRepeat;
/// <summary>
/// Constructs a new instance of <see cref="BoundKeyEventArgs"/>.
/// </summary>
@@ -52,23 +47,6 @@ namespace Robust.Shared.Input
CanFocus = canFocus;
}
/// <summary>
/// Constructs a new instance of <see cref="BoundKeyEventArgs"/>.
/// </summary>
/// <param name="function">Bound key that that is changing.</param>
/// <param name="state">New state of the function.</param>
/// <param name="pointerLocation">Current Pointer location in screen coordinates.</param>
/// <param name="isRepeat"></param>
public BoundKeyEventArgs(
BoundKeyFunction function,
BoundKeyState state,
ScreenCoordinates pointerLocation,
bool canFocus,
bool isRepeat = false) : this(function, state, pointerLocation, canFocus)
{
IsRepeat = isRepeat;
}
/// <summary>
/// Mark this event as handled.
/// </summary>

View File

@@ -17,8 +17,6 @@ namespace Robust.Shared.Localization
{
internal sealed partial class LocalizationManager
{
private static readonly Regex RegexWordMatch = new Regex(@"\w+");
private void AddBuiltInFunctions(FluentBundle bundle)
{
// Grammatical gender / pronouns
@@ -110,7 +108,7 @@ namespace Robust.Shared.Localization
var a = new LocValueString("a");
var an = new LocValueString("an");
var m = RegexWordMatch.Match(input);
var m = Regex.Match(input, @"\w+");
if (m.Success)
{
word = m.Groups[0].Value;

View File

@@ -249,9 +249,6 @@ namespace Robust.Shared.Network
IsServer = isServer;
_config.OnValueChanged(CVars.NetLidgrenLogWarning, LidgrenLogWarningChanged);
_config.OnValueChanged(CVars.NetLidgrenLogError, LidgrenLogErrorChanged);
_config.OnValueChanged(CVars.NetVerbose, NetVerboseChanged);
if (isServer)
{
@@ -275,22 +272,6 @@ namespace Robust.Shared.Network
}
}
private void LidgrenLogWarningChanged(bool newValue)
{
foreach (var netPeer in _netPeers)
{
netPeer.Peer.Configuration.SetMessageTypeEnabled(NetIncomingMessageType.WarningMessage, newValue);
}
}
private void LidgrenLogErrorChanged(bool newValue)
{
foreach (var netPeer in _netPeers)
{
netPeer.Peer.Configuration.SetMessageTypeEnabled(NetIncomingMessageType.ErrorMessage, newValue);
}
}
private void OnAuthModeChanged(int mode)
{
Auth = (AuthMode)mode;
@@ -441,8 +422,6 @@ namespace Robust.Shared.Network
_config.UnsubValueChanged(CVars.NetFakeLagMin, _fakeLagMinChanged);
_config.UnsubValueChanged(CVars.NetFakeLagRand, _fakeLagRandomChanged);
_config.UnsubValueChanged(CVars.NetFakeDuplicates, FakeDuplicatesChanged);
_config.UnsubValueChanged(CVars.NetLidgrenLogWarning, LidgrenLogWarningChanged);
_config.UnsubValueChanged(CVars.NetLidgrenLogError, LidgrenLogErrorChanged);
_serializer.ClientHandshakeComplete -= OnSerializerOnClientHandshakeComplete;
@@ -597,14 +576,6 @@ namespace Robust.Shared.Network
// ping the client once per second.
netConfig.PingInterval = 1f;
netConfig.SetMessageTypeEnabled(
NetIncomingMessageType.WarningMessage,
_config.GetCVar(CVars.NetLidgrenLogWarning));
netConfig.SetMessageTypeEnabled(
NetIncomingMessageType.ErrorMessage,
_config.GetCVar(CVars.NetLidgrenLogError));
var poolSize = _config.GetCVar(CVars.NetPoolSize);
if (poolSize <= 0)

View File

@@ -48,9 +48,6 @@ public abstract partial class SharedJointSystem
private void OnRelayShutdown(EntityUid uid, JointRelayTargetComponent component, ComponentShutdown args)
{
if (_gameTiming.ApplyingState)
return;
foreach (var relay in component.Relayed)
{
if (TerminatingOrDeleted(relay) || !_jointsQuery.TryGetComponent(relay, out var joint))

View File

@@ -21,7 +21,6 @@ public abstract partial class SharedJointSystem : EntitySystem
[Dependency] private readonly IGameTiming _gameTiming = default!;
private EntityQuery<JointComponent> _jointsQuery;
private EntityQuery<PhysicsComponent> _physicsQuery;
private EntityQuery<JointRelayTargetComponent> _relayQuery;
private EntityQuery<TransformComponent> _xformQuery;
@@ -38,7 +37,6 @@ public abstract partial class SharedJointSystem : EntitySystem
_jointsQuery = GetEntityQuery<JointComponent>();
_relayQuery = GetEntityQuery<JointRelayTargetComponent>();
_xformQuery = GetEntityQuery<TransformComponent>();
_physicsQuery = GetEntityQuery<PhysicsComponent>();
UpdatesOutsidePrediction = true;
UpdatesBefore.Add(typeof(SharedPhysicsSystem));
@@ -138,7 +136,7 @@ public abstract partial class SharedJointSystem : EntitySystem
var aUid = joint.BodyAUid;
var bUid = joint.BodyBUid;
if (!_physicsQuery.Resolve(aUid, ref bodyA, false) || !_physicsQuery.Resolve(bUid, ref bodyB, false))
if (!Resolve(aUid, ref bodyA, false) || !Resolve(bUid, ref bodyB, false))
return;
DebugTools.Assert(Transform(aUid).MapID == Transform(bUid).MapID, "Attempted to initialize cross-map joint");
@@ -313,7 +311,7 @@ public abstract partial class SharedJointSystem : EntitySystem
public WeldJoint GetOrCreateWeldJoint(EntityUid bodyA, EntityUid bodyB, string? id = null)
{
if (id != null &&
_jointsQuery.TryComp(bodyA, out JointComponent? jointComponent) &&
EntityManager.TryGetComponent(bodyA, out JointComponent? jointComponent) &&
jointComponent.Joints.TryGetValue(id, out var weldJoint))
{
return (WeldJoint) weldJoint;
@@ -406,17 +404,17 @@ public abstract partial class SharedJointSystem : EntitySystem
protected void AddJoint(Joint joint, PhysicsComponent? bodyA = null, PhysicsComponent? bodyB = null)
{
if (!_physicsQuery.Resolve(joint.BodyAUid, ref bodyA) || !_physicsQuery.Resolve(joint.BodyBUid, ref bodyB))
if (!Resolve(joint.BodyAUid, ref bodyA) || !Resolve(joint.BodyBUid, ref bodyB))
return;
if (!joint.CollideConnected)
FilterContactsForJoint(joint, bodyA, bodyB);
// Maybe make this method AddOrUpdate so we can have an Add one that explicitly throws if present?
var mapidA = Transform(joint.BodyAUid).MapID;
var mapidA = EntityManager.GetComponent<TransformComponent>(joint.BodyAUid).MapID;
if (mapidA == MapId.Nullspace ||
mapidA != Transform(joint.BodyBUid).MapID)
mapidA != EntityManager.GetComponent<TransformComponent>(joint.BodyBUid).MapID)
{
Log.Error($"Tried to add joint to ineligible bodies");
return;
@@ -449,8 +447,7 @@ public abstract partial class SharedJointSystem : EntitySystem
if (!Resolve(uid, ref xform))
return;
_jointsQuery.Resolve(uid, ref component, false);
_relayQuery.Resolve(uid, ref relay, false);
Resolve(uid, ref component, ref relay, false);
if (relay != null)
{
@@ -474,7 +471,7 @@ public abstract partial class SharedJointSystem : EntitySystem
/// </summary>
public void ClearJoints(EntityUid uid, JointComponent? component = null)
{
if (!_jointsQuery.Resolve(uid, ref component, false))
if (!Resolve(uid, ref component, false))
return;
// TODO PERFORMANCE
@@ -500,9 +497,15 @@ public abstract partial class SharedJointSystem : EntitySystem
}
}
[Obsolete("Use the other ClearJoints overload")]
public void ClearJoints(JointComponent joint)
{
ClearJoints(joint.Owner, joint);
}
public void RemoveJoint(EntityUid uid, string id)
{
if (!_jointsQuery.TryComp(uid, out var jointComp))
if (!TryComp<JointComponent>(uid, out var jointComp))
return;
if (!jointComp.Joints.TryGetValue(id, out var joint))
@@ -519,12 +522,12 @@ public abstract partial class SharedJointSystem : EntitySystem
// Originally I logged these but because of prediction the client can just nuke them multiple times in a row
// because each body has its own JointComponent, bleh.
if (!_jointsQuery.TryComp(bodyAUid, out var jointComponentA))
if (!TryComp<JointComponent>(bodyAUid, out var jointComponentA))
{
return;
}
if (!_jointsQuery.TryComp(bodyBUid, out var jointComponentB))
if (!TryComp<JointComponent>(bodyBUid, out var jointComponentB))
{
return;
}
@@ -540,7 +543,7 @@ public abstract partial class SharedJointSystem : EntitySystem
}
// Wake up connected bodies.
if (_physicsQuery.TryComp(bodyAUid, out var bodyA) &&
if (EntityManager.TryGetComponent<PhysicsComponent>(bodyAUid, out var bodyA) &&
MetaData(bodyAUid).EntityLifeStage < EntityLifeStage.Terminating)
{
var uidA = jointComponentA.Relay ?? bodyAUid;
@@ -604,7 +607,7 @@ public abstract partial class SharedJointSystem : EntitySystem
internal void FilterContactsForJoint(Joint joint, PhysicsComponent? bodyA = null, PhysicsComponent? bodyB = null)
{
if (!_physicsQuery.Resolve(joint.BodyBUid, ref bodyB))
if (!Resolve(joint.BodyBUid, ref bodyB))
return;
var node = bodyB.Contacts.First;

View File

@@ -115,7 +115,7 @@ public partial class SharedPhysicsSystem
public void ApplyAngularImpulse(EntityUid uid, float impulse, FixturesComponent? manager = null, PhysicsComponent? body = null)
{
if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
if (!Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
{
return;
}
@@ -125,7 +125,7 @@ public partial class SharedPhysicsSystem
public void ApplyForce(EntityUid uid, Vector2 force, Vector2 point, FixturesComponent? manager = null, PhysicsComponent? body = null)
{
if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
if (!Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
{
return;
}
@@ -137,7 +137,7 @@ public partial class SharedPhysicsSystem
public void ApplyForce(EntityUid uid, Vector2 force, FixturesComponent? manager = null, PhysicsComponent? body = null)
{
if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
if (!Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
{
return;
}
@@ -148,7 +148,7 @@ public partial class SharedPhysicsSystem
public void ApplyTorque(EntityUid uid, float torque, FixturesComponent? manager = null, PhysicsComponent? body = null)
{
if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
if (!Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
{
return;
}
@@ -159,7 +159,7 @@ public partial class SharedPhysicsSystem
public void ApplyLinearImpulse(EntityUid uid, Vector2 impulse, FixturesComponent? manager = null, PhysicsComponent? body = null)
{
if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
if (!Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
{
return;
}
@@ -169,7 +169,7 @@ public partial class SharedPhysicsSystem
public void ApplyLinearImpulse(EntityUid uid, Vector2 impulse, Vector2 point, FixturesComponent? manager = null, PhysicsComponent? body = null)
{
if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
if (!Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
{
return;
}
@@ -250,10 +250,7 @@ public partial class SharedPhysicsSystem
public void ResetMassData(EntityUid uid, FixturesComponent? manager = null, PhysicsComponent? body = null)
{
if (!PhysicsQuery.Resolve(uid, ref body))
return;
if (!_fixturesQuery.Resolve(uid, ref manager))
if (!Resolve(uid, ref manager, ref body))
return;
body._mass = 0.0f;
@@ -318,7 +315,7 @@ public partial class SharedPhysicsSystem
public bool SetAngularVelocity(EntityUid uid, float value, bool dirty = true, FixturesComponent? manager = null, PhysicsComponent? body = null)
{
if (!PhysicsQuery.Resolve(uid, ref body))
if (!Resolve(uid, ref body))
return false;
if (body.BodyType == BodyType.Static)
@@ -349,7 +346,7 @@ public partial class SharedPhysicsSystem
/// </summary>
public bool SetLinearVelocity(EntityUid uid, Vector2 velocity, bool dirty = true, bool wakeBody = true, FixturesComponent? manager = null, PhysicsComponent? body = null)
{
if (!PhysicsQuery.Resolve(uid, ref body))
if (!Resolve(uid, ref body))
return false;
if (body.BodyType == BodyType.Static)
@@ -470,7 +467,7 @@ public partial class SharedPhysicsSystem
public void SetBodyType(EntityUid uid, BodyType value, FixturesComponent? manager = null, PhysicsComponent? body = null, TransformComponent? xform = null)
{
if (!PhysicsQuery.Resolve(uid, ref body))
if (!Resolve(uid, ref body))
return;
if (body.BodyType == value)
@@ -534,7 +531,7 @@ public partial class SharedPhysicsSystem
FixturesComponent? manager = null,
PhysicsComponent? body = null)
{
if (!PhysicsQuery.Resolve(uid, ref body))
if (!Resolve(uid, ref body))
return false;
if (body.CanCollide == value)
@@ -548,7 +545,7 @@ public partial class SharedPhysicsSystem
if (_containerSystem.IsEntityOrParentInContainer(uid))
return false;
if (!_fixturesQuery.Resolve(uid, ref manager) || manager.FixtureCount == 0 && !_mapManager.IsGrid(uid))
if (!Resolve(uid, ref manager) || manager.FixtureCount == 0 && !_mapManager.IsGrid(uid))
return false;
}
else
@@ -578,7 +575,7 @@ public partial class SharedPhysicsSystem
public void SetFixedRotation(EntityUid uid, bool value, bool dirty = true, FixturesComponent? manager = null, PhysicsComponent? body = null)
{
if (!PhysicsQuery.Resolve(uid, ref body) || body.FixedRotation == value)
if (!Resolve(uid, ref body) || body.FixedRotation == value)
return;
body.FixedRotation = value;
@@ -671,13 +668,10 @@ public partial class SharedPhysicsSystem
/// <returns>true if the body is collidable and awake</returns>
public bool WakeBody(EntityUid uid, bool force = false, FixturesComponent? manager = null, PhysicsComponent? body = null)
{
if (!PhysicsQuery.Resolve(uid, ref body))
if (!SetCanCollide(uid, true, manager: manager, body: body, force: force) || !Resolve(uid, ref body))
return false;
if (!SetCanCollide(uid, true, manager: manager, body: body, force: force))
return false;
SetAwake((uid, body), true);
SetAwake(uid, body, true);
return body.Awake;
}
@@ -721,9 +715,7 @@ public partial class SharedPhysicsSystem
public Box2 GetHardAABB(EntityUid uid, FixturesComponent? manager = null, PhysicsComponent? body = null, TransformComponent? xform = null)
{
if (!PhysicsQuery.Resolve(uid, ref body)
|| !_fixturesQuery.Resolve(uid, ref manager)
|| !Resolve(uid, ref xform))
if (!Resolve(uid, ref body, ref xform, ref manager))
{
return Box2.Empty;
}

View File

@@ -198,7 +198,7 @@ public abstract partial class SharedPhysicsSystem
return (linearVelocity + linearVelocityAngularContribution, angularVelocity);
}
private void HandleParentChangeVelocity(EntityUid uid, PhysicsComponent physics, EntityUid oldParent, TransformComponent xform)
private void HandleParentChangeVelocity(EntityUid uid, PhysicsComponent physics, ref EntParentChangedMessage args, TransformComponent xform)
{
// If parent changed due to state handling, don't modify velocities. The physics comp state will take care of itself..
if (_gameTiming.ApplyingState)
@@ -217,13 +217,15 @@ public abstract partial class SharedPhysicsSystem
// I guess the question becomes, what do you do with conservation of momentum in that case. I guess its the job
// of the teleporter to select a velocity at the after the parent has changed.
var xformQuery = GetEntityQuery<TransformComponent>();
var physicsQuery = GetEntityQuery<PhysicsComponent>();
FixturesComponent? manager = null;
// for the new velocities (that need to be updated), we can just use the existing function:
var (newLinear, newAngular) = GetMapVelocities(uid, physics, xform);
// for the old velocities, we need to re-implement this function while using the old parent and old local position:
if (oldParent == EntityUid.Invalid)
if (args.OldParent is not { Valid: true } parent)
{
// no previous parent --> simple
// Old velocity + (old velocity - new velocity)
@@ -232,8 +234,7 @@ public abstract partial class SharedPhysicsSystem
return;
}
var parent = oldParent;
TransformComponent? parentXform = _xformQuery.GetComponent(parent);
TransformComponent? parentXform = xformQuery.GetComponent(parent);
var localPos = _transform.GetInvWorldMatrix(parentXform).Transform(_transform.GetWorldPosition(xform));
var oldLinear = physics.LinearVelocity;
@@ -242,7 +243,7 @@ public abstract partial class SharedPhysicsSystem
do
{
if (PhysicsQuery.TryGetComponent(parent, out var body))
if (physicsQuery.TryGetComponent(parent, out var body))
{
oldAngular += body.AngularVelocity;
@@ -258,7 +259,7 @@ public abstract partial class SharedPhysicsSystem
localPos = parentXform.LocalPosition + parentXform.LocalRotation.RotateVec(localPos);
parent = parentXform.ParentUid;
} while (parent.IsValid() && _xformQuery.TryGetComponent(parent, out parentXform));
} while (parent.IsValid() && xformQuery.TryGetComponent(parent, out parentXform));
oldLinear += linearAngularContribution;

View File

@@ -90,6 +90,7 @@ namespace Robust.Shared.Physics.Systems
SubscribeLocalEvent<GridAddEvent>(OnGridAdd);
SubscribeLocalEvent<CollisionChangeEvent>(OnCollisionChange);
SubscribeLocalEvent<PhysicsComponent, EntGotRemovedFromContainerMessage>(HandleContainerRemoved);
SubscribeLocalEvent<EntParentChangedMessage>(OnParentChange);
SubscribeLocalEvent<PhysicsMapComponent, ComponentInit>(HandlePhysicsMapInit);
SubscribeLocalEvent<PhysicsComponent, ComponentInit>(OnPhysicsInit);
SubscribeLocalEvent<PhysicsComponent, ComponentShutdown>(OnPhysicsShutdown);
@@ -149,45 +150,51 @@ namespace Robust.Shared.Physics.Systems
_substeps = (int)Math.Ceiling(targetMinTickrate / serverTickrate);
}
internal void OnParentChange(Entity<TransformComponent, MetaDataComponent> ent, EntityUid oldParent, EntityUid? oldMap)
private void OnParentChange(ref EntParentChangedMessage args)
{
// We do not have a directed/body subscription, because the entity changing parents may not have a physics component, but one of its children might.
var (uid, xform, meta) = ent;
var uid = args.Entity;
var xform = args.Transform;
// If this entity has yet to be initialized, then we can skip this as equivalent code will get run during
// init anyways. HOWEVER: it is possible that one of the children of this entity are already post-init, in
// which case they still need to handle map changes. This frequently happens when clients receives a server
// state where a known/old entity gets attached to a new, previously unknown, entity. The new entity will be
// uninitialized but have an initialized child.
if (xform.ChildCount == 0 && meta.EntityLifeStage < EntityLifeStage.Initialized)
if (xform.ChildCount == 0 && LifeStage(uid) < EntityLifeStage.Initialized)
return;
// Is this entity getting recursively detached after it's parent was already detached to null?
if (oldMap == null && xform.MapUid == null)
if (args.OldMapId == MapId.Nullspace && xform.MapID == MapId.Nullspace)
return;
var body = PhysicsQuery.CompOrNull(uid);
var body = CompOrNull<PhysicsComponent>(uid);
// Handle map changes
if (oldMap != xform.MapUid)
if (args.OldMapId != xform.MapID)
{
// This will also handle broadphase updating & joint clearing.
HandleMapChange(uid, xform, body, oldMap, xform.MapUid);
return;
HandleMapChange(uid, xform, body, args.OldMapId, xform.MapID);
}
if (args.OldMapId != xform.MapID)
return;
if (body != null)
HandleParentChangeVelocity(uid, body, oldParent, xform);
HandleParentChangeVelocity(uid, body, ref args, xform);
}
/// <summary>
/// Recursively add/remove from awake bodies, clear joints, remove from move buffer, and update broadphase.
/// </summary>
private void HandleMapChange(EntityUid uid, TransformComponent xform, PhysicsComponent? body, EntityUid? oldMapId, EntityUid? newMapId)
private void HandleMapChange(EntityUid uid, TransformComponent xform, PhysicsComponent? body, MapId oldMapId, MapId newMapId)
{
PhysMapQuery.TryGetComponent(oldMapId, out var oldMap);
PhysMapQuery.TryGetComponent(newMapId, out var newMap);
RecursiveMapUpdate(uid, xform, body, newMap, oldMap);
var jointQuery = GetEntityQuery<JointComponent>();
PhysMapQuery.TryGetComponent(_mapManager.GetMapEntityId(oldMapId), out var oldMap);
PhysMapQuery.TryGetComponent(_mapManager.GetMapEntityId(newMapId), out var newMap);
RecursiveMapUpdate(uid, xform, body, newMap, oldMap, jointQuery);
}
/// <summary>
@@ -198,7 +205,8 @@ namespace Robust.Shared.Physics.Systems
TransformComponent xform,
PhysicsComponent? body,
PhysicsMapComponent? newMap,
PhysicsMapComponent? oldMap)
PhysicsMapComponent? oldMap,
EntityQuery<JointComponent> jointQuery)
{
DebugTools.Assert(!Deleted(uid));
@@ -215,14 +223,16 @@ namespace Robust.Shared.Physics.Systems
DebugTools.Assert(oldMap?.AwakeBodies.Contains(body) != true);
}
_joints.ClearJoints(uid);
if (jointQuery.TryGetComponent(uid, out var joint))
_joints.ClearJoints(uid, joint);
foreach (var child in xform._children)
{
if (_xformQuery.TryGetComponent(child, out var childXform))
{
PhysicsQuery.TryGetComponent(child, out var childBody);
RecursiveMapUpdate(child, childXform, childBody, newMap, oldMap);
RecursiveMapUpdate(child, childXform, childBody, newMap, oldMap, jointQuery);
}
}
}

View File

@@ -1,7 +1,4 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations;
@@ -45,59 +42,3 @@ public readonly record struct EntProtoId(string Id) : IEquatable<string>, ICompa
public override string ToString() => Id ?? string.Empty;
}
/// <inheritdoc cref="EntProtoId"/>
[Serializable]
public readonly record struct EntProtoId<T>(string Id) : IEquatable<string>, IComparable<EntProtoId> where T : IComponent, new()
{
public static implicit operator string(EntProtoId<T> protoId)
{
return protoId.Id;
}
public static implicit operator EntProtoId(EntProtoId<T> protoId)
{
return new EntProtoId(protoId.Id);
}
public static implicit operator EntProtoId<T>(string id)
{
return new EntProtoId<T>(id);
}
public static implicit operator EntProtoId<T>?(string? id)
{
return id == null ? default(EntProtoId<T>?) : new EntProtoId<T>(id);
}
public bool Equals(string? other)
{
return Id == other;
}
public int CompareTo(EntProtoId other)
{
return string.Compare(Id, other.Id, StringComparison.Ordinal);
}
public override string ToString() => Id ?? string.Empty;
public T Get(IPrototypeManager? prototypes, IComponentFactory compFactory)
{
prototypes ??= IoCManager.Resolve<IPrototypeManager>();
var proto = prototypes.Index(this);
if (!proto.TryGetComponent(out T? comp, compFactory))
{
throw new ArgumentException($"{nameof(EntityPrototype)} {proto.ID} has no {nameof(T)}");
}
return comp;
}
public bool TryGet([NotNullWhen(true)] out T? comp, IPrototypeManager? prototypes, IComponentFactory compFactory)
{
prototypes ??= IoCManager.Resolve<IPrototypeManager>();
var proto = prototypes.Index(this);
return proto.TryGetComponent(out comp, compFactory);
}
}

View File

@@ -11,7 +11,6 @@ using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.Markdown.Mapping;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Robust.Shared.Prototypes
@@ -169,37 +168,28 @@ namespace Robust.Shared.Prototypes
_loc = IoCManager.Resolve<ILocalizationManager>();
}
[Obsolete("Pass in IComponentFactory")]
public bool TryGetComponent<T>([NotNullWhen(true)] out T? component)
where T : IComponent
public bool TryGetComponent<T>([NotNullWhen(true)] out T? component, IComponentFactory? factory = null) where T : IComponent
{
var compName = IoCManager.Resolve<IComponentFactory>().GetComponentName(typeof(T));
return TryGetComponent(compName, out component);
}
if (factory == null)
{
factory = IoCManager.Resolve<IComponentFactory>();
}
public bool TryGetComponent<T>([NotNullWhen(true)] out T? component, IComponentFactory factory) where T : IComponent, new()
{
var compName = factory.GetComponentName<T>();
var compName = factory.GetComponentName(typeof(T));
return TryGetComponent(compName, out component);
}
public bool TryGetComponent<T>(string name, [NotNullWhen(true)] out T? component) where T : IComponent
{
DebugTools.AssertEqual(IoCManager.Resolve<IComponentFactory>().GetComponentName(typeof(T)), name);
if (!Components.TryGetValue(name, out var componentUnCast))
{
component = default;
return false;
}
if (componentUnCast.Component is not T cast)
{
component = default;
return false;
}
component = cast;
// There are no duplicate component names
// TODO Sanity check with names being in an attribute of the type instead
component = (T) componentUnCast.Component;
return true;
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations;
using Robust.Shared.Reflection;
using Robust.Shared.Serialization.Markdown;
@@ -441,7 +440,5 @@ namespace Robust.Shared.Serialization.Manager
}
#endregion
public bool TryGetVariableType(Type type, string variableName, [NotNullWhen(true)] out Type? variableType);
}
}

View File

@@ -264,26 +264,6 @@ namespace Robust.Shared.Serialization.Manager
return dataDefinition != null;
}
public bool TryGetVariableType(Type type, string variableName, [NotNullWhen(true)] out Type? variableType)
{
if (!TryGetDefinition(type, out var definition))
{
variableType = null;
return false;
}
var foundFieldDef = definition.BaseFieldDefinitions.FirstOrDefault(fieldDef => fieldDef?.Attribute is DataFieldAttribute attr && attr.Tag==variableName, null);
if(foundFieldDef != null)
{
variableType = foundFieldDef.BackingField.FieldType;
return true;
}
else
{
variableType = null;
return false;
}
}
private Type ResolveConcreteType(Type baseType, string typeName)
{
var type = ReflectionManager.YamlTypeTagLookup(baseType, typeName);

View File

@@ -1,11 +1,8 @@
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.Markdown;
using Robust.Shared.Serialization.Markdown.Mapping;
using Robust.Shared.Serialization.Markdown.Sequence;
using Robust.Shared.Serialization.Markdown.Validation;
using Robust.Shared.Serialization.Markdown.Value;
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
@@ -43,49 +40,3 @@ public sealed class EntProtoIdSerializer : ITypeSerializer<EntProtoId, ValueData
return source;
}
}
/// <summary>
/// Serializer used automatically for <see cref="EntProtoId"/> types.
/// </summary>
[TypeSerializer]
public sealed class EntProtoIdSerializer<T> : ITypeSerializer<EntProtoId<T>, ValueDataNode>, ITypeCopyCreator<EntProtoId<T>> where T : IComponent, new()
{
public ValidationNode Validate(ISerializationManager serialization, ValueDataNode node, IDependencyCollection dependencies, ISerializationContext? context = null)
{
var prototypes = dependencies.Resolve<IPrototypeManager>();
if (!prototypes.TryGetMapping(typeof(EntityPrototype), node.Value, out var mapping))
return new ErrorNode(node, $"No {nameof(EntityPrototype)} found with id {node.Value} that has a {typeof(T).Name}");
if (!mapping.TryGet("components", out SequenceDataNode? components))
return new ErrorNode(node, $"{nameof(EntityPrototype)} {node.Value} doesn't have a {typeof(T).Name}.");
var compFactory = dependencies.Resolve<IComponentFactory>();
var registration = compFactory.GetRegistration<T>();
foreach (var componentNode in components)
{
if (componentNode is MappingDataNode component &&
component.TryGet("type", out ValueDataNode? compName) &&
compName.Value == registration.Name)
{
return new ValidatedValueNode(node);
}
}
return new ErrorNode(node, $"{nameof(EntityPrototype)} {node.Value} doesn't have a {typeof(T).Name}.");
}
public EntProtoId<T> Read(ISerializationManager serialization, ValueDataNode node, IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context = null, InstantiationDelegate<EntProtoId<T>>? instanceProvider = null)
{
return new EntProtoId<T>(node.Value);
}
public DataNode Write(ISerializationManager serialization, EntProtoId<T> value, IDependencyCollection dependencies, bool alwaysWrite = false, ISerializationContext? context = null)
{
return new ValueDataNode(value.Id);
}
public EntProtoId<T> CreateCopy(ISerializationManager serializationManager, EntProtoId<T> source, IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context = null)
{
return source;
}
}

View File

@@ -97,7 +97,6 @@ namespace Robust.UnitTesting.Client.UserInterface
control4.OnKeyBindDown += _ => Assert.Fail("Control 4 should not get a mouse event.");
_userInterfaceManager.KeyBindDown(mouseEvent);
_userInterfaceManager.KeyBindUp(mouseEvent);
Assert.Multiple(() =>
{
@@ -125,7 +124,6 @@ namespace Robust.UnitTesting.Client.UserInterface
control2.MouseFilter = Control.MouseFilterMode.Pass;
_userInterfaceManager.KeyBindDown(mouseEvent);
_userInterfaceManager.KeyBindUp(mouseEvent);
Assert.Multiple(() =>
{
@@ -249,7 +247,6 @@ namespace Robust.UnitTesting.Client.UserInterface
pos, true, pos.Position / 1 - control.GlobalPosition, pos.Position - control.GlobalPixelPosition);
_userInterfaceManager.KeyBindDown(mouseEvent);
_userInterfaceManager.KeyBindUp(mouseEvent);
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);

View File

@@ -92,7 +92,7 @@ namespace Robust.UnitTesting.Shared.GameObjects.Systems
Assert.That(parentXform.MapID, Is.EqualTo(mapId));
Assert.That(childXform.MapID, Is.EqualTo(mapId));
xformSystem.DetachEntity(parent, parentXform);
xformSystem.DetachParentToNull(parent, parentXform);
Assert.That(parentXform.MapID, Is.EqualTo(MapId.Nullspace));
Assert.That(childXform.MapID, Is.EqualTo(MapId.Nullspace));
}

View File

@@ -354,7 +354,7 @@ public sealed class Broadphase_Test
Assert.That(lookup.FindBroadphase(child2), Is.EqualTo(mapBroadphase));
// They should get deparented to the map and updated to the map's broadphase instead.
xformSystem.DetachEntity(parent, parentXform);
xformSystem.DetachParentToNull(parent, parentXform);
Assert.That(lookup.FindBroadphase(parent), Is.EqualTo(null));
Assert.That(lookup.FindBroadphase(child1), Is.EqualTo(null));
Assert.That(lookup.FindBroadphase(child2), Is.EqualTo(null));