244 lines
7.2 KiB
C#
244 lines
7.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using HG;
|
|
using HG.Reflection;
|
|
using JetBrains.Annotations;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
|
|
namespace RoR2.Items;
|
|
|
|
public abstract class BaseItemBodyBehavior : MonoBehaviour
|
|
{
|
|
private struct ItemTypePair
|
|
{
|
|
public ItemIndex itemIndex;
|
|
|
|
public Type behaviorType;
|
|
}
|
|
|
|
private struct NetworkContextSet
|
|
{
|
|
public ItemTypePair[] itemTypePairs;
|
|
|
|
public FixedSizeArrayPool<BaseItemBodyBehavior> behaviorArraysPool;
|
|
|
|
public void SetItemTypePairs(List<ItemTypePair> itemTypePairs)
|
|
{
|
|
this.itemTypePairs = itemTypePairs.ToArray();
|
|
behaviorArraysPool = new FixedSizeArrayPool<BaseItemBodyBehavior>(this.itemTypePairs.Length);
|
|
}
|
|
}
|
|
|
|
[MeansImplicitUse]
|
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
|
|
public class ItemDefAssociationAttribute : HG.Reflection.SearchableAttribute
|
|
{
|
|
public Type behaviorTypeOverride;
|
|
|
|
public bool useOnServer = true;
|
|
|
|
public bool useOnClient = true;
|
|
}
|
|
|
|
public int stack;
|
|
|
|
private static NetworkContextSet server;
|
|
|
|
private static NetworkContextSet client;
|
|
|
|
private static NetworkContextSet shared;
|
|
|
|
private static CharacterBody earlyAssignmentBody = null;
|
|
|
|
private static Dictionary<UnityObjectWrapperKey<CharacterBody>, BaseItemBodyBehavior[]> bodyToItemBehaviors = new Dictionary<UnityObjectWrapperKey<CharacterBody>, BaseItemBodyBehavior[]>();
|
|
|
|
public CharacterBody body { get; private set; }
|
|
|
|
protected void Awake()
|
|
{
|
|
body = earlyAssignmentBody;
|
|
earlyAssignmentBody = null;
|
|
}
|
|
|
|
[SystemInitializer(new Type[] { typeof(ItemCatalog) })]
|
|
private static void Init()
|
|
{
|
|
List<ItemTypePair> list = new List<ItemTypePair>();
|
|
List<ItemTypePair> list2 = new List<ItemTypePair>();
|
|
List<ItemTypePair> list3 = new List<ItemTypePair>();
|
|
List<ItemDefAssociationAttribute> list4 = new List<ItemDefAssociationAttribute>();
|
|
HG.Reflection.SearchableAttribute.GetInstances(list4);
|
|
Type typeFromHandle = typeof(BaseItemBodyBehavior);
|
|
Type typeFromHandle2 = typeof(ItemDef);
|
|
foreach (ItemDefAssociationAttribute item in list4)
|
|
{
|
|
if (!(item.target is MethodInfo methodInfo))
|
|
{
|
|
Debug.LogError("ItemDefAssociationAttribute cannot be applied to object of type '" + item?.GetType().FullName + "'");
|
|
continue;
|
|
}
|
|
if (!methodInfo.IsStatic)
|
|
{
|
|
Debug.LogError("ItemDefAssociationAttribute cannot be applied to method " + methodInfo.DeclaringType.FullName + "." + methodInfo.Name + ": Method is not static.");
|
|
continue;
|
|
}
|
|
Type type = item.behaviorTypeOverride ?? methodInfo.DeclaringType;
|
|
if (!typeFromHandle.IsAssignableFrom(type))
|
|
{
|
|
Debug.LogError("ItemDefAssociationAttribute cannot be applied to method " + methodInfo.DeclaringType.FullName + "." + methodInfo.Name + ": " + methodInfo.DeclaringType.FullName + " does not derive from " + typeFromHandle.FullName + ".");
|
|
continue;
|
|
}
|
|
if (type.IsAbstract)
|
|
{
|
|
Debug.LogError("ItemDefAssociationAttribute cannot be applied to method " + methodInfo.DeclaringType.FullName + "." + methodInfo.Name + ": " + methodInfo.DeclaringType.FullName + " is an abstract type.");
|
|
continue;
|
|
}
|
|
if (!typeFromHandle2.IsAssignableFrom(methodInfo.ReturnType))
|
|
{
|
|
Debug.LogError(string.Format("{0} cannot be applied to method {1}.{2}: {3}.{4} returns type '{5}' instead of {6}.", "ItemDefAssociationAttribute", methodInfo.DeclaringType.FullName, methodInfo.Name, methodInfo.DeclaringType.FullName, methodInfo, methodInfo.ReturnType?.FullName ?? "void", typeFromHandle2.FullName));
|
|
continue;
|
|
}
|
|
if (methodInfo.GetGenericArguments().Length != 0)
|
|
{
|
|
Debug.LogError(string.Format("{0} cannot be applied to method {1}.{2}: {3}.{4} must take no arguments.", "ItemDefAssociationAttribute", methodInfo.DeclaringType.FullName, methodInfo.Name, methodInfo.DeclaringType.FullName, methodInfo));
|
|
continue;
|
|
}
|
|
ItemDef itemDef = (ItemDef)methodInfo.Invoke(null, Array.Empty<object>());
|
|
if (!itemDef)
|
|
{
|
|
Debug.LogError(methodInfo.DeclaringType.FullName + "." + methodInfo.Name + " returned null.");
|
|
continue;
|
|
}
|
|
if (itemDef.itemIndex < ItemIndex.Count)
|
|
{
|
|
Debug.LogError($"{methodInfo.DeclaringType.FullName}.{methodInfo.Name} returned an ItemDef that's not registered in the ItemCatalog. result={itemDef}");
|
|
continue;
|
|
}
|
|
if (item.useOnServer)
|
|
{
|
|
list.Add(new ItemTypePair
|
|
{
|
|
itemIndex = itemDef.itemIndex,
|
|
behaviorType = type
|
|
});
|
|
}
|
|
if (item.useOnClient)
|
|
{
|
|
list2.Add(new ItemTypePair
|
|
{
|
|
itemIndex = itemDef.itemIndex,
|
|
behaviorType = type
|
|
});
|
|
}
|
|
if (item.useOnServer || item.useOnClient)
|
|
{
|
|
list3.Add(new ItemTypePair
|
|
{
|
|
itemIndex = itemDef.itemIndex,
|
|
behaviorType = type
|
|
});
|
|
}
|
|
}
|
|
server.SetItemTypePairs(list);
|
|
client.SetItemTypePairs(list2);
|
|
shared.SetItemTypePairs(list3);
|
|
CharacterBody.onBodyAwakeGlobal += OnBodyAwakeGlobal;
|
|
CharacterBody.onBodyDestroyGlobal += OnBodyDestroyGlobal;
|
|
CharacterBody.onBodyInventoryChangedGlobal += OnBodyInventoryChangedGlobal;
|
|
}
|
|
|
|
private static ref NetworkContextSet GetNetworkContext()
|
|
{
|
|
bool active = NetworkServer.active;
|
|
bool active2 = NetworkClient.active;
|
|
if (active)
|
|
{
|
|
if (active2)
|
|
{
|
|
return ref shared;
|
|
}
|
|
return ref server;
|
|
}
|
|
if (active2)
|
|
{
|
|
return ref client;
|
|
}
|
|
throw new InvalidOperationException("Neither server nor client is running.");
|
|
}
|
|
|
|
private static void OnBodyAwakeGlobal(CharacterBody body)
|
|
{
|
|
BaseItemBodyBehavior[] value = GetNetworkContext().behaviorArraysPool.Request();
|
|
bodyToItemBehaviors.Add(body, value);
|
|
}
|
|
|
|
private static void OnBodyDestroyGlobal(CharacterBody body)
|
|
{
|
|
BaseItemBodyBehavior[] array = bodyToItemBehaviors[body];
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
UnityEngine.Object.Destroy(array[i]);
|
|
}
|
|
bodyToItemBehaviors.Remove(body);
|
|
if (NetworkServer.active || NetworkClient.active)
|
|
{
|
|
GetNetworkContext().behaviorArraysPool.Return(array);
|
|
}
|
|
}
|
|
|
|
private static void OnBodyInventoryChangedGlobal(CharacterBody body)
|
|
{
|
|
UpdateBodyItemBehaviorStacks(body);
|
|
}
|
|
|
|
private static void UpdateBodyItemBehaviorStacks(CharacterBody body)
|
|
{
|
|
ref NetworkContextSet networkContext = ref GetNetworkContext();
|
|
BaseItemBodyBehavior[] array = bodyToItemBehaviors[body];
|
|
ItemTypePair[] itemTypePairs = networkContext.itemTypePairs;
|
|
Inventory inventory = body.inventory;
|
|
if ((bool)inventory)
|
|
{
|
|
for (int i = 0; i < itemTypePairs.Length; i++)
|
|
{
|
|
ItemTypePair itemTypePair = itemTypePairs[i];
|
|
SetItemStack(body, ref array[i], itemTypePair.behaviorType, inventory.GetItemCount(itemTypePair.itemIndex));
|
|
}
|
|
return;
|
|
}
|
|
for (int j = 0; j < itemTypePairs.Length; j++)
|
|
{
|
|
ref BaseItemBodyBehavior reference = ref array[j];
|
|
if ((object)reference != null)
|
|
{
|
|
UnityEngine.Object.Destroy(reference);
|
|
reference = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void SetItemStack(CharacterBody body, ref BaseItemBodyBehavior behavior, Type behaviorType, int stack)
|
|
{
|
|
if ((object)behavior == null != stack <= 0)
|
|
{
|
|
if (stack <= 0)
|
|
{
|
|
UnityEngine.Object.Destroy(behavior);
|
|
behavior = null;
|
|
}
|
|
else
|
|
{
|
|
earlyAssignmentBody = body;
|
|
behavior = (BaseItemBodyBehavior)body.gameObject.AddComponent(behaviorType);
|
|
earlyAssignmentBody = null;
|
|
}
|
|
}
|
|
if ((object)behavior != null)
|
|
{
|
|
behavior.stack = stack;
|
|
}
|
|
}
|
|
}
|