Callbacks not functioning properly in IL2CPP Builds

Here is the context: I am working on a project with multiple developers. One of our senior developers made this script for our project that seems to work in most circumstances. However, at that time we were working with an older version of Android. And now that we’ve upgraded to Android 15, which requires me to switch the Scripting Backend to IL2CPP in order to install it properly, the script no longer works. Instead, it is giving an infinite loop of null reference exceptions.

Here is the exact error code that I am getting:

--- App Info ---
App name: Endarth-LWRP
Bundle identifier: com.DefaultCompany.EndarthLWRP
App version: 1.0 (1)
Unity version: 6000.0.25f1

--- Device Info ---
Device name: Galaxy Note20 5G
Device model: samsung SM-N981B
Operation system: Android OS 13 / API-33 (TP1A.220624.014/N981BXXSDHXH1)
System language: Spanish
Device orientation: FaceUp
Connectivity: Local (LAN/Wifi)

--- CPU Info ---
CPU type: ARM64 FP ASIMD AES (8 core(s))
CPU speed: 2730 MHz
System memory size: 7443 MB
Allocated memory: 391,65 MB
Reserved memory: 559,37 MB
Mono used memory: 401,20 MB

--- GPU Info ---
GPU: Mali-G77
Graphic memory size: 2048 MB
Screen size: 1920x1080@60Hz
Screen dpi: 160



[11:59:10.16]  NullReferenceException: Object reference not set to an instance of an object.

