RealityStop / Bolt.Addons.Community

A community-driven project for extending Unity Bolt
MIT License
253 stars 34 forks source link

Error with Defined Events in Builds #73

Open ron77950 opened 1 month ago

ron77950 commented 1 month ago

I recently updated to the newest release in my project and all of my graphs using Defined Events started throwing errors in build only (they work when played in editor still).

Here's more information as well: I tried release 3.1.2 and it still worked normally so the bug is in release 3.2, the project is in Unity Editor 2021.3.29f1, and here's the error stack.

ArgumentNullException: Value cannot be null.
Parameter name: type
  at System.Reflection.IntrospectionExtensions.GetTypeInfo (System.Type type) [0x00009] in <47fc8c70fa834cbf8141d7c1a7589125>:0 
  at Unity.VisualScripting.Community.DefinedEventNode.ConstructHook (UnityEngine.GameObject target, System.Type eventType) [0x0000a] in [...]\Library\PackageCache\dev.bolt.addons@9da87745de\Runtime\Events\Nodes\DefinedEventNode.cs:167 
  at Unity.VisualScripting.Community.DefinedEventNode.GetHook (Unity.VisualScripting.GraphReference reference) [0x00009] in [...]\Library\PackageCache\dev.bolt.addons@9da87745de\Runtime\Events\Nodes\DefinedEventNode.cs:160 
  at Unity.VisualScripting.EventUnit`1[TArgs].StartListening (Unity.VisualScripting.GraphStack stack) [0x0003a] in [...]\Library\PackageCache\com.unity.visualscripting@1.8.0\Runtime\VisualScripting.Flow\Framework\Events\EventUnit.cs:66 
  at Unity.VisualScripting.GameObjectEventUnit`1[TArgs].StartListening (Unity.VisualScripting.GraphStack stack, System.Boolean updateTarget) [0x0005a] in [...]\Library\PackageCache\com.unity.visualscripting@1.8.0\Runtime\VisualScripting.Flow\Framework\Events\GameObjectEventUnit.cs:96 
  at Unity.VisualScripting.GameObjectEventUnit`1[TArgs].StartListening (Unity.VisualScripting.GraphStack stack) [0x00001] in [...]\Library\PackageCache\com.unity.visualscripting@1.8.0\Runtime\VisualScripting.Flow\Framework\Events\GameObjectEventUnit.cs:101 
  at Unity.VisualScripting.FlowGraph.StartListening (Unity.VisualScripting.GraphStack stack) [0x00026] in [...]\Library\PackageCache\com.unity.visualscripting@1.8.0\Runtime\VisualScripting.Flow\FlowGraph.cs:48 
  at Unity.VisualScripting.SubgraphUnit.StartListening (Unity.VisualScripting.GraphStack stack) [0x0000d] in [...]\Library\PackageCache\com.unity.visualscripting@1.8.0\Runtime\VisualScripting.Flow\SubgraphUnit.cs:142 
  at Unity.VisualScripting.FlowGraph.StartListening (Unity.VisualScripting.GraphStack stack) [0x00026] in [...]\Library\PackageCache\com.unity.visualscripting@1.8.0\Runtime\VisualScripting.Flow\FlowGraph.cs:48 
  at Unity.VisualScripting.XGraphEventListener.StartListening (Unity.VisualScripting.IGraphEventListener listener, Unity.VisualScripting.GraphReference reference) [0x00009] in [...]\Library\PackageCache\com.unity.visualscripting@1.8.0\Runtime\VisualScripting.Core\Listeners\IGraphEventListener.cs:18 
  at Unity.VisualScripting.ScriptMachine.OnEnable () [0x0000c] in [...]\Library\PackageCache\com.unity.visualscripting@1.8.0\Runtime\VisualScripting.Flow\ScriptMachine.cs:22 

My best guess on this cause of the problem is the eventType being left null after it's being replaced with NeweventType on the Defined Event node in the new version. Here's the json that changed in my ScriptGraphAsset for reference (I replaced my custom event class namespace with -mycustomeventclass-):

Old

{"eventType":"-mycustomeventclass-","coroutine":false,"defaultValues":{"target":null},"position":{"x":-617.0,"y":-92.0},"guid":"88296867-6217-4075-b0a7-a1158a9f6964","$version":"A","$type":"Unity.VisualScripting.Community.DefinedEventNode","$id":"9"}

New

{"eventType":null,"NeweventType":{"type":"-mycustomeventclass-"},"coroutine":false,"defaultValues":{"target":null},"position":{"x":-625.0,"y":-92.0},"guid":"49596e6c-1502-4e2f-9314-b6fd01d75124","$version":"A","$type":"Unity.VisualScripting.Community.DefinedEventNode","$id":"18"}

P.S. I think you need to add "com.unity.nuget.newtonsoft-json": "3.2.1" to the package dependencies in the newest version as it's used in the MethodDeclaration.cs. Just something I ran into while testing.

S2NX7 commented 1 month ago

I recently updated to the newest release in my project and all of my graphs using Defined Events started throwing errors in build only (they work when played in editor still).

Here's more information as well: I tried release 3.1.2 and it still worked normally so the bug is in release 3.2, the project is in Unity Editor 2021.3.29f1, and here's the error stack.

Oh ok I will fix this thanks!

P.S. I think you need to add "com.unity.nuget.newtonsoft-json": "3.2.1" to the package dependencies in the newest version as it's used in the MethodDeclaration.cs. Just something I ran into while testing.

In the newest version i am working on does not use Newtonsoft anymore so it will not be needed.

S2NX7 commented 1 month ago

Can you replace the DefinedEventNode script with this:


using Unity.VisualScripting.Community.Utility;
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;

namespace Unity.VisualScripting.Community
{
    /// <summary>
    /// Listens for an event by type, rather than by name.  In other respects, it acts similar to the built-in Custom Unit
    /// </summary>
    [UnitCategory("Events/Community")]
    [UnitTitle("Defined Event")]
    [RenamedFrom("Bolt.Addons.Community.DefinedEvents.Units.DefinedEvent")]
    [RenamedFrom("Bolt.Addons.Community.DefinedEvents.Units.DefinedEventUnit")]
    public class DefinedEventNode : GameObjectEventUnit<DefinedEventArgs>, IDefinedEventNode
    {
        const string EventName = "OnDefinedEvent";

        #region Previous Event Type Handling (for backward compatibility)
        [SerializeAs(nameof(eventType))]
        private System.Type _eventType;

        [DoNotSerialize]
        //[InspectableIf(nameof(IsNotRestricted))]
        public System.Type eventType
        {
            get {
                return _eventType; }
            set
            {
                _eventType = value;
            }
        }

        [DoNotSerialize]
        //[UnitHeaderInspectable]
        //[InspectableIf(nameof(IsRestricted))]
        public System.Type restrictedEventType
        {
            get
            {
                return _eventType;
            }
            set
            {
                _eventType = value;
            }
        }

        #endregion

        #region New Event Type Handling
        [SerializeAs(nameof(NeweventType))]
        private IDefinedEventType New_eventType;

        [DoNotSerialize]
        public IDefinedEventType NeweventType
        {
            get { return New_eventType; }
            set { New_eventType = value; }
        }

        [DoNotSerialize]
        [UnitHeaderInspectable]
        [InspectableIf(nameof(IsRestricted))]
        public IDefinedEventType NewrestrictedEventType
        {
            get { return New_eventType; }
            set { New_eventType = value; }
        }

        public bool IsRestricted
        {
            get { return CommunityOptionFetcher.DefinedEvent_RestrictEventTypes; }
        }

        public bool IsNotRestricted
        {
            get { return !IsRestricted; }
        }
#endregion

        [DoNotSerialize]
        public List<ValueOutput> outputPorts { get; } = new List<ValueOutput>();

        [DoNotSerialize]
        private ReflectedInfo Info;

        public override Type MessageListenerType => null;

        protected override string hookName => EventName;

        protected override bool ShouldTrigger(Flow flow, DefinedEventArgs args)
        {
            return args.eventData.GetType() == NeweventType.type;
        }

        protected override void Definition()
        {
            base.Definition();

            // For backward compatibility, convert the Type to IDefinedEventType
            if (restrictedEventType != null)
            {
                NewrestrictedEventType = new IDefinedEventType(restrictedEventType);
                restrictedEventType = null;
            }

            if (NewrestrictedEventType == null)
            {
                NewrestrictedEventType = new IDefinedEventType();
            }

            BuildFromInfo();
        }

        private void BuildFromInfo()
        {
            outputPorts.Clear();
            if (NeweventType.type == null)
                return;

            Info = ReflectedInfo.For(NeweventType.type);
            foreach (var field in Info.reflectedFields)
            {
                outputPorts.Add(ValueOutput(field.Value.FieldType, field.Value.Name));
            }

            foreach (var property in Info.reflectedProperties)
            {
                outputPorts.Add(ValueOutput(property.Value.PropertyType, property.Value.Name));
            }
        }

        protected override void AssignArguments(Flow flow, DefinedEventArgs args)
        {
            for (var i = 0; i < outputPorts.Count; i++)
            {
                var outputPort = outputPorts[i];
                var key = outputPort.key;
                if (Info.reflectedFields.ContainsKey(key))
                {
                    var reflectedField = Info.reflectedFields[key];
                    flow.SetValue(outputPort, reflectedField.GetValue(args.eventData));
                }
                else if (Info.reflectedProperties.ContainsKey(key))
                {
                    var reflectedProperty = Info.reflectedProperties[key];
                    flow.SetValue(outputPort, reflectedProperty.GetValue(args.eventData));
                }
            } 
        }
        public override EventHook GetHook(GraphReference reference)
        {
            var refData = reference.GetElementData<Data>(this);
            return ConstructHook(refData.target, New_eventType.type);
        }

        private static EventHook ConstructHook(GameObject target, Type eventType)
        {
            EventHook hook;
            if (DefinedEventSupport.IsOptimized())
                hook = new EventHook(EventName, target, eventType.GetTypeInfo().FullName);
            else
                hook = new EventHook(EventName, target);
            return hook;
        }

        public static void Trigger(GameObject target,object eventData)
        {
            var eventHook = ConstructHook(target, eventData.GetType());
            EventBus.Trigger(eventHook, new DefinedEventArgs(eventData));
        }

        public static IDisposable RegisterListener<T>(GameObject target, Action<T> onEvent) 
        {
            var eventHook = ConstructHook(target, typeof(T));
            Action<DefinedEventArgs> action = (x) => {
                if (x.eventData.GetType() == typeof(T))
                    onEvent((T)x.eventData);
            };
            EventBus.Register<DefinedEventArgs>(eventHook, action);

            return Disposable.Create(() => { EventBus.Unregister(eventHook, action); });
        }
    }
}

This should fix the error.

ron77950 commented 1 month ago

This does seem to fix it, thanks!

ron77950 commented 1 month ago

Any chance this fix will be added to the github relatively soon, so I can use the git url to add the package in my project again?

S2NX7 commented 1 month ago

Any chance this fix will be added to the github relatively soon, so I can use the git url to add the package in my project again?

Mmm not sure because the C# Generators might take awhile to finish, If you do really need it i can add the fix by its self now.

ron77950 commented 1 month ago

Any chance this fix will be added to the github relatively soon, so I can use the git url to add the package in my project again?

Mmm not sure because the C# Generators might take awhile to finish, If you do really need it i can add the fix by its self now.

It doesn't have to be really soon. I just wondered if it's something I could get within the next week or 2 or if it'd be months.

rktvr commented 1 week ago

the same issue with the newtonsoft namespace exists in the methoddeclaration script

S2NX7 commented 1 week ago

the same issue with the newtonsoft namespace exists in the methoddeclaration script

You can replace the script with this:

using Unity.VisualScripting;
using System;
using UnityEngine;
using System.Collections.Generic;
using Unity.VisualScripting.Community.Utility;
using Unity.VisualScripting.Community.Libraries.CSharp;
using System.Reflection;

namespace Unity.VisualScripting.Community
{
    [Serializable]
    [Inspectable]
    [TypeIcon(typeof(Method))]
    [RenamedFrom("Bolt.Addons.Community.Code.MethodDeclaration")]
    public abstract class MethodDeclaration : Macro<FlowGraph>
    {
        [Inspectable]
        public string methodName;

        [Inspectable]
        [TypeFilter(Abstract = true, Classes = true, Enums = true, Generic = false, Interfaces = true,
            Nested = true, NonPublic = false, NonSerializable = true, Object = true, Obsolete = false, OpenConstructedGeneric = false,
            Primitives = true, Public = true, Reference = true, Sealed = true, Static = false, Structs = true, Value = true)]
        public Type returnType = typeof(Libraries.CSharp.Void);

        [SerializeField]
        [HideInInspector]
        private string qualifiedReturnTypeName;

        public int genericParameterCount = 0;

        public ValueTuple<int, List<Type>> genericParameterConstraints = new ValueTuple<int, List<Type>>();
        public ValueTuple<int, List<GenericParameterAttributes>> genericParameterAttributes = new ValueTuple<int, List<GenericParameterAttributes>>();

        /// <summary>
        /// Left this to not overwrite current methodParameters
        /// and instead get the parameters from this and move it to the serializtion variable
        /// </summary>
        [SerializeField]
        [HideInInspector]
        private string serializedParams;

        [SerializeField]
        [HideInInspector]
        private SerializationData serialization;

        [Inspectable]
        public ClassAsset classAsset;

        [Inspectable]
        public StructAsset structAsset;

        [Serialize]
        [InspectorWide]
        public List<TypeParam> parameters = new List<TypeParam>();

        [Serialize]
        public List<AttributeDeclaration> attributes = new List<AttributeDeclaration>();

        public AccessModifier scope = AccessModifier.Public;

        public MethodModifier modifier = MethodModifier.None;

        public Action OnSerialized;
#if UNITY_EDITOR
        public bool opened;
        public bool parametersOpened;
        public bool attributesOpened;
#endif

        public override FlowGraph DefaultGraph()
        {
            return new FlowGraph();
        }

        protected override void OnAfterDeserialize()
        {
            base.OnAfterDeserialize();

            if (!(string.IsNullOrWhiteSpace(qualifiedReturnTypeName) || string.IsNullOrEmpty(qualifiedReturnTypeName)))
            {
                returnType = Type.GetType(qualifiedReturnTypeName);
            }

            foreach (var param in parameters)
            {
                param.OnAfterDeserialize();
            }

            OnSerialized?.Invoke();
        }

        protected override void OnBeforeSerialize()
        {
            base.OnBeforeSerialize();

            if (returnType == null)
            {
                qualifiedReturnTypeName = string.Empty;
                return;
            }

            qualifiedReturnTypeName = returnType.AssemblyQualifiedName;
            foreach (var param in parameters)
            {
                param.OnBeforeSerialize();
            }
        }
    }
    /// <summary>
    /// This is a empty class used for the typeIcon
    /// it does not have any functionality
    /// </summary>
    public class Method
    {
    }
}

if it gives you more errors then send them

rktvr commented 1 week ago

nice, that fixes it