瀏覽代碼

Add LuaScriptEventListener and LuaScriptEventInvoker.

aster2013 11 年之前
父節點
當前提交
f5522a07a8

+ 177 - 229
Source/Urho3D/LuaScript/LuaScript.cpp

@@ -122,6 +122,8 @@ LuaScript::LuaScript(Context* context) :
 
     tolua_LuaScriptLuaAPI_open(luaState_);
 
+    eventInvoker_ = new LuaScriptEventInvoker(context_);
+
     coroutineUpdate_ = GetFunction("coroutine.update");
 
     // Subscribe to post update
@@ -129,16 +131,10 @@ LuaScript::LuaScript(Context* context) :
 
     // Subscribe to console commands
     SetExecuteConsoleCommands(true);
-    
-    // Record the internally handled script functions so that UnsubscribeFromAllEvents doesn't destroy them
-    internalEvents_.Push(E_POSTUPDATE);
-    internalEvents_.Push(E_CONSOLECOMMAND);
 }
 
 LuaScript::~LuaScript()
 {
-    functionNameToFunctionMap_.Clear();
-
     lua_State* luaState = luaState_;
     luaState_ = 0;
 
@@ -148,258 +144,138 @@ LuaScript::~LuaScript()
         lua_close(luaState);
 }
 
-bool LuaScript::ExecuteFile(const String& fileName)
+void LuaScript::AddEventHandler(const String& eventName, int functionIndex)
 {
-    PROFILE(ExecuteFile);
-
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    LuaFile* luaFile = cache->GetResource<LuaFile>(fileName);
-    return luaFile && luaFile->LoadAndExecute(luaState_);
+    WeakPtr<LuaFunction> function = GetFunction(functionIndex);
+    if (function)
+        eventInvoker_->AddEventHandler(0, eventName, function);
 }
 
-bool LuaScript::ExecuteString(const String& string)
+void LuaScript::AddEventHandler(const String& eventName, const String& functionName)
 {
-    PROFILE(ExecuteString);
-
-    int top = lua_gettop(luaState_);
-
-    if (luaL_dostring(luaState_, string.CString()) != 0)
-    {
-        const char* message = lua_tostring(luaState_, -1);
-        LOGERROR("Execute Lua string failed: " + String(message));
-        lua_settop(luaState_, top);
-        return false;
-    }
-
-    return true;
+    WeakPtr<LuaFunction> function = GetFunction(functionName);
+    if (function)
+        eventInvoker_->AddEventHandler(0, eventName, function);
 }
 
-bool LuaScript::ExecuteFunction(const String& functionName)
+void LuaScript::AddEventHandler(Object* sender, const String& eventName, int functionIndex)
 {
-    WeakPtr<LuaFunction> function = GetFunction(functionName);
-    return function && function->BeginCall() && function->EndCall();
+    if (!sender)
+        return;
+
+    WeakPtr<LuaFunction> function = GetFunction(functionIndex);
+    if (!function)
+        eventInvoker_->AddEventHandler(sender, eventName, function);
 }
 
-void LuaScript::ScriptSendEvent(const String& eventName, VariantMap& eventData)
+void LuaScript::AddEventHandler(Object* sender, const String& eventName, const String& functionName)
 {
-    SendEvent(StringHash(eventName), eventData);
+    if (!sender)
+        return;
+
+    WeakPtr<LuaFunction> function = GetFunction(functionName);
+    if (!function)
+        eventInvoker_->AddEventHandler(sender, eventName, function);
 }
 
-void LuaScript::ScriptSubscribeToEvent(const String& eventName, int functionIndex)
+void LuaScript::RemoveEventHandler(const String& eventName, int functionIndex)
 {
-    StringHash eventType(eventName);
-
     WeakPtr<LuaFunction> function = GetFunction(functionIndex);
     if (function)
-    {
-        LuaFunctionVector& functions = eventHandleFunctions_[eventType];
-
-        SubscribeToEvent(eventType, HANDLER(LuaScript, HandleEvent));
-
-        if (!functions.Contains(function))
-            functions.Push(function);
-    }
+        eventInvoker_->RemoveEventHandler(0, eventName, function);
 }
 
-void LuaScript::ScriptSubscribeToEvent(const String& eventName, const String& functionName)
+void LuaScript::RemoveEventHandler(const String& eventName, const String& functionName)
 {
-    StringHash eventType(eventName);
-
     WeakPtr<LuaFunction> function = GetFunction(functionName);
     if (function)
-    {
-        LuaFunctionVector& functions = eventHandleFunctions_[eventType];
-        
-        SubscribeToEvent(eventType, HANDLER(LuaScript, HandleEvent));
-
-        if (!functions.Contains(function))
-            functions.Push(function);
-    }
+        eventInvoker_->RemoveEventHandler(0, eventName, function);
 }
 
-void LuaScript::ScriptUnsubscribeFromEvent(const String& eventName)
+void LuaScript::RemoveEventHandler(const String& eventName)
 {
-    StringHash eventType(eventName);
-
-    HashMap<StringHash, LuaFunctionVector>::Iterator i = eventHandleFunctions_.Find(eventType);
-    if (i != eventHandleFunctions_.End())
-    {
-        LuaFunctionVector& functions = i->second_;
-        UnsubscribeFromEvent(eventType);
-        eventHandleFunctions_.Erase(i);
-    }
+    eventInvoker_->RemoveEventHandler(0, eventName, WeakPtr<LuaFunction>());
 }
 