Microsoft.CSharp.RuntimeBinder.Semantics.ExpressionTreeRewriter.VisitBoundLambda (Microsoft.CSharp.RuntimeBinder.Semantics.ExprBoundLambda anonmeth) (at <00000000000000000000000000000000>:0)
Microsoft.CSharp.RuntimeBinder.Semantics.ExpressionTreeRewriter.Rewrite (Microsoft.CSharp.RuntimeBinder.Semantics.ExprBoundLambda expr) (at <00000000000000000000000000000000>:0)
Microsoft.CSharp.RuntimeBinder.RuntimeBinder.CreateExpressionTreeFromResult (System.Linq.Expressions.Expression[] parameters, Microsoft.CSharp.RuntimeBinder.Semantics.Scope pScope, Microsoft.CSharp.RuntimeBinder.Semantics.Expr pResult) (at <00000000000000000000000000000000>:0)
Microsoft.CSharp.RuntimeBinder.RuntimeBinder.BindCore (Microsoft.CSharp.RuntimeBinder.ICSharpBinder payload, System.Linq.Expressions.Expression[] parameters, System.Dynamic.DynamicMetaObject[] args, System.Dynamic.DynamicMetaObject& deferredBinding) (at <00000000000000000000000000000000>:0)
Microsoft.CSharp.RuntimeBinder.RuntimeBinder.Bind (Microsoft.CSharp.RuntimeBinder.ICSharpBinder payload, System.Linq.Expressions.Expression[] parameters, System.Dynamic.DynamicMetaObject[] args, System.Dynamic.DynamicMetaObject& deferredBinding) (at <00000000000000000000000000000000>:0)
Microsoft.CSharp.RuntimeBinder.BinderHelper.Bind (Microsoft.CSharp.RuntimeBinder.ICSharpBinder action, Microsoft.CSharp.RuntimeBinder.RuntimeBinder binder, System.Dynamic.DynamicMetaObject[] args, System.Collections.Generic.IEnumerable`1[T] arginfos, System.Dynamic.DynamicMetaObject onBindingError) (at <00000000000000000000000000000000>:0)
Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder.FallbackInvokeMember (System.Dynamic.DynamicMetaObject target, System.Dynamic.DynamicMetaObject[] args, System.Dynamic.DynamicMetaObject errorSuggestion) (at <00000000000000000000000000000000>:0)
System.Dynamic.InvokeMemberBinder.FallbackInvokeMember (System.Dynamic.DynamicMetaObject target, System.Dynamic.DynamicMetaObject[] args) (at <00000000000000000000000000000000>:0)
System.Dynamic.DynamicMetaObject.BindInvokeMember (System.Dynamic.InvokeMemberBinder binder, System.Dynamic.DynamicMetaObject[] args) (at <00000000000000000000000000000000>:0)
System.Dynamic.InvokeMemberBinder.Bind (System.Dynamic.DynamicMetaObject target, System.Dynamic.DynamicMetaObject[] args) (at <00000000000000000000000000000000>:0)
System.Dynamic.DynamicMetaObjectBinder.Bind (System.Object[] args, System.Collections.ObjectModel.ReadOnlyCollection`1[T] parameters, System.Linq.Expressions.LabelTarget returnLabel) (at <00000000000000000000000000000000>:0)
System.Runtime.CompilerServices.CallSiteBinder.BindCore[T] (System.Runtime.CompilerServices.CallSite`1[T] site, System.Object[] args) (at <00000000000000000000000000000000>:0)
System.Runtime.CompilerServices.CallSiteOps.Bind[T] (System.Runtime.CompilerServices.CallSiteBinder binder, System.Runtime.CompilerServices.CallSite`1[T] site, System.Object[] args) (at <00000000000000000000000000000000>:0)
System.Reflection.RuntimeMethodInfo.InternalInvoke (System.Object obj, System.Object[] parameters, System.Exception& exc) (at <00000000000000000000000000000000>:0)
System.Reflection.RuntimeMethodInfo.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <00000000000000000000000000000000>:0)
System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at <00000000000000000000000000000000>:0)
System.Linq.Expressions.Interpreter.MethodInfoCallInstruction.Run (System.Linq.Expressions.Interpreter.InterpretedFrame frame) (at <00000000000000000000000000000000>:0)
System.Linq.Expressions.Interpreter.Interpreter.Run (System.Linq.Expressions.Interpreter.InterpretedFrame frame) (at <00000000000000000000000000000000>:0)
System.Linq.Expressions.Interpreter.LightLambda.RunVoid3[T0,T1,T2] (T0 arg0, T1 arg1, T2 arg2) (at <00000000000000000000000000000000>:0)
PreGameplaySelection.<SelectMapID>b__113_0 () (at <00000000000000000000000000000000>:0)
NFCReader.CardOnField (System.Object data) (at <00000000000000000000000000000000>:0)
UILerpPos..ctor () (at <00000000000000000000000000000000>:0)
UnityEngine.Events.InvokableCall`1[T1].Invoke (T1 args0) (at <00000000000000000000000000000000>:0)
UnityEngine.Events.UnityEvent`1[T0].Invoke (T0 arg0) (at <00000000000000000000000000000000>:0)
SerialPortUtility.SerialPortUtilityPro.UpdateOnStreaming (System.Boolean binaryMode) (at <00000000000000000000000000000000>:0)
SerialPortUtility.SerialPortUtilityPro.ReadUpdate () (at <00000000000000000000000000000000>:0)
SerialPortUtility.SerialPortUtilityPro.Update () (at <00000000000000000000000000000000>:0)
--- End of stack trace from previous location where exception was thrown ---
System.Linq.Expressions.Interpreter.ExceptionHelpers.UnwrapAndRethrow (System.Reflection.TargetInvocationException exception) (at <00000000000000000000000000000000>:0)
System.Linq.Expressions.Interpreter.MethodInfoCallInstruction.Run (System.Linq.Expressions.Interpreter.InterpretedFrame frame) (at <00000000000000000000000000000000>:0)
System.Linq.Expressions.Interpreter.Interpreter.Run (System.Linq.Expressions.Interpreter.InterpretedFrame frame) (at <00000000000000000000000000000000>:0)
System.Linq.Expressions.Interpreter.LightLambda.RunVoid3[T0,T1,T2] (T0 arg0, T1 arg1, T2 arg2) (at <00000000000000000000000000000000>:0)
PreGameplaySelection.<SelectMapID>b__113_0 () (at <00000000000000000000000000000000>:0)
NFCReader.CardOnField (System.Object data) (at <00000000000000000000000000000000>:0)
UILerpPos..ctor () (at <00000000000000000000000000000000>:0)
UnityEngine.Events.InvokableCall`1[T1].Invoke (T1 args0) (at <00000000000000000000000000000000>:0)
UnityEngine.Events.UnityEvent`1[T0].Invoke (T0 arg0) (at <00000000000000000000000000000000>:0)
SerialPortUtility.SerialPortUtilityPro.UpdateOnStreaming (System.Boolean binaryMode) (at <00000000000000000000000000000000>:0)
SerialPortUtility.SerialPortUtilityPro.ReadUpdate () (at <00000000000000000000000000000000>:0)
SerialPortUtility.SerialPortUtilityPro.Update () (at <00000000000000000000000000000000>:0)

