Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework PrefabHelper #266

Merged
merged 1 commit into from
Nov 30, 2024
Merged
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
100 changes: 56 additions & 44 deletions EXILED/Exiled.API/Features/PrefabHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ namespace Exiled.API.Features
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;

using Exiled.API.Enums;
Expand All @@ -23,83 +21,97 @@ namespace Exiled.API.Features
/// </summary>
public static class PrefabHelper
{
private static readonly Dictionary<PrefabType, GameObject> Stored = new();
/// <summary>
/// A <see cref="Dictionary{TKey,TValue}"/> containing all <see cref="PrefabType"/> and their corresponding <see cref="GameObject"/>.
/// </summary>
internal static readonly Dictionary<PrefabType, GameObject> Prefabs = new(Enum.GetValues(typeof(PrefabType)).Length);

/// <summary>
/// Gets a dictionary of <see cref="PrefabType"/> to <see cref="GameObject"/>.
/// Gets a <see cref="IReadOnlyDictionary{TKey,TValue}"/> of <see cref="PrefabType"/> and their corresponding <see cref="GameObject"/>.
/// </summary>
public static ReadOnlyDictionary<PrefabType, GameObject> PrefabToGameObject => new(Stored);
public static IReadOnlyDictionary<PrefabType, GameObject> PrefabToGameObject => Prefabs;

/// <summary>
/// Gets the prefab attribute of a prefab type.
/// Gets the <see cref="PrefabAttribute"/> from a <see cref="PrefabType"/>.
/// </summary>
/// <param name="prefabType">The prefab type.</param>
/// <returns>The <see cref="PrefabAttribute" />.</returns>
/// <param name="prefabType">The <see cref="PrefabType"/>.</param>
/// <returns>The corresponding <see cref="PrefabAttribute"/>.</returns>
public static PrefabAttribute GetPrefabAttribute(this PrefabType prefabType)
{
Type type = prefabType.GetType();
return type.GetField(Enum.GetName(type, prefabType)).GetCustomAttribute<PrefabAttribute>();
}

/// <summary>
/// Gets the prefab of the specified <see cref="PrefabType"/>.
/// Gets the <see cref="GameObject"/> of the specified <see cref="PrefabType"/>.
/// </summary>
/// <param name="prefabType">The <see cref="PrefabType"/>.</param>
/// <returns>Returns the <see cref="GameObject"/>.</returns>
public static GameObject GetPrefab(PrefabType prefabType)
{
if (Prefabs.TryGetValue(prefabType, out GameObject prefab))
return prefab;

return null;
}

/// <summary>
/// Tries to get the <see cref="GameObject"/> of the specified <see cref="PrefabType"/>.
/// </summary>
/// <param name="prefabType">The <see cref="PrefabType"/>.</param>
/// <param name="gameObject">The <see cref="GameObject"/> of the .</param>
/// <returns>Returns true if the <see cref="GameObject"/> was found.</returns>
public static bool TryGetPrefab(PrefabType prefabType, out GameObject gameObject)
{
gameObject = GetPrefab(prefabType);
return gameObject is not null;
}

/// <summary>
/// Gets a <see cref="Component"/> from the <see cref="GameObject"/> of the specified <see cref="PrefabType"/>.
/// </summary>
/// <param name="type">The <see cref="PrefabType"/> to get prefab of.</param>
/// <typeparam name="T">The <see cref="Component"/> to get.</typeparam>
/// <returns>Returns the prefab component as {T}.</returns>
public static T GetPrefab<T>(PrefabType type)
/// <param name="prefabType">The <see cref="PrefabType"/>.</param>
/// <typeparam name="T">The <see cref="Component"/> type.</typeparam>
/// <returns>Returns the <see cref="Component"/>.</returns>
public static T GetPrefab<T>(PrefabType prefabType)
where T : Component
{
if (!Stored.TryGetValue(type, out GameObject gameObject) || !gameObject.TryGetComponent(out T component))
return null;
if (Prefabs.TryGetValue(prefabType, out GameObject prefab) && prefab.TryGetComponent(out T component))
return component;

return component;
return null;
}

/// <summary>
/// Spawns a prefab on server.
/// Spawns the <see cref="GameObject"/> of the specified <see cref="PrefabType"/>.
/// </summary>
/// <param name="prefabType">The prefab type.</param>
/// <param name="position">The position to spawn the prefab.</param>
/// <param name="rotation">The rotation of the prefab.</param>
/// <returns>The <see cref="GameObject"/> instantied.</returns>
/// <param name="prefabType">The <see cref="PrefabType"/>.</param>
/// <param name="position">The <see cref="Vector3"/> position where the <see cref="GameObject"/> will spawn.</param>
/// <param name="rotation">The <see cref="Quaternion"/> rotation of the <see cref="GameObject"/>.</param>
/// <returns>Returns the <see cref="GameObject"/> instantied.</returns>
public static GameObject Spawn(PrefabType prefabType, Vector3 position = default, Quaternion rotation = default)
{
if (!Stored.TryGetValue(prefabType, out GameObject gameObject))
if (!TryGetPrefab(prefabType, out GameObject gameObject))
return null;

GameObject newGameObject = UnityEngine.Object.Instantiate(gameObject, position, rotation);
NetworkServer.Spawn(newGameObject);
return newGameObject;
}

/// <summary>
/// Spawns a prefab on server.
/// Spawns the <see cref="GameObject"/> of the specified <see cref="PrefabType"/>.
/// </summary>
/// <param name="prefabType">The prefab type.</param>
/// <param name="position">The position to spawn the prefab.</param>
/// <param name="rotation">The rotation of the prefab.</param>
/// <param name="prefabType">The <see cref="PrefabType"/>.</param>
/// <param name="position">The <see cref="Vector3"/> position where the <see cref="GameObject"/> will spawn.</param>
/// <param name="rotation">The <see cref="Quaternion"/> rotation of the <see cref="GameObject"/>.</param>
/// <typeparam name="T">The <see cref="Component"/> type.</typeparam>
/// <returns>The <see cref="Component"/> instantied.</returns>
/// <returns>Returns the <see cref="Component"/> of the <see cref="GameObject"/>.</returns>
public static T Spawn<T>(PrefabType prefabType, Vector3 position = default, Quaternion rotation = default)
where T : Component
{
T obj = UnityEngine.Object.Instantiate(GetPrefab<T>(prefabType), position, rotation);
NetworkServer.Spawn(obj.gameObject);
return obj;
}

/// <summary>
/// Loads all prefabs.
/// </summary>
internal static void LoadPrefabs()
{
Stored.Clear();

foreach (PrefabType prefabType in EnumUtils<PrefabType>.Values)
{
PrefabAttribute attribute = prefabType.GetPrefabAttribute();
Stored.Add(prefabType, NetworkClient.prefabs.FirstOrDefault(prefab => prefab.Key == attribute.AssetId || prefab.Value.name.Contains(attribute.Name)).Value);
}
GameObject gameObject = Spawn(prefabType, position, rotation);
return gameObject?.GetComponent<T>();
}
}
}
2 changes: 2 additions & 0 deletions EXILED/Exiled.Events/Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public override void OnEnabled()
Log.Info($"{(Config.UseDynamicPatching ? "Non-event" : "All")} patches completed in {watch.Elapsed}");
PlayerAuthenticationManager.OnInstanceModeChanged -= RoleAssigner.CheckLateJoin;

