diff --git a/EXILED/Exiled.API/Features/Npc.cs b/EXILED/Exiled.API/Features/Npc.cs index 82a6c73745..e79038065c 100644 --- a/EXILED/Exiled.API/Features/Npc.cs +++ b/EXILED/Exiled.API/Features/Npc.cs @@ -16,7 +16,6 @@ namespace Exiled.API.Features using CentralAuth; using CommandSystem; using Exiled.API.Enums; - using Exiled.API.Extensions; using Exiled.API.Features.Components; using Exiled.API.Features.Roles; using Footprinting; @@ -142,6 +141,7 @@ public override Vector3 Position /// The userID of the NPC. /// The position to spawn the NPC. /// The spawned. + [Obsolete("This metod is marked as obsolet due to a bug that make player have the same id. Use Npc.Spawn(string) instead")] public static Npc Spawn(string name, RoleTypeId role, int id = 0, string userId = PlayerAuthenticationManager.DedicatedId, Vector3? position = null) { GameObject newObject = UnityEngine.Object.Instantiate(Mirror.NetworkManager.singleton.playerPrefab); @@ -208,18 +208,118 @@ public static Npc Spawn(string name, RoleTypeId role, int id = 0, string userId return npc; } + /// + /// Spawns an NPC based on the given parameters. + /// + /// The name of the NPC. + /// The RoleTypeId of the NPC, defaulting to None. + /// Whether the NPC should be ignored by round ending checks. + /// The userID of the NPC for authentication. Defaults to the Dedicated ID. + /// The position where the NPC should spawn. If null, the default spawn location is used. + /// The spawned. + public static Npc Spawn(string name, RoleTypeId role = RoleTypeId.None, bool ignored = false, string userId = PlayerAuthenticationManager.DedicatedId, Vector3? position = null) + { + GameObject newObject = UnityEngine.Object.Instantiate(Mirror.NetworkManager.singleton.playerPrefab); + + Npc npc = new(newObject) + { + IsNPC = true, + }; + + FakeConnection fakeConnection = new(npc.Id); + + try + { + if (userId == PlayerAuthenticationManager.DedicatedId) + { + npc.ReferenceHub.authManager.SyncedUserId = userId; + try + { + npc.ReferenceHub.authManager.InstanceMode = ClientInstanceMode.DedicatedServer; + } + catch (Exception e) + { + Log.Debug($"Ignore: {e.Message}"); + } + } + else + { + npc.ReferenceHub.authManager.InstanceMode = ClientInstanceMode.Unverified; + npc.ReferenceHub.authManager._privUserId = userId == string.Empty ? $"Dummy-{npc.Id}@localhost" : userId; + } + } + catch (Exception e) + { + Log.Debug($"Ignore: {e.Message}"); + } + + try + { + npc.ReferenceHub.roleManager.InitializeNewRole(RoleTypeId.None, RoleChangeReason.None); + } + catch (Exception e) + { + Log.Debug($"Ignore: {e.Message}"); + } + + NetworkServer.AddPlayerForConnection(fakeConnection, newObject); + + npc.ReferenceHub.nicknameSync.Network_myNickSync = name; + Dictionary.Add(newObject, npc); + + Timing.CallDelayed(0.5f, () => + { + npc.Role.Set(role, SpawnReason.RoundStart, position is null ? RoleSpawnFlags.All : RoleSpawnFlags.AssignInventory); + + if (position is not null) + npc.Position = position.Value; + }); + + if (ignored) + Round.IgnoredPlayers.Add(npc.ReferenceHub); + + return npc; + } + + /// + /// Destroys all NPCs currently spawned. + /// + public static void DestroyAll() + { + foreach (Npc npc in List) + npc.Destroy(); + } + /// /// Destroys the NPC. /// public void Destroy() { - NetworkConnectionToClient conn = ReferenceHub.connectionToClient; - if (ReferenceHub._playerId.Value <= RecyclablePlayerId._autoIncrement) - ReferenceHub._playerId.Destroy(); - ReferenceHub.OnDestroy(); - CustomNetworkManager.TypedSingleton.OnServerDisconnect(conn); - Dictionary.Remove(GameObject); - Object.Destroy(GameObject); + try + { + Round.IgnoredPlayers.Remove(ReferenceHub); + NetworkConnectionToClient conn = ReferenceHub.connectionToClient; + ReferenceHub.OnDestroy(); + CustomNetworkManager.TypedSingleton.OnServerDisconnect(conn); + Dictionary.Remove(GameObject); + Object.Destroy(GameObject); + } + catch (Exception e) + { + Log.Error($"Error while destroying a NPC: {e.Message}"); + } + } + + /// + /// Schedules the destruction of the NPC after a delay. + /// + /// The delay in seconds before the NPC is destroyed. + public void LateDestroy(float time) + { + Timing.CallDelayed(time, () => + { + this?.Destroy(); + }); } } }