I will also be including the code for the script that seems to be breaking. I am pretty sure that the problem is here.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using EndarthUtilities;
using System.Reflection;
using System.Linq;
using Sirenix.OdinInspector;
using System.Linq.Expressions;
using EndarthUtilities.DataModels;

#region STATIC
public static class NFCReaderTools
{
    public static NFCReader instance = null;

    private static void SetInstance()
    {
        if (instance == null)
        {
            instance = GameObject.FindObjectOfType<NFCReader>();
        }
    }

    #region Callbacks Subscriptions
    #region ADD
    public static void SubscribeCallbacks<T>(Action<T, int> callback, int receiver = -1) where T : class, new()
    {
        SetInstance();
        instance.SubscribeCallbacks(callback, receiver);
    }

    public static void SubscribeCallbacks<T>(Action<T> callback, int receiver = -1) where T : class, new()
    {
        SetInstance();
        instance.SubscribeCallbacks(callback, receiver);
    }

    public static void SubscribeCallbacks<T>(Action<T> callback, ScreenArea receiver) where T : class, new()
    {
        SubscribeCallbacks(callback, (int)receiver);
    }

    public static void SubscribeCallbacks<T>(Action<T, int> callback, ScreenArea receiver) where T : class, new()
    {
        SubscribeCallbacks(callback, (int)receiver);
    }
    #endregion

    #region Remove
    public static void RemoveCallback<T>(Action<T> callback, int receiver = -1) where T : class, new()
    {
        SetInstance();
        instance.RemoveCallback(callback, receiver);
    }

    public static void RemoveCallback<T>(Action<T, int> callback, int receiver = -1) where T : class, new()
    {
        SetInstance();
        instance.RemoveCallback(callback, receiver);
    }

    public static void RemoveCallback<T>(Action<T> callback, ScreenArea receiver) where T : class, new()
    {
        RemoveCallback(callback, (int)receiver);
    }

    public static void RemoveCallback<T>(Action<T, int> callback, ScreenArea receiver) where T : class, new()
    {
        RemoveCallback(callback, (int)receiver);
    }

    public static void ClearAntiSpamStatus(ScreenArea screenArea)
    {
        ClearAntiSpamStatus((int)screenArea);
    }

    public static void ClearAntiSpamStatus(int reader)
    {
        SetInstance();
        instance.ClearAntiSpamStatus(reader);
    }

    #endregion
    #endregion
}
#endregion

public class CardReader<T> where T : class, new()
{
    #region VARS
    private const int MaxNFCReaders = 12;
    private const char DataDivisor = '|';
    private const float MinTimeBetweenCards = 2f;
    private const string NfcDebugPrefix = "<color=red><b>[NFC]</b></color> ";

    private static readonly YieldInstruction antiSpamWait = new WaitForSeconds(MinTimeBetweenCards);

    private string[] lineContent;

