diff --git a/Content.IntegrationTests/Tests/Embedding/EmbedTest.cs b/Content.IntegrationTests/Tests/Embedding/EmbedTest.cs
new file mode 100644
index 0000000000..5e09b5c482
--- /dev/null
+++ b/Content.IntegrationTests/Tests/Embedding/EmbedTest.cs
@@ -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
+{
+ ///
+ /// Embeddable entity that will be thrown at the target.
+ ///
+ private const string EmbeddableProtoId = "SurvivalKnife";
+
+ ///
+ /// Target entity that the thrown item will embed into.
+ ///
+ private const string TargetProtoId = "AirlockGlass";
+
+ ///
+ /// Embeds an entity with a into a target,
+ /// then disconnects the client. Intended to reveal any clientside issues that might
+ /// occur due to reparenting during cleanup.
+ ///
+ [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(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();
+ await Client.WaitPost(Client.EntMan.FlushEntities);
+ await Pair.RunTicksSync(1);
+ }
+
+ ///
+ /// Embeds an entity with a into a target,
+ /// then deletes the target and makes sure the embeddable is not deleted.
+ ///
+ [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(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);
+ }
+}
diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs
index be86fd1af2..d0cb3b2261 100644
--- a/Content.Shared/Projectiles/SharedProjectileSystem.cs
+++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs
@@ -143,6 +143,8 @@ public abstract partial class SharedProjectileSystem : EntitySystem
}
var xform = Transform(uid);
+ if (TerminatingOrDeleted(xform.GridUid) && TerminatingOrDeleted(xform.MapUid))
+ return;
TryComp(uid, out var physics);
_physics.SetBodyType(uid, BodyType.Dynamic, body: physics, xform: xform);
_transform.AttachToGridOrMap(uid, xform);