Skip to content

Commit

Permalink
[EXILED::API] Fixing NPC PlayerId assignment (#135)
Browse files Browse the repository at this point in the history
  • Loading branch information
SrLicht authored Oct 24, 2024
1 parent 7e10cbf commit 4e8c5d5
Showing 1 changed file with 108 additions and 8 deletions.
116 changes: 108 additions & 8 deletions EXILED/Exiled.API/Features/Npc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -142,6 +141,7 @@ public override Vector3 Position
/// <param name="userId">The userID of the NPC.</param>
/// <param name="position">The position to spawn the NPC.</param>
/// <returns>The <see cref="Npc"/> spawned.</returns>
[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);
Expand Down Expand Up @@ -208,18 +208,118 @@ public static Npc Spawn(string name, RoleTypeId role, int id = 0, string userId
return npc;
}

/// <summary>
/// Spawns an NPC based on the given parameters.
/// </summary>
/// <param name="name">The name of the NPC.</param>
/// <param name="role">The RoleTypeId of the NPC, defaulting to None.</param>
/// <param name="ignored">Whether the NPC should be ignored by round ending checks.</param>
/// <param name="userId">The userID of the NPC for authentication. Defaults to the Dedicated ID.</param>
/// <param name="position">The position where the NPC should spawn. If null, the default spawn location is used.</param>
/// <returns>The <see cref="Npc"/> spawned.</returns>
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;
}

/// <summary>
/// Destroys all NPCs currently spawned.
/// </summary>
public static void DestroyAll()
{
foreach (Npc npc in List)
npc.Destroy();
}

/// <summary>
/// Destroys the NPC.
/// </summary>
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}");
}
}

/// <summary>
/// Schedules the destruction of the NPC after a delay.
/// </summary>
/// <param name="time">The delay in seconds before the NPC is destroyed.</param>
public void LateDestroy(float time)
{
Timing.CallDelayed(time, () =>
{
this?.Destroy();
});
}
}
}

0 comments on commit 4e8c5d5

Please sign in to comment.