Compare commits

...

34 Commits

Author SHA1 Message Date
Pieter-Jan Briers
1904207358 Version: 162.2.2 2024-03-10 20:50:29 +01:00
Pieter-Jan Briers
939840ddab Backport 859f150404
(cherry picked from commit 24d5c26fa6)
(cherry picked from commit 688efac67b634c613539b783a9fb6e679948cd53)
2024-03-10 20:50:29 +01:00
DrSmugleaf
a6c295b89c Version: 162.2.1 2023-09-28 17:55:17 -07:00
DrSmugleaf
165913a4de Add IComparable to ProtoId, EntProtoId and LocId (#4460) 2023-09-28 17:53:23 -07:00
metalgearsloth
675dfdaabd Fix scroll containers invalidating on first scroll (#4449) 2023-09-28 16:58:18 -07:00
Kara
fab172d6f6 Allow force submitting line edits (#4455) 2023-09-28 11:07:55 -07:00
metalgearsloth
e75c1659f6 Version: 162.2.0 2023-09-28 20:25:52 +10:00
DrSmugleaf
0c440a8fc9 Localize "View Variables" (#4454) 2023-09-28 20:22:54 +10:00
DrSmugleaf
0c2c8f352a Add LocId and LocIdSerializer (#4456) 2023-09-28 20:22:17 +10:00
DrSmugleaf
0a4a2b7a36 Add nullable conversion operators to ProtoId and EntProtoId (#4447) 2023-09-28 20:18:45 +10:00
metalgearsloth
b5b59c1d2f Use CollectionsMarshal in clientgamestatemanager (#4453) 2023-09-28 20:13:38 +10:00
metalgearsloth
f4f0967fdc Fix double contact deletion throwing (#4450) 2023-09-28 20:12:45 +10:00
metalgearsloth
3d69766112 Use CollectionsMarshal for mergeimplicitdata (#4451) 2023-09-28 20:12:22 +10:00
DrSmugleaf
d1eb3438d5 Add support for automatically networking entity lists and sets (#4446) 2023-09-25 18:02:53 +10:00
ElectroJr
8f6b189d29 Version: 162.1.1 2023-09-19 17:59:16 -04:00
Leon Friedrich
ef8b278b47 Fix HideSpawnMenu (#4437) 2023-09-20 07:57:50 +10:00
metalgearsloth
c53ce2c907 Version: 162.1.0 2023-09-19 23:19:39 +10:00
Leon Friedrich
f063aa3ea1 Use CollectionsMarshal in RobustTree (#4429) 2023-09-19 23:17:27 +10:00
Leon Friedrich
30f63254ef Mark ProtoId<T> as serializable (#4430) 2023-09-19 23:13:14 +10:00
Leon Friedrich
30a5b6152c Slightly improve AddToChunkSetRecursively() (#4432) 2023-09-19 23:13:00 +10:00
Leon Friedrich
910a7f8bff Fix visibility layers not updating on children (#4431) 2023-09-19 23:12:52 +10:00
Leon Friedrich
526a88293e Use CollectionsMarshal in AddComponentInternal() (#4435) 2023-09-19 23:09:41 +10:00
metalgearsloth
22cd840b83 Revert "Add force ack threshold (#4423)" (#4436) 2023-09-19 18:36:41 +10:00
metalgearsloth
415c518bc7 Version: 162.0.0 2023-09-19 17:40:46 +10:00
Leon Friedrich
2417dbb0e0 Use CollectionsMarshal in DynamicTree (#4433) 2023-09-19 15:58:02 +10:00
Leon Friedrich
005673a957 Add force ack threshold (#4423) 2023-09-19 15:16:01 +10:00
Leon Friedrich
942db3120c Make entity system proxy methods use Metadata & Transform queries (#4434) 2023-09-19 15:11:21 +10:00
Leon Friedrich
c0a5fab19e Add missing EntitySystem - EntityManager proxy method (#4427) 2023-09-18 12:17:46 +10:00
Leon Friedrich
d16c62b132 Use CollectionsMarshal in PVS (#4428) 2023-09-18 12:17:32 +10:00
Leon Friedrich
7da22557fe Add entity categories (#4356) 2023-09-18 12:14:26 +10:00
metalgearsloth
92f47c0f20 Version: 161.1.0 2023-09-18 11:48:03 +10:00
Leon Friedrich
9576d0739f Add more DebugTools assert variants (#4425) 2023-09-18 11:18:35 +10:00
Leon Friedrich
10f25faabf Try fix oldest ack issues (#4426) 2023-09-18 11:17:49 +10:00
Leon Friedrich
74831a177e Don't attempt to insert entities into deleted containers (#4424) 2023-09-18 10:57:14 +10:00
49 changed files with 906 additions and 246 deletions

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,6 +54,79 @@ END TEMPLATE-->
*None yet*
## 162.2.2
## 162.2.1
## 162.2.0
### New features
* Add support for automatically networking entity lists and sets.
* Add nullable conversion operators for ProtoIds.
* Add LocId serializer for validation.
### Bugfixes
* Fix deleting a contact inside of collision events throwing.
* Localize VV.
### Internal
* Use CollectionsMarshal in GameStateManager.
## 162.1.1
### Bugfixes
* Fixes "NoSpawn" entities appearing in the spawn menu.
## 162.1.0
### New features
* Mark ProtoId as NetSerializable.
### Bugfixes
* Temporarily revert NetForceAckThreshold change as it can lead to client stalling.
* Fix eye visibility layers not updating on children when a parent changes.
### Internal
* Use CollectionsMarshal in RobustTree and AddComponentInternal.
## 162.0.0
### New features
* Add entity categories for prototypes and deprecate the `noSpawn` tag.
* Add missing proxy method for `TryGetEntityData`.
* Add NetForceAckThreshold cvar to forcibly update acks for late clients.
### Internal
* Use CollectionMarshals in PVS and DynamicTree.
* Make the proxy methods use MetaQuery / TransformQuery.
## 161.1.0
### New features
* Add more DebugTools assert variations.
### Bugfixes
* Don't attempt to insert entities into deleted containers.
* Try to fix oldestAck not being set correctly leading to deletion history getting bloated for pvs.
## 161.0.0
### Breaking changes

View File

@@ -1,7 +1,7 @@
- type: entity
id: debugRotation
abstract: true
suffix: DEBUG
categories: [ debug ]
components:
- type: Sprite
netsync: false

View File

@@ -0,0 +1,17 @@
# debug related entities
- type: entityCategory
id: debug
name: entity-category-name-debug
description: entity-category-desc-debug
# entities that spawn other entities
- type: entityCategory
id: spawner
name: entity-category-name-spawner
description: entity-category-desc-spawner
# entities that should be hidden from the spawn menu
- type: entityCategory
id: hideSpawnMenu
name: entity-category-name-hide
description: entity-category-desc-hide

View File

@@ -0,0 +1,8 @@
entity-category-name-debug = Debug
entity-category-desc-debug = Entity prototypes intended for debugging & testing.
entity-category-name-spawner = Spawner
entity-category-desc-spawner = Entity prototypes that spawn other entities.
entity-category-name-hide = Hidden
entity-category-desc-hide = Entity prototypes that should be hidden from the spawn menu

View File

@@ -1,5 +1,6 @@
## ViewVariablesInstanceEntity
view-variables = View Variables
view-variable-instance-entity-server-components-add-component-button-placeholder = Add Component
view-variable-instance-entity-client-variables-tab-title = Client Variables
view-variable-instance-entity-client-components-tab-title = Client Components
@@ -8,4 +9,4 @@ view-variable-instance-entity-server-components-tab-title = Server Components
view-variable-instance-entity-client-components-search-bar-placeholder = Search
view-variable-instance-entity-server-components-search-bar-placeholder = Search
view-variable-instance-entity-add-window-server-components = Add Component [S]
view-variable-instance-entity-add-window-client-components = Add Component [C]
view-variable-instance-entity-add-window-client-components = Add Component [C]

View File

@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.InteropServices;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Input;
@@ -759,25 +760,29 @@ namespace Robust.Client.GameStates
if (es.EntityLastModified != nextState.ToSequence)
continue;
if (_toApply.TryGetValue(uid.Value, out var state))
_toApply[uid.Value] = (es.NetEntity, meta, state.EnteringPvs, state.LastApplied, state.curState, es);
ref var state = ref CollectionsMarshal.GetValueRefOrAddDefault(_toApply, uid.Value, out var exists);
if (exists)
state = (es.NetEntity, meta, state.EnteringPvs, state.LastApplied, state.curState, es);
else
_toApply[uid.Value] = (es.NetEntity, meta, false, GameTick.Zero, null, es);
state = (es.NetEntity, meta, false, GameTick.Zero, null, es);
}
}
// Check pending states and see if we need to force any entities to re-run component states.
foreach (var uid in _pendingReapplyNetStates.Keys)
{
// State already being re-applied so don't bulldoze it.
if (_toApply.ContainsKey(uid))
continue;
// Original entity referencing the NetEntity may have been deleted.
if (!metas.TryGetComponent(uid, out var meta))
continue;
_toApply[uid] = (meta.NetEntity, meta, false, GameTick.Zero, null, null);
// State already being re-applied so don't bulldoze it.
ref var state = ref CollectionsMarshal.GetValueRefOrAddDefault(_toApply, uid, out var exists);
if (exists)
continue;
state = (meta.NetEntity, meta, false, GameTick.Zero, null, null);
}
var queuedBroadphaseUpdates = new List<(EntityUid, TransformComponent)>(enteringPvs);
@@ -1198,10 +1203,13 @@ namespace Robust.Client.GameStates
continue;
}
if (_compStateWork.TryGetValue(compState.NetID, out var state))
_compStateWork[compState.NetID] = (comp, state.curState, compState.State);
ref var state =
ref CollectionsMarshal.GetValueRefOrAddDefault(_compStateWork, compState.NetID, out var exists);
if (exists)
state = (comp, state.curState, compState.State);
else
_compStateWork[compState.NetID] = (comp, null, compState.State);
state = (comp, null, compState.State);
}
}
@@ -1218,14 +1226,19 @@ namespace Robust.Client.GameStates
if (netId == null)
continue;
if (_compStateWork.ContainsKey(netId.Value) ||
!meta.NetComponents.TryGetValue(netId.Value, out var comp) ||
if (!meta.NetComponents.TryGetValue(netId.Value, out var comp) ||
!lastState.TryGetValue(netId.Value, out var lastCompState))
{
continue;
}
_compStateWork[netId.Value] = (comp, lastCompState, null);
ref var compState =
ref CollectionsMarshal.GetValueRefOrAddDefault(_compStateWork, netId.Value, out var exists);
if (exists)
continue;
compState = (comp, lastCompState, null);
}
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Robust.Client.Timing;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
@@ -361,9 +362,11 @@ namespace Robust.Client.GameStates
foreach (var (netId, implicitCompState) in implicitEntState)
{
if (!fullRep.TryGetValue(netId, out var serverState))
ref var serverState = ref CollectionsMarshal.GetValueRefOrAddDefault(fullRep, netId, out var exists);
if (!exists)
{
fullRep.Add(netId, implicitCompState);
serverState = implicitCompState;
continue;
}
@@ -379,7 +382,7 @@ namespace Robust.Client.GameStates
}
serverDelta.ApplyToFullState(implicitCompState);
fullRep[netId] = implicitCompState;
serverState = implicitCompState;
DebugTools.Assert(implicitCompState is IComponentDeltaState d && d.FullState);
}
}

View File

@@ -17,7 +17,7 @@
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' != 'True'" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.NFluidsynth" Version="0.1.1" PrivateAssets="compile" />
<PackageReference Include="NVorbis" Version="0.10.1" PrivateAssets="compile" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.7" />
<PackageReference Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" PrivateAssets="compile" />
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.SharpFont" Version="1.0.1" PrivateAssets="compile" />

View File

@@ -10,6 +10,7 @@ using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Enums;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
using static Robust.Client.UserInterface.Controls.BaseButton;
using static Robust.Client.UserInterface.Controls.LineEdit;
@@ -199,7 +200,7 @@ public sealed class EntitySpawningUIController : UIController
continue;
}
if (prototype.NoSpawn)
if (prototype.HideSpawnMenu)
{
continue;
}

View File

@@ -98,6 +98,11 @@ namespace Robust.Client.UserInterface.Controls
OnTextChanged?.Invoke(new LineEditEventArgs(this, _text));
}
public void ForceSubmitText()
{
OnTextEntered?.Invoke(new LineEditEventArgs(this, _text));
}
/// <summary>
/// The text
/// </summary>
@@ -607,7 +612,7 @@ namespace Robust.Client.UserInterface.Controls
{
if (Editable)
{
OnTextEntered?.Invoke(new LineEditEventArgs(this, _text));
ForceSubmitText();
}
args.Handle();

View File

@@ -50,13 +50,13 @@ namespace Robust.Client.UserInterface.Controls
Action<Range> ev = _scrollValueChanged;
_hScrollBar = new HScrollBar
{
Visible = false,
Visible = _hScrollEnabled,
VerticalAlignment = VAlignment.Bottom,
HorizontalAlignment = HAlignment.Stretch
};
_vScrollBar = new VScrollBar
{
Visible = false,
Visible = _vScrollEnabled,
VerticalAlignment = VAlignment.Stretch,
HorizontalAlignment = HAlignment.Right
};

View File

@@ -10,6 +10,7 @@ using Robust.Client.ViewVariables.Editors;
using Robust.Client.ViewVariables.Instances;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Network;
@@ -232,7 +233,7 @@ namespace Robust.Client.ViewVariables
instance = new ViewVariablesInstanceObject(this, _robustSerializer);
}
var window = new DefaultWindow {Title = "View Variables"};
var window = new DefaultWindow {Title = Loc.GetString("view-variables")};
instance.Initialize(window, obj);
window.OnClose += () => _closeInstance(instance, false);
_windows.Add(instance, window);
@@ -250,7 +251,7 @@ namespace Robust.Client.ViewVariables
{
var window = new DefaultWindow
{
Title = "View Variables",
Title = Loc.GetString("view-variables"),
SetSize = _defaultWindowSize
};
var loadingLabel = new Label {Text = "Retrieving remote object data from server..."};

View File

@@ -75,13 +75,4 @@ public sealed class ServerMetaDataSystem : MetaDataSystem
Dirty(uid, comp);
}
}
public override void SetVisibilityMask(EntityUid uid, int value, MetaDataComponent? meta = null)
{
if (!Resolve(uid, ref meta) || meta.VisibilityMask == value)
return;
base.SetVisibilityMask(uid, value, meta);
_pvsSystem.MarkDirty(uid);
}
}

View File

@@ -1,4 +1,5 @@
using System;
using Robust.Server.GameStates;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -6,11 +7,18 @@ namespace Robust.Server.GameObjects
{
public sealed class VisibilitySystem : EntitySystem
{
[Dependency] private readonly MetaDataSystem _metaSys = default!;
[Dependency] private readonly PvsSystem _pvs = default!;
private EntityQuery<TransformComponent> _xformQuery;
private EntityQuery<MetaDataComponent> _metaQuery;
private EntityQuery<VisibilityComponent> _visiblityQuery;
public override void Initialize()
{
base.Initialize();
_xformQuery = GetEntityQuery<TransformComponent>();
_metaQuery = GetEntityQuery<MetaDataComponent>();
_visiblityQuery = GetEntityQuery<VisibilityComponent>();
SubscribeLocalEvent<EntParentChangedMessage>(OnParentChange);
EntityManager.EntityInitialized += OnEntityInit;
}
@@ -82,27 +90,58 @@ namespace Robust.Server.GameObjects
RefreshVisibility(uid);
}
public void RefreshVisibility(EntityUid uid, MetaDataComponent? metaDataComponent = null, VisibilityComponent? visibilityComponent = null)
public void RefreshVisibility(EntityUid uid,
VisibilityComponent? visibilityComponent = null,
MetaDataComponent? meta = null)
{
if (Resolve(uid, ref metaDataComponent, false))
_metaSys.SetVisibilityMask(uid, GetVisibilityMask(uid, visibilityComponent), metaDataComponent);
if (!_metaQuery.Resolve(uid, ref meta, false))
return;
// Iterate up through parents and calculate the cumulative visibility mask.
var mask = GetParentVisibilityMask(uid, visibilityComponent);
// Iterate down through children and propagate mask changes.
RecursivelyApplyVisibility(uid, mask, meta);
}
private void RecursivelyApplyVisibility(EntityUid uid, int mask, MetaDataComponent meta)
{
if (meta.VisibilityMask == mask)
return;
var xform = _xformQuery.GetComponent(uid);
meta.VisibilityMask = mask;
_pvs.MarkDirty(uid, xform);
foreach (var child in xform.ChildEntities)
{
if (!_metaQuery.TryGetComponent(child, out var childMeta))
continue;
var childMask = mask;
if (_visiblityQuery.TryGetComponent(child, out VisibilityComponent? hildVis))
childMask |= hildVis.Layer;
RecursivelyApplyVisibility(child, childMask, childMeta);
}
}
[Obsolete("Use overload that takes an EntityUid instead")]
public void RefreshVisibility(VisibilityComponent visibilityComponent)
{
RefreshVisibility(visibilityComponent.Owner, null, visibilityComponent);
RefreshVisibility(visibilityComponent.Owner, visibilityComponent);
}
private int GetVisibilityMask(EntityUid uid, VisibilityComponent? visibilityComponent = null, TransformComponent? xform = null)
private int GetParentVisibilityMask(EntityUid uid, VisibilityComponent? visibilityComponent = null)
{
int visMask = 1; // apparently some content expects everything to have the first bit/flag set to true.
if (Resolve(uid, ref visibilityComponent, false))
if (_visiblityQuery.Resolve(uid, ref visibilityComponent, false))
visMask |= visibilityComponent.Layer;
// Include parent vis masks
if (Resolve(uid, ref xform) && xform.ParentUid.IsValid())
visMask |= GetVisibilityMask(xform.ParentUid);
if (_xformQuery.TryGetComponent(uid, out var xform) && xform.ParentUid.IsValid())
visMask |= GetParentVisibilityMask(xform.ParentUid);
return visMask;
}

View File

@@ -92,9 +92,9 @@ namespace Robust.Server.GameStates
/// <summary>
/// Marks an entity's current chunk as dirty.
/// </summary>
internal void MarkDirty(EntityUid uid)
internal void MarkDirty(EntityUid uid, TransformComponent xform)
{
var coordinates = _transform.GetMoverCoordinates(uid);
var coordinates = _transform.GetMoverCoordinates(uid, xform);
_entityPvsCollection.MarkDirty(_entityPvsCollection.GetChunkIndex(coordinates));
}
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Extensions.ObjectPool;
using Robust.Server.Configuration;
@@ -24,7 +25,7 @@ namespace Robust.Server.GameStates;
internal sealed partial class PvsSystem : EntitySystem
{
[Shared.IoC.Dependency] private readonly IConfigurationManager _configManager = default!;
[Shared.IoC.Dependency] private readonly IMapManagerInternal _mapManager = default!;
[Shared.IoC.Dependency] private readonly INetworkedMapManager _mapManager = default!;
[Shared.IoC.Dependency] private readonly IPlayerManager _playerManager = default!;
[Shared.IoC.Dependency] private readonly IParallelManager _parallelManager = default!;
[Shared.IoC.Dependency] private readonly IServerGameStateManager _serverGameStateManager = default!;
@@ -227,6 +228,7 @@ internal sealed partial class PvsSystem : EntitySystem
public void CullDeletionHistory(GameTick oldestAck)
{
_entityPvsCollection.CullDeletionHistoryUntil(oldestAck);
_mapManager.CullDeletionHistory(oldestAck);
}
#region PVSCollection methods to maybe make public someday:tm:
@@ -648,16 +650,16 @@ internal sealed partial class PvsSystem : EntitySystem
}
}
private bool AddToChunkSetRecursively(in EntityUid uid, in NetEntity netEntity, MetaDataComponent mComp,
private void AddToChunkSetRecursively(in EntityUid uid, in NetEntity netEntity, MetaDataComponent mComp,
int visMask, RobustTree<NetEntity> tree, Dictionary<NetEntity, MetaDataComponent> set)
{
if (set.ContainsKey(netEntity))
return true;
// TODO: Don't need to know about parents so no longer need to use bool for this method.
// If the eye is missing ANY layer this entity or any of its parents belongs to, it is considered invisible.
// If the eye is missing ANY layer that this entity is on, or any layer that any of its parents belongs to, then
// it is considered invisible.
if ((visMask & mComp.VisibilityMask) != mComp.VisibilityMask)
return false;
return;
if (!set.TryAdd(netEntity, mComp))
return; // already sending
var xform = _xformQuery.GetComponent(uid);
@@ -667,8 +669,7 @@ internal sealed partial class PvsSystem : EntitySystem
{
DebugTools.Assert(_mapManager.IsGrid(uid) || _mapManager.IsMap(uid));
tree.Set(netEntity);
set.Add(netEntity, mComp);
return true;
return;
}
DebugTools.Assert(!_mapManager.IsGrid(uid) && !_mapManager.IsMap(uid));
@@ -677,15 +678,12 @@ internal sealed partial class PvsSystem : EntitySystem
var parentMeta = _metaQuery.GetComponent(parent);
var parentNetEntity = parentMeta.NetEntity;
if (!AddToChunkSetRecursively(in parent, in parentNetEntity, parentMeta, visMask, tree, set)) //did we just fail to add the parent?
{
return false; //we failed? suppose we dont get added either
}
// Child should have all o the same flags as the parent.
DebugTools.Assert((parentMeta.VisibilityMask & mComp.VisibilityMask) == parentMeta.VisibilityMask);
//i want it to crash here if it gets added double bc that shouldnt happen and will add alot of unneeded cycles
// Add our parent.
AddToChunkSetRecursively(in parent, in parentNetEntity, parentMeta, visMask, tree, set);
tree.Set(netEntity, parentNetEntity);
set.Add(netEntity, mComp);
return true;
}
internal (List<EntityState>? updates, List<NetEntity>? deletions, List<NetEntity>? leftPvs, GameTick fromTick)
@@ -900,15 +898,22 @@ internal sealed partial class PvsSystem : EntitySystem
// As every map is parented to uid 0 in the tree we still need to get their children, plus because we go top-down
// we may find duplicate parents with children we haven't encountered before
// on different chunks (this is especially common with direct grid children)
if (!toSend.ContainsKey(currentNodeIndex))
ref var value = ref CollectionsMarshal.GetValueRefOrAddDefault(toSend, currentNodeIndex, out var exists);
if (!exists)
{
var (entered, shouldAdd) = ProcessEntry(in currentNodeIndex, lastAcked, lastSent, lastSeen,
ref newEntityCount, ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
if (!shouldAdd)
{
// In the majority of instances entities do get added.
// So its better to add and maybe remove, rather than checking ContainsKey() and then maybe adding it.
toSend.Remove(currentNodeIndex);
continue;
}
AddToSendSet(in currentNodeIndex, metaDataCache[currentNodeIndex], toSend, fromTick, in entered, ref entStateCount);
AddToSendSet(in currentNodeIndex, metaDataCache[currentNodeIndex], ref value, toSend, fromTick, in entered, ref entStateCount);
}
var node = tree[currentNodeIndex];
@@ -957,14 +962,15 @@ internal sealed partial class PvsSystem : EntitySystem
// to the toSend set, it doesn't guarantee that its parents have been. E.g., if a player ghost just teleported
// to follow a far away entity, the player's own entity is still being sent, but we need to ensure that we also
// send the new parents, which may otherwise be delayed because of the PVS budget..
if (!toSend.ContainsKey(netEntity))
ref var value = ref CollectionsMarshal.GetValueRefOrAddDefault(toSend, netEntity, out var exists);
if (!exists)
{
// TODO PERFORMANCE.
// ProcessEntry() unnecessarily checks lastSent.ContainsKey() and maybe lastSeen.Contains(). Given that at this
// point the budgets are just ignored, this should just bypass those checks. But then again 99% of the time this
// is just the player's own entity + maybe a singularity. So currently not all that performance intensive.
var (entered, _) = ProcessEntry(in netEntity, lastAcked, lastSent, lastSeen, ref newEntityCount, ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
AddToSendSet(in netEntity, metadata, toSend, fromTick, in entered, ref entStateCount);
AddToSendSet(in netEntity, metadata, ref value, toSend, fromTick, in entered, ref entStateCount);
}
if (addChildren)
@@ -996,12 +1002,13 @@ internal sealed partial class PvsSystem : EntitySystem
var metadata = _metaQuery.GetComponent(child);
var childNetEntity = metadata.NetEntity;
if (!toSend.ContainsKey(childNetEntity))
ref var value = ref CollectionsMarshal.GetValueRefOrAddDefault(toSend, childNetEntity, out var exists);
if (!exists)
{
var (entered, _) = ProcessEntry(in childNetEntity, lastAcked, lastSent, lastSeen, ref newEntityCount,
ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
AddToSendSet(in childNetEntity, metadata, toSend, fromTick, in entered, ref entStateCount);
AddToSendSet(in childNetEntity, metadata, ref value, toSend, fromTick, in entered, ref entStateCount);
}
RecursivelyAddChildren(childXform, lastAcked, lastSent, toSend, lastSeen, fromTick, ref newEntityCount,
@@ -1044,10 +1051,13 @@ internal sealed partial class PvsSystem : EntitySystem
return (entered, true);
}
private void AddToSendSet(in NetEntity netEntity, MetaDataComponent metaDataComponent, Dictionary<NetEntity, PvsEntityVisibility> toSend, GameTick fromTick, in bool entered, ref int entStateCount)
private void AddToSendSet(in NetEntity netEntity, MetaDataComponent metaDataComponent,
ref PvsEntityVisibility vis, Dictionary<NetEntity, PvsEntityVisibility> toSend,
GameTick fromTick, in bool entered, ref int entStateCount)
{
if (metaDataComponent.EntityLifeStage >= EntityLifeStage.Terminating)
{
toSend.Remove(netEntity);
var rep = new EntityStringRepresentation(GetEntity(netEntity), metaDataComponent.EntityDeleted, metaDataComponent.EntityName, metaDataComponent.EntityPrototype?.ID);
Log.Error($"Attempted to add a deleted entity to PVS send set: '{rep}'. Trace:\n{Environment.StackTrace}");
return;
@@ -1055,7 +1065,7 @@ internal sealed partial class PvsSystem : EntitySystem
if (entered)
{
toSend.Add(netEntity, PvsEntityVisibility.Entered);
vis = PvsEntityVisibility.Entered;
entStateCount++;
return;
}
@@ -1063,12 +1073,12 @@ internal sealed partial class PvsSystem : EntitySystem
if (metaDataComponent.EntityLastModifiedTick <= fromTick)
{
//entity has been sent before and hasnt been updated since
toSend.Add(netEntity, PvsEntityVisibility.StayedUnchanged);
vis = PvsEntityVisibility.StayedUnchanged;
return;
}
//add us
toSend.Add(netEntity, PvsEntityVisibility.StayedChanged);
vis = PvsEntityVisibility.StayedChanged;
entStateCount++;
}

View File

@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Microsoft.Extensions.ObjectPool;
using Robust.Shared.Utility;
@@ -89,56 +89,76 @@ public sealed class RobustTree<T> where T : notnull
throw new InvalidOperationException("Node neither had a parent nor was a RootNode.");
}
public TreeNode Set(T rootNode)
public void Set(T rootNode)
{
//root node, for now
if (_nodeIndex.TryGetValue(rootNode, out var node))
ref var node = ref CollectionsMarshal.GetValueRefOrAddDefault(_nodeIndex, rootNode, out var exists);
if (exists)
{
if(!RootNodes.Contains(rootNode))
throw new InvalidOperationException("Node already exists as non-root node.");
return node;
return;
}
node = new TreeNode(rootNode);
_nodeIndex.Add(rootNode, node);
RootNodes.Add(rootNode);
return node;
if(!RootNodes.Add(rootNode))
throw new InvalidOperationException("Non-existent node was already a root node?");
}
public TreeNode Set(T child, T parent)
public void Set(T child, T parent)
{
if (!_nodeIndex.TryGetValue(parent, out var parentNode))
parentNode = Set(parent);
if (parentNode.Children == null)
// Code block for where parentNode is a valid ref
{
_nodeIndex[parent] = parentNode = parentNode.WithChildren(_pool.Get());
}
ref var parentNode = ref CollectionsMarshal.GetValueRefOrAddDefault(_nodeIndex, parent, out var parentExists);
if (_nodeIndex.TryGetValue(child, out var existingNode))
{
if (RootNodes.Contains(child))
// If parent does not exist we make it a new root node.
if (!parentExists)
{
parentNode.Children!.Add(existingNode.Value);
RootNodes.Remove(child);
_parents.Add(child, parent);
return existingNode;
parentNode = new TreeNode(parent);
if (!RootNodes.Add(parent))
{
_nodeIndex.Remove(parent);
throw new InvalidOperationException("Non-existent node was already a root node?");
}
}
if (!_parents.TryGetValue(child, out var previousParent) || _nodeIndex.TryGetValue(previousParent, out var previousParentNode))
throw new InvalidOperationException("Could not find old parent for non-root node.");
previousParentNode.Children?.Remove(existingNode.Value);
parentNode.Children!.Add(existingNode.Value);
_parents[child] = parent;
return existingNode;
var children = parentNode.Children;
if (children == null)
{
children = _pool.Get();
parentNode = parentNode.WithChildren(children);
DebugTools.AssertNotNull(_nodeIndex[parent].Children);
}
children.Add(child);
}
existingNode = new TreeNode(child);
_nodeIndex.Add(child, existingNode);
parentNode.Children!.Add(existingNode.Value);
_parents.Add(child, parent);
return existingNode;
// No longer safe to access parentNode ref after this.
ref var node = ref CollectionsMarshal.GetValueRefOrAddDefault(_nodeIndex, child, out var childExists);
if (!childExists)
{
// This is the path that PVS should take 99% of the time.
node = new TreeNode(child);
_parents.Add(child, parent);
return;
}
if (RootNodes.Remove(child))
{
DebugTools.Assert(!_parents.ContainsKey(child));
_parents.Add(child, parent);
return;
}
ref var parentEntry = ref CollectionsMarshal.GetValueRefOrAddDefault(_parents, child, out var previousParentExists);
if (!previousParentExists || !_nodeIndex.TryGetValue(parentEntry!, out var previousParentNode))
{
parentEntry = parent;
throw new InvalidOperationException("Could not find old parent for non-root node.");
}
previousParentNode.Children?.Remove(child);
parentEntry = parent;
}
public readonly struct TreeNode : IEquatable<TreeNode>

View File

@@ -99,10 +99,10 @@ namespace Robust.Server.GameStates
.Select(x => $"{x.Name} ({_entityManager.ToPrettyString(x.AttachedEntity)})");
shell.WriteLine($@"Current tick: {_gameTiming.CurTick}
Last Acked tick: {_lastOldestAck}
Deletion history size: {_pvs.EntityPVSCollection.GetDeletedIndices(_lastOldestAck)?.Count ?? 0}
Actual last acked tick: {ack}
Last ack players: {string.Join(", ", players)}
Stored oldest acked tick: {_lastOldestAck}
Deletion history size: {_pvs.EntityPVSCollection.GetDeletedIndices(GameTick.First)?.Count ?? 0}
Actual oldest: {ack}
Oldest acked clients: {string.Join(", ", players)}
");
}
@@ -168,15 +168,6 @@ Last ack players: {string.Join(", ", players)}
/// <inheritdoc />
public void SendGameStateUpdate()
{
if (!_networkManager.IsConnected)
{
// Prevent deletions piling up if we have no clients.
_pvs.CullDeletionHistory(GameTick.MaxValue);
_mapManager.CullDeletionHistory(GameTick.MaxValue);
_pvs.CleanupDirty(Enumerable.Empty<IPlayerSession>());
return;
}
var players = _playerManager.ServerSessions.Where(o => o.Status == SessionStatus.InGame).ToArray();
// Update entity positions in PVS chunks/collections
@@ -216,14 +207,21 @@ Last ack players: {string.Join(", ", players)}
_pvs.CleanupDirty(players);
}
// keep the deletion history buffers clean
if (oldestAck > _lastOldestAck)
if (oldestAck == GameTick.MaxValue)
{
using var _ = _usageHistogram.WithLabels("Cull History").NewTimer();
_lastOldestAck = oldestAck;
_pvs.CullDeletionHistory(oldestAck);
_mapManager.CullDeletionHistory(oldestAck);
// There were no connected players?
// In that case we just clear all deletion history.
_pvs.CullDeletionHistory(GameTick.MaxValue);
_lastOldestAck = GameTick.Zero;
return;
}
if (oldestAck == _lastOldestAck)
return;
_lastOldestAck = oldestAck;
using var __ = _usageHistogram.WithLabels("Cull History").NewTimer();
_pvs.CullDeletionHistory(oldestAck);
}
private GameTick SendStates(IPlayerSession[] players, PvsData? pvsData)

View File

@@ -51,6 +51,7 @@ namespace Robust.Server.Prototypes
public override void LoadDefaultPrototypes(Dictionary<Type, HashSet<string>>? changed = null)
{
LoadDirectory(new("/EnginePrototypes/"), changed: changed);
LoadDirectory(_server.Options.PrototypeDirectory, changed: changed);
ResolveResults();
}

View File

@@ -16,12 +16,15 @@ namespace Robust.Shared.CompNetworkGenerator
private const string MemberAttributeName = "Robust.Shared.Analyzers.AutoNetworkedFieldAttribute";
private const string GlobalEntityUidName = "global::Robust.Shared.GameObjects.EntityUid";
private const string GlobalNullableEntityUidName = "global::Robust.Shared.GameObjects.EntityUid?";
private const string GlobalEntityCoordinatesName = "global::Robust.Shared.Map.EntityCoordinates?";
private const string GlobalNullableEntityCoordinatesName = "global::Robust.Shared.Map.EntityCoordinates";
private const string GlobalEntityCoordinatesName = "global::Robust.Shared.Map.EntityCoordinates";
private const string GlobalNullableEntityCoordinatesName = "global::Robust.Shared.Map.EntityCoordinates?";
private const string GlobalEntityUidSetName = "global::System.Collections.Generic.HashSet<global::Robust.Shared.GameObjects.EntityUid>";
private const string GlobalNetEntityUidSetName = "global::System.Collections.Generic.HashSet<global::Robust.Shared.GameObjects.NetEntity>";
private const string GlobalEntityUidListName = "global::System.Collections.Generic.List<global::Robust.Shared.GameObjects.EntityUid>";
private const string GlobalNetEntityUidListName = "global::System.Collections.Generic.List<global::Robust.Shared.GameObjects.NetEntity>";
private static string GenerateSource(in GeneratorExecutionContext context, INamedTypeSymbol classSymbol, CSharpCompilation comp, bool raiseAfterAutoHandle)
{
// Debugger.Launch();
var nameSpace = classSymbol.ContainingNamespace.ToDisplayString();
var componentName = classSymbol.Name;
var stateName = $"{componentName}_AutoState";
@@ -172,6 +175,26 @@ namespace Robust.Shared.CompNetworkGenerator
component.{name} = state.{name};");
}
break;
case GlobalEntityUidSetName:
stateFields.Append($@"
public {GlobalNetEntityUidSetName} {name} = default!;");
getStateInit.Append($@"
{name} = GetNetEntitySet(component.{name}),");
handleStateSetters.Append($@"
component.{name} = EnsureEntitySet<{componentName}>(state.{name}, uid);");
break;
case GlobalEntityUidListName:
stateFields.Append($@"
public {GlobalNetEntityUidListName} {name} = default!;");
getStateInit.Append($@"
{name} = GetNetEntityList(component.{name}),");
handleStateSetters.Append($@"
component.{name} = EnsureEntityList<{componentName}>(state.{name}, uid);");
break;
}
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using JetBrains.Annotations;
using Robust.Shared.Utility;
@@ -35,9 +36,15 @@ namespace Robust.Shared.Maths
/// </summary>
[FieldOffset(sizeof(float) * 3)] public float Top;
[NonSerialized]
[FieldOffset(sizeof(float) * 0)] public Vector2 BottomLeft;
[NonSerialized]
[FieldOffset(sizeof(float) * 2)] public Vector2 TopRight;
[NonSerialized]
[FieldOffset(sizeof(float) * 0)] public System.Numerics.Vector4 AsVector4;
public readonly Vector2 BottomRight
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -141,6 +148,11 @@ namespace Robust.Shared.Maths
return new Box2(min, max);
}
public readonly bool HasNan()
{
return Vector128.EqualsAny(AsVector4.AsVector128(), Vector128.Create(float.NaN));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[Pure]
public readonly bool Intersects(in Box2 other)

View File

@@ -13,6 +13,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Robust.Shared.Log;
using Robust.Shared.Serialization;
namespace Robust.Shared.Containers
@@ -111,6 +112,7 @@ namespace Robust.Shared.Containers
DebugTools.Assert(ownerTransform == null || ownerTransform.Owner == Owner);
DebugTools.Assert(physics == null || physics.Owner == toinsert);
DebugTools.Assert(!ExpectedEntities.Contains(entMan.GetNetEntity(toinsert)));
DebugTools.Assert(Manager.Containers.ContainsKey(ID));
var physicsQuery = entMan.GetEntityQuery<PhysicsComponent>();
var transformQuery = entMan.GetEntityQuery<TransformComponent>();
@@ -121,6 +123,26 @@ namespace Robust.Shared.Containers
var jointSys = entMan.EntitySysManager.GetEntitySystem<SharedJointSystem>();
var containerSys = entMan.EntitySysManager.GetEntitySystem<SharedContainerSystem>();
// If someone is attempting to insert an entity into a container that is getting deleted, then we will
// automatically delete that entity. I.e., the insertion automatically "succeeds" and both entities get deleted.
// This is consistent with what happens if you attempt to attach an entity to a terminating parent.
if (!entMan.TryGetComponent(Owner, out MetaDataComponent? ownerMeta))
{
Logger.ErrorS("container",
$"Attempted to insert an entity {entMan.ToPrettyString(toinsert)} into a non-existent entity.");
entMan.QueueDeleteEntity(toinsert);
return false;
}
if (ownerMeta.EntityLifeStage >= EntityLifeStage.Terminating)
{
Logger.ErrorS("container",
$"Attempted to insert an entity {entMan.ToPrettyString(toinsert)} into an entity that is terminating. Entity: {entMan.ToPrettyString(Owner)}.");
entMan.QueueDeleteEntity(toinsert);
return false;
}
//Verify we can insert into this container
if (!force && !containerSys.CanInsert(toinsert, this))
return false;

View File

@@ -189,15 +189,8 @@ namespace Robust.Shared.GameObjects
/// <remarks>
/// Every entity will always have the first bit set to true.
/// </remarks>
[Access(typeof(MetaDataSystem))]
public int VisibilityMask = 1;
[UsedImplicitly, ViewVariables(VVAccess.ReadWrite)]
private int VVVisibilityMask
{
get => VisibilityMask;
set => IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<MetaDataSystem>().SetVisibilityMask(Owner, value, this);
}
[ViewVariables] // TODO ACCESS RRestrict writing to server-side visibility system
public int VisibilityMask { get; internal set; }= 1;
[ViewVariables]
public bool EntityPaused => PauseTime != null;

View File

@@ -4,6 +4,7 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using JetBrains.Annotations;
using Robust.Shared.GameStates;
using Robust.Shared.Log;
@@ -285,17 +286,29 @@ namespace Robust.Shared.GameObjects
var dict = _entTraitArray[type.Value];
DebugTools.Assert(dict != null);
if (dict.TryGetValue(uid, out var duplicate))
// Code block to restrict access to ref comp.
{
if (!overwrite && !duplicate.Deleted)
throw new InvalidOperationException(
$"Component reference type {component.GetType().Name} already occupied by {duplicate}");
ref var comp = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, uid, out var exists);
if (exists)
{
if (!overwrite && !comp!.Deleted)
{
throw new InvalidOperationException(
$"Component reference type {reg.Name} already occupied by {comp}");
}
RemoveComponentImmediate(duplicate, uid, false, metadata);
// This will invalidate the comp ref as it removes the key from the dictionary.
// This is inefficient, but component overriding rarely ever happens.
RemoveComponentImmediate(comp!, uid, false, metadata);
dict.Add(uid, component);
}
else
{
comp = component;
}
}
// actually ADD the component
dict.Add(uid, component);
_entCompIndex.Add(uid, component);
// add the component to the netId grid

View File

@@ -95,7 +95,7 @@ public partial class EntityManager
ComponentRegistry? overrides = null)
{
uid = null;
if (!_xformQuery.Resolve(target, ref xform))
if (!TransformQuery.Resolve(target, ref xform))
return false;
if (!xform.ParentUid.IsValid())
@@ -157,7 +157,7 @@ public partial class EntityManager
public EntityUid SpawnNextToOrDrop(string? protoName, EntityUid target, TransformComponent? xform = null, ComponentRegistry? overrides = null)
{
xform ??= _xformQuery.GetComponent(target);
xform ??= TransformQuery.GetComponent(target);
if (!xform.ParentUid.IsValid())
return Spawn(protoName);
@@ -181,7 +181,7 @@ public partial class EntityManager
|| !container.Insert(uid, this))
{
xform ??= _xformQuery.GetComponent(containerUid);
xform ??= TransformQuery.GetComponent(containerUid);
if (xform.ParentUid.IsValid())
_xforms.PlaceNextToOrDrop(uid, containerUid, targetXform: xform);
}

View File

@@ -39,8 +39,8 @@ namespace Robust.Shared.GameObjects
// positions on spawn....
private SharedTransformSystem _xforms = default!;
protected EntityQuery<MetaDataComponent> MetaQuery;
private EntityQuery<TransformComponent> _xformQuery;
public EntityQuery<MetaDataComponent> MetaQuery;
public EntityQuery<TransformComponent> TransformQuery;
#endregion Dependencies
@@ -241,7 +241,7 @@ namespace Robust.Shared.GameObjects
_mapSystem = System<SharedMapSystem>();
_xforms = System<SharedTransformSystem>();
MetaQuery = GetEntityQuery<MetaDataComponent>();
_xformQuery = GetEntityQuery<TransformComponent>();
TransformQuery = GetEntityQuery<TransformComponent>();
}
public virtual void Shutdown()
@@ -324,7 +324,7 @@ namespace Robust.Shared.GameObjects
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
{
var newEntity = CreateEntity(prototypeName, overrides);
_xforms.SetCoordinates(newEntity, _xformQuery.GetComponent(newEntity), coordinates, unanchor: false);
_xforms.SetCoordinates(newEntity, TransformQuery.GetComponent(newEntity), coordinates, unanchor: false);
return newEntity;
}
@@ -332,7 +332,7 @@ namespace Robust.Shared.GameObjects
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
{
var newEntity = CreateEntity(prototypeName, overrides);
var transform = _xformQuery.GetComponent(newEntity);
var transform = TransformQuery.GetComponent(newEntity);
if (coordinates.MapId == MapId.Nullspace)
{
@@ -371,18 +371,8 @@ namespace Robust.Shared.GameObjects
public virtual void DirtyEntity(EntityUid uid, MetaDataComponent? metadata = null)
{
// We want to retrieve MetaDataComponent even if its Deleted flag is set.
if (metadata == null)
{
if (!_entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()].TryGetValue(uid, out var component))
throw new KeyNotFoundException($"Entity {uid} does not exist, cannot dirty it.");
metadata = (MetaDataComponent)component;
}
else
{
#pragma warning disable CS0618
DebugTools.Assert(metadata.Owner == uid);
#pragma warning restore CS0618
}
if (!MetaQuery.ResolveInternal(uid, ref metadata))
return;
if (metadata.EntityLastModifiedTick == _gameTiming.CurTick)
return;
@@ -453,7 +443,7 @@ namespace Robust.Shared.GameObjects
EntityUid uid,
MetaDataComponent metadata)
{
var transform = _xformQuery.GetComponent(uid);
var transform = TransformQuery.GetComponent(uid);
metadata.EntityLifeStage = EntityLifeStage.Terminating;
try
@@ -485,7 +475,7 @@ namespace Robust.Shared.GameObjects
{
// Note about this method: #if EXCEPTION_TOLERANCE is not used here because we're gonna it in the future...
var netEntity = GetNetEntity(uid, metadata);
var transform = _xformQuery.GetComponent(uid);
var transform = TransformQuery.GetComponent(uid);
// 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).
@@ -567,7 +557,7 @@ namespace Robust.Shared.GameObjects
public bool EntityExists(EntityUid uid)
{
return _entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()].ContainsKey(uid);
return MetaQuery.HasComponentInternal(uid);
}
public bool EntityExists(EntityUid? uid)
@@ -586,12 +576,12 @@ namespace Robust.Shared.GameObjects
public bool Deleted(EntityUid uid)
{
return !_entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()].TryGetValue(uid, out var comp) || ((MetaDataComponent) comp).EntityDeleted;
return !MetaQuery.TryGetComponentInternal(uid, out var comp) || comp.EntityDeleted;
}
public bool Deleted([NotNullWhen(false)] EntityUid? uid)
{
return !uid.HasValue || !_entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()].TryGetValue(uid.Value, out var comp) || ((MetaDataComponent) comp).EntityDeleted;
return !uid.HasValue || !MetaQuery.TryGetComponentInternal(uid.Value, out var comp) || comp.EntityDeleted;
}
/// <summary>
@@ -716,7 +706,7 @@ namespace Robust.Shared.GameObjects
StartEntity(entity);
// If the map we're initializing the entity on is initialized, run map init on it.
if (_mapManager.IsMapInitialized(mapId ?? _xformQuery.GetComponent(entity).MapID))
if (_mapManager.IsMapInitialized(mapId ?? TransformQuery.GetComponent(entity).MapID))
RunMapInit(entity, meta);
}
catch (Exception e)

View File

@@ -61,7 +61,7 @@ public partial class EntitySystem
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool Deleted(EntityUid uid, MetaDataComponent? metaData = null)
{
if (!Resolve(uid, ref metaData, false))
if (!EntityManager.MetaQuery.Resolve(uid, ref metaData, false))
return true;
return metaData.EntityDeleted;
@@ -73,7 +73,7 @@ public partial class EntitySystem
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool TerminatingOrDeleted(EntityUid uid, MetaDataComponent? metaData = null)
{
if (!Resolve(uid, ref metaData, false))
if (!EntityManager.MetaQuery.Resolve(uid, ref metaData, false))
return true;
return metaData.EntityLifeStage >= EntityLifeStage.Terminating;
@@ -104,7 +104,7 @@ public partial class EntitySystem
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityLifeStage LifeStage(EntityUid uid, MetaDataComponent? metaData = null)
{
if (!Resolve(uid, ref metaData, false))
if (!EntityManager.MetaQuery.Resolve(uid, ref metaData, false))
throw CompNotFound<MetaDataComponent>(uid);
return metaData.EntityLifeStage;
@@ -169,7 +169,7 @@ public partial class EntitySystem
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool TryLifeStage(EntityUid uid, [NotNullWhen(true)] out EntityLifeStage? lifeStage, MetaDataComponent? metaData = null)
{
if (!Resolve(uid, ref metaData))
if (!EntityManager.MetaQuery.Resolve(uid, ref metaData, false))
{
lifeStage = null;
return false;
@@ -227,7 +227,7 @@ public partial class EntitySystem
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected string Name(EntityUid uid, MetaDataComponent? metaData = null)
{
if(!Resolve(uid, ref metaData, false))
if (!EntityManager.MetaQuery.Resolve(uid, ref metaData, false))
throw CompNotFound<MetaDataComponent>(uid);
return metaData.EntityName;
@@ -240,7 +240,7 @@ public partial class EntitySystem
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected string Description(EntityUid uid, MetaDataComponent? metaData = null)
{
if(!Resolve(uid, ref metaData, false))
if (!EntityManager.MetaQuery.Resolve(uid, ref metaData, false))
throw CompNotFound<MetaDataComponent>(uid);
return metaData.EntityDescription;
@@ -253,7 +253,7 @@ public partial class EntitySystem
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityPrototype? Prototype(EntityUid uid, MetaDataComponent? metaData = null)
{
if (!Resolve(uid, ref metaData, false))
if (!EntityManager.MetaQuery.Resolve(uid, ref metaData, false))
throw CompNotFound<MetaDataComponent>(uid);
return metaData.EntityPrototype;
@@ -265,7 +265,7 @@ public partial class EntitySystem
/// <exception cref="KeyNotFoundException">Thrown when the entity doesn't exist.</exception>
protected GameTick LastModifiedTick(EntityUid uid, MetaDataComponent? metaData = null)
{
if (!Resolve(uid, ref metaData, false))
if (!EntityManager.MetaQuery.Resolve(uid, ref metaData, false))
throw CompNotFound<MetaDataComponent>(uid);
return metaData.EntityLastModifiedTick;
@@ -278,7 +278,7 @@ public partial class EntitySystem
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool Paused(EntityUid uid, MetaDataComponent? metaData = null)
{
if (!Resolve(uid, ref metaData, false))
if (!EntityManager.MetaQuery.Resolve(uid, ref metaData, false))
throw CompNotFound<MetaDataComponent>(uid);
return metaData.EntityPaused;
@@ -291,7 +291,7 @@ public partial class EntitySystem
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void SetPaused(EntityUid uid, bool paused, MetaDataComponent? metaData = null)
{
if (!Resolve(uid, ref metaData, false))
if (!EntityManager.MetaQuery.Resolve(uid, ref metaData, false))
throw CompNotFound<MetaDataComponent>(uid);
EntityManager.EntitySysManager.GetEntitySystem<MetaDataSystem>().SetEntityPaused(uid, paused, metaData);
@@ -302,12 +302,12 @@ public partial class EntitySystem
/// </summary>
/// <returns>Whether the operation succeeded.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool TryDirty(EntityUid uid)
protected bool TryDirty(EntityUid uid, MetaDataComponent? metaData = null)
{
if (!Exists(uid))
if (!EntityManager.MetaQuery.Resolve(uid, ref metaData, false))
return false;
DirtyEntity(uid);
DirtyEntity(uid, metaData);
return true;
}
@@ -318,7 +318,7 @@ public partial class EntitySystem
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool TryName(EntityUid uid, [NotNullWhen(true)] out string? name, MetaDataComponent? metaData = null)
{
if (!Resolve(uid, ref metaData, false))
if (!EntityManager.MetaQuery.Resolve(uid, ref metaData, false))
{
name = null;
return false;
@@ -335,7 +335,7 @@ public partial class EntitySystem
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool TryDescription(EntityUid uid, [NotNullWhen(true)] out string? description, MetaDataComponent? metaData = null)
{
if (!Resolve(uid, ref metaData, false))
if (!EntityManager.MetaQuery.Resolve(uid, ref metaData, false))
{
description = null;
return false;
@@ -352,7 +352,7 @@ public partial class EntitySystem
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool TryPrototype(EntityUid uid, [NotNullWhen(true)] out EntityPrototype? prototype, MetaDataComponent? metaData = null)
{
if (!Resolve(uid, ref metaData, false))
if (!EntityManager.MetaQuery.Resolve(uid, ref metaData, false))
{
prototype = null;
return false;
@@ -369,7 +369,7 @@ public partial class EntitySystem
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool TryLastModifiedTick(EntityUid uid, [NotNullWhen(true)] out GameTick? lastModifiedTick, MetaDataComponent? metaData = null)
{
if (!Resolve(uid, ref metaData, false))
if (!EntityManager.MetaQuery.Resolve(uid, ref metaData, false))
{
lastModifiedTick = null;
return false;
@@ -386,7 +386,7 @@ public partial class EntitySystem
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool TryPaused(EntityUid uid, [NotNullWhen(true)] out bool? paused, MetaDataComponent? metaData = null)
{
if (!Resolve(uid, ref metaData, false))
if (!EntityManager.MetaQuery.Resolve(uid, ref metaData, false))
{
paused = null;
return false;
@@ -458,6 +458,20 @@ public partial class EntitySystem
return EntityManager.TryGetComponent(uid, out comp);
}
/// <inheritdoc cref="IEntityManager.TryGetComponent&lt;T&gt;(EntityUid, out T)"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool TryComp(EntityUid uid, [NotNullWhen(true)] out TransformComponent? comp)
{
return EntityManager.TransformQuery.TryGetComponent(uid, out comp);
}
/// <inheritdoc cref="IEntityManager.TryGetComponent&lt;T&gt;(EntityUid, out T)"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool TryComp(EntityUid uid, [NotNullWhen(true)] out MetaDataComponent? comp)
{
return EntityManager.MetaQuery.TryGetComponent(uid, out comp);
}
/// <inheritdoc cref="IEntityManager.TryGetComponent&lt;T&gt;(EntityUid?, out T)"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool TryComp<T>([NotNullWhen(true)] EntityUid? uid, [NotNullWhen(true)] out T? comp)
@@ -471,6 +485,30 @@ public partial class EntitySystem
return EntityManager.TryGetComponent(uid.Value, out comp);
}
/// <inheritdoc cref="IEntityManager.TryGetComponent&lt;T&gt;(EntityUid?, out T)"/>
protected bool TryComp([NotNullWhen(true)] EntityUid? uid, [NotNullWhen(true)] out TransformComponent? comp)
{
if (!uid.HasValue)
{
comp = default;
return false;
}
return EntityManager.TransformQuery.TryGetComponent(uid.Value, out comp);
}
/// <inheritdoc cref="IEntityManager.TryGetComponent&lt;T&gt;(EntityUid?, out T)"/>
protected bool TryComp([NotNullWhen(true)] EntityUid? uid, [NotNullWhen(true)] out MetaDataComponent? comp)
{
if (!uid.HasValue)
{
comp = default;
return false;
}
return EntityManager.MetaQuery.TryGetComponent(uid.Value, out comp);
}
/// <inheritdoc cref="IEntityManager.GetComponents"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected IEnumerable<IComponent> AllComps(EntityUid uid)
@@ -492,7 +530,7 @@ public partial class EntitySystem
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected TransformComponent Transform(EntityUid uid)
{
return EntityManager.GetComponent<TransformComponent>(uid);
return EntityManager.TransformQuery.GetComponent(uid);
}
/// <summary>
@@ -502,7 +540,7 @@ public partial class EntitySystem
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected MetaDataComponent MetaData(EntityUid uid)
{
return EntityManager.GetComponent<MetaDataComponent>(uid);
return EntityManager.MetaQuery.GetComponent(uid);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -511,6 +549,13 @@ public partial class EntitySystem
return EntityManager.GetEntityData(nuid);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool TryGetEntityData(NetEntity nuid, [NotNullWhen(true)] out EntityUid? uid,
[NotNullWhen(true)] out MetaDataComponent? meta)
{
return EntityManager.TryGetEntityData(nuid, out uid, out meta);
}
#endregion
#region Component Has

View File

@@ -32,6 +32,22 @@ namespace Robust.Shared.GameObjects
return found;
}
/// <inheritdoc cref="Resolve{TComp}"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool Resolve(EntityUid uid, [NotNullWhen(true)] ref MetaDataComponent? component,
bool logMissing = true)
{
return EntityManager.MetaQuery.Resolve(uid, ref component);
}
/// <inheritdoc cref="Resolve{TComp}"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool Resolve(EntityUid uid, [NotNullWhen(true)] ref TransformComponent? component,
bool logMissing = true)
{
return EntityManager.TransformQuery.Resolve(uid, ref component);
}
/// <summary>
/// Resolves the components on the entity for the null component references.
/// </summary>

View File

@@ -148,12 +148,6 @@ public abstract class MetaDataSystem : EntitySystem
component.Flags &= ~ev.ToRemove;
}
public virtual void SetVisibilityMask(EntityUid uid, int value, MetaDataComponent? meta = null)
{
if (Resolve(uid, ref meta))
meta.VisibilityMask = value;
}
}
/// <summary>

View File

@@ -495,7 +495,7 @@ public abstract partial class SharedTransformSystem
throw new InvalidOperationException($"Attempted to parent entity {ToPrettyString(uid)} to non-existent entity {value.EntityId}");
}
if (newParent.LifeStage > ComponentLifeStage.Running || LifeStage(value.EntityId) > EntityLifeStage.MapInitialized)
if (newParent.LifeStage >= ComponentLifeStage.Stopping || LifeStage(value.EntityId) >= EntityLifeStage.Terminating)
{
DetachParentToNull(uid, xform);
if (_netMan.IsServer || IsClientSide(uid))

View File

@@ -2,7 +2,6 @@ using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using JetBrains.Annotations;
using Robust.Shared.ContentPack;
using Robust.Shared.Serialization;
namespace Robust.Shared.Localization
@@ -23,7 +22,7 @@ namespace Robust.Shared.Localization
public interface ILocalizationManager
{
/// <summary>
/// Gets a language approrpiate string represented by the supplied messageId.
/// Gets a language appropriate string represented by the supplied messageId.
/// </summary>
/// <param name="messageId">Unique Identifier for a translated message.</param>
/// <returns>
@@ -31,6 +30,13 @@ namespace Robust.Shared.Localization
/// </returns>
string GetString(string messageId);
/// <summary>
/// Checks if the specified id has been registered, without checking its arguments.
/// </summary>
/// <param name="messageId">Unique Identifier for a translated message.</param>
/// <returns>true if it exists, even if it requires any parameters to be passed.</returns>
bool HasString(string messageId);
/// <summary>
/// Try- version of <see cref="GetString(string)"/>
/// </summary>
@@ -46,7 +52,7 @@ namespace Robust.Shared.Localization
string GetString(string messageId, params (string, object)[] args);
/// <summary>
/// Try- version of <see cref="GetString(string, ValueTuple{string, object}[])"/>
/// Try- version of <see cref="GetString(string, (string, object)[])"/>
/// </summary>
/// <remarks>
/// Does not log a warning if the message does not exist.

View File

@@ -0,0 +1,41 @@
using System;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations;
namespace Robust.Shared.Localization;
/// <summary>
/// Wrapper type for a localization string id.
/// </summary>
/// <param name="Id">The id of the localization string.</param>
/// <remarks>
/// This will be automatically validated by <see cref="LocIdSerializer"/> if used in data fields.</remarks>
/// <seealso cref="Loc.GetString(string)"/>
[Serializable, NetSerializable]
public readonly record struct LocId(string Id) : IEquatable<string>, IComparable<LocId>
{
public static implicit operator string(LocId locId)
{
return locId.Id;
}
public static implicit operator LocId(string id)
{
return new LocId(id);
}
public static implicit operator LocId?(string? id)
{
return id == null ? default(LocId?) : new LocId(id);
}
public bool Equals(string? other)
{
return Id == other;
}
public int CompareTo(LocId other)
{
return string.Compare(Id, other.Id, StringComparison.Ordinal);
}
}

View File

@@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using JetBrains.Annotations;
using Linguini.Bundle;
using Linguini.Bundle.Builder;
using Linguini.Bundle.Errors;
@@ -55,7 +54,6 @@ namespace Robust.Shared.Localization
return msg;
}
public string GetString(string messageId, params (string, object)[] args0)
{
if (_defaultCulture == null)
@@ -70,6 +68,11 @@ namespace Robust.Shared.Localization
return msg;
}
public bool HasString(string messageId)
{
return HasMessage(messageId, out _);
}
public bool TryGetString(string messageId, [NotNullWhen(true)] out string? value)
{
return TryGetString(messageId, out value, null);

View File

@@ -24,6 +24,8 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using JetBrains.Annotations;
using Robust.Shared.Maths;
@@ -131,7 +133,7 @@ namespace Robust.Shared.Physics
aabb ??= _extractAabb(item);
if (HasNaNs(aabb.Value))
if (aabb.Value.HasNan())
{
_nodeLookup[item] = DynamicTree.Proxy.Free;
return true;
@@ -179,28 +181,25 @@ namespace Robust.Shared.Physics
[MethodImpl(MethodImplOptions.NoInlining)]
public bool Update(in T item, Box2? newBox = null)
{
if (!TryGetProxy(item, out var proxy))
{
ref var proxy = ref CollectionsMarshal.GetValueRefOrNullRef(_nodeLookup, item);
if (Unsafe.IsNullRef(ref proxy))
return false;
}
newBox ??= _extractAabb(item);
if (HasNaNs(newBox.Value))
if (newBox.Value.HasNan())
{
if (proxy == DynamicTree.Proxy.Free)
{
return false;
}
_b2Tree.DestroyProxy(proxy);
_nodeLookup[item] = DynamicTree.Proxy.Free;
proxy = DynamicTree.Proxy.Free;
return true;
}
if (proxy == DynamicTree.Proxy.Free)
{
_nodeLookup[item] = _b2Tree.CreateProxy(newBox.Value, item);
proxy = _b2Tree.CreateProxy(newBox.Value, item);
return true;
}
@@ -331,36 +330,30 @@ namespace Robust.Shared.Physics
public void AddOrUpdate(T item, Box2? aabb = null)
{
aabb ??= _extractAabb(item);
if (!_nodeLookup.TryGetValue(item, out var proxy))
ref var proxy = ref CollectionsMarshal.GetValueRefOrAddDefault(_nodeLookup, item, out var exists);
if (!exists)
{
_nodeLookup[item] = HasNaNs(aabb.Value) ? DynamicTree.Proxy.Free : _b2Tree.CreateProxy(aabb.Value, item);
proxy = aabb.Value.HasNan() ? DynamicTree.Proxy.Free : _b2Tree.CreateProxy(aabb.Value, item);
return;
}
if (HasNaNs(aabb.Value))
if (aabb.Value.HasNan())
{
if (proxy == DynamicTree.Proxy.Free)
return;
_b2Tree.DestroyProxy(proxy);
_nodeLookup[item] = DynamicTree.Proxy.Free;
proxy = DynamicTree.Proxy.Free;
return;
}
if (proxy == DynamicTree.Proxy.Free)
_nodeLookup[item] = _b2Tree.CreateProxy(aabb.Value, item);
proxy = _b2Tree.CreateProxy(aabb.Value, item);
else
_b2Tree.MoveProxy(proxy, aabb.Value, Vector2.Zero);
}
private static bool HasNaNs(in Box2 box)
{
return float.IsNaN(box.Left)
|| float.IsNaN(box.Top)
|| float.IsNaN(box.Bottom)
|| float.IsNaN(box.Right);
}
[Conditional("DEBUG_DYNAMIC_TREE")]
[Conditional("DEBUG_DYNAMIC_TREE_ASSERTS")]
[DebuggerNonUserCode]

View File

@@ -365,5 +365,7 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
/// Is this a special contact for grid-grid collisions
/// </summary>
Grid = 1 << 2,
Deleting = 1 << 3,
}
}

View File

@@ -189,8 +189,6 @@ namespace Robust.Shared.Physics.Systems
_lookup.DestroyProxies(uid, fixtureId, fixture, xform, broadphase, physicsMap);
}
if (updates)
{
var resetMass = fixture.Density > 0f;

View File

@@ -312,12 +312,17 @@ public abstract partial class SharedPhysicsSystem
public void DestroyContact(Contact contact)
{
// Don't recursive update or we're in for a bad time.
if ((contact.Flags & ContactFlags.Deleting) != 0x0)
return;
Fixture fixtureA = contact.FixtureA!;
Fixture fixtureB = contact.FixtureB!;
var bodyA = contact.BodyA!;
var bodyB = contact.BodyB!;
var aUid = contact.EntityA;
var bUid = contact.EntityB;
contact.Flags |= ContactFlags.Deleting;
if (contact.IsTouching)
{

View File

@@ -13,7 +13,7 @@ namespace Robust.Shared.Prototypes;
/// </remarks>
/// <remarks><seealso cref="ProtoId{T}"/> for a wrapper of other prototype kinds.</remarks>
[Serializable, NetSerializable]
public readonly record struct EntProtoId(string Id)
public readonly record struct EntProtoId(string Id) : IEquatable<string>, IComparable<EntProtoId>
{
public static implicit operator string(EntProtoId protoId)
{
@@ -24,4 +24,19 @@ public readonly record struct EntProtoId(string Id)
{
return new EntProtoId(id);
}
public static implicit operator EntProtoId?(string? id)
{
return id == null ? default(EntProtoId?) : new EntProtoId(id);
}
public bool Equals(string? other)
{
return Id == other;
}
public int CompareTo(EntProtoId other)
{
return string.Compare(Id, other.Id, StringComparison.Ordinal);
}
}

View File

@@ -0,0 +1,25 @@
using Robust.Shared.Serialization.Manager.Attributes;
namespace Robust.Shared.Prototypes;
/// <summary>
/// Prototype that represents game entities.
/// </summary>
[Prototype("entityCategory")]
public sealed class EntityCategoryPrototype : IPrototype
{
[IdDataField]
public string ID { get; private set; } = default!;
/// <summary>
/// Localized name of the category, for use in entity spawn menus.
/// </summary>
[DataField("name")]
public string? Name { get; private set; }
/// <summary>
/// Localized description of the category, for use in entity spawn menus.
/// </summary>
[DataField("description")]
public string? Description { get; private set; }
}

View File

@@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
@@ -12,6 +11,7 @@ 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.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
using Robust.Shared.ViewVariables;
namespace Robust.Shared.Prototypes
@@ -27,6 +27,9 @@ namespace Robust.Shared.Prototypes
private static readonly Dictionary<string, string> LocPropertiesDefault = new();
[ValidatePrototypeId<EntityCategoryPrototype>]
private const string HideCategory = "hideSpawnMenu";
// LOCALIZATION NOTE:
// Localization-related properties in here are manually localized in LocalizationManager.
// As such, they should NOT be inherited to avoid confusing the system.
@@ -57,6 +60,10 @@ namespace Robust.Shared.Prototypes
[DataField("suffix")]
public string? SetSuffix { get; private set; }
[DataField("categories")]
[AlwaysPushInheritance]
public HashSet<string> Categories = new();
[ViewVariables]
public IReadOnlyDictionary<string, string> LocProperties => _locPropertiesSet ?? LocPropertiesDefault;
@@ -92,8 +99,11 @@ namespace Robust.Shared.Prototypes
[ViewVariables]
[NeverPushInheritance]
[DataField("noSpawn")]
[Obsolete("Use the HideSpawnMenu")]
public bool NoSpawn { get; private set; }
public bool HideSpawnMenu => Categories.Contains(HideCategory) || NoSpawn;
[DataField("placement")]
private EntityPlacementProperties PlacementProperties = new();

View File

@@ -1,4 +1,5 @@
using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic;
using System;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic;
namespace Robust.Shared.Prototypes;
@@ -11,7 +12,8 @@ namespace Robust.Shared.Prototypes;
/// This will be automatically validated by <see cref="ProtoIdSerializer{T}"/> if used in data fields.
/// </remarks>
/// <remarks><seealso cref="EntProtoId"/> for an <see cref="EntityPrototype"/> alias.</remarks>
public readonly record struct ProtoId<T>(string Id) where T : class, IPrototype
[Serializable]
public readonly record struct ProtoId<T>(string Id) : IEquatable<string>, IComparable<ProtoId<T>> where T : class, IPrototype
{
public static implicit operator string(ProtoId<T> protoId)
{
@@ -22,4 +24,19 @@ public readonly record struct ProtoId<T>(string Id) where T : class, IPrototype
{
return new ProtoId<T>(id);
}
public static implicit operator ProtoId<T>?(string? id)
{
return id == null ? default(ProtoId<T>?) : new ProtoId<T>(id);
}
public bool Equals(string? other)
{
return Id == other;
}
public int CompareTo(ProtoId<T> other)
{
return string.Compare(Id, other.Id, StringComparison.Ordinal);
}
}

View File

@@ -19,7 +19,7 @@
<PackageReference Include="Linguini.Bundle" Version="0.1.3" />
<PackageReference Include="SharpZstd.Interop" Version="1.5.2-beta2" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.Sodium" Version="0.2.1" PrivateAssets="compile" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.7" />
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.20348-rc2" PrivateAssets="compile" />
</ItemGroup>
<ItemGroup>

View File

@@ -112,14 +112,40 @@ public partial class SerializationManager
}, (node, this));
}
private SequenceDataNode PushInheritanceSequence(SequenceDataNode child, SequenceDataNode _)
private SequenceDataNode PushInheritanceSequence(SequenceDataNode child, SequenceDataNode parent)
{
return child; //todo implement different inheritancebehaviours for yamlfield
//todo implement different inheritancebehaviours for yamlfield
// I have NFI what this comment means.
var result = new SequenceDataNode(child.Count + parent.Count);
foreach (var entry in parent)
{
result.Add(entry);
}
foreach (var entry in child)
{
result.Add(entry);
}
return result;
}
private MappingDataNode PushInheritanceMapping(MappingDataNode child, MappingDataNode _)
private MappingDataNode PushInheritanceMapping(MappingDataNode child, MappingDataNode parent)
{
return child; //todo implement different inheritancebehaviours for yamlfield
//todo implement different inheritancebehaviours for yamlfield
// I have NFI what this comment means.
var result = new MappingDataNode(child.Count + parent.Count);
foreach (var (k, v) in parent)
{
result[k] = v;
}
foreach (var (k, v) in child)
{
result[k] = v;
}
return result;
}
private MappingDataNode PushInheritanceDefinition(MappingDataNode child, MappingDataNode parent,

View File

@@ -0,0 +1,42 @@
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.Markdown;
using Robust.Shared.Serialization.Markdown.Validation;
using Robust.Shared.Serialization.Markdown.Value;
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
using static Robust.Shared.Serialization.Manager.ISerializationManager;
namespace Robust.Shared.Serialization.TypeSerializers.Implementations;
/// <summary>
/// Serializer used automatically for <see cref="LocId"/> types.
/// </summary>
[TypeSerializer]
public sealed class LocIdSerializer : ITypeSerializer<LocId, ValueDataNode>, ITypeCopyCreator<LocId>
{
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node, IDependencyCollection dependencies, ISerializationContext? context = null)
{
var loc = dependencies.Resolve<ILocalizationManager>();
if (loc.HasString(node.Value))
return new ValidatedValueNode(node);
return new ErrorNode(node, $"No localization message found with id {node.Value}");
}
public LocId Read(ISerializationManager serializationManager, ValueDataNode node, IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context = null, InstantiationDelegate<LocId>? instanceProvider = null)
{
return new LocId(node.Value);
}
public DataNode Write(ISerializationManager serializationManager, LocId value, IDependencyCollection dependencies, bool alwaysWrite = false, ISerializationContext? context = null)
{
return new ValueDataNode(value);
}
public LocId CreateCopy(ISerializationManager serializationManager, LocId source, IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context = null)
{
return source;
}
}

View File

@@ -1,8 +1,10 @@
using System;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
namespace Robust.Shared.Utility
{
@@ -36,6 +38,67 @@ namespace Robust.Shared.Utility
throw new DebugAssertException();
}
[Conditional("DEBUG")]
[AssertionMethod]
public static void AssertEqual(object? objA, object? objB)
{
if (ReferenceEquals(objA, objB))
return;
if (objA == null || !objA.Equals(objB))
throw new DebugAssertException($"Expected: {objB ?? "null"} but was {objA ?? "null"}");
}
[Conditional("DEBUG")]
[AssertionMethod]
public static void AssertEqual(object? objA, object? objB, string message)
{
if (ReferenceEquals(objA, objB))
return;
if (objA == null || !objA.Equals(objB))
throw new DebugAssertException($"{message}\nExpected: {objB ?? "null"} but was {objA ?? "null"}");
}
[Conditional("DEBUG")]
[AssertionMethod]
public static void AssertNotEqual(object? objA, object? objB)
{
if (ReferenceEquals(objA, objB))
throw new DebugAssertException($"Expected: not {objB ?? "null"}");
if (objA == null || !objA.Equals(objB))
return;
throw new DebugAssertException($"Expected: not {objB}");
}
[Conditional("DEBUG")]
[AssertionMethod]
public static void AssertNotEqual(object? objA, object? objB, string message)
{
if (ReferenceEquals(objA, objB))
throw new DebugAssertException($"{message}\nExpected: not {objB ?? "null"}");
if (objA == null || !objA.Equals(objB))
return;
throw new DebugAssertException($"{message}\nExpected: not {objB}");
}
[Conditional("DEBUG")]
[AssertionMethod]
public static void AssertOwner(EntityUid? uid, IComponent component)
{
if (uid == null)
throw new DebugAssertException($"Null entity uid cannot own a component. Component: {component.GetType().Name}");
// Whenever .owner is removed this will need to be replaced by something.
// We need some way to ensure that people don't mix up uids & components when calling methods.
if (component.Owner != uid)
throw new DebugAssertException($"Entity {uid} is not the owner of the component. Component: {component.GetType().Name}");
}
/// <summary>
/// An assertion that will <see langword="throw" /> an exception if the
/// <paramref name="condition" /> is not true.

View File

@@ -43,14 +43,7 @@ namespace Robust.Shared.Utility
public bool Add(TKey key, TValue value)
{
InitializedCheck();
if (_index.TryGetValue(key, out var set))
{
return set.Add(value);
}
_index.Add(key, new HashSet<TValue> {value});
return true;
return _index.GetOrNew(key).Add(value);
}
/// <inheritdoc />

View File

@@ -223,13 +223,14 @@ namespace Robust.UnitTesting.Server
container.RegisterInstance<IConsoleHost>(new Mock<IConsoleHost>().Object); //Console is technically a frontend, we want to run headless
container.Register<IEntityManager, EntityManager>();
container.Register<EntityManager, EntityManager>();
container.Register<IMapManager, MapManager>();
container.Register<IMapManager, NetworkedMapManager>();
container.Register<INetworkedMapManager, NetworkedMapManager>();
container.Register<IMapManagerInternal, NetworkedMapManager>();
container.Register<ISerializationManager, SerializationManager>();
container.Register<IPrototypeManager, ServerPrototypeManager>();
container.Register<IComponentFactory, ComponentFactory>();
container.Register<IEntitySystemManager, EntitySystemManager>();
container.Register<IManifoldManager, CollisionManager>();
container.Register<IMapManagerInternal, MapManager>();
container.Register<INetManager, NetManager>();
container.Register<IAuthManager, AuthManager>();
container.Register<ITileDefinitionManager, TileDefinitionManager>();

View File

@@ -0,0 +1,131 @@
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Network;
namespace Robust.UnitTesting.Shared.GameState;
public sealed partial class VisibilityTest : RobustIntegrationTest
{
/// <summary>
/// This tests checks that entity visibility masks are recursively applied to children.
/// </summary>
[Test]
public async Task UnknownEntityTest()
{
var server = StartServer();
var xforms = server.System<SharedTransformSystem>();
var vis = server.System<VisibilitySystem>();
const int RequiredMask = 1;
// All entities need to have this mask set ... which defeat the whole point of that bit?
// Spawn a stack of entities
int N = 6;
var ents = new EntityUid[N];
var metaComp = new MetaDataComponent[N];
var visComp = new VisibilityComponent[N];
await server.WaitPost(() =>
{
for (int i = 0; i < N; i++)
{
var ent = server.EntMan.Spawn();
ents[i] = ent;
metaComp[i] = server.EntMan.GetComponent<MetaDataComponent>(ent);
visComp[i] = server.EntMan.AddComponent<VisibilityComponent>(ent);
vis.AddLayer(ent, visComp[i], 1 << i);
if (i > 0)
xforms.SetParent(ent, ents[i - 1]);
}
});
// Each entity's visibility mask should include the parent's mask
var mask = RequiredMask;
for (int i = 0; i < N; i++)
{
mask |= 1 << i;
var meta = metaComp[i];
Assert.That(meta.VisibilityMask, Is.EqualTo(mask));
}
// Adding a layer to the root entity's mask will apply it to all children
var extraMask = 1 << (N + 1);
mask = RequiredMask | extraMask;
vis.AddLayer(ents[0], visComp[0], extraMask);
for (int i = 0; i < N; i++)
{
mask |= 1 << i;
var meta = metaComp[i];
Assert.That(meta.VisibilityMask, Is.EqualTo(mask));
}
// Removing the removes it from all children.
vis.RemoveLayer(ents[0], visComp[0], extraMask);
mask = RequiredMask;
for (int i = 0; i < N; i++)
{
mask |= 1 << i;
var meta = metaComp[i];
Assert.That(meta.VisibilityMask, Is.EqualTo(mask));
}
// Detaching an entity from the stack updates it, and it's children's mask
var split = N / 2;
await server.WaitPost(() => xforms.SetParent(ents[split], EntityUid.Invalid));
mask = RequiredMask;
for (int i = 0; i < split; i++)
{
mask |= 1 << i;
var meta = metaComp[i];
Assert.That(meta.VisibilityMask, Is.EqualTo(mask));
}
mask = RequiredMask;
for (int i = split; i < N; i++)
{
mask |= 1 << i;
var meta = metaComp[i];
Assert.That(meta.VisibilityMask, Is.EqualTo(mask));
}
// Re-attaching the entity also updates the masks.
await server.WaitPost(() => xforms.SetParent(ents[split], ents[split-1]));
mask = RequiredMask;
for (int i = 0; i < N; i++)
{
mask |= 1 << i;
var meta = metaComp[i];
Assert.That(meta.VisibilityMask, Is.EqualTo(mask));
}
// Setting a mask on a child does not propagate upwards, only downwards
vis.AddLayer(ents[split], visComp[split], extraMask);
mask = RequiredMask;
for (int i = 0; i < split; i++)
{
mask |= 1 << i;
var meta = metaComp[i];
Assert.That(meta.VisibilityMask, Is.EqualTo(mask));
}
mask |= extraMask;
for (int i = split; i < N; i++)
{
mask |= 1 << i;
var meta = metaComp[i];
Assert.That(meta.VisibilityMask, Is.EqualTo(mask));
}
}
}