CustomNetworkManager.OnClientStarted += Handlers.Internal.ClientStarted.OnClientStarted;
SceneManager.sceneUnloaded += Handlers.Internal.SceneUnloaded.OnSceneUnloaded;
MapGeneration.SeedSynchronizer.OnGenerationFinished += Handlers.Internal.MapGenerated.OnMapGenerated;
UsableItemsController.ServerOnUsingCompleted += Handlers.Internal.Round.OnServerOnUsingCompleted;
Expand Down Expand Up @@ -91,6 +92,7 @@ public override void OnDisabled()

Unpatch();

CustomNetworkManager.OnClientStarted -= Handlers.Internal.ClientStarted.OnClientStarted;
SceneManager.sceneUnloaded -= Handlers.Internal.SceneUnloaded.OnSceneUnloaded;
MapGeneration.SeedSynchronizer.OnGenerationFinished -= Handlers.Internal.MapGenerated.OnMapGenerated;
UsableItemsController.ServerOnUsingCompleted -= Handlers.Internal.Round.OnServerOnUsingCompleted;
Expand Down
53 changes: 53 additions & 0 deletions EXILED/Exiled.Events/Handlers/Internal/ClientStarted.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// -----------------------------------------------------------------------
// <copyright file="ClientStarted.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Events.Handlers.Internal
{
using System.Collections.Generic;
using System.Linq;

using Exiled.API.Enums;
using Exiled.API.Features;
using Exiled.API.Features.Attributes;
using Mirror;
using PlayerRoles.Ragdolls;
using UnityEngine;

/// <summary>
/// Handles on client started event.
/// </summary>
internal static class ClientStarted
{
/// <summary>
/// Called once when the client is started.
/// </summary>
public static void OnClientStarted()
{
PrefabHelper.Prefabs.Clear();

Dictionary<uint, GameObject> prefabs = new();

foreach (KeyValuePair<uint, GameObject> prefab in NetworkClient.prefabs)
{
if(!prefabs.ContainsKey(prefab.Key))
prefabs.Add(prefab.Key, prefab.Value);
}

foreach (NetworkIdentity ragdollPrefab in RagdollManager.AllRagdollPrefabs)
{
if(!prefabs.ContainsKey(ragdollPrefab.assetId))
prefabs.Add(ragdollPrefab.assetId, ragdollPrefab.gameObject);
}

foreach (PrefabType prefabType in EnumUtils<PrefabType>.Values)
{
PrefabAttribute attribute = prefabType.GetPrefabAttribute();
PrefabHelper.Prefabs.Add(prefabType, prefabs.FirstOrDefault(prefab => prefab.Key == attribute.AssetId || prefab.Value.name.Contains(attribute.Name)).Value);
}
}
}
}
15 changes: 0 additions & 15 deletions EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,11 @@

namespace Exiled.Events.Handlers.Internal
{
using System;
using System.Collections.Generic;
using System.Linq;

using API.Features;
using API.Features.Items;
using API.Features.Pools;
using API.Structs;

using Exiled.API.Enums;
using Exiled.API.Extensions;
using Exiled.API.Features.Lockers;
using InventorySystem.Items.Firearms.Attachments;
using InventorySystem.Items.Firearms.Attachments.Components;

using MEC;

using Utils.NonAllocLINQ;

/// <summary>
/// Handles <see cref="Handlers.Map.Generated"/> event.
/// </summary>
Expand All @@ -46,7 +32,6 @@ internal static class MapGenerated
public static void OnMapGenerated()
{
Map.ClearCache();
PrefabHelper.LoadPrefabs();
Locker.ClearCache();

// TODO: Fix For (https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/377)
Expand Down