    private List<Action<T>>[] onNewCard;
    private List<Action<object>>[] onNewGenericCard;
    private List<Action<T, int>>[] onNewCardWithReader;
    private List<Action<object, int>>[] onNewGenericCardWithReader;
    private Dictionary<uint, bool> eventsSent;

    private Dictionary<int, bool> antiSpamStatus = new Dictionary<int, bool>();
    private Dictionary<int, Coroutine> antiSpam = new Dictionary<int, Coroutine>();
    private Dictionary<int, int> lastCardID = new Dictionary<int, int>();
    private bool initialized = false;
    #endregion

    #region MAIN
    public CardReader()
    {
        Initialize();
    }

    void Initialize()
    {
        if (!initialized)
        {
            for (int i = 0; i < MaxNFCReaders; i++)
            {
                antiSpamStatus.Add(i, false);
                antiSpam.Add(i, Coroutines.StartCor(CardAntiSpam(i)));
                lastCardID.Add(i, i);
            }

            InitCallbackList();
            initialized = true;
        }
    }

    public void CardOnField(string info)
    {
        lineContent = info.Split(DataDivisor);

        if (lineContent.Length >= 8) //Los datos parecen correctos.
        {
            //Debug.Log(NfcDebugPrefix + "Los datos están estructurados.");
            NFCCardBase auxCard = new NFCCardBase();
            int reader;

            if (!uint.TryParse(lineContent[1], out uint serialMessageID))
            {
                //Debug.LogError(NfcDebugPrefix + "El id del mensaje no ha podido ser leido");
                return;
            }

            if (!int.TryParse(lineContent[2], out reader))
            {
                //Debug.LogError(NfcDebugPrefix + "El lector no ha podido ser identificado");
                return;
            }

            auxCard.RFIDType.Set(lineContent[3], lineContent[4]);
            auxCard.RFIDSubType.Set(lineContent[5], lineContent[6]);
            auxCard.SetID();

            if (!auxCard.Identified()) //No la hemos identificado.
            {
                //Se ha leido una carta que no tenemos ni puta idea de que es, probablemente con errores de lectura.
                if (auxCard.TryToRecoverInfo()) //Intentamos recuperar la info.
                {
                    //Debug.Log(NfcDebugPrefix + "Nos hemos recuperado de un error de lectura");
                }
                else
                {
                    //Debug.LogError("Error de lectura");
                    //No recuperada.
                    return;
                }
            }

            if (lastCardID[reader] == auxCard.id)
            {
                if (antiSpamStatus[reader])
                {
                    //Debug.Log(NfcDebugPrefix + "antiSpamEnable preventing card showing");
                    Coroutines.StopCor(antiSpam[reader]);
                    antiSpam[reader] = Coroutines.StartCor(CardAntiSpam(reader));
                    return;
                }
            }
            else
            {
                Coroutines.StopCor(antiSpam[reader]);
                antiSpamStatus[reader] = false;
            }

            lastCardID[reader] = auxCard.id;
            antiSpam[reader] = Coroutines.StartCor(CardAntiSpam(reader));

            if (!eventsSent.ContainsKey(serialMessageID))
            {
                eventsSent.Add(serialMessageID, false);
            }

            if (auxCard.TryGetInfo<T>(out T cardInfo))
            {
                PushEvent(cardInfo, reader);
            }
        }
        else
        {
            //Debug.Log(NfcDebugPrefix + "Los datos <color=red>NO</color> están estructurados.");
        }
    }
    #endregion

    #region Coroutines
    IEnumerator CardAntiSpam(int reader)
    {
        antiSpamStatus[reader] = true;
        yield return antiSpamWait;
        antiSpamStatus[reader] = false;
    }

    IEnumerator AddCallbackWhenInit(Action<T> newCallback, int receiver)
    {
        while (!initialized)
        {
            yield return null;
        }

        AddCallback(newCallback, receiver);
    }

