소스 검색

Added procedural scripted event handling support.
Reduced the maximum script execution nesting level.

Lasse Öörni 15 년 전
부모
커밋
a37f84c0a6

+ 22 - 0
Engine/Engine/RegisterEvent.cpp

@@ -22,6 +22,7 @@
 //
 
 #include "Precompiled.h"
+#include "ScriptFile.h"
 #include "ScriptInstance.h"
 
 #include <angelscript.h>
@@ -35,23 +36,44 @@ void SendEvent(const std::string& eventType, VariantMap& parameters)
 
 void SubscribeToEvent(const std::string& eventType, const std::string& handlerName)
 {
+    // Try to subscribe to an instance event first, then to a script file event if not found
     ScriptInstance* instance = getScriptContextComponent();
     if (instance)
         instance->addEventHandler(StringHash(eventType), handlerName);
+    else
+    {
+        ScriptFile* file = getLastScriptFile();
+        if (file)
+            file->addEventHandler(StringHash(eventType), handlerName);
+    }
 }
 
 void UnsubscribeFromEvent(const std::string& eventType)
 {
+    // Try to unsubscribe from an instance event first, then from a script file event if not found
     ScriptInstance* instance = getScriptContextComponent();
     if (instance)
         instance->removeEventHandler(StringHash(eventType));
+    else
+    {
+        ScriptFile* file = getLastScriptFile();
+        if (file)
+            file->removeEventHandler(StringHash(eventType));
+    }
 }
 
 void UnsubscribeFromAllEvents()
 {
+    // Try to unsubscribe from instance events first, then from script file events if not found
     ScriptInstance* instance = getScriptContextComponent();
     if (instance)
         instance->removeAllEventHandlers();
+    else
+    {
+        ScriptFile* file = getLastScriptFile();
+        if (file)
+            file->removeAllEventHandlers();
+    }
 }
 
 void registerEventLibrary(asIScriptEngine* engine)

+ 42 - 0
Engine/Script/ScriptEventListener.cpp

@@ -0,0 +1,42 @@
+//
+// Urho3D Engine
+// Copyright (c) 2008-2011 Lasse Öörni
+//
+// 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.
+//
+
+#include "Precompiled.h"
+#include "ScriptEventListener.h"
+
+void ScriptEventListener::removeEventHandler(StringHash eventType)
+{
+    std::map<StringHash, asIScriptFunction*>::iterator i = mEventHandlers.find(eventType);
+    if (i != mEventHandlers.end())
+    {
+        unsubscribeFromEvent(eventType);
+        mEventHandlers.erase(i);
+    }
+}
+
+void ScriptEventListener::removeAllEventHandlers()
+{
+    for (std::map<StringHash, asIScriptFunction*>::iterator i = mEventHandlers.begin(); i != mEventHandlers.end(); ++i)
+        unsubscribeFromEvent(i->first);
+    mEventHandlers.clear();
+}

+ 46 - 0
Engine/Script/ScriptEventListener.h

@@ -0,0 +1,46 @@
+//
+// Urho3D Engine
+// Copyright (c) 2008-2011 Lasse Öörni
+//
+// 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.
+//
+
+#ifndef SCRIPT_SCRIPTEVENTLISTENER_H
+#define SCRIPT_SCRIPTEVENTLISTENER_H
+
+#include "EventListener.h"
+
+class asIScriptFunction;
+
+class ScriptEventListener : public EventListener
+{
+public:
+    //! Add an event handler. Called by script exposed subscribeToEvent() function
+    virtual void addEventHandler(StringHash eventType, const std::string& handlerName) = 0;
+    //! Remove an event handler. Called by script exposed version of unsubcribeFromEvent() function
+    void removeEventHandler(StringHash eventType);
+    //! Remove all event handlers. Called by script exposed version of unsubscribeFromAllEvent() function
+    void removeAllEventHandlers();
+    
+protected:
+    //! Pointers to event handler methods
+    std::map<StringHash, asIScriptFunction*> mEventHandlers;
+};
+
+#endif // SCRIPT_SCRIPTEVENTLISTENER_H

+ 57 - 3
Engine/Script/ScriptFile.cpp

@@ -33,13 +33,15 @@
 
 #include "DebugNew.h"
 
-static const int MAX_NESTING_LEVEL = 100;
+static const int MAX_NESTING_LEVEL = 32;
 static int executeNestingLevel = 0;
