Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions S1API.Tests/NPCs/NPCNetworkObjectTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using S1API.Entities;

namespace S1API.Tests.NPCs;

public sealed class NPCNetworkObjectTests
{
[Fact]
public void AnNpcCreatedBeforeTheNetworkStartsKeepsItsNetworkObject()
{
// The Big Pimpin creates its escorts at scene load on the second load of a session, before the server is
// up. Removing their NetworkObject then left them bound to @Managers/@NPCs and their spawn failed.
Assert.False(NPC.ShouldRemoveNetworkObject(hasNetworkManager: true, isClient: false, isServer: false));
Assert.False(NPC.ShouldRemoveNetworkObject(hasNetworkManager: false, isClient: false, isServer: false));
}

[Fact]
public void TheServerAndHostKeepIt()
{
Assert.False(NPC.ShouldRemoveNetworkObject(hasNetworkManager: true, isClient: false, isServer: true));
Assert.False(NPC.ShouldRemoveNetworkObject(hasNetworkManager: true, isClient: true, isServer: true));
}

[Fact]
public void AClientOnlyPeerRemovesItBecauseTheServerSpawnsTheNpcToIt()
{
Assert.True(NPC.ShouldRemoveNetworkObject(hasNetworkManager: true, isClient: true, isServer: false));
}
}
20 changes: 13 additions & 7 deletions S1API/Entities/NPC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,14 @@ internal static readonly System.Collections.Generic.HashSet<System.Type>

#region Template Prefab Helpers

/// <summary>
/// Should a new NPC instance lose its <c>NetworkObject</c>? Only on a client-only peer, which the server spawns
/// the NPC to. Before the network starts (a mod creating its NPCs at scene load) it must keep it, or its network
/// behaviours bind to the parent's NetworkObject and the later spawn fails.
/// </summary>
internal static bool ShouldRemoveNetworkObject(bool hasNetworkManager, bool isClient, bool isServer) =>
hasNetworkManager && isClient && !isServer;

private static GameObject InstantiateTemplateInstance(System.Type npcType, NPC owner)
{
GameObject prefab = GetOrCreatePerNpcPrefab(npcType, owner);
Expand Down Expand Up @@ -245,18 +253,16 @@ private static GameObject InstantiateTemplateInstance(System.Type npcType, NPC o
try
{
var nm = InstanceFinder.NetworkManager;
bool isServer = nm != null && nm.IsServer;
var existingNo = instance.GetComponent<NetworkObject>();
if (isServer)
{
if (existingNo == null)
existingNo = instance.AddComponent<NetworkObject>();
}
else
if (ShouldRemoveNetworkObject(nm != null, nm != null && nm.IsClient, nm != null && nm.IsServer))
{
if (existingNo != null)
UnityEngine.Object.Destroy(existingNo);
}
else if (existingNo == null)
{
instance.AddComponent<NetworkObject>();
}
}
catch { }
if (S1NPCs.NPCManager.InstanceExists)
Expand Down
Loading