    IEnumerator AddCallbackWhenInit(Action<T, int> newCallback, int receiver)
    {
        while (!initialized)
        {
            yield return null;
        }

        AddCallback(newCallback, receiver);
    }
    IEnumerator AddCallbackWhenInit(Action<object> newCallback, int receiver)
    {
        while (!initialized)
        {
            yield return null;
        }

        AddCallback(newCallback, receiver);
    }

    IEnumerator AddCallbackWhenInit(Action<object, int> newCallback, int receiver)
    {
        while (!initialized)
        {
            yield return null;
        }

        AddCallback(newCallback, receiver);
    }
    #endregion

    #region Callbacks
    private void InitCallbackList()
    {
        onNewCard = new List<Action<T>>[MaxNFCReaders];
        onNewCardWithReader = new List<Action<T, int>>[MaxNFCReaders];

        onNewGenericCard = new List<Action<object>>[MaxNFCReaders];
        onNewGenericCardWithReader = new List<Action<object, int>>[MaxNFCReaders];

        eventsSent = new Dictionary<uint, bool>();

        for (int i = 0; i < MaxNFCReaders; i++)
        {
            onNewCard[i] = new List<Action<T>>();
        }

        for (int i = 0; i < MaxNFCReaders; i++)
        {
            onNewGenericCard[i] = new List<Action<object>>();
        }

        for (int i = 0; i < MaxNFCReaders; i++)
        {
            onNewCardWithReader[i] = new List<Action<T, int>>();
        }

        for (int i = 0; i < MaxNFCReaders; i++)
        {
            onNewGenericCardWithReader[i] = new List<Action<object, int>>();
        }
    }

    #region ADD
    public void AddCallback(Action<object> newCallback, int receiver)
    {
        if (!initialized)
        {
            Coroutines.StartCor(AddCallbackWhenInit(newCallback, receiver));
        }
        else
        {
            onNewGenericCard[receiver].Add(newCallback);
        }
    }

    public void AddCallback(Action<object, int> newCallback, int receiver)
    {
        if (!initialized)
        {
            Coroutines.StartCor(AddCallbackWhenInit(newCallback, receiver));
        }
        else
        {
            onNewGenericCardWithReader[receiver].Add(newCallback);
        }
    }

    public void AddCallback(Action<T> newCallback, int receiver)
    {
        if (typeof(T) == typeof(object))
        {
            AddCallback((Action<object>)newCallback, receiver);
        }
        else
        {
            if (!initialized)
            {
                Coroutines.StartCor(AddCallbackWhenInit(newCallback, receiver));
            }
            else
            {
                onNewCard[receiver].Add(newCallback);
            }
        }
    }

    public void AddCallback(Action<T, int> newCallback, int receiver)
    {
        if (typeof(T) == typeof(object))
        {
            AddCallback((Action<object, int>)newCallback, receiver);
        }
        else
        {
            if (!initialized)
            {
                Coroutines.StartCor(AddCallbackWhenInit(newCallback, receiver));
            }
            else
            {
                onNewCardWithReader[receiver].Add(newCallback);
            }
        }
    }

    public void AddCallback(Action<T, int> newCallback)
    {
        for (int i = 0; i < MaxNFCReaders; i++)
        {
            AddCallback(newCallback, i);
        }
    }

    public void AddCallback(Action<T> newCallback)
    {
        for (int i = 0; i < MaxNFCReaders; i++)
        {
            AddCallback(newCallback, i);
        }
    }
    #endregion

    #region REMOVE

    public void RemoveCallback(Action<object> callback, int receiver)
    {
        onNewGenericCard[receiver].Remove(callback);
    }

    public void RemoveCallback(Action<object, int> callback, int receiver)
    {
        onNewGenericCardWithReader[receiver].Remove(callback);
    }

    public void RemoveCallback(Action<T> callback, int receiver)
    {
        onNewCard[receiver].Remove(callback);
    }

