Skip to content
Open
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
133 changes: 133 additions & 0 deletions Features/Extensions/ExiledExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using InventorySystem.Items.Pickups;
using LabApi.Features.Wrappers;
using LabApi.Loader;
using LabApi.Loader.Features.Plugins;
using UnityEngine;
namespace ProjectMER.Features.Extensions
{
public static class ExiledExtensions
{
private static MethodInfo? CustomItemTrySpawnMethodInfo;

private static MethodInfo? PickupGetBaseMethodInfo;

public static Pickup? TrySpawn(string customItemName, Vector3 position, Quaternion rotation, Vector3 scale)
{
if (!TrySpawnCustomItem(customItemName, position, out Pickup? pickup))
return null;

pickup.Rotation = rotation;
pickup.Transform.localScale = scale;

return pickup;
}

internal static bool TrySpawnCustomItem(string name, Vector3 position, [NotNullWhen(true)] out Pickup? pickup)
{
pickup = null;

if (CustomItemTrySpawnMethodInfo is null)
return false;

object?[] args = [name, position, null];

CustomItemTrySpawnMethodInfo.Invoke(null, args);

if (args[2] is null)
return false;

pickup = GetPickupFromExiledPickup(args[2]!);

return pickup is not null;
}


internal static Pickup? GetPickupFromExiledPickup(object pickup)
{
if (PickupGetBaseMethodInfo is null)
return null;

object obj = PickupGetBaseMethodInfo.Invoke(pickup, []);

if (obj is not ItemPickupBase pickupBase)
{
Logger.Warn("Failed to get ItemPickupBase from Exiled Pickup");
return null;
}

return Pickup.Get(pickupBase);
}

internal static void TryInitialize()
{
const string ErrorMessage = "Failed to find a component of Exiled! This can be ignored if you do not use Exiled Custom Items in ProjectMER. Cause: ";

KeyValuePair<Plugin, Assembly> kvp = PluginLoader.Plugins.FirstOrDefault(kvp => kvp.Key.Name is "Exiled Loader");

if (kvp.Value is null)
{
// No debug in config :(
// not printed because ppl can just not use Exiled, but having Exiled.Loader and not API / CI is suspicious
// Logger.Debug(ErrorMessage + "Failed to get ExiledLoader Plugin");
return;
}

Assembly? exiledLoaderAssembly = kvp.Value;

object? plugin = exiledLoaderAssembly.GetType("Exiled.Loader.Loader")?.GetMethod("GetPlugin")?.Invoke(null, ["exiled_custom_items"]);

if (plugin is null)
{
// It's possible people use Exiled without CI, so I'll leave this out too
// Logger.Debug(ErrorMessage + "Failed to get CustomItems Plugin");
return;
}

object? nullableAssembly = plugin.GetType().GetProperty("Assembly")?.GetValue(plugin);

if (nullableAssembly is not Assembly customItemAssembly)
{
Logger.Warn(ErrorMessage + "Failed to get CustomItems Assembly");
return;
}

Type? customItem = customItemAssembly.GetType("Exiled.CustomItems.API.Features.CustomItem");

if (customItem is null)
{
Logger.Warn(ErrorMessage + "Failed to get CustomItem Type");
return;
}

CustomItemTrySpawnMethodInfo = customItem.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(method => method.Name is "TrySpawn" && method.GetParameters().Any(parameter => parameter.ParameterType == typeof(string)));

if (CustomItemTrySpawnMethodInfo is null)
Logger.Warn(ErrorMessage + "Failed to get CustomItem.TrySpawn method");

Assembly? apiAssembly = PluginLoader.Dependencies.FirstOrDefault(assembly => assembly.GetName().Name is "Exiled.API");

if (apiAssembly is null)
{
Logger.Warn(ErrorMessage + "Failed to get Exiled.API Assembly");
return;
}

Type? pickupType = apiAssembly.GetType("Exiled.API.Features.Pickups.Pickup");

if (pickupType is null)
{
Logger.Warn(ErrorMessage + "Failed to get Pickup Type");
return;
}

PickupGetBaseMethodInfo = pickupType.GetProperty("Base")?.GetGetMethod();

if (PickupGetBaseMethodInfo is null)
{
Logger.Warn(ErrorMessage + "Failed to get Pickup::Base getter method");
}
}
}
}
12 changes: 11 additions & 1 deletion Features/Serializable/Schematics/SchematicBlockData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,17 @@ private GameObject CreatePickup(SchematicObject schematicObject)
if (Properties.TryGetValue("Chance", out object property) && UnityEngine.Random.Range(0, 101) > Convert.ToSingle(property))
return new("Empty Pickup");

Pickup pickup = Pickup.Create((ItemType)Convert.ToInt32(Properties["ItemType"]), Vector3.zero)!;
Pickup? pickup = null;
if (Properties["CustomItem"] is string str && str != string.Empty)
{
ExiledExtensions.TrySpawnCustomItem(str, Vector3.zero, out pickup);
}

pickup ??= Pickup.Create((ItemType)Convert.ToInt32(Properties["ItemType"]), Vector3.zero);

if (pickup is null)
return new("Empty Pickup");

if (Properties.ContainsKey("Locked"))
PickupEventsHandler.ButtonPickups.Add(pickup.Serial, schematicObject);

Expand Down
10 changes: 9 additions & 1 deletion Features/Serializable/SerializableItemSpawnpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace ProjectMER.Features.Serializable;
public class SerializableItemSpawnpoint : SerializableObject, IIndicatorDefinition
{
public ItemType ItemType { get; set; } = ItemType.Lantern;
public string CustomItem { get; set; } = string.Empty;
public float Weight { get; set; } = -1;
public string AttachmentsCode { get; set; } = "-1";
public uint NumberOfItems { get; set; } = 1;
Expand Down Expand Up @@ -41,7 +42,14 @@ public class SerializableItemSpawnpoint : SerializableObject, IIndicatorDefiniti

for (int i = 0; i < NumberOfItems; i++)
{
Pickup pickup = Pickup.Create(ItemType, position, rotation, Scale)!;
Pickup? pickup = CustomItem != string.Empty ? ExiledExtensions.TrySpawn(CustomItem, position, rotation, Scale) : Pickup.Create(ItemType, position, rotation, Scale);

if (pickup is null)
{
// there is no Debug config option?
// Logger.Debug($"Failed to spawn an item with CustomItem: {CustomItem}", true);
continue;
}

pickup.Transform.parent = itemSpawnPoint.transform;
if (Weight != -1)
Expand Down
6 changes: 5 additions & 1 deletion ProjectMER.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using ProjectMER.Configs;
using ProjectMER.Events.Handlers.Internal;
using ProjectMER.Features;
using ProjectMER.Features.Extensions;

namespace ProjectMER;

Expand Down Expand Up @@ -90,6 +91,9 @@ public override void Enable()

Logger.Debug("FileSystemWatcher enabled!");
}

// plugin loading is synchronous (minus exiled delaying 1 tick), so after all plugins loaded, try checking for Exiled and Exiled CI
Timing.CallDelayed(1, ExiledExtensions.TryInitialize);
}

private void OnMapFileChanged(object _, FileSystemEventArgs ev)
Expand Down Expand Up @@ -131,7 +135,7 @@ public override void Disable()

public override string Author => "Michal78900";

public override Version Version => new Version(2025, 8, 4, 2);
public override Version Version => new Version(2025, 11, 2, 1);

public override Version RequiredApiVersion => new Version(1, 0, 0, 0);
}