-void LuaScript::ScriptUnsubscribeFromEvent(const String& eventName, int functionIndex)
+void LuaScript::RemoveEventHandler(Object* sender, const String& eventName, int functionIndex)
 {
-    WeakPtr<LuaFunction> function = GetFunction(functionIndex);
-    if (!function)
+    if (!sender)
         return;
 
-    StringHash eventType(eventName);
-
-    HashMap<StringHash, LuaFunctionVector>::Iterator i = eventHandleFunctions_.Find(eventType);
-    if (i != eventHandleFunctions_.End())
-    {
-        LuaFunctionVector& functions = i->second_;
-        functions.Remove(function);
-
-        if (functions.Empty())
-        {
-            UnsubscribeFromEvent(eventType);
-            eventHandleFunctions_.Erase(i);
-        }
-    }
+    WeakPtr<LuaFunction> function = GetFunction(functionIndex);
+    if (!function)
+        eventInvoker_->RemoveEventHandler(sender, eventName, function);
 }
 
-void LuaScript::ScriptUnsubscribeFromEvent(const String& eventName, const String& functionName)
+void LuaScript::RemoveEventHandler(Object* sender, const String& eventName, const String& functionName)
 {
-    if (functionName.Empty())
+    if (!sender)
         return;
 
     WeakPtr<LuaFunction> function = GetFunction(functionName);
     if (!function)
-        return;
-
-    StringHash eventType(eventName);    
-    HashMap<StringHash, LuaFunctionVector>::Iterator i = eventHandleFunctions_.Find(eventType);
-    if (i != eventHandleFunctions_.End())
-    {
-        LuaFunctionVector& functions = i->second_;
-        functions.Remove(function);
-
-        if (functions.Empty())
-        {
-            UnsubscribeFromEvent(eventType);
-            eventHandleFunctions_.Erase(i);
-        }
-    }
+        eventInvoker_->RemoveEventHandler(sender, eventName, function);
 }
 
-void LuaScript::ScriptUnsubscribeFromAllEvents()
+void LuaScript::RemoveEventHandler(Object* sender, const String& eventName)
 {
-    if (eventHandleFunctions_.Empty())
+    if (!sender)
         return;
-    
-    UnsubscribeFromAllEventsExcept(internalEvents_, false);
 
-    eventHandleFunctions_.Clear();
+    eventInvoker_->RemoveEventHandler(sender, eventName, WeakPtr<LuaFunction>());
 }
-
-void LuaScript::ScriptSubscribeToEvent(void* sender, const String& eventName, int functionIndex)
+void LuaScript::RemoveEventHandlers(Object* sender)
 {
-    StringHash eventType(eventName);
-    Object* object = (Object*)sender;
-
-    WeakPtr<LuaFunction> function = GetFunction(functionIndex);
-    if (function)
-    {
-        LuaFunctionVector& functions = objectHandleFunctions_[object][eventType];
-
-        // Fix issue #256
-        HashSet<Object*>* receivers = context_->GetEventReceivers(object, eventType);
-        if ((!receivers || !receivers->Contains(this)) && !functions.Empty())
-            functions.Clear();
-
-        SubscribeToEvent(object, eventType, HANDLER(LuaScript, HandleObjectEvent));
+    if (!sender)
+        return;
 
-        if (!functions.Contains(function))
-            functions.Push(function);
-    }
+    eventInvoker_->RemoveAllEventHandlers(sender);
 }
 
-void LuaScript::ScriptSubscribeToEvent(void* sender, const String& eventName, const String& functionName)
+void LuaScript::RemoveAllEventHandlers()
 {
-    StringHash eventType(eventName);
-    Object* object = (Object*)sender;
-
-    WeakPtr<LuaFunction> function = GetFunction(functionName);
-    if (function)
-    {
-        LuaFunctionVector& functions = objectHandleFunctions_[object][eventType];
-
-        // Fix issue #256
-        HashSet<Object*>* receivers = context_->GetEventReceivers(object, eventType);
-        if ((!receivers || !receivers->Contains(this)) && !functions.Empty())
-            functions.Clear();
-
-        SubscribeToEvent(object, eventType, HANDLER(LuaScript, HandleObjectEvent));
-
-        if (!functions.Contains(function))
-            functions.Push(function);
-    }
+    eventInvoker_->RemoveAllEventHandlers(0);
+}
+void LuaScript::RemoveEventHandlersExcept(const Vector<String>& exceptionNames)
+{
+    eventInvoker_->RemoveEventHandlersExcept(exceptionNames);
 }
 
-void LuaScript::ScriptUnsubscribeFromEvent(void* sender, const String& eventName)
+bool LuaScript::ExecuteFile(const String& fileName)
 {
-    StringHash eventType(eventName);
-    Object* object = (Object*)sender;
+    PROFILE(ExecuteFile);
 
-    HashMap<StringHash, LuaFunctionVector>::Iterator i = objectHandleFunctions_[object].Find(eventType);
-    if (i != objectHandleFunctions_[object].End())
-    {
-        LuaFunctionVector& functions = i->second_;
-        UnsubscribeFromEvent(object, eventType);
-        objectHandleFunctions_[object].Erase(i);
-    }
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    LuaFile* luaFile = cache->GetResource<LuaFile>(fileName);
+    return luaFile && luaFile->LoadAndExecute(luaState_);
 }
 
-void LuaScript::ScriptUnsubscribeFromEvent(void* sender, const String& eventName, int functionIndex)
+bool LuaScript::ExecuteString(const String& string)
 {
-    WeakPtr<LuaFunction> function = GetFunction(functionIndex);
-    if (!function)
-        return;
+    PROFILE(ExecuteString);
 
-    StringHash eventType(eventName);
-    Object* object = (Object*)sender;
+    int top = lua_gettop(luaState_);
 
-    HashMap<StringHash, LuaFunctionVector>::Iterator i = objectHandleFunctions_[object].Find(eventType);
-    if (i != objectHandleFunctions_[object].End())
+    if (luaL_dostring(luaState_, string.CString()) != 0)
     {
-        LuaFunctionVector& functions = i->second_;
-        functions.Remove(function);
-
-        if (functions.Empty())
-        {
-            UnsubscribeFromEvent(object, eventType);
-            objectHandleFunctions_[object].Erase(i);
-        }
+        const char* message = lua_tostring(luaState_, -1);
+        LOGERROR("Execute Lua string failed: " + String(message));
+        lua_settop(luaState_, top);
+        return false;
     }
+
+    return true;
 }
 