    public void RemoveCallback(Action<T, int> callback, int receiver)
    {
        onNewCardWithReader[receiver].Remove(callback);
    }

    public void RemoveCallback(Action<T> callback)
    {
        for (int i = 0; i < MaxNFCReaders; i++)
        {
            RemoveCallback(callback, i);
        }
    }

    public void RemoveCallback(Action<T, int> callback)
    {
        for (int i = 0; i < MaxNFCReaders; i++)
        {
            RemoveCallback(callback, i);
        }
    }

    public void ClearAntiSpamStatus(int reader)
    {
        Coroutines.StopCor(antiSpam[reader]);
        antiSpamStatus[reader] = false;
        lastCardID[reader] = -1;
    }
    #endregion

    private void PushEvent(T card, int Reader)
    {
        onNewCard[Reader]?.ForEach(x => x?.Invoke(card));
        onNewCardWithReader[Reader]?.ForEach(x => x?.Invoke(card, Reader));
        onNewGenericCard[Reader]?.ForEach(x => x?.Invoke(card));
        onNewGenericCardWithReader[Reader]?.ForEach(x => x?.Invoke(card, Reader));
    }

    #endregion
}

public class NFCReader : SerializedMonoBehaviour
{
    //Solo permitimos los tipos que tengamos definidos por el desarrollador.
    public bool strictMode = false;

    [ShowIf("strictMode")]
    //Tipos permitidos.
    public Type[] allowedTypes = new Type[] { typeof(object), typeof(NFCCardAbility), typeof(NFCCardWeapon), typeof(NFCCardSpell), typeof(NFCCardAction), typeof(NFCCardAccessory), typeof(NFCFigure), typeof(NFCCardConsum), typeof(NFCCardArmor), typeof(NFCGem), typeof(NFCCardTrap), typeof(NFCCardMod) };

    //Callbacks genericos.
    private Dictionary<Type, int> indexByCallbackType = new Dictionary<Type, int>();
    private List<dynamic> callbacksLists = new List<dynamic>();

    public void CardOnField(object data) //Serial Port
    {
        string cardInfo = data as string;
        Debug.Log($"CardOnField {data}");
        callbacksLists?.ForEach(x => x?.CardOnField(cardInfo));
    }

    #region Callbacks Subscriptions

    #region ADD
    public void SubscribeCallbacks<T>(Action<T, int> callback, int receiver = -1) where T : class, new()
    {
        if (strictMode && !allowedTypes.Contains(typeof(T)))
        {
            Debug.LogError("Sorry bruh... this type is not allowed by your dev, fuck it.");
            return;
        }

        if (indexByCallbackType.TryGetValue(typeof(T), out int index))
        {
            Debug.Log("Ya tenemos la lista de este tipo creada, lo añadimos a su lista <color=blue>" + typeof(T) + "</color>");

            if (receiver >= 0)
            {
                callbacksLists[index]?.AddCallback(callback, receiver);
            }
            else
            {
                callbacksLists[index]?.AddCallback(callback);
            }
        }
        else
        {
            Debug.Log("Añadiendo a la lista de callbacks el tipo <color=yellow>" + typeof(T) + "</color>");

            CardReader<T> newReader = new CardReader<T>();
            indexByCallbackType.Add(typeof(T), callbacksLists.Count);
            callbacksLists?.Add(newReader);

            if (receiver >= 0)
            {
                newReader.AddCallback(callback, receiver);
            }
            else
            {
                newReader.AddCallback(callback);
            }
        }
    }

