diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index ee0903b530e..795110e630c 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -39,7 +39,7 @@ END TEMPLATE-->
### New features
-*None yet*
+* Added `EntityManager.DirtyFields()`, which allows components with delta states to simultaneously mark several fields as dirty at the same time.
### Bugfixes
diff --git a/Robust.Client/GameObjects/ClientEntityManager.cs b/Robust.Client/GameObjects/ClientEntityManager.cs
index 3b33663a8c0..bef870d34d4 100644
--- a/Robust.Client/GameObjects/ClientEntityManager.cs
+++ b/Robust.Client/GameObjects/ClientEntityManager.cs
@@ -101,11 +101,33 @@ public override void Dirty(EntityUid uid, IComponent component, MetaDataComponen
///
public override void Dirty(Entity ent, MetaDataComponent? meta = null)
{
- // Client only dirties during prediction
+ // Client only dirties during prediction
if (_gameTiming.InPrediction)
base.Dirty(ent, meta);
}
+ public override void DirtyField(EntityUid uid, T comp, string fieldName, MetaDataComponent? metadata = null)
+ {
+ // TODO Prediction
+ // does the client actually need to dirty the field?
+ // I.e., can't it just dirty the whole component to trigger a reset?
+
+ // Client only dirties during prediction
+ if (_gameTiming.InPrediction)
+ base.DirtyField(uid, comp, fieldName, metadata);
+ }
+
+ public override void DirtyFields(EntityUid uid, T comp, MetaDataComponent? meta, params ReadOnlySpan fields)
+ {
+ // TODO Prediction
+ // does the client actually need to dirty the field?
+ // I.e., can't it just dirty the whole component to trigger a reset?
+
+ // Client only dirties during prediction
+ if (_gameTiming.InPrediction)
+ base.DirtyFields(uid, comp, meta, fields);
+ }
+
///
public override void Dirty(Entity ent, MetaDataComponent? meta = null)
{
diff --git a/Robust.Shared/Containers/SharedContainerSystem.Insert.cs b/Robust.Shared/Containers/SharedContainerSystem.Insert.cs
index 8e7dab6feb5..1c4961c31a9 100644
--- a/Robust.Shared/Containers/SharedContainerSystem.Insert.cs
+++ b/Robust.Shared/Containers/SharedContainerSystem.Insert.cs
@@ -197,6 +197,10 @@ private void RecursivelyUpdatePhysics(Entity(EntityUid uid, T comp, string fieldName, MetaDataComponent? metadata = null)
+ public virtual void DirtyField(EntityUid uid, T comp, string fieldName, MetaDataComponent? metadata = null)
where T : IComponentDelta
{
var compReg = ComponentFactory.GetRegistration(CompIdx.Index());
+ // TODO
+ // consider storing this on MetaDataComponent?
+ // We alsready store other dirtying information there anyways, and avoids having to fetch the registration.
if (!compReg.NetworkedFieldLookup.TryGetValue(fieldName, out var idx))
{
_sawmill.Error($"Tried to dirty delta field {fieldName} on {ToPrettyString(uid)} that isn't implemented.");
@@ -54,6 +58,25 @@ public void DirtyField(EntityUid uid, T comp, string fieldName, MetaDataCompo
comp.LastModifiedFields[idx] = curTick;
Dirty(uid, comp, metadata);
}
+
+
+ public virtual void DirtyFields(EntityUid uid, T comp, MetaDataComponent? meta, params ReadOnlySpan fields)
+ where T : IComponentDelta
+ {
+ var compReg = ComponentFactory.GetRegistration(CompIdx.Index());
+
+ var curTick = _gameTiming.CurTick;
+ foreach (var field in fields)
+ {
+ if (!compReg.NetworkedFieldLookup.TryGetValue(field, out var idx))
+ _sawmill.Error($"Tried to dirty delta field {field} on {ToPrettyString(uid)} that isn't implemented.");
+ else
+ comp.LastModifiedFields[idx] = curTick;
+ }
+
+ comp.LastFieldUpdate = curTick;
+ Dirty(uid, comp, meta);
+ }
}
///
diff --git a/Robust.Shared/GameObjects/EntityManager.Components.cs b/Robust.Shared/GameObjects/EntityManager.Components.cs
index fa3798c2280..95889c291c7 100644
--- a/Robust.Shared/GameObjects/EntityManager.Components.cs
+++ b/Robust.Shared/GameObjects/EntityManager.Components.cs
@@ -1694,7 +1694,6 @@ public bool HasComponent([NotNullWhen(true)] EntityUid? uid)
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- [Pure]
public bool Resolve(EntityUid uid, [NotNullWhen(true)] ref TComp1? component, bool logMissing = true)
{
if (component != null)
@@ -1717,7 +1716,7 @@ public bool Resolve(EntityUid uid, [NotNullWhen(true)] ref TComp1? component, bo
return false;
}
- [MethodImpl(MethodImplOptions.AggressiveInlining), Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Resolve(ref Entity entity, bool logMissing = true)
{
return Resolve(entity.Owner, ref entity.Comp, logMissing);
diff --git a/Robust.Shared/GameObjects/EntitySystem.Proxy.cs b/Robust.Shared/GameObjects/EntitySystem.Proxy.cs
index a22b3225d51..0075987d002 100644
--- a/Robust.Shared/GameObjects/EntitySystem.Proxy.cs
+++ b/Robust.Shared/GameObjects/EntitySystem.Proxy.cs
@@ -171,6 +171,13 @@ protected void DirtyField(EntityUid uid, T component, string fieldName, MetaD
EntityManager.DirtyField(uid, component, fieldName, meta);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected void DirtyFields(EntityUid uid, T comp, MetaDataComponent? meta, params ReadOnlySpan fields)
+ where T : IComponentDelta
+ {
+ EntityManager.DirtyFields(uid, comp, meta);
+ }
+
///
/// Marks a component as dirty. This also implicitly dirties the entity this component belongs to.
///
diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs
index 6a97bea3fb6..e475f2b2e0a 100644
--- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs
+++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs
@@ -139,26 +139,26 @@ private void OnPhysicsHandleState(EntityUid uid, PhysicsComponent component, ref
if (args.Current is PhysicsLinearVelocityDeltaState linearState)
{
- SetLinearVelocity(uid, linearState.LinearVelocity, body: component, manager: manager);
+ SetLinearVelocity(uid, linearState.LinearVelocity, dirty: false, body: component, manager: manager);
}
else if (args.Current is PhysicsVelocityDeltaState velocityState)
{
- SetLinearVelocity(uid, velocityState.LinearVelocity, body: component, manager: manager);
- SetAngularVelocity(uid, velocityState.AngularVelocity, body: component, manager: manager);
+ SetLinearVelocity(uid, velocityState.LinearVelocity, dirty: false, body: component, manager: manager);
+ SetAngularVelocity(uid, velocityState.AngularVelocity, dirty: false, body: component, manager: manager);
}
else if (args.Current is PhysicsComponentState newState)
{
- SetSleepingAllowed(uid, component, newState.SleepingAllowed);
- SetFixedRotation(uid, newState.FixedRotation, body: component);
- SetCanCollide(uid, newState.CanCollide, body: component);
+ SetSleepingAllowed(uid, component, newState.SleepingAllowed, dirty: false);
+ SetFixedRotation(uid, newState.FixedRotation, body: component, dirty: false);
+ SetCanCollide(uid, newState.CanCollide, body: component, dirty: false);
component.BodyStatus = newState.Status;
- SetLinearVelocity(uid, newState.LinearVelocity, body: component, manager: manager);
- SetAngularVelocity(uid, newState.AngularVelocity, body: component, manager: manager);
+ SetLinearVelocity(uid, newState.LinearVelocity, dirty: false, body: component, manager: manager);
+ SetAngularVelocity(uid, newState.AngularVelocity, dirty: false, body: component, manager: manager);
SetBodyType(uid, newState.BodyType, manager, component);
- SetFriction(uid, component, newState.Friction);
- SetLinearDamping(uid, component, newState.LinearDamping);
- SetAngularDamping(uid, component, newState.AngularDamping);
+ SetFriction(uid, component, newState.Friction, dirty: false);
+ SetLinearDamping(uid, component, newState.LinearDamping, dirty: false);
+ SetAngularDamping(uid, component, newState.AngularDamping, dirty: false);
component.Force = newState.Force;
component.Torque = newState.Torque;
}
@@ -270,29 +270,12 @@ public void DestroyContacts(PhysicsComponent body)
///
public void ResetDynamics(EntityUid uid, PhysicsComponent body, bool dirty = true)
{
- if (body.Torque != 0f)
- {
- body.Torque = 0f;
- DirtyField(uid, body, nameof(PhysicsComponent.Torque));
- }
-
- if (body.AngularVelocity != 0f)
- {
- body.AngularVelocity = 0f;
- DirtyField(uid, body, nameof(PhysicsComponent.AngularVelocity));
- }
-
- if (body.Force != Vector2.Zero)
- {
- body.Force = Vector2.Zero;
- DirtyField(uid, body, nameof(PhysicsComponent.Force));
- }
-
- if (body.LinearVelocity != Vector2.Zero)
- {
- body.LinearVelocity = Vector2.Zero;
- DirtyField(uid, body, nameof(PhysicsComponent.LinearVelocity));
- }
+ body.Torque = 0f;
+ body.AngularVelocity = 0f;
+ body.Force = Vector2.Zero;
+ body.LinearVelocity = Vector2.Zero;
+ if (dirty)
+ DirtyFields(uid, body, null, nameof(PhysicsComponent.Torque), nameof(PhysicsComponent.AngularVelocity), nameof(PhysicsComponent.Force), nameof(PhysicsComponent.LinearVelocity));
}
public void ResetMassData(EntityUid uid, FixturesComponent? manager = null, PhysicsComponent? body = null)
@@ -397,7 +380,8 @@ public bool SetAngularVelocity(EntityUid uid, float value, bool dirty = true, Fi
return false;
body.AngularVelocity = value;
- DirtyField(uid, body, nameof(PhysicsComponent.AngularVelocity));
+ if (dirty)
+ DirtyField(uid, body, nameof(PhysicsComponent.AngularVelocity));
return true;
}
@@ -423,7 +407,9 @@ public bool SetLinearVelocity(EntityUid uid, Vector2 velocity, bool dirty = true
return false;
body.LinearVelocity = velocity;
- DirtyField(uid, body, nameof(PhysicsComponent.LinearVelocity));
+ if (dirty)
+ DirtyField(uid, body, nameof(PhysicsComponent.LinearVelocity));
+
return true;
}
@@ -433,7 +419,8 @@ public void SetAngularDamping(EntityUid uid, PhysicsComponent body, float value,
return;
body.AngularDamping = value;
- DirtyField(uid, body, nameof(PhysicsComponent.AngularDamping));
+ if (dirty)
+ DirtyField(uid, body, nameof(PhysicsComponent.AngularDamping));
}
public void SetLinearDamping(EntityUid uid, PhysicsComponent body, float value, bool dirty = true)
@@ -442,7 +429,8 @@ public void SetLinearDamping(EntityUid uid, PhysicsComponent body, float value,
return;
body.LinearDamping = value;
- DirtyField(uid, body, nameof(PhysicsComponent.LinearDamping));
+ if (dirty)
+ DirtyField(uid, body, nameof(PhysicsComponent.LinearDamping));
}
[Obsolete("Use SetAwake with EntityUid")]
@@ -518,32 +506,27 @@ public void SetBodyType(EntityUid uid, BodyType value, FixturesComponent? manage
body.BodyType = value;
ResetMassData(uid, manager, body);
+ body.Force = Vector2.Zero;
+ body.Torque = 0f;
+
if (body.BodyType == BodyType.Static)
{
SetAwake((uid, body), false);
- if (body.LinearVelocity != Vector2.Zero)
- {
- body.LinearVelocity = Vector2.Zero;
- DirtyField(uid, body, nameof(PhysicsComponent.LinearVelocity));
- }
+ body.LinearVelocity = Vector2.Zero;
+ body.AngularVelocity = 0f;
- if (body.AngularVelocity != 0f)
- {
- body.AngularVelocity = 0f;
- DirtyField(uid, body, nameof(PhysicsComponent.AngularVelocity));
- }
+ DirtyFields(uid, body, null,
+ nameof(PhysicsComponent.LinearVelocity),
+ nameof(PhysicsComponent.AngularVelocity),
+ nameof(PhysicsComponent.Force),
+ nameof(PhysicsComponent.Torque));
}
// Even if it's dynamic if it can't collide then don't force it awake.
else if (body.CanCollide)
{
SetAwake((uid, body), true);
- }
-
- if (body.Torque != 0f)
- {
- body.Torque = 0f;
- DirtyField(uid, body, nameof(PhysicsComponent.Torque));
+ DirtyFields(uid, body, null, nameof(PhysicsComponent.Force), nameof(PhysicsComponent.Torque));
}
_broadphase.RegenerateContacts(uid, body, manager, xform);
@@ -561,7 +544,8 @@ public void SetBodyStatus(EntityUid uid, PhysicsComponent body, BodyStatus statu
return;
body.BodyStatus = status;
- DirtyField(uid, body, nameof(PhysicsComponent.BodyStatus));
+ if (dirty)
+ DirtyField(uid, body, nameof(PhysicsComponent.BodyStatus));
}
///
@@ -612,7 +596,10 @@ public bool SetCanCollide(
var ev = new CollisionChangeEvent(uid, body, value);
RaiseLocalEvent(ref ev);
}
- DirtyField(uid, body, nameof(PhysicsComponent.CanCollide));
+
+ if (dirty)
+ DirtyField(uid, body, nameof(PhysicsComponent.CanCollide));
+
return value;
}
@@ -622,13 +609,10 @@ public void SetFixedRotation(EntityUid uid, bool value, bool dirty = true, Fixtu
return;
body.FixedRotation = value;
- DirtyField(uid, body, nameof(PhysicsComponent.FixedRotation));
+ body.AngularVelocity = 0.0f;
- if (body.AngularVelocity != 0f)
- {
- body.AngularVelocity = 0.0f;
- DirtyField(uid, body, nameof(PhysicsComponent.AngularVelocity));
- }
+ if (dirty)
+ DirtyFields(uid, body, null, nameof(PhysicsComponent.FixedRotation), nameof(PhysicsComponent.AngularVelocity));
ResetMassData(uid, manager: manager, body: body);
}
@@ -639,7 +623,8 @@ public void SetFriction(EntityUid uid, PhysicsComponent body, float value, bool
return;
body._friction = value;
- DirtyField(uid, body, nameof(PhysicsComponent.Friction));
+ if (dirty)
+ DirtyField(uid, body, nameof(PhysicsComponent.Friction));
}
public void SetInertia(EntityUid uid, PhysicsComponent body, float value, bool dirty = true)
@@ -678,7 +663,8 @@ public void SetSleepingAllowed(EntityUid uid, PhysicsComponent body, bool value,
SetAwake((uid, body), true);
body.SleepingAllowed = value;
- DirtyField(uid, body, nameof(PhysicsComponent.SleepingAllowed));
+ if (dirty)
+ DirtyField(uid, body, nameof(PhysicsComponent.SleepingAllowed));
}
public void SetSleepTime(PhysicsComponent body, float value)
diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.cs
index 848f44fbccc..4aa69d3d67f 100644
--- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.cs
+++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.cs
@@ -83,6 +83,13 @@ public override void Initialize()
_physicsReg = EntityManager.ComponentFactory.GetRegistration(CompIdx.Index());
+ // TODO PHYSICS STATE
+ // Consider condensing the possible fields into just Linear velocity, angular velocity, and "Other"
+ // Or maybe even just "velocity" & "other"
+ // Then get-state doesn't have to iterate over a 10-element array.
+ // And it simplifies the DirtyField calls.
+ // Though I guess combining fixtures & physics will complicate it a bit more again.
+
// If you update this then update the delta state + GetState + HandleState!
EntityManager.ComponentFactory.RegisterNetworkedFields(_physicsReg,
nameof(PhysicsComponent.CanCollide),