From 9f4aa1ebe0532be6f305d0dcc504ded088f6fbc4 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 21 Dec 2024 15:54:11 +1100 Subject: [PATCH] Implement some field-level deltas (#28242) * Update GasTileOverlayState * Update DecalGridState * Update NavMapState * poke * poke2 * poke3 * Implement field deltas for guns * Content done * Update --------- Co-authored-by: ElectroJr --- .../Ranged/Systems/GunSystem.Ballistic.cs | 2 + .../Ranged/Systems/GunSystem.Solution.cs | 7 +- .../Nutrition/Components/HungerComponent.cs | 2 +- .../Nutrition/Components/ThirstComponent.cs | 2 +- .../Nutrition/EntitySystems/HungerSystem.cs | 3 +- .../Nutrition/EntitySystems/ThirstSystem.cs | 3 +- .../Ranged/Components/AmmoComponent.cs | 16 ++-- .../BallisticAmmoProviderComponent.cs | 6 +- .../Weapons/Ranged/Components/GunComponent.cs | 2 +- .../SolutionAmmoProviderComponent.cs | 14 ++-- .../Systems/SharedGunSystem.Ballistic.cs | 10 +-- .../Weapons/Ranged/Systems/SharedGunSystem.cs | 78 +++++++++++++++---- 12 files changed, 98 insertions(+), 47 deletions(-) diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.Ballistic.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.Ballistic.cs index 798be3fc8eab..60680deaaaa0 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.Ballistic.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.Ballistic.cs @@ -15,6 +15,7 @@ protected override void Cycle(EntityUid uid, BallisticAmmoProviderComponent comp { var existing = component.Entities[^1]; component.Entities.RemoveAt(component.Entities.Count - 1); + DirtyField(uid, component, nameof(BallisticAmmoProviderComponent.Entities)); Containers.Remove(existing, component.Container); EnsureShootable(existing); @@ -22,6 +23,7 @@ protected override void Cycle(EntityUid uid, BallisticAmmoProviderComponent comp else if (component.UnspawnedCount > 0) { component.UnspawnedCount--; + DirtyField(uid, component, nameof(BallisticAmmoProviderComponent.UnspawnedCount)); ent = Spawn(component.Proto, coordinates); EnsureShootable(ent.Value); } diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.Solution.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.Solution.cs index cc36132e8e50..d0898efaaf84 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.Solution.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.Solution.cs @@ -39,8 +39,9 @@ protected override void UpdateSolutionShots(EntityUid uid, SolutionAmmoProviderC if (solution == null && !_solutionContainer.TryGetSolution(uid, component.SolutionId, out _, out solution)) { component.Shots = shots; + DirtyField(uid, component, nameof(SolutionAmmoProviderComponent.Shots)); component.MaxShots = maxShots; - Dirty(uid, component); + DirtyField(uid, component, nameof(SolutionAmmoProviderComponent.MaxShots)); return; } @@ -48,8 +49,10 @@ protected override void UpdateSolutionShots(EntityUid uid, SolutionAmmoProviderC maxShots = (int) (solution.MaxVolume / component.FireCost); component.Shots = shots; + DirtyField(uid, component, nameof(SolutionAmmoProviderComponent.Shots)); + component.MaxShots = maxShots; - Dirty(uid, component); + DirtyField(uid, component, nameof(SolutionAmmoProviderComponent.MaxShots)); UpdateSolutionAppearance(uid, component); } diff --git a/Content.Shared/Nutrition/Components/HungerComponent.cs b/Content.Shared/Nutrition/Components/HungerComponent.cs index 33abb257dd10..d5db991b1a00 100644 --- a/Content.Shared/Nutrition/Components/HungerComponent.cs +++ b/Content.Shared/Nutrition/Components/HungerComponent.cs @@ -10,7 +10,7 @@ namespace Content.Shared.Nutrition.Components; [RegisterComponent, NetworkedComponent, Access(typeof(HungerSystem))] -[AutoGenerateComponentState, AutoGenerateComponentPause] +[AutoGenerateComponentState(fieldDeltas: true), AutoGenerateComponentPause] public sealed partial class HungerComponent : Component { /// diff --git a/Content.Shared/Nutrition/Components/ThirstComponent.cs b/Content.Shared/Nutrition/Components/ThirstComponent.cs index f3ac881361fc..5c32b4af2868 100644 --- a/Content.Shared/Nutrition/Components/ThirstComponent.cs +++ b/Content.Shared/Nutrition/Components/ThirstComponent.cs @@ -7,7 +7,7 @@ namespace Content.Shared.Nutrition.Components; [RegisterComponent, NetworkedComponent, Access(typeof(ThirstSystem))] -[AutoGenerateComponentState, AutoGenerateComponentPause] +[AutoGenerateComponentState(fieldDeltas: true), AutoGenerateComponentPause] public sealed partial class ThirstComponent : Component { // Base stuff diff --git a/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs b/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs index 063c1f6bb1a6..a8697ffee455 100644 --- a/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs @@ -122,7 +122,7 @@ private void SetAuthoritativeHungerValue(Entity entity, float v { entity.Comp.LastAuthoritativeHungerChangeTime = _timing.CurTime; entity.Comp.LastAuthoritativeHungerValue = ClampHungerWithinThresholds(entity.Comp, value); - Dirty(entity); + DirtyField(entity.Owner, entity.Comp, nameof(HungerComponent.LastAuthoritativeHungerChangeTime)); } private void UpdateCurrentThreshold(EntityUid uid, HungerComponent? component = null) @@ -133,6 +133,7 @@ private void UpdateCurrentThreshold(EntityUid uid, HungerComponent? component = var calculatedHungerThreshold = GetHungerThreshold(component); if (calculatedHungerThreshold == component.CurrentThreshold) return; + component.CurrentThreshold = calculatedHungerThreshold; DoHungerThresholdEffects(uid, component); } diff --git a/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs b/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs index 0b2bb2e0efac..2937c48d482f 100644 --- a/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs @@ -102,7 +102,8 @@ public void SetThirst(EntityUid uid, ThirstComponent component, float amount) component.ThirstThresholds[ThirstThreshold.Dead], component.ThirstThresholds[ThirstThreshold.OverHydrated] ); - Dirty(uid, component); + + EntityManager.DirtyField(uid, component, nameof(ThirstComponent.CurrentThirst)); } private bool IsMovementThreshold(ThirstThreshold threshold) diff --git a/Content.Shared/Weapons/Ranged/Components/AmmoComponent.cs b/Content.Shared/Weapons/Ranged/Components/AmmoComponent.cs index 3e1111a97d17..13cee5bad6f8 100644 --- a/Content.Shared/Weapons/Ranged/Components/AmmoComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/AmmoComponent.cs @@ -13,27 +13,27 @@ public partial class AmmoComponent : Component, IShootable { // Muzzle flash stored on ammo because if we swap a gun to whatever we may want to override it. - [ViewVariables(VVAccess.ReadWrite), DataField("muzzleFlash", customTypeSerializer:typeof(PrototypeIdSerializer))] - public string? MuzzleFlash = "MuzzleFlashEffect"; + [DataField] + public EntProtoId? MuzzleFlash = "MuzzleFlashEffect"; } /// /// Spawns another prototype to be shot instead of itself. /// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true)] public sealed partial class CartridgeAmmoComponent : AmmoComponent { - [ViewVariables(VVAccess.ReadWrite), DataField("proto", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Prototype = default!; + [ViewVariables(VVAccess.ReadWrite), DataField("proto", required: true)] + public EntProtoId Prototype; - [ViewVariables(VVAccess.ReadWrite), DataField("spent")] + [ViewVariables(VVAccess.ReadWrite), DataField] [AutoNetworkedField] - public bool Spent = false; + public bool Spent; /// /// Caseless ammunition. /// - [DataField("deleteOnSpawn")] + [DataField] public bool DeleteOnSpawn; [DataField("soundEject")] diff --git a/Content.Shared/Weapons/Ranged/Components/BallisticAmmoProviderComponent.cs b/Content.Shared/Weapons/Ranged/Components/BallisticAmmoProviderComponent.cs index f825f10550d4..ee806125e786 100644 --- a/Content.Shared/Weapons/Ranged/Components/BallisticAmmoProviderComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/BallisticAmmoProviderComponent.cs @@ -7,13 +7,13 @@ namespace Content.Shared.Weapons.Ranged.Components; -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedGunSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true), Access(typeof(SharedGunSystem))] public sealed partial class BallisticAmmoProviderComponent : Component { - [ViewVariables(VVAccess.ReadWrite), DataField] + [DataField] public SoundSpecifier? SoundRack = new SoundPathSpecifier("/Audio/Weapons/Guns/Cock/smg_cock.ogg"); - [ViewVariables(VVAccess.ReadWrite), DataField] + [DataField] public SoundSpecifier? SoundInsert = new SoundPathSpecifier("/Audio/Weapons/Guns/MagIn/bullet_insert.ogg"); [ViewVariables(VVAccess.ReadWrite), DataField] diff --git a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs index 98b1d2fe2a4f..3ec832613d2e 100644 --- a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs @@ -8,7 +8,7 @@ namespace Content.Shared.Weapons.Ranged.Components; -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true), AutoGenerateComponentPause] [Access(typeof(SharedGunSystem))] public sealed partial class GunComponent : Component { diff --git a/Content.Shared/Weapons/Ranged/Components/SolutionAmmoProviderComponent.cs b/Content.Shared/Weapons/Ranged/Components/SolutionAmmoProviderComponent.cs index d936f3a9e2fe..19c6af69b186 100644 --- a/Content.Shared/Weapons/Ranged/Components/SolutionAmmoProviderComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/SolutionAmmoProviderComponent.cs @@ -5,38 +5,38 @@ namespace Content.Shared.Weapons.Ranged.Components; -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedGunSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true), Access(typeof(SharedGunSystem))] public sealed partial class SolutionAmmoProviderComponent : Component { /// /// The solution where reagents are extracted from for the projectile. /// - [DataField("solutionId", required: true), AutoNetworkedField] + [DataField(required: true), AutoNetworkedField] public string SolutionId = default!; /// /// How much reagent it costs to fire once. /// - [DataField("fireCost"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + [DataField, AutoNetworkedField] public float FireCost = 5; /// /// The amount of shots currently available. /// used for network predictions. /// - [DataField("shots"), ViewVariables, AutoNetworkedField] + [DataField, AutoNetworkedField] public int Shots; /// /// The max amount of shots the gun can fire. /// used for network prediction /// - [DataField("maxShots"), ViewVariables, AutoNetworkedField] + [DataField, AutoNetworkedField] public int MaxShots; /// /// The prototype that's fired by the gun. /// - [DataField("proto", customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)] - public string Prototype = default!; + [DataField("proto")] + public EntProtoId Prototype; } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs index 9ef5cbd70285..044f75dbc909 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs @@ -57,7 +57,7 @@ private void OnBallisticInteractUsing(EntityUid uid, BallisticAmmoProviderCompon Audio.PlayPredicted(component.SoundInsert, uid, args.User); args.Handled = true; UpdateBallisticAppearance(uid, component); - Dirty(uid, component); + DirtyField(uid, component, nameof(BallisticAmmoProviderComponent.Entities)); } private void OnBallisticAfterInteract(EntityUid uid, BallisticAmmoProviderComponent component, AfterInteractEvent args) @@ -194,10 +194,9 @@ private void ManualCycle(EntityUid uid, BallisticAmmoProviderComponent component !Paused(uid)) { gunComp.NextFire = Timing.CurTime + TimeSpan.FromSeconds(1 / gunComp.FireRateModified); - Dirty(uid, gunComp); + DirtyField(uid, gunComp, nameof(GunComponent.NextFire)); } - Dirty(uid, component); Audio.PlayPredicted(component.SoundRack, uid, user); var shots = GetBallisticShots(component); @@ -228,7 +227,7 @@ private void OnBallisticMapInit(EntityUid uid, BallisticAmmoProviderComponent co { component.UnspawnedCount = Math.Max(0, component.Capacity - component.Container.ContainedEntities.Count); UpdateBallisticAppearance(uid, component); - Dirty(uid, component); + DirtyField(uid, component, nameof(BallisticAmmoProviderComponent.UnspawnedCount)); } } @@ -249,18 +248,19 @@ private void OnBallisticTakeAmmo(EntityUid uid, BallisticAmmoProviderComponent c args.Ammo.Add((entity, EnsureShootable(entity))); component.Entities.RemoveAt(component.Entities.Count - 1); + DirtyField(uid, component, nameof(BallisticAmmoProviderComponent.Entities)); Containers.Remove(entity, component.Container); } else if (component.UnspawnedCount > 0) { component.UnspawnedCount--; + DirtyField(uid, component, nameof(BallisticAmmoProviderComponent.UnspawnedCount)); entity = Spawn(component.Proto, args.Coordinates); args.Ammo.Add((entity, EnsureShootable(entity))); } } UpdateBallisticAppearance(uid, component); - Dirty(uid, component); } private void OnBallisticAmmoCount(EntityUid uid, BallisticAmmoProviderComponent component, ref GetAmmoCountEvent args) diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index c7cc09e618bd..62e63501ee8a 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -119,7 +119,7 @@ private void OnGunMelee(EntityUid uid, GunComponent component, MeleeHitEvent arg if (melee.NextAttack > component.NextFire) { component.NextFire = melee.NextAttack; - Dirty(uid, component); + EntityManager.DirtyField(uid, component, nameof(MeleeWeaponComponent.NextAttack)); } } @@ -200,7 +200,7 @@ private void StopShooting(EntityUid uid, GunComponent gun) gun.ShotCounter = 0; gun.ShootCoordinates = null; gun.Target = null; - Dirty(uid, gun); + EntityManager.DirtyField(uid, gun, nameof(GunComponent.ShotCounter)); } /// @@ -211,6 +211,7 @@ public void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun, Ent gun.ShootCoordinates = toCoordinates; AttemptShoot(user, gunUid, gun); gun.ShotCounter = 0; + EntityManager.DirtyField(gunUid, gun, nameof(GunComponent.ShotCounter)); } /// @@ -228,7 +229,9 @@ private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun) { if (gun.FireRateModified <= 0f || !_actionBlockerSystem.CanAttack(user)) + { return; + } var toCoordinates = gun.ShootCoordinates; @@ -277,7 +280,7 @@ private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun) } // NextFire has been touched regardless so need to dirty the gun. - Dirty(gunUid, gun); + EntityManager.DirtyField(gunUid, gun, nameof(GunComponent.NextFire)); // Get how many shots we're actually allowed to make, due to clip size or otherwise. // Don't do this in the loop so we still reset NextFire. @@ -331,6 +334,7 @@ private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun) // Even if we don't actually shoot update the ShotCounter. This is to avoid spamming empty sounds // where the gun may be SemiAuto or Burst. gun.ShotCounter += shots; + EntityManager.DirtyField(gunUid, gun, nameof(GunComponent.ShotCounter)); if (ev.Ammo.Count <= 0) { @@ -387,8 +391,6 @@ private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun) if (_gravity.IsWeightless(user, userPhysics)) CauseImpulse(fromCoordinates, toCoordinates.Value, user, userPhysics); } - - Dirty(gunUid, gun); } public void Shoot( @@ -442,7 +444,7 @@ protected virtual void UpdateAmmoCount(EntityUid uid, bool prediction = true) {} protected void SetCartridgeSpent(EntityUid uid, CartridgeAmmoComponent cartridge, bool spent) { if (cartridge.Spent != spent) - Dirty(uid, cartridge); + DirtyField(uid, cartridge, nameof(CartridgeAmmoComponent.Spent)); cartridge.Spent = spent; Appearance.SetData(uid, AmmoVisuals.Spent, spent); @@ -541,17 +543,59 @@ public void RefreshModifiers(Entity gun) RaiseLocalEvent(gun, ref ev); - comp.SoundGunshotModified = ev.SoundGunshot; - comp.CameraRecoilScalarModified = ev.CameraRecoilScalar; - comp.AngleIncreaseModified = ev.AngleIncrease; - comp.AngleDecayModified = ev.AngleDecay; - comp.MaxAngleModified = ev.MaxAngle; - comp.MinAngleModified = ev.MinAngle; - comp.ShotsPerBurstModified = ev.ShotsPerBurst; - comp.FireRateModified = ev.FireRate; - comp.ProjectileSpeedModified = ev.ProjectileSpeed; - - Dirty(gun); + if (comp.SoundGunshotModified != ev.SoundGunshot) + { + comp.SoundGunshotModified = ev.SoundGunshot; + DirtyField(gun, nameof(GunComponent.SoundGunshotModified)); + } + + if (!MathHelper.CloseTo(comp.CameraRecoilScalarModified, ev.CameraRecoilScalar)) + { + comp.CameraRecoilScalarModified = ev.CameraRecoilScalar; + DirtyField(gun, nameof(GunComponent.CameraRecoilScalarModified)); + } + + if (!comp.AngleIncreaseModified.EqualsApprox(ev.AngleIncrease)) + { + comp.AngleIncreaseModified = ev.AngleIncrease; + DirtyField(gun, nameof(GunComponent.AngleIncreaseModified)); + } + + if (!comp.AngleDecayModified.EqualsApprox(ev.AngleDecay)) + { + comp.AngleDecayModified = ev.AngleDecay; + DirtyField(gun, nameof(GunComponent.AngleDecayModified)); + } + + if (!comp.MaxAngleModified.EqualsApprox(ev.MinAngle)) + { + comp.MaxAngleModified = ev.MaxAngle; + DirtyField(gun, nameof(GunComponent.MaxAngleModified)); + } + + if (!comp.MinAngleModified.EqualsApprox(ev.MinAngle)) + { + comp.MinAngleModified = ev.MinAngle; + DirtyField(gun, nameof(GunComponent.MinAngleModified)); + } + + if (comp.ShotsPerBurstModified != ev.ShotsPerBurst) + { + comp.ShotsPerBurstModified = ev.ShotsPerBurst; + DirtyField(gun, nameof(GunComponent.ShotsPerBurstModified)); + } + + if (!MathHelper.CloseTo(comp.FireRateModified, ev.FireRate)) + { + comp.FireRateModified = ev.FireRate; + DirtyField(gun, nameof(GunComponent.FireRateModified)); + } + + if (!MathHelper.CloseTo(comp.ProjectileSpeedModified, ev.ProjectileSpeed)) + { + comp.ProjectileSpeedModified = ev.ProjectileSpeed; + DirtyField(gun, nameof(GunComponent.ProjectileSpeedModified)); + } } protected abstract void CreateEffect(EntityUid gunUid, MuzzleFlashEvent message, EntityUid? user = null);