-void LuaScript::ScriptUnsubscribeFromEvent(void* sender, const String& eventName, const String& functionName)
+bool LuaScript::ExecuteFunction(const String& functionName)
 {
     WeakPtr<LuaFunction> function = GetFunction(functionName);
-    if (!function)
-        return;
-
-    StringHash eventType(eventName);
-    Object* object = (Object*)sender;
-
-    HashMap<StringHash, LuaFunctionVector>::Iterator i = objectHandleFunctions_[object].Find(eventType);
-    if (i != objectHandleFunctions_[object].End())
-    {
-        LuaFunctionVector& functions = i->second_;
-        functions.Remove(function);
-        
-        if (functions.Empty())
-        {
-            UnsubscribeFromEvent(object, eventType);
-            objectHandleFunctions_[object].Erase(i);
-        }
-    }
+    return function && function->BeginCall() && function->EndCall();
 }
 
-void LuaScript::ScriptUnsubscribeFromEvents(void* sender)
+void LuaScript::SendEvent(const String& eventName, VariantMap& eventData)
 {
-    Object* object = (Object*)sender;
-
-    HashMap<Object*, HashMap<StringHash, LuaFunctionVector> >::Iterator it = objectHandleFunctions_.Find(object);
-    if (it == objectHandleFunctions_.End())
-        return;
-
-    UnsubscribeFromEvents(object);
-    objectHandleFunctions_.Erase(it);
+    Object::SendEvent(StringHash(eventName), eventData);
 }
 
 void LuaScript::SetExecuteConsoleCommands(bool enable)
@@ -548,42 +424,8 @@ WeakPtr<LuaFunction> LuaScript::GetFunction(const String& functionName, bool sil
     return WeakPtr<LuaFunction>(function);
 }
 
-void LuaScript::HandleEvent(StringHash eventType, VariantMap& eventData)
-{
-    LuaFunctionVector& functions = eventHandleFunctions_[eventType];
-    for (unsigned i = 0; i < functions.Size(); ++i)
-    {
-        WeakPtr<LuaFunction> function = functions[i];
-        if (function && function->BeginCall())
-        {
-            function->PushUserType(eventType, "StringHash");
-            function->PushUserType(eventData, "VariantMap");
-            function->EndCall();
-        }
-    }
-}
-
-void LuaScript::HandleObjectEvent(StringHash eventType, VariantMap& eventData)
-{
-    Object* object = GetEventSender();
-    LuaFunctionVector& functions = objectHandleFunctions_[object][eventType];
-    for (unsigned i = 0; i < functions.Size(); ++i)
-    {
-        WeakPtr<LuaFunction> function = functions[i];
-        if (function && function->BeginCall())
-        {
-            function->PushUserType(eventType, "StringHash");
-            function->PushUserType(eventData, "VariantMap");
-            function->EndCall();
-        }
-    }
-}
-
 void LuaScript::HandlePostUpdate(StringHash eventType, VariantMap& eventData)
