Compare commits

...

47 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
ElectroJr
88d3168913 Version: 161.0.0 2023-09-17 13:56:34 -04:00
Leon Friedrich
902519093c Add PVS debug command (#4422) 2023-09-18 03:49:57 +10:00
Leon Friedrich
366266a8ae Fix light animations not working (#4413) 2023-09-18 03:49:47 +10:00
Leon Friedrich
a22cce7783 Fix metadata assert (#4419) 2023-09-17 17:18:44 +10:00
metalgearsloth
e5e738b8cd Maybe fix heisentest (#4418) 2023-09-17 16:13:37 +10:00
metalgearsloth
b8f6e83473 Use stackalloc Span<Vector2> in ComputeHull (#4417) 2023-09-17 12:41:53 +10:00
Artur
c5fb186c57 Add missing InvariantCulture to AngleTypeParser (#4411) 2023-09-17 11:32:01 +10:00
Leon Friedrich
131d7f5422 Add string formatting for client-side NetEntity ids (#4415) 2023-09-17 11:29:54 +10:00
Leon Friedrich
217996f1ed Use ToPrettyString() in state error logs. (#4416) 2023-09-17 11:29:02 +10:00
DrSmugleaf
fc718d68a5 Fix IClydeWindow resized xmldoc (#4414) 2023-09-17 11:28:43 +10:00
Leon Friedrich
d7d9578803 Mark EntPrototId as NetSerializable (#4412) 2023-09-17 11:25:40 +10:00
metalgearsloth
9bbeb54569 Version: 160.1.0 2023-09-17 11:20:03 +10:00
metalgearsloth
1ea7071ffb Backport some arch comp net changes (#4408)
Co-authored-by: ElectroJr <leonsfriedrich@gmail.com>
2023-09-17 11:03:11 +10:00
61 changed files with 1208 additions and 423 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,112 @@ 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
* Point light animations now need to use different component fields in order to animate the lights. `Enabled` should be replaced with `AnimatedEnable` and `Radius` should be replaced with `AnimatedRadius`
### New features
* EntProtoId is now net-serializable
* Added print_pvs_ack command to debug PVS issues.
### Bugfixes
* Fixes AngleTypeParser not using InvariantCulture
* Fixed a bug that was causing `MetaDataComponent.LastComponentRemoved` to be updated improperly.
### Other
* The string representation of client-side entities now looks nicer and simply uses a 'c' prefix.
## 160.1.0
### New features
* Add optional MetaDataComponent args to Entitymanager methods.
### Internal
* Move _netComponents onto MetaDataComponent.
* Remove some component resolves internally on adding / removing components.
## 160.0.2
### Other

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

@@ -1,7 +1,6 @@
using Robust.Client.Timing;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Utility;
using System;
using System.Collections.Generic;
@@ -54,7 +53,7 @@ public sealed class ClientDirtySystem : EntitySystem
var uid = args.BaseArgs.Owner;
var comp = args.BaseArgs.Component;
if (!_timing.InPrediction || !comp.NetSyncEnabled || IsClientSide(uid))
if (!_timing.InPrediction || !comp.NetSyncEnabled || IsClientSide(uid, args.Meta))
return;
// Was this component added during prediction? If yes, then there is no need to re-add it when resetting.

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;
@@ -47,7 +48,7 @@ namespace Robust.Client.GameStates
= new();
// Game state dictionaries that get used every tick.
private readonly Dictionary<EntityUid, (NetEntity NetEntity, bool EnteringPvs, GameTick LastApplied, EntityState? curState, EntityState? nextState)> _toApply = new();
private readonly Dictionary<EntityUid, (NetEntity NetEntity, MetaDataComponent Meta, bool EnteringPvs, GameTick LastApplied, EntityState? curState, EntityState? nextState)> _toApply = new();
private readonly Dictionary<NetEntity, EntityState> _toCreate = new();
private readonly Dictionary<ushort, (IComponent Component, ComponentState? curState, ComponentState? nextState)> _compStateWork = new();
private readonly Dictionary<EntityUid, HashSet<Type>> _pendingReapplyNetStates = new();
@@ -497,11 +498,7 @@ namespace Robust.Client.GameStates
countReset += 1;
var netComps = _entityManager.GetNetComponentsOrNull(entity);
if (netComps == null)
continue;
foreach (var (netId, comp) in netComps.Value)
foreach (var (netId, comp) in meta.NetComponents)
{
if (!comp.NetSyncEnabled)
continue;
@@ -549,13 +546,13 @@ namespace Robust.Client.GameStates
{
foreach (var netId in netIds)
{
if (_entities.HasComponent(entity, netId))
if (meta.NetComponents.ContainsKey(netId))
continue;
if (!last.TryGetValue(netId, out var state))
continue;
var comp = _entityManager.AddComponent(entity, netId);
var comp = _entityManager.AddComponent(entity, netId, meta);
if (_sawmill.Level <= LogLevel.Debug)
_sawmill.Debug($" A component was removed: {comp.GetType()}");
@@ -597,11 +594,11 @@ namespace Robust.Client.GameStates
foreach (var netEntity in createdEntities)
{
var createdEntity = _entityManager.GetEntity(netEntity);
var (createdEntity, meta) = _entityManager.GetEntityData(netEntity);
var compData = new Dictionary<ushort, ComponentState>();
outputData.Add(netEntity, compData);
foreach (var (netId, component) in _entityManager.GetNetComponents(createdEntity))
foreach (var (netId, component) in meta.NetComponents)
{
if (!component.NetSyncEnabled)
continue;
@@ -692,9 +689,9 @@ namespace Robust.Client.GameStates
var uid = _entities.CreateEntity(metaState.PrototypeId);
_toCreate.Add(es.NetEntity, es);
_toApply.Add(uid, (es.NetEntity, false, GameTick.Zero, es, null));
var newMeta = metas.GetComponent(uid);
_toApply.Add(uid, (es.NetEntity, newMeta, false, GameTick.Zero, es, null));
// Client creates a client-side net entity for the newly created entity.
// We need to clear this mapping before assigning the real net id.
@@ -742,7 +739,7 @@ namespace Robust.Client.GameStates
continue;
}
_toApply.Add(uid, (es.NetEntity, isEnteringPvs, meta.LastStateApplied, es, null));
_toApply.Add(uid, (es.NetEntity, meta, isEnteringPvs, meta.LastStateApplied, es, null));
meta.LastStateApplied = curState.ToSequence;
}
@@ -756,34 +753,36 @@ namespace Robust.Client.GameStates
{
foreach (var es in nextState.EntityStates.Span)
{
if (!_entityManager.TryGetEntity(es.NetEntity, out var uid))
if (!_entityManager.TryGetEntityData(es.NetEntity, out var uid, out var meta))
continue;
DebugTools.Assert(metas.HasComponent(uid));
// Does the next state actually have any future information about this entity that could be used for interpolation?
if (es.EntityLastModified != nextState.ToSequence)
continue;
if (_toApply.TryGetValue(uid.Value, out var state))
_toApply[uid.Value] = (es.NetEntity, 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, 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] = (_entityManager.GetNetEntity(uid, 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);
@@ -793,7 +792,7 @@ namespace Robust.Client.GameStates
{
foreach (var (entity, data) in _toApply)
{
HandleEntityState(entity, data.NetEntity, _entities.EventBus, data.curState,
HandleEntityState(entity, data.NetEntity, data.Meta, _entities.EventBus, data.curState,
data.nextState, data.LastApplied, curState.ToSequence, data.EnteringPvs);
if (!data.EnteringPvs)
@@ -1133,7 +1132,7 @@ namespace Robust.Client.GameStates
#endif
}
private void HandleEntityState(EntityUid uid, NetEntity netEntity, IEventBus bus, EntityState? curState,
private void HandleEntityState(EntityUid uid, NetEntity netEntity, MetaDataComponent meta, IEventBus bus, EntityState? curState,
EntityState? nextState, GameTick lastApplied, GameTick toTick, bool enteringPvs)
{
_compStateWork.Clear();
@@ -1142,7 +1141,7 @@ namespace Robust.Client.GameStates
if (curState?.NetComponents != null)
{
RemQueue<Component> toRemove = new();
foreach (var (id, comp) in _entities.GetNetComponents(uid))
foreach (var (id, comp) in meta.NetComponents)
{
if (comp.NetSyncEnabled && !curState.NetComponents.Contains(id))
toRemove.Add(comp);
@@ -1150,7 +1149,7 @@ namespace Robust.Client.GameStates
foreach (var comp in toRemove)
{
_entities.RemoveComponent(uid, comp);
_entities.RemoveComponent(uid, comp, meta);
}
}
@@ -1163,12 +1162,11 @@ namespace Robust.Client.GameStates
// the entity. most notably, all entities will have been ejected from their containers.
foreach (var (id, state) in _processor.GetLastServerStates(netEntity))
{
if (!_entityManager.TryGetComponent(uid, id, out var comp))
if (!meta.NetComponents.TryGetValue(id, out var comp))
{
comp = _compFactory.GetComponent(id);
var newComp = (Component)comp;
newComp.Owner = uid;
_entityManager.AddComponent(uid, newComp, true);
comp = (Component) _compFactory.GetComponent(id);
comp.Owner = uid;
_entityManager.AddComponent(uid, comp, true, metadata: meta);
}
_compStateWork[id] = (comp, state, null);
@@ -1178,12 +1176,11 @@ namespace Robust.Client.GameStates
{
foreach (var compChange in curState.ComponentChanges.Span)
{
if (!_entityManager.TryGetComponent(uid, compChange.NetID, out var comp))
if (!meta.NetComponents.TryGetValue(compChange.NetID, out var comp))
{
comp = _compFactory.GetComponent(compChange.NetID);
var newComp = (Component)comp;
newComp.Owner = uid;
_entityManager.AddComponent(uid, newComp, true);
comp = (Component) _compFactory.GetComponent(compChange.NetID);
comp.Owner = uid;
_entityManager.AddComponent(uid, comp, true, metadata:meta);
}
else if (compChange.LastModifiedTick <= lastApplied && lastApplied != GameTick.Zero)
continue;
@@ -1199,17 +1196,20 @@ namespace Robust.Client.GameStates
if (compState.LastModifiedTick != toTick + 1)
continue;
if (!_entityManager.TryGetComponent(uid, compState.NetID, out var comp))
if (!meta.NetComponents.TryGetValue(compState.NetID, out var comp))
{
// The component can be null here due to interp, because the NEXT state will have a new
// component, but the component does not yet exist.
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);
}
}
@@ -1226,14 +1226,19 @@ namespace Robust.Client.GameStates
if (netId == null)
continue;
if (_compStateWork.ContainsKey(netId.Value) ||
!_entityManager.TryGetComponent(uid, type, 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);
}
}
@@ -1247,10 +1252,10 @@ namespace Robust.Client.GameStates
catch (Exception e)
{
#if EXCEPTION_TOLERANCE
_sawmill.Error($"Failed to apply comp state: entity={comp.Owner}, comp={comp.GetType()}");
_runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(HandleEntityState)}");
_sawmill.Error($"Failed to apply comp state: entity={_entities.ToPrettyString(uid)}, comp={comp.GetType()}");
_runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(HandleEntityState)}");
#else
_sawmill.Error($"Failed to apply comp state: entity={uid}, comp={comp.GetType()}");
_sawmill.Error($"Failed to apply comp state: entity={_entities.ToPrettyString(uid)}, comp={comp.GetType()}");
throw;
#endif
}
@@ -1398,12 +1403,11 @@ namespace Robust.Client.GameStates
foreach (var (id, state) in lastState)
{
if (!_entityManager.TryGetComponent(uid, id, out var comp))
if (!meta.NetComponents.TryGetValue(id, out var comp))
{
comp = _compFactory.GetComponent(id);
var newComp = (Component)comp;
newComp.Owner = uid;
_entityManager.AddComponent(uid, newComp, true);
comp = (Component) _compFactory.GetComponent(id);
comp.Owner = uid;
_entityManager.AddComponent(uid, comp, true, meta);
}
var handleState = new ComponentHandleState(state, null);
@@ -1412,7 +1416,7 @@ namespace Robust.Client.GameStates
// ensure we don't have any extra components
RemQueue<Component> toRemove = new();
foreach (var (id, comp) in _entities.GetNetComponents(uid))
foreach (var (id, comp) in meta.NetComponents)
{
if (comp.NetSyncEnabled && !lastState.ContainsKey(id))
toRemove.Add(comp);

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

@@ -38,8 +38,7 @@ namespace Robust.Client.Graphics
event Action<WindowDestroyedEventArgs> Destroyed;
/// <summary>
/// Raised when the window has been definitively closed.
/// This means the window must not be used anymore (it is disposed).
/// Raised when the window has been resized.
/// </summary>
event Action<WindowResizedEventArgs> Resized;
}

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

@@ -879,7 +879,7 @@ public sealed class MapLoaderSystem : EntitySystem
return;
}
foreach (var (netId, component) in EntityManager.GetNetComponents(entity))
foreach (var component in metadata.NetComponents.Values)
{
var compName = _factory.GetComponentName(component.GetType());

View File

@@ -7,6 +7,7 @@ namespace Robust.Server.GameObjects;
public sealed class ServerMetaDataSystem : MetaDataSystem
{
[Dependency] private readonly PvsSystem _pvsSystem = default!;
private EntityQuery<MetaDataComponent> _mQuery;
public override void Initialize()
{
@@ -16,6 +17,7 @@ public sealed class ServerMetaDataSystem : MetaDataSystem
EntityManager.ComponentAdded += OnComponentAdded;
EntityManager.ComponentRemoved += OnComponentRemoved;
_mQuery = GetEntityQuery<MetaDataComponent>();
}
public override void Shutdown()
@@ -45,7 +47,7 @@ public sealed class ServerMetaDataSystem : MetaDataSystem
if (obj.Terminating || !removed.NetSyncEnabled || (!removed.SessionSpecific && !removed.SendOnlyToOwner))
return;
foreach (var (_, comp) in EntityManager.GetNetComponents(obj.BaseArgs.Owner))
foreach (var comp in obj.Meta.NetComponents.Values)
{
if (comp.LifeStage >= ComponentLifeStage.Removing)
continue;
@@ -55,7 +57,7 @@ public sealed class ServerMetaDataSystem : MetaDataSystem
}
// remove the flag
MetaData(obj.BaseArgs.Owner).Flags &= ~MetaDataFlags.SessionSpecific;
obj.Meta.Flags &= ~MetaDataFlags.SessionSpecific;
}
/// <summary>
@@ -67,19 +69,10 @@ public sealed class ServerMetaDataSystem : MetaDataSystem
if ((meta.Flags & MetaDataFlags.SessionSpecific) == 0)
return;
foreach (var (_, comp) in EntityManager.GetNetComponents(uid))
foreach (var (_, comp) in meta.NetComponents)
{
if (comp.SessionSpecific || comp.SendOnlyToOwner)
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

@@ -139,9 +139,6 @@ namespace Robust.Server.GameObjects
{
_networkManager.RegisterNetMessage<MsgEntity>(HandleEntityNetworkMessage);
// For syncing component deletions.
ComponentRemoved += OnComponentRemoved;
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
_configurationManager.OnValueChanged(CVars.NetLogLateMsg, b => _logLateMsgs = b, true);
@@ -168,15 +165,6 @@ namespace Robust.Server.GameObjects
return _lastProcessedSequencesCmd[session];
}
private void OnComponentRemoved(RemovedComponentEventArgs e)
{
if (e.Terminating || !e.BaseArgs.Component.NetSyncEnabled)
return;
if (TryGetComponent(e.BaseArgs.Owner, out MetaDataComponent? meta))
meta.LastComponentRemoved = _gameTiming.CurTick;
}
/// <inheritdoc />
public void SendSystemNetworkMessage(EntityEventArgs message, bool recordReplay = true)
{

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:
@@ -313,7 +315,7 @@ internal sealed partial class PvsSystem : EntitySystem
// since elements are cached grid-/map-relative, we don't need to update a given grids/maps children
DebugTools.Assert(!_mapManager.IsGrid(uid) && !_mapManager.IsMap(uid));
var indices = PVSCollection<EntityUid>.GetChunkIndices(coordinates.Position);
var indices = PVSCollection<NetEntity>.GetChunkIndices(coordinates.Position);
if (xform.GridUid != null)
_entityPvsCollection.UpdateIndex(metadata.NetEntity, xform.GridUid.Value, indices, forceDirty: forceDirty);
else
@@ -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++;
}
@@ -1204,7 +1214,7 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
bool sendCompList = meta.LastComponentRemoved > fromTick;
HashSet<ushort>? netComps = sendCompList ? new() : null;
foreach (var (netId, component) in EntityManager.GetNetComponents(entityUid))
foreach (var (netId, component) in meta.NetComponents)
{
if (!component.NetSyncEnabled)
continue;
@@ -1253,7 +1263,7 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
HashSet<ushort> netComps = new();
foreach (var (netId, component) in EntityManager.GetNetComponents(entityUid))
foreach (var (netId, component) in meta.NetComponents)
{
if (!component.NetSyncEnabled)
continue;

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

@@ -24,6 +24,7 @@ using SharpZstd.Interop;
using Microsoft.Extensions.ObjectPool;
using Prometheus;
using Robust.Server.Replays;
using Robust.Shared.Console;
using Robust.Shared.Map.Components;
using Robust.Shared.Players;
@@ -48,6 +49,7 @@ namespace Robust.Server.GameStates
[Dependency] private readonly IServerEntityNetworkManager _entityNetworkManager = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IParallelManager _parallelMgr = default!;
[Dependency] private readonly IConsoleHost _conHost = default!;
private static readonly Histogram _usageHistogram = Metrics.CreateHistogram("robust_game_state_update_usage",
"Amount of time spent processing different parts of the game state update", new HistogramConfiguration
@@ -83,6 +85,25 @@ namespace Robust.Server.GameStates
_parallelMgr.AddAndInvokeParallelCountChanged(ResetParallelism);
_cfg.OnValueChanged(CVars.NetPVSCompressLevel, _ => ResetParallelism(), true);
// temporary command for debugging PVS bugs.
_conHost.RegisterCommand("print_pvs_ack", PrintPvsAckInfo);
}
private void PrintPvsAckInfo(IConsoleShell shell, string argstr, string[] args)
{
var ack = _pvs.PlayerData.Min(x => x.Value.LastReceivedAck);
var players = _pvs.PlayerData
.Where(x => x.Value.LastReceivedAck == ack)
.Select(x => x.Key)
.Select(x => $"{x.Name} ({_entityManager.ToPrettyString(x.AttachedEntity)})");
shell.WriteLine($@"Current tick: {_gameTiming.CurTick}
Stored oldest acked tick: {_lastOldestAck}
Deletion history size: {_pvs.EntityPVSCollection.GetDeletedIndices(GameTick.First)?.Count ?? 0}
Actual oldest: {ack}
Oldest acked clients: {string.Join(", ", players)}
");
}
private void ResetParallelism()
@@ -147,15 +168,6 @@ namespace Robust.Server.GameStates
/// <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
@@ -195,14 +207,21 @@ namespace Robust.Server.GameStates
_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

@@ -46,10 +46,13 @@ namespace Robust.Shared.GameObjects
public readonly bool Terminating;
public RemovedComponentEventArgs(ComponentEventArgs baseArgs, bool terminating)
public readonly MetaDataComponent Meta;
public RemovedComponentEventArgs(ComponentEventArgs baseArgs, bool terminating, MetaDataComponent meta)
{
BaseArgs = baseArgs;
Terminating = terminating;
Meta = meta;
}
}

View File

@@ -1,9 +1,11 @@
using System;
using Robust.Shared.Animations;
using Robust.Shared.GameStates;
using Robust.Shared.Maths;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
using System.Numerics;
using Robust.Shared.IoC;
namespace Robust.Shared.GameObjects
{
@@ -39,16 +41,35 @@ namespace Robust.Shared.GameObjects
[Access(typeof(SharedPointLightSystem))]
[DataField("enabled")]
public bool Enabled = true;
// TODO ECS animations
[Animatable]
public bool Enabled { get; set; } = true;
public bool AnimatedEnable
{
[Obsolete]
get => Enabled;
[Obsolete]
set => IoCManager.Resolve<IEntityManager>().System<SharedPointLightSystem>().SetEnabled(Owner, value, this);
}
// TODO ECS animations
[Animatable]
public float AnimatedRadius
{
[Obsolete]
get => Radius;
[Obsolete]
set => IoCManager.Resolve<IEntityManager>().System<SharedPointLightSystem>().SetRadius(Owner, value, this);
}
/// <summary>
/// How far the light projects.
/// </summary>
[DataField("radius")]
[Access(typeof(SharedPointLightSystem))]
[Animatable]
public float Radius { get; set; } = 5f;
public float Radius = 5f;
[ViewVariables]
public bool ContainerOccluded;

View File

@@ -8,6 +8,7 @@ using Robust.Shared.Timing;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
using System;
using System.Collections.Generic;
namespace Robust.Shared.GameObjects
{
@@ -63,6 +64,12 @@ namespace Robust.Shared.GameObjects
[DataField("desc")] internal string? _entityDescription;
internal EntityPrototype? _entityPrototype;
/// <summary>
/// The components attached to the entity that are currently networked.
/// </summary>
[ViewVariables]
internal readonly Dictionary<ushort, Component> NetComponents = new();
/// <summary>
/// Network identifier for this entity.
/// </summary>
@@ -87,7 +94,8 @@ namespace Robust.Shared.GameObjects
public GameTick LastStateApplied { get; internal set; } = GameTick.Zero;
/// <summary>
/// This is the most recent tick at which some component was removed from this entity.
/// This is the most recent tick at which a networked component was removed from this entity.
/// Currently only reliable server-side, client side prediction may cause the value to be wrong.
/// </summary>
[ViewVariables]
public GameTick LastComponentRemoved { get; internal set; } = GameTick.Zero;
@@ -181,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;
@@ -35,9 +36,6 @@ namespace Robust.Shared.GameObjects
private static readonly ComponentState DefaultComponentState = new();
private readonly Dictionary<EntityUid, Dictionary<ushort, Component>> _netComponents
= new(EntityCapacity);
private readonly Dictionary<Type, Dictionary<EntityUid, Component>> _entTraitDict
= new();
@@ -70,7 +68,6 @@ namespace Robust.Shared.GameObjects
/// </summary>
public void ClearComponents()
{
_netComponents.Clear();
_entCompIndex.Clear();
_deleteSet.Clear();
foreach (var dict in _entTraitDict.Values)
@@ -175,13 +172,13 @@ namespace Robust.Shared.GameObjects
}
}
public Component AddComponent(EntityUid uid, ushort netId)
public Component AddComponent(EntityUid uid, ushort netId, MetaDataComponent? meta = null)
{
var newComponent = (Component)_componentFactory.GetComponent(netId);
#pragma warning disable CS0618 // Type or member is obsolete
newComponent.Owner = uid;
#pragma warning restore CS0618 // Type or member is obsolete
AddComponent(uid, newComponent);
AddComponent(uid, newComponent, metadata: meta);
return newComponent;
}
@@ -249,7 +246,7 @@ namespace Robust.Shared.GameObjects
}
/// <inheritdoc />
public void AddComponent<T>(EntityUid uid, T component, bool overwrite = false) where T : Component
public void AddComponent<T>(EntityUid uid, T component, bool overwrite = false, MetaDataComponent? metadata = null) where T : Component
{
if (!uid.IsValid() || !EntityExists(uid))
throw new ArgumentException($"Entity {uid} is not valid.", nameof(uid));
@@ -267,35 +264,51 @@ namespace Robust.Shared.GameObjects
}
#pragma warning restore CS0618 // Type or member is obsolete
AddComponentInternal(uid, component, overwrite, false);
AddComponentInternal(uid, component, overwrite, false, metadata);
}
private void AddComponentInternal<T>(EntityUid uid, T component, bool overwrite, bool skipInit) where T : Component
private void AddComponentInternal<T>(EntityUid uid, T component, bool overwrite, bool skipInit, MetaDataComponent? metadata = null) where T : Component
{
// 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 = null) where T : Component
{
// We can't use typeof(T) here in case T is just Component
DebugTools.Assert(component is MetaDataComponent ||
GetComponent<MetaDataComponent>(uid).EntityLifeStage < EntityLifeStage.Terminating,
(metadata ?? MetaQuery.GetComponent(uid)).EntityLifeStage < EntityLifeStage.Terminating,
$"Attempted to add a {component.GetType().Name} component to an entity ({ToPrettyString(uid)}) while it is terminating");
// get interface aliases for mapping
var reg = _componentFactory.GetRegistration(component);
// Check that there is no existing component.
var type = reg.Idx;
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);
// 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
@@ -303,14 +316,8 @@ namespace Robust.Shared.GameObjects
{
// the main comp grid keeps this in sync
var netId = reg.NetID.Value;
if (!_netComponents.TryGetValue(uid, out var netSet))
{
netSet = new Dictionary<ushort, Component>(NetComponentCapacity);
_netComponents.Add(uid, netSet);
}
netSet.Add(netId, component);
metadata ??= MetaQuery.GetComponentInternal(uid);
metadata.NetComponents.Add(netId, component);
}
else
{
@@ -326,7 +333,7 @@ namespace Robust.Shared.GameObjects
if (skipInit)
return;
var metadata = GetComponent<MetaDataComponent>(uid);
metadata ??= MetaQuery.GetComponentInternal(uid);
if (!metadata.EntityInitialized && !metadata.EntityInitializing)
return;
@@ -345,45 +352,48 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool RemoveComponent<T>(EntityUid uid)
public bool RemoveComponent<T>(EntityUid uid, MetaDataComponent? meta = null)
{
return RemoveComponent(uid, typeof(T));
return RemoveComponent(uid, typeof(T), meta);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool RemoveComponent(EntityUid uid, Type type)
public bool RemoveComponent(EntityUid uid, Type type, MetaDataComponent? meta = null)
{
if (!TryGetComponent(uid, type, out var comp))
return false;
RemoveComponentImmediate((Component)comp, uid, false);
RemoveComponentImmediate((Component)comp, uid, false, meta);
return true;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool RemoveComponent(EntityUid uid, ushort netId)
public bool RemoveComponent(EntityUid uid, ushort netId, MetaDataComponent? meta = null)
{
if (!TryGetComponent(uid, netId, out var comp))
if (!MetaQuery.Resolve(uid, ref meta))
return false;
RemoveComponentImmediate((Component)comp, uid, false);
if (!TryGetComponent(uid, netId, out var comp, meta))
return false;
RemoveComponentImmediate((Component)comp, uid, false, meta);
return true;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveComponent(EntityUid uid, IComponent component)
public void RemoveComponent(EntityUid uid, IComponent component, MetaDataComponent? meta = null)
{
RemoveComponent(uid, (Component)component);
RemoveComponent(uid, (Component)component, meta);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveComponent(EntityUid uid, Component component)
public void RemoveComponent(EntityUid uid, Component component, MetaDataComponent? meta = null)
{
RemoveComponentImmediate(component, uid, false);
RemoveComponentImmediate(component, uid, false, meta);
}
/// <inheritdoc />
@@ -405,9 +415,12 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool RemoveComponentDeferred(EntityUid uid, ushort netId)
public bool RemoveComponentDeferred(EntityUid uid, ushort netId, MetaDataComponent? meta = null)
{
if (!TryGetComponent(uid, netId, out var comp))
if (!MetaQuery.Resolve(uid, ref meta))
return false;
if (!TryGetComponent(uid, netId, out var comp, meta))
return false;
RemoveComponentDeferred((Component)comp, uid, false);
@@ -443,22 +456,28 @@ namespace Robust.Shared.GameObjects
}
/// <inheritdoc />
public void RemoveComponents(EntityUid uid)
public void RemoveComponents(EntityUid uid, MetaDataComponent? meta = null)
{
if (!MetaQuery.Resolve(uid, ref meta))
return;
foreach (var comp in InSafeOrder(_entCompIndex[uid]))
{
RemoveComponentImmediate(comp, uid, false);
RemoveComponentImmediate(comp, uid, false, meta);
}
}
/// <inheritdoc />
public void DisposeComponents(EntityUid uid)
public void DisposeComponents(EntityUid uid, MetaDataComponent? meta = null)
{
if (!MetaQuery.Resolve(uid, ref meta))
return;
foreach (var comp in InSafeOrder(_entCompIndex[uid]))
{
try
{
RemoveComponentImmediate(comp, uid, true);
RemoveComponentImmediate(comp, uid, true, meta);
}
catch (Exception)
{
@@ -467,7 +486,6 @@ namespace Robust.Shared.GameObjects
}
_entCompIndex.Remove(uid);
_netComponents.Remove(uid);
}
private void RemoveComponentDeferred(Component component, EntityUid uid, bool terminating)
@@ -508,7 +526,8 @@ namespace Robust.Shared.GameObjects
#endif
}
private void RemoveComponentImmediate(Component component, EntityUid uid, bool terminating)
private void RemoveComponentImmediate(Component component, EntityUid uid, bool terminating,
MetaDataComponent? meta)
{
if (component.Deleted)
{
@@ -541,10 +560,7 @@ namespace Robust.Shared.GameObjects
_runtimeLog.LogException(e, nameof(RemoveComponentImmediate));
}
#endif
var eventArgs = new RemovedComponentEventArgs(new ComponentEventArgs(component, uid), terminating);
ComponentRemoved?.Invoke(eventArgs);
_eventBus.OnComponentRemoved(eventArgs);
DeleteComponent(uid, component, terminating);
DeleteComponent(uid, component, terminating, meta);
}
/// <inheritdoc />
@@ -579,30 +595,34 @@ namespace Robust.Shared.GameObjects
_runtimeLog.LogException(e, nameof(CullRemovedComponents));
}
#endif
var eventArgs = new RemovedComponentEventArgs(new ComponentEventArgs(component, uid), false);
ComponentRemoved?.Invoke(eventArgs);
_eventBus.OnComponentRemoved(eventArgs);
DeleteComponent(uid, component, false);
}
_deleteSet.Clear();
}
private void DeleteComponent(EntityUid entityUid, Component component, bool terminating)
private void DeleteComponent(EntityUid entityUid, Component component, bool terminating, MetaDataComponent? metadata = null)
{
var type = component.GetType();
var reg = _componentFactory.GetRegistration(type);
if (!MetaQuery.ResolveInternal(entityUid, ref metadata))
return;
if (!terminating && reg.NetID != null && _netComponents.TryGetValue(entityUid, out var netSet))
var eventArgs = new RemovedComponentEventArgs(new ComponentEventArgs(component, entityUid), false, metadata);
ComponentRemoved?.Invoke(eventArgs);
_eventBus.OnComponentRemoved(eventArgs);
var reg = _componentFactory.GetRegistration(component);
DebugTools.Assert(component.Networked == (reg.NetID != null));
if (!terminating && reg.NetID != null)
{
if (netSet.Count == 1)
_netComponents.Remove(entityUid);
else
netSet.Remove(reg.NetID.Value);
if (!metadata.NetComponents.Remove(reg.NetID.Value))
_sawmill.Error($"Entity {ToPrettyString(entityUid, metadata)} did not have {component.GetType().Name} in its networked component dictionary during component deletion.");
if (component.NetSyncEnabled)
DirtyEntity(entityUid);
{
DirtyEntity(entityUid, metadata);
metadata.LastComponentRemoved = _gameTiming.CurTick;
}
}
_entTraitArray[reg.Idx.Value].Remove(entityUid);
@@ -610,6 +630,10 @@ namespace Robust.Shared.GameObjects
// TODO if terminating the entity, maybe defer this?
// _entCompIndex.Remove(uid) gets called later on anyways.
_entCompIndex.Remove(entityUid, component);
DebugTools.Assert(_netMan.IsClient // Client side prediction can set LastComponentRemoved to some future tick,
|| metadata.EntityLastModifiedTick >= metadata.LastComponentRemoved);
}
/// <inheritdoc />
@@ -651,23 +675,25 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasComponent(EntityUid uid, ushort netId)
public bool HasComponent(EntityUid uid, ushort netId, MetaDataComponent? meta = null)
{
return _netComponents.TryGetValue(uid, out var netSet)
&& netSet.ContainsKey(netId);
if (!MetaQuery.Resolve(uid, ref meta))
return false;
return meta.NetComponents.ContainsKey(netId);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasComponent(EntityUid? uid, ushort netId)
public bool HasComponent(EntityUid? uid, ushort netId, MetaDataComponent? meta = null)
{
if (!uid.HasValue)
{
DebugTools.AssertNull(meta);
return false;
}
return _netComponents.TryGetValue(uid.Value, out var netSet)
&& netSet.ContainsKey(netId);
return HasComponent(uid.Value, netId, meta);
}
/// <inheritdoc />
@@ -752,9 +778,9 @@ namespace Robust.Shared.GameObjects
}
/// <inheritdoc />
public IComponent GetComponent(EntityUid uid, ushort netId)
public IComponent GetComponent(EntityUid uid, ushort netId, MetaDataComponent? meta = null)
{
return _netComponents[uid][netId];
return (meta ?? MetaQuery.GetComponentInternal(uid)).NetComponents[netId];
}
/// <inheritdoc />
@@ -763,7 +789,7 @@ namespace Robust.Shared.GameObjects
var dict = _entTraitArray[type.Value];
if (dict.TryGetValue(uid, out var comp))
{
return comp;
return comp;
}
throw new KeyNotFoundException($"Entity {uid} does not have a component of type {type}");
@@ -868,10 +894,10 @@ namespace Robust.Shared.GameObjects
}
/// <inheritdoc />
public bool TryGetComponent(EntityUid uid, ushort netId, [MaybeNullWhen(false)] out IComponent component)
public bool TryGetComponent(EntityUid uid, ushort netId, [MaybeNullWhen(false)] out IComponent component, MetaDataComponent? meta = null)
{
if (_netComponents.TryGetValue(uid, out var netSet)
&& netSet.TryGetValue(netId, out var comp))
if (MetaQuery.TryGetComponentInternal(uid, out var metadata)
&& metadata.NetComponents.TryGetValue(netId, out var comp))
{
component = comp;
return true;
@@ -883,23 +909,16 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public bool TryGetComponent([NotNullWhen(true)] EntityUid? uid, ushort netId,
[MaybeNullWhen(false)] out IComponent component)
[MaybeNullWhen(false)] out IComponent component, MetaDataComponent? meta = null)
{
if (!uid.HasValue)
{
DebugTools.AssertNull(meta);
component = default;
return false;
}
if (_netComponents.TryGetValue(uid.Value, out var netSet)
&& netSet.TryGetValue(netId, out var comp))
{
component = comp;
return true;
}
component = default;
return false;
return TryGetComponent(uid.Value, netId, out component, meta);
}
public EntityQuery<TComp1> GetEntityQuery<TComp1>() where TComp1 : Component
@@ -967,17 +986,18 @@ namespace Robust.Shared.GameObjects
}
/// <inheritdoc />
public NetComponentEnumerable GetNetComponents(EntityUid uid)
public NetComponentEnumerable GetNetComponents(EntityUid uid, MetaDataComponent? meta = null)
{
return new NetComponentEnumerable(_netComponents[uid]);
meta ??= MetaQuery.GetComponentInternal(uid);
return new NetComponentEnumerable(meta.NetComponents);
}
/// <inheritdoc />
public NetComponentEnumerable? GetNetComponentsOrNull(EntityUid uid)
public NetComponentEnumerable? GetNetComponentsOrNull(EntityUid uid, MetaDataComponent? meta = null)
{
return _netComponents.TryGetValue(uid, out var data)
? new NetComponentEnumerable(data)
: null;
return MetaQuery.Resolve(uid, ref meta)
? new NetComponentEnumerable(meta.NetComponents)
: null;
}
#region Join Functions

View File

@@ -72,6 +72,21 @@ public partial class EntityManager
return false;
}
/// <inheritdoc />
public bool TryGetEntityData(NetEntity nEntity, [NotNullWhen(true)] out EntityUid? entity, [NotNullWhen(true)] out MetaDataComponent? meta)
{
if (NetEntityLookup.TryGetValue(nEntity, out var went))
{
entity = went.Item1;
meta = went.Item2;
return true;
}
entity = null;
meta = null;
return false;
}
/// <inheritdoc />
public bool TryGetEntity(NetEntity? nEntity, [NotNullWhen(true)] out EntityUid? entity)
{

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

@@ -2,9 +2,11 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using Prometheus;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Profiling;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager;
@@ -31,13 +33,14 @@ namespace Robust.Shared.GameObjects
[IoC.Dependency] private readonly IGameTiming _gameTiming = default!;
[IoC.Dependency] private readonly ISerializationManager _serManager = default!;
[IoC.Dependency] private readonly ProfManager _prof = default!;
[IoC.Dependency] private readonly INetManager _netMan = default!;
// I feel like PJB might shed me for putting a system dependency here, but its required for setting entity
// 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
@@ -85,6 +88,9 @@ namespace Robust.Shared.GameObjects
private string _xformName = string.Empty;
private ComponentRegistration _metaReg = default!;
private ComponentRegistration _xformReg = default!;
private SharedMapSystem _mapSystem = default!;
private ISawmill _sawmill = default!;
@@ -111,7 +117,9 @@ namespace Robust.Shared.GameObjects
_eventBus = new EntityEventBus(this);
InitializeComponents();
_xformName = _componentFactory.GetComponentName(typeof(TransformComponent));
_metaReg = _componentFactory.GetRegistration(typeof(MetaDataComponent));
_xformReg = _componentFactory.GetRegistration(typeof(TransformComponent));
_xformName = _xformReg.Name;
_sawmill = LogManager.GetSawmill("entity");
_resolveSawmill = LogManager.GetSawmill("resolve");
@@ -233,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()
@@ -316,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;
}
@@ -324,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)
{
@@ -363,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;
@@ -445,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
@@ -477,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).
@@ -559,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)
@@ -578,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>
@@ -645,10 +643,12 @@ namespace Robust.Shared.GameObjects
Entities.Add(uid);
// add the required MetaDataComponent directly.
AddComponentInternal(uid, metadata, false, false);
AddComponentInternal(uid, metadata, _metaReg, false, true, metadata);
// allocate the required TransformComponent
AddComponent<TransformComponent>(uid);
var xformComp = Unsafe.As<TransformComponent>(_componentFactory.GetComponent(_xformReg));
xformComp.Owner = uid;
AddComponentInternal(uid, xformComp, false, true, metadata);
return uid;
}
@@ -706,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
@@ -915,9 +960,9 @@ public partial class EntitySystem
#region NetEntities
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool IsClientSide(EntityUid entity)
protected bool IsClientSide(EntityUid entity, MetaDataComponent? meta = null)
{
return EntityManager.IsClientSide(entity);
return EntityManager.IsClientSide(entity, meta);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

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

@@ -49,7 +49,7 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// Adds a Component with a given network id to an entity.
/// </summary>
Component AddComponent(EntityUid uid, ushort netId);
Component AddComponent(EntityUid uid, ushort netId, MetaDataComponent? meta = null);
/// <summary>
/// Adds an uninitialized Component type to an entity.
@@ -70,7 +70,7 @@ namespace Robust.Shared.GameObjects
/// <param name="uid">Entity being modified.</param>
/// <param name="component">Component to add.</param>
/// <param name="overwrite">Should it overwrite existing components?</param>
void AddComponent<T>(EntityUid uid, T component, bool overwrite = false) where T : Component;
void AddComponent<T>(EntityUid uid, T component, bool overwrite = false, MetaDataComponent? metadata = null) where T : Component;
/// <summary>
/// Removes the component with the specified reference type,
@@ -78,7 +78,7 @@ namespace Robust.Shared.GameObjects
/// </summary>
/// <typeparam name="T">The component reference type to remove.</typeparam>
/// <param name="uid">Entity UID to modify.</param>
bool RemoveComponent<T>(EntityUid uid);
bool RemoveComponent<T>(EntityUid uid, MetaDataComponent? meta = null);
/// <summary>
/// Removes the component with a specified type.
@@ -86,7 +86,7 @@ namespace Robust.Shared.GameObjects
/// <param name="uid">Entity UID to modify.</param>
/// <param name="type">A trait or component type to check for.</param>
/// <returns>Returns false if the entity did not have the specified component.</returns>
bool RemoveComponent(EntityUid uid, Type type);
bool RemoveComponent(EntityUid uid, Type type, MetaDataComponent? meta = null);
/// <summary>
/// Removes the component with a specified network ID.
@@ -94,14 +94,14 @@ namespace Robust.Shared.GameObjects
/// <param name="uid">Entity UID to modify.</param>
/// <param name="netID">Network ID of the component to remove.</param>
/// <returns>Returns false if the entity did not have the specified component.</returns>
bool RemoveComponent(EntityUid uid, ushort netID);
bool RemoveComponent(EntityUid uid, ushort netID, MetaDataComponent? meta = null);
/// <summary>
/// Removes the specified component. Throws if the given component does not belong to the entity.
/// </summary>
/// <param name="uid">Entity UID to modify.</param>
/// <param name="component">Component to remove.</param>
void RemoveComponent(EntityUid uid, IComponent component);
void RemoveComponent(EntityUid uid, IComponent component, MetaDataComponent? meta = null);
/// <summary>
/// Immediately shuts down a component, but defers the removal and deletion until the end of the tick.
@@ -125,7 +125,7 @@ namespace Robust.Shared.GameObjects
/// <param name="uid">Entity UID to modify.</param>
/// <param name="netID">Network ID of the component to remove.</param>
/// <returns>Returns false if the entity did not have the specified component.</returns>
bool RemoveComponentDeferred(EntityUid uid, ushort netID);
bool RemoveComponentDeferred(EntityUid uid, ushort netID, MetaDataComponent? meta = null);
/// <summary>
/// Immediately shuts down a component, but defers the removal and deletion until the end of the tick.
@@ -147,7 +147,7 @@ namespace Robust.Shared.GameObjects
/// Removes all components from an entity, except the required components.
/// </summary>
/// <param name="uid">Entity UID to modify.</param>
void RemoveComponents(EntityUid uid);
void RemoveComponents(EntityUid uid, MetaDataComponent? meta = null);
/// <summary>
/// Removes ALL components from an entity. This includes the required components,
@@ -155,7 +155,7 @@ namespace Robust.Shared.GameObjects
/// used when deleting an entity.
/// </summary>
/// <param name="uid">Entity UID to modify.</param>
void DisposeComponents(EntityUid uid);
void DisposeComponents(EntityUid uid, MetaDataComponent? meta = null);
/// <summary>
/// Checks if the entity has a component type.
@@ -187,7 +187,7 @@ namespace Robust.Shared.GameObjects
/// <param name="uid">Entity UID to check.</param>
/// <param name="type">A trait or component type to check for.</param>
/// <returns>True if the entity has the component type, otherwise false.</returns>
bool HasComponent(EntityUid ?uid, Type type);
bool HasComponent(EntityUid? uid, Type type);
/// <summary>
/// Checks if the entity has a component with a given network ID. This does not check
@@ -196,7 +196,7 @@ namespace Robust.Shared.GameObjects
/// <param name="uid">Entity UID to check.</param>
/// <param name="netId">Network ID to check for.</param>
/// <returns>True if the entity has a component with the given network ID, otherwise false.</returns>
bool HasComponent(EntityUid uid, ushort netId);
bool HasComponent(EntityUid uid, ushort netId, MetaDataComponent? meta = null);
/// <summary>
/// Checks if the entity has a component with a given network ID. This does not check
@@ -205,7 +205,7 @@ namespace Robust.Shared.GameObjects
/// <param name="uid">Entity UID to check.</param>
/// <param name="netId">Network ID to check for.</param>
/// <returns>True if the entity has a component with the given network ID, otherwise false.</returns>
bool HasComponent(EntityUid? uid, ushort netId);
bool HasComponent(EntityUid? uid, ushort netId, MetaDataComponent? meta = null);
/// <summary>
/// This method will always return a component for a certain entity, adding it if it's not there already.
@@ -255,7 +255,7 @@ namespace Robust.Shared.GameObjects
/// <param name="uid">Entity UID to look on.</param>
/// <param name="netId">Network ID of the component to retrieve.</param>
/// <returns>The component with the specified network id.</returns>
IComponent GetComponent(EntityUid uid, ushort netId);
IComponent GetComponent(EntityUid uid, ushort netId, MetaDataComponent? meta = null);
/// <summary>
/// Returns the component of a specific type, even if it has been marked for deletion.
@@ -318,7 +318,7 @@ namespace Robust.Shared.GameObjects
/// <param name="netId">Component Network ID to check for.</param>
/// <param name="component">Component with the specified network id.</param>
/// <returns>If the component existed in the entity.</returns>
bool TryGetComponent(EntityUid uid, ushort netId, [NotNullWhen(true)] out IComponent? component);
bool TryGetComponent(EntityUid uid, ushort netId, [NotNullWhen(true)] out IComponent? component, MetaDataComponent? meta = null);
/// <summary>
/// Returns the component with a specified network ID. This does not check
@@ -328,7 +328,7 @@ namespace Robust.Shared.GameObjects
/// <param name="netId">Component Network ID to check for.</param>
/// <param name="component">Component with the specified network id.</param>
/// <returns>If the component existed in the entity.</returns>
bool TryGetComponent([NotNullWhen(true)] EntityUid? uid, ushort netId, [NotNullWhen(true)] out IComponent? component);
bool TryGetComponent([NotNullWhen(true)] EntityUid? uid, ushort netId, [NotNullWhen(true)] out IComponent? component, MetaDataComponent? meta = null);
/// <summary>
/// Returns a cached struct enumerator with the specified component.
@@ -364,7 +364,7 @@ namespace Robust.Shared.GameObjects
/// </summary>
/// <param name="uid">Entity UID to look on.</param>
/// <returns>All components that have a network ID.</returns>
NetComponentEnumerable GetNetComponents(EntityUid uid);
NetComponentEnumerable GetNetComponents(EntityUid uid, MetaDataComponent? meta = null);
/// <summary>
/// Returns ALL networked components on an entity, including deleted ones. Returns null if the entity does
@@ -372,7 +372,7 @@ namespace Robust.Shared.GameObjects
/// </summary>
/// <param name="uid">Entity UID to look on.</param>
/// <returns>All components that have a network ID.</returns>
public NetComponentEnumerable? GetNetComponentsOrNull(EntityUid uid);
public NetComponentEnumerable? GetNetComponentsOrNull(EntityUid uid, MetaDataComponent? meta = null);
/// <summary>
/// Gets a component state.

View File

@@ -22,6 +22,12 @@ public partial interface IEntityManager
/// </summary>
public bool TryGetEntity(NetEntity? nEntity, [NotNullWhen(true)] out EntityUid? entity);
/// <summary>
/// Tries to returns the corresponding local <see cref="EntityUid"/> along with the metdata component.
/// </summary>
public bool TryGetEntityData(NetEntity nEntity, [NotNullWhen(true)] out EntityUid? entity,
[NotNullWhen(true)] out MetaDataComponent? meta);
/// <summary>
/// TryGet version of <see cref="GetNetEntity"/>
/// </summary>

View File

@@ -4,6 +4,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Robust.Shared.GameObjects;
@@ -47,7 +48,14 @@ public readonly struct NetEntity : IEquatable<NetEntity>, IComparable<NetEntity>
/// </summary>
public static NetEntity Parse(ReadOnlySpan<char> uid)
{
return new NetEntity(int.Parse(uid));
if (uid[0] != 'c')
return new NetEntity(int.Parse(uid));
if (uid.Length == 1)
throw new FormatException($"'c' is not a valid NetEntity");
var id = int.Parse(uid.Slice(1));
return new NetEntity(id | ClientEntity);
}
public static bool TryParse(ReadOnlySpan<char> uid, out NetEntity entity)
@@ -121,6 +129,9 @@ public readonly struct NetEntity : IEquatable<NetEntity>, IComparable<NetEntity>
/// <inheritdoc />
public override string ToString()
{
if (IsClientSide())
return $"c{Id & ~ClientEntity}";
return Id.ToString();
}
@@ -135,6 +146,14 @@ public readonly struct NetEntity : IEquatable<NetEntity>, IComparable<NetEntity>
ReadOnlySpan<char> format,
IFormatProvider? provider)
{
if (IsClientSide())
{
return FormatHelpers.TryFormatInto(
destination,
out charsWritten,
$"c{Id & ~ClientEntity}");
}
return Id.TryFormat(destination, out charsWritten);
}

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

@@ -28,7 +28,7 @@ public record struct PhysicsHull()
e.Normalize();
// discard points left of e and find point furthest to the right of e
var rightPoints = new Vector2[PhysicsConstants.MaxPolygonVertices];
Span<Vector2> rightPoints = stackalloc Vector2[PhysicsConstants.MaxPolygonVertices];
var rightCount = 0;
var bestIndex = 0;

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

@@ -1,4 +1,6 @@
using Robust.Shared.Serialization.TypeSerializers.Implementations;
using System;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations;
namespace Robust.Shared.Prototypes;
@@ -10,7 +12,8 @@ namespace Robust.Shared.Prototypes;
/// This will be automatically validated by <see cref="EntProtoIdSerializer"/> if used in data fields.
/// </remarks>
/// <remarks><seealso cref="ProtoId{T}"/> for a wrapper of other prototype kinds.</remarks>
public readonly record struct EntProtoId(string Id)
[Serializable, NetSerializable]
public readonly record struct EntProtoId(string Id) : IEquatable<string>, IComparable<EntProtoId>
{
public static implicit operator string(EntProtoId protoId)
{
@@ -21,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

@@ -34,7 +34,7 @@ public sealed class AngleTypeParser : TypeParser<Angle>
if (word.EndsWith("deg"))
{
if (!float.TryParse(word[..^3], out var f))
if (!float.TryParse(word[..^3], CultureInfo.InvariantCulture, out var f))
{
error = new InvalidAngle(word);
result = null;
@@ -47,7 +47,7 @@ public sealed class AngleTypeParser : TypeParser<Angle>
}
else
{
if (!float.TryParse(word, out var f))
if (!float.TryParse(word, CultureInfo.InvariantCulture, out var f))
{
error = new InvalidAngle(word);
result = null;

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));
}
}
}