+ScriptFile* lastScriptFile = 0;
 
 ScriptFile::ScriptFile(ScriptEngine* scriptEngine, const std::string& name) :
     Resource(name),
     mScriptEngine(scriptEngine),
     mScriptModule(0),
+    mScriptContext(0),
     mCompiled(false)
 {
     if (!mScriptEngine)
@@ -54,6 +56,13 @@ ScriptFile::~ScriptFile()
         engine->DiscardModule(getName().c_str());
         mScriptModule = 0;
     }
+    if (mScriptContext)
+    {
+        mScriptContext->Release();
+        mScriptContext = 0;
+    }
+    if (lastScriptFile == this)
+        lastScriptFile = 0;
 }
 
 void ScriptFile::load(Deserializer& source, ResourceCache* cache)
@@ -65,11 +74,13 @@ void ScriptFile::load(Deserializer& source, ResourceCache* cache)
     source.read((void*)buffer.getPtr(), dataSize);
     
     // Discard the previous module if there was one
-    setMemoryUse(0);
     mCompiled = false;
+    setMemoryUse(0);
+    removeAllEventHandlers();
+    
+    // Create the module
     asIScriptEngine* engine = mScriptEngine->getAngelScriptEngine();
     mScriptModule = engine->GetModule(getName().c_str(), asGM_ALWAYS_CREATE);
-    
     if (!mScriptModule)
         EXCEPTION("Failed to create script module " + getName());
     if (mScriptModule->AddScriptSection(getName().c_str(), (const char*)buffer.getPtr(), dataSize) < 0)
@@ -83,6 +94,27 @@ void ScriptFile::load(Deserializer& source, ResourceCache* cache)
     mCompiled = true;
 }
 
