]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Fix error when disconnecting the client while a projectile is embedded (#36048)
authorTayrtahn <tayrtahn@gmail.com>
Tue, 25 Mar 2025 00:16:13 +0000 (20:16 -0400)
committerGitHub <noreply@github.com>
Tue, 25 Mar 2025 00:16:13 +0000 (01:16 +0100)
* Add test for disconnecting the client while a projectile is embedded

* Add check for non-terminating grid or map

* Add test that an embeddable detaches when the target is deleted.

* Remove Explicit tag from TestDisconnectWhileEmbedded

Content.IntegrationTests/Tests/Embedding/EmbedTest.cs [new file with mode: 0644]
Content.Shared/Projectiles/SharedProjectileSystem.cs

diff --git a/Content.IntegrationTests/Tests/Embedding/EmbedTest.cs b/Content.IntegrationTests/Tests/Embedding/EmbedTest.cs
new file mode 100644 (file)
index 0000000..5e09b5c
--- /dev/null
@@ -0,0 +1,91 @@
+using Content.IntegrationTests.Tests.Interaction;
+using Content.Shared.Projectiles;
+using Robust.Shared.Network;
+
+namespace Content.IntegrationTests.Tests.Embedding;
+
+public sealed class EmbedTest : InteractionTest
+{
+    /// <summary>
+    /// Embeddable entity that will be thrown at the target.
+    /// </summary>
+    private const string EmbeddableProtoId = "SurvivalKnife";
+
+    /// <summary>
+    /// Target entity that the thrown item will embed into.
+    /// </summary>
+    private const string TargetProtoId = "AirlockGlass";
+
+    /// <summary>
+    /// Embeds an entity with a <see cref="EmbeddableProjectileComponent"/> into a target,
+    /// then disconnects the client. Intended to reveal any clientside issues that might
+    /// occur due to reparenting during cleanup.
+    /// </summary>
+    [Test]
+    public async Task TestDisconnectWhileEmbedded()
+    {
+        // Spawn the target we're going to throw at
+        await SpawnTarget(TargetProtoId);
+
+        // Give the player the embeddable to throw
+        var projectile = await PlaceInHands(EmbeddableProtoId);
+        Assert.That(TryComp<EmbeddableProjectileComponent>(projectile, out var embedComp),
+            $"{EmbeddableProtoId} does not have EmbeddableProjectileComponent");
+        // Make sure the projectile isn't already embedded into anything
+        Assert.That(embedComp.EmbeddedIntoUid, Is.Null,
+            $"Projectile already embedded into {SEntMan.ToPrettyString(embedComp.EmbeddedIntoUid)}");
+
+        // Have the player throw the embeddable at the target
+        await ThrowItem();
+
+        // Wait a moment for the item to hit and embed
+        await RunSeconds(0.5f);
+
+        // Make sure the projectile is embedded into the target
+        Assert.That(embedComp.EmbeddedIntoUid, Is.EqualTo(ToServer(Target)),
+            "Projectile not embedded into target");
+
+        // Disconnect the client
+        var cNetMgr = Client.ResolveDependency<IClientNetManager>();
+        await Client.WaitPost(Client.EntMan.FlushEntities);
+        await Pair.RunTicksSync(1);
+    }
+
+    /// <summary>
+    /// Embeds an entity with a <see cref="EmbeddableProjectileComponent"/> into a target,
+    /// then deletes the target and makes sure the embeddable is not deleted.
+    /// </summary>
+    [Test]
+    public async Task TestEmbedDetach()
+    {
+        // Spawn the target we're going to throw at
+        await SpawnTarget(TargetProtoId);
+
+        // Give the player the embeddable to throw
+        var projectile = await PlaceInHands(EmbeddableProtoId);
+        Assert.That(TryComp<EmbeddableProjectileComponent>(projectile, out var embedComp),
+            $"{EmbeddableProtoId} does not have EmbeddableProjectileComponent");
+        // Make sure the projectile isn't already embedded into anything
+        Assert.That(embedComp.EmbeddedIntoUid, Is.Null,
+            $"Projectile already embedded into {SEntMan.ToPrettyString(embedComp.EmbeddedIntoUid)}");
+
+        // Have the player throw the embeddable at the target
+        await ThrowItem();
+
+        // Wait a moment for the item to hit and embed
+        await RunSeconds(0.5f);
+
+        // Make sure the projectile is embedded into the target
+        Assert.That(embedComp.EmbeddedIntoUid, Is.EqualTo(ToServer(Target)),
+            "Projectile not embedded into target");
+
+        // Delete the target
+        await Delete(Target.Value);
+
+        await RunTicks(1);
+
+        // Make sure the embeddable wasn't deleted with the target
+        AssertExists(projectile);
+        await AssertEntityLookup(EmbeddableProtoId);
+    }
+}
index be86fd1af238b46a344ef2536d63e3c8cd67cb7c..d0cb3b22614f420754fabf2820eb4b744f4b0bbf 100644 (file)
@@ -143,6 +143,8 @@ public abstract partial class SharedProjectileSystem : EntitySystem
         }
 
         var xform = Transform(uid);
+        if (TerminatingOrDeleted(xform.GridUid) && TerminatingOrDeleted(xform.MapUid))
+            return;
         TryComp<PhysicsComponent>(uid, out var physics);
         _physics.SetBodyType(uid, BodyType.Dynamic, body: physics, xform: xform);
         _transform.AttachToGridOrMap(uid, xform);