Browse Source

Add support for sender-specific events

Matt Benic 9 years ago
parent
commit
09c4b27b07

+ 99 - 2
Script/AtomicNET/AtomicNET/Core/AObject.cs

@@ -7,7 +7,32 @@ namespace AtomicEngine
 
     public partial class AObject : RefCounted
     {
-        internal Dictionary<uint, EventDelegate> eventHandlers = new Dictionary<uint, EventDelegate>();
+        private Dictionary<uint, EventDelegate> EventHandlers
+        {
+            get
+            {
+                if (eventHandlers == null)
+                {
+                    eventHandlers = new Dictionary<uint, EventDelegate>();
+                }
+                return eventHandlers;
+            }
+        }
+        private Dictionary<uint, EventDelegate> eventHandlers;
+
+        private Dictionary<SenderEventKey, SenderEventDelegate> SenderEventHandlers
+        {
+            get
+            {
+                if (senderEventHandlers == null)
+                {
+                    SenderEventKeyComparer comparer = SenderEventKeyComparer.Default;
+                    senderEventHandlers = new Dictionary<SenderEventKey, SenderEventDelegate>(comparer);
+                }
+                return senderEventHandlers;
+            }
+        }
+        private Dictionary<SenderEventKey, SenderEventDelegate> senderEventHandlers;
 
         public static T GetSubsystem<T>() where T : AObject
         {
@@ -19,10 +44,16 @@ namespace AtomicEngine
             eventHandlers[eventType](eventType, eventData);
         }
 
+        internal void HandleEvent(AObject sender, uint eventType, ScriptVariantMap eventData)
+        {
+            var key = new SenderEventKey(eventType, sender.nativeInstance);
+            senderEventHandlers[key](sender, eventType, eventData);
+        }
+
         public void SubscribeToEvent(uint eventType, EventDelegate eventDelegate)
         {
             NETCore.RegisterNETEventType(eventType);
-            eventHandlers[eventType] = eventDelegate;
+            EventHandlers[eventType] = eventDelegate;
             NativeCore.SubscribeToEvent(this, eventType);
         }
 
@@ -31,6 +62,37 @@ namespace AtomicEngine
             SubscribeToEvent(AtomicNET.StringToStringHash(eventType), eventDelegate);
         }
 
+        public void UnsubscribeFromEvent(uint eventType)
+        {
+            NativeCore.UnsubscribeFromEvent(this, eventType);
+            EventHandlers.Remove(eventType);
+        }
+
+        public void SubscribeToEvent(AObject sender, uint eventType, SenderEventDelegate eventDelegate)
+        {
+            if (sender == null)
+            {
+                throw new InvalidOperationException("AObject.SubscribeToEvent - trying to subscribe to events from a null object");
+            }
+
+            NETCore.RegisterNETEventType(eventType);
+            var key = new SenderEventKey(eventType, sender.nativeInstance);
+            SenderEventHandlers[key] = eventDelegate;
+            NativeCore.SubscribeToEvent(this, sender, eventType);
+        }
+
+        public void SubscribeToEvent(AObject sender, string eventType, SenderEventDelegate eventDelegate)
+        {
+            SubscribeToEvent(sender, AtomicNET.StringToStringHash(eventType), eventDelegate);
+        }
+
+        public void UnsubscribeFromEvent(AObject sender, uint eventType)
+        {
+            NativeCore.UnsubscribeFromEvent(this, sender, eventType);
+            var key = new SenderEventKey(eventType, sender.nativeInstance);
+            SenderEventHandlers.Remove(key);
+        }
+
         public void SendEvent(string eventType, ScriptVariantMap eventData = null)
         {
 
@@ -41,7 +103,42 @@ namespace AtomicEngine
         [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
         private static extern void csi_Atomic_AObject_SendEvent(IntPtr self, string eventType, IntPtr variantMap);
 
+        private struct SenderEventKey
+        {
+            public readonly uint EventType;
+            public readonly IntPtr Sender; 
 
+            public SenderEventKey(uint eventType, IntPtr sender)
+            {
+                EventType = eventType;
+                Sender = sender;
+            }
+        }
+
+        // Use a comparer to avoid boxing on struct .Equals override
+        private class SenderEventKeyComparer : IEqualityComparer<SenderEventKey>
+        {
+            public static readonly SenderEventKeyComparer Default = new SenderEventKeyComparer();
+
+            private SenderEventKeyComparer() { }
+
+            public bool Equals(SenderEventKey lhs, SenderEventKey rhs)
+            {
+                return (lhs.EventType == rhs.EventType &&
+                    lhs.Sender == rhs.Sender);
+            }
+
+            public int GetHashCode(SenderEventKey key)
+            {
+                unchecked
+                {
+                    int hash = 17;
+                    hash = hash * 23 + key.EventType.GetHashCode();
+                    hash = hash * 23 + key.Sender.GetHashCode();
+                    return hash;
+                }
+            }
+        }
     }
 
 }