-{
-    // Call also user-subscribed PostUpdate handler (if any)
-    HandleEvent(eventType, eventData);
-    
+{   
     if (coroutineUpdate_ && coroutineUpdate_->BeginCall())
     {
         using namespace PostUpdate;
@@ -643,6 +485,112 @@ bool LuaScript::PushScriptFunction(const String& functionName, bool silentIfNotF
     return true;
 }
 
+LuaScriptEventInvoker::LuaScriptEventInvoker(Context* context) : Object(context)
+{
+}
+
+LuaScriptEventInvoker::~LuaScriptEventInvoker()
+{
+}
+
+void LuaScriptEventInvoker::AddEventHandler(Object* sender, const String& eventName, WeakPtr<LuaFunction> function)
+{
+    EventTypeToLuaFunctionVectorMap& eventTypeToFunctionVectorMap = GetEventTypeToLuaFunctionVectorMap(sender);
+
+    StringHash eventType(eventName);
+    EventTypeToLuaFunctionVectorMap::Iterator i = eventTypeToFunctionVectorMap.Find(eventType);
+
+    if (i == eventTypeToFunctionVectorMap.End())
+    {
+        eventTypeToFunctionVectorMap[eventType].Push(function);
+        
+        if (!sender)
+            SubscribeToEvent(eventType, HANDLER(LuaScriptEventInvoker, HandleLuaScriptEvent));
+        else
+            SubscribeToEvent(sender, eventType, HANDLER(LuaScriptEventInvoker, HandleLuaScriptEvent));
+    }
+    else
+    {
+        if (!i->second_.Contains(function))
+            i->second_.Push(function);
+    }
+}
+
+void LuaScriptEventInvoker::RemoveEventHandler(Object* sender, const String& eventName, WeakPtr<LuaFunction> function)
+{
+    EventTypeToLuaFunctionVectorMap& eventTypeToLuaFunctionVectorMap = GetEventTypeToLuaFunctionVectorMap(sender);
+
+    StringHash eventType(eventName);
+    EventTypeToLuaFunctionVectorMap::Iterator i = eventTypeToLuaFunctionVectorMap.Find(eventType);
+    if (i == eventTypeToLuaFunctionVectorMap.End())
+        return;
+
+    if (function)
+        i->second_.Remove(function);
+    else
+        i->second_.Clear();
+    
+    if (i->second_.Empty())
+    {
+        eventTypeToLuaFunctionVectorMap.Erase(i);
+
+        if (!sender)
+            UnsubscribeFromEvent(eventType);
+        else
+            UnsubscribeFromEvent(sender, eventType);
+    }
+}
+
+void LuaScriptEventInvoker::RemoveAllEventHandlers(Object* sender)
+{
+    if (!sender)
+    {
+        UnsubscribeFromAllEvents();
+        eventTypeToLuaFunctionVectorMap.Clear();
+        senderEventTypeToLuaFunctionVectorMap.Clear();
+    }
+    else
+    {
+        UnsubscribeFromEvents(sender);
+        senderEventTypeToLuaFunctionVectorMap.Erase(sender);
+    }
+}
+
+void LuaScriptEventInvoker::RemoveEventHandlersExcept(const Vector<String>& exceptionNames)
+{
+    PODVector<StringHash> exceptionTypes(exceptionNames.Size());
+    for (unsigned i = 0; i < exceptionTypes.Size(); ++i)
+    {
+        StringHash eventType(exceptionNames[i]);
+        exceptionTypes[i] = eventType;
+
+        eventTypeToLuaFunctionVectorMap.Erase(eventType);
+    }
+
+    UnsubscribeFromAllEventsExcept(exceptionTypes, false);
+}
+
+void LuaScriptEventInvoker::HandleLuaScriptEvent(StringHash eventType, VariantMap& eventData)
+{
+    Object* sender = GetEventHandler()->GetSender();
+    EventTypeToLuaFunctionVectorMap& eventTypeToLuaFunctionVectorMap = GetEventTypeToLuaFunctionVectorMap(sender);
+    EventTypeToLuaFunctionVectorMap::Iterator i = eventTypeToLuaFunctionVectorMap.Find(eventType);
+    if (i == eventTypeToLuaFunctionVectorMap.End())
+        return;
+
+    LuaFunctionVector& luaFunctionVector = i->second_;
+    for (unsigned i = 0; i < luaFunctionVector.Size(); ++i)
+    {
+        WeakPtr<LuaFunction>& function = luaFunctionVector[i];
+        if (function && function->BeginCall())
+        {
+            function->PushUserType(eventType, "StringHash");
+            function->PushUserType(eventData, "VariantMap");
+            function->EndCall();
+        }
+    }
+}
+
 void RegisterLuaScriptLibrary(Context* context)
 {
     LuaFile::RegisterObject(context);

+ 76 - 41
Source/Urho3D/LuaScript/LuaScript.h

@@ -24,6 +24,7 @@
 
 #include "../Core/Context.h"
 #include "../Core/Object.h"
+#include "../LuaScript/LuaScriptEventListener.h"
 
 struct lua_State;
 
@@ -33,10 +34,11 @@ namespace Urho3D
 extern const char* LOGIC_CATEGORY;
 
 class LuaFunction;
+class LuaScriptEventInvoker;
 class Scene;
 
 /// Lua script subsystem.
-class URHO3D_API LuaScript : public Object
+class URHO3D_API LuaScript : public Object, public LuaScriptEventListener
 {
     OBJECT(LuaScript);
 
@@ -46,39 +48,41 @@ public:
     /// Destruct.
     ~LuaScript();
 
+    /// Add a scripted event handler by function.
+    virtual void AddEventHandler(const String& eventName, int functionIndex);
+    /// Add a scripted event handler by function name.
+    virtual void AddEventHandler(const String& eventName, const String& functionName);
+    /// Add a scripted event handler by function for a specific sender.
+    virtual void AddEventHandler(Object* sender, const String& eventName, int functionIndex);
+    /// Add a scripted event handler by function name for a specific sender.
+    virtual void AddEventHandler(Object* sender, const String& eventName, const String& functionName);
+    /// Remove a scripted event handler by function.
+    virtual void RemoveEventHandler(const String& eventName, int functionIndex);
+    /// Remove a scripted event handler by function name.
+    virtual void RemoveEventHandler(const String& eventName, const String& functionName);
+    /// Remove a scripted event handler.
+    virtual void RemoveEventHandler(const String& eventName);
+    /// Remove a scripted event handler for a specific sender by function.
+    virtual void RemoveEventHandler(Object* sender, const String& eventName, int functionIndex);
+    /// Remove a scripted event handler for a specific sender by function name.
+    virtual void RemoveEventHandler(Object* sender, const String& eventName, const String& functionName);
+    /// Remove a scripted event handler for a specific sender.
+    virtual void RemoveEventHandler(Object* sender, const String& eventName);
+    /// Remove all scripted event handlers for a specific sender.
+    virtual void RemoveEventHandlers(Object* sender);
+    /// Remove all scripted event handlers.
+    virtual void RemoveAllEventHandlers();
+    /// Remove all scripted event handlers, except those listed.
+    virtual void RemoveEventHandlersExcept(const Vector<String>& exceptionNames);
+
     /// Execute script file. Return true if successful.
     bool ExecuteFile(const String& fileName);
     /// Execute script string. Return true if successful.
     bool ExecuteString(const String& string);
     /// Execute script function.
     bool ExecuteFunction(const String& functionName);
-    /// Script send event.
-    void ScriptSendEvent(const String& eventName, VariantMap& eventData);
-    /// Script subscribe to an event that can by send by any sender.
-    void ScriptSubscribeToEvent(const String& eventName, int functionIndex);
-    /// Script subscribe to an event that can by send by any sender.
-    void ScriptSubscribeToEvent(const String& eventName, const String& functionName);
-    /// Script unsubscribe from an event.
-    void ScriptUnsubscribeFromEvent(const String& eventName);
-    /// Script unsubscribe from an event.
-    void ScriptUnsubscribeFromEvent(const String& eventName, int functionIndex);
-    /// Script unsubscribe from an event.
-    void ScriptUnsubscribeFromEvent(const String& eventName, const String& functionName);
-    /// Script unsubscribe from all events.
-    void ScriptUnsubscribeFromAllEvents();
-    /// Script subscribe to a specific sender's event.
-    void ScriptSubscribeToEvent(void* sender, const String& eventName, int functionIndex);
-    /// Script subscribe to a specific sender's event.
-    void ScriptSubscribeToEvent(void* sender, const String& eventName, const String& functionName);
-    /// Script unsubscribe from a specific sender's event.
-    void ScriptUnsubscribeFromEvent(void* sender, const String& eventName);
-    /// Script unsubscribe from a specific sender's event.
-    void ScriptUnsubscribeFromEvent(void* sender, const String& eventName, int functionIndex);
-    /// Script unsubscribe from a specific sender's event.
-    void ScriptUnsubscribeFromEvent(void* sender, const String& eventName, const String& functionName);
-
-    /// Script unsubscribe from a specific sender's all events.
-    void ScriptUnsubscribeFromEvents(void* sender);
+    /// Send event.
+    void SendEvent(const String& eventName, VariantMap& eventData);
     /// Set whether to execute engine console commands as script code.
     void SetExecuteConsoleCommands(bool enable);
 
@@ -96,10 +100,6 @@ private:
     void RegisterLoader();
     /// Replace print.
     void ReplacePrint();
-    /// Handle event.
-    void HandleEvent(StringHash eventType, VariantMap& eventData);
-    /// Handle object event.
-    void HandleObjectEvent(StringHash eventType, VariantMap& eventData);
     /// Handle post update.
     void HandlePostUpdate(StringHash eventType, VariantMap& eventData);
     /// Handle a console command event.
@@ -116,22 +116,57 @@ private:
 
     /// Lua state.
     lua_State* luaState_;
+    /// Event invoker.
+    SharedPtr<LuaScriptEventInvoker> eventInvoker_;
     /// Coroutine update function.
     WeakPtr<LuaFunction> coroutineUpdate_;
+    /// Flag for executing engine console commands as script code. Default to true.
+    bool executeConsoleCommands_;
     /// Function pointer to function map.
     HashMap<const void*, SharedPtr<LuaFunction> > functionPointerToFunctionMap_;
     /// Function name to function map.
     HashMap<String, SharedPtr<LuaFunction> > functionNameToFunctionMap_;
-    /// Typedef Lua function vector.
+};
+
+class LuaScriptEventInvoker : public Object
+{
+    OBJECT(LuaScriptEventInvoker);
+
+public:
+    /// Construct.
+    LuaScriptEventInvoker(Context* context);
+    /// Destruct.
+    virtual ~LuaScriptEventInvoker();
+
+    /// Add a scripted event handler.
+    void AddEventHandler(Object* sender, const String& eventName, WeakPtr<LuaFunction> function);
+    /// Remove a scripted event handler.
+    void RemoveEventHandler(Object* sender, const String& eventName, WeakPtr<LuaFunction> function);
+    /// Remove all scripted event handlers.
+    void RemoveAllEventHandlers(Object* sender);
+    /// Remove all scripted event handlers, except those listed.
+    void RemoveEventHandlersExcept(const Vector<String>& exceptionNames);
+
+private:
+    /// Handle script event in Lua script.
+    void HandleLuaScriptEvent(StringHash eventType, VariantMap& eventData);
+
     typedef Vector<WeakPtr<LuaFunction> > LuaFunctionVector;
-    /// Event handle functions.
-    HashMap<StringHash, LuaFunctionVector> eventHandleFunctions_;
-    /// Object event handle functions.
-    HashMap<Object*, HashMap<StringHash, LuaFunctionVector> > objectHandleFunctions_;
-    /// Internally used events, which should not be unsubscribed from.
-    PODVector<StringHash> internalEvents_;
-    /// Flag for executing engine console commands as script code. Default to true.
-    bool executeConsoleCommands_;
+    typedef HashMap<StringHash, LuaFunctionVector> EventTypeToLuaFunctionVectorMap;
+    
+    /// Return event type to Lua function vector map.
+    EventTypeToLuaFunctionVectorMap& GetEventTypeToLuaFunctionVectorMap(Object* sender)
+    {
+        if (!sender)
+            return eventTypeToLuaFunctionVectorMap;
+
+        return senderEventTypeToLuaFunctionVectorMap[sender];
+    }
+
+    /// Event type to Lua function vector map.
+    EventTypeToLuaFunctionVectorMap eventTypeToLuaFunctionVectorMap;
+    /// Event type to Lua function vector map for specific sender.
+    HashMap<Object*, EventTypeToLuaFunctionVectorMap> senderEventTypeToLuaFunctionVectorMap;
 };
 
 /// Register Lua script library objects.

+ 65 - 0
Source/Urho3D/LuaScript/LuaScriptEventListener.h

@@ -0,0 +1,65 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../Core/Object.h"
+
+namespace Urho3D
+{
+
+/// Lua script event listener.
+class URHO3D_API LuaScriptEventListener
+{
+public:
+    /// Destruct
+    virtual ~LuaScriptEventListener() {};
+
+    /// Add a scripted event handler by function.
+    virtual void AddEventHandler(const String& eventName, int functionIndex) = 0;
+    /// Add a scripted event handler by function name.
+    virtual void AddEventHandler(const String& eventName, const String& functionName) = 0;
+    /// Add a scripted event handler by function for a specific sender.
+    virtual void AddEventHandler(Object* sender, const String& eventName, int functionIndex) = 0;
+    /// Add a scripted event handler by function name for a specific sender.
+    virtual void AddEventHandler(Object* sender, const String& eventName, const String& functionName) = 0;
+    /// Remove a scripted event handler by function.
+    virtual void RemoveEventHandler(const String& eventName, int functionIndex) = 0;
+    /// Remove a scripted event handler by function name.
+    virtual void RemoveEventHandler(const String& eventName, const String& functionName) = 0;
+    /// Remove a scripted event handler.
+    virtual void RemoveEventHandler(const String& eventName) = 0;
+    /// Remove a scripted event handler for a specific sender by function.
+    virtual void RemoveEventHandler(Object* sender, const String& eventName, int functionIndex) = 0;
+    /// Remove a scripted event handler for a specific sender by function name.
+    virtual void RemoveEventHandler(Object* sender, const String& eventName, const String& functionName) = 0;
+    /// Remove a scripted event handler for a specific sender.
+    virtual void RemoveEventHandler(Object* sender, const String& eventName) = 0;
+    /// Remove all scripted event handlers for a specific sender.
+    virtual void RemoveEventHandlers(Object* sender) = 0;
+    /// Remove all scripted event handlers.
+    virtual void RemoveAllEventHandlers() = 0;
+    /// Remove all scripted event handlers, except those listed.
+    virtual void RemoveEventHandlersExcept(const Vector<String>& exceptionNames) = 0;
+};
+
+}

+ 159 - 82
Source/Urho3D/LuaScript/pkgs/LuaScript/LuaScript.pkg

@@ -1,14 +1,19 @@
 $#include "LuaScript/LuaScript.h"
 
-void LuaScriptSendEvent @ SendEvent(const String eventName, VariantMap& eventData);
-void LuaScriptSubscribeToEvent @ SubscribeToEvent(const String eventName, void* functionOrFunctionName);
-void LuaScriptUnsubscribeFromEvent @ UnsubscribeFromEvent(const String eventName);
-void LuaScriptUnsubscribeFromEvent @ UnsubscribeFromEvent(const String eventName, void* functionOrFunctionName);
-void LuaScriptUnsubscribeFromAllEvents @ UnsubscribeFromAllEvents();
-void LuaScriptSubscribeToEvent @ SubscribeToEvent(void* sender, const String eventName, void* functionOrFunctionName);
-void LuaScriptUnsubscribeFromEvent @ UnsubscribeFromEvent(void* sender, const String eventName, void* functionOrFunctionName);
-void LuaScriptUnsubscribeFromEvents @ UnsubscribeFromEvents(void* sender);
+void LuaScriptAddEventHandler @ SubscribeToEvent(const String eventName, void* functionOrFunctionName);
+void LuaScriptAddEventHandler @ SubscribeToEvent(void* sender, const String eventName, void* functionOrFunctionName);
+
+void LuaScriptRemoveEventHandler @ UnsubscribeFromEvent(const String eventName, void* functionOrFunctionName);
+void LuaScriptRemoveEventHandler @ UnsubscribeFromEvent(const String eventName);
+
+void LuaScriptRemoveEventHandler @ UnsubscribeFromEvent(Object* sender, const String eventName, void* functionOrFunctionName);
+void LuaScriptRemoveEventHandler @ UnsubscribeFromEvent(Object* sender, const String eventName);
 
+void LuaScriptRemoveEventHandlers @ UnsubscribeFromEvents(Object* sender);
+void LuaScriptRemoveAllEventHandlers @ UnsubscribeFromAllEvents();
+void LuaScriptRemoveEventHandlersExcept @ UnsubscribeFromAllEventsExcept(const Vector<String>& exceptionNames);
+
+void LuaScriptSendEvent @ SendEvent(const String eventName, VariantMap& eventData);
 void LuaScriptSetExecuteConsoleCommands @ SetExecuteConsoleCommands(bool enable);
 bool LuaScriptGetExecuteConsoleCommands @ GetExecuteConsoleCommands();
 
@@ -18,12 +23,13 @@ static LuaScript* GetLuaScript(lua_State* L)
     return GetContext(L)->GetSubsystem<LuaScript>();
 }
 
-#define LuaScriptSendEvent GetLuaScript(tolua_S)->ScriptSendEvent
-#define LuaScriptSubscribeToEvent GetLuaScript(tolua_S)->ScriptSubscribeToEvent
-#define LuaScriptUnsubscribeFromEvent GetLuaScript(tolua_S)->ScriptUnsubscribeFromEvent
-#define LuaScriptUnsubscribeFromAllEvents GetLuaScript(tolua_S)->ScriptUnsubscribeFromAllEvents
-#define LuaScriptUnsubscribeFromEvents GetLuaScript(tolua_S)->ScriptUnsubscribeFromEvents
+#define LuaScriptAddEventHandler GetLuaScript(tolua_S)->AddEventHandler
+#define LuaScriptRemoveEventHandler GetLuaScript(tolua_S)->RemoveEventHandler
+#define LuaScriptRemoveEventHandlers GetLuaScript(tolua_S)->RemoveEventHandlers
+#define LuaScriptRemoveAllEventHandlers GetLuaScript(tolua_S)->RemoveAllEventHandlers
+#define LuaScriptRemoveEventHandlersExcept GetLuaScript(tolua_S)->RemoveEventHandlersExcept
 
+#define LuaScriptSendEvent GetLuaScript(tolua_S)->SendEvent
 #define LuaScriptSetExecuteConsoleCommands GetLuaScript(tolua_S)->SetExecuteConsoleCommands
 #define LuaScriptGetExecuteConsoleCommands GetLuaScript(tolua_S)->GetExecuteConsoleCommands
 
@@ -31,119 +37,190 @@ static bool tolua_isfunctionorurho3dstring(lua_State* L, int lo, int def, tolua_
 {
     return lua_isfunction(L, lo) || tolua_isurho3dstring(L, lo, def, err);
 }
-
-#define TOLUA_DISABLE_tolua_LuaScriptLuaAPI_SubscribeToEvent00
-static int tolua_LuaScriptLuaAPI_SubscribeToEvent00(lua_State* tolua_S)
+static int tolua_LuaScriptLuaAPI_SubscribeToEvent(lua_State* tolua_S)
 {
-#ifndef TOLUA_RELEASE
+    int args = lua_gettop(tolua_S);
+
+#ifndef TOLUA_RELEASE    
     tolua_Error tolua_err;
-    if (
-        !tolua_isurho3dstring(tolua_S,1,0,&tolua_err) ||
-        !tolua_isfunctionorurho3dstring(tolua_S,2,0,&tolua_err) ||
-        !tolua_isnoobj(tolua_S,3,&tolua_err)
-        )
-        goto tolua_lerror;
+    if (args == 2)
+    {   
+        // SubscribeToEvent(const String eventName, void* functionOrFunctionName);
+        if (!tolua_isurho3dstring(tolua_S,1,0,&tolua_err) || 
+            !tolua_isfunctionorurho3dstring(tolua_S,2,0,&tolua_err))
+            goto tolua_lerror;
+    }
+    else if (args == 3)
+    {
+        // SubscribeToEvent(Object* sender, const String eventName, void* functionOrFunctionName);
+        if (!tolua_isuserdata(tolua_S,1,0,&tolua_err) ||
+            !tolua_isurho3dstring(tolua_S,2,0,&tolua_err) ||
+            !tolua_isfunctionorurho3dstring(tolua_S,3,0,&tolua_err))
+            goto tolua_lerror;
+    }
     else
+        goto tolua_lerror;
 #endif
+
+    if (args == 2)
     {
+        // SubscribeToEvent(const String eventName, void* functionOrFunctionName);
         const String eventName = ((const String)  tolua_tourho3dstring(tolua_S,1,0));
         if (lua_isfunction(tolua_S,2))
-            LuaScriptSubscribeToEvent(eventName,2);
+            LuaScriptAddEventHandler(eventName,2);
         else
         {
             const String functionName = (const String)tolua_tourho3dstring(tolua_S,2,0);
-            LuaScriptSubscribeToEvent(eventName,functionName);
+            LuaScriptAddEventHandler(eventName,functionName);
         }        
     }
+    else if (args == 3)
+    {
+        // SubscribeToEvent(Object* sender, const String eventName, void* functionOrFunctionName);
+        Object* sender = ((Object*)  tolua_touserdata(tolua_S,1,0));
+        const String eventName = ((const String)  tolua_tourho3dstring(tolua_S,2,0));
+        if (lua_isfunction(tolua_S,3))
+            LuaScriptAddEventHandler(sender,eventName,3);
+        else
+        {
+            const String functionName = (const String)tolua_tourho3dstring(tolua_S,3,0);
+            LuaScriptAddEventHandler(sender,eventName,functionName);
+        }
+    }
+
     return 0;
+
 #ifndef TOLUA_RELEASE
 tolua_lerror:
     tolua_error(tolua_S,"#ferror in function 'SubscribeToEvent'.",&tolua_err);
     return 0;
-#endif
+#endif    
+}
+
+#define TOLUA_DISABLE_tolua_LuaScriptLuaAPI_SubscribeToEvent00
+static int tolua_LuaScriptLuaAPI_SubscribeToEvent00(lua_State* tolua_S)
+{
+    return tolua_LuaScriptLuaAPI_SubscribeToEvent(tolua_S);
 }
 
 #define TOLUA_DISABLE_tolua_LuaScriptLuaAPI_SubscribeToEvent01
 static int tolua_LuaScriptLuaAPI_SubscribeToEvent01(lua_State* tolua_S)
 {
+    return tolua_LuaScriptLuaAPI_SubscribeToEvent(tolua_S);
+}
+
+static int tolua_LuaScriptLuaAPI_UnsubscribeFromEvent(lua_State* tolua_S)
+{
+    int args = lua_gettop(tolua_S);
+#ifndef TOLUA_RELEASE    
     tolua_Error tolua_err;
-    if (
-        !tolua_isuserdata(tolua_S,1,0,&tolua_err) ||
-        !tolua_isurho3dstring(tolua_S,2,0,&tolua_err) ||
-        !tolua_isfunctionorurho3dstring(tolua_S,3,0,&tolua_err) ||
-        !tolua_isnoobj(tolua_S,4,&tolua_err)
-        )
-        goto tolua_lerror;
+    if (args == 1)
+    {
+        // UnsubscribeFromEvent(const String eventName);
+        if (!tolua_isurho3dstring(tolua_S,1,0,&tolua_err))
+            goto tolua_lerror;
+    }
+    else if (args == 2)
+    {
+        // UnsubscribeFromEvent(const String eventName, void* functionOrFunctionName);
+        if (tolua_isurho3dstring(tolua_S,1,0,&tolua_err))
+        {
+            if (!tolua_isfunctionorurho3dstring(tolua_S,2,0,&tolua_err))
+                goto tolua_lerror;
+        }
+        // UnsubscribeFromEvent(Object* sender, const String eventName);
+        else if (tolua_isuserdata(tolua_S,1,0,&tolua_err))
+        {
+            if (!tolua_isurho3dstring(tolua_S,2,0,&tolua_err))
+                goto tolua_lerror;
+        }
+        else
+            goto tolua_lerror;
+    }
+    else if (args == 3)
+    {
+        // UnsubscribeFromEvent(Object* sender, const String eventName, void* functionOrFunctionName);
+        if (!tolua_isuserdata(tolua_S,1,0,&tolua_err) ||
+            !tolua_isurho3dstring(tolua_S,2,0,&tolua_err) ||
+            !tolua_isfunctionorurho3dstring(tolua_S,3,0,&tolua_err))
+            goto tolua_lerror;
+    }
     else
+        goto tolua_lerror;
+#endif
+
+    if (args == 1)
+    {
+        // UnsubscribeFromEvent(const String eventName);
+        const String eventName = ((const String)  tolua_tourho3dstring(tolua_S,1,0));
+        LuaScriptRemoveEventHandler(eventName);
+    }
+    else if (args == 2)
+    {
+        // UnsubscribeFromEvent(const String eventName, void* functionOrFunctionName);
+        if (tolua_isurho3dstring(tolua_S,1,0,&tolua_err))
+        {
+            const String eventName = ((const String)  tolua_tourho3dstring(tolua_S,1,0));
+            if (lua_isfunction(tolua_S, 2))
+                LuaScriptRemoveEventHandler(eventName, 2);
+            else
+            {
+                const String functionName = (const String)tolua_tourho3dstring(tolua_S,2,0);
+                LuaScriptRemoveEventHandler(eventName, functionName);
+            }
+        }
+        // UnsubscribeFromEvent(Object* sender, const String eventName);
+        else if (tolua_isuserdata(tolua_S,1,0,&tolua_err))
+        {
+            Object* sender = ((Object*)  tolua_touserdata(tolua_S,1,0));
+            const String eventName = ((const String)  tolua_tourho3dstring(tolua_S,2,0));
+            LuaScriptRemoveEventHandler(sender, eventName);
+        }
+    }
+    else if (args == 3)
     {
-        void* sender = ((void*)  tolua_touserdata(tolua_S,1,0));
+        // UnsubscribeFromEvent(Object* sender, const String eventName, void* functionOrFunctionName);
+        Object* sender = ((Object*)  tolua_touserdata(tolua_S,1,0));
         const String eventName = ((const String)  tolua_tourho3dstring(tolua_S,2,0));
         if (lua_isfunction(tolua_S,3))
-            LuaScriptSubscribeToEvent(sender,eventName,3);
+            LuaScriptRemoveEventHandler(sender,eventName,3);
         else
         {
             const String functionName = (const String)tolua_tourho3dstring(tolua_S,3,0);
-            LuaScriptSubscribeToEvent(sender,eventName,functionName);
+            LuaScriptRemoveEventHandler(sender,eventName,functionName);
         }
     }
+
     return 0;
+
+#ifndef TOLUA_RELEASE
 tolua_lerror:
-    return tolua_LuaScriptLuaAPI_SubscribeToEvent00(tolua_S);
+    tolua_error(tolua_S,"#ferror in function 'UnsubscribeFromEvent'.",&tolua_err);
+    return 0;
+#endif
 }
 
-static int tolua_LuaScriptLuaAPI_UnsubscribeFromEvent00(lua_State* tolua_S);
+#define TOLUA_DISABLE_tolua_LuaScriptLuaAPI_UnsubscribeFromEvent00
+static int tolua_LuaScriptLuaAPI_UnsubscribeFromEvent00(lua_State* tolua_S)
+{
+    return tolua_LuaScriptLuaAPI_UnsubscribeFromEvent(tolua_S);
+}
 
 #define TOLUA_DISABLE_tolua_LuaScriptLuaAPI_UnsubscribeFromEvent01
 static int tolua_LuaScriptLuaAPI_UnsubscribeFromEvent01(lua_State* tolua_S)
 {
-    tolua_Error tolua_err;
-    if (
-        !tolua_isurho3dstring(tolua_S,1,0,&tolua_err) ||
-        !tolua_isfunctionorurho3dstring(tolua_S,2,0,&tolua_err) ||
-        !tolua_isnoobj(tolua_S,3,&tolua_err)
-        )
-        goto tolua_lerror;
-    else
-    {
-        const String eventName = ((const String)  tolua_tourho3dstring(tolua_S,1,0));
-        if (lua_isfunction(tolua_S,2))
-            LuaScriptUnsubscribeFromEvent(eventName,2);
-        else
-        {
-            const String functionName = (const String)tolua_tourho3dstring(tolua_S,2,0);
-            LuaScriptUnsubscribeFromEvent(eventName,functionName);
-        }
-    }
-    return 0;
-tolua_lerror:
-    return tolua_LuaScriptLuaAPI_UnsubscribeFromEvent00(tolua_S);
+    return tolua_LuaScriptLuaAPI_UnsubscribeFromEvent(tolua_S);
 }
 
 #define TOLUA_DISABLE_tolua_LuaScriptLuaAPI_UnsubscribeFromEvent02
 static int tolua_LuaScriptLuaAPI_UnsubscribeFromEvent02(lua_State* tolua_S)
 {
-    tolua_Error tolua_err;
-    if (
-        !tolua_isuserdata(tolua_S,1,0,&tolua_err) ||
-        !tolua_isurho3dstring(tolua_S,2,0,&tolua_err) ||
-        !tolua_isfunctionorurho3dstring(tolua_S,3,0,&tolua_err) ||
-        !tolua_isnoobj(tolua_S,4,&tolua_err)
-        )
-        goto tolua_lerror;
-    else
-    {
-        void* sender = ((void*)  tolua_touserdata(tolua_S,1,0));
-        const String eventName = ((const String)  tolua_tourho3dstring(tolua_S,2,0));
-        if (lua_isfunction(tolua_S,3))
-            LuaScriptUnsubscribeFromEvent(sender,eventName,3);
-        else
-        {
-            const String functionName = (const String)tolua_tourho3dstring(tolua_S,3,0);
-            LuaScriptUnsubscribeFromEvent(sender,eventName,functionName);
-        }
-    }
-    return 0;
-tolua_lerror:
-    return tolua_LuaScriptLuaAPI_UnsubscribeFromEvent01(tolua_S);
+    return tolua_LuaScriptLuaAPI_UnsubscribeFromEvent(tolua_S);
+}
+
+#define TOLUA_DISABLE_tolua_LuaScriptLuaAPI_UnsubscribeFromEvent03
+static int tolua_LuaScriptLuaAPI_UnsubscribeFromEvent03(lua_State* tolua_S)
+{
+    return tolua_LuaScriptLuaAPI_UnsubscribeFromEvent(tolua_S);
 }
 $}