+void ScriptFile::addEventHandler(StringHash eventType, const std::string& handlerName)
+{
+    if (!mCompiled)
+        return;
+    
+    std::string declaration = "void " + handlerName + "(StringHash, VariantMap&)";
+    asIScriptFunction* function = getFunction(declaration);
+    if (!function)
+    {
+        LOGERROR("Event handler function " + declaration + " not found in " + getName());
+        return;
+    }
+    
+    subscribeToEvent(eventType, EVENT_HANDLER(ScriptFile, handleScriptEvent));
+    mEventHandlers[eventType] = function;
+    
+    // Create a context for script event handling if does not exist already
+    if (!mScriptContext)
+        mScriptContext = mScriptEngine->createScriptContext();
+}
+
 bool ScriptFile::execute(const std::string& declaration, asIScriptContext* context, const std::vector<Variant>& parameters)
 {
     asIScriptFunction* function = getFunction(declaration);
@@ -260,6 +292,8 @@ asIScriptFunction* ScriptFile::getMethod(asIScriptObject* object, const std::str
 
 void ScriptFile::setParameters(asIScriptContext* context, asIScriptFunction* function, const std::vector<Variant>& parameters)
 {
+    lastScriptFile = this;
+    
     unsigned paramCount = function->GetParamCount();
     for (unsigned i = 0; (i < parameters.size()) && (i < paramCount); ++i)
     {
@@ -319,3 +353,23 @@ void ScriptFile::setParameters(asIScriptContext* context, asIScriptFunction* fun
         }
     }
 }
+
+void ScriptFile::handleScriptEvent(StringHash eventType, VariantMap& eventData)
+{
+    if (!mCompiled)
+        return;
+    
+    std::map<StringHash, asIScriptFunction*>::iterator i = mEventHandlers.find(eventType);
+    if (i == mEventHandlers.end())
+        return;
+    
+    std::vector<Variant> parameters;
+    parameters.push_back(Variant((void*)&eventType));
+    parameters.push_back(Variant((void*)&eventData));
+    execute(i->second, mScriptContext, parameters);
+}
+
+ScriptFile* getLastScriptFile()
+{
+    return lastScriptFile;
+}

+ 13 - 2
Engine/Script/ScriptFile.h

@@ -25,6 +25,7 @@
 #define SCRIPT_SCRIPTFILE_H
 
 #include "Resource.h"
+#include "ScriptEventListener.h"
 #include "SharedPtr.h"
 #include "Variant.h"
 
@@ -32,12 +33,13 @@
 
 class ScriptEngine;
 class Variant;
-class asIScriptModule;
+class asIScriptContext;
 class asIScriptFunction;
+class asIScriptModule;
 class asIScriptObject;
 
 //! A script file resource
-class ScriptFile : public Resource
+class ScriptFile : public Resource, public ScriptEventListener
 {
     DEFINE_TYPE(ScriptFile);
     
@@ -49,6 +51,8 @@ public:
     
     //! Load resource
     virtual void load(Deserializer& source, ResourceCache* cache = 0);
+    //! Add an event handler. Called by script exposed subscribeToEvent() function
+    virtual void addEventHandler(StringHash eventType, const std::string& handlerName);
     
     //! Query for a function by declaration and execute if found. If context is null, use the immediate context
     bool execute(const std::string& declaration, asIScriptContext* context = 0, const std::vector<Variant>& parameters = std::vector<Variant>());
@@ -75,13 +79,20 @@ public:
 private:
     //! Set parameters for a function or method
     void setParameters(asIScriptContext* context, asIScriptFunction* function, const std::vector<Variant>& parameters);
+    //! Handle an event with a handler in script
+    void handleScriptEvent(StringHash eventType, VariantMap& eventData);
     
     //! Script engine
     SharedPtr<ScriptEngine> mScriptEngine;
     //! Script module
     asIScriptModule* mScriptModule;
+    //! Event handler script context
+    asIScriptContext* mScriptContext;
     //! Compiled flag
     bool mCompiled;
 };
 
+//! Get last script file that is executing or has executed script code
+ScriptFile* getLastScriptFile();
+
 #endif // SCRIPT_SCRIPTFILE_H

+ 0 - 17
Engine/Script/ScriptInstance.cpp

@@ -341,23 +341,6 @@ void ScriptInstance::addEventHandler(StringHash eventType, const std::string& ha
     mEventHandlers[eventType] = method;
 }
 
-void ScriptInstance::removeEventHandler(StringHash eventType)
-{
-    std::map<StringHash, asIScriptFunction*>::iterator i = mEventHandlers.find(eventType);
-    if (i != mEventHandlers.end())
-    {
-        unsubscribeFromEvent(eventType);
-        mEventHandlers.erase(i);
-    }
-}
-
-void ScriptInstance::removeAllEventHandlers()
-{
-    for (std::map<StringHash, asIScriptFunction*>::iterator i = mEventHandlers.begin(); i != mEventHandlers.end(); ++i)
-        unsubscribeFromEvent(i->first);
-    mEventHandlers.clear();
-}
-
 void ScriptInstance::releaseObject()
 {
     if (mScriptObject)

+ 5 - 10
Engine/Script/ScriptInstance.h

@@ -27,6 +27,7 @@
 #include "Component.h"
 #include "EventListener.h"
 #include "Quaternion.h"
+#include "ScriptEventListener.h"
 #include "SharedPtr.h"
 
 class Entity;
@@ -61,7 +62,7 @@ enum ScriptInstanceMethod
 };
 
 //! A scripted component
-class ScriptInstance : public Component, public EventListener
+class ScriptInstance : public Component, public ScriptEventListener
 {
     DEFINE_TYPE(ScriptInstance);
     
@@ -91,6 +92,8 @@ public:
     virtual void interpolate(bool snapToEnd);
     //! Return component references
     virtual void getComponentRefs(std::vector<ComponentRef>& dest);
+    //! Add an event handler. Called by script exposed subscribeToEvent() function
+    virtual void addEventHandler(StringHash eventType, const std::string& handlerName);
     
     //! Set script file and class
     bool setScriptClass(ScriptFile* scriptFile, const std::string& className);
@@ -117,13 +120,6 @@ public:
     //! Return whether scripted updates and event handlers are enabled
     bool isEnabled() const { return mEnabled; }
     
-    //! Add an event handler. Called by script exposed subscribeToEvent() function
-    void addEventHandler(StringHash eventType, const std::string& handlerName);
-    //! Remove an event handler. Called by script exposed unsubcribeFromEvent() function
-    void removeEventHandler(StringHash eventType);
-    //! Remove all event handlers. Called by script exposed unsubscribeFromAllEvent() function
-    void removeAllEventHandlers();
-    
 private:
     //! Release object
     void releaseObject();
@@ -154,8 +150,7 @@ private:
     std::string mClassName;
     //! Pointers to supported inbuilt methods
     asIScriptFunction* mMethods[MAX_SCRIPT_METHODS];
-    //! Pointers to event handler methods
-    std::map<StringHash, asIScriptFunction*> mEventHandlers;
+
     //! Enabled flag
     bool mEnabled;
 };