+ 118 - 35
Script/AtomicNET/AtomicNET/Core/NativeCore.cs

@@ -32,19 +32,22 @@ namespace AtomicEngine
 
         static internal void SubscribeToEvent(AObject receiver, uint eventType)
         {
-            List<WeakReference<AObject>> eventReceivers;
+            SubscribeToEvent(receiver, null, eventType);
+        }
+
+        static internal void SubscribeToEvent(AObject receiver, AObject sender, uint eventType)
+        {
+            List<EventReceiver> eventReceivers;
 
             if (!eventReceiverLookup.TryGetValue(eventType, out eventReceivers))
             {
-                eventReceivers = eventReceiverLookup[eventType] = new List<WeakReference<AObject>>();
+                eventReceivers = eventReceiverLookup[eventType] = new List<EventReceiver>();
             }
 
             AObject obj;
-
-            foreach (WeakReference<AObject> wr in eventReceivers)
+            foreach (EventReceiver er in eventReceivers)
             {
-
-                if (!wr.TryGetTarget(out obj))
+                if (!er.Receiver.TryGetTarget(out obj))
                     continue; // GC'd
 
                 // already on list?
@@ -53,20 +56,59 @@ namespace AtomicEngine
             }
 
             WeakReference<RefCounted> w = null;
-
             if (!nativeLookup.TryGetValue(receiver.nativeInstance, out w))
             {
-                throw new System.InvalidOperationException("NativeCore.SubscribeToEvent - unable to find native instance");
+                throw new InvalidOperationException("NativeCore.SubscribeToEvent - unable to find native receiver instance");
             }
 
             RefCounted refcounted;
-
             if (!w.TryGetTarget(out refcounted))
             {
-                throw new System.InvalidOperationException("NativeCore.SubscribeToEvent - attempting to subscribe a GC'd AObject");
+                throw new InvalidOperationException("NativeCore.SubscribeToEvent - attempting to subscribe a GC'd AObject");
+            }
+
+            IntPtr nativeSender = IntPtr.Zero;
+            if (sender != null)
+            {
+                nativeSender = sender.nativeInstance;
+                if (!nativeLookup.TryGetValue(sender.nativeInstance, out w))
+                {
+                    throw new InvalidOperationException("NativeCore.SubscribeToEvent - unable to find native sender instance");
+                }
+
+                if (!w.TryGetTarget(out refcounted))
+                {
+                    throw new InvalidOperationException("NativeCore.SubscribeToEvent - attempting to subscribe to a GC'd AObject");
+                }
             }
 
-            eventReceivers.Add(new WeakReference<AObject>(receiver));
+            eventReceivers.Add(new EventReceiver(receiver, nativeSender));
+        }
+
+        static internal void UnsubscribeFromEvent(AObject receiver, uint eventType)
+        {
+            UnsubscribeFromEvent(receiver, null, eventType);
+        }
+
+        static internal void UnsubscribeFromEvent(AObject receiver, RefCounted sender, uint eventType)
+        {
+            List<EventReceiver> eventReceivers;
+            if (!eventReceiverLookup.TryGetValue(eventType, out eventReceivers))
+                return;
+
+            AObject obj;
+            foreach (EventReceiver er in eventReceivers)
+            {
+                if (!er.Receiver.TryGetTarget(out obj))
+                    continue; // GC'd
+
+                if (obj == receiver &&
+                    (sender == null || er.Sender == sender.nativeInstance))
+                {
+                    eventReceivers.Remove(er);
+                    return;
+                }
+            }
         }
 
         static ScriptVariantMap[] svm;
@@ -81,39 +123,57 @@ namespace AtomicEngine
                 svm[i] = new ScriptVariantMap();
         }
 