    public void SubscribeCallbacks<T>(Action<T> callback, int receiver = -1) where T : class, new()
    {
        if (strictMode && !allowedTypes.Contains(typeof(T)))
        {
            Debug.LogError("Sorry bruh... " + typeof(T).ToString() + " is not allowed by your dev, fuck it.");
            return;
        }

        if (indexByCallbackType.TryGetValue(typeof(T), out int index))
        {
            if (receiver >= 0)
            {
                callbacksLists[index]?.AddCallback(callback, receiver);
            }
            else
            {
                callbacksLists[index]?.AddCallback(callback);
            }
        }
        else
        {
            CardReader<T> newReader = new CardReader<T>();
            indexByCallbackType.Add(typeof(T), callbacksLists.Count);
            callbacksLists?.Add(newReader);

            if (receiver >= 0)
            {
                newReader.AddCallback(callback, receiver);
            }
            else
            {
                newReader.AddCallback(callback);
            }
        }
    }

    public void SubscribeCallbacks<T>(Action<T> callback, ScreenArea receiver) where T : class, new()
    {
        SubscribeCallbacks(callback, (int)receiver);
    }

    public void SubscribeCallbacks<T>(Action<T, int> callback, ScreenArea receiver) where T : class, new()
    {
        SubscribeCallbacks(callback, (int)receiver);
    }
    #endregion

    #region Remove
    public void RemoveCallback<T>(Action<T> callback, int receiver = -1) where T : class, new()
    {
        if (strictMode && !allowedTypes.Contains(typeof(T)))
        {
            return;
        }

        if (indexByCallbackType.TryGetValue(typeof(T), out int index))
        {
            if (receiver >= 0)
            {
                callbacksLists[index]?.RemoveCallback(callback, receiver);
            }
            else
            {
                callbacksLists[index]?.RemoveCallback(callback);
            }
        }
    }

    public void RemoveCallback<T>(Action<T, int> callback, int receiver = -1) where T : class, new()
    {
        if (strictMode && !allowedTypes.Contains(typeof(T)))
        {
            return;
        }

        if (indexByCallbackType.TryGetValue(typeof(T), out int index))
        {
            if (receiver >= 0)
            {
                callbacksLists[index]?.RemoveCallback(callback, receiver);
            }
            else
            {
                callbacksLists[index]?.RemoveCallback(callback);
            }
        }
    }

    public void RemoveCallback<T>(Action<T> callback, ScreenArea receiver) where T : class, new()
    {
        RemoveCallback(callback, (int)receiver);
    }

    public void RemoveCallback<T>(Action<T, int> callback, ScreenArea receiver) where T : class, new()
    {
        RemoveCallback(callback, (int)receiver);
    }

    public void ClearAntiSpamStatus(ScreenArea screenArea)
    {
        ClearAntiSpamStatus((int)screenArea);
    }

    public void ClearAntiSpamStatus(int reader)
    {
        callbacksLists?.ForEach(x => x?.ClearAntiSpamStatus(reader));
    }

    #endregion
    #endregion
}

I believe the problem is related to callbacks. For some reason, the element inside the callback list is returning null, even though it is assigned normally and gives no errors at the time it is assigned. Unfortunately, I just don’t have enough experience with callbacks to know how to fix this. I would even be open to replacing the callback system with something else, but I’m not really sure where I would even begin. The other problem is the fact the original person who coded this script is long gone, and no one else at the company really knows how to tackle this issue either. Any sort of assistance would be duly appreciated. I have spent like 4 months trying to solve this and have yet to get anywhere.

Did you try attaching managed debugger - Unity - Manual: Debug C# code in Unity

and debugging the code?

Yes, that is how I got the error code in the first place. It’s the NullReferenceException that I posted in my original message, relating to the NFCReader script

From callstack, it seems nullreference comes from PreGameplaySelection ?

IL2CPP does not support dynamic - see the list of scripting restrictions.

You’ll have to update your List<dynamic> callbacks to a concrete type. If you really need some sort of dynamic behavior you’ll have to fallback to using reflection.

Although if I’m following your code I believe you could make CardReader<T> implement a generic interface like:

public interface ICardReader 
{
    void CardOnField(string info)
}

And change the callbacks list to List<ICardReader>