-        public static void EventDispatch(uint eventType, IntPtr eventData)
+        public static void EventDispatch(IntPtr sender, uint eventType, IntPtr eventData)
         {
-            List<WeakReference<AObject>> eventReceivers;
+            List<EventReceiver> eventReceivers;
 
             if (!eventReceiverLookup.TryGetValue(eventType, out eventReceivers))
             {
                 // This should not happen, as event NET objects are subscribed to are filtered 
-                throw new System.InvalidOperationException("NativeCore.EventDispatch - received unregistered event type");
+                throw new InvalidOperationException("NativeCore.EventDispatch - received unregistered event type");
 
             }
 
-            ScriptVariantMap scriptMap = null;
-            AObject receiver;
+            AObject managedSender = null;
+            WeakReference<RefCounted> wr;
+            if (sender != IntPtr.Zero &&
+                nativeLookup.TryGetValue(sender, out wr))
+            {
+                RefCounted refCounted;
+                if (wr.TryGetTarget(out refCounted))
+                {
+                    managedSender = refCounted as AObject;
+                }
+            }
 
             // iterate over copy of list so we can modify it while running
-            foreach (var w in eventReceivers.ToList())
+            ScriptVariantMap scriptMap = null;
+            AObject receiver;
+            foreach (EventReceiver er in eventReceivers.ToList())
             {
                 // GC'd?
-                if (!w.TryGetTarget(out receiver))
+                if (!er.Receiver.TryGetTarget(out receiver))
                     continue;
 
                 if (scriptMap == null)
                 {
                     if (svmDepth == svmMax)
                     {
-                        throw new System.InvalidOperationException("NativeCore.EventDispatch - exceeded max svm");
+                        throw new InvalidOperationException("NativeCore.EventDispatch - exceeded max svm");
                     }
 
                     scriptMap = svm[svmDepth++];
                     scriptMap.CopyVariantMap(eventData);
                 }
 
-                receiver.HandleEvent(eventType, scriptMap);
+                if (managedSender != null && er.Sender == sender)
+                {
+                    receiver.HandleEvent(managedSender, eventType, scriptMap);
+                }
+                else
+                {
+                    receiver.HandleEvent(eventType, scriptMap);
+                }
             }
 
             if (scriptMap != null)
@@ -128,19 +188,19 @@ namespace AtomicEngine
 
             // expire event listeners
 
-            int eventListenersRemoved = 0;
-            int nativesRemoved = 0;
+            //int eventListenersRemoved = 0;
+            //int nativesRemoved = 0;
 
             AObject obj;
 
-            foreach (List<WeakReference<AObject>> receiverList in eventReceiverLookup.Values)
+            foreach (List<EventReceiver> receiverList in eventReceiverLookup.Values)
             {
-                foreach (WeakReference<AObject> receiver in receiverList.ToList())
+                foreach (EventReceiver er in receiverList.ToList())
                 {
-                    if (!receiver.TryGetTarget(out obj))
+                    if (!er.Receiver.TryGetTarget(out obj))
                     {
-                        receiverList.Remove(receiver);
-                        eventListenersRemoved++;
+                        receiverList.Remove(er);
+                        //eventListenersRemoved++;
                     }
 
                     if (watch.ElapsedMilliseconds > 16)
@@ -163,7 +223,7 @@ namespace AtomicEngine
                     // expired
                     csi_AtomicEngine_ReleaseRef(native);
                     nativeLookup.Remove(native);
-                    nativesRemoved++;
+                    //nativesRemoved++;
                 }
 
                 if (watch.ElapsedMilliseconds > 16)
@@ -197,7 +257,7 @@ namespace AtomicEngine
         {
             if (nativeLookup.ContainsKey(native))
             {
-                throw new System.InvalidOperationException("NativeCore.RegisterNative - Duplicate IntPtr key");
+                throw new InvalidOperationException("NativeCore.RegisterNative - Duplicate IntPtr key");
             }
 
             r.nativeInstance = native;
@@ -253,7 +313,7 @@ namespace AtomicEngine
 
             if (!nativeClassIDToNativeType.TryGetValue(classID, out nativeType))
             {
-                throw new System.InvalidOperationException("NativeCore.WrapNative - Attempting to wrap unknown native class id");
+                throw new InvalidOperationException("NativeCore.WrapNative - Attempting to wrap unknown native class id");
             }
 
             r = nativeType.managedConstructor(native);
@@ -277,11 +337,11 @@ namespace AtomicEngine
         {
             if (nativeClassIDToNativeType.ContainsKey(nativeType.nativeClassID))
             {
-                throw new System.InvalidOperationException("NativeCore.RegisterNativeType - Duplicate NativeType class id registered");
+                throw new InvalidOperationException("NativeCore.RegisterNativeType - Duplicate NativeType class id registered");
             }
             if (typeToNativeType.ContainsKey(nativeType.type))
             {
-                throw new System.InvalidOperationException("NativeCore.RegisterNativeType - Duplicate NativeType type registered");
+                throw new InvalidOperationException("NativeCore.RegisterNativeType - Duplicate NativeType type registered");
             }
 
             nativeClassIDToNativeType[nativeType.nativeClassID] = nativeType;
@@ -297,6 +357,17 @@ namespace AtomicEngine
             return false;
         }
 
+        public static Type GetNativeAncestorType(Type type)
+        {
+            Type ancestorType = type;
+            do
+            {
+                ancestorType = ancestorType.BaseType;
+            } while (ancestorType != null && !IsNativeType(ancestorType));
+
+            return ancestorType;
+        }
+
         static public IntPtr NativeContructorOverride
         {
             get
@@ -310,7 +381,7 @@ namespace AtomicEngine
             {
                 if (nativeContructorOverride != IntPtr.Zero)
                 {
-                    throw new System.InvalidOperationException("NativeCore.NativeContructorOverride - Previous nativeContructorOverride not consumed");
+                    throw new InvalidOperationException("NativeCore.NativeContructorOverride - Previous nativeContructorOverride not consumed");
                 }
 
                 nativeContructorOverride = value;
@@ -321,7 +392,7 @@ namespace AtomicEngine
         {
             if (nativeContructorOverride != IntPtr.Zero)
             {
-                throw new System.InvalidOperationException("NativeCore.VerifyNativeContructorOverrideConsumed -  NativeContructorOverride not consumed");
+                throw new InvalidOperationException("NativeCore.VerifyNativeContructorOverrideConsumed -  NativeContructorOverride not consumed");
             }
         }
 
@@ -332,7 +403,7 @@ namespace AtomicEngine
         internal static Dictionary<IntPtr, WeakReference<RefCounted>> nativeLookup = new Dictionary<IntPtr, WeakReference<RefCounted>>();
 
         // weak references here, hold a ref native side
-        internal static Dictionary<uint, List<WeakReference<AObject>>> eventReceiverLookup = new Dictionary<uint, List<WeakReference<AObject>>>();
+        internal static Dictionary<uint, List<EventReceiver>> eventReceiverLookup = new Dictionary<uint, List<EventReceiver>>();
 
 
         // Native ClassID to NativeType lookup
@@ -345,6 +416,18 @@ namespace AtomicEngine
         [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
         private static extern void csi_AtomicEngine_ReleaseRef(IntPtr refCounted);
 
+        internal struct EventReceiver
+        {
+            public WeakReference<AObject> Receiver;
+            public IntPtr Sender;
+
+            public EventReceiver(AObject obj, IntPtr source)
+            {
+                Receiver = new WeakReference<AObject>(obj);
+                Sender = source;
+            }
+        }
+
     }
 
 

+ 3 - 2
Script/AtomicNET/AtomicNET/Core/NativeEvents.cs

@@ -1,17 +1,18 @@
 using System;
-using System.Collections.Generic;
 using System.Runtime.InteropServices;
 
 namespace AtomicEngine
 {
     [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
-    public delegate void EventDispatchDelegate(uint eventType, IntPtr eventData);
+    public delegate void EventDispatchDelegate(IntPtr sender, uint eventType, IntPtr eventData);
 
     [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
     public delegate void UpdateDispatchDelegate(float timeStep);
 
     public delegate void EventDelegate(uint eventType, ScriptVariantMap eventData);
 
+    public delegate void SenderEventDelegate(AObject sender, uint eventType, ScriptVariantMap eventData);
+
     public delegate void HandleUpdateDelegate(float timeStep);
 
     [StructLayout(LayoutKind.Sequential)]

+ 3 - 3
Source/AtomicNET/NETNative/NETCore.h

@@ -28,8 +28,8 @@
 namespace Atomic
 {
 
-typedef void (*NETCoreEventDispatchFunction)(unsigned eventID, VariantMap* eventData);
-typedef void(*NETCoreUpdateDispatchFunction)(float timeStep);
+typedef void (*NETCoreEventDispatchFunction)(RefCounted* refCounted, unsigned eventID, VariantMap* eventData);
+typedef void (*NETCoreUpdateDispatchFunction)(float timeStep);
 
 struct NETCoreDelegates
 {
@@ -54,7 +54,7 @@ public:
 
     static void RegisterNETEventType(unsigned eventType);
 
-    inline static void DispatchEvent(unsigned eventID, VariantMap* eventData = nullptr) { eventDispatch_(eventID, eventData); }
+    inline static void DispatchEvent(RefCounted* refCounted, unsigned eventID, VariantMap* eventData = nullptr) { eventDispatch_(refCounted, eventID, eventData); }
     inline static void DispatchUpdateEvent(float timeStep) { if (updateDispatch_) updateDispatch_(timeStep); }
 
     /// We access this directly in binding code, where there isn't a context

+ 2 - 2
Source/AtomicNET/NETNative/NETEventDispatcher.cpp

@@ -63,13 +63,13 @@ namespace Atomic
 
             ncEventData[eventType] = ncEventData;
 
-            NETCore::DispatchEvent(eventType.Value(), &ncEventData);
+            NETCore::DispatchEvent(sender, eventType.Value(), &ncEventData);
 
             return;
 
         }
 
-        NETCore::DispatchEvent(eventType.Value(), &eventData);
+        NETCore::DispatchEvent(sender, eventType.Value(), &eventData);
 
     }